如何在JavaCC中实现JavaScript自动分号插入?

时间:2014-11-21 20:27:28

标签: javascript parsing dojo grammar javacc

我正在完成ECMAScript 5.1/JavaScript grammar for JavaCC。我按照规范完成了所有的代币和制作。

现在我面临一个我不知道如何解决的大问题。

JavaScript具有自动分号插入的 nice 功能:

  

What are the rules for JavaScript's automatic semicolon insertion (ASI)?

quote the specifications,规则为:

  

分号插入有三个基本规则:

     
      
  1. 当从左到右解析程序时,遇到任何不允许的令牌(称为违规令牌)   生成语法,然后自动插入分号   如果出现以下一种或多种情况,则在违规令牌之前   是的:

         
        
    • 违规令牌与前一个令牌分开   至少有一个LineTerminator。
    •   
    • 违规令牌为}
    •   
  2.   
  3. 当从左到右解析程序时,会遇到令牌输入流的末尾,并且解析器无法   将输入令牌流解析为单个完整的ECMAScript程序,   然后在输入的末尾自动插入分号   流。

  4.   
  5. 当从左到右解析程序时,会遇到某些语法生成所允许的令牌,但是   生产是限制生产,令牌将是第一个   紧随其后的终端或非终端的令牌   限制生产中的注释[no LineTerminator here]   (因此这样的令牌被称为限制令牌),并且   受限令牌与前一令牌分开至少一个   LineTerminator,然后在分配前自动插入分号   限制令牌。

  6.         

    然而,还有一个更重要的条件   在前面的规则:如果是,则永远不会自动插入分号   然后分号将被解析为空语句或者如果那样   分号将成为一个标题中的两个分号之一   for statement(见12.6.3)。

我如何使用JavaCC实现此功能?

我到目前为止找到答案的结果是来自Dojo工具包的this grammar,其中JAVACODE部分称为insertSemiColon专用于该任务。但我没有看到这个方法在任何地方被调用(在语法和整个jslinker代码中都没有)。

如何使用JavaCC解决此问题?

另见这个问题:

  

javascript grammar and automatic semocolon insertion

(没有回答。)

评论中的一个问题:

  

说分号只需要在语法上允许分号的情况下插入是否正确?

我认为,只有在分号在语法上需要的情况下才能插入分号是正确的。

这里的相关部分是§7.9:

  

7.9自动分号插入

     

某些ECMAScript语句(空语句,变量语句,表达式语句,do-while   声明,继续声明,中断声明,返回声明和   throw语句)必须以分号结束。这样的分号   可能始终显式出现在源文本中。为了方便,   但是,可以从源文本中省略这样的分号   某些情况。这些情况通过这样说来描述   分号会自动插入源代码令牌中   在这些情况下流。

我们以return语句为例:

ReturnStatement :
    return ;
    return [no LineTerminator here] Expression ;

所以(根据我的理解)语法分号是必需的,而不仅仅是允许(如你的问题所示)。

1 个答案:

答案 0 :(得分:1)

分号插入的3条规则可以在section 7.9.1 of the ECMAScript 5.1 standard

中找到

我认为标准中的规则1和2可以用语义预测来处理。

void PossiblyInsertedSemicolon() 
{}
{
    LOOKAHEAD( {semicolonNeedsInserting()} ) {}
|
    ";"
}

那么分号什么时候需要插入?当其中一个是真的时

  • 当下一个标记不是分号并且在另一条线上时(getToken(1).kind != SEMICOLON && getToken(0).endLine < getToken(1).beginLine
  • 当下一个标记是右括号时。
  • 当下一个标记是EOF时

所以我们需要

boolean semicolonNeedsInserting() {
    return (`getToken(1).kind != SEMICOLON && getToken(0).endLine < getToken(1).beginLine`) 
    || getToken(1).kind == RBRACE
    || getToken(1).kind == EOF ;
}

这照顾了标准的第1条和第2条。

对于规则3(限制制作),如我对this question的回答中所述,您可以执行以下操作

void returnStatement()
{}
{
    "return"
    [   // Parse an expression unless either the next token is a ";", "}" or EOF, or the next token is on another line.
        LOOKAHEAD( {   getToken(1).kind != SEMICOLON
                    && getToken(1).kind != RBRACE
                    && getToken(1).kind != EOF
                    && getToken(0).endLine == getToken(1).beginLine} )
        Expression()
    ]
    PossiblyInsertedSemicolon() 
}