Calculator Step 4

- 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_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
213 bool parser::get_add_expr(node& result)
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
232 bool parser::get_mul_expr(node& result)
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
272 void parser::get_expr_list(node_list& result)
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
293 bool parser::get_primary(node& result)
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 }
parse_loop
void parse_loop(std::istream &input, std::ostream &output)
Definition: parse.cpp:338
parser::isprint
bool isprint(char c) const
Definition: parse.hpp:177
calc_error
Definition: calc_error.hpp:14
parser::get_expr
bool get_expr(node &result)
Definition: parse.cpp:208
parser::parser
parser(std::istream &input)
Definition: parse.cpp:10
parser::get_identifier
void get_identifier(std::string &identifier)
Definition: parse.cpp:41
parser::charify
std::string charify(char c)
Definition: parse.cpp:17
node
Definition: node.hpp:26
parser::get_definition
void get_definition(std::string &name, identifier_list &parameters, node &definition)
Definition: parse.cpp:157
calc_error.hpp
parser::push_back
void push_back(std::string const &token, kind k)
Definition: parse.cpp:60
parser::get_statement
bool get_statement(std::ostream &output)
Definition: parse.cpp:178
parser
Definition: parse.hpp:25
parser::get_primary
bool get_primary(node &result)
Definition: parse.cpp:293
syntax_error
Definition: calc_error.hpp:27
identifier_list
std::vector< std::string > identifier_list
A sequence of identifiers (e.g., parameter names).
Definition: node.hpp:19
parser::get_namelist
OutputIterator get_namelist(OutputIterator output)
Definition: parse.hpp:193
parser::get_unary
bool get_unary(node &result)
Definition: parse.cpp:251
parser::isalpha
bool isalpha(char c) const
Definition: parse.hpp:162
parser::get_token
kind get_token(std::string &token)
Definition: parse.cpp:69
parser::token_
std::string token_
One token push-back.
Definition: parse.hpp:181
set_function
void set_function(std::string const &name, node value)
Definition: variables.cpp:70
parser::number
Definition: parse.hpp:31
parse.hpp
parser::input_
std::istream & input_
Share the input stream.
Definition: parse.hpp:179
parser::kind_
kind kind_
The kind of token that was pushed back.
Definition: parse.hpp:182
node.hpp
node::evaluate
double evaluate() const
Definition: node.cpp:57
parser::eof
Definition: parse.hpp:31
parser::kind
kind
Definition: parse.hpp:31
node_list
std::vector< node > node_list
A sequence of nodes.
Definition: node.hpp:13
parser::get_number
bool get_number(std::string const &token, node &result)
Definition: parse.cpp:146
parser::get_mul_expr
bool get_mul_expr(node &result)
Definition: parse.cpp:232
variables.hpp
parser::get_add_expr
bool get_add_expr(node &result)
Definition: parse.cpp:213
parser::get_expr_list
void get_expr_list(node_list &result)
Definition: parse.cpp:272
parser::isalnum
bool isalnum(char c) const
Definition: parse.hpp:167
parser::identifier
Definition: parse.hpp:31