Calculator Step 3

- Main Page
- Classes
- Files

- File List
- File Members

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 */
178 bool parser::get_add_expr(node& result)
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 */
200 bool parser::get_mul_expr(node& result)
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 */
244 bool parser::get_primary(node& result)
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 }
parse_loop
void parse_loop(std::istream &input, std::ostream &output)
Definition: parse.cpp:272
parser::isprint
bool isprint(char c) const
Definition: parse.hpp:139
parser::get_expr
bool get_expr(node &result)
Definition: parse.cpp:143
parser::parser
parser(std::istream &input)
Definition: parse.cpp:6
parser::get_identifier
void get_identifier(std::string &identifier)
Definition: parse.cpp:37
parser::charify
std::string charify(char c)
Definition: parse.cpp:13
node
Definition: node.hpp:16
parser::push_back
void push_back(std::string const &token, kind k)
Definition: parse.hpp:118
parser
Definition: parse.hpp:30
parser::get_primary
bool get_primary(node &result)
Definition: parse.cpp:244
parser::get_unary
bool get_unary(node &result)
Definition: parse.cpp:222
parser::isalpha
bool isalpha(char c) const
Definition: parse.hpp:124
parser::get_token
kind get_token(std::string &token)
Definition: parse.cpp:56
parser::token_
std::string token_
One token push-back.
Definition: parse.hpp:143
parse_error
Definition: parse.hpp:13
parser::number
Definition: parse.hpp:36
parse.hpp
parser::input_
std::istream & input_
Share the input stream.
Definition: parse.hpp:141
parser::kind_
kind kind_
The kind of token that was pushed back.
Definition: parse.hpp:144
node.hpp
node::evaluate
double evaluate() const
Definition: node.cpp:50
parser::eof
Definition: parse.hpp:36
parser::kind
kind
Definition: parse.hpp:36
parser::get_number
bool get_number(std::string const &token, node &result)
Definition: parse.cpp:129
parser::get_mul_expr
bool get_mul_expr(node &result)
Definition: parse.cpp:200
parser::get_add_expr
bool get_add_expr(node &result)
Definition: parse.cpp:178
parser::isalnum
bool isalnum(char c) const
Definition: parse.hpp:129
parser::identifier
Definition: parse.hpp:36