diff --git a/doc/x3/spirit_x3.qbk b/doc/x3/spirit_x3.qbk index 6e1f8af90..a72197215 100644 --- a/doc/x3/spirit_x3.qbk +++ b/doc/x3/spirit_x3.qbk @@ -187,6 +187,7 @@ [/ Tutorials -----------------------------------------------------------------] +[template tutorial_roman[str] [link spirit_x3.tutorials.roman [str]]] [template tutorial_employee[str] [link spirit_x3.tutorials.employee [str]]] [template tutorial_annotation[str] [link spirit_x3.tutorials.annotation [str]]] [template tutorial_error_handling[str] [link spirit_x3.tutorials.error_handling [str]]] diff --git a/doc/x3/tutorial/employee.qbk b/doc/x3/tutorial/employee.qbk index f437069ff..34517dd1c 100644 --- a/doc/x3/tutorial/employee.qbk +++ b/doc/x3/tutorial/employee.qbk @@ -89,7 +89,11 @@ Let's walk through this one step at a time (not necessarily from top to bottom). [heading Rule Declaration] - x3::rule employee("employee"); +We are assuming that you already know about rules. We introduced rules in the +previous [tutorial_roman Roman Numerals example]. Please go back and review +the previous tutorial if you have to. + + x3::rule employee = "employee"; [heading Lexeme] @@ -178,6 +182,11 @@ is: [heading Rule Definition] +Again, we are assuming that you already know about rules and rule +definitions. We introduced rules in the previous [tutorial_roman Roman +Numerals example]. Please go back and review the previous tutorial if you +have to. + employee = lit("employee") >> '{' @@ -190,7 +199,6 @@ is: BOOST_SPIRIT_DEFINE(employee); - Applying our collapsing rules above, the RHS has an attribute of: fusion::vector diff --git a/doc/x3/tutorial/minimal.qbk b/doc/x3/tutorial/minimal.qbk index 1094c41e5..23058c34d 100644 --- a/doc/x3/tutorial/minimal.qbk +++ b/doc/x3/tutorial/minimal.qbk @@ -76,8 +76,37 @@ Here, we adapt the AST for Fusion, making it a first-class fusion citizen: [heading Main parser API] This is the main header file that all other cpp files need to include. -Here, we use BOOST_SPIRIT_DECLARE for the *top* rule. In this example, -the top rule is `employee`. We declare `employee` in this header file: + +[#__tutorial_spirit_declare__] +[heading BOOST_SPIRIT_DECLARE] + +Remember [link __tutorial_spirit_define__ `BOOST_SPIRIT_DEFINE`]? If not, +then you probably want to go back and review that section to get a better +understanding of what's happening. + +Here in the header file, instead of `BOOST_SPIRIT_DEFINE`, we use +`BOOST_SPIRIT_DECLARE` for the *top* rule. Behind the scenes, what's actually +happening is that we are declaring a `parse_rule` function in the client +namespace. For example, given a rule named `my_rule`, +`BOOST_SPIRIT_DECLARE(my_rule)` expands to this code: + + template + bool parse_rule( + decltype(my_rule) + , Iterator& first, Iterator const& last + , Context const& context, Attribute& attr); + +If you went back and reviewed [link __tutorial_spirit_define__ +BOOST_SPIRIT_DEFINE], you'll see why it is exactly what we need to use for +header files. `BOOST_SPIRIT_DECLARE` generates function declarations that are +meant to be placed in hpp (header) files while `BOOST_SPIRIT_DEFINE` +generates function definitions that are meant to be placed in cpp files. + +[note `BOOST_SPIRIT_DECLARE` is variadic and may be used for one or more rules. +Example: `BOOST_SPIRIT_DECLARE(r1, r2, r3);`] + +In this example, the top rule is `employee`. We declare `employee` in this +header file: namespace client { @@ -133,8 +162,9 @@ Here is where we place the actual rules that make up our grammar: return parser::employee; } -In the parser definition, we use `BOOST_SPIRIT_DEFINE` just like we did in the -[tutorial_employee employee example]. +In the parser definition, we use [link __tutorial_spirit_define__ +`BOOST_SPIRIT_DEFINE`] just like we did in the [tutorial_employee employee +example]. While this is another header file, it is not meant to be included by the client. Its purpose is to be included by an instantiations cpp file (see @@ -167,6 +197,39 @@ Now we instantiate our parser here, for our specific configuration: For that, we use `BOOST_SPIRIT_INSTANTIATE`, passing in the parser type, the iterator type, and the context type. +[heading BOOST_SPIRIT_INSTANTIATE] + +Go back and review [link __tutorial_spirit_define__ `BOOST_SPIRIT_DEFINE`] +and [link __tutorial_spirit_declare__ `BOOST_SPIRIT_DECLARE`] to get a better +grasp of what's happening with `BOOST_SPIRIT_INSTANTIATE` and why it is +needed. + +So what the heck is `BOOST_SPIRIT_INSTANTIATE`? What we want is to isolate +the instantiation of our parsers (rules and all that), into separate +translation units (or cpp files, if you will). In this example, we want to +place our x3 employee stuff in [@../../../example/x3/minimal/employee.cpp +employee.cpp]. That way, we have separate compilation. Every time we update +our employee parser source code, we only have to build the `employee.cpp` +file. All the rest will not be affected. By compiling only once in one +translation unit, we save on build times and avoid code bloat. There is no +code duplication, which can happen otherwise if you simply include the +employee parser ([@../../../example/x3/minimal/employee.hpp employee.hpp]) +everywhere. + +But how do you do that. Remember that our parser definitions are also placed +in its oen header file for flexibility, so we have the freedom to instantiate +the parser with different iterator types. + +What we need to do is explicitly instantiate the `parse_rule` function we +declared and defined via `BOOST_SPIRIT_DECLARE` and `BOOST_SPIRIT_DEFINE` +respectively, using `BOOST_SPIRIT_INSTANTIATE`. For our particular example, +`BOOST_SPIRIT_INSTANTIATE` expands to this code: + + template bool parse_rule( + employee_type rule_ + , iterator_type& first, iterator_type const& last + , context_type const& context, employee_type::attribute_type& attr); + [heading Main Program] Finally, we have our main program. The code is the same as single cpp file diff --git a/doc/x3/tutorial/roman.qbk b/doc/x3/tutorial/roman.qbk index dadf7c0c6..7cb6d483a 100644 --- a/doc/x3/tutorial/roman.qbk +++ b/doc/x3/tutorial/roman.qbk @@ -6,7 +6,7 @@ file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) ===============================================================================/] -[section Roman Numerals] +[section:roman Roman Numerals] This example demonstrates: @@ -120,35 +120,73 @@ various ways to declare rules. The simplest form is: rule const r = "some-name"; +[heading Rule ID] + At the very least, the rule needs an identification tag. This ID can be any struct or class type and need not be defined. Forward declaration would suffice. -The name is optional, but is useful for debugging and error handling, as we'll -see later. Notice that rule `r` is declared `const`. Rules are immutable and are -best declared as `const`. +In subsequent tutorials, we will see that the rule ID can have additional +functionalities for error handling and annotation. + +[heading Rule Name] + +The name is optional, but is useful for debugging and error handling, as +we'll see later. Notice that rule `r` is declared `const`. Rules are +immutable and are best declared as `const`. Rules are lightweight and can be +passed around by value. Its only member variable is a `std::string`: its +name. [note Unlike Qi (Spirit V2), X3 rules can be used with both `phrase_parse` and `parse` without having to specify the skip parser] +[heading Rule Attributes] + For our next example, there's one more rule form you should know about: rule const r = "some-name"; -The Attribute specifies the attributes of the rule. You've seen that our parsers -can have an attribute. Recall that the `double_` parser has an attribute of -`double`. To be precise, these are /synthesized/ attributes. The parser -"synthesizes" the attribute value. Think of them as function return values. +The Attribute parameter specifies the attribute type of the rule. You've seen +that our parsers can have an attribute. Recall that the `double_` parser has +an attribute of `double`. To be precise, these are /synthesized/ attributes. +The parser "synthesizes" the attribute value. If the parser is a function, +think of them as function return values. + +[heading Rule Definition] After having declared a rule, you need a definition for the rule. Example: auto const r_def = double_ >> *(',' >> double_); By convention, rule definitions have a _def suffix. Like rules, rule definitions -are immutable and are best declared as `const`. Now that we have a rule and its -definition, we tie the rule with a rule definition using the -`BOOST_SPIRIT_DEFINE` macro: +are immutable and are best declared as `const`. + +[#__tutorial_spirit_define__] +[heading BOOST_SPIRIT_DEFINE] + +Now that we have a rule and its definition, we tie the rule with a rule +definition using the `BOOST_SPIRIT_DEFINE` macro: BOOST_SPIRIT_DEFINE(r); +Behind the scenes, what's actually happening is that we are defining a `parse_rule` +function in the client namespace that tells X3 how to invoke the rule. For example, +given a rule named `my_rule` and a corresponding definition named `my_rule_def`, +`BOOST_SPIRIT_DEFINE(my_rule)` expands to this code: + + template + inline bool parse_rule( + decltype(my_rule) + , Iterator& first, Iterator const& last + , Context const& context, Attribute& attr) + { + using boost::spirit::x3::unused; + static auto const def_ = my_rule_def; + return def_.parse(first, last, context, unused, attr); + } + +And so for each rule defined using `BOOST_SPIRIT_DEFINE`, there is an +overloaded `parse_rule` function. At parse time, Spirit X3 recursively calls +the appropriate `parse_rule` function. + [note `BOOST_SPIRIT_DEFINE` is variadic and may be used for one or more rules. Example: `BOOST_SPIRIT_DEFINE(r1, r2, r3);`] @@ -204,6 +242,11 @@ Things to take notice of: * The rule `roman` and the definition `roman_def` are const objects. +* The rule's ID is `class roman`. C++ allows you to declare the class + in the actual template declaration as you can see in the example: + + x3::rule const roman = "roman"; + [heading Let's Parse!] bool r = parse(iter, end, roman, result); diff --git a/example/x3/calc/calc8/main.cpp b/example/x3/calc/calc8/main.cpp index 2bfc876a2..e87ccbb7d 100644 --- a/example/x3/calc/calc8/main.cpp +++ b/example/x3/calc/calc8/main.cpp @@ -57,7 +57,6 @@ main() iterator_type iter(source.begin()); iterator_type end(source.end()); - client::vmachine vm; // Our virtual machine client::code_gen::program program; // Our VM program client::ast::statement_list ast; // Our AST