slotMap,
String currentFn) {
- // 获取操作数所在槽号
- int slotId = slotMap.get((IRVirtualRegister) ins.operands().getFirst());
- // 加载操作数到虚拟机栈顶
- out.emit(OpHelper.opcode("I_LOAD") + " " + slotId);
- // 生成对应的一元运算操作码(如取负等)
- out.emit(OpHelper.opcode(IROpCodeMapper.toVMOp(ins.op())));
- // 将结果存储到目标寄存器槽
- out.emit(OpHelper.opcode("I_STORE") + " " + slotMap.get(ins.dest()));
+
+ /* -------- 1. 源槽位与类型 -------- */
+ int srcSlot = slotMap.get((IRVirtualRegister) ins.operands().getFirst());
+ char prefix = out.getSlotType(srcSlot); // 未登记则返回默认 'I'
+
+ String loadOp = prefix + "_LOAD";
+ String storeOp = prefix + "_STORE";
+
+ /* -------- 2. 指令序列 -------- */
+ // 2-A. 加载操作数
+ out.emit(OpHelper.opcode(loadOp) +
+ " " + srcSlot);
+
+ // 2-B. 执行具体一元运算(NEG、NOT…)
+ out.emit(OpHelper.opcode(
+ IROpCodeMapper.toVMOp(ins.op())));
+
+ // 2-C. 存结果到目标槽
+ int destSlot = slotMap.get(ins.dest());
+ out.emit(OpHelper.opcode(storeOp) +
+ " " + destSlot);
+
+ /* -------- 3. 更新目标槽类型 -------- */
+ out.setSlotType(destSlot, prefix);
}
}
diff --git a/src/main/java/org/jcnc/snow/compiler/ir/builder/ExpressionBuilder.java b/src/main/java/org/jcnc/snow/compiler/ir/builder/ExpressionBuilder.java
index a658d9d..11288ba 100644
--- a/src/main/java/org/jcnc/snow/compiler/ir/builder/ExpressionBuilder.java
+++ b/src/main/java/org/jcnc/snow/compiler/ir/builder/ExpressionBuilder.java
@@ -3,6 +3,7 @@ package org.jcnc.snow.compiler.ir.builder;
import org.jcnc.snow.compiler.ir.core.IROpCode;
import org.jcnc.snow.compiler.ir.instruction.CallInstruction;
import org.jcnc.snow.compiler.ir.instruction.LoadConstInstruction;
+import org.jcnc.snow.compiler.ir.instruction.UnaryOperationInstruction;
import org.jcnc.snow.compiler.ir.value.IRConstant;
import org.jcnc.snow.compiler.ir.value.IRVirtualRegister;
import org.jcnc.snow.compiler.ir.utils.ExpressionUtils;
@@ -32,8 +33,15 @@ public record ExpressionBuilder(IRContext ctx) {
* 会根据节点的实际类型分别处理:
*
* - 数字字面量:新建常量寄存器
+ * - 布尔字面量:生成值为 0 或 1 的常量寄存器
* - 标识符:查找当前作用域中的寄存器
* - 二元表达式:递归处理子表达式并进行相应运算
+ * - 一元运算符:
+ *
+ * -x(取负,生成 NEG_I32 指令)与
+ * - code>!x(逻辑非,转换为
x == 0 比较指令)
+ *
+ *
* - 函数调用:生成对应的Call指令
* - 其它类型不支持,抛出异常
*
@@ -42,6 +50,7 @@ public record ExpressionBuilder(IRContext ctx) {
* @return 该表达式的计算结果寄存器
* @throws IllegalStateException 如果遇到未定义的标识符或不支持的表达式类型
*/
+
public IRVirtualRegister build(ExpressionNode expr) {
return switch (expr) {
// 数字字面量
@@ -59,11 +68,33 @@ public record ExpressionBuilder(IRContext ctx) {
case BinaryExpressionNode bin -> buildBinary(bin);
// 函数调用
case CallExpressionNode call -> buildCall(call);
+ case UnaryExpressionNode u -> buildUnary(u);
default -> throw new IllegalStateException(
"不支持的表达式类型: " + expr.getClass().getSimpleName());
};
}
+ /** 处理一元表达式 */
+ private IRVirtualRegister buildUnary(UnaryExpressionNode un) {
+ String op = un.operator();
+ IRVirtualRegister val = build(un.operand());
+
+ // -x → NEG_*(根据类型自动选择位宽)
+ if (op.equals("-")) {
+ IRVirtualRegister dest = ctx.newRegister();
+ IROpCode code = ExpressionUtils.negOp(un.operand());
+ ctx.addInstruction(new UnaryOperationInstruction(code, dest, val));
+ return dest;
+ }
+
+ // !x → (x == 0)
+ if (op.equals("!")) {
+ IRVirtualRegister zero = InstructionFactory.loadConst(ctx, 0);
+ return InstructionFactory.binOp(ctx, IROpCode.CMP_EQ, val, zero);
+ }
+
+ throw new IllegalStateException("未知一元运算符: " + op);
+ }
/**
* 直接将表达式计算结果写入指定的目标寄存器(dest)。
@@ -154,7 +185,7 @@ public record ExpressionBuilder(IRContext ctx) {
.toList();
// 获取完整调用目标名称(支持成员/模块调用和普通调用)
String fullName = switch (call.callee()) {
- case MemberExpressionNode member when member.object() instanceof IdentifierNode mod ->
+ case MemberExpressionNode member when member.object() instanceof IdentifierNode _ ->
((IdentifierNode)member.object()).name() + "." + member.member();
case IdentifierNode id -> id.name();
default -> throw new IllegalStateException("不支持的调用目标: " + call.callee().getClass().getSimpleName());
diff --git a/src/main/java/org/jcnc/snow/compiler/ir/builder/IRContext.java b/src/main/java/org/jcnc/snow/compiler/ir/builder/IRContext.java
index 7b7232d..4c5078d 100644
--- a/src/main/java/org/jcnc/snow/compiler/ir/builder/IRContext.java
+++ b/src/main/java/org/jcnc/snow/compiler/ir/builder/IRContext.java
@@ -19,7 +19,7 @@ import org.jcnc.snow.compiler.ir.value.IRVirtualRegister;
*/
public class IRContext {
- /* ➡ 新增:生成唯一标签用 */
+ /* 生成唯一标签用 */
private int labelCounter = 0;
/**
* 当前正在构建的 IRFunction 对象,所有指令将添加至此
diff --git a/src/main/java/org/jcnc/snow/compiler/ir/builder/InstructionFactory.java b/src/main/java/org/jcnc/snow/compiler/ir/builder/InstructionFactory.java
index b0a9993..9b7a467 100644
--- a/src/main/java/org/jcnc/snow/compiler/ir/builder/InstructionFactory.java
+++ b/src/main/java/org/jcnc/snow/compiler/ir/builder/InstructionFactory.java
@@ -78,7 +78,7 @@ public class InstructionFactory {
}
/**
- * 简易 Move 指令(src → dest)。若寄存器相同也安全。
+ * Move 指令(src → dest)。若寄存器相同也安全。
*
* 实现方式:dest = src + 0(即加上常量 0)。
*
@@ -88,7 +88,11 @@ public class InstructionFactory {
* @param dest 目标寄存器
*/
public static void move(IRContext ctx, IRVirtualRegister src, IRVirtualRegister dest) {
- /* 采用 “dest = src + 0” 的最简实现 */
+ // 自赋值无需任何操作,避免生成多余的常量 0 寄存器
+ if (src == dest) {
+ return;
+ }
+ // 回退实现:dest = src + 0
IRVirtualRegister zero = loadConst(ctx, 0);
ctx.addInstruction(new BinaryOperationInstruction(IROpCode.ADD_I32, dest, src, zero));
}
diff --git a/src/main/java/org/jcnc/snow/compiler/ir/builder/StatementBuilder.java b/src/main/java/org/jcnc/snow/compiler/ir/builder/StatementBuilder.java
index 18f750b..8a95ca5 100644
--- a/src/main/java/org/jcnc/snow/compiler/ir/builder/StatementBuilder.java
+++ b/src/main/java/org/jcnc/snow/compiler/ir/builder/StatementBuilder.java
@@ -8,6 +8,8 @@ import org.jcnc.snow.compiler.parser.ast.*;
import org.jcnc.snow.compiler.parser.ast.base.ExpressionNode;
import org.jcnc.snow.compiler.parser.ast.base.StatementNode;
+import java.util.Locale;
+
/**
* StatementBuilder —— 将 AST 语句节点 ({@link StatementNode}) 转换为 IR 指令序列的构建器。
*
@@ -177,4 +179,16 @@ public class StatementBuilder {
InstructionFactory.cmpJump(ctx, IROpCode.CMP_EQ, condReg, zero, falseLabel);
}
}
+
+ private static char typeSuffixFromType(String type) {
+ if (type == null) return '\0';
+ return switch (type.toLowerCase(Locale.ROOT)) {
+ case "byte" -> 'b';
+ case "short" -> 's';
+ case "long" -> 'l';
+ case "float" -> 'f';
+ case "double" -> 'd';
+ default -> '\0'; // 其余默认按 32-bit 整型处理
+ };
+ }
}
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 cc1e426..91155df 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
@@ -78,6 +78,30 @@ public class ExpressionUtils {
};
}
+ /**
+ * 根据表达式节点推断一元取负(-)运算应使用的操作码。
+ *
+ *
优先级与 {@link #resolveOpCode} 使用的类型提升规则保持一致:
+ *
+ * - 字面量或标识符带显式后缀时,直接以后缀决定位宽;
+ * - 未显式指定时,默认使用 32 位整型 {@link IROpCode#NEG_I32}。
+ *
+ *
+ * @param operand 一元取负运算的操作数
+ * @return 匹配的 {@link IROpCode}
+ */
+ public static IROpCode negOp(ExpressionNode operand) {
+ char t = typeChar(operand);
+ return switch (t) {
+ case 'b' -> IROpCode.NEG_B8;
+ case 's' -> IROpCode.NEG_S16;
+ case 'l' -> IROpCode.NEG_L64;
+ case 'f' -> IROpCode.NEG_F32;
+ case 'd' -> IROpCode.NEG_D64;
+ default -> IROpCode.NEG_I32;
+ };
+ }
+
/* =================== 类型推断与操作符匹配 =================== */
/**
diff --git a/src/main/java/org/jcnc/snow/compiler/lexer/scanners/OperatorTokenScanner.java b/src/main/java/org/jcnc/snow/compiler/lexer/scanners/OperatorTokenScanner.java
index 6b8dde9..ec2f2bf 100644
--- a/src/main/java/org/jcnc/snow/compiler/lexer/scanners/OperatorTokenScanner.java
+++ b/src/main/java/org/jcnc/snow/compiler/lexer/scanners/OperatorTokenScanner.java
@@ -5,26 +5,29 @@ import org.jcnc.snow.compiler.lexer.token.Token;
import org.jcnc.snow.compiler.lexer.token.TokenType;
/**
- * 运算符扫描器:识别逻辑与比较运算符,包括单字符和双字符组合。
- *
- * 支持的运算符包括:
+ * 运算符扫描器(OperatorTokenScanner)
+ *
+ *
负责在词法分析阶段识别由 = ! < > | & % 等字符
+ * 起始的单字符或双字符运算符,并生成相应 {@link Token}:
+ *
*
- * - 赋值与比较:=、==、!=
- * - 关系运算符:>、>=、<、<=
- * - 逻辑运算符:&&、||
+ * - 赋值 / 比较:{@code =}, {@code ==}, {@code !=}
+ * - 关系运算:{@code >}, {@code >=}, {@code <}, {@code <=}
+ * - 逻辑运算:{@code &&}, {@code ||}
+ * - 取模运算:{@code %}
+ * - 逻辑非:{@code !}
*
- *
- * 不符合上述组合的字符会返回 {@code UNKNOWN} 类型的 Token。
+ *
+ *
如果无法匹配到合法组合,将返回 {@link TokenType#UNKNOWN}。
*/
public class OperatorTokenScanner extends AbstractTokenScanner {
/**
- * 判断是否可以处理当前位置的字符。
- * 运算符扫描器关注的起始字符包括:=、!、<、>、|、&
+ * 判断当前字符是否可能是运算符的起始字符。
*
* @param c 当前字符
- * @param ctx 当前词法上下文
- * @return 如果是潜在的运算符起始字符,则返回 true
+ * @param ctx 词法上下文
+ * @return 若是关注的起始字符则返回 {@code true}
*/
@Override
public boolean canHandle(char c, LexerContext ctx) {
@@ -32,14 +35,12 @@ public class OperatorTokenScanner extends AbstractTokenScanner {
}
/**
- * 扫描并识别运算符 Token。
- * 支持组合运算符判断,如 ==、!=、>= 等,
- * 若无法匹配组合形式则退回单字符形式。
+ * 按最长匹配优先原则扫描并生成运算符 token。
*
- * @param ctx 词法上下文
+ * @param ctx 词法上下文
* @param line 当前行号
- * @param col 当前列号
- * @return 对应的运算符 Token,无法识别的运算符返回 {@code UNKNOWN}
+ * @param col 当前列号
+ * @return 已识别的 {@link Token}
*/
@Override
protected Token scanToken(LexerContext ctx, int line, int col) {
@@ -51,64 +52,71 @@ public class OperatorTokenScanner extends AbstractTokenScanner {
case '=':
if (ctx.match('=')) {
lexeme = "==";
- type = TokenType.DOUBLE_EQUALS;
+ type = TokenType.DOUBLE_EQUALS;
} else {
lexeme = "=";
- type = TokenType.EQUALS;
+ type = TokenType.EQUALS;
}
break;
+
case '!':
if (ctx.match('=')) {
lexeme = "!=";
- type = TokenType.NOT_EQUALS;
+ type = TokenType.NOT_EQUALS;
} else {
lexeme = "!";
- type = TokenType.UNKNOWN;
+ type = TokenType.NOT;
}
break;
+
case '>':
if (ctx.match('=')) {
lexeme = ">=";
- type = TokenType.GREATER_EQUAL;
+ type = TokenType.GREATER_EQUAL;
} else {
lexeme = ">";
- type = TokenType.GREATER_THAN;
+ type = TokenType.GREATER_THAN;
}
break;
+
case '<':
if (ctx.match('=')) {
lexeme = "<=";
- type = TokenType.LESS_EQUAL;
+ type = TokenType.LESS_EQUAL;
} else {
lexeme = "<";
- type = TokenType.LESS_THAN;
+ type = TokenType.LESS_THAN;
}
break;
+
case '%':
lexeme = "%";
- type = TokenType.MODULO;
+ type = TokenType.MODULO;
break;
+
case '&':
if (ctx.match('&')) {
lexeme = "&&";
- type = TokenType.AND;
+ type = TokenType.AND;
} else {
lexeme = "&";
- type = TokenType.UNKNOWN;
+ type = TokenType.UNKNOWN;
}
break;
+
case '|':
if (ctx.match('|')) {
lexeme = "||";
- type = TokenType.OR;
+ type = TokenType.OR;
} else {
lexeme = "|";
- type = TokenType.UNKNOWN;
+ type = TokenType.UNKNOWN;
}
break;
+
default:
lexeme = String.valueOf(c);
- type = TokenType.UNKNOWN;
+ type = TokenType.UNKNOWN;
}
return new Token(type, lexeme, line, col);
diff --git a/src/main/java/org/jcnc/snow/compiler/lexer/token/TokenFactory.java b/src/main/java/org/jcnc/snow/compiler/lexer/token/TokenFactory.java
index 98155f6..e6463f4 100644
--- a/src/main/java/org/jcnc/snow/compiler/lexer/token/TokenFactory.java
+++ b/src/main/java/org/jcnc/snow/compiler/lexer/token/TokenFactory.java
@@ -19,9 +19,6 @@ import java.util.Set;
* 对不合法的词素自动标记为 UNKNOWN 类型。
*
*
- *
- * @author 你的名字
- * @version 1.0
*/
public class TokenFactory {
diff --git a/src/main/java/org/jcnc/snow/compiler/lexer/token/TokenType.java b/src/main/java/org/jcnc/snow/compiler/lexer/token/TokenType.java
index 4ff4394..554d48c 100644
--- a/src/main/java/org/jcnc/snow/compiler/lexer/token/TokenType.java
+++ b/src/main/java/org/jcnc/snow/compiler/lexer/token/TokenType.java
@@ -10,6 +10,7 @@ package org.jcnc.snow.compiler.lexer.token;
*/
public enum TokenType {
+ /* ---------- 基础 ---------- */
/** 普通标识符,如变量名、函数名等 */
IDENTIFIER,
@@ -19,14 +20,17 @@ public enum TokenType {
/** 内置类型名称(如 int、string、bool 等) */
TYPE,
+ /* ---------- 字面量 ---------- */
/** 布尔字面量 (true / false) */
BOOL_LITERAL,
+
/** 字符串字面量(如 "hello") */
STRING_LITERAL,
/** 数字字面量(整数或浮点数) */
NUMBER_LITERAL,
+ /* ---------- 分隔符 ---------- */
/** 冒号 ':' */
COLON,
@@ -36,6 +40,7 @@ public enum TokenType {
/** 点号 '.' */
DOT,
+ /* ---------- 运算符 ---------- */
/** 赋值符号 '=' */
EQUALS,
@@ -53,6 +58,9 @@ public enum TokenType {
/** 减号 '-' */
MINUS,
+ /** 取反 '!' */
+ NOT,
+
/** 左括号 '(' */
LPAREN,
diff --git a/src/main/java/org/jcnc/snow/compiler/parser/ast/BoolLiteralNode.java b/src/main/java/org/jcnc/snow/compiler/parser/ast/BoolLiteralNode.java
index a82b98c..15c7088 100644
--- a/src/main/java/org/jcnc/snow/compiler/parser/ast/BoolLiteralNode.java
+++ b/src/main/java/org/jcnc/snow/compiler/parser/ast/BoolLiteralNode.java
@@ -5,15 +5,13 @@ import org.jcnc.snow.compiler.parser.ast.base.ExpressionNode;
/**
* 表示布尔字面量(boolean literal)的抽象语法树(AST)节点。
*
- * 本类实现了 {@link ExpressionNode} 接口,用于在编译器前端构建语法分析过程中,
+ * 该纪录类实现 {@link ExpressionNode} 接口,用于在编译器前端构建语法分析过程中,
* 表达布尔类型的字面量常量(如 "true" 或 "false")。
*
+ *
+ * @param value 字面量的布尔值
*/
-public class BoolLiteralNode implements ExpressionNode {
- /**
- * 字面量的布尔值。
- */
- private final boolean value;
+public record BoolLiteralNode(boolean value) implements ExpressionNode {
/**
* 使用布尔字面量字符串构造一个 {@code BoolLiteralNode} 实例。
@@ -25,7 +23,7 @@ public class BoolLiteralNode implements ExpressionNode {
* @param lexeme 布尔字面量的字符串表示
*/
public BoolLiteralNode(String lexeme) {
- this.value = Boolean.parseBoolean(lexeme);
+ this(Boolean.parseBoolean(lexeme));
}
/**
diff --git a/src/main/java/org/jcnc/snow/compiler/parser/ast/UnaryExpressionNode.java b/src/main/java/org/jcnc/snow/compiler/parser/ast/UnaryExpressionNode.java
new file mode 100644
index 0000000..d956fc8
--- /dev/null
+++ b/src/main/java/org/jcnc/snow/compiler/parser/ast/UnaryExpressionNode.java
@@ -0,0 +1,31 @@
+package org.jcnc.snow.compiler.parser.ast;
+
+import org.jcnc.snow.compiler.parser.ast.base.ExpressionNode;
+
+/**
+ * {@code UnaryExpressionNode} —— 前缀一元运算 AST 节点。
+ *
+ * 代表两种受支持的一元前缀表达式:
+ *
+ * - 取负:{@code -x}
+ * - 逻辑非:{@code !x}
+ *
+ *
+ * {@link #equals(Object)}、{@link #hashCode()} 等方法。
+ *
+ * @param operator 一元运算符(仅 "-" 或 "!")
+ * @param operand 运算对象 / 右操作数
+ */
+public record UnaryExpressionNode(String operator,
+ ExpressionNode operand) implements ExpressionNode {
+
+ /**
+ * 生成调试友好的字符串表示,例如 {@code "-x"} 或 {@code "!flag"}。
+ *
+ * @return 一元表达式的串表示
+ */
+ @Override
+ public String toString() {
+ return operator + operand;
+ }
+}
diff --git a/src/main/java/org/jcnc/snow/compiler/parser/expression/PrattExpressionParser.java b/src/main/java/org/jcnc/snow/compiler/parser/expression/PrattExpressionParser.java
index 1a1d124..4f79274 100644
--- a/src/main/java/org/jcnc/snow/compiler/parser/expression/PrattExpressionParser.java
+++ b/src/main/java/org/jcnc/snow/compiler/parser/expression/PrattExpressionParser.java
@@ -45,6 +45,10 @@ public class PrattExpressionParser implements ExpressionParser {
prefixes.put(TokenType.STRING_LITERAL.name(), new StringLiteralParselet());
prefixes.put(TokenType.BOOL_LITERAL.name(), new BoolLiteralParselet());
+ // 注册一元前缀运算
+ prefixes.put(TokenType.MINUS.name(), new UnaryOperatorParselet());
+ prefixes.put(TokenType.NOT.name(), new UnaryOperatorParselet());
+
// 注册中缀解析器
infixes.put("+", new BinaryOperatorParselet(Precedence.SUM, true));
infixes.put("-", new BinaryOperatorParselet(Precedence.SUM, true));
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 5eb7efe..61f4dab 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
@@ -24,6 +24,9 @@ public enum Precedence {
*/
PRODUCT,
+ /** 一元前缀(-x !x) */
+ UNARY,
+
/**
* 函数调用、成员访问等最强绑定(例如 foo()、obj.prop)。
*/
diff --git a/src/main/java/org/jcnc/snow/compiler/parser/expression/UnaryOperatorParselet.java b/src/main/java/org/jcnc/snow/compiler/parser/expression/UnaryOperatorParselet.java
new file mode 100644
index 0000000..abfa97a
--- /dev/null
+++ b/src/main/java/org/jcnc/snow/compiler/parser/expression/UnaryOperatorParselet.java
@@ -0,0 +1,55 @@
+package org.jcnc.snow.compiler.parser.expression;
+
+import org.jcnc.snow.compiler.lexer.token.Token;
+import org.jcnc.snow.compiler.parser.ast.UnaryExpressionNode;
+import org.jcnc.snow.compiler.parser.ast.base.ExpressionNode;
+import org.jcnc.snow.compiler.parser.context.ParserContext;
+import org.jcnc.snow.compiler.parser.expression.base.PrefixParselet;
+
+/**
+ * {@code UnaryOperatorParselet} —— 前缀一元运算符的 Pratt 解析器。
+ *
+ * 当前 parselet 负责解析两种前缀运算:
+ *
+ * - 取负:{@code -x}
+ * - 逻辑非:{@code !x}
+ *
+ *
+ * 解析过程:
+ *
+ *
+ * - 该 parselet 在外层解析器已消费运算符 {@code token} 后被调用。
+ * - 以 {@link Precedence#UNARY} 作为 绑定强度 递归解析右侧子表达式,
+ * 保证任何更高优先级的表达式(括号、字面量等)优先归属右侧。
+ * - 最终生成 {@link UnaryExpressionNode} AST 节点,记录运算符与操作数。
+ *
+ *
+ * 此类仅负责语法结构的构建:
+ *
+ * - 类型正确性在 {@code UnaryExpressionAnalyzer} 中校验;
+ * - IR 生成在 {@code ExpressionBuilder.buildUnary} 中完成。
+ *
+ */
+public class UnaryOperatorParselet implements PrefixParselet {
+
+ /**
+ * 解析前缀一元表达式。
+ *
+ * @param ctx 当前解析上下文
+ * @param token 已被消费的运算符 Token(字面值应为 {@code "-" 或 "!"})
+ * @return 构建出的 {@link UnaryExpressionNode}
+ */
+ @Override
+ public ExpressionNode parse(ParserContext ctx, Token token) {
+ /* ------------------------------------------------------------
+ * 1. 以 UNARY 优先级递归解析操作数,避免错误结合顺序。
+ * ------------------------------------------------------------ */
+ ExpressionNode operand =
+ new PrattExpressionParser().parseExpression(ctx, Precedence.UNARY);
+
+ /* ------------------------------------------------------------
+ * 2. 封装成 AST 节点并返回。
+ * ------------------------------------------------------------ */
+ return new UnaryExpressionNode(token.getLexeme(), operand);
+ }
+}
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 372387c..af2251e 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
@@ -140,7 +140,6 @@ public class FunctionParser implements TopLevelParser {
private void parseFunctionFooter(TokenStream ts) {
ts.expect("end");
ts.expect("function");
- ts.expectType(TokenType.NEWLINE);
}
/**
diff --git a/src/main/java/org/jcnc/snow/compiler/parser/utils/ASTJsonSerializer.java b/src/main/java/org/jcnc/snow/compiler/parser/utils/ASTJsonSerializer.java
index 87dc160..aec4b50 100644
--- a/src/main/java/org/jcnc/snow/compiler/parser/utils/ASTJsonSerializer.java
+++ b/src/main/java/org/jcnc/snow/compiler/parser/utils/ASTJsonSerializer.java
@@ -13,7 +13,7 @@ import java.util.*;
* 并可借助 {@code JSONParser.toJson(Object)} 方法将其序列化为 JSON 字符串,用于调试、
* 可视化或跨语言数据传输。
*
- * 支持的节点类型包括:
+ * 支持的节点类型包括(新增对 {@code BoolLiteralNode}、{@code UnaryExpressionNode} 的完整支持):
*
* - {@link ModuleNode}
* - {@link FunctionNode}
@@ -23,7 +23,9 @@ import java.util.*;
* - {@link LoopNode}
* - {@link ReturnNode}
* - {@link ExpressionStatementNode}
- * - 各类 {@link ExpressionNode} 子类型,如 {@code BinaryExpressionNode}, {@code IdentifierNode} 等
+ * - {@link BoolLiteralNode}
+ * - {@link UnaryExpressionNode}
+ * - 以及各类 {@link ExpressionNode} 子类型,如 {@code BinaryExpressionNode}, {@code IdentifierNode} 等
*
*/
public class ASTJsonSerializer {
@@ -174,19 +176,32 @@ public class ASTJsonSerializer {
*/
private static Object exprToMap(ExpressionNode expr) {
return switch (expr) {
+ // 二元表达式
case BinaryExpressionNode(ExpressionNode left, String operator, ExpressionNode right) -> exprMap("BinaryExpression",
"left", exprToMap(left),
"operator", operator,
"right", exprToMap(right)
);
+ // 一元表达式
+ case UnaryExpressionNode(String operator, ExpressionNode operand) -> exprMap("UnaryExpression",
+ "operator", operator,
+ "operand", exprToMap(operand)
+ );
+ // 布尔字面量
+ case BoolLiteralNode(boolean value) -> exprMap("BoolLiteral", "value", value);
+ // 标识符
case IdentifierNode(String name) -> exprMap("Identifier", "name", name);
+ // 数字字面量
case NumberLiteralNode(String value) -> exprMap("NumberLiteral", "value", value);
+ // 字符串字面量
case StringLiteralNode(String value) -> exprMap("StringLiteral", "value", value);
- case CallExpressionNode(ExpressionNode callee, List arguments, int line, int column, String file) -> {
+ // 调用表达式
+ case CallExpressionNode(ExpressionNode callee, List arguments, _, _, _) -> {
List