我正在开发一个本质上是模板化特定领域语言的项目。在我的项目中,我接受以下形式的用户输入行:
'{{index(1, 5)}}'
'{{firstName()}} X. {{lastName()}}'
'{{floating(-0.5, 0.5)}}'
'{{text(5, "words")}}'
双花括号({{ }}
)之间的任何命令都有一个相应的Javascript方法,在遇到该命令时应该调用它。 (例如,在第一个的情况下为function index(min, max) {...}
。)
我很难确定如何安全地接受输入并调用适当的功能。我知道我现在这样做的方式并不安全。我只是eval()
两组花括号之间的任何东西。
如何解析这些输入字符串,以便我可以灵活地匹配花括号之间的函数调用,并使用给定的任何参数执行该函数,同时仍然不会盲目地用代码调用eval()
?
我考虑过制作一个映射(如果命令是index()
,请调用function index() {}
),但这似乎不太灵活;我如何收集并传递任何参数(例如{{index(2, 5)}}
),如果有的话?
这是用Node.js编写的。
答案 0 :(得分:1)
这个问题分解为:
解析字符串
评估结果函数图
调度到每个函数(作为上面#2的一部分)
不幸的是,根据您的要求,解析{{...}}
字符串非常复杂。您至少要处理以下问题:
函数可以嵌套{{function1(function2(), 2, 3)}}
。
字符串可以包含(转义)引号,并且可以包含逗号,因此即使没有上面的要求#1,找到离散参数(在逗号上拆分)的简单方法也行不通。
< / LI> 醇>所以......你需要一个合适的解析器。您可以尝试将其中的一个拼凑在一起,但这是解析器生成器进入图片的位置,如PEG.js或Jison(这些只是示例,不一定是建议 - 我确实碰巧注意到其中一个Jison的例子是JSON parser,大约是战斗的一半)。编写解析器超出了回答问题的范围,我担心。 : - )
根据您使用的工具,您的解析器生成器可能会为您处理此问题。 (例如,我很确定PEG.js和Jison都会这样做。)
如果没有,那么在解析之后你可能会得到某种类型的对象图,它会为你提供函数及其参数(可能是带参数的函数......可能是......)。 / p>
functionA有五个参数,第三个是带有两个参数的functionB,依此类推。
接下来的任务是评估最深的(并且在相同的深度,从左到右)并在相关参数列表中将它们替换为结果,这样您我需要一个depth-first traversal algorithm。通过最深的第一个和从左到右(上面的项目符号列表中从上到下)我的意思是在上面的列表中,你必须首先调用functionC,然后调用functionB,然后调用functionD,最后功能A.
根据您使用的工具,它也可以处理这个位。我再次怀疑PEG.js会这样做,如果Jison也这样做我也不会感到惊讶。
当你准备调用一个函数(不再)将函数调用作为参数时,你可能会有函数名和一个参数数组。假设您将函数存储在地图中:
var functions = {
index: function() { /* ... */ },
firstName: function() { /* ... */ },
// ...
};
......打电话给他们很容易:
functionResult = functions[functionName].apply(undefined, functionArguments);
我很抱歉不能说“只做X,你就在那里”,但这确实不是一个小问题。我会把工具扔给它,我不会自己发明这个轮子。
答案 1 :(得分:0)
您可以尝试这样的事情:
假设你有这样的功能:
'{{floating(-0.5, 0.5)}}'
所有实际函数都在对象中引用,如下所示:
var myFunctions = {
'index': function(){/* Do stuff */},
'firstName': function(){}
}
然后,这应该有效:
function parse(var input){
var temp = input.replace('{{','').replace(')}}','').split('('),
fn = temp[0];
arguments = temp[1].split(',');
myFunctions[fn].apply(this, arguments);
}
请注意,这仅适用于没有嵌套作为参数的函数的简单函数调用。它还将所有参数作为字符串传递,而不是可能的类型(数字,布尔值等)。
如果您想处理更复杂的字符串,则需要使用正确的解析器或模板引擎,@T.J. Crowder suggested in the comments。
答案 2 :(得分:0)
最后一个意味着而不是使用eval()
使用new Function()
或专门设计的库,例如https://github.com/dtao/lemming.js
有关eval与新功能()
的更多信息,请参阅http://www.2ality.com/2014/01/eval.html对于更复杂的方法,尝试创建自己的解析器,请检查https://stackoverflow.com/a/2630085/481422
在https://github.com/douglascrockford/JSLint/blob/master/jslint.js
中搜索评论// ECMAScript parser