JavaCC自定义语法解析问题:末尾单个空行触发ParseException
解决JavaCC解析器末尾单个空行报错的问题
我来帮你分析一下问题的根源:
你的解析器在遇到末尾单个空行时抛出异常,核心原因是单个空行的换行符被错误地纳入了Body()规则的匹配逻辑,而Body()期望换行后跟着非空的<STRING> Token,但此时已经到了EOF,无法匹配,从而触发错误。
具体来说:
- 在
DEFAULT状态下,你定义了"\n" : INBODY,意味着任何换行符都会切换到INBODY状态,尝试匹配<STRING>。 - 你的
Body()规则是<STRING> ("\n" <STRING>)*,它会不断尝试匹配“换行+字符串”的组合。当末尾只有单个空行时,换行符触发状态切换后,解析器试图匹配<STRING>,但此时已经是EOF,且<STRING>默认不允许空匹配(JavaCC默认禁止空Token),因此抛出ParseException。 - 你之前修改
S()规则时去掉了["\n"],但并没有解决单个空行被Body()规则捕获的问题。
解决方案
我们需要调整Token定义和语法规则,将“单元结束后的空行”与“Body内部的换行”区分开:
1. 添加空行Token并调整匹配优先级
首先,在DEFAULT状态下定义专门匹配空行的Token,并确保它在单独的换行符之前被匹配,避免空行触发状态切换到INBODY:
<DEFAULT> TOKEN: { "(" | ")" | <ID: (["a"-"z","A"-"Z","0"-"9","-","_"])+ > | <#RAND: (" " | "\t" | "\r")* > | // 匹配单个空行(前后可含空白字符) <EMPTY_LINE: <RAND> "\n" <RAND> > | // 非空行的换行,切换到INBODY状态 "\n" : INBODY }
同时移除原来的<END> Token定义,我们用<EMPTY_LINE>*来表示任意数量的空行。
2. 修正<STRING>的定义(确保匹配非空行)
为了符合你的目标语言(Body行都是非空的),将<STRING>改为匹配至少一个非换行字符:
<INBODY> TOKEN: { // 匹配至少一个非换行/回车字符,匹配完成后切回DEFAULT状态 <STRING: (~["\n", "\r"])+> : DEFAULT }
3. 重新设计S()规则
让每个Signature()+Body()块之后可以跟任意数量(包括0)的空行,最后以EOF结束:
void S(): {} { (Signature() "\n" Body() <EMPTY_LINE>* )+ <EOF> }
4. (可选)添加OPTIONS配置
如果你的JavaCC版本需要显式允许空Token(比如<RAND>允许空匹配),可以在PARSER_BEGIN之前添加:
OPTIONS { ALLOW_EMPTY_TOKENS = true; }
完整修改后的代码
OPTIONS { ALLOW_EMPTY_TOKENS = true; } PARSER_BEGIN(Compiler) public class Compiler { public static void main(String[] args) { try { (new Compiler(new java.io.BufferedReader(new java.io.FileReader(args[0])))).S(); System.out.println("Syntax is correct"); } catch (Throwable e) { e.printStackTrace(); } } } PARSER_END(Compiler) <DEFAULT, INBODY> SKIP: { " " | "\t" | "\r" } <DEFAULT> TOKEN: { "(" | ")" | <ID: (["a"-"z","A"-"Z","0"-"9","-","_"])+ > | <#RAND: (" " | "\t" | "\r")* > | <EMPTY_LINE: <RAND> "\n" <RAND> > | "\n" : INBODY } <INBODY> TOKEN: { <STRING: (~["\n", "\r"])+> : DEFAULT } void S(): {} { (Signature() "\n" Body() <EMPTY_LINE>* )+ <EOF> } void Signature(): {} { "(" <ID> <ID> ")" } void Body(): {} { <STRING> ("\n" <STRING> )* }
验证
现在你的解析器可以正确处理以下场景:
- 末尾无空行
- 末尾有单个空行
- 末尾有多个空行
- 单元之间有任意数量的空行
内容的提问来源于stack exchange,提问作者Michael Langhammer
相关产品推荐
相关产品推荐

