javascript - 特定用途的javascript解析器

标签 javascript abstract-syntax-tree

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

<select id="dropDown"></select>

// js
bindings: { 
 "dropDown": function() {
              translate(someValue);
              // translate then set option
           }
 }


上面您可以看到我在运行时创建和转换值的地方有一个下拉菜单。我当时认为AST是实现此目标的正确方法。基本上,我需要遍历.html文件以查找缺少行内翻译的标签(使用{{t value}}完成),然后在相应的.js文件中搜索运行时翻译。有没有更好的方法可以做到这一点?关于创建AST的工具有什么建议吗?

最佳答案

我认为您想在代码中寻找模式。特别是,我认为您想为每个HTML select构造确定是否存在具有正确的嵌入式ID名称的相应的,形状正确的JavaScript片段。

您可以使用AST来做到这一点,对。在您的情况下,您需要为HTML文件提供一个AST(节点本质上是HTML标签),并为包含已解析的JavaScript的脚本块()标签提供子AST。

为此,您需要两个解析器:一个用于解析HTML(由于HTML混乱而令人讨厌),该解析器将生成包含仅包含文本的脚本节点的树。然后,您需要一个JavaScript解析器,您可以将其应用于Script标记下的文本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....)


里面有很多头发。从技术上讲,它只是汗水。您必须知道(并编码)树的精确细节,才能正确地向上和向下攀爬它的链接并一一检查节点类型。如果语法稍有改变,该代码也将中断。它对语法了解太多。

您可以通过从头开始编写自己的解析器来完成此操作。多汗。

有一些工具可以使这变得容易得多。参见Program Transformation Systems。此类工具可让您定义语言语法,并为此类语法生成解析器和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);
                            }
                    } ";


虽然这些模式看起来像文本,但是它们被DMS解析为带有参数列表元素占位符节点的AST,在内部用“ \ nnnn”表示
围绕感兴趣的程序文本的(元)引号“ ...”。可以将此类AST模式与AST模式匹配;如果模式树匹配,它们将匹配,然后模式变量叶子将被捕获为绑定。
(请参见下面的Registry:PatternMatch,以及带有槽匹配(布尔值)和绑定(由匹配产生的绑定子树的数组)的结果match参数。对于工具构建者来说,这是一个巨大的胜利:他不必对语法的细节,因为他编写了模式,并且该工具为他隐式生成了所有树节点。

使用这些模式,您可以编写过程PARLANSE(DMS的Lisp风格的编程语言)代码来实现检查(为缩短演示文稿而采取的自由方式):

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


此过程代码首先解析HTML源文件,从而生成HTML树。所有这些节点都标记为来自“ HTML〜dirty”语言。
然后,它扫描该树以找到SCRIPT节点,并用从遇到的脚本节点的文本内容的JavaScript表达式解析获得的AST替换它们。最后,它找到所有SELECT节点,挑选出ID子句中提到的函数的名称,并检查所有JavaScript AST以查找OP指定的匹配“绑定”表达式。所有这些都依赖于模式匹配机制,而模式匹配机制又依赖于底层AST库的顶部,该库提供了多种方法来检查/导航/更改树节点。

我遗漏了一些细节(特别是错误处理代码),显然还没有测试。但这给了您如何使用DMS的味道。

在其他程序转换系统中可以使用类似的模式和匹配过程。

关于javascript - 特定用途的javascript解析器,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/28033984/

相关文章:

Python:深度复制ast节点树

javascript - 为什么 ECMAScript 6 包含 Set 对象,但不提供比较它们是否相等的方法?

javascript - CoffeeScript 中的异步映射

javascript - 无法在 Angular 服务中更新函数内部的值

java - VariableDeclarationFragment 节点 resolveBindind() 在 eclipse/jdt/ast 中返回 null

java - 从 java 服务器页面中删除所有 HTML

Haskell:使用算法 W 用类型信息标记 AST

c++ - clang : What does AST (abstract syntax tree) look like?

javascript - 访问 javascript 中的 props

javascript - 两个 Html 按钮用于两种不同的功能