#include <array> #include <cassert> #include <cctype> #include <functional> #include <iostream> #include <map> #include <string> #include <vector> using namespace std; enum { TOK_END, TOK_NUM, TOK_VAR, TOK_LP, TOK_RP, TOK_BIN, TOK_UN }; enum { ST_IF='f', ST_ELSE='l', ST_END='n', ST_WHILE='h', ST_SET='e', ST_PRINT='r', ST_NONE=0 }; struct BinOp { int prec; function<int(int, int)> func; BinOp(int prec, decltype(func) func) : prec{prec}, func{func} { } static constexpr int key1(const char* op) { return op[0] * 256; } static constexpr int key2(const char* op) { return key1(op) + op[1]; } }; const map<int, BinOp> binary_operators{ #define OP(sym, prec) {BinOp::key2(#sym), {prec, [](int a, int b) { return a sym b; }}} OP(*, 5), OP(/, 5), OP(%, 5), OP(+, 4), OP(-, 4), OP(<, 3), OP(<=, 3), OP(>, 3), OP(>=, 3), OP(==, 2), OP(!=, 2), OP(&&, 1), OP(||, 0), #undef OP }; const int max_prec = 6; const map<int, function<int(int)>> unary_operators{ #define OP(sym) {BinOp::key1(#sym), [](int a) { return sym a; }} OP(-), OP(!), #undef OP }; array<int, 26> vars; class ExprParser { public: static int eval_str(const string& expr) { ExprParser parser; parser.token_type = TOK_END; parser.line_ptr = expr.data(); parser.next_token(); return parser.eval_r(0); } private: int next_token() { int prev_value = token_value; while (isspace(*line_ptr)) ++line_ptr; if (!*line_ptr) { set_token(TOK_END, 0, 0); } else if (isdigit(*line_ptr)) { set_token(TOK_NUM, 0, 0); while (isdigit(*line_ptr)) token_value = token_value * 10 + *(line_ptr++) - '0'; } else if (islower(*line_ptr)) { set_token(TOK_VAR, *line_ptr - 'a', 1); } else if (*line_ptr == '(') { set_token(TOK_LP, 0, 1); } else if (*line_ptr == ')') { set_token(TOK_RP, 0, 1); } else if (token_type != TOK_NUM && token_type != TOK_VAR && token_type != TOK_RP) { assert(unary_operators.count(BinOp::key1(line_ptr))); set_token(TOK_UN, BinOp::key1(line_ptr), 1); } else if (binary_operators.count(BinOp::key2(line_ptr))) { set_token(TOK_BIN, BinOp::key2(line_ptr), 2); } else { assert(binary_operators.count(BinOp::key1(line_ptr))); set_token(TOK_BIN, BinOp::key1(line_ptr), 1); } return prev_value; } void set_token(int type, int value, int num_chars) { token_type = type; token_value = value; line_ptr += num_chars; } int eval_r(int min_prec) { int result, unop; switch (token_type) { case TOK_LP: next_token(); result = eval_r(0); next_token(); break; case TOK_UN: unop = next_token(); result = unary_operators.at(unop)(eval_r(max_prec)); break; case TOK_NUM: result = next_token(); break; case TOK_VAR: result = vars[next_token()]; break; default: assert(!"parse error"); } const BinOp* op; while (token_type == TOK_BIN && (op = &binary_operators.at(token_value))->prec >= min_prec) { next_token(); result = op->func(result, eval_r(op->prec + 1)); } return result; } int token_type, token_value; const char *line_ptr; }; template <typename Stmt> struct Block { vector<Stmt*> statements; Block() = default; Block(Block&&) = default; Block(const Block&) = delete; ~Block() { for (auto* stmt : statements) delete stmt; } void operator=(const Block&) = delete; Block& operator=(Block&&) = default; void run() { for (auto* stmt : statements) stmt->run(); } bool empty() const { return statements.empty(); } }; struct Statement { int type; Block<Statement> body1, body2; int varidx; string expr; int eval_expr() const { return ExprParser::eval_str(expr); } void run() { switch (type) { case ST_IF: if (eval_expr()) body1.run(); else body2.run(); break; case ST_WHILE: while (eval_expr()) body1.run(); break; case ST_SET: vars[varidx] = eval_expr(); break; case ST_PRINT: cout << eval_expr() << '\n'; break; } } }; class StatementParser { public: static Block<Statement> parse_program() { StatementParser parser; cin >> parser.lines_remain; getline(cin, parser.line); parser.next_line(); return parser.parse_block(); } private: Block<Statement> parse_block() { Block<Statement> result; while (type != ST_NONE && type != ST_ELSE && type != ST_END) { result.statements.push_back(parse_statement()); } return result; } Statement* parse_statement() { Statement* result = new Statement; result->type = type; switch (type) { case ST_IF: result->expr = line_ptr + 2; next_line(); result->body1 = parse_block(); if (type == ST_ELSE) { next_line(); result->body2 = parse_block(); } break; case ST_WHILE: result->expr = line_ptr + 5; next_line(); result->body1 = parse_block(); break; case ST_SET: line_ptr += 3; while (isspace(*line_ptr)) ++line_ptr; result->varidx = line_ptr[0] - 'a'; line_ptr += 1; while (isspace(*line_ptr)) ++line_ptr; result->expr = line_ptr + 1; break; case ST_PRINT: result->expr = line_ptr + 5; break; default: assert(!"parse error"); } next_line(); return result; } void next_line() { if (lines_remain) { --lines_remain; getline(cin, line); line_ptr = line.data(); while (isspace(*line_ptr)) ++line_ptr; type = line_ptr[1]; } else { type = ST_NONE; } } int lines_remain; string line; const char* line_ptr; int type; }; int main() { for (;;) { auto prog = StatementParser::parse_program(); if (prog.empty()) break; vars.fill(0); prog.run(); } }