From 586ede1cf0a642e9128e7ae999c38581d806a458 Mon Sep 17 00:00:00 2001
From: Luke
Date: Fri, 29 Aug 2025 18:19:24 +0800
Subject: [PATCH] =?UTF-8?q?refactor:=20=E9=87=8D=E6=9E=84=E8=A1=A8?=
=?UTF-8?q?=E8=BE=BE=E5=BC=8F=E8=A7=A3=E6=9E=90=E5=99=A8=E6=96=87=E6=A1=A3?=
=?UTF-8?q?=E5=92=8C=E4=BB=A3=E7=A0=81?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
- 更新类文档,使其更清晰地描述 Pratt 表达式解析器的功能和结构
- 优化方法注释,增加对核心解析逻辑的解释
- 调整代码格式,提高可读性
- 补充注释说明前缀和中缀解析器的注册方式及使用场景
---
.../expression/PrattExpressionParser.java | 134 ++++++++----------
1 file changed, 63 insertions(+), 71 deletions(-)
diff --git a/src/main/java/org/jcnc/snow/compiler/parser/expression/PrattExpressionParser.java b/src/main/java/org/jcnc/snow/compiler/parser/expression/PrattExpressionParser.java
index f18813f..3fa483c 100644
--- a/src/main/java/org/jcnc/snow/compiler/parser/expression/PrattExpressionParser.java
+++ b/src/main/java/org/jcnc/snow/compiler/parser/expression/PrattExpressionParser.java
@@ -13,62 +13,62 @@ import java.util.HashMap;
import java.util.Map;
/**
- * {@code PrattExpressionParser} 基于 Pratt 算法的表达式解析器实现。
+ * {@code PrattExpressionParser}
*
- * 该类通过前缀(PrefixParselet)和中缀(InfixParselet)解析器注册表,
- * 支持灵活扩展的表达式语法,包括字面量、变量、函数调用、成员访问和各种运算符表达式。
- *
- *
- * 运算符优先级通过枚举控制,结合递归解析实现高效的优先级处理和语法结构解析。
- * 未注册的语法类型或运算符会统一抛出 {@link UnsupportedFeature} 异常。
+ * 基于 Pratt 算法的表达式解析器(经典“运算符优先级”递归解析框架)。
+ *
+ * - 支持注册前缀和中缀解析器,灵活组合表达式语法。
+ * - 支持字面量、变量、函数调用、成员访问、对象创建、各类一元/二元/多元运算。
+ * - 可快速扩展新语法(只需注册新的 Parselet 即可)。
+ * - 出错时统一抛出 {@link UnsupportedFeature},便于调试和错误提示。
+ *
*
*/
public class PrattExpressionParser implements ExpressionParser {
/**
- * 前缀解析器注册表(按 Token 类型名或词素索引)。
- *
- * 用于存储所有支持的前缀表达式解析器,例如字面量、变量、分组、数组、一元运算等。
- * 支持通过 TokenType 的名称和特定词素(如 "(", "[")两种方式索引。
- *
+ * 前缀解析器注册表(通过 Token 类型名或词素作为索引)。
+ *
+ * - 如数字字面量、标识符、字符串、布尔值、new、分组、数组、一元运算等。
+ * - 支持同时用 TokenType 名称和具体词素(如 "("、"-")注册。
+ *
*/
private static final Map prefixes = new HashMap<>();
/**
- * 中缀解析器注册表(按运算符词素索引)。
- *
- * 用于存储所有支持的中缀表达式解析器,如二元运算、函数调用、下标、成员访问等。
- * 仅通过词素索引(如 "+", "-", "(", "[" 等)。
- *
+ * 中缀解析器注册表(通过运算符词素索引)。
+ *
+ * - 如 + - * / % 及比较、逻辑、函数调用、下标、成员访问等。
+ * - 仅词素索引(如 "+"、"-"、"("、".")
+ *
*/
private static final Map infixes = new HashMap<>();
static {
- // -------- 前缀解析器注册 --------
- // 注册数字字面量解析
- prefixes.put(TokenType.NUMBER_LITERAL.name(), new NumberLiteralParselet());
- // 注册标识符(变量名)解析
- prefixes.put(TokenType.IDENTIFIER.name(), new IdentifierParselet());
- // 注册字符串字面量解析
- prefixes.put(TokenType.STRING_LITERAL.name(), new StringLiteralParselet());
- // 注册布尔字面量解析
- prefixes.put(TokenType.BOOL_LITERAL.name(), new BoolLiteralParselet());
+ // ----------------- 前缀解析器注册 -----------------
+ // 各种字面量/标识符
+ prefixes.put(TokenType.NUMBER_LITERAL.name(), new NumberLiteralParselet());
+ prefixes.put(TokenType.IDENTIFIER.name(), new IdentifierParselet());
+ prefixes.put(TokenType.STRING_LITERAL.name(), new StringLiteralParselet());
+ prefixes.put(TokenType.BOOL_LITERAL.name(), new BoolLiteralParselet());
- // 支持括号分组、数组字面量
- prefixes.put(TokenType.LPAREN.name(), new GroupingParselet());
- prefixes.put(TokenType.LBRACKET.name(), new ArrayLiteralParselet());
- // 兼容直接以词素注册(如 '(' '[')
+ // 分组与数组字面量(两种索引方式)
+ prefixes.put(TokenType.LPAREN.name(), new GroupingParselet());
+ prefixes.put(TokenType.LBRACKET.name(), new ArrayLiteralParselet());
prefixes.put("(", new GroupingParselet());
prefixes.put("[", new ArrayLiteralParselet());
- // 一元前缀运算符(负号、逻辑非)
+ // 一元前缀运算符(如负号、逻辑非),同样用两种方式注册
prefixes.put(TokenType.MINUS.name(), new UnaryOperatorParselet());
prefixes.put(TokenType.NOT.name(), new UnaryOperatorParselet());
prefixes.put("-", new UnaryOperatorParselet());
prefixes.put("!", new UnaryOperatorParselet());
- // -------- 中缀解析器注册 --------
- // 注册常见二元运算符(加减乘除、取模)
+ // 对象创建 new TypeName(args...)
+ prefixes.put("new", new NewObjectParselet());
+
+ // ----------------- 中缀解析器注册 -----------------
+ // 常见二元算数运算符
infixes.put("+", new BinaryOperatorParselet(Precedence.SUM, true));
infixes.put("-", new BinaryOperatorParselet(Precedence.SUM, true));
infixes.put("*", new BinaryOperatorParselet(Precedence.PRODUCT, true));
@@ -79,25 +79,25 @@ public class PrattExpressionParser implements ExpressionParser {
infixes.put("<", new BinaryOperatorParselet(Precedence.COMPARISON, true));
infixes.put(">=", new BinaryOperatorParselet(Precedence.COMPARISON, true));
infixes.put("<=", new BinaryOperatorParselet(Precedence.COMPARISON, true));
- // 相等性
+ // 相等性判断
infixes.put("==", new BinaryOperatorParselet(Precedence.EQUALITY, true));
infixes.put("!=", new BinaryOperatorParselet(Precedence.EQUALITY, true));
- // 逻辑与或
+ // 逻辑运算
infixes.put("&&", new BinaryOperatorParselet(Precedence.AND, true));
- infixes.put("||", new BinaryOperatorParselet(Precedence.OR, true));
- // 调用、索引、成员访问
- infixes.put("(", new CallParselet());
- infixes.put("[", new IndexParselet());
- infixes.put(".", new MemberParselet());
+ infixes.put("||", new BinaryOperatorParselet(Precedence.OR, true));
+ // 函数调用、数组下标、成员访问
+ infixes.put("(", new CallParselet());
+ infixes.put("[", new IndexParselet());
+ infixes.put(".", new MemberParselet());
}
/**
- * 解析任意表达式的统一入口。
+ * 统一表达式解析入口,自动以最低优先级递归解析整个表达式。
*
- * 该方法将以最低优先级启动表达式递归解析,能够自动适配和处理多层嵌套或复杂组合表达式。
+ * 能解析嵌套、复合等所有合法表达式结构。
*
*
- * @param ctx 当前解析上下文对象(持有 token 流等信息)
+ * @param ctx 当前解析上下文对象(含 token 流等信息)
* @return 解析得到的表达式 AST 节点对象
*/
@Override
@@ -106,50 +106,42 @@ public class PrattExpressionParser implements ExpressionParser {
}
/**
- * 按指定优先级解析表达式(Pratt 算法核心)。
+ * Pratt 算法主递归循环:按给定优先级递归解析表达式。
*
- * 1. 先取当前 token,查找对应的前缀解析器进行初始解析,构建表达式左侧(如字面量、变量等)。
- * 2. 然后循环检测是否有更高优先级的中缀操作符,
- * 若有则递归处理右侧表达式并组合为新的表达式节点。
- *
- *
- * 未找到对应前缀或中缀解析器时会抛出 {@link UnsupportedFeature} 异常。
+ * 实现按优先级吸收中缀操作符(如连续算术、链式调用、组合表达式等)。
*
*
* @param ctx 解析上下文
- * @param prec 当前运算符优先级(用于控制递归层级)
- * @return 解析构建好的表达式节点
- * @throws UnsupportedFeature 遇到未注册的解析器时抛出
+ * @param prec 当前已绑定优先级
+ * @return 已解析的表达式节点
*/
ExpressionNode parseExpression(ParserContext ctx, Precedence prec) {
- // 取下一个 token 作为本轮前缀表达式起始
+ // 1) 消耗一个 token 作为前缀起点
Token token = ctx.getTokens().next();
- // 查找前缀解析器(先按类型名,再按词素)
- PrefixParselet prefix = prefixes.get(token.getType().name());
+ // 2) 查找前缀解析器(优先按词素,再按 TokenType)
+ PrefixParselet prefix = prefixes.get(token.getLexeme());
if (prefix == null) {
- prefix = prefixes.get(token.getLexeme());
+ prefix = prefixes.get(token.getType().name());
}
if (prefix == null) {
- // 未找到前缀解析器则报错
+ // 未注册前缀解析器,直接报错
throw new UnsupportedFeature(
- "没有为该 Token 类型注册前缀解析器: " + token.getType(),
+ "没有为该 Token 注册前缀解析器: " + token.getLexeme() + " / " + token.getType(),
token.getLine(),
token.getCol()
);
}
- // 执行前缀解析,获得左侧表达式
+ // 3) 前缀解析得到左侧表达式
ExpressionNode left = prefix.parse(ctx, token);
- // 不断尝试查找优先级更高的中缀运算符,递归处理表达式链
- while (!ctx.getTokens().isAtEnd()
- && prec.ordinal() < nextPrecedence(ctx)) {
- // 查看下一个 token 词素,查找中缀解析器
+ // 4) 主循环:不断吸收更高优先级的中缀操作,直到优先级不再提升
+ while (!ctx.getTokens().isAtEnd() && prec.ordinal() < nextPrecedence(ctx)) {
String lex = ctx.getTokens().peek().getLexeme();
InfixParselet infix = infixes.get(lex);
if (infix == null) {
- // 若未注册中缀解析器,则直接抛异常(常见于语法错误)
+ // nextPrecedence > prec 时一般已注册中缀解析器
Token t = ctx.getTokens().peek();
throw new UnsupportedFeature(
"没有为该运算符注册中缀解析器: '" + lex + "'",
@@ -157,21 +149,21 @@ public class PrattExpressionParser implements ExpressionParser {
t.getCol()
);
}
- // 使用中缀解析器处理表达式组合
+ // 递归组合更高优先级的中缀表达式
left = infix.parse(ctx, left);
}
- // 返回本层递归已解析的表达式节点
+ // 5) 返回本层解析完成的表达式节点
return left;
}
/**
- * 获取下一个 token 词素对应的中缀运算符优先级(Pratt 算法关键)。
+ * 获取下一个 token 对应的中缀运算符优先级(Pratt 算法关键)。
*
- * 用于决定当前是否需要递归处理更高优先级的中缀操作。
+ * 若无注册的中缀解析器,则返回 -1。
*
*
- * @param ctx 当前解析上下文
- * @return 下一个中缀运算符的优先级序号;若无注册解析器则返回 -1
+ * @param ctx 解析上下文
+ * @return 下一个运算符优先级序号(无则-1)
*/
private int nextPrecedence(ParserContext ctx) {
InfixParselet infix = infixes.get(ctx.getTokens().peek().getLexeme());