Phoenix

| Phoenix |

The preceding chapter introduced Phoenix as a means to implementing your semantic actions. We shall look a little bit more into this important library with focus on how you can use it handily with Spirit. This chapter is by no means a thorough discourse of the library. For more information on Phoenix, please take some time to read the Phoenix User's Guide. If you just want to use it quickly, this chapter will probably suffice. Rather than taking you to the theories and details of the library, we shall try to provide you with annotated exemplars instead. Hopefully, this will get you into high gear quickly.

Semantic actions in Spirit can be just about any function or function object (functor) as long as it can satisfy the required signature. For example, `uint_p` requires a signature of `void F(T)`, where `T` is the type of the integer (typically `unsigned int`). Plain vanilla actions are of the `void F(IterT, IterT)` variety. You can code your actions in plain C++. Calls to C++ functions or functors will thus be of the form `P[&F]` or `P[F()]` etc. (see Semantic Actions). Phoenix on the other hand, attempts to mimic C++ such that you can define the function body inlined in the code.

C++ in C++? In as much as Spirit attempts to mimic EBNF in C++, Phoenix attempts to mimic C++ in C++!!!

## var

Remember the `boost::ref`? We discussed that in the Parametric Parsers chapter. Phoenix has a similar, but more flexible, counterpart. It's called `var`. The usage is similar to `boost::ref` and you can use it as a direct replacement. However, unlike `boost::ref`, you can use it to form more complex expressions. Here are some examples:

        var(x) += 3
        var(x) = var(y) + var(z)
        var(x) = var(y) + (3 * var(z))
        var(x) = var(y)[var(i)] // assuming y is indexable and i is an index

Let's start with a simple example. We'll want to parse a comma separated list of numbers and report the sum of all the numbers. Using phoenix's var, we do not have to write external semantic actions. We simply inline the code inside the semantic action slots. Here's the complete grammar with our phoenix actions (see sum.cpp in the examples):

        real_p[var(n) = arg1] >> *(',' >> real_p[var(n) += arg1])

The full source code can be viewed here. This is part of the Spirit distribution.

### argN

Notice the expression: `var(n) = arg1` . What is `arg1` and what is it doing there? `arg1` is an argument placeholder. Remember that `real_p` (see Numerics) reports the parsed number to its attached semantic action. `arg1` is a placeholder for the first argument passed to the semantic action by the parser. If there are more than one arguments passed in, these arguments can be referred to using `arg1`..`argN`. For instance, generic semantic actions (transduction interface; see Semantic Actions) are passed 2 arguments: the iterators (`first`/`last`) to the matching portion of the input stream. You can refer to `first` and `last` through `arg1` and `arg2`, respectively.

Like var, argN is also composable. Here are some examples:

        var(x) += arg1
        var(x) = arg1 + var(z)
        var(x) = arg1 + (3 * arg2)
        var(x) = arg1[arg2] // assuming arg1 is indexable and arg2 is an index

### val

Note the expression: `3 * arg2.` This expression is actually a short-hand equivalent to: `val(3) * arg2`. We shall see later why, in some cases, we need to explicitly wrap constants and literals inside the val. Again, like var and argN, val is also composable.

### Functions

Remember our very first example? In the Quick Start chapter, we presented a parser that parses a comma separated list and stuffs the parsed numbers in a vector (see number_list.cpp) . For simplicity, we used Spirit's pre-defined actors (see Predefined Actors). In the example, we used `push_back_a`:

        real_p[push_back_a(v)] >> *(',' >> real_p[push_back_a(v)])

Phoenix allows you to write more powerful polymorphic functions, similar to `push_back_a`, easily. See stuff_vector.cpp. The example is similar to number_list.cpp in functionality, but this time, using phoenix a function to actually implement the `push_back` function:

        struct push_back_impl
        {
            template <typename Container, typename Item>
            struct result
            {
                typedef void type;
            };

            template <typename Container, typename Item>
            void operator()(Container& c, Item const& item) const
            {
                c.push_back(item);
            }
        };

        function<push_back_impl> const push_back = push_back_impl();

The full source code can be viewed here. This is part of the Spirit distribution.

Predefined Phoenix Functions A future version of Phoenix will include an extensive set of predefined functions covering the whole of STL containers, iterators and algorithms. push_back, will be part of this suite.

`push_back_impl` is a simple wrapper over the `push_back` member function of STL containers. The extra scaffolding is there to provide phoenix with additional information that otherwise cannot be directly deduced. `result` relays to phoenix the return type of the functor (`operator()`) given its argument types (`Container` and `Item`) . In this case, the return type is always, simply `void`.

`push_back` is a phoenix function object. This is the actual function object that we shall use. The beauty behind phoenix function objects is that the actual use is strikingly similar to a normal C++ function call. Here's the number list parser rewritten using our phoenix function object:

        real_p[push_back(var(v), arg1)] >> *(',' >> real_p[push_back(var(v), arg1)])

And, unlike predefined actors, they can be composed. See the pattern? Here are some examples:

        push_back(var(v), arg1 + 2)
        push_back(var(v), var(x) + arg1)
        push_back(var(v)[arg1], arg2) // assuming v is a vector of vectors and arg1 is an index

push_back does not have a return type. Say, for example, we wrote another phoenix function `sin`, we can use it in expressions as well:

        push_back(var(v), sin(arg1) * 2)

### Construct

Sometimes, we wish to construct an object. For instance, we might want to create a `std::string` given the first/last iterators. For instance, say we want to parse a list of identifiers instead. Our grammar, without the actions, is:

        (+alpha_p) >> *(',' >> (+alpha_p))

`construct_` is a predefined phoenix function that, you guessed it, constructs an object, from the arguments passed in. The usage is:

        construct_<T>(arg1, arg2,... argN)

where T is the desired type and arg1..argN are the constructor arguments. For example, we can construct a `std::string` from the first/last iterator pair this way:

        construct_<std::string>(arg1, arg2)

Now, we attach the actions to our grammar:

        (+alpha_p)
        [
            push_back(var(v), construct_<std::string>(arg1, arg2))
        ]
        >>
        *(',' >>
            (+alpha_p)
            [
                push_back(var(v), construct_<std::string>(arg1, arg2))
            ]
        )

The full source code can be viewed here. This is part of the Spirit distribution.

### Lambda expressions

All these phoenix expressions we see above are lambda expressions. The important thing to note is that these expressions are not evaluated immediately. At grammar construction time, when the actions are attached to the productions, a lambda expression actually generates an unnamed function object that is evaluated later, at parse time. In other words, lambda expressions are lazily evaluated.

Lambda Expressions? Lambda expressions are actually unnamed partially applied functions where placeholders (e.g. arg1, arg2) are provided in place of some of the arguments. The reason this is called a lambda expression is that traditionally, such placeholders are written using the Greek letter lambda .

Phoenix uses tricks not unlike those used by Spirit to mimic C++ such that you can define the function body inlined in the code. It's weird, but as mentioned, Phoenix actually mimicks C++ in C++ using expression templates. Surely, there are limitations...

All components in a Phoenix expression must be an actor (in phoenix parlance) in the same way that components in Spirit should be a `parser`. In Spirit, you can write:

        r = ch_p('x') >> 'y';

But not:

        r = 'x' >> 'y';

In essence, `parser >> char` is a parser, but `char >> char` is a char (the char shift-right by another char).

The same restrictions apply to Phoenix. For instance:

        int x = 1;
        cout << var(x) << "pizza"

is a well formed Phoenix expression that's lazily evaluated. But:

        cout << x << "pizza"

is not. Such expressions are immediately executed. C++ syntax dictates that at least one of the operands must be a Phoenix actor type. This also applies to compound expressions. For example:

        cout << var(x) << "pizza" << "man"

This is evaluated as:

        (((cout << var(x)) << "pizza") << "man")

Since `(cout << var(x))` is an actor, at least one of the operands is a phoenix actor, `((cout << var(x)) << "pizza")` is also a Phoenix actor, and the whole expression is thus also an actor.

Sometimes, it is safe to write:

        cout << var(x) << val("pizza") << val("man")

just to make it explicitly clear what we are dealing with, especially with complex expressions, in the same way as we explicitly wrap literal strings in `str_p("lit")` in Spirit.

Phoenix (and Spirit) also deals with unary operators. In such cases, we have no choice. The operand must be a Phoenix actor (or Spirit parser). Examples:

Spirit:

        *ch_p('z')  // good
        *('z') // bad

Phoenix:

        *var(x) // good (lazy)
        *x // bad (immediate)

Also, in Phoenix, for assignments and indexing to be lazily evaluated, the object acted upon should be a Phoenix actor. Examples:

        var(x) = 123 // good (lazy)
        x = 123 // bad (immediate)
        var(x)[0] // good (lazy)
        x[0] // bad, immediate
        var(x)[var(i)] // good (lazy)
        x[var(i)] // bad and illegal (x is not an actor)
        var(x[var(i)]) // bad and illegal (x is not an actor)

### Wrapping up

Well, there you have it. I hope with this jump-start chapter, you may be able to harness the power of lambda expressions. By all means, please read the phoenix manual to learn more about the nitty gritty details. Surely, you'll get to know a lot more than just by reading this chapter. There are a lot of things still to be touched. There won't be enough space here to cover all the features of Phoenix even in brief.

The next chapter, Closures, we'll see more of phoenix. Stay tuned.

Copyright © 1998-2003 Joel de Guzman
Use, modification and distribution is subject to the Boost Software License, Version 1.0. (See accompanying file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)