Warning: file_get_contents(/data/phpspider/zhask/data//catemap/6/cplusplus/131.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Javascript 自动插入分号的Bison错误恢复_Javascript_C++_Bison_Error Recovery - Fatal编程技术网

Javascript 自动插入分号的Bison错误恢复

Javascript 自动插入分号的Bison错误恢复,javascript,c++,bison,error-recovery,Javascript,C++,Bison,Error Recovery,我试图编写一个BysC++解析程序来解析JavaScript文件,但是我不能理解如何使分号可选。 至于ECMAScript 2018规范(第11.9章),分号实际上不是可选的,而是在解析过程中自动插入的。本规范中规定: 当从左到右解析源文本时,会遇到一个标记(称为违规标记),该标记是 语法的任何产物都不允许使用分号,则会自动将分号插入有问题的 如果以下一个或多个条件为真,则标记: 至少有一个LineTerminator[…]将有问题的标记与前一个标记分隔开 根据这一点,我试图以一种天真的方式

我试图编写一个BysC++解析程序来解析JavaScript文件,但是我不能理解如何使分号可选。 至于ECMAScript 2018规范(第11.9章),分号实际上不是可选的,而是在解析过程中自动插入的。本规范中规定:

当从左到右解析源文本时,会遇到一个标记(称为违规标记),该标记是 语法的任何产物都不允许使用分号,则会自动将分号插入有问题的 如果以下一个或多个条件为真,则标记:

  • 至少有一个LineTerminator[…]将有问题的标记与前一个标记分隔开
根据这一点,我试图以一种天真的方式解决这个问题:

  • 使用
    错误
    特殊令牌检测错误
  • 告诉lexer在操作过程中发生语法错误;如果在当前标记之前遇到换行符,lexer将在下一次
    yylex
    调用时返回一个新的分号标记;在随后的调用中,它将返回以前发生语法错误时出现问题的令牌
我的解析器的一个非常简化的结构如下所示:

program:
   stmt_list END
;

stmt_list:
    %empty
 |  stmt_list stmt
 |  stmt_list error  { /* error detected; tell the lexer about the syntax error */ }
;

stmt:
    value SEMICOLON
|   [other types of statements...]
;

value:
    NUMBER
|   STRING
;
但是这样做的话,如果文件包含一个有效的JavaScript语句,没有终止分号,但是包含一个换行符,那么当遇到一个有问题的标记时,解析器会将该语句的其余部分缩减为一个
错误
特殊标记。当我告诉lexer语法错误时,解析器已经将
错误
标记减少为
stmt\u列表
1,并且之前的有效指令丢失,使得分号插入无效

显然,我不想让解析器放弃有效语句而转到下一个语句


我怎样才能做到这一点?这是正确的方法还是我遗漏了什么?

我认为这种方法不可行

需要注意的是,您必须在任何减少发生之前检测错误。因此,对于在语句末尾插入分号,您需要将错误结果添加到
stmt
,而不是
stmt\u列表
。所以你会得到这样的结果:

stmt_list
     :  %empty
     |  stmt_list stmt

stmt: value ';'   { handle_value_stmt(); }
    | value error { handle_value_stmt(); }
    | [other types of statements...]
不会插入分号;它只是假装插入了分号。(如果无法插入分号,则会触发另一个错误。)

但是,由于它不涉及lexer,因此不管缺少的分号是否在一行的末尾,都会发生这种情况,这太过分了。因此,理想的解决方案是告诉lexer生成一个分号标记作为下一个标记。但是在检测到错误的地方,lexer已经生成了lookahead令牌,解析器知道lookahead令牌是什么。它将使用记录的先行标记继续解析

还有一个问题是,在这一点上如何与lexer通信,因为中间规则操作与错误恢复算法并不能很好地配合。理论上,您可以使用调用
yyerror
来报告错误的事实,但这意味着
yyerror
需要能够推断这不是一个“真正的”错误,这意味着它必须深入
yyparse
的内脏。(我确信这是可能的,但我不知道怎么做,而且我觉得这不值得推荐。)

现在,从理论上讲,可以告诉解析器放弃先行标记,并告诉词法分析器生成一个分号,后跟它刚刚发送的标记的重复。所以,如果你够固执的话,通过把hack叠加到hack上,你几乎不可能做到这一点。但最终你会遇到一些很难维护、验证和测试的问题。(确保它在所有角落的情况下都能正常工作也是一项挑战。)

这不需要考虑其他可以插入分号的情况

我使用ASI的方法是简单地分析语法,找出哪些连续标记对是可能的。(这很容易做到;您只需要构造第一个和最后一个集合,然后通过查看连续符号来阅读所有的结果。)然后,如果输入包含标记A,后跟一个或多个新行,后跟标记B,并且在语法中A不可能后跟B,那么这就是分号插入的候选。分号插入可能会失败,但这将生成语法错误,因此不能得到假阳性。(您可能需要修复语法错误消息,但此时您至少知道插入了分号。)

证明该算法有效更为棘手,因为理论上可能出现这样的情况:在某些上下文中,
A
后面可能跟有
B
,但在当前上下文中这是不可能的,而
A;B
在当前环境下是可能的。在这种情况下,您可能会错过可能的分号插入。我没有详细看过最近的JS版本,但很久以前,当我写一个JS lexer时,我成功地证明了我自己的满意,没有这样的情况


注意:因为这个问题是在评论中提出的,所以我会加上一点握手,尽管我真的不建议采用这种方法

如果不深入野牛的内脏,就不可能“解除”标记,包括
error
token(或多或少是真正的标记)。当
错误
标记移位时,解析将有效地提交到错误生成。因此,如果你想取消这个错误,你必须接受这个事实并解决它

错误
标记移位后,解析器将跳过标记,直到遇到可移位标记为止。
    stmt: value ';'       { handle_value_stmt(); }
        | value error ';' { handle_value_stmt(); }
    stmt: value ';'       { handle_value_stmt(); }
        | value error ';' { handle_value_stmt(); }
        | value error NO_ASI { handle_real_error(); }