Calculator Step 1

- Main Page
- Classes
- Files

- File List
- File Members

parse.cpp
Go to the documentation of this file.
1 #include <sstream>
2 #include <utility>
3
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 {}
10
11 std::string parser::charify(char c)
12 {
13 if (c == '\a') return R"('\a')";
14 if (c == '\b') return R"('\b')";
15 if (c == '\f') return R"('\f')";
16 if (c == '\n') return R"('\n')";
17 if (c == '\r') return R"('\r')";
18 if (c == '\t') return R"('\t')";
19 if (c == '\v') return R"('\v')";
20 if (c == '\'') return R"('\'')";
21 if (c == '\\') return R"('\\')";
22
23 if (isprint(c))
24 return std::string{"\'"} + std::string(1,c) + "\'";
25 else {
26 std::ostringstream stream{};
27 stream << "'\\x" << std::hex;
28 stream.fill('0');
29 stream.width(2);
30 stream << (std::char_traits<char>::to_int_type(c) & 0xFF) << '\'';
31 return stream.str();
32 }
33 }
34
35 /* Parse a floating point number.
36 * NUMBER ::= SIGN? DIGIT+ ('.' DIGITS+)? ('E' SIGN? DIGITS+)?
37 */
38 bool parser::get_number(double& result)
39 {
40 std::string token{};
41 char c{};
42 if (not input_.get(c))
43 return false;
44 if (c == '+' or c == '-') {
45 token += c;
46 if (not input_.get(c))
47 throw parse_error{"unterminated number: expected a digit after the sign"};
48 }
49 if (c < '0' or c > '9') {
50 input_.unget();
51 throw parse_error{"syntax error: expected digit, got " + charify(c)};
52 }
53 while (c >= '0' and c <= '9') {
54 token += c;
55 if (not input_.get(c)) {
56 std::istringstream tmp{std::move(token)};
57 // If the value overflows, return false.
58 return (tmp >> result);
59 }
60 }
61 if (c == '.') {
62 token += c;
63 if (not input_.get(c))
64 throw parse_error{"unterminated number: expected digit after the decimal point"};
65 if (c < '0' or c > '9') {
66 input_.unget();
67 throw parse_error{"syntax error: expected digit after decimal point, got " + charify(c)};
68 }
69 while (c >= '0' and c <= '9') {
70 token += c;
71 if (not input_.get(c)) {
72 std::istringstream tmp{std::move(token)};
73 // If the value overflows or is otherwise invalid, return false.
74 return (tmp >> result);
75 }
76 }
77 }
78 if (c == 'e' or c == 'E') {
79 token += c;
80 if (not input_.get(c))
81 throw parse_error{"unterminated number: expected digit in the exponent"};
82 if (c == '-' or c == '+') {
83 token += c;
84 if (not input_.get(c))
85 throw parse_error{"unterminated number: expected digit after sign in the exponent"};
86 }
87 if (c < '0' or c > '9') {
88 input_.unget();
89 throw parse_error{"syntax error: expected digit in the exponent, got " + charify(c)};
90 }
91 while (c >= '0' and c <= '9') {
92 token += c;
93 if (not input_.get(c)) {
94 std::istringstream tmp{std::move(token)};
95 // If the value overflows or is otherwise invalid, return false.
96 return (tmp >> result);
97 }
98 }
99 }
100 input_.unget();
101
102 std::istringstream tmp{std::move(token)};
103 // If the value overflows or is otherwise invalid, return false.
104 return (tmp >> result);
105 }
106
107 /* Parse an expression.
108 * EXPR ::= MULT_EXPR | EXPR + MULT_EXPR | EXPR - MULT_EXP
109 */
110 bool parser::get_expr(double& result)
111 {
112 if (not get_mult_expr(result))
113 return false;
114 char c{};
115 while (input_ >> c) {
116 if (c != '+' and c != '-') {
117 input_.unget();
118 return true;
119 } else {
120 double right{};
121 if (not get_mult_expr(right))
122 throw parse_error{"syntax error: unterminated expression. Expected a multiplicative-exprssion after " + std::string(c,1)};
123 if (c == '+')
124 result += right;
125 else
126 result -= right;
127 }
128 }
129 return true;
130 }
131
132 /* Parse a multiplicative-expression.
133 * MULT_EXPR ::= PRIMARY | MULT_EXPR + PRIMARY | MULT_EXPR - PRIMARY
134 */
135 bool parser::get_mult_expr(double& result)
136 {
137 if (not get_primary(result))
138 return false;
139 char c{};
140 while (input_ >> c) {
141 if (c != '*' and c != '/') {
142 input_.unget();
143 return true;
144 } else {
145 double right{};
146 if (not get_primary(right))
147 throw parse_error{"syntax error: unterminated expression. Expected a primary after " + std::string(c,1)};
148 if (c == '*')
149 result *= right;
150 else if (right == 0.0)
151 throw parse_error{"division by zero"};
152 else
153 result /= right;
154 }
155 }
156 return true;
157 }
158
159 /* Parse a primary.
160 * PRIMARY ::= NUMBER | '(' EXPR ')'
161 */
162 bool parser::get_primary(double& result)
163 {
164 char c{};
165 if (not (input_ >> c))
166 // Can't read one character, so must be end-of-file
167 return false;
168 else if (c == '(') {
169 if (not get_expr(result))
170 return false;
171 if (not (input_ >> c))
172 throw parse_error{"syntax error: EOF when expecting ')'"};
173 else if (c != ')')
174 throw parse_error{"syntax error: expected ')', but got " + charify(c)};
175 else
176 return true;
177 } else {
178 input_.unget();
179 return get_number(result);
180 }
181 }
182
183 void parse_loop(std::istream& input, std::ostream& output)
184 {
185 std::string line;
186 // No portable way to test whether the console is an interactive terminal
187 // vs. a non-interactive file. If you have a system-specific way to test,
188 // output the prompt only for the interactive case.
189 for (output << "> "; std::getline(input, line); output << "> ") {
190 std::istringstream input{std::move(line)};
191 parser p{input};
192 try {
193 double x{};
194 while (p.get_expr(x))
195 output << x << '\n';
196 } catch(parse_error const& ex) {
197 output << ex.what() << '\n';
198 } catch(std::exception const& ex) {
199 output << "exception: " << ex.what() << '\n';
200 }
201 }
202 }
parser::get_primary
bool get_primary(double &result)
Definition: parse.cpp:162
parse_loop
void parse_loop(std::istream &input, std::ostream &output)
Definition: parse.cpp:183
parser::parser
parser(std::istream &input)
Definition: parse.cpp:6
parser::charify
std::string charify(char c)
Definition: parse.cpp:11
parser::get_number
bool get_number(double &result)
Definition: parse.cpp:38
parser
Definition: parse.hpp:17
parser::get_expr
bool get_expr(double &result)
Definition: parse.cpp:110
parse_error
Definition: parse.hpp:11
parse.hpp
parser::input_
std::istream & input_
Definition: parse.hpp:55
parser::get_mult_expr
bool get_mult_expr(double &result)
Definition: parse.cpp:135