From 856d113b53a1788da5ce1b320b6cecb146d95ac8 Mon Sep 17 00:00:00 2001 From: Luke Date: Mon, 28 Jul 2025 18:00:41 +0800 Subject: [PATCH 1/4] =?UTF-8?q?feat:=20=E6=B7=BB=E5=8A=A0=20break=20?= =?UTF-8?q?=E8=AF=AD=E5=8F=A5=E6=94=AF=E6=8C=81?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 新增 BreakNode、BreakAnalyzer 和 BreakStatementParser 类 - 在 AnalyzerRegistrar 中注册 break 语句分析器 - 在 ASTPrinter 中添加 break 语句的打印处理 - 在 StatementBuilder 中实现 break 语句的 IR 构建逻辑 - 更新 StatementParserFactory 和 TokenFactory,支持 break 关键字 --- .../compiler/ir/builder/StatementBuilder.java | 24 ++++++++++++++-- .../compiler/lexer/token/TokenFactory.java | 2 +- .../snow/compiler/parser/ast/BreakNode.java | 16 +++++++++++ .../factory/StatementParserFactory.java | 1 + .../compiler/parser/function/ASTPrinter.java | 1 + .../statement/BreakStatementParser.java | 28 +++++++++++++++++++ .../analyzers/statement/BreakAnalyzer.java | 28 +++++++++++++++++++ .../semantic/core/AnalyzerRegistrar.java | 5 ++-- 8 files changed, 100 insertions(+), 5 deletions(-) create mode 100644 src/main/java/org/jcnc/snow/compiler/parser/ast/BreakNode.java create mode 100644 src/main/java/org/jcnc/snow/compiler/parser/statement/BreakStatementParser.java create mode 100644 src/main/java/org/jcnc/snow/compiler/semantic/analyzers/statement/BreakAnalyzer.java 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 47bd7ca..0c540fa 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 @@ -9,6 +9,7 @@ import org.jcnc.snow.compiler.parser.ast.base.ExpressionNode; import org.jcnc.snow.compiler.parser.ast.base.NodeContext; import org.jcnc.snow.compiler.parser.ast.base.StatementNode; +import java.util.ArrayDeque; import java.util.Locale; /** @@ -27,6 +28,10 @@ public class StatementBuilder { * 表达式 IR 构建器,用于将表达式节点转为 IR 指令。 */ private final ExpressionBuilder expr; + /** + * break 目标标签栈(保存每层循环的结束标签) + */ + private final ArrayDeque breakTargets = new ArrayDeque<>(); /** * 构造方法。 @@ -120,6 +125,14 @@ public class StatementBuilder { } return; } + if (stmt instanceof BreakNode) { + // break 语句:跳转到当前最近一层循环的结束标签 + if (breakTargets.isEmpty()) { + throw new IllegalStateException("`break` appears outside of a loop"); + } + InstructionFactory.jmp(ctx, breakTargets.peek()); + return; + } // 不支持的语句类型 throw new IllegalStateException("Unsupported statement: " + stmt.getClass().getSimpleName() + ": " + stmt); } @@ -163,8 +176,15 @@ public class StatementBuilder { // 条件不满足则跳出循环 emitConditionalJump(loop.cond(), lblEnd); - // 构建循环体 - buildStatements(loop.body()); + // 在进入循环体前,记录本层循环的结束标签,供 break 使用 + breakTargets.push(lblEnd); + try { + // 构建循环体 + buildStatements(loop.body()); + } finally { + // 离开循环体时弹出标签,避免影响外层 + breakTargets.pop(); + } // 更新部分(如 for 的 i++) if (loop.step() != null) build(loop.step()); 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 c22acf0..a251532 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 @@ -25,7 +25,7 @@ public class TokenFactory { /** * 语言的保留关键字集合。 */ - private static final Set KEYWORDS = Set.of("module", "function", "parameter", "return_type", "body", "end", "if", "then", "else", "loop", "declare", "return", "import", "init", "cond", "step","globals"); + private static final Set KEYWORDS = Set.of("module", "function", "parameter", "return_type", "body", "end", "if", "then", "else", "loop", "declare", "return", "import", "init", "cond", "step","globals","break"); /** * 内置类型名称集合,如 int、string 等。 diff --git a/src/main/java/org/jcnc/snow/compiler/parser/ast/BreakNode.java b/src/main/java/org/jcnc/snow/compiler/parser/ast/BreakNode.java new file mode 100644 index 0000000..f131c72 --- /dev/null +++ b/src/main/java/org/jcnc/snow/compiler/parser/ast/BreakNode.java @@ -0,0 +1,16 @@ +package org.jcnc.snow.compiler.parser.ast; + +import org.jcnc.snow.compiler.parser.ast.base.NodeContext; +import org.jcnc.snow.compiler.parser.ast.base.StatementNode; + +/** + * {@code BreakNode} 表示循环体中的 break 语句。 + * 出现时应立即终止当前(最内层)循环。 + */ +public record BreakNode(NodeContext context) implements StatementNode { + + @Override + public String toString() { + return "break@" + context; + } +} diff --git a/src/main/java/org/jcnc/snow/compiler/parser/factory/StatementParserFactory.java b/src/main/java/org/jcnc/snow/compiler/parser/factory/StatementParserFactory.java index 06be40a..1272ffa 100644 --- a/src/main/java/org/jcnc/snow/compiler/parser/factory/StatementParserFactory.java +++ b/src/main/java/org/jcnc/snow/compiler/parser/factory/StatementParserFactory.java @@ -27,6 +27,7 @@ public class StatementParserFactory { registry.put("if", new IfStatementParser()); registry.put("loop", new LoopStatementParser()); registry.put("return", new ReturnStatementParser()); + registry.put("break", new BreakStatementParser()); // 默认处理器: 表达式语句 registry.put("", new ExpressionStatementParser()); 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 91ed077..b440a4a 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 @@ -116,6 +116,7 @@ public class ASTPrinter { } case ReturnNode r -> System.out.println(pad + "return" + r.getExpression().map(e -> " " + e).orElse("")); + case BreakNode _ -> System.out.println(pad + "break"); case ExpressionStatementNode(ExpressionNode expression, NodeContext _) -> System.out.println(pad + expression); case null, default -> System.out.println(pad + n); // 回退处理 diff --git a/src/main/java/org/jcnc/snow/compiler/parser/statement/BreakStatementParser.java b/src/main/java/org/jcnc/snow/compiler/parser/statement/BreakStatementParser.java new file mode 100644 index 0000000..6e38335 --- /dev/null +++ b/src/main/java/org/jcnc/snow/compiler/parser/statement/BreakStatementParser.java @@ -0,0 +1,28 @@ +package org.jcnc.snow.compiler.parser.statement; + +import org.jcnc.snow.compiler.lexer.token.TokenType; +import org.jcnc.snow.compiler.parser.ast.BreakNode; +import org.jcnc.snow.compiler.parser.ast.base.NodeContext; +import org.jcnc.snow.compiler.parser.context.ParserContext; + +/** + * 解析 break 语句:仅包含关键字本身,并以换行结束。 + * 语义:立即终止当前(最内层)循环。 + */ +public class BreakStatementParser implements StatementParser { + + @Override + public BreakNode parse(ParserContext ctx) { + // 记录当前位置作为 NodeContext + int line = ctx.getTokens().peek().getLine(); + int column = ctx.getTokens().peek().getCol(); + String file = ctx.getSourceName(); + + // 消耗 'break' + ctx.getTokens().expect("break"); + // 行结束 + ctx.getTokens().expectType(TokenType.NEWLINE); + + return new BreakNode(new NodeContext(line, column, file)); + } +} diff --git a/src/main/java/org/jcnc/snow/compiler/semantic/analyzers/statement/BreakAnalyzer.java b/src/main/java/org/jcnc/snow/compiler/semantic/analyzers/statement/BreakAnalyzer.java new file mode 100644 index 0000000..2aeba8e --- /dev/null +++ b/src/main/java/org/jcnc/snow/compiler/semantic/analyzers/statement/BreakAnalyzer.java @@ -0,0 +1,28 @@ +package org.jcnc.snow.compiler.semantic.analyzers.statement; + +import org.jcnc.snow.compiler.parser.ast.BreakNode; +import org.jcnc.snow.compiler.parser.ast.FunctionNode; +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.symbol.SymbolTable; + +/** + * {@code BreakAnalyzer} —— break 语句的语义分析器。 + *

+ * 目前不做额外检查;是否位于循环体内的限制由 IR 构建阶段(StatementBuilder) + * 进行安全校验(若出现在循环外,会抛出清晰的错误)。 + * 如需在语义阶段提前报错,可在此处结合“循环上下文”进行校验。 + */ +public class BreakAnalyzer implements StatementAnalyzer { + + @Override + public void analyze(Context ctx, + ModuleInfo mi, + FunctionNode fn, + SymbolTable locals, + BreakNode stmt) { + // no-op: break 本身不引入新符号或类型约束 + // 在语义阶段校验“是否处于循环内”,需要在 ctx 或 SymbolTable 上增加上下文标记后在此检查。 + } +} 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 47af0c2..18d4c0c 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 @@ -12,7 +12,7 @@ import org.jcnc.snow.compiler.semantic.analyzers.statement.*; * 本类为静态工具类,不可实例化,其唯一公开方法 {@link #registerAll(AnalyzerRegistry)} * 应在语义分析初始化阶段调用一次,确保所有节点类型都能正确分发到对应分析器。 *

- * 注册内容包括: + * 注册内容包括: *

    *
  • 所有标准语句节点(如变量声明、赋值、条件、循环、返回等)的分析器;
  • *
  • 所有标准表达式节点(如字面量、标识符、函数调用、二元表达式等)的分析器;
  • @@ -40,6 +40,7 @@ public final class AnalyzerRegistrar { registry.registerStatementAnalyzer(LoopNode.class, new LoopAnalyzer()); registry.registerStatementAnalyzer(ReturnNode.class, new ReturnAnalyzer()); registry.registerExpressionAnalyzer(BoolLiteralNode.class, new BoolLiteralAnalyzer()); + registry.registerStatementAnalyzer(BreakNode.class, new BreakAnalyzer()); // 特殊处理: 表达式语句(如 "foo();")作为语句包装表达式 registry.registerStatementAnalyzer(ExpressionStatementNode.class, @@ -56,7 +57,7 @@ public final class AnalyzerRegistrar { registry.registerExpressionAnalyzer(BinaryExpressionNode.class, new BinaryExpressionAnalyzer()); // ---------- 注册一元表达式分析器 ---------- - registry.registerExpressionAnalyzer(UnaryExpressionNode.class,new UnaryExpressionAnalyzer()); + registry.registerExpressionAnalyzer(UnaryExpressionNode.class, new UnaryExpressionAnalyzer()); // 对尚未实现的表达式类型使用兜底处理器 registry.registerExpressionAnalyzer(MemberExpressionNode.class, From 63e8b0163ad11f267c289e367d78702e10f474a8 Mon Sep 17 00:00:00 2001 From: Luke Date: Mon, 28 Jul 2025 18:01:06 +0800 Subject: [PATCH 2/4] =?UTF-8?q?test:=20=E6=B7=BB=E5=8A=A0=20Demo17?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- playground/Demo/Demo17/Main.snow | 23 +++++++++++++++++++++++ playground/Demo/Demo17/OS.snow | 11 +++++++++++ 2 files changed, 34 insertions(+) create mode 100644 playground/Demo/Demo17/Main.snow create mode 100644 playground/Demo/Demo17/OS.snow diff --git a/playground/Demo/Demo17/Main.snow b/playground/Demo/Demo17/Main.snow new file mode 100644 index 0000000..6171781 --- /dev/null +++ b/playground/Demo/Demo17/Main.snow @@ -0,0 +1,23 @@ +module: Main + import: os + function: main + return_type: int + body: + loop: + init: + declare i:int = 1 + cond: + i <= 10 + step: + i = i + 1 + body: + if i % 2 == 0 then + print(i) + break + end if + end body + end loop + return 0 + end body + end function +end module diff --git a/playground/Demo/Demo17/OS.snow b/playground/Demo/Demo17/OS.snow new file mode 100644 index 0000000..6026d43 --- /dev/null +++ b/playground/Demo/Demo17/OS.snow @@ -0,0 +1,11 @@ +module: os + import: os + function: print + parameter: + declare i1: int + return_type: void + body: + syscall("PRINT",i1) + end body + end function +end module \ No newline at end of file From 5b8d7b98f37e9fc23325740824dc0d0f9f29e515 Mon Sep 17 00:00:00 2001 From: Luke Date: Mon, 28 Jul 2025 18:01:41 +0800 Subject: [PATCH 3/4] =?UTF-8?q?chore:=20=E6=9B=B4=E6=96=B0=20Demo16=20?= =?UTF-8?q?=E5=92=8C=20Demo17=20=E7=9A=84=E8=BF=90=E8=A1=8C=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/Demo16.run.xml | 2 +- .run/Demo17.run.xml | 10 ++++++++++ 2 files changed, 11 insertions(+), 1 deletion(-) create mode 100644 .run/Demo17.run.xml diff --git a/.run/Demo16.run.xml b/.run/Demo16.run.xml index 049eba5..f217625 100644 --- a/.run/Demo16.run.xml +++ b/.run/Demo16.run.xml @@ -2,7 +2,7 @@