写bison语法来识别javascript函数并忽略其他一切

时间:2016-05-13 11:53:37

标签: javascript parsing yacc context-free-grammar jison

我想做的是

  1. 通过语法阅读我的javascript代码
  2. 在每个函数的主体内写一个特定的行。
  3. 有关。例如 输入

    function(){
        console.log('this is some function');
    }
    function somefunc (args){
        console.log('this is some other function');
        function(){
             console.log('function inside a function');
        }
    }
    

    输出

        function(){
            //auto generated debug log
            debug('some text');
            console.log('this is some function');
        }
        function somefunc (args){
           //auto generated debug log
           debug('some text');
           console.log('this is some other function');
           function(){
              //auto generated debug log
              debug('some text');
              console.log('function inside a function');
          }
       }
    

    我能够识别这个功能,但不能把所有东西都放在一块。我需要一些面包屑来正确地编写语法,以达到理想的输出。

    是我的想法,只专注于解析函数并忽略其他所有内容(即将其倾倒)本身存在缺陷。

    到目前为止我所做的是

    /* lexical grammar */
    %lex
    %%
    
    \s+                   /* skip whitespace */
    "("                   return '('
    ")"                   return ')'
    <<EOF>>               return 'EOF'
    "function"        return 'FUNCTION'
    "{"           return '{'
    "}"           return '}'              
    .             return 'ANYTHING'
    /lex
    
    /* operator associations and precedence */
    
    
    %start expr
    
    %% /* language grammar */
    
    expr: any EOF 
          {typeof console !== 'undefined' ? console.log($1) : print($1);
              return $1;}
    ;
    fun: FUNCTION '(' any ')' '{' any '}'
         {$$=$1+$2+$3+$4+$5+'console.log(something)'+$6+$7}
    ;
    
    any: ANYTHING
         {$$=$1;}
       | any ANYTHING
          {$$=$1+$2}
       |  FUNCTION '(' any ')' '{' any '}'
         {$$=$1+$2+$3+$4+$5+'console.log(something)'+$6+$7} 
    ;
    

1 个答案:

答案 0 :(得分:4)

  

我的想法是专注于解析功能而忽略其他一切(即将其倾倒)本身存在缺陷吗?

它本身并没有缺陷。实际上,这是一种常见的方法[注1]。但是,您的解决方案需要更多工作。

首先,您需要让词法分析器更加健壮。它应该正确识别注释和字符串文字。否则,您冒着匹配误报的风险:隐藏在这些文字中的明显标记。理想情况下,您还可以识别正则表达式,但这要复杂得多,因为它需要来自解析器的协作,而简单的解析器(例如您提出的解析器)没有足够的上下文来区分除法运算符和正则表达式的开头。 [注2]

您还需要识别标识符;否则,碰巧包含字符function的标识符(例如compare_function)也将是错误匹配。

问题出现是因为any不能包含FUNCTION令牌。因此,如果您的扫描程序产生一个迷路FUNCTION令牌,则该解析将失败。

另外,请记住括号和括号不是ANYTHING标记。由于程序通常会有许多不属于函数文字的括号和大括号,因此您需要将这些括号和大括号添加到any规则中。请注意,您不希望将它们添加为单个标记;相反,您需要添加括号平衡序列(例如'(' any ')')。否则,您将在'}'上发生shift-reduce冲突。 (function(){ var a = { };...:解析器如何知道}没有关闭函数体?)

有两个非终端可能会更简单,例如[注3]:

any: /* empty */    { $$ = ""; }
   | any any_object { $$ = $1 + $2; }
   ;
any_object
   : ANYTHING
   | fun
   | '(' any ')'    { $$ = $1 + $2 + $3; }
   | '{' any '}'    { $$ = $1 + $2 + $3; }
   ;

您遇到的另一个问题是扫描仪会跳过空格,因此您的解析器永远不会看到它。这意味着它不会出现在语义值中,因此它将被您的转换剥离。这将打破依赖于自动分号插入的任何程序,以及某些其他结构(例如return 42;; return42;完全不同。)您可能希望将空格识别为单独的标记,并将其添加到您的any规则(或上面的any_object规则),以及funfunction之间的(规则中的可选元素以及){之间。 (由于空格将包含在any中,因此您不能将其添加到any非终端旁边;这可能会导致减少 - 减少冲突。)

说到自动分号插入,建议您不要在转换后的程序中依赖它。您应该在插入的console.log(...)语句后添加一个显式分号。

注释

  1. 正如Ira Baxter在评论中指出的那样,这种方法通常被称为“岛屿解析器”,因为你试图在其他无趣文本的海洋中找到“岛屿”。我相信推广这个术语的一篇有用的论文是Leon Moonen 2001年对WCRE的贡献,“使用岛语法生成强大的解析器”。 (谷歌会找到你的全文PDF。)谷歌也会找到关于这个范例的其他信息,包括艾拉巴克斯特自己更悲观的回答here on SO
  2. 这可能是对这个基本想法最严重的反对意见。如果您不想解决它,则需要对要转换的程序中的正则表达式进行以下限制:

    • 括号和括号必须平衡
    • 正则表达式不能包含字符串function

    第二个限制相对简单,因为您可以将function替换为完全等效的[f]unction。第一个更麻烦;您需要将/(/替换为/\x28/

  3. 在您提出的语法中,由于对any所代表的内容存在疑惑,因此存在错误。 any的第三个作品不应与fun作品重复;相反,它应该允许fun添加到any序列。 (也许你只是从那个作品中遗漏了any。但即便如此,当你可以使用非终端时,也没有必要重复fun制作。)