TokenQueue_t calculator::toRPN(const char* expr, TokenMap vars, const char* delim, const char** rest, OppMap_t opPrecedence, rWordMap_t rWordMap) { rpnBuilder data(vars, opPrecedence); char* nextChar; static char c = '\0'; if (!delim) delim = &c; while (*expr && isspace(*expr) && !strchr(delim, *expr)) ++expr; if (*expr == '\0' || strchr(delim, *expr)) { throw std::invalid_argument("Cannot build a calculator from an empty expression!"); } // In one pass, ignore whitespace and parse the expression into RPN // using Dijkstra's Shunting-yard algorithm. while (*expr && (data.bracketLevel || !strchr(delim, *expr))) { if (isdigit(*expr)) { // If the token is a number, add it to the output queue. int64_t _int = strtol(expr, &nextChar, 10); // If the number was not a float: if (!strchr(".eE", *nextChar)) { data.handle_token(new Token<int64_t>(_int, INT)); } else { double digit = strtod(expr, &nextChar); data.handle_token(new Token<double>(digit, REAL)); } expr = nextChar; } else if (isvariablechar(*expr)) { rWordMap_t::iterator it; // If the token is a variable, resolve it and // add the parsed number to the output queue. std::stringstream ss; ss << *expr; ++expr; while (isvariablechar(*expr) || isdigit(*expr)) { ss << *expr; ++expr; } std::string key = ss.str(); if (data.lastTokenWasOp == '.') { data.handle_token(new Token<std::string>(key, STR)); } else if ((it=rWordMap.find(key)) != rWordMap.end()) { // Parse reserved words: try { it->second(expr, &expr, &data); } catch (...) { rpnBuilder::cleanRPN(&data.rpn); throw; } } else { packToken* value = vars.find(key); if (value) { // Save a reference token: TokenBase* copy = (*value)->clone(); data.handle_token(new RefToken(key, copy)); } else { // Save the variable name: data.handle_token(new Token<std::string>(key, VAR)); } } } else if (*expr == '\'' || *expr == '"') { // If it is a string literal, parse it and // add to the output queue. char quote = *expr; ++expr; std::stringstream ss; while (*expr && *expr != quote && *expr != '\n') { if (*expr == '\\') { switch (expr[1]) { case 'n': expr+=2; ss << '\n'; break; case 't': expr+=2; ss << '\t'; break; default: if (strchr("\"'\n", expr[1])) ++expr; ss << *expr; ++expr; } } else { ss << *expr; ++expr; } } if (*expr != quote) { std::string squote = (quote == '"' ? "\"": "'"); rpnBuilder::cleanRPN(&data.rpn); throw syntax_error("Expected quote (" + squote + ") at end of string declaration: " + squote + ss.str() + "."); } ++expr; data.handle_token(new Token<std::string>(ss.str(), STR)); } else { // Otherwise, the variable is an operator or paranthesis. // Check for syntax errors (excess of operators i.e. 10 + + -1): if (data.lastTokenWasUnary) { std::string op; op.push_back(*expr); rpnBuilder::cleanRPN(&data.rpn); throw syntax_error("Expected operand after unary operator `" + data.opStack.top() + "` but found: `" + op + "` instead."); } switch (*expr) { case '(': // If it is a function call: if (data.lastTokenWasOp == false) { // This counts as a bracket and as an operator: data.handle_op("()"); // Add it as a bracket to the op stack: } data.open_bracket("("); ++expr; break; case '[': if (data.lastTokenWasOp == false) { // If it is an operator: data.handle_op("[]"); } else { // If it is the list constructor: // Add the list constructor to the rpn: data.handle_token(new CppFunction(&TokenList::default_constructor, "list")); // We make the program see it as a normal function call: data.handle_op("()"); } // Add it as a bracket to the op stack: data.open_bracket("["); ++expr; break; case '{': // Add a map constructor call to the rpn: data.handle_token(new CppFunction(&TokenMap::default_constructor, "map")); // We make the program see it as a normal function call: data.handle_op("()"); data.open_bracket("{"); ++expr; break; case ')': data.close_bracket("("); ++expr; break; case ']': data.close_bracket("["); ++expr; break; case '}': data.close_bracket("{"); ++expr; break; default: { // Then the token is an operator std::stringstream ss; ss << *expr; ++expr; while (*expr && ispunct(*expr) && !strchr("+-'\"()_", *expr)) { ss << *expr; ++expr; } std::string op = ss.str(); rWordMap_t::iterator it; if ((it=rWordMap.find(op)) != rWordMap.end()) { // Parse reserved operators: try { it->second(expr, &expr, &data); } catch (...) { rpnBuilder::cleanRPN(&data.rpn); throw; } } else { data.handle_op(op); } } } } // Ignore spaces but stop on delimiter if not inside brackets. while (*expr && isspace(*expr) && (data.bracketLevel || !strchr(delim, *expr))) ++expr; } // Check for syntax errors (excess of operators i.e. 10 + + -1): if (data.lastTokenWasUnary) { rpnBuilder::cleanRPN(&data.rpn); throw syntax_error("Expected operand after unary operator `" + data.opStack.top() + "`"); } while (!data.opStack.empty()) { data.rpn.push(new Token<std::string>(data.opStack.top(), OP)); data.opStack.pop(); } // In case one of the custom parsers left an empty expression: if (data.rpn.size() == 0) data.rpn.push(new TokenNone()); if (rest) *rest = expr; return data.rpn; }
TokenQueue_t calculator::toRPN(const char* expr, std::map<std::string, double>* vars, std::map<std::string, int> opPrecedence) { TokenQueue_t rpnQueue; std::stack<std::string> operatorStack; bool lastTokenWasOp = true; // In one pass, ignore whitespace and parse the expression into RPN // using Dijkstra's Shunting-yard algorithm. while (*expr && isspace(*expr )) ++expr; while (*expr ) { if (isdigit(*expr )) { // If the token is a number, add it to the output queue. char* nextChar = 0; double digit = strtod(expr , &nextChar); # ifdef DEBUG std::cout << digit << std::endl; # endif rpnQueue.push(new Token<double>(digit, NUM)); expr = nextChar; lastTokenWasOp = false; } else if (isvariablechar(*expr )) { // If the function is a variable, resolve it and // add the parsed number to the output queue. std::stringstream ss; ss << *expr; ++expr; while( isvariablechar(*expr ) || isdigit(*expr) ) { ss << *expr; ++expr; } bool found = false; double val; std::string key = ss.str(); if(key == "true") { found = true; val = 1; } else if(key == "false") { found = true; val = 0; } else if(vars) { std::map<std::string, double>::iterator it = vars->find(key); if(it != vars->end()) { found = true; val = it->second; } } if (found) { // Save the number # ifdef DEBUG std::cout << val << std::endl; # endif rpnQueue.push(new Token<double>(val, NUM));; } else { // Save the variable name: # ifdef DEBUG std::cout << key << std::endl; # endif rpnQueue.push(new Token<std::string>(key, VAR)); } lastTokenWasOp = false; } else { // Otherwise, the variable is an operator or paranthesis. switch (*expr) { case '(': operatorStack.push("("); ++expr; break; case ')': while (operatorStack.top().compare("(")) { rpnQueue.push(new Token<std::string>(operatorStack.top(), OP)); operatorStack.pop(); } operatorStack.pop(); ++expr; break; default: { // The token is an operator. // // Let p(o) denote the precedence of an operator o. // // If the token is an operator, o1, then // While there is an operator token, o2, at the top // and p(o1) <= p(o2), then // pop o2 off the stack onto the output queue. // Push o1 on the stack. std::stringstream ss; ss << *expr; ++expr; while (*expr && !isspace(*expr ) && !isdigit(*expr ) && !isvariablechar(*expr) && *expr != '(' && *expr != ')') { ss << *expr; ++expr; } ss.clear(); std::string str; ss >> str; # ifdef DEBUG std::cout << str << std::endl; # endif if (lastTokenWasOp) { // Convert unary operators to binary in the RPN. if (!str.compare("-") || !str.compare("+")) { rpnQueue.push(new Token<double>(0, NUM)); } else { throw std::domain_error( "Unrecognized unary operator: '" + str + "'."); } } while (!operatorStack.empty() && opPrecedence[str] >= opPrecedence[operatorStack.top()]) { rpnQueue.push(new Token<std::string>(operatorStack.top(), OP)); operatorStack.pop(); } operatorStack.push(str); lastTokenWasOp = true; } } } while (*expr && isspace(*expr )) ++expr; } while (!operatorStack.empty()) { rpnQueue.push(new Token<std::string>(operatorStack.top(), OP)); operatorStack.pop(); } return rpnQueue; }