refactor: 重构表达式解析器文档和代码
- 更新类文档,使其更清晰地描述 Pratt 表达式解析器的功能和结构 - 优化方法注释,增加对核心解析逻辑的解释 - 调整代码格式,提高可读性 - 补充注释说明前缀和中缀解析器的注册方式及使用场景
This commit is contained in:
		
							parent
							
								
									17b078b6f3
								
							
						
					
					
						commit
						586ede1cf0
					
				| @ -13,62 +13,62 @@ import java.util.HashMap; | |||||||
| import java.util.Map; | import java.util.Map; | ||||||
| 
 | 
 | ||||||
| /** | /** | ||||||
|  * {@code PrattExpressionParser} 基于 Pratt 算法的表达式解析器实现。 |  * {@code PrattExpressionParser} | ||||||
|  * <p> |  * <p> | ||||||
|  * 该类通过前缀(PrefixParselet)和中缀(InfixParselet)解析器注册表, |  * 基于 Pratt 算法的表达式解析器(经典“运算符优先级”递归解析框架)。 | ||||||
|  * 支持灵活扩展的表达式语法,包括字面量、变量、函数调用、成员访问和各种运算符表达式。 |  * <ul> | ||||||
|  * </p> |  *   <li>支持注册前缀和中缀解析器,灵活组合表达式语法。</li> | ||||||
|  * <p> |  *   <li>支持字面量、变量、函数调用、成员访问、对象创建、各类一元/二元/多元运算。</li> | ||||||
|  * 运算符优先级通过枚举控制,结合递归解析实现高效的优先级处理和语法结构解析。 |  *   <li>可快速扩展新语法(只需注册新的 Parselet 即可)。</li> | ||||||
|  * 未注册的语法类型或运算符会统一抛出 {@link UnsupportedFeature} 异常。 |  *   <li>出错时统一抛出 {@link UnsupportedFeature},便于调试和错误提示。</li> | ||||||
|  |  * </ul> | ||||||
|  * </p> |  * </p> | ||||||
|  */ |  */ | ||||||
| public class PrattExpressionParser implements ExpressionParser { | public class PrattExpressionParser implements ExpressionParser { | ||||||
| 
 | 
 | ||||||
|     /** |     /** | ||||||
|      * 前缀解析器注册表(按 Token 类型名或词素索引)。 |      * 前缀解析器注册表(通过 Token 类型名或词素作为索引)。 | ||||||
|      * <p> |      * <ul> | ||||||
|      * 用于存储所有支持的前缀表达式解析器,例如字面量、变量、分组、数组、一元运算等。 |      *   <li>如数字字面量、标识符、字符串、布尔值、new、分组、数组、一元运算等。</li> | ||||||
|      * 支持通过 TokenType 的名称和特定词素(如 "(", "[")两种方式索引。 |      *   <li>支持同时用 TokenType 名称和具体词素(如 "("、"-")注册。</li> | ||||||
|      * </p> |      * </ul> | ||||||
|      */ |      */ | ||||||
|     private static final Map<String, PrefixParselet> prefixes = new HashMap<>(); |     private static final Map<String, PrefixParselet> prefixes = new HashMap<>(); | ||||||
| 
 | 
 | ||||||
|     /** |     /** | ||||||
|      * 中缀解析器注册表(按运算符词素索引)。 |      * 中缀解析器注册表(通过运算符词素索引)。 | ||||||
|      * <p> |      * <ul> | ||||||
|      * 用于存储所有支持的中缀表达式解析器,如二元运算、函数调用、下标、成员访问等。 |      *   <li>如 + - * / % 及比较、逻辑、函数调用、下标、成员访问等。</li> | ||||||
|      * 仅通过词素索引(如 "+", "-", "(", "[" 等)。 |      *   <li>仅词素索引(如 "+"、"-"、"("、".")</li> | ||||||
|      * </p> |      * </ul> | ||||||
|      */ |      */ | ||||||
|     private static final Map<String, InfixParselet> infixes = new HashMap<>(); |     private static final Map<String, InfixParselet> infixes = new HashMap<>(); | ||||||
| 
 | 
 | ||||||
|     static { |     static { | ||||||
|         // -------- 前缀解析器注册 -------- |         // ----------------- 前缀解析器注册 ----------------- | ||||||
|         // 注册数字字面量解析 |         // 各种字面量/标识符 | ||||||
|         prefixes.put(TokenType.NUMBER_LITERAL.name(), new NumberLiteralParselet()); |         prefixes.put(TokenType.NUMBER_LITERAL.name(), new NumberLiteralParselet()); | ||||||
|         // 注册标识符(变量名)解析 |  | ||||||
|         prefixes.put(TokenType.IDENTIFIER.name(),     new IdentifierParselet()); |         prefixes.put(TokenType.IDENTIFIER.name(),     new IdentifierParselet()); | ||||||
|         // 注册字符串字面量解析 |  | ||||||
|         prefixes.put(TokenType.STRING_LITERAL.name(), new StringLiteralParselet()); |         prefixes.put(TokenType.STRING_LITERAL.name(), new StringLiteralParselet()); | ||||||
|         // 注册布尔字面量解析 |  | ||||||
|         prefixes.put(TokenType.BOOL_LITERAL.name(),   new BoolLiteralParselet()); |         prefixes.put(TokenType.BOOL_LITERAL.name(),   new BoolLiteralParselet()); | ||||||
| 
 | 
 | ||||||
|         // 支持括号分组、数组字面量 |         // 分组与数组字面量(两种索引方式) | ||||||
|         prefixes.put(TokenType.LPAREN.name(),   new GroupingParselet()); |         prefixes.put(TokenType.LPAREN.name(),   new GroupingParselet()); | ||||||
|         prefixes.put(TokenType.LBRACKET.name(), new ArrayLiteralParselet()); |         prefixes.put(TokenType.LBRACKET.name(), new ArrayLiteralParselet()); | ||||||
|         // 兼容直接以词素注册(如 '(' '[') |  | ||||||
|         prefixes.put("(", new GroupingParselet()); |         prefixes.put("(", new GroupingParselet()); | ||||||
|         prefixes.put("[", new ArrayLiteralParselet()); |         prefixes.put("[", new ArrayLiteralParselet()); | ||||||
| 
 | 
 | ||||||
|         // 一元前缀运算符(负号、逻辑非) |         // 一元前缀运算符(如负号、逻辑非),同样用两种方式注册 | ||||||
|         prefixes.put(TokenType.MINUS.name(), new UnaryOperatorParselet()); |         prefixes.put(TokenType.MINUS.name(), new UnaryOperatorParselet()); | ||||||
|         prefixes.put(TokenType.NOT.name(),   new UnaryOperatorParselet()); |         prefixes.put(TokenType.NOT.name(),   new UnaryOperatorParselet()); | ||||||
|         prefixes.put("-", new UnaryOperatorParselet()); |         prefixes.put("-", 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.SUM,     true)); |         infixes.put("-",  new BinaryOperatorParselet(Precedence.SUM,     true)); | ||||||
|         infixes.put("*",  new BinaryOperatorParselet(Precedence.PRODUCT, 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.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.EQUALITY, true)); |         infixes.put("!=", new BinaryOperatorParselet(Precedence.EQUALITY, true)); | ||||||
|         // 逻辑与或 |         // 逻辑运算 | ||||||
|         infixes.put("&&", new BinaryOperatorParselet(Precedence.AND, true)); |         infixes.put("&&", new BinaryOperatorParselet(Precedence.AND, true)); | ||||||
|         infixes.put("||", new BinaryOperatorParselet(Precedence.OR,  true)); |         infixes.put("||", new BinaryOperatorParselet(Precedence.OR,  true)); | ||||||
|         // 调用、索引、成员访问 |         // 函数调用、数组下标、成员访问 | ||||||
|         infixes.put("(", new CallParselet()); |         infixes.put("(", new CallParselet()); | ||||||
|         infixes.put("[", new IndexParselet()); |         infixes.put("[", new IndexParselet()); | ||||||
|         infixes.put(".", new MemberParselet()); |         infixes.put(".", new MemberParselet()); | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     /** |     /** | ||||||
|      * 解析任意表达式的统一入口。 |      * 统一表达式解析入口,自动以最低优先级递归解析整个表达式。 | ||||||
|      * <p> |      * <p> | ||||||
|      * 该方法将以最低优先级启动表达式递归解析,能够自动适配和处理多层嵌套或复杂组合表达式。 |      * 能解析嵌套、复合等所有合法表达式结构。 | ||||||
|      * </p> |      * </p> | ||||||
|      * |      * | ||||||
|      * @param ctx 当前解析上下文对象(持有 token 流等信息) |      * @param ctx 当前解析上下文对象(含 token 流等信息) | ||||||
|      * @return 解析得到的表达式 AST 节点对象 |      * @return 解析得到的表达式 AST 节点对象 | ||||||
|      */ |      */ | ||||||
|     @Override |     @Override | ||||||
| @ -106,50 +106,42 @@ public class PrattExpressionParser implements ExpressionParser { | |||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     /** |     /** | ||||||
|      * 按指定优先级解析表达式(Pratt 算法核心)。 |      * Pratt 算法主递归循环:按给定优先级递归解析表达式。 | ||||||
|      * <p> |      * <p> | ||||||
|      * 1. 先取当前 token,查找对应的前缀解析器进行初始解析,构建表达式左侧(如字面量、变量等)。 |      * 实现按优先级吸收中缀操作符(如连续算术、链式调用、组合表达式等)。 | ||||||
|      * 2. 然后循环检测是否有更高优先级的中缀操作符, |  | ||||||
|      *    若有则递归处理右侧表达式并组合为新的表达式节点。 |  | ||||||
|      * </p> |  | ||||||
|      * <p> |  | ||||||
|      * 未找到对应前缀或中缀解析器时会抛出 {@link UnsupportedFeature} 异常。 |  | ||||||
|      * </p> |      * </p> | ||||||
|      * |      * | ||||||
|      * @param ctx  解析上下文 |      * @param ctx  解析上下文 | ||||||
|      * @param prec 当前运算符优先级(用于控制递归层级) |      * @param prec 当前已绑定优先级 | ||||||
|      * @return 解析构建好的表达式节点 |      * @return 已解析的表达式节点 | ||||||
|      * @throws UnsupportedFeature 遇到未注册的解析器时抛出 |  | ||||||
|      */ |      */ | ||||||
|     ExpressionNode parseExpression(ParserContext ctx, Precedence prec) { |     ExpressionNode parseExpression(ParserContext ctx, Precedence prec) { | ||||||
|         // 取下一个 token 作为本轮前缀表达式起始 |         // 1) 消耗一个 token 作为前缀起点 | ||||||
|         Token token = ctx.getTokens().next(); |         Token token = ctx.getTokens().next(); | ||||||
| 
 | 
 | ||||||
|         // 查找前缀解析器(先按类型名,再按词素) |         // 2) 查找前缀解析器(优先按词素,再按 TokenType) | ||||||
|         PrefixParselet prefix = prefixes.get(token.getType().name()); |         PrefixParselet prefix = prefixes.get(token.getLexeme()); | ||||||
|         if (prefix == null) { |         if (prefix == null) { | ||||||
|             prefix = prefixes.get(token.getLexeme()); |             prefix = prefixes.get(token.getType().name()); | ||||||
|         } |         } | ||||||
|         if (prefix == null) { |         if (prefix == null) { | ||||||
|             // 未找到前缀解析器则报错 |             // 未注册前缀解析器,直接报错 | ||||||
|             throw new UnsupportedFeature( |             throw new UnsupportedFeature( | ||||||
|                     "没有为该 Token 类型注册前缀解析器: " + token.getType(), |                     "没有为该 Token 注册前缀解析器: " + token.getLexeme() + " / " + token.getType(), | ||||||
|                     token.getLine(), |                     token.getLine(), | ||||||
|                     token.getCol() |                     token.getCol() | ||||||
|             ); |             ); | ||||||
|         } |         } | ||||||
| 
 | 
 | ||||||
|         // 执行前缀解析,获得左侧表达式 |         // 3) 前缀解析得到左侧表达式 | ||||||
|         ExpressionNode left = prefix.parse(ctx, token); |         ExpressionNode left = prefix.parse(ctx, token); | ||||||
| 
 | 
 | ||||||
|         // 不断尝试查找优先级更高的中缀运算符,递归处理表达式链 |         // 4) 主循环:不断吸收更高优先级的中缀操作,直到优先级不再提升 | ||||||
|         while (!ctx.getTokens().isAtEnd() |         while (!ctx.getTokens().isAtEnd() && prec.ordinal() < nextPrecedence(ctx)) { | ||||||
|                 && prec.ordinal() < nextPrecedence(ctx)) { |  | ||||||
|             // 查看下一个 token 词素,查找中缀解析器 |  | ||||||
|             String lex = ctx.getTokens().peek().getLexeme(); |             String lex = ctx.getTokens().peek().getLexeme(); | ||||||
|             InfixParselet infix = infixes.get(lex); |             InfixParselet infix = infixes.get(lex); | ||||||
|             if (infix == null) { |             if (infix == null) { | ||||||
|                 // 若未注册中缀解析器,则直接抛异常(常见于语法错误) |                 // nextPrecedence > prec 时一般已注册中缀解析器 | ||||||
|                 Token t = ctx.getTokens().peek(); |                 Token t = ctx.getTokens().peek(); | ||||||
|                 throw new UnsupportedFeature( |                 throw new UnsupportedFeature( | ||||||
|                         "没有为该运算符注册中缀解析器: '" + lex + "'", |                         "没有为该运算符注册中缀解析器: '" + lex + "'", | ||||||
| @ -157,21 +149,21 @@ public class PrattExpressionParser implements ExpressionParser { | |||||||
|                         t.getCol() |                         t.getCol() | ||||||
|                 ); |                 ); | ||||||
|             } |             } | ||||||
|             // 使用中缀解析器处理表达式组合 |             // 递归组合更高优先级的中缀表达式 | ||||||
|             left = infix.parse(ctx, left); |             left = infix.parse(ctx, left); | ||||||
|         } |         } | ||||||
|         // 返回本层递归已解析的表达式节点 |         // 5) 返回本层解析完成的表达式节点 | ||||||
|         return left; |         return left; | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     /** |     /** | ||||||
|      * 获取下一个 token 词素对应的中缀运算符优先级(Pratt 算法关键)。 |      * 获取下一个 token 对应的中缀运算符优先级(Pratt 算法关键)。 | ||||||
|      * <p> |      * <p> | ||||||
|      * 用于决定当前是否需要递归处理更高优先级的中缀操作。 |      * 若无注册的中缀解析器,则返回 -1。 | ||||||
|      * </p> |      * </p> | ||||||
|      * |      * | ||||||
|      * @param ctx 当前解析上下文 |      * @param ctx 解析上下文 | ||||||
|      * @return 下一个中缀运算符的优先级序号;若无注册解析器则返回 -1 |      * @return 下一个运算符优先级序号(无则-1) | ||||||
|      */ |      */ | ||||||
|     private int nextPrecedence(ParserContext ctx) { |     private int nextPrecedence(ParserContext ctx) { | ||||||
|         InfixParselet infix = infixes.get(ctx.getTokens().peek().getLexeme()); |         InfixParselet infix = infixes.get(ctx.getTokens().peek().getLexeme()); | ||||||
|  | |||||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user