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 function}:函数名声明
- * - {@code parameter}:参数列表(支持可选的 {@code declare} 关键字)
- * - {@code return_type}:返回类型声明
- * - {@code body}:函数体代码块,包含函数内的语句
+ * - 函数名
+ * - 参数列表
+ * - 返回类型
+ * - 函数体
*
- * 每个部分的顺序是非固定的,只要结构合法即可。
+ *
+ * 解析顺序不固定,且每个部分的解析逻辑由 {@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;
+ }
+ }
+}