#include "expression.hpp" #include "print.h" #include #include #include #include namespace Parser { class Parser { enum State { I, // idle RC, // read constant RV, // read variable OP, // read operator } s; const char *state_str(enum State e) { switch (e) { case I: return "I"; case RC: return "RC"; case RV: return "RV"; case OP: return "OP"; default: return "Not a valid state!"; } } int switch_state(enum State e) { int cumulative_backtrack = 0; switch (s) { case I: cumulative_backtrack += leave_i(); break; case RC: cumulative_backtrack += leave_rc(); break; case RV: cumulative_backtrack += leave_rv(); break; case OP: cumulative_backtrack += leave_op(); break; default: pr_warn("Warning: broken state machine on leave"); break; } switch (e) { case I: enter_i(); break; case RC: enter_rc(); break; case RV: enter_rv(); break; case OP: enter_op(); break; default: pr_warn("Warning: broken state machine on enter"); break; } s = e; return cumulative_backtrack; } public: char buffer[256]; std::vector& pv; Parser(std::vector& pv) : s(I), pv(pv) { buf_clear(); } void buf_clear(void) { memset(buffer, 0, MAX_TOKEN_STRING_LEN); } void buf_push(char c) { if (int i = strlen(buffer); i < MAX_TOKEN_STRING_LEN - 1) { buffer[i] = c; } } int dispatch_to_current(char c) { switch (s) { case I: return parse_i(c); case RC: return parse_rc(c); case RV: return parse_rv(c); case OP: return parse_op(c); default: pr_warn("State machine is broken, unable to dispatch character " << c << ", returning 0"); return 0; } } void enter_i(void) {} int parse_i(char c) { if (isdigit(c) || c == '.') { return switch_state(RC) + 1; } else if (isalpha(c)) { return switch_state(RV) + 1; } else if (c == '+' || c == '-' || c == '*' || c == '/' || c == '(' || c == ')') { return switch_state(OP) + 1; } else { // as per requirements (report all wrong symbols) if (c != ' ' && c != '\0') { pr_warn("Unrecognized character: " << c); } return 0; } } int leave_i(void) { return 0; } void enter_rc(void) { buf_clear(); } int parse_rc(char c) { if (!(isdigit(c) || c == '.')) { return switch_state(I) + 1; } else { buf_push(c); return 0; } } int leave_rc(void) { // additional check as per requirements { int i, c; #if (MAX_TOKEN_STRING_LEN % 8) == 0 for (i = 0, c = 0; i < MAX_TOKEN_STRING_LEN; i += 8) { c += (buffer[i+0] == '.') + (buffer[i+1] == '.') + (buffer[i+2] == '.') + (buffer[i+3] == '.') + (buffer[i+4] == '.') + (buffer[i+5] == '.') + (buffer[i+6] == '.') + (buffer[i+7] == '.'); } #else for (int i = 0, c = 0; i < MAX_TOKEN_STRING_LEN; i++) { c += buffer[i] == '.'; } #endif if (c > 1) { pr_warn("Too many dots in CONST: " << buffer); return 0; } } double value; if (!sscanf(buffer, "%lf", &value)) { pr_warn("Invalid CONST: " << buffer); } else { pv.push_back(Expression::Token(value)); } return 0; } void enter_rv(void) { buf_clear(); } int parse_rv(char c) { if (!isalpha(c)) { return switch_state(I) + 1; } else { buf_push(c); return 0; } } int leave_rv(void) { pv.push_back(Expression::Token(buffer)); return 0; } void enter_op(void) { buf_clear(); } int parse_op(char c) { if (!(c == '+' || c == '-' || c == '*' || c == '/' || c == '(' || c == ')')) { return switch_state(I) + 1; } else { buf_push(c); return 0; } } int leave_op(void) { int i, downsize; for (i = strlen(buffer), downsize = 0; i > 0; buffer[--i] = '\0', downsize++) { if (!strcmp(buffer, "(")) { pv.push_back(Expression::Token(Expression::Token::OPA)); break; } else if (!strcmp(buffer, ")")) { pv.push_back(Expression::Token(Expression::Token::CPA)); break; } else if (!strcmp(buffer, "+")) { pv.push_back(Expression::Token(Expression::Token::ADD)); break; } else if (!strcmp(buffer, "-")) { pv.push_back(Expression::Token(Expression::Token::SUB)); break; } else if (!strcmp(buffer, "*")) { pv.push_back(Expression::Token(Expression::Token::MUL)); break; } else if (!strcmp(buffer, "/")) { pv.push_back(Expression::Token(Expression::Token::DIV)); break; } else if (!strcmp(buffer, "**")) { pv.push_back(Expression::Token(Expression::Token::POW)); break; } } if (i) { return downsize; } else { pr_warn("Skip unknown operator: " << buffer); return 0; } } }; std::vector tokenize_input(const char * const input_string) { std::vector pv; Parser p(pv); for (int i = 0, l = strlen(input_string) + 1; i < l; i++) { i -= p.dispatch_to_current(input_string[i]); } return pv; } int assert_same(const std::vector& a, const std::vector& b) { if (a.size() != b.size()) { std::cout << "Vectors are of different lengths: " << a.size() << " != " << b.size() << "\n"; return 1; } for (int i = 0, l = a.size(); i < l; i++) { if (a[i].t != b[i].t) { std::cout << "Token " << i << " types differ: " << a[i].type_str() << " != " << b[i].type_str() << "\n"; return 1; } switch (a[i].t) { case Expression::Token::VAR: if (strcmp((*(a[i].v.s)).c_str(), (*(b[i].v.s)).c_str())) { std::cout << "Variable names at " << i << " differ: " << (*(a[i].v.s)).c_str() << " != " << (*(b[i].v.s)).c_str() << "\n"; return 1; } break; case Expression::Token::CONST: if (a[i].v.d != b[i].v.d) { std::cout << "Constants at " << i << " differ: " << a[i].v.d << " != " << b[i].v.d << "\n"; return 1; } break; case Expression::Token::OPERATOR: if (a[i].v.o != b[i].v.o) { std::cout << "Operators at " << i << " differ: " << a[i].op_str() << " != " << b[i].op_str() << "\n"; return 1; } break; default: pr_warn("Token skipped"); break; } } return 0; } void print_token_vector(const std::vector& tv) { int i, l; for (i = 0, l = tv.size() - 1; i < l; i++) { std::cout << "(" << *tv[i].to_str() << ") "; } if (tv.size()) { std::cout << "(" << *tv[i].to_str() << ")\n"; } } void print_math_vector(const std::vector& tv) { int i, l; for (i = 0, l = tv.size() - 1; i < l; i++) { std::cout << *tv[i].to_math_str(); //<< " "; } if (tv.size()) { std::cout << *tv[i].to_math_str() << "\n"; } } void print_math_vector(const std::vector& tv, const std::vector& highlight_locations) { int i, l, h, hl; for (i = 0, l = tv.size() - 1, h = 0, hl = highlight_locations.size(); i < l; i++) { if (h < hl && highlight_locations[h] == i) { std::cout << "\033[31m" << *tv[i].to_math_str() << "\033[0m"; h++; } else { std::cout << *tv[i].to_math_str();; } } if (tv.size()) { if (highlight_locations[highlight_locations.size()-1] == (long) (tv.size() - 1)) { std::cout << "\033[31m" << *tv[tv.size() - 1].to_math_str() << "\033[0m\n"; } else { std::cout << *tv[tv.size() - 1].to_math_str() << "\n"; } } } }