与 TransPyV/includes 侧(c27ebc1)逐点对齐:`struct X:` / `struct X(Base):` 脱糖为 `@t.NoVTable class X(t.CStruct):`,禁止 virtual/novirtual 修饰、禁止类体内 def。 - lib/vast/Parser.py:软关键字分派(语句起始 + 后随标识符;`struct = 5` 不受影响)、 _ParseStructDef(复用 _ParseClassDef 全路径 + 合成 @t.NoVTable + 追加 t.CStruct 标记基类 + 类体禁 def 校验)、_StructSyntaxError 报文、_ParseVirtualModified 显式拦截 `virtual/novirtual struct`;@deco 前缀同样支持。 - lib/vast_c/vast_c.c:同构实现(ParseStatement / ParseDecorated 分派、 ParseStructDef、ParseVirtualModified 拦截)。 - lib/vast_c/_parser.pyd:按 README §4.3 的命令重建(gcc 15.2.0)。 验证(两条路径逐一对照):bases=[t.CStruct] / [Base, t.CStruct]、 decos=[t.NoVTable];`virtual struct` 与 `struct 内 def` 均 fail-fast; `struct = 5` 仍解析为普通赋值。
1544 lines
68 KiB
Python
1544 lines
68 KiB
Python
"""vast.parser - Viper/Python 语句级语法分析器
|
||
|
||
基于 vast.tokenizer + vast.exprparser,实现与 CPython 3.12 ``ast.parse``
|
||
结构完全一致的语句解析:
|
||
|
||
- 简单语句:赋值/增强赋值/带注解赋值/表达式/import/from-import/global/
|
||
nonlocal/del/assert/pass/break/continue/return/raise
|
||
- 复合语句:if/while/for/try/with/match/def/class/async def-for-with
|
||
- 装饰器、嵌套块(INDENT/DEDENT)、分号分隔、类型注解
|
||
- Viper 软关键字 fn/fun/func/function → FunctionDef(并入解析器,无需
|
||
ExpandSoftKeywords 预处理)
|
||
|
||
产出与 CPython ast 完全一致的节点,含 4 位置属性。
|
||
"""
|
||
from __future__ import annotations
|
||
|
||
from lib.vast import Nodes as N
|
||
from lib.vast.ExprParser import ExprParserMixin
|
||
from lib.vast.Tokenizer import (
|
||
Token, NAME, NUMBER, STRING, FSTRING, OP, NEWLINE, NL, INDENT, DEDENT,
|
||
ENDMARKER, SOFT_KEYWORDS,
|
||
)
|
||
|
||
|
||
# ======================================================================
|
||
# virtual / novirtual 修饰符(软关键字)
|
||
# ======================================================================
|
||
# 语法:`virtual class` / `novirtual class` / `virtual def` / `novirtual def`
|
||
# (修饰符写在 class/def **之前**,允许与修饰对象同行,也允许各占一行)。
|
||
#
|
||
# 语义:与既有装饰器**同一套表示**——virtual → `@t.CVTable`,
|
||
# novirtual → `@t.NoVTable`。本层只"合成装饰器节点",不新增第二套表示:
|
||
# - 类级:CVTable 强制有 vtable / NoVTable 强制无 vtable
|
||
# (TPC:HandlesClassDef._resolve_class_flags 读 ClassDef.decorator_list
|
||
# 的 CVTable/NoVTable;TPV:ClassGenBase._detect_vtable_status)
|
||
# - 方法级:CVTable 进虚表槽 / NoVTable 不进虚表槽
|
||
# (TPV:ClassGenBase._should_method_be_virtual;TPC:同判据见
|
||
# SymbolUtils.ClassVtableSlotMethods / 方法收集处)
|
||
#
|
||
# 行号守恒:合成节点的位置取"修饰符 token"的位置,ClassDef/FunctionDef 自身的
|
||
# lineno/col_offset 仍是源码里 class/def 的真实位置 ⇒ 与手写装饰器形态在
|
||
# 行号上也不产生任何偏移(vs 文本插入装饰器行会整篇 +1 行)。
|
||
#
|
||
# 软关键字边界(非保留字):
|
||
# - 只在**语句起始**且后随 class/def 时生效;作标识符的其它位置
|
||
# (`x.virtual` / `f(virtual)` / `d = {"virtual": 1}`)逐字不变;
|
||
# - 语句起始但后随非 class/def ⇒ **响亮失败**(绝不静默丢弃修饰符);
|
||
# - `virtual def` / `novirtual def` 出现在类体之外(模块级 / 函数体 / 分支体)
|
||
# ⇒ **响亮失败**。
|
||
_VIRTUAL_MARKERS: dict[str, str] = {'virtual': 'CVTable', 'novirtual': 'NoVTable'}
|
||
# def 的别名(与 Tokenizer.SOFT_KEYWORDS 的 def 系列同集合,**不含** extern——
|
||
# extern 由 _ParseExternDef 单独解析,不能走 _ParseFunctionDef)
|
||
_DEF_ALIASES: tuple[str, ...] = ('fn', 'fun', 'func', 'function')
|
||
_VIRTUAL_POSITION_HINT: str = (
|
||
'可写位置:类级 `virtual class C:` / `novirtual class C:`;'
|
||
'方法级 `virtual def m(self): ...` / `novirtual def m(self): ...`'
|
||
'(**只能写在类体内**:virtual class / class / novirtual class 的类体)'
|
||
)
|
||
_VIRTUAL_EXAMPLE: str = (
|
||
'virtual class Shape: / novirtual class Node: / '
|
||
'class A: virtual def run(self) -> int: ... / '
|
||
'novirtual class B: novirtual def helper(self) -> int: ...'
|
||
)
|
||
|
||
# ======================================================================
|
||
# struct 纯数据类型定义关键字(软关键字)
|
||
# ======================================================================
|
||
# 语法:`struct X:` / `struct X(Base):`(与 class 同形,含类型参数 [T] 与关键字实参)
|
||
# 语义(用户 2026-10-01 定):与 class 同形,但**只能表示数据类型**:
|
||
# - 禁止 virtual / novirtual 修饰(见 _ParseVirtualModified 的显式拦截);
|
||
# - 禁止类体内定义 def(见 _ParseStructDef 的 fail-fast);
|
||
# - 解析期合成 `@t.NoVTable` 装饰器 + 追加 `t.CStruct` 标记基类,
|
||
# 与手写 `@t.NoVTable class X(t.CStruct):` 逐字段同形 ⇒ 下游
|
||
# (HandlesClassDef._resolve_class_flags / _MARKER_BASES)零新增判据。
|
||
# - 允许出现在 REnum 内部(REnum 变体本就是嵌套 ClassDef),也允许 struct 内
|
||
# 再写 struct(与 class 内写 class 同口径)。
|
||
# 软关键字边界:只在"语句起始 + 后随标识符"时判定,故 `struct = 5` / `x.struct`
|
||
# 等作标识符的写法逐字不变。
|
||
_STRUCT_KEYWORD: str = 'struct'
|
||
_STRUCT_EXAMPLE: str = (
|
||
'struct Point: x: int / struct Point: x: int(换行缩进)y: int / '
|
||
'struct Node(Base): value: int'
|
||
)
|
||
|
||
|
||
class Parser(ExprParserMixin):
|
||
"""Viper/Python 完整语法分析器。"""
|
||
|
||
# virtual / novirtual 修饰符:语句起始 + 紧跟 class/def 时才生效(软关键字)。
|
||
# _InClassBody:当前正在解析的套件的**直接拥有者**是否为 ClassDef
|
||
# (由 _ParseSuite 按 owner_is_class 维护),用于 fail-fast
|
||
# "virtual def / novirtual def 只能直接写在类体内"。
|
||
_InClassBody: bool = False
|
||
|
||
# ==================================================================
|
||
# 模块级入口
|
||
# ==================================================================
|
||
def ParseModule(self) -> N.Module:
|
||
"""解析整个模块:返回 Module(body=..., type_ignores=[])。"""
|
||
body: list[N.stmt] = []
|
||
self._SkipNEWLINE()
|
||
while self._Peek().Type != ENDMARKER:
|
||
stmt: N.stmt = self.ParseStatement()
|
||
body.append(stmt)
|
||
# 分号分隔后续语句(简单语句一行多条)
|
||
while self._AtOp(';'):
|
||
self._Next()
|
||
if self._At(NEWLINE):
|
||
break
|
||
extra: N.stmt = self.ParseStatement()
|
||
body.append(extra)
|
||
self._SkipNEWLINE()
|
||
module: N.Module = N.Module(body=body, type_ignores=[])
|
||
return module
|
||
|
||
# ==================================================================
|
||
# 语句分派
|
||
# ==================================================================
|
||
def ParseStatement(self) -> N.stmt:
|
||
"""解析单条语句(含复合语句分派)。"""
|
||
tok: Token = self._Peek()
|
||
if tok.Type == NAME:
|
||
v: str = tok.Value
|
||
# extern 函数声明(FFI,等价 t.State)——必须在软关键字 def 分派之前
|
||
if v == 'extern':
|
||
return self._ParseExternDef()
|
||
# export 函数声明(等价 t.CExport,裸名导出不增加 SHA1 头)——
|
||
# 必须在软关键字 def 分派之前
|
||
if v == 'export':
|
||
return self._ParseExportDef()
|
||
# static 存储类软关键字(已从"注解位置"迁出为**语句起始前置**修饰符):
|
||
# 仅 `static def 名字` 形态判定为函数定义——判据必须同时要求第二个 token
|
||
# 是 `def`,故 `static = 5` / `static x = 5` 不会被误吃成函数定义。
|
||
# 变量形式 `static 名字: 类型 = 值` 不在本层处理,由 SymbolUtils 的前置
|
||
# 修饰符展开统一脱糖为 `名字: t.CStatic | 类型 = 值`。
|
||
if (v == 'static' and self._Peek(1).Type == NAME
|
||
and self._Peek(1).Value == 'def'):
|
||
return self._ParseStaticDef()
|
||
# define 宏函数定义(与 def 语义一致,返回注解自动追加 t.CDefine
|
||
# 标记为宏函数;软关键字:仅"define + 标识符"形态才判定为函数定义,
|
||
# 变量名/调用(define = 5、define(x))不受影响)——必须在软关键字
|
||
# def 分派之前
|
||
if v == 'define' and self._Peek(1).Type == NAME:
|
||
return self._ParseDefineDef()
|
||
# virtual / novirtual 修饰符(软关键字):只在"语句起始 + 紧跟 class/def"
|
||
# 时生效,合成既有装饰器 @t.CVTable / @t.NoVTable(见 _ParseVirtualModified)。
|
||
# 放在 def/class 分派之前:修饰符必须先被消费,class/def 才能照常分派。
|
||
# 与软关键字 fn→def 的先后关系:`.vp` 源码在进入本解析器之前已过
|
||
# SymbolUtils.ExpandSoftKeywords(fn/fun/func/function → def),
|
||
# 故 `virtual fn m()` 在此已是 `virtual def m()`;本层只认 class/def。
|
||
if v in _VIRTUAL_MARKERS:
|
||
return self._ParseVirtualModified()
|
||
# 软关键字函数定义:fn/fun/func/function + 标识符
|
||
if v in SOFT_KEYWORDS and self._Peek(1).Type == NAME:
|
||
return self._ParseFunctionDef()
|
||
if v == 'async':
|
||
nxt: Token = self._Peek(1)
|
||
if nxt.Type == NAME and nxt.Value == 'def':
|
||
return self._ParseFunctionDef(is_async=True)
|
||
if nxt.Type == NAME and nxt.Value == 'for':
|
||
return self._ParseFor(is_async=True)
|
||
if nxt.Type == NAME and nxt.Value == 'with':
|
||
return self._ParseWith(is_async=True)
|
||
if v == 'def':
|
||
return self._ParseFunctionDef()
|
||
if v == 'class':
|
||
return self._ParseClassDef()
|
||
# struct 纯数据类型定义(软关键字):仅"语句起始 + 后随标识符"时判定,
|
||
# 故 `struct = 5` / `x.struct` 等作标识符的写法逐字不变。
|
||
if v == _STRUCT_KEYWORD and self._Peek(1).Type == NAME:
|
||
return self._ParseStructDef()
|
||
if v == 'if':
|
||
return self._ParseIf()
|
||
if v == 'while':
|
||
return self._ParseWhile()
|
||
if v == 'for':
|
||
return self._ParseFor()
|
||
if v == 'try':
|
||
return self._ParseTry()
|
||
if v == 'with':
|
||
return self._ParseWith()
|
||
if v == 'match':
|
||
# 软关键字 match:尝试 match 语句,失败回退为表达式
|
||
save: int = self.Idx
|
||
try:
|
||
return self._ParseMatch()
|
||
except SyntaxError:
|
||
self.Idx = save
|
||
if v == 'return':
|
||
return self._ParseReturn()
|
||
if v == 'raise':
|
||
return self._ParseRaise()
|
||
if v == 'import':
|
||
return self._ParseImport()
|
||
if v == 'from':
|
||
return self._ParseImportFrom()
|
||
if v == 'global':
|
||
return self._ParseGlobal('global')
|
||
if v == 'nonlocal':
|
||
return self._ParseGlobal('nonlocal')
|
||
if v == 'del':
|
||
return self._ParseDelete()
|
||
if v == 'assert':
|
||
return self._ParseAssert()
|
||
if v == 'pass':
|
||
self._Next()
|
||
node: N.Pass = N.Pass()
|
||
self._FixLoc(node, tok)
|
||
return node
|
||
if v == 'break':
|
||
self._Next()
|
||
node = N.Break()
|
||
self._FixLoc(node, tok)
|
||
return node
|
||
if v == 'continue':
|
||
self._Next()
|
||
node = N.Continue()
|
||
self._FixLoc(node, tok)
|
||
return node
|
||
if v == 'yield':
|
||
# yield 作为表达式语句
|
||
return self._ParseExprStmt()
|
||
if tok.Type == OP and tok.Value == '@':
|
||
return self._ParseDecorated()
|
||
return self._ParseSimpleStmt()
|
||
|
||
# ==================================================================
|
||
# 简单语句
|
||
# ==================================================================
|
||
def _ParseSimpleStmt(self) -> N.stmt:
|
||
"""解析简单语句(赋值/增强赋值/带注解赋值/表达式)。
|
||
|
||
不消费分号/NEWLINE,由调用方循环(ParseModule/_ParseSuite)统一处理。
|
||
"""
|
||
start: Token = self._Peek()
|
||
# 尝试带注解赋值 x: T [= v]
|
||
ann: N.stmt | None = self._TryParseAnnAssign()
|
||
if ann is not None:
|
||
return ann
|
||
# 表达式/赋值(testlist_star_expr)
|
||
expr: N.expr = self.ParseExprList()
|
||
# 赋值链
|
||
if self._AtOp('='):
|
||
targets: list[N.expr] = [self._AsLoadTarget(expr)]
|
||
while True:
|
||
self._Next() # '='
|
||
value: N.expr = self.ParseExprList()
|
||
if self._AtOp('='):
|
||
targets.append(self._AsLoadTarget(value))
|
||
continue
|
||
break
|
||
node: N.Assign = N.Assign(targets=targets, value=value, type_comment=None)
|
||
self._FixLoc(node, start)
|
||
stmt = node
|
||
elif self._AtOp('+='):
|
||
self._Next()
|
||
value = self.ParseExpr()
|
||
node = N.AugAssign(target=self._AsLoadTarget(expr), op=N.Add(), value=value)
|
||
self._FixLoc(node, start)
|
||
stmt = node
|
||
elif self._AtOp('-='):
|
||
self._Next()
|
||
value = self.ParseExpr()
|
||
node = N.AugAssign(target=self._AsLoadTarget(expr), op=N.Sub(), value=value)
|
||
self._FixLoc(node, start)
|
||
stmt = node
|
||
elif self._AtOp('*='):
|
||
self._Next()
|
||
value = self.ParseExpr()
|
||
node = N.AugAssign(target=self._AsLoadTarget(expr), op=N.Mult(), value=value)
|
||
self._FixLoc(node, start)
|
||
stmt = node
|
||
elif self._AtOp('/='):
|
||
self._Next()
|
||
value = self.ParseExpr()
|
||
node = N.AugAssign(target=self._AsLoadTarget(expr), op=N.Div(), value=value)
|
||
self._FixLoc(node, start)
|
||
stmt = node
|
||
elif self._AtOp('//='):
|
||
self._Next()
|
||
value = self.ParseExpr()
|
||
node = N.AugAssign(target=self._AsLoadTarget(expr), op=N.FloorDiv(), value=value)
|
||
self._FixLoc(node, start)
|
||
stmt = node
|
||
elif self._AtOp('%='):
|
||
self._Next()
|
||
value = self.ParseExpr()
|
||
node = N.AugAssign(target=self._AsLoadTarget(expr), op=N.Mod(), value=value)
|
||
self._FixLoc(node, start)
|
||
stmt = node
|
||
elif self._AtOp('**='):
|
||
self._Next()
|
||
value = self.ParseExpr()
|
||
node = N.AugAssign(target=self._AsLoadTarget(expr), op=N.Pow(), value=value)
|
||
self._FixLoc(node, start)
|
||
stmt = node
|
||
elif self._AtOp('<<='):
|
||
self._Next()
|
||
value = self.ParseExpr()
|
||
node = N.AugAssign(target=self._AsLoadTarget(expr), op=N.LShift(), value=value)
|
||
self._FixLoc(node, start)
|
||
stmt = node
|
||
elif self._AtOp('>>='):
|
||
self._Next()
|
||
value = self.ParseExpr()
|
||
node = N.AugAssign(target=self._AsLoadTarget(expr), op=N.RShift(), value=value)
|
||
self._FixLoc(node, start)
|
||
stmt = node
|
||
elif self._AtOp('&='):
|
||
self._Next()
|
||
value = self.ParseExpr()
|
||
node = N.AugAssign(target=self._AsLoadTarget(expr), op=N.BitAnd(), value=value)
|
||
self._FixLoc(node, start)
|
||
stmt = node
|
||
elif self._AtOp('|='):
|
||
self._Next()
|
||
value = self.ParseExpr()
|
||
node = N.AugAssign(target=self._AsLoadTarget(expr), op=N.BitOr(), value=value)
|
||
self._FixLoc(node, start)
|
||
stmt = node
|
||
elif self._AtOp('^='):
|
||
self._Next()
|
||
value = self.ParseExpr()
|
||
node = N.AugAssign(target=self._AsLoadTarget(expr), op=N.BitXor(), value=value)
|
||
self._FixLoc(node, start)
|
||
stmt = node
|
||
elif self._AtOp('@='):
|
||
self._Next()
|
||
value = self.ParseExpr()
|
||
node = N.AugAssign(target=self._AsLoadTarget(expr), op=N.MatMult(), value=value)
|
||
self._FixLoc(node, start)
|
||
stmt = node
|
||
else:
|
||
node = N.Expr(value=expr)
|
||
self._FixLoc(node, start)
|
||
stmt = node
|
||
return stmt
|
||
|
||
def _AsLoadTarget(self, expr: N.expr) -> N.expr:
|
||
"""将已解析表达式(Load 上下文)转为赋值目标(Store 上下文)。"""
|
||
self._SetStoreContext(expr)
|
||
return expr
|
||
|
||
def _TryParseAnnAssign(self) -> N.AnnAssign | None:
|
||
"""尝试解析带注解赋值:x: T [= v] 或 x: T(目标为 Name/Attribute/Subscript)。
|
||
|
||
注意:仅当目标为简单 Name/Attribute/Subscript 且后随 ':' 时匹配;
|
||
元组/列表解包不做注解(返回 None 走普通路径)。
|
||
"""
|
||
start: Token = self._Peek()
|
||
# 名称
|
||
if self._At(NAME) and self._Peek(1).Type == OP and self._Peek(1).Value == ':':
|
||
name_tok: Token = self._Next()
|
||
self._Next() # ':'
|
||
annotation: N.expr = self._ParseTypeAnnotation()
|
||
value: N.expr | None = None
|
||
if self._AtOp('='):
|
||
self._Next()
|
||
value = self.ParseExpr()
|
||
target: N.Name = N.Name(id=name_tok.Value, ctx=N.Store())
|
||
self._FixLoc(target, name_tok)
|
||
node: N.AnnAssign = N.AnnAssign(target=target, annotation=annotation,
|
||
value=value, simple=1)
|
||
self._FixLoc(node, start)
|
||
return node
|
||
# 属性/下标目标(无简单解包)
|
||
if self._At(NAME) or (self._At(OP) and self._Peek().Value in ('(', '[')):
|
||
save: int = self.Idx
|
||
try:
|
||
expr: N.expr = self.ParseExpr()
|
||
except SyntaxError:
|
||
self.Idx = save
|
||
return None
|
||
if self._AtOp(':') and isinstance(expr, (N.Name, N.Attribute, N.Subscript)):
|
||
self._Next()
|
||
annotation = self._ParseTypeAnnotation()
|
||
value = None
|
||
if self._AtOp('='):
|
||
self._Next()
|
||
value = self.ParseExpr()
|
||
self._SetStoreContext(expr)
|
||
simple: int = 1 if isinstance(expr, N.Name) else 0
|
||
node = N.AnnAssign(target=expr, annotation=annotation, value=value, simple=simple)
|
||
self._FixLoc(node, start)
|
||
return node
|
||
self.Idx = save
|
||
return None
|
||
|
||
# ------------------------------------------------------------------
|
||
# return / raise / import / global / del / assert
|
||
# ------------------------------------------------------------------
|
||
def _ParseReturn(self) -> N.Return:
|
||
start: Token = self._Next() # 'return'
|
||
value: N.expr | None = None
|
||
if not self._At(NEWLINE) and not self._AtOp(';'):
|
||
value = self.ParseExprList()
|
||
node: N.Return = N.Return(value=value)
|
||
self._FixLoc(node, start)
|
||
return node
|
||
|
||
def _ParseRaise(self) -> N.Raise:
|
||
start: Token = self._Next() # 'raise'
|
||
exc: N.expr | None = None
|
||
cause: N.expr | None = None
|
||
if not self._At(NEWLINE) and not self._AtOp(';'):
|
||
exc = self.ParseExpr()
|
||
if self._AtName('from'):
|
||
self._Next()
|
||
cause = self.ParseExpr()
|
||
node: N.Raise = N.Raise(exc=exc, cause=cause)
|
||
self._FixLoc(node, start)
|
||
return node
|
||
|
||
def _ParseImport(self) -> N.Import:
|
||
start: Token = self._Next() # 'import'
|
||
names: list[N.alias] = []
|
||
while True:
|
||
# 点分模块名:alias 位置从首个名称起、到末个名称(或 asname)止
|
||
name_tok: Token = self._ExpectName()
|
||
end_tok: Token = name_tok
|
||
name_parts: list[str] = [name_tok.Value]
|
||
while self._AtOp('.'):
|
||
self._Next()
|
||
end_tok = self._ExpectName()
|
||
name_parts.append(end_tok.Value)
|
||
full: str = '.'.join(name_parts)
|
||
asname: str | None = None
|
||
if self._AtName('as'):
|
||
self._Next()
|
||
as_tok: Token = self._ExpectName()
|
||
asname = as_tok.Value
|
||
end_tok = as_tok
|
||
alias: N.alias = N.alias(name=full, asname=asname)
|
||
self._FixLoc(alias, name_tok, end_tok)
|
||
names.append(alias)
|
||
if self._AtOp(','):
|
||
self._Next()
|
||
continue
|
||
break
|
||
node: N.Import = N.Import(names=names)
|
||
self._FixLoc(node, start)
|
||
return node
|
||
|
||
def _ParseImportFrom(self) -> N.ImportFrom:
|
||
start: Token = self._Next() # 'from'
|
||
level: int = 0
|
||
while self._AtOp('.'):
|
||
self._Next()
|
||
level += 1
|
||
module: str | None = None
|
||
# from . import x → 点后直接是 import(无模块名)
|
||
if not self._AtName('import'):
|
||
parts: list[str] = [self._ExpectName().Value]
|
||
while self._AtOp('.'):
|
||
self._Next()
|
||
parts.append(self._ExpectName().Value)
|
||
module = '.'.join(parts)
|
||
self._ExpectName('import')
|
||
names: list[N.alias] = []
|
||
# from x import * | (a, b) | a, b
|
||
if self._AtOp('('):
|
||
self._Next()
|
||
self._SkipNL()
|
||
if not self._AtOp(')'):
|
||
while True:
|
||
names.append(self._ParseImportAlias())
|
||
self._SkipNL()
|
||
if self._AtOp(','):
|
||
self._Next()
|
||
self._SkipNL()
|
||
if self._AtOp(')'):
|
||
break
|
||
continue
|
||
break
|
||
self._ExpectOp(')')
|
||
else:
|
||
if self._AtOp('*'):
|
||
star_tok: Token = self._Next()
|
||
alias = N.alias(name='*', asname=None)
|
||
self._FixLoc(alias, star_tok)
|
||
names.append(alias)
|
||
else:
|
||
while True:
|
||
names.append(self._ParseImportAlias())
|
||
if self._AtOp(','):
|
||
self._Next()
|
||
continue
|
||
break
|
||
node: N.ImportFrom = N.ImportFrom(module=module, names=names, level=level)
|
||
self._FixLoc(node, start)
|
||
return node
|
||
|
||
def _ParseImportAlias(self) -> N.alias:
|
||
"""解析单个 import 别名:name [as alias]。
|
||
|
||
alias 节点位置与 CPython 一致:起点为首个名称 token,
|
||
终点为 asname(存在时)或名称结束 token。
|
||
"""
|
||
name_tok: Token = self._ExpectName()
|
||
asname: str | None = None
|
||
end_tok: Token = name_tok
|
||
if self._AtName('as'):
|
||
self._Next()
|
||
as_tok: Token = self._ExpectName()
|
||
asname = as_tok.Value
|
||
end_tok = as_tok
|
||
alias: N.alias = N.alias(name=name_tok.Value, asname=asname)
|
||
self._FixLoc(alias, name_tok, end_tok)
|
||
return alias
|
||
|
||
def _ParseGlobal(self, kw: str) -> N.AST:
|
||
start: Token = self._Next()
|
||
names: list[str] = []
|
||
while True:
|
||
names.append(self._ExpectName().Value)
|
||
if self._AtOp(','):
|
||
self._Next()
|
||
continue
|
||
break
|
||
node: N.AST = N.Global(names=names) if kw == 'global' else N.Nonlocal(names=names)
|
||
self._FixLoc(node, start)
|
||
return node
|
||
|
||
def _ParseDelete(self) -> N.Delete:
|
||
start: Token = self._Next() # 'del'
|
||
targets: list[N.expr] = []
|
||
while True:
|
||
expr: N.expr = self.ParseExpr()
|
||
self._SetDelContext(expr)
|
||
targets.append(expr)
|
||
if self._AtOp(','):
|
||
self._Next()
|
||
continue
|
||
break
|
||
node: N.Delete = N.Delete(targets=targets)
|
||
self._FixLoc(node, start)
|
||
return node
|
||
|
||
def _SetDelContext(self, node: N.AST) -> None:
|
||
"""将 Load 上下文节点递归改为 Del(del 语句目标)。"""
|
||
if isinstance(node, N.Name):
|
||
node.ctx = N.Del()
|
||
elif isinstance(node, N.Attribute):
|
||
node.ctx = N.Del()
|
||
elif isinstance(node, N.Subscript):
|
||
node.ctx = N.Del()
|
||
elif isinstance(node, (N.Tuple, N.List)):
|
||
node.ctx = N.Del()
|
||
for elt in node.elts:
|
||
self._SetDelContext(elt)
|
||
|
||
def _ParseAssert(self) -> N.Assert:
|
||
start: Token = self._Next() # 'assert'
|
||
test: N.expr = self.ParseExpr()
|
||
msg: N.expr | None = None
|
||
if self._AtOp(','):
|
||
self._Next()
|
||
msg = self.ParseExpr()
|
||
node: N.Assert = N.Assert(test=test, msg=msg)
|
||
self._FixLoc(node, start)
|
||
return node
|
||
|
||
def _ConsumeSemicolons(self) -> None:
|
||
"""消费语句后的分号(允许空语句 ;;)。"""
|
||
while self._AtOp(';'):
|
||
self._Next()
|
||
|
||
# ------------------------------------------------------------------
|
||
# 表达式语句(含 yield)
|
||
# ------------------------------------------------------------------
|
||
def _ParseExprStmt(self) -> N.stmt:
|
||
start: Token = self._Peek()
|
||
# yield 语句:yield [expr_list]
|
||
if self._AtName('yield'):
|
||
expr: N.expr = self._ParseYield()
|
||
else:
|
||
expr = self.ParseExprList()
|
||
node: N.Expr = N.Expr(value=expr)
|
||
self._FixLoc(node, start)
|
||
return node
|
||
|
||
# ==================================================================
|
||
# 装饰器
|
||
# ==================================================================
|
||
def _ParseDecorated(self) -> N.stmt:
|
||
"""解析装饰器列表 + def/class。"""
|
||
decorator_list: list[N.expr] = []
|
||
while self._AtOp('@'):
|
||
deco_start: Token = self._Next() # '@'
|
||
deco: N.expr = self.ParseExpr()
|
||
self._FixLoc(deco, deco_start)
|
||
decorator_list.append(deco)
|
||
self._ExpectNEWLINE()
|
||
self._SkipNEWLINE()
|
||
tok: Token = self._Peek()
|
||
if tok.Type == NAME and tok.Value == 'extern':
|
||
return self._ParseExternDef(decorators=decorator_list)
|
||
if tok.Type == NAME and tok.Value == 'export':
|
||
return self._ParseExportDef(decorators=decorator_list)
|
||
if (tok.Type == NAME and tok.Value == 'static' and self._Peek(1).Type == NAME
|
||
and self._Peek(1).Value == 'def'):
|
||
return self._ParseStaticDef(decorators=decorator_list)
|
||
if tok.Type == NAME and tok.Value == 'define':
|
||
return self._ParseDefineDef(decorators=decorator_list)
|
||
if tok.Type == NAME and tok.Value == 'def':
|
||
return self._ParseFunctionDef(decorators=decorator_list)
|
||
if tok.Type == NAME and tok.Value == 'async':
|
||
nxt: Token = self._Peek(1)
|
||
if nxt.Type == NAME and nxt.Value == 'def':
|
||
return self._ParseFunctionDef(is_async=True, decorators=decorator_list)
|
||
if tok.Type == NAME and tok.Value == 'class':
|
||
return self._ParseClassDef(decorators=decorator_list)
|
||
if tok.Type == NAME and tok.Value == _STRUCT_KEYWORD and self._Peek(1).Type == NAME:
|
||
return self._ParseStructDef(decorators=decorator_list)
|
||
if tok.Type == NAME and tok.Value in _VIRTUAL_MARKERS:
|
||
# `@deco` 与 `virtual class`/`virtual def` 组合:合成标记追加在既有装饰器之后
|
||
return self._ParseVirtualModified(decorators=decorator_list)
|
||
raise SyntaxError(f"decorator must be followed by def/class, got {tok.Value!r}")
|
||
|
||
# ==================================================================
|
||
# virtual / novirtual 修饰符 + class/def
|
||
# ==================================================================
|
||
def _SourceLine(self, lineno: int) -> str:
|
||
"""取 lineno 行的源码文本(去掉行终止符),供 fail-fast 报文附源码片段。"""
|
||
starts: list[int] = self.LineStarts
|
||
if lineno < 1 or lineno > len(starts):
|
||
return ''
|
||
begin: int = starts[lineno - 1]
|
||
end: int = starts[lineno] if lineno < len(starts) else len(self.Source)
|
||
return self.Source[begin:end].rstrip('\n').rstrip('\r')
|
||
|
||
def _VirtualSyntaxError(self, detail: str, tok: Token, extra: str = '') -> SyntaxError:
|
||
"""构造 virtual/novirtual 修饰符非法形态的 fail-fast 报文。
|
||
|
||
报文含 行:列 + 源码片段 + 该修饰符允许出现的位置 + 正确示例;行尾另附
|
||
`at line N`(TPC 既有诊断路径按该正则兜底提取行号,见
|
||
HandlesImports._ParseFailureSite),文件名由调用方补全。
|
||
"""
|
||
return SyntaxError(
|
||
f"[TPC] virtual/novirtual 修饰符非法({detail}):行 {tok.Line} 列 {tok.Col},"
|
||
f"源码片段 {self._SourceLine(tok.Line).strip()!r}。"
|
||
f"{_VIRTUAL_POSITION_HINT}。正确示例:{_VIRTUAL_EXAMPLE}。"
|
||
+ (f" {extra}" if extra else '')
|
||
+ " (修饰符不会被静默忽略:非法形态一律终止编译)"
|
||
+ f" at line {tok.Line}"
|
||
)
|
||
|
||
def _ParseVirtualModified(self, decorators: list[N.expr] | None = None) -> N.stmt:
|
||
"""`virtual class` / `novirtual class` / `virtual def` / `novirtual def`。
|
||
|
||
合成 `@t.CVTable` / `@t.NoVTable` 装饰器后交给 _ParseClassDef /
|
||
_ParseFunctionDef,产出与**手写同名装饰器**逐字段同形的 AST
|
||
(decorator_list 多一个 Attribute(Name('t', Load), 'CVTable'|'NoVTable', Load)),
|
||
故下游(_resolve_class_flags / _should_method_be_virtual 同族判据)零新增分支。
|
||
|
||
非法形态**响亮失败**(不静默丢修饰符):
|
||
- 后随非 class/def;
|
||
- `virtual def` / `novirtual def` 不在类体内(模块级 / 函数体 / 分支体)。
|
||
"""
|
||
mod: Token = self._Peek() # virtual / novirtual
|
||
nxt: Token = self._Peek(1)
|
||
marker: str = _VIRTUAL_MARKERS[mod.Value]
|
||
# 后随必须是 class / def;def 的别名 fn/fun/func/function 同样接受
|
||
# (与 TPV 的 _is_funcdef_kw 口径一致)。注意:ExpandSoftKeywords 的
|
||
# fn→def 展开只命中**行首**的 fn,`virtual fn m()` 不会被先展开,
|
||
# 故此处必须显式接受别名(否则两端对同一源码一端报错一端通过)。
|
||
if nxt.Type != NAME or (nxt.Value != 'class'
|
||
and nxt.Value != 'def'
|
||
and nxt.Value not in _DEF_ALIASES):
|
||
nxt_text: str = ('(行尾)' if nxt.Type in (NEWLINE, NL, ENDMARKER)
|
||
else repr(nxt.Value))
|
||
if nxt.Type == NAME and nxt.Value == _STRUCT_KEYWORD:
|
||
# struct 是纯数据类型:禁止 virtual / novirtual 修饰(用户 2026-10-01 定)。
|
||
# 单独报文(而不是落到通用"后随非 class/def")以便直接给出修法。
|
||
raise self._StructSyntaxError(
|
||
f'{mod.Value} struct 非法:struct 禁止 virtual/novirtual 修饰', mod,
|
||
'struct 天然无虚表(解析期已合成 @t.NoVTable),请直接写 `struct X:`;'
|
||
'需要虚表语义请改用 class。')
|
||
raise self._VirtualSyntaxError(
|
||
f"{mod.Value!r} 后随 {nxt_text}(非 class/def)", mod,
|
||
f"`{mod.Value}` 只在 class/def(def 的别名 fn/fun/func/function)"
|
||
f"之前生效;若此处本意是普通标识符名,"
|
||
f"请改名(该词在语句起始会被判为修饰符)。")
|
||
if nxt.Value == 'def' and not self._InClassBody:
|
||
raise self._VirtualSyntaxError(
|
||
f'{mod.Value} def 出现在类体之外', mod,
|
||
f'{mod.Value} def 描述的是「类体内方法的虚/非虚」,'
|
||
f'模块级 / 函数体 / 分支体内没有可归属的类。')
|
||
self._Next() # 消费修饰符
|
||
deco: N.expr = N.Attribute(value=N.Name(id='t', ctx=N.Load()),
|
||
attr=marker, ctx=N.Load())
|
||
self._FixLoc(deco, mod)
|
||
deco_list: list[N.expr] = list(decorators or [])
|
||
deco_list.append(deco)
|
||
if nxt.Value == 'class':
|
||
return self._ParseClassDef(decorators=deco_list)
|
||
return self._ParseFunctionDef(decorators=deco_list)
|
||
|
||
# ==================================================================
|
||
# struct 纯数据类型定义(软关键字:`struct X:` / `struct X(Base):`)
|
||
# ==================================================================
|
||
def _StructSyntaxError(self, detail: str, tok: Token, extra: str = '') -> SyntaxError:
|
||
"""构造 struct 非法形态的 fail-fast 报文(与 _VirtualSyntaxError 同结构)。
|
||
|
||
含 行:列 + 源码片段 + struct 的三条约束 + 正确示例;行尾附 `at line N`
|
||
供 TPC 既有诊断路径兜底提取行号(见 HandlesImports._ParseFailureSite)。
|
||
"""
|
||
return SyntaxError(
|
||
f"[TPC] struct 定义非法({detail}):行 {tok.Line} 列 {tok.Col},"
|
||
f"源码片段 {self._SourceLine(tok.Line).strip()!r}。"
|
||
f"struct 与 class 同形,但**只能表示数据类型**:"
|
||
f"① 禁止 virtual / novirtual 修饰(struct 天然无虚表,已内置 @t.NoVTable);"
|
||
f"② 禁止在 struct 内定义 def(需要方法请改用 class);"
|
||
f"③ 允许在 REnum 内作为变体、也允许 struct 内再写 struct。"
|
||
f"正确示例:{_STRUCT_EXAMPLE}。"
|
||
+ (f" {extra}" if extra else '')
|
||
+ " (struct 的约束不会被静默忽略:非法形态一律终止编译)"
|
||
+ f" at line {tok.Line}"
|
||
)
|
||
|
||
def _ParseStructDef(self, decorators: list[N.expr] | None = None) -> N.ClassDef:
|
||
"""`struct X:` / `struct X(Base):` → 与 `@t.NoVTable class X(t.CStruct):`
|
||
逐字段同形的 ClassDef(与 TPV 的 _parse_structdef 同口径)。
|
||
|
||
实现:复用 _ParseClassDef 的完整解析路径(类型参数 / 基类 / 关键字实参 /
|
||
类体套件),只在**解析前后**补三件事:
|
||
· 解析前把合成的 `@t.NoVTable` 追加进装饰器列表(与 virtual/novirtual
|
||
同款表示)⇒ 类级强制无虚表,即使父类带虚表;
|
||
· 解析后把 `t.CStruct` 标记基类**追加**到 bases 末尾(不提位用户基类,
|
||
真实父类解析顺序不变)⇒ 数据类语义(_MARKER_BASES 认它,不当真实父类);
|
||
· 解析后校验类体**直接子节点**里没有 def —— struct 只能表示数据类型。
|
||
_ParseClassDef 的首个 `_Next()` 消费的正是 `struct` 关键字本身,故本函数
|
||
不预先消费,行号/列号与写法均继承自该 token。
|
||
"""
|
||
start: Token = self._Peek() # struct(由 _ParseClassDef 的 _Next() 消费)
|
||
deco: N.expr = N.Attribute(value=N.Name(id='t', ctx=N.Load()),
|
||
attr='NoVTable', ctx=N.Load())
|
||
self._FixLoc(deco, start)
|
||
deco_list: list[N.expr] = list(decorators or [])
|
||
deco_list.append(deco)
|
||
node: N.ClassDef = self._ParseClassDef(decorators=deco_list)
|
||
marker_base: N.expr = N.Attribute(value=N.Name(id='t', ctx=N.Load()),
|
||
attr='CStruct', ctx=N.Load())
|
||
self._FixLoc(marker_base, start)
|
||
node.bases.append(marker_base)
|
||
for item in node.body:
|
||
if isinstance(item, N.FunctionDef):
|
||
bad: Token = start
|
||
raise self._StructSyntaxError(
|
||
f"struct {node.name!r} 内定义 def {getattr(item, 'name', '(anonymous)')!r}",
|
||
bad,
|
||
'struct 只表示数据类型(与 C 的 struct 同口径);'
|
||
'若确实需要方法与虚表,请把该类型写成 class。')
|
||
return node
|
||
|
||
# ==================================================================
|
||
# 函数定义(含软关键字 fn/fun/func/function)
|
||
# ==================================================================
|
||
def _ParseFunctionDef(self, is_async: bool = False,
|
||
decorators: list[N.expr] | None = None) -> N.FunctionDef:
|
||
start: Token = self._Next() # def/fn/...(或 async)
|
||
if start.Value == 'async':
|
||
start = self._Next() # def
|
||
name_tok: Token = self._ExpectName()
|
||
# 类型参数(PEP 695):def f[T](
|
||
type_params: list[N.AST] = []
|
||
if self._AtOp('['):
|
||
type_params = self._ParseTypeParams()
|
||
args: N.arguments = self._ParseArguments()
|
||
returns: N.expr | None = None
|
||
if self._AtOp('->'):
|
||
self._Next()
|
||
returns = self._ParseTypeAnnotation()
|
||
self._ExpectOp(':')
|
||
body: list[N.stmt] = self._ParseSuite()
|
||
cls: type = N.AsyncFunctionDef if is_async else N.FunctionDef
|
||
node: N.FunctionDef = cls(
|
||
name=name_tok.Value, args=args, body=body,
|
||
decorator_list=decorators or [], returns=returns,
|
||
type_comment=None, type_params=type_params,
|
||
)
|
||
self._FixLoc(node, start)
|
||
return node
|
||
|
||
def _ParseExternDef(self, decorators: list[N.expr] | None = None) -> N.FunctionDef:
|
||
"""解析 extern 函数声明(FFI,与 t.State 作用相当)。
|
||
|
||
允许语法(均声明不定义,不生成函数体):
|
||
extern def A(x: int) -> int: pass
|
||
extern A(x: int) -> int: pass
|
||
extern def A(x: int) -> int
|
||
extern A(x: int) -> int
|
||
|
||
产出标准 FunctionDef 节点,返回注解改写为 ``<原类型> | t.State``
|
||
以复用 TPC 的 IsState 路径;并设自定义属性 ``Extern = True``
|
||
供 Unparser 还原 extern 语法。
|
||
"""
|
||
start: Token = self._Next() # 'extern'
|
||
if self._AtName('def'):
|
||
self._Next()
|
||
name_tok: Token = self._ExpectName()
|
||
type_params: list[N.AST] = []
|
||
if self._AtOp('['):
|
||
type_params = self._ParseTypeParams()
|
||
args: N.arguments = self._ParseArguments()
|
||
returns: N.expr | None = None
|
||
if self._AtOp('->'):
|
||
self._Next()
|
||
returns = self._ParseTypeAnnotation()
|
||
body: list[N.stmt] = []
|
||
if self._AtOp(':'):
|
||
self._Next()
|
||
body = self._ParseSuite()
|
||
node: N.FunctionDef = N.FunctionDef(
|
||
name=name_tok.Value, args=args, body=body,
|
||
decorator_list=decorators or [], returns=returns,
|
||
type_comment=None, type_params=type_params,
|
||
)
|
||
node.Extern = True
|
||
# 返回注解改写为 <原类型> | t.State,使 TPC 识别为 State 函数(仅声明)
|
||
state_attr: N.expr = N.Attribute(value=N.Name(id='t', ctx=N.Load()),
|
||
attr='State', ctx=N.Load())
|
||
if returns is not None:
|
||
node.returns = N.BinOp(left=returns, op=N.BitOr(), right=state_attr)
|
||
else:
|
||
node.returns = state_attr
|
||
self._FixLoc(node, start)
|
||
return node
|
||
|
||
def _ParseExportDef(self, decorators: list[N.expr] | None = None) -> N.FunctionDef:
|
||
"""解析 export 函数声明(等价 t.CExport,导出裸名、不加模块 SHA1 头)。
|
||
|
||
允许语法(与 extern 对称,配不配 def 都一样):
|
||
export def A(x: int) -> int: pass
|
||
export A(x: int) -> int: pass
|
||
export def A(x: int) -> int
|
||
export A(x: int) -> int
|
||
|
||
产出标准 FunctionDef 节点,返回注解改写为 ``<原类型> | t.CExport``
|
||
以复用 TPC 的 CExport 路径(符号名不增加 SHA1 前缀);并设自定义
|
||
属性 ``Export = True`` 供 Unparser 还原 export 语法。
|
||
"""
|
||
start: Token = self._Next() # 'export'
|
||
if self._AtName('def'):
|
||
self._Next()
|
||
name_tok: Token = self._ExpectName()
|
||
type_params: list[N.AST] = []
|
||
if self._AtOp('['):
|
||
type_params = self._ParseTypeParams()
|
||
args: N.arguments = self._ParseArguments()
|
||
returns: N.expr | None = None
|
||
if self._AtOp('->'):
|
||
self._Next()
|
||
returns = self._ParseTypeAnnotation()
|
||
body: list[N.stmt] = []
|
||
if self._AtOp(':'):
|
||
self._Next()
|
||
body = self._ParseSuite()
|
||
node: N.FunctionDef = N.FunctionDef(
|
||
name=name_tok.Value, args=args, body=body,
|
||
decorator_list=decorators or [], returns=returns,
|
||
type_comment=None, type_params=type_params,
|
||
)
|
||
node.Export = True
|
||
# 返回注解改写为 <原类型> | t.CExport,使 TPC 识别为 CExport 函数
|
||
# (导出裸名,不增加模块 SHA1 头;有条件情况下导出)
|
||
export_attr: N.expr = N.Attribute(value=N.Name(id='t', ctx=N.Load()),
|
||
attr='CExport', ctx=N.Load())
|
||
if returns is not None:
|
||
node.returns = N.BinOp(left=returns, op=N.BitOr(), right=export_attr)
|
||
else:
|
||
node.returns = export_attr
|
||
self._FixLoc(node, start)
|
||
return node
|
||
|
||
def _ParseStaticDef(self, decorators: list[N.expr] | None = None) -> N.FunctionDef:
|
||
"""解析 static 函数定义(等价 ``@t.CStatic``,与 TransPyV 的
|
||
`_ParseStaticModified` 同源)。
|
||
|
||
允许语法(与 export/extern 对称,配不配 def 都一样):
|
||
static def A(x: int) -> int: ...
|
||
static A(x: int) -> int: ...
|
||
|
||
与 export/extern 的**两处不同**:
|
||
- **不改写返回注解**(不追加 `| t.CStatic` / `| t.State`):CStatic 是
|
||
函数属性,由下面的合成装饰器承载,返回类型语义逐字不变;
|
||
- **合成 `@t.CStatic` 装饰器**追加在既有装饰器之后,产出与**手写
|
||
`@t.CStatic`** 逐字段同形(decorator_list 多一个
|
||
Attribute(Name('t', Load), 'CStatic', Load)),下游零新增判据。
|
||
|
||
另设自定义属性 ``Static = True`` 供 Unparser 还原 static 语法
|
||
(镜像 Extern/Export;因 FunctionDef 的 __slots__ 槽位未含 Static 且
|
||
补槽位需改第三个文件,此处仅在槽位存在时设置,解析不会因此中断)。
|
||
"""
|
||
start: Token = self._Next() # 'static'
|
||
if self._AtName('def'):
|
||
self._Next()
|
||
name_tok: Token = self._ExpectName()
|
||
type_params: list[N.AST] = []
|
||
if self._AtOp('['):
|
||
type_params = self._ParseTypeParams()
|
||
args: N.arguments = self._ParseArguments()
|
||
returns: N.expr | None = None
|
||
if self._AtOp('->'):
|
||
self._Next()
|
||
returns = self._ParseTypeAnnotation()
|
||
body: list[N.stmt] = []
|
||
if self._AtOp(':'):
|
||
self._Next()
|
||
body = self._ParseSuite()
|
||
# 合成 @t.CStatic 装饰器(追加在既有装饰器之后)
|
||
static_attr: N.expr = N.Attribute(value=N.Name(id='t', ctx=N.Load()),
|
||
attr='CStatic', ctx=N.Load())
|
||
self._FixLoc(static_attr, start)
|
||
deco_list: list[N.expr] = list(decorators or [])
|
||
deco_list.append(static_attr)
|
||
node: N.FunctionDef = N.FunctionDef(
|
||
name=name_tok.Value, args=args, body=body,
|
||
decorator_list=deco_list, returns=returns,
|
||
type_comment=None, type_params=type_params,
|
||
)
|
||
# 自定义属性 Static = True 供 Unparser 还原 static 语法(镜像 Extern/Export)。
|
||
# 注:FunctionDef 是 __slots__ 类(见 vast/Nodes.py 的 _FUNC_ATTRS,当前槽位
|
||
# 为 Extern/Export/Define,未含 Static)——补槽位需改第三个文件(本次不许可),
|
||
# 故仅在槽位存在时设置,避免 AttributeError 中断解析;下游对 static 函数的
|
||
# 识别靠上面合成的 @t.CStatic 装饰器(HandlesFunctions 的存储类装饰器分支),
|
||
# 与 virtual/novirtual 仅合成装饰器同构。
|
||
if hasattr(type(node), 'Static'):
|
||
node.Static = True
|
||
self._FixLoc(node, start)
|
||
return node
|
||
|
||
def _ParseDefineDef(self, decorators: list[N.expr] | None = None) -> N.FunctionDef:
|
||
"""解析 define 宏函数定义(语法与 def 完全一致,但固定为宏函数)。
|
||
|
||
允许语法(与 def 相同,函数体必填):
|
||
define A(x: int) -> int: ...
|
||
define A(x: int): ...
|
||
|
||
产出标准 FunctionDef 节点,返回注解改写为 ``<原类型> | t.CDefine``
|
||
(无显式返回注解时单独 ``t.CDefine``),使 TPC/TPV 复用既有的
|
||
CDefine 宏函数识别路径(StubGen 原样保留生成 C #define、
|
||
alwaysinline 内联);并设自定义属性 ``Define = True`` 供
|
||
Unparser 还原 define 语法。
|
||
"""
|
||
start: Token = self._Next() # 'define'
|
||
name_tok: Token = self._ExpectName()
|
||
type_params: list[N.AST] = []
|
||
if self._AtOp('['):
|
||
type_params = self._ParseTypeParams()
|
||
args: N.arguments = self._ParseArguments()
|
||
returns: N.expr | None = None
|
||
if self._AtOp('->'):
|
||
self._Next()
|
||
returns = self._ParseTypeAnnotation()
|
||
self._ExpectOp(':')
|
||
body: list[N.stmt] = self._ParseSuite()
|
||
node: N.FunctionDef = N.FunctionDef(
|
||
name=name_tok.Value, args=args, body=body,
|
||
decorator_list=decorators or [], returns=returns,
|
||
type_comment=None, type_params=type_params,
|
||
)
|
||
node.Define = True
|
||
# 返回注解改写为 <原类型> | t.CDefine,使下游识别为宏函数
|
||
# (StubGen 保留原样输出 C #define;TPC 标记 alwaysinline 内联)
|
||
define_attr: N.expr = N.Attribute(value=N.Name(id='t', ctx=N.Load()),
|
||
attr='CDefine', ctx=N.Load())
|
||
if returns is not None:
|
||
node.returns = N.BinOp(left=returns, op=N.BitOr(), right=define_attr)
|
||
else:
|
||
node.returns = define_attr
|
||
self._FixLoc(node, start)
|
||
return node
|
||
|
||
def _ParseTypeParams(self) -> list[N.AST]:
|
||
"""解析 PEP 695 类型参数:[T, *Ts, **P, U: Bound]"""
|
||
self._ExpectOp('[')
|
||
self._SkipNL()
|
||
params: list[N.AST] = []
|
||
if not self._AtOp(']'):
|
||
while True:
|
||
self._SkipNL()
|
||
star: int = 0
|
||
if self._AtOp('*'):
|
||
self._Next()
|
||
star = 1
|
||
elif self._AtOp('**'):
|
||
self._Next()
|
||
star = 2
|
||
name_tok: Token = self._ExpectName()
|
||
bound: N.expr | None = None
|
||
if star == 0 and self._AtOp(':'):
|
||
self._Next()
|
||
bound = self.ParseExpr()
|
||
if star == 1:
|
||
tvt: N.TypeVarTuple = N.TypeVarTuple(name=name_tok.Value)
|
||
self._FixLoc(tvt, name_tok)
|
||
params.append(tvt)
|
||
elif star == 2:
|
||
ps: N.ParamSpec = N.ParamSpec(name=name_tok.Value)
|
||
self._FixLoc(ps, name_tok)
|
||
params.append(ps)
|
||
else:
|
||
tv: N.TypeVar = N.TypeVar(name=name_tok.Value, bound=bound)
|
||
self._FixLoc(tv, name_tok)
|
||
params.append(tv)
|
||
self._SkipNL()
|
||
if self._AtOp(','):
|
||
self._Next()
|
||
continue
|
||
break
|
||
self._ExpectOp(']')
|
||
return params
|
||
|
||
# ==================================================================
|
||
# 类定义
|
||
# ==================================================================
|
||
def _ParseClassDef(self, decorators: list[N.expr] | None = None) -> N.ClassDef:
|
||
start: Token = self._Next() # class
|
||
name_tok: Token = self._ExpectName()
|
||
type_params: list[N.AST] = []
|
||
if self._AtOp('['):
|
||
type_params = self._ParseTypeParams()
|
||
bases: list[N.expr] = []
|
||
keywords: list[N.keyword] = []
|
||
if self._AtOp('('):
|
||
self._Next()
|
||
self._SkipNL()
|
||
if not self._AtOp(')'):
|
||
while True:
|
||
self._SkipNL()
|
||
if self._AtOp('**'):
|
||
self._Next()
|
||
value: N.expr = self.ParseExpr()
|
||
kw: N.keyword = N.keyword(arg=None, value=value)
|
||
self._FixLoc(kw, self._Prev())
|
||
keywords.append(kw)
|
||
elif self._At(NAME) and self._Peek(1).Type == OP and self._Peek(1).Value == '=':
|
||
arg_tok: Token = self._Next()
|
||
self._Next() # '='
|
||
value = self.ParseExpr()
|
||
kw = N.keyword(arg=arg_tok.Value, value=value)
|
||
self._FixLoc(kw, arg_tok)
|
||
keywords.append(kw)
|
||
else:
|
||
bases.append(self.ParseExpr())
|
||
self._SkipNL()
|
||
if self._AtOp(','):
|
||
self._Next()
|
||
self._SkipNL()
|
||
if self._AtOp(')'):
|
||
break
|
||
continue
|
||
break
|
||
self._ExpectOp(')')
|
||
self._ExpectOp(':')
|
||
body: list[N.stmt] = self._ParseSuite(owner_is_class=True)
|
||
node: N.ClassDef = N.ClassDef(
|
||
name=name_tok.Value, bases=bases, keywords=keywords, body=body,
|
||
decorator_list=decorators or [], type_params=type_params,
|
||
)
|
||
self._FixLoc(node, start)
|
||
return node
|
||
|
||
# ==================================================================
|
||
# 语句块(套件)
|
||
# ==================================================================
|
||
def _ParseSuite(self, owner_is_class: bool = False) -> list[N.stmt]:
|
||
"""解析语句块:':' NEWLINE INDENT ... DEDENT 或单行简单语句。
|
||
|
||
块首允许出现空行/注释行(NL),跳过后再检测 INDENT。
|
||
|
||
owner_is_class:本套件的**直接拥有者**是否 ClassDef。套件解析期间置
|
||
`_InClassBody`,供 `_ParseVirtualModified` 判 `virtual def`/`novirtual def`
|
||
是否"直接写在类体内"(函数体 / 分支体 / 循环体都不算)。默认 False ⇒
|
||
除类体外的所有套件拥有者天然正确,无需逐个改动。
|
||
"""
|
||
prev_in_class: bool = self._InClassBody
|
||
self._InClassBody = owner_is_class
|
||
# 已消费 ':' 后的情况:检查是否换行
|
||
if self._At(NEWLINE):
|
||
self._Next()
|
||
# 跳过空行/注释行
|
||
while self._At(NL):
|
||
self._Next()
|
||
if self._At(INDENT):
|
||
self._Next()
|
||
body: list[N.stmt] = []
|
||
self._SkipNEWLINE()
|
||
while self._Peek().Type != DEDENT and self._Peek().Type != ENDMARKER:
|
||
stmt: N.stmt = self.ParseStatement()
|
||
body.append(stmt)
|
||
# 分号分隔后续语句
|
||
while self._AtOp(';'):
|
||
self._Next()
|
||
if self._At(NEWLINE):
|
||
break
|
||
extra: N.stmt = self.ParseStatement()
|
||
body.append(extra)
|
||
self._SkipNEWLINE()
|
||
self._Expect(DEDENT)
|
||
self._InClassBody = prev_in_class
|
||
return body
|
||
# 空套件(语法错误,宽松返回空)
|
||
self._InClassBody = prev_in_class
|
||
return []
|
||
# 单行套件:statement (; statement)* NEWLINE
|
||
body = [self.ParseStatement()]
|
||
while self._AtOp(';'):
|
||
self._Next()
|
||
if self._At(NEWLINE):
|
||
break
|
||
body.append(self.ParseStatement())
|
||
self._ExpectNEWLINE()
|
||
# 吞掉紧跟在单行套件之后的注释行/空行 NL:多行套件分支在 DEDENT 之前
|
||
# 已由循环内 _SkipNEWLINE 吞掉这些 NL,单行套件没有 DEDENT,若不在此吞掉,
|
||
# 调用方紧随其后的 elif/else/except/finally 续接子句会被 NL 挡住 → 分支链断裂。
|
||
self._SkipNL()
|
||
self._InClassBody = prev_in_class
|
||
return body
|
||
|
||
def _Expect(self, tok_type: str, value: str | None = None) -> Token:
|
||
"""期望指定类型的 token。"""
|
||
tok: Token = self._Next()
|
||
if tok.Type != tok_type or (value is not None and tok.Value != value):
|
||
raise SyntaxError(
|
||
f"expected {tok_type}({value!r}), got {tok.Type}({tok.Value!r}) "
|
||
f"at line {tok.Line}"
|
||
)
|
||
return tok
|
||
|
||
def _ConsumeNewlines(self) -> None:
|
||
"""消费连续 NEWLINE(空行)。"""
|
||
while self._At(NEWLINE):
|
||
self._Next()
|
||
|
||
# ==================================================================
|
||
# if / while / for / try / with / match
|
||
# ==================================================================
|
||
def _ParseIf(self) -> N.If:
|
||
start: Token = self._Next() # if
|
||
test: N.expr = self.ParseExpr()
|
||
self._ExpectOp(':')
|
||
body: list[N.stmt] = self._ParseSuite()
|
||
orelse: list[N.stmt] = []
|
||
if self._AtName('elif'):
|
||
# elif → 嵌套 If 于 orelse
|
||
orelse.append(self._ParseIf())
|
||
elif self._AtName('else'):
|
||
self._Next()
|
||
self._ExpectOp(':')
|
||
orelse = self._ParseSuite()
|
||
node: N.If = N.If(test=test, body=body, orelse=orelse)
|
||
self._FixLoc(node, start)
|
||
return node
|
||
|
||
def _ParseWhile(self) -> N.While:
|
||
start: Token = self._Next() # while
|
||
test: N.expr = self.ParseExpr()
|
||
self._ExpectOp(':')
|
||
body: list[N.stmt] = self._ParseSuite()
|
||
orelse: list[N.stmt] = []
|
||
if self._AtName('else'):
|
||
self._Next()
|
||
self._ExpectOp(':')
|
||
orelse = self._ParseSuite()
|
||
node: N.While = N.While(test=test, body=body, orelse=orelse)
|
||
self._FixLoc(node, start)
|
||
return node
|
||
|
||
def _ParseFor(self, is_async: bool = False) -> N.For:
|
||
start: Token = self._Next()
|
||
if start.Value == 'async':
|
||
start = self._Next() # for
|
||
target: N.expr = self._ParseTargetList()
|
||
self._ExpectName('in')
|
||
iterable: N.expr = self.ParseExpr()
|
||
self._ExpectOp(':')
|
||
body: list[N.stmt] = self._ParseSuite()
|
||
orelse: list[N.stmt] = []
|
||
if self._AtName('else'):
|
||
self._Next()
|
||
self._ExpectOp(':')
|
||
orelse = self._ParseSuite()
|
||
cls: type = N.AsyncFor if is_async else N.For
|
||
node: N.For = cls(target=target, iter=iterable, body=body, orelse=orelse,
|
||
type_comment=None)
|
||
self._FixLoc(node, start)
|
||
return node
|
||
|
||
def _ParseTargetList(self) -> N.expr:
|
||
"""解析 for 目标列表(支持裸元组 a, b = ...)。"""
|
||
start: Token = self._Peek()
|
||
first: N.expr = self._ParseTarget()
|
||
if self._AtOp(','):
|
||
elts: list[N.expr] = [first]
|
||
while self._AtOp(','):
|
||
self._Next()
|
||
self._SkipNL()
|
||
elts.append(self._ParseTarget())
|
||
tup: N.Tuple = N.Tuple(elts=elts, ctx=N.Store())
|
||
self._FixLoc(tup, start)
|
||
return tup
|
||
return first
|
||
|
||
def _ParseTry(self) -> N.Try:
|
||
start: Token = self._Next() # try
|
||
self._ExpectOp(':')
|
||
body: list[N.stmt] = self._ParseSuite()
|
||
handlers: list[N.ExceptHandler] = []
|
||
orelse: list[N.stmt] = []
|
||
finalbody: list[N.stmt] = []
|
||
is_star: bool = False
|
||
while self._AtName('except'):
|
||
self._Next()
|
||
if self._AtOp('*'):
|
||
is_star = True
|
||
self._Next()
|
||
handlers.append(self._ParseExceptHandler())
|
||
if self._AtName('else'):
|
||
self._Next()
|
||
self._ExpectOp(':')
|
||
orelse = self._ParseSuite()
|
||
if self._AtName('finally'):
|
||
self._Next()
|
||
self._ExpectOp(':')
|
||
finalbody = self._ParseSuite()
|
||
cls: type = N.TryStar if is_star else N.Try
|
||
node: N.Try = cls(body=body, handlers=handlers, orelse=orelse, finalbody=finalbody)
|
||
self._FixLoc(node, start)
|
||
return node
|
||
|
||
def _ParseExceptHandler(self) -> N.ExceptHandler:
|
||
start: Token = self._Peek()
|
||
exc_type: N.expr | None = None
|
||
name: str | None = None
|
||
if not self._AtOp(':'):
|
||
exc_type = self.ParseExpr()
|
||
if self._AtName('as'):
|
||
self._Next()
|
||
name = self._ExpectName().Value
|
||
self._ExpectOp(':')
|
||
handler_body: list[N.stmt] = self._ParseSuite()
|
||
node: N.ExceptHandler = N.ExceptHandler(type=exc_type, name=name, body=handler_body)
|
||
self._FixLoc(node, start)
|
||
return node
|
||
|
||
def _ParseWith(self, is_async: bool = False) -> N.With:
|
||
start: Token = self._Next()
|
||
if start.Value == 'async':
|
||
start = self._Next() # with
|
||
items: list[N.withitem] = []
|
||
while True:
|
||
ctx: N.expr = self.ParseExpr()
|
||
optional_vars: N.expr | None = None
|
||
if self._AtName('as'):
|
||
self._Next()
|
||
optional_vars = self._ParseTarget()
|
||
item: N.withitem = N.withitem(context_expr=ctx, optional_vars=optional_vars)
|
||
self._FixLoc(item, start)
|
||
items.append(item)
|
||
if self._AtOp(','):
|
||
self._Next()
|
||
continue
|
||
break
|
||
self._ExpectOp(':')
|
||
body: list[N.stmt] = self._ParseSuite()
|
||
cls: type = N.AsyncWith if is_async else N.With
|
||
node: N.With = cls(items=items, body=body, type_comment=None)
|
||
self._FixLoc(node, start)
|
||
return node
|
||
|
||
# ==================================================================
|
||
# match 语句(PEP 634)
|
||
# ==================================================================
|
||
def _ParseMatch(self) -> N.Match:
|
||
start: Token = self._Next() # match
|
||
subject: N.expr = self.ParseExpr()
|
||
self._ExpectOp(':')
|
||
self._ExpectNEWLINE()
|
||
self._Expect(INDENT)
|
||
cases: list[N.match_case] = []
|
||
self._SkipNEWLINE()
|
||
while self._AtName('case'):
|
||
case_start: Token = self._Next() # case
|
||
pattern: N.pattern = self._ParsePattern()
|
||
guard: N.expr | None = None
|
||
if self._AtName('if'):
|
||
self._Next()
|
||
guard = self.ParseExpr()
|
||
self._ExpectOp(':')
|
||
case_body: list[N.stmt] = self._ParseSuite()
|
||
mc: N.match_case = N.match_case(pattern=pattern, guard=guard, body=case_body)
|
||
self._FixLoc(mc, case_start)
|
||
cases.append(mc)
|
||
self._SkipNEWLINE()
|
||
self._Expect(DEDENT)
|
||
node: N.Match = N.Match(subject=subject, cases=cases)
|
||
self._FixLoc(node, start)
|
||
return node
|
||
|
||
def _ParsePattern(self) -> N.pattern:
|
||
"""解析 match 模式(顶层,处理 | 或模式)。"""
|
||
start: Token = self._Peek()
|
||
patterns: list[N.pattern] = [self._ParsePatternOr()]
|
||
while self._AtOp('|'):
|
||
self._Next()
|
||
patterns.append(self._ParsePatternOr())
|
||
if len(patterns) == 1:
|
||
return patterns[0]
|
||
node: N.MatchOr = N.MatchOr(patterns=patterns)
|
||
self._FixLoc(node, start)
|
||
return node
|
||
|
||
def _ParsePatternOr(self) -> N.pattern:
|
||
"""解析单模式(含 as 绑定)。"""
|
||
start: Token = self._Peek()
|
||
pat: N.pattern = self._ParsePatternAtom()
|
||
if self._AtName('as'):
|
||
self._Next()
|
||
name: str = self._ExpectName().Value
|
||
node: N.MatchAs = N.MatchAs(pattern=pat, name=name)
|
||
self._FixLoc(node, start)
|
||
return node
|
||
return pat
|
||
|
||
def _ParsePatternAtom(self) -> N.pattern:
|
||
"""解析模式原子:字面量/捕获/序列/映射/类/通配符。"""
|
||
start: Token = self._Peek()
|
||
tok: Token = self._Peek()
|
||
# 通配符 / 捕获
|
||
if tok.Type == NAME:
|
||
# 字面量 True/False/None
|
||
if tok.Value in ('True', 'False', 'None'):
|
||
self._Next()
|
||
node: N.MatchSingleton = N.MatchSingleton(
|
||
value={'True': True, 'False': False, 'None': None}[tok.Value])
|
||
self._FixLoc(node, tok)
|
||
return node
|
||
# 类模式:Name(...) 或 a.b.c(...)——扫描属性链末尾判断 '('
|
||
peek_i: int = 1
|
||
while (self._Peek(peek_i).Type == OP and self._Peek(peek_i).Value == '.'
|
||
and self._Peek(peek_i + 1).Type == NAME):
|
||
peek_i += 2
|
||
if self._Peek(peek_i).Type == OP and self._Peek(peek_i).Value == '(':
|
||
return self._ParseMatchClass()
|
||
# 属性值模式 a.b(无括号)
|
||
if self._Peek(1).Type == OP and self._Peek(1).Value == '.':
|
||
return self._ParseMatchValueAttr()
|
||
# 捕获模式(含 _ 通配符;_ 产生空 MatchAs 与 CPython 一致)
|
||
self._Next()
|
||
node = N.MatchAs(pattern=None, name=None if tok.Value == '_' else tok.Value)
|
||
self._FixLoc(node, tok)
|
||
return node
|
||
# 序列模式 [a, b] / (a, b)
|
||
if tok.Type == OP and tok.Value in ('[', '('):
|
||
return self._ParseMatchSequence()
|
||
# 映射模式 {k: v, **rest}
|
||
if tok.Type == OP and tok.Value == '{':
|
||
return self._ParseMatchMapping()
|
||
# 值模式:数字/字符串/f-string/带符号字面量(不消费顶层 '|',留给 _ParsePattern 组 MatchOr)
|
||
if tok.Type == NUMBER or tok.Type == STRING or tok.Type == FSTRING or (
|
||
tok.Type == OP and tok.Value in ('-', '+')):
|
||
value: N.expr = self._ParsePatternValue()
|
||
node = N.MatchValue(value=value)
|
||
self._FixLoc(node, start)
|
||
return node
|
||
if tok.Type == OP and tok.Value == '*':
|
||
self._Next()
|
||
name = None
|
||
if not self._At(NEWLINE) and not self._AtOp(','):
|
||
name = self._ExpectName().Value
|
||
node = N.MatchStar(name=name)
|
||
self._FixLoc(node, tok)
|
||
return node
|
||
raise SyntaxError(f"unexpected pattern token {tok.Type}({tok.Value!r})")
|
||
|
||
def _ParsePatternValue(self) -> N.expr:
|
||
"""解析值模式表达式:带可选 +/- 前缀的字面量。
|
||
|
||
与 CPython 一致:'|' 是模式或分隔符,这里只解析单个字面量
|
||
(数字/字符串/f-string),不消费顶层 '|'。
|
||
"""
|
||
start: Token = self._Peek()
|
||
tok: Token = self._Peek()
|
||
if tok.Type == OP and tok.Value in ('-', '+'):
|
||
op_tok: Token = self._Next()
|
||
inner: N.expr
|
||
if self._Peek().Type == NUMBER:
|
||
inner = self._ParseNumber()
|
||
elif self._Peek().Type in (STRING, FSTRING):
|
||
inner = self._ParseStringConcat()
|
||
else:
|
||
raise SyntaxError(
|
||
f"expected literal after '{op_tok.Value}' in pattern at line {op_tok.Line}"
|
||
)
|
||
node: N.expr
|
||
if op_tok.Value == '-':
|
||
node = N.UnaryOp(op=N.USub(), operand=inner)
|
||
else:
|
||
node = N.UnaryOp(op=N.UAdd(), operand=inner)
|
||
self._FixLoc(node, start)
|
||
return node
|
||
if tok.Type == NUMBER:
|
||
return self._ParseNumber()
|
||
return self._ParseStringConcat()
|
||
|
||
def _ParseMatchValueAttr(self) -> N.pattern:
|
||
"""解析值模式属性访问:a.b.c(无括号)。"""
|
||
start: Token = self._Peek()
|
||
expr: N.expr = self.ParseExpr()
|
||
node: N.MatchValue = N.MatchValue(value=expr)
|
||
self._FixLoc(node, start)
|
||
return node
|
||
|
||
def _ParseMatchClass(self) -> N.pattern:
|
||
"""解析类模式:Name(pos, kw=v, ...) 或 a.b.c(...)。
|
||
|
||
类名仅解析 Name/Attribute 链(不消费调用括号),括号由本方法消费。
|
||
"""
|
||
start: Token = self._Peek()
|
||
cls_name: N.expr = self._ParsePatternClassName()
|
||
self._ExpectOp('(')
|
||
self._SkipNL()
|
||
patterns: list[N.pattern] = []
|
||
kwd_attrs: list[str] = []
|
||
kwd_patterns: list[N.pattern] = []
|
||
if not self._AtOp(')'):
|
||
while True:
|
||
self._SkipNL()
|
||
if self._AtOp('*'):
|
||
self._Next()
|
||
patterns.append(self._ParsePattern())
|
||
elif self._At(NAME) and self._Peek(1).Type == OP and self._Peek(1).Value == '=':
|
||
attr_tok: Token = self._Next()
|
||
self._Next() # '='
|
||
kwd_attrs.append(attr_tok.Value)
|
||
kwd_patterns.append(self._ParsePattern())
|
||
else:
|
||
patterns.append(self._ParsePattern())
|
||
self._SkipNL()
|
||
if self._AtOp(','):
|
||
self._Next()
|
||
if self._AtOp(')'):
|
||
break
|
||
continue
|
||
break
|
||
self._ExpectOp(')')
|
||
node: N.MatchClass = N.MatchClass(cls=cls_name, patterns=patterns,
|
||
kwd_attrs=kwd_attrs, kwd_patterns=kwd_patterns)
|
||
self._FixLoc(node, start)
|
||
return node
|
||
|
||
def _ParsePatternClassName(self) -> N.expr:
|
||
"""解析类模式的类名(Name / a.b.c 属性链,不含调用括号)。"""
|
||
start: Token = self._Peek()
|
||
name_tok: Token = self._ExpectName()
|
||
node: N.expr = N.Name(id=name_tok.Value, ctx=N.Load())
|
||
self._FixLoc(node, name_tok)
|
||
while self._AtOp('.'):
|
||
self._Next()
|
||
attr_tok: Token = self._ExpectName()
|
||
attr_node: N.Attribute = N.Attribute(value=node, attr=attr_tok.Value, ctx=N.Load())
|
||
self._FixLoc(attr_node, start)
|
||
node = attr_node
|
||
return node
|
||
|
||
def _ParseMatchSequence(self) -> N.pattern:
|
||
start: Token = self._Next() # '[' 或 '('
|
||
close: str = ']' if start.Value == '[' else ')'
|
||
patterns: list[N.pattern] = []
|
||
self._SkipNL()
|
||
if not self._AtOp(close):
|
||
while True:
|
||
self._SkipNL()
|
||
patterns.append(self._ParsePattern())
|
||
self._SkipNL()
|
||
if self._AtOp(','):
|
||
self._Next()
|
||
if self._AtOp(close):
|
||
break
|
||
continue
|
||
break
|
||
self._ExpectOp(close)
|
||
node: N.MatchSequence = N.MatchSequence(patterns=patterns)
|
||
self._FixLoc(node, start)
|
||
return node
|
||
|
||
def _ParseMatchMapping(self) -> N.pattern:
|
||
start: Token = self._Next() # '{'
|
||
keys: list[N.expr] = []
|
||
patterns: list[N.pattern] = []
|
||
rest: str | None = None
|
||
self._SkipNL()
|
||
if not self._AtOp('}'):
|
||
while True:
|
||
self._SkipNL()
|
||
if self._AtOp('**'):
|
||
self._Next()
|
||
rest = self._ExpectName().Value
|
||
else:
|
||
key: N.expr = self.ParseExpr()
|
||
self._ExpectOp(':')
|
||
keys.append(key)
|
||
patterns.append(self._ParsePattern())
|
||
self._SkipNL()
|
||
if self._AtOp(','):
|
||
self._Next()
|
||
if self._AtOp('}'):
|
||
break
|
||
continue
|
||
break
|
||
self._ExpectOp('}')
|
||
node: N.MatchMapping = N.MatchMapping(keys=keys, patterns=patterns, rest=rest)
|
||
self._FixLoc(node, start)
|
||
return node
|
||
|
||
|
||
def Parse(source: str) -> N.Module:
|
||
"""便捷入口:源码字符串 → Module AST(供 vast.parse 调用)。"""
|
||
from lib.vast.Tokenizer import Tokenizer
|
||
|
||
tokens = Tokenizer(source).Tokenize()
|
||
line_starts: list[int] = [0]
|
||
# 从 token 流重建行起始表(由 Tokenizer 维护,此处直接计算)
|
||
line_starts = _ComputeLineStarts(source)
|
||
parser: Parser = Parser(tokens, source, line_starts)
|
||
return parser.ParseModule()
|
||
|
||
|
||
def _ComputeLineStarts(source: str) -> list[int]:
|
||
"""计算源码所有行起始字符索引(含首行 0)。
|
||
|
||
Uses C-level str.find instead of a per-character Python loop (large .vp
|
||
sources: millions of characters scanned once per parse).
|
||
"""
|
||
starts: list[int] = [0]
|
||
Find = source.find
|
||
i: int = Find('\n')
|
||
while i >= 0:
|
||
starts.append(i + 1)
|
||
i = Find('\n', i + 1)
|
||
return starts
|