feat(symbols): TPC 侧同步 export/extern 变量声明语法糖

新增 _ExpandExportExternVar:export 名字: T [= 初值] → 名字: T | t.CExport [= 初值];extern 名字: T → 名字: T | t.State;extern 带初值响亮失败 SyntaxError

排在 _ExpandArrayPrefixAnnotations 之后(先让注解规范化,再插标记);_AtAnnotationColon 把 export/extern 认作语句边界,使数组前缀仍能脱糖

新增 _NextSignificantTokenIndex;ExpandSoftKeywords 文档改列九种展开
This commit is contained in:
TermiNexus committed 2026-09-27 18:00:54 +08:00
1 parent 4346552c72
commit 6fce79d531
1 file changed
+138 -2
+138 -2
View File
@@ -395,6 +395,19 @@ def _PrevSignificantTokenIndex(Tokens: list, Index: int) -> int:
return j
def _NextSignificantTokenIndex(Tokens: list, Index: int) -> int:
"""取 Index 之后第一个"有意义"的 token 下标(跳过注释与 NL;-1=无)。
与 `_PrevSignificantTokenIndex` 配对:`export 名字 :` 的 lookahead 与
注解类型终点后的 `=` 判定(见 `_ExpandExportExternVar`)共用本函数。
"""
j: int = Index + 1
n: int = len(Tokens)
while j < n and Tokens[j].type in _DESCRIPTOR_SKIP_TYPES:
j += 1
return j if j < n else -1
def _TypeDescriptorSyntaxError(Detail: str, Lineno: int, Col: int, Snippet: str) -> SyntaxError:
"""构造类型描述符非法形态的 fail-fast 报文(行:列 + 源码片段 + 期望形态 + 正确示例)。"""
return SyntaxError(
@@ -563,6 +576,15 @@ def _AtAnnotationColon(Tokens: list, ColonIdx: int, Stack: list) -> bool:
if BoundIdx < 0:
return True
Bound = Tokens[BoundIdx]
# `export 名字: T` / `extern 名字: T`(变量声明的存储类修饰符,见
# _ExpandExportExternVar):修饰符与名字之间已构成语句起始,同样算注解位置。
# 本判据必须在 export/extern 展开**之前**成立——`[N]T` 数组前缀与
# `__attribute__(...)` 两条路径都先于该展开运行,靠它把
# `export gdt: [7]gdt_entry = []` 正常脱糖成 `export gdt: t.CArray[gdt_entry, 7] = []`
# (若等到展开后再插入 ` | t.CExport`,`|` 不在 _TYPE_EXPR_STOP_TOKENS 中,
# 会被并进数组元素类型 → `t.CArray[gdt_entry | t.CExport, 7]`)。
if Bound.type == tokenize.NAME and Bound.string in _EXPORT_EXTERN_MODIFIERS:
return True
return Bound.type in _ARRAY_STMT_BOUNDARY or Bound.string == ';'
@@ -654,6 +676,109 @@ def _ExpandArrayPrefixAnnotations(Code: str) -> str:
return _ApplySameLineEdits(Code, Edits)
# ---------------------------------------------------------------------------
# 变量声明的存储类修饰符 export / extern(语句级软关键字)——与 TPV
# includes/ast/parser.vp:_parse_export_extern_var **同一标记、同一消费路径**:
# export 名字: T [= 初值] → 名字: T | t.CExport [= 初值]
# (本模块**定义**:定义端裸名,不加模块 SHA1 头;内联汇编 / C 符号对接必需。
# 无初值时按既有零初始化全局定义处理,如 `export tss_buf: [104]uint8`。)
# extern 名字: T → 名字: T | t.State
# (**仅声明**:t.State = t.CExtern | t.CExport ⇒ 定义端 `@裸名 = external global T`。
# extern 是外部定义,**不允许**初值 ⇒ 带 `= 初值` 一律终止编译。)
# 与函数形态(`export def f()` / `export f()` / `extern def f()`,vast 既有处理)
# 由第三个 token 区分:`:` 是变量,`(` / `[` 是函数 ⇒ 函数语义逐字不变。
# `export` / `extern` 不在任何 .vp 源码中作标识符(仅本语法与注释使用)。
# ---------------------------------------------------------------------------
_EXPORT_EXTERN_MODIFIERS: frozenset[str] = frozenset({'export', 'extern'})
_EXPORT_EXTERN_HINT: str = (
'`export 名字: 类型 [= 初值]`(裸名导出,等价 `名字: 类型 | t.CExport`);'
'`extern 名字: 类型`(仅声明,等价 `名字: 类型 | t.State`,**不允许初值**)'
)
_EXPORT_EXTERN_EXAMPLE: str = (
'export gdt: [7]gdt_entry = [] / export tss_buf: [104]uint8 / extern a: int'
)
def _ExportExternSyntaxError(Detail: str, Lineno: int, Col: int,
Snippet: str) -> SyntaxError:
"""构造 export/extern 变量声明非法形态的 fail-fast 报文。"""
return SyntaxError(
f"[TPC] export/extern 变量声明非法({Detail}):行 {Lineno} 列 {Col},"
f"源码片段 {Snippet.strip()!r}。"
f"期望形态:{_EXPORT_EXTERN_HINT};正确示例:{_EXPORT_EXTERN_EXAMPLE}。"
f"(非法形态一律终止编译,绝不静默改写)"
)
def _ExpandExportExternVar(Code: str) -> str:
"""把 `export 名字: T [= 初值]` / `extern 名字: T` 展开为带存储类标记的注解。
与 TPV `_parse_export_extern_var` 同口径:export → `| t.CExport`;
extern → `| t.State`;extern 带初值 ⇒ **响亮失败**(SyntaxError)。
- 只在"语句起始(可缩进)+ export/extern + 标识符 + `:`"形态触发;函数形态
(`export def f()` / `export f()` / `extern def f()`)第三个 token 不是 `:`
⇒ 不命中,vast 的既有函数处理逐字不变。
- **必须排在 `_ExpandArrayPrefixAnnotations` 之后**:此时注解已是规范形态
(`t.CArray[T, N]` / `t.CConst | ...`),插入点由 `_ScanAnnotationTypeEnd`
确定;`|` 不在 `_TYPE_EXPR_STOP_TOKENS` 中,若在数组前缀展开前插入会被
并进数组元素类型(`t.CArray[gdt_entry | t.CExport, 7]`)。数组前缀之所以
仍能脱糖,靠的是 `_AtAnnotationColon` 已把 export/extern 认作语句边界。
- 同行编辑(删修饰符 + 插标记),不增删换行 ⇒ 行号映射保持有效。
"""
if 'export' not in Code and 'extern' 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]] = []
i: int = 0
while i < n:
Tok = Tokens[i]
if Tok.type == tokenize.NAME and Tok.string in _EXPORT_EXTERN_MODIFIERS:
NameIdx: int = _NextSignificantTokenIndex(Tokens, i)
if NameIdx >= 0 and Tokens[NameIdx].type == tokenize.NAME:
ColonIdx: int = _NextSignificantTokenIndex(Tokens, NameIdx)
if ColonIdx >= 0 and Tokens[ColonIdx].string == ':':
NameTok = Tokens[NameIdx]
TypeStart: int = _NextSignificantTokenIndex(Tokens, ColonIdx)
Line: int = Tok.start[0]
Snippet: str = Lines0[Line - 1]
if TypeStart < 0 or Tokens[TypeStart].start[0] != Line:
raise _ExportExternSyntaxError(
'缺少类型注解', Line, Tok.start[1] + 1, Snippet)
TypeEnd: int = _ScanAnnotationTypeEnd(Tokens, TypeStart, n)
if TypeEnd <= TypeStart:
raise _ExportExternSyntaxError(
'缺少类型注解', Line, Tok.start[1] + 1, Snippet)
IsExtern: bool = Tok.string == 'extern'
AfterIdx: int = _NextSignificantTokenIndex(Tokens, TypeEnd - 1)
if (IsExtern and AfterIdx >= 0
and Tokens[AfterIdx].string == '='):
raise _ExportExternSyntaxError(
'extern 变量声明不能带初始值(符号定义在外部)',
Line, Tok.start[1] + 1, Snippet)
# 编辑 1:删掉修饰符及其后的空白(保留缩进;只删 `export` 会
# 在顶格行留下前导空格 ⇒ IndentationError)。
DelStart: int = Tok.start[1]
DelEnd: int = NameTok.start[1]
DelOrig: str = Snippet[DelStart:DelEnd]
Edits.append((Line, DelStart, DelEnd, '', DelOrig))
# 编辑 2:在注解类型终点插入存储类标记。
InsAt: tuple[int, int] = Tokens[TypeEnd - 1].end
Marker: str = ' | t.State' if IsExtern else ' | t.CExport'
Edits.append((InsAt[0], InsAt[1], InsAt[1], Marker, ''))
i = TypeEnd
continue
i += 1
if not Edits:
return Code
return _ApplySameLineEdits(Code, Edits)
# ---------------------------------------------------------------------------
# 注解位置的 C 风格 `__attribute__(...)` 描述符(与 TPV
# includes/ast/parser.vp 的 _IsMagicAttributeCall / _MakeTAttrMarker 同源书写法,
@@ -1290,7 +1415,7 @@ def EnumMemberCountMismatchError(ClassName: str, Visible: list[tuple[str, object
)
# ExpandSoftKeywords 是 Code 的纯函数(全程无全局可变状态,见其"八种展开"实现):
# ExpandSoftKeywords 是 Code 的纯函数(全程无全局可变状态,见其"九种展开"实现):
# 同一文件内容在一次编译里会被多处重复展开(Phase1 / Phase2 / 依赖扫描),
# 缓存结果避免重复正则替换与 tokenize(cProfile:_ExpandTypeDescriptors 里的
# tokenize.generate_tokens 全程 ~590 万次 TokenInfo._make)。纯函数缓存不改输出;有界防膨胀。
@@ -1328,7 +1453,12 @@ def ExpandSoftKeywords(Code: str) -> str:
**同一 AST/同一注册路径**(见 _ExpandRenumBlocks)。成员为普通类体(嵌套
`class 变体:` + 无载荷赋值)。行首 `renum` 的非 `renum 名字:` 形态**响亮失败**
(该词不在任何源码中作标识符)。
八种展开都同行替换(长度可增,不增删换行),AST 行号映射保持有效。
9) `export 名字: T [= 初值]` → `名字: T | t.CExport [= 初值]`;
`extern 名字: T` → `名字: T | t.State`(见 _ExpandExportExternVar)。
与 TPV `_parse_export_extern_var` 同一标记、同一消费路径;函数形态
(`export def f()` / `export f()` / `extern def f()`)不受影响;
`extern` 带初值**响亮失败**(外部定义不写初值)。
九种展开都同行替换(长度可增,不增删换行),AST 行号映射保持有效。
"""
if not Code:
return Code
@@ -1371,6 +1501,12 @@ def _ExpandSoftKeywordsImpl(Code: str) -> str:
# `t.CConst | t.CArray[T, N]`(此处不再有注解位置的裸 '['),避免两条路径重复改写。
if '[' in Code:
Code = _ExpandArrayPrefixAnnotations(Code)
# export / extern 变量声明(语句级软关键字):必须排在**最后**——此时注解已由
# 数组前缀 / 描述符两层脱糖成规范形态(`t.CArray[T, N]` / `t.CConst | ...`),
# 本层只删修饰符 + 在注解类型终点插入存储类标记(`| t.CExport` / `| t.State`)。
# 顺序若反过来,`|` 会被数组前缀展开并进元素类型(见 _ExpandExportExternVar)。
if 'export' in Code or 'extern' in Code:
Code = _ExpandExportExternVar(Code)
return Code