Calculator  Step 4
parse.cpp
Go to the documentation of this file.
1 #include <cstdlib>
2 #include <iterator>
3 #include <sstream>
4 
5 #include "calc_error.hpp"
6 #include "node.hpp"
7 #include "parse.hpp"
8 #include "variables.hpp"
9 
10 parser::parser(std::istream& input)
11 : input_(input),
12  ctype_(std::use_facet<std::ctype<char> >(input.getloc())),
13  token_(),
14  kind_()
15 {}
16 
17 std::string parser::charify(char c)
18 {
19  if (c == '\a') return R"('\a')";
20  if (c == '\b') return R"('\b')";
21  if (c == '\f') return R"('\f')";
22  if (c == '\n') return R"('\n')";
23  if (c == '\r') return R"('\r')";
24  if (c == '\t') return R"('\t')";
25  if (c == '\v') return R"('\v')";
26  if (c == '\'') return R"('\'')";
27  if (c == '\\') return R"('\\')";
28 
29  if (isprint(c))
30  return std::string{"\'"} + std::string(1,c) + "\'";
31  else {
32  std::ostringstream stream{};
33  stream << "'\\x" << std::hex;
34  stream.fill('0');
35  stream.width(2);
36  stream << (std::char_traits<char>::to_int_type(c) & 0xFF) << '\'';
37  return stream.str();
38  }
39 }
40 
41 void parser::get_identifier(std::string& identifier)
42 {
43  identifier.clear();
44  char c{};
45  if (not input_.get(c))
46  return;
47  if (not isalpha(c))
48  throw syntax_error("expected alphabetic, got " + charify(c));
49  identifier += c;
50  while (input_.get(c)) {
51  if (not isalnum(c)) {
52  input_.unget();
53  return;
54  }
55  identifier += c;
56  }
57  return;
58 }
59 
60 void parser::push_back(std::string const& token, kind k)
61 {
62  kind_ = k;
63  if (kind_ == eof)
64  token_ = "end of line";
65  else
66  token_ = token;
67 }
68 
69 parser::kind parser::get_token(std::string& token)
70 {
71  if (not token_.empty())
72  {
73  kind result{kind_};
74  token = token_;
75 
76  token_.clear();
77  kind_ = eof;
78 
79  return result;
80  }
81 
82  char c{};
83  if (not (input_ >> c)) {
84  token = "end of line";
85  return eof;
86  }
87  if (isalpha(c)) {
88  input_.unget();
89  get_identifier(token);
90  return identifier;
91  }
92 
93  // Get a numeric literal.
94  token.clear();
95  if (c == '+' or c == '-' or c == '*' or c == '/' or c == '%' or c == '(' or c == ')' or c == '=' or c == ',') {
96  token += c;
97  return kind(c);
98  }
99 
100  if (c < '0' or c > '9') {
101  input_.unget();
102  throw syntax_error{"expected digit, got " + charify(c)};
103  }
104  while (c >= '0' and c <= '9') {
105  token += c;
106  if (not input_.get(c))
107  return number;
108  }
109  if (c == '.') {
110  token += c;
111  if (not input_.get(c))
112  throw syntax_error{"unterminated number: expected digit after the decimal point"};
113  if (c < '0' or c > '9') {
114  input_.unget();
115  throw syntax_error{"expected digit after decimal point, got " + charify(c)};
116  }
117  while (c >= '0' and c <= '9') {
118  token += c;
119  if (not input_.get(c))
120  return number;
121  }
122  }
123  if (c == 'e' or c == 'E') {
124  token += c;
125  if (not input_.get(c))
126  throw syntax_error{"unterminated number: expected digit in the exponent"};
127  if (c == '-' or c == '+') {
128  token += c;
129  if (not input_.get(c))
130  throw syntax_error{"unterminated number: expected digit after sign in the exponent"};
131  }
132  if (c < '0' or c > '9') {
133  input_.unget();
134  throw syntax_error{"expected digit in the exponent, got " + charify(c)};
135  }
136  while (c >= '0' and c <= '9') {
137  token += c;
138  if (not input_.get(c))
139  return number;
140  }
141  }
142  input_.unget();
143  return number;
144 }
145 
146 bool parser::get_number(std::string const& token, node& result)
147 {
148  std::istringstream stream{token};
149  // If the value overflows or is otherwise invalid, return false.
150  double value{};
151  if (not (stream >> value))
152  return false;
153  result = node(value);
154  return true;
155 }
156 
157 void parser::get_definition(std::string& name, identifier_list& parameters, node& definition)
158 {
159  // Define a variable.
160  kind k{get_token(name)};
161  if (k != identifier)
162  throw syntax_error{"expected IDENTIFIER, got " + name};
163 
164  std::string token;
165  k = get_token(token);
166  if (k == '(') {
167  get_namelist(std::back_inserter(parameters));
168  k = get_token(token);
169  }
170 
171  if (k != '=')
172  throw syntax_error{"expected = in definition, got " + token};
173 
174  if (not get_expr(definition))
175  throw syntax_error{"expected exprssion in assignment"};
176 }
177 
178 bool parser::get_statement(std::ostream& output)
179 {
180  std::string token{};
181  kind k(get_token(token));
182  if (k == eof)
183  return false;
184 
185  if (k == identifier and token == "def") {
186  node definition{};
187  identifier_list parameters{};
188  get_definition(token, parameters, definition);
189  set_function(token, node{std::move(parameters), definition});
190  return true;
191  }
192 
193  if (k == identifier and token == "quit")
194  std::exit(0);
195 
196  // Otherwise, the statement must be an expression.
197  push_back(token, k);
198  node n{};
199  if (not get_expr(n))
200  return false;
201  else {
202  // Evaluate the expression and print the result.
203  output << n.evaluate() << '\n';
204  return true;
205  }
206 }
207 
208 bool parser::get_expr(node& result)
209 {
210  return get_add_expr(result);
211 }
212 
214 {
215  if (not get_mul_expr(result))
216  return false;
217  std::string token{};
218  while (kind k = get_token(token)) {
219  if (k != '+' and k != '-') {
220  push_back(token, k);
221  return true;
222  } else {
223  node right{};
224  if (not get_mul_expr(right))
225  throw syntax_error{"unterminated expression. Expected a multiplicative-expression after " + token};
226  result = node(result, k, right);
227  }
228  }
229  return true;
230 }
231 
233 {
234  if (not get_unary(result))
235  return false;
236  std::string token{};
237  while (kind k = get_token(token)) {
238  if (k != '*' and k != '/') {
239  push_back(token, k);
240  return true;
241  } else {
242  node right{};
243  if (not get_unary(right))
244  throw syntax_error{"unterminated expression. Expected a unary-expression after " + token};
245  result = node(result, k, right);
246  }
247  }
248  return true;
249 }
250 
251 bool parser::get_unary(node& result)
252 {
253  std::string token{};
254  kind k = get_token(token);
255  if (k == eof)
256  return false;
257  if (k == '-') {
258  if (not get_primary(result))
259  throw syntax_error{"expected primary after unary " + token + ", got end of line"};
260  result = node(k, result);
261  return true;
262  } else if (k == '+') {
263  if (not get_primary(result))
264  throw syntax_error{"expected primary after unary +, got end of line"};
265  return true;
266  } else {
267  push_back(token, k);
268  return get_primary(result);
269  }
270 }
271 
273 {
274  result.clear();
275  std::string token{};
276  while (kind k = get_token(token)) {
277  if (k == ')')
278  return;
279  push_back(token, k);
280  node expr{};
281  if (not get_expr(expr))
282  throw syntax_error{"unexpected end of line in function argument"};
283  result.push_back(expr);
284  k = get_token(token);
285  if (k == ')')
286  return;
287  else if (k != ',')
288  throw syntax_error{"expected comma in argument list, got " + token};
289  }
290  throw syntax_error{"unexpected end of line in function argument list"};
291 }
292 
294 {
295  std::string token{};
296  kind k = get_token(token);
297  if (k == eof)
298  return false;
299 
300  if (k == '(') {
301  // Parenthesized expression
302  if (not get_expr(result))
303  throw syntax_error{"expected expression, got end of line"};
304  k = get_token(token);
305  if (k != ')')
306  throw syntax_error{"expected ')', got " + token};
307  else
308  return true;
309  }
310 
311  if (k == number) {
312  // Numeric literal
313  if (not get_number(token, result))
314  throw syntax_error{"Invalid numeric literal: " + token};
315  return true;
316  }
317 
318  if (k == identifier) {
319  // Identifier: variable or function call
320  std::string next{};
321  k = get_token(next);
322  if (k == '(') {
323  // function call
324  node_list arguments{};
325  get_expr_list(arguments);
326  result = node{std::move(token), std::move(arguments)};
327  } else {
328  static const node_list no_arguments{};
329  // Variable reference or function call with no arguments
330  push_back(next, k);
331  result = node(std::move(token), no_arguments);
332  }
333  return true;
334  }
335  throw syntax_error("expected a primary, got " + token);
336 }
337 
338 void parse_loop(std::istream& input, std::ostream& output)
339 {
340  std::string line{};
341  // No portable way to test whether the console is an interactive terminal
342  // vs. a non-interactive file. If you have a system-specific way to test,
343  // output the prompt only for the interactive case.
344  for (output << "> "; std::getline(input, line); output << "> ") {
345  std::istringstream input{std::move(line)};
346  parser p{input};
347  try {
348  while (p.get_statement(output)) {
349  /* empty */
350  }
351  } catch(calc_error const& ex) {
352  output << ex.what() << '\n';
353  } catch(std::exception const& ex) {
354  output << "exception: " << ex.what() << '\n';
355  }
356  }
357 }
void parse_loop(std::istream &input, std::ostream &output)
Definition: parse.cpp:338
bool isprint(char c) const
Definition: parse.hpp:177
bool get_expr(node &result)
Definition: parse.cpp:208
parser(std::istream &input)
Definition: parse.cpp:10
void get_identifier(std::string &identifier)
Definition: parse.cpp:41
std::string charify(char c)
Definition: parse.cpp:17
Definition: node.hpp:26
void get_definition(std::string &name, identifier_list &parameters, node &definition)
Definition: parse.cpp:157
void push_back(std::string const &token, kind k)
Definition: parse.cpp:60
bool get_statement(std::ostream &output)
Definition: parse.cpp:178
Definition: parse.hpp:25
bool get_primary(node &result)
Definition: parse.cpp:293
std::vector< std::string > identifier_list
A sequence of identifiers (e.g., parameter names).
Definition: node.hpp:19
OutputIterator get_namelist(OutputIterator output)
Definition: parse.hpp:193
bool get_unary(node &result)
Definition: parse.cpp:251
bool isalpha(char c) const
Definition: parse.hpp:162
kind get_token(std::string &token)
Definition: parse.cpp:69
std::string token_
One token push-back.
Definition: parse.hpp:181
void set_function(std::string const &name, node value)
Definition: variables.cpp:70
std::istream & input_
Share the input stream.
Definition: parse.hpp:179
kind kind_
The kind of token that was pushed back.
Definition: parse.hpp:182
double evaluate() const
Definition: node.cpp:57
kind
Definition: parse.hpp:31
std::vector< node > node_list
A sequence of nodes.
Definition: node.hpp:13
bool get_number(std::string const &token, node &result)
Definition: parse.cpp:146
bool get_mul_expr(node &result)
Definition: parse.cpp:232
bool get_add_expr(node &result)
Definition: parse.cpp:213
void get_expr_list(node_list &result)
Definition: parse.cpp:272
bool isalnum(char c) const
Definition: parse.hpp:167