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