parser.cpp 2.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127
  1. // Copyright Oliver Kowalke 2016.
  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 <cstdio>
  6. #include <cstdlib>
  7. #include <exception>
  8. #include <functional>
  9. #include <iostream>
  10. #include <memory>
  11. #include <sstream>
  12. #include <boost/context/fiber.hpp>
  13. namespace ctx = boost::context;
  14. /*
  15. * grammar:
  16. * P ---> E '\0'
  17. * E ---> T {('+'|'-') T}
  18. * T ---> S {('*'|'/') S}
  19. * S ---> digit | '(' E ')'
  20. */
  21. class Parser{
  22. char next;
  23. std::istream& is;
  24. std::function<void(char)> cb;
  25. char pull(){
  26. return std::char_traits<char>::to_char_type(is.get());
  27. }
  28. void scan(){
  29. do{
  30. next=pull();
  31. }
  32. while(isspace(next));
  33. }
  34. public:
  35. Parser(std::istream& is_,std::function<void(char)> cb_) :
  36. next(), is(is_), cb(cb_)
  37. {}
  38. void run() {
  39. scan();
  40. E();
  41. }
  42. private:
  43. void E(){
  44. T();
  45. while (next=='+'||next=='-'){
  46. cb(next);
  47. scan();
  48. T();
  49. }
  50. }
  51. void T(){
  52. S();
  53. while (next=='*'||next=='/'){
  54. cb(next);
  55. scan();
  56. S();
  57. }
  58. }
  59. void S(){
  60. if (isdigit(next)){
  61. cb(next);
  62. scan();
  63. }
  64. else if(next=='('){
  65. cb(next);
  66. scan();
  67. E();
  68. if (next==')'){
  69. cb(next);
  70. scan();
  71. }else{
  72. throw std::runtime_error("parsing failed");
  73. }
  74. }
  75. else{
  76. throw std::runtime_error("parsing failed");
  77. }
  78. }
  79. };
  80. int main() {
  81. try {
  82. std::istringstream is("1+1");
  83. // user-code pulls parsed data from parser
  84. // invert control flow
  85. char c;
  86. bool done = false;
  87. // execute parser in new execution context
  88. ctx::fiber source{[&is,&c,&done](ctx::fiber && sink){
  89. // create parser with callback function
  90. Parser p( is,
  91. [&sink,&c](char c_){
  92. // resume main execution context
  93. c = c_;
  94. sink = std::move( sink).resume();
  95. });
  96. // start recursive parsing
  97. p.run();
  98. // signal termination
  99. done = true;
  100. // resume main execution context
  101. return std::move(sink);
  102. }};
  103. source = std::move( source).resume();
  104. while(!done){
  105. printf("Parsed: %c\n",c);
  106. source = std::move( source).resume();
  107. }
  108. std::cout << "main: done" << std::endl;
  109. return EXIT_SUCCESS;
  110. } catch (std::exception const& e) {
  111. std::cerr << "exception: " << e.what() << std::endl;
  112. }
  113. return EXIT_FAILURE;
  114. }