In the previous installment of the ‘Tip of the Day’ I started to talk about some lesser known features related to semantic actions. Today I will highlight some more details. If a semantic action is attached to a component which is part of an expression assigned to a rule (the rule’s right hand side) it is not only possible to access the attributes of the components it is connected with. In addition it is possible to access rule specific values! Sounds interesting? Read on!
I chose this topic even if I got some comments asking me to avoid writing about things already covered in Spirit’s documentation. At the same time, I believe it’s valuable for some of you to repeat the facts from the docs but presented in a more pointed and descriptive way. Additionally, I think the articles as published here lay the ground for more detailed things not yet covered in the docs, but the basics need to be explained properly before we can touch the more advanced stuff. But please leave your comments below telling me what you would like to see highlighted. I am always interested in getting feedback on what works for you and what not.
Ok, here we go – let us talk about rule bound semantic actions.
Any semantic action attached to the right hand side of a rule (or a part of it) is special. We will call those ‘rule bound semantic actions’. Spirit provides you with extra placeholder variables (in addition to _1, _2, etc.) usable to access values related to the enclosing rule (the rule, the expression has been assigned to). For the purpose of this post I assume, that the semantic actions are written using Boost.Phoenix. In fact, all placeholders described below are implemented using Phoenix and do not integrate very well with other, similar facilities available in Boost (such as Boost.Bind or Boost.Lambda). You should avoid mixing Phoenix placeholder with expressions built from those libraries. This is no limitation, as from my experience, using Phoenix is just the simplest way to write semantic actions.
Let us assume further, that we have a component c with an attached semantic action f, which is assigned as the right hand side of a rule exposing a synthesized attribute of type Attr and providing local variables of types L1, L2, etc. (I will explain local variables shortly):
rule<Iterator, Attr(), locals<L1, L2, ...> > r = c[f];
||Nth attribute of
||The enclosing rule’s synthesized attribute (of type Attr)|
||The enclosing rule’s local variables (
||The enclosing rule’s Nth inherited attribute|
We already talked about _val, _1, _2, etc. last time, and I will not describe inherited attributes today, that is for another day. I would like to concentrate on local variables here.
Local variables allow to associate arbitrary typed data instances with each rule invocation. These variables are very similar to local variables in a function. They are valid only during the invocation of the rule they are defined in and they automatically go out of scope after the right hand side of the rule has finished executing. Further, each invocation of a rule creates a new set of (default constructed) local variables, even if the same rule is invoked recursively.
Here is a small Karma example generating a list of items prefixed with a sequence number (note: _a, eps, string, lit, rule, and locals are imported from namespace boost::spirit::karma).
rule<OutIter, locals<int>, std::vector<std::string>()> r = eps[_a = 1] << (lit(_a) << eps[++_a] << " " << string) % '\n';
Let us analyze, what this rule definition does.
- The attribute of this rule is std::vector<std::string>, for the sake of simplicity we will pass in all items stored in this type of container. The items are emitted using the list operator (%), causing them to be interleaved with newlines.
- One of the rule’s template parameters is locals<int>. That is the way we declare the types of the local variables. The template locals<> is a type container you can use to list the types for all local variables to instantiate for each of the rule’s invocations.
- In our example, we have a single local variable of type int and we reference it from the right hand side of the rule definition using the placeholder _a.
- The generator eps is utilized to inject invocations of semantic actions initializing and incrementing the local variable. The generator eps is perfect for doing this as it succeeds always while emitting nothing.
- The most notable construct is lit(_a) showing that it is possible to directly create a generator component from the local variable. Many of Spirit’s construct are enabled for lazy evaluation, and lit() is one of them.
The only additional thing I want to add is that everything shown above works equally well in Qi, but you probably already guessed this. For your convenience, the corresponding symbols are available from the namespace boost::spirit::qi as well. If you are interested in seeing locals in action in a more complete Qi example you should have a look at the mini-xml series as described in the docs here.
This ‘Tip of the Day’ got a little longer than usual, but I hope it is worth reading anyways. Spirit is a large library and there are uncounted hidden gems waiting not only to be described, but some of them are still waiting to be discovered. Happy hacking and experimenting!