如何在Python版ANTLR4中检查语句内操作运算的返回值类型
Python版ANTLR4方法返回值类型校验实现方案
1 替代ParseTreeProperty的存储方案
Java版ANTLR的ParseTreeProperty本质就是一个以语法节点为key、存储附加属性的映射结构,Python环境下你直接在自定义Listener类中声明一个字典属性即可实现同等能力:
- 字典key为每个
ParseTree节点对象 - 字典value为对应节点的推导类型值
2 表达式类型推导逻辑
采用Listener的exit回调(而非enter回调)处理类型计算,深度优先遍历的特性会保证子节点处理完成后才触发父节点的退出回调,刚好满足表达式类型从底向上推导的要求:
- 退出基础字面量节点(int、string、bool字面量)时,直接将对应基础类型存入字典的当前节点条目
- 退出变量引用节点时,查询符号表获取变量类型,存入当前节点条目
- 退出运算表达式节点时,先从字典中获取左右子表达式的类型,再根据运算符规则推导当前表达式的最终类型:
- 算术运算符(+、-、*、/):左右子节点均为int时,返回int类型,否则标记类型错误
- 比较运算符:左右子节点类型匹配时,返回bool类型,否则标记类型错误
- 括号表达式:直接继承内部子表达式的类型即可
- 退出方法调用表达式节点时,查询符号表获取目标方法的返回类型,存入当前节点条目
3 返回值类型校验逻辑
- 进入方法定义节点时,将方法声明的返回类型存入Listener的临时属性
current_method_return_type - 退出return语句节点时,从字典中获取return后关联的表达式节点的类型,和
current_method_return_type对比,不一致则触发类型错误提示
代码示例
from DecafListener import DecafListener from DecafParser import DecafParser class TypeCheckListener(DecafListener): def __init__(self, symbol_table): self.symbol_table = symbol_table self.node_type_map = {} # 替代ParseTreeProperty存储节点类型 self.current_method_return_type = None # 处理int字面量 def exitIntLiteral(self, ctx: DecafParser.IntLiteralContext): self.node_type_map[ctx] = "int" # 处理字符串字面量 def exitStringLiteral(self, ctx: DecafParser.StringLiteralContext): self.node_type_map[ctx] = "string" # 处理变量引用 def exitVarReference(self, ctx: DecafParser.VarReferenceContext): var_name = ctx.IDENTIFIER().getText() self.node_type_map[ctx] = self.symbol_table.query(var_name)["type"] # 处理二元运算表达式 def exitBinaryExpr(self, ctx: DecafParser.BinaryExprContext): op = ctx.op.text left_type = self.node_type_map[ctx.expr(0)] right_type = self.node_type_map[ctx.expr(1)] if op in ("+", "-", "*", "/", "%"): if left_type == right_type == "int": self.node_type_map[ctx] = "int" else: self.node_type_map[ctx] = "error" print(f"[行号{ctx.start.line}] 算术运算不支持{left_type}与{right_type}类型操作") # 处理方法定义 def enterMethodDef(self, ctx: DecafParser.MethodDefContext): self.current_method_return_type = ctx.typeType().getText() # 处理return语句 def exitReturnStmt(self, ctx: DecafParser.ReturnStmtContext): actual_type = self.node_type_map[ctx.expr()] if actual_type != self.current_method_return_type: print(f"[行号{ctx.start.line}] 返回值类型不匹配:要求{self.current_method_return_type},实际{actual_type}")
注意事项
- 需覆盖所有表达式规则的exit回调,包括一元运算、括号表达式、数组访问等场景,避免出现节点类型缺失的情况
- 类型错误标记后需要在后续校验中跳过错误节点的关联校验,避免引发连锁报错
内容的提问来源于stack exchange,提问作者Rodrigo Zea
相关产品推荐
相关产品推荐

