diff --git a/doc/Snow语言文档.md b/doc/Snow语言文档.md
index dab887d..36c5105 100644
--- a/doc/Snow语言文档.md
+++ b/doc/Snow语言文档.md
@@ -27,8 +27,6 @@ SCompiler 项目将源代码依次经过多个阶段处理,最终生成字节
扫描过程采用 **多通道扫描**:Lexer按字符顺序读取输入,对每个字符依次尝试上述扫描器,哪个扫描器的`canHandle()`方法返回true就交由其`handle()`处理。每个扫描器从`LexerContext`获取当前字符流状态,消费相应字符序列并通过`TokenFactory`创建Token加入结果列表。例如,IdentifierScanner读取字母序列后由TokenFactory判断是标识符还是关键字。Lexer会跳过空白和注释,不生成多余Token。扫描循环持续直到输入结尾,然后显式追加一个EOF(TokenType.EOF)作为结束标记。整个词法分析的输出是有序的Token列表,可供语法分析器消费。
-*健壮性*: 当前Lexer对未知字符不会抛异常,而是生成类型为UNKNOWN的Token并继续。这在后续解析中会导致错误。可改进之处包括:当出现非法字符时Lexer直接报错或记录错误信息,而不是依赖Parser再处理。
-
## 语法分析模块
**语法分析器(Parser)** 将Token序列按照语言文法规则还原为抽象语法树(AST)。SCompiler语言的文法大致为:*模块*由`module 模块名:`及缩进的多个函数定义组成,模块以`end module`结束;*函数定义*以`function 函数名(参数列表):`开始,内部缩进块包含若干语句,以`end function`结束。语句包括变量声明、赋值、条件`if/else`、循环`loop`、返回`return`以及表达式调用等。
@@ -53,9 +51,13 @@ Parser采用**递归下降**和**运算符优先解析**相结合的方法:顶
* **AST节点** – 抽象语法树采用面向对象节点类层次表示。基类`Node`定义了通用接口,各子类对应不同语法成分。主要节点类例如:ModuleNode(模块,含名称和函数列表)、FunctionNode(函数定义,含名称、参数列表、返回类型、函数体)、ImportNode(导入声明)、ParameterNode(形参定义,含名和类型)、Block结构节点如IfNode、LoopNode(包含条件和内部语句块),以及表达式节点如 IdentifierNode、NumberLiteralNode、BinaryExpressionNode 等。AST节点在解析时被实例化,语义分析阶段可能会在节点上附加类型等注释信息。整个Parser输出一个AST节点列表,一般情况下包含一个ModuleNode(源文件级模块)作为根。如果语法有误,解析器会抛出带错误位置和原因的异常停止编译。
-*健壮性*: 语法分析目前采用严格的期望匹配(`expect()`方法)和显式的`end`结束符,能够检测出缩进层级错误或缺失`end`等问题并报错。但由于没有维护一个专门的缩进栈,缩进不正确主要通过`end`不匹配来发现。设计上原本计划基于缩进来确定作用域(类似Python),但实际实现中通过`:`和成对的`end`显式标记块级范围,这种混合风格稍显冗余。改进方向可以是统一语法风格,例如完全采用缩进+换行而无`end`关键字,或采用显式花括号/`end`而无冒号。
+上面是Parser相关介绍
-另一个问题是Parser对顶层非模块内容的支持不完善:当前TopLevelParserFactory只注册了模块解析。如果源码未以`module`开头(比如仅有独立函数定义或语句),ParserEngine将无法识别顶层结构而报错。这在设计上可能考虑过支持**脚本模式**(无模块包裹的代码),从IR生成器看也有相应处理逻辑(将顶层Statement封装进`_start`函数),但由于顶层解析未实现直接处理Statement,实际使用中需要至少有一个模块声明。为提高灵活性,可扩展Parser支持隐式模块包装或允许顶层函数定义。最后,Parser目前对错误恢复支持有限,一旦遇到语法错误通常终止分析;将来可考虑在捕获错误后跳过一定Token继续分析,收集多个错误再统一报告。
+## 需要你完成的
+1. 解压然后读取项目里面全部相关代码,然后解决下面的问题
+2. 目前问题是Parser对顶层非模块内容的支持不完善:当前TopLevelParserFactory只注册了模块解析。如果源码未以`module`开头(比如仅有独立函数定义或语句),ParserEngine将无法识别顶层结构而报错。这在设计上可能考虑过支持**脚本模式**(无模块包裹的代码),从IR生成器看也有相应处理逻辑(将顶层Statement封装进`_start`函数),但由于顶层解析未实现直接处理Statement,实际使用中需要至少有一个模块声明。目标:为提高灵活性,扩展Parser支持隐式模块包装或允许顶层函数定义。
+3. Parser目前对错误恢复支持有限,一旦遇到语法错误通常终止分析;实现在捕获错误后跳过一定Token继续分析,收集多个错误再统一报告。
+4. 帮我解决以上两个问题,确保修改后和后面模块兼容,给我修改后的代码和新增的代码
## 语义分析模块
diff --git a/src/main/java/org/jcnc/snow/compiler/parser/context/TokenStream.java b/src/main/java/org/jcnc/snow/compiler/parser/context/TokenStream.java
index f4a1cc4..b16a395 100644
--- a/src/main/java/org/jcnc/snow/compiler/parser/context/TokenStream.java
+++ b/src/main/java/org/jcnc/snow/compiler/parser/context/TokenStream.java
@@ -115,11 +115,13 @@ public class TokenStream {
}
/**
- * 判断是否尚未到达 EOF。
+ * 判断是否“已经”到达 EOF。
*
- * @return 若当前位置 Token 非 EOF,则返回 true
+ * @return 若当前位置 Token 为 EOF,则返回 true,否则 false
*/
public boolean isAtEnd() {
- return peek().getType() != TokenType.EOF;
+ return peek().getType() == TokenType.EOF; // ← 修正逻辑
}
+
+
}
\ No newline at end of file
diff --git a/src/main/java/org/jcnc/snow/compiler/parser/core/ParserEngine.java b/src/main/java/org/jcnc/snow/compiler/parser/core/ParserEngine.java
index 8567eb2..df71c8d 100644
--- a/src/main/java/org/jcnc/snow/compiler/parser/core/ParserEngine.java
+++ b/src/main/java/org/jcnc/snow/compiler/parser/core/ParserEngine.java
@@ -10,62 +10,59 @@ import org.jcnc.snow.compiler.parser.ast.base.Node;
import java.util.ArrayList;
import java.util.List;
-/**
- * {@code ParserEngine} 是语法分析阶段的主控引擎,
- * 负责驱动顶层语法结构(如模块定义、导入语句、函数定义等)的解析流程。
- *
- * 它通过不断读取 TokenStream 中的标记,动态选择合适的解析器(由工厂提供),
- * 并构建对应的抽象语法树(AST)节点。
- * 同时具备跳过空行、处理非法标记等基本容错能力。
- *
- */
public class ParserEngine {
- /** 解析上下文,封装 TokenStream 与语法状态 */
private final ParserContext ctx;
- /**
- * 构造一个 {@code ParserEngine} 实例。
- *
- * @param ctx 提供语法分析所需的上下文信息(如 Token 流)
- */
public ParserEngine(ParserContext ctx) {
this.ctx = ctx;
}
- /**
- * 启动语法解析流程,提取所有顶层 AST 节点。
- *
- * 该方法会循环调用 {@link TopLevelParserFactory} 选择合适解析器,
- * 并将解析结果存入返回列表,直到遇到文件结束符(EOF)。
- *
- *
- * @return 顶层 AST 节点列表(如模块、导入、函数等)
- * @throws IllegalStateException 若遇到无法识别的顶层标记
- */
public List parse() {
- List nodes = new ArrayList<>();
- TokenStream ts = ctx.getTokens();
+ List nodes = new ArrayList<>();
+ List errs = new ArrayList<>();
+ TokenStream ts = ctx.getTokens();
- while (ts.isAtEnd()) {
- // 跳过 NEWLINE 以增强容错性
+ while (!ts.isAtEnd()) { // ← 取反
+ // 跳过空行
if (ts.peek().getType() == TokenType.NEWLINE) {
ts.next();
continue;
}
- // 当前词素作为解析关键字(如 module, import)
- String lex = ts.peek().getLexeme();
+ TopLevelParser parser = TopLevelParserFactory.get(ts.peek().getLexeme());
- TopLevelParser parser = TopLevelParserFactory.get(lex);
- if (parser == null) {
- throw new IllegalStateException("意外的顶级标记: " + lex);
+ try {
+ nodes.add(parser.parse(ctx));
+ } catch (Exception ex) {
+ errs.add(ex.getMessage());
+ synchronize(ts); // 错误恢复
}
-
- // 执行对应解析器
- nodes.add(parser.parse(ctx));
}
+ if (!errs.isEmpty()) {
+ throw new IllegalStateException("解析过程中检测到 "
+ + errs.size() + " 处错误:\n - "
+ + String.join("\n - ", errs));
+ }
return nodes;
}
-}
\ No newline at end of file
+
+ /** 错误同步:跳到下一行或下一个已注册顶层关键字 */
+ private void synchronize(TokenStream ts) {
+ while (!ts.isAtEnd()) {
+ if (ts.peek().getType() == TokenType.NEWLINE) {
+ ts.next();
+ break;
+ }
+ if (TopLevelParserFactory.get(ts.peek().getLexeme()) != null) {
+ break;
+ }
+ ts.next();
+ }
+ // 连续空行全部吃掉
+ while (!ts.isAtEnd() && ts.peek().getType() == TokenType.NEWLINE) {
+ ts.next();
+ }
+ }
+}
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 cae7e8c..c2a818b 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
@@ -27,33 +27,37 @@ import java.util.Map;
*/
public class PrattExpressionParser implements ExpressionParser {
- /** 前缀解析器注册表:按 Token 类型映射 */
+ /**
+ * 前缀解析器注册表:按 Token 类型映射
+ */
private static final Map prefixes = new HashMap<>();
- /** 中缀解析器注册表:按运算符词素映射 */
- private static final Map infixes = new HashMap<>();
+ /**
+ * 中缀解析器注册表:按运算符词素映射
+ */
+ private static final Map infixes = new HashMap<>();
static {
// 注册前缀解析器
prefixes.put(TokenType.NUMBER_LITERAL.name(), new NumberLiteralParselet());
- prefixes.put(TokenType.IDENTIFIER.name(), new IdentifierParselet());
- prefixes.put(TokenType.LPAREN.name(), new GroupingParselet());
+ prefixes.put(TokenType.IDENTIFIER.name(), new IdentifierParselet());
+ prefixes.put(TokenType.LPAREN.name(), new GroupingParselet());
prefixes.put(TokenType.STRING_LITERAL.name(), new StringLiteralParselet());
// 注册中缀解析器
- infixes.put("+", new BinaryOperatorParselet(Precedence.SUM, true));
- infixes.put("-", new BinaryOperatorParselet(Precedence.SUM, true));
- infixes.put("*", new BinaryOperatorParselet(Precedence.PRODUCT, true));
- infixes.put("/", new BinaryOperatorParselet(Precedence.PRODUCT, true));
- infixes.put("%", new BinaryOperatorParselet(Precedence.PRODUCT, true));
- infixes.put(">", new BinaryOperatorParselet(Precedence.SUM, true));
- infixes.put("<", new BinaryOperatorParselet(Precedence.SUM, true));
+ infixes.put("+", new BinaryOperatorParselet(Precedence.SUM, true));
+ infixes.put("-", new BinaryOperatorParselet(Precedence.SUM, true));
+ infixes.put("*", new BinaryOperatorParselet(Precedence.PRODUCT, true));
+ infixes.put("/", new BinaryOperatorParselet(Precedence.PRODUCT, true));
+ infixes.put("%", new BinaryOperatorParselet(Precedence.PRODUCT, true));
+ infixes.put(">", new BinaryOperatorParselet(Precedence.SUM, true));
+ infixes.put("<", new BinaryOperatorParselet(Precedence.SUM, true));
infixes.put("==", new BinaryOperatorParselet(Precedence.SUM, true));
infixes.put("!=", new BinaryOperatorParselet(Precedence.SUM, true));
infixes.put(">=", new BinaryOperatorParselet(Precedence.SUM, true));
infixes.put("<=", new BinaryOperatorParselet(Precedence.SUM, true));
- infixes.put("(", new CallParselet());
- infixes.put(".", new MemberParselet());
+ infixes.put("(", new CallParselet());
+ infixes.put(".", new MemberParselet());
}
/**
@@ -83,14 +87,13 @@ public class PrattExpressionParser implements ExpressionParser {
ExpressionNode left = prefix.parse(ctx, token);
- while (ctx.getTokens().isAtEnd() && prec.ordinal() < nextPrecedence(ctx)) {
+ while (!ctx.getTokens().isAtEnd()
+ && prec.ordinal() < nextPrecedence(ctx)) {
String lex = ctx.getTokens().peek().getLexeme();
InfixParselet infix = infixes.get(lex);
if (infix == null) break;
-
left = infix.parse(ctx, left);
}
-
return left;
}
diff --git a/src/main/java/org/jcnc/snow/compiler/parser/factory/TopLevelParserFactory.java b/src/main/java/org/jcnc/snow/compiler/parser/factory/TopLevelParserFactory.java
index 3521d31..7fa779c 100644
--- a/src/main/java/org/jcnc/snow/compiler/parser/factory/TopLevelParserFactory.java
+++ b/src/main/java/org/jcnc/snow/compiler/parser/factory/TopLevelParserFactory.java
@@ -1,42 +1,29 @@
package org.jcnc.snow.compiler.parser.factory;
import org.jcnc.snow.compiler.parser.base.TopLevelParser;
-import org.jcnc.snow.compiler.parser.core.ParserEngine;
import org.jcnc.snow.compiler.parser.module.ModuleParser;
+import org.jcnc.snow.compiler.parser.function.FunctionParser;
+import org.jcnc.snow.compiler.parser.top.ScriptTopLevelParser;
import java.util.Map;
import java.util.HashMap;
-/**
- * {@code TopLevelParserFactory} 是一个顶层结构解析器工厂类,
- * 用于根据源文件起始关键字(如 {@code module})动态选择相应的 {@link TopLevelParser} 实现。
- *
- *
- * 每种顶层结构(如模块、导入等)应实现 {@link TopLevelParser} 接口,并在本工厂中静态注册,
- * 从而在语法分析阶段由 {@link ParserEngine} 根据关键字调用对应的解析器。
- *
- *
- *
- * 本类采用静态注册表机制,在类加载时将支持的关键字与其解析器一一映射并缓存于内部 Map 中。
- * 若调用时传入的关键字未注册,则返回 {@code null},调用方应自行处理此情况。
- *
- */
public class TopLevelParserFactory {
- /** 顶层关键字 -> 顶层结构解析器 的映射表 */
+
private static final Map registry = new HashMap<>();
+ private static final TopLevelParser DEFAULT = new ScriptTopLevelParser(); // ← 默认解析器
static {
- // 注册顶层结构解析器
- registry.put("module", new ModuleParser());
+ // 顶层结构解析器
+ registry.put("module", new ModuleParser());
+ registry.put("function", new FunctionParser());
+ // 也可按需继续注册其它关键字
}
/**
- * 根据给定关键字获取对应的顶层结构解析器。
- *
- * @param keyword 顶层结构关键字(例如 "module")。
- * @return 对应的 {@link TopLevelParser} 实例;若关键字未注册,则返回 {@code null}。
+ * 根据关键字获取解析器;若未注册,回退到脚本语句解析。
*/
public static TopLevelParser get(String keyword) {
- return registry.get(keyword);
+ return registry.getOrDefault(keyword, DEFAULT);
}
}
diff --git a/src/main/java/org/jcnc/snow/compiler/parser/top/ScriptTopLevelParser.java b/src/main/java/org/jcnc/snow/compiler/parser/top/ScriptTopLevelParser.java
new file mode 100644
index 0000000..2190423
--- /dev/null
+++ b/src/main/java/org/jcnc/snow/compiler/parser/top/ScriptTopLevelParser.java
@@ -0,0 +1,25 @@
+package org.jcnc.snow.compiler.parser.top;
+
+import org.jcnc.snow.compiler.parser.base.TopLevelParser;
+import org.jcnc.snow.compiler.parser.ast.base.Node;
+import org.jcnc.snow.compiler.parser.ast.base.StatementNode;
+import org.jcnc.snow.compiler.parser.context.ParserContext;
+import org.jcnc.snow.compiler.parser.factory.StatementParserFactory;
+import org.jcnc.snow.compiler.parser.statement.StatementParser;
+
+/**
+ * {@code ScriptTopLevelParser} 允许在无 module 包裹的情况下
+ * 直接解析单条顶层语句(脚本模式)。
+ *
+ * 解析得到的 {@link StatementNode} 将在 IR 阶段被封装成 _start 函数。
+ */
+public class ScriptTopLevelParser implements TopLevelParser {
+
+ @Override
+ public Node parse(ParserContext ctx) {
+ String first = ctx.getTokens().peek().getLexeme();
+ StatementParser sp = StatementParserFactory.get(first);
+ StatementNode stmt = sp.parse(ctx);
+ return stmt; // StatementNode 亦是 Node
+ }
+}
diff --git a/test b/test
index 92297d4..1f3471f 100644
--- a/test
+++ b/test
@@ -1,5 +1,5 @@
module: CommonTasks
- function: main@
+ function: main
parameter:
return_type:int