使用 Bison 识别特定的语法错误
Recognize specific grammatical mistakes with Bison
我正在尝试使用 Bison 来开发我自己的编程语言。我已经为我的语法编写了 .y 文件。但是,我想知道是否有办法在用户尝试使用无效语法解析源代码的情况下让 Bison 给出有用的错误消息。例如,假设我的语法中有以下规则:
if_statement: IF expr '{' statement_list '}' {$$=createNode(IF,,);}
;
假设源代码遗漏了右大括号。按照我的理解,Bison会报找不到减少代码的规则。能否让 Bison 认识到有一个未完成的 if
从某某行开始并向用户报告?
大括号缺失的地方很少被发现,因为通常情况下,缺失大括号之后的任何内容都可能出现在它之前。如果缺少的右大括号紧跟另一个右大括号,这一点尤其明显,但它可以简单地跟在(在这种情况下)另一个语句之后:
function some_function() {
....
while (some_condition) {
...
if (some_other_condition) {
...
break;
// } /* Commented out by mistake */
a = 3;
...
}
return a;
}
function another_function() {
...
}
如果您的语言不允许嵌套函数定义,那么 another_function
的定义将触发错误;如果它确实允许嵌套函数定义,那么 another_function
将只是在一个意想不到的范围内定义,并且解析将继续,可能直到文件结束。
检测此类错误的一种方法是用预期的缩进检查每一行的缩进。但是,除非您的语言有一些正确缩进的概念(例如 Python),否则您不能将误导性缩进标记为错误。所以你能做的最好的就是记录意外的缩进,以便在最终遇到语法错误时将其用作线索(如果有语法错误,因为它可能只是程序员不关心让他们人类可读的程序)。这种错误检测方法的复杂性可能是它在主流语言中如此罕见的原因,尽管我个人认为这是一种很有潜力的方法。
我通常提倡对错误程序进行二次解析。第一个解析针对正确的程序进行了优化,这意味着它不需要任何良好错误消息所需的开销,例如跟踪每个标记的位置。如果程序在语法上是正确的,那么您可以继续将 AST 转换为编译代码。如果程序出现语法错误,您可以在开始时重新开始解析,然后您当然可以自由使用启发式方法(例如缩进检查)来尝试更好地定位错误。
说了这么多,您可能会更好地继续实施您的语言,return 稍后会产生更好的诊断问题。
Bison 确实提供了一种在某些情况下生成更有用的错误消息的机制。
首先,您至少应该从 Flex 启用行号跟踪,这几乎是零工作量。您可能还想跟踪精确的令牌位置,这需要做更多的工作,但不会太多。 (有关示例代码,请参阅 Character Position from starting of a line, and (以及其他)。)
其次,让野牛产生详细的错误信息。这只需要在你的野牛序言中增加两行:
%define parse.error verbose
%define parse.lac full
请务必阅读 bison 手册以了解一些重要的注意事项。具体而言,LAC 可能涉及大量开销。但是产生的错误消息通常很有帮助。
最后,使用 bison 的错误恢复机制,在检测到第一个语法错误后继续解析,从而允许您一次报告多个语法错误 运行。对于用户来说,这通常不会那么令人沮丧,尽管您应该在某个阈值错误计数时终止解析,因为错误恢复后非常高的错误计数通常意味着错误恢复本身失败并且许多后续错误消息是伪造的。
同样,野牛手册有一些关于如何使用 error
设施的有用建议。
我正在尝试使用 Bison 来开发我自己的编程语言。我已经为我的语法编写了 .y 文件。但是,我想知道是否有办法在用户尝试使用无效语法解析源代码的情况下让 Bison 给出有用的错误消息。例如,假设我的语法中有以下规则:
if_statement: IF expr '{' statement_list '}' {$$=createNode(IF,,);}
;
假设源代码遗漏了右大括号。按照我的理解,Bison会报找不到减少代码的规则。能否让 Bison 认识到有一个未完成的 if
从某某行开始并向用户报告?
大括号缺失的地方很少被发现,因为通常情况下,缺失大括号之后的任何内容都可能出现在它之前。如果缺少的右大括号紧跟另一个右大括号,这一点尤其明显,但它可以简单地跟在(在这种情况下)另一个语句之后:
function some_function() {
....
while (some_condition) {
...
if (some_other_condition) {
...
break;
// } /* Commented out by mistake */
a = 3;
...
}
return a;
}
function another_function() {
...
}
如果您的语言不允许嵌套函数定义,那么 another_function
的定义将触发错误;如果它确实允许嵌套函数定义,那么 another_function
将只是在一个意想不到的范围内定义,并且解析将继续,可能直到文件结束。
检测此类错误的一种方法是用预期的缩进检查每一行的缩进。但是,除非您的语言有一些正确缩进的概念(例如 Python),否则您不能将误导性缩进标记为错误。所以你能做的最好的就是记录意外的缩进,以便在最终遇到语法错误时将其用作线索(如果有语法错误,因为它可能只是程序员不关心让他们人类可读的程序)。这种错误检测方法的复杂性可能是它在主流语言中如此罕见的原因,尽管我个人认为这是一种很有潜力的方法。
我通常提倡对错误程序进行二次解析。第一个解析针对正确的程序进行了优化,这意味着它不需要任何良好错误消息所需的开销,例如跟踪每个标记的位置。如果程序在语法上是正确的,那么您可以继续将 AST 转换为编译代码。如果程序出现语法错误,您可以在开始时重新开始解析,然后您当然可以自由使用启发式方法(例如缩进检查)来尝试更好地定位错误。
说了这么多,您可能会更好地继续实施您的语言,return 稍后会产生更好的诊断问题。
Bison 确实提供了一种在某些情况下生成更有用的错误消息的机制。
首先,您至少应该从 Flex 启用行号跟踪,这几乎是零工作量。您可能还想跟踪精确的令牌位置,这需要做更多的工作,但不会太多。 (有关示例代码,请参阅 Character Position from starting of a line,
其次,让野牛产生详细的错误信息。这只需要在你的野牛序言中增加两行:
%define parse.error verbose
%define parse.lac full
请务必阅读 bison 手册以了解一些重要的注意事项。具体而言,LAC 可能涉及大量开销。但是产生的错误消息通常很有帮助。
最后,使用 bison 的错误恢复机制,在检测到第一个语法错误后继续解析,从而允许您一次报告多个语法错误 运行。对于用户来说,这通常不会那么令人沮丧,尽管您应该在某个阈值错误计数时终止解析,因为错误恢复后非常高的错误计数通常意味着错误恢复本身失败并且许多后续错误消息是伪造的。
同样,野牛手册有一些关于如何使用 error
设施的有用建议。