我正在完成ECMAScript 5.1/JavaScript grammar for JavaCC。我按照规范完成了所有的代币和制作。
现在我面临一个我不知道如何解决的大问题。
JavaScript具有自动分号插入的 nice 功能:
What are the rules for JavaScript's automatic semicolon insertion (ASI)?
至quote the specifications,规则为:
分号插入有三个基本规则:
当从左到右解析程序时,遇到任何不允许的令牌(称为违规令牌) 生成语法,然后自动插入分号 如果出现以下一种或多种情况,则在违规令牌之前 是的:
- 违规令牌与前一个令牌分开 至少有一个LineTerminator。
- 违规令牌为
}
。当从左到右解析程序时,会遇到令牌输入流的末尾,并且解析器无法 将输入令牌流解析为单个完整的ECMAScript程序, 然后在输入的末尾自动插入分号 流。
- 醇>
当从左到右解析程序时,会遇到某些语法生成所允许的令牌,但是 生产是限制生产,令牌将是第一个 紧随其后的终端或非终端的令牌 限制生产中的注释
[no LineTerminator here]
(因此这样的令牌被称为限制令牌),并且 受限令牌与前一令牌分开至少一个LineTerminator
,然后在分配前自动插入分号 限制令牌。然而,还有一个更重要的条件 在前面的规则:如果是,则永远不会自动插入分号 然后分号将被解析为空语句或者如果那样 分号将成为一个标题中的两个分号之一 for statement(见12.6.3)。
我如何使用JavaCC实现此功能?
我到目前为止找到答案的结果是来自Dojo工具包的this grammar,其中JAVACODE
部分称为insertSemiColon
专用于该任务。但我没有看到这个方法在任何地方被调用(在语法和整个jslinker代码中都没有)。
如何使用JavaCC解决此问题?
另见这个问题:
(没有回答。)
评论中的一个问题:
说分号只需要在语法上允许分号的情况下插入是否正确?
我认为,只有在分号在语法上需要的情况下才能插入分号是正确的。
这里的相关部分是§7.9:
7.9自动分号插入
某些ECMAScript语句(空语句,变量语句,表达式语句,do-while 声明,继续声明,中断声明,返回声明和 throw语句)必须以分号结束。这样的分号 可能始终显式出现在源文本中。为了方便, 但是,可以从源文本中省略这样的分号 某些情况。这些情况通过这样说来描述 分号会自动插入源代码令牌中 在这些情况下流。
我们以return
语句为例:
ReturnStatement :
return ;
return [no LineTerminator here] Expression ;
所以(根据我的理解)语法分号是必需的,而不仅仅是允许(如你的问题所示)。
答案 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
)所以我们需要
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()
}