2
0
mirror of https://github.com/boostorg/spirit.git synced 2026-01-19 04:42:11 +00:00

error handling docs

This commit is contained in:
djowel
2018-03-08 11:07:48 +08:00
parent 130e6395d7
commit 54d0167bc8
6 changed files with 546 additions and 182 deletions

View File

@@ -199,6 +199,8 @@ pages and pages of reference documentation.
[[__x3_raw__`[a]`] [__boost_iterator_range__`<I>`] [Presents the transduction of `a` as an iterator range]]
[[__x3_expectd__`[a]`] [`A`] [Throw an exception if parsing `a` fails]]
[[[x3_repeat `repeat[a]`]] [`vector<A>`] [Repeat `a` zero or more times]]
[[[x3_repeat `repeat(N)[a]`]] [`vector<A>`] [Repeat `a` `N` times]]
[[[x3_repeat `repeat(N, M)[a]`]] [`vector<A>`] [Repeat `a` `N` to `M` times]]

View File

@@ -6,11 +6,11 @@
file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
===============================================================================/]
[article Spirit
[article Spirit X3
[quickbook 1.5]
[version 3.0.1]
[authors [de Guzman, Joel], [Kaiser, Hartmut]]
[copyright 2001 2002 2003 2004 2005 2006 2007 2008 2009 2010 2011 2012 2013 2014 2015 Joel de Guzman, Hartmut Kaiser]
[copyright 2001-2018 Joel de Guzman, Hartmut Kaiser]
[/ purpose Parser Library]
[license
Distributed under the Boost Software License, Version 1.0.
@@ -21,18 +21,14 @@
[/ May 12, 2015 ]
[/==============================================================================
[/=============================================================================
Some links and defines
===============================================================================/]
=============================================================================/]
[def __version__ V3.0.1]
[/ external]
[def __boost__ [@http://www.boost.org/ Boost]]
[def __spirit__ [@http://boost-spirit.com Spirit]]
[def __spirit_list__ [@https://lists.sourceforge.net/lists/listinfo/spirit-general Spirit Mailing List]]
[def __spirit_general__ [@news://news.gmane.org/gmane.comp.spirit.general Spirit General NNTP news portal]]
[def __gmane__ [@http://www.gmane.org Gmane]]
[def __mlist_archive__ [@http://news.gmane.org/gmane.comp.parsers.spirit.general]]
[def __boost_tools__ [@http://www.boost.org/tools/index.html Boost Tools]]
@@ -53,7 +49,7 @@
[def __caution__ [$./images/caution.png]]
[def __danger__ [$./images/alert.png]]
[def __x3__ /Spirit.X3/]
[def __x3__ /spirit_x3/]
[def __sd_start_stop__ [$./images/start_stop.png]]
[def __sd_terminals__ [$./images/terminal.png]]
@@ -68,115 +64,138 @@
[def __sd_not_predicate__ [$./images/not_predicate.png]]
[def __pascal_string__ [$./images/pascal_string.png]]
[def __rd__ Recursive Descent] [/$$$ TODO: link me $$$]
[def __syntax_diagrams__ [link spirit.abstracts.syntax_diagrams Syntax Diagram]]
[def __peg__ [link spirit.abstracts.parsing_expression_grammar Parsing Expression Grammar]]
[def __ast__ Abstract Syntax Tree] [/$$$ TODO: link me $$$]
[def __sec_x3_reference__ [/ link spirit.x3.reference] X3 Reference]
[def __sec_x3_primitive__ [/ link spirit.x3.quick_reference.x3_parsers] X3 Parsers]
[def __sec_x3_compound__ [/ link spirit.x3.quick_reference.compound_attribute_rules] Parser Compound Attribute Rules]
[def __sec_x3_reference__ [/ link spirit_x3.reference] X3 Reference]
[def __sec_x3_primitive__ [/ link spirit_x3.quick_reference.x3_parsers] X3 Parsers]
[def __sec_x3_compound__ [/ link spirit_x3.quick_reference.compound_attribute_rules] Parser Compound Attribute Rules]
[/ support]
[def __info__ `info`] [/ TODO Link Me]
[def __x3_error_handler__ Error Handler] [/ TODO Link Me]
[def __unused_type__ `unused_type`] [/$$$ TODO: link me $$$]
[def __unused__ `unused`] [/$$$ TODO: link me $$$]
[def __char_class_types__ Character Class Types] [/$$$ TODO: link me $$$]
[/ quick-ref]
[def __x3_quickref__ [link spirit.x3.quick_reference X3 Quick Reference]]
[def __x3_comp_attr_notation__ [link spirit.x3.quick_reference.compound_attribute_rules.notation Compound Attribute Notation]]
[def __x3_quickref__ [link spirit_x3.quick_reference X3 Quick Reference]]
[def __x3_comp_attr_notation__ [link spirit_x3.quick_reference.compound_attribute_rules.notation Compound Attribute Notation]]
[/ concepts]
[def __parser_concept__ [/ link spirit.x3.reference.parser_concepts.parser] `Parser`]
[def __primitive_parser_concept__ [/ link spirit.x3.reference.parser_concepts.primitiveparser] `PrimitiveParser`]
[def __unary_parser_concept__ [/ link spirit.x3.reference.parser_concepts.unaryparser] `UnaryParser`]
[def __binary_parser_concept__ [/ link spirit.x3.reference.parser_concepts.binaryparser] `BinaryParser`]
[def __nary_parser_concept__ [/ link spirit.x3.reference.parser_concepts.naryparser] `NaryParser`]
[def __x3_nonterminal__ [/ link spirit.x3.reference.parser_concepts.nonterminal] `Nonterminal`]
[def __x3_nonterminal_attribute__ [/ link spirit.x3.reference.parser_concepts.nonterminal.attributes] `Attribute`]
[def __parser_concept__ [/ link spirit_x3.reference.parser_concepts.parser] `Parser`]
[def __primitive_parser_concept__ [/ link spirit_x3.reference.parser_concepts.primitiveparser] `PrimitiveParser`]
[def __unary_parser_concept__ [/ link spirit_x3.reference.parser_concepts.unaryparser] `UnaryParser`]
[def __binary_parser_concept__ [/ link spirit_x3.reference.parser_concepts.binaryparser] `BinaryParser`]
[def __nary_parser_concept__ [/ link spirit_x3.reference.parser_concepts.naryparser] `NaryParser`]
[def __x3_nonterminal__ [/ link spirit_x3.reference.parser_concepts.nonterminal] `Nonterminal`]
[def __x3_nonterminal_attribute__ [/ link spirit_x3.reference.parser_concepts.nonterminal.attributes] `Attribute`]
[/ basics]
[def __x3_lazy_argument__ [/ link spirit.x3.reference.basics.lazy_argument] Lazy Argument]
[def __x3_lazy_arguments__ [/ link spirit.x3.reference.basics.lazy_argument] Lazy Arguments]
[def __char_encoding_namespace__ [/ link spirit.x3.reference.basics.character_encoding_namespace] Character Encoding Namespace]
[def __x3_basics_examples__ [/ link spirit.x3.reference.basics.examples] Basics Examples]
[def __x3_lazy_argument__ [/ link spirit_x3.reference.basics.lazy_argument] Lazy Argument]
[def __x3_lazy_arguments__ [/ link spirit_x3.reference.basics.lazy_argument] Lazy Arguments]
[def __char_encoding_namespace__ [/ link spirit_x3.reference.basics.character_encoding_namespace] Character Encoding Namespace]
[def __x3_basics_examples__ [/ link spirit_x3.reference.basics.examples] Basics Examples]
[/ string]
[template x3_lit_string[str] [[/ link spirit.x3.reference.string.string] str]]
[def __string__ [/ link spirit.x3.reference.basics.string] String]
[def __x3_symbols__ [/ link spirit.x3.reference.string.symbols] `symbols<T>`]
[template x3_lit_string[str] [[/ link spirit_x3.reference.string.string] str]]
[def __string__ [/ link spirit_x3.reference.basics.string] String]
[def __x3_symbols__ [/ link spirit_x3.reference.string.symbols] `symbols<T>`]
[/ action]
[def __x3_semantic_actions__ Semantic Actions] [/ TODO Link Me]
[/ char]
[template x3_char[str] [/ link spirit.x3.reference.char.char] [str]]
[template x3_char_class[str] [/ link spirit.x3.reference.char.char_class] [str]]
[template x3_lit_char[char] [/ link spirit.x3.reference.char.char] [char]]
[template x3_char[str] [/ link spirit_x3.reference.char.char] [str]]
[template x3_char_class[str] [/ link spirit_x3.reference.char.char_class] [str]]
[template x3_lit_char[char] [/ link spirit_x3.reference.char.char] [char]]
[/ numerics]
[template x3_signed_int[str] [/ link spirit.x3.reference.numeric.int] [str]]
[template x3_unsigned_int[str] [/ link spirit.x3.reference.numeric.uint] [str]]
[template x3_real_number[str] [/ link spirit.x3.reference.numeric.real] [str]]
[template x3_boolean[str] [/ link spirit.x3.reference.numeric.boolean] [str]]
[template x3_signed_int[str] [/ link spirit_x3.reference.numeric.int] [str]]
[template x3_unsigned_int[str] [/ link spirit_x3.reference.numeric.uint] [str]]
[template x3_real_number[str] [/ link spirit_x3.reference.numeric.real] [str]]
[template x3_boolean[str] [/ link spirit_x3.reference.numeric.boolean] [str]]
[/ binary]
[template x3_native_binary[str] [/ link spirit.x3.reference.binary.binary_native] [str]]
[template x3_little_binary[str] [/ link spirit.x3.reference.binary.binary_little] [str]]
[template x3_big_binary[str] [/ link spirit.x3.reference.binary.binary_big] [str]]
[template x3_native_binary[str] [/ link spirit_x3.reference.binary.binary_native] [str]]
[template x3_little_binary[str] [/ link spirit_x3.reference.binary.binary_little] [str]]
[template x3_big_binary[str] [/ link spirit_x3.reference.binary.binary_big] [str]]
[/ auxiliary]
[def __x3_attr__ [/ link spirit.x3.reference.auxiliary.attr] `attr(attrib)`]
[def __x3_eol__ [/ link spirit.x3.reference.auxiliary.eol] `eol`]
[def __x3_eoi__ [/ link spirit.x3.reference.auxiliary.eoi] `eoi`]
[def __x3_eps__ [/ link spirit.x3.reference.auxiliary.eps] `eps`]
[def __x3_lazy__ [/ link spirit.x3.reference.auxiliary.lazy] `lazy`]
[def __x3_attr__ [/ link spirit_x3.reference.auxiliary.attr] `attr(attrib)`]
[def __x3_eol__ [/ link spirit_x3.reference.auxiliary.eol] `eol`]
[def __x3_eoi__ [/ link spirit_x3.reference.auxiliary.eoi] `eoi`]
[def __x3_eps__ [/ link spirit_x3.reference.auxiliary.eps] `eps`]
[def __x3_lazy__ [/ link spirit_x3.reference.auxiliary.lazy] `lazy`]
[/ directives]
[def __x3_lexeme__ [/ link spirit.x3.reference.directive.lexeme] `lexeme`]
[def __x3_no_case__ [/ link spirit.x3.reference.directive.no_case] `no_case`]
[def __x3_omit__ [/ link spirit.x3.reference.directive.omit] `omit`]
[def __x3_matches__ [/ link spirit.x3.reference.directive.matches] `matches`]
[def __x3_raw__ [/ link spirit.x3.reference.directive.raw] `raw`]
[def __x3_repeat__ [/ link spirit.x3.reference.directive.repeat] `repeat`]
[template x3_repeat[str] [[/ link spirit.x3.reference.directive.repeat] str]]
[def __x3_skip__ [/ link spirit.x3.reference.directive.skip] `skip`]
[template x3_no_skip[str] [[/ link spirit.x3.reference.directive.no_skip] str]]
[def __x3_hold__ [/ link spirit.x3.reference.directive.hold] `hold`]
[def __x3_lexeme__ [/ link spirit_x3.reference.directive.lexeme] `lexeme`]
[def __x3_no_case__ [/ link spirit_x3.reference.directive.no_case] `no_case`]
[def __x3_omit__ [/ link spirit_x3.reference.directive.omit] `omit`]
[def __x3_matches__ [/ link spirit_x3.reference.directive.matches] `matches`]
[def __x3_raw__ [/ link spirit_x3.reference.directive.raw] `raw`]
[def __x3_expectd__ [/ link spirit_x3.reference.directive.expect] `expect`]
[def __x3_repeat__ [/ link spirit_x3.reference.directive.repeat] `repeat`]
[template x3_repeat[str] [[/ link spirit_x3.reference.directive.repeat] str]]
[def __x3_skip__ [/ link spirit_x3.reference.directive.skip] `skip`]
[template x3_no_skip[str] [[/ link spirit_x3.reference.directive.no_skip] str]]
[def __x3_hold__ [/ link spirit_x3.reference.directive.hold] `hold`]
[/ operator]
[def __x3_alternative__ [/ link spirit.x3.reference.operator.alternative] `a | b`]
[def __x3_and_predicate__ [/ link spirit.x3.reference.operator.and_predicate] `&a`]
[def __x3_difference__ [/ link spirit.x3.reference.operator.difference] `a - b`]
[def __x3_expect__ [/ link spirit.x3.reference.operator.expect] `a > b`]
[def __x3_expectation_failure__ [/ link spirit.x3.reference.operator.expect.expectation_failure] `expectation_failure`]
[def __x3_kleene__ [/ link spirit.x3.reference.operator.kleene] `*a`]
[def __x3_list__ [/ link spirit.x3.reference.operator.list] `a % b`]
[def __x3_not_predicate__ [/ link spirit.x3.reference.operator.not_predicate] `!a`]
[def __x3_optional__ [/ link spirit.x3.reference.operator.optional] `-a`]
[def __x3_plus__ [/ link spirit.x3.reference.operator.plus] `+a`]
[def __x3_sequence__ [/ link spirit.x3.reference.operator.sequence] `a >> b`]
[def __x3_alternative__ [/ link spirit_x3.reference.operator.alternative] `a | b`]
[def __x3_and_predicate__ [/ link spirit_x3.reference.operator.and_predicate] `&a`]
[def __x3_difference__ [/ link spirit_x3.reference.operator.difference] `a - b`]
[def __x3_expect__ [/ link spirit_x3.reference.operator.expect] `a > b`]
[def __x3_expectation_failure__ [/ link spirit_x3.reference.operator.expect.expectation_failure] `expectation_failure`]
[def __x3_kleene__ [/ link spirit_x3.reference.operator.kleene] `*a`]
[def __x3_list__ [/ link spirit_x3.reference.operator.list] `a % b`]
[def __x3_not_predicate__ [/ link spirit_x3.reference.operator.not_predicate] `!a`]
[def __x3_optional__ [/ link spirit_x3.reference.operator.optional] `-a`]
[def __x3_plus__ [/ link spirit_x3.reference.operator.plus] `+a`]
[def __x3_sequence__ [/ link spirit_x3.reference.operator.sequence] `a >> b`]
[def __x3_stream__ [/ link spirit.x3.reference.stream.stream] `stream`]
[def __x3_stream__ [/ link spirit_x3.reference.stream.stream] `stream`]
[/ nonterminal]
[def __x3_rule__ [/ link spirit.x3.reference.nonterminal.rule] rule]
[def __x3_rules__ [/ link spirit.x3.reference.nonterminal.rule] rules]
[def __x3_grammar__ [/ link spirit.x3.reference.nonterminal.grammar] grammar]
[def __x3_grammars__ [/ link spirit.x3.reference.nonterminal.grammar] grammars]
[def __x3_rule__ [/ link spirit_x3.reference.nonterminal.rule] rule]
[def __x3_rules__ [/ link spirit_x3.reference.nonterminal.rule] rules]
[def __x3_grammar__ [/ link spirit_x3.reference.nonterminal.grammar] grammar]
[def __x3_grammars__ [/ link spirit_x3.reference.nonterminal.grammar] grammars]
[/ stream]
[template x3_match[str] [/ link spirit.x3.reference.parse_api.stream_api] str]
[template x3_auto[str] [/ link spirit.x3.reference.auto] str]
[def __create_parser__ [/ link spirit.x3.reference.parse_api.create_parser] `create_parser`]
[template x3_match[str] [/ link spirit_x3.reference.parse_api.stream_api] str]
[template x3_auto[str] [/ link spirit_x3.reference.auto] str]
[def __create_parser__ [/ link spirit_x3.reference.parse_api.create_parser] `create_parser`]
[def __parse_api__ [/ link spirit.x3.reference.parse_api] The Parse API]
[def __parse_api__ [/ link spirit_x3.reference.parse_api] The Parse API]
[/==============================================================================
[/=============================================================================
Better Links (March 7, 2018)
=============================================================================/]
[/ external --------------------]
[def __boost__ [@http://www.boost.org/ Boost]]
[def __spirit__ [@http://boost-spirit.com Spirit]]
[def __spirit_list__ [@https://lists.sourceforge.net/lists/listinfo/spirit-general Spirit Mailing List]]
[def __spirit_general__ [@news://news.gmane.org/gmane.comp.spirit.general Spirit General NNTP news portal]]
[def __clang__ [@https://clang.llvm.org/ Clang]]
[def __rd__ [@https://en.wikipedia.org/wiki/Recursive_descent_parser Recursive Descent]]
[/ Sections --------------------]
[/ Tutorials -------------------]
[template tutorial_employee[str] [link spirit_x3.tutorials.employee [str]]]
[template tutorial_annotation[str] [link spirit_x3.tutorials.annotation [str]]]
[/ support ---------------------]
[def __x3_error_handler__ [/ link fix.me] error_handler]
[/=============================================================================
Documentation Start
===============================================================================/]
=============================================================================/]
This is the documentation of the newest version of __spirit__ (currently,
__version__).
@@ -199,6 +218,7 @@ __version__).
[include tutorial/employee.qbk]
[include tutorial/annotation.qbk]
[include tutorial/rexpr.qbk]
[include tutorial/error_handling.qbk]
[endsect]
[section Quick Reference]

View File

@@ -1,6 +1,5 @@
[/==============================================================================
Copyright (C) 2001-2015 Joel de Guzman
Copyright (C) 2001-2011 Hartmut Kaiser
Copyright (C) 2001-2018 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)
@@ -9,34 +8,39 @@
for sponsoring this work and donating it to the community.
===============================================================================/]
[section Annotations - Decorating the ASTs]
[section:annotation Annotations - Decorating the ASTs]
Stop and think about it... We're actually generating ASTs (abstract
syntax trees) in our previoius examples. We parsed a single structure and
generated an in-memory representation of it in the form of a struct: the
struct employee. If we changed the implementation to parse one or more
employees, the result would be a std::vector<employee>. We can go on and
add more hierarchy: teams, departments, corporations. Then we'll have an
AST representation of it all.
As a prerequisite in understanding this tutorial, please review the previous
[tutorial_employee employee example]. This example builds on top of that
example.
This example shows how to annotate the AST with the iterator positions
for access to the source code when post processing using a client supplied
`on_success` handler. The example will show how to get the position in
input source stream that corresponds to a given element in the AST.
Stop and think about it... We're actually generating ASTs (abstract syntax
trees) in our previoius examples. We parsed a single structure and generated
an in-memory representation of it in the form of a struct: the struct
employee. If we changed the implementation to parse one or more employees,
the result would be a std::vector<employee>. We can go on and add more
hierarchy: teams, departments, corporations, etc. We can have an AST
representation of it all.
In addition, This example also shows how to "inject" client data, using
the "with" directive, that the `on_success` handler can access as it is
called within the parse traversal through the parser's context.
This example shows how to annotate the AST with the iterator positions for
access to the source code when post processing using a client supplied
`on_success` handler. The example will show how to get the position in input
source stream that corresponds to a given element in the AST.
The full cpp file for this example can be found here: [@../../../example/x3/annotation.cpp]
In addition, This example also shows how to "inject" client data, using the
"with" directive, that the `on_success` handler can access as it is called
within the parse traversal through the parser's context.
The full cpp file for this example can be found here:
[@../../../example/x3/annotation.cpp]
[heading The AST]
First, we'll update our previous employee struct, this time separating
the person into its own struct. So now, we have two structs, the `person`
and the `employee`. Take note too that we now inherit `person` and `employee`
from `x3::position_tagged` which provides positional information that we
can use to tell the AST's position in the input stream anytime.
First, we'll update our previous employee struct, this time separating the
person into its own struct. So now, we have two structs, the `person` and the
`employee`. Take note too that we now inherit `person` and `employee` from
`x3::position_tagged` which provides positional information that we can use
to tell the AST's position in the input stream anytime.
namespace client { namespace ast
{
@@ -61,8 +65,8 @@ can use to tell the AST's position in the input stream anytime.
};
}}
Like before, we need to tell __fusion__ about our structs to make them first-class
fusion citizens that the grammar can utilize:
Like before, we need to tell __fusion__ about our structs to make them
first-class fusion citizens that the grammar can utilize:
BOOST_FUSION_ADAPT_STRUCT(client::ast::person,
first_name, last_name
@@ -74,40 +78,41 @@ fusion citizens that the grammar can utilize:
[heading x3::position_cache]
Before we proceed, let me introduce a helper class called the `position_cache`.
It is a simple class that collects iterator ranges that point to where each
element in the AST are located in the input stream. Given an AST, you can
ask the position_cache about its position. For example:
Before we proceed, let me introduce a helper class called the
`position_cache`. It is a simple class that collects iterator ranges that
point to where each element in the AST are located in the input stream. Given
an AST, you can query the position_cache about AST's position. For example:
auto pos = positions.position_of(my_ast);
Where `my_ast` is the AST, `positions` and is the `position_cache`, `position_of`
returns an iterator range that points to the start and end (`pos.begin()`
and `pos.end()`) positions where the AST was parsed from. `positions.begin()`
and `positions.end()` points to the start and end of the entire input stream.
Where `my_ast` is the AST, `positions` and is the `position_cache`,
`position_of` returns an iterator range that points to the start and end
(`pos.begin()` and `pos.end()`) positions where the AST was parsed from.
`positions.begin()` and `positions.end()` points to the start and end of the
entire input stream.
[heading on_success]
The `on_success` gives you everything you want from semantic actions without
the visual clutter. Declarative code can and should be free from imperative
code. `on_success` as a concept and mechanism is an important departure
from how things are done in Spirit's previous version: Qi.
code. `on_success` as a concept and mechanism is an important departure from
how things are done in Spirit's previous version: Qi.
As demonstrated in the previous employee example, the preferred way to
extract data from an input source is by having the parser collect the data
for us into C++ structs as it traverses the input stream. Ideally, Spirit
X3 grammars are fully attributed and declared in such a way that you do
not have to add any imperative code and there should be no need for semantic
actions at all. The parser simply works as declared and you get your data
back as a result.
As demonstrated in the previous [tutorial_employee employee example], the
preferred way to extract data from an input source is by having the parser
collect the data for us into C++ structs as it traverses the input stream.
Ideally, Spirit X3 grammars are fully attributed and declared in such a way
that you do not have to add any imperative code and there should be no need
for semantic actions at all. The parser simply works as declared and you get
your data back as a result.
However, there are certain cases where there's no way to avoid introducing
imperative code. Yet, if we want to keep our code clean and free from semantic
actions that messes up our clean declarative grammars, `on_success` handlers
are alternative means to provide hooks to client code that is executed by the
parser upon successful parse without polluting the grammar. Like semantic
actions, `on_success` handlers also have access to the AST, the iterators,
and context. But, unlike semantic actions, `on_success` handlers are cleanly
imperative code. But semantic actions messes up our clean declarative
grammars. If we care to keep our code clean, `on_success` handlers are
alternative callback hooks to client code that are executed by the parser
after a successful parse without polluting the grammar. Like semantic
actions, `on_success` handlers have access to the AST, the iterators, and
context. But, unlike semantic actions, `on_success` handlers are cleanly
separated from the actual grammar.
[heading Annotation Handler]
@@ -133,18 +138,19 @@ our `on_success` handler:
actual `position_cache`, client data that we will inject at very start, when
we call parse. More on that later.
Our `on_success` handler gets a reference to the actual `position_cache`
and calls its `annotate` member function, passing in the AST and the iterators.
`position_cache.annotate(ast, first, last)` annotates the AST with information
required by `x3::position_tagged`.
Our `on_success` handler gets a reference to the actual `position_cache` and
calls its `annotate` member function, passing in the AST and the iterators.
`position_cache.annotate(ast, first, last)` annotates the AST with
information required by `x3::position_tagged`.
[heading The Parser]
Now we'll write a parser for our employee. Like before, inputs will
be of the form:
Now we'll write a parser for our employee. To simplify, inputs will be of the
form:
employee{ age, "forename", "surname", salary }
{ age, "forename", "surname", salary }
[#__tutorial_annotated_employee_parser__]
Here we go:
namespace parser
@@ -178,9 +184,6 @@ Here we go:
BOOST_SPIRIT_DEFINE(quoted_string, person, employee);
}
Take a step back and look at the previous Employee example. We are incrementally
building on top of that.
[heading Rule Declarations]
struct quoted_string_class;
@@ -191,6 +194,7 @@ building on top of that.
x3::rule<person_class, ast::person> const person = "person";
x3::rule<employee_class, ast::employee> const employee = "employee";
Go back and review the original [link __tutorial_employee_parser__ employee parser].
What has changed?
* We split the single employee rule into three smaller rules: `quoted_string`,
@@ -200,35 +204,34 @@ What has changed?
[heading Rule Classes]
In this example, the rule classes, `quoted_string_class`, `person_class`, and
`employee_class` provide statically known IDs for the rules required by X3 to
perform its tasks. In addition from that, the rule class can also be extended
to have some user-defined customization hooks that are called:
Like before, in this example, the rule classes, `quoted_string_class`,
`person_class`, and `employee_class` provide statically known IDs for the
rules required by X3 to perform its tasks. In addition to that, the rule
class can also be extended to have some user-defined customization hooks that
are called:
* On success: After a rule sucessfully parses an input.
* On Error: After a rule fails to parse.
By subclassing the rule class from a client supplied handler such as
our our `annotate_position` handler above:
By subclassing the rule class from a client supplied handler such as our
`annotate_position` handler above:
struct person_class : annotate_position {};
struct employee_class : annotate_position {};
The code above tells X3 to check the rule class if it has an `on_success`
or `on_error` member functions and calls appropriately calls them on
such events.
The code above tells X3 to check the rule class if it has an `on_success` or
`on_error` member functions and appropriately calls them on such events.
[#__tutorial_with_directive__]
[heading The with Directive]
With any parser `'p`, one can inject any data into that the that semantic
actions and handlers can access later on when they are called. The general
syntax is:
For any parser `p`, one can inject supplementary data that semantic actions
and handlers can access later on when they are called. The general syntax is:
with<tag>(data)[p]
For our particular example, we use to inject the `position_cache` into
the parse for our `annotate_position` on_success handler to have access
to:
For our particular example, we use to inject the `position_cache` into the
parse for our `annotate_position` on_success handler to have access to:
auto const parser =
// we pass our position_cache to the parser so we can access
@@ -239,16 +242,16 @@ to:
];
Typically this is done just before calling `x3::parse` or `x3::phrase_parse`.
`with` is a very lightwight operation. It is possible to inject as much
data as you want, even multiple `with` directives:
`with` is a very lightwight operation. It is possible to inject as much data
as you want, even multiple `with` directives:
with<tag1>(data1)
[
with<tag2>(data2)[p]
]
Multiple `with` directives can (perhaps not obviously) be injected from the
outside caller function. Here's an outline:
Multiple `with` directives can (perhaps not obviously) be injected from
outside the called function. Here's an outline:
template <typename Parser>
void bar(Parser const& p)
@@ -267,7 +270,7 @@ outside caller function. Here's an outline:
[heading Let's Parse]
Now we have the complete parse mechanism:
Now we have the complete parse mechanism with support for annotations:
using iterator_type = std::string::const_iterator;
using position_cache = boost::spirit::x3::position_cache<std::vector<iterator_type>>;
@@ -305,15 +308,15 @@ Now we have the complete parse mechanism:
Let's walk through the code.
First, we have some typedefs for 1) The iterator type we are using for the
parser, `iterator_type` and 2) For the `position_cache` type. The latter
is a template that accepts the type of container it will hold. In this case,
a `std::vector<iterator_type>`.
parser, `iterator_type` and 2) For the `position_cache` type. The latter is a
template that accepts the type of container it will hold. In this case, a
`std::vector<iterator_type>`.
The main parse function accepts an input, a std::string and a reference
to a position_cache, and retuns an AST: `std::vector<client::ast::employee>`.
The main parse function accepts an input, a std::string and a reference to a
position_cache, and retuns an AST: `std::vector<client::ast::employee>`.
Inside the parse function, we first create an AST where parsed data will
be stored:
Inside the parse function, we first create an AST where parsed data will be
stored:
std::vector<client::ast::employee> ast;
@@ -338,8 +341,8 @@ On successful parse, the AST, `ast`, will contain the actual parsed data.
[heading Getting The Source Positions]
Now that we have our main parse function, let's have an example sourcefile to
parse and show how we can obtain the position of an AST element, returned on
a successful parse.
parse and show how we can obtain the position of an AST element, returned
after a successful parse.
Given this input:
@@ -376,18 +379,18 @@ Given this input:
}
)";
We call our parse function after instantiating a `position_cache` object
that will hold the source stream positions:
We call our parse function after instantiating a `position_cache` object that
will hold the source stream positions:
position_cache positions{input.begin(), input.end()};
auto ast = parse(input, positions);
We now have an AST, `ast`, that contains the parsed results. Let us get
the source positions of the 2nd employee:
We now have an AST, `ast`, that contains the parsed results. Let us get the
source positions of the 2nd employee:
auto pos = positions.position_of(ast[1]); // zero based of course!
`pos` is an iterator range that contians iterators to the start and
end of `ast[1]` in the input stream.
`pos` is an iterator range that contians iterators to the start and end of
`ast[1]` in the input stream.
[endsect]

View File

@@ -6,7 +6,7 @@
file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
===============================================================================/]
[section Employee - Parsing into structs]
[section:employee Employee - Parsing into structs]
It's a common question in the __spirit_list__: How do I parse and place
the results into a C++ struct? Of course, at this point, you already
@@ -51,6 +51,7 @@ Now we'll write a parser for our employee. Inputs will be of the form:
employee{ age, "forename", "surname", salary }
[#__tutorial_employee_parser__]
Here goes:
namespace parser

View File

@@ -0,0 +1,344 @@
[/==============================================================================
Copyright (C) 2001-2018 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)
I would like to thank Rainbowverse, llc (https://primeorbial.com/)
for sponsoring this work and donating it to the community.
===============================================================================/]
[section Error Handling]
As a prerequisite in understanding this tutorial, please review the previous
[tutorial_employee employee] and [tutorial_annotation annotations] examples.
This example builds on top of these previous examples.
This tutorial wouldn't be complete without touching on error handling. for
this example, we will continue and expand on the [tutorial_employee employee
example].
The full cpp file for this example can be found here:
[@../../../example/x3/error_handling.cpp]
Please review the previous [tutorial_annotation annotations example]. The
information there will be very helpful in understanding error handling.
[heading The AST]
Our AST is exactly the same as what we had before in the [tutorial_annotation
annotations]:
namespace client { namespace ast
{
struct person : x3::position_tagged
{
person(
std::string const& first_name = ""
, std::string const& last_name = ""
)
: first_name(first_name)
, last_name(last_name)
{}
std::string first_name, last_name;
};
struct employee : x3::position_tagged
{
int age;
person who;
double salary;
};
}}
We have two structs, the `person` and the `employee`. Each inherits from
`x3::position_tagged` which provides positional information that we can use
to tell the AST's position in the input stream anytime. We will need these
information for error handling and reporting.
Like before, we need to tell __fusion__ about our structs to make them
first-class fusion citizens that the grammar can utilize:
BOOST_FUSION_ADAPT_STRUCT(client::ast::person,
first_name, last_name
)
BOOST_FUSION_ADAPT_STRUCT(client::ast::employee,
age, who, salary
)
[heading Expectations]
There are occasions in which it is expected that the input must match a
particular parser or the input is invalid. Such cases generally arise after
matching a portion of a grammar, such that the context is fully known. In
such a situation, failure to match should result in an exception. For
example, when parsing an e-mail address, a name, an "@" and a domain name
must be matched or the address is invalid.
Two X3 mechanisms facilitate parser expectations:
# The expectation operator (__x3_expect__)
# The expect directive (__x3_expectd__`[p]`)
The expectation operator (__x3_expect__) requires that the following parser
(`b`) match the input or an __x3_expectation_failure__ is emitted. Using a
client supplied `on_error` handler, the exception can be serviced by calling
the handler with the source iterators and context at which the parsing failed
can be reported.
By contrast, the sequence operator (__x3_sequence__) does not require that
the following parser match the input, which allows for backtracking or simply
returning false from the parse function with no exceptions.
The expect directive (__x3_expectd__`[p]`) requires that the argument parser
matches the input or an exception is emitted. Using on_error(), that
exception can be handled by calling a handler with the context at which the
parsing failed can be reported.
[heading on_error]
`on_error` is the counterpart of `on_success`, as discussed in the
[tutorial_annotation annotations example]. While `on_success` handlers are
callback hooks to client code that are executed by the parser after a
/successful/ parse, `on_error` handlers are callback hooks to client code
that are executed by the parser when an __x3_expectation_failure__ is thrown
via the expect operator or directive. `on_error` handlers have access to the
iterators, the context and the exception that was thrown.
[heading Error Handling]
Before we proceed, let me introduce a helper class, the
x3::__x3_error_handler__. It is utility class that provides __clang__ style
error reporting which gives you nice reports such as the following:
[pre
In line 16:
Error! Expecting: person here:
'I am not a person!' <--- this should be a person
____^_
]
We'll see later that this error message is exactly what this example emits.
Here's our `on_error` handler:
struct error_handler
{
template <typename Iterator, typename Exception, typename Context>
x3::error_handler_result on_error(
Iterator& first, Iterator const& last
, Exception const& x, Context const& context)
{
auto& error_handler = x3::get<x3::error_handler_tag>(context).get();
std::string message = "Error! Expecting: " + x.which() + " here:";
error_handler(x.where(), message);
return x3::error_handler_result::fail;
}
};
`x3::error_handler_tag` is a special tag we will use to get a reference to
the actual x3::__x3_error_handler__ that we will inject at very start, when
we call parse. We get the x3::__x3_error_handler__ here:
auto& error_handler = x3::get<error_handler_tag>(context).get();
The x3::__x3_error_handler__ handles all the nitty gritty details such as
determining the line number and actual column position, and formatting the
error message printed. All we have to do is provide the actual error string
which we extract from the __x3_expectation_failure__ exception:
std::string message = "Error! Expecting: " + x.which() + " here:";
Then, we return `x3::error_handler_result::fail` to tell X3 that we want to
fail the parse when such an event is caught. You can return one of:
[table
[[`Action`] [Description]]
[[fail] [Quit and fail. Return a no_match.]]
[[retry] [Attempt error recovery, possibly moving the iterator position.]]
[[accept] [Force success, moving the iterator position appropriately.]]
[[rethrow] [Rethrows the error.]]
]
[heading The Parser]
Now we'll rewrite employee parser with error handling in mind. Like the
[tutorial_annotation annotations] example, inputs will be of the form:
{ age, "forename", "surname", salary }
Here we go:
namespace parser
{
using x3::int_;
using x3::double_;
using x3::lexeme;
using ascii::char_;
struct quoted_string_class;
struct person_class;
struct employee_class;
x3::rule<quoted_string_class, std::string> const quoted_string = "quoted_string";
x3::rule<person_class, ast::person> const person = "person";
x3::rule<employee_class, ast::employee> const employee = "employee";
auto const quoted_string_def = lexeme['"' >> +(char_ - '"') >> '"'];
auto const person_def = quoted_string > ',' > quoted_string;
auto const employee_def =
'{'
> int_ > ','
> person > ','
> double_
> '}'
;
auto const employees = employee >> *(',' >> employee);
BOOST_SPIRIT_DEFINE(quoted_string, person, employee);
struct quoted_string_class {};
struct person_class : x3::annotate_on_success {};
struct employee_class : error_handler, x3::annotate_on_success {};
}
Go back and review the [link __tutorial_annotated_employee_parser__ annotated
employee parser]. What has changed? It is almost identical, except:
Where appropriate, we're using the expectation operator (__x3_expect__) in
place of the sequence operator (__x3_sequence__):
auto const person_def = quoted_string > ',' > quoted_string;
auto const employee_def =
'{'
> int_ > ','
> person > ','
> double_
> '}'
;
You will have some "deterministic points" in the grammar. Those are the
places where backtracking *cannot* occur. For our example above, when you get
a `'{'`, you definitely must see an `int_` next. After that, you definitely
must have a `','` next and then a `person` and so on until the final `'}'`.
Otherwise, there is no point in proceeding and trying other branches,
regardless where they are. The input is definitely erroneous. When this
happens, an expectation_failure exception is thrown. Somewhere outward, the
error handler will catch the exception. In our case, it is caught in our
`on_error` handler.
Notice too that we subclass the `employee_class` from our `error_handler`. By
doing so, we tell X3 that we want to call our `error_handler` whenever an
exception is thrown somewhere inside the `employee` rule and whatever else it
calls (i.e. the `person` and `quoted_string` rules).
[heading Let's Parse]
Now we have the complete parse mechanism with error handling:
void parse(std::string const& input)
{
using boost::spirit::x3::ascii::space;
typedef std::string::const_iterator iterator_type;
std::vector<client::ast::employee> ast;
iterator_type iter = input.begin();
iterator_type const end = input.end();
using boost::spirit::x3::with;
using boost::spirit::x3::error_handler_tag;
using error_handler_type = boost::spirit::x3::error_handler<iterator_type>;
// Our error handler
error_handler_type error_handler(iter, end, std::cerr);
// Our parser
using client::parser::employees;
auto const parser =
// we pass our error handler to the parser so we can access
// it later in our on_error and on_sucess handlers
with<error_handler_tag>(std::ref(error_handler))
[
employees
];
bool r = phrase_parse(iter, end, parser, space, ast);
// ... Some final reports here
}
Prior to calling `phrase_parse`, we first create an AST where parsed data will be
stored:
std::vector<client::ast::employee> ast;
We also create the actual error handler, sending message to `std::cerr`:
error_handler_type error_handler(iter, end, std::cerr);
Then, we inject a reference to `error_handler`, using the `with` directive
similar to what we did in the [link __tutorial_with_directive__ annotations
example]:
auto const parser =
// we pass our error handler to the parser so we can access
// it later in our on_error and on_sucess handlers
with<error_handler_tag>(std::ref(error_handler))
[
employees
];
Now, if we give the parser an erroneous input:
std::string bad_input = R"(
{
23,
"Amanda",
"Stefanski",
1000.99
},
{
35,
"Angie",
"Chilcote",
2000.99
},
{
43,
'I am not a person!' <--- this should be a person
3000.99
},
{
22,
"Dorene",
"Dole",
2500.99
},
{
38,
"Rossana",
"Rafferty",
5000.99
}
)";
The parser will compplain as expected:
[pre
-------------------------
Now we have some errors
In line 16:
Error! Expecting: person here:
'I am not a person!' <--- this should be a person
____^_
-------------------------
Parsing failed
-------------------------
]
[endsect]

View File

@@ -82,21 +82,15 @@ namespace client
///////////////////////////////////////////////////////////////////////
// Our error handler
///////////////////////////////////////////////////////////////////////
template <typename Iterator>
using error_handler = x3::error_handler<Iterator>;
// tag used to get our error handler from the context
using error_handler_tag = x3::error_handler_tag;
struct error_handler_base
struct error_handler
{
template <typename Iterator, typename Exception, typename Context>
x3::error_handler_result on_error(
Iterator& first, Iterator const& last
, Exception const& x, Context const& context)
{
auto& error_handler = x3::get<x3::error_handler_tag>(context).get();
std::string message = "Error! Expecting: " + x.which() + " here:";
auto& error_handler = x3::get<error_handler_tag>(context).get();
error_handler(x.where(), message);
return x3::error_handler_result::fail;
}
@@ -136,7 +130,7 @@ namespace client
struct quoted_string_class {};
struct person_class : x3::annotate_on_success {};
struct employee_class : error_handler_base, x3::annotate_on_success {};
struct employee_class : error_handler, x3::annotate_on_success {};
}
}
@@ -158,8 +152,8 @@ void parse(std::string const& input)
iterator_type const end = input.end();
using boost::spirit::x3::with;
using error_handler_type = client::parser::error_handler<iterator_type>;
using client::parser::error_handler_tag;
using boost::spirit::x3::error_handler_tag;
using error_handler_type = boost::spirit::x3::error_handler<iterator_type>;
// Our error handler
error_handler_type error_handler(iter, end, std::cerr);