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

source code of boost/libs/spirit/example/x3/annotation.cpp