We had an example of a terminal, the int parser. next, well
investigate extended terminals, those that accept parameters.
First, we'll have another example of a simple terminal, the
bare eps parser. Like before, we start of by enabling our place holder.
But wait, we haven't really seen how we define our place holders.
It's actually quite simple. For the int_ (and friends) we
have this code:
Our eps parser also has its eps placeholder. Unlike the int_
placeholder, however, eps can stand in its own, or can accept
a parameter. eps basicallly has two forms:
eps // as is
eps(c) // with a condition
the condition can be an immediate boolean, or a function or function
object (typically phoenix expressions) that return a bool. Some
The parameterized forms are called semantic predicates. The parser
fails or succeeds depending on the bool condition or the result of
the function f, in the last example. The function f is evaluated not
at grammar defintion time, but at parse time. In other words, it
is lazy. All function-like parsers, that take in functions as its
arguments, like the eps(f), char_(f), lit(f), etc., are lazily
evaluated at parse time.
For terminals with function-like forms, we use a related macro:
Hence, for eps:
Similarly, you'll see in support/common_terminals.hpp a macro
call to BOOST_SPIRIT_DEFINE_TERMINALS_EX which lists all the
extended terminals used by Spirit:
(terminal_ex is the one used for "extended" terminals when
we use the macro BOOST_SPIRIT_TERMINAL_EX)
and we enable the use_terminal only if A0 is convertible to
a bool. The fusion::vector holds the arguments passed into our
eps terminal. Here, we care only for single arguments. eps
only accepts a single argument.
Then all the lazy magic happens. use_lazy_terminal is only
concerned about 1) the domain 2) the terminal tag and 3) the
arity. The code above reads as: use lazy terminal in qi::domain
with tag eps and an arity of 1.
At parser construction time, this is all that's needed. At
parse time, the function f, passed to the eps terminal, will
be converted to a semantic_predicate. Again, take note, this
happens at parse time. The actual creation of the semantic_predicate
is delayed. Only at parse time can we truly know the boolean
predicate. For example, this may be a rule local variable, a
rule inherited attribute, and so on.
Attached is the full eps.hpp file. Peruse the code. Try to see how it
compares to the one in "classic' spirit. You'll be amazed :-)
Copyright (c) 2001-2008 Joel de Guzman
Distributed under 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)
Definitely nicely done. Cannot wait till the next installment.
On Sun, Nov 16, 2008 at 10:11 PM, Joel de Guzman
<[hidden email]> wrote:
> Hi! here's an (important) update...
> So far, I've been writng code with the attribute metafunction
> writen as (example):
>> template <typename Context>
>> struct attribute
>> typedef unused_type type;
> This will have to be changed to:
> template <typename Iterator, typename Context>
> struct attribute;
> The iterator type is needed in certain (rare) occasions.
> An example is the raw[p] directive which creates an
> iterator range as its attribute. There might be more
> in karma.
> Joel de Guzman
> http://www.boostpro.com > http://spirit.sf.net >
> This SF.Net email is sponsored by the Moblin Your Move Developer's challenge
> Build the coolest Linux based applications with Moblin SDK & win great prizes
> Grand prize is a trip for two to an Open Source event anywhere in the world
> http://moblin-contest.org/redirect.php?banner_id=100&url=/ > _______________________________________________
> Spirit-devel mailing list
> [hidden email] > https://lists.sourceforge.net/lists/listinfo/spirit-devel >