2

我想做的是

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

为了。例如输入

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} 
;
4

1 回答 1

4

我的想法是否只专注于解析函数而忽略其他所有内容(即把它们倒回去)本质上是有缺陷的?

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

首先,您需要使您的词法分析器更加健壮。它应该正确识别注释和字符串文字。否则,您可能会匹配误报:隐藏在此类文字中的明显标记。理想情况下,您还可以识别正则表达式,但这要复杂得多,因为它需要解析器的合作,并且像您建议的那样简单的解析器没有足够的上下文来区分除法运算符和正则表达式的开头。[笔记2]

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

出现问题是因为any不能包含FUNCTION令牌。因此,如果您的扫描仪产生一个杂散FUNCTION令牌,解析将失败。

另外,请记住括号和大括号不是ANYTHING标记。由于程序通常会有许多不属于函数字面量的括号和大括号,因此您需要将它们添加到您的any规则中。请注意,您不想将它们添加为单个标记;相反,您需要添加括号平衡的序列('(' any ')'例如)。否则,您将在'}'. ( 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上面的规则),以及规则中的可选元素funbetween functionand(和 between )and {。(由于空格将包含在 中any,因此您不能将其添加到any非终端旁边;这可能会导致 reduce-reduce 冲突。)

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


笔记

  1. 正如 Ira Baxter 在评论中指出的那样,这种方法通常被称为“岛屿解析器”,因为您试图在原本无趣的文本海洋中找到“岛屿”。我认为推广这个术语的一篇有用的论文是 Leon Moonen 2001 年对 WCRE 的贡献,“使用岛语法生成健壮的解析器”。(Google 会为您找到全文 PDF。)Google 还会为您找到有关此范例的其他信息,包括 Ira Baxter 自己在 SO 上更悲观的回答

  2. 这可能是对基本思想最严重的反对意见。如果您不想解决它,则需要在要转换的程序中对正则表达式进行以下限制:

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

    第二个限制相对简单,因为您可以function用完全等效的[f]unction. 第一个比较麻烦;你需要/(/用类似的东西替换/\x28/

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

于 2016-05-13T16:16:31.213 回答