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