fix(ast): 修正实参位前导 * 的优先级,*p + n 解析为 (*p) + n
规范 rule 5 允许把 c.Deref(x) 写作 *x,但语法分析器在实参位置对前导 * 取整条表达式作为操作数:*buf + *length 被解析成 *(buf + *length),*length + data_len 成 *(length + data_len),多解引用一层。表现为 zlib decompressobj 全部失效(decompress 返回 0 字节、eof 恒 0、unused_data 取不到),requests/download 同类语句亦受影响。 改为:实参位 *expr 只按 _parse_factor 取紧绑定操作数,经新增 Parser.starred_seed 挂起,由 _parse_factor 顶部接手,再交由上层续接二元运算。*p[0](即 *(p[0]))、*p**2(即 *(p**2))等结合性保持原 C/Python 语义。 验证:全量 77/77 顺序回归一次通过;ZlibTest 内部断言由 74 passed / 6 failed 变为 79 passed / 0 failed。
This commit is contained in:
1 parent
e80284a6d5
commit
3e1ed0992b
1 file changed
+22
-3
+22
-3
@@ -55,6 +55,8 @@ class Parser:
|
||||
pool: rawptr[memhub.MemManager] # memory pool
|
||||
error_count: int # error count
|
||||
pending_decorators: rawptr[list[rawptr[AST]]] # pending decorators list
|
||||
# 实参位前导 `*` 的已解析左操作数挂起槽(见 _parse_call / _parse_power)。
|
||||
starred_seed: rawptr[AST]
|
||||
# `static` 修饰符(软关键字)挂起标记:语句起始消费 `static`(后随变量声明
|
||||
# `Name :`)后置 1,由紧随的 `_parse_type_ann` 在类型联合最左插入 t.CStatic
|
||||
# 标记(`static x: int = 0` → `x: t.CStatic | int = 0`),插入后立即清零,
|
||||
@@ -95,6 +97,7 @@ def _parser_init(ps: rawptr[Parser], tokens: rawptr[Token],
|
||||
ps.pool = pool
|
||||
ps.error_count = 0
|
||||
ps.pending_decorators = None
|
||||
ps.starred_seed = None
|
||||
ps.pending_static = 0
|
||||
ps.in_class_body = 0
|
||||
ps.step_count = 0
|
||||
@@ -1640,6 +1643,13 @@ def _parse_term(ps: rawptr[Parser]) -> rawptr[AST]:
|
||||
|
||||
def _parse_factor(ps: rawptr[Parser]) -> rawptr[AST]:
|
||||
"""factor: '+' factor | '-' factor | '~' factor | '*' factor | '&' factor | power"""
|
||||
# 实参位前导 `*` 已由 _parse_call 解析出紧绑定的 Starred 左操作数,直接作为本层
|
||||
# 因子返回(必须先于下面的一元判定,否则其后的二元 `+`/`-` 会被误判为一元)。
|
||||
# 上层各层随即以该左操作数续接二元运算:`*p + n` ⇒ `(*p) + n`,而非 `*(p + n)`。
|
||||
if ps.starred_seed is not None:
|
||||
seed: rawptr[AST] = ps.starred_seed
|
||||
ps.starred_seed = None
|
||||
return seed
|
||||
if (_match_op(ps, TokOp.Plus) or _match_op(ps, TokOp.Minus) or _match_op(ps, TokOp.Tilde)
|
||||
or _match_op(ps, TokOp.Star) or _match_op(ps, TokOp.Amp)):
|
||||
op_token: int = _cur_op(ps)
|
||||
@@ -1755,10 +1765,19 @@ def _parse_call(ps: rawptr[Parser], func: rawptr[AST]) -> rawptr[AST]:
|
||||
elif _match_op(ps, TokOp.Star):
|
||||
_advance(ps)
|
||||
_skip_nls_and_newlines(ps)
|
||||
# 只按一元因子取下界(保持 `*p[0]` = `*(p[0])` 的 C 结合),再挂到
|
||||
# starred_seed 上,交由 _parse_expr 续接其后二元运算:`*p + n` ⇒
|
||||
# `(*p) + n`。若整体用 _parse_expr,前导 `*` 会吞掉整条 `p + n`
|
||||
# 而错解为 `*(p + n)`(zlib append_bytes 的 out 参数不回写根因)。
|
||||
seed: rawptr[AST] = _parse_factor(ps)
|
||||
if seed is None:
|
||||
ps.error_count += 1
|
||||
break
|
||||
star: rawptr[AST] = Starred(ps.pool, seed, ASTCtx.Load)
|
||||
_set_pos(star, seed.lineno, seed.col_offset, 0, 0)
|
||||
ps.starred_seed = star
|
||||
val = _parse_expr(ps)
|
||||
star: rawptr[AST] = Starred(ps.pool, val, ASTCtx.Load)
|
||||
_set_pos(star, val.lineno, val.col_offset, 0, 0)
|
||||
args.append(star)
|
||||
args.append(val)
|
||||
# keyword = expr
|
||||
elif _cur_type(ps) == TokenType.Name:
|
||||
if ps.cur.next is not None and ps.cur.next.type == TokenType.Op and ps.cur.next.op_subtype == TokOp.Equal:
|
||||
|
||||
Reference in new issue
Block a user