Exemplo n.º 1
0
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;
}