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:
1 parent
3b5d85ce9d
commit
64538e08f8
1 file changed
+267
-16
+267
-16
@@ -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 消化为
|
||||
|
||||
Reference in new issue
Block a user