docs: 优化 ExpressionUtils 的代码注释

This commit is contained in:
Luke 2025-07-11 10:04:15 +08:00
parent 099f4f934c
commit e7d7e7f96f

View File

@ -12,33 +12,46 @@ import org.jcnc.snow.compiler.parser.ast.base.NodeContext;
import java.util.Map;
/**
* 表达式分析与运算符辅助工具类
*
* <p>主要功能</p>
* <ul>
* <li>字面量常量的解析与类型推断</li>
* <li>自动匹配算术/比较操作码</li>
* <li>表达式类型合并与提升</li>
* </ul>
* 表达式分析与操作符选择工具类
* <p>
* 主要功能
* - 解析字面量常量自动推断类型
* - 自动匹配并选择适合的算术/比较操作码
* - 表达式类型的合并与类型提升
* - 支持线程隔离的函数级默认类型后缀
*/
public final class ExpressionUtils {
private ExpressionUtils() {}
/* ────────────────── 线程级默认类型后缀 ────────────────── */
// 线程级默认类型后缀
/** 默认类型后缀(如当前函数返回类型),线程隔离。 */
/**
* 当前线程的默认类型后缀如当前函数返回类型等用于类型推断兜底
*/
private static final ThreadLocal<Character> 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 常量值
* <p>
* 支持的字面量后缀有 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);
}
/**
* 兼容旧调用仅凭操作符返回 <em>int32</em> 比较指令
* 兼容旧逻辑仅凭操作符直接返回 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<String, String> 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
* <p>
* 类型推断优先使用左右表达式的类型后缀推断失败时回退为线程级默认类型后缀再失败则默认为 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<String, IROpCode> 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