优化函数器

This commit is contained in:
Luke 2025-04-25 16:07:30 +08:00
parent aae5aebed9
commit f6804d05a9
2 changed files with 87 additions and 63 deletions

View File

@ -26,76 +26,83 @@ import java.util.Map;
* <li>返回类型</li>
* <li>函数体</li>
* </ul>
* </p>
* 解析顺序不固定且每个部分的解析逻辑由 {@code FlexibleSectionParser} 配合外部条件和解析器来控制
* <p>
* 解析过程中函数定义的各个部分被依次解析并最终构建成 {@code FunctionNode}作为抽象语法树的一部分
* 各部分的顺序不固定 FlexibleSectionParser 按规则识别和处理
* </p>
*/
public class FunctionParser implements TopLevelParser {
/**
* 解析函数定义构建函数的抽象语法树
* 解析函数定义构建函数的抽象语法树节点
* <p>
* 此方法解析函数的多个部分包括函数名参数返回类型和函数体各部分的解析顺序由 {@code FlexibleSectionParser} 控制
* 此方法首先解析函数的头部和名称然后通过 FlexibleSectionParser
* 分别解析函数的参数返回类型和函数体最后封装为 FunctionNode 返回
* </p>
*
* @param ctx 解析上下文包含 Token 流等信息
* @return 解析后的函数语法树节点
* @param ctx 解析上下文包含 Token 作用域信息等
* @return 构建完成的 FunctionNode
*/
@Override
public FunctionNode parse(ParserContext ctx) {
TokenStream tokens = ctx.getTokens();
// 匹配函数头部
// 匹配并消费 "function:" 标记
parseFunctionHeader(tokens);
// 解析函数名
// 获取函数名称
String functionName = parseFunctionName(tokens);
// 用于存储参数返回类型和函数体
// 用于接收参数返回类型和函数体的容器
List<ParameterNode> parameters = new ArrayList<>();
String[] returnType = {null}; // 模拟引用传参
String[] returnType = {null}; // 使用数组模拟引用以便 lambda 内修改值
List<StatementNode> body = new ArrayList<>();
// 定义可变区块的解析规则
// 构建区块解析定义参数返回类型函数体
Map<String, SectionDefinition> sectionDefinitions = getStringSectionDefinitionMap(parameters, returnType, body);
// 使用 FlexibleSectionParser 解析函数的可变部分顺序无关
// 使用 FlexibleSectionParser 解析区块顺序无关
FlexibleSectionParser.parse(ctx, tokens, sectionDefinitions);
// 匹配函数结尾标签
// 匹配函数结束部分end function
parseFunctionFooter(tokens);
// 返回构建好的函数节点
// 构建语法树节点并返回
return new FunctionNode(functionName, parameters, returnType[0], body);
}
/**
* 构造区块定义映射包括参数返回类型函数体
*
* @param parameters 参数节点容器
* @param returnType 返回类型容器数组模拟引用
* @param body 函数体语句节点容器
* @return 各关键字对应的解析逻辑映射
*/
private Map<String, SectionDefinition> getStringSectionDefinitionMap(List<ParameterNode> parameters, String[] returnType, List<StatementNode> body) {
Map<String, SectionDefinition> sectionDefinitions = new HashMap<>();
// 参数解析
// 参数部分解析规则
sectionDefinitions.put("parameter", new SectionDefinition(
ts -> ts.peek().getLexeme().equals("parameter"),
(_, ts1) -> parameters.addAll(parseParameters(ts1))
));
// 返回类型解析
// 返回类型部分解析规则
sectionDefinitions.put("return_type", new SectionDefinition(
ts -> ts.peek().getLexeme().equals("return_type"),
(_, ts1) -> returnType[0] = parseReturnType(ts1)
));
// 函数体解析
// 函数体部分解析规则
sectionDefinitions.put("body", new SectionDefinition(
ts -> ts.peek().getLexeme().equals("body"),
(ctx1, ts1) -> body.addAll(parseFunctionBody(ctx1, ts1))
));
return sectionDefinitions;
}
/**
* 匹配并解析函数头部标记例如function:
* 匹配并解析函数头部function:
*
* @param ts Token
*/
@ -105,10 +112,10 @@ public class FunctionParser implements TopLevelParser {
}
/**
* 解析函数名并跳过换行
* 解析函数名并跳过换行
*
* @param ts Token
* @return 函数名
* @return 函数名
*/
private String parseFunctionName(TokenStream ts) {
String name = ts.expectType(TokenType.IDENTIFIER).getLexeme();
@ -117,7 +124,7 @@ public class FunctionParser implements TopLevelParser {
}
/**
* 匹配并解析函数结尾标记例如end function
* 匹配并解析函数尾部end function
*
* @param ts Token
*/
@ -128,10 +135,12 @@ public class FunctionParser implements TopLevelParser {
}
/**
* 解析参数列表
* <p>
* 该方法解析以 "declare" 开头的参数声明行并返回一个包含参数的 {@code ParameterNode} 列表
* </p>
* 解析函数参数部分格式
* <pre>
* parameter:
* declare param1: int
* declare param2: string
* </pre>
*
* @param ts Token
* @return 参数节点列表
@ -143,18 +152,21 @@ public class FunctionParser implements TopLevelParser {
List<ParameterNode> params = new ArrayList<>();
// 持续解析参数声明行直到下一个区块或 "end"
while (true) {
if (ts.peek().getType() == TokenType.NEWLINE) {
ts.next();
ts.next(); // 跳过空行
continue;
}
if ("return_type".equals(ts.peek().getLexeme()) || "body".equals(ts.peek().getLexeme()) || "end".equals(ts.peek().getLexeme())) {
// 到达下一个区块或函数结尾时停止
String keyword = ts.peek().getLexeme();
if ("return_type".equals(keyword) || "body".equals(keyword) || "end".equals(keyword)) {
break;
}
// 匹配 "declare paramName: Type" 格式
ts.expect("declare");
String paramName = ts.expectType(TokenType.IDENTIFIER).getLexeme();
ts.expect(":");
String paramType = ts.expectType(TokenType.TYPE).getLexeme();
@ -167,10 +179,13 @@ public class FunctionParser implements TopLevelParser {
}
/**
* 解析返回类型
* 解析返回类型部分格式如下
* <pre>
* return_type: int
* </pre>
*
* @param ts Token
* @return 返回类型
* @return 返回类型字符串
*/
private String parseReturnType(TokenStream ts) {
ts.expect("return_type");
@ -181,14 +196,17 @@ public class FunctionParser implements TopLevelParser {
}
/**
* 解析函数体
* <p>
* 该方法解析函数体中的每个语句并返回一个 {@code StatementNode} 列表
* </p>
* 解析函数体部分格式示例
* <pre>
* body:
* print "hello"
* return x
* end body
* </pre>
*
* @param ctx 解析上下文
* @param ts Token
* @return 函数体的语句列表
* @return 语句节点列表
*/
private List<StatementNode> parseFunctionBody(ParserContext ctx, TokenStream ts) {
ts.expect("body");
@ -197,6 +215,7 @@ public class FunctionParser implements TopLevelParser {
List<StatementNode> body = new ArrayList<>();
// 持续解析直到遇到 end
while (true) {
if (ts.peek().getType() == TokenType.NEWLINE) {
ts.next();
@ -207,11 +226,13 @@ public class FunctionParser implements TopLevelParser {
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);

View File

@ -14,9 +14,8 @@ import java.util.function.Predicate;
* {@code FlexibleSectionParser} 提供了一个灵活的机制来解析可变的语法块每个语法块的解析逻辑通过外部提供
* 该类仅负责按顺序解析不同的区块直到遇到结束标记它完全解耦了具体的语法关键字和解析逻辑
* </p>
*
* <p>
* 使用此类解析函数定义中的多个部分如参数返回类型函数体等不需要在解析器中显式地硬编码每个部分
* 使用此类可以解析函数定义中的多个部分如参数返回类型函数体等无需在解析器中硬编码这些结构
* </p>
*/
public class FlexibleSectionParser {
@ -24,41 +23,45 @@ public class FlexibleSectionParser {
/**
* 解析一系列的可变区块解析顺序和具体内容由外部定义
* <p>
* 该方法接受一个包含区块定义的映射每个区块定义包含一个条件`Predicate<TokenStream>`和一个解析器`BiConsumer<ParserContext, TokenStream>`
* 条件用于判断是否应该解析该区块解析器则负责实际的解析过程
* 该方法接受一个包含区块定义的映射每个区块定义包含一个条件Predicate<TokenStream>
* 和一个解析器BiConsumer<ParserContext, TokenStream>
* 条件用于判断当前是否应解析该区块解析器则执行解析逻辑
* </p>
*
* @param ctx 解析上下文包含词法流等信息
* @param tokens 当前的 Token
* @param sectionDefinitions 各种区块定义键值为区块的名称值为对应的解析器和条件
* @param ctx 当前的解析上下文包含语法分析所需的所有信息如作用域错误处理等
* @param tokens 当前的词法 token 用于逐个查看或消耗 token
* @param sectionDefinitions 区块定义映射每个关键字 "params", "returns", "body"对应一个区块定义
*/
public static void parse(ParserContext ctx,
TokenStream tokens,
Map<String, SectionDefinition> sectionDefinitions) {
// 跳过空行
// 跳过最开始的空行避免误判开始关键字
while (tokens.peek().getType() == TokenType.NEWLINE) {
tokens.next();
}
// 遍历所有可变部分的解析
// 主循环逐个处理区块直到遇到 "end" 为止
while (true) {
// 获取当前 token 的字面量通常是关键字字符串
String keyword = tokens.peek().getLexeme();
// 查找是否有与当前关键字匹配的区块
// 尝试在映射中找到对应该关键字的区块定义
SectionDefinition definition = sectionDefinitions.get(keyword);
// 如果找到了定义且其条件满足当前 token
if (definition != null && definition.condition().test(tokens)) {
// 执行解析动作
// 执行该区块的解析逻辑
definition.parser().accept(ctx, tokens);
} else if ("end".equals(keyword)) {
// 如果遇到 "end"则退出解析
// 如果遇到 "end"表示所有区块结束退出循环
break;
} else {
// 如果关键字无法识别或不满足条件则抛出异常
throw new RuntimeException("未识别的关键字或条件不满足: " + keyword);
}
// 跳过空行继续解析
// 每次解析完成后继续跳过空行准备进入下一个区块的判断
while (tokens.peek().getType() == TokenType.NEWLINE) {
tokens.next();
}
@ -66,31 +69,31 @@ public class FlexibleSectionParser {
}
/**
* 定义区块的结构每个区块有一个条件和一个解析器
* 区块定义类表示一个语法区块的匹配条件和解析逻辑
* <p>
* {@code SectionDefinition} 是描述如何解析某个语法块的结构
* 它包含一个条件`Predicate<TokenStream>`用于判断当前 Token 是否符合该区块的开始标识
* 以及一个解析器`BiConsumer<ParserContext, TokenStream>`用于执行具体的解析操作
* 每个区块由两个部分组成
* - 条件用于判断当前 token 流是否应进入该区块的解析
* - 解析器具体的解析逻辑通常是消费若干 token 并更新解析上下文
* </p>
*
* @param condition 条件判断是否需要解析该区块
* @param parser 区块的解析器
* @param condition 匹配条件返回 true 表示此区块应被解析
* @param parser 实际解析逻辑
*/
public record SectionDefinition(Predicate<TokenStream> condition, BiConsumer<ParserContext, TokenStream> parser) {
/**
* 构造一个新的区块定义
* 构造函数Java Record 自动生成但我们保留注释以说明其目的
*
* @param condition 判断该区块是否应该解析的条件
* @param parser 区块的解析
* @param condition 判断当前是否应解析该区块的逻辑
* @param parser 负责实际解析过程的处理
*/
public SectionDefinition {
}
/**
* 获取该区块的条件用于判断是否解析此区块
* 获取条件判断函数
*
* @return 区块的条件
* @return 一个用于判断是否进入该区块的 Predicate
*/
@Override
public Predicate<TokenStream> condition() {
@ -98,9 +101,9 @@ public class FlexibleSectionParser {
}
/**
* 获取该区块的解析器
* 获取解析器函数
*
* @return 区块的解析器
* @return 一个解析该区块的 BiConsumer 函数
*/
@Override
public BiConsumer<ParserContext, TokenStream> parser() {