Files
TransPyC/lib/vast/Parser.py
T
TermiNexus a8f6ca006d feat(struct): TPC 前端支持 struct 关键字(含 C 加速解析器)
与 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` 仍解析为普通赋值。
2026-10-01 23:07:49 +08:00

1544 lines
68 KiB
Python
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
"""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