Files
TransPyC/includes/ast/parser.py
2026-07-26 20:32:26 +08:00

2353 lines
84 KiB
Python
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
import t, c
from stdint import *
import memhub
import string
from .tokens import (Token, TokenType, Keyword, TokOp)
from .base import (
AST, ASTCtx, ASTKind, OpKind, ASTFlag,
_set_pos, _binop_from_op, _augop_from_op, _cmpop_from_op, _copy_str, _inherit_pos,
_init_ast,
CONST_INT, CONST_FLOAT, CONST_STR, CONST_BOOL, CONST_NONE,
FLAG_IS_ASYNC,
)
from .exprs import (
BoolOp, BinOp, UnaryOp, Lambda, IfExp, Dict, Set,
ListComp, SetComp, DictComp, GeneratorExp,
Await, Yield, YieldFrom, FormattedValue, JoinedStr,
Constant, NamedExpr, Attribute, Subscript, Starred, Name,
List, Tuple, Slice, Call, Compare, OpNode,
)
from .stmts import (
Module, Expression, Interactive, FunctionType,
FunctionDef, ClassDef, Return, Delete, Assign, AugAssign, AnnAssign,
For, While, If, With, Raise, Try, Assert, Global, Nonlocal,
Pass, Break, Continue, Expr, Import, ImportFrom, Match,
)
from .astaux import (
ExceptHandler, Arguments, Arg, Keyword as KwNode, Alias, WithItem, Comprehension,
)
from .match import (
MatchCase, MatchValue, MatchSingleton, MatchSequence,
MatchMapping, MatchClass, MatchStar, MatchAs, MatchOr,
)
from .lexer import Lexer, _lexer_init, tokenize, new_lexer
# ============================================================
# Parser 状态
# ============================================================
class Parser:
tokens: Token | t.CPtr # token 链表头
cur: Token | t.CPtr # 当前 token
pool: memhub.MemManager | t.CPtr # 内存池
error_count: t.CInt # 错误计数
pending_decorators: list[AST | t.CPtr] | t.CPtr # 待处理的装饰器 list
def __new__(self, pool: memhub.MemManager | t.CPtr):
ptr: Parser | t.CPtr = pool.alloc(Parser.__sizeof__())
if ptr:
string.memset(ptr, 0, Parser.__sizeof__())
return ptr
def new_parser(pool: memhub.MemManager | t.CPtr) -> Parser | t.CPtr:
"""从 pool 分配并返回 Parser跨模块安全包装"""
return Parser(pool)
def _parser_init(ps: Parser | t.CPtr, tokens: Token | t.CPtr,
pool: memhub.MemManager | t.CPtr):
if ps is None: return
ps.tokens = tokens
ps.cur = tokens
ps.pool = pool
ps.error_count = 0
ps.pending_decorators = None
# ============================================================
# Token 导航辅助
# ============================================================
def _advance(ps: Parser | t.CPtr):
"""前进到下一个 token"""
if ps.cur is None: return
if ps.cur.next is not None:
ps.cur = ps.cur.next
def _cur_type(ps: Parser | t.CPtr) -> t.CInt:
if ps.cur is None: return TokenType.EndMarker
return ps.cur.type
def _cur_op(ps: Parser | t.CPtr) -> t.CInt:
if ps.cur is None: return 0
return ps.cur.op_subtype
def _cur_kw(ps: Parser | t.CPtr) -> t.CInt:
if ps.cur is None: return 0
return ps.cur.kw_subtype
def _cur_str(ps: Parser | t.CPtr) -> str:
if ps.cur is None: return None
return ps.cur.str_val
def _cur_lineno(ps: Parser | t.CPtr) -> t.CInt:
if ps.cur is None: return 0
return ps.cur.lineno
def _cur_col(ps: Parser | t.CPtr) -> t.CInt:
if ps.cur is None: return 0
return ps.cur.col_offset
def _skip_nl(ps: Parser | t.CPtr):
"""跳过 TokenType.Nl token"""
while ps.cur is not None and ps.cur.type == TokenType.Nl:
ps.cur = ps.cur.next
def _skip_nls_and_newlines(ps: Parser | t.CPtr):
"""跳过 TokenType.Nl 和 TokenType.NewLine用于续行"""
while ps.cur is not None:
if ps.cur.type == TokenType.Nl or ps.cur.type == TokenType.NewLine:
ps.cur = ps.cur.next
else:
break
def _expect_type(ps: Parser | t.CPtr, ttype: t.CInt) -> t.CInt:
"""期望当前 token 是指定类型,是则前进并返回 1否则返回 0"""
if _cur_type(ps) == ttype:
_advance(ps)
return 1
ps.error_count += 1
return 0
def _expect_op(ps: Parser | t.CPtr, op_id: t.CInt) -> t.CInt:
"""期望当前是某个运算符"""
if _cur_type(ps) == TokenType.Op and _cur_op(ps) == op_id:
_advance(ps)
return 1
ps.error_count += 1
return 0
def _match_op(ps: Parser | t.CPtr, op_id: t.CInt) -> t.CInt:
"""检查但不消耗"""
if _cur_type(ps) == TokenType.Op and _cur_op(ps) == op_id:
return 1
return 0
def _accept_op(ps: Parser | t.CPtr, op_id: t.CInt) -> t.CInt:
"""如果匹配则消耗并返回 1"""
if _match_op(ps, op_id):
_advance(ps)
return 1
return 0
# ============================================================
# 常量构造辅助(新 APIConstant 直接接受 lineno/col
# ============================================================
def _new_const_int(pool: memhub.MemManager | t.CPtr, val: t.CInt64T,
lineno: t.CInt, col: t.CInt) -> AST | t.CPtr:
return Constant(pool, CONST_INT, val, 0.0, None, lineno, col)
def _new_const_float(pool: memhub.MemManager | t.CPtr, val: t.CDouble,
lineno: t.CInt, col: t.CInt) -> AST | t.CPtr:
return Constant(pool, CONST_FLOAT, 0, val, None, lineno, col)
def _new_const_str(pool: memhub.MemManager | t.CPtr, val: str,
lineno: t.CInt, col: t.CInt) -> AST | t.CPtr:
return Constant(pool, CONST_STR, 0, 0.0, val, lineno, col)
def _new_const_bool(pool: memhub.MemManager | t.CPtr, val: t.CInt,
lineno: t.CInt, col: t.CInt) -> AST | t.CPtr:
return Constant(pool, CONST_BOOL, val, 0.0, None, lineno, col)
def _new_const_none(pool: memhub.MemManager | t.CPtr,
lineno: t.CInt, col: t.CInt) -> AST | t.CPtr:
return Constant(pool, CONST_NONE, 0, 0.0, None, lineno, col)
# ============================================================
# ctx 辅助(递归修正赋值目标的 ctx
# ============================================================
def _set_ctx(node: AST | t.CPtr, ctx: t.CInt):
"""设置节点的 ctx 字段(按 kind 转型后赋值)"""
if node is None: return
k: t.CInt = node.kind()
if k == ASTKind.Name:
nm: Name | t.CPtr = (Name | t.CPtr)(node)
nm.ctx = ctx
elif k == ASTKind.Attribute:
at: Attribute | t.CPtr = (Attribute | t.CPtr)(node)
at.ctx = ctx
elif k == ASTKind.Subscript:
sb: Subscript | t.CPtr = (Subscript | t.CPtr)(node)
sb.ctx = ctx
elif k == ASTKind.Tuple:
tp: Tuple | t.CPtr = (Tuple | t.CPtr)(node)
tp.ctx = ctx
elif k == ASTKind.List:
ls: List | t.CPtr = (List | t.CPtr)(node)
ls.ctx = ctx
elif k == ASTKind.Starred:
st: Starred | t.CPtr = (Starred | t.CPtr)(node)
st.ctx = ctx
def _fix_store_ctx(node: AST | t.CPtr):
"""递归修正赋值目标的 ctx 为 Store"""
if node is None: return
_set_ctx(node, ASTCtx.Store)
k: t.CInt = node.kind()
if k == ASTKind.Tuple or k == ASTKind.List:
tp_elts: list[AST | t.CPtr] | t.CPtr = None
if k == ASTKind.Tuple:
tp: Tuple | t.CPtr = (Tuple | t.CPtr)(node)
tp_elts = tp.elts
else:
ls: List | t.CPtr = (List | t.CPtr)(node)
tp_elts = ls.elts
if tp_elts is not None:
n: t.CSizeT = tp_elts.__len__()
i: t.CSizeT = 0
while i < n:
cur: AST | t.CPtr = tp_elts.get(i)
_fix_store_ctx(cur)
i += 1
elif k == ASTKind.Starred:
st2: Starred | t.CPtr = (Starred | t.CPtr)(node)
_fix_store_ctx(st2.value)
# ============================================================
# 表达式解析
# ============================================================
def _parse_expr(ps: Parser | t.CPtr) -> AST | t.CPtr:
"""expr: yield_expr | namedexpr_test"""
if _cur_kw(ps) == Keyword.Yield:
return _parse_yield(ps)
return _parse_namedexpr(ps)
def _parse_namedexpr(ps: Parser | t.CPtr) -> AST | t.CPtr:
"""namedexpr_test: test [':=' test]"""
left: AST | t.CPtr = _parse_test(ps)
if left is None: return None
if _match_op(ps, TokOp.ColonEq):
_advance(ps)
right: AST | t.CPtr = _parse_test(ps)
node: AST | t.CPtr = NamedExpr(ps.pool, left, right)
_set_pos(node, left.lineno, left.col_offset, 0, 0)
return node
return left
def _parse_test(ps: Parser | t.CPtr) -> AST | t.CPtr:
"""test: or_test ['if' or_test 'else' test] | lambdef"""
if _cur_kw(ps) == Keyword.Lambda:
return _parse_lambda(ps)
left: AST | t.CPtr = _parse_or_test(ps)
if left is None: return None
if _cur_kw(ps) == Keyword.If:
_advance(ps)
test: AST | t.CPtr = _parse_or_test(ps)
if _cur_kw(ps) == Keyword.Else:
_advance(ps)
orelse: AST | t.CPtr = _parse_test(ps)
node: AST | t.CPtr = IfExp(ps.pool, test, left, orelse)
_set_pos(node, left.lineno, left.col_offset, 0, 0)
return node
ps.error_count += 1
return left
return left
def _parse_lambda(ps: Parser | t.CPtr) -> AST | t.CPtr:
"""lambdef: 'lambda' [varargslist] ':' test"""
lineno: t.CInt = _cur_lineno(ps)
col: t.CInt = _cur_col(ps)
_advance(ps) # lambda
args: AST | t.CPtr = None
if not _match_op(ps, TokOp.Colon):
args = _parse_varargslist(ps, 1)
if not _expect_op(ps, TokOp.Colon):
return None
body: AST | t.CPtr = _parse_test(ps)
node: AST | t.CPtr = Lambda(ps.pool, args, body)
_set_pos(node, lineno, col, 0, 0)
return node
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
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)
right: AST | t.CPtr = _parse_and_test(ps)
if right is None: break
values.append(right)
node: AST | t.CPtr = BoolOp(ps.pool, OpKind.Or, values)
_set_pos(node, left.lineno, left.col_offset, 0, 0)
return node
return left
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
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)
right: AST | t.CPtr = _parse_not_test(ps)
if right is None: break
values.append(right)
node: AST | t.CPtr = BoolOp(ps.pool, OpKind.And, values)
_set_pos(node, left.lineno, left.col_offset, 0, 0)
return node
return left
def _parse_not_test(ps: Parser | t.CPtr) -> AST | t.CPtr:
"""not_test: 'not' not_test | comparison"""
if _cur_kw(ps) == Keyword.Not:
lineno: t.CInt = _cur_lineno(ps)
col: t.CInt = _cur_col(ps)
_advance(ps)
operand: AST | t.CPtr = _parse_not_test(ps)
node: AST | t.CPtr = UnaryOp(ps.pool, OpKind.Not, operand)
_set_pos(node, lineno, col, 0, 0)
return node
return _parse_comparison(ps)
def _parse_comparison(ps: Parser | t.CPtr) -> AST | t.CPtr:
"""comparison: bitor_expr (comp_op bitor_expr)*
单比较设计a < b → Compare(left=a, op=Lt, right=b)
链式比较 a < b < c → BoolOp(And, [Compare(a,<,b), Compare(b,<,c)])
"""
left: AST | t.CPtr = _parse_bitor(ps)
if left is None: return None
op_id: t.CInt = _match_comp_op(ps)
if op_id == OpKind.NoneOp:
return left
_advance_comp_op(ps)
right: AST | t.CPtr = _parse_bitor(ps)
# 单比较:构造 ops 和 comparators list
ops: list[t.CInt] | t.CPtr = list[t.CInt](ps.pool, 8)
ops.append(op_id)
comparators: list[AST | t.CPtr] | t.CPtr = list[AST | t.CPtr](ps.pool, 8)
comparators.append(right)
first_cmp: AST | t.CPtr = Compare(ps.pool, left, ops, comparators)
_set_pos(first_cmp, left.lineno, left.col_offset, 0, 0)
# 检查链式比较
next_op: t.CInt = _match_comp_op(ps)
if next_op == OpKind.NoneOp:
return first_cmp
# 链式a < b < c → BoolOp(And, [Compare(a,<,b), Compare(b,<,c), ...])
values: list[AST | t.CPtr] | t.CPtr = list[AST | t.CPtr](ps.pool, 8)
values.append(first_cmp)
cur_left: AST | t.CPtr = right
while next_op != OpKind.NoneOp:
_advance_comp_op(ps)
cur_right: AST | t.CPtr = _parse_bitor(ps)
ops2: list[t.CInt] | t.CPtr = list[t.CInt](ps.pool, 8)
ops2.append(next_op)
comparators2: list[AST | t.CPtr] | t.CPtr = list[AST | t.CPtr](ps.pool, 8)
comparators2.append(cur_right)
cmp: AST | t.CPtr = Compare(ps.pool, cur_left, ops2, comparators2)
_set_pos(cmp, cur_left.lineno, cur_left.col_offset, 0, 0)
values.append(cmp)
cur_left = cur_right
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)
return node
def _match_comp_op(ps: Parser | t.CPtr) -> t.CInt:
"""检查当前是否是比较运算符,返回 OP_* 或 OpKind.NoneOp"""
if _cur_type(ps) == TokenType.Op:
op_id: t.CInt = _cur_op(ps)
if op_id == TokOp.EqEq: return OpKind.Eq
if op_id == TokOp.ExclaimEq: return OpKind.Ne
if op_id == TokOp.Less: return OpKind.Lt
if op_id == TokOp.LessEq: return OpKind.Le
if op_id == TokOp.Greater: return OpKind.Gt
if op_id == TokOp.GreaterEq: return OpKind.Ge
if _cur_kw(ps) == Keyword.Is:
if ps.cur.next is not None and ps.cur.next.type == TokenType.Name and ps.cur.next.kw_subtype == Keyword.Not:
return OpKind.IsNot
return OpKind.Is
if _cur_kw(ps) == Keyword.Not:
if ps.cur.next is not None and ps.cur.next.kw_subtype == Keyword.In:
return OpKind.NotIn
if _cur_kw(ps) == Keyword.In:
return OpKind.In
return OpKind.NoneOp
def _advance_comp_op(ps: Parser | t.CPtr):
"""消耗比较运算符 token"""
if _cur_type(ps) == TokenType.Op:
_advance(ps)
return
if _cur_kw(ps) == Keyword.Is:
_advance(ps)
if _cur_kw(ps) == Keyword.Not:
_advance(ps)
return
if _cur_kw(ps) == Keyword.Not:
_advance(ps)
if _cur_kw(ps) == Keyword.In:
_advance(ps)
return
if _cur_kw(ps) == Keyword.In:
_advance(ps)
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):
_advance(ps)
_skip_nl(ps)
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)
left = node
return left
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):
_advance(ps)
_skip_nl(ps)
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)
left = node
return left
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):
_advance(ps)
_skip_nl(ps)
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)
left = node
return left
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):
op_token: t.CInt = _cur_op(ps)
_advance(ps)
_skip_nl(ps)
right: AST | t.CPtr = _parse_arith(ps)
ast_op: t.CInt = OpKind.LShift
if op_token == TokOp.GtGt:
ast_op = OpKind.RShift
node: AST | t.CPtr = BinOp(ps.pool, left, ast_op, right)
_set_pos(node, left.lineno, left.col_offset, 0, 0)
left = node
return left
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):
op_token: t.CInt = _cur_op(ps)
_advance(ps)
_skip_nl(ps)
right: AST | t.CPtr = _parse_term(ps)
ast_op: t.CInt = OpKind.Add
if op_token == TokOp.Minus:
ast_op = OpKind.Sub
node: AST | t.CPtr = BinOp(ps.pool, left, ast_op, right)
_set_pos(node, left.lineno, left.col_offset, 0, 0)
left = node
return left
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:
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
elif _match_op(ps, TokOp.DSlash): op_token = TokOp.DSlash
elif _match_op(ps, TokOp.Percent): op_token = TokOp.Percent
elif _match_op(ps, TokOp.At): op_token = TokOp.At
if op_token == 0: break
_advance(ps)
_skip_nl(ps)
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)
_set_pos(node, left.lineno, left.col_offset, 0, 0)
left = node
return left
def _parse_factor(ps: Parser | t.CPtr) -> AST | t.CPtr:
"""factor: '+' factor | '-' factor | '~' factor | power"""
if _match_op(ps, TokOp.Plus) or _match_op(ps, TokOp.Minus) or _match_op(ps, TokOp.Tilde):
op_token: t.CInt = _cur_op(ps)
lineno: t.CInt = _cur_lineno(ps)
col: t.CInt = _cur_col(ps)
_advance(ps)
operand: AST | t.CPtr = _parse_factor(ps)
ast_op: t.CInt = OpKind.UAdd
if op_token == TokOp.Minus:
ast_op = OpKind.USub
elif op_token == TokOp.Tilde:
ast_op = OpKind.Invert
node: AST | t.CPtr = UnaryOp(ps.pool, ast_op, operand)
_set_pos(node, lineno, col, 0, 0)
return node
return _parse_power(ps)
def _parse_power(ps: Parser | t.CPtr) -> AST | t.CPtr:
"""power: await_expr ['**' factor] | atom_expr ['**' factor]"""
base: AST | t.CPtr = _parse_atom_expr(ps)
if base is None: return None
if _match_op(ps, TokOp.StarStar):
_advance(ps)
exp: AST | t.CPtr = _parse_factor(ps)
node: AST | t.CPtr = BinOp(ps.pool, base, OpKind.Pow, exp)
_set_pos(node, base.lineno, base.col_offset, 0, 0)
return node
return base
def _parse_atom_expr(ps: Parser | t.CPtr) -> AST | t.CPtr:
"""atom_expr: ['await'] atom trailer*"""
if _cur_kw(ps) == Keyword.Await:
lineno: t.CInt = _cur_lineno(ps)
col: t.CInt = _cur_col(ps)
_advance(ps)
inner: AST | t.CPtr = _parse_atom_expr(ps)
node: AST | t.CPtr = Await(ps.pool, inner)
_set_pos(node, lineno, col, 0, 0)
return node
base: AST | t.CPtr = _parse_atom(ps)
if base is None: return None
# trailer 循环
while True:
if _match_op(ps, TokOp.Dot):
_advance(ps)
if _cur_type(ps) != TokenType.Name:
ps.error_count += 1
break
attr_name: str = _cur_str(ps)
attr_lineno: t.CInt = _cur_lineno(ps)
attr_col: t.CInt = _cur_col(ps)
_advance(ps)
ctx: t.CInt = ASTCtx.Load
if _match_op(ps, TokOp.Equal):
ctx = ASTCtx.Store
elif _match_op(ps, TokOp.PlusEq) or _match_op(ps, TokOp.MinusEq):
ctx = ASTCtx.Store
base = Attribute(ps.pool, base, attr_name, ctx)
_set_pos(base, attr_lineno, attr_col, 0, 0)
elif _match_op(ps, TokOp.LPar):
base = _parse_call(ps, base)
elif _match_op(ps, TokOp.Lsqb):
base = _parse_subscript(ps, base)
else:
break
return base
def _parse_call(ps: Parser | t.CPtr, func: AST | t.CPtr) -> AST | t.CPtr:
"""解析函数调用 trailer。args 和 keywords 均为 list。"""
lineno: t.CInt = _cur_lineno(ps)
col: t.CInt = _cur_col(ps)
_advance(ps) # (
_skip_nls_and_newlines(ps) # 跳过 ( 后的换行
args: list[AST | t.CPtr] | t.CPtr = list[AST | t.CPtr](ps.pool, 8)
keywords: list[AST | t.CPtr] | t.CPtr = list[AST | t.CPtr](ps.pool, 8)
if not _match_op(ps, TokOp.RPar):
while True:
# **expr
if _match_op(ps, TokOp.StarStar):
_advance(ps)
_skip_nls_and_newlines(ps)
val: AST | t.CPtr = _parse_expr(ps)
kw: AST | t.CPtr = KwNode(ps.pool, None, val)
_set_pos(kw, _cur_lineno(ps), _cur_col(ps), 0, 0)
keywords.append(kw)
# *expr
elif _match_op(ps, TokOp.Star):
_advance(ps)
_skip_nls_and_newlines(ps)
val = _parse_expr(ps)
star: AST | t.CPtr = Starred(ps.pool, val, ASTCtx.Load)
_set_pos(star, val.lineno, val.col_offset, 0, 0)
args.append(star)
# 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:
kw_name: str = _cur_str(ps)
kw_lineno: t.CInt = _cur_lineno(ps)
kw_col: t.CInt = _cur_col(ps)
_advance(ps) # name
_advance(ps) # =
_skip_nls_and_newlines(ps)
val = _parse_expr(ps)
kw = KwNode(ps.pool, kw_name, val)
_set_pos(kw, kw_lineno, kw_col, 0, 0)
keywords.append(kw)
else:
val = _parse_expr(ps)
args.append(val)
else:
val = _parse_expr(ps)
args.append(val)
_skip_nls_and_newlines(ps) # 跳过参数后的换行
if not _accept_op(ps, TokOp.Comma):
break
_skip_nls_and_newlines(ps) # 跳过逗号后的换行
_skip_nls_and_newlines(ps) # 跳过 ) 前的换行
_expect_op(ps, TokOp.RPar)
node: AST | t.CPtr = Call(ps.pool, func, args, keywords)
_set_pos(node, lineno, col, 0, 0)
return node
def _parse_subscript(ps: Parser | t.CPtr, value: AST | t.CPtr) -> AST | t.CPtr:
"""解析下标 trailer"""
lineno: t.CInt = _cur_lineno(ps)
col: t.CInt = _cur_col(ps)
_advance(ps) # [
_skip_nl(ps)
slice_node: AST | t.CPtr = _parse_slice(ps)
_expect_op(ps, TokOp.Rsqb)
ctx: t.CInt = ASTCtx.Load
if _match_op(ps, TokOp.Equal):
ctx = ASTCtx.Store
node: AST | t.CPtr = Subscript(ps.pool, value, slice_node, ctx)
_set_pos(node, lineno, col, 0, 0)
return node
def _parse_slice(ps: Parser | t.CPtr) -> AST | t.CPtr:
"""解析 slice[expr] ':' [expr] [':' [expr]] | expr | expr, expr, ..."""
lower: AST | t.CPtr = None
if not _match_op(ps, TokOp.Colon):
lower = _parse_expr(ps)
# 检查是否有逗号(元组语法: a[x, y, ...]
if _match_op(ps, TokOp.Comma):
elts: list[AST | t.CPtr] | t.CPtr = list[AST | t.CPtr](ps.pool, 8)
if lower is not None:
elts.append(lower)
while _accept_op(ps, TokOp.Comma):
if _match_op(ps, TokOp.Rsqb):
break # 尾随逗号
elem: AST | t.CPtr = _parse_expr(ps)
if elem is not None:
elts.append(elem)
lineno: t.CInt = 0
if lower is not None:
lineno = lower.lineno
node: AST | t.CPtr = Tuple(ps.pool, elts, ASTCtx.Load)
_set_pos(node, lineno, 0, 0, 0)
return node
if _match_op(ps, TokOp.Colon):
_advance(ps)
upper: AST | t.CPtr = None
if not _match_op(ps, TokOp.Colon) and not _match_op(ps, TokOp.Rsqb):
upper = _parse_expr(ps)
step: AST | t.CPtr = None
if _accept_op(ps, TokOp.Colon):
if not _match_op(ps, TokOp.Rsqb):
step = _parse_expr(ps)
lineno: t.CInt = 0
if lower is not None:
lineno = lower.lineno
elif upper is not None:
lineno = upper.lineno
else:
lineno = _cur_lineno(ps)
node: AST | t.CPtr = Slice(ps.pool, lower, upper, step)
_set_pos(node, lineno, 0, 0, 0)
return node
return lower
def _parse_atom(ps: Parser | t.CPtr) -> AST | t.CPtr:
"""atom: 最小表达式单元"""
ttype: t.CInt = _cur_type(ps)
lineno: t.CInt = _cur_lineno(ps)
col: t.CInt = _cur_col(ps)
if ttype == TokenType.Name:
kw: t.CInt = _cur_kw(ps)
if kw == Keyword.True_:
_advance(ps)
return _new_const_bool(ps.pool, 1, lineno, col)
if kw == Keyword.False_:
_advance(ps)
return _new_const_bool(ps.pool, 0, lineno, col)
if kw == Keyword.None_:
_advance(ps)
return _new_const_none(ps.pool, lineno, col)
name_str: str = _cur_str(ps)
_advance(ps)
node: AST | t.CPtr = Name(ps.pool, name_str, ASTCtx.Load)
_set_pos(node, lineno, col, 0, 0)
return node
if ttype == TokenType.Number:
tok: Token | t.CPtr = ps.cur
_advance(ps)
if tok.is_float:
return _new_const_float(ps.pool, tok.float_val, lineno, col)
return _new_const_int(ps.pool, tok.int_val, lineno, col)
if ttype == TokenType.String:
sval: str = _cur_str(ps)
is_fstr: t.CInt = ps.cur.kw_subtype & 4
_advance(ps)
if is_fstr:
return _parse_fstring(ps, sval, lineno, col)
return _new_const_str(ps.pool, sval, lineno, col)
if _match_op(ps, TokOp.Ellipsis):
_advance(ps)
return _new_const_none(ps.pool, lineno, col)
if _match_op(ps, TokOp.LPar):
return _parse_paren(ps)
if _match_op(ps, TokOp.Lsqb):
return _parse_list(ps)
if _match_op(ps, TokOp.LBrace):
return _parse_brace(ps)
ps.error_count += 1
return None
def _parse_paren(ps: Parser | t.CPtr) -> AST | t.CPtr:
"""括号表达式: () | (expr) | (expr,) | (expr, ...) | (yield)"""
lineno: t.CInt = _cur_lineno(ps)
col: t.CInt = _cur_col(ps)
_advance(ps) # (
if _match_op(ps, TokOp.RPar):
_advance(ps)
# 空元组
empty: list[AST | t.CPtr] | t.CPtr = list[AST | t.CPtr](ps.pool, 8)
node: AST | t.CPtr = Tuple(ps.pool, empty, ASTCtx.Load)
_set_pos(node, lineno, col, 0, 0)
return node
# yield
if _cur_kw(ps) == Keyword.Yield:
y: AST | t.CPtr = _parse_yield(ps)
_expect_op(ps, TokOp.RPar)
return y
first: AST | t.CPtr = _parse_expr(ps)
if _match_op(ps, TokOp.Comma):
# 元组
elts: list[AST | t.CPtr] | t.CPtr = list[AST | t.CPtr](ps.pool, 8)
elts.append(first)
while _accept_op(ps, TokOp.Comma):
if _match_op(ps, TokOp.RPar):
break
e: AST | t.CPtr = _parse_expr(ps)
elts.append(e)
_expect_op(ps, TokOp.RPar)
node = Tuple(ps.pool, elts, ASTCtx.Load)
_set_pos(node, lineno, col, 0, 0)
return node
_expect_op(ps, TokOp.RPar)
# 检查推导式
if _cur_kw(ps) == Keyword.For:
return _parse_comprehension(ps, first, ASTKind.GeneratorExp, lineno, col)
return first
def _parse_list(ps: Parser | t.CPtr) -> AST | t.CPtr:
"""列表: [] | [expr_list] | [expr_list comp_for]"""
lineno: t.CInt = _cur_lineno(ps)
col: t.CInt = _cur_col(ps)
_advance(ps) # [
if _match_op(ps, TokOp.Rsqb):
_advance(ps)
empty: list[AST | t.CPtr] | t.CPtr = list[AST | t.CPtr](ps.pool, 8)
node: AST | t.CPtr = List(ps.pool, empty, ASTCtx.Load)
_set_pos(node, lineno, col, 0, 0)
return node
first: AST | t.CPtr = _parse_expr(ps)
if _cur_kw(ps) == Keyword.For:
node = _parse_comprehension(ps, first, ASTKind.ListComp, lineno, col)
_expect_op(ps, TokOp.Rsqb)
return node
elts: list[AST | t.CPtr] | t.CPtr = list[AST | t.CPtr](ps.pool, 8)
elts.append(first)
while _accept_op(ps, TokOp.Comma):
_skip_nl(ps)
if _match_op(ps, TokOp.Rsqb):
break
e: AST | t.CPtr = _parse_expr(ps)
elts.append(e)
_expect_op(ps, TokOp.Rsqb)
node = List(ps.pool, elts, ASTCtx.Load)
_set_pos(node, lineno, col, 0, 0)
return node
def _parse_brace(ps: Parser | t.CPtr) -> AST | t.CPtr:
"""花括号: {} | {set_items} | {dict_items} | {comp_for}"""
lineno: t.CInt = _cur_lineno(ps)
col: t.CInt = _cur_col(ps)
_advance(ps) # {
_skip_nl(ps)
if _match_op(ps, TokOp.RBrace):
_advance(ps)
empty_k: list[AST | t.CPtr] | t.CPtr = list[AST | t.CPtr](ps.pool, 8)
empty_v: list[AST | t.CPtr] | t.CPtr = list[AST | t.CPtr](ps.pool, 8)
node: AST | t.CPtr = Dict(ps.pool, empty_k, empty_v)
_set_pos(node, lineno, col, 0, 0)
return node
first: AST | t.CPtr = _parse_expr(ps)
# dict: key: value
if _match_op(ps, TokOp.Colon):
_advance(ps)
val: AST | t.CPtr = _parse_expr(ps)
if _cur_kw(ps) == Keyword.For:
# dict comprehension
gens: list[AST | t.CPtr] | t.CPtr = _parse_comp_for(ps)
node = DictComp(ps.pool, first, val, gens)
_set_pos(node, lineno, col, 0, 0)
_expect_op(ps, TokOp.RBrace)
return node
keys: list[AST | t.CPtr] | t.CPtr = list[AST | t.CPtr](ps.pool, 8)
values: list[AST | t.CPtr] | t.CPtr = list[AST | t.CPtr](ps.pool, 8)
keys.append(first)
values.append(val)
while _accept_op(ps, TokOp.Comma):
_skip_nl(ps)
if _match_op(ps, TokOp.RBrace):
break
k: AST | t.CPtr = _parse_expr(ps)
_expect_op(ps, TokOp.Colon)
v: AST | t.CPtr = _parse_expr(ps)
keys.append(k)
values.append(v)
_expect_op(ps, TokOp.RBrace)
node = Dict(ps.pool, keys, values)
_set_pos(node, lineno, col, 0, 0)
return node
# set
if _cur_kw(ps) == Keyword.For:
node = _parse_comprehension(ps, first, ASTKind.SetComp, lineno, col)
_expect_op(ps, TokOp.RBrace)
return node
elts: list[AST | t.CPtr] | t.CPtr = list[AST | t.CPtr](ps.pool, 8)
elts.append(first)
while _accept_op(ps, TokOp.Comma):
if _match_op(ps, TokOp.RBrace):
break
e: AST | t.CPtr = _parse_expr(ps)
elts.append(e)
_expect_op(ps, TokOp.RBrace)
node = Set(ps.pool, elts)
_set_pos(node, lineno, col, 0, 0)
return node
# ============================================================
# f-string 解析
# ============================================================
def _parse_sub_expr(ps: Parser | t.CPtr, src: str) -> AST | t.CPtr:
"""创建子 lexer + parser 解析表达式(用于 f-string 内的 {expr}"""
pool: memhub.MemManager | t.CPtr = ps.pool
lx: Lexer | t.CPtr = new_lexer(pool)
if lx is None: return None
_lexer_init(lx, src, pool)
tokens: Token | t.CPtr = tokenize(lx)
if tokens is None: return None
sub_ps: Parser | t.CPtr = new_parser(pool)
if sub_ps is None: return None
_parser_init(sub_ps, tokens, pool)
return _parse_test(sub_ps)
def _parse_fstring(ps: Parser | t.CPtr, content: str,
lineno: t.CInt, col: t.CInt) -> AST | t.CPtr:
"""解析 f-string 内容,构建 JoinedStr 或 FormattedValue。"""
pool: memhub.MemManager | t.CPtr = ps.pool
values: list[AST | t.CPtr] | t.CPtr = list[AST | t.CPtr](pool, 8)
i: t.CSizeT = 0
content_len: t.CSizeT = string.strlen(content)
# 字面量文本缓冲区
lit_buf: str = pool.alloc(content_len + 1)
if lit_buf is None: return None
lit_pos: t.CSizeT = 0
while i < content_len:
ch: t.CChar = content[i]
if ch == '{':
# {{ → 字面量 {
if i + 1 < content_len and content[i + 1] == '{':
lit_buf[lit_pos] = '{'
lit_pos += 1
i += 2
continue
# 刷新字面量缓冲区
if lit_pos > 0:
lit_buf[lit_pos] = '\0'
lit_node: AST | t.CPtr = _new_const_str(pool, lit_buf, lineno, col)
values.append(lit_node)
lit_buf = pool.alloc(content_len + 1)
if lit_buf is None: break
lit_pos = 0
# 查找匹配的 },同时跟踪嵌套 {} 和字符串字面量
j: t.CSizeT = i + 1
depth: t.CInt = 1
conv_pos: t.CSizeT = 0
fmt_pos: t.CSizeT = 0
while j < content_len and depth > 0:
c: t.CChar = content[j]
if c == '{':
depth += 1
elif c == '}':
depth -= 1
if depth == 0:
break
elif depth == 1:
if c == '!' and conv_pos == 0 and fmt_pos == 0:
if j + 1 < content_len:
nc: t.CChar = content[j + 1]
if nc == 's' or nc == 'r' or nc == 'a':
if j + 2 < content_len:
after: t.CChar = content[j + 2]
if after == ':' or after == '}':
conv_pos = j
elif j + 2 == content_len:
conv_pos = j
elif c == ':' and fmt_pos == 0:
fmt_pos = j
j += 1
if depth != 0:
ps.error_count += 1
break
# 提取表达式文本
expr_end: t.CSizeT = j
if conv_pos > 0:
expr_end = conv_pos
elif fmt_pos > 0:
expr_end = fmt_pos
expr_len: t.CSizeT = expr_end - i - 1
if expr_len == 0:
ps.error_count += 1
i = j + 1
continue
expr_buf: str = pool.alloc(expr_len + 1)
if expr_buf is None: break
string.memcpy(expr_buf, content + i + 1, expr_len)
expr_buf[expr_len] = '\0'
# 解析表达式
expr_node: AST | t.CPtr = _parse_sub_expr(ps, expr_buf)
# 转换符
conversion: t.CInt = -1
if conv_pos > 0:
conv_char: t.CChar = content[conv_pos + 1]
if conv_char == 's':
conversion = 115
elif conv_char == 'r':
conversion = 114
elif conv_char == 'a':
conversion = 97
# 格式说明符
format_spec: AST | t.CPtr = None
if fmt_pos > 0:
spec_len: t.CSizeT = j - fmt_pos - 1
if spec_len > 0:
spec_buf: str = pool.alloc(spec_len + 1)
if spec_buf is not None:
string.memcpy(spec_buf, content + fmt_pos + 1, spec_len)
spec_buf[spec_len] = '\0'
format_spec = _new_const_str(pool, spec_buf, lineno, col)
fv: AST | t.CPtr = FormattedValue(pool, expr_node, conversion, format_spec)
_set_pos(fv, lineno, col, 0, 0)
values.append(fv)
i = j + 1
elif ch == '}':
if i + 1 < content_len and content[i + 1] == '}':
lit_buf[lit_pos] = '}'
lit_pos += 1
i += 2
else:
ps.error_count += 1
i += 1
else:
lit_buf[lit_pos] = ch
lit_pos += 1
i += 1
# 刷新剩余字面量
if lit_pos > 0:
lit_buf[lit_pos] = '\0'
lit_node2: AST | t.CPtr = _new_const_str(pool, lit_buf, lineno, col)
values.append(lit_node2)
# 如果没有内容,返回空字符串
if values.__len__() == 0:
return _new_const_str(pool, "", lineno, col)
# 如果只有一个 FormattedValue 且无字面量,返回 FormattedValue
if values.__len__() == 1:
first: AST | t.CPtr = values.get(0)
if first.kind() == ASTKind.FormattedValue:
return first
node: AST | t.CPtr = JoinedStr(pool, values)
_set_pos(node, lineno, col, 0, 0)
return node
def _parse_comprehension(ps: Parser | t.CPtr, elt: AST | t.CPtr,
vtype: t.CInt, lineno: t.CInt, col: t.CInt) -> AST | t.CPtr:
"""通用推导式elt comp_for"""
gens: list[AST | t.CPtr] | t.CPtr = _parse_comp_for(ps)
node: AST | t.CPtr = None
if vtype == ASTKind.ListComp:
node = ListComp(ps.pool, elt, gens)
elif vtype == ASTKind.SetComp:
node = SetComp(ps.pool, elt, gens)
else:
node = GeneratorExp(ps.pool, elt, gens)
_set_pos(node, lineno, col, 0, 0)
return node
def _parse_comp_for(ps: Parser | t.CPtr) -> list[AST | t.CPtr] | t.CPtr:
"""comp_for: ['async'] 'for' exprlist 'in' or_test [comp_if] (comp_for)*
返回 generators list。"""
gens: list[AST | t.CPtr] | t.CPtr = list[AST | t.CPtr](ps.pool, 8)
while True:
is_async: t.CInt = 0
if _cur_kw(ps) == Keyword.Async:
is_async = 1
_advance(ps)
if _cur_kw(ps) != Keyword.For:
break
lineno: t.CInt = _cur_lineno(ps)
col: t.CInt = _cur_col(ps)
_advance(ps)
target: AST | t.CPtr = _parse_target_list(ps)
if _cur_kw(ps) != Keyword.In:
ps.error_count += 1
break
_advance(ps)
iter_: AST | t.CPtr = _parse_or_test(ps)
# 收集 ifs 到 list
ifs: list[AST | t.CPtr] | t.CPtr = list[AST | t.CPtr](ps.pool, 8)
while _cur_kw(ps) == Keyword.If:
_advance(ps)
cond: AST | t.CPtr = _parse_or_test(ps)
ifs.append(cond)
comp: AST | t.CPtr = Comprehension(ps.pool, target, iter_, ifs, is_async)
_set_pos(comp, lineno, col, 0, 0)
gens.append(comp)
return gens
def _parse_target_list(ps: Parser | t.CPtr) -> AST | t.CPtr:
"""解析赋值目标列表(用逗号分隔)"""
first: AST | t.CPtr = _parse_target(ps)
if _match_op(ps, TokOp.Comma):
elts: list[AST | t.CPtr] | t.CPtr = list[AST | t.CPtr](ps.pool, 8)
elts.append(first)
while _accept_op(ps, TokOp.Comma):
if _cur_kw(ps) == Keyword.In:
break
e: AST | t.CPtr = _parse_target(ps)
elts.append(e)
node: AST | t.CPtr = Tuple(ps.pool, elts, ASTCtx.Store)
_set_pos(node, first.lineno, first.col_offset, 0, 0)
return node
return first
def _parse_target(ps: Parser | t.CPtr) -> AST | t.CPtr:
"""解析单个赋值目标"""
if _match_op(ps, TokOp.Star):
_advance(ps)
inner: AST | t.CPtr = _parse_atom_expr(ps)
node: AST | t.CPtr = Starred(ps.pool, inner, ASTCtx.Store)
_set_pos(node, inner.lineno, inner.col_offset, 0, 0)
return node
if _match_op(ps, TokOp.LPar):
_advance(ps)
_skip_nl(ps)
t_: AST | t.CPtr = _parse_target_list(ps)
_skip_nl(ps)
_expect_op(ps, TokOp.RPar)
return t_
if _match_op(ps, TokOp.Lsqb):
_advance(ps)
_skip_nl(ps)
first: AST | t.CPtr = _parse_target(ps)
elts: list[AST | t.CPtr] | t.CPtr = list[AST | t.CPtr](ps.pool, 8)
elts.append(first)
while _accept_op(ps, TokOp.Comma):
_skip_nl(ps)
if _match_op(ps, TokOp.Rsqb):
break
e: AST | t.CPtr = _parse_target(ps)
elts.append(e)
_expect_op(ps, TokOp.Rsqb)
node = List(ps.pool, elts, ASTCtx.Store)
_set_pos(node, first.lineno, first.col_offset, 0, 0)
return node
# 普通 atom_expr但 ctx=Store
node = _parse_atom_expr(ps)
if node is not None:
_set_ctx(node, ASTCtx.Store)
return node
def _parse_yield(ps: Parser | t.CPtr) -> AST | t.CPtr:
lineno: t.CInt = _cur_lineno(ps)
col: t.CInt = _cur_col(ps)
_advance(ps) # yield
if _cur_kw(ps) == Keyword.From:
_advance(ps)
val: AST | t.CPtr = _parse_expr(ps)
node: AST | t.CPtr = YieldFrom(ps.pool, val)
_set_pos(node, lineno, col, 0, 0)
return node
val2: AST | t.CPtr = None
if not _match_op(ps, TokOp.RPar) and _cur_type(ps) != TokenType.NewLine and _cur_type(ps) != TokenType.EndMarker:
if _cur_type(ps) == TokenType.Op and (_cur_op(ps) == TokOp.Comma or _cur_op(ps) == TokOp.Rsqb):
pass
else:
val2 = _parse_test(ps)
node = Yield(ps.pool, val2)
_set_pos(node, lineno, col, 0, 0)
return node
def _parse_varargslist(ps: Parser | t.CPtr, is_lambda: t.CInt = 0) -> AST | t.CPtr:
"""函数参数列表解析lambda 和 def 共用)
新 APIargs/defaults/kw_defaults 为 listvararg/kwarg 为字段。"""
args: list[AST | t.CPtr] | t.CPtr = list[AST | t.CPtr](ps.pool, 8)
vararg: AST | t.CPtr = None
kwarg: AST | t.CPtr = None
defaults: list[AST | t.CPtr] | t.CPtr = list[AST | t.CPtr](ps.pool, 8)
kw_defaults: list[AST | t.CPtr] | t.CPtr = list[AST | t.CPtr](ps.pool, 8)
while True:
_skip_nl(ps)
if _match_op(ps, TokOp.RPar):
break
# *args 或 * (kwonly 分隔)
if _match_op(ps, TokOp.Star):
_advance(ps)
_skip_nl(ps)
if _cur_type(ps) == TokenType.Name:
vararg = _parse_arg_def(ps, is_lambda)
if not _accept_op(ps, TokOp.Comma):
break
_skip_nl(ps)
continue
# **kwargs
if _match_op(ps, TokOp.StarStar):
_advance(ps)
_skip_nl(ps)
kwarg = _parse_arg_def(ps, is_lambda)
if not _accept_op(ps, TokOp.Comma):
break
_skip_nl(ps)
continue
# / 分隔符posonly 结束)— 新 API 中 posonly/regular 不区分
if _match_op(ps, TokOp.Slash):
_advance(ps)
if not _accept_op(ps, TokOp.Comma):
break
_skip_nl(ps)
continue
# 普通参数
a: AST | t.CPtr = _parse_arg_def(ps, is_lambda)
if a is None: break
args.append(a)
if _match_op(ps, TokOp.Equal):
_advance(ps)
d: AST | t.CPtr = _parse_test(ps)
defaults.append(d)
if not _accept_op(ps, TokOp.Comma):
break
_skip_nl(ps)
return Arguments(ps.pool, args, vararg, kwarg, defaults, kw_defaults)
def _parse_arg_def(ps: Parser | t.CPtr, is_lambda: t.CInt = 0) -> AST | t.CPtr:
"""arg: TokenType.Name [':' annotation]"""
if _cur_type(ps) != TokenType.Name:
ps.error_count += 1
return None
name_str: str = _cur_str(ps)
lineno: t.CInt = _cur_lineno(ps)
col: t.CInt = _cur_col(ps)
_advance(ps)
annotation: AST | t.CPtr = None
if is_lambda == 0:
if _accept_op(ps, TokOp.Colon):
annotation = _parse_test(ps)
result: AST | t.CPtr = Arg(ps.pool, name_str, annotation)
_set_pos(result, lineno, col, 0, 0)
return result
# ============================================================
# 语句解析(设计:直接 append 到 parent不返回 list
# ============================================================
def _parse_suite_into_children(ps: Parser | t.CPtr, parent: AST | t.CPtr):
"""解析语句块,直接 append 为 parent 的子节点。
suite: TokenType.NewLine TokenType.Indent stmt+ TokenType.Dedent | simple_stmt"""
_skip_nl(ps)
if _cur_type(ps) == TokenType.NewLine:
_advance(ps)
_skip_nl(ps)
if _cur_type(ps) != TokenType.Indent:
ps.error_count += 1
return
_advance(ps)
sidx: t.CInt = 0
while _cur_type(ps) != TokenType.Dedent and _cur_type(ps) != TokenType.EndMarker:
sidx += 1
# 装饰器(类体内方法也可以有装饰器)
if _match_op(ps, TokOp.At):
ps.pending_decorators = _parse_decorators(ps)
before_cur: Token | t.CPtr = ps.cur
_parse_statement_into(ps, parent)
# 防御:若解析未前进 token强制前进以避免死循环
if ps.cur is before_cur:
ps.error_count += 1
_advance(ps)
_skip_nl(ps)
_expect_type(ps, TokenType.Dedent)
return
# 单行 suite
_parse_simple_stmt_into(ps, parent)
def _parse_statement_into(ps: Parser | t.CPtr, parent: AST | t.CPtr):
"""解析一条语句复合或简单append 到 parent。"""
ttype: t.CInt = _cur_type(ps)
kw: t.CInt = _cur_kw(ps)
if ttype == TokenType.Name:
if kw == Keyword.If or kw == Keyword.While or kw == Keyword.For or kw == Keyword.Try:
stmt: AST | t.CPtr = _parse_compound(ps)
if stmt is not None:
parent.append(stmt)
return
if kw == Keyword.With or kw == Keyword.Def or kw == Keyword.Class:
stmt = _parse_compound(ps)
if stmt is not None:
parent.append(stmt)
return
if kw == Keyword.Match or kw == Keyword.Async:
stmt = _parse_compound(ps)
if stmt is not None:
parent.append(stmt)
return
_parse_simple_stmt_into(ps, parent)
def _parse_simple_stmt_into(ps: Parser | t.CPtr, parent: AST | t.CPtr):
"""simple_stmt: small_stmt (';' small_stmt)* [';'] TokenType.NewLine"""
first: AST | t.CPtr = _parse_small_stmt(ps)
if first is not None:
parent.append(first)
while _accept_op(ps, TokOp.Semi):
if _cur_type(ps) == TokenType.NewLine or _cur_type(ps) == TokenType.EndMarker:
break
s: AST | t.CPtr = _parse_small_stmt(ps)
if s is None: break
parent.append(s)
_skip_nl(ps)
if _cur_type(ps) == TokenType.NewLine:
_advance(ps)
def _parse_small_stmt(ps: Parser | t.CPtr) -> AST | t.CPtr:
"""small_stmt: 各种简单语句"""
kw: t.CInt = _cur_kw(ps)
if kw == Keyword.Pass:
lineno: t.CInt = _cur_lineno(ps)
col: t.CInt = _cur_col(ps)
_advance(ps)
node: AST | t.CPtr = Pass(ps.pool)
_set_pos(node, lineno, col, 0, 0)
return node
if kw == Keyword.Break:
lineno = _cur_lineno(ps)
col = _cur_col(ps)
_advance(ps)
node = Break(ps.pool)
_set_pos(node, lineno, col, 0, 0)
return node
if kw == Keyword.Continue:
lineno = _cur_lineno(ps)
col = _cur_col(ps)
_advance(ps)
node = Continue(ps.pool)
_set_pos(node, lineno, col, 0, 0)
return node
if kw == Keyword.Return:
return _parse_return(ps)
if kw == Keyword.Raise:
return _parse_raise(ps)
if kw == Keyword.Global:
return _parse_global(ps)
if kw == Keyword.Nonlocal:
return _parse_nonlocal(ps)
if kw == Keyword.Import:
return _parse_import(ps)
if kw == Keyword.From:
return _parse_from_import(ps)
if kw == Keyword.Del:
return _parse_del(ps)
if kw == Keyword.Assert:
return _parse_assert(ps)
if kw == Keyword.Yield:
return _parse_expr_stmt(ps, _parse_yield(ps))
# 表达式语句 / 赋值 / augassign / annassign
return _parse_expr_stmt(ps, None)
def _maybe_tuple(ps: Parser | t.CPtr, first: AST | t.CPtr) -> AST | t.CPtr:
"""如果 first 后面有逗号,收集逗号分隔的表达式为 Tuple。"""
if first is None: return None
if not _match_op(ps, TokOp.Comma):
return first
lineno: t.CInt = first.lineno
col: t.CInt = first.col_offset
elts: list[AST | t.CPtr] | t.CPtr = list[AST | t.CPtr](ps.pool, 8)
elts.append(first)
_accept_op(ps, TokOp.Comma)
while True:
tt: t.CInt = _cur_type(ps)
if tt == TokenType.NewLine or tt == TokenType.EndMarker:
break
if tt == TokenType.Op:
op: t.CInt = _cur_op(ps)
if op == TokOp.Equal or op == TokOp.Semi:
break
e: AST | t.CPtr = _parse_expr(ps)
if e is None: break
elts.append(e)
if not _accept_op(ps, TokOp.Comma):
break
node: AST | t.CPtr = Tuple(ps.pool, elts, ASTCtx.Load)
_set_pos(node, lineno, col, 0, 0)
return node
def _parse_testlist(ps: Parser | t.CPtr) -> AST | t.CPtr:
"""解析逗号分隔的表达式列表多于1个则形成 Tuple。"""
first: AST | t.CPtr = _parse_expr(ps)
return _maybe_tuple(ps, first)
def _parse_expr_stmt(ps: Parser | t.CPtr, first: AST | t.CPtr) -> AST | t.CPtr:
"""表达式语句,处理赋值/augassign/annassign"""
if first is None:
first = _parse_testlist(ps)
else:
first = _maybe_tuple(ps, first)
if first is None:
return None
lineno: t.CInt = first.lineno
col: t.CInt = first.col_offset
# 赋值
if _match_op(ps, TokOp.Equal):
targets: list[AST | t.CPtr] | t.CPtr = list[AST | t.CPtr](ps.pool, 8)
targets.append(first)
_fix_store_ctx(first)
value: AST | t.CPtr = None
accept_result: t.CInt = _accept_op(ps, TokOp.Equal)
while accept_result:
v: AST | t.CPtr = _parse_testlist(ps)
if v is None: break
if value is not None:
targets.append(value)
_fix_store_ctx(value)
value = v
accept_result = _accept_op(ps, TokOp.Equal)
node: AST | t.CPtr = Assign(ps.pool, targets, value)
_set_pos(node, lineno, col, 0, 0)
return node
# augassign
if _cur_type(ps) == TokenType.Op:
op_id: t.CInt = _cur_op(ps)
aug_op: t.CInt = _augop_from_op(op_id)
if aug_op != OpKind.NoneOp:
_advance(ps)
value2: AST | t.CPtr = _parse_testlist(ps)
_fix_store_ctx(first)
node2: AST | t.CPtr = AugAssign(ps.pool, first, aug_op, value2)
_set_pos(node2, lineno, col, 0, 0)
return node2
# annassign: target ':' annotation [= value]
if _match_op(ps, TokOp.Colon):
_advance(ps)
annotation: AST | t.CPtr = _parse_test(ps)
value3: AST | t.CPtr = None
if _accept_op(ps, TokOp.Equal):
value3 = _parse_testlist(ps)
if ps.pool is None:
return None
_fix_store_ctx(first)
if ps.pool is None:
return None
node3: AST | t.CPtr = AnnAssign(ps.pool, first, annotation, value3, 1)
_set_pos(node3, lineno, col, 0, 0)
return node3
# 表达式语句
node4: AST | t.CPtr = Expr(ps.pool, first)
_set_pos(node4, lineno, col, 0, 0)
return node4
def _parse_return(ps: Parser | t.CPtr) -> AST | t.CPtr:
lineno: t.CInt = _cur_lineno(ps)
col: t.CInt = _cur_col(ps)
_advance(ps)
val: AST | t.CPtr = None
if _cur_type(ps) != TokenType.NewLine and _cur_type(ps) != TokenType.EndMarker and _cur_type(ps) != TokenType.Op:
val = _parse_testlist(ps)
elif _cur_type(ps) == TokenType.Op and _cur_op(ps) != TokOp.Semi:
val = _parse_testlist(ps)
node: AST | t.CPtr = Return(ps.pool, val)
_set_pos(node, lineno, col, 0, 0)
return node
def _parse_raise(ps: Parser | t.CPtr) -> AST | t.CPtr:
lineno: t.CInt = _cur_lineno(ps)
col: t.CInt = _cur_col(ps)
_advance(ps)
exc: AST | t.CPtr = None
cause: AST | t.CPtr = None
if _cur_type(ps) != TokenType.NewLine and _cur_type(ps) != TokenType.EndMarker:
exc = _parse_test(ps)
if _cur_kw(ps) == Keyword.From:
_advance(ps)
cause = _parse_test(ps)
node: AST | t.CPtr = Raise(ps.pool, exc, cause)
_set_pos(node, lineno, col, 0, 0)
return node
def _parse_global(ps: Parser | t.CPtr) -> AST | t.CPtr:
lineno: t.CInt = _cur_lineno(ps)
col: t.CInt = _cur_col(ps)
_advance(ps)
names: list[AST | t.CPtr] | t.CPtr = list[AST | t.CPtr](ps.pool, 8)
if _cur_type(ps) == TokenType.Name:
n: AST | t.CPtr = Name(ps.pool, _cur_str(ps), ASTCtx.Store)
_set_pos(n, _cur_lineno(ps), _cur_col(ps), 0, 0)
names.append(n)
_advance(ps)
while _accept_op(ps, TokOp.Comma):
if _cur_type(ps) != TokenType.Name: break
n = Name(ps.pool, _cur_str(ps), ASTCtx.Store)
_set_pos(n, _cur_lineno(ps), _cur_col(ps), 0, 0)
names.append(n)
_advance(ps)
node: AST | t.CPtr = Global(ps.pool, names)
_set_pos(node, lineno, col, 0, 0)
return node
def _parse_nonlocal(ps: Parser | t.CPtr) -> AST | t.CPtr:
lineno: t.CInt = _cur_lineno(ps)
col: t.CInt = _cur_col(ps)
_advance(ps)
names: list[AST | t.CPtr] | t.CPtr = list[AST | t.CPtr](ps.pool, 8)
if _cur_type(ps) == TokenType.Name:
n: AST | t.CPtr = Name(ps.pool, _cur_str(ps), ASTCtx.Store)
_set_pos(n, _cur_lineno(ps), _cur_col(ps), 0, 0)
names.append(n)
_advance(ps)
while _accept_op(ps, TokOp.Comma):
if _cur_type(ps) != TokenType.Name: break
n = Name(ps.pool, _cur_str(ps), ASTCtx.Store)
_set_pos(n, _cur_lineno(ps), _cur_col(ps), 0, 0)
names.append(n)
_advance(ps)
node: AST | t.CPtr = Nonlocal(ps.pool, names)
_set_pos(node, lineno, col, 0, 0)
return node
def _parse_import(ps: Parser | t.CPtr) -> AST | t.CPtr:
lineno: t.CInt = _cur_lineno(ps)
col: t.CInt = _cur_col(ps)
_advance(ps)
names: list[AST | t.CPtr] | t.CPtr = _parse_alias_list(ps)
node: AST | t.CPtr = Import(ps.pool, names)
_set_pos(node, lineno, col, 0, 0)
return node
def _parse_from_import(ps: Parser | t.CPtr) -> AST | t.CPtr:
lineno: t.CInt = _cur_lineno(ps)
col: t.CInt = _cur_col(ps)
_advance(ps)
# 解析 module可能带点号
level: t.CInt = 0
while _match_op(ps, TokOp.Dot):
level += 1
_advance(ps)
while _match_op(ps, TokOp.Ellipsis):
level += 3
_advance(ps)
module_name: str = None
if _cur_type(ps) == TokenType.Name:
module_name = _cur_str(ps)
_advance(ps)
while _match_op(ps, TokOp.Dot):
_advance(ps)
if _cur_type(ps) == TokenType.Name:
next_name: str = _cur_str(ps)
mlen: t.CInt = string.strlen(module_name)
nlen: t.CInt = string.strlen(next_name)
new_mod: str = ps.pool.alloc(mlen + 1 + nlen + 1)
if new_mod is not None:
string.strcpy(new_mod, module_name)
new_mod[mlen] = '.'
string.strcpy(new_mod + mlen + 1, next_name)
module_name = new_mod
_advance(ps)
if _cur_kw(ps) != Keyword.Import:
ps.error_count += 1
return None
_advance(ps)
# import *
if _match_op(ps, TokOp.Star):
_advance(ps)
star_alias: AST | t.CPtr = Alias(ps.pool, "*", None)
_set_pos(star_alias, lineno, col, 0, 0)
star_names: list[AST | t.CPtr] | t.CPtr = list[AST | t.CPtr](ps.pool, 8)
star_names.append(star_alias)
node: AST | t.CPtr = ImportFrom(ps.pool, module_name, star_names, level)
_set_pos(node, lineno, col, 0, 0)
return node
# ( names ) or names
if _match_op(ps, TokOp.LPar):
_advance(ps)
_skip_nl(ps)
names: list[AST | t.CPtr] | t.CPtr = _parse_alias_list(ps)
_skip_nl(ps)
_expect_op(ps, TokOp.RPar)
else:
names = _parse_alias_list(ps)
node: AST | t.CPtr = ImportFrom(ps.pool, module_name, names, level)
_set_pos(node, lineno, col, 0, 0)
return node
def _parse_alias_list(ps: Parser | t.CPtr) -> list[AST | t.CPtr] | t.CPtr:
"""返回 alias list。"""
names: list[AST | t.CPtr] | t.CPtr = list[AST | t.CPtr](ps.pool, 8)
while True:
if _cur_type(ps) != TokenType.Name:
break
name_str: str = _cur_str(ps)
lineno: t.CInt = _cur_lineno(ps)
col: t.CInt = _cur_col(ps)
_advance(ps)
# dotted name
while _match_op(ps, TokOp.Dot):
_advance(ps)
if _cur_type(ps) == TokenType.Name:
next_n: str = _cur_str(ps)
nmlen: t.CInt = string.strlen(name_str)
nxtlen: t.CInt = string.strlen(next_n)
new_name: str = ps.pool.alloc(nmlen + 1 + nxtlen + 1)
if new_name is not None:
string.strcpy(new_name, name_str)
new_name[nmlen] = '.'
string.strcpy(new_name + nmlen + 1, next_n)
name_str = new_name
_advance(ps)
asname: str = None
if _cur_kw(ps) == Keyword.As:
_advance(ps)
if _cur_type(ps) == TokenType.Name:
asname = _cur_str(ps)
_advance(ps)
a: AST | t.CPtr = Alias(ps.pool, name_str, asname)
_set_pos(a, lineno, col, 0, 0)
names.append(a)
if not _accept_op(ps, TokOp.Comma):
break
_skip_nl(ps)
return names
def _parse_del(ps: Parser | t.CPtr) -> AST | t.CPtr:
lineno: t.CInt = _cur_lineno(ps)
col: t.CInt = _cur_col(ps)
_advance(ps)
targets: list[AST | t.CPtr] | t.CPtr = list[AST | t.CPtr](ps.pool, 8)
while True:
t_: AST | t.CPtr = _parse_atom_expr(ps)
if t_ is None: break
_set_ctx(t_, ASTCtx.Del)
targets.append(t_)
if not _accept_op(ps, TokOp.Comma):
break
node: AST | t.CPtr = Delete(ps.pool, targets)
_set_pos(node, lineno, col, 0, 0)
return node
def _parse_assert(ps: Parser | t.CPtr) -> AST | t.CPtr:
lineno: t.CInt = _cur_lineno(ps)
col: t.CInt = _cur_col(ps)
_advance(ps)
test: AST | t.CPtr = _parse_test(ps)
msg: AST | t.CPtr = None
if _accept_op(ps, TokOp.Comma):
msg = _parse_test(ps)
node: AST | t.CPtr = Assert(ps.pool, test, msg)
_set_pos(node, lineno, col, 0, 0)
return node
# ============================================================
# 复合语句
# ============================================================
def _parse_compound(ps: Parser | t.CPtr) -> AST | t.CPtr:
kw: t.CInt = _cur_kw(ps)
if kw == Keyword.If:
return _parse_if(ps)
if kw == Keyword.While:
return _parse_while(ps)
if kw == Keyword.For:
return _parse_for(ps, 0)
if kw == Keyword.Try:
return _parse_try(ps)
if kw == Keyword.With:
return _parse_with(ps, 0)
if kw == Keyword.Def:
return _parse_funcdef(ps, 0)
if kw == Keyword.Class:
return _parse_classdef(ps)
if kw == Keyword.Async:
_advance(ps)
if _cur_kw(ps) == Keyword.Def:
return _parse_funcdef(ps, 1)
if _cur_kw(ps) == Keyword.For:
return _parse_for(ps, 1)
if _cur_kw(ps) == Keyword.With:
return _parse_with(ps, 1)
ps.error_count += 1
return None
if kw == Keyword.Match:
return _parse_match(ps)
ps.error_count += 1
return None
def _parse_if(ps: Parser | t.CPtr) -> AST | t.CPtr:
lineno: t.CInt = _cur_lineno(ps)
col: t.CInt = _cur_col(ps)
_advance(ps) # if
test: AST | t.CPtr = _parse_test(ps)
_expect_op(ps, TokOp.Colon)
# 先用空 orelse 构造,后面填充
empty: list[AST | t.CPtr] | t.CPtr = list[AST | t.CPtr](ps.pool, 8)
node: AST | t.CPtr = If(ps.pool, test, empty)
_parse_suite_into_children(ps, node)
orelse: list[AST | t.CPtr] | t.CPtr = _parse_if_tail(ps)
node.orelse = orelse
_set_pos(node, lineno, col, 0, 0)
return node
def _parse_if_tail(ps: Parser | t.CPtr) -> list[AST | t.CPtr] | t.CPtr:
"""Parse elif/else chain and return orelse list.
elif: orelse = list containing single If node (nested)
else: orelse = statement list
"""
if _cur_kw(ps) == Keyword.Elif:
lineno: t.CInt = _cur_lineno(ps)
col: t.CInt = _cur_col(ps)
_advance(ps)
elif_test: AST | t.CPtr = _parse_test(ps)
_expect_op(ps, TokOp.Colon)
empty: list[AST | t.CPtr] | t.CPtr = list[AST | t.CPtr](ps.pool, 8)
elif_node: AST | t.CPtr = If(ps.pool, elif_test, empty)
_parse_suite_into_children(ps, elif_node)
elif_orelse: list[AST | t.CPtr] | t.CPtr = _parse_if_tail(ps)
elif_node.orelse = elif_orelse
_set_pos(elif_node, lineno, col, 0, 0)
orelse: list[AST | t.CPtr] | t.CPtr = list[AST | t.CPtr](ps.pool, 8)
orelse.append(elif_node)
return orelse
if _cur_kw(ps) == Keyword.Else:
_advance(ps)
_expect_op(ps, TokOp.Colon)
scratch: Module | t.CPtr = Module(ps.pool)
_parse_suite_into_children(ps, scratch)
return scratch.children
return list[AST | t.CPtr](ps.pool, 8) # 空 list
# ============================================================
# 复合语句解析(续)
# ============================================================
def _parse_while(ps: Parser | t.CPtr) -> AST | t.CPtr:
lineno: t.CInt = _cur_lineno(ps)
col: t.CInt = _cur_col(ps)
_advance(ps) # while
test: AST | t.CPtr = _parse_test(ps)
_expect_op(ps, TokOp.Colon)
empty: list[AST | t.CPtr] | t.CPtr = list[AST | t.CPtr](ps.pool, 8)
node: AST | t.CPtr = While(ps.pool, test, empty)
_parse_suite_into_children(ps, node)
if _cur_kw(ps) == Keyword.Else:
_advance(ps)
_expect_op(ps, TokOp.Colon)
scratch: Module | t.CPtr = Module(ps.pool)
_parse_suite_into_children(ps, scratch)
node.orelse = scratch.children
_set_pos(node, lineno, col, 0, 0)
return node
def _parse_for(ps: Parser | t.CPtr, is_async: t.CInt) -> AST | t.CPtr:
lineno: t.CInt = _cur_lineno(ps)
col: t.CInt = _cur_col(ps)
_advance(ps) # for
target: AST | t.CPtr = _parse_target_list(ps)
if _cur_kw(ps) != Keyword.In:
ps.error_count += 1
return None
_advance(ps)
iter_: AST | t.CPtr = _parse_or_test(ps)
_expect_op(ps, TokOp.Colon)
empty: list[AST | t.CPtr] | t.CPtr = list[AST | t.CPtr](ps.pool, 8)
node: AST | t.CPtr = For(ps.pool, target, iter_, empty, is_async)
_fix_store_ctx(target)
_parse_suite_into_children(ps, node)
if _cur_kw(ps) == Keyword.Else:
_advance(ps)
_expect_op(ps, TokOp.Colon)
scratch: Module | t.CPtr = Module(ps.pool)
_parse_suite_into_children(ps, scratch)
node.orelse = scratch.children
_set_pos(node, lineno, col, 0, 0)
return node
def _parse_try(ps: Parser | t.CPtr) -> AST | t.CPtr:
lineno: t.CInt = _cur_lineno(ps)
col: t.CInt = _cur_col(ps)
_advance(ps) # try
_expect_op(ps, TokOp.Colon)
empty: list[AST | t.CPtr] | t.CPtr = list[AST | t.CPtr](ps.pool, 8)
node: AST | t.CPtr = Try(ps.pool, empty, empty, empty)
_parse_suite_into_children(ps, node)
# handlers (except)
handlers: list[AST | t.CPtr] | t.CPtr = list[AST | t.CPtr](ps.pool, 8)
while _cur_kw(ps) == Keyword.Except:
h: AST | t.CPtr = _parse_except_handler(ps)
if h is not None:
handlers.append(h)
node.handlers = handlers
# else
if _cur_kw(ps) == Keyword.Else:
_advance(ps)
_expect_op(ps, TokOp.Colon)
scratch: Module | t.CPtr = Module(ps.pool)
_parse_suite_into_children(ps, scratch)
node.orelse = scratch.children
# finally
if _cur_kw(ps) == Keyword.Finally:
_advance(ps)
_expect_op(ps, TokOp.Colon)
scratch2: Module | t.CPtr = Module(ps.pool)
_parse_suite_into_children(ps, scratch2)
node.finalbody = scratch2.children
_set_pos(node, lineno, col, 0, 0)
return node
def _parse_except_handler(ps: Parser | t.CPtr) -> AST | t.CPtr:
lineno: t.CInt = _cur_lineno(ps)
col: t.CInt = _cur_col(ps)
_advance(ps) # except
type_: AST | t.CPtr = None
name: str = None
if _cur_op(ps) != TokOp.Colon:
type_ = _parse_test(ps)
if _cur_kw(ps) == Keyword.As:
_advance(ps)
if _cur_type(ps) == TokenType.Name:
name = _cur_str(ps)
_advance(ps)
_expect_op(ps, TokOp.Colon)
node: AST | t.CPtr = ExceptHandler(ps.pool, type_, name)
_parse_suite_into_children(ps, node)
_set_pos(node, lineno, col, 0, 0)
return node
def _parse_with(ps: Parser | t.CPtr, is_async: t.CInt) -> AST | t.CPtr:
lineno: t.CInt = _cur_lineno(ps)
col: t.CInt = _cur_col(ps)
_advance(ps) # with
items: list[AST | t.CPtr] | t.CPtr = list[AST | t.CPtr](ps.pool, 8)
# items
while True:
item: AST | t.CPtr = _parse_with_item(ps)
if item is None: break
items.append(item)
if not _accept_op(ps, TokOp.Comma):
break
# 检查是否可以续行with (...): 形式)
if _cur_type(ps) == TokenType.NewLine or _cur_type(ps) == TokenType.Nl:
break
_expect_op(ps, TokOp.Colon)
node: AST | t.CPtr = With(ps.pool, items, is_async)
_parse_suite_into_children(ps, node)
_set_pos(node, lineno, col, 0, 0)
return node
def _parse_with_item(ps: Parser | t.CPtr) -> AST | t.CPtr:
"""with_item: test ['as' expr]"""
lineno: t.CInt = _cur_lineno(ps)
col: t.CInt = _cur_col(ps)
context_expr: AST | t.CPtr = _parse_or_test(ps)
if context_expr is None: return None
optional_vars: AST | t.CPtr = None
if _cur_kw(ps) == Keyword.As:
_advance(ps)
optional_vars = _parse_target(ps)
_fix_store_ctx(optional_vars)
node: AST | t.CPtr = WithItem(ps.pool, context_expr, optional_vars)
_set_pos(node, lineno, col, 0, 0)
return node
def _parse_funcdef(ps: Parser | t.CPtr, is_async: t.CInt) -> AST | t.CPtr:
lineno: t.CInt = _cur_lineno(ps)
col: t.CInt = _cur_col(ps)
_advance(ps) # def
if _cur_type(ps) != TokenType.Name:
ps.error_count += 1
return None
name: str = _cur_str(ps)
_advance(ps)
args: AST | t.CPtr = None
if _accept_op(ps, TokOp.LPar):
args = _parse_varargslist(ps, 0)
_expect_op(ps, TokOp.RPar)
returns: AST | t.CPtr = None
if _accept_op(ps, TokOp.RArrow):
returns = _parse_test(ps)
_expect_op(ps, TokOp.Colon)
dec_list: list[AST | t.CPtr] | t.CPtr = ps.pending_decorators
ps.pending_decorators = None
node: AST | t.CPtr = FunctionDef(ps.pool, name, args, dec_list, returns, is_async)
_parse_suite_into_children(ps, node)
_set_pos(node, lineno, col, 0, 0)
return node
def _parse_classdef(ps: Parser | t.CPtr) -> AST | t.CPtr:
lineno: t.CInt = _cur_lineno(ps)
col: t.CInt = _cur_col(ps)
_advance(ps) # class
if _cur_type(ps) != TokenType.Name:
ps.error_count += 1
return None
name: str = _cur_str(ps)
_advance(ps)
# 解析类型参数 [T] 或 [T, U]PEP 695 泛型类语法)
type_params: list[str] | t.CPtr = None
if _accept_op(ps, TokOp.Lsqb):
type_params = list[str](ps.pool, 8)
while not _match_op(ps, TokOp.Rsqb):
if _cur_type(ps) == TokenType.EndMarker: break
if _cur_type(ps) == TokenType.Name:
tp_name: str = _cur_str(ps)
_advance(ps)
# 跳过可选的 bound/constraint: [T: bound] 或 [T: (A, B)]
if _match_op(ps, TokOp.Colon):
_advance(ps)
_parse_test(ps)
type_params.append(tp_name)
if not _accept_op(ps, TokOp.Comma):
break
_expect_op(ps, TokOp.Rsqb)
bases: list[AST | t.CPtr] | t.CPtr = list[AST | t.CPtr](ps.pool, 8)
keywords: list[AST | t.CPtr] | t.CPtr = list[AST | t.CPtr](ps.pool, 8)
# bases / keywords
if _accept_op(ps, TokOp.LPar):
while not _match_op(ps, TokOp.RPar):
if _cur_type(ps) == TokenType.EndMarker: break
# base 或 keyword=value
if _cur_type(ps) == TokenType.Name:
# 解析为表达式,若后跟 = 则作为 keyword
e: AST | t.CPtr = _parse_or_test(ps)
if _match_op(ps, TokOp.Equal):
_advance(ps)
v: AST | t.CPtr = _parse_test(ps)
kw_node: AST | t.CPtr = KwNode(ps.pool, e, v)
keywords.append(kw_node)
else:
bases.append(e)
if not _accept_op(ps, TokOp.Comma):
break
_expect_op(ps, TokOp.RPar)
_expect_op(ps, TokOp.Colon)
dec_list: list[AST | t.CPtr] | t.CPtr = ps.pending_decorators
ps.pending_decorators = None
node: AST | t.CPtr = ClassDef(ps.pool, name, bases, keywords, dec_list, type_params)
_parse_suite_into_children(ps, node)
_set_pos(node, lineno, col, 0, 0)
return node
def _parse_match(ps: Parser | t.CPtr) -> AST | t.CPtr:
"""match 语句match subject ':' NewLine Indent case+ Dedent"""
lineno: t.CInt = _cur_lineno(ps)
col: t.CInt = _cur_col(ps)
_advance(ps) # match
subject: AST | t.CPtr = _parse_test(ps)
_expect_op(ps, TokOp.Colon)
cases: list[AST | t.CPtr] | t.CPtr = list[AST | t.CPtr](ps.pool, 8)
node: AST | t.CPtr = Match(ps.pool, subject, cases)
# 解析 body与 _parse_suite_into_children 相同的 NewLine/Indent/Dedent 模式,
# 但解析 case 子句到 cases 列表而非普通语句到 children
_skip_nl(ps)
if _cur_type(ps) == TokenType.NewLine:
_advance(ps)
_skip_nl(ps)
if _cur_type(ps) == TokenType.Indent:
_advance(ps)
while _cur_type(ps) != TokenType.Dedent and _cur_type(ps) != TokenType.EndMarker:
if _cur_kw(ps) == Keyword.Case:
case_node: AST | t.CPtr = _parse_match_case(ps)
if case_node is not None:
cases.append(case_node)
else:
_advance(ps) # 跳过非 case token避免死循环
_expect_type(ps, TokenType.Dedent)
_set_pos(node, lineno, col, 0, 0)
return node
# ============================================================
# Match pattern 解析
# ============================================================
def _make_name_ast(pool: memhub.MemManager | t.CPtr, node: Name | t.CPtr) -> AST | t.CPtr:
"""将 Name* 转换为 AST*(解决 TransPyC 类型推断问题)"""
return node
def _parse_pattern(ps: Parser | t.CPtr) -> AST | t.CPtr:
"""pattern: or_pattern ['as' NAME]
or_pattern: closed_pattern ('|' closed_pattern)*"""
pat: AST | t.CPtr = _parse_closed_pattern(ps)
if pat is None: return None
# OR pattern
if _match_op(ps, TokOp.VBar):
patterns: list[AST | t.CPtr] | t.CPtr = list[AST | t.CPtr](ps.pool, 8)
patterns.append(pat)
while _accept_op(ps, TokOp.VBar):
p: AST | t.CPtr = _parse_closed_pattern(ps)
if p is not None:
patterns.append(p)
or_node: AST | t.CPtr = MatchOr(ps.pool, patterns)
_set_pos(or_node, pat.lineno, pat.col_offset, 0, 0)
pat = or_node
# AS pattern
if _cur_kw(ps) == Keyword.As:
_advance(ps)
if _cur_type(ps) != TokenType.Name:
ps.error_count += 1
return pat
name_str: str = _cur_str(ps)
_advance(ps)
as_node: AST | t.CPtr = MatchAs(ps.pool, pat, name_str)
_set_pos(as_node, pat.lineno, pat.col_offset, 0, 0)
return as_node
return pat
def _parse_closed_pattern(ps: Parser | t.CPtr) -> AST | t.CPtr:
"""closed_pattern: literal | capture | wildcard | value | group | sequence | mapping | class"""
lineno: t.CInt = _cur_lineno(ps)
col: t.CInt = _cur_col(ps)
ttype: t.CInt = _cur_type(ps)
val: AST | t.CPtr
node: AST | t.CPtr
tok: Token | t.CPtr
sval: str
# 数字字面量 pattern
if ttype == TokenType.Number:
tok = ps.cur
_advance(ps)
if tok.is_float:
val = _new_const_float(ps.pool, tok.float_val, lineno, col)
else:
val = _new_const_int(ps.pool, tok.int_val, lineno, col)
node = MatchValue(ps.pool, val)
_set_pos(node, lineno, col, 0, 0)
return node
# 字符串字面量 pattern
if ttype == TokenType.String:
sval = _cur_str(ps)
_advance(ps)
val = _new_const_str(ps.pool, sval, lineno, col)
node = MatchValue(ps.pool, val)
_set_pos(node, lineno, col, 0, 0)
return node
# 负数字面量 pattern: -1, -1.5
if _match_op(ps, TokOp.Minus):
_advance(ps)
if _cur_type(ps) == TokenType.Number:
tok = ps.cur
_advance(ps)
if tok.is_float:
val = _new_const_float(ps.pool, -tok.float_val, lineno, col)
else:
val = _new_const_int(ps.pool, -tok.int_val, lineno, col)
node = MatchValue(ps.pool, val)
_set_pos(node, lineno, col, 0, 0)
return node
ps.error_count += 1
return None
# Name: None/True/False/_ /capture/value/class
if ttype == TokenType.Name:
kw: t.CInt = _cur_kw(ps)
# None/True/False → MatchSingleton
if kw == Keyword.None_ or kw == Keyword.True_ or kw == Keyword.False_:
_advance(ps)
if kw == Keyword.None_:
val = _new_const_none(ps.pool, lineno, col)
elif kw == Keyword.True_:
val = _new_const_bool(ps.pool, 1, lineno, col)
else:
val = _new_const_bool(ps.pool, 0, lineno, col)
node = MatchSingleton(ps.pool, val)
_set_pos(node, lineno, col, 0, 0)
return node
name_str: str = _cur_str(ps)
# 通配符 _
if string.strcmp(name_str, "_") == 0:
_advance(ps)
node = MatchAs(ps.pool, None, None)
_set_pos(node, lineno, col, 0, 0)
return node
_advance(ps)
# 检查 dotted namevalue pattern 或 class pattern
if _match_op(ps, TokOp.Dot):
name_node: Name | t.CPtr = Name(ps.pool, name_str, ASTCtx.Load)
_set_pos(name_node, lineno, col, 0, 0)
base: AST | t.CPtr = _make_name_ast(ps.pool, name_node)
while _match_op(ps, TokOp.Dot):
_advance(ps)
if _cur_type(ps) != TokenType.Name:
ps.error_count += 1
break
attr_name: str = _cur_str(ps)
attr_lineno: t.CInt = _cur_lineno(ps)
attr_col: t.CInt = _cur_col(ps)
_advance(ps)
base = Attribute(ps.pool, base, attr_name, ASTCtx.Load)
_set_pos(base, attr_lineno, attr_col, 0, 0)
# class pattern: dotted.name(...)
if _match_op(ps, TokOp.LPar):
return _parse_class_pattern(ps, base, lineno, col)
# value pattern: dotted.name
node = MatchValue(ps.pool, base)
_set_pos(node, lineno, col, 0, 0)
return node
# class pattern: Name(...)
if _match_op(ps, TokOp.LPar):
cls_node: Name | t.CPtr = Name(ps.pool, name_str, ASTCtx.Load)
_set_pos(cls_node, lineno, col, 0, 0)
cls: AST | t.CPtr = _make_name_ast(ps.pool, cls_node)
return _parse_class_pattern(ps, cls, lineno, col)
# capture pattern: NAME
node = MatchAs(ps.pool, None, name_str)
_set_pos(node, lineno, col, 0, 0)
return node
# group pattern / sequence pattern (tuple): (...)
if _match_op(ps, TokOp.LPar):
_advance(ps)
inner: AST | t.CPtr = _parse_pattern(ps)
if _accept_op(ps, TokOp.Comma):
# sequence pattern (tuple)
patterns: list[AST | t.CPtr] | t.CPtr = list[AST | t.CPtr](ps.pool, 8)
if inner is not None:
patterns.append(inner)
while not _match_op(ps, TokOp.RPar) and _cur_type(ps) != TokenType.EndMarker:
p: AST | t.CPtr = _parse_pattern(ps)
if p is not None:
patterns.append(p)
if not _accept_op(ps, TokOp.Comma):
break
_expect_op(ps, TokOp.RPar)
node = MatchSequence(ps.pool, patterns)
_set_pos(node, lineno, col, 0, 0)
return node
_expect_op(ps, TokOp.RPar)
return inner # group pattern
# sequence pattern (list): [...]
if _match_op(ps, TokOp.Lsqb):
return _parse_sequence_pattern(ps, TokOp.Rsqb, lineno, col)
# mapping pattern: {...}
if _match_op(ps, TokOp.LBrace):
return _parse_mapping_pattern(ps, lineno, col)
ps.error_count += 1
return None
def _parse_sequence_pattern(ps: Parser | t.CPtr, close_op: t.CInt,
lineno: t.CInt, col: t.CInt) -> AST | t.CPtr:
"""sequence_pattern: '[' [pattern_list] ']'"""
patterns: list[AST | t.CPtr] | t.CPtr = list[AST | t.CPtr](ps.pool, 8)
_advance(ps) # [
while not _match_op(ps, close_op) and _cur_type(ps) != TokenType.EndMarker:
# *name (star pattern)
if _match_op(ps, TokOp.Star):
_advance(ps)
if _cur_type(ps) == TokenType.Name:
star_name: str = _cur_str(ps)
_advance(ps)
star_node: AST | t.CPtr
if string.strcmp(star_name, "_") == 0:
star_node = MatchStar(ps.pool, None)
else:
star_node = MatchStar(ps.pool, star_name)
_set_pos(star_node, lineno, col, 0, 0)
patterns.append(star_node)
else:
p: AST | t.CPtr = _parse_pattern(ps)
if p is not None:
patterns.append(p)
if not _accept_op(ps, TokOp.Comma):
break
_expect_op(ps, close_op)
node: AST | t.CPtr = MatchSequence(ps.pool, patterns)
_set_pos(node, lineno, col, 0, 0)
return node
def _parse_mapping_pattern(ps: Parser | t.CPtr,
lineno: t.CInt, col: t.CInt) -> AST | t.CPtr:
"""mapping_pattern: '{' [items] '}'
items: key ':' pattern (',' key ':' pattern)* [',' '**' NAME] | '**' NAME"""
keys: list[AST | t.CPtr] | t.CPtr = list[AST | t.CPtr](ps.pool, 8)
patterns: list[AST | t.CPtr] | t.CPtr = list[AST | t.CPtr](ps.pool, 8)
rest: str = None
node: AST | t.CPtr
key: AST | t.CPtr
val_pat: AST | t.CPtr
_advance(ps) # {
if _match_op(ps, TokOp.RBrace):
_advance(ps)
node = MatchMapping(ps.pool, keys, patterns, rest)
_set_pos(node, lineno, col, 0, 0)
return node
while _cur_type(ps) != TokenType.EndMarker:
if _match_op(ps, TokOp.StarStar):
_advance(ps)
if _cur_type(ps) == TokenType.Name:
rest = _cur_str(ps)
_advance(ps)
break
key = _parse_expr(ps)
_expect_op(ps, TokOp.Colon)
val_pat = _parse_pattern(ps)
if key is not None:
keys.append(key)
if val_pat is not None:
patterns.append(val_pat)
if not _accept_op(ps, TokOp.Comma):
break
_expect_op(ps, TokOp.RBrace)
node = MatchMapping(ps.pool, keys, patterns, rest)
_set_pos(node, lineno, col, 0, 0)
return node
def _parse_class_pattern(ps: Parser | t.CPtr, cls: AST | t.CPtr,
lineno: t.CInt, col: t.CInt) -> AST | t.CPtr:
"""class_pattern: cls '(' [pattern_list] ')'
pattern_list: positional_pattern (',' positional_pattern)*
[',' keyword_pattern]* | keyword_pattern (',' keyword_pattern)*
keyword_pattern: NAME '=' pattern"""
patterns: list[AST | t.CPtr] | t.CPtr = list[AST | t.CPtr](ps.pool, 8)
kwd_attrs: list[AST | t.CPtr] | t.CPtr = list[AST | t.CPtr](ps.pool, 8)
kwd_patterns: list[AST | t.CPtr] | t.CPtr = list[AST | t.CPtr](ps.pool, 8)
p: AST | t.CPtr
_advance(ps) # (
if not _match_op(ps, TokOp.RPar):
while _cur_type(ps) != TokenType.EndMarker:
# keyword pattern: NAME '=' pattern
if _cur_type(ps) == TokenType.Name and ps.cur is not None:
nxt: Token | t.CPtr = ps.cur.next
if nxt is not None and nxt.type == TokenType.Op and nxt.op_subtype == TokOp.Equal:
attr_name: str = _cur_str(ps)
_advance(ps) # NAME
_advance(ps) # =
kwd_pat: AST | t.CPtr = _parse_pattern(ps)
attr_n: Name | t.CPtr = Name(ps.pool, attr_name, ASTCtx.Load)
_set_pos(attr_n, lineno, col, 0, 0)
attr_node: AST | t.CPtr = _make_name_ast(ps.pool, attr_n)
kwd_attrs.append(attr_node)
if kwd_pat is not None:
kwd_patterns.append(kwd_pat)
else:
p = _parse_pattern(ps)
if p is not None:
patterns.append(p)
else:
p = _parse_pattern(ps)
if p is not None:
patterns.append(p)
if not _accept_op(ps, TokOp.Comma):
break
_expect_op(ps, TokOp.RPar)
node: AST | t.CPtr = MatchClass(ps.pool, cls, patterns, kwd_attrs, kwd_patterns)
_set_pos(node, lineno, col, 0, 0)
return node
def _parse_match_case(ps: Parser | t.CPtr) -> AST | t.CPtr:
"""case pattern [if guard] ':' suite"""
lineno: t.CInt = _cur_lineno(ps)
col: t.CInt = _cur_col(ps)
_advance(ps) # case
guard: AST | t.CPtr = None
pattern: AST | t.CPtr = _parse_pattern(ps)
if _cur_kw(ps) == Keyword.If:
_advance(ps)
guard = _parse_or_test(ps)
_expect_op(ps, TokOp.Colon)
node: AST | t.CPtr = MatchCase(ps.pool, pattern, guard)
_parse_suite_into_children(ps, node)
_set_pos(node, lineno, col, 0, 0)
return node
def _parse_decorators(ps: Parser | t.CPtr) -> list[AST | t.CPtr] | t.CPtr:
"""解析装饰器链,返回 list。"""
decs: list[AST | t.CPtr] | t.CPtr = list[AST | t.CPtr](ps.pool, 8)
while _match_op(ps, TokOp.At):
lineno: t.CInt = _cur_lineno(ps)
col: t.CInt = _cur_col(ps)
_advance(ps) # @
expr: AST | t.CPtr = _parse_namedexpr(ps)
_skip_nl(ps)
if _cur_type(ps) == TokenType.NewLine:
_advance(ps)
decs.append(expr)
return decs
# ============================================================
# 模块入口
# ============================================================
def _parse_module_body(ps: Parser | t.CPtr, module_node: AST | t.CPtr):
"""解析模块体append 到 module_node。"""
_skip_nl(ps)
stmt_idx: t.CInt = 0
while _cur_type(ps) != TokenType.EndMarker:
stmt_idx += 1
# 装饰器
if _match_op(ps, TokOp.At):
ps.pending_decorators = _parse_decorators(ps)
# 记录解析前的 token 指针,用于检测未前进的死循环
before_cur: Token | t.CPtr = ps.cur
_parse_statement_into(ps, module_node)
# 防御:若解析未前进 token如 _parse_atom 遇到无法识别的 token 返回 None
# 强制前进以避免主循环死循环;累计 error_count 供调用方诊断
if ps.cur is before_cur:
ps.error_count += 1
_advance(ps)
_skip_nl(ps)
def parse_tokens(pool: memhub.MemManager | t.CPtr,
tokens: Token | t.CPtr) -> AST | t.CPtr:
"""从 token 链表解析 AST Module。"""
if pool is None or tokens is None:
return None
ps: Parser | t.CPtr = new_parser(pool)
if ps is None: return None
_parser_init(ps, tokens, pool)
module_node: AST | t.CPtr = Module(pool)
_parse_module_body(ps, module_node)
return module_node