Jan 28

After writing about local variables for rules here I would like to get back to some information I have had lying around for some time already. Today’s topic seems to be a nice fit as porting the dynamic parsers from Spirit.Classic requires to utilize local variables. This allows me to give you some more examples for this facility.

Previous versions of Spirit, which are the versions we refer to as Spirit.Classic today, implemented special dynamic parsers allowing to insert control statements into the parsing process (such as if_p, while_p, and for_p). These dynamic parsers are not available anymore in Qi. But it is easy enough to achieve the same behavior using existing Qi components. This ‘Tip of the Day’ describes those techniques, which have been developed and contributed by Carl Barron. Thanks Carl!

Generally, Spirit.Classic allowed to employ the dynamic parser in two ways. The condition to check could be either a function or function object, or a parser expression. Functions or function objects are expected to return values convertible to bool. When the evaluation of the function or function object yields true it will be considered as meeting the condition. When the parser matches the condition is met as well.

General Notation

In the tables below we use the following notation:

  • c: A nullary function or function object evaluating to bool
  • p, p1, p2, …: Arbitrary parser expressions
  • init, step: Arbitrary Phoenix nullary functions or function objects
  • _a: a local variable of the rule the expression is assigned to (the type of the local variable should be bool)

All symbols are assumed to be referenced from their respective namespaces (i.e. boost::spirit::classic or boost::spirit::qi).

Conditional Parsing (if_p)
Spirit.Classic Expression Equivalent Spirit.Qi Expression
if_p(p)[p1] p >> p1 | eps
if_p(p)[p1].else_p[p2] p >> p1 | p2

The Qi constructs as shown exactly reproduce the behavior if the if_p construct: if the condition ‘p’ is a parser it will consume input in case it matches. If this is not required you may utilize predicates instead (see the post about What’s the Difference Between Qi’s ‘!’ and ‘~’? for more details).

Parsing With Loops (while_p, do_p, and for_p)

All looping constructs require a local Boolean variable to be defined in the rule the expression is assigned to. It does not necessarily have to be the local variable _a, which in the expressions below is used as a placeholder for any local variable (_a, _b, …_j). At the same time this restricts the function objects ‘init’ and ‘step’ to be Phoenix function objects (remember, Phoenix expressions do not mix well with non-Phoenix function objects).

Spirit.Classic Expression Equivalent Spirit.Qi Expression
while_p(p)[p1]     eps[_a = true]
>> *(p >> (p1 | eps[_a = false]))
>>  eps(_a)
do_p[p1].while_p(p)     eps[_a = true]
>> *((p1 | eps[_a = false]) >> p)
>>  eps(_a)
for_p(init, p, step)[p1]     eps[init, _a = true]
>> *(p >> (p1 | eps[_a = false]) >> eps[step])
>>  eps(_a)

All Qi constructs in the table above employ the same trick as shown in the last installment about local variables (What are Rule Bound Semantic Actions?). By using the eps component we inject arbitrary semantic actions into the parser execution. But please don’t confuse this with the eps(_a) component, which will propagate the current value of _a to its own return code. It will succeed parsing unless the current value of _a is false. In our case this forces the whole loop construct to fail parsing if the body parser ‘p1’ failed.

If you replace the ‘p’ with ‘c’ in any of the Classic expressions above (i.e. you want to use a function object as the condition instead of a parser), the ‘p’ in the equivalent Qi expression needs to be replaced by ‘eps(c)’.

Needless to say, all of the above expressions require you to add the proper #include statements to your code. Please consult the related documentation to see what’s necessary.

One Response to “Can the Classic Dynamic Parsers be Ported to Qi?”

  1. while e { s } can be rewritten to if e { s; while e { s } } else { skip }. Could this recursion be used to eliminate the need for a local variable in the while, and do .. while expressions?

Leave a Reply

preload preload preload