Playing with a comparison of python vs. c++ for something simple. stats.py reads lines on stdin and outputs some json, and goc.cpp reads stdin and outputs csv. I may just go with csv for the project but I'll see what json is like in C++ too.
This commit is contained in:
parent
6a777e4c2d
commit
567ffec4ac
7 changed files with 314 additions and 0 deletions
238
PPP3/ex07.cpp
Normal file
238
PPP3/ex07.cpp
Normal file
|
@ -0,0 +1,238 @@
|
|||
/*
|
||||
* Same code as ex06.cpp but I use auto everywhere I could to
|
||||
* see how it works.
|
||||
*/
|
||||
|
||||
#include <iostream>
|
||||
#include <queue>
|
||||
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<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) {
|
||||
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;
|
||||
}
|
Loading…
Add table
Add a link
Reference in a new issue