Calculator Step 2

- Main Page
- Classes
- Files

- Class List
- Class Index
- Class Hierarchy
- Class Members

Public Types | Public Member Functions | Private Member Functions | Private Attributes | List of all members
parser Class Reference

`#include <parse.hpp>`

## Public Types
enum | kind : int { eof, identifier, number, plus ='+', minus ='-', times ='*', slash ='/', lparen = '(', rparen =')', equal ='=' }

## Public Member Functions
| parser (std::istream &input)
bool | get_expr (double &result)

## Private Member Functions
std::string | charify (char c)
bool | get_number (std::string const &token, double &result)
bool | get_add_expr (double &result)
bool | get_mul_expr (double &result)
bool | get_primary (double &result)
bool | get_unary (double &result)
kind | get_token (std::string &token)
void | get_identifier (std::string &identifier)
void | push_back (std::string const &token, kind k)
bool | isalpha (char c) const
bool | isalnum (char c) const
bool | isdigit (char c) const
bool | isprint (char c) const

## Private Attributes
std::istream & | input_
| Share the input stream. More...
std::ctype< char > const & | ctype_
| Cache the ctype facet for checking character categories. More...
std::string | token_
| One token push-back. More...
kind | kind_
| The kind of token that was pushed back. More...

## Detailed Description

Parser class template. The parser reads tokens from an input stream. A token can be a keyword, numeric literal, identifier, or symbol (operator or punctuator). Symbols can have multiple characters (e.g., :=).

Because the recursive-descent parser can examine too many tokens from the input stream, it keeps a push-back token. Once the parser knows it has gone too far, it pushes back the most recently read token. The next call to get_token() retrieves the pushed-back token.

Only one push-back is available, which limits the complexity of the syntax.

Definition at line 31 of file parse.hpp.

## Member Enumeration Documentation

enum parser::kind : int

Token kind. Declare a name for each single-character token, to ensure the enumerated type can represent any operator or punctuator character.

Enumerator
eof |
identifier |
number |
plus |
minus |
times |
slash |
lparen |
rparen |
equal |

Definition at line 37 of file parse.hpp.

37 : int { eof, identifier, number,
38 plus='+', minus='-', times='*', slash='/', lparen = '(', rparen=')', equal='=' };
parser::slash
Definition: parse.hpp:38
parser::times
Definition: parse.hpp:38
parser::lparen
Definition: parse.hpp:38
parser::rparen
Definition: parse.hpp:38
parser::plus
Definition: parse.hpp:38
parser::minus
Definition: parse.hpp:38
parser::number
Definition: parse.hpp:37
parser::equal
Definition: parse.hpp:38
parser::eof
Definition: parse.hpp:37
parser::identifier
Definition: parse.hpp:37

## Constructor & Destructor Documentation

parser::parser | ( | std::istream & | input | ) |

Constructor. Save the `input` stream.

Parameters

  input | The input stream

Definition at line 5 of file parse.cpp.

6 : input_(input),
7 ctype_(std::use_facet<std::ctype<char>>(input.getloc())),
8 token_{},
9 kind_{}
10 {}
parser::token_
std::string token_
One token push-back.
Definition: parse.hpp:144
parser::input_
std::istream & input_
Share the input stream.
Definition: parse.hpp:142
parser::kind_
kind kind_
The kind of token that was pushed back.
Definition: parse.hpp:145
parser::ctype_
std::ctype< char > const & ctype_
Cache the ctype facet for checking character categories.
Definition: parse.hpp:143

## Member Function Documentation

std::string parser::charify | ( | char | c | ) |
| private

Convert a characer to a readable form.

Parameters

  c | The character

Returns
  A C++-style character literal that ensures `c` is readable.

Definition at line 12 of file parse.cpp.

References isprint().

Referenced by get_identifier(), and get_token().

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 }
parser::isprint
bool isprint(char c) const
Definition: parse.hpp:140

bool parser::get_add_expr | ( | double & | result | ) |
| private

Parse an addition expression

    ADD_EXPR ::= MUL_EXPR | ADD_EXPR + MUL_EXPR | ADD_EXPR - MUL_EXPR

Parameters

  result | Store the result here

Returns
  true to continue parsing or false to stop (end of file or error)

Definition at line 174 of file parse.cpp.

References get_mul_expr(), get_token(), and push_back().

Referenced by get_expr().

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 }
parser::push_back
void push_back(std::string const &token, kind k)
Definition: parse.hpp:119
parser::get_mul_expr
bool get_mul_expr(double &result)
Definition: parse.cpp:196
parser::get_token
kind get_token(std::string &token)
Definition: parse.cpp:62
parse_error
Definition: parse.hpp:14
parser::kind
kind
Definition: parse.hpp:37

bool parser::get_expr | ( | double & | result | ) |

Read one expression and store the result in `result`.

Parameters

  result | Where to store the result of the expression.

Returns
  true to continue or false to end the loop

Exceptions

  parse_error | for various syntax and other errors

Definition at line 142 of file parse.cpp.

References eof, get_add_expr(), get_token(), identifier, push_back(), and set_variable().

Referenced by get_primary(), and parse_loop().

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 }
set_variable
void set_variable(std::string name, double value)
Definition: variables.cpp:23
parser::push_back
void push_back(std::string const &token, kind k)
Definition: parse.hpp:119
parser::get_token
kind get_token(std::string &token)
Definition: parse.cpp:62
parse_error
Definition: parse.hpp:14
parser::get_add_expr
bool get_add_expr(double &result)
Definition: parse.cpp:174
parser::eof
Definition: parse.hpp:37
parser::kind
kind
Definition: parse.hpp:37
parser::identifier
Definition: parse.hpp:37

void parser::get_identifier | ( | std::string & | identifier | ) |
| private

Parse an identifer.

Parameters

  identifier | Store the identifier here.

Precondition
  first input character is alphabetic

Definition at line 36 of file parse.cpp.

References charify(), input_, isalnum(), and isalpha().

Referenced by get_token().

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 }
parser::charify
std::string charify(char c)
Definition: parse.cpp:12
parser::isalpha
bool isalpha(char c) const
Definition: parse.hpp:125
parse_error
Definition: parse.hpp:14
parser::input_
std::istream & input_
Share the input stream.
Definition: parse.hpp:142
parser::isalnum
bool isalnum(char c) const
Definition: parse.hpp:130
parser::identifier
Definition: parse.hpp:37

bool parser::get_mul_expr | ( | double & | result | ) |
| private

Parse a multiplicative expression.

    MUL_EXPR ::= UNARY | MUL_EXPR + UNARY | MUL_EXPR - UNARY

Parameters

  result | Store the result here

Returns
  true to continue parsing or false to stop (end of file or error)

Definition at line 196 of file parse.cpp.

References get_token(), get_unary(), and push_back().

Referenced by get_add_expr().

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 }
parser::push_back
void push_back(std::string const &token, kind k)
Definition: parse.hpp:119
parser::get_token
kind get_token(std::string &token)
Definition: parse.cpp:62
parse_error
Definition: parse.hpp:14
parser::get_unary
bool get_unary(double &result)
Definition: parse.cpp:220
parser::kind
kind
Definition: parse.hpp:37

bool parser::get_number | ( | std::string const & | token,
|  | double & | result
| ) |  |
| private

Parse a floating number.

Parameters

  token | The token to parse
  result | Store the number here

Returns
  true if `token` is a valid number or false for an error

Definition at line 135 of file parse.cpp.

Referenced by get_primary().

136 {
137 std::istringstream stream{token};
138 // If the value overflows or is otherwise invalid, return false.
139 return (stream >> result);
140 }

bool parser::get_primary | ( | double & | result | ) |
| private

Parse a primary expression.

    PRIMARY ::= NUMBER | IDENTIFIER | '(' EXPR ')'

Parameters

  result | Store the result here

Returns
  true to continue parsing or false to stop (end of file or error)

Definition at line 239 of file parse.cpp.

References eof, get_expr(), get_number(), get_token(), get_variable(), identifier, and number.

Referenced by get_unary().

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 }
parser::get_token
kind get_token(std::string &token)
Definition: parse.cpp:62
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::number
Definition: parse.hpp:37
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::identifier
Definition: parse.hpp:37

parser::kind parser::get_token | ( | std::string & | token | ) |
| private

Parse a token. A token can be a keyword, a literal or a symbol.

    TOKEN ::= IDENTIFIER | NUMBER | SYMBOL
    IDENTIIFER ::= ALPHA (ALPHA | DIGIT)*
    NUMBER ::= DIGIT+ ('.' DIGITS+)? ('E' SIGN? DIGITS+)?
    SYMBOL ::= '+' | '-' | '*' | '/' | '%' | '(' | ')' | '='

Parameters

  token | Store the text of the token here.

Returns
  the token kind

Definition at line 62 of file parse.cpp.

References charify(), eof, get_identifier(), identifier, input_, isalpha(), kind_, number, and token_.

Referenced by get_add_expr(), get_expr(), get_mul_expr(), get_primary(), and get_unary().

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 }
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::isalpha
bool isalpha(char c) const
Definition: parse.hpp:125
parser::token_
std::string token_
One token push-back.
Definition: parse.hpp:144
parse_error
Definition: parse.hpp:14
parser::number
Definition: parse.hpp:37
parser::input_
std::istream & input_
Share the input stream.
Definition: parse.hpp:142
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
parser::identifier
Definition: parse.hpp:37

bool parser::get_unary | ( | double & | result | ) |
| private

Parse a unary expression.

    UNARY ::= '-' PRIMARY | '+' PRIMARY | PRIMARY

Parameters

  result | Store the result here

Returns
  true to continue parsing or false to stop (end of file or error)

Definition at line 220 of file parse.cpp.

References get_primary(), get_token(), and push_back().

Referenced by get_mul_expr().

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 }
parser::get_primary
bool get_primary(double &result)
Definition: parse.cpp:239
parser::push_back
void push_back(std::string const &token, kind k)
Definition: parse.hpp:119
parser::get_token
kind get_token(std::string &token)
Definition: parse.cpp:62
parser::kind
kind
Definition: parse.hpp:37

bool parser::isalnum | ( | char | c | ) | const
| inline private

Return true if `c` is alphanumeric. Use the locale of the input stream.

Parameters

  c | The character to test.

Definition at line 130 of file parse.hpp.

References ctype_.

Referenced by get_identifier().

130 { return ctype_.is(ctype_.alnum, c); }
parser::ctype_
std::ctype< char > const & ctype_
Cache the ctype facet for checking character categories.
Definition: parse.hpp:143

bool parser::isalpha | ( | char | c | ) | const
| inline private

Return true if `c` is alphabetic. Use the locale of the input stream.

Parameters

  c | The character to test.

Definition at line 125 of file parse.hpp.

References ctype_.

Referenced by get_identifier(), and get_token().

125 { return ctype_.is(ctype_.alpha, c); }
parser::ctype_
std::ctype< char > const & ctype_
Cache the ctype facet for checking character categories.
Definition: parse.hpp:143

bool parser::isdigit | ( | char | c | ) | const
| inline private

Return true if `c` is a digit. Use the locale of the input stream.

Parameters

  c | The character to test.

Definition at line 135 of file parse.hpp.

References ctype_.

135 { return ctype_.is(ctype_.digit, c); }
parser::ctype_
std::ctype< char > const & ctype_
Cache the ctype facet for checking character categories.
Definition: parse.hpp:143

bool parser::isprint | ( | char | c | ) | const
| inline private

Return true if `c` is printable. Use the locale of the input stream.

Parameters

  c | The character to test.

Definition at line 140 of file parse.hpp.

References ctype_.

Referenced by charify().

140 { return ctype_.is(ctype_.print, c); }
parser::ctype_
std::ctype< char > const & ctype_
Cache the ctype facet for checking character categories.
Definition: parse.hpp:143

void parser::push_back | ( | std::string const & | token,
|  | kind | k
| ) |  |
| inline private

Push back a token. The next call to get_token() will return the pushed-back token.

Parameters

  token | The token to push back.
  k | The kind of token to push back

Definition at line 119 of file parse.hpp.

References kind_, and token_.

Referenced by get_add_expr(), get_expr(), get_mul_expr(), and get_unary().

119 { token_ = token; kind_ = k; }
parser::token_
std::string token_
One token push-back.
Definition: parse.hpp:144
parser::kind_
kind kind_
The kind of token that was pushed back.
Definition: parse.hpp:145

## Member Data Documentation

std::ctype<char> const& parser::ctype_
| private

Cache the ctype facet for checking character categories.

Definition at line 143 of file parse.hpp.

Referenced by isalnum(), isalpha(), isdigit(), and isprint().

std::istream& parser::input_
| private

Share the input stream.

Definition at line 142 of file parse.hpp.

Referenced by get_identifier(), and get_token().

kind parser::kind_
| private

The kind of token that was pushed back.

Definition at line 145 of file parse.hpp.

Referenced by get_token(), and push_back().

std::string parser::token_
| private

One token push-back.

Definition at line 144 of file parse.hpp.

Referenced by get_token(), and push_back().

The documentation for this class was generated from the following files:

- parse.hpp
- parse.cpp