From 55526d530aed39004530b484e2d934ca392a4813 Mon Sep 17 00:00:00 2001 From: Luke Date: Sun, 11 May 2025 23:59:29 +0800 Subject: [PATCH] =?UTF-8?q?=E5=A2=9E=E5=8A=A0=E6=B3=A8=E9=87=8A?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../parser/function/FunctionParser.java | 129 ++++++++++-------- .../compiler/parser/module/ImportParser.java | 43 ++++-- .../compiler/parser/module/ModuleParser.java | 57 ++++---- .../statement/DeclarationStatementParser.java | 45 ++++-- .../statement/ExpressionStatementParser.java | 55 +++++--- .../parser/statement/IfStatementParser.java | 71 ++++++---- .../parser/statement/LoopStatementParser.java | 52 +++++-- .../statement/ReturnStatementParser.java | 31 +++-- .../parser/statement/StatementParser.java | 17 ++- .../parser/utils/ASTJsonSerializer.java | 77 +++++++---- .../parser/utils/FlexibleSectionParser.java | 77 +++++++---- .../compiler/parser/utils/JSONParser.java | 2 +- .../compiler/parser/utils/ParserUtils.java | 53 ++++--- 13 files changed, 462 insertions(+), 247 deletions(-) diff --git a/src/main/java/org/jcnc/snow/compiler/parser/function/FunctionParser.java b/src/main/java/org/jcnc/snow/compiler/parser/function/FunctionParser.java index e6d37f1..372387c 100644 --- a/src/main/java/org/jcnc/snow/compiler/parser/function/FunctionParser.java +++ b/src/main/java/org/jcnc/snow/compiler/parser/function/FunctionParser.java @@ -18,53 +18,71 @@ import java.util.List; import java.util.Map; /** - * 顶层的函数定义解析器。 + * {@code FunctionParser} 是顶层函数定义的语法解析器, + * 实现 {@link TopLevelParser} 接口,用于将源代码中的函数块解析为抽象语法树(AST)中的 {@link FunctionNode}。 + * *

- * 通过 FlexibleSectionParser 按区块顺序解析函数的各个部分: - * 函数头(名称)、参数列表、返回类型、函数体,并最终生成 FunctionNode。 - * 支持在参数或返回类型声明中出现注释,自动跳过注释和空行而不干扰语法解析。 + * 本类使用 {@link FlexibleSectionParser} 机制,按照语义区块结构对函数进行模块化解析,支持以下部分: + *

+ * + * + * + *

+ * 各区块允许包含注释(类型 {@code COMMENT})与空行(类型 {@code NEWLINE}),解析器将自动跳过无效 token 保持语法连续性。 + *

+ * + *

+ * 最终将函数结构封装为 {@link FunctionNode} 并返回,供后续编译阶段使用。 + *

*/ public class FunctionParser implements TopLevelParser { /** - * 顶层解析入口。解析完整个函数定义,并返回 FunctionNode。 + * 顶层语法解析入口。 * - * @param ctx 解析上下文,包含 TokenStream 和作用域信息 - * @return 构建好的 FunctionNode + *

+ * 该方法负责完整解析函数定义,包括其所有组成部分,并构建对应的 {@link FunctionNode}。 + *

+ * + * @param ctx 当前解析上下文,包含 {@link TokenStream} 和符号表等作用域信息。 + * @return 构建完成的 {@link FunctionNode} 抽象语法树节点。 */ @Override public FunctionNode parse(ParserContext ctx) { TokenStream ts = ctx.getTokens(); - // 1. 解析函数头(function:) parseFunctionHeader(ts); - // 2. 读取并消费函数名称 String functionName = parseFunctionName(ts); - // 容器用于收集解析结果 List parameters = new ArrayList<>(); - String[] returnType = new String[1]; // 用一元素数组模拟可变引用 + String[] returnType = new String[1]; List body = new ArrayList<>(); - // 3. 构建区块解析逻辑,并使用 FlexibleSectionParser 解析 Map sections = getSectionDefinitions(parameters, returnType, body); FlexibleSectionParser.parse(ctx, ts, sections); - // 4. 解析函数尾部(end function) parseFunctionFooter(ts); - // 5. 构造并返回 AST return new FunctionNode(functionName, parameters, returnType[0], body); } /** - * 构造各区块的解析定义: - * 参数(parameter)、返回类型(return_type)、函数体(body)。 + * 构造函数定义中各区块的解析规则(parameter、return_type、body)。 * - * @param params 参数节点列表容器 - * @param returnType 返回类型容器 - * @param body 函数体语句列表容器 - * @return 区块关键字到解析逻辑的映射 + *

+ * 每个 {@link SectionDefinition} 包含两个部分:区块起始判断器(基于关键字)与具体的解析逻辑。 + *

+ * + * @param params 参数节点收集容器,解析结果将存入此列表。 + * @param returnType 返回类型容器,以单元素数组方式模拟引用传递。 + * @param body 函数体语句节点列表容器。 + * @return 区块关键字到解析定义的映射表。 */ private Map getSectionDefinitions( List params, @@ -72,19 +90,16 @@ public class FunctionParser implements TopLevelParser { List body) { Map map = new HashMap<>(); - // 参数区块 map.put("parameter", new SectionDefinition( (TokenStream stream) -> stream.peek().getLexeme().equals("parameter"), (ParserContext context, TokenStream stream) -> params.addAll(parseParameters(stream)) )); - // 返回类型区块 map.put("return_type", new SectionDefinition( (TokenStream stream) -> stream.peek().getLexeme().equals("return_type"), (ParserContext context, TokenStream stream) -> returnType[0] = parseReturnType(stream) )); - // 函数体区块 map.put("body", new SectionDefinition( (TokenStream stream) -> stream.peek().getLexeme().equals("body"), (ParserContext context, TokenStream stream) -> body.addAll(parseFunctionBody(context, stream)) @@ -94,9 +109,9 @@ public class FunctionParser implements TopLevelParser { } /** - * 解析函数头部:匹配 "function:",并跳过后续的注释和空行 + * 解析函数头部标识符 {@code function:},并跳过其后多余注释与空行。 * - * @param ts TokenStream + * @param ts 当前使用的 {@link TokenStream}。 */ private void parseFunctionHeader(TokenStream ts) { ts.expect("function"); @@ -106,10 +121,10 @@ public class FunctionParser implements TopLevelParser { } /** - * 解析函数名称:IDENTIFIER + 换行 + * 解析函数名称(标识符)并跳过换行。 * - * @param ts TokenStream - * @return 函数名称 + * @param ts 当前使用的 {@link TokenStream}。 + * @return 函数名字符串。 */ private String parseFunctionName(TokenStream ts) { String name = ts.expectType(TokenType.IDENTIFIER).getLexeme(); @@ -118,9 +133,9 @@ public class FunctionParser implements TopLevelParser { } /** - * 解析函数尾部:匹配 "end function" + 换行 + * 解析函数结束标记 {@code end function}。 * - * @param ts TokenStream + * @param ts 当前使用的 {@link TokenStream}。 */ private void parseFunctionFooter(TokenStream ts) { ts.expect("end"); @@ -129,16 +144,19 @@ public class FunctionParser implements TopLevelParser { } /** - * 解析参数区块参数列表,支持在声明行尾添加注释 - * 格式: + * 解析函数参数列表。 + * + *

+ * 支持声明后附加注释,格式示例: *

      * parameter:
-     *   declare x: int   // 注释
-     *   ...
+     *   declare x: int   // 说明文字
+     *   declare y: float
      * 
+ *

* - * @param ts TokenStream - * @return ParameterNode 列表 + * @param ts 当前使用的 {@link TokenStream}。 + * @return 所有参数节点的列表。 */ private List parseParameters(TokenStream ts) { ts.expect("parameter"); @@ -162,7 +180,6 @@ public class FunctionParser implements TopLevelParser { String pname = ts.expectType(TokenType.IDENTIFIER).getLexeme(); ts.expect(":"); String ptype = ts.expectType(TokenType.TYPE).getLexeme(); - // 跳过行内注释 skipComments(ts); ts.expectType(TokenType.NEWLINE); list.add(new ParameterNode(pname, ptype)); @@ -171,35 +188,37 @@ public class FunctionParser implements TopLevelParser { } /** - * 解析返回类型区块,支持在类型声明前后添加注释 - * 格式:
return_type: TYPE
+ * 解析返回类型声明。 * - * @param ts TokenStream - * @return 返回类型字符串 + *

+ * 格式为 {@code return_type: TYPE},支持前置或行尾注释。 + *

+ * + * @param ts 当前使用的 {@link TokenStream}。 + * @return 返回类型名称字符串。 */ private String parseReturnType(TokenStream ts) { ts.expect("return_type"); ts.expect(":"); - // 跳过块前注释 skipComments(ts); - // 捕获类型 token Token typeToken = ts.expectType(TokenType.TYPE); String rtype = typeToken.getLexeme(); - // 跳过行内注释 skipComments(ts); - // 匹配换行 ts.expectType(TokenType.NEWLINE); - // 跳过多余空行 skipNewlines(ts); return rtype; } /** - * 解析函数体区块,直到遇到 "end body" + * 解析函数体区块,直到遇到 {@code end body}。 * - * @param ctx ParserContext - * @param ts TokenStream - * @return StatementNode 列表 + *

+ * 每一行由对应的语句解析器处理,可嵌套控制结构、返回语句、表达式等。 + *

+ * + * @param ctx 当前解析上下文。 + * @param ts 当前使用的 {@link TokenStream}。 + * @return 所有函数体语句节点的列表。 */ private List parseFunctionBody(ParserContext ctx, TokenStream ts) { ts.expect("body"); @@ -228,9 +247,9 @@ public class FunctionParser implements TopLevelParser { } /** - * 跳过所有连续的注释行(COMMENT) + * 跳过连续的注释 token(类型 {@code COMMENT})。 * - * @param ts TokenStream + * @param ts 当前使用的 {@link TokenStream}。 */ private void skipComments(TokenStream ts) { while (ts.peek().getType() == TokenType.COMMENT) { @@ -239,13 +258,13 @@ public class FunctionParser implements TopLevelParser { } /** - * 跳过所有连续的空行(NEWLINE) + * 跳过连续的空行 token(类型 {@code NEWLINE})。 * - * @param ts TokenStream + * @param ts 当前使用的 {@link TokenStream}。 */ private void skipNewlines(TokenStream ts) { while (ts.peek().getType() == TokenType.NEWLINE) { ts.next(); } } -} +} \ No newline at end of file diff --git a/src/main/java/org/jcnc/snow/compiler/parser/module/ImportParser.java b/src/main/java/org/jcnc/snow/compiler/parser/module/ImportParser.java index 2b6ead4..38dd788 100644 --- a/src/main/java/org/jcnc/snow/compiler/parser/module/ImportParser.java +++ b/src/main/java/org/jcnc/snow/compiler/parser/module/ImportParser.java @@ -8,36 +8,57 @@ import java.util.ArrayList; import java.util.List; /** - * 负责解析 import 语句的解析器。 - * 支持在一行中导入多个模块,格式如: - * {@code import: mod1, mod2, mod3} + * {@code ImportParser} 类用于解析源码中的 import 导入语句。 + *

+ * 支持以下格式的语法: + *

+ * import: module1, module2, module3
+ * 
+ * 每个模块名称必须为合法的标识符,多个模块之间使用英文逗号(,)分隔,语句末尾必须以换行符结尾。 + * 本类的职责是识别并提取所有导入模块的名称,并将其封装为 {@link ImportNode} 节点,供后续语法树构建或语义分析阶段使用。 */ public class ImportParser { /** - * 解析一条 import 声明语句,返回对应的 {@link ImportNode} 列表。 - * 格式必须为:{@code import: MODULE[, MODULE]* NEWLINE} + * 解析 import 语句,并返回表示被导入模块的语法树节点列表。 + *

+ * 该方法会依次执行以下操作: + *

    + *
  1. 确认当前语句以关键字 {@code import} 开头。
  2. + *
  3. 确认后跟一个冒号 {@code :}。
  4. + *
  5. 解析至少一个模块名称(标识符),多个模块使用逗号分隔。
  6. + *
  7. 确认语句以换行符 {@code NEWLINE} 结束。
  8. + *
+ * 若语法不符合上述规则,将在解析过程中抛出异常。 * - * @param ctx 当前的解析上下文。 - * @return 所有被导入模块的节点列表。 + * @param ctx 表示当前解析器所处的上下文环境,包含词法流及语法状态信息。 + * @return 返回一个包含所有被导入模块的 {@link ImportNode} 实例列表。 */ public List parse(ParserContext ctx) { + // 期望第一个 token 是 "import" 关键字 ctx.getTokens().expect("import"); + + // 紧接其后必须是冒号 ":" ctx.getTokens().expect(":"); + // 用于存储解析得到的 ImportNode 对象 List imports = new ArrayList<>(); - // 读取第一个模块名,然后继续读取逗号分隔的模块名 + // 解析一个或多个模块名(标识符),允许使用逗号分隔多个模块 do { + // 获取当前标识符类型的词法单元,并提取其原始词素 String mod = ctx.getTokens() .expectType(TokenType.IDENTIFIER) .getLexeme(); - imports.add(new ImportNode(mod)); - } while (ctx.getTokens().match(",")); - // 消费行尾换行符 + // 创建 ImportNode 节点并加入列表 + imports.add(new ImportNode(mod)); + } while (ctx.getTokens().match(",")); // 如果匹配到逗号,继续解析下一个模块名 + + // 最后必须匹配换行符,标志 import 语句的结束 ctx.getTokens().expectType(TokenType.NEWLINE); + // 返回完整的 ImportNode 列表 return imports; } } 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 7a001ba..02ada73 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 @@ -13,79 +13,84 @@ import java.util.ArrayList; import java.util.List; /** - * 模块解析器,用于解析形如 module 块的顶层结构。 - * 语法示例: - *
{@code
- * module: my.module
- * import: mod1, mod2
- * function: ...
- * end module
- * }
- * 支持导入语句和多个函数定义,允许中间包含空行。 + * {@code ModuleParser} 类负责解析源码中的模块定义结构,属于顶层结构解析器的一种。 + *

+ * 模块中可包含多个导入语句和函数定义,导入语句可在模块中任意位置出现, + * 同时支持空行,空行将被自动忽略,不影响语法结构的正确性。 */ public class ModuleParser implements TopLevelParser { /** - * 解析模块定义块,返回 {@link ModuleNode} 表示模块结构。 + * 解析一个模块定义块,返回构建好的 {@link ModuleNode} 对象。 + *

+ * 本方法的语法流程包括: + *

    + *
  1. 匹配模块声明开头 {@code module: IDENTIFIER}。
  2. + *
  3. 收集模块体中的 import 语句与 function 定义,允许穿插空行。
  4. + *
  5. 模块结尾必须为 {@code end module},且后接换行符。
  6. + *
+ * 所有语法错误将在解析过程中抛出异常,以便准确反馈问题位置和原因。 * - * @param ctx 解析上下文。 - * @return 模块节点,包含模块名、导入列表和函数列表。 + * @param ctx 当前解析器上下文,包含词法流、状态信息等。 + * @return 返回一个 {@link ModuleNode} 实例,表示完整模块的语法结构。 + * @throws IllegalStateException 当模块体中出现未识别的语句时抛出。 */ @Override public ModuleNode parse(ParserContext ctx) { + // 获取当前上下文中提供的词法流 TokenStream ts = ctx.getTokens(); - // 期望以 "module" 开头 + // 期望模块声明以关键字 "module:" 开始 ts.expect("module"); ts.expect(":"); - // 读取模块名称(标识符) + // 读取模块名称(要求为标识符类型的词法单元) String name = ts.expectType(TokenType.IDENTIFIER).getLexeme(); - // 模块声明后的换行 + // 模块声明必须以换行符结束 ts.expectType(TokenType.NEWLINE); - // 初始化导入列表与函数列表 + // 初始化模块的导入节点列表与函数节点列表 List imports = new ArrayList<>(); List functions = new ArrayList<>(); - // 创建导入语句和函数解析器 + // 创建 import 与 function 的子解析器 ImportParser importParser = new ImportParser(); FunctionParser funcParser = new FunctionParser(); - // 解析模块主体内容(支持多个 import 和 function 语句) + // 进入模块主体内容解析循环 while (true) { - // 跳过空行(多个空行不会导致错误) + // 跳过所有空行(即连续的 NEWLINE) if (ts.peek().getType() == TokenType.NEWLINE) { ts.next(); continue; } - // 模块体结束判断:遇到 "end" 关键字 + // 若遇到 "end",则表明模块定义结束 if ("end".equals(ts.peek().getLexeme())) { break; } - // 判断当前行的语法元素,调用相应解析器 + // 根据当前行首关键字决定解析器的选择 String lex = ts.peek().getLexeme(); if ("import".equals(lex)) { - // 添加解析后的导入语句节点 + // 调用导入语句解析器,解析多个模块导入节点 imports.addAll(importParser.parse(ctx)); } else if ("function".equals(lex)) { - // 添加解析后的函数节点 + // 调用函数定义解析器,解析单个函数结构 functions.add(funcParser.parse(ctx)); } else { - // 非法语法,抛出异常提示位置与原因 + // 遇到无法识别的语句开头,抛出异常并提供详细提示 throw new IllegalStateException("Unexpected token in module: " + lex); } } - // 解析模块结尾结构,确保以 "end module" 结尾 + // 确保模块体以 "end module" 结束 ts.expect("end"); ts.expect("module"); ts.expectType(TokenType.NEWLINE); - // 返回构建完成的模块语法树节点 + // 构建并返回完整的模块语法树节点 return new ModuleNode(name, imports, functions); } } diff --git a/src/main/java/org/jcnc/snow/compiler/parser/statement/DeclarationStatementParser.java b/src/main/java/org/jcnc/snow/compiler/parser/statement/DeclarationStatementParser.java index 7ec9fe6..b99d09c 100644 --- a/src/main/java/org/jcnc/snow/compiler/parser/statement/DeclarationStatementParser.java +++ b/src/main/java/org/jcnc/snow/compiler/parser/statement/DeclarationStatementParser.java @@ -7,47 +7,68 @@ import org.jcnc.snow.compiler.parser.context.ParserContext; import org.jcnc.snow.compiler.parser.expression.PrattExpressionParser; /** - * 解析变量声明语句的解析器。 - * 支持语法格式: + * {@code DeclarationStatementParser} 类负责解析变量声明语句,是语句级解析器的一部分。 + *

+ * 本解析器支持以下两种形式的声明语法: *

{@code
- * declare name:type
- * declare name:type = expression
+ * declare myVar:Integer
+ * declare myVar:Integer = 42 + 3
  * }
- * 每条语句必须以换行(NEWLINE)结束。 + * 其中: + *
    + *
  • {@code myVar} 为变量名(必须为标识符类型);
  • + *
  • {@code Integer} 为类型标注(必须为类型标记);
  • + *
  • 可选的初始化表达式由 {@link PrattExpressionParser} 解析;
  • + *
  • 每条声明语句必须以换行符({@code NEWLINE})结束。
  • + *
+ * 若语法不满足上述结构,将在解析过程中抛出异常。 */ public class DeclarationStatementParser implements StatementParser { /** - * 解析一条 declare 声明语句。 + * 解析一条 {@code declare} 声明语句,并返回对应的抽象语法树节点 {@link DeclarationNode}。 + *

+ * 解析流程如下: + *

    + *
  1. 匹配关键字 {@code declare};
  2. + *
  3. 读取变量名称(标识符类型);
  4. + *
  5. 读取类型标注(在冒号后,要求为 {@code TYPE} 类型);
  6. + *
  7. 若存在 {@code =},则继续解析其后的表达式作为初始化值;
  8. + *
  9. 最终必须匹配 {@code NEWLINE} 表示语句结束。
  10. + *
+ * 若遇到非法语法结构,将触发异常并中断解析过程。 * - * @param ctx 当前解析上下文。 - * @return 构造好的 {@link DeclarationNode} AST 节点。 + * @param ctx 当前语法解析上下文,包含词法流、错误信息等。 + * @return 返回一个 {@link DeclarationNode} 节点,表示解析完成的声明语法结构。 */ @Override public DeclarationNode parse(ParserContext ctx) { + // 声明语句必须以 "declare" 开头 ctx.getTokens().expect("declare"); - // 获取变量名 + // 获取变量名称(标识符) String name = ctx.getTokens() .expectType(TokenType.IDENTIFIER) .getLexeme(); + // 类型标注的冒号分隔符 ctx.getTokens().expect(":"); - // 获取变量类型 + // 获取变量类型(类型标识符) String type = ctx.getTokens() .expectType(TokenType.TYPE) .getLexeme(); - // 可选初始化表达式 + // 可选的初始化表达式,若存在 "=",则解析等号右侧表达式 ExpressionNode init = null; if (ctx.getTokens().match("=")) { init = new PrattExpressionParser().parse(ctx); } - // 声明语句必须以 NEWLINE 结束 + // 声明语句必须以换行符结尾 ctx.getTokens().expectType(TokenType.NEWLINE); + // 返回构建好的声明语法树节点 return new DeclarationNode(name, type, init); } } 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 37eb095..dd9df94 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 @@ -10,46 +10,61 @@ import org.jcnc.snow.compiler.parser.context.TokenStream; import org.jcnc.snow.compiler.parser.expression.PrattExpressionParser; /** - * 表达式语句解析器:将赋值表达式或任意表达式作为语句进行解析。 - * 支持的形式包括: - *
- *   identifier = expression
- *   expression
- * 
- * 两者都必须以换行(NEWLINE)结尾。 + * {@code ExpressionStatementParser} 负责解析通用表达式语句,包括赋值语句和单一表达式语句。 + *

+ * 支持的语法结构如下: + *

{@code
+ * x = 1 + 2        // 赋值语句
+ * doSomething()    // 函数调用等普通表达式语句
+ * }
+ *
    + *
  • 若以标识符开头,且后接等号 {@code =},则视为赋值语句,解析为 {@link AssignmentNode}。
  • + *
  • 否则视为普通表达式,解析为 {@link ExpressionStatementNode}。
  • + *
  • 所有表达式语句必须以换行符 {@code NEWLINE} 结束。
  • + *
+ * 不允许以关键字或空行作为表达式的起始,若遇到非法开头,将抛出解析异常。 */ public class ExpressionStatementParser implements StatementParser { /** - * 解析一个表达式语句,可能是赋值语句或普通表达式。 + * 解析一个表达式语句,根据上下文决定其为赋值或一般表达式。 + *

+ * 具体逻辑如下: + *

    + *
  1. 若当前行为标识符后接等号,则作为赋值处理。
  2. + *
  3. 否则解析整个表达式作为单独语句。
  4. + *
  5. 所有语句都必须以换行符结束。
  6. + *
  7. 若表达式以关键字或空行开头,将立即抛出异常,避免非法解析。
  8. + *
* - * @param ctx 当前的解析上下文。 - * @return 表达式语句节点或赋值语句节点。 + * @param ctx 当前解析上下文,提供词法流与状态信息。 + * @return 返回 {@link AssignmentNode} 或 {@link ExpressionStatementNode} 表示的语法节点。 + * @throws IllegalStateException 若表达式起始为关键字或语法非法。 */ @Override public StatementNode parse(ParserContext ctx) { TokenStream ts = ctx.getTokens(); - // 空行或非法起始符号,提前退出(安全防护) + // 快速检查:若遇空行或关键字开头,不可作为表达式语句 if (ts.peek().getType() == TokenType.NEWLINE || ts.peek().getType() == TokenType.KEYWORD) { throw new IllegalStateException("Cannot parse expression starting with keyword: " + ts.peek().getLexeme()); } - // 判断是否是赋值语句(形如:identifier = expr) + // 处理赋值语句:格式为 identifier = expression if (ts.peek().getType() == TokenType.IDENTIFIER && ts.peek(1).getLexeme().equals("=")) { - String varName = ts.next().getLexeme(); // consume identifier - ts.expect("="); // consume '=' - ExpressionNode value = new PrattExpressionParser().parse(ctx); - ts.expectType(TokenType.NEWLINE); - return new AssignmentNode(varName, value); + String varName = ts.next().getLexeme(); // 消耗标识符 + ts.expect("="); // 消耗等号 + ExpressionNode value = new PrattExpressionParser().parse(ctx); // 解析表达式 + ts.expectType(TokenType.NEWLINE); // 语句必须以换行符结束 + return new AssignmentNode(varName, value); // 返回赋值节点 } - // 普通表达式语句(如函数调用) + // 处理普通表达式语句,如函数调用、字面量、运算表达式等 ExpressionNode expr = new PrattExpressionParser().parse(ctx); - ts.expectType(TokenType.NEWLINE); - return new ExpressionStatementNode(expr); + ts.expectType(TokenType.NEWLINE); // 语句必须以换行符结束 + return new ExpressionStatementNode(expr); // 返回表达式语句节点 } } diff --git a/src/main/java/org/jcnc/snow/compiler/parser/statement/IfStatementParser.java b/src/main/java/org/jcnc/snow/compiler/parser/statement/IfStatementParser.java index 7c42555..f0e2498 100644 --- a/src/main/java/org/jcnc/snow/compiler/parser/statement/IfStatementParser.java +++ b/src/main/java/org/jcnc/snow/compiler/parser/statement/IfStatementParser.java @@ -12,75 +12,96 @@ import java.util.ArrayList; import java.util.List; /** - * 解析 if 语句: + * {@code IfStatementParser} 类负责解析 if 条件语句,是语句级解析器中的条件分支处理器。 *

- * 支持格式: + * 本解析器支持以下结构的条件语法: + *

{@code
  * if  then
- *   
+ *     
  * [else
- *   ]
+ *     ]
  * end if
+ * }
+ * 其中: + *
    + *
  • {@code } 为任意可解析的布尔或数值表达式,使用 {@link PrattExpressionParser} 解析;
  • + *
  • {@code } 与 {@code } 可包含多条语句,自动跳过空行;
  • + *
  • {@code else} 分支为可选,若存在,必须紧跟换行与语句;
  • + *
  • {@code end if} 为终止标识,表示整个 if 语句块的结束。
  • + *
+ * 所有语句的实际解析由 {@link StatementParserFactory} 根据关键词动态分派处理。 */ public class IfStatementParser implements StatementParser { + /** + * 解析一条完整的 if 条件语句,返回语法树中对应的 {@link IfNode} 节点。 + *

+ * 本方法支持 then 分支和可选的 else 分支,并确保以 {@code end if} 正确结尾。 + * 在解析过程中自动跳过空行;遇到未知关键字或不符合预期的 token 时会抛出异常。 + * + * @param ctx 当前的语法解析上下文,包含 token 流和语义环境。 + * @return 构造完成的 {@link IfNode},包含条件表达式、then 分支和 else 分支语句列表。 + * @throws IllegalStateException 若语法结构不完整或存在非法 token。 + */ @Override public IfNode parse(ParserContext ctx) { - var ts = ctx.getTokens(); // 获取 Token 流管理器 + var ts = ctx.getTokens(); // 获取 token 流引用 - ts.expect("if"); // 消费 "if" 关键字 + // 消耗起始关键字 "if" + ts.expect("if"); - // 使用 Pratt 解析器解析布尔表达式或其他条件表达式 + // 使用 Pratt 算法解析 if 条件表达式 var condition = new PrattExpressionParser().parse(ctx); - // 消费 "then" 关键字和随后的换行符 + // 条件表达式后必须紧跟 "then" 和换行 ts.expect("then"); ts.expectType(TokenType.NEWLINE); - // 准备容器存储 then 和 else 分支的语句 + // 初始化 then 和 else 分支语句列表 List thenBranch = new ArrayList<>(); List elseBranch = new ArrayList<>(); - // ------------------- - // 解析 THEN 分支语句 - // ------------------- + // ------------------------- + // 解析 THEN 分支语句块 + // ------------------------- while (true) { Token peek = ts.peek(); - // 忽略空行 + // 跳过空行 if (peek.getType() == TokenType.NEWLINE) { ts.next(); continue; } - // 检测是否进入 else 或 end,跳出 then 区块 + // 遇到 else 或 end 表示 then 分支结束 if (peek.getType() == TokenType.KEYWORD && (peek.getLexeme().equals("else") || peek.getLexeme().equals("end"))) { break; } - // 提取关键词,交由 StatementParserFactory 派发对应解析器 + // 获取当前语句的关键字,调用工厂获取对应解析器 String keyword = peek.getType() == TokenType.KEYWORD ? peek.getLexeme() : ""; StatementNode stmt = StatementParserFactory.get(keyword).parse(ctx); thenBranch.add(stmt); } - // ------------------- - // 解析 ELSE 分支语句 - // ------------------- + // ------------------------- + // 解析 ELSE 分支语句块(可选) + // ------------------------- if (ts.peek().getLexeme().equals("else")) { - ts.next(); // 消费 "else" - ts.expectType(TokenType.NEWLINE); // 消费换行 + ts.next(); // 消耗 "else" + ts.expectType(TokenType.NEWLINE); // 消耗换行符 while (true) { Token peek = ts.peek(); - // 忽略空行 + // 跳过空行 if (peek.getType() == TokenType.NEWLINE) { ts.next(); continue; } - // "end" 关键字表示 else 块结束 + // "end" 表示 else 分支结束 if (peek.getType() == TokenType.KEYWORD && peek.getLexeme().equals("end")) { break; } @@ -91,12 +112,14 @@ public class IfStatementParser implements StatementParser { } } - // 统一消费 "end if" 和其后的换行 + // ------------------------- + // 统一结束处理:end if + // ------------------------- ts.expect("end"); ts.expect("if"); ts.expectType(TokenType.NEWLINE); - // 构造 AST 节点,返回 IfNode 包含条件、then 分支、else 分支 + // 构建并返回 IfNode,包含条件、then 分支和 else 分支 return new IfNode(condition, thenBranch, elseBranch); } } diff --git a/src/main/java/org/jcnc/snow/compiler/parser/statement/LoopStatementParser.java b/src/main/java/org/jcnc/snow/compiler/parser/statement/LoopStatementParser.java index b54c26f..583b5c9 100644 --- a/src/main/java/org/jcnc/snow/compiler/parser/statement/LoopStatementParser.java +++ b/src/main/java/org/jcnc/snow/compiler/parser/statement/LoopStatementParser.java @@ -18,7 +18,9 @@ import java.util.List; import java.util.Map; /** - * 用于解析 loop 语句块,支持如下结构: + * {@code LoopStatementParser} 类负责解析自定义结构化的 {@code loop} 语句块。 + *

+ * 该语法结构参考了传统的 for-loop,并将其拆解为命名的语义区块: *

{@code
  * loop:
  *   initializer:
@@ -28,31 +30,53 @@ import java.util.Map;
  *   update:
  *     i = i + 1
  *   body:
- *     ...语句...
+ *     print(i)
  * end body
  * end loop
  * }
- * 使用 FlexibleSectionParser 解析各区块,并统一入口出口处理。 + * + * 各区块说明: + *
    + *
  • {@code initializer}:初始化语句,通常为变量声明。
  • + *
  • {@code condition}:循环判断条件,必须为布尔或数值表达式。
  • + *
  • {@code update}:每轮执行后更新逻辑,通常为赋值语句。
  • + *
  • {@code body}:主执行语句块,支持任意多条语句。
  • + *
+ * 本类依赖 {@link FlexibleSectionParser} 实现各区块的统一处理,确保结构明确、可扩展。 */ public class LoopStatementParser implements StatementParser { + /** + * 解析 {@code loop} 语句块,构建出对应的 {@link LoopNode} 抽象语法树节点。 + *

+ * 本方法会按顺序检查各个命名区块(可乱序书写),并分别绑定其对应语义解析器: + *

    + *
  • 通过 {@link ParserUtils#matchHeader} 匹配区块开头;
  • + *
  • 通过 {@link FlexibleSectionParser} 派发区块逻辑;
  • + *
  • 通过 {@link StatementParserFactory} 调用实际语句解析;
  • + *
  • 最后以 {@code end loop} 表示结构终止。
  • + *
+ * + * @param ctx 当前解析上下文。 + * @return {@link LoopNode},包含初始化、条件、更新与循环体等信息。 + */ @Override public LoopNode parse(ParserContext ctx) { TokenStream ts = ctx.getTokens(); - // 匹配 loop: 开头 + // 匹配 loop: 起始语法 ParserUtils.matchHeader(ts, "loop"); - // 各区块中间值容器(模拟引用) + // 使用数组模拟引用以便在 lambda 中写入(Java 不支持闭包内修改局部变量) final StatementNode[] initializer = new StatementNode[1]; final ExpressionNode[] condition = new ExpressionNode[1]; final AssignmentNode[] update = new AssignmentNode[1]; final List body = new ArrayList<>(); - // 构造区块定义 + // 定义各命名区块的识别与处理逻辑 Map sections = new HashMap<>(); - // initializer 区块:解析初始化语句 + // initializer 区块:仅支持一条语句,通常为 declare sections.put("initializer", new FlexibleSectionParser.SectionDefinition( ts1 -> ts1.peek().getLexeme().equals("initializer"), (ctx1, ts1) -> { @@ -62,7 +86,7 @@ public class LoopStatementParser implements StatementParser { } )); - // condition 区块:解析布尔条件表达式 + // condition 区块:支持任意可解析为布尔的表达式 sections.put("condition", new FlexibleSectionParser.SectionDefinition( ts1 -> ts1.peek().getLexeme().equals("condition"), (ctx1, ts1) -> { @@ -73,7 +97,7 @@ public class LoopStatementParser implements StatementParser { } )); - // update 区块:解析变量赋值表达式 + // update 区块:目前仅支持单一变量赋值语句 sections.put("update", new FlexibleSectionParser.SectionDefinition( ts1 -> ts1.peek().getLexeme().equals("update"), (ctx1, ts1) -> { @@ -87,14 +111,14 @@ public class LoopStatementParser implements StatementParser { } )); - // body 区块:解析语句块 + // body 区块:支持多条语句,直到遇到 end body sections.put("body", new FlexibleSectionParser.SectionDefinition( ts1 -> ts1.peek().getLexeme().equals("body"), (ctx1, ts1) -> { ParserUtils.matchHeader(ts1, "body"); while (!(ts1.peek().getLexeme().equals("end") && - ts1.peek(1).getLexeme().equals("body"))) { + ts1.peek(1).getLexeme().equals("body"))) { String keyword = ts1.peek().getType() == TokenType.KEYWORD ? ts1.peek().getLexeme() : ""; @@ -109,13 +133,13 @@ public class LoopStatementParser implements StatementParser { } )); - // 使用 FlexibleSectionParser 解析所有结构部分 + // 使用通用区块解析器处理各命名结构块 FlexibleSectionParser.parse(ctx, ts, sections); - // 匹配 end loop + // 解析结尾的 end loop 标记 ParserUtils.matchFooter(ts, "loop"); - // 构造并返回 LoopNode 抽象语法树节点 + // 返回构造完成的 LoopNode return new LoopNode(initializer[0], condition[0], update[0], body); } } diff --git a/src/main/java/org/jcnc/snow/compiler/parser/statement/ReturnStatementParser.java b/src/main/java/org/jcnc/snow/compiler/parser/statement/ReturnStatementParser.java index b90ebbb..8a6a6a2 100644 --- a/src/main/java/org/jcnc/snow/compiler/parser/statement/ReturnStatementParser.java +++ b/src/main/java/org/jcnc/snow/compiler/parser/statement/ReturnStatementParser.java @@ -7,35 +7,48 @@ import org.jcnc.snow.compiler.parser.context.ParserContext; import org.jcnc.snow.compiler.parser.expression.PrattExpressionParser; /** - * 用于解析 return 语句。 - * 支持有无返回值两种形式: + * {@code ReturnStatementParser} 负责解析 return 语句,是语句级解析器的一部分。 + *

+ * 支持以下两种 return 语句形式: *

{@code
- * return
- * return expression
+ * return             // 无返回值
+ * return expression  // 带返回值
  * }
- * 语句必须以换行符(NEWLINE)结束。 + * 所有 return 语句都必须以换行符({@code NEWLINE})结束,返回值表达式(若存在)由 {@link PrattExpressionParser} 负责解析。 + * 若语法结构不满足要求,将在解析过程中抛出异常。 */ public class ReturnStatementParser implements StatementParser { /** - * 解析 return 语句并构建 {@link ReturnNode}。 + * 解析一条 return 语句,并返回对应的 {@link ReturnNode} 抽象语法树节点。 + *

+ * 解析逻辑如下: + *

    + *
  1. 匹配起始关键字 {@code return}。
  2. + *
  3. 判断其后是否为 {@code NEWLINE},若否则表示存在返回值表达式。
  4. + *
  5. 使用 {@link PrattExpressionParser} 解析返回值表达式(若存在)。
  6. + *
  7. 最后匹配换行符,标志语句结束。
  8. + *
* - * @param ctx 当前解析上下文。 - * @return 表示 return 语句的 AST 节点。 + * @param ctx 当前解析上下文,包含词法流与语法状态。 + * @return 构造完成的 {@link ReturnNode},表示 return 语句的语法树节点。 */ @Override public ReturnNode parse(ParserContext ctx) { + // 消耗 "return" 关键字 ctx.getTokens().expect("return"); ExpressionNode expr = null; - // 如果不是换行,说明有返回值 + // 如果下一 token 不是换行符,说明存在返回值表达式 if (ctx.getTokens().peek().getType() != TokenType.NEWLINE) { expr = new PrattExpressionParser().parse(ctx); } + // return 语句必须以换行符结束 ctx.getTokens().expectType(TokenType.NEWLINE); + // 构建并返回 ReturnNode(可能为空表达式) return new ReturnNode(expr); } } diff --git a/src/main/java/org/jcnc/snow/compiler/parser/statement/StatementParser.java b/src/main/java/org/jcnc/snow/compiler/parser/statement/StatementParser.java index bc60558..edcc075 100644 --- a/src/main/java/org/jcnc/snow/compiler/parser/statement/StatementParser.java +++ b/src/main/java/org/jcnc/snow/compiler/parser/statement/StatementParser.java @@ -4,16 +4,23 @@ import org.jcnc.snow.compiler.parser.context.ParserContext; import org.jcnc.snow.compiler.parser.ast.base.StatementNode; /** - * 语句解析器接口,用于将解析上下文中的 Token 转换为 {@link StatementNode}。 - * 各类语句(如声明、赋值、条件、循环、返回等)均应实现该接口。 + * {@code StatementParser} 是所有语句解析器的通用接口。 + *

+ * 其职责是从给定的 {@link ParserContext} 中读取并分析当前语句,构造并返回相应的抽象语法树节点。 + * 所有语句类型(如变量声明、赋值语句、控制结构、函数返回等)应提供对应的实现类。 + * + *

+ * 通常,此接口的实现由 {@code StatementParserFactory} 根据当前关键字动态派发,用于解析模块体、 + * 条件分支、循环体或其他语句块中的单条语句。 */ public interface StatementParser { /** - * 从给定的解析上下文中解析出一个语句节点。 + * 解析一条语句,将其从词法表示转换为结构化语法树节点。 * - * @param ctx 当前的解析上下文。 - * @return 表示语句的 AST 节点。 + * @param ctx 当前的解析上下文,提供 token 流、状态与符号环境等。 + * @return 表示该语句的 AST 节点,类型为 {@link StatementNode} 或其子类。 + * @throws IllegalStateException 若语法非法或结构不完整。 */ StatementNode parse(ParserContext ctx); } diff --git a/src/main/java/org/jcnc/snow/compiler/parser/utils/ASTJsonSerializer.java b/src/main/java/org/jcnc/snow/compiler/parser/utils/ASTJsonSerializer.java index 470c53e..df36075 100644 --- a/src/main/java/org/jcnc/snow/compiler/parser/utils/ASTJsonSerializer.java +++ b/src/main/java/org/jcnc/snow/compiler/parser/utils/ASTJsonSerializer.java @@ -7,19 +7,32 @@ import org.jcnc.snow.compiler.parser.ast.base.Node; import java.util.*; /** - * ASTJsonSerializer 工具类 + * {@code ASTJsonSerializer} 是抽象语法树(AST)序列化工具类。 *

- * 将编译器生成的 AST(抽象语法树)节点列表转换为通用的 Map/List 结构 - * 并借助 JSONParser.toJson(Object) 方法序列化为 JSON 字符串。 + * 该工具可将编译器内部构建的 AST 节点对象转换为通用的 {@code Map} 和 {@code List} 结构, + * 并可借助 {@code JSONParser.toJson(Object)} 方法将其序列化为 JSON 字符串,用于调试、 + * 可视化或跨语言数据传输。 *

- * 支持的节点类型包括:ModuleNode、FunctionNode、DeclarationNode、 - * AssignmentNode、IfNode、LoopNode、ReturnNode、ExpressionStatementNode - * 以及各种 ExpressionNode(如 BinaryExpressionNode、IdentifierNode 等)。 + * 支持的节点类型包括: + *

    + *
  • {@link ModuleNode}
  • + *
  • {@link FunctionNode}
  • + *
  • {@link DeclarationNode}
  • + *
  • {@link AssignmentNode}
  • + *
  • {@link IfNode}
  • + *
  • {@link LoopNode}
  • + *
  • {@link ReturnNode}
  • + *
  • {@link ExpressionStatementNode}
  • + *
  • 各类 {@link ExpressionNode} 子类型,如 {@code BinaryExpressionNode}, {@code IdentifierNode} 等
  • + *
*/ public class ASTJsonSerializer { /** - * 快速创建一个 LinkedHashMap,并写入 type 字段 + * 创建包含 {@code type} 字段的节点 Map,用于标识节点类型。 + * + * @param type 节点类型字符串。 + * @return 一个初始化后的 Map 实例。 */ private static Map newNodeMap(String type) { Map m = new LinkedHashMap<>(); @@ -28,7 +41,11 @@ public class ASTJsonSerializer { } /** - * 用于构建表达式节点的 Map + * 构建表达式节点的 Map 表示,支持动态键值对传参。 + * + * @param type 表达式类型。 + * @param kv 可变参数(key-value 键值对)。 + * @return 表示表达式节点的 Map。 */ private static Map exprMap(String type, Object... kv) { Map m = new LinkedHashMap<>(); @@ -40,10 +57,10 @@ public class ASTJsonSerializer { } /** - * 将 AST 根节点列表序列化为 JSON 字符串。 + * 将 AST 根节点列表转换为 JSON 字符串。 * - * @param ast 表示抽象语法树根节点的 List - * @return 对应的 JSON 格式字符串 + * @param ast 表示顶层语法树结构的节点列表。 + * @return 对应的 JSON 字符串表示形式。 */ public static String toJsonString(List ast) { List list = new ArrayList<>(ast.size()); @@ -54,20 +71,20 @@ public class ASTJsonSerializer { } /** - * 递归地将 AST 节点转换为 Map 或 List 等通用结构, - * 便于后续统一序列化。 + * 将任意 AST 节点递归转换为 Map/List 结构。 + * + * @param n 要转换的 AST 节点。 + * @return 以 Map/List 表示的结构化数据。 */ private static Object nodeToMap(Node n) { return switch (n) { + // 模块节点 case ModuleNode(String name, List imports, List functions) -> { Map map = newNodeMap("Module"); map.put("name", name); List imps = new ArrayList<>(imports.size()); for (ImportNode imp : imports) { - imps.add(Map.of( - "type", "Import", - "module", imp.moduleName() - )); + imps.add(Map.of("type", "Import", "module", imp.moduleName())); } map.put("imports", imps); List funcs = new ArrayList<>(functions.size()); @@ -77,15 +94,13 @@ public class ASTJsonSerializer { map.put("functions", funcs); yield map; } + // 函数定义节点 case FunctionNode f -> { Map map = newNodeMap("Function"); map.put("name", f.name()); List params = new ArrayList<>(f.parameters().size()); for (var p : f.parameters()) { - params.add(Map.of( - "name", p.name(), - "type", p.type() - )); + params.add(Map.of("name", p.name(), "type", p.type())); } map.put("parameters", params); map.put("returnType", f.returnType()); @@ -96,19 +111,20 @@ public class ASTJsonSerializer { map.put("body", body); yield map; } + // 变量声明节点 case DeclarationNode d -> { Map map = newNodeMap("Declaration"); map.put("name", d.getName()); map.put("varType", d.getType()); - map.put("initializer", - d.getInitializer().map(ASTJsonSerializer::exprToMap).orElse(null) - ); + map.put("initializer", d.getInitializer().map(ASTJsonSerializer::exprToMap).orElse(null)); yield map; } + // 赋值语句节点 case AssignmentNode a -> exprMap("Assignment", "variable", a.variable(), "value", exprToMap(a.value()) ); + // 条件语句节点 case IfNode i -> { Map map = newNodeMap("If"); map.put("condition", exprToMap(i.condition())); @@ -122,6 +138,7 @@ public class ASTJsonSerializer { } yield map; } + // 循环语句节点 case LoopNode l -> { Map map = newNodeMap("Loop"); map.put("initializer", l.initializer() != null ? nodeToMap(l.initializer()) : null); @@ -132,21 +149,28 @@ public class ASTJsonSerializer { map.put("body", body); yield map; } + // return 语句节点 case ReturnNode r -> { Map map = newNodeMap("Return"); r.getExpression().ifPresent(expr -> map.put("value", exprToMap(expr))); yield map; } + // 表达式语句节点 case ExpressionStatementNode e -> exprMap("ExpressionStatement", "expression", exprToMap(e.expression()) ); + // 通用表达式节点 case ExpressionNode expressionNode -> exprToMap(expressionNode); + // 其他类型(兜底处理) default -> Map.of("type", n.getClass().getSimpleName()); }; } /** - * 将表达式节点转换为 Map 表示。 + * 将表达式类型节点转换为 Map 表示形式。 + * + * @param expr 表达式 AST 节点。 + * @return 表示该表达式的 Map。 */ private static Object exprToMap(ExpressionNode expr) { return switch (expr) { @@ -167,7 +191,8 @@ public class ASTJsonSerializer { "object", exprToMap(object), "member", member ); + // 默认兜底处理:只写类型 default -> Map.of("type", expr.getClass().getSimpleName()); }; } -} \ No newline at end of file +} 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 db5f9b6..c319d4f 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 @@ -9,63 +9,84 @@ import java.util.function.BiConsumer; import java.util.function.Predicate; /** - * 通用的解析器,用于解析结构化的内容部分,完全解耦合关键字和语法。 + * {@code FlexibleSectionParser} 是一个通用的语法块解析工具。 *

- * FlexibleSectionParser 提供了一个灵活的机制来解析可变的语法块。每个语法块的解析逻辑通过外部提供, - * 该类仅负责按顺序解析不同的区块,直到遇到结束标记,同时能够跳过并收集注释供后续使用。 - *

- * 使用此类可以解析如函数定义中的多个部分(如参数、返回类型、函数体等),而无需在解析器中硬编码这些结构, - * 并且保留注释信息以便 IDE 或工具链进行注释跳转、重构等操作。 + * 该工具支持解析由关键字标识的多段结构化区块内容,常用于解析函数、类、模块、循环等语法单元中的命名子结构。 + * 相比传统硬编码方式,提供更灵活、可组合的解析能力,允许解析器模块动态注册处理逻辑,而非将所有逻辑写死在主流程中。 + * + *

典型应用包括: + *

    + *
  • 函数体解析中的 {@code params}、{@code returns}、{@code body} 等部分
  • + *
  • 模块定义中的 {@code imports}、{@code functions} 等部分
  • + *
  • 用户自定义 DSL 的可扩展语法结构
  • + *
+ * + *

该工具具备以下能力: + *

    + *
  • 自动跳过注释与空行
  • + *
  • 根据区块名称调用外部提供的解析器
  • + *
  • 支持终止标志(如 {@code end})来退出解析流程
  • + *
*/ public class FlexibleSectionParser { /** - * 解析一系列的可变区块,解析顺序和具体内容由外部定义。 - * 在解析过程中会跳过并收集注释,以及跳过空行。 + * 启动结构化区块的统一解析流程。 + *

+ * 每次调用会: + *

    + *
  1. 从 token 流中跳过空行与注释
  2. + *
  3. 依照当前 token 判断是否匹配某个区块
  4. + *
  5. 调用对应 {@link SectionDefinition} 执行区块解析逻辑
  6. + *
  7. 若遇到 {@code end} 关键字,则终止解析过程
  8. + *
  9. 若当前 token 不匹配任何已注册区块,抛出异常
  10. + *
* - * @param ctx 当前的解析上下文,包含语法分析所需的所有信息(如作用域、错误处理等) - * @param tokens 当前的词法 token 流,用于逐个查看或消耗 token - * @param sectionDefinitions 区块定义映射:每个关键字(如 "params", "returns", "body")对应一个区块定义 + * @param ctx 当前解析上下文,提供语法环境与作用域信息 + * @param tokens 当前 token 流 + * @param sectionDefinitions 各个区块的定义映射(key 为关键字,value 为判断 + 解析逻辑组合) + * @throws RuntimeException 若出现无法识别的关键字或未满足的匹配条件 */ public static void parse(ParserContext ctx, TokenStream tokens, Map sectionDefinitions) { - // 在开始解析之前,跳过并收集所有紧邻开头的注释和空行 + // 跳过开头的注释或空行 skipCommentsAndNewlines(tokens); while (true) { - // 在每次解析前,跳过并收集注释和空行 + // 跳过当前区块之间的空白与注释 skipCommentsAndNewlines(tokens); String keyword = tokens.peek().getLexeme(); + // 结束关键字表示解析流程终止 if ("end".equals(keyword)) { - // 遇到 'end' 则终止解析 break; } + // 查找匹配的区块定义 SectionDefinition definition = sectionDefinitions.get(keyword); if (definition != null && definition.condition().test(tokens)) { - // 执行该区块的解析逻辑 - definition.parser().accept(ctx, tokens); + definition.parser().accept(ctx, tokens); // 执行解析逻辑 } else { - // 无法识别该关键字或条件不满足则报错 throw new RuntimeException("未识别的关键字或条件不满足: " + keyword); } } } /** - * 跳过所有连续的注释行和空行。 + * 跳过连续出现的注释行或空行(NEWLINE)。 + *

+ * 该方法用于在区块之间清理无效 token,避免影响结构判断。 * - * @param tokens 词法流 + * @param tokens 当前 token 流 */ private static void skipCommentsAndNewlines(TokenStream tokens) { while (true) { TokenType type = tokens.peek().getType(); if (type == TokenType.COMMENT || type == TokenType.NEWLINE) { - tokens.next(); + tokens.next(); // 跳过注释或换行 continue; } break; @@ -73,13 +94,17 @@ public class FlexibleSectionParser { } /** - * 区块定义类:表示一个语法区块的匹配条件和解析逻辑。 - * 每个区块由两个部分组成: - * - 条件:用于判断当前 token 流是否应进入该区块的解析。 - * - 解析器:具体的解析逻辑,通常是消费若干 token 并更新解析上下文。 + * 表示一个结构区块的定义,包含匹配条件与解析器。 + *

+ * 每个区块由两部分组成: + *

    + *
  • {@code condition}:用于判断当前 token 是否应进入该区块
  • + *
  • {@code parser}:该区块对应的实际解析逻辑
  • + *
+ * 可实现懒加载、多语言支持或 DSL 的结构化扩展。 * - * @param condition 匹配条件,返回 true 表示此区块应被解析 - * @param parser 实际解析逻辑 + * @param condition 判断是否触发该区块的谓词函数 + * @param parser 区块解析逻辑(消费语法上下文与 token 流) */ public record SectionDefinition(Predicate condition, BiConsumer parser) { 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 70330e0..ce30193 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 @@ -16,7 +16,7 @@ import java.util.Map.Entry; * 4. 序列化器基于 StringBuilder,预分配容量,减少中间字符串创建 */ public class JSONParser { - // 私有构造,禁止外部实例化 + private JSONParser() {} /** diff --git a/src/main/java/org/jcnc/snow/compiler/parser/utils/ParserUtils.java b/src/main/java/org/jcnc/snow/compiler/parser/utils/ParserUtils.java index 48c485b..f20b7c6 100644 --- a/src/main/java/org/jcnc/snow/compiler/parser/utils/ParserUtils.java +++ b/src/main/java/org/jcnc/snow/compiler/parser/utils/ParserUtils.java @@ -4,44 +4,61 @@ import org.jcnc.snow.compiler.lexer.token.TokenType; import org.jcnc.snow.compiler.parser.context.TokenStream; /** - * 语法结构通用辅助工具类。 - * 提供常用的结构匹配和容错功能。 + * {@code ParserUtils} 是语法结构解析过程中的通用辅助工具类。 + *

+ * 提供一系列静态方法用于标准语法结构(如结构头、结构尾)的匹配校验,以及常用的容错处理操作。 + * 这些方法可在函数定义、模块定义、循环、条件语句等语法块中复用,有效减少冗余代码,提高解析器稳定性。 + * + *

主要功能包括: + *

    + *
  • 匹配结构性语法起始标记(如 {@code loop:}、{@code function:})
  • + *
  • 匹配结构性语法结尾标记(如 {@code end loop}、{@code end function})
  • + *
  • 跳过多余换行符,增强语法容错性
  • + *
*/ public class ParserUtils { /** - * 匹配形如 "keyword:" 的语法结构头部,并跳过换行。 + * 匹配结构语法的标准起始格式 {@code keyword:},并跳过其后的换行符。 + *

+ * 该方法适用于需要标识结构起点的语法元素,如 {@code loop:}、{@code function:} 等。 + * 若格式不匹配,将抛出语法异常。 * - * @param ts Token 流 - * @param keyword 结构标识关键字(如 "loop", "function" 等) + * @param ts 当前的 token 流 + * @param keyword 结构起始关键字(如 "loop", "function", "initializer" 等) */ public static void matchHeader(TokenStream ts, String keyword) { - ts.expect(keyword); - ts.expect(":"); - ts.expectType(TokenType.NEWLINE); - skipNewlines(ts); + ts.expect(keyword); // 匹配关键字 + ts.expect(":"); // 匹配冒号 + ts.expectType(TokenType.NEWLINE); // 匹配行尾换行 + skipNewlines(ts); // 跳过多余空行 } /** - * 匹配形如 "end keyword" 的语法结构结尾。 + * 匹配结构语法的标准结尾格式 {@code end keyword}。 + *

+ * 该方法用于验证结构块的结束,例如 {@code end loop}、{@code end if} 等。 + * 若格式不正确,将抛出异常。 * - * @param ts Token 流 - * @param keyword 结构标识关键字(如 "loop", "function" 等) + * @param ts 当前的 token 流 + * @param keyword 对应的结构关键字(必须与开始标记一致) */ public static void matchFooter(TokenStream ts, String keyword) { - ts.expect("end"); - ts.expect(keyword); - ts.expectType(TokenType.NEWLINE); + ts.expect("end"); // 匹配 'end' + ts.expect(keyword); // 匹配结构名 + ts.expectType(TokenType.NEWLINE); // 匹配行尾 } /** - * 跳过连续的换行符,常用于容错与美化语法结构。 + * 跳过连续的换行符({@code NEWLINE})。 + *

+ * 通常用于解析器之间的过渡阶段,以消除格式干扰,提升容错性。 * - * @param ts Token 流 + * @param ts 当前的 token 流 */ public static void skipNewlines(TokenStream ts) { while (ts.peek().getType() == TokenType.NEWLINE) { - ts.next(); + ts.next(); // 连续消费换行符 } } }