employee.qbk 6.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225
  1. [/==============================================================================
  2. Copyright (C) 2001-2011 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 Employee - Parsing into structs]
  8. It's a common question in the __spirit_list__: How do I parse and place
  9. the results into a C++ struct? Of course, at this point, you already
  10. know various ways to do it, using semantic actions. There are many ways
  11. to skin a cat. Spirit2, being fully attributed, makes it even easier.
  12. The next example demonstrates some features of Spirit2 that make this
  13. easy. In the process, you'll learn about:
  14. * More about attributes
  15. * Auto rules
  16. * Some more built-in parsers
  17. * Directives
  18. [import ../../example/qi/employee.cpp]
  19. First, let's create a struct representing an employee:
  20. [tutorial_employee_struct]
  21. Then, we need to tell __fusion__ about our employee struct to make it a first-class
  22. fusion citizen that the grammar can utilize. If you don't know fusion yet,
  23. it is a __boost__ library for working with heterogeneous collections of data,
  24. commonly referred to as tuples. Spirit uses fusion extensively as part of its
  25. infrastructure.
  26. In fusion's view, a struct is just a form of a tuple. You can adapt any struct
  27. to be a fully conforming fusion tuple:
  28. [tutorial_employee_adapt_struct]
  29. Now we'll write a parser for our employee. Inputs will be of the form:
  30. employee{ age, "surname", "forename", salary }
  31. Here goes:
  32. [tutorial_employee_parser]
  33. The full cpp file for this example can be found here: [@../../example/qi/employee.cpp]
  34. Let's walk through this one step at a time (not necessarily from top to bottom).
  35. template <typename Iterator>
  36. struct employee_parser : grammar<Iterator, employee(), space_type>
  37. `employee_parser` is a grammar. Like before, we make it a template so that we can
  38. reuse it for different iterator types. The grammar's signature is:
  39. employee()
  40. meaning, the parser generates employee structs. `employee_parser` skips white
  41. spaces using `space_type` as its skip parser.
  42. employee_parser() : employee_parser::base_type(start)
  43. Initializes the base class.
  44. rule<Iterator, std::string(), space_type> quoted_string;
  45. rule<Iterator, employee(), space_type> start;
  46. Declares two rules: `quoted_string` and `start`. `start` has the same template
  47. parameters as the grammar itself. `quoted_string` has a `std::string` attribute.
  48. [heading Lexeme]
  49. lexeme['"' >> +(char_ - '"') >> '"'];
  50. `lexeme` inhibits space skipping from the open brace to the closing brace.
  51. The expression parses quoted strings.
  52. +(char_ - '"')
  53. parses one or more chars, except the double quote. It stops when it sees
  54. a double quote.
  55. [heading Difference]
  56. The expression:
  57. a - b
  58. parses `a` but not `b`. Its attribute is just `A`; the attribute of `a`. `b`'s
  59. attribute is ignored. Hence, the attribute of:
  60. char_ - '"'
  61. is just `char`.
  62. [heading Plus]
  63. +a
  64. is similar to Kleene star. Rather than match everything, `+a` matches one or more.
  65. Like it's related function, the Kleene star, its attribute is a `std::vector<A>`
  66. where `A` is the attribute of `a`. So, putting all these together, the attribute
  67. of
  68. +(char_ - '"')
  69. is then:
  70. std::vector<char>
  71. [heading Sequence Attribute]
  72. Now what's the attribute of
  73. '"' >> +(char_ - '"') >> '"'
  74. ?
  75. Well, typically, the attribute of:
  76. a >> b >> c
  77. is:
  78. fusion::vector<A, B, C>
  79. where `A` is the attribute of `a`, `B` is the attribute of `b` and `C` is the
  80. attribute of `c`. What is `fusion::vector`? - a tuple.
  81. [note If you don't know what I am talking about, see: [@http://tinyurl.com/6xun4j
  82. Fusion Vector]. It might be a good idea to have a look into __fusion__ at this
  83. point. You'll definitely see more of it in the coming pages.]
  84. [heading Attribute Collapsing]
  85. Some parsers, especially those very little literal parsers you see, like `'"'`,
  86. do not have attributes.
  87. Nodes without attributes are disregarded. In a sequence, like above, all nodes
  88. with no attributes are filtered out of the `fusion::vector`. So, since `'"'` has
  89. no attribute, and `+(char_ - '"')` has a `std::vector<char>` attribute, the
  90. whole expression's attribute should have been:
  91. fusion::vector<std::vector<char> >
  92. But wait, there's one more collapsing rule: If the attribute is followed by a
  93. single element `fusion::vector`, The element is stripped naked from its container.
  94. To make a long story short, the attribute of the expression:
  95. '"' >> +(char_ - '"') >> '"'
  96. is:
  97. std::vector<char>
  98. [heading Auto Rules]
  99. It is typical to see rules like:
  100. r = p[_val = _1];
  101. If you have a rule definition such as the above, where the attribute of the RHS
  102. (right hand side) of the rule is compatible with the attribute of the LHS (left
  103. hand side), then you can rewrite it as:
  104. r %= p;
  105. The attribute of `p` automatically uses the attribute of `r`.
  106. So, going back to our `quoted_string` rule:
  107. quoted_string %= lexeme['"' >> +(char_ - '"') >> '"'];
  108. is a simplified version of:
  109. quoted_string = lexeme['"' >> +(char_ - '"') >> '"'][_val = _1];
  110. The attribute of the `quoted_string` rule: `std::string` *is compatible* with
  111. the attribute of the RHS: `std::vector<char>`. The RHS extracts the parsed
  112. attribute directly into the rule's attribute, in-situ.
  113. [note `r %= p` and `r = p` are equivalent if there are no semantic actions
  114. associated with `p`. ]
  115. [heading Finally]
  116. We're down to one rule, the start rule:
  117. start %=
  118. lit("employee")
  119. >> '{'
  120. >> int_ >> ','
  121. >> quoted_string >> ','
  122. >> quoted_string >> ','
  123. >> double_
  124. >> '}'
  125. ;
  126. Applying our collapsing rules above, the RHS has an attribute of:
  127. fusion::vector<int, std::string, std::string, double>
  128. These nodes do not have an attribute:
  129. * `lit("employee")`
  130. * `'{'`
  131. * `','`
  132. * `'}'`
  133. [note In case you are wondering, `lit("employee")` is the same as "employee". We
  134. had to wrap it inside `lit` because immediately after it is `>> '{'`. You can't
  135. right-shift a `char[]` and a `char` - you know, C++ syntax rules.]
  136. Recall that the attribute of `start` is the `employee` struct:
  137. [tutorial_employee_struct]
  138. Now everything is clear, right? The `struct employee` *IS* compatible with
  139. `fusion::vector<int, std::string, std::string, double>`. So, the RHS of `start`
  140. uses start's attribute (a `struct employee`) in-situ when it does its work.
  141. [endsect]