Jan 21

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];

Here is a list of all predefined placeholders available in the rule bound semantic action F (see also the related documentation here and here):

Placeholders Description
_1, _2 ... , _N Nth attribute of c
_val The enclosing rule’s synthesized attribute (of type Attr)
_a, _b ... , _j
The enclosing rule’s local variables (_a refers to the first of type L1, _b to the next of type L2, etc.)
_r1, _r2 ... , _rN The enclosing rule’s Nth inherited attribute
_pass
Assign false to _pass to force a parser or generator failure
     

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!

3 Responses to “What are Rule Bound Semantic Actions?”

  1. Haitham Gad says:

    Thanks Hartmut. Local variables seem quite interesting 🙂

    I have a question though regarding components and their bound semantic actions.

    Let’s say I have the following Qi rule:

    rule<Iterator> x = c1[ _val = … ] >> c2[ _val = … ]

    Does _val refer to the same variable (the rules synthesized attribute) in both semantic actions? If so, how to refer to the synthesized attributes of the separate components c1 and c2 from their respective semantic actions?

    Also, let’s say I have a rule like this:

    rule<Iterator> y = *c [ cout << _1 ]

    does _1 refer the std::vector<> resulting from *c?

    Thanks,
    Haitham

    • Joel de Guzman says:

      “Does _val refer to the same variable?”
      Yes.
      “how to refer to the synthesized attributes of the separate components c1 and c2”
      Use _1 and _2
      “does _1 refer the std::vector<> resulting from *c”
      No. The C++ precedence rules gives [] a higher precendence than *. Use (*c)[…]

  2. Shoyeb Inamdar says:

    can we increase the size of locals allowed using BOOST_SPIRIT_MAX_LOCALS_SIZE macro, if so how can we do that.

Leave a Reply

preload preload preload