From 030f8c5b440d42a2a5d6e82e3a87158e1cff5967 Mon Sep 17 00:00:00 2001 From: Luke Date: Fri, 25 Apr 2025 15:49:08 +0800 Subject: [PATCH] =?UTF-8?q?=E6=8F=90=E5=8F=96=E5=87=BA=E6=B7=B7=E5=90=88?= =?UTF-8?q?=E6=8E=92=E5=88=97=E6=96=B9=E6=B3=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../parser/function/FunctionParser.java | 214 ++++++------------ .../parser/util/FlexibleSectionParser.java | 110 +++++++++ 2 files changed, 182 insertions(+), 142 deletions(-) create mode 100644 src/main/java/org/jcnc/snow/compiler/parser/util/FlexibleSectionParser.java 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 b0d322e..2e4cda8 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 @@ -8,51 +8,46 @@ import org.jcnc.snow.compiler.parser.ast.StatementNode; import org.jcnc.snow.compiler.parser.context.ParserContext; import org.jcnc.snow.compiler.parser.context.TokenStream; import org.jcnc.snow.compiler.parser.factory.StatementParserFactory; +import org.jcnc.snow.compiler.parser.util.FlexibleSectionParser; +import org.jcnc.snow.compiler.parser.util.FlexibleSectionParser.SectionDefinition; import java.util.ArrayList; +import java.util.HashMap; import java.util.List; +import java.util.Map; /** - * {@code FunctionParser} 是一个顶层语法分析器,用于解析函数定义语法结构。 + * {@code FunctionParser} 是一个顶层语法分析器,用于解析函数定义的语法结构。 *

- * 它支持以下组成部分,且顺序可灵活排列: + * 该解析器通过 {@code FlexibleSectionParser} 解析函数定义的多个部分,包括: *

- * 每个部分的顺序是非固定的,只要结构合法即可。 + *

+ * 解析顺序不固定,且每个部分的解析逻辑由 {@code FlexibleSectionParser} 配合外部条件和解析器来控制。 *

- * 示例函数定义语法如下: - *

{@code
- * module: MathUtils
- *     function: square_number
- *          parameter:
- *              declare number: int
- *          return_type: int
- *          body:
- *              return number * number
- *          end body
- *     end function
- * end module
- * }
- * 本解析器将该结构转换为抽象语法树(AST)中的 {@code FunctionNode} 节点。 + * 解析过程中,函数定义的各个部分被依次解析,并最终构建成 {@code FunctionNode},作为抽象语法树的一部分。 + *

*/ - public class FunctionParser implements TopLevelParser { /** - * 解析完整的函数结构,构建 FunctionNode 语法树节点。 + * 解析函数定义,构建函数的抽象语法树。 + *

+ * 此方法解析函数的多个部分,包括函数名、参数、返回类型和函数体。各部分的解析顺序由 {@code FlexibleSectionParser} 控制。 + *

* - * @param ctx 解析上下文,包含 token 流和其他辅助信息。 - * @return 构造的函数语法树节点。 + * @param ctx 解析上下文,包含 Token 流等信息 + * @return 解析后的函数语法树节点 */ @Override public FunctionNode parse(ParserContext ctx) { TokenStream tokens = ctx.getTokens(); - // 匹配 function: 起始标签 + // 匹配函数头部 parseFunctionHeader(tokens); // 解析函数名 @@ -63,17 +58,41 @@ public class FunctionParser implements TopLevelParser { String[] returnType = {null}; // 模拟引用传参 List body = new ArrayList<>(); - // 解析 parameter / return_type / body,顺序任意 - parseFlexibleSections(ctx, tokens, parameters, body, type -> returnType[0] = type); + // 定义可变区块的解析规则 + Map sectionDefinitions = new HashMap<>(); + + // 参数解析 + sectionDefinitions.put("parameter", new SectionDefinition( + ts -> ts.peek().getLexeme().equals("parameter"), + (ctx1, ts1) -> parameters.addAll(parseParameters(ts1)) + )); + + // 返回类型解析 + sectionDefinitions.put("return_type", new SectionDefinition( + ts -> ts.peek().getLexeme().equals("return_type"), + (ctx1, ts1) -> returnType[0] = parseReturnType(ts1) + )); + + // 函数体解析 + sectionDefinitions.put("body", new SectionDefinition( + ts -> ts.peek().getLexeme().equals("body"), + (ctx1, ts1) -> body.addAll(parseFunctionBody(ctx1, ts1)) + )); + + // 使用 FlexibleSectionParser 解析函数的可变部分,顺序无关 + FlexibleSectionParser.parse(ctx, tokens, sectionDefinitions); // 匹配函数结尾标签 parseFunctionFooter(tokens); + // 返回构建好的函数节点 return new FunctionNode(functionName, parameters, returnType[0], body); } /** - * 匹配 function 起始标志(function:) + * 匹配并解析函数头部标记(例如:function:)。 + * + * @param ts Token 流 */ private void parseFunctionHeader(TokenStream ts) { ts.expect("function"); @@ -81,7 +100,10 @@ public class FunctionParser implements TopLevelParser { } /** - * 匹配函数名称标识符,并跳过换行 + * 解析函数名并跳过换行符。 + * + * @param ts Token 流 + * @return 函数名 */ private String parseFunctionName(TokenStream ts) { String name = ts.expectType(TokenType.IDENTIFIER).getLexeme(); @@ -90,77 +112,26 @@ public class FunctionParser implements TopLevelParser { } /** - * 解析函数的可变结构部分(parameter, return_type, body),顺序不限。 + * 匹配并解析函数结尾标记(例如:end function)。 * - * @param ctx 上下文 - * @param tokens Token 流 - * @param parameters 存储解析后的参数节点 - * @param body 存储解析后的语句节点 - * @param returnTypeSetter 设置返回类型(使用 lambda) + * @param ts Token 流 */ - private void parseFlexibleSections(ParserContext ctx, - TokenStream tokens, - List parameters, - List body, - java.util.function.Consumer returnTypeSetter) { - boolean parsedParam = false; - boolean parsedReturn = false; - boolean parsedBody = false; - - while (true) { - // 跳过空行 - while (tokens.peek().getType() == TokenType.NEWLINE) { - tokens.next(); - } - - // 获取当前关键字 - String keyword = tokens.peek().getLexeme(); - - switch (keyword) { - case "parameter": - if (parsedParam) throw new RuntimeException("重复定义 parameter 区块。"); - parameters.addAll(parseParameters(tokens)); - parsedParam = true; - break; - - case "return_type": - if (parsedReturn) throw new RuntimeException("重复定义 return_type 区块。"); - returnTypeSetter.accept(parseReturnType(tokens)); - parsedReturn = true; - break; - - case "body": - if (parsedBody) throw new RuntimeException("重复定义 body 区块。"); - body.addAll(parseFunctionBody(ctx, tokens)); - parsedBody = true; - break; - - case "end": - return; // 完成可变区块的解析,继续处理函数结尾 - - default: - throw new RuntimeException("函数定义中出现未识别的关键字: " + keyword); - } - } + private void parseFunctionFooter(TokenStream ts) { + ts.expect("end"); + ts.expect("function"); + ts.expectType(TokenType.NEWLINE); } /** - * 解析参数定义区块。 + * 解析参数列表。 *

- * 语法格式示例: - *

-     * parameter:
-     *     declare param1: int
-     *     declare param2: string
-     * 
- * 每一行参数定义都必须以 declare 开头。 - * 方法将跳过空行,并在遇到下一个语句区块(如 return_type、body、end)时终止。 + * 该方法解析以 "declare" 开头的参数声明行,并返回一个包含参数的 {@code ParameterNode} 列表。 + *

* - * @param ts Token 流,用于逐个读取语法标记 - * @return 参数节点列表,每个 {@link ParameterNode} 包含参数名和类型 + * @param ts Token 流 + * @return 参数节点列表 */ private List parseParameters(TokenStream ts) { - // 开始:匹配 "parameter:" 行 ts.expect("parameter"); ts.expect(":"); ts.expectType(TokenType.NEWLINE); @@ -168,47 +139,33 @@ public class FunctionParser implements TopLevelParser { List params = new ArrayList<>(); while (true) { - // 跳过空行 if (ts.peek().getType() == TokenType.NEWLINE) { ts.next(); continue; } - String lexeme = ts.peek().getLexeme(); - - // 遇到新语句区块的开始(结束当前参数解析块) - if ("return_type".equals(lexeme) || "body".equals(lexeme) || "end".equals(lexeme)) { + if ("return_type".equals(ts.peek().getLexeme()) || "body".equals(ts.peek().getLexeme()) || "end".equals(ts.peek().getLexeme())) { break; } - // 参数定义必须以 "declare" 开头 ts.expect("declare"); - // 获取参数名称 String paramName = ts.expectType(TokenType.IDENTIFIER).getLexeme(); - - // 冒号分隔符 ts.expect(":"); - - // 参数类型 String paramType = ts.expectType(TokenType.TYPE).getLexeme(); - - // 每行结束必须是 NEWLINE ts.expectType(TokenType.NEWLINE); - // 加入参数列表 params.add(new ParameterNode(paramName, paramType)); } return params; } - /** - * 解析返回类型区块。 + * 解析返回类型。 * * @param ts Token 流 - * @return 返回类型字符串 + * @return 返回类型 */ private String parseReturnType(TokenStream ts) { ts.expect("return_type"); @@ -219,25 +176,16 @@ public class FunctionParser implements TopLevelParser { } /** - * 解析函数体(body)部分,提取语句并构建 {@link StatementNode} 列表。 + * 解析函数体。 *

- * 语法结构形如: - *

{@code
-     * body:
-     *   declare x:int = 5
-     *   if x > 0
-     *     ...
-     * end body
-     * }
- *

- * 该方法将每一条语句委托给 {@link StatementParserFactory} 根据关键字调度解析器。 + * 该方法解析函数体中的每个语句,并返回一个 {@code StatementNode} 列表。 + *

* - * @param ctx 上下文对象,包含词法流与全局状态 - * @param ts 当前的 Token 流(词法单元序列) - * @return 包含函数体所有语句的列表 + * @param ctx 解析上下文 + * @param ts Token 流 + * @return 函数体的语句列表 */ private List parseFunctionBody(ParserContext ctx, TokenStream ts) { - // 匹配 body: 起始标记 ts.expect("body"); ts.expect(":"); ts.expectType(TokenType.NEWLINE); @@ -245,42 +193,24 @@ public class FunctionParser implements TopLevelParser { List body = new ArrayList<>(); while (true) { - // 跳过空行,确保处理有效语句 if (ts.peek().getType() == TokenType.NEWLINE) { ts.next(); continue; } - // 若遇到 end,则说明函数体结束,跳出循环 if ("end".equals(ts.peek().getLexeme())) { break; } - // 根据当前行的关键字选择对应的语句解析器 String keyword = ts.peek().getLexeme(); - - // 调用语句解析器解析当前语句,加入函数体列表中 StatementNode statement = StatementParserFactory.get(keyword).parse(ctx); body.add(statement); } - // 匹配函数体结束标记 end body ts.expect("end"); ts.expect("body"); ts.expectType(TokenType.NEWLINE); return body; } - - - /** - * 匹配函数定义结束标志(end function) - * - * @param ts Token 流 - */ - private void parseFunctionFooter(TokenStream ts) { - ts.expect("end"); - ts.expect("function"); - ts.expectType(TokenType.NEWLINE); - } } diff --git a/src/main/java/org/jcnc/snow/compiler/parser/util/FlexibleSectionParser.java b/src/main/java/org/jcnc/snow/compiler/parser/util/FlexibleSectionParser.java new file mode 100644 index 0000000..cdcc394 --- /dev/null +++ b/src/main/java/org/jcnc/snow/compiler/parser/util/FlexibleSectionParser.java @@ -0,0 +1,110 @@ +package org.jcnc.snow.compiler.parser.util; + +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 java.util.List; +import java.util.Map; +import java.util.function.BiConsumer; +import java.util.function.Predicate; + +/** + * 通用的解析器,用于解析结构化的内容部分,完全解耦合关键字和语法。 + *

+ * {@code FlexibleSectionParser} 提供了一个灵活的机制来解析可变的语法块。每个语法块的解析逻辑通过外部提供, + * 该类仅负责按顺序解析不同的区块,直到遇到结束标记。它完全解耦了具体的语法关键字和解析逻辑。 + *

+ * + *

+ * 例如,您可以使用此类解析函数定义中的多个部分(如参数、返回类型、函数体等),而不需要在解析器中显式地硬编码每个部分。 + *

+ */ +public class FlexibleSectionParser { + + /** + * 解析一系列的可变区块,解析顺序和具体内容由外部定义。 + *

+ * 该方法接受一个包含区块定义的映射,每个区块定义包含一个条件(`Predicate`)和一个解析器(`BiConsumer`)。 + * 条件用于判断是否应该解析该区块,解析器则负责实际的解析过程。 + *

+ * + * @param ctx 解析上下文,包含词法流等信息 + * @param tokens 当前的 Token 流 + * @param sectionDefinitions 各种区块定义,键值为区块的名称,值为对应的解析器和条件 + */ + public static void parse(ParserContext ctx, + TokenStream tokens, + Map sectionDefinitions) { + + // 跳过空行 + while (tokens.peek().getType() == TokenType.NEWLINE) { + tokens.next(); + } + + // 遍历所有可变部分的解析 + while (true) { + String keyword = tokens.peek().getLexeme(); + + // 查找是否有与当前关键字匹配的区块 + SectionDefinition definition = sectionDefinitions.get(keyword); + + if (definition != null && definition.getCondition().test(tokens)) { + // 执行解析动作 + definition.getParser().accept(ctx, tokens); + } else if ("end".equals(keyword)) { + // 如果遇到 "end",则退出解析 + break; + } else { + throw new RuntimeException("未识别的关键字或条件不满足: " + keyword); + } + + // 跳过空行,继续解析 + while (tokens.peek().getType() == TokenType.NEWLINE) { + tokens.next(); + } + } + } + + /** + * 定义区块的结构:每个区块有一个条件和一个解析器。 + *

+ * {@code SectionDefinition} 是描述如何解析某个语法块的结构。 + * 它包含一个条件(`Predicate`),用于判断当前 Token 是否符合该区块的开始标识, + * 以及一个解析器(`BiConsumer`),用于执行具体的解析操作。 + *

+ */ + public static class SectionDefinition { + + private final Predicate condition; // 条件,判断是否需要解析该区块 + private final BiConsumer parser; // 区块的解析器 + + /** + * 构造一个新的区块定义。 + * + * @param condition 判断该区块是否应该解析的条件 + * @param parser 区块的解析器 + */ + public SectionDefinition(Predicate condition, BiConsumer parser) { + this.condition = condition; + this.parser = parser; + } + + /** + * 获取该区块的条件,用于判断是否解析此区块。 + * + * @return 区块的条件 + */ + public Predicate getCondition() { + return condition; + } + + /** + * 获取该区块的解析器。 + * + * @return 区块的解析器 + */ + public BiConsumer getParser() { + return parser; + } + } +}