1 /*=============================================================================
2 Copyright (c) 2002-2018 Joel de Guzman
3
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 ///////////////////////////////////////////////////////////////////////////////
8 //
9 // Based on the employee parser (see employee.cpp), this example shows how
10 // to annotate the AST with the iterator positions for access to the source
11 // code when post processing. This example also shows how to "inject" client
12 // data, using the "with" directive, that the handlers can access.
13 //
14 // [ JDG May 9, 2007 ]
15 // [ JDG May 13, 2015 ] spirit X3
16 // [ JDG Feb 22, 2018 ] Parser annotations for spirit X3
17 //
18 // I would like to thank Rainbowverse, llc (https://primeorbial.com/)
19 // for sponsoring this work and donating it to the community.
20 //
21 ///////////////////////////////////////////////////////////////////////////////
22
23 #include <boost/config/warning_disable.hpp>
24 #include <boost/spirit/home/x3.hpp>
25 #include <boost/spirit/home/x3/support/ast/position_tagged.hpp>
26 #include <boost/fusion/include/adapt_struct.hpp>
27 #include <boost/fusion/include/io.hpp>
28
29 #include <iostream>
30 #include <string>
31
32 namespace client { namespace ast
33 {
34 ///////////////////////////////////////////////////////////////////////////
35 // Our AST (employee and person structs)
36 ///////////////////////////////////////////////////////////////////////////
37 namespace x3 = boost::spirit::x3;
38
39 struct person : x3::position_tagged
40 {
personclient::ast::person41 person(
42 std::string const& first_name = ""
43 , std::string const& last_name = ""
44 )
45 : first_name(first_name)
46 , last_name(last_name)
47 {}
48
49 std::string first_name, last_name;
50 };
51
52 struct employee : x3::position_tagged
53 {
54 int age;
55 person who;
56 double salary;
57 };
58
59 using boost::fusion::operator<<;
60 }}
61
62 // We need to tell fusion about our employee struct
63 // to make it a first-class fusion citizen. This has to
64 // be in global scope.
65
66 BOOST_FUSION_ADAPT_STRUCT(client::ast::person,
67 first_name, last_name
68 )
69
70 BOOST_FUSION_ADAPT_STRUCT(client::ast::employee,
71 age, who, salary
72 )
73
74 namespace client
75 {
76 namespace parser
77 {
78 namespace x3 = boost::spirit::x3;
79 namespace ascii = boost::spirit::x3::ascii;
80
81 ///////////////////////////////////////////////////////////////////////
82 // Our annotation handler
83 ///////////////////////////////////////////////////////////////////////
84
85 // tag used to get the position cache from the context
86 struct position_cache_tag;
87
88 struct annotate_position
89 {
90 template <typename T, typename Iterator, typename Context>
on_successclient::parser::annotate_position91 inline void on_success(Iterator const& first, Iterator const& last
92 , T& ast, Context const& context)
93 {
94 auto& position_cache = x3::get<position_cache_tag>(context).get();
95 position_cache.annotate(ast, first, last);
96 }
97 };
98
99 ///////////////////////////////////////////////////////////////////////
100 // Our employee parser
101 ///////////////////////////////////////////////////////////////////////
102
103 using x3::int_;
104 using x3::double_;
105 using x3::lexeme;
106 using ascii::char_;
107
108 struct quoted_string_class;
109 struct person_class;
110 struct employee_class;
111
112 x3::rule<quoted_string_class, std::string> const quoted_string = "quoted_string";
113 x3::rule<person_class, ast::person> const person = "person";
114 x3::rule<employee_class, ast::employee> const employee = "employee";
115
116 auto const quoted_string_def = lexeme['"' >> +(char_ - '"') >> '"'];
117 auto const person_def = quoted_string >> ',' >> quoted_string;
118
119 auto const employee_def =
120 '{'
121 >> int_ >> ','
122 >> person >> ','
123 >> double_
124 >> '}'
125 ;
126
127 auto const employees = employee >> *(',' >> employee);
128
129 BOOST_SPIRIT_DEFINE(quoted_string, person, employee);
130
131 struct quoted_string_class {};
132 struct person_class : annotate_position {};
133 struct employee_class : annotate_position {};
134 }
135 }
136
137 ///////////////////////////////////////////////////////////////////////////////
138 // Main program
139 ///////////////////////////////////////////////////////////////////////////////
140
141 ///////////////////////////////////////////////////////////////////////////////
142 // Our main parse entry point
143 ///////////////////////////////////////////////////////////////////////////////
144
145 using iterator_type = std::string::const_iterator;
146 using position_cache = boost::spirit::x3::position_cache<std::vector<iterator_type>>;
147
148 std::vector<client::ast::employee>
parse(std::string const & input,position_cache & positions)149 parse(std::string const& input, position_cache& positions)
150 {
151 using boost::spirit::x3::ascii::space;
152
153 std::vector<client::ast::employee> ast;
154 iterator_type iter = input.begin();
155 iterator_type const end = input.end();
156
157 using boost::spirit::x3::with;
158
159 // Our parser
160 using client::parser::employees;
161 using client::parser::position_cache_tag;
162
163 auto const parser =
164 // we pass our position_cache to the parser so we can access
165 // it later in our on_sucess handlers
166 with<position_cache_tag>(std::ref(positions))
167 [
168 employees
169 ];
170
171 bool r = phrase_parse(iter, end, parser, space, ast);
172
173 if (r && iter == end)
174 {
175 std::cout << boost::fusion::tuple_open('[');
176 std::cout << boost::fusion::tuple_close(']');
177 std::cout << boost::fusion::tuple_delimiter(", ");
178
179 std::cout << "-------------------------\n";
180 std::cout << "Parsing succeeded\n";
181
182 for (auto const& emp : ast)
183 {
184 std::cout << "got: " << emp << std::endl;
185 }
186 std::cout << "\n-------------------------\n";
187
188 }
189 else
190 {
191 std::cout << "-------------------------\n";
192 std::cout << "Parsing failed\n";
193 std::cout << "-------------------------\n";
194 ast.clear();
195 }
196 return ast;
197 }
198
199 // Sample input:
200
201 std::string input = R"(
202 {
203 23,
204 "Amanda",
205 "Stefanski",
206 1000.99
207 },
208 {
209 35,
210 "Angie",
211 "Chilcote",
212 2000.99
213 },
214 {
215 43,
216 "Dannie",
217 "Dillinger",
218 3000.99
219 },
220 {
221 22,
222 "Dorene",
223 "Dole",
224 2500.99
225 },
226 {
227 38,
228 "Rossana",
229 "Rafferty",
230 5000.99
231 }
232 )";
233
234 int
main()235 main()
236 {
237 position_cache positions{input.begin(), input.end()};
238 auto ast = parse(input, positions);
239
240 // Get the source of the 2nd employee and print it
241 auto pos = positions.position_of(ast[1]); // zero based of course!
242 std::cout << "Here's the 2nd employee:" << std::endl;
243 std::cout << std::string(pos.begin(), pos.end()) << std::endl;
244 std::cout << "-------------------------\n";
245 return 0;
246 }
247