"""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