返回
用 Antlr 重构脚本解释器(二)
后端
2023-11-11 10:07:21
在 上次 利用 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 来生成解析器和词法分析器,我们可以快速地构建一个脚本解释器,并且可以通过修改语法规则来支持不同的脚本语言。