JavaCUP无法生成A3Symbol.java符号文件的技术求助
问题概述
执行构建脚本时,java java_cup.Main -parser A3Parser -symbols A3Symbol < A3.cup 命令因Shift/Reduce冲突终止,未生成A3Symbol.java文件,导致后续编译A3.lex.java时出现大量"找不到符号"的错误,同时CUP提示冲突数量超出预期,终止代码生成。
核心问题分析
1. Shift/Reduce冲突(悬空else问题)
CUP输出的冲突提示是经典的悬空else歧义:
*** Shift/Reduce conflict found in state #75 between IfStmt ::= IF OPAREN BoolExpression CPAREN Statement (*) and IfStmt ::= IF OPAREN BoolExpression CPAREN Statement (*) ELSE Statement under symbol ELSE
旧版本CUP(如v0.10k)会因冲突数量超过预期而终止生成解析器代码,不会输出A3Symbol.java和A3Parser.java。
2. JLex编译的符号错误
所有"找不到符号"的错误都是因为A3Symbol.java未生成,JLex代码中引用的A3Symbol.Id、A3Symbol.OPAREN等常量不存在。此外,JLex生成的代码中Yytoken类型错误,不符合java_cup.runtime.Scanner接口的方法签名。
解决方案
一、解决Shift/Reduce冲突,让CUP正常生成文件
方法1:添加优先级声明
在CUP文件的precedence部分添加ELSE的优先级,强制CUP选择移进(绑定最近的else):
/* Precedences */ precedence left PLUS, MINUS; precedence left TIMES, DIVIDE; precedence left ELSE; // 添加此行,提升ELSE的优先级
方法2:修改语法结构消除歧义
拆分IfStmt为两个非终结符,从语法层面避免歧义:
/* Non terminals */ // 新增IfStmtWithElse非终结符 non terminal IfStmt, IfStmtWithElse; // 修改IfStmt的产生式 IfStmt ::= IfStmtWithElse | IF OPAREN BoolExpression CPAREN Statement; IfStmtWithElse ::= IF OPAREN BoolExpression CPAREN IfStmt ELSE Statement;
补充:完善未定义的非终结符
CUP文件中声明了BoolExpression、PrimaryExpr、ActualParams等非终结符,但没有对应的产生式,这会导致语法检查失败。需要补充这些规则,例如:
BoolExpression ::= Expression EQUALTO Expression | Expression NEQUALTO Expression; PrimaryExpr ::= Id | Num | OPAREN Expression CPAREN; ActualParams ::= /* empty */ | NonEmptyActualParams; NonEmptyActualParams ::= Expression | NonEmptyActualParams COM Expression;
二、修复JLex编译错误
1. 修正next_token方法返回类型
JLex生成的方法签名不符合java_cup.runtime.Scanner接口,修改JLex文件中的声明:
%function next_token : java_cup.runtime.Symbol
或者去掉%implements java_cup.runtime.Scanner,同时确保返回类型为Symbol。
2. 优化JLex规则
- 利用定义的
KEYWORDS规则,避免重复编写关键字匹配:
<YYINITIAL>{KEYWORDS} { switch(yytext()) { case "MAIN": return new Symbol(A3Symbol.MAIN); case "BEGIN": return new Symbol(A3Symbol.BEGIN); case "END": return new Symbol(A3Symbol.END); // 其他关键字同理 } }
- 转义正则表达式中的特殊字符,比如注释匹配规则:
<YYINITIAL>"/\\*\\*" { yybegin(COMMENT); } <COMMENT>"\\*\\*/" { yybegin(YYINITIAL); }
3. 调整脚本执行顺序
确保CUP生成文件后再编译JLex代码,修改构建脚本:
java JLex.Main A3.lex # 先执行CUP生成符号和解析器文件 java java_cup.Main -parser A3Parser -symbols A3Symbol < A3.cup # 先编译CUP生成的文件 javac A3Symbol.java A3Parser.java A3ParserUser.java # 再编译JLex生成的扫描器 javac A3.lex.java # 运行程序 java A3ParserUser
内容的提问来源于stack exchange,提问作者NickCecc

