JavaCC 自定义错误导致 "Expansion can be matched by empty string."
JavaCC custom errors cause "Expansion can be matched by empty string."
我希望使我的 JavaCC 解析器 return 比默认值更具体的自定义错误消息。
我目前的基本结构是这样的:
Foo(): {} { (A() B())+ }
A(): {} { <TOKA1> | <TOKA2> }
B(): {} { <TOKB1> | <TOKB2> }
我一直在研究如何抛出自定义错误消息,标准方法似乎是这样的:
A(): {} { <TOKA1> | <TOKA2>
| {throw new ParseException("Expected A, found " + getToken(1).image + ".");} }
但是,在 A 和 B 上执行此操作会导致编译器产生错误:
Expansion within "(...)+" can be matched by empty string.
这是可以理解的,因为解析器不会'know'空选项会终止解析过程。它预测它可以将空字符串匹配到无穷大。尽管如此,我找不到或想不出任何其他简单的方法来抛出这样的错误。达到我想要的结果的最佳方法是什么?
假设您的解析器已经完成了一次或多次循环。当下一个标记不是 TOKA1 或 TOKA2 时,您不希望解析器离开循环吗?
例如如果你的其余语法是
void Start() : {} { Foo() C() <EOF> }
如果输入是
,你绝对不希望出现错误
<TOKA1> <TOKB1> <TOKC> <EOF>
其中 <TOKC>
是一些可能位于 C 开头的标记。
所以我的建议是
Foo(): {} {
AForSure()
B()
(A() B())* }
AForSure() : {} { A() | {throw new ParseException("Expected A, found " + getToken(1).image + ".");} }
A(): {} { <TOKA1> | <TOKA2> }
B(): {} { <TOKB1> | <TOKB2> | {throw new ParseException("Expected B, found " + getToken(1).image + ".");} }}
这可能无法提供您想要的错误消息质量。例如。如果输入是
<TOKA1> <TOKB1> <TOKB2> <EOF>
您可能会遇到错误 "Expected C, found a TOKB2"。因此,您可能希望将该错误更改为 "Expected A or C, found a TOKB2"。
另一种方法是使用递归而不是循环。假设您的列表总是在括号中,例如,Foo 的唯一用途是在 Bar 中,而 Bar 看起来像这样
Bar() : {} { "(" Foo() ")" }
所以您只想在点击“)”时退出 Foo 循环。其他任何事情都是错误的。您可以将语法重写为
Bar() : {} { "(" A("A") B() MoreFoos() }
MoreFoos() : {} { ")" | A("A or ')'") B() MoreFoos() }
A(String expected): {} { <TOKA1> | <TOKA2>
| {throw new ParseException("Expected "+expected+", found " + getToken(1).image + ".");} } }
B(): {} { <TOKB1> | <TOKB2>
| {throw new ParseException("Expected B, found " + getToken(1).image + ".");} }}
我希望使我的 JavaCC 解析器 return 比默认值更具体的自定义错误消息。
我目前的基本结构是这样的:
Foo(): {} { (A() B())+ }
A(): {} { <TOKA1> | <TOKA2> }
B(): {} { <TOKB1> | <TOKB2> }
我一直在研究如何抛出自定义错误消息,标准方法似乎是这样的:
A(): {} { <TOKA1> | <TOKA2>
| {throw new ParseException("Expected A, found " + getToken(1).image + ".");} }
但是,在 A 和 B 上执行此操作会导致编译器产生错误:
Expansion within "(...)+" can be matched by empty string.
这是可以理解的,因为解析器不会'know'空选项会终止解析过程。它预测它可以将空字符串匹配到无穷大。尽管如此,我找不到或想不出任何其他简单的方法来抛出这样的错误。达到我想要的结果的最佳方法是什么?
假设您的解析器已经完成了一次或多次循环。当下一个标记不是 TOKA1 或 TOKA2 时,您不希望解析器离开循环吗?
例如如果你的其余语法是
void Start() : {} { Foo() C() <EOF> }
如果输入是
,你绝对不希望出现错误 <TOKA1> <TOKB1> <TOKC> <EOF>
其中 <TOKC>
是一些可能位于 C 开头的标记。
所以我的建议是
Foo(): {} {
AForSure()
B()
(A() B())* }
AForSure() : {} { A() | {throw new ParseException("Expected A, found " + getToken(1).image + ".");} }
A(): {} { <TOKA1> | <TOKA2> }
B(): {} { <TOKB1> | <TOKB2> | {throw new ParseException("Expected B, found " + getToken(1).image + ".");} }}
这可能无法提供您想要的错误消息质量。例如。如果输入是
<TOKA1> <TOKB1> <TOKB2> <EOF>
您可能会遇到错误 "Expected C, found a TOKB2"。因此,您可能希望将该错误更改为 "Expected A or C, found a TOKB2"。
另一种方法是使用递归而不是循环。假设您的列表总是在括号中,例如,Foo 的唯一用途是在 Bar 中,而 Bar 看起来像这样
Bar() : {} { "(" Foo() ")" }
所以您只想在点击“)”时退出 Foo 循环。其他任何事情都是错误的。您可以将语法重写为
Bar() : {} { "(" A("A") B() MoreFoos() }
MoreFoos() : {} { ")" | A("A or ')'") B() MoreFoos() }
A(String expected): {} { <TOKA1> | <TOKA2>
| {throw new ParseException("Expected "+expected+", found " + getToken(1).image + ".");} } }
B(): {} { <TOKB1> | <TOKB2>
| {throw new ParseException("Expected B, found " + getToken(1).image + ".");} }}