Study results from Stroustrup's PPP3 book.
This commit is contained in:
parent
6363457d0f
commit
7a1093f982
10 changed files with 579 additions and 1 deletions
233
PPP3/ex06.cpp
Normal file
233
PPP3/ex06.cpp
Normal file
|
@ -0,0 +1,233 @@
|
|||
#include <iostream>
|
||||
#include <queue>
|
||||
using namespace std;
|
||||
|
||||
class Error {
|
||||
public:
|
||||
string message;
|
||||
|
||||
Error(string m) : message{m} {};
|
||||
};
|
||||
|
||||
enum TokenKind {
|
||||
SEMI_COLON,
|
||||
QUIT,
|
||||
LPAREN,
|
||||
RPAREN,
|
||||
PLUS,
|
||||
MINUS,
|
||||
MULT,
|
||||
DIV,
|
||||
NUMBER,
|
||||
NAME,
|
||||
EQ,
|
||||
ERROR
|
||||
};
|
||||
|
||||
|
||||
class Token {
|
||||
public:
|
||||
TokenKind kind = 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:
|
||||
Token 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{SEMI_COLON};
|
||||
case 'q': return Token{QUIT};
|
||||
case '(': return Token{LPAREN};
|
||||
case ')': return Token{RPAREN};
|
||||
case '+': return Token{PLUS};
|
||||
case '-': return Token{MINUS};
|
||||
case '*': return Token{MULT};
|
||||
case '/': return Token{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);
|
||||
double val = 0;
|
||||
cin >> val;
|
||||
return Token{NUMBER, val};
|
||||
}
|
||||
case '=':
|
||||
return Token{EQ};
|
||||
default:
|
||||
if(isalpha(ch)) {
|
||||
cin.putback(ch);
|
||||
string s;
|
||||
cin >> s;
|
||||
return Token{NAME, s};
|
||||
} else {
|
||||
throw Error{"bad token"};
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void putback(Token t) {
|
||||
buffer.push(t);
|
||||
}
|
||||
|
||||
private:
|
||||
queue<Token> buffer;
|
||||
};
|
||||
|
||||
class Variable {
|
||||
public:
|
||||
string name;
|
||||
double value;
|
||||
};
|
||||
|
||||
vector<Variable> 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) {
|
||||
Token lval = stream.get();
|
||||
Token op = stream.get();
|
||||
Token rval = stream.get();
|
||||
double result = 0;
|
||||
|
||||
// parens start a sub-expression
|
||||
if(rval.kind == LPAREN) {
|
||||
rval = expression(stream);
|
||||
// eat the RPAREN
|
||||
Token rparen = stream.get();
|
||||
if(rparen.kind != RPAREN) {
|
||||
throw Error{"expected RPAREN"};
|
||||
}
|
||||
} else if(rval.kind == NAME) {
|
||||
double var_val = get_value(rval.name);
|
||||
rval = Token{NUMBER, var_val};
|
||||
}
|
||||
|
||||
switch(op.kind) {
|
||||
case PLUS:
|
||||
result = lval.value + rval.value;
|
||||
break;
|
||||
case MINUS:
|
||||
result = lval.value - rval.value;
|
||||
break;
|
||||
case DIV:
|
||||
result = lval.value / rval.value;
|
||||
break;
|
||||
case MULT:
|
||||
result = lval.value * rval.value;
|
||||
default:
|
||||
throw Error{"invalid syntax in expresion"};
|
||||
}
|
||||
|
||||
return Token{NUMBER, result};
|
||||
}
|
||||
|
||||
|
||||
Token parse(TokenStream &stream) {
|
||||
Token tok = stream.get();
|
||||
|
||||
switch(tok.kind) {
|
||||
case NUMBER:
|
||||
stream.putback(tok);
|
||||
return expression(stream);
|
||||
case NAME: {
|
||||
Token eq = stream.get();
|
||||
|
||||
if(eq.kind == EQ) {
|
||||
Token val = stream.get();
|
||||
if(val.kind != NUMBER) throw Error{"invalid value in variable assign"};
|
||||
|
||||
set_value(tok.name, val.value);
|
||||
return val;
|
||||
} else {
|
||||
double var_val = get_value(tok.name);
|
||||
stream.putback(Token{NUMBER, var_val});
|
||||
stream.putback(eq); // push the next token back on
|
||||
return expression(stream);
|
||||
}
|
||||
}
|
||||
case LPAREN: {
|
||||
Token val = expression(stream);
|
||||
tok = stream.get();
|
||||
if(tok.kind != RPAREN) {
|
||||
throw Error{"missing rparen"};
|
||||
}
|
||||
return val;
|
||||
}
|
||||
case QUIT:
|
||||
return tok;
|
||||
default:
|
||||
cout << "Got token " << tok.kind << ":" << tok.value << " expected NUMBER or LPAREN\n";
|
||||
throw Error{"invalid syntax in parse"};
|
||||
}
|
||||
|
||||
return Token{ERROR};
|
||||
}
|
||||
|
||||
int main() {
|
||||
TokenStream stream;
|
||||
|
||||
while(cin) {
|
||||
try {
|
||||
Token val = parse(stream);
|
||||
|
||||
if(val.kind == ERROR) {
|
||||
cout << "Parse returned an error token.\n";
|
||||
} else if(val.kind == QUIT) {
|
||||
cout << "Goodbye!\n";
|
||||
return 0;
|
||||
} else {
|
||||
cout << "=" << val.value << "\n";
|
||||
}
|
||||
} catch (Error &err) {
|
||||
cout << "ERROR " << err.message << "\n";
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
Loading…
Add table
Add a link
Reference in a new issue