From 7395256b5a48547462e220c41f8f9dff5cfadd32 Mon Sep 17 00:00:00 2001 From: Luke Date: Tue, 29 Jul 2025 11:20:46 +0800 Subject: [PATCH] =?UTF-8?q?feat:=20=E6=B7=BB=E5=8A=A0=20continue=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 - 新增 ContinueNode、ContinueAnalyzer 和 ContinueStatementParser 类 - 在 AnalyzerRegistrar 中注册 continue 语句分析器 - 在 StatementBuilder 中添加 continue 语句的处理逻辑 - 更新 StatementParserFactory 和 TokenFactory 以支持 continue 关键字 --- .../compiler/ir/builder/StatementBuilder.java | 21 +++++++++- .../compiler/lexer/token/TokenFactory.java | 5 ++- .../compiler/parser/ast/ContinueNode.java | 19 +++++++++ .../factory/StatementParserFactory.java | 1 + .../statement/ContinueStatementParser.java | 40 ++++++++++++++++++ .../analyzers/statement/ContinueAnalyzer.java | 42 +++++++++++++++++++ .../semantic/core/AnalyzerRegistrar.java | 1 + 7 files changed, 126 insertions(+), 3 deletions(-) create mode 100644 src/main/java/org/jcnc/snow/compiler/parser/ast/ContinueNode.java create mode 100644 src/main/java/org/jcnc/snow/compiler/parser/statement/ContinueStatementParser.java create mode 100644 src/main/java/org/jcnc/snow/compiler/semantic/analyzers/statement/ContinueAnalyzer.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 0c540fa..98ab6ce 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 @@ -32,6 +32,10 @@ public class StatementBuilder { * break 目标标签栈(保存每层循环的结束标签) */ private final ArrayDeque breakTargets = new ArrayDeque<>(); + /** + * continue 目标标签栈(保存每层循环的 step 起始标签) + */ + private final ArrayDeque continueTargets = new ArrayDeque<>(); /** * 构造方法。 @@ -133,6 +137,14 @@ public class StatementBuilder { InstructionFactory.jmp(ctx, breakTargets.peek()); return; } + if (stmt instanceof ContinueNode) { + // continue 语句:跳转到当前最近一层循环的 step 起始标签 + if (continueTargets.isEmpty()) { + throw new IllegalStateException("`continue` appears outside of a loop"); + } + InstructionFactory.jmp(ctx, continueTargets.peek()); + return; + } // 不支持的语句类型 throw new IllegalStateException("Unsupported statement: " + stmt.getClass().getSimpleName() + ": " + stmt); } @@ -178,17 +190,22 @@ public class StatementBuilder { emitConditionalJump(loop.cond(), lblEnd); // 在进入循环体前,记录本层循环的结束标签,供 break 使用 breakTargets.push(lblEnd); + // 记录本层循环的 step 起始标签,供 continue 使用 + String lblStep = ctx.newLabel(); + continueTargets.push(lblStep); try { // 构建循环体 buildStatements(loop.body()); } finally { // 离开循环体时弹出标签,避免影响外层 breakTargets.pop(); + continueTargets.pop(); } - // 更新部分(如 for 的 i++) + // step 起始标签(所有 continue 会跳到这里) + InstructionFactory.label(ctx, lblStep); if (loop.step() != null) build(loop.step()); - // 跳回循环起点 + // 回到 cond 检查 InstructionFactory.jmp(ctx, lblStart); // 循环结束标签 InstructionFactory.label(ctx, lblEnd); 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 a251532..28e4697 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,10 @@ 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","break"); + 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", "continue"); /** * 内置类型名称集合,如 int、string 等。 diff --git a/src/main/java/org/jcnc/snow/compiler/parser/ast/ContinueNode.java b/src/main/java/org/jcnc/snow/compiler/parser/ast/ContinueNode.java new file mode 100644 index 0000000..b81dd75 --- /dev/null +++ b/src/main/java/org/jcnc/snow/compiler/parser/ast/ContinueNode.java @@ -0,0 +1,19 @@ +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 ContinueNode} 表示循环体中的 continue 语句节点。 + *

+ * continue 语句用于跳过当前循环剩余部分,直接进入下一次循环的 step→cond 阶段。 + * 该节点仅作为语法树中的一种语句类型出现。 + *

+ */ +public record ContinueNode(NodeContext context) implements StatementNode { + + @Override + public String toString() { + return "continue"; + } +} 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 1272ffa..640db1b 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 @@ -28,6 +28,7 @@ public class StatementParserFactory { registry.put("loop", new LoopStatementParser()); registry.put("return", new ReturnStatementParser()); registry.put("break", new BreakStatementParser()); + registry.put("continue",new ContinueStatementParser()); // 默认处理器: 表达式语句 registry.put("", new ExpressionStatementParser()); diff --git a/src/main/java/org/jcnc/snow/compiler/parser/statement/ContinueStatementParser.java b/src/main/java/org/jcnc/snow/compiler/parser/statement/ContinueStatementParser.java new file mode 100644 index 0000000..3379087 --- /dev/null +++ b/src/main/java/org/jcnc/snow/compiler/parser/statement/ContinueStatementParser.java @@ -0,0 +1,40 @@ +package org.jcnc.snow.compiler.parser.statement; + +import org.jcnc.snow.compiler.lexer.token.TokenType; +import org.jcnc.snow.compiler.parser.ast.ContinueNode; +import org.jcnc.snow.compiler.parser.ast.base.NodeContext; +import org.jcnc.snow.compiler.parser.context.ParserContext; + +/** + * {@code ContinueStatementParser} 用于解析 continue 语句。 + *

+ * continue 语句语法格式简单,仅包含关键字本身,随后以换行结束。 + * 语义:跳过本次剩余循环体,继续执行 step → cond。 + *

+ */ +public class ContinueStatementParser implements StatementParser { + + /** + * 解析 continue 语句节点。 + *

+ * 期望格式为:'continue' NEWLINE + *

+ * + * @param ctx 解析上下文 + * @return ContinueNode AST 节点 + */ + @Override + public ContinueNode parse(ParserContext ctx) { + // 记录当前位置作为 NodeContext + int line = ctx.getTokens().peek().getLine(); + int column = ctx.getTokens().peek().getCol(); + String file = ctx.getSourceName(); + + // 消耗 'continue' + ctx.getTokens().expect("continue"); + // 行结束 + ctx.getTokens().expectType(TokenType.NEWLINE); + + return new ContinueNode(new NodeContext(line, column, file)); + } +} diff --git a/src/main/java/org/jcnc/snow/compiler/semantic/analyzers/statement/ContinueAnalyzer.java b/src/main/java/org/jcnc/snow/compiler/semantic/analyzers/statement/ContinueAnalyzer.java new file mode 100644 index 0000000..f78f7b1 --- /dev/null +++ b/src/main/java/org/jcnc/snow/compiler/semantic/analyzers/statement/ContinueAnalyzer.java @@ -0,0 +1,42 @@ +package org.jcnc.snow.compiler.semantic.analyzers.statement; + +import org.jcnc.snow.compiler.parser.ast.ContinueNode; +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 ContinueAnalyzer} —— continue 语句的语义分析器。 + *

+ * 当前实现不做任何额外检查。continue 是否出现在有效的循环体内, + * 由 IR 构建阶段(如 StatementBuilder)负责判定与错误提示。 + *

+ *

+ * 用于 AST 的 {@link ContinueNode} 语句节点。 + *

+ */ +public class ContinueAnalyzer implements StatementAnalyzer { + + /** + * 分析 continue 语句节点的语义。 + *

+ * 该方法为 no-op,相关语义约束由后续阶段处理。 + *

+ * + * @param ctx 语义分析上下文 + * @param mi 当前模块信息 + * @param fn 当前所在函数节点 + * @param locals 当前作用域下的符号表 + * @param stmt 需要分析的 continue 语句节点 + */ + @Override + public void analyze(Context ctx, + ModuleInfo mi, + FunctionNode fn, + SymbolTable locals, + ContinueNode stmt) { + // no-op: continue 语句的合法性由 IR 构建阶段检查 + } +} 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 18d4c0c..0e5e318 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 @@ -41,6 +41,7 @@ public final class AnalyzerRegistrar { registry.registerStatementAnalyzer(ReturnNode.class, new ReturnAnalyzer()); registry.registerExpressionAnalyzer(BoolLiteralNode.class, new BoolLiteralAnalyzer()); registry.registerStatementAnalyzer(BreakNode.class, new BreakAnalyzer()); + registry.registerStatementAnalyzer(ContinueNode.class, new ContinueAnalyzer()); // 特殊处理: 表达式语句(如 "foo();")作为语句包装表达式 registry.registerStatementAnalyzer(ExpressionStatementNode.class,