特定用途的javascript解析器
我正在尝试创建一个工具来查找.html文件中缺少的翻译。我们的一些翻译是在运行时用JS代码完成的。我想把这些画在一起。下面是一个例子特定用途的javascript解析器,javascript,abstract-syntax-tree,Javascript,Abstract Syntax Tree,我正在尝试创建一个工具来查找.html文件中缺少的翻译。我们的一些翻译是在运行时用JS代码完成的。我想把这些画在一起。下面是一个例子 <select id="dropDown"></select> // js bindings: { "dropDown": function() { translate(someValue); // translate then set option }
<select id="dropDown"></select>
// js
bindings: {
"dropDown": function() {
translate(someValue);
// translate then set option
}
}
//js
绑定:{
“下拉菜单”:函数(){
翻译(某个值);
//翻译然后设置选项
}
}
在上面,您可以看到我有一个下拉列表,其中的值是在运行时创建和转换的。我认为AST是实现这一目标的正确方法。基本上,我需要遍历.html文件,查找缺少行内翻译的标记(使用{{t value}}完成),并在相应的.js文件中搜索运行时翻译。有没有更好的方法来实现这一点?关于创建AST的工具有什么建议吗?我想您应该在代码中寻找模式。特别是,我认为您需要确定,对于每个HTMLSELECT构造,是否有一个具有正确嵌入ID名称的相应的、形状正确的JavaScript片段 你可以用AST来做,对吗。在您的例子中,您需要一个用于HTML文件(节点基本上是HTML标记)的AST,以及用于包含已解析JavaScript的脚本块()标记的子AST 为此,您需要两个解析器:一个用于解析HTML(这是一个令人讨厌的问题,因为HTML太乱了),它生成一个树,其中包含只包含文本的脚本节点。然后,您需要一个JavaScript解析器,可以将其应用于脚本标记下的文本blob,以生成JavaScript AST;理想情况下,您可以将它们拼接到HTML树中以替换文本blob节点。现在您有了一个混合树,其中一些节点是html,一些子树是JavaScript。理想情况下,HMTL节点标记为HTML,JavaScript节点标记为JavaScript 现在,您可以在树中搜索选定的节点,提取id,并在所有javascript子树中搜索预期的结构 您可以按程序对匹配进行编码,但这会很混乱:
for all node
if node is HTML and nodetype is Select
then
functionname=node.getchild("ID").text
for all node
if node is JavaScript and node.parent is HTML and nodetype is pair
then if node.getchild(left).ext is "bindings"
then if node.getchild(right)=structure
then... (lots more....)
这里面有很多头发。从技术上讲,这只是汗水。您必须知道(并编码)树的精确细节,以便正确地上下爬升其链接并逐个检查节点类型。如果语法稍有变化,代码也会中断;它对语法知道得太多了
您可以通过从头开始编写自己的解析器来完成这项工作。更多的汗水
有一些工具可以使这变得容易得多;看见这些工具允许您定义语言语法,并为这些语法生成解析器和AST构建器。(一般来说,它们非常擅长定义工作语法,因为它们被设计用于多种语言)。这至少在过程中加入了大量的结构,他们提供了大量的机械来完成这项工作。但好的方面是,您可以表达模式,通常使用源语言的表面语法,这可以使它更容易表达
其中一个工具是我们的DMS软件再工程工具包(我是架构师)
DMS已经有肮脏的HTML和完整的JavaScript解析器,所以不需要构建它们。
您必须为DMS编写一些代码来调用HTML解析器,找到脚本节点的子树,并应用JavaScript解析器。DMS允许您将文本块解析为语法中的任意非终结符,从而实现了这一点;在本例中,您希望将这些blob解析为非终结表达式
所有这些就绪后,您现在可以编写支持检查的模式:
pattern select_node(property: string): HTML~dirty.HTMLform =
" <select ID=\property></select> ";
pattern script(code: string): HTML~dirty.HTMLform =
" <script>\code</script> ";
pattern js_bindings(s: string, e:expression):JavaScript.expression =
" bindings : { \s : function ()
{ translate(\e);
}
} ";
模式选择_节点(属性:字符串):HTML~dirty.HTMLform=
" ";
模式脚本(代码:字符串):HTML~dirty.HTMLform=
“\code”;
模式js_绑定(s:string,e:expression):JavaScript.expression=
“绑定:{\s:函数()
{翻译(\e);
}
} ";
虽然这些模式看起来像文本,但DMS会将它们解析为AST,其中包含参数列表元素的占位符节点,内部用“\nnnn”表示
围绕感兴趣的节目文本的(元)引号“…”。这样的ASTs模式可以与ASTs模式匹配;如果模式树匹配,则它们匹配,然后将模式变量leaf作为绑定捕获。
(请参见下面的Registry:PatternMatch,以及结果匹配参数,其中插槽匹配(布尔值)和绑定(匹配后生成的绑定子树数组).对于工具构建者来说,这是一个巨大的胜利:他不必对语法的细节了解太多,因为他编写了模式,并且工具隐式地为他生成了所有的树节点
使用这些模式,您可以编写过程性PARLANSE(DMS的Lisp风格编程语言)代码来实现检查(缩短演示文稿所需的自由):
(;;`Parse HTML file':
(=HTML_树(HMTL:ParseFile…)
`查找脚本节点并替换为相同“”的AST:
(AST:FindAllMatchingSubtrees HTML_tree)
(lambda(布尔函数[html\u node AST:node])
(let(=[匹配注册表:匹配]
(注册表:PatternMatch html_节点“脚本”))
(如果其他匹配:布尔值)
(值(;;(AST:ReplaceNode)
(JavaScript:ParseStream)
“表达式”;所需的非终结符
(制作流:Stream)
(AST:GetString匹配:绑定:1)))
);;
~f;false:不要访问子树
)价值观
~t;对:继续扫描子树
)如果其他
)让
)(兰姆达)
`现在查找select节点,并检查“是否正常”:
(AST:FindAllMatchingSubtrees HTML_tree)
(lambda(布尔函数[html\u node AST:node])
(让(;)(=[选择匹配注册表:匹配];捕获匹配数据
(;; `Parse HTML file':
(= HTML_tree (HMTL:ParseFile .... ))
`Find script nodes and replace by ASTs for same':
(AST:FindAllMatchingSubtrees HTML_tree
(lambda (function boolean [html_node AST:Node])
(let (= [match Registry:Match]
(Registry:PatternMatch html_node "script"))
(ifthenelse match:boolean
(value (;; (AST:ReplaceNode node
(JavaScript:ParseStream
"expression" ; desired nonterminal
(make Streams:Stream
(AST:GetString match:bindings:1))))
);;
~f ; false: don't visit subtree
)value
~t ; true: continue scanning into subtree
)ifthenelse
)let
)lambda )
`Now find select nodes, and check sanity':
(AST:FindAllMatchingSubtrees HTML_tree
(lambda (function boolean [html_node AST:node])
(let (;; (= [select_match Registry:Match] ; capture match data
(Registry:PatternMatch "select" html_node)) ; hunt for this pattern
[select_function_name string]
);;
(ifthenelse select_match:boolean
(value (;; `Found <select> node.
Get name of function...':
(= select_function_name
(AST:GetString select_match:bindings:1))
`... and search for matching script fragment':
(ifthen
(~ (AST:FindFirstMatchingSubtree HTML_tree
(lambda (function boolean [js_node AST:Node])
(let (;; (= [match Registry:Match] ; capture match data
(Registry:PatternMatch js_node "js_bindings")) ; hunt for this pattern
(&& match:boolean
(== select_match:bindings:1
select_function_name)
)&& ; is true if we found matching function
)let
)lambda ) )~
(;; `Complain if we cant find matching script fragment'
(Format:SNN `Select #S with missing translation at line #D column #D'
select_function_name
(AST:GetLineNumber select_match:source_position)
(AST:GetColumnNumber select_match:source_position)
)
);;
)ifthen
);;
~f ; don't visit subtree
)value
~t ; continue scanning into subtree
)ifthenelse
)let
)lambda )
);;