ANTLR 访客处理的多个问题

问题描述 投票:0回答:1

我正在ANTLR和Java上做一个伪编程语言。我遇到了一些无法处理的问题:

  1. If、else if 和 else:每个条件仅执行一个语句,因此其他语句将被跳过。例如:

    else { show(a); show(b); }
    。 仅显示
    a
    。返回 int 之前可以正常工作,但现在
    null

  2. 我无法制作 arrayAccess,这意味着我无法制作此:

    int b = array[1]
    show(array[2])

  3. 考虑以下函数并调用:

    public string fun(string a) {
        return "Haiii, " + a + "!\n";
    }
    
    string greet = fun("John")
    

    greet
    仍然是空的,里面什么也没有。

这是我的 GrammarsVisitor.java:

import java.util.HashMap;
import java.util.Map;
import java.util.Stack;
import java.util.List;
import java.util.ArrayList;

public class GrammarsVisitor extends GrammarBaseVisitor<Object> {
    private final Stack<Map<String, Object>> scopes = new Stack<>();
    private final Map<String, FunctionDefinition> functions = new HashMap<>();

    public GrammarsVisitor() {
        // Push the global scope
        scopes.push(new HashMap<>());
    }

    @Override
    public Object visitVariable(GrammarParser.VariableContext ctx) {
        String type = ctx.TYPE().getText();
        String variableName = ctx.ID().getText();
        Object value = null;

        // If the variable is initialized
        if (ctx.expression() != null) {
            value = visitExpression(ctx.expression());
        }

        // Storing the variable in the current scope based on its type
        switch (type) {
            case "int":
                if (value == null) {
                    value = 0;
                } else {
                    value = Integer.parseInt(value.toString());
                }
                break;
            case "float":
                if (value == null) {
                    value = 0.0f;
                } else {
                    value = Float.parseFloat(value.toString());
                }
                break;
            case "string":
                if (value == null) {
                    value = "";
                } else {
                    value = value.toString();
                }
                break;
            case "bool":
                if (value == null) {
                    value = false;
                } else {
                    value = Boolean.parseBoolean(value.toString());
                }
                break;
            default:
                throw new RuntimeException("Unknown type: " + type);
        }

        // Adding the variable to the current scope
        if (scopes.peek().containsKey(variableName)) {
            throw new RuntimeException("Variable already declared in the current scope: " + variableName);
        }
        scopes.peek().put(variableName, value);
        System.out.println("Variable declared: " + type + " " + variableName + " = " + value);
        return null;
    }

    @Override
    public Object visitArray(GrammarParser.ArrayContext ctx) {
        String type = ctx.TYPE() != null ? ctx.TYPE().getText() : ctx.ID(0).getText();
        String arrayName = ctx.ID(0).getText();
        java.util.List<Object> array = new java.util.ArrayList<>();

        // If the array is initialized
        if (ctx.arrayInit() != null) {
            for (GrammarParser.ExpressionContext exprCtx : ctx.arrayInit().expression()) {
                array.add(visitExpression(exprCtx));
            }
        }

        // Adding the array to the current scope
        if (scopes.peek().containsKey(arrayName)) {
            throw new RuntimeException("Array already declared in the current scope: " + arrayName);
        }
        scopes.peek().put(arrayName, array);
        System.out.println("Array declared: " + type + "[] " + arrayName + " = " + array);
        return null;
    }

    public Object visitExpression(GrammarParser.ExpressionContext ctx) {
        if (ctx.functionCall() != null) {
            return visitFunctionCall(ctx.functionCall());
        }
        if (ctx.BOOL() != null) {
            return Boolean.parseBoolean(ctx.BOOL().getText());
        } else if (ctx.INT() != null) {
            return Integer.parseInt(ctx.INT().getText());
        } else if (ctx.FLOAT() != null) {
            return Float.parseFloat(ctx.FLOAT().getText());
        } else if (ctx.STRING() != null) {
            return ctx.STRING().getText()
            .replace("\"", "")
            .replace("\\n", "\n")
            .replace("\\t", "\t");
        } else if (ctx.ID() != null) {
            String variableName = ctx.ID().getText();
            for (int i = scopes.size() - 1; i >= 0; i--) {
                if (scopes.get(i).containsKey(variableName)) {
                    return scopes.get(i).get(variableName);
                }
            }
            throw new RuntimeException("Undefined variable: " + variableName);
        } else if (ctx.getChildCount() == 3 && ctx.getChild(0).getText().equals("(") && ctx.getChild(2).getText().equals(")")) {
            return visitExpression(ctx.expression(0));
        } else if (ctx.getChildCount() == 2 && ctx.getChild(0).getText().equals("!")) {
            return !(Boolean) visitExpression(ctx.expression(0));
        } else if (ctx.getChildCount() == 3) {
            Object left = visitExpression(ctx.expression(0));
            Object right = visitExpression(ctx.expression(1));
            String operator = ctx.getChild(1).getText();

            switch (operator) {
                case "%":
                    if (left instanceof Integer && right instanceof Integer) {
                        return (Integer) left % (Integer) right;
                    } else if (left instanceof Float || right instanceof Float) {
                        return Float.parseFloat(left.toString()) % Float.parseFloat(right.toString());
                    }
                    break;
                case "+":
                    if (left instanceof Integer && right instanceof Integer) {
                        return (Integer) left + (Integer) right;
                    } else if (left instanceof Float || right instanceof Float) {
                        return Float.parseFloat(left.toString()) + Float.parseFloat(right.toString());
                    } else if (left instanceof String || right instanceof String) {
                        return left.toString() + right.toString();
                    }
                    break;
                case "-":
                    if (left instanceof Integer && right instanceof Integer) {
                        return (Integer) left - (Integer) right;
                    } else if (left instanceof Float || right instanceof Float) {
                        return Float.parseFloat(left.toString()) - Float.parseFloat(right.toString());
                    }
                    break;
                case "*":
                    if (left instanceof Integer && right instanceof Integer) {
                        return (Integer) left * (Integer) right;
                    } else if (left instanceof Float || right instanceof Float) {
                        return Float.parseFloat(left.toString()) * Float.parseFloat(right.toString());
                    }
                    break;
                case "/":
                    if (left instanceof Integer && right instanceof Integer) {
                        return (Integer) left / (Integer) right;
                    } else if (left instanceof Float || right instanceof Float) {
                        return Float.parseFloat(left.toString()) / Float.parseFloat(right.toString());
                    }
                    break;
                case "&&":
                    return (Boolean) left && (Boolean) right;
                case "||":
                    return (Boolean) left || (Boolean) right;
                case "==":
                    return left.equals(right);
                case "!=" :
                    return !left.equals(right);
                case "<":
                    if (left instanceof Integer && right instanceof Integer) {
                        return (Integer) left < (Integer) right;
                    } else if (left instanceof Float || right instanceof Float) {
                        return Float.parseFloat(left.toString()) < Float.parseFloat(right.toString());
                    }
                    break;
                case ">":
                    if (left instanceof Integer && right instanceof Integer) {
                        return (Integer) left > (Integer) right;
                    } else if (left instanceof Float || right instanceof Float) {
                        return Float.parseFloat(left.toString()) > Float.parseFloat(right.toString());
                    }
                    break;
                case "<=" :
                    if (left instanceof Integer && right instanceof Integer) {
                        return (Integer) left <= (Integer) right;
                    } else if (left instanceof Float || right instanceof Float) {
                        return Float.parseFloat(left.toString()) <= Float.parseFloat(right.toString());
                    }
                    break;
                case ">=" :
                    if (left instanceof Integer && right instanceof Integer) {
                        return (Integer) left >= (Integer) right;
                    } else if (left instanceof Float || right instanceof Float) {
                        return Float.parseFloat(left.toString()) >= Float.parseFloat(right.toString());
                    }
                    break;
                default:
                    throw new RuntimeException("Unsupported operator: " + operator);
            }
        }
        throw new RuntimeException("Invalid expression: " + ctx.getText());
    }

    @Override
    public Object visitFunction(GrammarParser.FunctionContext ctx) {
        String functionName = ctx.ID(0).getText();
        List<String> paramNames = new ArrayList<>();
        if (ctx.TYPE() != null && ctx.ID().size() > 1) {
            for (int i = 1; i < ctx.ID().size(); i++) {
                paramNames.add(ctx.ID(i).getText());
            }
        }
        functions.put(functionName, new FunctionDefinition(ctx, paramNames));
        System.out.println("Function declared: " + functionName);
        return null;
    }

    @Override
    public Object visitFunctionCall(GrammarParser.FunctionCallContext ctx) {
        String functionName = ctx.ID(0).getText();
        FunctionDefinition functionDef = functions.get(functionName);
        if (functionDef == null) {
            throw new RuntimeException("Undefined function: " + functionName);
        }

        // Creating a new scope for function call
        scopes.push(new HashMap<>());

        // Assign parameter values to new scope
        List<GrammarParser.ExpressionContext> args = ctx.expression();
        for (int i = 0; i < args.size(); i++) {
            String paramName = functionDef.paramNames.get(i);
            Object paramValue = visitExpression(args.get(i));
            scopes.peek().put(paramName, paramValue);
        }

        // Execute the function body
        Object returnValue = null;
        for (GrammarParser.StatementContext stmtCtx : functionDef.functionCtx.statement()) {
            returnValue = visit(stmtCtx);
            if (returnValue != null) {
                break;
            }
        }

        // Pop the function scope
        scopes.pop();
        return returnValue;
    }

    @Override
    public Object visitReturnStatement(GrammarParser.ReturnStatementContext ctx) {
        return visitExpression(ctx.expression());
    }

    @Override
    public Object visitShow(GrammarParser.ShowContext ctx) {
        Object value = visitExpression(ctx.expression());
        System.out.println(value);
        return null;
    }

    @Override
    public Object visitLoop(GrammarParser.LoopContext ctx) {
        String loopVariable = ctx.ID().getText();
        Object start = visitExpression(ctx.expression(0));
        Object end = visitExpression(ctx.expression(1));

        if (!(start instanceof Integer) || !(end instanceof Integer)) {
            throw new RuntimeException("The 'for' loop must have integer start and end values: " + ctx.getText());
        }

        int startValue = (Integer) start;
        int endValue = (Integer) end;

        if (startValue <= endValue) {
            for (int i = startValue; i <= endValue; i++) {
                scopes.peek().put(loopVariable, i);

                for (GrammarParser.StatementContext stmtCtx : ctx.statement()) {
                    visit(stmtCtx);
                }
            }
        } else {
            for (int i = startValue; i >= endValue; i--) {
                scopes.peek().put(loopVariable, i);

                for (GrammarParser.StatementContext stmtCtx : ctx.statement()) {
                    visit(stmtCtx);
                }
            }
        }

        return null;
    }

    @Override
    public Object visitIfStatement(GrammarParser.IfStatementContext ctx) {
        for (int i = 0; i < ctx.expression().size(); i++) {
            Object condition = visitExpression(ctx.expression(i));
            if (!(condition instanceof Boolean)) {
                throw new RuntimeException("The condition must be a boolean expression: " + ctx.expression(i).getText());
            }

            if ((Boolean) condition) {
                visit(ctx.statement(i));
                return null; // Exit after the first true condition block
            }
        }

        // If none of the conditions are true and there's an 'else' block
        if (ctx.statement().size() > ctx.expression().size()) {
            visit(ctx.statement(ctx.expression().size()));
        }

        return null;
    }

    private static class FunctionDefinition {
        GrammarParser.FunctionContext functionCtx;
        List<String> paramNames;

        FunctionDefinition(GrammarParser.FunctionContext functionCtx, List<String> paramNames) {
            this.functionCtx = functionCtx;
            this.paramNames = paramNames;
        }
    }
}

这是我的 Grammar.g4(感谢 Bart Kiers):

grammar Grammar;

start : statement* EOF;

statement
    : variable ';'
    | objectInstance ';'
    | array ';'
    | ifStatement
    | function
    | loop
    | functionCall ';'
    | show ';'
    | arrayAccess ';'
    | class
    | returnStatement ';'
    ;

function
    : ACCESS TYPE ID '(' (TYPE ID (',' TYPE ID)*)? ')' '{' statement* '}'
    | ACCESS 'void' ID '(' (TYPE ID (',' TYPE ID)*)? ')' '{' statement* '}'
    ;

variable : TYPE ID ('=' expression)?;
objectInstance : objectType=ID instanceName=ID ('=' expression)?;
array          : (TYPE | ID) ID '[' ']' ('=' arrayInit)?;
ifStatement    : 'if' '(' expression ')' '{' statement* '}' ('else if' '(' expression ')' '{' statement* '}')* ('else' '{' statement* '}')?;
loop           : 'for' ID 'in' expression 'to' expression '{' statement* '}';
functionCall   : (ID '.')? ID '(' (expression (',' expression)*?)? ')';
show           : 'show' '(' expression ')';
class          : 'class' ID '{' classEntries '}';
classEntries   : ((variable ';') | (array ';') | function)*;
arrayInit      : '[' expression (',' expression)* ']';
returnStatement: 'return' expression;
arrayAccess    : ID '[' expression ']' ;

expression
    : '(' expression ')'
    | '!' expression
    | expression ('&&' | '||') expression
    | expression ('/' | '*' | '%') expression
    | expression ('+' | '-') expression
    | expression COMPARISON expression
    | 'new' ID '(' ((expression | arrayInit) (',' (expression | arrayInit))*)? ')' 
    | functionCall
    | STRING
    | ID
    | INT
    | BOOL
    | FLOAT
    | ID
    ;

ACCESS     : 'private' | 'public';
COMPARISON : '>' | '<' | '>=' | '<=' | '==';
TYPE       : 'int' | 'float' | 'string' | 'bool';
BOOL       : 'true' | 'false' ;
ID         : [a-zA-Z_][a-zA-Z0-9_]* ;
STRING     : '"' (~[\\"] | '\\' .)* '"';
INT        : [0-9]+;
FLOAT      : [0-9]+ '.' [0-9]+;
WS         : [ \t\r\n]+ -> channel(HIDDEN);
LINE_COMMENT : '//' ~[\r\n]* -> skip;
BLOCK_COMMENT : '/*' .*? '*/' -> skip;

非常感谢您帮助了解问题所在。

java antlr antlr4
1个回答
0
投票

您还应该重写访问者中的

Object visitStatement(GrammarParser.StatementContext ctx)
方法。如果不这样做,ANTLR 的基本访问者将处理它,并且始终返回 null。一般来说,您几乎应该总是重写所有解析器方法。

尝试添加这个:

@Override
public Object visitStatement(GrammarParser.StatementContext ctx) {
    if (ctx.variable() != null) return this.visitVariable(ctx.variable());
    if (ctx.objectInstance() != null) return this.visitObjectInstance(ctx.objectInstance());
    if (ctx.array() != null) return this.visitArray(ctx.array());
    if (ctx.ifStatement() != null) return this.visitIfStatement(ctx.ifStatement());
    if (ctx.function() != null) return this.visitFunction(ctx.function());
    if (ctx.loop() != null) return this.visitLoop(ctx.loop());
    if (ctx.functionCall() != null) return this.visitFunction(ctx.function());
    if (ctx.show() != null) return this.visitShow(ctx.show());
    if (ctx.arrayAccess() != null) return this.visitArrayAccess(ctx.arrayAccess());
    if (ctx.class_() != null) return this.visitClass(ctx.class_());
    if (ctx.returnStatement() != null) return this.visitReturnStatement(ctx.returnStatement());

    throw new RuntimeException("Unexpected statement: " + ctx.getText());
}
© www.soinside.com 2019 - 2024. All rights reserved.