indepth.qbk 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280
  1. [/==============================================================================
  2. Copyright (C) 2001-2011 Joel de Guzman
  3. Copyright (C) 2001-2011 Hartmut Kaiser
  4. Copyright (C) 2009 Andreas Haberstroh?
  5. Distributed under the Boost Software License, Version 1.0. (See accompanying
  6. file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
  7. ===============================================================================/]
  8. [section:indepth In Depth]
  9. [section:parsers_indepth Parsers in Depth]
  10. This section is not for the faint of heart. In here, are distilled the inner
  11. workings of __qi__ parsers, using real code from the __spirit__ library as
  12. examples. On the other hand, here is no reason to fear reading on, though.
  13. We tried to explain things step by step while highlighting the important
  14. insights.
  15. The `__parser_concept__` class is the base class for all parsers.
  16. [import ../../../../boost/spirit/home/qi/parser.hpp]
  17. [parser_base_parser]
  18. The `__parser_concept__` class does not really know how to parse anything but
  19. instead relies on the template parameter `Derived` to do the actual parsing.
  20. This technique is known as the "Curiously Recurring Template Pattern" in template
  21. meta-programming circles. This inheritance strategy gives us the power of
  22. polymorphism without the virtual function overhead. In essence this is a way to
  23. implement compile time polymorphism.
  24. The Derived parsers, `__primitive_parser_concept__`, `__unary_parser_concept__`,
  25. `__binary_parser_concept__` and `__nary_parser_concept__` provide the necessary
  26. facilities for parser detection, introspection, transformation and visitation.
  27. Derived parsers must support the following:
  28. [variablelist bool parse(f, l, context, skip, attr)
  29. [[`f`, `l`] [first/last iterator pair]]
  30. [[`context`] [enclosing rule context (can be unused_type)]]
  31. [[`skip`] [skipper (can be unused_type)]]
  32. [[`attr`] [attribute (can be unused_type)]]
  33. ]
  34. The /parse/ is the main parser entry point. /skipper/ can be an `unused_type`.
  35. It's a type used every where in __spirit__ to signify "don't-care". There
  36. is an overload for /skip/ for `unused_type` that is simply a no-op.
  37. That way, we do not have to write multiple parse functions for
  38. phrase and character level parsing.
  39. Here are the basic rules for parsing:
  40. * The parser returns `true` if successful, `false` otherwise.
  41. * If successful, `first` is incremented N number of times, where N
  42. is the number of characters parsed. N can be zero --an empty (epsilon)
  43. match.
  44. * If successful, the parsed attribute is assigned to /attr/
  45. * If unsuccessful, `first` is reset to its position before entering
  46. the parser function. /attr/ is untouched.
  47. [variablelist void what(context)
  48. [[`context`] [enclosing rule context (can be `unused_type`)]]
  49. ]
  50. The /what/ function should be obvious. It provides some information
  51. about ["what] the parser is. It is used as a debugging aid, for
  52. example.
  53. [variablelist P::template attribute<context>::type
  54. [[`P`] [a parser type]]
  55. [[`context`] [A context type (can be unused_type)]]
  56. ]
  57. The /attribute/ metafunction returns the expected attribute type
  58. of the parser. In some cases, this is context dependent.
  59. In this section, we will dissect two parser types:
  60. [variablelist Parsers
  61. [[`__primitive_parser_concept__`] [A parser for primitive data (e.g. integer parsing).]]
  62. [[`__unary_parser_concept__`] [A parser that has single subject (e.g. kleene star).]]
  63. ]
  64. [/------------------------------------------------------------------------------]
  65. [heading Primitive Parsers]
  66. For our dissection study, we will use a __spirit__ primitive, the `any_int_parser`
  67. in the boost::spirit::qi namespace.
  68. [import ../../../../boost/spirit/home/qi/numeric/int.hpp]
  69. [primitive_parsers_any_int_parser]
  70. The `any_int_parser` is derived from a `__primitive_parser_concept__<Derived>`,
  71. which in turn derives from `parser<Derived>`. Therefore, it supports the
  72. following requirements:
  73. * The `parse` member function
  74. * The `what` member function
  75. * The nested `attribute` metafunction
  76. /parse/ is the main entry point. For primitive parsers, our first thing to do is
  77. call:
  78. ``
  79. qi::skip(first, last, skipper);
  80. ``
  81. to do a pre-skip. After pre-skipping, the parser proceeds to do its thing. The
  82. actual parsing code is placed in `extract_int<T, Radix, MinDigits,
  83. MaxDigits>::call(first, last, attr);`
  84. This simple no-frills protocol is one of the reasons why __spirit__ is
  85. fast. If you know the internals of __classic__ and perhaps
  86. even wrote some parsers with it, this simple __spirit__ mechanism
  87. is a joy to work with. There are no scanners and all that crap.
  88. The /what/ function just tells us that it is an integer parser. Simple.
  89. The /attribute/ metafunction returns the T template parameter. We associate the
  90. `any_int_parser` to some placeholders for `short_`, `int_`, `long_` and
  91. `long_long` types. But, first, we enable these placeholders in namespace
  92. boost::spirit:
  93. [primitive_parsers_enable_short]
  94. [primitive_parsers_enable_int]
  95. [primitive_parsers_enable_long]
  96. [primitive_parsers_enable_long_long]
  97. Notice that `any_int_parser` is placed in the namespace boost::spirit::qi
  98. while these /enablers/ are in namespace boost::spirit. The reason is
  99. that these placeholders are shared by other __spirit__ /domains/. __qi__,
  100. the parser is one domain. __karma__, the generator is another domain.
  101. Other parser technologies may be developed and placed in yet
  102. another domain. Yet, all these can potentially share the same
  103. placeholders for interoperability. The interpretation of these
  104. placeholders is domain-specific.
  105. Now that we enabled the placeholders, we have to write generators
  106. for them. The make_xxx stuff (in boost::spirit::qi namespace):
  107. [primitive_parsers_make_int]
  108. This one above is our main generator. It's a simple function object
  109. with 2 (unused) arguments. These arguments are
  110. # The actual terminal value obtained by proto. In this case, either
  111. a short_, int_, long_ or long_long. We don't care about this.
  112. # Modifiers. We also don't care about this. This allows directives
  113. such as `no_case[p]` to pass information to inner parser nodes.
  114. We'll see how that works later.
  115. Now:
  116. [primitive_parsers_short_primitive]
  117. [primitive_parsers_int_primitive]
  118. [primitive_parsers_long_primitive]
  119. [primitive_parsers_long_long_primitive]
  120. These, specialize `qi:make_primitive` for specific tags. They all
  121. inherit from `make_int` which does the actual work.
  122. [heading Composite Parsers]
  123. Let me present the kleene star (also in namespace spirit::qi):
  124. [import ../../../../boost/spirit/home/qi/operator/kleene.hpp]
  125. [composite_parsers_kleene]
  126. Looks similar in form to its primitive cousin, the `int_parser`. And, again, it
  127. has the same basic ingredients required by `Derived`.
  128. * The nested attribute metafunction
  129. * The parse member function
  130. * The what member function
  131. kleene is a composite parser. It is a parser that composes another
  132. parser, its ["subject]. It is a `__unary_parser_concept__` and subclasses from it.
  133. Like `__primitive_parser_concept__`, `__unary_parser_concept__<Derived>` derives
  134. from `parser<Derived>`.
  135. unary_parser<Derived>, has these expression requirements on Derived:
  136. * p.subject -> subject parser ( ['p] is a __unary_parser_concept__ parser.)
  137. * P::subject_type -> subject parser type ( ['P] is a __unary_parser_concept__ type.)
  138. /parse/ is the main parser entry point. Since this is not a primitive
  139. parser, we do not need to call `qi::skip(first, last, skipper)`. The
  140. ['subject], if it is a primitive, will do the pre-skip. If if it is
  141. another composite parser, it will eventually call a primitive parser
  142. somewhere down the line which will do the pre-skip. This makes it a
  143. lot more efficient than __classic__. __classic__ puts the skipping business
  144. into the so-called "scanner" which blindly attempts a pre-skip
  145. every time we increment the iterator.
  146. What is the /attribute/ of the kleene? In general, it is a `std::vector<T>`
  147. where `T` is the attribute of the subject. There is a special case though.
  148. If `T` is an `unused_type`, then the attribute of kleene is also `unused_type`.
  149. `traits::build_std_vector` takes care of that minor detail.
  150. So, let's parse. First, we need to provide a local attribute of for
  151. the subject:
  152. ``
  153. typename traits::attribute_of<Subject, Context>::type val;
  154. ``
  155. `traits::attribute_of<Subject, Context>` simply calls the subject's
  156. `struct attribute<Context>` nested metafunction.
  157. /val/ starts out default initialized. This val is the one we'll
  158. pass to the subject's parse function.
  159. The kleene repeats indefinitely while the subject parser is
  160. successful. On each successful parse, we `push_back` the parsed
  161. attribute to the kleene's attribute, which is expected to be,
  162. at the very least, compatible with a `std::vector`. In other words,
  163. although we say that we want our attribute to be a `std::vector`,
  164. we try to be more lenient than that. The caller of kleene's
  165. parse may pass a different attribute type. For as long as it is
  166. also a conforming STL container with `push_back`, we are ok. Here
  167. is the kleene loop:
  168. ``
  169. while (subject.parse(first, last, context, skipper, val))
  170. {
  171. // push the parsed value into our attribute
  172. traits::push_back(attr, val);
  173. traits::clear(val);
  174. }
  175. return true;
  176. ``
  177. Take note that we didn't call attr.push_back(val). Instead, we
  178. called a Spirit provided function:
  179. ``
  180. traits::push_back(attr, val);
  181. ``
  182. This is a recurring pattern. The reason why we do it this way is
  183. because attr [*can] be `unused_type`. `traits::push_back` takes care
  184. of that detail. The overload for unused_type is a no-op. Now, you
  185. can imagine why __spirit__ is fast! The parsers are so simple and the
  186. generated code is as efficient as a hand rolled loop. All these
  187. parser compositions and recursive parse invocations are extensively
  188. inlined by a modern C++ compiler. In the end, you get a tight loop
  189. when you use the kleene. No more excess baggage. If the attribute
  190. is unused, then there is no code generated for that. That's how
  191. __spirit__ is designed.
  192. The /what/ function simply wraps the output of the subject in a
  193. "kleene[" ... "]".
  194. Ok, now, like the `int_parser`, we have to hook our parser to the
  195. _qi_ engine. Here's how we do it:
  196. First, we enable the prefix star operator. In proto, it's called
  197. the "dereference":
  198. [composite_parsers_kleene_enable_]
  199. This is done in namespace `boost::spirit` like its friend, the `use_terminal`
  200. specialization for our `int_parser`. Obviously, we use /use_operator/ to
  201. enable the dereference for the qi::domain.
  202. Then, we need to write our generator (in namespace qi):
  203. [composite_parsers_kleene_generator]
  204. This essentially says; for all expressions of the form: `*p`, to build a kleene
  205. parser. Elements is a __fusion__ sequence. For the kleene, which is a unary
  206. operator, expect only one element in the sequence. That element is the subject
  207. of the kleene.
  208. We still don't care about the Modifiers. We'll see how the modifiers is
  209. all about when we get to deep directives.
  210. [endsect]
  211. [endsect]