自动转换为string
This commit is contained in:
parent
379d1adfc7
commit
622d78b63c
@ -11,34 +11,9 @@ import org.jcnc.snow.compiler.semantic.type.BuiltinType;
|
||||
import org.jcnc.snow.compiler.semantic.type.Type;
|
||||
|
||||
/**
|
||||
* 二元表达式分析器:负责对形如 <code>left op right</code> 的表达式进行类型推导和类型检查。
|
||||
* <p>
|
||||
* 支持以下运算符:
|
||||
* <ul>
|
||||
* <li><code>+</code>:如果任一操作数为 {@link BuiltinType#STRING},则结果为字符串拼接,否则如果都是整数则结果为整数</li>
|
||||
* <li><code>-</code>, <code>*</code>, <code>/</code>, <code>%</code>:要求两个操作数都是整数,结果为整数</li>
|
||||
* <li><code><</code>, <code><=</code>, <code>></code>, <code>>=</code>, <code>==</code>, <code>!=</code>:
|
||||
* 要求两个操作数都是整数,结果为整数(表示真假)</li>
|
||||
* </ul>
|
||||
* 对于未知运算符或类型不匹配的情况,会在 {@link Context#getErrors() 错误列表} 中加入对应的
|
||||
* {@link SemanticError},并将结果类型降级为 {@link BuiltinType#INT} 以避免后续连锁错误。
|
||||
* 二元表达式分析器:支持数值宽化转换及字符串拼接。
|
||||
*/
|
||||
public class BinaryExpressionAnalyzer implements ExpressionAnalyzer<BinaryExpressionNode> {
|
||||
|
||||
/**
|
||||
* 对给定的二元表达式节点进行语义分析,返回推导出的类型。
|
||||
*
|
||||
* @param ctx 全局上下文,包含模块表、错误收集、日志开关、注册表等
|
||||
* @param mi 当前正在分析的模块信息,用于查找导入、函数签名等
|
||||
* @param fn 当前正在分析的函数节点,用于处理返回类型检查等(可用于更复杂场景)
|
||||
* @param locals 当前作用域的符号表,包含已定义的局部变量及其类型
|
||||
* @param bin 待分析的 {@link BinaryExpressionNode} 节点,包含左子表达式、运算符、右子表达式
|
||||
* @return 推导出的 {@link Type}:
|
||||
* <ul>
|
||||
* <li>符合运算规则时返回 {@link BuiltinType#INT} 或 {@link BuiltinType#STRING}</li>
|
||||
* <li>类型不匹配或未知运算符时降级返回 {@link BuiltinType#INT}</li>
|
||||
* </ul>
|
||||
*/
|
||||
@Override
|
||||
public Type analyze(Context ctx,
|
||||
ModuleInfo mi,
|
||||
@ -47,64 +22,40 @@ public class BinaryExpressionAnalyzer implements ExpressionAnalyzer<BinaryExpres
|
||||
BinaryExpressionNode bin) {
|
||||
ctx.log("检查二元表达式: " + bin.operator());
|
||||
|
||||
// 递归分析左右两边的子表达式
|
||||
var leftAnalyzer = ctx.getRegistry().getExpressionAnalyzer(bin.left());
|
||||
Type left = leftAnalyzer.analyze(ctx, mi, fn, locals, bin.left());
|
||||
|
||||
var rightAnalyzer = ctx.getRegistry().getExpressionAnalyzer(bin.right());
|
||||
Type right = rightAnalyzer.analyze(ctx, mi, fn, locals, bin.right());
|
||||
|
||||
// 递归获取左右子表达式类型
|
||||
Type left = ctx.getRegistry().getExpressionAnalyzer(bin.left())
|
||||
.analyze(ctx, mi, fn, locals, bin.left());
|
||||
Type right = ctx.getRegistry().getExpressionAnalyzer(bin.right())
|
||||
.analyze(ctx, mi, fn, locals, bin.right());
|
||||
String op = bin.operator();
|
||||
Type result;
|
||||
|
||||
// 根据运算符做类型推导
|
||||
switch (op) {
|
||||
case "+" -> {
|
||||
// 字符串拼接 或 整数相加
|
||||
if (left == BuiltinType.STRING || right == BuiltinType.STRING) {
|
||||
result = BuiltinType.STRING;
|
||||
} else if (left == BuiltinType.INT && right == BuiltinType.INT) {
|
||||
result = BuiltinType.INT;
|
||||
} else {
|
||||
result = null;
|
||||
// 字符串拼接
|
||||
if (op.equals("+") &&
|
||||
(left == BuiltinType.STRING || right == BuiltinType.STRING)) {
|
||||
return BuiltinType.STRING;
|
||||
}
|
||||
|
||||
// 数值运算或比较
|
||||
if ("+-*/%".contains(op) ||
|
||||
("<<=>>===!=").contains(op)) {
|
||||
if (left.isNumeric() && right.isNumeric()) {
|
||||
// 自动宽化到更宽的数值类型
|
||||
Type wide = Type.widen(left, right);
|
||||
if (wide == null) wide = BuiltinType.INT; // 容错降级
|
||||
// 比较运算返回 int
|
||||
if ("< <= > >= == !=".contains(op)) {
|
||||
return BuiltinType.INT;
|
||||
}
|
||||
}
|
||||
case "-", "*", "/", "%" -> {
|
||||
// 数学运算,要求整数操作数
|
||||
if (left == BuiltinType.INT && right == BuiltinType.INT) {
|
||||
result = BuiltinType.INT;
|
||||
} else {
|
||||
result = null;
|
||||
}
|
||||
}
|
||||
case "<", "<=", ">", ">=", "==", "!=" -> {
|
||||
// 比较运算,要求整数操作数,返回整数表示真假
|
||||
if (left == BuiltinType.INT && right == BuiltinType.INT) {
|
||||
result = BuiltinType.INT;
|
||||
} else {
|
||||
result = null;
|
||||
}
|
||||
}
|
||||
default -> {
|
||||
// 未知运算符
|
||||
ctx.getErrors().add(new SemanticError(bin, "未知运算符: " + op));
|
||||
ctx.log("错误: 未知运算符 " + op);
|
||||
return BuiltinType.INT;
|
||||
return wide;
|
||||
}
|
||||
}
|
||||
|
||||
// 如果推导失败(类型不匹配),记录错误并降级
|
||||
if (result == null) {
|
||||
ctx.getErrors().add(new SemanticError(
|
||||
bin,
|
||||
String.format("运算符 '%s' 不支持类型: %s 和 %s", op, left, right))
|
||||
);
|
||||
ctx.log("错误: 运算符 '" + op + "' 不支持类型: " + left + ", " + right);
|
||||
result = BuiltinType.INT;
|
||||
} else {
|
||||
ctx.log("二元表达式推导类型: " + result);
|
||||
}
|
||||
|
||||
return result;
|
||||
// 未知或不支持的运算符/类型
|
||||
ctx.getErrors().add(new SemanticError(
|
||||
bin,
|
||||
String.format("运算符 '%s' 不支持类型: %s 和 %s", op, left, right)
|
||||
));
|
||||
ctx.log("错误: 运算符 '" + op + "' 不支持类型: " + left + ", " + right);
|
||||
return BuiltinType.INT;
|
||||
}
|
||||
}
|
||||
|
||||
@ -1,9 +1,6 @@
|
||||
package org.jcnc.snow.compiler.semantic.analyzers.expression;
|
||||
|
||||
import org.jcnc.snow.compiler.parser.ast.CallExpressionNode;
|
||||
import org.jcnc.snow.compiler.parser.ast.FunctionNode;
|
||||
import org.jcnc.snow.compiler.parser.ast.IdentifierNode;
|
||||
import org.jcnc.snow.compiler.parser.ast.MemberExpressionNode;
|
||||
import org.jcnc.snow.compiler.parser.ast.*;
|
||||
import org.jcnc.snow.compiler.parser.ast.base.ExpressionNode;
|
||||
import org.jcnc.snow.compiler.semantic.analyzers.base.ExpressionAnalyzer;
|
||||
import org.jcnc.snow.compiler.semantic.core.Context;
|
||||
@ -19,38 +16,13 @@ import java.util.List;
|
||||
|
||||
/**
|
||||
* 函数调用表达式分析器:负责对 <code>callee(arg1, arg2, ...)</code> 形式的调用进行
|
||||
* 语义检查和类型推导。
|
||||
* <p>
|
||||
* 支持两种调用方式:
|
||||
* 语义检查和类型推导,并支持:
|
||||
* <ul>
|
||||
* <li>模块函数调用:<code>ModuleName.func(...)</code>,要求模块已注册且已导入,或与当前模块同名</li>
|
||||
* <li>本模块函数调用:<code>func(...)</code></li>
|
||||
* <li>数值到字符串的隐式转换(自动插入 to_string);</li>
|
||||
* <li>数值参数的宽化转换(如 int → double)。</li>
|
||||
* </ul>
|
||||
* <p>
|
||||
* 分析内容包括:
|
||||
* <ol>
|
||||
* <li>解析目标模块与函数名;</li>
|
||||
* <li>检查函数是否已定义;</li>
|
||||
* <li>递归分析并收集所有实参的类型;</li>
|
||||
* <li>检查参数个数与参数类型是否与函数签名匹配;</li>
|
||||
* <li>返回函数签名中的返回类型;</li>
|
||||
* <li>在任何错误(未知模块、未导入模块、调用方式不支持、函数未定义、
|
||||
* 参数数量或类型不匹配)时,记录对应的 {@link SemanticError},
|
||||
* 并降级返回 {@link BuiltinType#INT} 以避免后续连锁错误。</li>
|
||||
* </ol>
|
||||
*/
|
||||
public class CallExpressionAnalyzer implements ExpressionAnalyzer<CallExpressionNode> {
|
||||
|
||||
/**
|
||||
* 对给定的调用表达式节点进行语义分析,返回推导出的类型。
|
||||
*
|
||||
* @param ctx 全局上下文,包含模块表、错误收集、日志输出、分析器注册表等
|
||||
* @param mi 当前正在分析的模块信息,用于查找导入列表和函数签名
|
||||
* @param fn 当前正在分析的函数节点,可用于更复杂的上下文校验
|
||||
* @param locals 当前作用域的符号表,包含已定义的变量及其类型
|
||||
* @param call 待分析的 {@link CallExpressionNode},包含被调用的表达式和实参列表
|
||||
* @return 推导出的函数返回类型;若发生任何错误,则降级返回 {@link BuiltinType#INT}
|
||||
*/
|
||||
@Override
|
||||
public Type analyze(Context ctx,
|
||||
ModuleInfo mi,
|
||||
@ -58,35 +30,26 @@ public class CallExpressionAnalyzer implements ExpressionAnalyzer<CallExpression
|
||||
SymbolTable locals,
|
||||
CallExpressionNode call) {
|
||||
ctx.log("检查函数调用: " + call.callee());
|
||||
|
||||
ModuleInfo target = mi;
|
||||
String functionName;
|
||||
ExpressionNode callee = call.callee();
|
||||
|
||||
// 处理模块限定调用:ModuleName.func(...)
|
||||
if (callee instanceof MemberExpressionNode(var object, String member)) {
|
||||
if (object instanceof IdentifierNode(String name)) {
|
||||
if (!ctx.getModules().containsKey(name)
|
||||
|| (!mi.getImports().contains(name) && !mi.getName().equals(name))) {
|
||||
ctx.getErrors().add(new SemanticError(callee,
|
||||
"未知或未导入模块: " + name));
|
||||
ctx.log("错误: 未导入模块 " + name);
|
||||
return BuiltinType.INT;
|
||||
}
|
||||
target = ctx.getModules().get(name);
|
||||
functionName = member;
|
||||
ctx.log("调用模块函数: " + name + "." + member);
|
||||
} else {
|
||||
// 解析 callee:支持 Module.func(...) 与 本模块 func(...)
|
||||
ExpressionNode callee = call.callee();
|
||||
if (callee instanceof MemberExpressionNode(var obj, String member)
|
||||
&& obj instanceof IdentifierNode(String mod)) {
|
||||
if (!ctx.getModules().containsKey(mod)
|
||||
|| (!mi.getImports().contains(mod) && !mi.getName().equals(mod))) {
|
||||
ctx.getErrors().add(new SemanticError(callee,
|
||||
"不支持的调用方式: " + callee));
|
||||
ctx.log("错误: 不支持的调用方式 " + callee);
|
||||
"未知或未导入模块: " + mod));
|
||||
ctx.log("错误: 未导入模块 " + mod);
|
||||
return BuiltinType.INT;
|
||||
}
|
||||
target = ctx.getModules().get(mod);
|
||||
functionName = member;
|
||||
|
||||
// 处理本模块函数调用:func(...)
|
||||
} else if (callee instanceof IdentifierNode(String name)) {
|
||||
functionName = name;
|
||||
ctx.log("调用当前模块函数: " + functionName);
|
||||
|
||||
} else {
|
||||
ctx.getErrors().add(new SemanticError(callee,
|
||||
"不支持的调用方式: " + callee));
|
||||
@ -105,38 +68,46 @@ public class CallExpressionAnalyzer implements ExpressionAnalyzer<CallExpression
|
||||
|
||||
// 分析所有实参并收集类型
|
||||
List<Type> args = new ArrayList<>();
|
||||
for (var argExpr : call.arguments()) {
|
||||
var argAnalyzer = ctx.getRegistry().getExpressionAnalyzer(argExpr);
|
||||
args.add(argAnalyzer.analyze(ctx, mi, fn, locals, argExpr));
|
||||
for (ExpressionNode arg : call.arguments()) {
|
||||
args.add(ctx.getRegistry().getExpressionAnalyzer(arg)
|
||||
.analyze(ctx, mi, fn, locals, arg));
|
||||
}
|
||||
|
||||
// 参数数量检查
|
||||
// 参数检查(数量 + 类型兼容 / 宽化 / 数值->字符串隐式转换)
|
||||
if (args.size() != ft.paramTypes().size()) {
|
||||
ctx.getErrors().add(new SemanticError(call,
|
||||
"参数数量不匹配: 期望 "
|
||||
+ ft.paramTypes().size()
|
||||
+ " 个, 实际 "
|
||||
+ args.size()
|
||||
+ " 个"));
|
||||
"参数数量不匹配: 期望 " + ft.paramTypes().size()
|
||||
+ " 个, 实际 " + args.size() + " 个"));
|
||||
ctx.log("错误: 参数数量不匹配: 期望 "
|
||||
+ ft.paramTypes().size()
|
||||
+ ", 实际 "
|
||||
+ args.size());
|
||||
+ ft.paramTypes().size() + ", 实际 " + args.size());
|
||||
|
||||
} else {
|
||||
// 参数类型检查
|
||||
for (int i = 0; i < args.size(); i++) {
|
||||
Type expected = ft.paramTypes().get(i);
|
||||
Type actual = args.get(i);
|
||||
if (!expected.isCompatible(actual)) {
|
||||
Type actual = args.get(i);
|
||||
|
||||
// 完全兼容或宽化兼容
|
||||
boolean ok = expected.isCompatible(actual)
|
||||
|| (expected.isNumeric() && actual.isNumeric()
|
||||
&& Type.widen(actual, expected) == expected);
|
||||
|
||||
// 数值到字符串的隐式转换
|
||||
if (!ok
|
||||
&& expected == BuiltinType.STRING
|
||||
&& actual.isNumeric()) {
|
||||
ctx.log(String.format(
|
||||
"隐式将参数 %d 的数值类型 %s 转换为 string (to_string)",
|
||||
i, actual
|
||||
));
|
||||
ok = true;
|
||||
}
|
||||
|
||||
if (!ok) {
|
||||
ctx.getErrors().add(new SemanticError(call,
|
||||
String.format("参数类型不匹配 (位置 %d): 期望 %s, 实际 %s",
|
||||
i, expected, actual)));
|
||||
ctx.log("错误: 参数类型不匹配 (位置 "
|
||||
+ i
|
||||
+ "): 期望 "
|
||||
+ expected
|
||||
+ ", 实际 "
|
||||
+ actual);
|
||||
ctx.log("错误: 参数类型不匹配 (位置 " + i + "): 期望 "
|
||||
+ expected + ", 实际 " + actual);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -6,34 +6,13 @@ import org.jcnc.snow.compiler.semantic.analyzers.base.StatementAnalyzer;
|
||||
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.Symbol;
|
||||
import org.jcnc.snow.compiler.semantic.symbol.SymbolKind;
|
||||
import org.jcnc.snow.compiler.semantic.symbol.SymbolTable;
|
||||
import org.jcnc.snow.compiler.semantic.symbol.*;
|
||||
import org.jcnc.snow.compiler.semantic.type.Type;
|
||||
|
||||
/**
|
||||
* 赋值语句分析器:负责对 {@link AssignmentNode} 节点进行语义检查。
|
||||
* <p>
|
||||
* 分析步骤:
|
||||
* <ol>
|
||||
* <li>检查目标变量是否已在当前作用域中声明且种类为 {@link SymbolKind#VARIABLE};</li>
|
||||
* <li>递归分析赋值右侧表达式,获取其类型;</li>
|
||||
* <li>检查右侧表达式类型是否与目标变量类型兼容;</li>
|
||||
* <li>在任一检查失败时,向 {@link Context#getErrors() 错误列表} 中添加对应的
|
||||
* {@link SemanticError} 并记录日志。</li>
|
||||
* </ol>
|
||||
* 赋值语句分析器:支持数值宽化自动转换。
|
||||
*/
|
||||
public class AssignmentAnalyzer implements StatementAnalyzer<AssignmentNode> {
|
||||
|
||||
/**
|
||||
* 对给定的赋值语句节点进行语义分析。
|
||||
*
|
||||
* @param ctx 全局上下文,包含模块表、错误收集、日志输出和分析器注册表等
|
||||
* @param mi 当前模块信息,用于处理跨模块符号(此分析器不使用模块上下文)
|
||||
* @param fn 当前函数节点,可用于更复杂的上下文校验(此分析器不使用函数上下文)
|
||||
* @param locals 当前作用域的符号表,包含已声明的符号及其类型
|
||||
* @param asg 待分析的 {@link AssignmentNode},包含目标变量名和赋值表达式
|
||||
*/
|
||||
@Override
|
||||
public void analyze(Context ctx,
|
||||
ModuleInfo mi,
|
||||
@ -41,29 +20,26 @@ public class AssignmentAnalyzer implements StatementAnalyzer<AssignmentNode> {
|
||||
SymbolTable locals,
|
||||
AssignmentNode asg) {
|
||||
ctx.log("赋值检查: " + asg.variable());
|
||||
|
||||
// 1. 检查变量声明
|
||||
Symbol sym = locals.resolve(asg.variable());
|
||||
if (sym == null || sym.kind() != SymbolKind.VARIABLE) {
|
||||
ctx.getErrors().add(new SemanticError(
|
||||
asg,
|
||||
"未声明的变量: " + asg.variable()
|
||||
));
|
||||
ctx.getErrors().add(new SemanticError(asg,
|
||||
"未声明的变量: " + asg.variable()));
|
||||
ctx.log("错误: 未声明的变量 " + asg.variable());
|
||||
return;
|
||||
}
|
||||
|
||||
// 2. 分析赋值表达式
|
||||
var exprAnalyzer = ctx.getRegistry().getExpressionAnalyzer(asg.value());
|
||||
Type valType = exprAnalyzer.analyze(ctx, mi, fn, locals, asg.value());
|
||||
Type valType = ctx.getRegistry().getExpressionAnalyzer(asg.value())
|
||||
.analyze(ctx, mi, fn, locals, asg.value());
|
||||
|
||||
// 3. 类型兼容性检查
|
||||
if (!sym.type().isCompatible(valType)) {
|
||||
ctx.getErrors().add(new SemanticError(
|
||||
asg,
|
||||
"赋值类型不匹配: 期望 " + sym.type() + ", 实际 " + valType
|
||||
));
|
||||
ctx.log("错误: 赋值类型不匹配 " + asg.variable());
|
||||
// 数值宽化允许
|
||||
if (!(sym.type().isNumeric() && valType.isNumeric()
|
||||
&& Type.widen(valType, sym.type()) == sym.type())) {
|
||||
ctx.getErrors().add(new SemanticError(asg,
|
||||
"赋值类型不匹配: 期望 " + sym.type()
|
||||
+ ", 实际 " + valType));
|
||||
ctx.log("错误: 赋值类型不匹配 " + asg.variable());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -6,85 +6,54 @@ import org.jcnc.snow.compiler.semantic.analyzers.base.StatementAnalyzer;
|
||||
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.Symbol;
|
||||
import org.jcnc.snow.compiler.semantic.symbol.SymbolKind;
|
||||
import org.jcnc.snow.compiler.semantic.symbol.SymbolTable;
|
||||
import org.jcnc.snow.compiler.semantic.symbol.*;
|
||||
import org.jcnc.snow.compiler.semantic.type.BuiltinType;
|
||||
import org.jcnc.snow.compiler.semantic.type.Type;
|
||||
|
||||
/**
|
||||
* 变量声明语句分析器:负责对 {@link DeclarationNode} 节点进行语义检查。
|
||||
* <p>
|
||||
* 分析流程:
|
||||
* <ol>
|
||||
* <li>解析声明类型,如果未知则记录错误并降级为 {@link BuiltinType#INT};</li>
|
||||
* <li>在当前符号表中注册新变量,若名称已存在则记录重复声明错误;</li>
|
||||
* <li>如存在初始化表达式,递归分析表达式并检查其类型与声明类型的兼容性;</li>
|
||||
* <li>兼容性检查失败时记录类型不匹配错误。</li>
|
||||
* </ol>
|
||||
* 变量声明语句分析器:支持数值宽化自动转换和初始化检查。
|
||||
*/
|
||||
public class DeclarationAnalyzer implements StatementAnalyzer<DeclarationNode> {
|
||||
|
||||
/**
|
||||
* 对给定的声明节点进行语义分析。
|
||||
*
|
||||
* @param ctx 全局上下文,包含模块表、错误收集、日志输出和分析器注册表等
|
||||
* @param mi 当前模块信息(在声明分析中未直接使用,但保留以便扩展)
|
||||
* @param fn 当前函数节点(在声明分析中未直接使用,但可用于上下文检查)
|
||||
* @param locals 当前作用域的符号表,用于注册新变量及类型查询
|
||||
* @param decl 待分析的 {@link DeclarationNode},包含变量名、声明类型及可选初始化表达式
|
||||
*/
|
||||
@Override
|
||||
public void analyze(Context ctx,
|
||||
ModuleInfo mi,
|
||||
FunctionNode fn,
|
||||
SymbolTable locals,
|
||||
DeclarationNode decl) {
|
||||
// 1. 类型解析与降级
|
||||
Type varType = ctx.parseType(decl.getType());
|
||||
if (varType == null) {
|
||||
ctx.getErrors().add(new SemanticError(
|
||||
decl,
|
||||
"未知类型: " + decl.getType()
|
||||
));
|
||||
ctx.log("错误: 参数未知类型 " + decl.getType()
|
||||
+ " 在声明 " + decl.getName());
|
||||
ctx.getErrors().add(new SemanticError(decl,
|
||||
"未知类型: " + decl.getType()));
|
||||
ctx.log("错误: 未知类型 " + decl.getType()
|
||||
+ " 在声明 " + decl.getName());
|
||||
varType = BuiltinType.INT;
|
||||
}
|
||||
ctx.log("声明变量: " + decl.getName()
|
||||
+ " 类型: " + varType);
|
||||
+ " 类型: " + varType);
|
||||
|
||||
// 2. 注册新变量,检查重复声明
|
||||
if (!locals.define(new Symbol(
|
||||
decl.getName(),
|
||||
varType,
|
||||
SymbolKind.VARIABLE
|
||||
decl.getName(), varType, SymbolKind.VARIABLE
|
||||
))) {
|
||||
ctx.getErrors().add(new SemanticError(
|
||||
decl,
|
||||
"变量重复声明: " + decl.getName()
|
||||
));
|
||||
ctx.getErrors().add(new SemanticError(decl,
|
||||
"变量重复声明: " + decl.getName()));
|
||||
ctx.log("错误: 变量重复声明 " + decl.getName());
|
||||
}
|
||||
|
||||
// 3. 初始化表达式类型检查
|
||||
Type declaredType = varType;
|
||||
// 初始化表达式类型检查
|
||||
Type finalVarType = varType;
|
||||
decl.getInitializer().ifPresent(init -> {
|
||||
var exprAnalyzer = ctx.getRegistry()
|
||||
.getExpressionAnalyzer(init);
|
||||
Type initType = exprAnalyzer.analyze(
|
||||
ctx, mi, fn, locals, init
|
||||
);
|
||||
if (!declaredType.isCompatible(initType)) {
|
||||
ctx.getErrors().add(new SemanticError(
|
||||
decl,
|
||||
"初始化类型不匹配: 期望 "
|
||||
+ declaredType
|
||||
+ ", 实际 "
|
||||
+ initType
|
||||
));
|
||||
ctx.log("错误: 初始化类型不匹配 "
|
||||
Type initType = ctx.getRegistry().getExpressionAnalyzer(init)
|
||||
.analyze(ctx, mi, fn, locals, init);
|
||||
if (!finalVarType.isCompatible(initType)) {
|
||||
// 数值宽化允许
|
||||
if (!(finalVarType.isNumeric() && initType.isNumeric()
|
||||
&& Type.widen(initType, finalVarType) == finalVarType)) {
|
||||
ctx.getErrors().add(new SemanticError(decl,
|
||||
"初始化类型不匹配: 期望 " + finalVarType
|
||||
+ ", 实际 " + initType));
|
||||
ctx.log("错误: 初始化类型不匹配 "
|
||||
+ decl.getName());
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
@ -1,60 +1,73 @@
|
||||
package org.jcnc.snow.compiler.semantic.type;
|
||||
|
||||
/**
|
||||
* 内置基础类型枚举:定义编译器中常见的基础类型。
|
||||
* 内置基础类型枚举:支持多种数值类型以及字符串和 void。
|
||||
* <p>
|
||||
* 枚举值:
|
||||
* <ul>
|
||||
* <li>{@link #INT} – 整数类型(32 位);</li>
|
||||
* <li>{@link #LONG} – 长整数类型(64 位);</li>
|
||||
* <li>{@link #SHORT} – 短整数类型(16 位);</li>
|
||||
* <li>{@link #BYTE} – 字节类型(8 位);</li>
|
||||
* <li>{@link #FLOAT} – 单精度浮点数;</li>
|
||||
* <li>{@link #DOUBLE} – 双精度浮点数;</li>
|
||||
* <li>{@link #STRING} – 字符串类型;</li>
|
||||
* <li>{@link #VOID} – 空类型,用于表示无返回值的函数。</li>
|
||||
* <li>{@link #BYTE} – 8 位整数</li>
|
||||
* <li>{@link #SHORT} – 16 位整数</li>
|
||||
* <li>{@link #INT} – 32 位整数</li>
|
||||
* <li>{@link #LONG} – 64 位整数</li>
|
||||
* <li>{@link #FLOAT} – 单精度浮点数</li>
|
||||
* <li>{@link #DOUBLE} – 双精度浮点数</li>
|
||||
* <li>{@link #STRING} – 字符串类型</li>
|
||||
* <li>{@link #VOID} – 空类型,用于表示无返回值的函数</li>
|
||||
* </ul>
|
||||
* <p>
|
||||
* 本枚举实现了 {@link Type} 接口,提供了基本的类型兼容性判断和
|
||||
* 友好的字符串表示方法。
|
||||
* 本枚举实现了 {@link Type} 接口,提供了数值宽化和兼容性判断。
|
||||
*/
|
||||
public enum BuiltinType implements Type {
|
||||
/** 整数类型(32 位) */
|
||||
INT,
|
||||
/** 长整数类型(64 位) */
|
||||
LONG,
|
||||
/** 短整数类型(16 位) */
|
||||
SHORT,
|
||||
/** 字节类型(8 位) */
|
||||
BYTE,
|
||||
/** 单精度浮点数 */
|
||||
SHORT,
|
||||
INT,
|
||||
LONG,
|
||||
FLOAT,
|
||||
/** 双精度浮点数 */
|
||||
DOUBLE,
|
||||
/** 字符串类型 */
|
||||
STRING,
|
||||
/** 空类型,通常用于无返回值的函数 */
|
||||
VOID;
|
||||
|
||||
/**
|
||||
* 判断当前类型是否与另一个类型兼容。
|
||||
* <p>
|
||||
* 兼容条件:两个类型实例必须相同(同一枚举常量)。
|
||||
* 兼容条件:
|
||||
* <ul>
|
||||
* <li>完全相同;</li>
|
||||
* <li>对于数值类型,允许自动宽化转换(narrow → wide)。</li>
|
||||
* </ul>
|
||||
*
|
||||
* @param other 另一个需要检查兼容性的类型
|
||||
* @return 如果类型完全相同则返回 {@code true};否则返回 {@code false}
|
||||
* @param other 另一个要检查的类型
|
||||
* @return 如果兼容返回 true,否则 false
|
||||
*/
|
||||
@Override
|
||||
public boolean isCompatible(Type other) {
|
||||
return this == other;
|
||||
if (this == other) return true;
|
||||
// 如果两者都是数值类型,允许宽化
|
||||
if (this.isNumeric() && other.isNumeric()) {
|
||||
return Type.widen(other, this) == this;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* 返回小写形式的类型名称,便于输出和日志展示。
|
||||
* <p>
|
||||
* 例如:{@link #INT} 返回 {@code "int"};{@link #VOID} 返回 {@code "void"}。
|
||||
* 判断是否为数值类型(byte、short、int、long、float、double)。
|
||||
*
|
||||
* @return 当前类型的小写字符串表示
|
||||
* @return 如果是数值类型返回 true,否则 false
|
||||
*/
|
||||
@Override
|
||||
public boolean isNumeric() {
|
||||
switch (this) {
|
||||
case BYTE, SHORT, INT, LONG, FLOAT, DOUBLE:
|
||||
return true;
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 小写形式的类型名称,便于日志和错误输出。
|
||||
*
|
||||
* @return 小写的类型名称
|
||||
*/
|
||||
@Override
|
||||
public String toString() {
|
||||
|
||||
@ -1,3 +1,4 @@
|
||||
// File: src/main/java/org/jcnc/snow/compiler/semantic/type/Type.java
|
||||
package org.jcnc.snow.compiler.semantic.type;
|
||||
|
||||
/**
|
||||
@ -7,16 +8,44 @@ package org.jcnc.snow.compiler.semantic.type;
|
||||
public interface Type {
|
||||
/**
|
||||
* 判断当前类型是否与另一个类型兼容。
|
||||
* <p>
|
||||
* 例如:
|
||||
* <ul>
|
||||
* <li>对于内置类型,只有完全相同的枚举常量才兼容;</li>
|
||||
* <li>对于函数类型,需要参数列表和返回类型同时兼容;</li>
|
||||
* <li>对于其他复合类型,可按语言规则自行实现。</li>
|
||||
* </ul>
|
||||
*
|
||||
* @param other 要检查兼容性的另一个类型
|
||||
* @return 如果兼容则返回 {@code true},否则返回 {@code false}
|
||||
* @return 如果兼容则返回 true,否则返回 false
|
||||
*/
|
||||
boolean isCompatible(Type other);
|
||||
|
||||
/**
|
||||
* 判断当前类型是否为数值类型(byte/short/int/long/float/double)。
|
||||
* <p>
|
||||
* 默认实现返回 false,BuiltinType 会覆盖此方法。
|
||||
*
|
||||
* @return 如果是数值类型则返回 true,否则返回 false
|
||||
*/
|
||||
default boolean isNumeric() {
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* 对两个数值类型执行宽化转换,返回“更宽”的那个类型。
|
||||
* <p>
|
||||
* 若 a 和 b 都是数值类型,则按 byte→short→int→long→float→double 顺序选更宽的类型;
|
||||
* 否则返回 null。
|
||||
*
|
||||
* @param a 第一个类型
|
||||
* @param b 第二个类型
|
||||
* @return 两者中更宽的数值类型,或 null
|
||||
*/
|
||||
static Type widen(Type a, Type b) {
|
||||
if (!(a.isNumeric() && b.isNumeric())) return null;
|
||||
var order = java.util.List.of(
|
||||
BuiltinType.BYTE,
|
||||
BuiltinType.SHORT,
|
||||
BuiltinType.INT,
|
||||
BuiltinType.LONG,
|
||||
BuiltinType.FLOAT,
|
||||
BuiltinType.DOUBLE
|
||||
);
|
||||
int ia = order.indexOf(a), ib = order.indexOf(b);
|
||||
return order.get(Math.max(ia, ib));
|
||||
}
|
||||
}
|
||||
|
||||
6
test
6
test
@ -4,7 +4,7 @@ module: CommonTasks
|
||||
return num1 + num2
|
||||
end body
|
||||
parameter:
|
||||
declare num1: double
|
||||
declare num1: int
|
||||
declare num2: int
|
||||
return_type: int
|
||||
end function
|
||||
@ -93,7 +93,9 @@ module: MainModule
|
||||
|
||||
declare sum: int = CommonTasks.add_numbers(i, 10)
|
||||
declare squared: int = MathUtils.square_number(sum)
|
||||
BuiltinUtils.print(" i+10 squared = " + BuiltinUtils.to_string(squared))
|
||||
BuiltinUtils.print("i+10 squared = " + BuiltinUtils.to_string(squared))
|
||||
BuiltinUtils.print(1+1.1)
|
||||
|
||||
end body
|
||||
end loop
|
||||
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user