Guards/Conditions section of grammar

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.

Example

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 ...
    }
}

Guards written inline

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:

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.