Calculator  Step 2
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.

Constructor & Destructor Documentation

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

Constructor. Save the input stream.

Parameters
inputThe 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 {}
std::string token_
One token push-back.
Definition: parse.hpp:144
std::istream & input_
Share the input stream.
Definition: parse.hpp:142
kind kind_
The kind of token that was pushed back.
Definition: parse.hpp:145
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
cThe 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 }
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
resultStore 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 }
void push_back(std::string const &token, kind k)
Definition: parse.hpp:119
bool get_mul_expr(double &result)
Definition: parse.cpp:196
kind get_token(std::string &token)
Definition: parse.cpp:62
kind
Definition: parse.hpp:37
bool parser::get_expr ( double &  result)

Read one expression and store the result in result.

Parameters
resultWhere to store the result of the expression.
Returns
true to continue or false to end the loop
Exceptions
parse_errorfor 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 }
void set_variable(std::string name, double value)
Definition: variables.cpp:23
void push_back(std::string const &token, kind k)
Definition: parse.hpp:119
kind get_token(std::string &token)
Definition: parse.cpp:62
bool get_add_expr(double &result)
Definition: parse.cpp:174
kind
Definition: parse.hpp:37
void parser::get_identifier ( std::string &  identifier)
private

Parse an identifer.

Parameters
identifierStore 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 }
std::string charify(char c)
Definition: parse.cpp:12
bool isalpha(char c) const
Definition: parse.hpp:125
std::istream & input_
Share the input stream.
Definition: parse.hpp:142
bool isalnum(char c) const
Definition: parse.hpp:130
bool parser::get_mul_expr ( double &  result)
private

Parse a multiplicative expression.

MUL_EXPR ::= UNARY | MUL_EXPR + UNARY | MUL_EXPR - UNARY
Parameters
resultStore 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 }
void push_back(std::string const &token, kind k)
Definition: parse.hpp:119
kind get_token(std::string &token)
Definition: parse.cpp:62
bool get_unary(double &result)
Definition: parse.cpp:220
kind
Definition: parse.hpp:37
bool parser::get_number ( std::string const &  token,
double &  result 
)
private

Parse a floating number.

Parameters
tokenThe token to parse
resultStore 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
resultStore 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 }
kind get_token(std::string &token)
Definition: parse.cpp:62
bool get_number(std::string const &token, double &result)
Definition: parse.cpp:135
bool get_expr(double &result)
Definition: parse.cpp:142
kind
Definition: parse.hpp:37
double get_variable(std::string const &name)
Definition: variables.cpp:18
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
tokenStore 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 }
void get_identifier(std::string &identifier)
Definition: parse.cpp:36
std::string charify(char c)
Definition: parse.cpp:12
bool isalpha(char c) const
Definition: parse.hpp:125
std::string token_
One token push-back.
Definition: parse.hpp:144
std::istream & input_
Share the input stream.
Definition: parse.hpp:142
kind kind_
The kind of token that was pushed back.
Definition: parse.hpp:145
kind
Definition: parse.hpp:37
bool parser::get_unary ( double &  result)
private

Parse a unary expression.

UNARY ::= '-' PRIMARY | '+' PRIMARY | PRIMARY
Parameters
resultStore 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 }
bool get_primary(double &result)
Definition: parse.cpp:239
void push_back(std::string const &token, kind k)
Definition: parse.hpp:119
kind get_token(std::string &token)
Definition: parse.cpp:62
kind
Definition: parse.hpp:37
bool parser::isalnum ( char  c) const
inlineprivate

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

Parameters
cThe character to test.

Definition at line 130 of file parse.hpp.

References ctype_.

Referenced by get_identifier().

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

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

Parameters
cThe 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); }
std::ctype< char > const & ctype_
Cache the ctype facet for checking character categories.
Definition: parse.hpp:143
bool parser::isdigit ( char  c) const
inlineprivate

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

Parameters
cThe character to test.

Definition at line 135 of file parse.hpp.

References ctype_.

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

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

Parameters
cThe character to test.

Definition at line 140 of file parse.hpp.

References ctype_.

Referenced by charify().

140 { return ctype_.is(ctype_.print, c); }
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 
)
inlineprivate

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

Parameters
tokenThe token to push back.
kThe 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; }
std::string token_
One token push-back.
Definition: parse.hpp:144
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: