如何在不使用eval或构造函数的情况下在JavaScript中编写算术表达式解析器?

时间:2015-07-24 00:07:34

标签: javascript parsing abstract-syntax-tree recursive-descent

给出一个字符串:

 var str1 = "25*5+5*7";

如果不使用JavaScript中的eval或构造函数,我怎样才能编写一个名为“output”的函数,该函数接收字符串并输出字符串的算术值,在本例中为160 ?

4 个答案:

答案 0 :(得分:11)

这里是递归解析后的完整优先级表达式求值程序 我在OP的问题评论中链接到了这个想法。

为此,首先我为我想要处理的表达式写了一个简单的BNF语法:

sum =  product | sum "+" product | sum "-" product ;
product = term | product "*" term | product "/" term ;
term = "-" term | "(" sum ")" | number ;

这本身需要一些简单而直接的经验。如果您没有BNF的经验,您会发现 它非常适用于描述复杂的项目流,如表达式,消息,编程语言......

使用该语法,我按照其他消息中列出的步骤进行操作 生成以下代码。显而易见的是,它是以一种愚蠢的机械方式由语法驱动的,因此如果你有这种语法就很容易写出来。

(未经测试。我不是JavaScript编码器。这肯定会包含一些语法/语义问题。我花了大约15分钟来编写代码。)

var SE="Syntax Error";

function parse(str) { // returns integer expression result or SE
   var text=str;
   var scan=1;
   return parse_sum();

   function parse_sum() { 
      var number, number2;
      if (number=parse_product()==SE) return SE;
      while (true) {
        skip_blanks();
        if (match("+") {
           number2=parse_product();
           if (number2==SE) return SE;
           number+=number2;
        }
        else if (match('-')) {
                { number2=parse_product();
                  if (number2==SE) return SE;
                  number-=number2;
                } 
             else return number;
      }
   }

   function parse_product() {
      var number, number2;
      if (number=parse_number()==SE) return SE;
      while (true) {
        if (match("*") {
            number2=parse_term();
            if (number2==SE) return SE;
            number*=number2;
          }
          else if (match('/')) {
                  number2=parse_term();
                  if (number2==SE) return SE;
                  number/=number2;
               }
               else return number; 
      }
   }

   function parse_term() {
      var number;
      skip_blanks();
      if (match("(")) {
         number=parse_sum();
         if (number=SE) return SE;
         skip_blanks();
         if (!match(")") return SE;
      }
      else if match("-") {
              number= - parse_term();
           }
           else if (number=parse_number()==SE) return SE;
      return number;
   }

   function skip_blanks() {
      while (match(" ")) { };
      return;
    }

    function parse_number() {
       number=0;
       if (is_digit()) {
          while (is_digit()) {}
          return number;
        }
        else return SE;
    }

    var number;
    function is_digit() { // following 2 lines are likely wrong in detail but not intent
       if (text[scan]>="0" && text[scan]<="9") {
          number=number*10+text[scan].toInt();
          return true;
       }
       else return false;
    }

   function match(c) {
       if (text[scan]==c)
          { scan++; return true }
       else return false;
    }
 }

编写此类解析器/评估程序非常简单。请参阅my SO answer on how to build a parser(链接到如何构建评估者)。

答案 1 :(得分:2)

这是一个带* over +优先级的简单解析器。我试图让它尽可能具有教育意义。我会留给你添加除法和减法。或括号,如果你特别雄心勃勃。

function parse(str) {
    var signs = ["*", "+"];         // signs in the order in which they should be evaluated
    var funcs = [multiply, add];                 // the functions associated with the signs
    var tokens = str.split(/\b/);      // split the string into "tokens" (numbers or signs)
    for (var round = 0; round < signs.length; round++) {          // do this for every sign
        alert("tokens at this point: " + tokens.join(" "));
        for (var place = 0; place < tokens.length; place++) {    // do this for every token
            if (tokens[place] == signs[round]) {                         // a sign is found
                var a = parseInt(tokens[place - 1]);    // convert previous token to number
                var b = parseInt(tokens[place + 1]);        // convert next token to number
                var result = funcs[round](a, b);           // call the appropriate function
                alert("calculating: " + a + signs[round] + b + "=" + result);
                tokens[place - 1] = result.toString();      // store the result as a string
                tokens.splice(place--, 2);  // delete obsolete tokens and back up one place
            }
        }
    }
    return tokens[0];                  // at the end tokens[] has only one item: the result

    function multiply(x, y) {                   // the functions which actually do the math
        return x * y;
    }

    function add(x, y) {                        // the functions which actually do the math
        return x + y;
    }
}

var str = "25*5+5*7";
alert("result: " + str + " = " + parse(str));

答案 2 :(得分:1)

您可以使用math.js的表达式解析器:

&#13;
&#13;
irb> 5.times { Coaster.insert(name: "Coaster A") }
irb> 4.times { Coaster.insert(name: "Coaster B") }
irb> Coaster.latest.find_by(name: "Coaster A").version
   (2.2ms)  SELECT COUNT(*) FROM "coasters" WHERE "coasters"."original_version_id" = $1 AND (id <= 11)  [["original_version_id", 7]]
=> 5
irb> Coaster.original.find_by(name: "Coaster A").version
   (2.3ms)  SELECT COUNT(*) FROM "coasters" WHERE "coasters"."original_version_id" = $1 AND (id <= 7)  [["original_version_id", 7]]
=> 1
&#13;
var str1= "25*5+5*7"
document.write(str1 + ' = ' + math.eval(str1)); 
// output: "25*5+5*7 = 160"
&#13;
&#13;
&#13;

答案 3 :(得分:-1)

您可以创建一个新脚本:

function parse(str) {
  var s = document.createElement('script');
  s.text = "window.result = " + str;
  document.body.appendChild(s); // Run script
  document.body.removeChild(s); // Clean up
  return result;                // Return the result
}
document.body.innerHTML = parse("5*5+5*5");

或使用事件处理程序内容属性:

function parse(str) {
  var el = document.createElement('div');
  el.setAttribute('onclick', "this.result = " + str);
  el.onclick();     // Run script
  return el.result; // Return the result
}
document.body.innerHTML = parse("5*5+5*5");

请注意,这些方法不安全,像eval一样邪恶但更丑陋。所以我不推荐它们。