!11 release: 合并 v0.3.0 版本至 main 分支
Merge pull request !11 from Luke/release/v0.3.0
This commit is contained in:
commit
98fbca98db
17
.run/Demo6.run.xml
Normal file
17
.run/Demo6.run.xml
Normal file
@ -0,0 +1,17 @@
|
||||
<component name="ProjectRunConfigurationManager">
|
||||
<configuration default="false" name="Demo6" type="Application" factoryName="Application" folderName="Demo" activateToolWindowBeforeRun="false">
|
||||
<option name="ALTERNATIVE_JRE_PATH" value="graalvm-ce-23" />
|
||||
<option name="MAIN_CLASS_NAME" value="org.jcnc.snow.compiler.cli.SnowCompiler" />
|
||||
<module name="Snow" />
|
||||
<option name="PROGRAM_PARAMETERS" value="-d playground/Demo6" />
|
||||
<extension name="coverage">
|
||||
<pattern>
|
||||
<option name="PATTERN" value="org.jcnc.snow.compiler.parser.preprocessor.lexer.impl.api.*" />
|
||||
<option name="ENABLED" value="true" />
|
||||
</pattern>
|
||||
</extension>
|
||||
<method v="2">
|
||||
<option name="Make" enabled="true" />
|
||||
</method>
|
||||
</configuration>
|
||||
</component>
|
||||
17
.run/Demo7.run.xml
Normal file
17
.run/Demo7.run.xml
Normal file
@ -0,0 +1,17 @@
|
||||
<component name="ProjectRunConfigurationManager">
|
||||
<configuration default="false" name="Demo7" type="Application" factoryName="Application" folderName="Demo" activateToolWindowBeforeRun="false">
|
||||
<option name="ALTERNATIVE_JRE_PATH" value="graalvm-ce-23" />
|
||||
<option name="MAIN_CLASS_NAME" value="org.jcnc.snow.compiler.cli.SnowCompiler" />
|
||||
<module name="Snow" />
|
||||
<option name="PROGRAM_PARAMETERS" value="-d playground/Demo7" />
|
||||
<extension name="coverage">
|
||||
<pattern>
|
||||
<option name="PATTERN" value="org.jcnc.snow.compiler.parser.preprocessor.lexer.impl.api.*" />
|
||||
<option name="ENABLED" value="true" />
|
||||
</pattern>
|
||||
</extension>
|
||||
<method v="2">
|
||||
<option name="Make" enabled="true" />
|
||||
</method>
|
||||
</configuration>
|
||||
</component>
|
||||
@ -11,8 +11,8 @@
|
||||
<a href="https://gitee.com/jcnc-org/snow/blob/main/LICENSE">
|
||||
<img src="https://img.shields.io/badge/%20license-Apache--2.0%20-blue" alt="">
|
||||
</a>
|
||||
<a href="https://gitee.com/jcnc-org/snow/tree/v0.2.0/">
|
||||
<img src="https://img.shields.io/badge/version-v0.2.0-blue" alt="">
|
||||
<a href="https://gitee.com/jcnc-org/snow/tree/v0.3.0/">
|
||||
<img src="https://img.shields.io/badge/version-v0.3.0-blue" alt="">
|
||||
</a>
|
||||
</p>
|
||||
|
||||
|
||||
10
playground/Demo6/Main.snow
Normal file
10
playground/Demo6/Main.snow
Normal file
@ -0,0 +1,10 @@
|
||||
module: Main
|
||||
function: main
|
||||
parameter:
|
||||
return_type: int
|
||||
body:
|
||||
declare b1 :int = -1
|
||||
return b1
|
||||
end body
|
||||
end function
|
||||
end module
|
||||
10
playground/Demo7/Main.snow
Normal file
10
playground/Demo7/Main.snow
Normal file
@ -0,0 +1,10 @@
|
||||
module: Main
|
||||
function: main
|
||||
parameter:
|
||||
return_type: boolean
|
||||
body:
|
||||
declare b1 :boolean = true
|
||||
return !b1
|
||||
end body
|
||||
end function
|
||||
end module
|
||||
@ -2,24 +2,47 @@ package org.jcnc.snow.compiler.backend.generator;
|
||||
|
||||
import org.jcnc.snow.compiler.backend.builder.VMProgramBuilder;
|
||||
import org.jcnc.snow.compiler.backend.core.InstructionGenerator;
|
||||
import org.jcnc.snow.compiler.backend.util.IROpCodeMapper;
|
||||
import org.jcnc.snow.compiler.backend.util.OpHelper;
|
||||
import org.jcnc.snow.compiler.ir.core.IRValue;
|
||||
import org.jcnc.snow.compiler.ir.instruction.BinaryOperationInstruction;
|
||||
import org.jcnc.snow.compiler.ir.value.IRConstant;
|
||||
import org.jcnc.snow.compiler.ir.value.IRVirtualRegister;
|
||||
|
||||
import java.util.Map;
|
||||
import java.util.concurrent.atomic.AtomicInteger;
|
||||
|
||||
/**
|
||||
* 二元运算指令生成器
|
||||
* 支持二元运算指令的自动类型提升。
|
||||
* <p>类型提升优先级为:D > F > L > I > S > B</p>
|
||||
* 二元运算指令生成器。
|
||||
* <p>
|
||||
* 负责将中间表示的二元运算指令(算术、位运算及比较运算)生成对应的虚拟机指令序列,
|
||||
* 并自动进行类型提升。
|
||||
* 同时实现 "+0 → MOV" 的 Peephole 优化,避免多余的 PUSH/ADD 序列。
|
||||
* </p>
|
||||
* <p>类型提升优先级:D > F > L > I > S > B</p>
|
||||
*/
|
||||
public class BinaryOpGenerator implements InstructionGenerator<BinaryOperationInstruction> {
|
||||
|
||||
/* ---------- 类型优先级工具 ---------- */
|
||||
/* -------------------- 常量与工具 -------------------- */
|
||||
|
||||
/**
|
||||
* 返回类型前缀的优先级数值。数值越大,类型“越宽”。
|
||||
* D: 6, F: 5, L: 4, I: 3, S: 2, B: 1
|
||||
* 用于生成唯一标签的计数器
|
||||
*/
|
||||
private static final AtomicInteger COUNTER = new AtomicInteger(0);
|
||||
|
||||
/**
|
||||
* 生成一个新的唯一标签。
|
||||
*
|
||||
* @param fn 当前函数名,用于标签前缀
|
||||
* @param tag 标签用途标识
|
||||
* @return 形如 fn$tag$序号 的唯一标签
|
||||
*/
|
||||
private static String fresh(String fn, String tag) {
|
||||
return fn + "$" + tag + "$" + COUNTER.getAndIncrement();
|
||||
}
|
||||
|
||||
/**
|
||||
* 类型优先级:D > F > L > I > S > B
|
||||
*/
|
||||
private static int rank(char p) {
|
||||
return switch (p) {
|
||||
@ -34,30 +57,42 @@ public class BinaryOpGenerator implements InstructionGenerator<BinaryOperationIn
|
||||
}
|
||||
|
||||
/**
|
||||
* 返回a和b中优先级更高的类型前缀(即类型提升结果)。
|
||||
* 返回优先级更高的类型字符
|
||||
*/
|
||||
private static char promote(char a, char b) {
|
||||
return rank(a) >= rank(b) ? a : b;
|
||||
}
|
||||
|
||||
/**
|
||||
* 类型前缀转字符串,方便拼接。
|
||||
* 单字符转字符串
|
||||
*/
|
||||
private static String str(char p) {
|
||||
return String.valueOf(p);
|
||||
}
|
||||
|
||||
/* ---------- 类型转换指令工具 ---------- */
|
||||
/**
|
||||
* 判断常量值是否等于 0。
|
||||
* 仅支持 Java 原生数值类型。
|
||||
*/
|
||||
private static boolean isZero(Object v) {
|
||||
if (v == null) return false;
|
||||
return switch (v) {
|
||||
case Integer i -> i == 0;
|
||||
case Long l -> l == 0L;
|
||||
case Short s -> s == (short) 0;
|
||||
case Byte b -> b == (byte) 0;
|
||||
case Float f -> f == 0.0f;
|
||||
case Double d -> d == 0.0;
|
||||
default -> false;
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* 根据源类型和目标类型前缀,返回相应的类型转换指令助记符。
|
||||
*
|
||||
* @param from 源类型前缀
|
||||
* @param to 目标类型前缀
|
||||
* @return 转换指令字符串,如 "I2L" 或 "F2D",若无需转换则返回null
|
||||
* 获取从类型 {@code from} 到 {@code to} 的转换指令名。
|
||||
* 相同类型或无显式转换需求返回 {@code null}。
|
||||
*/
|
||||
private static String convert(char from, char to) {
|
||||
if (from == to) return null; // 类型一致,无需转换
|
||||
if (from == to) return null;
|
||||
return switch ("" + from + to) {
|
||||
case "IL" -> "I2L";
|
||||
case "ID" -> "I2D";
|
||||
@ -73,61 +108,104 @@ public class BinaryOpGenerator implements InstructionGenerator<BinaryOperationIn
|
||||
case "DF" -> "D2F";
|
||||
case "SI" -> "S2I";
|
||||
case "BI" -> "B2I";
|
||||
default -> null; // 其它组合暂未用到
|
||||
default -> null;
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* 返回本生成器支持的指令类型,即 BinaryOperationInstruction。
|
||||
*/
|
||||
/* -------------------- 接口实现 -------------------- */
|
||||
|
||||
@Override
|
||||
public Class<BinaryOperationInstruction> supportedClass() {
|
||||
return BinaryOperationInstruction.class;
|
||||
}
|
||||
|
||||
/**
|
||||
* 生成二元运算的虚拟机指令,实现自动类型提升和必要的类型转换。
|
||||
*
|
||||
* @param ins 当前二元运算IR指令
|
||||
* @param out 虚拟机程序构建器
|
||||
* @param slotMap IR虚拟寄存器到实际槽位编号的映射
|
||||
* @param currentFn 当前函数名
|
||||
*/
|
||||
@Override
|
||||
public void generate(BinaryOperationInstruction ins,
|
||||
VMProgramBuilder out,
|
||||
Map<IRVirtualRegister, Integer> slotMap,
|
||||
String currentFn) {
|
||||
|
||||
/* ------- 1. 获取左右操作数的槽位编号和类型 ------- */
|
||||
int lSlot = slotMap.get((IRVirtualRegister) ins.operands().get(0)); // 左操作数槽位
|
||||
int rSlot = slotMap.get((IRVirtualRegister) ins.operands().get(1)); // 右操作数槽位
|
||||
int dSlot = slotMap.get(ins.dest()); // 目标槽位
|
||||
char lType = out.getSlotType(lSlot); // 左操作数类型前缀
|
||||
char rType = out.getSlotType(rSlot); // 右操作数类型前缀
|
||||
/* ---------- 0. +0 → MOV Peephole 优化 ---------- */
|
||||
String irName = ins.op().name();
|
||||
if (irName.startsWith("ADD_")) {
|
||||
IRValue lhs = ins.operands().getFirst();
|
||||
IRValue rhs = ins.operands().get(1);
|
||||
|
||||
// 类型提升,确定本次二元运算的目标类型(优先级较高的那一个)
|
||||
char tType = promote(lType, rType);
|
||||
String tPref = str(tType); // 用于拼接指令字符串
|
||||
boolean lhsZero = lhs instanceof IRConstant && isZero(((IRConstant) lhs).value());
|
||||
boolean rhsZero = rhs instanceof IRConstant && isZero(((IRConstant) rhs).value());
|
||||
|
||||
/* ------- 2. 加载左操作数,并自动进行类型转换(如有必要) ------- */
|
||||
out.emit(OpHelper.opcode(str(lType) + "_LOAD") + " " + lSlot); // LOAD指令
|
||||
String cvt = convert(lType, tType); // 如需类型提升
|
||||
if (cvt != null) out.emit(OpHelper.opcode(cvt)); // 插入类型转换指令
|
||||
// 仅当一侧为常量 0 时可替换为 MOV
|
||||
if (lhsZero ^ rhsZero) {
|
||||
IRVirtualRegister srcVr = null;
|
||||
if ((lhsZero ? rhs : lhs) instanceof IRVirtualRegister) {
|
||||
srcVr = (IRVirtualRegister) (lhsZero ? rhs : lhs);
|
||||
}
|
||||
int srcSlot = slotMap.get(srcVr);
|
||||
int destSlot = slotMap.get(ins.dest());
|
||||
|
||||
/* ------- 3. 加载右操作数,并自动进行类型转换(如有必要) ------- */
|
||||
out.emit(OpHelper.opcode(str(rType) + "_LOAD") + " " + rSlot); // LOAD指令
|
||||
cvt = convert(rType, tType); // 如需类型提升
|
||||
if (cvt != null) out.emit(OpHelper.opcode(cvt)); // 插入类型转换指令
|
||||
// 源与目标槽位不同才需要发 MOV
|
||||
if (srcSlot != destSlot) {
|
||||
out.emit(OpHelper.opcode("MOV") + " " + srcSlot + " " + destSlot);
|
||||
}
|
||||
// 复制槽位类型信息
|
||||
out.setSlotType(destSlot, out.getSlotType(srcSlot));
|
||||
return; // 优化路径结束
|
||||
}
|
||||
}
|
||||
|
||||
/* ------- 4. 生成具体的二元运算指令 ------- */
|
||||
// 获取IR指令中的操作名(如ADD、SUB、MUL等,去掉结尾的"_"后缀)
|
||||
String opName = ins.op().name().split("_")[0];
|
||||
// 例如生成 "I_ADD", "D_MUL" 等虚拟机指令
|
||||
out.emit(OpHelper.opcode(tPref + "_" + opName));
|
||||
/* ---------- 1. 槽位与类型 ---------- */
|
||||
int lSlot = slotMap.get((IRVirtualRegister) ins.operands().get(0));
|
||||
int rSlot = slotMap.get((IRVirtualRegister) ins.operands().get(1));
|
||||
int dSlot = slotMap.get(ins.dest());
|
||||
|
||||
/* ------- 5. 结果存入目标槽位,并更新槽位类型 ------- */
|
||||
out.emit(OpHelper.opcode(tPref + "_STORE") + " " + dSlot);
|
||||
out.setSlotType(dSlot, tType); // 记录运算结果的类型前缀,便于后续指令正确处理
|
||||
char lType = out.getSlotType(lSlot); // 未登记默认 'I'
|
||||
char rType = out.getSlotType(rSlot);
|
||||
|
||||
char tType = promote(lType, rType); // 类型提升结果
|
||||
String tPre = str(tType);
|
||||
|
||||
/* ---------- 2. 加载并做类型转换 ---------- */
|
||||
out.emit(OpHelper.opcode(str(lType) + "_LOAD") + " " + lSlot);
|
||||
String cvt = convert(lType, tType);
|
||||
if (cvt != null) out.emit(OpHelper.opcode(cvt));
|
||||
|
||||
out.emit(OpHelper.opcode(str(rType) + "_LOAD") + " " + rSlot);
|
||||
cvt = convert(rType, tType);
|
||||
if (cvt != null) out.emit(OpHelper.opcode(cvt));
|
||||
|
||||
/* ---------- 3. 区分算术 / 比较 ---------- */
|
||||
boolean isCmp = irName.startsWith("CMP_");
|
||||
|
||||
/* === 3-A. 普通算术 / 位运算 === */
|
||||
if (!isCmp) {
|
||||
String opCore = irName.split("_")[0]; // ADD / SUB / MUL …
|
||||
out.emit(OpHelper.opcode(tPre + "_" + opCore));
|
||||
out.emit(OpHelper.opcode(tPre + "_STORE") + " " + dSlot);
|
||||
out.setSlotType(dSlot, tType);
|
||||
return;
|
||||
}
|
||||
|
||||
/* === 3-B. CMP_* —— 生成布尔结果 === */
|
||||
String branchOp = OpHelper.opcode(IROpCodeMapper.toVMOp(ins.op())); // IC_E / IC_NE …
|
||||
String lblTrue = fresh(currentFn, "true");
|
||||
String lblEnd = fresh(currentFn, "end");
|
||||
|
||||
// ① 条件跳转;成立 → lblTrue
|
||||
out.emitBranch(branchOp, lblTrue);
|
||||
|
||||
// ② 不成立:压 0
|
||||
out.emit(OpHelper.opcode("I_PUSH") + " 0");
|
||||
out.emitBranch(OpHelper.opcode("JUMP"), lblEnd);
|
||||
|
||||
// ③ 成立分支:压 1
|
||||
out.emit(lblTrue + ":");
|
||||
out.emit(OpHelper.opcode("I_PUSH") + " 1");
|
||||
|
||||
// ④ 结束标签
|
||||
out.emit(lblEnd + ":");
|
||||
|
||||
// ⑤ 写入目标槽位
|
||||
out.emit(OpHelper.opcode("I_STORE") + " " + dSlot);
|
||||
out.setSlotType(dSlot, 'I'); // 布尔 ➜ int
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -41,13 +41,29 @@ public class UnaryOpGenerator implements InstructionGenerator<UnaryOperationInst
|
||||
VMProgramBuilder out,
|
||||
Map<IRVirtualRegister, Integer> slotMap,
|
||||
String currentFn) {
|
||||
// 获取操作数所在槽号
|
||||
int slotId = slotMap.get((IRVirtualRegister) ins.operands().getFirst());
|
||||
// 加载操作数到虚拟机栈顶
|
||||
out.emit(OpHelper.opcode("I_LOAD") + " " + slotId);
|
||||
// 生成对应的一元运算操作码(如取负等)
|
||||
out.emit(OpHelper.opcode(IROpCodeMapper.toVMOp(ins.op())));
|
||||
// 将结果存储到目标寄存器槽
|
||||
out.emit(OpHelper.opcode("I_STORE") + " " + slotMap.get(ins.dest()));
|
||||
|
||||
/* -------- 1. 源槽位与类型 -------- */
|
||||
int srcSlot = slotMap.get((IRVirtualRegister) ins.operands().getFirst());
|
||||
char prefix = out.getSlotType(srcSlot); // 未登记则返回默认 'I'
|
||||
|
||||
String loadOp = prefix + "_LOAD";
|
||||
String storeOp = prefix + "_STORE";
|
||||
|
||||
/* -------- 2. 指令序列 -------- */
|
||||
// 2-A. 加载操作数
|
||||
out.emit(OpHelper.opcode(loadOp) +
|
||||
" " + srcSlot);
|
||||
|
||||
// 2-B. 执行具体一元运算(NEG、NOT…)
|
||||
out.emit(OpHelper.opcode(
|
||||
IROpCodeMapper.toVMOp(ins.op())));
|
||||
|
||||
// 2-C. 存结果到目标槽
|
||||
int destSlot = slotMap.get(ins.dest());
|
||||
out.emit(OpHelper.opcode(storeOp) +
|
||||
" " + destSlot);
|
||||
|
||||
/* -------- 3. 更新目标槽类型 -------- */
|
||||
out.setSlotType(destSlot, prefix);
|
||||
}
|
||||
}
|
||||
|
||||
@ -3,6 +3,7 @@ package org.jcnc.snow.compiler.ir.builder;
|
||||
import org.jcnc.snow.compiler.ir.core.IROpCode;
|
||||
import org.jcnc.snow.compiler.ir.instruction.CallInstruction;
|
||||
import org.jcnc.snow.compiler.ir.instruction.LoadConstInstruction;
|
||||
import org.jcnc.snow.compiler.ir.instruction.UnaryOperationInstruction;
|
||||
import org.jcnc.snow.compiler.ir.value.IRConstant;
|
||||
import org.jcnc.snow.compiler.ir.value.IRVirtualRegister;
|
||||
import org.jcnc.snow.compiler.ir.utils.ExpressionUtils;
|
||||
@ -32,8 +33,15 @@ public record ExpressionBuilder(IRContext ctx) {
|
||||
* <p>会根据节点的实际类型分别处理:
|
||||
* <ul>
|
||||
* <li>数字字面量:新建常量寄存器</li>
|
||||
* <li>布尔字面量:生成值为 0 或 1 的常量寄存器</li>
|
||||
* <li>标识符:查找当前作用域中的寄存器</li>
|
||||
* <li>二元表达式:递归处理子表达式并进行相应运算</li>
|
||||
* <li>一元运算符:
|
||||
* <ul>
|
||||
* <li><code>-x</code>(取负,生成 <code>NEG_I32</code> 指令)与</li>
|
||||
* <li>code>!x</code>(逻辑非,转换为 <code>x == 0</code> 比较指令)</li>
|
||||
* </ul>
|
||||
* </li>
|
||||
* <li>函数调用:生成对应的Call指令</li>
|
||||
* <li>其它类型不支持,抛出异常</li>
|
||||
* </ul>
|
||||
@ -42,6 +50,7 @@ public record ExpressionBuilder(IRContext ctx) {
|
||||
* @return 该表达式的计算结果寄存器
|
||||
* @throws IllegalStateException 如果遇到未定义的标识符或不支持的表达式类型
|
||||
*/
|
||||
|
||||
public IRVirtualRegister build(ExpressionNode expr) {
|
||||
return switch (expr) {
|
||||
// 数字字面量
|
||||
@ -59,11 +68,33 @@ public record ExpressionBuilder(IRContext ctx) {
|
||||
case BinaryExpressionNode bin -> buildBinary(bin);
|
||||
// 函数调用
|
||||
case CallExpressionNode call -> buildCall(call);
|
||||
case UnaryExpressionNode u -> buildUnary(u);
|
||||
default -> throw new IllegalStateException(
|
||||
"不支持的表达式类型: " + expr.getClass().getSimpleName());
|
||||
};
|
||||
}
|
||||
|
||||
/** 处理一元表达式 */
|
||||
private IRVirtualRegister buildUnary(UnaryExpressionNode un) {
|
||||
String op = un.operator();
|
||||
IRVirtualRegister val = build(un.operand());
|
||||
|
||||
// -x → NEG_*(根据类型自动选择位宽)
|
||||
if (op.equals("-")) {
|
||||
IRVirtualRegister dest = ctx.newRegister();
|
||||
IROpCode code = ExpressionUtils.negOp(un.operand());
|
||||
ctx.addInstruction(new UnaryOperationInstruction(code, dest, val));
|
||||
return dest;
|
||||
}
|
||||
|
||||
// !x → (x == 0)
|
||||
if (op.equals("!")) {
|
||||
IRVirtualRegister zero = InstructionFactory.loadConst(ctx, 0);
|
||||
return InstructionFactory.binOp(ctx, IROpCode.CMP_EQ, val, zero);
|
||||
}
|
||||
|
||||
throw new IllegalStateException("未知一元运算符: " + op);
|
||||
}
|
||||
|
||||
/**
|
||||
* 直接将表达式计算结果写入指定的目标寄存器(dest)。
|
||||
@ -154,7 +185,7 @@ public record ExpressionBuilder(IRContext ctx) {
|
||||
.toList();
|
||||
// 获取完整调用目标名称(支持成员/模块调用和普通调用)
|
||||
String fullName = switch (call.callee()) {
|
||||
case MemberExpressionNode member when member.object() instanceof IdentifierNode mod ->
|
||||
case MemberExpressionNode member when member.object() instanceof IdentifierNode _ ->
|
||||
((IdentifierNode)member.object()).name() + "." + member.member();
|
||||
case IdentifierNode id -> id.name();
|
||||
default -> throw new IllegalStateException("不支持的调用目标: " + call.callee().getClass().getSimpleName());
|
||||
|
||||
@ -19,7 +19,7 @@ import org.jcnc.snow.compiler.ir.value.IRVirtualRegister;
|
||||
*/
|
||||
public class IRContext {
|
||||
|
||||
/* ➡ 新增:生成唯一标签用 */
|
||||
/* 生成唯一标签用 */
|
||||
private int labelCounter = 0;
|
||||
/**
|
||||
* 当前正在构建的 IRFunction 对象,所有指令将添加至此
|
||||
|
||||
@ -78,7 +78,7 @@ public class InstructionFactory {
|
||||
}
|
||||
|
||||
/**
|
||||
* 简易 Move 指令(src → dest)。若寄存器相同也安全。
|
||||
* Move 指令(src → dest)。若寄存器相同也安全。
|
||||
* <p>
|
||||
* 实现方式:dest = src + 0(即加上常量 0)。
|
||||
* </p>
|
||||
@ -88,7 +88,11 @@ public class InstructionFactory {
|
||||
* @param dest 目标寄存器
|
||||
*/
|
||||
public static void move(IRContext ctx, IRVirtualRegister src, IRVirtualRegister dest) {
|
||||
/* 采用 “dest = src + 0” 的最简实现 */
|
||||
// 自赋值无需任何操作,避免生成多余的常量 0 寄存器
|
||||
if (src == dest) {
|
||||
return;
|
||||
}
|
||||
// 回退实现:dest = src + 0
|
||||
IRVirtualRegister zero = loadConst(ctx, 0);
|
||||
ctx.addInstruction(new BinaryOperationInstruction(IROpCode.ADD_I32, dest, src, zero));
|
||||
}
|
||||
|
||||
@ -8,6 +8,8 @@ import org.jcnc.snow.compiler.parser.ast.*;
|
||||
import org.jcnc.snow.compiler.parser.ast.base.ExpressionNode;
|
||||
import org.jcnc.snow.compiler.parser.ast.base.StatementNode;
|
||||
|
||||
import java.util.Locale;
|
||||
|
||||
/**
|
||||
* StatementBuilder —— 将 AST 语句节点 ({@link StatementNode}) 转换为 IR 指令序列的构建器。
|
||||
* <p>
|
||||
@ -177,4 +179,16 @@ public class StatementBuilder {
|
||||
InstructionFactory.cmpJump(ctx, IROpCode.CMP_EQ, condReg, zero, falseLabel);
|
||||
}
|
||||
}
|
||||
|
||||
private static char typeSuffixFromType(String type) {
|
||||
if (type == null) return '\0';
|
||||
return switch (type.toLowerCase(Locale.ROOT)) {
|
||||
case "byte" -> 'b';
|
||||
case "short" -> 's';
|
||||
case "long" -> 'l';
|
||||
case "float" -> 'f';
|
||||
case "double" -> 'd';
|
||||
default -> '\0'; // 其余默认按 32-bit 整型处理
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
@ -78,6 +78,30 @@ public class ExpressionUtils {
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* 根据表达式节点推断一元取负(-)运算应使用的操作码。
|
||||
*
|
||||
* <p>优先级与 {@link #resolveOpCode} 使用的类型提升规则保持一致:</p>
|
||||
* <ul>
|
||||
* <li>字面量或标识符带显式后缀时,直接以后缀决定位宽;</li>
|
||||
* <li>未显式指定时,默认使用 32 位整型 {@link IROpCode#NEG_I32}。</li>
|
||||
* </ul>
|
||||
*
|
||||
* @param operand 一元取负运算的操作数
|
||||
* @return 匹配的 {@link IROpCode}
|
||||
*/
|
||||
public static IROpCode negOp(ExpressionNode operand) {
|
||||
char t = typeChar(operand);
|
||||
return switch (t) {
|
||||
case 'b' -> IROpCode.NEG_B8;
|
||||
case 's' -> IROpCode.NEG_S16;
|
||||
case 'l' -> IROpCode.NEG_L64;
|
||||
case 'f' -> IROpCode.NEG_F32;
|
||||
case 'd' -> IROpCode.NEG_D64;
|
||||
default -> IROpCode.NEG_I32;
|
||||
};
|
||||
}
|
||||
|
||||
/* =================== 类型推断与操作符匹配 =================== */
|
||||
|
||||
/**
|
||||
|
||||
@ -5,26 +5,29 @@ import org.jcnc.snow.compiler.lexer.token.Token;
|
||||
import org.jcnc.snow.compiler.lexer.token.TokenType;
|
||||
|
||||
/**
|
||||
* 运算符扫描器:识别逻辑与比较运算符,包括单字符和双字符组合。
|
||||
* <p>
|
||||
* 支持的运算符包括:
|
||||
* 运算符扫描器(OperatorTokenScanner)
|
||||
*
|
||||
* <p>负责在词法分析阶段识别由 <b>= ! < > | & %</b> 等字符
|
||||
* 起始的单字符或双字符运算符,并生成相应 {@link Token}:</p>
|
||||
*
|
||||
* <ul>
|
||||
* <li>赋值与比较:=、==、!=</li>
|
||||
* <li>关系运算符:>、>=、<、<=</li>
|
||||
* <li>逻辑运算符:&&、||</li>
|
||||
* <li>赋值 / 比较:{@code =}, {@code ==}, {@code !=}</li>
|
||||
* <li>关系运算:{@code >}, {@code >=}, {@code <}, {@code <=}</li>
|
||||
* <li>逻辑运算:{@code &&}, {@code ||}</li>
|
||||
* <li>取模运算:{@code %}</li>
|
||||
* <li>逻辑非:{@code !}</li>
|
||||
* </ul>
|
||||
* <p>
|
||||
* 不符合上述组合的字符会返回 {@code UNKNOWN} 类型的 Token。
|
||||
*
|
||||
* <p>如果无法匹配到合法组合,将返回 {@link TokenType#UNKNOWN}。</p>
|
||||
*/
|
||||
public class OperatorTokenScanner extends AbstractTokenScanner {
|
||||
|
||||
/**
|
||||
* 判断是否可以处理当前位置的字符。
|
||||
* <p>运算符扫描器关注的起始字符包括:=、!、<、>、|、&</p>
|
||||
* 判断当前字符是否可能是运算符的起始字符。
|
||||
*
|
||||
* @param c 当前字符
|
||||
* @param ctx 当前词法上下文
|
||||
* @return 如果是潜在的运算符起始字符,则返回 true
|
||||
* @param ctx 词法上下文
|
||||
* @return 若是关注的起始字符则返回 {@code true}
|
||||
*/
|
||||
@Override
|
||||
public boolean canHandle(char c, LexerContext ctx) {
|
||||
@ -32,14 +35,12 @@ public class OperatorTokenScanner extends AbstractTokenScanner {
|
||||
}
|
||||
|
||||
/**
|
||||
* 扫描并识别运算符 Token。
|
||||
* <p>支持组合运算符判断,如 ==、!=、>= 等,
|
||||
* 若无法匹配组合形式则退回单字符形式。</p>
|
||||
* 按最长匹配优先原则扫描并生成运算符 token。
|
||||
*
|
||||
* @param ctx 词法上下文
|
||||
* @param ctx 词法上下文
|
||||
* @param line 当前行号
|
||||
* @param col 当前列号
|
||||
* @return 对应的运算符 Token,无法识别的运算符返回 {@code UNKNOWN}
|
||||
* @param col 当前列号
|
||||
* @return 已识别的 {@link Token}
|
||||
*/
|
||||
@Override
|
||||
protected Token scanToken(LexerContext ctx, int line, int col) {
|
||||
@ -51,64 +52,71 @@ public class OperatorTokenScanner extends AbstractTokenScanner {
|
||||
case '=':
|
||||
if (ctx.match('=')) {
|
||||
lexeme = "==";
|
||||
type = TokenType.DOUBLE_EQUALS;
|
||||
type = TokenType.DOUBLE_EQUALS;
|
||||
} else {
|
||||
lexeme = "=";
|
||||
type = TokenType.EQUALS;
|
||||
type = TokenType.EQUALS;
|
||||
}
|
||||
break;
|
||||
|
||||
case '!':
|
||||
if (ctx.match('=')) {
|
||||
lexeme = "!=";
|
||||
type = TokenType.NOT_EQUALS;
|
||||
type = TokenType.NOT_EQUALS;
|
||||
} else {
|
||||
lexeme = "!";
|
||||
type = TokenType.UNKNOWN;
|
||||
type = TokenType.NOT;
|
||||
}
|
||||
break;
|
||||
|
||||
case '>':
|
||||
if (ctx.match('=')) {
|
||||
lexeme = ">=";
|
||||
type = TokenType.GREATER_EQUAL;
|
||||
type = TokenType.GREATER_EQUAL;
|
||||
} else {
|
||||
lexeme = ">";
|
||||
type = TokenType.GREATER_THAN;
|
||||
type = TokenType.GREATER_THAN;
|
||||
}
|
||||
break;
|
||||
|
||||
case '<':
|
||||
if (ctx.match('=')) {
|
||||
lexeme = "<=";
|
||||
type = TokenType.LESS_EQUAL;
|
||||
type = TokenType.LESS_EQUAL;
|
||||
} else {
|
||||
lexeme = "<";
|
||||
type = TokenType.LESS_THAN;
|
||||
type = TokenType.LESS_THAN;
|
||||
}
|
||||
break;
|
||||
|
||||
case '%':
|
||||
lexeme = "%";
|
||||
type = TokenType.MODULO;
|
||||
type = TokenType.MODULO;
|
||||
break;
|
||||
|
||||
case '&':
|
||||
if (ctx.match('&')) {
|
||||
lexeme = "&&";
|
||||
type = TokenType.AND;
|
||||
type = TokenType.AND;
|
||||
} else {
|
||||
lexeme = "&";
|
||||
type = TokenType.UNKNOWN;
|
||||
type = TokenType.UNKNOWN;
|
||||
}
|
||||
break;
|
||||
|
||||
case '|':
|
||||
if (ctx.match('|')) {
|
||||
lexeme = "||";
|
||||
type = TokenType.OR;
|
||||
type = TokenType.OR;
|
||||
} else {
|
||||
lexeme = "|";
|
||||
type = TokenType.UNKNOWN;
|
||||
type = TokenType.UNKNOWN;
|
||||
}
|
||||
break;
|
||||
|
||||
default:
|
||||
lexeme = String.valueOf(c);
|
||||
type = TokenType.UNKNOWN;
|
||||
type = TokenType.UNKNOWN;
|
||||
}
|
||||
|
||||
return new Token(type, lexeme, line, col);
|
||||
|
||||
@ -19,9 +19,6 @@ import java.util.Set;
|
||||
* <li>对不合法的词素自动标记为 UNKNOWN 类型。</li>
|
||||
* </ul>
|
||||
* </p>
|
||||
*
|
||||
* @author 你的名字
|
||||
* @version 1.0
|
||||
*/
|
||||
public class TokenFactory {
|
||||
|
||||
|
||||
@ -10,6 +10,7 @@ package org.jcnc.snow.compiler.lexer.token;
|
||||
*/
|
||||
public enum TokenType {
|
||||
|
||||
/* ---------- 基础 ---------- */
|
||||
/** 普通标识符,如变量名、函数名等 */
|
||||
IDENTIFIER,
|
||||
|
||||
@ -19,14 +20,17 @@ public enum TokenType {
|
||||
/** 内置类型名称(如 int、string、bool 等) */
|
||||
TYPE,
|
||||
|
||||
/* ---------- 字面量 ---------- */
|
||||
/** 布尔字面量 (true / false) */
|
||||
BOOL_LITERAL,
|
||||
|
||||
/** 字符串字面量(如 "hello") */
|
||||
STRING_LITERAL,
|
||||
|
||||
/** 数字字面量(整数或浮点数) */
|
||||
NUMBER_LITERAL,
|
||||
|
||||
/* ---------- 分隔符 ---------- */
|
||||
/** 冒号 ':' */
|
||||
COLON,
|
||||
|
||||
@ -36,6 +40,7 @@ public enum TokenType {
|
||||
/** 点号 '.' */
|
||||
DOT,
|
||||
|
||||
/* ---------- 运算符 ---------- */
|
||||
/** 赋值符号 '=' */
|
||||
EQUALS,
|
||||
|
||||
@ -53,6 +58,9 @@ public enum TokenType {
|
||||
/** 减号 '-' */
|
||||
MINUS,
|
||||
|
||||
/** 取反 '!' */
|
||||
NOT,
|
||||
|
||||
/** 左括号 '(' */
|
||||
LPAREN,
|
||||
|
||||
|
||||
@ -5,15 +5,13 @@ import org.jcnc.snow.compiler.parser.ast.base.ExpressionNode;
|
||||
/**
|
||||
* 表示布尔字面量(boolean literal)的抽象语法树(AST)节点。
|
||||
* <p>
|
||||
* 本类实现了 {@link ExpressionNode} 接口,用于在编译器前端构建语法分析过程中,
|
||||
* 该纪录类实现 {@link ExpressionNode} 接口,用于在编译器前端构建语法分析过程中,
|
||||
* 表达布尔类型的字面量常量(如 "true" 或 "false")。
|
||||
* </p>
|
||||
*
|
||||
* @param value 字面量的布尔值
|
||||
*/
|
||||
public class BoolLiteralNode implements ExpressionNode {
|
||||
/**
|
||||
* 字面量的布尔值。
|
||||
*/
|
||||
private final boolean value;
|
||||
public record BoolLiteralNode(boolean value) implements ExpressionNode {
|
||||
|
||||
/**
|
||||
* 使用布尔字面量字符串构造一个 {@code BoolLiteralNode} 实例。
|
||||
@ -25,7 +23,7 @@ public class BoolLiteralNode implements ExpressionNode {
|
||||
* @param lexeme 布尔字面量的字符串表示
|
||||
*/
|
||||
public BoolLiteralNode(String lexeme) {
|
||||
this.value = Boolean.parseBoolean(lexeme);
|
||||
this(Boolean.parseBoolean(lexeme));
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@ -0,0 +1,31 @@
|
||||
package org.jcnc.snow.compiler.parser.ast;
|
||||
|
||||
import org.jcnc.snow.compiler.parser.ast.base.ExpressionNode;
|
||||
|
||||
/**
|
||||
* {@code UnaryExpressionNode} —— 前缀一元运算 AST 节点。
|
||||
*
|
||||
* <p>代表两种受支持的一元前缀表达式:
|
||||
* <ul>
|
||||
* <li><b>取负</b>:{@code -x}</li>
|
||||
* <li><b>逻辑非</b>:{@code !x}</li>
|
||||
* </ul>
|
||||
*
|
||||
* {@link #equals(Object)}、{@link #hashCode()} 等方法。</p>
|
||||
*
|
||||
* @param operator 一元运算符(仅 "-" 或 "!")
|
||||
* @param operand 运算对象 / 右操作数
|
||||
*/
|
||||
public record UnaryExpressionNode(String operator,
|
||||
ExpressionNode operand) implements ExpressionNode {
|
||||
|
||||
/**
|
||||
* 生成调试友好的字符串表示,例如 {@code "-x"} 或 {@code "!flag"}。
|
||||
*
|
||||
* @return 一元表达式的串表示
|
||||
*/
|
||||
@Override
|
||||
public String toString() {
|
||||
return operator + operand;
|
||||
}
|
||||
}
|
||||
@ -45,6 +45,10 @@ public class PrattExpressionParser implements ExpressionParser {
|
||||
prefixes.put(TokenType.STRING_LITERAL.name(), new StringLiteralParselet());
|
||||
prefixes.put(TokenType.BOOL_LITERAL.name(), new BoolLiteralParselet());
|
||||
|
||||
// 注册一元前缀运算
|
||||
prefixes.put(TokenType.MINUS.name(), new UnaryOperatorParselet());
|
||||
prefixes.put(TokenType.NOT.name(), new UnaryOperatorParselet());
|
||||
|
||||
// 注册中缀解析器
|
||||
infixes.put("+", new BinaryOperatorParselet(Precedence.SUM, true));
|
||||
infixes.put("-", new BinaryOperatorParselet(Precedence.SUM, true));
|
||||
|
||||
@ -24,6 +24,9 @@ public enum Precedence {
|
||||
*/
|
||||
PRODUCT,
|
||||
|
||||
/** 一元前缀(-x !x) */
|
||||
UNARY,
|
||||
|
||||
/**
|
||||
* 函数调用、成员访问等最强绑定(例如 foo()、obj.prop)。
|
||||
*/
|
||||
|
||||
@ -0,0 +1,55 @@
|
||||
package org.jcnc.snow.compiler.parser.expression;
|
||||
|
||||
import org.jcnc.snow.compiler.lexer.token.Token;
|
||||
import org.jcnc.snow.compiler.parser.ast.UnaryExpressionNode;
|
||||
import org.jcnc.snow.compiler.parser.ast.base.ExpressionNode;
|
||||
import org.jcnc.snow.compiler.parser.context.ParserContext;
|
||||
import org.jcnc.snow.compiler.parser.expression.base.PrefixParselet;
|
||||
|
||||
/**
|
||||
* {@code UnaryOperatorParselet} —— 前缀一元运算符的 Pratt 解析器。
|
||||
*
|
||||
* <p>当前 parselet 负责解析两种前缀运算:
|
||||
* <ul>
|
||||
* <li><b>取负</b>:{@code -x}</li>
|
||||
* <li><b>逻辑非</b>:{@code !x}</li>
|
||||
* </ul>
|
||||
*
|
||||
* 解析过程:
|
||||
*
|
||||
* <ol>
|
||||
* <li>该 parselet 在外层解析器已消费运算符 {@code token} 后被调用。</li>
|
||||
* <li>以 {@link Precedence#UNARY} 作为 <em>绑定强度</em> 递归解析右侧子表达式,
|
||||
* 保证任何更高优先级的表达式(括号、字面量等)优先归属右侧。</li>
|
||||
* <li>最终生成 {@link UnaryExpressionNode} AST 节点,记录运算符与操作数。</li>
|
||||
* </ol>
|
||||
*
|
||||
* <p>此类仅负责<strong>语法结构</strong>的构建:
|
||||
* <ul>
|
||||
* <li>类型正确性在 {@code UnaryExpressionAnalyzer} 中校验;</li>
|
||||
* <li>IR 生成在 {@code ExpressionBuilder.buildUnary} 中完成。</li>
|
||||
* </ul>
|
||||
*/
|
||||
public class UnaryOperatorParselet implements PrefixParselet {
|
||||
|
||||
/**
|
||||
* 解析前缀一元表达式。
|
||||
*
|
||||
* @param ctx 当前解析上下文
|
||||
* @param token 已被消费的运算符 Token(字面值应为 {@code "-" 或 "!"})
|
||||
* @return 构建出的 {@link UnaryExpressionNode}
|
||||
*/
|
||||
@Override
|
||||
public ExpressionNode parse(ParserContext ctx, Token token) {
|
||||
/* ------------------------------------------------------------
|
||||
* 1. 以 UNARY 优先级递归解析操作数,避免错误结合顺序。
|
||||
* ------------------------------------------------------------ */
|
||||
ExpressionNode operand =
|
||||
new PrattExpressionParser().parseExpression(ctx, Precedence.UNARY);
|
||||
|
||||
/* ------------------------------------------------------------
|
||||
* 2. 封装成 AST 节点并返回。
|
||||
* ------------------------------------------------------------ */
|
||||
return new UnaryExpressionNode(token.getLexeme(), operand);
|
||||
}
|
||||
}
|
||||
@ -140,7 +140,6 @@ public class FunctionParser implements TopLevelParser {
|
||||
private void parseFunctionFooter(TokenStream ts) {
|
||||
ts.expect("end");
|
||||
ts.expect("function");
|
||||
ts.expectType(TokenType.NEWLINE);
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@ -13,7 +13,7 @@ import java.util.*;
|
||||
* 并可借助 {@code JSONParser.toJson(Object)} 方法将其序列化为 JSON 字符串,用于调试、
|
||||
* 可视化或跨语言数据传输。
|
||||
* <p>
|
||||
* 支持的节点类型包括:
|
||||
* 支持的节点类型包括(新增对 {@code BoolLiteralNode}、{@code UnaryExpressionNode} 的完整支持):
|
||||
* <ul>
|
||||
* <li>{@link ModuleNode}</li>
|
||||
* <li>{@link FunctionNode}</li>
|
||||
@ -23,7 +23,9 @@ import java.util.*;
|
||||
* <li>{@link LoopNode}</li>
|
||||
* <li>{@link ReturnNode}</li>
|
||||
* <li>{@link ExpressionStatementNode}</li>
|
||||
* <li>各类 {@link ExpressionNode} 子类型,如 {@code BinaryExpressionNode}, {@code IdentifierNode} 等</li>
|
||||
* <li>{@link BoolLiteralNode}</li>
|
||||
* <li>{@link UnaryExpressionNode}</li>
|
||||
* <li>以及各类 {@link ExpressionNode} 子类型,如 {@code BinaryExpressionNode}, {@code IdentifierNode} 等</li>
|
||||
* </ul>
|
||||
*/
|
||||
public class ASTJsonSerializer {
|
||||
@ -174,19 +176,32 @@ public class ASTJsonSerializer {
|
||||
*/
|
||||
private static Object exprToMap(ExpressionNode expr) {
|
||||
return switch (expr) {
|
||||
// 二元表达式
|
||||
case BinaryExpressionNode(ExpressionNode left, String operator, ExpressionNode right) -> exprMap("BinaryExpression",
|
||||
"left", exprToMap(left),
|
||||
"operator", operator,
|
||||
"right", exprToMap(right)
|
||||
);
|
||||
// 一元表达式
|
||||
case UnaryExpressionNode(String operator, ExpressionNode operand) -> exprMap("UnaryExpression",
|
||||
"operator", operator,
|
||||
"operand", exprToMap(operand)
|
||||
);
|
||||
// 布尔字面量
|
||||
case BoolLiteralNode(boolean value) -> exprMap("BoolLiteral", "value", value);
|
||||
// 标识符
|
||||
case IdentifierNode(String name) -> exprMap("Identifier", "name", name);
|
||||
// 数字字面量
|
||||
case NumberLiteralNode(String value) -> exprMap("NumberLiteral", "value", value);
|
||||
// 字符串字面量
|
||||
case StringLiteralNode(String value) -> exprMap("StringLiteral", "value", value);
|
||||
case CallExpressionNode(ExpressionNode callee, List<ExpressionNode> arguments, int line, int column, String file) -> {
|
||||
// 调用表达式
|
||||
case CallExpressionNode(ExpressionNode callee, List<ExpressionNode> arguments, _, _, _) -> {
|
||||
List<Object> args = new ArrayList<>(arguments.size());
|
||||
for (ExpressionNode arg : arguments) args.add(exprToMap(arg));
|
||||
yield exprMap("CallExpression", "callee", exprToMap(callee), "arguments", args);
|
||||
}
|
||||
// 成员访问表达式
|
||||
case MemberExpressionNode(ExpressionNode object, String member) -> exprMap("MemberExpression",
|
||||
"object", exprToMap(object),
|
||||
"member", member
|
||||
@ -195,4 +210,4 @@ public class ASTJsonSerializer {
|
||||
default -> Map.of("type", expr.getClass().getSimpleName());
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -67,9 +67,9 @@ public class BinaryExpressionAnalyzer implements ExpressionAnalyzer<BinaryExpres
|
||||
Type wide = Type.widen(left, right);
|
||||
if (wide == null) wide = BuiltinType.INT; // 容错降级为 int
|
||||
|
||||
// 若为比较运算符,统一返回 int 类型作为布尔值表示
|
||||
// 若为比较运算符,统一返回 boolean 类型作为布尔值表示
|
||||
if ("< <= > >= == !=".contains(op)) {
|
||||
return BuiltinType.INT;
|
||||
return BuiltinType.BOOLEAN;
|
||||
}
|
||||
|
||||
return wide;
|
||||
|
||||
@ -0,0 +1,107 @@
|
||||
package org.jcnc.snow.compiler.semantic.analyzers.expression;
|
||||
|
||||
import org.jcnc.snow.compiler.parser.ast.FunctionNode;
|
||||
import org.jcnc.snow.compiler.parser.ast.UnaryExpressionNode;
|
||||
import org.jcnc.snow.compiler.semantic.analyzers.base.ExpressionAnalyzer;
|
||||
import org.jcnc.snow.compiler.semantic.core.Context;
|
||||
import org.jcnc.snow.compiler.semantic.core.ModuleInfo;
|
||||
import org.jcnc.snow.compiler.semantic.error.SemanticError;
|
||||
import org.jcnc.snow.compiler.semantic.symbol.SymbolTable;
|
||||
import org.jcnc.snow.compiler.semantic.type.BuiltinType;
|
||||
import org.jcnc.snow.compiler.semantic.type.Type;
|
||||
|
||||
/**
|
||||
* {@code UnaryExpressionAnalyzer} — 一元表达式的语义分析器。
|
||||
*
|
||||
* <p>目前实现两种一元运算:
|
||||
* <ul>
|
||||
* <li>{@code -x} 取负:仅允许作用于数值类型(int / float 等)。</li>
|
||||
* <li>{@code !x} 逻辑非:仅允许作用于 {@code boolean} 类型。</li>
|
||||
* </ul>
|
||||
*
|
||||
* <p>分析流程:
|
||||
* <ol>
|
||||
* <li>递归分析操作数表达式,获取其类型 {@code operandType}。</li>
|
||||
* <li>根据运算符检查类型合法性:
|
||||
* <ul>
|
||||
* <li>若类型不符,记录 {@link SemanticError} 并返回一个占位类型
|
||||
* (取负返回 {@link BuiltinType#INT},逻辑非返回
|
||||
* {@link BuiltinType#BOOLEAN})。</li>
|
||||
* <li>若合法,则返回运算后的结果类型
|
||||
* (取负为 {@code operandType},逻辑非为 {@link BuiltinType#BOOLEAN})。</li>
|
||||
* </ul>
|
||||
* </li>
|
||||
* </ol>
|
||||
*
|
||||
* <p>若遇到未支持的运算符,将生成错误并返回 {@code int} 作为占位类型。</p>
|
||||
*
|
||||
*/
|
||||
public class UnaryExpressionAnalyzer implements ExpressionAnalyzer<UnaryExpressionNode> {
|
||||
|
||||
/**
|
||||
* 对一元表达式进行语义分析。
|
||||
*
|
||||
* @param ctx 全局编译上下文,持有错误列表、注册表等
|
||||
* @param mi 当前模块信息
|
||||
* @param fn 所在函数节点(可为 {@code null} 表示顶层)
|
||||
* @param locals 当前作用域符号表
|
||||
* @param expr 要分析的一元表达式节点
|
||||
* @return 表达式的结果类型;若有错误,返回占位类型并在 {@code ctx.getErrors()}
|
||||
* 中记录 {@link SemanticError}
|
||||
*/
|
||||
@Override
|
||||
public Type analyze(Context ctx,
|
||||
ModuleInfo mi,
|
||||
FunctionNode fn,
|
||||
SymbolTable locals,
|
||||
UnaryExpressionNode expr) {
|
||||
|
||||
/* ------------------------------------------------------------------
|
||||
* 1. 先递归分析操作数,确定其类型
|
||||
* ------------------------------------------------------------------ */
|
||||
Type operandType = ctx.getRegistry()
|
||||
.getExpressionAnalyzer(expr.operand())
|
||||
.analyze(ctx, mi, fn, locals, expr.operand());
|
||||
|
||||
/* ------------------------------------------------------------------
|
||||
* 2. 根据运算符校验类型并给出结果类型
|
||||
* ------------------------------------------------------------------ */
|
||||
switch (expr.operator()) {
|
||||
/* -------------- 取负运算 -------------- */
|
||||
case "-" -> {
|
||||
if (!operandType.isNumeric()) {
|
||||
ctx.getErrors().add(new SemanticError(
|
||||
expr,
|
||||
"'-' 只能应用于数值类型,当前为 " + operandType
|
||||
));
|
||||
// 返回占位类型,避免后续阶段 NPE
|
||||
return BuiltinType.INT;
|
||||
}
|
||||
// 合法:结果类型与操作数相同
|
||||
return operandType;
|
||||
}
|
||||
|
||||
/* -------------- 逻辑非运算 -------------- */
|
||||
case "!" -> {
|
||||
if (operandType != BuiltinType.BOOLEAN) {
|
||||
ctx.getErrors().add(new SemanticError(
|
||||
expr,
|
||||
"'!' 只能应用于 boolean 类型,当前为 " + operandType
|
||||
));
|
||||
return BuiltinType.BOOLEAN;
|
||||
}
|
||||
// 合法:结果类型恒为 boolean
|
||||
return BuiltinType.BOOLEAN;
|
||||
}
|
||||
|
||||
/* -------------- 未知运算符 -------------- */
|
||||
default -> {
|
||||
ctx.getErrors().add(new SemanticError(
|
||||
expr,
|
||||
"未知一元运算符: " + expr.operator()
|
||||
));
|
||||
return BuiltinType.INT;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -55,7 +55,10 @@ public final class AnalyzerRegistrar {
|
||||
registry.registerExpressionAnalyzer(CallExpressionNode.class, new CallExpressionAnalyzer());
|
||||
registry.registerExpressionAnalyzer(BinaryExpressionNode.class, new BinaryExpressionAnalyzer());
|
||||
|
||||
// 对尚未实现的表达式类型使用兜底处理器(如 MemberExpression)
|
||||
// ---------- 注册一元表达式分析器 ----------
|
||||
registry.registerExpressionAnalyzer(UnaryExpressionNode.class,new UnaryExpressionAnalyzer());
|
||||
|
||||
// 对尚未实现的表达式类型使用兜底处理器
|
||||
registry.registerExpressionAnalyzer(MemberExpressionNode.class,
|
||||
new UnsupportedExpressionAnalyzer<>());
|
||||
}
|
||||
|
||||
@ -48,8 +48,8 @@ public class LXorCommand implements Command {
|
||||
}
|
||||
|
||||
// Pop the top two operands from the stack
|
||||
final int b = (int) operandStack.pop();
|
||||
final int a = (int) operandStack.pop();
|
||||
final long b = (long) operandStack.pop();
|
||||
final long a = (long) operandStack.pop();
|
||||
|
||||
// Perform the long64 bitwise XOR operation and push the result back onto the stack
|
||||
operandStack.push(a ^ b);
|
||||
|
||||
@ -20,7 +20,8 @@ import java.util.List;
|
||||
* <li>{@link CommandExecutionHandler} — dispatches opcodes</li>
|
||||
* </ul>
|
||||
*
|
||||
* <h2>Root-frame contract</h2>
|
||||
* Root-frame contract:
|
||||
* <p>
|
||||
* A <strong>root stack frame</strong> is pushed <em>once</em> via
|
||||
* {@link #ensureRootFrame()} before the first instruction executes
|
||||
* and is never popped. When a {@code RET} executed in the root frame
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user