From f1086a1ef925b898bf413fff1403adff881e3859 Mon Sep 17 00:00:00 2001
From: Luke
- * 当语法分析器遇到非法的语法结构或无法继续处理的标记序列时,
- * 应抛出该异常以中断当前解析流程,并向调用方报告错误信息。
- *
- * 该异常通常由 {@code ParserContext} 或各类语法规则处理器主动抛出,
- * 用于提示编译器前端或 IDE 系统进行错误提示与恢复。
- * 声明为 sealed,仅允许 {@link UnexpectedToken}、
+ * {@link MissingToken}、{@link UnsupportedFeature} 三个受信子类继承,
+ * 以便调用方根据异常类型进行精确处理。
+ * 负责驱动 Snow 源码的顶层语法结构解析,将源码 TokenStream
+ * 递交给各类 TopLevelParser,并收集语法树节点与异常。
+ * 支持容错解析,能够批量报告所有语法错误,并提供同步恢复功能。
+ *
+ * 典型用法:
+ *
+ * ParserEngine engine = new ParserEngine(context);
+ * List<Node> ast = engine.parse();
+ *
+ *
+ * 调用各类顶级语句解析器(如 module, func, import), + * 遇到错误时会自动跳过到下一行或已知结构关键字,继续后续分析, + * 最终汇总所有错误。如果解析出现错误,将以 + * {@link UnexpectedToken} 抛出所有语法错误信息。 + *
+ * + * @return AST 节点列表,每个节点对应一个顶层语法结构 + * @throws UnexpectedToken 如果解析期间发现语法错误 + */ public List+ * 同时会跳过连续空行。 + *
+ * + * @param ts 当前 TokenStream */ private void synchronize(TokenStream ts) { + // 跳到下一行或下一个顶层结构关键字 while (ts.isAtEnd()) { if (ts.peek().getType() == TokenType.NEWLINE) { ts.next(); @@ -56,7 +99,7 @@ public record ParserEngine(ParserContext ctx) { } 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 4f79274..c0a869e 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 @@ -4,6 +4,7 @@ import org.jcnc.snow.compiler.lexer.token.Token; import org.jcnc.snow.compiler.lexer.token.TokenType; import org.jcnc.snow.compiler.parser.ast.base.ExpressionNode; import org.jcnc.snow.compiler.parser.context.ParserContext; +import org.jcnc.snow.compiler.parser.context.UnsupportedFeature; import org.jcnc.snow.compiler.parser.expression.base.ExpressionParser; import org.jcnc.snow.compiler.parser.expression.base.InfixParselet; import org.jcnc.snow.compiler.parser.expression.base.PrefixParselet; @@ -87,7 +88,7 @@ public class PrattExpressionParser implements ExpressionParser { Token token = ctx.getTokens().next(); PrefixParselet prefix = prefixes.get(token.getType().name()); if (prefix == null) { - throw new IllegalStateException("没有为该 Token 类型注册前缀解析器: " + token.getType()); + throw new UnsupportedFeature("没有为该 Token 类型注册前缀解析器: " + token.getType()); } ExpressionNode left = prefix.parse(ctx, token); @@ -96,7 +97,10 @@ public class PrattExpressionParser implements ExpressionParser { && prec.ordinal() < nextPrecedence(ctx)) { String lex = ctx.getTokens().peek().getLexeme(); InfixParselet infix = infixes.get(lex); - if (infix == null) break; + if (infix == null) { + throw new UnsupportedFeature( + "没有为该 Token 类型注册中缀解析器: " + token.getType()); + } left = infix.parse(ctx, left); } return left; diff --git a/src/main/java/org/jcnc/snow/compiler/parser/module/ModuleParser.java b/src/main/java/org/jcnc/snow/compiler/parser/module/ModuleParser.java index ac9e05d..bcf2555 100644 --- a/src/main/java/org/jcnc/snow/compiler/parser/module/ModuleParser.java +++ b/src/main/java/org/jcnc/snow/compiler/parser/module/ModuleParser.java @@ -7,6 +7,7 @@ import org.jcnc.snow.compiler.parser.context.TokenStream; import org.jcnc.snow.compiler.parser.ast.ImportNode; import org.jcnc.snow.compiler.parser.ast.ModuleNode; import org.jcnc.snow.compiler.parser.ast.FunctionNode; +import org.jcnc.snow.compiler.parser.context.UnexpectedToken; import org.jcnc.snow.compiler.parser.function.FunctionParser; import java.util.ArrayList; @@ -33,7 +34,7 @@ public class ModuleParser implements TopLevelParser { * * @param ctx 当前解析器上下文,包含词法流、状态信息等。 * @return 返回一个 {@link ModuleNode} 实例,表示完整模块的语法结构。 - * @throws IllegalStateException 当模块体中出现未识别的语句时抛出。 + * @throws UnexpectedToken 当模块体中出现未识别的语句时抛出。 */ @Override public ModuleNode parse(ParserContext ctx) { @@ -86,7 +87,7 @@ public class ModuleParser implements TopLevelParser { functions.add(funcParser.parse(ctx)); } else { // 遇到无法识别的语句开头,抛出异常并提供详细提示 - throw new IllegalStateException("Unexpected token in module: " + lex); + throw new UnexpectedToken("Unexpected token in module: " + lex); } } diff --git a/src/main/java/org/jcnc/snow/compiler/parser/statement/ExpressionStatementParser.java b/src/main/java/org/jcnc/snow/compiler/parser/statement/ExpressionStatementParser.java index 526dbf0..3c14a90 100644 --- a/src/main/java/org/jcnc/snow/compiler/parser/statement/ExpressionStatementParser.java +++ b/src/main/java/org/jcnc/snow/compiler/parser/statement/ExpressionStatementParser.java @@ -7,6 +7,7 @@ import org.jcnc.snow.compiler.parser.ast.ExpressionStatementNode; import org.jcnc.snow.compiler.parser.ast.base.StatementNode; import org.jcnc.snow.compiler.parser.context.ParserContext; import org.jcnc.snow.compiler.parser.context.TokenStream; +import org.jcnc.snow.compiler.parser.context.UnexpectedToken; import org.jcnc.snow.compiler.parser.expression.PrattExpressionParser; /** @@ -39,7 +40,7 @@ public class ExpressionStatementParser implements StatementParser { * * @param ctx 当前解析上下文,提供词法流与状态信息。 * @return 返回 {@link AssignmentNode} 或 {@link ExpressionStatementNode} 表示的语法节点。 - * @throws IllegalStateException 若表达式起始为关键字或语法非法。 + * @throws UnexpectedToken 若表达式起始为关键字或语法非法。 */ @Override public StatementNode parse(ParserContext ctx) { @@ -47,7 +48,7 @@ public class ExpressionStatementParser implements StatementParser { // 快速检查:若遇空行或关键字开头,不可作为表达式语句 if (ts.peek().getType() == TokenType.NEWLINE || ts.peek().getType() == TokenType.KEYWORD) { - throw new IllegalStateException("Cannot parse expression starting with keyword: " + ts.peek().getLexeme()); + throw new UnexpectedToken("无法解析以关键字开头的表达式: " + ts.peek().getLexeme()); } // 获取当前 token 的行号、列号和文件名 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 index f669c8e..3403dcf 100644 --- a/src/main/java/org/jcnc/snow/compiler/parser/top/ScriptTopLevelParser.java +++ b/src/main/java/org/jcnc/snow/compiler/parser/top/ScriptTopLevelParser.java @@ -19,7 +19,6 @@ public class ScriptTopLevelParser implements TopLevelParser { 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 + return sp.parse(ctx); } } diff --git a/src/main/java/org/jcnc/snow/compiler/parser/utils/FlexibleSectionParser.java b/src/main/java/org/jcnc/snow/compiler/parser/utils/FlexibleSectionParser.java index c319d4f..90cbc2c 100644 --- a/src/main/java/org/jcnc/snow/compiler/parser/utils/FlexibleSectionParser.java +++ b/src/main/java/org/jcnc/snow/compiler/parser/utils/FlexibleSectionParser.java @@ -3,6 +3,7 @@ package org.jcnc.snow.compiler.parser.utils; import org.jcnc.snow.compiler.lexer.token.TokenType; import org.jcnc.snow.compiler.parser.context.ParserContext; import org.jcnc.snow.compiler.parser.context.TokenStream; +import org.jcnc.snow.compiler.parser.context.UnexpectedToken; import java.util.Map; import java.util.function.BiConsumer; @@ -45,7 +46,7 @@ public class FlexibleSectionParser { * @param ctx 当前解析上下文,提供语法环境与作用域信息 * @param tokens 当前 token 流 * @param sectionDefinitions 各个区块的定义映射(key 为关键字,value 为判断 + 解析逻辑组合) - * @throws RuntimeException 若出现无法识别的关键字或未满足的匹配条件 + * @throws UnexpectedToken 若出现无法识别的关键字或未满足的匹配条件 */ public static void parse(ParserContext ctx, TokenStream tokens, @@ -70,7 +71,7 @@ public class FlexibleSectionParser { if (definition != null && definition.condition().test(tokens)) { definition.parser().accept(ctx, tokens); // 执行解析逻辑 } else { - throw new RuntimeException("未识别的关键字或条件不满足: " + keyword); + throw new UnexpectedToken("未识别的关键字或条件不满足: " + keyword); } } } diff --git a/src/main/java/org/jcnc/snow/compiler/parser/utils/JSONParser.java b/src/main/java/org/jcnc/snow/compiler/parser/utils/JSONParser.java index ce30193..6d77478 100644 --- a/src/main/java/org/jcnc/snow/compiler/parser/utils/JSONParser.java +++ b/src/main/java/org/jcnc/snow/compiler/parser/utils/JSONParser.java @@ -1,5 +1,7 @@ package org.jcnc.snow.compiler.parser.utils; +import org.jcnc.snow.compiler.parser.context.UnexpectedToken; + import java.util.*; import java.util.Map.Entry; @@ -10,26 +12,28 @@ import java.util.Map.Entry; * - 序列化:将 Java 原生对象转换为符合 JSON 标准的字符串 *
* 设计要点:
- * 1. 使用静态方法作为唯一入口,避免状态共享导致的线程安全问题
- * 2. 解析器内部使用 char[] 缓冲区,提高访问性能
- * 3. 维护行列号信息,抛出异常时能精确定位错误位置
- * 4. 序列化器基于 StringBuilder,预分配容量,减少中间字符串创建
+ * 1. 使用静态方法作为唯一入口,避免状态共享导致的线程安全问题
+ * 2. 解析器内部使用 char[] 缓冲区,提高访问性能
+ * 3. 维护行列号信息,抛出异常时能精确定位错误位置
+ * 4. 序列化器基于 StringBuilder,预分配容量,减少中间字符串创建
*/
public class JSONParser {
- private JSONParser() {}
+ private JSONParser() {
+ }
/**
* 将 JSON 文本解析为对应的 Java 对象
+ *
* @param input JSON 格式字符串
* @return 对应的 Java 原生对象:
- * - JSON 对象 -> Map