#include "asm.h" #include "eval.h" #include #include #define CLASS TEvaluator #define DEF_VAL 0 std::ostream& operator<<(std::ostream& os, const Token& token) { os << token.str; return os; } CLASS::CLASS(T65816Asm &_asm) : assembler(_asm) { allowMX = false; } CLASS::~CLASS() { } std::deque CLASS::exprToTokens(const std::string& expr) { std::deque tokens; int state = 0; char c; char delim; std::string ident; //, asc; std::string ops = "+-*//^!.&()"; std::string c1; char *tokptr; char *tptr; bool numexpect; //bool highascii = false; Token::Type t; delim = 0; numexpect = true; for (const auto* p = expr.c_str(); *p; ++p) { c = *p; c1 = c; tptr = (char *)c1.c_str(); tokptr = strpbrk(tptr, (const char *)ops.c_str()); // printf("state=%d %c %p\n", state, c,tokptr); switch (state) { default: printf("bad token state\n"); state = 0; break; case 11: if ((c < ' ') || (c == delim)) { // SGQ - convert ascii to a number here //asc = "0"; //printf("ascii ident=|%s|\n", ident.c_str()); if (ident.length() > 0) { // SGQ - convert ascii to a number here } ident = delim + ident + delim; t = Token::Type::Ascii; int pr = 1; // precedence bool ra = false; tokens.push_back(Token { t, ident, pr, ra }); ident = ""; state = 0; if (c != delim) { p--; } //highascii = false; delim = 0; } else { ident += c; } break; case 10: case 20: if ((c <= ' ') || (tokptr != NULL)) { if (ident.length() > 0) { if (state == 20) { t = Token::Type::Symbol; } else { t = Token::Type::Number; } int pr = 1; // precedence bool ra = false; // rightAssociative tokens.push_back(Token { t, ident, pr, ra }); ident = ""; } state = 0; p--; } else { ident += c; } break; case 0: if ((c == '$') && (numexpect)) { state = 10; ident += c; numexpect = false; } else if ((numexpect) && ( (c == '^') || (c == '<') || (c == '>') || (c == '|'))) { ident = c; tokens.push_back(Token{Token::Type::Shift, ident, 1, true}); ident = ""; } else if ((c == '*') && (numexpect)) { numexpect = false; state = 0; ident += c; tokens.push_back(Token{Token::Type::Symbol, ident, 1, false}); ident = ""; } else if ((c == '%') && (numexpect)) { state = 10; ident += c; numexpect = false; } else if ((c == '\'') && (numexpect)) { delim = c; state = 11; numexpect = false; } else if ((c == '"') && (numexpect)) { delim = c; state = 11; //highascii = true; numexpect = false; } else if (((c == '-') || (c == '+')) && (numexpect)) { state = 10; ident += c; } else if (isdigit(c)) { state = 10; ident += c; numexpect = false; } else if (c >= ':') { state = 20; ident += c; numexpect = false; } else if ((tokptr != NULL) && (!numexpect)) { t = Token::Type::Unknown; int pr = -1; // precedence bool ra = false; // rightAssociative switch (c) { default: break; case '(': t = Token::Type::LeftParen; break; case ')': t = Token::Type::RightParen; break; case '!': t = Token::Type::Operator; pr = 5; break; case '.': t = Token::Type::Operator; pr = 5; break; case '&': t = Token::Type::Operator; pr = 5; break; case '^': t = Token::Type::Operator; pr = 4; ra = true; break; case '*': t = Token::Type::Operator; pr = 3; break; case '/': t = Token::Type::Operator; pr = 3; break; case '+': t = Token::Type::Operator; pr = 2; break; case '-': t = Token::Type::Operator; pr = 2; break; } tokens.push_back(Token { t, std::string(1, c), pr, ra }); numexpect = true; } } } return tokens; } std::deque CLASS::shuntingYard(const std::deque& tokens) { std::deque queue; std::vector stack; TSymbol *sym; char buff[128]; // While there are tokens to be read: for (auto token : tokens) { // Read a token switch (token.type) { case Token::Type::Symbol: token.type = Token::Type::Number; if (token.str == "*") { sprintf(buff, "$%X", assembler.PC.currentpc); token.str = buff; } else { std::string tok = Poco::toUpper(token.str); if ((tok == "MX") && (allowMX)) { //printf("MX EVAL\n"); sprintf(buff,"$%02X",assembler.mx&0x03); token.str=buff;; } else { sym = assembler.findSymbol(token.str); //printf("symbol find |%s| %p\n",token.str.c_str(),sym); if (sym != NULL) { sym->used = true; sprintf(buff, "$%X", sym->value); token.str = buff; } else { setError(Token::unknownSymbolErr); badsymbol = token.str; token.str = "0"; } } } queue.push_back(token); break; case Token::Type::Ascii: case Token::Type::Number: // If the token is a number, then add it to the output queue queue.push_back(token); break; case Token::Type::Shift: stack.push_back(token); break; case Token::Type::Operator: { // If the token is operator, o1, then: const auto o1 = token; // while there is an operator token, while (!stack.empty()) { // o2, at the top of stack, and const auto o2 = stack.back(); // either o1 is left-associative and its precedence is // *less than or equal* to that of o2, // or o1 if right associative, and has precedence // *less than* that of o2, if ( (! o1.rightAssociative && o1.precedence <= o2.precedence) || ( o1.rightAssociative && o1.precedence < o2.precedence) ) { // then pop o2 off the stack, stack.pop_back(); // onto the output queue; queue.push_back(o2); continue; } // @@ otherwise, exit. break; } // push o1 onto the stack. stack.push_back(o1); } break; case Token::Type::LeftParen: // If token is left parenthesis, then push it onto the stack stack.push_back(token); break; case Token::Type::RightParen: // If token is right parenthesis: { bool match = false; while (! stack.empty()) { // Until the token at the top of the stack // is a left parenthesis, const auto tos = stack.back(); if (tos.type != Token::Type::LeftParen) { // pop operators off the stack stack.pop_back(); // onto the output queue. queue.push_back(tos); } // Pop the left parenthesis from the stack, // but not onto the output queue. stack.pop_back(); match = true; break; } if (!match && stack.empty()) { // If the stack runs out without finding a left parenthesis, // then there are mismatched parentheses. //printf("RightParen error (%s)\n", token.str.c_str()); setError(Token::operatorErr); return queue; } } break; default: setError(Token::syntaxErr); //printf("error (%s)\n", token.str.c_str()); return queue; break; } //debugReport(token, queue, stack); } // When there are no more tokens to read: // While there are still operator tokens in the stack: while (! stack.empty()) { // If the operator token on the top of the stack is a parenthesis, // then there are mismatched parentheses. if (stack.back().type == Token::Type::LeftParen) { setError(Token::parenErr); //printf("Mismatched parentheses error\n"); return queue; } // Pop the operator onto the output queue. queue.push_back(std::move(stack.back())); stack.pop_back(); } //debugReport(Token { Token::Type::Unknown, "End" }, queue, stack); //Exit. return queue; } int CLASS::parseAscii(std::string n, int64_t &val) { int res = -1; val = 0; bool err = false; uint64_t tval = 0; bool high = false; uint8_t c; uint32_t l = (uint32_t)n.length(); for (uint32_t i = 0; i < l - 1; i++) { c = n[i]; if (i == 0) { if (c == '"') { high = true; } } else { tval <<= 8; if (high) { c |= 0x80; } else { c &= 0x7F; } tval = ((tval & 0xFFFFFF00) | c); } } if (!err) { val = (uint32_t)(tval & 0xFFFFFFFF); res = 0; } //printf("parseASCII |%s| %d %016lX\n", n.c_str(), res, val); return (res); } int CLASS::parseNumber(std::string n, int64_t &val) { int res = -1; int state = 0; char c; std::string s; uint32_t i, l; bool valid = false; bool err = false; bool neg = false; int64_t tval = 0; val = 0; //printf("parseNumber |%s|\n",n.c_str()); i = 0; l = (uint32_t)n.length(); s = ""; for (i = 0; i < l; i++) { c = n[i]; switch (state) { case 0: if (c == '$') { state = 10; } else if (c == '%') { state = 20; } else if (c == '-') { if (!valid) { neg = !neg; } else { state = 99; } } else if (isdigit(c)) { s += c; valid = true; state = 1; tval = c - '0'; } else { state = 99; } break; case 1: if (isdigit(c)) { valid = true; s += c; tval *= 10; tval += c - '0'; } else { state = 99; } break; case 10: if ((c >= 'a') && (c <= 'f')) { c = c - 0x20; // make it uppercase s += c; tval <<= 4; tval |= (c - 'A') + 10; valid = true; } else if ((c >= 'A') && (c <= 'F')) { s += c; tval <<= 4; tval |= (c - 'A') + 10; valid = true; } else if ((c >= '0') && (c <= '9')) { s += c; tval <<= 4;; tval += c - '0'; valid = true; } else { state = 99; } break; case 20: if ((c >= '0') && (c <= '1')) { s += c; tval <<= 1; if (c == '1') { tval |= 1; } valid = true; } else if (c == '_') { // allow these in binary } else { state = 99; } break; case 99: err = true; // if you get into this state there is an error break; } } uint32_t tv = (uint32_t)tval; uint64_t tv1 = tv; if (tv1 > (int64_t)0xFFFFFFFF) { setError(Token::overflowErr); } if ((state == 99) || (err)) { setError(Token::syntaxErr); valid = false; val = DEF_VAL; } if ((valid) && (!err)) { if (neg) { tval = -tval; } val = tval; //printf("value=%08lX\n", val); res = 0; } if (res != 0) { if (isDebug() > 2) { printf("parsenumber error result: %d\n", res); } } return (res); } void CLASS::setError(int ecode) { if ((evalerror == Token::noError) || (ecode == Token::noError)) { evalerror = ecode; } if (evalerror == Token::noError) { badsymbol = ""; } } int CLASS::evaluate(std::string & e, int64_t &res, uint8_t &_shiftmode) { // const std::string expr = "3+4*2/(1-5)^2^3"; // Wikipedia's example // const std::string expr = "20-30/3+4*2^3"; _shiftmode = shiftmode = 0; res = DEF_VAL; setError(Token::noError); int u; int64_t val; std::string expr = Poco::trim(e); expr += " "; // add a space at end to make parsing easier if (isDebug() >= 3) { printf("eval: expression: |%s|\n", expr.c_str()); } const auto tokens = exprToTokens(expr); auto queue = shuntingYard(tokens); std::vector stack; // printf("\nCalculation\n"); //printf("|%-3s|%-32s|%-10s|\n", "Tkn", "Queue", "Stack"); while (! queue.empty()) { //std::string op; const auto token = queue.front(); queue.pop_front(); switch (token.type) { case Token::Type::Symbol: stack.push_back(std::stoi((char *)"0")); //op = "Push " + token.str; //printf("shouldn't get this kind of token\n"); break; case Token::Type::Ascii: val = 0; u = parseAscii(token.str, val); if (u < 0) { setError(Token::numberErr); val = DEF_VAL; } stack.push_back(val); //op = "Push " + token.str; break; case Token::Type::Number: val = 0; u = parseNumber(token.str, val); if (u < 0) { setError(Token::numberErr); val = DEF_VAL; } stack.push_back(val); //op = "Push " + token.str; break; case Token::Type::Shift: { auto rhs = DEF_VAL; if (stack.size() > 0) { rhs = stack.back(); stack.pop_back(); shiftmode = token.str[0]; if (token.str == "^") { //rhs = (rhs >> 16) &0xFFFF ; } else if (token.str == "|") { //rhs = (rhs >> 16) & 0xFFFF; } else if (token.str == "<") { //rhs = (rhs) & 0xFFFF; } else if (token.str == ">") { //rhs=(rhs>>8) & 0xFFFF; } stack.push_back(rhs); } else { //printf("nothing on stack\n"); } } break; case Token::Type::Operator: { auto rhs = DEF_VAL; auto lhs = DEF_VAL; bool v = true; if (stack.size() > 0) { rhs = stack.back(); stack.pop_back(); } else { v = false; } if (stack.size() > 0) { lhs = stack.back(); stack.pop_back(); } else { v = false; } if (!v) { setError(Token::badParamErr); //printf("not enough parameters for the operator\n"); } switch (token.str[0]) { default: setError(Token::operatorErr); //printf("Operator error [%s]\n", token.str.c_str()); return (-1); break; case '^': stack.push_back(static_cast(pow(lhs, rhs))); break; case '*': stack.push_back(lhs * rhs); break; case '/': if (rhs != 0) { stack.push_back(lhs / rhs); } else { stack.push_back(0); } break; case '+': stack.push_back(lhs + rhs); break; case '-': stack.push_back(lhs - rhs); break; case '!': stack.push_back(lhs ^ rhs); break; case '&': stack.push_back(lhs & rhs); break; break; case '.': stack.push_back(lhs | rhs); break; } } break; default: //printf("Token error\n"); setError(Token::syntaxErr); goto out; } } out: int64_t v = DEF_VAL; if (stack.size() > 0) { v = stack.back(); } else { setError(Token::syntaxErr); } _shiftmode = shiftmode; res = v; return (evalerror); }