Boost C++ Libraries Home Libraries People FAQ More

PrevUpHomeNext
Activating Your Grammars

The Proto grammars we've seen so far are static. You can check at compile-time to see if an expression type matches a grammar, but that's it. Things get more interesting when you give them runtime behaviors. A grammar with embedded transforms is more than just a static grammar. It is a function object that accepts expressions that match the grammar and does something with them.

Below is a very simple grammar. It matches terminal expressions.

// A simple Proto grammar that matches all terminals
proto::terminal< _ >

Here is the same grammar with a transform that extracts the value from the terminal:

// A simple Proto grammar that matches all terminals
// *and* a function object that extracts the value from
// the terminal
proto::when<
    proto::terminal< _ >
  , proto::_value          // <-- Look, a transform!
>

You can read this as follows: when you match a terminal expression, extract the value. The type proto::_value is a so-called transform. Later we'll see what makes it a transform, but for now just think of it as a kind of function object. Note the use of proto::when<>: the first template parameter is the grammar to match and the second is the transform to execute. The result is both a grammar that matches terminal expressions and a function object that accepts terminal expressions and extracts their values.

As with ordinary grammars, we can define an empty struct that inherits from a grammar+transform to give us an easy way to refer back to the thing we're defining, as follows:

// A grammar and a function object, as before
struct Value
  : proto::when<
        proto::terminal< _ >
      , proto::_value
    >
{};

// "Value" is a grammar that matches terminal expressions
BOOST_MPL_ASSERT(( proto::matches< proto::terminal<int>::type, Value > ));

// "Value" also defines a function object that accepts terminals
// and extracts their value.
proto::terminal<int>::type answer = {42};
Value get_value;
int i = get_value( answer );

As already mentioned, Value is a grammar that matches terminal expressions and a function object that operates on terminal expressions. It would be an error to pass a non-terminal expression to the Value function object. This is a general property of grammars with transforms; when using them as function objects, expressions passed to them must match the grammar.

Proto grammars are valid TR1-style function objects. That means you can use boost::result_of<> to ask a grammar what its return type will be, given a particular expression type. For instance, we can access the Value grammar's return type as follows:

// We can use boost::result_of<> to get the return type
// of a Proto grammar.
typedef
    typename boost::result_of<Value(proto::terminal<int>::type)>::type
result_type;

// Check that we got the type we expected
BOOST_MPL_ASSERT(( boost::is_same<result_type, int> ));
[Note] Note

A grammar with embedded transforms is both a grammar and a function object. Calling these things "grammars with transforms" would get tedious. We could call them something like "active grammars", but as we'll see every grammar that you can define with Proto is "active"; that is, every grammar has some behavior when used as a function object. So we'll continue calling these things plain "grammars". The term "transform" is reserved for the thing that is used as the second parameter to the proto::when<> template.


PrevUpHomeNext