提取出混合排列方法

This commit is contained in:
Luke 2025-04-25 15:49:08 +08:00
parent 92e36f3dd0
commit 030f8c5b44
2 changed files with 182 additions and 142 deletions

View File

@ -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} 是一个顶层语法分析器用于解析函数定义语法结构
* <p>
* 它支持以下组成部分且顺序可灵活排列
* 该解析器通过 {@code FlexibleSectionParser} 解析函数定义的多个部分包括
* <ul>
* <li>{@code function}函数名声明</li>
* <li>{@code parameter}参数列表支持可选的 {@code declare} 关键字</li>
* <li>{@code return_type}返回类型声明</li>
* <li>{@code body}函数体代码块包含函数内的语句</li>
* <li>函数名</li>
* <li>参数列表</li>
* <li>返回类型</li>
* <li>函数体</li>
* </ul>
* 每个部分的顺序是非固定的只要结构合法即可
* </p>
* 解析顺序不固定且每个部分的解析逻辑由 {@code FlexibleSectionParser} 配合外部条件和解析器来控制
* <p>
* 示例函数定义语法如下
* <pre>{@code
* module: MathUtils
* function: square_number
* parameter:
* declare number: int
* return_type: int
* body:
* return number * number
* end body
* end function
* end module
* }</pre>
* 本解析器将该结构转换为抽象语法树AST中的 {@code FunctionNode} 节点
* 解析过程中函数定义的各个部分被依次解析并最终构建成 {@code FunctionNode}作为抽象语法树的一部分
* </p>
*/
public class FunctionParser implements TopLevelParser {
/**
* 解析完整的函数结构构建 FunctionNode 语法树节点
* 解析函数定义构建函数的抽象语法树
* <p>
* 此方法解析函数的多个部分包括函数名参数返回类型和函数体各部分的解析顺序由 {@code FlexibleSectionParser} 控制
* </p>
*
* @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<StatementNode> body = new ArrayList<>();
// 解析 parameter / return_type / body顺序任意
parseFlexibleSections(ctx, tokens, parameters, body, type -> returnType[0] = type);
// 定义可变区块的解析规则
Map<String, SectionDefinition> 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<ParameterNode> parameters,
List<StatementNode> body,
java.util.function.Consumer<String> 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);
}
/**
* 解析参数定义区块
* 解析参数列表
* <p>
* 语法格式示例
* <pre>
* parameter:
* declare param1: int
* declare param2: string
* </pre>
* 每一行参数定义都必须以 <code>declare</code> 开头
* 方法将跳过空行并在遇到下一个语句区块 return_typebodyend时终止
* 该方法解析以 "declare" 开头的参数声明行并返回一个包含参数的 {@code ParameterNode} 列表
* </p>
*
* @param ts Token 用于逐个读取语法标记
* @return 参数节点列表每个 {@link ParameterNode} 包含参数名和类型
* @param ts Token
* @return 参数节点列表
*/
private List<ParameterNode> parseParameters(TokenStream ts) {
// 开始匹配 "parameter:"
ts.expect("parameter");
ts.expect(":");
ts.expectType(TokenType.NEWLINE);
@ -168,47 +139,33 @@ public class FunctionParser implements TopLevelParser {
List<ParameterNode> 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} 列表
* 解析函数体
* <p>
* 语法结构形如
* <pre>{@code
* body:
* declare x:int = 5
* if x > 0
* ...
* end body
* }</pre>
* <p>
* 该方法将每一条语句委托给 {@link StatementParserFactory} 根据关键字调度解析器
* 该方法解析函数体中的每个语句并返回一个 {@code StatementNode} 列表
* </p>
*
* @param ctx 上下文对象包含词法流与全局状态
* @param ts 当前的 Token 词法单元序列
* @return 包含函数体所有语句的列表
* @param ctx 解析上下文
* @param ts Token
* @return 函数体的语句列表
*/
private List<StatementNode> 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<StatementNode> 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);
}
}

View File

@ -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;
/**
* 通用的解析器用于解析结构化的内容部分完全解耦合关键字和语法
* <p>
* {@code FlexibleSectionParser} 提供了一个灵活的机制来解析可变的语法块每个语法块的解析逻辑通过外部提供
* 该类仅负责按顺序解析不同的区块直到遇到结束标记它完全解耦了具体的语法关键字和解析逻辑
* </p>
*
* <p>
* 例如您可以使用此类解析函数定义中的多个部分如参数返回类型函数体等而不需要在解析器中显式地硬编码每个部分
* </p>
*/
public class FlexibleSectionParser {
/**
* 解析一系列的可变区块解析顺序和具体内容由外部定义
* <p>
* 该方法接受一个包含区块定义的映射每个区块定义包含一个条件`Predicate<TokenStream>`和一个解析器`BiConsumer<ParserContext, TokenStream>`
* 条件用于判断是否应该解析该区块解析器则负责实际的解析过程
* </p>
*
* @param ctx 解析上下文包含词法流等信息
* @param tokens 当前的 Token
* @param sectionDefinitions 各种区块定义键值为区块的名称值为对应的解析器和条件
*/
public static void parse(ParserContext ctx,
TokenStream tokens,
Map<String, SectionDefinition> 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();
}
}
}
/**
* 定义区块的结构每个区块有一个条件和一个解析器
* <p>
* {@code SectionDefinition} 是描述如何解析某个语法块的结构
* 它包含一个条件`Predicate<TokenStream>`用于判断当前 Token 是否符合该区块的开始标识
* 以及一个解析器`BiConsumer<ParserContext, TokenStream>`用于执行具体的解析操作
* </p>
*/
public static class SectionDefinition {
private final Predicate<TokenStream> condition; // 条件判断是否需要解析该区块
private final BiConsumer<ParserContext, TokenStream> parser; // 区块的解析器
/**
* 构造一个新的区块定义
*
* @param condition 判断该区块是否应该解析的条件
* @param parser 区块的解析器
*/
public SectionDefinition(Predicate<TokenStream> condition, BiConsumer<ParserContext, TokenStream> parser) {
this.condition = condition;
this.parser = parser;
}
/**
* 获取该区块的条件用于判断是否解析此区块
*
* @return 区块的条件
*/
public Predicate<TokenStream> getCondition() {
return condition;
}
/**
* 获取该区块的解析器
*
* @return 区块的解析器
*/
public BiConsumer<ParserContext, TokenStream> getParser() {
return parser;
}
}
}