返回

用 Antlr 重构脚本解释器(二)

后端

在 上次 利用 Antlr 重构一版 用 Antlr 重构脚本解释器 之后便着手新增其他功能,也就是现在看到的支持了作用域以及函数调用。整个语法规则大部分参考了 Java,现阶段支持了:

  • 函数声明与调用
  • 作用域(通过块和嵌套函数实现)
  • 类型(int 和 bool)
  • if 语句
  • while 语句
  • return 语句
  • 变量声明和赋值

语法规则

program: statement* EOF ;

statement:
    decl_statement
  | expr_statement
  | if_statement
  | while_statement
  | return_statement
  ;

decl_statement: type IDENTIFIER ASSIGN expr ;

expr_statement: expr ;

if_statement: IF expr THEN block ELSE block ;

while_statement: WHILE expr DO block ;

return_statement: RETURN expr ;

block: '{' statement* '}' ;

type: INT | BOOL ;

expr:
    IDENTIFIER
  | NUMBER
  | TRUE
  | FALSE
  | expr '+' expr
  | expr '-' expr
  | expr '*' expr
  | expr '/' expr
  | expr AND expr
  | expr OR expr
  | NOT expr
  | '(' expr ')'
  | '(' type ')' IDENTIFIER ASSIGN expr
  | IDENTIFIER '(' ')'
  | IDENTIFIER '(' expr (',' expr)* ')'
  ;

数据结构的设计

public class FunctionCallExpr extends Expr {
    private String identifier;
    private List<Expr> args;

    public FunctionCallExpr(String identifier, List<Expr> args) {
        this.identifier = identifier;
        this.args = args;
    }

    public String getIdentifier() {
        return identifier;
    }

    public List<Expr> getArgs() {
        return args;
    }
}
public class FunctionDefStmt extends Stmt {
    private Type type;
    private String identifier;
    private List<Type> argTypes;
    private Stmt block;

    public FunctionDefStmt(Type type, String identifier, List<Type> argTypes, Stmt block) {
        this.type = type;
        this.identifier = identifier;
        this.argTypes = argTypes;
        this.block = block;
    }

    public Type getType() {
        return type;
    }

    public String getIdentifier() {
        return identifier;
    }

    public List<Type> getArgTypes() {
        return argTypes;
    }

    public Stmt getBlock() {
        return block;
    }
}
public class VarDeclStmt extends Stmt {
    private Type type;
    private String identifier;
    private Expr initExpr;

    public VarDeclStmt(Type type, String identifier, Expr initExpr) {
        this.type = type;
        this.identifier = identifier;
        this.initExpr = initExpr;
    }

    public Type getType() {
        return type;
    }

    public String getIdentifier() {
        return identifier;
    }

    public Expr getInitExpr() {
        return initExpr;
    }
}

语义分析的实现

public void visitFunctionDefStmt(FunctionDefStmt ctx) {
    SymbolTable.enterScope();
    for (int i = 0; i < ctx.getArgTypes().size(); i++) {
        SymbolTable.add(ctx.getIdentifier(), ctx.getArgTypes().get(i));
    }
    visit(ctx.getBlock());
    SymbolTable.exitScope();
}

public void visitVarDeclStmt(VarDeclStmt ctx) {
    SymbolTable.add(ctx.getIdentifier(), ctx.getType());
    visit(ctx.getInitExpr());
}

public void visitFunctionCallExpr(FunctionCallExpr ctx) {
    Symbol symbol = SymbolTable.get(ctx.getIdentifier());
    if (symbol == null) {
        error("Function not defined: " + ctx.getIdentifier());
    } else if (symbol.getType() != FUNCTION) {
        error("Symbol is not a function: " + ctx.getIdentifier());
    } else {
        FunctionSymbol functionSymbol = (FunctionSymbol) symbol;
        if (functionSymbol.getArgTypes().size() != ctx.getArgs().size()) {
            error("Function call has incorrect number of arguments: " + ctx.getIdentifier());
        } else {
            for (int i = 0; i < ctx.getArgs().size(); i++) {
                visit(ctx.getArgs().get(i));
            }
        }
    }
}

总结

通过在 Antlr 中重构脚本解释器,我们能够支持作用域和函数调用,这使得脚本语言更加强大和灵活。通过使用 Antlr 来生成解析器和词法分析器,我们可以快速地构建一个脚本解释器,并且可以通过修改语法规则来支持不同的脚本语言。