Calculator  Step 3
parse.cpp
Go to the documentation of this file.
1 #include <sstream>
2 
3 #include "node.hpp"
4 #include "parse.hpp"
5 
6 parser::parser(std::istream& input)
7 : input_(input),
8  ctype_(std::use_facet<std::ctype<char> >(input.getloc())),
9  token_(),
10  kind_()
11 {}
12 
13 std::string parser::charify(char c)
14 {
15  if (c == '\a') return R"('\a')";
16  if (c == '\b') return R"('\b')";
17  if (c == '\f') return R"('\f')";
18  if (c == '\n') return R"('\n')";
19  if (c == '\r') return R"('\r')";
20  if (c == '\t') return R"('\t')";
21  if (c == '\v') return R"('\v')";
22  if (c == '\'') return R"('\'')";
23  if (c == '\\') return R"('\\')";
24 
25  if (isprint(c))
26  return std::string{"\'"} + std::string(1,c) + "\'";
27  else {
28  std::ostringstream stream{};
29  stream << "'\\x" << std::hex;
30  stream.fill('0');
31  stream.width(2);
32  stream << (std::char_traits<char>::to_int_type(c) & 0xFF) << '\'';
33  return stream.str();
34  }
35 }
36 
37 void parser::get_identifier(std::string& identifier)
38 {
39  identifier.clear();
40  char c{};
41  if (not input_.get(c))
42  return;
43  if (not isalpha(c))
44  throw parse_error{"syntax error: expected alphabetic, got " + charify(c)};
45  identifier += c;
46  while (input_.get(c)) {
47  if (not isalnum(c)) {
48  input_.unget();
49  return;
50  }
51  identifier += c;
52  }
53  return;
54 }
55 
56 parser::kind parser::get_token(std::string& token)
57 {
58  if (not token_.empty())
59  {
60  token = token_;
61  kind result(kind_);
62  token_.clear();
63  kind_ = eof;
64  return result;
65  }
66 
67  char c{};
68  if (not (input_ >> c))
69  return eof;
70  if (isalpha(c)) {
71  input_.unget();
72  get_identifier(token);
73  return identifier;
74  }
75 
76  // Get a numeric literal.
77  token.clear();
78  if (c == '+' or c == '-' or c == '*' or c == '/' or c == '%' or c == '(' or c == ')' or c == '=') {
79  token += c;
80  return static_cast<kind>(c);
81  }
82 
83  if (c < '0' or c > '9') {
84  input_.unget();
85  throw parse_error{"syntax error: expected digit, got " + charify(c)};
86  }
87  while (c >= '0' and c <= '9') {
88  token += c;
89  if (not input_.get(c))
90  return number;
91  }
92  if (c == '.') {
93  token += c;
94  if (not input_.get(c))
95  throw parse_error{"unterminated number: expected digit after the decimal point"};
96  if (c < '0' or c > '9') {
97  input_.unget();
98  throw parse_error{"syntax error: expected digit after decimal point, got " + charify(c)};
99  }
100  while (c >= '0' and c <= '9') {
101  token += c;
102  if (not input_.get(c))
103  return number;
104  }
105  }
106  if (c == 'e' or c == 'E') {
107  token += c;
108  if (not input_.get(c))
109  throw parse_error{"unterminated number: expected digit in the exponent"};
110  if (c == '-' or c == '+') {
111  token += c;
112  if (not input_.get(c))
113  throw parse_error{"unterminated number: expected digit after sign in the exponent"};
114  }
115  if (c < '0' or c > '9') {
116  input_.unget();
117  throw parse_error{"syntax error: expected digit in the exponent, got " + charify(c)};
118  }
119  while (c >= '0' and c <= '9') {
120  token += c;
121  if (not input_.get(c))
122  return number;
123  }
124  }
125  input_.unget();
126  return number;
127 }
128 
129 bool parser::get_number(std::string const& token, node& result)
130 {
131  std::istringstream stream{token};
132  // If the value overflows or is otherwise invalid, return false.
133  double value;
134  if (not (stream >> value))
135  return false;
136  result = node(value);
137  return true;
138 }
139 
140 /* Parse an expression.
141  * EXPR ::= "var" IDENTIFIER ":=" ADD_EXPR | ADD_EXPR
142  */
143 bool parser::get_expr(node& result)
144 {
145  std::string token{};
146  kind k(get_token(token));
147  if (k == eof)
148  return false;
149 
150  if (k == identifier and token == "var") {
151  std::string name{};
152  // Define a variable.
153  k = get_token(name);
154  if (k != identifier)
155  throw parse_error{"syntax error: expected IDENTIFIER, but got " + name};
156  k = get_token(token);
157  if (k != '=')
158  throw parse_error{"syntax error: expected =, but got " + token};
159  if (not get_add_expr(result))
160  throw parse_error{"syntax error: expected additive-exprssion in assignment"};
161  result = node(node{std::move(name)}, result);
162  return true;
163  }
164 
165  if (k == identifier and token == "quit")
166  std::exit(0);
167 
168  push_back(token, k);
169  if (not get_add_expr(result))
170  throw parse_error{"syntax error: expected an additive-expression"};
171 
172  return true;
173 }
174 
175 /* Parse an addition expression
176  * ADD_EXPR ::= MUL_EXPR | ADD_EXPR + MUL_EXPR | ADD_EXPR - MUL_EXPR
177  */
179 {
180  if (not get_mul_expr(result))
181  return false;
182  std::string token{};
183  while (kind k = get_token(token)) {
184  if (k != '+' and k != '-') {
185  push_back(token, k);
186  return true;
187  } else {
188  node right{};
189  if (not get_mul_expr(right))
190  throw parse_error{"syntax error: unterminated expression. Expected a multiplicative-expression after " + token};
191  result = node(result, k, right);
192  }
193  }
194  return true;
195 }
196 
197 /* Parse a multiplicative expression.
198  * MUL_EXPR ::= UNARY | MUL_EXPR + UNARY | MUL_EXPR - UNARY
199  */
201 {
202  if (not get_unary(result))
203  return false;
204  std::string token{};
205  while (kind k = get_token(token)) {
206  if (k != '*' and k != '/') {
207  push_back(token, k);
208  return true;
209  } else {
210  node right{};
211  if (not get_unary(right))
212  throw parse_error{"syntax error: unterminated expression. Expected a unary-expression after " + token};
213  result = node(result, k, right);
214  }
215  }
216  return true;
217 }
218 
219 /* Parse a unary expression.
220  * UNARY ::= '-' PRIMARY | '+' PRIMARY | PRIMARY
221  */
222 bool parser::get_unary(node& result)
223 {
224  std::string token{};
225  if (kind k = get_token(token)) {
226  if (k == '-') {
227  if (not get_primary(result))
228  return false;
229  result = node(k, result);
230  return true;
231  } else if (k == '+') {
232  return get_primary(result);
233  } else {
234  push_back(token, k);
235  return get_primary(result);
236  }
237  }
238  return false;
239 }
240 
241 /* Parse a primary expression.
242  * PRIMARY ::= NUMBER | IDENTIFIER | '(' EXPR ')'
243  */
245 {
246  std::string token{};
247  if (kind k = get_token(token)) {
248  if (k == '(') {
249  if (not get_expr(result))
250  return false;
251  k = get_token(token);
252  if (k == eof)
253  throw parse_error{"syntax error: EOF when expecting ')'"};
254  else if (k != ')')
255  throw parse_error{"syntax error: expected ')', but got " + token};
256  else
257  return true;
258  } else if (k == number) {
259  if (not get_number(token, result))
260  throw parse_error{"Invalid numeric literal: " + token};
261  return true;
262  } else if (k == identifier) {
263  result = node{std::move(token)};
264  return true;
265  } else {
266  throw parse_error{"syntax error: expected a primary, but got " + token};
267  }
268  }
269  return false;
270 }
271 
272 void parse_loop(std::istream& input, std::ostream& output)
273 {
274  std::string line{};
275  // No portable way to test whether the console is an interactive terminal
276  // vs. a non-interactive file. If you have a system-specific way to test,
277  // output the prompt only for the interactive case.
278  for (output << "> "; std::getline(input, line); output << "> ") {
279  std::istringstream input{std::move(line)};
280  parser p{input};
281  try {
282  node n;
283  while (p.get_expr(n))
284  output << n.evaluate() << '\n';
285  } catch(parse_error const& ex) {
286  output << ex.what() << '\n';
287  } catch(std::exception const& ex) {
288  output << "exception: " << ex.what() << '\n';
289  }
290  }
291 }
void parse_loop(std::istream &input, std::ostream &output)
Definition: parse.cpp:272
bool isprint(char c) const
Definition: parse.hpp:139
bool get_expr(node &result)
Definition: parse.cpp:143
parser(std::istream &input)
Definition: parse.cpp:6
void get_identifier(std::string &identifier)
Definition: parse.cpp:37
std::string charify(char c)
Definition: parse.cpp:13
Definition: node.hpp:16
void push_back(std::string const &token, kind k)
Definition: parse.hpp:118
Definition: parse.hpp:30
bool get_primary(node &result)
Definition: parse.cpp:244
bool get_unary(node &result)
Definition: parse.cpp:222
bool isalpha(char c) const
Definition: parse.hpp:124
kind get_token(std::string &token)
Definition: parse.cpp:56
std::string token_
One token push-back.
Definition: parse.hpp:143
std::istream & input_
Share the input stream.
Definition: parse.hpp:141
kind kind_
The kind of token that was pushed back.
Definition: parse.hpp:144
double evaluate() const
Definition: node.cpp:50
kind
Definition: parse.hpp:36
bool get_number(std::string const &token, node &result)
Definition: parse.cpp:129
bool get_mul_expr(node &result)
Definition: parse.cpp:200
bool get_add_expr(node &result)
Definition: parse.cpp:178
bool isalnum(char c) const
Definition: parse.hpp:129