语法报错误显EOF为违规符号:如何判断EOF Token并获取前序Token?
问题描述
我实现了一个重写syntaxError方法的CustomErrorListener。当紧邻EOF的最后一个Token存在语法错误时,syntaxError方法会将EOF报告为违规符号。
我的顶层语法规则如下:
program: statementSeparator* version statements statementSeparator* EOF;
类似如下结尾的程序:
h q[0, 4
会在4之后(即EOF所在位置)报告错误,但实际违规符号应为缺少闭合]的q[0, 4。
现咨询:
- 是否因语法规则问题,导致
EOF的前序Token未被报告为违规符号? - 若语法规则无问题:
a) 有无规范方法判断EOFToken?
b) 是否可获取EOF的前序Token?
词法分析器(Lexer)
lexer grammar CqasmLexer; // 空白字符和注释会被跳过,不传递给解析器 WHITE_SPACE: [ \t]+ -> skip; SINGLE_LINE_COMMENT: '//' ~[\r\n]* -> skip; MULTI_LINE_COMMENT: '/*' .*? '*/' -> skip; // 符号 NEW_LINE: '\r'?'\n'; SEMICOLON: ';'; COLON: ':'; COMMA: ','; DOT: '.'; EQUALS: '='; OPEN_BRACKET: '['; CLOSE_BRACKET: ']'; OPEN_BRACE: '{'; CLOSE_BRACE: '}'; OPEN_PARENS: '('; CLOSE_PARENS: ')'; PLUS: '+'; // 该Token被UNARY_PLUS_OP和PLUS_OP共享 MINUS: '-'; // 该Token被UNARY_MINUS_OP和MINUS_OP共享 // 运算符 BITWISE_NOT_OP: '~'; LOGICAL_NOT_OP: '!'; POWER_OP: '**'; PRODUCT_OP: '*'; DIVISION_OP: '/'; MODULO_OP: '%'; SHL_OP: '<<'; SHR_OP: '>>'; CMP_GT_OP: '>'; CMP_LT_OP: '<'; CMP_GE_OP: '>='; CMP_LE_OP: '<='; CMP_EQ_OP: '=='; CMP_NE_OP: '!='; BITWISE_AND_OP: '&'; BITWISE_XOR_OP: '^'; BITWISE_OR_OP: '|'; LOGICAL_AND_OP: '&&'; LOGICAL_XOR_OP: '^^'; LOGICAL_OR_OP: '||'; TERNARY_CONDITIONAL_OP: '?'; // 关键字 VERSION: 'version' -> pushMode(VERSION_STATEMENT); MEASURE: 'measure'; QUBIT_TYPE: 'qubit'; BIT_TYPE: 'bit'; AXIS_TYPE: 'axis'; BOOL_TYPE: 'bool'; INT_TYPE: 'int'; FLOAT_TYPE: 'float'; // 数值字面量 BOOLEAN_LITERAL: 'true' | 'false'; INTEGER_LITERAL: Digit+; FLOAT_LITERAL: Digit+ '.' Digit+ Exponent? | Digit+ '.' Exponent? // 浮点字面量可以以点结尾 | '.' Digit+ Exponent?; // 或仅以点开头 fragment Digit: [0-9]; fragment Exponent: [eE][-+]?Digit+; // 标识符 IDENTIFIER: Letter (Letter | Digit)*; fragment Letter: [a-zA-Z_]; // 版本模式 // 遇到'version' Token时,进入版本模式 // 在版本模式中,'3.0'这类序列会被视为版本号,而非浮点字面量 mode VERSION_STATEMENT; VERSION_WHITESPACE: [ \t]+ -> skip; VERSION_NUMBER: Digit+ ('.' Digit+)? -> popMode;
语法分析器(Parser)
parser grammar CqasmParser; options { tokenVocab = CqasmLexer; } program: statementSeparator* version statements statementSeparator* EOF; version: VERSION VERSION_NUMBER; statements: (statementSeparator+ statement)*; statementSeparator: NEW_LINE | SEMICOLON; statement: QUBIT_TYPE arraySizeDeclaration? IDENTIFIER # qubitTypeDeclaration | BIT_TYPE arraySizeDeclaration? IDENTIFIER # bitTypeDeclaration | AXIS_TYPE IDENTIFIER (EQUALS expression)? # axisTypeDeclaration | BOOL_TYPE arraySizeDeclaration? IDENTIFIER (EQUALS expression)? # boolTypeDeclaration | INT_TYPE arraySizeDeclaration? IDENTIFIER (EQUALS expression)? # intTypeDeclaration | FLOAT_TYPE arraySizeDeclaration? IDENTIFIER (EQUALS expression)? # floatTypeDeclaration | expression EQUALS MEASURE expression # measureInstruction | IDENTIFIER expressionList # instruction ; arraySizeDeclaration: OPEN_BRACKET INTEGER_LITERAL CLOSE_BRACKET; expressionList: expression (COMMA expression)*; indexList: indexEntry (COMMA indexEntry)*; indexEntry: expression # indexItem | expression COLON expression # indexRange ; expression: OPEN_PARENS expression CLOSE_PARENS # parensExpression | <assoc=right> (PLUS | MINUS) expression # unaryPlusMinusExpression | <assoc=right> BITWISE_NOT_OP expression # bitwiseNotExpression | <assoc=right> LOGICAL_NOT_OP expression # logicalNotExpression | <assoc=right> expression POWER_OP expression # powerExpression | expression (PRODUCT_OP | DIVISION_OP | MODULO_OP) expression # productExpression | expression (PLUS | MINUS) expression # additionExpression | expression (SHL_OP | SHR_OP) expression # shiftExpression | expression (CMP_GT_OP | CMP_LT_OP | CMP_GE_OP | CMP_LE_OP) expression # comparisonExpression | expression (CMP_EQ_OP | CMP_NE_OP) expression # equalityExpression | expression BITWISE_AND_OP expression # bitwiseAndExpression | expression BITWISE_XOR_OP expression # bitwiseXorExpression | expression BITWISE_OR_OP expression # bitwiseOrExpression | expression LOGICAL_AND_OP expression # logicalAndExpression | expression LOGICAL_XOR_OP expression # logicalXorExpression | expression LOGICAL_OR_OP expression # logicalOrExpression | <assoc=right> expression TERNARY_CONDITIONAL_OP expression COLON expression # ternaryConditionalExpression | IDENTIFIER OPEN_PARENS expressionList? CLOSE_PARENS # functionCall | IDENTIFIER OPEN_BRACKET indexList CLOSE_BRACKET # index | IDENTIFIER # identifier | OPEN_BRACKET expression COMMA expression COMMA expression CLOSE_BRACKET # axisInitializationList | OPEN_BRACE expressionList CLOSE_BRACE # initializationList | BOOLEAN_LITERAL # booleanLiteral | INTEGER_LITERAL # integerLiteral | FLOAT_LITERAL # floatLiteral ;
解答
1. 语法规则是否导致问题?
不是。你的语法规则本身没问题,问题出在ANTLR的错误恢复机制:当解析器遇到无法匹配的输入(比如缺少]的q[0,4),它会尝试跳过Token直到找到能继续匹配的点。在你的案例中,解析器一直读到EOF才触发错误,因为没有后续Token能匹配语法规则里的闭合括号或语句分隔符。
2. 语法规则无问题时的解决方案
a) 规范判断EOF Token的方法
ANTLR中,EOF Token的类型固定为Token.EOF(值为-1)。你可以在CustomErrorListener的syntaxError方法中直接判断:
if (offendingToken.getType() == Token.EOF) { // 处理EOF作为违规符号的情况 }
b) 获取EOF的前序Token
可以通过解析器的TokenStream获取。在你的CustomErrorListener中,你可以持有TokenStream的引用,或者通过解析器实例获取:
// 假设你有TokenStream的引用tokenStream int eofIndex = tokenStream.index(); Token previousToken = tokenStream.get(eofIndex - 1);
注意要处理边界情况(比如输入为空时,eofIndex为0,此时没有前序Token)。
另外,你也可以在syntaxError方法中,通过recognizer(即解析器实例)获取TokenStream:
TokenStream tokenStream = ((Parser) recognizer).getInputStream(); int currentIndex = tokenStream.index(); if (currentIndex > 0) { Token previousToken = tokenStream.get(currentIndex - 1); }
内容的提问来源于stack exchange,提问作者rturrado
相关产品推荐
相关产品推荐

