]>
Commit | Line | Data |
---|---|---|
7c673cae FG |
1 | [/============================================================================== |
2 | Copyright (C) 2001-2015 Joel de Guzman | |
3 | Copyright (C) 2001-2011 Hartmut Kaiser | |
4 | ||
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 | ||
9 | [section Employee - Parsing into structs] | |
10 | ||
11 | It's a common question in the __spirit_list__: How do I parse and place | |
12 | the results into a C++ struct? Of course, at this point, you already | |
13 | know various ways to do it, using semantic actions. There are many ways | |
14 | to skin a cat. Spirit X3, being fully attributed, makes it even easier. | |
15 | The next example demonstrates some features of Spirit X3 that make this | |
16 | easy. In the process, you'll learn about: | |
17 | ||
18 | * More about attributes | |
19 | * Auto rules | |
20 | * Some more built-in parsers | |
21 | * Directives | |
22 | ||
23 | First, let's create a struct representing an employee: | |
24 | ||
25 | namespace client { namespace ast | |
26 | { | |
27 | struct employee | |
28 | { | |
29 | int age; | |
30 | std::string surname; | |
31 | std::string forename; | |
32 | double salary; | |
33 | }; | |
34 | }} | |
35 | ||
36 | Then, we need to tell __fusion__ about our employee struct to make it a first-class | |
37 | fusion citizen that the grammar can utilize. If you don't know fusion yet, | |
38 | it is a __boost__ library for working with heterogeneous collections of data, | |
39 | commonly referred to as tuples. Spirit uses fusion extensively as part of its | |
40 | infrastructure. | |
41 | ||
42 | In fusion's view, a struct is just a form of a tuple. You can adapt any struct | |
43 | to be a fully conforming fusion tuple: | |
44 | ||
45 | BOOST_FUSION_ADAPT_STRUCT( | |
46 | client::ast::employee, | |
47 | age, surname, forename, salary | |
48 | ) | |
49 | ||
50 | Now we'll write a parser for our employee. Inputs will be of the form: | |
51 | ||
52 | employee{ age, "surname", "forename", salary } | |
53 | ||
54 | Here goes: | |
55 | ||
56 | namespace parser | |
57 | { | |
58 | namespace x3 = boost::spirit::x3; | |
59 | namespace ascii = boost::spirit::x3::ascii; | |
60 | ||
61 | using x3::int_; | |
62 | using x3::lit; | |
63 | using x3::double_; | |
64 | using x3::lexeme; | |
65 | using ascii::char_; | |
66 | ||
67 | x3::rule<class employee, ast::employee> const employee = "employee"; | |
68 | ||
69 | auto const quoted_string = lexeme['"' >> +(char_ - '"') >> '"']; | |
70 | ||
71 | auto const employee_def = | |
72 | lit("employee") | |
73 | >> '{' | |
74 | >> int_ >> ',' | |
75 | >> quoted_string >> ',' | |
76 | >> quoted_string >> ',' | |
77 | >> double_ | |
78 | >> '}' | |
79 | ; | |
80 | ||
81 | BOOST_SPIRIT_DEFINE(employee); | |
82 | } | |
83 | ||
84 | The full cpp file for this example can be found here: [@../../../example/x3/employee.cpp] | |
85 | ||
86 | Let's walk through this one step at a time (not necessarily from top to bottom). | |
87 | ||
88 | [heading Rule Declaration] | |
89 | ||
90 | x3::rule<class employee, ast::employee> employee("employee"); | |
91 | ||
92 | [heading Lexeme] | |
93 | ||
94 | lexeme['"' >> +(char_ - '"') >> '"']; | |
95 | ||
96 | `lexeme` inhibits space skipping from the open brace to the closing brace. | |
97 | The expression parses quoted strings. | |
98 | ||
99 | +(char_ - '"') | |
100 | ||
101 | parses one or more chars, except the double quote. It stops when it sees | |
102 | a double quote. | |
103 | ||
104 | [heading Difference] | |
105 | ||
106 | The expression: | |
107 | ||
108 | a - b | |
109 | ||
110 | parses `a` but not `b`. Its attribute is just `A`; the attribute of `a`. `b`'s | |
111 | attribute is ignored. Hence, the attribute of: | |
112 | ||
113 | char_ - '"' | |
114 | ||
115 | is just `char`. | |
116 | ||
117 | [heading Plus] | |
118 | ||
119 | +a | |
120 | ||
121 | is similar to Kleene star. Rather than match everything, `+a` matches one or more. | |
122 | Like it's related function, the Kleene star, its attribute is a `std::vector<A>` | |
123 | where `A` is the attribute of `a`. So, putting all these together, the attribute | |
124 | of | |
125 | ||
126 | +(char_ - '"') | |
127 | ||
128 | is then: | |
129 | ||
130 | std::vector<char> | |
131 | ||
132 | [heading Sequence Attribute] | |
133 | ||
134 | Now what's the attribute of | |
135 | ||
136 | '"' >> +(char_ - '"') >> '"' | |
137 | ||
138 | ? | |
139 | ||
140 | Well, typically, the attribute of: | |
141 | ||
142 | a >> b >> c | |
143 | ||
144 | is: | |
145 | ||
146 | fusion::vector<A, B, C> | |
147 | ||
148 | where `A` is the attribute of `a`, `B` is the attribute of `b` and `C` is the | |
149 | attribute of `c`. What is `fusion::vector`? - a tuple. | |
150 | ||
151 | [note If you don't know what I am talking about, see: [@http://tinyurl.com/6xun4j | |
152 | Fusion Vector]. It might be a good idea to have a look into __fusion__ at this | |
153 | point. You'll definitely see more of it in the coming pages.] | |
154 | ||
155 | [heading Attribute Collapsing] | |
156 | ||
157 | Some parsers, especially those very little literal parsers you see, like `'"'`, | |
158 | do not have attributes. | |
159 | ||
160 | Nodes without attributes are disregarded. In a sequence, like above, all nodes | |
161 | with no attributes are filtered out of the `fusion::vector`. So, since `'"'` has | |
162 | no attribute, and `+(char_ - '"')` has a `std::vector<char>` attribute, the | |
163 | whole expression's attribute should have been: | |
164 | ||
165 | fusion::vector<std::vector<char> > | |
166 | ||
167 | But wait, there's one more collapsing rule: If the attribute is followed by a | |
168 | single element `fusion::vector`, The element is stripped naked from its container. | |
169 | To make a long story short, the attribute of the expression: | |
170 | ||
171 | '"' >> +(char_ - '"') >> '"' | |
172 | ||
173 | is: | |
174 | ||
175 | std::vector<char> | |
176 | ||
177 | [heading Rule Definition] | |
178 | ||
179 | employee = | |
180 | lit("employee") | |
181 | >> '{' | |
182 | >> int_ >> ',' | |
183 | >> quoted_string >> ',' | |
184 | >> quoted_string >> ',' | |
185 | >> double_ | |
186 | >> '}' | |
187 | ; | |
188 | ||
189 | BOOST_SPIRIT_DEFINE(employee); | |
190 | ||
191 | ||
192 | Applying our collapsing rules above, the RHS has an attribute of: | |
193 | ||
194 | fusion::vector<int, std::string, std::string, double> | |
195 | ||
196 | These nodes do not have an attribute: | |
197 | ||
198 | * `lit("employee")` | |
199 | * `'{'` | |
200 | * `','` | |
201 | * `'}'` | |
202 | ||
203 | [note In case you are wondering, `lit("employee")` is the same as "employee". We | |
204 | had to wrap it inside `lit` because immediately after it is `>> '{'`. You can't | |
205 | right-shift a `char[]` and a `char` - you know, C++ syntax rules.] | |
206 | ||
207 | Recall that the attribute of `parser::employee` is the `ast::employee` struct. | |
208 | ||
209 | Now everything is clear, right? The `struct employee` *IS* compatible with | |
210 | `fusion::vector<int, std::string, std::string, double>`. So, the RHS of `start` | |
211 | uses start's attribute (a `struct employee`) in-situ when it does its work. | |
212 | ||
213 | [endsect] |