fix(ast): 解析器支持括号内行首运算符跨行续接

and/or 与 binop/比较链循环此前在检查运算符前不跳 Nl:
A and B 换行后再 and C(ThreadingTest check 多行 and 链)停在 Nl 上
循环退出,上层把续行运算符当非法 token 报 SyntaxError。每轮循环先
_skip_nl 再查运算符(对齐 vast 实现),支持 (a\n + b)、(a < b\n < c)
等行首运算符续行。
This commit is contained in:
TermiNexus committed 2026-08-19 06:52:20 +08:00
1 parent 197a7f3314
commit e83c06041a
1 file changed
+65 -36
+65 -36
View File
@@ -502,20 +502,23 @@ def _parse_or_test(ps: Parser | t.CPtr) -> AST | t.CPtr:
"""or_test: and_test ('or' and_test)*"""
left: AST | t.CPtr = _parse_and_test(ps)
if left is None: return None
# 括号内行首 or((A\n or B)):or 前的 NL 在此跳过,否则 or_test 返回后
# 上层 _expect_op(RPar) 遇到 or 报"意外的 token 'or'"
# (CallGenMixin.vp L7918 跨模块调用检查根因;ClassGenMixin L1988 等
# 括号内换行行首 or 同理,按解析顺序未暴露)。
_skip_nl(ps)
if _cur_kw(ps) == Keyword.Or:
values: list[AST | t.CPtr] | t.CPtr = list[AST | t.CPtr](ps.pool, 8)
values.append(left)
while _cur_kw(ps) == Keyword.Or:
_advance(ps)
_skip_nl(ps) # 括号内跨行: a or\n b
right: AST | t.CPtr = _parse_and_test(ps)
if right is None: break
values.append(right)
values: list[AST | t.CPtr] | t.CPtr = None
while True:
# 括号内行首 or((A\n or B))与行尾 or(A or\n B)都支持:
# 每轮先 _skip_nl 再检查 or,否则 A or B\n or C 在 B 后停在 Nl 上
# 循环退出,上层把续行 or 当非法 token(对齐 and_test / vast 实现)。
_skip_nl(ps)
if _cur_kw(ps) != Keyword.Or:
break
if values is None:
values = list[AST | t.CPtr](ps.pool, 8)
values.append(left)
_advance(ps)
_skip_nl(ps) # 括号内跨行: a or\n b
right: AST | t.CPtr = _parse_and_test(ps)
if right is None: break
values.append(right)
if values is not None:
node: AST | t.CPtr = BoolOp(ps.pool, OpKind.Or, values)
_set_pos(node, left.lineno, left.col_offset, 0, 0)
return node
@@ -526,17 +529,24 @@ def _parse_and_test(ps: Parser | t.CPtr) -> AST | t.CPtr:
"""and_test: not_test ('and' not_test)*"""
left: AST | t.CPtr = _parse_not_test(ps)
if left is None: return None
# 括号内行首 and((A\n and B)):同 or_test 修复(or 前跳 NL)
_skip_nl(ps)
if _cur_kw(ps) == Keyword.And:
values: list[AST | t.CPtr] | t.CPtr = list[AST | t.CPtr](ps.pool, 8)
values.append(left)
while _cur_kw(ps) == Keyword.And:
_advance(ps)
_skip_nl(ps) # 括号内跨行: a and\n b
right: AST | t.CPtr = _parse_not_test(ps)
if right is None: break
values.append(right)
values: list[AST | t.CPtr] | t.CPtr = None
while True:
# 括号内行首 and((A\n and B))与行尾 and(A and\n B)都支持:
# 每轮先 _skip_nl 再检查 and,否则 A and B\n and C 在 B 后停在
# Nl 上循环退出,上层把续行 and 当非法 token
# (ThreadingTest check 多行 and 链解析失败根因)。
_skip_nl(ps)
if _cur_kw(ps) != Keyword.And:
break
if values is None:
values = list[AST | t.CPtr](ps.pool, 8)
values.append(left)
_advance(ps)
_skip_nl(ps) # 括号内跨行: a and\n b
right: AST | t.CPtr = _parse_not_test(ps)
if right is None: break
values.append(right)
if values is not None:
node: AST | t.CPtr = BoolOp(ps.pool, OpKind.And, values)
_set_pos(node, left.lineno, left.col_offset, 0, 0)
return node
@@ -565,6 +575,7 @@ def _parse_comparison(ps: Parser | t.CPtr) -> AST | t.CPtr:
left: AST | t.CPtr = _parse_bitor(ps)
if left is None: return None
_skip_nl(ps) # 括号内行首比较运算符: (a\n < b)
op_id: t.CInt = _match_comp_op(ps)
if op_id == OpKind.NoneOp:
return left
@@ -580,6 +591,7 @@ def _parse_comparison(ps: Parser | t.CPtr) -> AST | t.CPtr:
_set_pos(first_cmp, left.lineno, left.col_offset, 0, 0)
# Check chained comparison
_skip_nl(ps) # 括号内行首链式比较: (a < b\n < c)
next_op: t.CInt = _match_comp_op(ps)
if next_op == OpKind.NoneOp:
return first_cmp
@@ -600,6 +612,7 @@ def _parse_comparison(ps: Parser | t.CPtr) -> AST | t.CPtr:
_set_pos(cmp, cur_left.lineno, cur_left.col_offset, 0, 0)
values.append(cmp)
cur_left = cur_right
_skip_nl(ps) # 括号内行首链式比较: (a < b\n < c)
next_op = _match_comp_op(ps)
node: AST | t.CPtr = BoolOp(ps.pool, OpKind.And, values)
_set_pos(node, left.lineno, left.col_offset, 0, 0)
@@ -650,9 +663,12 @@ def _advance_comp_op(ps: Parser | t.CPtr):
def _parse_bitor(ps: Parser | t.CPtr) -> AST | t.CPtr:
left: AST | t.CPtr = _parse_bitxor(ps)
if left is None: return None
while _match_op(ps, TokOp.VBar):
while True:
_skip_nl(ps) # 括号内行首 '|': (a\n | b)
if _match_op(ps, TokOp.VBar) == 0:
break
_advance(ps)
_skip_nl(ps)
_skip_nl(ps) # 括号内跨行: a |\n b
right: AST | t.CPtr = _parse_bitxor(ps)
node: AST | t.CPtr = BinOp(ps.pool, left, OpKind.BitOr, right)
_set_pos(node, left.lineno, left.col_offset, 0, 0)
@@ -663,9 +679,12 @@ def _parse_bitor(ps: Parser | t.CPtr) -> AST | t.CPtr:
def _parse_bitxor(ps: Parser | t.CPtr) -> AST | t.CPtr:
left: AST | t.CPtr = _parse_bitand(ps)
if left is None: return None
while _match_op(ps, TokOp.Caret):
while True:
_skip_nl(ps) # 括号内行首 '^': (a\n ^ b)
if _match_op(ps, TokOp.Caret) == 0:
break
_advance(ps)
_skip_nl(ps)
_skip_nl(ps) # 括号内跨行: a ^\n b
right: AST | t.CPtr = _parse_bitand(ps)
node: AST | t.CPtr = BinOp(ps.pool, left, OpKind.BitXor, right)
_set_pos(node, left.lineno, left.col_offset, 0, 0)
@@ -676,9 +695,12 @@ def _parse_bitxor(ps: Parser | t.CPtr) -> AST | t.CPtr:
def _parse_bitand(ps: Parser | t.CPtr) -> AST | t.CPtr:
left: AST | t.CPtr = _parse_shift(ps)
if left is None: return None
while _match_op(ps, TokOp.Amp):
while True:
_skip_nl(ps) # 括号内行首 '&': (a\n & b)
if _match_op(ps, TokOp.Amp) == 0:
break
_advance(ps)
_skip_nl(ps)
_skip_nl(ps) # 括号内跨行: a &\n b
right: AST | t.CPtr = _parse_shift(ps)
node: AST | t.CPtr = BinOp(ps.pool, left, OpKind.BitAnd, right)
_set_pos(node, left.lineno, left.col_offset, 0, 0)
@@ -689,10 +711,13 @@ def _parse_bitand(ps: Parser | t.CPtr) -> AST | t.CPtr:
def _parse_shift(ps: Parser | t.CPtr) -> AST | t.CPtr:
left: AST | t.CPtr = _parse_arith(ps)
if left is None: return None
while _match_op(ps, TokOp.LtLt) or _match_op(ps, TokOp.GtGt):
while True:
_skip_nl(ps) # 括号内行首 '<<'/'>>': (a\n << b)
if _match_op(ps, TokOp.LtLt) == 0 and _match_op(ps, TokOp.GtGt) == 0:
break
op_token: t.CInt = _cur_op(ps)
_advance(ps)
_skip_nl(ps)
_skip_nl(ps) # 括号内跨行: a <<\n b
right: AST | t.CPtr = _parse_arith(ps)
ast_op: t.CInt = OpKind.LShift
if op_token == TokOp.GtGt:
@@ -706,10 +731,13 @@ def _parse_shift(ps: Parser | t.CPtr) -> AST | t.CPtr:
def _parse_arith(ps: Parser | t.CPtr) -> AST | t.CPtr:
left: AST | t.CPtr = _parse_term(ps)
if left is None: return None
while _match_op(ps, TokOp.Plus) or _match_op(ps, TokOp.Minus):
while True:
_skip_nl(ps) # 括号内行首 '+'/'-': (a\n + b)
if _match_op(ps, TokOp.Plus) == 0 and _match_op(ps, TokOp.Minus) == 0:
break
op_token: t.CInt = _cur_op(ps)
_advance(ps)
_skip_nl(ps)
_skip_nl(ps) # 括号内跨行: a +\n b
right: AST | t.CPtr = _parse_term(ps)
ast_op: t.CInt = OpKind.Add
if op_token == TokOp.Minus:
@@ -724,6 +752,7 @@ def _parse_term(ps: Parser | t.CPtr) -> AST | t.CPtr:
left: AST | t.CPtr = _parse_factor(ps)
if left is None: return None
while True:
_skip_nl(ps) # 括号内行首 '*'/'/'/'//'/'%'/'@': (a\n * b)
op_token: t.CInt = 0
if _match_op(ps, TokOp.Star): op_token = TokOp.Star
elif _match_op(ps, TokOp.Slash): op_token = TokOp.Slash
@@ -732,7 +761,7 @@ def _parse_term(ps: Parser | t.CPtr) -> AST | t.CPtr:
elif _match_op(ps, TokOp.At): op_token = TokOp.At
if op_token == 0: break
_advance(ps)
_skip_nl(ps)
_skip_nl(ps) # 括号内跨行: a *\n b
right: AST | t.CPtr = _parse_factor(ps)
ast_op: t.CInt = _binop_from_op(op_token)
node: AST | t.CPtr = BinOp(ps.pool, left, ast_op, right)