annotation.cpp 6.9 KB

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