toy_spirit2.cpp 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466
  1. ///////////////////////////////////////////////////////////////////////////////
  2. // toy_spirit3.cpp
  3. //
  4. // Copyright 2008 Eric Niebler. Distributed under the Boost
  5. // Software License, Version 1.0. (See accompanying file
  6. // LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
  7. #include <cctype>
  8. #include <string>
  9. #include <cstring>
  10. #include <iomanip>
  11. #include <iostream>
  12. #include <boost/assert.hpp>
  13. #include <boost/mpl/assert.hpp>
  14. #include <boost/utility/result_of.hpp>
  15. #include <boost/type_traits/is_same.hpp>
  16. #include <boost/proto/core.hpp>
  17. #include <boost/proto/transform.hpp>
  18. #include <boost/fusion/include/for_each.hpp>
  19. #include <boost/fusion/include/fold.hpp>
  20. #include <boost/fusion/include/cons.hpp>
  21. #include <boost/fusion/include/any.hpp>
  22. #include <boost/test/unit_test.hpp>
  23. namespace boost
  24. {
  25. // global tags
  26. struct char_tag {};
  27. struct space_tag {};
  28. // global primitives
  29. proto::terminal<char_tag>::type const char_ = {{}};
  30. proto::terminal<space_tag>::type const space = {{}};
  31. using proto::lit;
  32. using proto::literal;
  33. }
  34. namespace boost { namespace spirit2
  35. {
  36. namespace utility
  37. {
  38. inline bool char_icmp(char ch, char lo, char hi)
  39. {
  40. return ch == lo || ch == hi;
  41. }
  42. template<typename FwdIter>
  43. inline bool string_cmp(char const *sz, FwdIter &begin, FwdIter end)
  44. {
  45. FwdIter tmp = begin;
  46. for(; *sz; ++tmp, ++sz)
  47. if(tmp == end || *tmp != *sz)
  48. return false;
  49. begin = tmp;
  50. return true;
  51. }
  52. template<typename FwdIter>
  53. inline bool string_icmp(std::string const &str, FwdIter &begin, FwdIter end)
  54. {
  55. BOOST_ASSERT(0 == str.size() % 2);
  56. FwdIter tmp = begin;
  57. std::string::const_iterator istr = str.begin(), estr = str.end();
  58. for(; istr != estr; ++tmp, istr += 2)
  59. if(tmp == end || (*tmp != *istr && *tmp != *(istr+1)))
  60. return false;
  61. begin = tmp;
  62. return true;
  63. }
  64. inline bool in_range(char ch, char lo, char hi)
  65. {
  66. return ch >= lo && ch <= hi;
  67. }
  68. inline bool in_irange(char ch, char lo, char hi)
  69. {
  70. return in_range(ch, lo, hi)
  71. || in_range(std::tolower(ch), lo, hi)
  72. || in_range(std::toupper(ch), lo, hi);
  73. }
  74. inline std::string to_istr(char const *sz)
  75. {
  76. std::string res;
  77. res.reserve(std::strlen(sz) * 2);
  78. for(; *sz; ++sz)
  79. {
  80. res.push_back(std::tolower(*sz));
  81. res.push_back(std::toupper(*sz));
  82. }
  83. return res;
  84. }
  85. } // namespace utility
  86. template<typename List>
  87. struct alternate
  88. {
  89. explicit alternate(List const &list)
  90. : elems(list)
  91. {}
  92. List elems;
  93. };
  94. template<typename List>
  95. struct sequence
  96. {
  97. explicit sequence(List const &list)
  98. : elems(list)
  99. {}
  100. List elems;
  101. };
  102. struct char_range
  103. : std::pair<char, char>
  104. {
  105. char_range(char from, char to)
  106. : std::pair<char, char>(from, to)
  107. {}
  108. };
  109. struct ichar
  110. {
  111. ichar(char ch)
  112. : lo_(std::tolower(ch))
  113. , hi_(std::toupper(ch))
  114. {}
  115. char lo_, hi_;
  116. };
  117. struct istr
  118. {
  119. istr(char const *sz)
  120. : str_(utility::to_istr(sz))
  121. {}
  122. std::string str_;
  123. };
  124. struct ichar_range
  125. : std::pair<char, char>
  126. {
  127. ichar_range(char from, char to)
  128. : std::pair<char, char>(from, to)
  129. {}
  130. };
  131. // The no-case directive
  132. struct no_case_tag {};
  133. struct True : mpl::true_ {};
  134. ///////////////////////////////////////////////////////////////////////////////
  135. /// Begin Spirit grammar here
  136. ///////////////////////////////////////////////////////////////////////////////
  137. namespace grammar
  138. {
  139. using namespace proto;
  140. using namespace fusion;
  141. struct SpiritExpr;
  142. struct AnyChar
  143. : terminal<char_tag>
  144. {};
  145. struct CharLiteral
  146. : terminal<char>
  147. {};
  148. struct NTBSLiteral
  149. : terminal<char const *>
  150. {};
  151. struct CharParser
  152. : proto::function<AnyChar, CharLiteral>
  153. {};
  154. struct CharRangeParser
  155. : proto::function<AnyChar, CharLiteral, CharLiteral>
  156. {};
  157. struct NoCase
  158. : terminal<no_case_tag>
  159. {};
  160. // The data determines the case-sensitivity of the terminals
  161. typedef _data _icase;
  162. // Ugh, would be nice to find a work-around for this:
  163. #if BOOST_WORKAROUND(BOOST_MSVC, == 1310)
  164. #define _value(x) call<_value(x)>
  165. #define True() make<True()>
  166. #endif
  167. // Extract the child from terminals
  168. struct SpiritTerminal
  169. : or_<
  170. when< AnyChar, _value >
  171. , when< CharLiteral, if_<_icase, ichar(_value), _value> >
  172. , when< CharParser, if_<_icase, ichar(_value(_child1)), _value(_child1)> > // char_('a')
  173. , when< NTBSLiteral, if_<_icase, istr(_value), char const*(_value)> >
  174. , when< CharRangeParser, if_<_icase
  175. , ichar_range(_value(_child1), _value(_child2))
  176. , char_range(_value(_child1), _value(_child2))> > // char_('a','z')
  177. >
  178. {};
  179. struct FoldToList
  180. : reverse_fold_tree<_, nil(), cons<SpiritExpr, _state>(SpiritExpr, _state)>
  181. {};
  182. // sequence rule folds all >>'s together into a list
  183. // and wraps the result in a sequence<> wrapper
  184. struct SpiritSequence
  185. : when< shift_right<SpiritExpr, SpiritExpr>, sequence<FoldToList>(FoldToList) >
  186. {};
  187. // alternate rule folds all |'s together into a list
  188. // and wraps the result in a alternate<> wrapper
  189. struct SpiritAlternate
  190. : when< bitwise_or<SpiritExpr, SpiritExpr>, alternate<FoldToList>(FoldToList) >
  191. {};
  192. // Directives such as no_case are handled here
  193. struct SpiritDirective
  194. : when< subscript<NoCase, SpiritExpr>, SpiritExpr(_right, _state, True()) >
  195. {};
  196. // A SpiritExpr is an alternate, a sequence, a directive or a terminal
  197. struct SpiritExpr
  198. : or_<
  199. SpiritSequence
  200. , SpiritAlternate
  201. , SpiritDirective
  202. , SpiritTerminal
  203. >
  204. {};
  205. } // namespace grammar
  206. using grammar::SpiritExpr;
  207. using grammar::NoCase;
  208. ///////////////////////////////////////////////////////////////////////////////
  209. /// End SpiritExpr
  210. ///////////////////////////////////////////////////////////////////////////////
  211. // Globals
  212. NoCase::type const no_case = {{}};
  213. template<typename Iterator>
  214. struct parser;
  215. template<typename Iterator>
  216. struct fold_alternate
  217. {
  218. parser<Iterator> const &parse;
  219. explicit fold_alternate(parser<Iterator> const &p)
  220. : parse(p)
  221. {}
  222. template<typename T>
  223. bool operator ()(T const &t) const
  224. {
  225. Iterator tmp = this->parse.first;
  226. if(this->parse(t))
  227. return true;
  228. this->parse.first = tmp;
  229. return false;
  230. }
  231. };
  232. template<typename Iterator>
  233. struct fold_sequence
  234. {
  235. parser<Iterator> const &parse;
  236. explicit fold_sequence(parser<Iterator> const &p)
  237. : parse(p)
  238. {}
  239. typedef bool result_type;
  240. template<typename T>
  241. bool operator ()(bool success, T const &t) const
  242. {
  243. return success && this->parse(t);
  244. }
  245. };
  246. template<typename Iterator>
  247. struct parser
  248. {
  249. mutable Iterator first;
  250. Iterator second;
  251. parser(Iterator begin, Iterator end)
  252. : first(begin)
  253. , second(end)
  254. {}
  255. bool done() const
  256. {
  257. return this->first == this->second;
  258. }
  259. template<typename List>
  260. bool operator ()(alternate<List> const &alternates) const
  261. {
  262. return fusion::any(alternates.elems, fold_alternate<Iterator>(*this));
  263. }
  264. template<typename List>
  265. bool operator ()(sequence<List> const &sequence) const
  266. {
  267. return fusion::fold(sequence.elems, true, fold_sequence<Iterator>(*this));
  268. }
  269. bool operator ()(char_tag ch) const
  270. {
  271. if(this->done())
  272. return false;
  273. ++this->first;
  274. return true;
  275. }
  276. bool operator ()(char ch) const
  277. {
  278. if(this->done() || ch != *this->first)
  279. return false;
  280. ++this->first;
  281. return true;
  282. }
  283. bool operator ()(ichar ich) const
  284. {
  285. if(this->done() || !utility::char_icmp(*this->first, ich.lo_, ich.hi_))
  286. return false;
  287. ++this->first;
  288. return true;
  289. }
  290. bool operator ()(char const *sz) const
  291. {
  292. return utility::string_cmp(sz, this->first, this->second);
  293. }
  294. bool operator ()(istr const &s) const
  295. {
  296. return utility::string_icmp(s.str_, this->first, this->second);
  297. }
  298. bool operator ()(char_range rng) const
  299. {
  300. if(this->done() || !utility::in_range(*this->first, rng.first, rng.second))
  301. return false;
  302. ++this->first;
  303. return true;
  304. }
  305. bool operator ()(ichar_range rng) const
  306. {
  307. if(this->done() || !utility::in_irange(*this->first, rng.first, rng.second))
  308. return false;
  309. ++this->first;
  310. return true;
  311. }
  312. };
  313. template<typename Rule, typename Iterator>
  314. typename enable_if<proto::matches< Rule, SpiritExpr >, bool >::type
  315. parse_impl(Rule const &rule, Iterator begin, Iterator end)
  316. {
  317. mpl::false_ is_case_sensitive;
  318. parser<Iterator> parse_fun(begin, end);
  319. return parse_fun(SpiritExpr()(rule, proto::ignore(), is_case_sensitive));
  320. }
  321. // 2nd overload provides a short error message for invalid rules
  322. template<typename Rule, typename Iterator>
  323. typename disable_if<proto::matches< Rule, SpiritExpr >, bool >::type
  324. parse_impl(Rule const &rule, Iterator begin, Iterator end)
  325. {
  326. BOOST_MPL_ASSERT((proto::matches<Rule, SpiritExpr>));
  327. return false;
  328. }
  329. // parse() converts rule literals to proto expressions if necessary
  330. // and dispatches to parse_impl
  331. template<typename Rule, typename Iterator>
  332. bool parse(Rule const &rule, Iterator begin, Iterator end)
  333. {
  334. return parse_impl(proto::as_expr(rule), begin, end);
  335. }
  336. }}
  337. void test_toy_spirit3()
  338. {
  339. using boost::spirit2::no_case;
  340. using boost::char_;
  341. std::string hello("abcd");
  342. BOOST_CHECK(
  343. boost::spirit2::parse(
  344. "abcd"
  345. , hello.begin()
  346. , hello.end()
  347. )
  348. );
  349. BOOST_CHECK(
  350. boost::spirit2::parse(
  351. char_ >> char_('b') >> 'c' >> char_
  352. , hello.begin()
  353. , hello.end()
  354. )
  355. );
  356. BOOST_CHECK(
  357. !boost::spirit2::parse(
  358. char_ >> char_('b') >> 'c' >> 'D'
  359. , hello.begin()
  360. , hello.end()
  361. )
  362. );
  363. BOOST_CHECK(
  364. boost::spirit2::parse(
  365. char_ >> char_('b') >> 'c' >> 'e'
  366. | char_ >> no_case[char_('B') >> "C" >> char_('D','Z')]
  367. , hello.begin()
  368. , hello.end()
  369. )
  370. );
  371. std::string nest_alt_input("abd");
  372. BOOST_CHECK(
  373. boost::spirit2::parse(
  374. char_('a')
  375. >> ( char_('b')
  376. | char_('c')
  377. )
  378. >> char_('d')
  379. , nest_alt_input.begin()
  380. , nest_alt_input.end()
  381. )
  382. );
  383. }
  384. using namespace boost::unit_test;
  385. ///////////////////////////////////////////////////////////////////////////////
  386. // init_unit_test_suite
  387. //
  388. test_suite* init_unit_test_suite( int argc, char* argv[] )
  389. {
  390. test_suite *test = BOOST_TEST_SUITE("test proto, grammars and tree transforms");
  391. test->add(BOOST_TEST_CASE(&test_toy_spirit3));
  392. return test;
  393. }