feat: 支持 new 表达式并优化成员访问

- 新增对 new 表达式(如 new Array())的支持,实现数组/对象的动态创建
- 优化成员访问表达式处理,支持字段回退机制
- 重构标识符解析逻辑,提高变量查找效率
-改进函数调用表达式处理,支持更多调用场景
This commit is contained in:
Luke 2025-08-29 17:30:39 +08:00
parent 82578880ae
commit 5b259a01d8

View File

@ -43,12 +43,7 @@ public record ExpressionBuilder(IRContext ctx) {
// 布尔字面量例如 true / false
case BoolLiteralNode b -> buildBoolLiteral(b.getValue());
// 标识符变量名 ab
case IdentifierNode id -> {
// 查找当前作用域中的变量寄存器
IRVirtualRegister reg = ctx.getScope().lookup(id.name());
if (reg == null) throw new IllegalStateException("未定义标识符: " + id.name());
yield reg;
}
case IdentifierNode id -> buildIdentifier(id);
// 模块常量 / 全局变量 ModuleA.a
case MemberExpressionNode mem -> buildMember(mem);
// 二元表达式 a+b, x==y
@ -59,6 +54,7 @@ public record ExpressionBuilder(IRContext ctx) {
case UnaryExpressionNode un -> buildUnary(un);
case IndexExpressionNode idx -> buildIndex(idx);
case ArrayLiteralNode arr -> buildArrayLiteral(arr);
case NewExpressionNode n -> buildNew(n);
// 默认分支遇到未知表达式类型则直接抛异常
default -> throw new IllegalStateException(
"不支持的表达式类型: " + expr.getClass().getSimpleName());
@ -66,76 +62,172 @@ public record ExpressionBuilder(IRContext ctx) {
}
/**
* 成员访问表达式构建
* 构造标识符节点对应的 IR 虚拟寄存器
* <p>
* 支持普通变量查找以及结构体方法/构造器中的字段回退机制即自动将裸标识符回退为 this.<id>
*
* @param mem 成员表达式节点
* @return 存储结果的虚拟寄存器
* @param id 标识符节点
* @return 查找到的 IR 虚拟寄存器
* @throws IllegalStateException 若标识符未定义且无法回退为字段
*/
private IRVirtualRegister buildMember(MemberExpressionNode mem) {
if (!(mem.object() instanceof IdentifierNode id)) {
throw new IllegalStateException("不支持的成员访问对象类型: "
+ mem.object().getClass().getSimpleName());
}
String qualified = id.name() + "." + mem.member();
private IRVirtualRegister buildIdentifier(IdentifierNode id) {
// ====================== 普通变量查找 ======================
// 1. 在当前作用域查找变量可能是局部变量形参临时变量等
IRVirtualRegister reg = ctx.getScope().lookup(id.name());
if (reg != null) return reg;
/* 1) 尝试直接获取已有寄存器绑定 */
IRVirtualRegister reg = ctx.getScope().lookup(qualified);
if (reg != null) {
return reg;
// ====================== 字段回退机制 ======================
// 2. 若未找到则判断是否处于结构体方法或构造器中
// 尝试将裸标识符自动视为 this.<id>即访问当前结构体实例的成员字段
IRVirtualRegister thisReg = ctx.getScope().lookup("this");
String thisType = ctx.getScope().lookupType("this");
if (thisReg != null && thisType != null) {
// 生成成员表达式节点 this.<id>
MemberExpressionNode asField = new MemberExpressionNode(
new IdentifierNode("this", id.context()), // 构造 this 节点
id.name(), // 字段名
id.context()
);
// 递归构造成员访问相当于构造 this.<id> IR
return buildMember(asField);
}
/* 2) 折叠为编译期常量:先查作用域,再查全局常量表 */
Object v = ctx.getScope().getConstValue(qualified);
if (v == null) {
v = GlobalConstTable.get(qualified);
}
if (v != null) {
IRVirtualRegister r = ctx.newRegister();
ctx.addInstruction(new LoadConstInstruction(r, new IRConstant(v)));
return r;
// ====================== 标识符未定义异常 ======================
// 3. 无法查找到变量且不能字段回退抛出异常
throw new IllegalStateException("未定义标识符: " + id.name());
}
throw new IllegalStateException("未定义的常量: " + qualified);
}
/* ───────────────── 写入指定寄存器 ───────────────── */
/**
* 将表达式节点 {@link ExpressionNode} 的结果写入指定的虚拟寄存器 {@code dest}
* 构建成员访问表达式的 IR 虚拟寄存器
* <p>
* 按表达式类型分派处理包括
* 支持两类成员访问
* <ul>
* <li>字面量数字字符串布尔数组生成 loadConst 指令直接写入目标寄存器</li>
* <li>变量标识符查表获取源寄存器 move 到目标寄存器</li>
* <li>二元表达式下标调用表达式递归生成子表达式结果并写入目标寄存器</li>
* <li>其它类型统一先 build 到临时寄存器 move 到目标寄存器</li>
* <li>模块常量访问 ModuleName.CONST</li>
* <li>结构体/对象字段访问 obj.field this.field</li>
* </ul>
*
* @param mem 成员访问表达式节点 a.b ModuleName.CONST
* @return 存储成员值的 IR 虚拟寄存器
* @throws IllegalStateException 若找不到成员或无法解析类型
*/
private IRVirtualRegister buildMember(MemberExpressionNode mem) {
// ===== 1. 处理模块常量 (ModuleName.member) =====
// 检查成员访问的对象是否为一个标识符即模块名
if (mem.object() instanceof IdentifierNode oid) {
String mod = oid.name();
// 查找是否存在该模块下的全局常量定义
Object c = GlobalConstTable.get(mod + "." + mem.member());
if (c != null) {
// 若找到常量分配新寄存器并生成 LoadConst 指令
IRVirtualRegister r = ctx.newRegister();
ctx.addInstruction(new LoadConstInstruction(r, new IRConstant(c)));
return r;
}
}
// ===== 2. 结构体/对象字段访问 =====
// (1) 递归构建成员对象 a.b先获得 a 的寄存器
IRVirtualRegister objReg = build(mem.object());
// (2) 尝试解析成员访问接收者object的类型
String ownerType = null;
if (mem.object() instanceof IdentifierNode oid) {
// 如果对象是标识符直接查询其类型例如 a.xa 的类型
ownerType = ctx.getScope().lookupType(oid.name());
}
if (ownerType == null || ownerType.isEmpty()) {
// 兜底如果访问 this.xxx并且 this 有类型则使用 this 的类型
String thisType = ctx.getScope().lookupType("this");
if (thisType != null) ownerType = thisType;
}
if (ownerType == null || ownerType.isEmpty()) {
// 类型无法解析抛出异常
throw new IllegalStateException("无法解析成员访问接收者的类型");
}
// (3) 查找字段槽位下标ownerType mem.member() 字段的序号
Integer fieldIndex = ctx.getScope().lookupFieldIndex(ownerType, mem.member());
if (fieldIndex == null) {
// 字段不存在抛出异常
throw new IllegalStateException("类型 " + ownerType + " 不存在字段: " + mem.member());
}
// (4) 生成读取字段的 IR 指令CALL __index_r, objReg, const(fieldIndex)
// 4.1 先将字段下标加载到寄存器
IRVirtualRegister idxReg = ctx.newRegister();
ctx.addInstruction(new LoadConstInstruction(
idxReg,
IRConstant.fromNumber(Integer.toString(fieldIndex))
));
// 4.2 生成成员读取调用指令
IRVirtualRegister out = ctx.newRegister();
List<IRValue> args = new ArrayList<>();
args.add(objReg); // 对象寄存器
args.add(idxReg); // 字段下标寄存器
ctx.addInstruction(new CallInstruction(out, "__index_r", args));
return out;
}
/**
* 将表达式节点 {@link ExpressionNode} 的求值结果写入指定的目标虚拟寄存器 {@code dest}
* <p>
* 根据不同表达式类型采取高效或递归方式生成中间代码涵盖常量变量运算数组调用等
* </p>
*
* @param node 要求值的表达式节点
* @param dest 结果目标虚拟寄存器
* @throws IllegalStateException 若标识符未定义如变量未声明时引用
* @param node 表达式节点可为字面量变量数组运算等
* @param dest 目标虚拟寄存器写入结果
* @throws IllegalStateException 变量标识符未定义
*/
public void buildInto(ExpressionNode node, IRVirtualRegister dest) {
switch (node) {
// 数字字面量生成 loadConst 指令将数值常量写入目标寄存器
// ===================== 数字字面量 =====================
// 423.14 直接生成 loadConst 指令常量写入目标寄存器
case NumberLiteralNode n -> InstructionFactory.loadConstInto(
ctx, dest, ExpressionUtils.buildNumberConstant(ctx, n.value()));
// 字符串字面量生成 loadConst 指令将字符串常量写入目标寄存器
// ===================== 字符串字面量 =====================
// "hello"直接生成 loadConst 指令
case StringLiteralNode s -> InstructionFactory.loadConstInto(
ctx, dest, new IRConstant(s.value()));
// 布尔字面量转换为 int 1/0生成 loadConst 指令写入目标寄存器
// ===================== 布尔字面量 =====================
// true/false转换为 int 常量 1/0生成 loadConst
case BoolLiteralNode b -> InstructionFactory.loadConstInto(
ctx, dest, new IRConstant(b.getValue() ? 1 : 0));
// 数组字面量生成数组常量并写入目标寄存器
// ===================== 数组字面量 =====================
// 直接构造数组常量只支持静态初始化生成 loadConst
case ArrayLiteralNode arr -> InstructionFactory.loadConstInto(
ctx, dest, buildArrayConstant(arr));
// 变量标识符查表获得源寄存器move 到目标寄存器
// ===================== new 表达式构造数组/对象 =====================
// 生成空数组然后按参数依次初始化每一项使用 __setindex_r 填充目标寄存器
case NewExpressionNode newExpr -> {
// 步骤1先写入空 List目标寄存器 dest
InstructionFactory.loadConstInto(ctx, dest, new IRConstant(java.util.List.of()));
// 步骤2依次写入构造参数填充各下标项
for (int i = 0; i < newExpr.arguments().size(); i++) {
IRVirtualRegister argReg = build(newExpr.arguments().get(i)); // 参数值寄存器
IRVirtualRegister idxReg = ctx.newTempRegister(); // 下标寄存器
InstructionFactory.loadConstInto(ctx, idxReg, new IRConstant(i));
// 组装参数arridxvalue调用 runtime __setindex_r 填充元素
List<IRValue> args = new ArrayList<>();
args.add(dest); // 数组本身
args.add(idxReg); // 下标
args.add(argReg); // 元素值
ctx.addInstruction(new CallInstruction(null, "__setindex_r", args));
}
}
// ===================== 变量标识符 =====================
// x查找符号表move 到目标寄存器未定义时报错
case IdentifierNode id -> {
IRVirtualRegister src = ctx.getScope().lookup(id.name());
if (src == null)
@ -143,22 +235,26 @@ public record ExpressionBuilder(IRContext ctx) {
InstructionFactory.move(ctx, src, dest);
}
// 二元表达式递归生成左右子表达式并将结果写入目标寄存器
// ===================== 二元表达式 a + b =====================
// 递归生成左右操作数并将运算结果写入目标寄存器
case BinaryExpressionNode bin -> buildBinaryInto(bin, dest);
// 下标表达式递归生成索引结果move 到目标寄存器
// ===================== 下标访问 arr[1] =====================
// 先递归构造表达式将索引结果 move 到目标寄存器
case IndexExpressionNode idx -> {
IRVirtualRegister tmp = buildIndex(idx);
InstructionFactory.move(ctx, tmp, dest);
}
// 调用表达式递归生成调用结果move 到目标寄存器
// ===================== 函数/方法调用 foo(a, b) =====================
// 递归生成调用结果 move 到目标寄存器
case CallExpressionNode call -> {
IRVirtualRegister tmp = buildCall(call);
InstructionFactory.move(ctx, tmp, dest);
}
// 其它类型统一先 build 到临时寄存器 move 到目标寄存器
// ===================== 其它所有情况兜底处理 =====================
// 通用流程先生成结果到临时寄存器 move 到目标寄存器
default -> {
IRVirtualRegister tmp = build(node);
InstructionFactory.move(ctx, tmp, dest);
@ -397,43 +493,119 @@ public record ExpressionBuilder(IRContext ctx) {
/**
* 建函数或方法调用表达式
* new 对象/数组创建表达式的 IR 虚拟寄存器支持列表型构造
* <p>
* 支持普通函数调用foo(a, b)与成员方法调用obj.method(a, b)
* 语义说明本方法用于生成new 表达式的中间代码流程具体包括
* <ul>
* <li>首先递归生成所有参数的虚拟寄存器列表</li>
* <li>根据 callee 类型区分成员访问或直接标识符调用并规范化方法名如加前缀</li>
* <li>为返回值分配新寄存器生成 Call 指令</li>
* <li>分配一个新的寄存器用于保存新对象/数组引用</li>
* <li>将一个空列表常量写入该寄存器后端 runtime 识别为可变列表/对象</li>
* <li>遍历所有构造参数依次写入目标列表的 [0..n-1] 位置</li>
* <li>最终返回该寄存器</li>
* </ul>
* </p>
* @param node new 表达式节点包含所有构造参数
* @return 保存新创建对象/数组引用的目标寄存器
*/
private IRVirtualRegister buildNew(NewExpressionNode node) {
// 1. 分配新的寄存器作为 new 表达式的结果
IRVirtualRegister dest = ctx.newRegister();
// 2. 先写入一个空列表常量由后端 R_PUSH runtime 扩展为动态对象/列表
InstructionFactory.loadConstInto(ctx, dest, new IRConstant(java.util.List.of()));
// 3. 遍历所有构造参数依次写入目标列表的 [0..n-1] 下标位置
for (int i = 0; i < node.arguments().size(); i++) {
// 3.1 构造第 i 个参数的值存入 argReg
IRVirtualRegister argReg = build(node.arguments().get(i));
// 3.2 临时分配一个寄存器 idxReg 存储下标 i
IRVirtualRegister idxReg = ctx.newTempRegister();
InstructionFactory.loadConstInto(ctx, idxReg, new IRConstant(i));
// 3.3 调用 runtime __setindex_r 写入元素
List<IRValue> args = new ArrayList<>();
args.add(dest); // 目标列表对象
args.add(idxReg); // 当前下标
args.add(argReg); // 参数值
ctx.addInstruction(new CallInstruction(null, "__setindex_r", args));
}
// 4. 返回最终保存新建对象/数组的寄存器
return dest;
}
/**
* 构建函数或方法调用表达式的 IR 指令并返回存放调用结果的寄存器
* <p>
* 支持以下两大类调用
* <ul>
* <li>普通函数调用 foo(a, b)</li>
* <li>成员方法调用 obj.method(a, b) ModuleName.func(a, b)</li>
* </ul>
* 核心流程
* <ol>
* <li>递归生成所有参数的虚拟寄存器列表</li>
* <li>根据 callee 类型区分结构体方法/模块静态函数/普通函数</li>
* <li>规范化被调用方法名并整理最终参数表</li>
* <li>分配用于结果的新寄存器生成 Call 指令</li>
* </ol>
*
* @param call 函数/方法调用表达式节点
* @return 存放调用结果的虚拟寄存器
* @param call 函数或方法调用表达式节点
* @return 存放调用结果的目标虚拟寄存器
* @throws IllegalStateException 被调用表达式类型不支持或参数异常
*/
private IRVirtualRegister buildCall(CallExpressionNode call) {
// 1. 递归生成所有参数的寄存器
List<IRVirtualRegister> argv = call.arguments().stream().map(this::build).toList();
// 1. 先递归生成所有参数表达式对应的虚拟寄存器
List<IRVirtualRegister> explicitRegs = new ArrayList<>();
for (ExpressionNode a : call.arguments()) explicitRegs.add(build(a));
// 2. 规范化被调用方法名区分成员方法与普通函数
String callee = switch (call.callee()) {
// 成员方法调用 obj.method()
case MemberExpressionNode m when m.object() instanceof IdentifierNode id -> id.name() + "." + m.member();
// 普通函数调用或处理命名空间前缀如当前方法名为 namespace.func
case IdentifierNode id -> {
String current = ctx.getFunction().name();
int dot = current.lastIndexOf('.');
if (dot > 0)
yield current.substring(0, dot) + "." + id.name(); // 同命名空间内调用
yield id.name(); // 全局函数调用
String callee; // 被调用方法/函数规范化后的名称
List<IRValue> finalArgs = new ArrayList<>(); // 最终传递给 Call 指令的参数表
// 2. 判断被调用对象callee类型
if (call.callee() instanceof MemberExpressionNode m && m.object() instanceof IdentifierNode idObj) {
// ==== 成员方法/模块函数调用 ====
// 分为两种情况
// a) 结构体实例方法 a.method(...)
// b) 模块静态函数 ModuleName.func(...)
String recvName = idObj.name(); // 接收者名字
String recvType = ctx.getScope().lookupType(recvName); // 查找接收者类型
if (recvType == null || recvType.isEmpty()) {
// 情况 b接收者不是变量而是模块名ModuleName.func
// 规范化函数名为 "模块名.成员名"参数直接用 explicitRegs
callee = recvName + "." + m.member();
finalArgs.addAll(explicitRegs);
} else {
// 情况 a结构体实例方法obj.method(...)
// 方法名规范化为 "类型名.成员名"并将实例自身作为第一个参数
callee = recvType + "." + m.member();
IRVirtualRegister thisReg = ctx.getScope().lookup(recvName);
if (thisReg == null)
throw new IllegalStateException("未定义标识符: " + recvName);
finalArgs.add(thisReg); // 隐式 this 作为第一个参数
finalArgs.addAll(explicitRegs); // 其余参数
}
} else if (call.callee() instanceof IdentifierNode id) {
// ==== 普通函数调用 ====
// 继承当前命名空间前缀若当前函数为 ModuleName.xxx foo() 自动补前缀为 ModuleName.foo
String current = ctx.getFunction().name(); // 当前函数全名
int dot = current.lastIndexOf('.');
if (dot >= 0 && !id.name().contains(".")) {
callee = current.substring(0, dot) + "." + id.name();
} else {
callee = id.name();
}
finalArgs.addAll(explicitRegs);
} else {
// ==== 其它类型不支持 ====
throw new IllegalStateException("不支持的被调用表达式: " + call.callee().getClass().getSimpleName());
}
// 其它类型不支持
default -> throw new IllegalStateException(
"不支持的调用目标: " + call.callee().getClass().getSimpleName());
};
// 3. 分配用于存放返回值的新寄存器并生成 Call 指令
// 3. 分配目标寄存器生成函数/方法调用指令
IRVirtualRegister dest = ctx.newRegister();
ctx.addInstruction(new CallInstruction(dest, callee, new ArrayList<>(argv)));
ctx.addInstruction(new CallInstruction(dest, callee, finalArgs));
return dest;
}