roman.qbk 9.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278
  1. [/==============================================================================
  2. Copyright (C) 2001-2015 Joel de Guzman
  3. Copyright (C) 2001-2011 Hartmut Kaiser
  4. Distributed under the Boost Software License, Version 1.0. (See accompanying
  5. file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
  6. ===============================================================================/]
  7. [section:roman Roman Numerals]
  8. This example demonstrates:
  9. * The Symbol Table
  10. * Non-terminal rules
  11. [heading Symbol Table]
  12. The symbol table holds a dictionary of symbols where each symbol is a sequence
  13. of characters. The template class, can work efficiently with 8, 16, 32 and even
  14. 64 bit characters. Mutable data of type T are associated with each symbol.
  15. Traditionally, symbol table management is maintained separately outside the BNF
  16. grammar through semantic actions. Contrary to standard practice, the Spirit
  17. symbol table class `symbols` is a parser. An object of which may be used
  18. anywhere in the EBNF grammar specification. It is an example of a dynamic
  19. parser. A dynamic parser is characterized by its ability to modify its behavior
  20. at run time. Initially, an empty symbols object matches nothing. At any time,
  21. symbols may be added or removed, thus, dynamically altering its behavior.
  22. Each entry in a symbol table may have an associated mutable data slot. In this
  23. regard, one can view the symbol table as an associative container (or map) of
  24. key-value pairs where the keys are strings.
  25. The symbols class expects one template parameter to specify the data type
  26. associated with each symbol: its attribute. There are a couple of
  27. namespaces in X3 where you can find various versions of the symbols class
  28. for handling different character encoding including ascii, standard,
  29. standard_wide, iso8859_1, and unicode. The default symbol parser type in
  30. the main x3 namespace is standard.
  31. Here's a parser for roman hundreds (100..900) using the symbol table. Keep in
  32. mind that the data associated with each slot is the parser's attribute (which is
  33. passed to attached semantic actions).
  34. struct hundreds_ : x3::symbols<unsigned>
  35. {
  36. hundreds_()
  37. {
  38. add
  39. ("C" , 100)
  40. ("CC" , 200)
  41. ("CCC" , 300)
  42. ("CD" , 400)
  43. ("D" , 500)
  44. ("DC" , 600)
  45. ("DCC" , 700)
  46. ("DCCC" , 800)
  47. ("CM" , 900)
  48. ;
  49. }
  50. } hundreds;
  51. Here's a parser for roman tens (10..90):
  52. struct tens_ : x3::symbols<unsigned>
  53. {
  54. tens_()
  55. {
  56. add
  57. ("X" , 10)
  58. ("XX" , 20)
  59. ("XXX" , 30)
  60. ("XL" , 40)
  61. ("L" , 50)
  62. ("LX" , 60)
  63. ("LXX" , 70)
  64. ("LXXX" , 80)
  65. ("XC" , 90)
  66. ;
  67. }
  68. } tens;
  69. and, finally, for ones (1..9):
  70. struct ones_ : x3::symbols<unsigned>
  71. {
  72. ones_()
  73. {
  74. add
  75. ("I" , 1)
  76. ("II" , 2)
  77. ("III" , 3)
  78. ("IV" , 4)
  79. ("V" , 5)
  80. ("VI" , 6)
  81. ("VII" , 7)
  82. ("VIII" , 8)
  83. ("IX" , 9)
  84. ;
  85. }
  86. } ones;
  87. Now we can use `hundreds`, `tens` and `ones` anywhere in our parser expressions.
  88. They are all parsers.
  89. [heading Rules]
  90. Up until now, we've been inlining our parser expressions, passing them directly
  91. to the `phrase_parse` function. The expression evaluates into a temporary,
  92. unnamed parser which is passed into the `phrase_parse` function, used, and then
  93. destroyed. This is fine for small parsers. When the expressions get complicated,
  94. you'd want to break the expressions into smaller easier-to-understand pieces,
  95. name them, and refer to them from other parser expressions by name.
  96. A parser expression can be assigned to what is called a "rule". There are
  97. various ways to declare rules. The simplest form is:
  98. rule<ID> const r = "some-name";
  99. [heading Rule ID]
  100. At the very least, the rule needs an identification tag. This ID can be any
  101. struct or class type and need not be defined. Forward declaration would suffice.
  102. In subsequent tutorials, we will see that the rule ID can have additional
  103. functionalities for error handling and annotation.
  104. [heading Rule Name]
  105. The name is optional, but is useful for debugging and error handling, as
  106. we'll see later. Notice that rule `r` is declared `const`. Rules are
  107. immutable and are best declared as `const`. Rules are lightweight and can be
  108. passed around by value. Its only member variable is a `std::string`: its
  109. name.
  110. [note Unlike Qi (Spirit V2), X3 rules can be used with both `phrase_parse` and
  111. `parse` without having to specify the skip parser]
  112. [heading Rule Attributes]
  113. For our next example, there's one more rule form you should know about:
  114. rule<ID, Attribute> const r = "some-name";
  115. The Attribute parameter specifies the attribute type of the rule. You've seen
  116. that our parsers can have an attribute. Recall that the `double_` parser has
  117. an attribute of `double`. To be precise, these are /synthesized/ attributes.
  118. The parser "synthesizes" the attribute value. If the parser is a function,
  119. think of them as function return values.
  120. [heading Rule Definition]
  121. After having declared a rule, you need a definition for the rule. Example:
  122. auto const r_def = double_ >> *(',' >> double_);
  123. By convention, rule definitions have a _def suffix. Like rules, rule definitions
  124. are immutable and are best declared as `const`.
  125. [#__tutorial_spirit_define__]
  126. [heading BOOST_SPIRIT_DEFINE]
  127. Now that we have a rule and its definition, we tie the rule with a rule
  128. definition using the `BOOST_SPIRIT_DEFINE` macro:
  129. BOOST_SPIRIT_DEFINE(r);
  130. Behind the scenes, what's actually happening is that we are defining a `parse_rule`
  131. function in the client namespace that tells X3 how to invoke the rule. For example,
  132. given a rule named `my_rule` and a corresponding definition named `my_rule_def`,
  133. `BOOST_SPIRIT_DEFINE(my_rule)` expands to this code:
  134. template <typename Iterator, typename Context>
  135. inline bool parse_rule(
  136. decltype(my_rule)
  137. , Iterator& first, Iterator const& last
  138. , Context const& context, decltype(my_rule)::attribute_type& attr)
  139. {
  140. using boost::spirit::x3::unused;
  141. static auto const def_ = my_rule_def;
  142. return def_.parse(first, last, context, unused, attr);
  143. }
  144. And so for each rule defined using `BOOST_SPIRIT_DEFINE`, there is an
  145. overloaded `parse_rule` function. At parse time, Spirit X3 recursively calls
  146. the appropriate `parse_rule` function.
  147. [note `BOOST_SPIRIT_DEFINE` is variadic and may be used for one or more rules.
  148. Example: `BOOST_SPIRIT_DEFINE(r1, r2, r3);`]
  149. [heading Grammars]
  150. Unlike Qi (Spirit V2), X3 discards the notion of a grammar as a concrete
  151. entity for encapsulating rules. In X3, a grammar is simply a logical group of
  152. rules that work together, typically with a single top-level start rule which
  153. serves as the main entry point. X3 grammars are grouped using namespaces.
  154. The roman numeral grammar is a very nice and simple example of a grammar:
  155. namespace parser
  156. {
  157. using x3::eps;
  158. using x3::lit;
  159. using x3::_val;
  160. using x3::_attr;
  161. using ascii::char_;
  162. auto set_zero = [&](auto& ctx){ _val(ctx) = 0; };
  163. auto add1000 = [&](auto& ctx){ _val(ctx) += 1000; };
  164. auto add = [&](auto& ctx){ _val(ctx) += _attr(ctx); };
  165. x3::rule<class roman, unsigned> const roman = "roman";
  166. auto const roman_def =
  167. eps [set_zero]
  168. >>
  169. (
  170. -(+lit('M') [add1000])
  171. >> -hundreds [add]
  172. >> -tens [add]
  173. >> -ones [add]
  174. )
  175. ;
  176. BOOST_SPIRIT_DEFINE(roman);
  177. }
  178. Things to take notice of:
  179. * The start rule's attribute is `unsigned`.
  180. * `_val(ctx)` gets a reference to the rule's synthesized attribute.
  181. * `_attr(ctx)` gets a reference to the parser's synthesized attribute.
  182. * `eps` is a special spirit parser that consumes no input but is always
  183. successful. We use it to initialize the rule's synthesized
  184. attribute, to zero before anything else. The actual parser starts at
  185. `+lit('M')`, parsing roman thousands. Using `eps` this way is good
  186. for doing pre and post initializations.
  187. * The rule `roman` and the definition `roman_def` are const objects.
  188. * The rule's ID is `class roman`. C++ allows you to declare the class
  189. in the actual template declaration as you can see in the example:
  190. x3::rule<class roman, unsigned> const roman = "roman";
  191. [heading Let's Parse!]
  192. bool r = parse(iter, end, roman, result);
  193. if (r && iter == end)
  194. {
  195. std::cout << "-------------------------\n";
  196. std::cout << "Parsing succeeded\n";
  197. std::cout << "result = " << result << std::endl;
  198. std::cout << "-------------------------\n";
  199. }
  200. else
  201. {
  202. std::string rest(iter, end);
  203. std::cout << "-------------------------\n";
  204. std::cout << "Parsing failed\n";
  205. std::cout << "stopped at: \": " << rest << "\"\n";
  206. std::cout << "-------------------------\n";
  207. }
  208. `roman` is our roman numeral parser. This time around we are using the
  209. no-skipping version of the parse functions. We do not want to skip any spaces!
  210. We are also passing in an attribute, `unsigned result`, which will receive the
  211. parsed value.
  212. The full cpp file for this example can be found here:
  213. [@../../../example/x3/roman.cpp roman.cpp]
  214. [endsect]