Calculator  Step 6
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_escape(std::string& str)
42 {
43  char c;
44  if (not input_.get(c))
45  throw syntax_error{"incomplete escape"};
46  if (c == '\n')
47  return;
48 
49  if (c == 'a')
50  str += '\a';
51  else if (c == 'b')
52  str += '\b';
53  else if (c == 'n')
54  str += '\n';
55  else if (c == 'f')
56  str += '\f';
57  else if (c == 'r')
58  str += '\r';
59  else if (c == 't')
60  str += '\t';
61  else if (c == 'v')
62  str += '\v';
63  else if (c == 'x') {
64  // hexadecimal sequence
65  std::string digits{};
66  while(input_.get(c) and ctype_.is(ctype_.xdigit, c))
67  digits += c;
68  if (input_)
69  input_.unget();
70  unsigned long value{std::stoul(digits, nullptr, 16)};
71  str += static_cast<char>(static_cast<unsigned char>(value));
72  } else if (c >= '0' and c <= '7') {
73  // octal sequence
74  std::string digits{};
75  for (int i = 0; i < 3 and input_.get(c) and c >= '0' and c <= '7'; ++i)
76  digits += c;
77  if (input_ or c < '0' or c > '7')
78  input_.unget();
79  int value{std::stoi(digits, nullptr, 8)};
80  str += static_cast<char>(static_cast<unsigned char>(value));
81  } else {
82  str += c;
83  }
84 }
85 
86 void parser::get_string(std::string& result, char delimiter)
87 {
88  char c{};
89  while (input_.get(c)) {
90  if (c == delimiter)
91  return;
92  else if (c == '\\')
93  get_escape(result);
94  else if (c == '\n')
95  throw syntax_error{"unterminated string"};
96  else
97  result += c;
98  }
99  throw syntax_error{"unterminated string"};
100 }
101 
102 void parser::get_identifier(std::string& identifier)
103 {
104  identifier.clear();
105  char c{};
106  if (not input_.get(c))
107  return;
108  if (not isalpha(c))
109  throw syntax_error{"expected alphabetic, got " + charify(c)};
110  identifier += c;
111  while (input_.get(c)) {
112  if (not isalnum(c)) {
113  input_.unget();
114  return;
115  }
116  identifier += c;
117  }
118  return;
119 }
120 
121 void parser::push_back(std::string const& token, kind k)
122 {
123  kind_ = k;
124  if (kind_ == eof)
125  token_ = "end of line";
126  else
127  token_ = token;
128 }
129 
130 parser::kind parser::get_token(std::string& token)
131 {
132  if (not token_.empty())
133  {
134  kind result(kind_);
135  token = token_;
136 
137  token_.clear();
138  kind_ = eof;
139 
140  return result;
141  }
142 
143  char c{};
144  if (not (input_ >> c)) {
145  token = "end of line";
146  return eof;
147  }
148  if (isalpha(c)) {
149  input_.unget();
150  get_identifier(token);
151  return identifier;
152  }
153 
154  if (c == '\'' or c == '"') {
155  // Quoted string
156  token.clear();
157  get_string(token, c);
158  return string;
159  }
160 
161  // Get a numeric literal.
162  token.clear();
163  if (c == '+' or c == '-' or c == '*' or c == '/' or c == '%' or c == '(' or c == ')' or c == '=' or c == ',') {
164  token += c;
165  return static_cast<kind>(c);
166  }
167 
168  if (c < '0' or c > '9') {
169  input_.unget();
170  throw syntax_error{"expected digit, got " + charify(c)};
171  }
172 
173  kind k = integer;
174 
175  while (c >= '0' and c <= '9') {
176  token += c;
177  if (not input_.get(c))
178  return k;
179  }
180  if (c == '.') {
181  k = floating_point;
182  token += c;
183  if (not input_.get(c))
184  throw syntax_error{"unterminated number: expected digit after the decimal point"};
185  if (c < '0' or c > '9') {
186  input_.unget();
187  throw syntax_error{"expected digit after decimal point, got " + charify(c)};
188  }
189  while (c >= '0' and c <= '9') {
190  token += c;
191  if (not input_.get(c))
192  return k;
193  }
194  }
195  if (c == 'e' or c == 'E') {
196  k = floating_point;
197  token += c;
198  if (not input_.get(c))
199  throw syntax_error{"unterminated number: expected digit in the exponent"};
200  if (c == '-' or c == '+') {
201  token += c;
202  if (not input_.get(c))
203  throw syntax_error{"unterminated number: expected digit after sign in the exponent"};
204  }
205  if (c < '0' or c > '9') {
206  input_.unget();
207  throw syntax_error{"expected digit in the exponent, got " + charify(c)};
208  }
209  while (c >= '0' and c <= '9') {
210  token += c;
211  if (not input_.get(c))
212  return k;
213  }
214  }
215  input_.unget();
216  return k;
217 }
218 
219 bool parser::get_integer(std::string const& token, node& result)
220 {
221  std::istringstream stream{token};
222  // If the value overflows or is otherwise invalid, return false.
223  long long value{};
224  if (not (stream >> value))
225  return false;
226  result = node{number{value}};
227  return true;
228 }
229 
230 bool parser::get_float(std::string const& token, node& result)
231 {
232  std::istringstream stream{token};
233  // If the value overflows or is otherwise invalid, return false.
234  double value{};
235  if (not (stream >> value))
236  return false;
237  result = node{number{value}};
238  return true;
239 }
240 
241 void parser::get_definition(std::string& name, identifier_list& parameters, node& definition)
242 {
243  // Define a variable.
244  kind k = get_token(name);
245  if (k != identifier)
246  throw syntax_error{"expected IDENTIFIER, got " + name};
247 
248  std::string token{};
249  k = get_token(token);
250  if (k == '(') {
251  get_namelist(std::back_inserter(parameters));
252  k = get_token(token);
253  }
254 
255  if (k != '=')
256  throw syntax_error{"expected = in definition, got " + token};
257 
258  if (not get_expr(definition))
259  throw syntax_error{"expected exprssion in assignment"};
260 }
261 
262 bool parser::get_statement(std::ostream& output)
263 {
264  std::string token{};
265  kind k(get_token(token));
266  if (k == eof)
267  return false;
268 
269  if (k == identifier and token == "def") {
270  node definition{};
271  identifier_list parameters{};
272  get_definition(token, parameters, definition);
273  set_function(token, node{std::move(parameters), definition});
274  return true;
275  }
276 
277  if (k == identifier and token == "quit")
278  std::exit(0);
279 
280  if (k == identifier and token == "save") {
281  std::string filename{};
282  if (get_token(filename) != string)
283  throw syntax_error{"expected FILENAME after save, got " + token};
284  save_library(filename);
285  output << "Library saved to " << filename << '\n';
286  }
287 
288  if (k == identifier and token == "load") {
289  std::string filename{};
290  if (get_token(filename) != string)
291  throw syntax_error{"expected FILENAME after load, got " + token};
292  load_library(filename);
293  output << "Library loaded from " << filename << '\n';
294  }
295  // Otherwise, the statement must be an expression.
296  push_back(token, k);
297  node n{};
298  if (not get_expr(n))
299  return false;
300  else {
301  // Evaluate the expression and print the result.
302  output << n.evaluate() << '\n';
303  return true;
304  }
305 }
306 
307 bool parser::get_expr(node& result)
308 {
309  return get_add_expr(result);
310 }
311 
313 {
314  if (not get_mul_expr(result))
315  return false;
316  std::string token{};
317  while (kind k = get_token(token)) {
318  if (k != '+' and k != '-') {
319  push_back(token, k);
320  return true;
321  } else {
322  node right{};
323  if (not get_mul_expr(right))
324  throw syntax_error{"unterminated expression. Expected a multiplicative-expression after " + token};
325  result = node(result, k, right);
326  }
327  }
328  return true;
329 }
330 
332 {
333  if (not get_unary(result))
334  return false;
335  std::string token{};
336  while (kind k = get_token(token)) {
337  if (k != '*' and k != '/') {
338  push_back(token, k);
339  return true;
340  } else {
341  node right{};
342  if (not get_unary(right))
343  throw syntax_error{"unterminated expression. Expected a unary-expression after " + token};
344  result = node(result, k, right);
345  }
346  }
347  return true;
348 }
349 
350 bool parser::get_unary(node& result)
351 {
352  std::string token{};
353  kind k = get_token(token);
354  if (k == eof)
355  return false;
356  if (k == '-') {
357  if (not get_primary(result))
358  throw syntax_error{"expected primary after unary " + token + ", got end of line"};
359  result = node(k, result);
360  return true;
361  } else if (k == '+') {
362  if (not get_primary(result))
363  throw syntax_error{"expected primary after unary +, got end of line"};
364  return true;
365  } else {
366  push_back(token, k);
367  return get_primary(result);
368  }
369 }
370 
372 {
373  result.clear();
374  std::string token{};
375  while (kind k = get_token(token)) {
376  if (k == ')')
377  return;
378  push_back(token, k);
379  node expr{};
380  if (not get_expr(expr))
381  throw syntax_error{"unexpected end of line in function argument"};
382  result.push_back(expr);
383  k = get_token(token);
384  if (k == ')')
385  return;
386  else if (k != ',')
387  throw syntax_error{"expected comma in argument list, got " + token};
388  }
389  throw syntax_error{"unexpected end of line in function argument list"};
390 }
391 
393 {
394  std::string token{};
395  kind k = get_token(token);
396  if (k == eof)
397  return false;
398 
399  if (k == '(') {
400  // Parenthesized expression
401  if (not get_expr(result))
402  throw syntax_error{"expected expression, got end of line"};
403  k = get_token(token);
404  if (k != ')')
405  throw syntax_error{"expected ')', got " + token};
406  else
407  return true;
408  }
409 
410  if (k == integer) {
411  // Integer literal
412  if (not get_integer(token, result))
413  throw syntax_error{"Invalid integer literal: " + token};
414  return true;
415  }
416 
417  if (k == floating_point) {
418  // Integer literal
419  if (not get_float(token, result))
420  throw syntax_error{"Invalid integer literal: " + token};
421  return true;
422  }
423 
424  if (k == identifier) {
425  // Identifier: variable or function call
426  std::string next{};
427  k = get_token(next);
428  if (k == '(') {
429  // function call
430  node_list arguments{};
431  get_expr_list(arguments);
432  result = node{std::move(token), std::move(arguments)};
433  } else {
434  static const node_list no_arguments;
435  // Variable reference or function call with no arguments
436  push_back(next, k);
437  result = node{std::move(token), no_arguments};
438  }
439  return true;
440  }
441  throw syntax_error{"expected a primary, got " + token};
442 }
443 
444 void parse_loop(std::istream& input, std::ostream& output)
445 {
446  std::string line{};
447  // No portable way to test whether the console is an interactive terminal
448  // vs. a non-interactive file. If you have a system-specific way to test,
449  // output the prompt only for the interactive case.
450  for (output << "> "; std::getline(input, line); output << "> ") {
451  std::istringstream input{std::move(line)};
452  parser p{input};
453  try {
454  while (p.get_statement(output)) {
455  /* empty */
456  }
457  } catch(calc_error const& ex) {
458  output << ex.what() << '\n';
459  } catch(std::exception const& ex) {
460  output << "exception: " << ex.what() << '\n';
461  }
462  }
463 }
void load_library(std::string const &filename)
Definition: variables.cpp:100
void get_string(std::string &result, char delimiter)
Definition: parse.cpp:86
void parse_loop(std::istream &input, std::ostream &output)
Definition: parse.cpp:444
bool isprint(char c) const
Definition: parse.hpp:200
bool get_expr(node &result)
Definition: parse.cpp:307
void save_library(std::string const &filename)
Definition: variables.cpp:79
parser(std::istream &input)
Definition: parse.cpp:10
void get_identifier(std::string &identifier)
Definition: parse.cpp:102
std::string charify(char c)
Definition: parse.cpp:17
Definition: node.hpp:28
void get_definition(std::string &name, identifier_list &parameters, node &definition)
Definition: parse.cpp:241
void push_back(std::string const &token, kind k)
Definition: parse.cpp:121
bool get_statement(std::ostream &output)
Definition: parse.cpp:262
bool get_integer(std::string const &token, node &result)
Definition: parse.cpp:219
Definition: parse.hpp:26
bool get_primary(node &result)
Definition: parse.cpp:392
std::vector< std::string > identifier_list
A sequence of identifiers (e.g., parameter names).
Definition: node.hpp:21
OutputIterator get_namelist(OutputIterator output)
Definition: parse.hpp:216
bool get_unary(node &result)
Definition: parse.cpp:350
bool isalpha(char c) const
Definition: parse.hpp:185
kind get_token(std::string &token)
Definition: parse.cpp:130
std::string token_
One token push-back.
Definition: parse.hpp:204
void set_function(std::string const &name, node value)
Definition: variables.cpp:74
bool get_float(std::string const &token, node &result)
Definition: parse.cpp:230
number evaluate() const
Definition: node.cpp:73
std::istream & input_
Share the input stream.
Definition: parse.hpp:202
kind kind_
The kind of token that was pushed back.
Definition: parse.hpp:205
kind
Definition: parse.hpp:32
std::vector< node > node_list
A sequence of nodes.
Definition: node.hpp:15
std::ctype< char > const & ctype_
Cache the ctype facet for checking character categories.
Definition: parse.hpp:203
bool get_mul_expr(node &result)
Definition: parse.cpp:331
bool get_add_expr(node &result)
Definition: parse.cpp:312
void get_expr_list(node_list &result)
Definition: parse.cpp:371
bool isalnum(char c) const
Definition: parse.hpp:190
void get_escape(std::string &str)
Definition: parse.cpp:41