diff --git a/src/main/java/org/jcnc/snow/compiler/ir/utils/ExpressionUtils.java b/src/main/java/org/jcnc/snow/compiler/ir/utils/ExpressionUtils.java index ba6f663..c12525d 100644 --- a/src/main/java/org/jcnc/snow/compiler/ir/utils/ExpressionUtils.java +++ b/src/main/java/org/jcnc/snow/compiler/ir/utils/ExpressionUtils.java @@ -12,33 +12,46 @@ import org.jcnc.snow.compiler.parser.ast.base.NodeContext; import java.util.Map; /** - * 表达式分析与运算符辅助工具类。 - * - *

主要功能:

- * + * 表达式分析与操作符选择工具类。 + *

+ * 主要功能: + * - 解析字面量常量,自动推断类型 + * - 自动匹配并选择适合的算术/比较操作码 + * - 表达式类型的合并与类型提升 + * - 支持线程隔离的函数级默认类型后缀 */ public final class ExpressionUtils { private ExpressionUtils() {} - /* ────────────────── 线程级默认类型后缀 ────────────────── */ + // ───────────── 线程级默认类型后缀 ───────────── - /** 默认类型后缀(如当前函数返回类型),线程隔离。 */ + /** + * 当前线程的默认类型后缀(如当前函数返回类型等),用于类型推断兜底。 + */ private static final ThreadLocal DEFAULT_SUFFIX = ThreadLocal.withInitial(() -> '\0'); + /** + * 设置当前线程的默认类型后缀。 + * + * @param suffix 类型后缀字符(b/s/i/l/f/d),'\0'表示无 + */ public static void setDefaultSuffix(char suffix) { DEFAULT_SUFFIX.set(suffix); } + /** + * 清除当前线程的默认类型后缀,重置为无。 + */ public static void clearDefaultSuffix() { DEFAULT_SUFFIX.set('\0'); } - /* ───────────────────── 字面量 & 常量 ───────────────────── */ + // ───────────── 字面量常量解析 ───────────── /** - * 解析整数字面量字符串,自动去除类型后缀(b/s/l/f/d/B/S/L/F/D),并转换为 int。 + * 安全解析整数字面量字符串,自动去除单字符类型后缀(b/s/l/f/d,大小写均可),并转换为 int。 + * + * @param literal 字面量字符串 + * @return 字面量对应的 int 数值 + * @throws NumberFormatException 如果字面量无法转换为整数 */ public static int parseIntSafely(String literal) { String digits = literal.replaceAll("[bslfdBSDLF]$", ""); @@ -46,8 +59,14 @@ public final class ExpressionUtils { } /** - * 根据数字字面量字符串自动判断类型,生成对应类型的 {@link IRConstant}。 - * 支持 b/s/l/f/d 后缀与浮点格式。 + * 根据数字字面量字符串推断类型并生成对应的 IRConstant 常量值。 + *

+ * 支持的字面量后缀有 b/s/l/f/d(大小写均可)。 + * 无后缀时,优先参考 IRContext 当前变量类型,否则根据字面量格式(含'.'或'e'等)判断为 double,否则为 int。 + * + * @param ctx IRContext,允许参考变量声明类型 + * @param value 数字字面量字符串 + * @return 对应类型的 IRConstant 常量 */ public static IRConstant buildNumberConstant(IRContext ctx, String value) { char suffix = value.isEmpty() ? '\0' @@ -56,7 +75,7 @@ public final class ExpressionUtils { String digits = switch (suffix) { case 'b','s','l','f','d' -> value.substring(0, value.length() - 1); default -> { - /* 如果字面量本身没有后缀,则回退到变量目标类型(如声明语句左值) */ + // 无后缀,优先参考变量类型 if (ctx.getVarType() != null) { String t = ctx.getVarType(); suffix = switch (t) { @@ -73,7 +92,7 @@ public final class ExpressionUtils { } }; - /* 创建常量 */ + // 生成常量对象 return switch (suffix) { case 'b' -> new IRConstant(Byte.parseByte(digits)); case 's' -> new IRConstant(Short.parseShort(digits)); @@ -86,10 +105,13 @@ public final class ExpressionUtils { }; } - /* ────────────────────── 一元运算 ────────────────────── */ + // ───────────── 一元运算指令匹配 ───────────── /** - * 推断一元取负(-)运算应使用的 {@link IROpCode}。 + * 根据表达式节点的类型后缀,选择对应的取负(-)运算操作码。 + * + * @param operand 操作数表达式 + * @return 匹配类型的 IROpCode */ public static IROpCode negOp(ExpressionNode operand) { char t = typeChar(operand); @@ -99,34 +121,54 @@ public final class ExpressionUtils { case 'l' -> IROpCode.NEG_L64; case 'f' -> IROpCode.NEG_F32; case 'd' -> IROpCode.NEG_D64; - default -> IROpCode.NEG_I32; // '\0' 或 'i' + default -> IROpCode.NEG_I32; // 无法推断或为 int }; } - /* ────────────────── 比较运算(已适配 long) ────────────────── */ + // ───────────── 比较运算相关 ───────────── - /** 判断给定字符串是否是比较运算符(==, !=, <, >, <=, >=)。 */ + /** + * 判断给定字符串是否为支持的比较运算符(==, !=, <, >, <=, >=)。 + * + * @param op 操作符字符串 + * @return 若为比较运算符返回 true,否则返回 false + */ public static boolean isComparisonOperator(String op) { return ComparisonUtils.isComparisonOperator(op); } /** - * 兼容旧调用:仅凭操作符返回 int32 比较指令。 + * 兼容旧逻辑:仅凭操作符直接返回 int32 比较指令。 + * + * @param op 比较操作符 + * @return int32 类型的比较操作码 */ public static IROpCode cmpOp(String op) { - return IROpCodeMappings.CMP_I32.get(op); // 旧逻辑:一律 i32 + return IROpCodeMappings.CMP_I32.get(op); } /** - * 推荐调用:根据左右表达式类型自动选择 int / long 比较指令。 + * 推荐调用:根据左右表达式类型自动选择 int/long/float/double 等合适的比较操作码。 + * + * @param variables 变量名到类型的映射 + * @param op 比较符号 + * @param left 左操作数表达式 + * @param right 右操作数表达式 + * @return 匹配类型的比较操作码 */ public static IROpCode cmpOp(Map variables, String op, ExpressionNode left, ExpressionNode right) { return ComparisonUtils.cmpOp(variables, op, left, right); } - /* ──────────────── 类型推断 & 算术操作码匹配 ──────────────── */ + // ───────────── 类型推断与算术操作码匹配 ───────────── - /** 递归推断单个表达式节点的类型后缀(b/s/i/l/f/d)。 */ + /** + * 递归推断表达式节点的类型后缀(b/s/i/l/f/d)。 + * 优先从字面量和二元表达式合并类型,变量节点暂不处理,返回 '\0'。 + * + * @param node 表达式节点 + * @return 推断的类型后缀(小写),不确定时返回 '\0' + */ private static char typeChar(ExpressionNode node) { if (node instanceof NumberLiteralNode(String value, NodeContext _)) { char last = Character.toLowerCase(value.charAt(value.length() - 1)); @@ -141,12 +183,25 @@ public final class ExpressionUtils { return '\0'; // 变量等暂不处理 } - /** 合并两侧表达式的类型后缀。 */ + /** + * 合并两个表达式节点的类型后缀,按照 d > f > l > i > s > b > '\0' 优先级返回最宽类型。 + * + * @param left 左表达式 + * @param right 右表达式 + * @return 合并后的类型后缀 + */ public static char resolveSuffix(ExpressionNode left, ExpressionNode right) { return maxTypeChar(typeChar(left), typeChar(right)); } - /** 类型优先级:d > f > l > i > s > b > '\0' */ + /** + * 返回两个类型后缀中的最大类型(宽度优先)。 + * 优先级:d > f > l > i > s > b > '\0' + * + * @param l 类型后缀1 + * @param r 类型后缀2 + * @return 最宽类型后缀 + */ private static char maxTypeChar(char l, char r) { if (l == 'd' || r == 'd') return 'd'; if (l == 'f' || r == 'f') return 'f'; @@ -158,35 +213,42 @@ public final class ExpressionUtils { } /** - * 根据操作符和两侧表达式选择正确的算术 {@link IROpCode}。 + * 根据操作符和两侧表达式,选择匹配的算术操作码(IROpCode)。 + *

+ * 类型推断优先使用左右表达式的类型后缀,推断失败时回退为线程级默认类型后缀,再失败则默认为 int32。 + * + * @param op 算术操作符 + * @param left 左表达式 + * @param right 右表达式 + * @return 匹配类型的 IROpCode */ public static IROpCode resolveOpCode(String op, ExpressionNode left, ExpressionNode right) { - - /* 1. 尝试根据字面量推断 */ + // 1. 优先根据表达式类型推断 char suffix = resolveSuffix(left, right); - - /* 2. 若失败则使用函数级默认类型 */ + // 2. 推断失败则使用线程默认类型 if (suffix == '\0') suffix = DEFAULT_SUFFIX.get(); - - /* 3. 仍失败则默认为 int32 */ + // 3. 仍失败则默认为 int32 Map table = switch (suffix) { case 'b' -> IROpCodeMappings.OP_B8; case 's' -> IROpCodeMappings.OP_S16; - case 'i' -> IROpCodeMappings.OP_I32; case 'l' -> IROpCodeMappings.OP_L64; case 'f' -> IROpCodeMappings.OP_F32; case 'd' -> IROpCodeMappings.OP_D64; default -> IROpCodeMappings.OP_I32; }; - return table.get(op); } - /* ────────────────────────── 工具 ───────────────────────── */ + // ───────────── 字符串辅助工具 ───────────── - /** 是否像浮点字面量(包含 '.' 或 e/E)。 */ + /** + * 判断字面量字符串是否看起来像浮点数(包含小数点或 e/E 科学计数法)。 + * + * @param digits 字面量字符串 + * @return 是浮点格式则返回 true + */ private static boolean looksLikeFloat(String digits) { return digits.indexOf('.') >= 0 || digits.indexOf('e') >= 0