Calculator Step 2

- Main Page
- Classes
- Files

- File List
- File Members

parse.cpp
Go to the documentation of this file.
1 #include <sstream>
2
3 #include "parse.hpp"
4
5 parser::parser(std::istream& input)
6 : input_(input),
7 ctype_(std::use_facet<std::ctype<char>>(input.getloc())),
8 token_{},
9 kind_{}
10 {}
11
12 std::string parser::charify(char c)
13 {
14 if (c == '\a') return R"('\a')";
15 if (c == '\b') return R"('\b')";
16 if (c == '\f') return R"('\f')";
17 if (c == '\n') return R"('\n')";
18 if (c == '\r') return R"('\r')";
19 if (c == '\t') return R"('\t')";
20 if (c == '\v') return R"('\v')";
21 if (c == '\'') return R"('\'')";
22 if (c == '\\') return R"('\\')";
23
24 if (isprint(c))
25 return std::string{"\'"} + std::string(1,c) + "\'";
26 else {
27 std::ostringstream stream{};
28 stream << "'\\x" << std::hex;
29 stream.fill('0');
30 stream.width(2);
31 stream << (std::char_traits<char>::to_int_type(c) & 0xFF) << '\'';
32 return stream.str();
33 }
34 }
35
36 void parser::get_identifier(std::string& identifier)
37 {
38 identifier.clear();
39 char c{};
40 if (not input_.get(c))
41 return;
42 if (not isalpha(c))
43 throw parse_error{"syntax error: expected alphabetic, got " + charify(c)};
44 identifier += c;
45 while (input_.get(c)) {
46 if (not isalnum(c)) {
47 input_.unget();
48 return;
49 }
50 identifier += c;
51 }
52 return;
53 }
54
55 /* Parse a token.
56 * A token can be a keyword, a literal or a symbol.
57 * TOKEN ::= IDENTIFIER | NUMBER | SYMBOL
58 * IDENTIIFER ::= ALPHA (ALPHA | DIGIT)*
59 * NUMBER ::= DIGIT+ ('.' DIGITS+)? ('E' SIGN? DIGITS+)?
60 * SYMBOL ::= '+' | '-' | '*' | '/' | '%' | '(' | ')' | '='
61 */
62 parser::kind parser::get_token(std::string& token)
63 {
64 if (not token_.empty())
65 {
66 token = token_;
67 kind result(kind_);
68 token_.clear();
69 kind_ = eof;
70 return result;
71 }
72
73 char c{};
74 if (not (input_ >> c))
75 return eof;
76 if (isalpha(c)) {
77 input_.unget();
78 get_identifier(token);
79 return identifier;
80 }
81
82 // Get a numeric literal.
83 token.clear();
84 if (c == '+' or c == '-' or c == '*' or c == '/' or c == '%' or c == '(' or c == ')' or c == '=') {
85 token += c;
86 return kind(c);
87 }
88
89 if (c < '0' or c > '9') {
90 input_.unget();
91 throw parse_error{"syntax error: expected digit, got " + charify(c)};
92 }
93 while (c >= '0' and c <= '9') {
94 token += c;
95 if (not input_.get(c))
96 return number;
97 }
98 if (c == '.') {
99 token += c;
100 if (not input_.get(c))
101 throw parse_error{"unterminated number: expected digit after the decimal point"};
102 if (c < '0' or c > '9') {
103 input_.unget();
104 throw parse_error{"syntax error: expected digit after decimal point, got " + charify(c)};
105 }
106 while (c >= '0' and c <= '9') {
107 token += c;
108 if (not input_.get(c))
109 return number;
110 }
111 }
112 if (c == 'e' or c == 'E') {
113 token += c;
114 if (not input_.get(c))
115 throw parse_error{"unterminated number: expected digit in the exponent"};
116 if (c == '-' or c == '+') {
117 token += c;
118 if (not input_.get(c))
119 throw parse_error{"unterminated number: expected digit after sign in the exponent"};
120 }
121 if (c < '0' or c > '9') {
122 input_.unget();
123 throw parse_error{"syntax error: expected digit in the exponent, got " + charify(c)};
124 }
125 while (c >= '0' and c <= '9') {
126 token += c;
127 if (not input_.get(c))
128 return number;
129 }
130 }
131 input_.unget();
132 return number;
133 }
134
135 bool parser::get_number(std::string const& token, double& result)
136 {
137 std::istringstream stream{token};
138 // If the value overflows or is otherwise invalid, return false.
139 return (stream >> result);
140 }
141
142 bool parser::get_expr(double& result)
143 {
144 std::string token{};
145 kind k{get_token(token)};
146 if (k == eof)
147 return false;
148
149 if (k == identifier and token == "var") {
150 std::string name{};
151 // Define a variable.
152 k = get_token(name);
153 if (k != identifier)
154 throw parse_error{"syntax error: expected IDENTIFIER, but got " + name};
155 k = get_token(token);
156 if (k != '=')
157 throw parse_error{"syntax error: expected =, but got " + token};
158 if (not get_add_expr(result))
159 throw parse_error{"syntax error: expected additive-exprssion in assignment"};
160 set_variable(name, result);
161 return true;
162 }
163
164 if (k == identifier and token == "quit")
165 std::exit(0);
166
167 push_back(token, k);
168 if (not get_add_expr(result))
169 throw parse_error{"syntax error: expected an additive-expression"};
170
171 return true;
172 }
173
174 bool parser::get_add_expr(double& result)
175 {
176 if (not get_mul_expr(result))
177 return false;
178 std::string token{};
179 while (kind k = get_token(token)) {
180 if (k != '+' and k != '-') {
181 push_back(token, k);
182 return true;
183 } else {
184 double right{};
185 if (not get_mul_expr(right))
186 throw parse_error{"syntax error: unterminated expression. Expected a multiplicative-expression after " + token};
187 if (k == '+')
188 result += right;
189 else
190 result -= right;
191 }
192 }
193 return true;
194 }
195
196 bool parser::get_mul_expr(double& result)
197 {
198 if (not get_unary(result))
199 return false;
200 std::string token{};
201 while (kind k = get_token(token)) {
202 if (k != '*' and k != '/') {
203 push_back(token, k);
204 return true;
205 } else {
206 double right{};
207 if (not get_unary(right))
208 throw parse_error{"syntax error: unterminated expression. Expected a unary-expression after " + token};
209 if (k == '*')
210 result *= right;
211 else if (right == 0.0)
212 throw parse_error{"division by zero"};
213 else
214 result /= right;
215 }
216 }
217 return true;
218 }
219
220 bool parser::get_unary(double& result)
221 {
222 std::string token{};
223 if (kind k = get_token(token)) {
224 if (k == '-') {
225 if (not get_primary(result))
226 return false;
227 result = -result;
228 return true;
229 } else if (k == '+') {
230 return get_primary(result);
231 } else {
232 push_back(token, k);
233 return get_primary(result);
234 }
235 }
236 return false;
237 }
238
239 bool parser::get_primary(double& result)
240 {
241 std::string token{};
242 if (kind k = get_token(token)) {
243 if (k == '(') {
244 if (not get_expr(result))
245 return false;
246 k = get_token(token);
247 if (k == eof)
248 throw parse_error{"syntax error: EOF when expecting ')'"};
249 else if (k != ')')
250 throw parse_error{"syntax error: expected ')', but got " + token};
251 else
252 return true;
253 } else if (k == number) {
254 if (not get_number(token, result))
255 throw parse_error{"Invalid numeric literal: " + token};
256 return true;
257 } else if (k == identifier) {
258 result = get_variable(token);
259 return true;
260 } else {
261 throw parse_error{"syntax error: expected a primary, but got " + token};
262 }
263 }
264 return false;
265 }
266
267 void parse_loop(std::istream& input, std::ostream& output)
268 {
269 std::string line{};
270 // No portable way to test whether the console is an interactive terminal
271 // vs. a non-interactive file. If you have a system-specific way to test,
272 // output the prompt only for the interactive case.
273 for (output << "> "; std::getline(input, line); output << "> ") {
274 std::istringstream input{std::move(line)};
275 parser p(input);
276 try {
277 double x;
278 while (p.get_expr(x))
279 output << x << '\n';
280 } catch(parse_error const& ex) {
281 output << ex.what() << '\n';
282 } catch(std::exception const& ex) {
283 output << "exception: " << ex.what() << '\n';
284 }
285 }
286 }
parser::get_primary
bool get_primary(double &result)
Definition: parse.cpp:239
parse_loop
void parse_loop(std::istream &input, std::ostream &output)
Definition: parse.cpp:267
parser::isprint
bool isprint(char c) const
Definition: parse.hpp:140
set_variable
void set_variable(std::string name, double value)
Definition: variables.cpp:23
parser::parser
parser(std::istream &input)
Definition: parse.cpp:5
parser::get_identifier
void get_identifier(std::string &identifier)
Definition: parse.cpp:36
parser::charify
std::string charify(char c)
Definition: parse.cpp:12
parser::push_back
void push_back(std::string const &token, kind k)
Definition: parse.hpp:119
parser
Definition: parse.hpp:31
parser::get_mul_expr
bool get_mul_expr(double &result)
Definition: parse.cpp:196
parser::isalpha
bool isalpha(char c) const
Definition: parse.hpp:125
parser::get_token
kind get_token(std::string &token)
Definition: parse.cpp:62
parser::token_
std::string token_
One token push-back.
Definition: parse.hpp:144
parser::get_number
bool get_number(std::string const &token, double &result)
Definition: parse.cpp:135
parser::get_expr
bool get_expr(double &result)
Definition: parse.cpp:142
parse_error
Definition: parse.hpp:14
parser::get_add_expr
bool get_add_expr(double &result)
Definition: parse.cpp:174
parser::number
Definition: parse.hpp:37
parse.hpp
parser::input_
std::istream & input_
Share the input stream.
Definition: parse.hpp:142
parser::get_unary
bool get_unary(double &result)
Definition: parse.cpp:220
parser::kind_
kind kind_
The kind of token that was pushed back.
Definition: parse.hpp:145
parser::eof
Definition: parse.hpp:37
parser::kind
kind
Definition: parse.hpp:37
get_variable
double get_variable(std::string const &name)
Definition: variables.cpp:18
parser::isalnum
bool isalnum(char c) const
Definition: parse.hpp:130
parser::identifier
Definition: parse.hpp:37