/* * Same code as ex06.cpp but I use auto everywhere I could to * see how it works. */ #include #include using namespace std; class Error { public: string message; Error(string m) : message{m} {}; }; enum class TokenKind { SEMI_COLON, QUIT, LPAREN, RPAREN, PLUS, MINUS, MULT, DIV, NUMBER, NAME, EQ, ERROR }; class Token { public: TokenKind kind = TokenKind::ERROR; double value = 0.0; string name = ""; Token(TokenKind k) : kind(k) {} Token(TokenKind k, double v) : kind{k}, value{v} {} Token(TokenKind k, string n) : kind{k}, name{n}{} }; class TokenStream { public: auto get() { if(!buffer.empty()) { auto first = buffer.front(); buffer.pop(); return first; } char ch = 0; if(!(cin >> ch)) throw Error{"no input"}; switch(ch) { case ';': return Token{TokenKind::SEMI_COLON}; case 'q': return Token{TokenKind::QUIT}; case '(': return Token{TokenKind::LPAREN}; case ')': return Token{TokenKind::RPAREN}; case '+': return Token{TokenKind::PLUS}; case '-': return Token{TokenKind::MINUS}; case '*': return Token{TokenKind::MULT}; case '/': return Token{TokenKind::DIV}; case '.': case '0': // fallthrough case '1': // fallthrough case '2': // fallthrough case '3': // fallthrough case '4': case '5': // fallthrough case '6': // fallthrough case '7': // fallthrough case '8': // fallthrough case '9': // falltrhough { cin.putback(ch); auto val = 0.0; cin >> val; return Token{TokenKind::NUMBER, val}; } case '=': return Token{TokenKind::EQ}; default: if(isalpha(ch)) { cin.putback(ch); string s; cin >> s; return Token{TokenKind::NAME, s}; } else { throw Error{"bad token"}; } } } void putback(Token t) { buffer.push(t); } private: queue buffer; }; class Variable { public: string name; double value; }; vector var_table; double get_value(string s) { for(const Variable& v : var_table) { if(v.name == s) { return v.value; } } throw Error{"variable not found"}; } void set_value(string s, double d) { for(Variable& v : var_table) { if(v.name == s) { v.value = d; return; } } // variable not found make it var_table.push_back(Variable{s, d}); } Token parse(TokenStream &stream); Token expression(TokenStream &stream) { auto lval = stream.get(); auto op = stream.get(); auto rval = stream.get(); double result = 0; // parens start a sub-expression if(rval.kind == TokenKind::LPAREN) { rval = expression(stream); // eat the RPAREN auto rparen = stream.get(); if(rparen.kind != TokenKind::RPAREN) { throw Error{"expected RPAREN"}; } } else if(rval.kind == TokenKind::NAME) { auto var_val = get_value(rval.name); rval = Token{TokenKind::NUMBER, var_val}; } switch(op.kind) { case TokenKind::PLUS: result = lval.value + rval.value; break; case TokenKind::MINUS: result = lval.value - rval.value; break; case TokenKind::DIV: result = lval.value / rval.value; break; case TokenKind::MULT: result = lval.value * rval.value; default: throw Error{"invalid syntax in expresion"}; } return Token{TokenKind::NUMBER, result}; } Token parse(TokenStream &stream) { auto tok = stream.get(); switch(tok.kind) { case TokenKind::NUMBER: stream.putback(tok); return expression(stream); case TokenKind::NAME: { auto eq = stream.get(); if(eq.kind == TokenKind::EQ) { auto val = stream.get(); if(val.kind != TokenKind::NUMBER) throw Error{"invalid value in variable assign"}; set_value(tok.name, val.value); return val; } else { auto var_val = get_value(tok.name); stream.putback(Token{TokenKind::NUMBER, var_val}); stream.putback(eq); // push the next token back on return expression(stream); } } case TokenKind::LPAREN: { auto val = expression(stream); tok = stream.get(); if(tok.kind != TokenKind::RPAREN) { throw Error{"missing rparen"}; } return val; } case TokenKind::QUIT: return tok; default: cout << "Got token " << ":" << tok.value << " expected NUMBER or LPAREN\n"; throw Error{"invalid syntax in parse"}; } return Token{TokenKind::ERROR}; } int main() { TokenStream stream; while(cin) { try { auto val = parse(stream); if(val.kind == TokenKind::ERROR) { cout << "Parse returned an error token.\n"; } else if(val.kind == TokenKind::QUIT) { cout << "Goodbye!\n"; return 0; } else { cout << "=" << val.value << "\n"; } } catch (Error &err) { cout << "ERROR " << err.message << "\n"; } } return 0; }