2353 lines
84 KiB
Python
2353 lines
84 KiB
Python
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
|
||
|
||
|
||
# ============================================================
|
||
# 常量构造辅助(新 API:Constant 直接接受 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 共用)
|
||
新 API:args/defaults/kw_defaults 为 list,vararg/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 name(value 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
|