基于ANTLR4的解释器return语句返回‘Invalid Expression’求助
基于ANTLR4的解释器函数return语句报错问题
我正在用ANTLR4开发一款解释器,现在给语言添加函数功能。没有return语句时函数能正常运行,但加了return语句后就无法正常工作,会返回‘Invalid Expression’。以下是完整语法定义和Visitor类实现代码:
语法定义(ANTLR4)
DECLARE : 'declare'; SET : 'set'; TO : 'to'; SHOW : 'show'; SHOWLINE : 'showline'; REPEAT : 'repeat'; WHILE : 'while'; TIMES : 'times'; END_REPEAT : 'end repeat'; IF : 'if'; THEN : 'then'; ELSE : 'else'; ELSE_IF : 'else if'; END_IF : 'end if'; INPUT : 'input'; FOR : 'for'; END_FOR : 'end for'; FROM : 'from'; STEP : 'step'; BY : 'by'; GENERATE : 'generate'; STOP : 'stop'; FUNCTION : 'function'; RETURN : 'return'; END_FUNCTION : 'end function'; ITERATE : 'iterate'; IN : 'in'; OVER : 'over'; END_ITERATE : 'end iterate'; CHOOSE : 'choose'; WHEN : 'when'; OTHERWISE : 'otherwise:'; END_CHOOSE : 'end choose'; TRUE : 'true'; FALSE : 'false'; LPAREN : '('; RPAREN : ')'; LBRACKET : '['; RBRACKET : ']'; LBRACE : '{'; RBRACE : '}'; COMMA : ','; LESS_THAN : '<'; LESS_THAN_EQUAL : '<='; GREATER_THAN : '>'; GREATER_THAN_EQUAL: '>='; EQUAL : '=='; NOT_EQUAL : '!='; MUL : '*'; DIV : '/'; MODULO : 'remind'; POW : '^'; PLUS : '+'; MINUS : '-'; SEMICOLON : ';'; COLON : ':'; AND : 'and'; OR : 'or'; NOT : 'not'; NEWLINE : '\r'? '\n'; INDENT : [\t]+; STRING : '"' ( '\\' . | ~[\\"] )* '"' | '\'' ( '\\' . | ~[\\'] )* '\'' ; NUMBER : ('+' | '-')? [0-9]+ ('.' [0-9]+)?; ID : [a-zA-Z] [a-zA-Z0-9_]*; COMMENT : '//' ~[\r\n]* -> skip; WS : [ \t\r\n]+ -> skip; SPACE : ' ' -> skip; program : statement_list* | EOF; statement_list : statement+ (NEWLINE|SEMICOLON)*; statement : declareStatement | setStatement | assignStatement | ifStatement | inputStatement | forStatement | generateStatement | showStatement | repeatStatement | repeatTimeStatement | showlnStatement | iterateStatement | chooseStatement | functionDecl | functionCall ; declareStatement : DECLARE (ID | arrayAccess) (COMMA (ID | arrayAccess))*; setStatement : (SET ID | ID) (TO expression | TO arrayAccess | TO arrayElement); assignStatement : (ID | arrayAccess) TO expression; showStatement : SHOW expression; ifStatement : IF expression THEN statement_list* (elseifStatement)* (elseStatement)? END_IF; elseifStatement : ELSE_IF expression THEN statement_list*; elseStatement : ELSE statement_list*; inputStatement : (SET ID | ID) TO INPUT LPAREN expression RPAREN; repeatStatement : REPEAT WHILE LPAREN expression RPAREN statement_list* END_REPEAT; repeatTimeStatement : REPEAT expression TIMES statement_list* END_REPEAT; showlnStatement : SHOWLINE LPAREN RPAREN; forStatement : FOR ID FROM expression TO expression (STEP expression)? (NEWLINE)? (statement_list)* END_FOR; generateStatement : GENERATE ID FROM expression TO expression (BY expression)? (statement_list)* STOP; iterateStatement : ITERATE LPAREN ID OVER iteratable RPAREN (NEWLINE)? statement_list* END_ITERATE; iteratable : expression; chooseStatement : CHOOSE expression (NEWLINE)? whenStatement+ defaultStatement? END_CHOOSE; whenStatement : WHEN expression COLON statement+ (NEWLINE)?; defaultStatement : OTHERWISE statement+; functionDecl : FUNCTION ID LPAREN param_list RPAREN (NEWLINE)? statement_list* returnStatement? END_FUNCTION; returnStatement : RETURN expression? (NEWLINE|SEMICOLON)?; param_list : (ID (COMMA ID)*)?; functionCall : ID LPAREN arg_list RPAREN; arg_list : (expression (COMMA expression)*)?; arrayAccess : ID LBRACKET index RBRACKET; arrayElement : LBRACKET (expression (COMMA expression)*) RBRACKET; index : expression; expression : LPAREN expression RPAREN | expression (MUL | DIV | MODULO | POW) expression | expression (PLUS | MINUS) expression | expression (LESS_THAN_EQUAL | GREATER_THAN_EQUAL | LESS_THAN | GREATER_THAN) expression | expression (EQUAL | NOT_EQUAL) expression | expression AND expression | expression OR expression | NOT expression | functionCall | arrayAccess | TRUE | FALSE |STRING | NUMBER | ID ;
Visitor类实现(C#)
private bool _hasReturn = false; public override object VisitFunctionDecl(EasyBiteParser.FunctionDeclContext context) { var functionName = context.ID().GetText(); var functionParams = context.param_list() != null ? context.param_list().ID().Select(id => id.GetText()).ToList() : null; var functionBody = context.statement_list(); Func<List<object>, object> functionImpl = (args) => { // 创建函数的局部作用域 var localVariables = new Dictionary<string, object>(); // 将参数绑定到局部作用域 if (functionParams != null && args != null) { for (int i = 0; i < functionParams.Count; i++) { localVariables[functionParams[i]] = args[i]; } } // 将局部变量字典压入栈 localVariablesStack.Push(localVariables); // 执行函数体语句 try { _hasReturn = false; foreach (var statement in functionBody) { Visit(statement); } } catch (ReturnException e) { // 捕获return语句,返回指定值 _hasReturn = true; return e.Value; } // 弹出局部变量字典 localVariablesStack.Pop(); // 没有return语句时返回null if (!_hasReturn) { return null; } return null; }; // 将函数添加到全局变量字典 functionTable[functionName] = functionImpl; return null; } public override object VisitFunctionCall(EasyBiteParser.FunctionCallContext context) { // 获取函数名和实现 var functionName = context.ID().GetText(); var functionImpl = (Func<List<object>, object>)functionTable[functionName]; // 获取函数参数 var functionArgs = context.arg_list() != null ? context.arg_list().expression().Select(expr => Visit(expr)).ToList() : null; // 创建函数的局部作用域 var localVariables = new Dictionary<string, object>(); // 将局部作用域压入栈 localVariablesStack.Push(localVariables); // 将参数绑定到局部作用域 if (functionArgs != null && functionArgs.Count > 0) { for (int i = 0; i < functionArgs.Count; i++) { localVariables[Convert.ToString(functionArgs[i])] = functionArgs[i]; } } object result = null; try { // 调用函数实现 result = functionImpl(functionArgs); } catch (ReturnException ex) { // 从ReturnException中提取返回值 result = ex.Value; } // 弹出局部作用域 localVariablesStack.Pop(); return result; } public override object VisitReturnStatement(EasyBiteParser.ReturnStatementContext context) { var value = context.expression() != null ? Visit(context.expression()) : null; throw new ReturnException(value); }
问题分析与修复方案
核心问题1:语法规则未包含returnStatement
当前statement规则里没有把returnStatement列为合法语句,导致解析器遇到return时会将其识别为无效表达式,直接抛出错误。
修复:更新statement规则
把returnStatement添加到statement的可选分支中:
statement : declareStatement | setStatement | assignStatement | ifStatement | inputStatement | forStatement | generateStatement | showStatement | repeatStatement | repeatTimeStatement | showlnStatement | iterateStatement | chooseStatement | functionDecl | functionCall | returnStatement // 新增该行 ;
核心问题2:函数调用时重复处理作用域
VisitFunctionCall中手动创建并压入了局部作用域,但函数实现functionImpl内部已经完成了作用域的创建和压栈操作,导致作用栈被重复压入,后续弹出时会出现栈不平衡,引发变量查找异常。
修复:移除VisitFunctionCall中的冗余作用域代码
修改后的VisitFunctionCall:
public override object VisitFunctionCall(EasyBiteParser.FunctionCallContext context) { var functionName = context.ID().GetText(); var functionImpl = (Func<List<object>, object>)functionTable[functionName]; var functionArgs = context.arg_list() != null ? context.arg_list().expression().Select(expr => Visit(expr)).ToList() : null; object result = null; try { result = functionImpl(functionArgs); } catch (ReturnException ex) { result = ex.Value; } return result; }
核心问题3:函数实现中栈弹出时机错误
原代码中localVariablesStack.Pop()放在try块外部,当触发ReturnException时会直接返回,导致栈无法弹出,后续代码会出现作用域混乱。
修复:用finally块确保栈弹出
修改functionImpl中的代码:
Func<List<object>, object> functionImpl = (args) => { var localVariables = new Dictionary<string, object>(); if (functionParams != null && args != null) { for (int i = 0; i < functionParams.Count; i++) { localVariables[functionParams[i]] = args[i]; } } localVariablesStack.Push(localVariables); object result = null; bool hasReturn = false; try { foreach (var statement in functionBody) { Visit(statement); } result = null; } catch (ReturnException e) { hasReturn = true; result = e.Value; } finally { // 无论是否触发return,都确保弹出栈 localVariablesStack.Pop(); } return result; };
额外优化:线程安全问题
原代码中_hasReturn是类级成员变量,若解释器支持多线程或递归调用,会出现状态混乱。建议改为functionImpl内部的局部变量,如上述修复代码所示。
内容的提问来源于stack exchange,提问作者Muhammad Baba Goni
相关产品推荐
相关产品推荐

