parser.cpp 2.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122
  1. // Copyright Oliver Kowalke 2014.
  2. // Distributed under the Boost Software License, Version 1.0.
  3. // (See accompanying file LICENSE_1_0.txt or copy at
  4. // http://www.boost.org/LICENSE_1_0.txt)
  5. #include <cctype>
  6. #include <cstdio>
  7. #include <exception>
  8. #include <functional>
  9. #include <iostream>
  10. #include <sstream>
  11. #include <boost/coroutine2/all.hpp>
  12. class parser_error : public std::runtime_error {
  13. public:
  14. parser_error() :
  15. std::runtime_error("parsing failed") {
  16. }
  17. };
  18. /*
  19. * grammar:
  20. * P ---> E '\0'
  21. * E ---> T {('+'|'-') T}
  22. * T ---> S {('*'|'/') S}
  23. * S ---> digit | '(' E ')'
  24. */
  25. class Parser{
  26. char next;
  27. std::istream& is;
  28. std::function<void(char)> cb;
  29. char pull(){
  30. return std::char_traits<char>::to_char_type(is.get());
  31. }
  32. void scan(){
  33. do{
  34. next=pull();
  35. }
  36. while(isspace(next));
  37. }
  38. public:
  39. Parser(std::istream& is_,std::function<void(char)> cb_) :
  40. next(), is(is_), cb(cb_)
  41. {}
  42. void run() {
  43. scan();
  44. E();
  45. }
  46. private:
  47. void E(){
  48. T();
  49. while (next=='+'||next=='-'){
  50. cb(next);
  51. scan();
  52. T();
  53. }
  54. }
  55. void T(){
  56. S();
  57. while (next=='*'||next=='/'){
  58. cb(next);
  59. scan();
  60. S();
  61. }
  62. }
  63. void S(){
  64. if (std::isdigit(next)){
  65. cb(next);
  66. scan();
  67. }
  68. else if(next=='('){
  69. cb(next);
  70. scan();
  71. E();
  72. if (next==')'){
  73. cb(next);
  74. scan();
  75. }else{
  76. throw parser_error();
  77. }
  78. }
  79. else{
  80. throw parser_error();
  81. }
  82. }
  83. };
  84. typedef boost::coroutines2::coroutine< char > coro_t;
  85. int main() {
  86. try {
  87. std::istringstream is("1+1");
  88. // invert control flow
  89. coro_t::pull_type seq(
  90. [&is]( coro_t::push_type & yield) {
  91. Parser p( is,
  92. [&yield](char ch){
  93. yield(ch);
  94. });
  95. p.run();
  96. });
  97. // user-code pulls parsed data from parser
  98. for(char c:seq){
  99. printf("Parsed: %c\n",c);
  100. }
  101. std::cout << "\nDone" << std::endl;
  102. return EXIT_SUCCESS;
  103. } catch ( std::exception const& ex) {
  104. std::cerr << "exception: " << ex.what() << std::endl;
  105. }
  106. return EXIT_FAILURE;
  107. }