error_handling.cpp 7.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277
  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 implement error handling. This example also shows how to "inject" client
  10. // data, using the "with" directive, that the handlers can access.
  11. //
  12. // [ JDG May 9, 2007 ]
  13. // [ JDG May 13, 2015 ] spirit X3
  14. // [ JDG Feb 19, 2018 ] Error handling for spirit X3
  15. //
  16. // I would like to thank Rainbowverse, llc (https://primeorbial.com/)
  17. // for sponsoring this work and donating it to the community.
  18. //
  19. ///////////////////////////////////////////////////////////////////////////////
  20. #include <boost/config/warning_disable.hpp>
  21. #include <boost/spirit/home/x3.hpp>
  22. #include <boost/spirit/home/x3/support/ast/position_tagged.hpp>
  23. #include <boost/spirit/home/x3/support/utility/error_reporting.hpp>
  24. #include <boost/spirit/home/x3/support/utility/annotate_on_success.hpp>
  25. #include <boost/fusion/include/adapt_struct.hpp>
  26. #include <boost/fusion/include/io.hpp>
  27. #include <iostream>
  28. #include <string>
  29. namespace client { namespace ast
  30. {
  31. ///////////////////////////////////////////////////////////////////////////
  32. // Our AST (employee and person structs)
  33. ///////////////////////////////////////////////////////////////////////////
  34. namespace x3 = boost::spirit::x3;
  35. struct person : x3::position_tagged
  36. {
  37. person(
  38. std::string const& first_name = ""
  39. , std::string const& last_name = ""
  40. )
  41. : first_name(first_name)
  42. , last_name(last_name)
  43. {}
  44. std::string first_name, last_name;
  45. };
  46. struct employee : x3::position_tagged
  47. {
  48. int age;
  49. person who;
  50. double salary;
  51. };
  52. using boost::fusion::operator<<;
  53. }}
  54. // We need to tell fusion about our employee struct
  55. // to make it a first-class fusion citizen. This has to
  56. // be in global scope.
  57. BOOST_FUSION_ADAPT_STRUCT(client::ast::person,
  58. first_name, last_name
  59. )
  60. BOOST_FUSION_ADAPT_STRUCT(client::ast::employee,
  61. age, who, salary
  62. )
  63. namespace client
  64. {
  65. namespace parser
  66. {
  67. namespace x3 = boost::spirit::x3;
  68. namespace ascii = boost::spirit::x3::ascii;
  69. ///////////////////////////////////////////////////////////////////////
  70. // Our error handler
  71. ///////////////////////////////////////////////////////////////////////
  72. struct error_handler
  73. {
  74. template <typename Iterator, typename Exception, typename Context>
  75. x3::error_handler_result on_error(
  76. Iterator& first, Iterator const& last
  77. , Exception const& x, Context const& context)
  78. {
  79. auto& error_handler = x3::get<x3::error_handler_tag>(context).get();
  80. std::string message = "Error! Expecting: " + x.which() + " here:";
  81. error_handler(x.where(), message);
  82. return x3::error_handler_result::fail;
  83. }
  84. };
  85. ///////////////////////////////////////////////////////////////////////
  86. // Our employee parser
  87. ///////////////////////////////////////////////////////////////////////
  88. using x3::int_;
  89. using x3::double_;
  90. using x3::lexeme;
  91. using ascii::char_;
  92. struct quoted_string_class;
  93. struct person_class;
  94. struct employee_class;
  95. x3::rule<quoted_string_class, std::string> const quoted_string = "quoted_string";
  96. x3::rule<person_class, ast::person> const person = "person";
  97. x3::rule<employee_class, ast::employee> const employee = "employee";
  98. auto const quoted_string_def = lexeme['"' >> +(char_ - '"') >> '"'];
  99. auto const person_def = quoted_string > ',' > quoted_string;
  100. auto const employee_def =
  101. '{'
  102. > int_ > ','
  103. > person > ','
  104. > double_
  105. > '}'
  106. ;
  107. auto const employees = employee >> *(',' >> employee);
  108. BOOST_SPIRIT_DEFINE(quoted_string, person, employee);
  109. struct quoted_string_class {};
  110. struct person_class : x3::annotate_on_success {};
  111. struct employee_class : error_handler, x3::annotate_on_success {};
  112. }
  113. }
  114. ///////////////////////////////////////////////////////////////////////////////
  115. // Main program
  116. ///////////////////////////////////////////////////////////////////////////////
  117. ///////////////////////////////////////////////////////////////////////////////
  118. // Our main parse entry point
  119. ///////////////////////////////////////////////////////////////////////////////
  120. void parse(std::string const& input)
  121. {
  122. using boost::spirit::x3::ascii::space;
  123. typedef std::string::const_iterator iterator_type;
  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. using boost::spirit::x3::error_handler_tag;
  129. using error_handler_type = boost::spirit::x3::error_handler<iterator_type>;
  130. // Our error handler
  131. error_handler_type error_handler(iter, end, std::cerr);
  132. // Our parser
  133. using client::parser::employees;
  134. auto const parser =
  135. // we pass our error handler to the parser so we can access
  136. // it later in our on_error and on_sucess handlers
  137. with<error_handler_tag>(std::ref(error_handler))
  138. [
  139. employees
  140. ];
  141. bool r = phrase_parse(iter, end, parser, space, ast);
  142. if (r && iter == end)
  143. {
  144. std::cout << boost::fusion::tuple_open('[');
  145. std::cout << boost::fusion::tuple_close(']');
  146. std::cout << boost::fusion::tuple_delimiter(", ");
  147. std::cout << "-------------------------\n";
  148. std::cout << "Parsing succeeded\n";
  149. for (auto const& emp : ast)
  150. {
  151. std::cout << "got: " << emp << std::endl;
  152. }
  153. std::cout << "\n-------------------------\n";
  154. }
  155. else
  156. {
  157. std::cout << "-------------------------\n";
  158. std::cout << "Parsing failed\n";
  159. std::cout << "-------------------------\n";
  160. ast.clear();
  161. }
  162. }
  163. // Good sample:
  164. std::string good_input = R"(
  165. {
  166. 23,
  167. "Amanda",
  168. "Stefanski",
  169. 1000.99
  170. },
  171. {
  172. 35,
  173. "Angie",
  174. "Chilcote",
  175. 2000.99
  176. },
  177. {
  178. 43,
  179. "Dannie",
  180. "Dillinger",
  181. 3000.99
  182. },
  183. {
  184. 22,
  185. "Dorene",
  186. "Dole",
  187. 2500.99
  188. },
  189. {
  190. 38,
  191. "Rossana",
  192. "Rafferty",
  193. 5000.99
  194. }
  195. )";
  196. // Input sample with error:
  197. std::string bad_input = R"(
  198. {
  199. 23,
  200. "Amanda",
  201. "Stefanski",
  202. 1000.99
  203. },
  204. {
  205. 35,
  206. "Angie",
  207. "Chilcote",
  208. 2000.99
  209. },
  210. {
  211. 43,
  212. 'I am not a person!' <--- this should be a person
  213. 3000.99
  214. },
  215. {
  216. 22,
  217. "Dorene",
  218. "Dole",
  219. 2500.99
  220. },
  221. {
  222. 38,
  223. "Rossana",
  224. "Rafferty",
  225. 5000.99
  226. }
  227. )";
  228. int
  229. main()
  230. {
  231. // Good input
  232. parse(good_input);
  233. // Bad input
  234. std::cout << "Now we have some errors" << std::endl;
  235. parse(bad_input);
  236. return 0;
  237. }