diff --git a/lib/core/SymbolUtils.py b/lib/core/SymbolUtils.py index 2ea47bd..33ab73e 100644 --- a/lib/core/SymbolUtils.py +++ b/lib/core/SymbolUtils.py @@ -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