From 64538e08f8a19465889e11ded4922fd20bedda32 Mon Sep 17 00:00:00 2001 From: TermiNexus Date: Sun, 27 Sep 2026 14:06:04 +0800 Subject: [PATCH] =?UTF-8?q?feat(descriptor):=20=E6=B3=A8=E8=A7=A3=E4=BD=8D?= =?UTF-8?q?=E7=BD=AE=20=5F=5Fattribute=5F=5F(...)=20=E8=84=B1=E7=B3=96?= =?UTF-8?q?=E4=B8=BA=20t.attr.*=EF=BC=8C=E5=B9=B6=E6=94=AF=E6=8C=81=20[]T?= =?UTF-8?q?=20=E8=87=AA=E5=8A=A8=E6=8E=A8=E5=AF=BC=E6=95=B0=E7=BB=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit SymbolUtils 新增 _ExpandAttributeDescriptors:注解位置(冒号后 / 形参 / 返回箭头后,可与 const/volatile 混写)的 __attribute__(a, b(x)) 文本脱糖为 t.attr.a | t.attr.b(x),与 TPV includes/ast/parser.vp 的 _IsMagicAttributeCall/_MakeTAttrMarker 同源;紧跟的 C 风格前置数组前缀 [N]T / []T 在同一次编辑里一并脱糖为 t.CArray[T, N] / t.CArray[T, None](None = TPC 既有长度自动推导数组,见 HandlesAssign.py:1606)。 修 TransPyV/App/main.vp:44 —— V_TPV_VERSION: __attribute__(section('.PRVERSN')) []char = '...' 此前 []char 被 vast 当成 __attribute__ 调用的下标,报 unexpected token OP(']')(与根因脱节);现展开为 t.attr.section('.PRVERSN') | t.CArray[char, None]。 把 _AtAnnotationColon 从 _InArrayAnnotationPosition 抽出,数组前缀与属性描述符共用同一注解位置判据,避免两份副本分叉。回归:306 个 .vp 仅 main.vp:44 一行被改,无意外改动;11 条合成用例与 main.vp 全文件 vast.parse 均通过。 --- lib/core/SymbolUtils.py | 283 +++++++++++++++++++++++++++++++++++++--- 1 file changed, 267 insertions(+), 16 deletions(-) diff --git a/lib/core/SymbolUtils.py b/lib/core/SymbolUtils.py index 24851e5..2ea7ffa 100644 --- a/lib/core/SymbolUtils.py +++ b/lib/core/SymbolUtils.py @@ -477,23 +477,14 @@ def _ArrayPrefixSyntaxError(Detail: str, Lineno: int, Col: int, Snippet: str) -> ) -def _InArrayAnnotationPosition(Tokens: list, Index: int, Stack: list) -> bool: - """True if the '[' at Index begins an annotation-position type. +def _AtAnnotationColon(Tokens: list, ColonIdx: int, Stack: list) -> bool: + """':' 是否位于注解分隔位置(AnnAssign / 类字段 / def 形参)——注解位置判据真源。 - Stack holds open brackets before Index as (char, open_index). AnnAssign / class - field: ':' preceded by a (possibly dotted) target name at a statement boundary. - Parameter: ':' inside a def's parameter paren. Return annotation: '->' before '['. - Dict / slice / lambda colons are excluded so right-hand `[...]` literals stay put. + 被 `_InArrayAnnotationPosition`(`[N]T` 数组前缀)与 `_InAttributeAnnotationPosition` + (`__attribute__(...)` 描述符)共用:两处若各留一份副本,判据迟早分叉。 + dict / slice / lambda 的冒号在此被排除(右值字面量绝不被改写)。 """ - PrevIdx: int = _PrevSignificantTokenIndex(Tokens, Index) - if PrevIdx < 0: - return False - Prev = Tokens[PrevIdx] - if Prev.string == '->': - return True - if Prev.string != ':': - return False - TargetIdx: int = _PrevSignificantTokenIndex(Tokens, PrevIdx) + TargetIdx: int = _PrevSignificantTokenIndex(Tokens, ColonIdx) if TargetIdx < 0 or Tokens[TargetIdx].type != tokenize.NAME: return False if Stack: @@ -506,7 +497,7 @@ def _InArrayAnnotationPosition(Tokens: list, Index: int, Stack: list) -> bool: KwIdx: int = _PrevSignificantTokenIndex(Tokens, FnIdx) if KwIdx < 0 or Tokens[KwIdx].string != 'def': return False - for Tok in Tokens[OpenIdx + 1:Index]: # a default lambda is not an annotation + for Tok in Tokens[OpenIdx + 1:ColonIdx]: # a default lambda is not an annotation if Tok.type == tokenize.NAME and Tok.string == 'lambda': return False return True @@ -526,6 +517,25 @@ def _InArrayAnnotationPosition(Tokens: list, Index: int, Stack: list) -> bool: return Bound.type in _ARRAY_STMT_BOUNDARY or Bound.string == ';' +def _InArrayAnnotationPosition(Tokens: list, Index: int, Stack: list) -> bool: + """True if the '[' at Index begins an annotation-position type. + + Stack holds open brackets before Index as (char, open_index). AnnAssign / class + field: ':' preceded by a (possibly dotted) target name at a statement boundary. + Parameter: ':' inside a def's parameter paren. Return annotation: '->' before '['. + Dict / slice / lambda colons are excluded so right-hand `[...]` literals stay put. + """ + PrevIdx: int = _PrevSignificantTokenIndex(Tokens, Index) + if PrevIdx < 0: + return False + Prev = Tokens[PrevIdx] + if Prev.string == '->': + return True + if Prev.string != ':': + return False + return _AtAnnotationColon(Tokens, PrevIdx, Stack) + + def _ApplySameLineEdits(Code: str, Edits: list[tuple[int, int, int, str, str]]) -> str: """Apply (lineno, col_start, col_end, text, orig) edits right-to-left, keeping line endings. @@ -595,6 +605,242 @@ def _ExpandArrayPrefixAnnotations(Code: str) -> str: return _ApplySameLineEdits(Code, Edits) +# --------------------------------------------------------------------------- +# 注解位置的 C 风格 `__attribute__(...)` 描述符(与 TPV +# includes/ast/parser.vp 的 _IsMagicAttributeCall / _MakeTAttrMarker 同源书写法, +# TPC 侧在本层文本脱糖;与 const/volatile、`[N]T` 数组前缀同属一套语法糖): +# V: __attribute__(section(".PRVERSN")) []char = "0.0.3-dev.428" +# → V: t.attr.section(".PRVERSN") | t.CArray[char, None] = "0.0.3-dev.428" +# x: __attribute__(aligned(16)) int = 0 +# → x: t.attr.aligned(16) | int = 0 +# 脱糖目标与手写 `T | t.attr.X(...)`(HandlesAssign 的模块级全局 t.attr.* 分支 +# 所认的形态)逐字同形,故下游 AnnotationLoader / HandlesAssign / +# DeclarationGenerator 零新增判据。 +# `t.CArray[T, None]` = TPC 的"长度自动推导数组"(HandlesAssign.py:1606), +# 恰是 `[]char` 的语义。 +# --------------------------------------------------------------------------- +_ATTRIBUTE_CALLEE: str = '__attribute__' +_ATTR_MARKER_PREFIX: str = 't.attr.' +_ATTRIBUTE_DESCRIPTOR_HINT: str = ( + '`名字: __attribute__(section(".text")) 类型`(形参 / 返回值同样成立,' + '可与 const/volatile 混写,其后可接 C 风格数组前缀 `[N]T` / `[]T`)' +) + + +def _AttributeSyntaxError(Detail: str, Lineno: int, Col: int, Snippet: str) -> SyntaxError: + """构造注解位置 `__attribute__(...)` 非法形态的 fail-fast 报文。""" + return SyntaxError( + f"[TPC] __attribute__ 描述符非法({Detail}):行 {Lineno} 列 {Col}," + f"源码片段 {Snippet.strip()!r}。" + f"`__attribute__(...)` 只在**注解位置**生效(AnnAssign 的 ':' 之后 / 形参 / " + f"'->' 之后 / 类字段),期望形态:{_ATTRIBUTE_DESCRIPTOR_HINT};" + f"正确示例:`V_TPV_VERSION: __attribute__(section('.PRVERSN')) []char = \"x\"` / " + f"`g: __attribute__(aligned(16)) int = 0`。" + f"(注解位置的 __attribute__ 无法脱糖时一律终止编译,绝不静默留作普通调用)" + ) + + +def _MatchOpenBracket(Tokens: list, CloseIdx: int) -> int: + """回溯找与 CloseIdx 处闭括号配对的 `'[' / '(' / '{'` 下标(-1=未配对)。""" + Depth: int = 0 + j: int = CloseIdx + while j >= 0: + Tok = Tokens[j] + if Tok.type == tokenize.OP: + if Tok.string in _TYPE_BRACKET_CLOSERS: + Depth += 1 + elif Tok.string in _TYPE_BRACKET_OPENERS: + Depth -= 1 + if Depth == 0: + return j + j -= 1 + return -1 + + +def _AttributeMarkerText(ArgText: str) -> str: + """`__attribute__` 单个实参文本 → 描述符文本(与 TPV _MakeTAttrMarker 同口径)。 + + - 裸名 `always_inline` → `t.attr.always_inline` + - 调用 `section(".text")` → `t.attr.section(".text")` + - 其余(已含点的 `t.attr.X` / `c.Attribute(...)`)→ 原样保留(允许混写) + """ + T: str = (ArgText or '').strip() + if not T: + return T + if re.fullmatch(r'[A-Za-z_][A-Za-z0-9_]*', T) is not None: + return f'{_ATTR_MARKER_PREFIX}{T}' + if re.match(r'^[A-Za-z_][A-Za-z0-9_]*\s*\(', T) is not None: + return f'{_ATTR_MARKER_PREFIX}{T}' + return T + + +def _InAttributeAnnotationPosition(Tokens: list, Index: int, Stack: list) -> bool: + """`Index` 处的 `__attribute__` 是否处于注解位置的描述符序列中。 + + 与 TPV `_IsMagicAttributeCall` + `_parse_type_ann` 的描述符循环同口径:允许 + `x: __attribute__(...) T`、`def f(p: __attribute__(...) T)`、 + `-> __attribute__(...) T`,以及 `const` / `volatile` / 前序 `__attribute__(...)` + 之后(顺序自由、可混写)。表达式位置的普通调用 + (`y = __attribute__(x)` / `f(__attribute__(x))`)不命中 ⇒ 原样保留。 + """ + j: int = _PrevSignificantTokenIndex(Tokens, Index) + Guard: int = 0 + while j >= 0 and Guard < 64: + Tok = Tokens[j] + if Tok.string == '|': + j = _PrevSignificantTokenIndex(Tokens, j) + Guard += 1 + continue + if Tok.type == tokenize.NAME and Tok.string in _TYPE_DESCRIPTOR_MARKERS: + j = _PrevSignificantTokenIndex(Tokens, j) + Guard += 1 + continue + if Tok.type == tokenize.OP and Tok.string == ')': + # 前序 `__attribute__(...)`:回溯到配对的 '(' 并确认被调用者就是它 + OpenIdx: int = _MatchOpenBracket(Tokens, j) + if OpenIdx < 0: + return False + CalleeIdx: int = _PrevSignificantTokenIndex(Tokens, OpenIdx) + if CalleeIdx < 0 or Tokens[CalleeIdx].string != _ATTRIBUTE_CALLEE: + return False + j = _PrevSignificantTokenIndex(Tokens, CalleeIdx) + Guard += 1 + continue + break + if j < 0: + return False + Tok = Tokens[j] + if Tok.string == '->': + return True + if Tok.string != ':': + return False + return _AtAnnotationColon(Tokens, j, Stack) + + +def _ExpandAttributeDescriptors(Code: str) -> str: + """把注解位置的 `__attribute__(...)` 脱糖为 `t.attr.<名>` 描述符(同行替换)。 + + 规则与示例见上方常量块注释。紧跟的 C 风格前置数组前缀 `[N]T` / `[]T` 在**同一次 + 编辑**里一并脱糖(`_DesugarArrayPrefix`,已支持空长度 ⇒ `None`)——否则 `[]char` + 会被 vast 当成 `__attribute__(...)` 调用的下标,报出与根因脱节的 + `unexpected token OP(']')`(本轮实测现象)。 + 跨行 / 未闭合 / 实参为空 / 后随非类型且非数组前缀 ⇒ 响亮失败。 + 非注解位置与 `@__attribute__(...)`(装饰器通道,另有 desugar)不受影响。 + """ + if _ATTRIBUTE_CALLEE not in Code: + return Code + try: + Tokens: list = list(tokenize.generate_tokens(io.StringIO(Code).readline)) + except Exception: + return Code + n: int = len(Tokens) + Lines0: list[str] = Code.splitlines() + Edits: list[tuple[int, int, int, str, str]] = [] + Stack: list = [] + i: int = 0 + while i < n: + Tok = Tokens[i] + if Tok.type == tokenize.OP: + if Tok.string in _TYPE_BRACKET_OPENERS: + Stack.append((Tok.string, i)) + elif Tok.string in _TYPE_BRACKET_CLOSERS: + if Stack: + Stack.pop() + i += 1 + continue + if Tok.type != tokenize.NAME or Tok.string != _ATTRIBUTE_CALLEE \ + or not _InAttributeAnnotationPosition(Tokens, i, Stack): + i += 1 + continue + LineNo: int = Tok.start[0] + Line0: str = Lines0[LineNo - 1] if 0 <= LineNo - 1 < len(Lines0) else '' + OpenIdx: int = i + 1 + while OpenIdx < n and Tokens[OpenIdx].type in _DESCRIPTOR_SKIP_TYPES: + OpenIdx += 1 + if OpenIdx >= n or Tokens[OpenIdx].string != '(': + raise _AttributeSyntaxError('`__attribute__` 后缺实参列表 `(...)`', + Tok.start[0], Tok.start[1], Line0) + Depth: int = 0 + k: int = OpenIdx + while k < n: + T2 = Tokens[k] + if T2.type == tokenize.OP: + if T2.string in _TYPE_BRACKET_OPENERS: + Depth += 1 + elif T2.string in _TYPE_BRACKET_CLOSERS: + Depth -= 1 + if Depth == 0: + break + k += 1 + if k >= n or Depth != 0: + raise _AttributeSyntaxError('`__attribute__(...)` 括号未闭合', + Tok.start[0], Tok.start[1], Line0) + for q in range(OpenIdx, k + 1): + if Tokens[q].type not in _DESCRIPTOR_SKIP_TYPES and Tokens[q].start[0] != LineNo: + raise _AttributeSyntaxError('`__attribute__(...)` 跨行(本层只做同行替换)', + Tok.start[0], Tok.start[1], Line0) + # 顶层逗号切分实参(跳过注释/NL;嵌套括号内的逗号不算) + ArgSpans: list[tuple[int, int]] = [] + ArgFirst: int = -1 + Nest: int = 0 + m: int = OpenIdx + 1 + while m < k: + T3 = Tokens[m] + if T3.type in _DESCRIPTOR_SKIP_TYPES: + m += 1 + continue + if T3.type == tokenize.OP and T3.string in _TYPE_BRACKET_OPENERS: + Nest += 1 + elif T3.type == tokenize.OP and T3.string in _TYPE_BRACKET_CLOSERS: + Nest -= 1 + elif T3.type == tokenize.OP and T3.string == ',' and Nest == 0: + if ArgFirst >= 0: + ArgSpans.append((ArgFirst, m - 1)) + ArgFirst = -1 + m += 1 + continue + if ArgFirst < 0: + ArgFirst = m + m += 1 + if ArgFirst >= 0: + ArgSpans.append((ArgFirst, k - 1)) + Markers: list[str] = [] + for (ArgA, ArgB) in ArgSpans: + ArgText: str = Line0[Tokens[ArgA].start[1]:Tokens[ArgB].end[1]] + Marker: str = _AttributeMarkerText(ArgText) + if Marker: + Markers.append(Marker) + if not Markers: + raise _AttributeSyntaxError('`__attribute__(...)` 实参为空', + Tok.start[0], Tok.start[1], Line0) + Nxt: int = k + 1 + while Nxt < n and Tokens[Nxt].type in _DESCRIPTOR_SKIP_TYPES: + Nxt += 1 + LastCol: int = Tokens[k].end[1] + if Nxt < n and Tokens[Nxt].type == tokenize.OP and Tokens[Nxt].string == '[': + Desugared = _DesugarArrayPrefix(Tokens, Nxt, n, Code) + if Desugared is None: + raise _AttributeSyntaxError( + '`__attribute__(...)` 后的数组前缀无法脱糖(缺元素类型 / 跨行 / 不完整)', + Tok.start[0], Tok.start[1], Line0) + End: int = Desugared[0] + ArrayText: str = Desugared[1] + LastCol = Tokens[End - 1].end[1] + Text: str = ' | '.join(Markers) + f' | {ArrayText}' + i = End + elif Nxt < n and Tokens[Nxt].type == tokenize.NAME: + Text = ' | '.join(Markers) + ' |' + i = k + 1 + else: + raise _AttributeSyntaxError('`__attribute__(...)` 后缺少类型', + Tok.start[0], Tok.start[1], Line0) + Edits.append((LineNo, Tok.start[1], LastCol, Text, Line0[Tok.start[1]:LastCol])) + continue + if not Edits: + return Code + return _ApplySameLineEdits(Code, Edits) + + def _ExpandTypeDescriptors(Code: str) -> str: """把 `const T` / `volatile T`(含组合、任意顺序)展开为 `t.CConst | T` 形态(行号不变)。 @@ -1057,6 +1303,11 @@ def _ExpandSoftKeywordsImpl(Code: str) -> str: # 描述符放在最后:先让语句级语法糖(enum/union/define/typedef)落到规范形态, # 本层再看规范注解(如 `define X: const int = 1` → `X: const int | t.CDefine = 1` # → `X: t.CConst | int | t.CDefine = 1`,两种顺序结果相同)。 + # `__attribute__(...)` 排在 const/volatile 之前:其注解位置判据需能回溯到**原始** + # 描述符串(`const __attribute__(x) int` 的 `const` 尚未被改写为 `t.CConst |`), + # 且它自身产出的 `t.attr.X |` 不会干扰紧随的 const/volatile 展开。 + if _ATTRIBUTE_CALLEE in Code: + Code = _ExpandAttributeDescriptors(Code) if 'const' in Code or 'volatile' in Code: Code = _ExpandTypeDescriptors(Code) # 裸 `[N]T` 数组前缀在描述符之后:`const [N]T` 已由 _ExpandTypeDescriptors 消化为