几乎可以肯定,最好写一个解析器或使用一个别人已经写好的。正如您在 cmets 中指出的那样,对于这种 非常 受限的输入,它实际上非常简单:
- 在运算符上拆分字符串
- 遍历生成的拆分字符串:
- 验证运算符
- 将
, 转换为||
- 可选择验证名称
- 用
true(如果在数组中)或false(如果不在)替换名称
- 再次将结果重新加入字符串
- 通过
eval 运行结果(因为您现在知道它只有具有您已列入白名单的操作员和文本true 或false)
这是一个快速的概念验证: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 表达式很容易。然后你可以:
使用别人已经写过的解析器,like this one(详情在this blog post) (那个似乎不支持&&和@987654336 @,但也许您可以将其扩展为),或
将数组转换为对象属性并使用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 一起使用之前仔细检查字符串。