Grammar Guards - RopleyIT/GLRParser GitHub Wiki
The guards or conditions section of the grammar input file is optional. It is
denoted by either the keyword guards
or conditions
followed by the comma-separated list of guard function names enclosed in curly
braces. The parselr parser extends the more traditional model of a parser
generator by supporting guard conditions that must be true at the time an input
event or token arrives. This has little or no meaning for traditional parsers,
but when the grammar is describing a state machine, the transitions between
states are caused by events that can be accompanied by a guard condition. Only
if the event occurs and the guard condition is true will the transition be made.
Guard conditions can be placed on both tokens and on non-terminal symbols in the grammar. When placed on a non-terminal symbol, the interpretation is that the guard condition is being imposed on the last terminal token within the non-terminal rule expansions. This is explained in more detail elsewhere.
For in-line parsers, note that every guard condition listed in the guards
section
must have corresponding guard testing code within the parser class. This
can either be written between curly braces after the guard name in the grammar,
or can be a guard function written into the parser class that accompanies the
parser. If your grammar's options
section had
the namespace
set to MyApplication
and the
parserclass
to MyParser
, this means you have to write a
guard function with the correct signature and name within the class
MyApplication.MyParser
.
Each guard function must be public, must return a boolean, and takes an
argument of type object
. In other words, it type-matches the
Func<object, bool>
delegate. The argument equals the Value
property from the input token that is being processed at the time the guard
condition is being evaluated. It is assumed that the programmer
writing the guard function will know the true type of the argument passed to it. Hence
the correct cast can be applied to the object
argument to gain access to the parameter if it is needed.
An example of a guards section containing the names of four guard-evaluating methods from your parser:
guards
{
IsFriendly,
EatsHumans,
Asleep,
Alive
}
An example of the parser class that provides these four method implementations:
namespace MyApplication
{
public partial class MyParser
{
... other things ...
public bool IsFriendly(object tokenValue)
{
return !(tokenValue as Animal).EatsHumans();
}
... the other three guard methods ...
}
}
As mentioned above, you also have the option of writing the guard function code embedded in the grammar description. To do this, a number of points should be noted:
- The code for the body of each guard method should immediately follow the name
of the guard function in the
guards
orconditions
section; - The code for the body of each guard method should be enclosed in curly braces;
- The single object argument to the guard function, which contains the value from
the input token the guard is being applied to, is represented by the special
identifier
$token
; - The code block should return a boolean result for all possible paths through the code block.
Here is an example showing the IsFriendly
guard function from
the example above, rewritten as an inline guard function. The remaining three
guard functions are written manually into the parser class in the way described
above:
guards
{
IsFriendly
{
return !($token as Animal).EatsHumans();
},
EatsHumans,
Asleep,
Alive
}
Interestingly this guard function makes a call on one
of the other guards as part of its guard logic. Another
way this might have been done would have been to apply
the logical not operator where the guard condition is applied
to a token in the grammar, i.e. to use [!EatsHumans]
rather than IsFriendly
.