You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

基于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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.28 03:19:58