我想解析一个包含if
语句并评估其输出的字符串.
我可以得到评估部分,但其他人对我来说太复杂了.
请看以下示例:
if $a == 10 && ($b == '5' || $c == 'test')
if $x != 10 || $y == false
if $z < 10
我想让它们在数组中 - 也就是说,我想要的相应示例的输出将是:
[ [ '$a', '==', '10' ], '&&', [ [ '$b', '==', '5' ], '||', [ '$c', '==', 'test' ] ] ]
[ [ '$x', '!=', '10' ], '||', [ '$y', '==', 'false' ] ]
[ '$z', '<', '10' ]
我想我要求太多的逻辑/代码,但如果有人能帮助我,这将是很好的.使用正则表达式或正常的字符串解析是可以的.
如果您只是想解析字符串,那么有很多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
将被视为一个令牌,但$a == 10
将被视为3个令牌.
接下来你需要的是一个基本的解析器.您可以手动实现,但是自己编写所有运算符优先级规则会很麻烦.相反,我建议您使用基于Dijkstra的分流码算法的以下解析器.
我们现在可以创建一个解析器,如下所示:
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/