2014-01-13 80 views
1

比方说,我有一个数组。JavaScript数组解析匹配

["red", "blue", "neon", "black", "orange"] 

我想评估某个匹配模式是否为真。

我想用逗号来表示OR,并用& &来表示AND。

"red&&blue" -> true 
"blue&&white" -> false 
"red,white" -> true 
"(red&&blue),(red&&white)" -> true 
"(red&&blue)&&(red&&white)" -> false 
"(red&&blue)&&(red&&neon)" -> true 

应该使用什么样的匹配方案?我想不从头开始实现解析器,如果有任何现有的将是伟大的,但否则我希望逻辑工作就像它在javascript中的工作方式,无限复杂。

基本上,我在寻找这样的事情,但对JavaScript: Java library for parsing & building logical expressions

+0

Javascript中有相当多的[parser generators](http://stackoverflow.com/questions/6211111/javascript-parser-generator)。 – georg

回答

1

你几乎肯定是最好关闭使用一个其他人已经写编写解析器或。当您在评论中指出,该非常限制输入,它实际上是非常简单:

  • 拆分对经营者的字符串
  • 漫步生成的拆分字符串:
    • 验证运营商
    • 转换,||
    • 可选验证名称
    • true更换名称(如果它在阵列中)或false(如果不是)
  • 归队结果转换成字符串再次
  • 贯穿eval的结果(因为你现在知道它有你列入白名单运营商和文本truefalse

这里有一个快速验证的概念:Live Copy | Live Source

<!DOCTYPE html> 
<html> 
<head> 
<meta charset=utf-8 /> 
<title>Expression Thingy</title> 
    <style> 
    .good { 
     color: green; 
    } 
    .bad { 
     color: #d22; 
    } 
    </style> 
</head> 
<body> 
    <script> 
    (function() { 
     var array = ["red", "blue", "neon", "black", "orange"]; 
     var tests = [ 
     {expr: "red&&blue",     expect: true}, 
     {expr: "blue&&white",    expect: false}, 
     {expr: "red,white",     expect: true}, 
     {expr: "(red&&blue),(red&&white)", expect: true}, 
     {expr: "(red&&blue)&&(red&&white)", expect: false}, 
     {expr: "(red&&blue)&&(red&&neon)", expect: true}, 
     {expr: "(red+blue)&&(red!neon)", expectInvalid: true} 
     ]; 
     var data; 

     // Turn data into an object with named properties, to make lookups 
     // faster 
     data = {}; 
     array.forEach(function(entry) { 
     data[entry] = true; 
     }); 

     // Run the tests 
     tests.forEach(runTest); 

     function runTest(test) { 
     var parts, invalid = false; 

     // Get an array of tokens: We'll get `(`, `)`, `,`, `&&`, whitespace, or a name in each array slot 
     parts = test.expr.match(/&&|,|\(|\)|\s+|[^()&,]+/g); 

     // Validate the operators and turn the names into "true" or "false" 
     parts.forEach(function(part, index) { 
      switch (part) { 
      case ",": 
       // Valid operator, replace with || 
       parts[index] = "||"; 
       break; 
      case "&&": 
      case "(": 
      case ")": 
       // Valid operator 
       break; 
      default: 
       // Name or whitespace 
       if (!part.replace(/\s+/g, "")) { 
       // Whitespace 
       } 
       else { 
       // Name, validate it -- obviously apply whatever rule works 
       // for your data, the rule below allows A-Z, $, and _ in 
       // the first position and those plus digits in subsequent 
       // positions. 
       if (!/^[A-Za-z$_][A-Za-z0-9$_]*$/.test(part)) { 
        // Invalid 
        display("Invalid name: " + part, test.expectInvalid); 
        invalid = true; 
       } 
       else { 
        // Valid, replace it 
        parts[index] = data[part] ? "true" : "false"; 
       } 
       } 
       break; 
      } 
     }); 
     if (!invalid) { 
      // Now we know parts only has valid stuff we can trust in it, rejoin 
      // and eval it 
      result = !!eval(parts.join("")); 
      display(test.expr + ": Got " + result + ", expected " + test.expect, result === test.expect); 
     } 
     } 

     function display(msg, good) { 
     var p = document.createElement('p'); 
     p.innerHTML = String(msg); 
     if (typeof good !== "undefined") { 
      p.className = good ? "good" : "bad"; 
     } 
     document.body.appendChild(p); 
     } 
    })(); 
</script> 
</body> 
</html> 

你可能想按摩验证规则至少位。


老答案,这主要是假设你能信任的输入:

可以很容易地把这些投入有效的JavaScript表达式。此后,您可以:

  1. 使用解析器别人已经写的,like this one(在this blog post细节)(即一个似乎不支持&&||,虽然也许你可以把它扩展到)

  2. 将数组转换为对象属性并使用eval从不信任eval对不安全或不能安全的输入使得安全。但是,如果输入是安全的或可以安全的,eval是好的。

假设阵列中的值是有效的JavaScript标识符,则可以简单地通过改变,||把这些表达式转化为有效的JavaScript表达式:

str = str.replace(/,/g, "||"); 

同样,这原来该数组到对象与那些命名的属性:

var obj = {}; 
data.forEach(function(entry) { 
    obj[entry] = true; 
}); 

...你大概会传入表达式评估器。

如果你打算在eval路线,你必须做字符串多一点准备,把"(red&&blue),(red&&white)"'(obj["red"]&&obj["blue"])||(obj["red"]&&obj["white"])',像这样:

str = str.replace(/,/g, "||").replace(/\b([a-zA-Z0-9_]+)\b/g, 'obj["$1"]'); 

使用表达式我不会做榜样评估者库,但这里是与eval的基础知识:Live Copy | Live Source

<!DOCTYPE html> 
<html> 
<head> 
<meta charset=utf-8 /> 
<title>Expression Thingy</title> 
    <style> 
    .good { 
     color: green; 
    } 
    .bad { 
     color: #d22; 
    } 
    </style> 
</head> 
<body> 
    <script> 
    (function() { 
     var data = ["red", "blue", "neon", "black", "orange"]; 
     var tests = [ 
     {expr: "red&&blue",     expect: true}, 
     {expr: "blue&&white",    expect: false}, 
     {expr: "red,white",     expect: true}, 
     {expr: "(red&&blue),(red&&white)", expect: true}, 
     {expr: "(red&&blue)&&(red&&white)", expect: false}, 
     {expr: "(red&&blue)&&(red&&neon)", expect: true} 
     ]; 
     var obj; 

     // Turn data into an object with named properties 
     obj = {}; 
     data.forEach(function(entry) { 
     obj[entry] = true; 
     }); 

     // Turn the expressions into eval strings 
     tests.forEach(createEvalString); 

     // Run the tests 
     tests.forEach(runTest); 

     function createEvalString(test) { 
     test.evalStr = test.expr.replace(/,/g, "||").replace(/\b([a-zA-Z0-9_]+)\b/g, 'obj["$1"]'); 
     } 

     function runTest(test) { 
     var result; 

     display(test.evalStr); 
     result = !!eval(test.evalStr); // Relies on us closing over `obj` 
     display(test.expr + ": Got " + result + ", expected " + test.expect, result === test.expect); 
     } 

     function display(msg, good) { 
     var p = document.createElement('p'); 
     p.innerHTML = String(msg); 
     if (typeof good !== "undefined") { 
      p.className = good ? "good" : "bad"; 
     } 
     document.body.appendChild(p); 
     } 
    })(); 
    </script> 
</body> 
</html> 

这只是一个起点。首先,您需要仔细检查琴弦,然后再转换它们并使用eval

+0

非常感谢你,但输入是不安全的,这是有问题的。 – Harry

+0

我想到了一个想法,你怎么看?:用(),&&逐个分解每个表达式,直到找到最小的组件。然后为每个部分检查它是否在数组中。给它一个真实或虚假的价值。完成后,再次使用与之前分割的值相同的值重新加入它们。那么你应该能够安全地评估(因为它只是真实/虚假的表达) – Harry

+0

@哈里:你激励我看到这是多么容易。这真的很容易,看到更新的答案。此外,我们应该清理这些评论,因为他们不再添加任何内容。 –

2

对于未来的读者,这里做的工作更可靠的方式:

基本上所有这一切 - 无需重新发明轮子。

对于例子你张贴的所有语法可以是这样的:

{ 
var props = ["red", "blue", "neon", "black", "orange"]; 
} 


start 
    = additive 

additive 
    = left:multiplicative "," right:additive { return left || right } 
/multiplicative 

multiplicative 
    = left:primary "&&" right:multiplicative { return left && right } 
/primary 

primary 
    = atom 
/"(" additive:additive ")" { return additive; } 

atom 
    = letters:[a-z]+ { return props.indexOf(letters.join("")) >= 0 } 
+0

漂亮!我无法抗拒地看到实现Harry的想法有多困难。这是微不足道的。 :-) –

+0

这真的很有趣,非常感谢。 – Harry

1

我认为这种特殊情况下,可以用这个简单的功能

var whiteList = ["red", "blue", "neon", "black", "orange"]; 
function evaluator(inputString) { 
    var data = whiteList.reduce(function(previous, current) { 
     return previous.split(current).join("####"); 
    }, inputString); 
    data = data.replace(",", "||").replace(/[a-zA-Z]+/g, "false"); 
    return eval(data.replace(/####/g, "true")); 
} 

Sample run得到解决,与测试用例(感谢@TJCrowder :)

+0

愚蠢的问题,但你为什么使用'####'? – Harry

+0

@哈利这只是一个占位符。我本可以将它设置为“真”,但是'[a-zA-Z] +'会将所有'真'同样变为'假'。 – thefourtheye

+0

噢,谢谢你解释。 – Harry