Files
TransPyC/lib/vast/Nodes.py
T
TermiNexus 9605af8ea1 perf(vast): AST 节点类加 __slots__,去 __dict__ 提速降内存
AST 基类 __slots__=(),131 个动态节点类按 fields+attrs 生成 __slots__,
_Init 改 object.__setattr__;类默认值改为 _node_defaults 字典(原类属性
与 __slots__ 冲突)。解析提速 ~9%,实例内存 Name 由 48B+独立 dict 降为
80B 平铺,无 __dict__ 残留。Python 3.12 pickle 对 __slots__ 类开箱即用
(__reduce_ex__(2)),131 类 pickle roundtrip 全过,vast 差分 51/53 0 FAIL。
2026-08-17 23:27:43 +08:00

356 lines
15 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.nodes - 与 CPython 3.12 ast 完全同构的 AST 节点类
vast 是自研 Viper/Python 语法解析器,产出与标准库 ast 模块结构完全一致的
AST 树,从而使编译器侧 ``import vast as ast`` 后原有代码零修改。
本模块定义全部 131 个 AST 节点类(含遗留兼容类),字段/属性与 CPython
3.12 的 ``ast`` 模块逐一对应:
- ``_fields``:节点结构字段(顺序与位置参数构造一致)
- ``_attributes``:dump(include_attributes=True) 时随节点输出的位置属性
构造语义与 CPython 一致:
- 位置参数按 ``_fields`` 顺序赋值,超出字段数抛 TypeError
- 关键字参数按名赋值(实例为 __slots__ 紧凑布局,仅接受已声明字段)
- 未传入的字段不设置属性(访问时 AttributeError,与 CPython 一致)
"""
from __future__ import annotations
class AST:
"""所有 AST 节点的基类(与 ast.AST 同构)。"""
# 空 __slots__:保证子类定义 __slots__ 后实例不携带 __dict__
# (基类若有 __dict__,子类 __slots__ 将失效)
__slots__ = ()
_fields: tuple[str, ...] = ()
_attributes: tuple[str, ...] = ()
def __init__(self, *args: object, **kwargs: object) -> None:
fields: tuple[str, ...] = self._fields
if len(args) > len(fields):
raise TypeError(
f'{type(self).__name__} constructor takes at most '
f'{len(fields)} positional arguments'
)
for name, val in zip(fields, args):
setattr(self, name, val)
for k, v in kwargs.items():
setattr(self, k, v)
def __eq__(self, other: object) -> bool:
"""AST 节点按对象身份比较(与 CPython ast 一致)。"""
return self is other
# =============================================================================
# 节点定义表:字段表(含位置属性表),数据驱动生成全部节点类
# =============================================================================
_POS_ATTRS: tuple[str, ...] = ('lineno', 'col_offset', 'end_lineno', 'end_col_offset')
_NODE_DEFS: dict[str, tuple[tuple[str, ...], tuple[str, ...]]] = {
# ---- 模块/交互/表达式顶层 ----
'Module': (('body', 'type_ignores'), ()),
'Interactive': (('body',), ()),
'Expression': (('body',), ()),
'FunctionType': (('argtypes', 'returns'), ()),
'Suite': ((), ()),
# ---- 语句 ----
'FunctionDef': (('name', 'args', 'body', 'decorator_list', 'returns', 'type_comment', 'type_params'), _POS_ATTRS),
'AsyncFunctionDef': (('name', 'args', 'body', 'decorator_list', 'returns', 'type_comment', 'type_params'), _POS_ATTRS),
'ClassDef': (('name', 'bases', 'keywords', 'body', 'decorator_list', 'type_params'), _POS_ATTRS),
'Return': (('value',), _POS_ATTRS),
'Delete': (('targets',), _POS_ATTRS),
'Assign': (('targets', 'value', 'type_comment'), _POS_ATTRS),
'AugAssign': (('target', 'op', 'value'), _POS_ATTRS),
'AnnAssign': (('target', 'annotation', 'value', 'simple'), _POS_ATTRS),
'For': (('target', 'iter', 'body', 'orelse', 'type_comment'), _POS_ATTRS),
'AsyncFor': (('target', 'iter', 'body', 'orelse', 'type_comment'), _POS_ATTRS),
'While': (('test', 'body', 'orelse'), _POS_ATTRS),
'If': (('test', 'body', 'orelse'), _POS_ATTRS),
'With': (('items', 'body', 'type_comment'), _POS_ATTRS),
'AsyncWith': (('items', 'body', 'type_comment'), _POS_ATTRS),
'Match': (('subject', 'cases'), _POS_ATTRS),
'Raise': (('exc', 'cause'), _POS_ATTRS),
'Try': (('body', 'handlers', 'orelse', 'finalbody'), _POS_ATTRS),
'TryStar': (('body', 'handlers', 'orelse', 'finalbody'), _POS_ATTRS),
'Assert': (('test', 'msg'), _POS_ATTRS),
'Import': (('names',), _POS_ATTRS),
'ImportFrom': (('module', 'names', 'level'), _POS_ATTRS),
'Global': (('names',), _POS_ATTRS),
'Nonlocal': (('names',), _POS_ATTRS),
'Expr': (('value',), _POS_ATTRS),
'Pass': ((), _POS_ATTRS),
'Break': ((), _POS_ATTRS),
'Continue': ((), _POS_ATTRS),
# ---- 表达式 ----
'BoolOp': (('op', 'values'), _POS_ATTRS),
'NamedExpr': (('target', 'value'), _POS_ATTRS),
'BinOp': (('left', 'op', 'right'), _POS_ATTRS),
'UnaryOp': (('op', 'operand'), _POS_ATTRS),
'Lambda': (('args', 'body'), _POS_ATTRS),
'IfExp': (('test', 'body', 'orelse'), _POS_ATTRS),
'Dict': (('keys', 'values'), _POS_ATTRS),
'Set': (('elts',), _POS_ATTRS),
'ListComp': (('elt', 'generators'), _POS_ATTRS),
'SetComp': (('elt', 'generators'), _POS_ATTRS),
'DictComp': (('key', 'value', 'generators'), _POS_ATTRS),
'GeneratorExp': (('elt', 'generators'), _POS_ATTRS),
'Await': (('value',), _POS_ATTRS),
'Yield': (('value',), _POS_ATTRS),
'YieldFrom': (('value',), _POS_ATTRS),
'Compare': (('left', 'ops', 'comparators'), _POS_ATTRS),
'Call': (('func', 'args', 'keywords'), _POS_ATTRS),
'FormattedValue': (('value', 'conversion', 'format_spec'), _POS_ATTRS),
'JoinedStr': (('values',), _POS_ATTRS),
'Constant': (('value', 'kind'), _POS_ATTRS),
'Attribute': (('value', 'attr', 'ctx'), _POS_ATTRS),
'Subscript': (('value', 'slice', 'ctx'), _POS_ATTRS),
'Starred': (('value', 'ctx'), _POS_ATTRS),
'Name': (('id', 'ctx'), _POS_ATTRS),
'List': (('elts', 'ctx'), _POS_ATTRS),
'Tuple': (('elts', 'ctx'), _POS_ATTRS),
'Slice': (('lower', 'upper', 'step'), _POS_ATTRS),
# ---- match 模式(PEP 634) ----
'MatchValue': (('value',), _POS_ATTRS),
'MatchSingleton': (('value',), _POS_ATTRS),
'MatchSequence': (('patterns',), _POS_ATTRS),
'MatchMapping': (('keys', 'patterns', 'rest'), _POS_ATTRS),
'MatchClass': (('cls', 'patterns', 'kwd_attrs', 'kwd_patterns'), _POS_ATTRS),
'MatchStar': (('name',), _POS_ATTRS),
'MatchAs': (('pattern', 'name'), _POS_ATTRS),
'MatchOr': (('patterns',), _POS_ATTRS),
# ---- 类型参数(PEP 695,仅声明性支持) ----
'TypeVar': (('name', 'bound'), _POS_ATTRS),
'TypeVarTuple': (('name',), _POS_ATTRS),
'ParamSpec': (('name',), _POS_ATTRS),
'TypeAlias': (('name', 'type_params', 'value'), _POS_ATTRS),
# ---- 复合辅助节点 ----
'comprehension': (('target', 'iter', 'ifs', 'is_async'), ()),
'ExceptHandler': (('type', 'name', 'body'), _POS_ATTRS),
'arguments': (('posonlyargs', 'args', 'vararg', 'kwonlyargs', 'kw_defaults', 'kwarg', 'defaults'), ()),
'arg': (('arg', 'annotation', 'type_comment'), _POS_ATTRS),
'keyword': (('arg', 'value'), _POS_ATTRS),
'alias': (('name', 'asname'), _POS_ATTRS),
'withitem': (('context_expr', 'optional_vars'), ()),
'match_case': (('pattern', 'guard', 'body'), ()),
'TypeIgnore': (('lineno', 'tag'), ()),
# ---- 运算符 / 上下文(无字段单例类) ----
'Add': ((), ()),
'Sub': ((), ()),
'Mult': ((), ()),
'MatMult': ((), ()),
'Div': ((), ()),
'Mod': ((), ()),
'Pow': ((), ()),
'LShift': ((), ()),
'RShift': ((), ()),
'BitOr': ((), ()),
'BitXor': ((), ()),
'BitAnd': ((), ()),
'FloorDiv': ((), ()),
'Invert': ((), ()),
'Not': ((), ()),
'UAdd': ((), ()),
'USub': ((), ()),
'Eq': ((), ()),
'NotEq': ((), ()),
'Lt': ((), ()),
'LtE': ((), ()),
'Gt': ((), ()),
'GtE': ((), ()),
'Is': ((), ()),
'IsNot': ((), ()),
'In': ((), ()),
'NotIn': ((), ()),
'And': ((), ()),
'Or': ((), ()),
'Load': ((), ()),
'Store': ((), ()),
'Del': ((), ()),
'AugLoad': ((), ()),
'AugStore': ((), ()),
'Param': ((), ()),
'Ellipsis': ((), _POS_ATTRS),
# ---- 遗留兼容类(已废弃但保留,供 hasattr 检查) ----
'Index': ((), ()),
'ExtSlice': ((), ()),
# ---- 抽象类型占位(纯类型标注用,不实例化) ----
'mod': ((), ()),
'stmt': ((), ()),
'expr': ((), ()),
'expr_context': ((), ()),
'boolop': ((), ()),
'operator': ((), ()),
'unaryop': ((), ()),
'cmpop': ((), ()),
'slice': ((), ()),
'pattern': ((), ()),
'type_ignore': ((), ()),
'type_param': ((), ()),
'excepthandler': ((), ()),
}
_DOCS: dict[str, str] = {
'FunctionDef': '函数定义节点(对应 def 语句)。',
'AsyncFunctionDef': '异步函数定义节点(对应 async def)。',
'ClassDef': '类定义节点(对应 class 语句)。',
'AnnAssign': '带注解的赋值节点(x: T = v)。',
'Constant': '常量节点(数字/字符串/bool/None/Ellipsis)。',
'Name': '名称节点(标识符引用,含 Load/Store/Del 上下文)。',
'Call': '函数调用节点。',
'BinOp': '二元运算节点。',
'Attribute': '属性访问节点(obj.attr)。',
'Subscript': '下标访问节点(obj[idx])。',
'arguments': '函数参数列表节点(含默认值、*args、**kwargs)。',
'alias': 'import 别名节点。',
'keyword': '关键字实参节点(f(x=1))。',
'withitem': 'with 语句上下文节点。',
}
# 类属性默认值(与 CPython 3.12 ast 节点一致):
# dump 时"实例值为 None 且类默认值为 None"的字段会被跳过;
# 无类默认值的字段(如 Constant.value)即使为 None 也会显示。
_CLASS_DEFAULTS: dict[str, dict[str, object]] = {
'AnnAssign': {'value': None},
'Assert': {'msg': None},
'Assign': {'type_comment': None},
'AsyncFor': {'type_comment': None},
'AsyncFunctionDef': {'returns': None, 'type_comment': None},
'AsyncWith': {'type_comment': None},
'Constant': {'kind': None},
'ExceptHandler': {'type': None, 'name': None},
'For': {'type_comment': None},
'FormattedValue': {'format_spec': None},
'FunctionDef': {'returns': None, 'type_comment': None},
'ImportFrom': {'module': None, 'level': None},
'match_case': {'guard': None},
'MatchAs': {'pattern': None, 'name': None},
'MatchMapping': {'rest': None},
'MatchStar': {'name': None},
'Raise': {'exc': None, 'cause': None},
'Return': {'value': None},
'Slice': {'lower': None, 'upper': None, 'step': None},
'TypeVar': {'bound': None},
'With': {'type_comment': None},
'Yield': {'value': None},
'alias': {'asname': None},
'arg': {'annotation': None, 'type_comment': None},
'arguments': {'vararg': None, 'kwarg': None},
'keyword': {'arg': None},
'withitem': {'optional_vars': None},
}
# 节点抽象基类映射(与 CPython 3.12 一致:expr/stmt/mod/operator/cmpop/...)
# 节点类继承对应的抽象基类,使 isinstance(node, ast.expr) 等判断可用
_BASE_CLASS: dict[str, str] = {
# stmt
'AnnAssign': 'stmt', 'Assert': 'stmt', 'Assign': 'stmt', 'AsyncFor': 'stmt',
'AsyncFunctionDef': 'stmt', 'AsyncWith': 'stmt', 'AugAssign': 'stmt',
'Break': 'stmt', 'ClassDef': 'stmt', 'Continue': 'stmt', 'Delete': 'stmt',
'Expr': 'stmt', 'For': 'stmt', 'FunctionDef': 'stmt', 'Global': 'stmt',
'If': 'stmt', 'Import': 'stmt', 'ImportFrom': 'stmt', 'Match': 'stmt',
'Nonlocal': 'stmt', 'Pass': 'stmt', 'Raise': 'stmt', 'Return': 'stmt',
'Try': 'stmt', 'TryStar': 'stmt', 'TypeAlias': 'stmt', 'While': 'stmt',
'With': 'stmt',
# expr
'Attribute': 'expr', 'Await': 'expr', 'BinOp': 'expr', 'BoolOp': 'expr',
'Call': 'expr', 'Compare': 'expr', 'Constant': 'expr', 'Dict': 'expr',
'DictComp': 'expr', 'FormattedValue': 'expr', 'GeneratorExp': 'expr',
'IfExp': 'expr', 'JoinedStr': 'expr', 'Lambda': 'expr', 'List': 'expr',
'ListComp': 'expr', 'Name': 'expr', 'NamedExpr': 'expr', 'Set': 'expr',
'SetComp': 'expr', 'Slice': 'expr', 'Starred': 'expr', 'Subscript': 'expr',
'Tuple': 'expr', 'UnaryOp': 'expr', 'Yield': 'expr', 'YieldFrom': 'expr',
# mod
'Module': 'mod', 'Interactive': 'mod', 'Expression': 'mod',
'FunctionType': 'mod', 'Suite': 'mod',
# operator
'Add': 'operator', 'Sub': 'operator', 'Mult': 'operator', 'MatMult': 'operator',
'Div': 'operator', 'Mod': 'operator', 'Pow': 'operator', 'LShift': 'operator',
'RShift': 'operator', 'BitOr': 'operator', 'BitXor': 'operator',
'BitAnd': 'operator', 'FloorDiv': 'operator',
# unaryop
'Invert': 'unaryop', 'Not': 'unaryop', 'UAdd': 'unaryop', 'USub': 'unaryop',
# boolop
'And': 'boolop', 'Or': 'boolop',
# cmpop
'Eq': 'cmpop', 'NotEq': 'cmpop', 'Lt': 'cmpop', 'LtE': 'cmpop',
'Gt': 'cmpop', 'GtE': 'cmpop', 'Is': 'cmpop', 'IsNot': 'cmpop',
'In': 'cmpop', 'NotIn': 'cmpop',
# expr_context
'Load': 'expr_context', 'Store': 'expr_context', 'Del': 'expr_context',
'AugLoad': 'expr_context', 'AugStore': 'expr_context', 'Param': 'expr_context',
# slice
'Index': 'slice', 'ExtSlice': 'slice',
# pattern
'MatchValue': 'pattern', 'MatchSingleton': 'pattern', 'MatchSequence': 'pattern',
'MatchMapping': 'pattern', 'MatchClass': 'pattern', 'MatchStar': 'pattern',
'MatchAs': 'pattern', 'MatchOr': 'pattern',
# type_param / type_ignore
'TypeVar': 'type_param', 'TypeVarTuple': 'type_param', 'ParamSpec': 'type_param',
'TypeIgnore': 'type_ignore',
# excepthandler
'ExceptHandler': 'excepthandler',
}
def _MakeNodeClass(name: str, fields: tuple[str, ...], attrs: tuple[str, ...]) -> type:
"""按字段表生成节点类(继承对应抽象基类)。"""
def _Init(self: AST, *args: object, **kwargs: object) -> None:
if len(args) > len(fields):
raise TypeError(
f'{name} constructor takes at most {len(fields)} positional arguments'
)
for fname, val in zip(fields, args):
object.__setattr__(self, fname, val)
for k, v in kwargs.items():
object.__setattr__(self, k, v)
base_name: str | None = _BASE_CLASS.get(name)
base_cls: type = globals()[base_name] if base_name else AST
# __slots__ = 位置字段 + 位置属性(实例的全部属性名)
slots: tuple[str, ...] = tuple(dict.fromkeys(fields + attrs))
ns: dict[str, object] = {
'__init__': _Init,
'__slots__': slots,
'_fields': fields,
'_attributes': attrs,
# 字段默认值不能直接作为类属性(与同名 __slots__ 冲突),
# 存入独立类属性供 dump 判断"值为 None 且默认值为 None"时跳过
'_node_defaults': dict(_CLASS_DEFAULTS.get(name, {})),
'__module__': __name__,
'__doc__': _DOCS.get(name, f'{name} AST 节点(与 ast.{name} 同构)。'),
}
cls: type = type(name, (base_cls,), ns)
return cls
# 动态生成全部节点类到本模块命名空间
# 抽象基类(mod/stmt/expr/...)在 _NODE_DEFS 末尾定义,但需先生成
_ABSTRACT_BASES: tuple[str, ...] = (
'mod', 'stmt', 'expr', 'expr_context', 'boolop', 'operator', 'unaryop',
'cmpop', 'slice', 'pattern', 'type_ignore', 'type_param', 'excepthandler',
)
for _name in _ABSTRACT_BASES:
_fields, _attrs = _NODE_DEFS[_name]
globals()[_name] = _MakeNodeClass(_name, _fields, _attrs)
for _name, (_fields, _attrs) in _NODE_DEFS.items():
if _name in _ABSTRACT_BASES:
continue
globals()[_name] = _MakeNodeClass(_name, _fields, _attrs)
# 显式别名:AnnAssign 已在表内;这里保证模块级 __all__ 完整性
__all__: list[str] = list(_NODE_DEFS.keys()) + ['AST']