Now would be a good time to describe the parse context in some detail. Any semantic action that you write will need to use state in the parse context, so you need to know what's available.
The parse context is an object that stores the current state of the parse
— the current- and end-iterators, the error handler, etc. Data may
seem to be "added" to or "removed" from it at different
times during the parse. For instance, when a parser p
with a semantic action a
succeeds, the context adds the attribute that p
produces to the parse context, then calls a
,
passing it the context.
Though the context object appears to have things added to or removed from it, it does not. In reality, there is no one context object. Contexts are formed at various times during the parse, usually when starting a subparser. Each context is formed by taking the previous context and adding or changing members as needed to form a new context object. When the function containing the new context object returns, its context object (if any) is destructed. This is efficient to do, because the parse context has only about a dozen data members, and each data member is less than or equal to the size of a pointer. Copying the entire context when mutating the context is therefore fast. The context does no memory allocation.
Tip | |
---|---|
All these functions that take the parse context as their first parameter
will find by found by Argument-Dependent Lookup. You will probably never
need to qualify them with |
By convention, the names of all Boost.Parser functions that take a parse context, and are therefore intended for use inside semantic actions, contain a leading underscore.
_pass()
returns a reference to a
bool
indicating the success
of failure of the current parse. This can be used to force the current parse
to pass or fail:
[](auto & ctx) { // If the attribute fails to meet this predicate, fail the parse. if (!necessary_condition(_attr(ctx))) _pass(ctx) = false; }
Note that for a semantic action to be executed, its associated parser must
already have succeeded. So unless you previously wrote _pass(ctx)
= false
within your action, _pass(ctx)
= true
does nothing; it's redundant.
_begin()
and _end()
return the beginning and end of the range that you passed to parse()
, respectively. _where()
returns a subrange
indicating the bounds
of the input matched by the current parse. _where()
can be useful if you just want to parse some text and return a result consisting
of where certain elements are located, without producing any other attributes.
_where()
can also be essential in
tracking where things are located, to provide good diagnostics at a later
point in the parse. Think mismatched tags in XML; if you parse a close-tag
at the end of an element, and it does not match the open-tag, you want to
produce an error message that mentions or shows both tags. Stashing _where(ctx).begin()
somewhere that is available to the close-tag parser will enable that. See
Error
Handling and Debugging for an example of this.
_error_handler()
returns a reference to the
error handler associated with the parser passed to parse()
.
Using _error_handler()
, you can generate errors
and warnings from within your semantic actions. See Error
Handling and Debugging for concrete examples.
_attr()
returns a reference to the
value of the current parser's attribute. It is available only when the current
parser's parse is successful. If the parser has no semantic action, no attribute
gets added to the parse context. It can be used to read and write the current
parser's attribute:
[](auto & ctx) { _attr(ctx) = 3; }
If the current parser has no attribute, a none
is returned.
_val()
returns a reference to the
value of the attribute of the current rule being used to parse (if any),
and is available even before the rule's parse is successful. It can be used
to set the current rule's attribute, even from a parser that is a subparser
inside the rule. Let's say we're writing a parser with a semantic action
that is within a rule. If we want to set the current rule's value to some
function of subparser's attribute, we would write this semantic action:
[](auto & ctx) { _val(ctx) = some_function(_attr(ctx)); }
If there is no current rule, or the current rule has no attribute, a none
is returned.
You need to use _val()
in cases where the default
attribute for a rule
's
parser is not directly compatible with the attribute type of the rule
.
In these cases, you'll need to write some code like the example above to
compute the rule
's
attribute from the rule
's
parser's generated attribute. For more info on rules
, see the next page, and
More About Rules.
_globals()
returns a reference to a
user-supplied object that contains whatever data you want to use during the
parse. The "globals" for a parse is an object — typically
a struct — that you give to the top-level parser. Then you can use
_globals()
to access it at any time
during the parse. We'll see how globals get associated with the top-level
parser in The parse()
API later. As an example, say that you have an early part of the parse
that needs to record some black-listed values, and that later parts of the
parse might need to parse values, failing the parse if they see the black-listed
values. In the early part of the parse, you could write something like this.
[](auto & ctx) { // black_list is a std::unordered_set. _globals(ctx).black_list.insert(_attr(ctx)); }
Later in the parse, you could then use black_list
to check values as they are parsed.
[](auto & ctx) { if (_globals(ctx).black_list.contains(_attr(ctx))) _pass(ctx) = false; }
_locals()
returns a reference to one
or more values that are local to the current rule being parsed, if any. If
there are two or more local values, _locals()
returns a reference to a boost::parser::tuple
. Rules with locals are
something we haven't gotten to yet (see More
About Rules), but for now all you need to know is that you can provide
a template parameter (LocalState
)
to rule
,
and the rule will default construct an object of that type for use within
the rule. You access it via _locals()
:
[](auto & ctx) { auto & local = _locals(ctx); // Use local here. If 'local' is a hana::tuple, access its members like this: using namespace hana::literals; auto & first_element = local[0_c]; auto & second_element = local[1_c]; }
If there is no current rule, or the current rule has no locals, a none
is returned.
_params()
, like _locals()
,
applies to the current rule being used to parse, if any (see More
About Rules). It also returns a reference to a single value, if the
current rule has only one parameter, or a boost::parser::tuple
of multiple values if
the current rule has multiple parameters. If there is no current rule, or
the current rule has no parameters, a none
is returned.
Unlike with _locals()
, you do
not provide a template parameter to rule
. Instead you call the
rule
's
with()
member function (again, see More
About Rules).
Note | |
---|---|
|
_no_case()
_no_case()
returns true
if the current parse context is inside one or more (possibly nested) no_case[]
directives. I don't have a
use case for this, but if I didn't expose it, it would be the only thing
in the context that you could not examine from inside a semantic action.
It was easy to add, so I did.