From 7bc2ec6ebec9d129e20f567d0e77beb2a87556e4 Mon Sep 17 00:00:00 2001 From: Luke Date: Thu, 12 Jun 2025 14:15:08 +0800 Subject: [PATCH 01/24] =?UTF-8?q?chore:=20=E5=A2=9E=E5=8A=A0Demo6=E9=85=8D?= =?UTF-8?q?=E7=BD=AE=E6=96=87=E4=BB=B6?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .run/Demo6.run.xml | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) create mode 100644 .run/Demo6.run.xml diff --git a/.run/Demo6.run.xml b/.run/Demo6.run.xml new file mode 100644 index 0000000..236a33b --- /dev/null +++ b/.run/Demo6.run.xml @@ -0,0 +1,17 @@ + + + + \ No newline at end of file From c15f54e611398640f1051cbca2136e9a1655233c Mon Sep 17 00:00:00 2001 From: Luke Date: Thu, 12 Jun 2025 14:15:21 +0800 Subject: [PATCH 02/24] =?UTF-8?q?test:=20=E5=A2=9E=E5=8A=A0Demo6?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- playground/Demo6/Main.snow | 10 ++++++++++ 1 file changed, 10 insertions(+) create mode 100644 playground/Demo6/Main.snow diff --git a/playground/Demo6/Main.snow b/playground/Demo6/Main.snow new file mode 100644 index 0000000..829a49f --- /dev/null +++ b/playground/Demo6/Main.snow @@ -0,0 +1,10 @@ +module: Main + function: main + parameter: + return_type: int + body: + + return 0 + end body + end function +end module \ No newline at end of file From c188eb478b6c9c7a0d2d6694f37e8ee3754284be Mon Sep 17 00:00:00 2001 From: Luke Date: Thu, 12 Jun 2025 15:23:51 +0800 Subject: [PATCH 03/24] =?UTF-8?q?feat:=20=E5=A2=9E=E5=8A=A0=E5=8F=96?= =?UTF-8?q?=E5=8F=8D=E7=AC=A6=E5=8F=B7?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../org/jcnc/snow/compiler/lexer/token/TokenType.java | 8 ++++++++ 1 file changed, 8 insertions(+) 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, From 7708a88771574892699c579d06d11aa9140c537a Mon Sep 17 00:00:00 2001 From: Luke Date: Thu, 12 Jun 2025 15:24:27 +0800 Subject: [PATCH 04/24] =?UTF-8?q?test:=20=E4=BF=AE=E6=94=B9Demo6?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- playground/Demo6/Main.snow | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/playground/Demo6/Main.snow b/playground/Demo6/Main.snow index 829a49f..d83ff5d 100644 --- a/playground/Demo6/Main.snow +++ b/playground/Demo6/Main.snow @@ -3,8 +3,8 @@ module: Main parameter: return_type: int body: - - return 0 + declare b1 :int =-1 + return b1 end body end function end module \ No newline at end of file From ac8afa8d0c38f51e900347f13e537589290981e7 Mon Sep 17 00:00:00 2001 From: Luke Date: Thu, 12 Jun 2025 16:20:55 +0800 Subject: [PATCH 05/24] =?UTF-8?q?fix:=20TokenType.UNKNOWN=E6=94=B9?= =?UTF-8?q?=E4=B8=BATokenType.NOT=E4=BB=A5=E6=94=AF=E6=8C=81=E5=8F=96?= =?UTF-8?q?=E5=8F=8D=E7=AC=A6=E5=8F=B7?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../jcnc/snow/compiler/lexer/scanners/OperatorTokenScanner.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) 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..1d52c02 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 @@ -63,7 +63,7 @@ public class OperatorTokenScanner extends AbstractTokenScanner { type = TokenType.NOT_EQUALS; } else { lexeme = "!"; - type = TokenType.UNKNOWN; + type = TokenType.NOT; } break; case '>': From a9819b769a2c21ebb3760cd2258b880bbd7f7560 Mon Sep 17 00:00:00 2001 From: Luke Date: Thu, 12 Jun 2025 16:21:54 +0800 Subject: [PATCH 06/24] =?UTF-8?q?feat:=20=E5=A2=9E=E5=8A=A0=E4=B8=80?= =?UTF-8?q?=E5=85=83=E5=89=8D=E7=BC=80(-x=20!x)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../org/jcnc/snow/compiler/parser/expression/Precedence.java | 3 +++ 1 file changed, 3 insertions(+) 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)。 */ From 852c6c0924238152dfb8e5ce6f3edc3ffb3dcb27 Mon Sep 17 00:00:00 2001 From: Luke Date: Thu, 12 Jun 2025 16:37:52 +0800 Subject: [PATCH 07/24] =?UTF-8?q?test:=20=E5=A2=9E=E5=8A=A0Demo7?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- playground/Demo7/Main.snow | 12 ++++++++++++ 1 file changed, 12 insertions(+) create mode 100644 playground/Demo7/Main.snow diff --git a/playground/Demo7/Main.snow b/playground/Demo7/Main.snow new file mode 100644 index 0000000..ef4ebf0 --- /dev/null +++ b/playground/Demo7/Main.snow @@ -0,0 +1,12 @@ +module: Main + function: main + parameter: + return_type: boolean + body: + declare b1 :boolean = true + declare b2 :boolean = !b1 + + return b2 + end body + end function +end module \ No newline at end of file From 7bd85d40828ad6c1f0f8d3377dca99f365f18ead Mon Sep 17 00:00:00 2001 From: Luke Date: Thu, 12 Jun 2025 16:38:00 +0800 Subject: [PATCH 08/24] =?UTF-8?q?test:=20=E4=BF=AE=E6=94=B9Demo6?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- playground/Demo6/Main.snow | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/playground/Demo6/Main.snow b/playground/Demo6/Main.snow index d83ff5d..71b29c5 100644 --- a/playground/Demo6/Main.snow +++ b/playground/Demo6/Main.snow @@ -3,7 +3,7 @@ module: Main parameter: return_type: int body: - declare b1 :int =-1 + declare b1 :int = -1 return b1 end body end function From bca073cb28488fe54cb108229db20e1fb7c4516d Mon Sep 17 00:00:00 2001 From: Luke Date: Thu, 12 Jun 2025 16:38:29 +0800 Subject: [PATCH 09/24] =?UTF-8?q?chore:=20=E5=A2=9E=E5=8A=A0Demo7=E9=85=8D?= =?UTF-8?q?=E7=BD=AE?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .run/Demo7.run.xml | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) create mode 100644 .run/Demo7.run.xml diff --git a/.run/Demo7.run.xml b/.run/Demo7.run.xml new file mode 100644 index 0000000..2addca0 --- /dev/null +++ b/.run/Demo7.run.xml @@ -0,0 +1,17 @@ + + + + \ No newline at end of file From 7679da265793b1ecda4c6518b28870374b537578 Mon Sep 17 00:00:00 2001 From: Luke Date: Thu, 12 Jun 2025 16:39:15 +0800 Subject: [PATCH 10/24] =?UTF-8?q?feat:=20=E5=AE=9E=E7=8E=B0=E4=B8=80?= =?UTF-8?q?=E5=85=83=E8=BF=90=E7=AE=97=E7=AC=A6=E5=8F=96=E5=8F=8D'-'?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../ir/builder/ExpressionBuilder.java | 22 +++++++++ .../compiler/lexer/token/TokenFactory.java | 3 -- .../parser/ast/UnaryExpressionNode.java | 17 +++++++ .../expression/PrattExpressionParser.java | 4 ++ .../expression/UnaryOperatorParselet.java | 19 +++++++ .../expression/UnaryExpressionAnalyzer.java | 49 +++++++++++++++++++ .../semantic/core/AnalyzerRegistrar.java | 5 +- 7 files changed, 115 insertions(+), 4 deletions(-) create mode 100644 src/main/java/org/jcnc/snow/compiler/parser/ast/UnaryExpressionNode.java create mode 100644 src/main/java/org/jcnc/snow/compiler/parser/expression/UnaryOperatorParselet.java create mode 100644 src/main/java/org/jcnc/snow/compiler/semantic/analyzers/expression/UnaryExpressionAnalyzer.java 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..b725ff1 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; @@ -59,11 +60,32 @@ 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_I32 + if (op.equals("-")) { + IRVirtualRegister dest = ctx.newRegister(); + ctx.addInstruction(new UnaryOperationInstruction( + IROpCode.NEG_I32, 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)。 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/parser/ast/UnaryExpressionNode.java b/src/main/java/org/jcnc/snow/compiler/parser/ast/UnaryExpressionNode.java new file mode 100644 index 0000000..be60c24 --- /dev/null +++ b/src/main/java/org/jcnc/snow/compiler/parser/ast/UnaryExpressionNode.java @@ -0,0 +1,17 @@ +package org.jcnc.snow.compiler.parser.ast; + +import org.jcnc.snow.compiler.parser.ast.base.ExpressionNode; + +/** + * 一元表达式节点,例如 -x 或 !x。 + * + * @param operator 运算符字符串 ("-" / "!") + * @param operand 操作数表达式 + */ +public record UnaryExpressionNode(String operator, + ExpressionNode operand) implements ExpressionNode { + + @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/UnaryOperatorParselet.java b/src/main/java/org/jcnc/snow/compiler/parser/expression/UnaryOperatorParselet.java new file mode 100644 index 0000000..9d7a323 --- /dev/null +++ b/src/main/java/org/jcnc/snow/compiler/parser/expression/UnaryOperatorParselet.java @@ -0,0 +1,19 @@ +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; + +/** 前缀一元运算解析器(支持 - 和 !) */ +public class UnaryOperatorParselet implements PrefixParselet { + + @Override + public ExpressionNode parse(ParserContext ctx, Token token) { + // 递归解析右侧,使用自身优先级 + ExpressionNode right = + new PrattExpressionParser().parseExpression(ctx, Precedence.UNARY); + return new UnaryExpressionNode(token.getLexeme(), right); + } +} diff --git a/src/main/java/org/jcnc/snow/compiler/semantic/analyzers/expression/UnaryExpressionAnalyzer.java b/src/main/java/org/jcnc/snow/compiler/semantic/analyzers/expression/UnaryExpressionAnalyzer.java new file mode 100644 index 0000000..37f2ec0 --- /dev/null +++ b/src/main/java/org/jcnc/snow/compiler/semantic/analyzers/expression/UnaryExpressionAnalyzer.java @@ -0,0 +1,49 @@ +package org.jcnc.snow.compiler.semantic.analyzers.expression; + +import org.jcnc.snow.compiler.parser.ast.FunctionNode; +import org.jcnc.snow.compiler.parser.ast.UnaryExpressionNode; +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.BuiltinType; +import org.jcnc.snow.compiler.semantic.type.Type; + +/** 一元表达式语义分析 */ +public class UnaryExpressionAnalyzer implements ExpressionAnalyzer { + + @Override + public Type analyze(Context ctx, ModuleInfo mi, FunctionNode fn, + SymbolTable locals, UnaryExpressionNode expr) { + + // 先分析操作数 + Type operandType = ctx.getRegistry() + .getExpressionAnalyzer(expr.operand()) + .analyze(ctx, mi, fn, locals, expr.operand()); + + switch (expr.operator()) { + case "-" -> { + if (!operandType.isNumeric()) { + ctx.getErrors().add(new SemanticError(expr, + "'-' 只能应用于数值类型,当前为 " + operandType)); + return BuiltinType.INT; + } + return operandType; + } + case "!" -> { + if (operandType != BuiltinType.BOOLEAN) { + ctx.getErrors().add(new SemanticError(expr, + "'!' 只能应用于 boolean 类型,当前为 " + operandType)); + return BuiltinType.BOOLEAN; + } + return BuiltinType.BOOLEAN; + } + default -> { + ctx.getErrors().add(new SemanticError(expr, + "未知一元运算符: " + expr.operator())); + return BuiltinType.INT; + } + } + } +} 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 865bb5e..c8de9e0 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 @@ -55,7 +55,10 @@ public final class AnalyzerRegistrar { registry.registerExpressionAnalyzer(CallExpressionNode.class, new CallExpressionAnalyzer()); registry.registerExpressionAnalyzer(BinaryExpressionNode.class, new BinaryExpressionAnalyzer()); - // 对尚未实现的表达式类型使用兜底处理器(如 MemberExpression) + // ---------- 注册一元表达式分析器 ---------- + registry.registerExpressionAnalyzer(UnaryExpressionNode.class,new UnaryExpressionAnalyzer()); + + // 对尚未实现的表达式类型使用兜底处理器 registry.registerExpressionAnalyzer(MemberExpressionNode.class, new UnsupportedExpressionAnalyzer<>()); } From f4e2cf52f502fd20d8245d2ed730ae64a50da926 Mon Sep 17 00:00:00 2001 From: Luke Date: Thu, 12 Jun 2025 16:56:34 +0800 Subject: [PATCH 11/24] =?UTF-8?q?docs:=20=E5=A2=9E=E5=8A=A0=E6=B3=A8?= =?UTF-8?q?=E9=87=8Adoc?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../ir/builder/ExpressionBuilder.java | 10 ++- .../lexer/scanners/OperatorTokenScanner.java | 72 ++++++++-------- .../parser/ast/UnaryExpressionNode.java | 22 ++++- .../expression/UnaryOperatorParselet.java | 44 +++++++++- .../expression/UnaryExpressionAnalyzer.java | 82 ++++++++++++++++--- .../snow/vm/engine/VirtualMachineEngine.java | 3 +- 6 files changed, 179 insertions(+), 54 deletions(-) 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 b725ff1..2d2e75e 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 @@ -33,8 +33,15 @@ public record ExpressionBuilder(IRContext ctx) { *

    会根据节点的实际类型分别处理: *

      *
    • 数字字面量:新建常量寄存器
    • + *
    • 布尔字面量:生成值为 0 或 1 的常量寄存器
    • *
    • 标识符:查找当前作用域中的寄存器
    • *
    • 二元表达式:递归处理子表达式并进行相应运算
    • + *
    • 一元运算符: + *
        + *
      • -x(取负,生成 NEG_I32 指令)与
      • + *
      • code>!x(逻辑非,转换为 x == 0 比较指令)
      • + *
      + *
    • *
    • 函数调用:生成对应的Call指令
    • *
    • 其它类型不支持,抛出异常
    • *
    @@ -43,6 +50,7 @@ public record ExpressionBuilder(IRContext ctx) { * @return 该表达式的计算结果寄存器 * @throws IllegalStateException 如果遇到未定义的标识符或不支持的表达式类型 */ + public IRVirtualRegister build(ExpressionNode expr) { return switch (expr) { // 数字字面量 @@ -176,7 +184,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/lexer/scanners/OperatorTokenScanner.java b/src/main/java/org/jcnc/snow/compiler/lexer/scanners/OperatorTokenScanner.java index 1d52c02..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.NOT; + 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/parser/ast/UnaryExpressionNode.java b/src/main/java/org/jcnc/snow/compiler/parser/ast/UnaryExpressionNode.java index be60c24..d956fc8 100644 --- a/src/main/java/org/jcnc/snow/compiler/parser/ast/UnaryExpressionNode.java +++ b/src/main/java/org/jcnc/snow/compiler/parser/ast/UnaryExpressionNode.java @@ -3,15 +3,29 @@ package org.jcnc.snow.compiler.parser.ast; import org.jcnc.snow.compiler.parser.ast.base.ExpressionNode; /** - * 一元表达式节点,例如 -x 或 !x。 + * {@code UnaryExpressionNode} —— 前缀一元运算 AST 节点。 * - * @param operator 运算符字符串 ("-" / "!") - * @param operand 操作数表达式 + *

    代表两种受支持的一元前缀表达式: + *

      + *
    • 取负:{@code -x}
    • + *
    • 逻辑非:{@code !x}
    • + *
    + * + * {@link #equals(Object)}、{@link #hashCode()} 等方法。

    + * + * @param operator 一元运算符(仅 "-" 或 "!") + * @param operand 运算对象 / 右操作数 */ public record UnaryExpressionNode(String operator, ExpressionNode operand) implements ExpressionNode { - @Override public String toString() { + /** + * 生成调试友好的字符串表示,例如 {@code "-x"} 或 {@code "!flag"}。 + * + * @return 一元表达式的串表示 + */ + @Override + public String toString() { return operator + operand; } } 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 index 9d7a323..abfa97a 100644 --- a/src/main/java/org/jcnc/snow/compiler/parser/expression/UnaryOperatorParselet.java +++ b/src/main/java/org/jcnc/snow/compiler/parser/expression/UnaryOperatorParselet.java @@ -6,14 +6,50 @@ 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}
    • + *
    + * + * 解析过程: + * + *
      + *
    1. 该 parselet 在外层解析器已消费运算符 {@code token} 后被调用。
    2. + *
    3. 以 {@link Precedence#UNARY} 作为 绑定强度 递归解析右侧子表达式, + * 保证任何更高优先级的表达式(括号、字面量等)优先归属右侧。
    4. + *
    5. 最终生成 {@link UnaryExpressionNode} AST 节点,记录运算符与操作数。
    6. + *
    + * + *

    此类仅负责语法结构的构建: + *

      + *
    • 类型正确性在 {@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) { - // 递归解析右侧,使用自身优先级 - ExpressionNode right = + /* ------------------------------------------------------------ + * 1. 以 UNARY 优先级递归解析操作数,避免错误结合顺序。 + * ------------------------------------------------------------ */ + ExpressionNode operand = new PrattExpressionParser().parseExpression(ctx, Precedence.UNARY); - return new UnaryExpressionNode(token.getLexeme(), right); + + /* ------------------------------------------------------------ + * 2. 封装成 AST 节点并返回。 + * ------------------------------------------------------------ */ + return new UnaryExpressionNode(token.getLexeme(), operand); } } diff --git a/src/main/java/org/jcnc/snow/compiler/semantic/analyzers/expression/UnaryExpressionAnalyzer.java b/src/main/java/org/jcnc/snow/compiler/semantic/analyzers/expression/UnaryExpressionAnalyzer.java index 37f2ec0..53f6c8f 100644 --- a/src/main/java/org/jcnc/snow/compiler/semantic/analyzers/expression/UnaryExpressionAnalyzer.java +++ b/src/main/java/org/jcnc/snow/compiler/semantic/analyzers/expression/UnaryExpressionAnalyzer.java @@ -10,38 +10,96 @@ import org.jcnc.snow.compiler.semantic.symbol.SymbolTable; import org.jcnc.snow.compiler.semantic.type.BuiltinType; import org.jcnc.snow.compiler.semantic.type.Type; -/** 一元表达式语义分析 */ +/** + * {@code UnaryExpressionAnalyzer} — 一元表达式的语义分析器。 + * + *

    目前实现两种一元运算: + *

      + *
    • {@code -x} 取负:仅允许作用于数值类型(int / float 等)。
    • + *
    • {@code !x} 逻辑非:仅允许作用于 {@code boolean} 类型。
    • + *
    + * + *

    分析流程: + *

      + *
    1. 递归分析操作数表达式,获取其类型 {@code operandType}。
    2. + *
    3. 根据运算符检查类型合法性: + *
        + *
      • 若类型不符,记录 {@link SemanticError} 并返回一个占位类型 + * (取负返回 {@link BuiltinType#INT},逻辑非返回 + * {@link BuiltinType#BOOLEAN})。
      • + *
      • 若合法,则返回运算后的结果类型 + * (取负为 {@code operandType},逻辑非为 {@link BuiltinType#BOOLEAN})。
      • + *
      + *
    4. + *
    + * + *

    若遇到未支持的运算符,将生成错误并返回 {@code int} 作为占位类型。

    + * + */ public class UnaryExpressionAnalyzer implements ExpressionAnalyzer { + /** + * 对一元表达式进行语义分析。 + * + * @param ctx 全局编译上下文,持有错误列表、注册表等 + * @param mi 当前模块信息 + * @param fn 所在函数节点(可为 {@code null} 表示顶层) + * @param locals 当前作用域符号表 + * @param expr 要分析的一元表达式节点 + * @return 表达式的结果类型;若有错误,返回占位类型并在 {@code ctx.getErrors()} + * 中记录 {@link SemanticError} + */ @Override - public Type analyze(Context ctx, ModuleInfo mi, FunctionNode fn, - SymbolTable locals, UnaryExpressionNode expr) { + public Type analyze(Context ctx, + ModuleInfo mi, + FunctionNode fn, + SymbolTable locals, + UnaryExpressionNode expr) { - // 先分析操作数 + /* ------------------------------------------------------------------ + * 1. 先递归分析操作数,确定其类型 + * ------------------------------------------------------------------ */ Type operandType = ctx.getRegistry() - .getExpressionAnalyzer(expr.operand()) - .analyze(ctx, mi, fn, locals, expr.operand()); + .getExpressionAnalyzer(expr.operand()) + .analyze(ctx, mi, fn, locals, expr.operand()); + /* ------------------------------------------------------------------ + * 2. 根据运算符校验类型并给出结果类型 + * ------------------------------------------------------------------ */ switch (expr.operator()) { + /* -------------- 取负运算 -------------- */ case "-" -> { if (!operandType.isNumeric()) { - ctx.getErrors().add(new SemanticError(expr, - "'-' 只能应用于数值类型,当前为 " + operandType)); + ctx.getErrors().add(new SemanticError( + expr, + "'-' 只能应用于数值类型,当前为 " + operandType + )); + // 返回占位类型,避免后续阶段 NPE return BuiltinType.INT; } + // 合法:结果类型与操作数相同 return operandType; } + + /* -------------- 逻辑非运算 -------------- */ case "!" -> { if (operandType != BuiltinType.BOOLEAN) { - ctx.getErrors().add(new SemanticError(expr, - "'!' 只能应用于 boolean 类型,当前为 " + operandType)); + ctx.getErrors().add(new SemanticError( + expr, + "'!' 只能应用于 boolean 类型,当前为 " + operandType + )); return BuiltinType.BOOLEAN; } + // 合法:结果类型恒为 boolean return BuiltinType.BOOLEAN; } + + /* -------------- 未知运算符 -------------- */ default -> { - ctx.getErrors().add(new SemanticError(expr, - "未知一元运算符: " + expr.operator())); + ctx.getErrors().add(new SemanticError( + expr, + "未知一元运算符: " + expr.operator() + )); return BuiltinType.INT; } } 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 14cef48..0f188ab 100644 --- a/src/main/java/org/jcnc/snow/vm/engine/VirtualMachineEngine.java +++ b/src/main/java/org/jcnc/snow/vm/engine/VirtualMachineEngine.java @@ -20,7 +20,8 @@ import java.util.List; *
  • {@link CommandExecutionHandler} — dispatches opcodes
  • * * - *

    Root-frame contract

    + * Root-frame contract: + *

    * A root stack frame is pushed once via * {@link #ensureRootFrame()} before the first instruction executes * and is never popped. When a {@code RET} executed in the root frame From 5ab850f354fd753a037398488d5b1b2e5347a462 Mon Sep 17 00:00:00 2001 From: zhangxun <1958638841@qq.com> Date: Thu, 12 Jun 2025 17:39:04 +0800 Subject: [PATCH 12/24] fix: LXorCommand may be truncated --- .../org/jcnc/snow/vm/commands/bitwise/long64/LXorCommand.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/main/java/org/jcnc/snow/vm/commands/bitwise/long64/LXorCommand.java b/src/main/java/org/jcnc/snow/vm/commands/bitwise/long64/LXorCommand.java index 2a668dd..dbce6ab 100644 --- a/src/main/java/org/jcnc/snow/vm/commands/bitwise/long64/LXorCommand.java +++ b/src/main/java/org/jcnc/snow/vm/commands/bitwise/long64/LXorCommand.java @@ -48,8 +48,8 @@ public class LXorCommand implements Command { } // Pop the top two operands from the stack - final int b = (int) operandStack.pop(); - final int a = (int) operandStack.pop(); + final long b = (long) operandStack.pop(); + final long a = (long) operandStack.pop(); // Perform the long64 bitwise XOR operation and push the result back onto the stack operandStack.push(a ^ b); From 84940af2bd6124e7c9aa14b7b00750ff8b650962 Mon Sep 17 00:00:00 2001 From: Luke Date: Thu, 12 Jun 2025 18:03:42 +0800 Subject: [PATCH 13/24] =?UTF-8?q?docs:=20=E5=88=A0=E9=99=A4=E6=97=A0?= =?UTF-8?q?=E5=85=B3=E6=B3=A8=E9=87=8A?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/main/java/org/jcnc/snow/compiler/ir/builder/IRContext.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) 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 对象,所有指令将添加至此 From ddea7e887fd31c412e86310ad7ac7c2b06ffbdc5 Mon Sep 17 00:00:00 2001 From: Luke Date: Thu, 12 Jun 2025 22:17:26 +0800 Subject: [PATCH 14/24] =?UTF-8?q?feat=20:=20=E5=AE=8C=E5=96=84AST=20JSON?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../parser/utils/ASTJsonSerializer.java | 21 ++++++++++++++++--- 1 file changed, 18 insertions(+), 3 deletions(-) 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..775d492 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) -> { List args = new ArrayList<>(arguments.size()); for (ExpressionNode arg : arguments) args.add(exprToMap(arg)); yield exprMap("CallExpression", "callee", exprToMap(callee), "arguments", args); } + // 成员访问表达式 case MemberExpressionNode(ExpressionNode object, String member) -> exprMap("MemberExpression", "object", exprToMap(object), "member", member @@ -195,4 +210,4 @@ public class ASTJsonSerializer { default -> Map.of("type", expr.getClass().getSimpleName()); }; } -} \ No newline at end of file +} From bea8a65c8e03345344d374b9adc373978c8e6fa7 Mon Sep 17 00:00:00 2001 From: Luke Date: Thu, 12 Jun 2025 22:18:16 +0800 Subject: [PATCH 15/24] =?UTF-8?q?test=20:=20=E4=BF=AE=E6=94=B9=E6=B5=8B?= =?UTF-8?q?=E8=AF=95=E4=BB=A3=E7=A0=81?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- playground/Demo7/Main.snow | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/playground/Demo7/Main.snow b/playground/Demo7/Main.snow index ef4ebf0..aae7e15 100644 --- a/playground/Demo7/Main.snow +++ b/playground/Demo7/Main.snow @@ -4,9 +4,7 @@ module: Main return_type: boolean body: declare b1 :boolean = true - declare b2 :boolean = !b1 - - return b2 + return !b1 end body end function end module \ No newline at end of file From 97c8542eb629fa17581da21abf3bc0941d1a86cd Mon Sep 17 00:00:00 2001 From: Luke Date: Thu, 12 Jun 2025 22:18:44 +0800 Subject: [PATCH 16/24] =?UTF-8?q?style=20:=20=E6=94=B9=E4=B8=BArecord?= =?UTF-8?q?=E7=B1=BB?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../snow/compiler/parser/ast/BoolLiteralNode.java | 12 +++++------- 1 file changed, 5 insertions(+), 7 deletions(-) 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)); } /** From 78ceca9a3661e151d59634bd843356d2df95e925 Mon Sep 17 00:00:00 2001 From: Luke Date: Thu, 12 Jun 2025 22:43:20 +0800 Subject: [PATCH 17/24] =?UTF-8?q?docs:=20=E5=A2=9E=E5=8A=A0=E6=B3=A8?= =?UTF-8?q?=E9=87=8Adoc?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../backend/generator/BinaryOpGenerator.java | 160 +++++++++++++----- 1 file changed, 113 insertions(+), 47 deletions(-) diff --git a/src/main/java/org/jcnc/snow/compiler/backend/generator/BinaryOpGenerator.java b/src/main/java/org/jcnc/snow/compiler/backend/generator/BinaryOpGenerator.java index de907fb..4e5bd83 100644 --- a/src/main/java/org/jcnc/snow/compiler/backend/generator/BinaryOpGenerator.java +++ b/src/main/java/org/jcnc/snow/compiler/backend/generator/BinaryOpGenerator.java @@ -2,24 +2,45 @@ package org.jcnc.snow.compiler.backend.generator; import org.jcnc.snow.compiler.backend.builder.VMProgramBuilder; import org.jcnc.snow.compiler.backend.core.InstructionGenerator; +import org.jcnc.snow.compiler.backend.util.IROpCodeMapper; import org.jcnc.snow.compiler.backend.util.OpHelper; import org.jcnc.snow.compiler.ir.instruction.BinaryOperationInstruction; import org.jcnc.snow.compiler.ir.value.IRVirtualRegister; import java.util.Map; +import java.util.concurrent.atomic.AtomicInteger; /** - * 二元运算指令生成器 - * 支持二元运算指令的自动类型提升。 - *

    类型提升优先级为:D > F > L > I > S > B

    + * 二元运算指令生成器。 + *

    + * 负责将中间表示的二元运算指令(算术运算及比较运算)生成对应的虚拟机指令序列。 + * 支持对操作数进行自动类型提升,以保证运算结果的正确性。 + *

    + *

    类型提升优先级:D > F > L > I > S > B

    */ public class BinaryOpGenerator implements InstructionGenerator { - /* ---------- 类型优先级工具 ---------- */ + /** + * 用于生成唯一标签的计数器。 + */ + private static final AtomicInteger COUNTER = new AtomicInteger(0); /** - * 返回类型前缀的优先级数值。数值越大,类型“越宽”。 - * D: 6, F: 5, L: 4, I: 3, S: 2, B: 1 + * 生成一个新的唯一标签。 + * + * @param fn 当前函数名,用于标签前缀 + * @param tag 标签用途标识 + * @return 格式为 fn$tag$序号 的唯一标签字符串 + */ + private static String fresh(String fn, String tag) { + return fn + "$" + tag + "$" + COUNTER.getAndIncrement(); + } + + /** + * 返回类型字符对应的优先级。 + * + * @param p 类型字符(例如 'D','F','L','I','S','B') + * @return 对应的优先级整数,数值越大优先级越高 */ private static int rank(char p) { return switch (p) { @@ -29,35 +50,41 @@ public class BinaryOpGenerator implements InstructionGenerator 3; case 'S' -> 2; case 'B' -> 1; - default -> 0; + default -> 0; }; } /** - * 返回a和b中优先级更高的类型前缀(即类型提升结果)。 + * 比较两个类型字符,返回优先级更高的那个。 + * + * @param a 左操作数类型 + * @param b 右操作数类型 + * @return 优先级更高者的类型字符 */ private static char promote(char a, char b) { return rank(a) >= rank(b) ? a : b; } /** - * 类型前缀转字符串,方便拼接。 + * 将类型字符转换为字符串形式。 + * + * @param p 类型字符 + * @return 长度为1的字符串 */ private static String str(char p) { return String.valueOf(p); } - /* ---------- 类型转换指令工具 ---------- */ /** - * 根据源类型和目标类型前缀,返回相应的类型转换指令助记符。 + * 获取从类型 from 到类型 to 的转换指令名称。 * - * @param from 源类型前缀 - * @param to 目标类型前缀 - * @return 转换指令字符串,如 "I2L" 或 "F2D",若无需转换则返回null + * @param from 源类型字符 + * @param to 目标类型字符 + * @return 对应的指令名称,如 "I2L";若两类型相同或不可转换则返回 null */ private static String convert(char from, char to) { - if (from == to) return null; // 类型一致,无需转换 + if (from == to) return null; return switch ("" + from + to) { case "IL" -> "I2L"; case "ID" -> "I2D"; @@ -73,12 +100,14 @@ public class BinaryOpGenerator implements InstructionGenerator "D2F"; case "SI" -> "S2I"; case "BI" -> "B2I"; - default -> null; // 其它组合暂未用到 + default -> null; }; } /** - * 返回本生成器支持的指令类型,即 BinaryOperationInstruction。 + * 返回该生成器支持的指令类型。 + * + * @return BinaryOperationInstruction 的 Class 对象 */ @Override public Class supportedClass() { @@ -86,12 +115,24 @@ public class BinaryOpGenerator implements InstructionGenerator步骤:

    + *
      + *
    1. 获取操作数与目标操作数寄存槽位及其类型。
    2. + *
    3. 将左右操作数加载到栈并根据需要进行类型转换。
    4. + *
    5. 区分算术/位运算与比较运算,分别生成不同指令序列:
    6. + *
        + *
      • 算术/位运算:直接调用对应的运算指令并保存结果。
      • + *
      • 比较运算:使用条件跳转生成布尔结果。
      • + *
      + *
    7. 将结果存回目标槽位,并更新槽位类型。
    8. + *
    + * + * @param ins 中间表示的二元运算指令实例 + * @param out 字节码生成器,用于输出虚拟机指令 + * @param slotMap 虚拟寄存器到槽位编号的映射 + * @param currentFn 当前函数名,用于生成唯一标签 */ @Override public void generate(BinaryOperationInstruction ins, @@ -99,35 +140,60 @@ public class BinaryOpGenerator implements InstructionGenerator slotMap, String currentFn) { - /* ------- 1. 获取左右操作数的槽位编号和类型 ------- */ - int lSlot = slotMap.get((IRVirtualRegister) ins.operands().get(0)); // 左操作数槽位 - int rSlot = slotMap.get((IRVirtualRegister) ins.operands().get(1)); // 右操作数槽位 - int dSlot = slotMap.get(ins.dest()); // 目标槽位 - char lType = out.getSlotType(lSlot); // 左操作数类型前缀 - char rType = out.getSlotType(rSlot); // 右操作数类型前缀 + /* ---------- 1. 槽位与类型 ---------- */ + int lSlot = slotMap.get((IRVirtualRegister) ins.operands().get(0)); + int rSlot = slotMap.get((IRVirtualRegister) ins.operands().get(1)); + int dSlot = slotMap.get(ins.dest()); - // 类型提升,确定本次二元运算的目标类型(优先级较高的那一个) - char tType = promote(lType, rType); - String tPref = str(tType); // 用于拼接指令字符串 + char lType = out.getSlotType(lSlot); // 如未登记默认 'I' + char rType = out.getSlotType(rSlot); - /* ------- 2. 加载左操作数,并自动进行类型转换(如有必要) ------- */ - out.emit(OpHelper.opcode(str(lType) + "_LOAD") + " " + lSlot); // LOAD指令 - String cvt = convert(lType, tType); // 如需类型提升 - if (cvt != null) out.emit(OpHelper.opcode(cvt)); // 插入类型转换指令 + char tType = promote(lType, rType); // 类型提升结果 + String tPre = str(tType); - /* ------- 3. 加载右操作数,并自动进行类型转换(如有必要) ------- */ - out.emit(OpHelper.opcode(str(rType) + "_LOAD") + " " + rSlot); // LOAD指令 - cvt = convert(rType, tType); // 如需类型提升 - if (cvt != null) out.emit(OpHelper.opcode(cvt)); // 插入类型转换指令 + /* ---------- 2. 加载并做类型转换 ---------- */ + out.emit(OpHelper.opcode(str(lType) + "_LOAD") + " " + lSlot); + String cvt = convert(lType, tType); + if (cvt != null) out.emit(OpHelper.opcode(cvt)); - /* ------- 4. 生成具体的二元运算指令 ------- */ - // 获取IR指令中的操作名(如ADD、SUB、MUL等,去掉结尾的"_"后缀) - String opName = ins.op().name().split("_")[0]; - // 例如生成 "I_ADD", "D_MUL" 等虚拟机指令 - out.emit(OpHelper.opcode(tPref + "_" + opName)); + out.emit(OpHelper.opcode(str(rType) + "_LOAD") + " " + rSlot); + cvt = convert(rType, tType); + if (cvt != null) out.emit(OpHelper.opcode(cvt)); - /* ------- 5. 结果存入目标槽位,并更新槽位类型 ------- */ - out.emit(OpHelper.opcode(tPref + "_STORE") + " " + dSlot); - out.setSlotType(dSlot, tType); // 记录运算结果的类型前缀,便于后续指令正确处理 + /* ---------- 3. 区分算术 / 比较 ---------- */ + String irName = ins.op().name(); + boolean isCmp = irName.startsWith("CMP_"); + + /* === 3-A. 普通算术 / 位运算 === */ + if (!isCmp) { + String opName = irName.split("_")[0]; // ADD / SUB / MUL … + out.emit(OpHelper.opcode(tPre + "_" + opName)); // I_ADD / D_MUL … + out.emit(OpHelper.opcode(tPre + "_STORE") + " " + dSlot); + out.setSlotType(dSlot, tType); + return; + } + + /* === 3-B. CMP_* —— 生成布尔结果 === */ + String branchOp = OpHelper.opcode(IROpCodeMapper.toVMOp(ins.op())); // IC_E / IC_NE … + String lblTrue = fresh(currentFn, "true"); + String lblEnd = fresh(currentFn, "end"); + + // ① 条件跳转;成立 → lblTrue + out.emitBranch(branchOp, lblTrue); + + // ② 不成立:压 0 + out.emit(OpHelper.opcode("I_PUSH") + " 0"); + out.emitBranch(OpHelper.opcode("JUMP"), lblEnd); + + // ③ 成立分支:压 1 + out.emit(lblTrue + ":"); + out.emit(OpHelper.opcode("I_PUSH") + " 1"); + + // ④ 结束标签 + out.emit(lblEnd + ":"); + + // ⑤ 写入目标槽位 + out.emit(OpHelper.opcode("I_STORE") + " " + dSlot); + out.setSlotType(dSlot, 'I'); // 布尔 ➜ int } } From 91bc1f15cc93dce07dbe495c097f9177f24097e6 Mon Sep 17 00:00:00 2001 From: Luke Date: Thu, 12 Jun 2025 23:06:52 +0800 Subject: [PATCH 18/24] =?UTF-8?q?style:=20=E5=88=A0=E9=99=A4=E6=B2=A1?= =?UTF-8?q?=E6=9C=89=E4=BD=BF=E7=94=A8=E7=9A=84=E6=A8=A1=E5=BC=8F=E5=8F=98?= =?UTF-8?q?=E9=87=8F?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../org/jcnc/snow/compiler/parser/utils/ASTJsonSerializer.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) 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 775d492..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 @@ -196,7 +196,7 @@ public class ASTJsonSerializer { // 字符串字面量 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 args = new ArrayList<>(arguments.size()); for (ExpressionNode arg : arguments) args.add(exprToMap(arg)); yield exprMap("CallExpression", "callee", exprToMap(callee), "arguments", args); From 616d361c9d8f2838bf45ad4d5edfb6b088b7477c Mon Sep 17 00:00:00 2001 From: Luke Date: Fri, 13 Jun 2025 10:42:14 +0800 Subject: [PATCH 19/24] =?UTF-8?q?fix:=20=E6=B6=88=E9=99=A4=E7=94=A8?= =?UTF-8?q?=E4=BA=8E=E8=87=AA=E6=88=91=E8=B5=8B=E5=80=BC=E7=9A=84=E5=86=97?= =?UTF-8?q?=E4=BD=99=E7=A7=BB=E5=8A=A8=E6=8C=87=E4=BB=A4?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../jcnc/snow/compiler/ir/builder/InstructionFactory.java | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) 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)); } From 6843bb4af2bd42524d254561c78073f4e1a2624f Mon Sep 17 00:00:00 2001 From: Luke Date: Fri, 13 Jun 2025 14:46:42 +0800 Subject: [PATCH 20/24] =?UTF-8?q?fix:=20=E4=BF=AE=E5=A4=8D=E5=8F=96?= =?UTF-8?q?=E5=8F=8D=E7=B1=BB=E5=9E=8B=E6=8E=A8=E6=96=AD=E9=94=99=E8=AF=AF?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../backend/generator/BinaryOpGenerator.java | 128 ++++++++++-------- .../backend/generator/UnaryOpGenerator.java | 32 +++-- .../ir/builder/ExpressionBuilder.java | 11 +- .../compiler/ir/builder/StatementBuilder.java | 14 ++ .../compiler/ir/utils/ExpressionUtils.java | 24 ++++ 5 files changed, 138 insertions(+), 71 deletions(-) diff --git a/src/main/java/org/jcnc/snow/compiler/backend/generator/BinaryOpGenerator.java b/src/main/java/org/jcnc/snow/compiler/backend/generator/BinaryOpGenerator.java index 4e5bd83..91a4d91 100644 --- a/src/main/java/org/jcnc/snow/compiler/backend/generator/BinaryOpGenerator.java +++ b/src/main/java/org/jcnc/snow/compiler/backend/generator/BinaryOpGenerator.java @@ -4,7 +4,9 @@ import org.jcnc.snow.compiler.backend.builder.VMProgramBuilder; import org.jcnc.snow.compiler.backend.core.InstructionGenerator; import org.jcnc.snow.compiler.backend.util.IROpCodeMapper; import org.jcnc.snow.compiler.backend.util.OpHelper; +import org.jcnc.snow.compiler.ir.core.IRValue; import org.jcnc.snow.compiler.ir.instruction.BinaryOperationInstruction; +import org.jcnc.snow.compiler.ir.value.IRConstant; import org.jcnc.snow.compiler.ir.value.IRVirtualRegister; import java.util.Map; @@ -13,15 +15,18 @@ import java.util.concurrent.atomic.AtomicInteger; /** * 二元运算指令生成器。 *

    - * 负责将中间表示的二元运算指令(算术运算及比较运算)生成对应的虚拟机指令序列。 - * 支持对操作数进行自动类型提升,以保证运算结果的正确性。 + * 负责将中间表示的二元运算指令(算术、位运算及比较运算)生成对应的虚拟机指令序列, + * 并自动进行类型提升。 + * 同时实现 "+0 → MOV" 的 Peephole 优化,避免多余的 PUSH/ADD 序列。 *

    *

    类型提升优先级:D > F > L > I > S > B

    */ public class BinaryOpGenerator implements InstructionGenerator { + /* -------------------- 常量与工具 -------------------- */ + /** - * 用于生成唯一标签的计数器。 + * 用于生成唯一标签的计数器 */ private static final AtomicInteger COUNTER = new AtomicInteger(0); @@ -30,17 +35,14 @@ public class BinaryOpGenerator implements InstructionGenerator 3; case 'S' -> 2; case 'B' -> 1; - default -> 0; + default -> 0; }; } /** - * 比较两个类型字符,返回优先级更高的那个。 - * - * @param a 左操作数类型 - * @param b 右操作数类型 - * @return 优先级更高者的类型字符 + * 返回优先级更高的类型字符 */ private static char promote(char a, char b) { return rank(a) >= rank(b) ? a : b; } /** - * 将类型字符转换为字符串形式。 - * - * @param p 类型字符 - * @return 长度为1的字符串 + * 单字符转字符串 */ private static String str(char p) { return String.valueOf(p); } + /** + * 判断常量值是否等于 0。 + * 仅支持 Java 原生数值类型。 + */ + private static boolean isZero(Object v) { + if (v == null) return false; + return switch (v) { + case Integer i -> i == 0; + case Long l -> l == 0L; + case Short s -> s == (short) 0; + case Byte b -> b == (byte) 0; + case Float f -> f == 0.0f; + case Double d -> d == 0.0; + default -> false; + }; + } /** - * 获取从类型 from 到类型 to 的转换指令名称。 - * - * @param from 源类型字符 - * @param to 目标类型字符 - * @return 对应的指令名称,如 "I2L";若两类型相同或不可转换则返回 null + * 获取从类型 {@code from} 到 {@code to} 的转换指令名。 + * 相同类型或无显式转换需求返回 {@code null}。 */ private static String convert(char from, char to) { if (from == to) return null; @@ -100,55 +108,60 @@ public class BinaryOpGenerator implements InstructionGenerator "D2F"; case "SI" -> "S2I"; case "BI" -> "B2I"; - default -> null; + default -> null; }; } - /** - * 返回该生成器支持的指令类型。 - * - * @return BinaryOperationInstruction 的 Class 对象 - */ + /* -------------------- 接口实现 -------------------- */ + @Override public Class supportedClass() { return BinaryOperationInstruction.class; } - /** - * 根据中间表示的二元运算指令生成对应的虚拟机指令序列。 - * - *

    步骤:

    - *
      - *
    1. 获取操作数与目标操作数寄存槽位及其类型。
    2. - *
    3. 将左右操作数加载到栈并根据需要进行类型转换。
    4. - *
    5. 区分算术/位运算与比较运算,分别生成不同指令序列:
    6. - *
        - *
      • 算术/位运算:直接调用对应的运算指令并保存结果。
      • - *
      • 比较运算:使用条件跳转生成布尔结果。
      • - *
      - *
    7. 将结果存回目标槽位,并更新槽位类型。
    8. - *
    - * - * @param ins 中间表示的二元运算指令实例 - * @param out 字节码生成器,用于输出虚拟机指令 - * @param slotMap 虚拟寄存器到槽位编号的映射 - * @param currentFn 当前函数名,用于生成唯一标签 - */ @Override public void generate(BinaryOperationInstruction ins, VMProgramBuilder out, Map slotMap, String currentFn) { + /* ---------- 0. +0 → MOV Peephole 优化 ---------- */ + String irName = ins.op().name(); + if (irName.startsWith("ADD_")) { + IRValue lhs = ins.operands().getFirst(); + IRValue rhs = ins.operands().get(1); + + boolean lhsZero = lhs instanceof IRConstant && isZero(((IRConstant) lhs).value()); + boolean rhsZero = rhs instanceof IRConstant && isZero(((IRConstant) rhs).value()); + + // 仅当一侧为常量 0 时可替换为 MOV + if (lhsZero ^ rhsZero) { + IRVirtualRegister srcVr = null; + if ((lhsZero ? rhs : lhs) instanceof IRVirtualRegister) { + srcVr = (IRVirtualRegister) (lhsZero ? rhs : lhs); + } + int srcSlot = slotMap.get(srcVr); + int destSlot = slotMap.get(ins.dest()); + + // 源与目标槽位不同才需要发 MOV + if (srcSlot != destSlot) { + out.emit(OpHelper.opcode("MOV") + " " + srcSlot + " " + destSlot); + } + // 复制槽位类型信息 + out.setSlotType(destSlot, out.getSlotType(srcSlot)); + return; // 优化路径结束 + } + } + /* ---------- 1. 槽位与类型 ---------- */ int lSlot = slotMap.get((IRVirtualRegister) ins.operands().get(0)); int rSlot = slotMap.get((IRVirtualRegister) ins.operands().get(1)); int dSlot = slotMap.get(ins.dest()); - char lType = out.getSlotType(lSlot); // 如未登记默认 'I' + char lType = out.getSlotType(lSlot); // 未登记默认 'I' char rType = out.getSlotType(rSlot); - char tType = promote(lType, rType); // 类型提升结果 + char tType = promote(lType, rType); // 类型提升结果 String tPre = str(tType); /* ---------- 2. 加载并做类型转换 ---------- */ @@ -161,13 +174,12 @@ public class BinaryOpGenerator implements InstructionGenerator 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 2d2e75e..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 @@ -74,15 +74,16 @@ public record ExpressionBuilder(IRContext ctx) { }; } + /** 处理一元表达式 */ private IRVirtualRegister buildUnary(UnaryExpressionNode un) { - String op = un.operator(); - IRVirtualRegister val = build(un.operand()); + String op = un.operator(); + IRVirtualRegister val = build(un.operand()); - // -x → NEG_I32 + // -x → NEG_*(根据类型自动选择位宽) if (op.equals("-")) { IRVirtualRegister dest = ctx.newRegister(); - ctx.addInstruction(new UnaryOperationInstruction( - IROpCode.NEG_I32, dest, val)); + IROpCode code = ExpressionUtils.negOp(un.operand()); + ctx.addInstruction(new UnaryOperationInstruction(code, dest, val)); return dest; } 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; + }; + } + /* =================== 类型推断与操作符匹配 =================== */ /** From 11c2dd7ad7fd18c27a354c03b49fc7cf4399d6d6 Mon Sep 17 00:00:00 2001 From: Luke Date: Fri, 13 Jun 2025 14:48:48 +0800 Subject: [PATCH 21/24] =?UTF-8?q?fix:=20end=20function=E5=90=8E=E5=BF=85?= =?UTF-8?q?=E9=A1=BB=E5=A4=9A=E4=B8=80=E4=B8=AA=E7=A9=BA=E8=A1=8C=E7=9A=84?= =?UTF-8?q?=E9=97=AE=E9=A2=98?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../org/jcnc/snow/compiler/parser/function/FunctionParser.java | 1 - 1 file changed, 1 deletion(-) 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); } /** From 34d45c787850a018682c71bd43b970bf639bad92 Mon Sep 17 00:00:00 2001 From: zhangxun <1958638841@qq.com> Date: Fri, 13 Jun 2025 19:46:34 +0800 Subject: [PATCH 22/24] =?UTF-8?q?fix:=20=E6=AF=94=E8=BE=83=E8=BF=90?= =?UTF-8?q?=E7=AE=97=E7=AC=A6=E6=94=AF=E6=8C=81=E8=BF=94=E5=9B=9E=20boolea?= =?UTF-8?q?n=20=E7=B1=BB=E5=9E=8B=E5=80=BC?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../semantic/analyzers/expression/BinaryExpressionAnalyzer.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/java/org/jcnc/snow/compiler/semantic/analyzers/expression/BinaryExpressionAnalyzer.java b/src/main/java/org/jcnc/snow/compiler/semantic/analyzers/expression/BinaryExpressionAnalyzer.java index a9caa0c..b52a519 100644 --- a/src/main/java/org/jcnc/snow/compiler/semantic/analyzers/expression/BinaryExpressionAnalyzer.java +++ b/src/main/java/org/jcnc/snow/compiler/semantic/analyzers/expression/BinaryExpressionAnalyzer.java @@ -69,7 +69,7 @@ public class BinaryExpressionAnalyzer implements ExpressionAnalyzer >= == !=".contains(op)) { - return BuiltinType.INT; + return BuiltinType.BOOLEAN; } return wide; From cdc7dea410e661a81d8ca334b06a91c809fd4e38 Mon Sep 17 00:00:00 2001 From: zhangxun <1958638841@qq.com> Date: Fri, 13 Jun 2025 19:56:24 +0800 Subject: [PATCH 23/24] =?UTF-8?q?docs:=20=E4=BF=AE=E6=94=B9=E6=B3=A8?= =?UTF-8?q?=E9=87=8A?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../semantic/analyzers/expression/BinaryExpressionAnalyzer.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/java/org/jcnc/snow/compiler/semantic/analyzers/expression/BinaryExpressionAnalyzer.java b/src/main/java/org/jcnc/snow/compiler/semantic/analyzers/expression/BinaryExpressionAnalyzer.java index b52a519..2bc22de 100644 --- a/src/main/java/org/jcnc/snow/compiler/semantic/analyzers/expression/BinaryExpressionAnalyzer.java +++ b/src/main/java/org/jcnc/snow/compiler/semantic/analyzers/expression/BinaryExpressionAnalyzer.java @@ -67,7 +67,7 @@ public class BinaryExpressionAnalyzer implements ExpressionAnalyzer >= == !=".contains(op)) { return BuiltinType.BOOLEAN; } From 5eca94abe0e4dbce39a6d1f7f7d1864c6db26c39 Mon Sep 17 00:00:00 2001 From: Luke Date: Sat, 14 Jun 2025 09:16:06 +0800 Subject: [PATCH 24/24] =?UTF-8?q?docs:=20=E6=9B=B4=E6=96=B0v0.3.0=E7=9A=84?= =?UTF-8?q?=E5=BE=BD=E7=AB=A0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- README.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index 3268187..a00106f 100644 --- a/README.md +++ b/README.md @@ -11,8 +11,8 @@ - - + +