feat(descriptor): 注解位置 __attribute__(...) 脱糖为 t.attr.*,并支持 []T 自动推导数组

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 均通过。
This commit is contained in:
TermiNexus committed 2026-09-27 14:06:04 +08:00
1 parent 3b5d85ce9d
commit 64538e08f8
1 file changed
+267 -16
+267 -16
View File
@@ -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 消化为