allow commas inside matching pairs of parentheses in protocol parameters
This commit is contained in:
@ -482,12 +482,63 @@ To make this easier, <em>protocol arguments</em> can be used:
|
||||
move { out "\$1 GOTO %d"; }
|
||||
</pre>
|
||||
<p>
|
||||
Now, the protocol can be references in the <code>OUT</code> link
|
||||
Now the same protocol can be used in the <code>OUT</code> link
|
||||
of three different records as <code>move(X)</code>,
|
||||
<code>move(Y)</code> and <code>move(Z)</code>.
|
||||
Up to 9 parameters, referenced as <code>$1</code> ... <code>$9</code>
|
||||
can be specified in parentheses, separated by comma.
|
||||
The variable <code>$0</code> is replaced by the name of the protocol.
|
||||
</p>
|
||||
<p>
|
||||
Up to 9 parameters can be specified in parentheses, separated by comma.
|
||||
In the protocol, they are referenced as <code>$1</code> ...
|
||||
<code>$9</code> outside quotes or <code>\$1</code> ... <code>\$9</code>
|
||||
within quotes. The parameter <code>$0</code> resolves to the protocol name.
|
||||
</p>
|
||||
<div class="new">
|
||||
<p>
|
||||
To make links more readable, one space is allowed before and after each comma
|
||||
and the enclosing parentheses. This space is not part of the parameter string.
|
||||
Any additional space is part of the parameter.
|
||||
</p>
|
||||
<p>
|
||||
If a parameter contains matching pairs of parentheses, these and all commas
|
||||
inside are part of the parameter.
|
||||
This allows to pass parameter strings like <code>(1,2)</code> easily without
|
||||
much escaping.
|
||||
</p>
|
||||
<p>
|
||||
Unmatched parentheses must be escaped with double backslash <code>\\</code>
|
||||
as well as must be commas outside pairs of parentheses.
|
||||
Double backslash is necessary because one backslash is already consumed by
|
||||
the db file parser.
|
||||
To pass a literal backslash in a parameter string use 4 backslashes
|
||||
<code>\\\\</code>.
|
||||
</p>
|
||||
</div>
|
||||
<p>
|
||||
Note that macros can be used in parameters. That makes it possible to
|
||||
pass part of the record name to the protocol to be used in
|
||||
<a href="formats.html#redirection">redirections</a>.
|
||||
</p>
|
||||
|
||||
<h4>Example:</h3>
|
||||
<pre>
|
||||
record(ai, "$(PREFIX)recX5") {
|
||||
field(DTYP, "stream")
|
||||
field(INP, "@$(PROTOCOLFILE) read(5, X\\,Y $(PREFIX)) $(PORT)")
|
||||
}
|
||||
record(ai, "$(PREFIX)recY5") {}
|
||||
|
||||
read { out 0x8$1 "READ \$2"; in "%f,%(\$3recY\$1)f" }
|
||||
</pre>
|
||||
<p>
|
||||
The protocol resolves to:
|
||||
</p>
|
||||
<pre>
|
||||
read { out 0x85 "READ X,Y"; in "%f,%($(PREFIX)recY5)f" }
|
||||
</pre>
|
||||
<p>
|
||||
Here <code>$(PREFIX)</code> is replaced with its macro value.
|
||||
But be aware that the macro is actually replaced before the link is parsed so
|
||||
that macro values containing comma or parentheses may have unintended effects.
|
||||
</p>
|
||||
|
||||
<a name="usrvar"></a>
|
||||
@ -565,7 +616,7 @@ There is a fixed set of exception handler names starting with
|
||||
|
||||
</dd>
|
||||
</dl>
|
||||
<h3>Example:</h3>
|
||||
<h4>Example:</h3>
|
||||
<pre>
|
||||
setPosition {
|
||||
out "POS %f";
|
||||
|
@ -189,14 +189,26 @@ parse(const char* filename, const char* _protocolname)
|
||||
ssize_t i = protocolname.find('(');
|
||||
if (i >= 0)
|
||||
{
|
||||
while (i >= 0)
|
||||
while (i < (ssize_t)protocolname.length())
|
||||
{
|
||||
if (protocolname[i-1] == ' ')
|
||||
protocolname.remove(--i, 1); // remove trailing space
|
||||
protocolname[i] = '\0'; // replace '(' and ',' with '\0'
|
||||
protocolname[i] = '\0'; // replace initial '(' and separating ',' with '\0'
|
||||
if (protocolname[i+1] == ' ')
|
||||
protocolname.remove(i+1, 1); // remove leading space
|
||||
i = protocolname.find(',', i+1);
|
||||
int brackets = 0;
|
||||
do {
|
||||
i++;
|
||||
i += strcspn(protocolname(i), ",()\\");
|
||||
char c = protocolname[i];
|
||||
if (c == '(') brackets++;
|
||||
else if (c == ')') brackets--;
|
||||
else if (c == ',' && brackets <= 0) break;
|
||||
else if (c == '\\') {
|
||||
if (protocolname[i+1] == '\\') i++; // keep '\\'
|
||||
else protocolname.remove(i, 1); // else skip over next char
|
||||
}
|
||||
} while (i < (ssize_t)protocolname.length());
|
||||
}
|
||||
// should have closing parentheses
|
||||
if (protocolname[-1] != ')')
|
||||
@ -206,9 +218,8 @@ parse(const char* filename, const char* _protocolname)
|
||||
}
|
||||
protocolname.truncate(-1); // remove ')'
|
||||
if (protocolname[-1] == ' ')
|
||||
{
|
||||
protocolname.truncate(-1); // remove trailing space
|
||||
}
|
||||
debug("StreamCore::parse \"%s\" -> \"%s\"\n", _protocolname, protocolname.expand()());
|
||||
}
|
||||
StreamProtocolParser::Protocol* protocol;
|
||||
protocol = StreamProtocolParser::getProtocol(filename, protocolname);
|
||||
|
@ -764,7 +764,7 @@ initRecord(char* linkstring /* modifiable copy */)
|
||||
}
|
||||
|
||||
// attach to bus interface
|
||||
debug("Stream::initRecord %s: attachBus(%s, %ld, \"%s\")\n",
|
||||
debug("Stream::initRecord %s: attachBus(\"%s\", %ld, \"%s\")\n",
|
||||
name(), busname, addr, busparam);
|
||||
if (!attachBus(busname, addr, busparam))
|
||||
{
|
||||
@ -774,7 +774,7 @@ initRecord(char* linkstring /* modifiable copy */)
|
||||
}
|
||||
|
||||
// parse protocol file
|
||||
debug("Stream::initRecord %s: parse(%s, %s)\n",
|
||||
debug("Stream::initRecord %s: parse(\"%s\", \"%s\")\n",
|
||||
name(), filename, protocol);
|
||||
if (!parse(filename, protocol))
|
||||
{
|
||||
|
@ -10,7 +10,28 @@ set records {
|
||||
record(ao, "DZ:test1")
|
||||
{
|
||||
field (DTYP, "stream")
|
||||
field (OUT, "@test.proto test1(ARG(10)) device")
|
||||
field (OUT, "@test.proto test1(ARG(10),20,30) device")
|
||||
}
|
||||
|
||||
record(ao, "DZ:test2")
|
||||
{
|
||||
field (DTYP, "stream")
|
||||
field (OUT, "@test.proto test1 (ARG(10,20), 30) device")
|
||||
}
|
||||
record(ao, "DZ:test3")
|
||||
{
|
||||
field (DTYP, "stream")
|
||||
field (OUT, "@test.proto test1 ( ARG ( 10 , 20 ) , 30 ) device")
|
||||
}
|
||||
record(ao, "DZ:test4")
|
||||
{
|
||||
field (DTYP, "stream")
|
||||
field (OUT, "@test.proto test1( ARG \\( 10 , 20 , 30 ) device")
|
||||
}
|
||||
record(ao, "DZ:test5")
|
||||
{
|
||||
field (DTYP, "stream")
|
||||
field (OUT, "@test.proto test1(\\ ARG\\,\\\\(10,20)\\,30) device")
|
||||
}
|
||||
}
|
||||
|
||||
@ -28,5 +49,13 @@ startioc
|
||||
|
||||
put DZ:test1 "1"
|
||||
assure "VAL:ARG(10):1\n"
|
||||
put DZ:test2 "1"
|
||||
assure "VAL:ARG(10,20):1\n"
|
||||
put DZ:test3 "1"
|
||||
assure "VAL:ARG ( 10 , 20 ):1\n"
|
||||
put DZ:test4 "1"
|
||||
assure "VAL: ARG ( 10 :1\n"
|
||||
put DZ:test5 "1"
|
||||
assure "VAL: ARG,\\(10,20),30:1\n"
|
||||
|
||||
finish
|
||||
#finish
|
||||
|
Reference in New Issue
Block a user