ANTLR4输入不匹配及函数语法未识别问题求助
问题:ANTLR4解析
g(1,x);时的词法与语法错误 原始问题场景
使用Java结合ANTLR4开发f1vae,定义的Expr语法规则如下:
grammar Expr; // parser rules prog : ((decl_list expr_seq | expr_seq) ';' NEWLINE?)*; decl_list : decl (decl)*; expr_seq : expr (expr)*; decl : DEF fun var_list '=' expr ENDEF | DEF fun '=' expr ENDEF ; var_list : param (param)*; expr_list : expr (',' expr)*; expr : LET var '=' expr IN expr # assignExpr | fun '()' # callExpr | fun '(' expr_list ')' # callExpr | expr ('*'|'/') expr # infixExpr | expr ('+'|'-') expr # infixExpr | '(' expr ')' # parensExpr | num # numberExpr | '~' expr # negateExpr | var # varExpr ; num : '-'? INT | '-'? FLOAT ; fun : STR (('0' | '1' | '2' | '3' | '4' | '5' | '6' | '7' | '8' | '9') STR)*; var : STR (('_' | '-') STR)*; param : STR ('_' STR)*; // lexer rules NEWLINE: [\r\n]+ ; INT: [0-9]+ ; // should handle negatives FLOAT: [0-9]+'.'[0-9]* ; // should handle signs(+/-) WS: [ \t\r\n]+ -> skip; DEF: 'def'; ENDEF: 'endef'; LET: 'let'; IN: 'in'; STR: [a-zA-Z]+;
输入g(1,x);时触发错误:
line 1:2 mismatched input '1' expecting {'(', '-', '~', INT, FLOAT, 'let', STR}
解析树错误地将g识别为变量,而非函数名:
(prog (expr_seq (expr (var g)) (expr ( (expr 1 ,) x ))) ; )
错误原因分析
- 函数名规则定义缺陷:原
fun规则要求标识符必须包含「数字+字母」的后缀(STR (数字 STR)*),但输入的g是单纯字母组成的标识符,无法匹配fun规则,只能被识别为var,导致g(无法被解析为函数调用。 - 标识符规则重叠与解析优先级问题:
fun和var规则均能匹配单纯字母标识符,ANTLR解析器因上下文预测不足,优先将g识别为变量,后续的(被当作独立的括号表达式起始;而括号表达式仅允许单个expr,1,x中的逗号不符合规则,触发语法错误。 - 数字词法规则未处理正负号:原
num规则中的'-'?属于parser规则,词法分析时会将-1拆分为'-'和INT两个token,而非完整的负数。
解决方案:修改后的语法规则
grammar Expr; // parser rules prog : ((decl_list expr_seq | expr_seq) ';' NEWLINE?)*; decl_list : decl+; expr_seq : expr+; decl : DEF ID var_list '=' expr ENDEF | DEF ID '=' expr ENDEF ; var_list : param+; expr_list : expr (',' expr)*; expr : LET ID '=' expr IN expr # assignExpr | ID '()' # callExpr | ID '(' expr_list ')' # callExpr | expr ('*'|'/') expr # infixExpr | expr ('+'|'-') expr # infixExpr | '(' expr ')' # parensExpr | num # numberExpr | '~' expr # negateExpr | ID # varExpr ; num : INT | FLOAT ; param : ID; // lexer rules NEWLINE: [\r\n]+ ; INT: '-'? [0-9]+ ; FLOAT: '-'? [0-9]+ '.' [0-9]* ; WS: [ \t\r\n]+ -> skip; DEF: 'def'; ENDEF: 'endef'; LET: 'let'; IN: 'in'; ID: [a-zA-Z]+ ( [a-zA-Z0-9_'-]+ )*;
关键修改说明
- 统一标识符规则:用
ID词法规则替代原fun、var、param的parser规则,ID支持字母开头,后续可跟字母、数字、下划线、连字符,覆盖原函数名和变量名的所有需求。 - 确保解析优先级:
callExpr分支仍位于varExpr之前,解析器遇到ID后跟(时,会优先匹配函数调用,而非将ID单独识别为变量。 - 修复数字词法规则:将正负号整合到
INT和FLOAT的词法规则中,确保负数被识别为单个数字token。 - 简化重复规则:将
decl (decl)*改为decl+,expr (expr)*改为expr+,语义一致但语法更简洁。
修改后,输入g(1,x);会被正确解析为函数调用,解析树如下:
(prog (expr_seq (expr (callExpr g ( (expr_list (expr (numberExpr 1)) , (expr (varExpr x))) )) ) ; )
内容的提问来源于stack exchange,提问作者Gamja
相关产品推荐
相关产品推荐

