+ * 用于存储所有支持的中缀表达式解析器,如二元运算、函数调用、下标、成员访问等。
+ * 仅通过词素索引(如 "+", "-", "(", "[" 等)。
+ *
+ */
private static final Map infixes = new HashMap<>();
static {
- // 前缀解析器注册
+ // -------- 前缀解析器注册 --------
+ // 注册数字字面量解析
prefixes.put(TokenType.NUMBER_LITERAL.name(), new NumberLiteralParselet());
+ // 注册标识符(变量名)解析
prefixes.put(TokenType.IDENTIFIER.name(), new IdentifierParselet());
- prefixes.put(TokenType.LPAREN.name(), new GroupingParselet());
+ // 注册字符串字面量解析
prefixes.put(TokenType.STRING_LITERAL.name(), new StringLiteralParselet());
+ // 注册布尔字面量解析
prefixes.put(TokenType.BOOL_LITERAL.name(), new BoolLiteralParselet());
- // 一元前缀运算符
+ // 支持括号分组、数组字面量
+ prefixes.put(TokenType.LPAREN.name(), new GroupingParselet());
+ prefixes.put(TokenType.LBRACKET.name(), new ArrayLiteralParselet());
+ // 兼容直接以词素注册(如 '(' '[')
+ prefixes.put("(", new GroupingParselet());
+ prefixes.put("[", new ArrayLiteralParselet());
+
+ // 一元前缀运算符(负号、逻辑非)
prefixes.put(TokenType.MINUS.name(), new UnaryOperatorParselet());
prefixes.put(TokenType.NOT.name(), new UnaryOperatorParselet());
+ prefixes.put("-", new UnaryOperatorParselet());
+ prefixes.put("!", new UnaryOperatorParselet());
- // 中缀解析器注册
+ // -------- 中缀解析器注册 --------
+ // 注册常见二元运算符(加减乘除、取模)
infixes.put("+", new BinaryOperatorParselet(Precedence.SUM, true));
infixes.put("-", new BinaryOperatorParselet(Precedence.SUM, true));
infixes.put("*", new BinaryOperatorParselet(Precedence.PRODUCT, true));
infixes.put("/", new BinaryOperatorParselet(Precedence.PRODUCT, true));
infixes.put("%", new BinaryOperatorParselet(Precedence.PRODUCT, true));
- infixes.put(">", new BinaryOperatorParselet(Precedence.SUM, true));
- infixes.put("<", new BinaryOperatorParselet(Precedence.SUM, true));
- infixes.put("==", new BinaryOperatorParselet(Precedence.SUM, true));
- infixes.put("!=", new BinaryOperatorParselet(Precedence.SUM, true));
- infixes.put(">=", new BinaryOperatorParselet(Precedence.SUM, true));
- infixes.put("<=", new BinaryOperatorParselet(Precedence.SUM, true));
+ // 比较运算符
+ infixes.put(">", new BinaryOperatorParselet(Precedence.COMPARISON, true));
+ infixes.put("<", new BinaryOperatorParselet(Precedence.COMPARISON, true));
+ infixes.put(">=", new BinaryOperatorParselet(Precedence.COMPARISON, true));
+ infixes.put("<=", new BinaryOperatorParselet(Precedence.COMPARISON, true));
+ // 相等性
+ infixes.put("==", new BinaryOperatorParselet(Precedence.EQUALITY, true));
+ infixes.put("!=", new BinaryOperatorParselet(Precedence.EQUALITY, true));
+ // 逻辑与或
+ infixes.put("&&", new BinaryOperatorParselet(Precedence.AND, true));
+ infixes.put("||", new BinaryOperatorParselet(Precedence.OR, true));
+ // 调用、索引、成员访问
infixes.put("(", new CallParselet());
+ infixes.put("[", new IndexParselet());
infixes.put(".", new MemberParselet());
}
/**
- * 表达式解析统一入口。
- * 以最低优先级启动递归下降,适配任意表达式复杂度。
+ * 解析任意表达式的统一入口。
+ *
+ * 该方法将以最低优先级启动表达式递归解析,能够自动适配和处理多层嵌套或复杂组合表达式。
+ *
*
- * @param ctx 当前解析上下文
- * @return 解析后的表达式 AST 节点
+ * @param ctx 当前解析上下文对象(持有 token 流等信息)
+ * @return 解析得到的表达式 AST 节点对象
*/
@Override
public ExpressionNode parse(ParserContext ctx) {
@@ -71,21 +106,32 @@ public class PrattExpressionParser implements ExpressionParser {
}
/**
- * 按指定优先级解析表达式。Pratt 算法主循环。
+ * 按指定优先级解析表达式(Pratt 算法核心)。
*
- * 先根据当前 Token 类型查找前缀解析器进行初始解析,
- * 然后根据优先级不断递归处理中缀运算符和右侧表达式。
+ * 1. 先取当前 token,查找对应的前缀解析器进行初始解析,构建表达式左侧(如字面量、变量等)。
+ * 2. 然后循环检测是否有更高优先级的中缀操作符,
+ * 若有则递归处理右侧表达式并组合为新的表达式节点。
+ *
+ *
+ * 未找到对应前缀或中缀解析器时会抛出 {@link UnsupportedFeature} 异常。
*
*
* @param ctx 解析上下文
- * @param prec 当前运算符优先级阈值
- * @return 构建完成的表达式节点
- * @throws UnsupportedFeature 若遇到未注册的前缀或中缀解析器
+ * @param prec 当前运算符优先级(用于控制递归层级)
+ * @return 解析构建好的表达式节点
+ * @throws UnsupportedFeature 遇到未注册的解析器时抛出
*/
ExpressionNode parseExpression(ParserContext ctx, Precedence prec) {
+ // 取下一个 token 作为本轮前缀表达式起始
Token token = ctx.getTokens().next();
+
+ // 查找前缀解析器(先按类型名,再按词素)
PrefixParselet prefix = prefixes.get(token.getType().name());
if (prefix == null) {
+ prefix = prefixes.get(token.getLexeme());
+ }
+ if (prefix == null) {
+ // 未找到前缀解析器则报错
throw new UnsupportedFeature(
"没有为该 Token 类型注册前缀解析器: " + token.getType(),
token.getLine(),
@@ -93,13 +139,17 @@ public class PrattExpressionParser implements ExpressionParser {
);
}
+ // 执行前缀解析,获得左侧表达式
ExpressionNode left = prefix.parse(ctx, token);
+ // 不断尝试查找优先级更高的中缀运算符,递归处理表达式链
while (!ctx.getTokens().isAtEnd()
&& prec.ordinal() < nextPrecedence(ctx)) {
+ // 查看下一个 token 词素,查找中缀解析器
String lex = ctx.getTokens().peek().getLexeme();
InfixParselet infix = infixes.get(lex);
if (infix == null) {
+ // 若未注册中缀解析器,则直接抛异常(常见于语法错误)
Token t = ctx.getTokens().peek();
throw new UnsupportedFeature(
"没有为该运算符注册中缀解析器: '" + lex + "'",
@@ -107,16 +157,21 @@ public class PrattExpressionParser implements ExpressionParser {
t.getCol()
);
}
+ // 使用中缀解析器处理表达式组合
left = infix.parse(ctx, left);
}
+ // 返回本层递归已解析的表达式节点
return left;
}
/**
- * 获取下一个中缀解析器的优先级(Pratt 算法核心)。
+ * 获取下一个 token 词素对应的中缀运算符优先级(Pratt 算法关键)。
+ *
+ * 用于决定当前是否需要递归处理更高优先级的中缀操作。
+ *
*
* @param ctx 当前解析上下文
- * @return 下一个中缀运算符的优先级序号;若无解析器则为 -1
+ * @return 下一个中缀运算符的优先级序号;若无注册解析器则返回 -1
*/
private int nextPrecedence(ParserContext ctx) {
InfixParselet infix = infixes.get(ctx.getTokens().peek().getLexeme());
diff --git a/src/main/java/org/jcnc/snow/compiler/parser/expression/Precedence.java b/src/main/java/org/jcnc/snow/compiler/parser/expression/Precedence.java
index 61f4dab..e47a5aa 100644
--- a/src/main/java/org/jcnc/snow/compiler/parser/expression/Precedence.java
+++ b/src/main/java/org/jcnc/snow/compiler/parser/expression/Precedence.java
@@ -9,26 +9,30 @@ package org.jcnc.snow.compiler.parser.expression;
*/
public enum Precedence {
- /**
- * 最低优先级,通常用于整个表达式解析的起始入口。
- */
+ /** 最低优先级,通常用于整个表达式解析的起始入口。 */
LOWEST,
- /**
- * 加法和减法的优先级(例如 +、-)。
- */
+ /** 逻辑或(||) */
+ OR,
+
+ /** 逻辑与(&&) */
+ AND,
+
+ /** 相等/不等(==, !=) */
+ EQUALITY,
+
+ /** 大小比较(<, >, <=, >=) */
+ COMPARISON,
+
+ /** 加法和减法(+、-) */
SUM,
- /**
- * 乘法、除法、取模等更高优先级的二元运算符(例如 *、/、%)。
- */
+ /** 乘法、除法、取模(*、/、%) */
PRODUCT,
/** 一元前缀(-x !x) */
UNARY,
- /**
- * 函数调用、成员访问等最强绑定(例如 foo()、obj.prop)。
- */
+ /** 函数调用、成员访问等最强绑定(foo()、obj.prop) */
CALL
}
diff --git a/src/main/java/org/jcnc/snow/compiler/parser/function/ASTPrinter.java b/src/main/java/org/jcnc/snow/compiler/parser/function/ASTPrinter.java
index b440a4a..ae45674 100644
--- a/src/main/java/org/jcnc/snow/compiler/parser/function/ASTPrinter.java
+++ b/src/main/java/org/jcnc/snow/compiler/parser/function/ASTPrinter.java
@@ -71,7 +71,7 @@ public class ASTPrinter {
NodeContext _
) -> {
System.out.println(pad + "function " + name
- + "(params=" + parameters + ", return=" + returnType + ")");
+ + "(params=" + parameters + ", returns=" + returnType + ")");
for (StatementNode stmt : body) {
print(stmt, indent + 1);
}
diff --git a/src/main/java/org/jcnc/snow/compiler/parser/function/FunctionParser.java b/src/main/java/org/jcnc/snow/compiler/parser/function/FunctionParser.java
index 39d928a..dba62b3 100644
--- a/src/main/java/org/jcnc/snow/compiler/parser/function/FunctionParser.java
+++ b/src/main/java/org/jcnc/snow/compiler/parser/function/FunctionParser.java
@@ -27,8 +27,8 @@ import java.util.*;
*
*
* 函数头(关键字 {@code function:} 与函数名)
- * 参数列表(parameter 区块)
- * 返回类型(return_type 区块)
+ * 参数列表(params 区块)
+ * 返回类型(returns 区块)
* 函数体(body 区块)
* 函数结束(关键字 {@code end function})
*
@@ -107,7 +107,7 @@ public class FunctionParser implements TopLevelParser {
}
/**
- * 构造函数定义中各区块的解析规则(parameter、return_type、body)。
+ * 构造函数定义中各区块的解析规则(params、returns、body)。
*
*
* 每个 {@link SectionDefinition} 包含两个部分: 区块起始判断器(基于关键字)与具体的解析逻辑。
@@ -124,13 +124,13 @@ public class FunctionParser implements TopLevelParser {
List body) {
Map map = new HashMap<>();
- map.put("parameter", new SectionDefinition(
- (TokenStream stream) -> stream.peek().getLexeme().equals("parameter"),
+ map.put("params", new SectionDefinition(
+ (TokenStream stream) -> stream.peek().getLexeme().equals("params"),
(ParserContext context, TokenStream stream) -> params.addAll(parseParameters(context))
));
- map.put("return_type", new SectionDefinition(
- (TokenStream stream) -> stream.peek().getLexeme().equals("return_type"),
+ map.put("returns", new SectionDefinition(
+ (TokenStream stream) -> stream.peek().getLexeme().equals("returns"),
(ParserContext context, TokenStream stream) -> returnType[0] = parseReturnType(stream)
));
@@ -182,7 +182,7 @@ public class FunctionParser implements TopLevelParser {
*
* 支持声明后附加注释,格式示例:
*
- * parameter:
+ * params:
* declare x: int // 说明文字
* declare y: float
*
@@ -194,7 +194,7 @@ public class FunctionParser implements TopLevelParser {
private List parseParameters(ParserContext ctx) {
TokenStream ts = ctx.getTokens();
- ts.expect("parameter");
+ ts.expect("params");
ts.expect(":");
skipComments(ts);
ts.expectType(TokenType.NEWLINE);
@@ -208,7 +208,7 @@ public class FunctionParser implements TopLevelParser {
continue;
}
String lex = ts.peek().getLexeme();
- if (lex.equals("return_type") || lex.equals("body") || lex.equals("end")) {
+ if (lex.equals("returns") || lex.equals("body") || lex.equals("end")) {
break;
}
@@ -232,14 +232,14 @@ public class FunctionParser implements TopLevelParser {
* 解析返回类型声明。
*
*
- * 格式为 {@code return_type: TYPE},支持前置或行尾注释。
+ * 格式为 {@code returns: TYPE},支持前置或行尾注释。
*
*
* @param ts 当前使用的 {@link TokenStream}。
* @return 返回类型名称字符串。
*/
private String parseReturnType(TokenStream ts) {
- ts.expect("return_type");
+ ts.expect("returns");
ts.expect(":");
skipComments(ts);
Token typeToken = ts.expectType(TokenType.TYPE);
diff --git a/src/main/java/org/jcnc/snow/compiler/parser/statement/DeclarationStatementParser.java b/src/main/java/org/jcnc/snow/compiler/parser/statement/DeclarationStatementParser.java
index 5cbcc40..5e0470b 100644
--- a/src/main/java/org/jcnc/snow/compiler/parser/statement/DeclarationStatementParser.java
+++ b/src/main/java/org/jcnc/snow/compiler/parser/statement/DeclarationStatementParser.java
@@ -8,39 +8,29 @@ import org.jcnc.snow.compiler.parser.context.ParserContext;
import org.jcnc.snow.compiler.parser.expression.PrattExpressionParser;
/**
- * {@code DeclarationStatementParser} 类负责解析变量声明语句,是语句级解析器的一部分。
+ * {@code DeclarationStatementParser} 负责解析变量声明语句节点。
*
- * 本解析器支持以下两种形式的声明语法:
+ * 支持以下两种语法结构:
*
{@code
* declare myVar:Integer
* declare myVar:Integer = 42 + 3
* }
- * 其中:
- *
- * {@code myVar} 为变量名(必须为标识符类型);
- * {@code Integer} 为类型标注(必须为类型标记);
- * 可选的初始化表达式由 {@link PrattExpressionParser} 解析;
- * 每条声明语句必须以换行符({@code NEWLINE})结束。
- *
- * 若语法不满足上述结构,将在解析过程中抛出异常。
+ * 解析器能够识别多维数组类型(如 {@code int[]}, {@code string[][]}),并支持可选初始化表达式。
+ *
+ * 每个声明语句均要求以换行符结尾,语法不合法时会抛出异常。
+ *
*/
public class DeclarationStatementParser implements StatementParser {
/**
- * 解析一条 {@code declare} 声明语句,并返回对应的抽象语法树节点 {@link DeclarationNode}。
+ * 解析一条 {@code declare} 语句,生成对应的抽象语法树节点 {@link DeclarationNode}。
*
- * 解析流程如下:
- *
- * 匹配关键字 {@code declare};
- * 读取变量名称(标识符类型);
- * 读取类型标注(在冒号后,要求为 {@code TYPE} 类型);
- * 若存在 {@code =},则继续解析其后的表达式作为初始化值;
- * 最终必须匹配 {@code NEWLINE} 表示语句结束。
- *
- * 若遇到非法语法结构,将触发异常并中断解析过程。
+ * 支持类型标注和可选初始化表达式。类型部分自动拼接数组维度(如 int[][])。
+ *
*
- * @param ctx 当前语法解析上下文,包含词法流、错误信息等。
- * @return 返回一个 {@link DeclarationNode} 节点,表示解析完成的声明语法结构。
+ * @param ctx 当前语法解析上下文(包含词法流、错误信息等)
+ * @return {@link DeclarationNode} 表示声明语句结构
+ * @throws RuntimeException 语法不合法时抛出
*/
@Override
public DeclarationNode parse(ParserContext ctx) {
@@ -61,11 +51,19 @@ public class DeclarationStatementParser implements StatementParser {
ctx.getTokens().expect(":");
// 获取变量类型(类型标识符)
- String type = ctx.getTokens()
- .expectType(TokenType.TYPE)
- .getLexeme();
+ StringBuilder type = new StringBuilder(
+ ctx.getTokens()
+ .expectType(TokenType.TYPE)
+ .getLexeme()
+ );
- // 可选的初始化表达式,若存在 "=",则解析等号右侧表达式
+ // 消费多维数组类型后缀 "[]"
+ while (ctx.getTokens().match("[")) {
+ ctx.getTokens().expectType(TokenType.RBRACKET); // 必须配对
+ type.append("[]"); // 类型名称拼接 [],如 int[][] 等
+ }
+
+ // 可选初始化表达式(=号右侧)
ExpressionNode init = null;
if (ctx.getTokens().match("=")) {
init = new PrattExpressionParser().parse(ctx);
@@ -75,6 +73,6 @@ public class DeclarationStatementParser implements StatementParser {
ctx.getTokens().expectType(TokenType.NEWLINE);
// 返回构建好的声明语法树节点
- return new DeclarationNode(name, type, init, new NodeContext(line, column, file));
+ return new DeclarationNode(name, type.toString(), init, new NodeContext(line, column, file));
}
}
diff --git a/src/main/java/org/jcnc/snow/compiler/parser/statement/ExpressionStatementParser.java b/src/main/java/org/jcnc/snow/compiler/parser/statement/ExpressionStatementParser.java
index 7e80851..955baa7 100644
--- a/src/main/java/org/jcnc/snow/compiler/parser/statement/ExpressionStatementParser.java
+++ b/src/main/java/org/jcnc/snow/compiler/parser/statement/ExpressionStatementParser.java
@@ -51,7 +51,7 @@ public class ExpressionStatementParser implements StatementParser {
int column = ts.peek().getCol();
String file = ctx.getSourceName();
- // 赋值语句: IDENTIFIER = expr
+ // 简单形式: IDENTIFIER = expr
if (ts.peek().getType() == TokenType.IDENTIFIER && "=".equals(ts.peek(1).getLexeme())) {
String varName = ts.next().getLexeme();
ts.expect("=");
@@ -60,9 +60,23 @@ public class ExpressionStatementParser implements StatementParser {
return new AssignmentNode(varName, value, new NodeContext(line, column, file));
}
+ // 尝试解析更通用的左值形式(支持下标): =
+ ExpressionNode lhs = new PrattExpressionParser().parse(ctx);
+ if ("=".equals(ts.peek().getLexeme())) {
+ ts.next(); // consume '='
+ ExpressionNode rhs = new PrattExpressionParser().parse(ctx);
+ ts.expectType(TokenType.NEWLINE);
+ // 根据左值类型构造具体赋值节点
+ if (lhs instanceof org.jcnc.snow.compiler.parser.ast.IdentifierNode id) {
+ return new AssignmentNode(id.name(), rhs, new NodeContext(line, column, file));
+ } else if (lhs instanceof org.jcnc.snow.compiler.parser.ast.IndexExpressionNode idx) {
+ return new org.jcnc.snow.compiler.parser.ast.IndexAssignmentNode(idx, rhs, new NodeContext(line, column, file));
+ } else {
+ throw new UnexpectedToken("不支持的赋值左值类型: " + lhs.getClass().getSimpleName(), line, column);
+ }
+ }
// 普通表达式语句
- ExpressionNode expr = new PrattExpressionParser().parse(ctx);
ts.expectType(TokenType.NEWLINE);
- return new ExpressionStatementNode(expr, new NodeContext(line, column, file));
+ return new ExpressionStatementNode(lhs, new NodeContext(line, column, file));
}
}
diff --git a/src/main/java/org/jcnc/snow/compiler/semantic/analyzers/AnalyzerRegistry.java b/src/main/java/org/jcnc/snow/compiler/semantic/analyzers/AnalyzerRegistry.java
index d61496c..5f0bc30 100644
--- a/src/main/java/org/jcnc/snow/compiler/semantic/analyzers/AnalyzerRegistry.java
+++ b/src/main/java/org/jcnc/snow/compiler/semantic/analyzers/AnalyzerRegistry.java
@@ -12,68 +12,64 @@ import java.util.Map;
/**
* {@code AnalyzerRegistry} 是语义分析器的注册与分发中心。
*
- * 它负责根据 AST 节点的类型,查找并返回相应的 {@link StatementAnalyzer} 或 {@link ExpressionAnalyzer} 实例。
- * 同时支持注册自定义分析器,并在未找到对应表达式分析器时提供默认兜底处理器。
+ * 该类维护了 AST 节点类型与相应 {@link StatementAnalyzer}、
+ * {@link ExpressionAnalyzer} 实例的映射关系。调用者可以通过节点类型注册自定义的分析器,
+ * 并在后续通过节点对象高效获取对应分析器,实现语义分析分发。
+ *
+ *
+ * 对于表达式分析器的获取({@link #getExpressionAnalyzer(ExpressionNode)}),
+ * 支持“最近父类匹配”查找机制:若找不到节点的精确类型分析器,则向上递归查找已注册的最近父类类型分析器;
+ * 若依然未找到,则自动 fallback 到默认的 {@link UnsupportedExpressionAnalyzer},确保分析流程健壮性。
+ *
*
- * 主要职责:
- *
- * 支持注册语句和表达式节点类型对应的分析器;
- * 在语义分析阶段,根据 AST 节点动态查找对应的分析器;
- * 为未注册的表达式类型提供默认处理器 {@link UnsupportedExpressionAnalyzer};
- * 不为语句提供默认兜底分析器,未注册类型将返回 {@code null}。
- *
*/
public class AnalyzerRegistry {
- /** Statement 节点类型 → 对应语义分析器映射表 */
- private final Map, StatementAnalyzer>> stmtAnalyzers = new HashMap<>();
-
- /** Expression 节点类型 → 对应语义分析器映射表 */
- private final Map, ExpressionAnalyzer>> exprAnalyzers = new HashMap<>();
-
- /** 默认兜底表达式分析器,用于处理未注册的表达式类型 */
- private final ExpressionAnalyzer defaultUnsupported =
- new UnsupportedExpressionAnalyzer<>();
-
- // ========================= 注册方法 =========================
+ /**
+ * Statement 节点类型 → 对应语义分析器映射表
+ */
+ private final Map, StatementAnalyzer>> stmtAnalyzers = new HashMap<>();
/**
- * 注册一个 {@link StatementAnalyzer} 实例,用于处理指定类型的语句节点。
- *
- * @param cls 要注册的语句节点类型(Class 对象)
- * @param analyzer 与该类型匹配的分析器实例
- * @param {@link StatementNode} 的具体子类
+ * Expression 节点类型 → 对应语义分析器映射表
*/
- public void registerStatementAnalyzer(
- Class cls,
- StatementAnalyzer analyzer
- ) {
- stmtAnalyzers.put(cls, analyzer);
+ private final Map, ExpressionAnalyzer>> exprAnalyzers = new HashMap<>();
+
+ /**
+ * 默认表达式兜底分析器:用于未注册或不能识别的表达式类型
+ */
+ private final ExpressionAnalyzer defaultUnsupported = new UnsupportedExpressionAnalyzer<>();
+
+ /**
+ * 注册 Statement 类型的语义分析器。
+ *
+ * @param clazz AST 语句节点类型(class对象),例如 {@code IfStatementNode.class}
+ * @param analyzer 针对该节点类型的语义分析器实例
+ * @param 语句节点类型参数,必须是 {@link StatementNode} 的子类
+ */
+ public void registerStatementAnalyzer(Class clazz, StatementAnalyzer analyzer) {
+ stmtAnalyzers.put(clazz, analyzer);
}
/**
- * 注册一个 {@link ExpressionAnalyzer} 实例,用于处理指定类型的表达式节点。
+ * 注册 Expression 类型的语义分析器。
*
- * @param cls 要注册的表达式节点类型(Class 对象)
- * @param analyzer 与该类型匹配的分析器实例
- * @param {@link ExpressionNode} 的具体子类
+ * @param clazz AST 表达式节点类型(class对象),例如 {@code BinaryExprNode.class}
+ * @param analyzer 针对该节点类型的语义分析器实例
+ * @param 表达式节点类型参数,必须是 {@link ExpressionNode} 的子类
*/
- public void registerExpressionAnalyzer(
- Class cls,
- ExpressionAnalyzer analyzer
- ) {
- exprAnalyzers.put(cls, analyzer);
+ public void registerExpressionAnalyzer(Class clazz, ExpressionAnalyzer analyzer) {
+ exprAnalyzers.put(clazz, analyzer);
}
- // ========================= 获取方法 =========================
-
/**
- * 根据语句节点的实际类型查找对应的 {@link StatementAnalyzer}。
+ * 获取指定语句节点对象的分析器实例。
*
- * 若节点类型未注册,返回 {@code null}。
+ * 只支持“精确类型匹配”,即仅当注册过该节点 class 时才返回分析器,否则返回 null。
+ *
*
- * @param stmt 要分析的语句节点实例
- * @param 语句类型(推断自参数)
- * @return 与该节点类型对应的分析器,若未注册则为 {@code null}
+ * @param stmt 语句节点对象
+ * @param 节点类型,需为 {@link StatementNode} 子类
+ * @return 匹配的 {@link StatementAnalyzer},若未注册则返回 null
*/
@SuppressWarnings("unchecked")
public StatementAnalyzer getStatementAnalyzer(S stmt) {
@@ -81,17 +77,34 @@ public class AnalyzerRegistry {
}
/**
- * 根据表达式节点的实际类型查找对应的 {@link ExpressionAnalyzer}。
+ * 获取指定表达式节点对象的分析器实例。
*
- * 若节点类型未注册,返回默认兜底分析器 {@link UnsupportedExpressionAnalyzer}。
+ * 首先尝试节点类型的精确匹配;若未注册,向上递归查找其最近的已注册父类类型分析器;
+ * 若依然未命中,则返回默认的 {@link UnsupportedExpressionAnalyzer},保证分析器始终有返回值。
+ *
*
- * @param expr 要分析的表达式节点实例
- * @param 表达式类型(推断自参数)
- * @return 与该节点类型对应的分析器,或默认兜底分析器
+ * @param expr 表达式节点对象
+ * @param 节点类型,需为 {@link ExpressionNode} 子类
+ * @return 匹配的 {@link ExpressionAnalyzer} 实例;若未注册,则返回兜底分析器
*/
@SuppressWarnings("unchecked")
public ExpressionAnalyzer getExpressionAnalyzer(E expr) {
- return (ExpressionAnalyzer)
- exprAnalyzers.getOrDefault(expr.getClass(), defaultUnsupported);
+ Class> cls = expr.getClass();
+ // 精确匹配
+ ExpressionAnalyzer> analyzer = exprAnalyzers.get(cls);
+ if (analyzer != null) {
+ return (ExpressionAnalyzer) analyzer;
+ }
+ // 向上遍历父类尝试匹配
+ Class> current = cls.getSuperclass();
+ while (current != null && ExpressionNode.class.isAssignableFrom(current)) {
+ analyzer = exprAnalyzers.get(current);
+ if (analyzer != null) {
+ return (ExpressionAnalyzer) analyzer;
+ }
+ current = current.getSuperclass();
+ }
+ // fallback
+ return (ExpressionAnalyzer) defaultUnsupported;
}
}
diff --git a/src/main/java/org/jcnc/snow/compiler/semantic/analyzers/expression/ArrayLiteralAnalyzer.java b/src/main/java/org/jcnc/snow/compiler/semantic/analyzers/expression/ArrayLiteralAnalyzer.java
new file mode 100644
index 0000000..752cc61
--- /dev/null
+++ b/src/main/java/org/jcnc/snow/compiler/semantic/analyzers/expression/ArrayLiteralAnalyzer.java
@@ -0,0 +1,63 @@
+package org.jcnc.snow.compiler.semantic.analyzers.expression;
+
+import org.jcnc.snow.compiler.parser.ast.ArrayLiteralNode;
+import org.jcnc.snow.compiler.parser.ast.FunctionNode;
+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;
+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.ArrayType;
+import org.jcnc.snow.compiler.semantic.type.BuiltinType;
+import org.jcnc.snow.compiler.semantic.type.Type;
+
+import java.util.List;
+
+/**
+ * {@code ArrayLiteralAnalyzer} 用于分析数组字面量表达式(ArrayLiteralNode)。
+ *
+ * 主要负责:
+ *
+ * 推断数组字面量的元素类型,生成对应的 {@link ArrayType}。
+ * 检查所有元素类型是否一致,不一致时报错并降级为 {@code int[]}。
+ * 若数组为空,默认类型为 {@code int[]},并产生相应语义错误。
+ *
+ *
+ */
+public class ArrayLiteralAnalyzer implements ExpressionAnalyzer {
+ /**
+ * 分析数组字面量表达式,推断类型,并检查元素类型一致性。
+ *
+ * @param ctx 全局/当前语义分析上下文
+ * @param mi 所属模块信息
+ * @param fn 当前分析的函数节点
+ * @param locals 当前作用域符号表
+ * @param expr 当前数组字面量节点
+ * @return 推断出的数组类型,若类型冲突或无法推断,则为 {@code int[]}
+ */
+ @Override
+ public Type analyze(Context ctx, ModuleInfo mi, FunctionNode fn, SymbolTable locals, ArrayLiteralNode expr) {
+ List elems = expr.elements();
+ if (elems.isEmpty()) {
+ ctx.getErrors().add(new SemanticError(expr, "空数组字面量的类型无法推断,已默认为 int[]"));
+ return new ArrayType(BuiltinType.INT);
+ }
+ // 以第一个元素为基准
+ Type first = ctx.getRegistry()
+ .getExpressionAnalyzer(elems.getFirst())
+ .analyze(ctx, mi, fn, locals, elems.getFirst());
+
+ for (int i = 1; i < elems.size(); i++) {
+ ExpressionNode e = elems.get(i);
+ Type t = ctx.getRegistry()
+ .getExpressionAnalyzer(e)
+ .analyze(ctx, mi, fn, locals, e);
+ if (!t.equals(first)) {
+ ctx.getErrors().add(new SemanticError(e, "数组元素类型不一致: 期望 " + first.name() + ",实际 " + t.name()));
+ return new ArrayType(BuiltinType.INT);
+ }
+ }
+ return new ArrayType(first);
+ }
+}
diff --git a/src/main/java/org/jcnc/snow/compiler/semantic/analyzers/expression/CallExpressionAnalyzer.java b/src/main/java/org/jcnc/snow/compiler/semantic/analyzers/expression/CallExpressionAnalyzer.java
index df0993c..250e12e 100644
--- a/src/main/java/org/jcnc/snow/compiler/semantic/analyzers/expression/CallExpressionAnalyzer.java
+++ b/src/main/java/org/jcnc/snow/compiler/semantic/analyzers/expression/CallExpressionAnalyzer.java
@@ -80,35 +80,7 @@ public class CallExpressionAnalyzer implements ExpressionAnalyzer
+ * 语义规则:
+ *
+ * 先分析 array 的类型,必须为 {@link ArrayType}
+ * 再分析 index 的类型,必须为数值类型
+ * 表达式结果类型为 array 的元素类型;若 array 非数组类型,报错并返回 int 类型兜底
+ *
+ */
+public class IndexExpressionAnalyzer implements ExpressionAnalyzer {
+
+ /**
+ * 分析并类型检查下标访问表达式。
+ *
+ * @param ctx 当前语义分析上下文
+ * @param mi 当前模块信息
+ * @param fn 当前函数节点
+ * @param locals 当前作用域符号表
+ * @param node 要分析的下标访问表达式节点
+ * @return 若 array 合法,则返回元素类型;否则兜底为 int 类型
+ */
+ @Override
+ public Type analyze(Context ctx, ModuleInfo mi, FunctionNode fn, SymbolTable locals, IndexExpressionNode node) {
+ // 先分析被下标访问的数组表达式
+ Type arrType = ctx.getRegistry()
+ .getExpressionAnalyzer(node.array())
+ .analyze(ctx, mi, fn, locals, node.array());
+
+ if (arrType instanceof ArrayType(Type elementType)) {
+ // 再分析下标表达式
+ Type idxType = ctx.getRegistry()
+ .getExpressionAnalyzer(node.index())
+ .analyze(ctx, mi, fn, locals, node.index());
+
+ if (!idxType.isNumeric()) {
+ ctx.getErrors().add(new SemanticError(node, "数组下标必须是数值类型"));
+ }
+ // array[index] 的类型是数组元素类型
+ return elementType;
+ }
+
+ ctx.getErrors().add(new SemanticError(node, "仅数组类型支持下标访问"));
+ return BuiltinType.INT;
+ }
+}
diff --git a/src/main/java/org/jcnc/snow/compiler/semantic/analyzers/statement/IndexAssignmentAnalyzer.java b/src/main/java/org/jcnc/snow/compiler/semantic/analyzers/statement/IndexAssignmentAnalyzer.java
new file mode 100644
index 0000000..4990c79
--- /dev/null
+++ b/src/main/java/org/jcnc/snow/compiler/semantic/analyzers/statement/IndexAssignmentAnalyzer.java
@@ -0,0 +1,72 @@
+package org.jcnc.snow.compiler.semantic.analyzers.statement;
+
+import org.jcnc.snow.compiler.parser.ast.FunctionNode;
+import org.jcnc.snow.compiler.parser.ast.IndexAssignmentNode;
+import org.jcnc.snow.compiler.parser.ast.IndexExpressionNode;
+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.SymbolTable;
+import org.jcnc.snow.compiler.semantic.type.ArrayType;
+import org.jcnc.snow.compiler.semantic.type.Type;
+
+/**
+ * {@code IndexAssignmentAnalyzer} 用于分析和类型检查数组下标赋值语句。
+ *
+ * 主要职责:
+ *
+ * 检查左侧是否为数组类型
+ * 检查下标是否为数值类型
+ * 检查右值(被赋值表达式)类型是否与数组元素类型兼容
+ * 如有类型不符,将语义错误写入 {@link Context#getErrors()}
+ *
+ */
+public class IndexAssignmentAnalyzer implements StatementAnalyzer {
+
+ /**
+ * 分析并类型检查数组下标赋值语句,如 {@code arr[i] = v}。
+ *
+ * 左侧必须是数组类型
+ * 下标必须为数值类型
+ * 右侧赋值类型需与数组元素类型一致
+ * 任何不合法情况均会产生 {@link SemanticError}
+ *
+ *
+ * @param ctx 当前语义分析上下文
+ * @param mi 当前模块信息
+ * @param fn 所属函数节点
+ * @param locals 局部符号表
+ * @param node 赋值语句 AST 节点
+ */
+ @Override
+ public void analyze(Context ctx, ModuleInfo mi, FunctionNode fn, SymbolTable locals, IndexAssignmentNode node) {
+ IndexExpressionNode target = node.target();
+
+ // 分析左侧类型(必须为数组类型)
+ Type arrT = ctx.getRegistry()
+ .getExpressionAnalyzer(target.array())
+ .analyze(ctx, mi, fn, locals, target.array());
+ if (!(arrT instanceof ArrayType(Type elementType))) {
+ ctx.getErrors().add(new SemanticError(node, "左侧不是数组,无法进行下标赋值"));
+ return;
+ }
+
+ // 分析下标类型(必须为数值类型)
+ Type idxT = ctx.getRegistry()
+ .getExpressionAnalyzer(target.index())
+ .analyze(ctx, mi, fn, locals, target.index());
+ if (!idxT.isNumeric()) {
+ ctx.getErrors().add(new SemanticError(node, "数组下标必须是数值类型"));
+ }
+
+ // 分析右值类型(必须与元素类型一致)
+ Type rhsT = ctx.getRegistry()
+ .getExpressionAnalyzer(node.value())
+ .analyze(ctx, mi, fn, locals, node.value());
+ if (!rhsT.equals(elementType)) {
+ ctx.getErrors().add(new SemanticError(node,
+ "数组元素赋值类型不匹配: 期望 " + elementType.name() + ",实际 " + rhsT.name()));
+ }
+ }
+}
diff --git a/src/main/java/org/jcnc/snow/compiler/semantic/core/AnalyzerRegistrar.java b/src/main/java/org/jcnc/snow/compiler/semantic/core/AnalyzerRegistrar.java
index 0e5e318..f0b301a 100644
--- a/src/main/java/org/jcnc/snow/compiler/semantic/core/AnalyzerRegistrar.java
+++ b/src/main/java/org/jcnc/snow/compiler/semantic/core/AnalyzerRegistrar.java
@@ -57,6 +57,11 @@ public final class AnalyzerRegistrar {
registry.registerExpressionAnalyzer(CallExpressionNode.class, new CallExpressionAnalyzer());
registry.registerExpressionAnalyzer(BinaryExpressionNode.class, new BinaryExpressionAnalyzer());
+ registry.registerExpressionAnalyzer(ArrayLiteralNode.class, new ArrayLiteralAnalyzer());
+ registry.registerExpressionAnalyzer(IndexExpressionNode.class,new IndexExpressionAnalyzer()); // ★ 关键行
+ registry.registerStatementAnalyzer(IndexAssignmentNode.class, new IndexAssignmentAnalyzer());
+
+
// ---------- 注册一元表达式分析器 ----------
registry.registerExpressionAnalyzer(UnaryExpressionNode.class, new UnaryExpressionAnalyzer());
diff --git a/src/main/java/org/jcnc/snow/compiler/semantic/core/Context.java b/src/main/java/org/jcnc/snow/compiler/semantic/core/Context.java
index 881f083..6a69b67 100644
--- a/src/main/java/org/jcnc/snow/compiler/semantic/core/Context.java
+++ b/src/main/java/org/jcnc/snow/compiler/semantic/core/Context.java
@@ -2,42 +2,54 @@ package org.jcnc.snow.compiler.semantic.core;
import org.jcnc.snow.compiler.semantic.analyzers.AnalyzerRegistry;
import org.jcnc.snow.compiler.semantic.error.SemanticError;
+import org.jcnc.snow.compiler.semantic.type.ArrayType;
import org.jcnc.snow.compiler.semantic.type.Type;
import java.util.List;
import java.util.Map;
/**
- * {@code Context} 表示语义分析阶段的共享上下文环境。
+ * {@code Context} 表示语义分析阶段的全局上下文环境。
*
- * 它贯穿整个语义分析流程,用于维护并提供以下核心服务:
+ * 该类贯穿整个语义分析流程,集中管理以下内容:
*
- * 模块信息管理: 包含所有已加载模块(源模块与内置模块);
- * 错误收集: 集中存储语义分析期间产生的 {@link SemanticError};
- * 日志控制: 支持按需输出详细调试日志;
- * 分析器调度: 通过 {@link AnalyzerRegistry} 管理语句/表达式的分析器分发。
+ * 模块信息管理(所有已加载模块,包括源模块和内置模块);
+ * 错误收集(集中存储语义分析期间产生的 {@link SemanticError});
+ * 日志输出控制(可选,支持调试信息);
+ * 分析器调度(通过 {@link AnalyzerRegistry} 分发对应分析器);
*
+ *
+ * 提供便捷的 getter 方法和类型解析工具方法。
+ *
*/
public class Context {
- /** 模块表: 模块名 → {@link ModuleInfo},用于模块查找与跨模块引用 */
+ /**
+ * 模块表:模块名 → {@link ModuleInfo},用于模块查找与跨模块引用。
+ */
private final Map modules;
- /** 错误列表: 语义分析过程中收集的所有 {@link SemanticError} */
+ /**
+ * 错误列表:语义分析过程中收集的所有 {@link SemanticError}。
+ */
private final List errors;
- /** 日志开关: 若为 true,将启用 {@link #log(String)} 输出日志信息 */
+ /**
+ * 日志开关:若为 true,将启用 {@link #log(String)} 输出日志信息。
+ */
private final boolean verbose;
- /** 语义分析器注册表: 用于按节点类型动态调度分析器 */
+ /**
+ * 语义分析器注册表:用于按节点类型动态调度分析器。
+ */
private final AnalyzerRegistry registry;
/**
* 构造语义分析上下文对象。
*
- * @param modules 已注册的模块信息集合
- * @param errors 错误收集器,分析器将所有语义错误写入此列表
- * @param verbose 是否启用调试日志输出
- * @param registry 分析器注册表,提供类型到分析器的映射与调度能力
+ * @param modules 已注册的模块信息集合
+ * @param errors 错误收集器,分析器将所有语义错误写入此列表
+ * @param verbose 是否启用调试日志输出
+ * @param registry 分析器注册表,提供类型到分析器的映射与调度能力
*/
public Context(Map modules,
List errors,
@@ -54,13 +66,17 @@ public class Context {
/**
* 获取所有模块信息映射表。
*
- * @return 模块名 → 模块信息 {@link ModuleInfo} 的映射
+ * @return 模块名到模块信息({@link ModuleInfo})的映射表
*/
public Map getModules() {
return modules;
}
- /** @return 模块信息(快捷方式) */
+ /**
+ * 模块信息 getter(快捷方式)。
+ *
+ * @return 模块名到模块信息({@link ModuleInfo})的映射表
+ */
public Map modules() {
return modules;
}
@@ -76,7 +92,11 @@ public class Context {
return errors;
}
- /** @return 错误列表(快捷方式) */
+ /**
+ * 错误列表 getter(快捷方式)。
+ *
+ * @return 错误列表
+ */
public List errors() {
return errors;
}
@@ -92,7 +112,11 @@ public class Context {
return registry;
}
- /** @return 注册表(快捷方式) */
+ /**
+ * 注册表 getter(快捷方式)。
+ *
+ * @return {@link AnalyzerRegistry} 实例
+ */
public AnalyzerRegistry registry() {
return registry;
}
@@ -113,15 +137,26 @@ public class Context {
// ------------------ 工具函数 ------------------
/**
- * 将类型名称字符串解析为对应的内置 {@link Type} 实例。
+ * 将类型名称字符串解析为对应的类型实例(支持多维数组后缀)。
*
- * 若类型在 {@link BuiltinTypeRegistry#BUILTIN_TYPES} 中存在,则返回对应类型;
- * 否则返回 {@code null},调用方可据此决定是否降级处理。
+ * 例如,"int" → int 类型,"int[][]" → 二维整型数组类型。
+ *
*
- * @param name 类型名称(如 "int", "float", "void", "string" 等)
- * @return 匹配的 {@link Type},若无匹配项则返回 {@code null}
+ * @param name 类型名称(支持 "[]" 数组后缀)
+ * @return 对应的 {@link Type} 实例,若无法识别返回 null
*/
public Type parseType(String name) {
- return BuiltinTypeRegistry.BUILTIN_TYPES.get(name);
+ int dims = 0;
+ while (name.endsWith("[]")) {
+ name = name.substring(0, name.length() - 2);
+ dims++;
+ }
+ Type base = BuiltinTypeRegistry.BUILTIN_TYPES.get(name);
+ if (base == null) return null;
+ Type t = base;
+ for (int i = 0; i < dims; i++) {
+ t = new ArrayType(t);
+ }
+ return t;
}
}
diff --git a/src/main/java/org/jcnc/snow/compiler/semantic/core/FunctionChecker.java b/src/main/java/org/jcnc/snow/compiler/semantic/core/FunctionChecker.java
index cb54eb6..50f1ec6 100644
--- a/src/main/java/org/jcnc/snow/compiler/semantic/core/FunctionChecker.java
+++ b/src/main/java/org/jcnc/snow/compiler/semantic/core/FunctionChecker.java
@@ -59,7 +59,13 @@ public record FunctionChecker(Context ctx) {
SymbolTable globalScope = new SymbolTable(null);
for (DeclarationNode g : mod.globals()) {
var t = ctx.parseType(g.getType());
- globalScope.define(new Symbol(g.getName(), t, SymbolKind.VARIABLE));
+ // 检查全局变量是否重复声明
+ if (!globalScope.define(new Symbol(g.getName(), t, SymbolKind.VARIABLE))) {
+ ctx.errors().add(new SemanticError(
+ g,
+ "全局变量重复声明: " + g.getName()
+ ));
+ }
}
// 遍历模块中所有函数定义
diff --git a/src/main/java/org/jcnc/snow/compiler/semantic/error/SemanticError.java b/src/main/java/org/jcnc/snow/compiler/semantic/error/SemanticError.java
index ce7bfc4..c43e6c9 100644
--- a/src/main/java/org/jcnc/snow/compiler/semantic/error/SemanticError.java
+++ b/src/main/java/org/jcnc/snow/compiler/semantic/error/SemanticError.java
@@ -48,9 +48,9 @@ public record SemanticError(Node node, String message) {
}
- StringBuilder sb = new StringBuilder();
- if (file != null && !file.isBlank()) sb.append(file).append(": ");
- sb.append((line >= 0 && col >= 0) ? "行 " + line + ", 列 " + col : "未知位置");
+ StringBuilder sb = new StringBuilder("file:///");
+ if (file != null && !file.isBlank()) sb.append(file).append(":");
+ sb.append((line >= 0 && col >= 0) ? line + ":" + col : "未知位置");
sb.append(": ").append(message);
return sb.toString();
}
diff --git a/src/main/java/org/jcnc/snow/compiler/semantic/type/ArrayType.java b/src/main/java/org/jcnc/snow/compiler/semantic/type/ArrayType.java
new file mode 100644
index 0000000..9b7bd41
--- /dev/null
+++ b/src/main/java/org/jcnc/snow/compiler/semantic/type/ArrayType.java
@@ -0,0 +1,73 @@
+package org.jcnc.snow.compiler.semantic.type;
+
+import java.util.Objects;
+
+/**
+ * {@code ArrayType} 表示数组类型,每个数组类型包含其元素类型。
+ *
+ * 例如,int[]、string[] 等均可用本类表示。内部通过 {@link #elementType()} 字段保存元素类型。
+ *
+ */
+public record ArrayType(
+ /*
+ 数组元素的类型。
+ */
+ Type elementType
+) implements Type {
+
+ /**
+ * 判断当前数组类型能否与另一类型兼容(主要用于类型检查)。
+ *
+ * 只有当 {@code other} 也是 ArrayType,且元素类型兼容时返回 true。
+ *
+ *
+ * @param other 需判断的类型
+ * @return 类型兼容性结果
+ */
+ @Override
+ public boolean isCompatible(Type other) {
+ if (!(other instanceof ArrayType(Type type))) return false;
+ return elementType.isCompatible(type);
+ }
+
+ /**
+ * 数组类型不是数值类型,直接调用父接口默认实现。
+ *
+ * @return 总为 false
+ */
+ @Override
+ public boolean isNumeric() {
+ return Type.super.isNumeric();
+ }
+
+ /**
+ * 判断两个 ArrayType 是否等价(元素类型完全一致即视为等价)。
+ *
+ * @param o 比较对象
+ * @return 是否等价
+ */
+ @Override
+ public boolean equals(Object o) {
+ return o instanceof ArrayType(Type type) && Objects.equals(elementType, type);
+ }
+
+ /**
+ * 返回数组类型的字符串描述,如 "int[]"。
+ *
+ * @return 类型名称
+ */
+ @Override
+ public String toString() {
+ return name();
+ }
+
+ /**
+ * 获取数组类型的名称描述,如 "int[]"。
+ *
+ * @return 类型名称
+ */
+ @Override
+ public String name() {
+ return elementType.name() + "[]";
+ }
+}
diff --git a/src/main/java/org/jcnc/snow/compiler/semantic/type/Type.java b/src/main/java/org/jcnc/snow/compiler/semantic/type/Type.java
index fc7798f..209e578 100644
--- a/src/main/java/org/jcnc/snow/compiler/semantic/type/Type.java
+++ b/src/main/java/org/jcnc/snow/compiler/semantic/type/Type.java
@@ -47,4 +47,6 @@ public interface Type {
int ia = order.indexOf(a), ib = order.indexOf(b);
return order.get(Math.max(ia, ib));
}
+ /** 类型名字符串(如 int、double[]) */
+ default String name() { return toString(); }
}
diff --git a/src/main/java/org/jcnc/snow/pkg/tasks/GenerateTask.java b/src/main/java/org/jcnc/snow/pkg/tasks/GenerateTask.java
index 4794994..3364823 100644
--- a/src/main/java/org/jcnc/snow/pkg/tasks/GenerateTask.java
+++ b/src/main/java/org/jcnc/snow/pkg/tasks/GenerateTask.java
@@ -16,7 +16,7 @@ import java.util.List;
* main.snow。
*
*
- * 生成内容包括:
+ * 生成内容包括:
*
* src/ —— 源码根目录
* src/{group package}/ —— 按 project.group 创建的包路径
@@ -31,7 +31,9 @@ import java.util.List;
*/
public final class GenerateTask implements Task {
- /** 项目信息元数据 */
+ /**
+ * 项目信息元数据
+ */
private final Project project;
/**
@@ -90,6 +92,13 @@ public final class GenerateTask implements Task {
System.out.println("[generate] created " + root.relativize(mainSnow));
}
+ /* ---------- 5. 写入系统库文件 os.snow ---------- */
+ Path osSnow = packageDir.resolve("OS.snow");
+ if (Files.notExists(osSnow)) {
+ Files.writeString(osSnow, SnowExample.getOsModule());
+ System.out.println("[generate] created " + root.relativize(osSnow));
+ }
+
System.out.println("[generate] project scaffold is ready.");
}
}
diff --git a/src/main/java/org/jcnc/snow/pkg/utils/SnowExample.java b/src/main/java/org/jcnc/snow/pkg/utils/SnowExample.java
index 7358487..a4aba61 100644
--- a/src/main/java/org/jcnc/snow/pkg/utils/SnowExample.java
+++ b/src/main/java/org/jcnc/snow/pkg/utils/SnowExample.java
@@ -1,7 +1,7 @@
package org.jcnc.snow.pkg.utils;
/**
- * 示例模块模板工具类,提供 main.snow 的标准示例代码字符串。
+ * 示例模块模板工具类,提供标准的示例 Snow 代码片段。
*
* 用于项目脚手架生成或帮助用户快速上手 .snow 语言。
*
@@ -23,19 +23,18 @@ public final class SnowExample {
public static String getMainModule() {
return """
module: Math
+ import: os
function: main
- parameter:
- return_type: int
+ returns: void
body:
- Math.factorial(6)
- return 0
+ os.print(Math.factorial(6))
end body
end function
function: factorial
- parameter:
+ params:
declare n: int
- return_type: int
+ returns: int
body:
declare num1: int = 1
loop:
@@ -55,4 +54,25 @@ public final class SnowExample {
end module
""";
}
+
+ /**
+ * 获取系统库 os.snow 模块的内容字符串。
+ *
+ * @return os.snow 模块的完整代码
+ */
+ public static String getOsModule() {
+ return """
+ module: os
+ import: os
+ function: print
+ params:
+ declare i1: int
+ returns: void
+ body:
+ syscall("PRINT", i1)
+ end body
+ end function
+ end module
+ """;
+ }
}
diff --git a/src/main/java/org/jcnc/snow/vm/commands/ref/control/RPushCommand.java b/src/main/java/org/jcnc/snow/vm/commands/ref/control/RPushCommand.java
index 1386018..6c12299 100644
--- a/src/main/java/org/jcnc/snow/vm/commands/ref/control/RPushCommand.java
+++ b/src/main/java/org/jcnc/snow/vm/commands/ref/control/RPushCommand.java
@@ -5,59 +5,287 @@ import org.jcnc.snow.vm.module.CallStack;
import org.jcnc.snow.vm.module.LocalVariableStore;
import org.jcnc.snow.vm.module.OperandStack;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.List;
+
/**
- * The {@code RPushCommand} class implements the {@link Command} interface and represents the
- * reference push instruction ({@code R_PUSH}) in the virtual machine.
+ * The {@code RPushCommand} class implements the {@link Command} interface
+ * and represents the "reference push" instruction ({@code R_PUSH}) in the virtual machine.
*
*
- * This instruction pushes a reference object, such as a String literal, onto the operand stack.
+ * This instruction pushes a reference-type value onto the operand stack.
+ * The input is parsed from the textual instruction form, which can represent:
+ *
+ * String literals
+ * Array literals (e.g., {@code [1, 2, 3]}), including nested arrays
+ *
*
*
- * Instruction format: {@code R_PUSH }
- *
- * {@code }: The reference value (e.g., string) to be pushed onto the stack.
- * If the literal contains spaces, all parts after {@code R_PUSH} are joined into a single string.
- *
- *
- * Behavior:
- *
- * Checks that the instruction has at least one parameter after the operator.
- * Concatenates all parameters after {@code R_PUSH} into a single string (separated by spaces).
- * Pushes the resulting string as a reference object onto the operand stack.
- * Increments the program counter to the next instruction.
- * Throws an {@code IllegalStateException} if the instruction is missing required parameters.
- *
+ *
+ * For array literals, a nested list structure is constructed. In this implementation,
+ * array literals are pushed as mutable {@link java.util.ArrayList} structures,
+ * so that subsequent system calls such as {@code ARR_SET} can modify elements in-place.
+ *
*/
-public final class RPushCommand implements Command {
+public class RPushCommand implements Command {
/**
- * Executes the {@code R_PUSH} instruction, pushing a reference (such as a string literal)
- * onto the operand stack.
+ * Executes the R_PUSH command.
*
- * @param parts The instruction parameters. {@code parts[0]} is the operator ("R_PUSH"),
- * {@code parts[1..]} are the parts of the literal to be concatenated and pushed.
- * @param pc The current program counter value, indicating the instruction address being executed.
- * @param stack The operand stack manager. The literal will be pushed onto this stack.
- * @param lvs The local variable store. (Not used in this instruction.)
- * @param cs The call stack manager. (Not used in this instruction.)
- * @return The next program counter value ({@code pc + 1}), pointing to the next instruction.
- * @throws IllegalStateException if the instruction is missing required parameters.
+ * @param parts The parts of the instruction, where {@code parts[1..n]} are concatenated as the literal.
+ * @param pc The current program counter.
+ * @param stack The operand stack where the result will be pushed.
+ * @param local The local variable store (unused in this instruction).
+ * @param callStack The call stack (unused in this instruction).
+ * @return The new program counter (typically {@code pc+1}).
+ * @throws IllegalStateException if no literal parameter is provided.
*/
@Override
- public int execute(String[] parts, int pc,
- OperandStack stack,
- LocalVariableStore lvs,
- CallStack cs) {
-
+ public int execute(String[] parts, int pc, OperandStack stack, LocalVariableStore local, CallStack callStack) {
if (parts.length < 2)
throw new IllegalStateException("R_PUSH missing parameter");
+ // Join all arguments into a complete literal string
StringBuilder sb = new StringBuilder();
for (int i = 1; i < parts.length; i++) {
if (i > 1) sb.append(' ');
sb.append(parts[i]);
}
- stack.push(sb.toString());
+ String literal = sb.toString().trim();
+
+ // Check if this is an array literal
+ if (literal.startsWith("[") && literal.endsWith("]")) {
+ Object parsed = parseValue(new Cursor(literal));
+ if (!(parsed instanceof List> list)) {
+ // Should not happen in theory; safety fallback
+ stack.push(parsed);
+ } else {
+ // Push a deep-mutable copy so ARR_SET can modify elements in-place
+ stack.push(deepMutable(list));
+ }
+ } else {
+ // Regular string, push as-is
+ stack.push(literal);
+ }
return pc + 1;
}
+
+ /**
+ * A simple string cursor, supporting index increment and character reading, for use by the parser.
+ */
+ static class Cursor {
+ final String s;
+ int i;
+
+ /**
+ * Constructs a new {@code Cursor} for the given string.
+ *
+ * @param s The string to parse.
+ */
+ Cursor(String s) {
+ this.s = s;
+ this.i = 0;
+ }
+
+ /**
+ * Advances the cursor by one character.
+ */
+ void skip() {
+ i++;
+ }
+
+ /**
+ * @return {@code true} if the cursor has reached the end of the string.
+ */
+ boolean end() {
+ return i >= s.length();
+ }
+
+ /**
+ * Gets the character at the current cursor position.
+ *
+ * @return current character
+ * @throws StringIndexOutOfBoundsException if at end of string
+ */
+ char ch() {
+ return s.charAt(i);
+ }
+ }
+
+ /**
+ * Parses a value from the input string at the current cursor position.
+ * This can be an array literal, a quoted string, or a simple atom (number, word).
+ *
+ * @param c The cursor for parsing.
+ * @return The parsed value (could be List, String, Number).
+ */
+ Object parseValue(Cursor c) {
+ skipWs(c);
+ if (c.end()) return "";
+ char ch = c.ch();
+ if (ch == '[') return parseArray(c);
+ if (ch == '\"') return parseQuoted(c);
+ return parseAtom(c);
+ }
+
+ /**
+ * Skips whitespace characters in the input string.
+ *
+ * @param c The cursor to advance.
+ */
+ private static void skipWs(Cursor c) {
+ while (!c.end()) {
+ char ch = c.ch();
+ if (ch == ' ' || ch == '\t' || ch == '\r' || ch == '\n') c.skip();
+ else break;
+ }
+ }
+
+ /**
+ * Parses an array literal from the input, including nested arrays.
+ *
+ * @param c The cursor (positioned at '[' at entry).
+ * @return A List representing the parsed array.
+ */
+ private Object parseArray(Cursor c) {
+ // assumes current char is '['
+ c.skip(); // skip '['
+ List out = new ArrayList<>();
+ skipWs(c);
+ while (!c.end()) {
+ if (c.ch() == ']') {
+ c.skip(); // skip ']'
+ break;
+ }
+ Object v = parseValue(c);
+ out.add(v);
+ skipWs(c);
+ if (!c.end() && c.ch() == ',') {
+ c.skip();
+ skipWs(c);
+ }
+ }
+ return out;
+ }
+
+ /**
+ * Parses a quoted string literal, handling escape characters.
+ *
+ * @param c The cursor (positioned at '"' at entry).
+ * @return The parsed string value.
+ */
+ private static String parseQuoted(Cursor c) {
+ // assumes current char is '"'
+ c.skip(); // skip opening quote
+ StringBuilder sb = new StringBuilder();
+ while (!c.end()) {
+ char ch = c.ch();
+ c.skip();
+ if (ch == '\\') {
+ if (c.end()) break;
+ char esc = c.ch();
+ c.skip();
+ switch (esc) {
+ case 'n' -> sb.append('\n');
+ case 'r' -> sb.append('\r');
+ case 't' -> sb.append('\t');
+ case '\"' -> sb.append('\"');
+ case '\\' -> sb.append('\\');
+ default -> sb.append(esc);
+ }
+ } else if (ch == '\"') {
+ break;
+ } else {
+ sb.append(ch);
+ }
+ }
+ return sb.toString();
+ }
+
+ /**
+ * Parses an atom (number, hexadecimal, binary, or plain string token).
+ *
+ * @param c The cursor.
+ * @return An Integer, Double, or String, depending on the content.
+ */
+ private static Object parseAtom(Cursor c) {
+ StringBuilder sb = new StringBuilder();
+ while (!c.end()) {
+ char ch = c.ch();
+ if (ch == ',' || ch == ']' || ch == ' ' || ch == '\t' || ch == '\r' || ch == '\n') break;
+ sb.append(ch);
+ c.skip();
+ }
+ String token = sb.toString();
+ // try number
+ try {
+ if (token.startsWith("0x") || token.startsWith("0X")) {
+ return Integer.parseInt(token.substring(2), 16);
+ }
+ if (token.startsWith("0b") || token.startsWith("0B")) {
+ return Integer.parseInt(token.substring(2), 2);
+ }
+ if (token.contains(".")) {
+ return Double.parseDouble(token);
+ }
+ return Integer.parseInt(token);
+ } catch (NumberFormatException e) {
+ // fallback as string
+ return token;
+ }
+ }
+
+ // ---------------------- helpers for immutability/mutability ----------------------
+
+ /**
+ * Recursively creates an unmodifiable copy of a list, with all nested lists also unmodifiable.
+ *
+ * @param l The list to make unmodifiable.
+ * @return An unmodifiable deep copy of the list.
+ */
+ List> deepUnmodifiable(List> l) {
+ List out = new ArrayList<>(l.size());
+ for (Object v : l) out.add(deepUnmodifiableObject(v));
+ return Collections.unmodifiableList(out);
+ }
+
+ /**
+ * Helper method for {@link #deepUnmodifiable(List)}. Recursively processes each element.
+ *
+ * @param v The object to process.
+ * @return Unmodifiable list if input is a list, otherwise the value itself.
+ */
+ Object deepUnmodifiableObject(Object v) {
+ if (v instanceof List> l) {
+ return deepUnmodifiable(l);
+ }
+ return v;
+ }
+
+ /**
+ * Create a deep mutable copy of a nested List structure, preserving element values.
+ * Nested lists are turned into {@link java.util.ArrayList} so they can be modified by ARR_SET.
+ *
+ * @param l The source list.
+ * @return Deep mutable copy of the list.
+ */
+ private static java.util.List> deepMutable(java.util.List> l) {
+ java.util.List out = new java.util.ArrayList<>(l.size());
+ for (Object v : l) out.add(deepMutableObject(v));
+ return out;
+ }
+
+ /**
+ * Helper method for {@link #deepMutable(List)}. Recursively processes each element.
+ *
+ * @param v The object to process.
+ * @return Mutable list if input is a list, otherwise the value itself.
+ */
+ private static Object deepMutableObject(Object v) {
+ if (v instanceof java.util.List> l) {
+ return deepMutable(l);
+ }
+ return v;
+ }
}
diff --git a/src/main/java/org/jcnc/snow/vm/commands/system/control/SyscallCommand.java b/src/main/java/org/jcnc/snow/vm/commands/system/control/SyscallCommand.java
index 90caf8b..80428fa 100644
--- a/src/main/java/org/jcnc/snow/vm/commands/system/control/SyscallCommand.java
+++ b/src/main/java/org/jcnc/snow/vm/commands/system/control/SyscallCommand.java
@@ -46,6 +46,97 @@ import static java.nio.file.StandardOpenOption.*;
*/
public class SyscallCommand implements Command {
+ /**
+ * 根据传入的文件打开标志,构造 NIO {@link OpenOption} 集合。
+ *
+ * 本方法负责将底层虚拟机传递的 flags 整数型位域,转换为 Java NIO 标准的文件打开选项集合,
+ * 以支持文件读、写、创建、截断、追加等多种访问场景。
+ * 常用于 SYSCALL 的 OPEN 子命令。
+ *
+ *
+ * @param flags 文件打开模式标志。各标志可组合使用,具体含义请参见虚拟机文档。
+ * @return 转换后的 OpenOption 集合,可直接用于 FileChannel.open 等 NIO 方法
+ */
+ private static Set flagsToOptions(int flags) {
+ Set opts = new HashSet<>();
+ // 如果有写入标志,则添加WRITE,否则默认为READ。
+ if ((flags & 0x1) != 0) opts.add(WRITE);
+ else opts.add(READ);
+ // 如果包含创建标志,允许创建文件。
+ if ((flags & 0x40) != 0) opts.add(CREATE);
+ // 包含截断标志,打开时清空内容。
+ if ((flags & 0x200) != 0) opts.add(TRUNCATE_EXISTING);
+ // 包含追加标志,文件写入时追加到末尾。
+ if ((flags & 0x400) != 0) opts.add(APPEND);
+ return opts;
+ }
+
+ /**
+ * 捕获所有异常并统一处理,操作数栈压入 -1 代表本次系统调用失败。
+ *
+ * 本方法是全局错误屏障,任何命令异常都会转换为虚拟机通用的失败信号,
+ * 保证上层调用逻辑不会被异常打断。实际应用中可拓展错误码机制。
+ *
+ *
+ * @param stack 操作数栈,将失败信号写入此栈
+ * @param e 抛出的异常对象,可在调试时输出日志
+ */
+ private static void pushErr(OperandStack stack, Exception e) {
+ stack.push(-1);
+ System.err.println("Syscall exception: " + e);
+ }
+
+ /**
+ * 控制台输出通用方法,支持基本类型、字节数组、任意数组、对象等。
+ *
+ * 该方法用于 SYSCALL PRINT/PRINTLN,将任意类型对象转为易读字符串输出到标准输出流。
+ * 字节数组自动按 UTF-8 解码,其它原生数组按格式化字符串输出。
+ *
+ *
+ * @param obj 待输出的内容,可以为任何类型(如基本类型、byte[]、数组、对象等)
+ * @param newline 是否自动换行。如果为 true,则在输出后换行;否则直接输出。
+ */
+ private static void output(Object obj, boolean newline) {
+ String str;
+ if (obj == null) {
+ str = "null";
+ } else if (obj instanceof byte[] bytes) {
+ // 字节数组作为文本输出
+ str = new String(bytes);
+ } else if (obj.getClass().isArray()) {
+ // 其它数组格式化输出
+ str = arrayToString(obj);
+ } else {
+ str = obj.toString();
+ }
+ if (newline) System.out.println(str);
+ else System.out.print(str);
+ }
+
+ /**
+ * 将各种原生数组和对象数组转换为可读字符串,便于控制台输出和调试。
+ *
+ * 本方法针对 int、long、double、float、short、char、byte、boolean 等所有原生数组类型
+ * 以及对象数组都能正确格式化,统一输出格式风格,避免显示为类型 hashCode。
+ * 若为不支持的类型,返回通用提示字符串。
+ *
+ *
+ * @param array 任意原生数组或对象数组
+ * @return 该数组的可读字符串表示
+ */
+ private static String arrayToString(Object array) {
+ if (array instanceof int[] a) return Arrays.toString(a);
+ if (array instanceof long[] a) return Arrays.toString(a);
+ if (array instanceof double[] a) return Arrays.toString(a);
+ if (array instanceof float[] a) return Arrays.toString(a);
+ if (array instanceof short[] a) return Arrays.toString(a);
+ if (array instanceof char[] a) return Arrays.toString(a);
+ if (array instanceof byte[] a) return Arrays.toString(a);
+ if (array instanceof boolean[] a) return Arrays.toString(a);
+ if (array instanceof Object[] a) return Arrays.deepToString(a);
+ return "Unsupported array";
+ }
+
/**
* 分发并执行 SYSCALL 子命令,根据子命令类型从操作数栈取出参数、操作底层资源,并将结果压回栈顶。
*
@@ -226,6 +317,105 @@ public class SyscallCommand implements Command {
sel.close();
}
+ // 数组元素访问:arr[idx] —— 保留所有类型精度(byte/short/int/long/float/double/boolean/string/ref)
+ case "ARR_GET" -> {
+ /*
+ 执行数组下标访问操作 arr[idx],并将对应元素以真实类型压入操作数栈。
+
+ 支持 List 与任意原生数组类型(int[]、double[] 等);
+ idx 参数支持 Number/String 类型,自动转 int;
+ 下标越界将抛出异常,非数组类型将报错;
+ 返回结果保持类型精度:byte/short/int/long/float/double/boolean/string/object;
+ boolean 元素以 1/0 压栈,string/引用直接压栈;
+
+
+ 异常与出错行为:
+
+ 索引类型非法、目标非数组/列表,将抛 IllegalArgumentException;
+ 索引越界,将抛 IndexOutOfBoundsException;
+
+ */
+ Object idxObj = stack.pop();
+ Object arrObj = stack.pop();
+ int idx;
+ if (idxObj instanceof Number n) idx = n.intValue();
+ else if (idxObj instanceof String s) idx = Integer.parseInt(s.trim());
+ else throw new IllegalArgumentException("ARR_GET: invalid index type " + idxObj);
+
+ Object elem;
+ if (arrObj instanceof java.util.List> list) {
+ if (idx < 0 || idx >= list.size())
+ throw new IndexOutOfBoundsException("数组下标越界: " + idx + " (长度 " + list.size() + ")");
+ elem = list.get(idx);
+ } else if (arrObj != null && arrObj.getClass().isArray()) {
+ int len = java.lang.reflect.Array.getLength(arrObj);
+ if (idx < 0 || idx >= len)
+ throw new IndexOutOfBoundsException("数组下标越界: " + idx + " (长度 " + len + ")");
+ elem = java.lang.reflect.Array.get(arrObj, idx);
+ } else {
+ throw new IllegalArgumentException("ARR_GET: not an array/list: " + arrObj);
+ }
+
+ // === 按真实类型压栈(byte/short/int/long/float/double/boolean/string/ref)===
+ if (elem instanceof Number n) {
+ if (elem instanceof Double) {
+ stack.push(n.doubleValue());
+ } else if (elem instanceof Float) {
+ stack.push(n.floatValue());
+ } else if (elem instanceof Long) {
+ stack.push(n.longValue());
+ } else if (elem instanceof Integer) {
+ stack.push(n.intValue());
+ } else if (elem instanceof Short) {
+ stack.push(n.shortValue());
+ } else if (elem instanceof Byte) {
+ stack.push(n.byteValue());
+ } else {
+ stack.push(n.intValue()); // 兜底
+ }
+ } else if (elem instanceof Boolean b) {
+ stack.push(b ? 1 : 0);
+ } else {
+ // string 或其它引用类型,直接返回
+ stack.push(elem);
+ }
+ }
+
+ case "ARR_SET" -> {
+ /*
+ arr[idx] = value
+ 支持 List 和所有原生数组类型(int[], double[], ...)
+ 参数顺序:栈顶 value、idx、arr
+ 不返回值(成功/失败由异常控制)
+ */
+ Object value = stack.pop();
+ Object idxObj = stack.pop();
+ Object arrObj = stack.pop();
+ int idx;
+ if (idxObj instanceof Number n) idx = n.intValue();
+ else if (idxObj instanceof String s) idx = Integer.parseInt(s.trim());
+ else throw new IllegalArgumentException("ARR_SET: invalid index type " + idxObj);
+
+ if (arrObj instanceof java.util.List> list) {
+ // 必须是可变 List
+ @SuppressWarnings("unchecked")
+ java.util.List mlist = (java.util.List) list;
+ if (idx < 0 || idx >= mlist.size())
+ throw new IndexOutOfBoundsException("数组下标越界: " + idx + " (长度 " + mlist.size() + ")");
+ mlist.set(idx, value);
+ } else if (arrObj != null && arrObj.getClass().isArray()) {
+ int len = java.lang.reflect.Array.getLength(arrObj);
+ if (idx < 0 || idx >= len)
+ throw new IndexOutOfBoundsException("数组下标越界: " + idx + " (长度 " + len + ")");
+ java.lang.reflect.Array.set(arrObj, idx, value);
+ } else {
+ throw new IllegalArgumentException("ARR_SET: not an array/list: " + arrObj);
+ }
+ // 操作成功,push 0 作为 ok 信号;不需要返回时可省略
+ stack.push(0);
+ }
+
+
// 控制台输出
case "PRINT" -> {
Object dataObj = stack.pop();
@@ -246,95 +436,4 @@ public class SyscallCommand implements Command {
return pc + 1;
}
-
- /**
- * 根据传入的文件打开标志,构造 NIO {@link OpenOption} 集合。
- *
- * 本方法负责将底层虚拟机传递的 flags 整数型位域,转换为 Java NIO 标准的文件打开选项集合,
- * 以支持文件读、写、创建、截断、追加等多种访问场景。
- * 常用于 SYSCALL 的 OPEN 子命令。
- *
- *
- * @param flags 文件打开模式标志。各标志可组合使用,具体含义请参见虚拟机文档。
- * @return 转换后的 OpenOption 集合,可直接用于 FileChannel.open 等 NIO 方法
- */
- private static Set flagsToOptions(int flags) {
- Set opts = new HashSet<>();
- // 如果有写入标志,则添加WRITE,否则默认为READ。
- if ((flags & 0x1) != 0) opts.add(WRITE);
- else opts.add(READ);
- // 如果包含创建标志,允许创建文件。
- if ((flags & 0x40) != 0) opts.add(CREATE);
- // 包含截断标志,打开时清空内容。
- if ((flags & 0x200) != 0) opts.add(TRUNCATE_EXISTING);
- // 包含追加标志,文件写入时追加到末尾。
- if ((flags & 0x400) != 0) opts.add(APPEND);
- return opts;
- }
-
- /**
- * 捕获所有异常并统一处理,操作数栈压入 -1 代表本次系统调用失败。
- *
- * 本方法是全局错误屏障,任何命令异常都会转换为虚拟机通用的失败信号,
- * 保证上层调用逻辑不会被异常打断。实际应用中可拓展错误码机制。
- *
- *
- * @param stack 操作数栈,将失败信号写入此栈
- * @param e 抛出的异常对象,可在调试时输出日志
- */
- private static void pushErr(OperandStack stack, Exception e) {
- stack.push(-1);
- System.err.println("Syscall exception: " + e);
- }
-
- /**
- * 控制台输出通用方法,支持基本类型、字节数组、任意数组、对象等。
- *
- * 该方法用于 SYSCALL PRINT/PRINTLN,将任意类型对象转为易读字符串输出到标准输出流。
- * 字节数组自动按 UTF-8 解码,其它原生数组按格式化字符串输出。
- *
- *
- * @param obj 待输出的内容,可以为任何类型(如基本类型、byte[]、数组、对象等)
- * @param newline 是否自动换行。如果为 true,则在输出后换行;否则直接输出。
- */
- private static void output(Object obj, boolean newline) {
- String str;
- if (obj == null) {
- str = "null";
- } else if (obj instanceof byte[] bytes) {
- // 字节数组作为文本输出
- str = new String(bytes);
- } else if (obj.getClass().isArray()) {
- // 其它数组格式化输出
- str = arrayToString(obj);
- } else {
- str = obj.toString();
- }
- if (newline) System.out.println(str);
- else System.out.print(str);
- }
-
- /**
- * 将各种原生数组和对象数组转换为可读字符串,便于控制台输出和调试。
- *
- * 本方法针对 int、long、double、float、short、char、byte、boolean 等所有原生数组类型
- * 以及对象数组都能正确格式化,统一输出格式风格,避免显示为类型 hashCode。
- * 若为不支持的类型,返回通用提示字符串。
- *
- *
- * @param array 任意原生数组或对象数组
- * @return 该数组的可读字符串表示
- */
- private static String arrayToString(Object array) {
- if (array instanceof int[] a) return Arrays.toString(a);
- if (array instanceof long[] a) return Arrays.toString(a);
- if (array instanceof double[] a) return Arrays.toString(a);
- if (array instanceof float[] a) return Arrays.toString(a);
- if (array instanceof short[] a) return Arrays.toString(a);
- if (array instanceof char[] a) return Arrays.toString(a);
- if (array instanceof byte[] a) return Arrays.toString(a);
- if (array instanceof boolean[] a) return Arrays.toString(a);
- if (array instanceof Object[] a) return Arrays.deepToString(a);
- return "Unsupported array";
- }
}
diff --git a/src/main/java/org/jcnc/snow/vm/engine/VirtualMachineEngine.java b/src/main/java/org/jcnc/snow/vm/engine/VirtualMachineEngine.java
index 704822b..aa6d227 100644
--- a/src/main/java/org/jcnc/snow/vm/engine/VirtualMachineEngine.java
+++ b/src/main/java/org/jcnc/snow/vm/engine/VirtualMachineEngine.java
@@ -1,6 +1,5 @@
package org.jcnc.snow.vm.engine;
-import org.jcnc.snow.common.Mode;
import org.jcnc.snow.vm.execution.CommandExecutionHandler;
import org.jcnc.snow.vm.module.*;
@@ -51,8 +50,6 @@ public class VirtualMachineEngine {
/**
* Builds a VM engine with fresh runtime structures.
- *
- * @param vmMode execution mode (DEBUG / RUN)
*/
public VirtualMachineEngine() {
this.operandStack = new OperandStack();