!46 feat: loop 循环实现了 break 关键字的支持
Merge pull request !46 from Luke/feature/add-break-operator
This commit is contained in:
commit
c334dd3e28
@ -2,7 +2,7 @@
|
|||||||
<configuration default="false" name="Demo16" type="Application" factoryName="Application" folderName="Demo">
|
<configuration default="false" name="Demo16" type="Application" factoryName="Application" folderName="Demo">
|
||||||
<option name="MAIN_CLASS_NAME" value="org.jcnc.snow.cli.SnowCLI" />
|
<option name="MAIN_CLASS_NAME" value="org.jcnc.snow.cli.SnowCLI" />
|
||||||
<module name="Snow" />
|
<module name="Snow" />
|
||||||
<option name="PROGRAM_PARAMETERS" value="compile run -d playground/Demo/Demo16 -o target/Demo16" />
|
<option name="PROGRAM_PARAMETERS" value="compile run -d playground/Demo/Demo16 -o target/Demo16 --debug" />
|
||||||
<method v="2">
|
<method v="2">
|
||||||
<option name="Make" enabled="true" />
|
<option name="Make" enabled="true" />
|
||||||
</method>
|
</method>
|
||||||
|
|||||||
10
.run/Demo17.run.xml
Normal file
10
.run/Demo17.run.xml
Normal file
@ -0,0 +1,10 @@
|
|||||||
|
<component name="ProjectRunConfigurationManager">
|
||||||
|
<configuration default="false" name="Demo17" type="Application" factoryName="Application" folderName="Demo">
|
||||||
|
<option name="MAIN_CLASS_NAME" value="org.jcnc.snow.cli.SnowCLI" />
|
||||||
|
<module name="Snow" />
|
||||||
|
<option name="PROGRAM_PARAMETERS" value="compile run -d playground/Demo/Demo17 -o target/Demo17 --debug" />
|
||||||
|
<method v="2">
|
||||||
|
<option name="Make" enabled="true" />
|
||||||
|
</method>
|
||||||
|
</configuration>
|
||||||
|
</component>
|
||||||
@ -7,6 +7,8 @@
|
|||||||
<toRun name="Demo13" type="Application" />
|
<toRun name="Demo13" type="Application" />
|
||||||
<toRun name="Demo14" type="Application" />
|
<toRun name="Demo14" type="Application" />
|
||||||
<toRun name="Demo15" type="Application" />
|
<toRun name="Demo15" type="Application" />
|
||||||
|
<toRun name="Demo16" type="Application" />
|
||||||
|
<toRun name="Demo17" type="Application" />
|
||||||
<toRun name="Demo2" type="Application" />
|
<toRun name="Demo2" type="Application" />
|
||||||
<toRun name="Demo3" type="Application" />
|
<toRun name="Demo3" type="Application" />
|
||||||
<toRun name="Demo4" type="Application" />
|
<toRun name="Demo4" type="Application" />
|
||||||
|
|||||||
23
playground/Demo/Demo17/Main.snow
Normal file
23
playground/Demo/Demo17/Main.snow
Normal file
@ -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
|
||||||
11
playground/Demo/Demo17/OS.snow
Normal file
11
playground/Demo/Demo17/OS.snow
Normal file
@ -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
|
||||||
@ -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.NodeContext;
|
||||||
import org.jcnc.snow.compiler.parser.ast.base.StatementNode;
|
import org.jcnc.snow.compiler.parser.ast.base.StatementNode;
|
||||||
|
|
||||||
|
import java.util.ArrayDeque;
|
||||||
import java.util.Locale;
|
import java.util.Locale;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -27,6 +28,10 @@ public class StatementBuilder {
|
|||||||
* 表达式 IR 构建器,用于将表达式节点转为 IR 指令。
|
* 表达式 IR 构建器,用于将表达式节点转为 IR 指令。
|
||||||
*/
|
*/
|
||||||
private final ExpressionBuilder expr;
|
private final ExpressionBuilder expr;
|
||||||
|
/**
|
||||||
|
* break 目标标签栈(保存每层循环的结束标签)
|
||||||
|
*/
|
||||||
|
private final ArrayDeque<String> breakTargets = new ArrayDeque<>();
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 构造方法。
|
* 构造方法。
|
||||||
@ -120,6 +125,14 @@ public class StatementBuilder {
|
|||||||
}
|
}
|
||||||
return;
|
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);
|
throw new IllegalStateException("Unsupported statement: " + stmt.getClass().getSimpleName() + ": " + stmt);
|
||||||
}
|
}
|
||||||
@ -163,8 +176,15 @@ public class StatementBuilder {
|
|||||||
|
|
||||||
// 条件不满足则跳出循环
|
// 条件不满足则跳出循环
|
||||||
emitConditionalJump(loop.cond(), lblEnd);
|
emitConditionalJump(loop.cond(), lblEnd);
|
||||||
// 构建循环体
|
// 在进入循环体前,记录本层循环的结束标签,供 break 使用
|
||||||
buildStatements(loop.body());
|
breakTargets.push(lblEnd);
|
||||||
|
try {
|
||||||
|
// 构建循环体
|
||||||
|
buildStatements(loop.body());
|
||||||
|
} finally {
|
||||||
|
// 离开循环体时弹出标签,避免影响外层
|
||||||
|
breakTargets.pop();
|
||||||
|
}
|
||||||
// 更新部分(如 for 的 i++)
|
// 更新部分(如 for 的 i++)
|
||||||
if (loop.step() != null) build(loop.step());
|
if (loop.step() != null) build(loop.step());
|
||||||
|
|
||||||
|
|||||||
@ -25,7 +25,7 @@ public class TokenFactory {
|
|||||||
/**
|
/**
|
||||||
* 语言的保留关键字集合。
|
* 语言的保留关键字集合。
|
||||||
*/
|
*/
|
||||||
private static final Set<String> 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<String> KEYWORDS = Set.of("module", "function", "parameter", "return_type", "body", "end", "if", "then", "else", "loop", "declare", "return", "import", "init", "cond", "step","globals","break");
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 内置类型名称集合,如 int、string 等。
|
* 内置类型名称集合,如 int、string 等。
|
||||||
|
|||||||
@ -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;
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -27,6 +27,7 @@ public class StatementParserFactory {
|
|||||||
registry.put("if", new IfStatementParser());
|
registry.put("if", new IfStatementParser());
|
||||||
registry.put("loop", new LoopStatementParser());
|
registry.put("loop", new LoopStatementParser());
|
||||||
registry.put("return", new ReturnStatementParser());
|
registry.put("return", new ReturnStatementParser());
|
||||||
|
registry.put("break", new BreakStatementParser());
|
||||||
|
|
||||||
// 默认处理器: 表达式语句
|
// 默认处理器: 表达式语句
|
||||||
registry.put("", new ExpressionStatementParser());
|
registry.put("", new ExpressionStatementParser());
|
||||||
|
|||||||
@ -116,6 +116,7 @@ public class ASTPrinter {
|
|||||||
}
|
}
|
||||||
case ReturnNode r -> System.out.println(pad + "return" +
|
case ReturnNode r -> System.out.println(pad + "return" +
|
||||||
r.getExpression().map(e -> " " + e).orElse(""));
|
r.getExpression().map(e -> " " + e).orElse(""));
|
||||||
|
case BreakNode _ -> System.out.println(pad + "break");
|
||||||
case ExpressionStatementNode(ExpressionNode expression, NodeContext _) ->
|
case ExpressionStatementNode(ExpressionNode expression, NodeContext _) ->
|
||||||
System.out.println(pad + expression);
|
System.out.println(pad + expression);
|
||||||
case null, default -> System.out.println(pad + n); // 回退处理
|
case null, default -> System.out.println(pad + n); // 回退处理
|
||||||
|
|||||||
@ -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));
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -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 语句的语义分析器。
|
||||||
|
* <p>
|
||||||
|
* 目前不做额外检查;是否位于循环体内的限制由 IR 构建阶段(StatementBuilder)
|
||||||
|
* 进行安全校验(若出现在循环外,会抛出清晰的错误)。
|
||||||
|
* 如需在语义阶段提前报错,可在此处结合“循环上下文”进行校验。
|
||||||
|
*/
|
||||||
|
public class BreakAnalyzer implements StatementAnalyzer<BreakNode> {
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void analyze(Context ctx,
|
||||||
|
ModuleInfo mi,
|
||||||
|
FunctionNode fn,
|
||||||
|
SymbolTable locals,
|
||||||
|
BreakNode stmt) {
|
||||||
|
// no-op: break 本身不引入新符号或类型约束
|
||||||
|
// 在语义阶段校验“是否处于循环内”,需要在 ctx 或 SymbolTable 上增加上下文标记后在此检查。
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -40,6 +40,7 @@ public final class AnalyzerRegistrar {
|
|||||||
registry.registerStatementAnalyzer(LoopNode.class, new LoopAnalyzer());
|
registry.registerStatementAnalyzer(LoopNode.class, new LoopAnalyzer());
|
||||||
registry.registerStatementAnalyzer(ReturnNode.class, new ReturnAnalyzer());
|
registry.registerStatementAnalyzer(ReturnNode.class, new ReturnAnalyzer());
|
||||||
registry.registerExpressionAnalyzer(BoolLiteralNode.class, new BoolLiteralAnalyzer());
|
registry.registerExpressionAnalyzer(BoolLiteralNode.class, new BoolLiteralAnalyzer());
|
||||||
|
registry.registerStatementAnalyzer(BreakNode.class, new BreakAnalyzer());
|
||||||
|
|
||||||
// 特殊处理: 表达式语句(如 "foo();")作为语句包装表达式
|
// 特殊处理: 表达式语句(如 "foo();")作为语句包装表达式
|
||||||
registry.registerStatementAnalyzer(ExpressionStatementNode.class,
|
registry.registerStatementAnalyzer(ExpressionStatementNode.class,
|
||||||
@ -56,7 +57,7 @@ public final class AnalyzerRegistrar {
|
|||||||
registry.registerExpressionAnalyzer(BinaryExpressionNode.class, new BinaryExpressionAnalyzer());
|
registry.registerExpressionAnalyzer(BinaryExpressionNode.class, new BinaryExpressionAnalyzer());
|
||||||
|
|
||||||
// ---------- 注册一元表达式分析器 ----------
|
// ---------- 注册一元表达式分析器 ----------
|
||||||
registry.registerExpressionAnalyzer(UnaryExpressionNode.class,new UnaryExpressionAnalyzer());
|
registry.registerExpressionAnalyzer(UnaryExpressionNode.class, new UnaryExpressionAnalyzer());
|
||||||
|
|
||||||
// 对尚未实现的表达式类型使用兜底处理器
|
// 对尚未实现的表达式类型使用兜底处理器
|
||||||
registry.registerExpressionAnalyzer(MemberExpressionNode.class,
|
registry.registerExpressionAnalyzer(MemberExpressionNode.class,
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user