Calculator Step 6

- Main Page
- Classes
- Files

- File List
- File Members

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
312 bool parser::get_add_expr(node& result)
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
331 bool parser::get_mul_expr(node& result)
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
371 void parser::get_expr_list(node_list& result)
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
392 bool parser::get_primary(node& result)
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 }
load_library
void load_library(std::string const &filename)
Definition: variables.cpp:100
parser::get_string
void get_string(std::string &result, char delimiter)
Definition: parse.cpp:86
parse_loop
void parse_loop(std::istream &input, std::ostream &output)
Definition: parse.cpp:444
parser::isprint
bool isprint(char c) const
Definition: parse.hpp:200
calc_error
Definition: calc_error.hpp:11
parser::get_expr
bool get_expr(node &result)
Definition: parse.cpp:307
save_library
void save_library(std::string const &filename)
Definition: variables.cpp:79
number
Definition: number.hpp:16
parser::parser
parser(std::istream &input)
Definition: parse.cpp:10
parser::get_identifier
void get_identifier(std::string &identifier)
Definition: parse.cpp:102
parser::charify
std::string charify(char c)
Definition: parse.cpp:17
node
Definition: node.hpp:28
parser::get_definition
void get_definition(std::string &name, identifier_list &parameters, node &definition)
Definition: parse.cpp:241
calc_error.hpp
parser::push_back
void push_back(std::string const &token, kind k)
Definition: parse.cpp:121
parser::get_statement
bool get_statement(std::ostream &output)
Definition: parse.cpp:262
parser::get_integer
bool get_integer(std::string const &token, node &result)
Definition: parse.cpp:219
parser::integer
Definition: parse.hpp:32
parser
Definition: parse.hpp:26
parser::get_primary
bool get_primary(node &result)
Definition: parse.cpp:392
syntax_error
Definition: calc_error.hpp:24
identifier_list
std::vector< std::string > identifier_list
A sequence of identifiers (e.g., parameter names).
Definition: node.hpp:21
parser::get_namelist
OutputIterator get_namelist(OutputIterator output)
Definition: parse.hpp:216
parser::get_unary
bool get_unary(node &result)
Definition: parse.cpp:350
parser::isalpha
bool isalpha(char c) const
Definition: parse.hpp:185
parser::get_token
kind get_token(std::string &token)
Definition: parse.cpp:130
parser::token_
std::string token_
One token push-back.
Definition: parse.hpp:204
set_function
void set_function(std::string const &name, node value)
Definition: variables.cpp:74
parser::get_float
bool get_float(std::string const &token, node &result)
Definition: parse.cpp:230
parser::floating_point
Definition: parse.hpp:32
node::evaluate
number evaluate() const
Definition: node.cpp:73
parse.hpp
parser::input_
std::istream & input_
Share the input stream.
Definition: parse.hpp:202
parser::kind_
kind kind_
The kind of token that was pushed back.
Definition: parse.hpp:205
node.hpp
parser::eof
Definition: parse.hpp:32
parser::kind
kind
Definition: parse.hpp:32
node_list
std::vector< node > node_list
A sequence of nodes.
Definition: node.hpp:15
parser::ctype_
std::ctype< char > const & ctype_
Cache the ctype facet for checking character categories.
Definition: parse.hpp:203
parser::get_mul_expr
bool get_mul_expr(node &result)
Definition: parse.cpp:331
variables.hpp
parser::get_add_expr
bool get_add_expr(node &result)
Definition: parse.cpp:312
parser::get_expr_list
void get_expr_list(node_list &result)
Definition: parse.cpp:371
parser::isalnum
bool isalnum(char c) const
Definition: parse.hpp:190
parser::string
Definition: parse.hpp:32
parser::get_escape
void get_escape(std::string &str)
Definition: parse.cpp:41
parser::identifier
Definition: parse.hpp:32