特定用途的javascript解析器

特定用途的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 }

我正在尝试创建一个工具来查找.html文件中缺少的翻译。我们的一些翻译是在运行时用JS代码完成的。我想把这些画在一起。下面是一个例子

<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 )
);;