Control Hooks
July 4, 2016 ยท View on GitHub
Beyond the top-level grammar rule, which has to be supplied to a parsing run, and an action class template, which can be supplied to a parsing run, a third customisation point within the PEGTL allows the user to provide a control class (template) to a parsing run.
The control class must implement a set of static hook functions that are called by the PEGTL in order to
-
customise which exceptions are thrown in case of errors, and
-
add custom debug logging to follow the details of a parsing run.
Contents
- Normal Control
- Debug Functions
- Exception Throwing
- Debugging and Tracing
- Advanced Control
- Changing Control
Normal Control
The normal control class template included with the PEGTL is used by default and shows which hook functions there are.
template< typename Rule >
struct normal
{
template< typename Input, typename ... States >
static void start( const Input &, States && ... )
{ }
template< typename Input, typename ... States >
static void success( const Input &, States && ... )
{ }
template< typename Input, typename ... States >
static void failure( const Input &, States && ... )
{ }
template< typename Input, typename ... States >
static void raise( const Input & in, States && ... )
{
throw parse_error( "parse error matching " +
internal::demangle< Rule >(), in );
}
template< apply_mode A,
template< typename ... > class Action,
template< typename ... > class Control,
typename Input,
typename ... States >
static bool match( Input & in, States && ... st )
{
return internal::rule_match_one< Rule,
A,
Action,
Control >( in, st ... );
}
};
All control functions are called with the current input and all additional state arguments.
Debug Functions
Before attempting to match a rule R, the PEGTL calls C< R >::start() where C is the current control class template.
Depending on what happens during the attempt to match R, one of the other three functions might be called.
-
If
Rsucceeds, thenC< R >::success()is called; compared to the call toC< R >::start(), the input will have consumed whatever the successful match ofRconsumed. -
If
Rfinishes with a failure, i.e. a return value offalsefrom itsmatch()-function, thenC< R >::failure()is called; a failed match is not allowed to consume input. -
If
Ris wrapped inmust< R >, a global failure is generated by callingC< R >::raise()to throw some exception as is expected by the PEGTL in this case. -
If a sub-rule of
Rfinishes with a global failure, and the exception is not caught by atry_catchor similar combinator, then no other function ofC< R >is called afterC< R >::start().
Exception Throwing
The raise()-control-hook-function must throw an exception.
For most parts of the PEGTL the exception class is irrelevant and any user-defined data type can be thrown by a user-defined control hook.
The try_catch rule only catches exceptions of type pegtl::parse_error!
When custom exception types are used then try_catch_type must be used with the custom exception class that they are supposed to catch as first template argument.
Debugging and Tracing
For debugging a grammar and tracing exactly what happens during a parsing run, the control class methods start(), success() and failure() can be used.
A minimal example of how this can be done is included with the PEGTL in the pegtl/trace.hh header file.
template< typename Rule >
struct tracer
: normal< Rule >
{
template< typename Input, typename ... States >
static void start( const Input & in, States && ... )
{
std::cerr << position_info( in )
<< " start "
<< internal::demangle< Rule >()
<< std::endl;
}
template< typename Input, typename ... States >
static void success( const Input & in, States && ... )
{
std::cerr << position_info( in )
<< " success "
<< internal::demangle< Rule >()
<< std::endl;
}
template< typename Input, typename ... States >
static void failure( const Input & in, States && ... st )
{
std::cerr << position_info( in )
<< " failure "
<< internal::demangle< Rule >()
<< std::endl;
}
};
Advanced Control
The control template's match()-function is the first or outside-most function that is called in the flow that eventually leads to calling a rule's match()-function.
The normal implementation simply forwards all template parameters and function arguments unchanged.
For advanced use cases it is however possible to create a custom control class with a custom match()-function that can change "everything" before forwarding as needed.
Changing Control
Just like the action class template, a custom control class template can be used by either
-
supplying it as explicit template argument to the
parse()-functions, or -
setting it for a portion of the grammar with the
pegtl::controlcombinator.
Copyright (c) 2014-2016 Dr. Colin Hirsch and Daniel Frey