如果您只是想解析您的字符串,那么有很多 JavaScript 解析库可以为您做到这一点。例如,您可以使用acorn 将有效 JavaScript 代码解析为Mozilla AST。您也可以使用escodegen 将其转换回字符串。
不幸的是,您的字符串似乎不是有效的 JavaScript 代码,但如果您删除每个字符串开头的 if,那么您绝对可以使用 acorn 解析字符串。输出将是 AST,这不是您要查找的内容,但您可以轻松地将其转换为您想要的格式。
但是,对于这样一个微不足道的用例,使用完整的解析器在我看来是一种矫枉过正的做法。例如,如果您只是想评估您的字符串,那么您可以使用 Function 构造函数,如下所示:
function read(expression) {
var variables = expression.match(/\$\w+/g);
var length = variables.length;
var uniqueVariables = [];
var index = 0;
while (index < length) {
var variable = variables[index++];
if (uniqueVariables.indexOf(variable) < 0)
uniqueVariables.push(variable);
}
return Function.apply(null, uniqueVariables.concat("return " + expression));
}
这个read 函数允许您读取如下表达式:
var condition = read("$a == 10 && ($b == '5' || $c == 'test')");
您现在可以按如下方式使用condition 函数:
alert(condition(10, "10", "test")); // true
alert(condition(5, "10", "test")); // false
亲自观看演示:http://jsfiddle.net/ZnUh2/
当然,您需要删除所有字符串开头的if 才能读取它们。这可以通过使用string.slice(2) 删除if 轻松完成。
如果您一心想将字符串转换为数组,那么这将需要更多的工作,但可以通过使用像 Lexer 这样的词法分析器轻松完成。您需要做的第一件事是为不同类型的令牌编写一些规则:
var lexer = new Lexer;
lexer.addRule(/\s+/, function () { /* skip whitespace */ });
lexer.addRule(/if\b/g, function () { /* skip the if keyword */ });
// match opening parentheses
lexer.addRule(/\(/, function () { return "("; });
// match closing parentheses
lexer.addRule(/\)/, function () { return ")"; });
// match any other token
lexer.addRule(/[^\s\(\)]+/, function (lexeme) { return lexeme; });
请注意,此词法分析器希望每个标记(括号除外)之间都有空格。例如,$a==10 将被视为 1 个令牌,而 $a == 10 将被视为 3 个令牌。
接下来你需要一个基本的解析器。您可以手动实现,但自己编写所有运算符优先规则会很痛苦。相反,我建议您根据Dijkstra 的shunting yard algorithm 使用以下parser。
我们现在可以如下创建解析器:
var relational = {
precedence: 3,
associativity: "left"
};
var equality = {
precedence: 2,
associativity: "left"
};
var parser = new Parser({
"<": relational,
"<=": relational,
">": relational,
">=": relational,
"==": equality,
"!=": equality,
"&&": {
precedence: 1,
associativity: "right"
},
"||": {
precedence: 0,
associativity: "right"
}
});
最后我们编写代码将词法分析器连接到解析器并生成所需的输出:
function parse(string) {
lexer.setInput(string);
var tokens = [], token;
while (token = lexer.lex()) tokens.push(token);
tokens = parser.parse(tokens);
var stack = [], length = tokens.length, index = 0;
while (index < length) {
token = tokens[index++];
switch (token) {
case "<":
case "<=":
case ">":
case ">=":
case "==":
case "!=":
case "&&":
case "||":
var b = stack.pop();
var a = stack.pop();
stack.push([a, token, b]);
break;
default:
stack.push(token);
}
}
return stack.length && stack[0];
}
就是这样。现在您可以将字符串解析为数组,如下所示:
var array = parse("if $a == 10 && ($b == '5' || $c == 'test')");
要查看输出,您可以使用JSON.stringify。亲自观看演示:http://jsfiddle.net/d2UYZ/3/