feat(ast): 支持 export/extern 变量声明语法糖

export NAME: T [= v] → NAME: T | t.CExport [= v](裸名导出,不加模块 SHA1 头)

extern NAME: T → NAME: T | t.State(仅声明 + 裸名);extern 带初始值响亮失败

新增 _is_export_extern_var lookahead(第三 token 为 : 判变量,(: / [ 判函数不影响 export def)与 _parse_export_extern_var
This commit is contained in:
TermiNexus committed 2026-09-27 17:56:46 +08:00
1 parent 46cd4b4ebf
commit f854f7e99a
1 file changed
+112
+112
View File
@@ -251,6 +251,40 @@ def _is_typedef(ps: rawptr[Parser]) -> int:
return 1
def _is_export_extern_var(ps: rawptr[Parser]) -> int:
"""`export NAME :` / `extern NAME :` 变量声明的软关键字判定(1=是,0=否)。
`export` / `extern` 既可用于**函数**声明(`export def f()` / `export f()` /
`extern def f()`,见 `_is_funcdef_kw`),也可用于**变量**声明
(`export gdt: [7]gdt_entry = []`;`extern a: int`)。二者由第三个 token 区分:
函数是 `(`(参数表)或 `[`(泛型),变量是 `:`(类型注解)。
- `export name(` / `extern def f(`(函数)⇒ 本判据不命中,函数语义逐字不变;
- `export = 5` / `export(x)` / `export.attr`(把 export 当标识符)⇒ 不命中;
- `export NAME:` 后缺类型等非法形态在 `_parse_export_extern_var` 内
`_record_parse_err` **响亮失败**(非静默)。
"""
kw: int = _cur_kw(ps)
if kw != Keyword.Export and kw != Keyword.Extern:
return 0
nxt: rawptr[Token] = ps.cur.next
if nxt is None:
return 0
if nxt.type != TokenType.Name:
return 0
# 名字必须是**非关键字**标识符(排除 `export def f()` 之类把关键字当名字的形态)
if nxt.kw_subtype != Keyword.NotKeyword:
return 0
nxt2: rawptr[Token] = nxt.next
if nxt2 is None:
return 0
if nxt2.type != TokenType.Op:
return 0
if nxt2.op_subtype != TokOp.Colon:
return 0
return 1
def _is_match_stmt(ps: rawptr[Parser]) -> int:
"""match 是软关键字(CPython 3.10+):lookahead 判断是否为 match 语句
@@ -2760,6 +2794,15 @@ def _parse_statement_into(ps: rawptr[Parser], parent: rawptr[AST]):
if td_stmt is not None:
parent.append(td_stmt)
return
# export / extern 变量声明(软关键字):`export NAME: T [= v]` /
# `extern NAME: T`(**不带**初始值)→ 带存储类标记的模块级 AnnAssign
# (见 _parse_export_extern_var)。函数形态(`export NAME(` /
# `extern def NAME(`)被 lookahead 排除,语义逐字不变。
if _is_export_extern_var(ps) != 0:
ee_stmt: rawptr[AST] = _parse_export_extern_var(ps)
if ee_stmt is not None:
parent.append(ee_stmt)
return
# virtual / novirtual 修饰符(软关键字):只在"语句起始 + 紧跟 class/def"
# 时生效;合成既有装饰器 @t.CVTable / @t.NoVTable 后照常走 class/def 解析
# (见 _ParseVirtualModified)。放在 class/def 分派之前:修饰符必须先被消费,
@@ -3813,6 +3856,75 @@ def _parse_typedef(ps: rawptr[Parser]) -> rawptr[AST]:
return node
def _parse_export_extern_var(ps: rawptr[Parser]) -> rawptr[AST]:
"""`export NAME: T [= v]` / `extern NAME: T` → 带存储类标记的模块级 AnnAssign。
- **export**:注解改写为 `T | t.CExport`,与 `export def` **同一标记、同一消费
路径**(定义端裸名,不加模块 SHA1 头——内联汇编 / C 符号对接必需)。
允许带初始值;`export tss_buf: [104]uint8`(无初始值)生成零初始化全局定义。
- **extern**:注解改写为 `T | t.State`(= t.CExtern | t.CExport:仅声明 + 裸名),
与 `extern def` 同口径。**不允许初始值**(符号定义在外部),
`extern a: int = 1` → `_record_parse_err` **响亮失败**(非静默)。
行号/列号取自 `export`/`extern` 关键字位置,报错回显定位到真实源码行;
语句必须换行/文件尾结束(与 _parse_typedef / _parse_define_const 同口径)。
"""
lineno: int = _cur_lineno(ps)
col: int = _cur_col(ps)
is_extern: int = 0
if _cur_kw(ps) == Keyword.Extern:
is_extern = 1
_advance(ps) # export / extern
if _cur_type(ps) != TokenType.Name:
# 缺变量名(如 `export : int`):响亮失败。
_record_parse_err(ps)
ps.error_count += 1
return None
name: str = _cur_str(ps)
_advance(ps)
if _expect_op(ps, TokOp.Colon) == 0:
# 缺 `:`(如 `export x`):响亮失败。
return None
ann: rawptr[AST] = _parse_type_ann(ps)
if ann is None:
ps.error_count += 1
_record_parse_err(ps)
return None
# 存储类标记:export → t.CExport(裸名导出);extern → t.State(声明 + 裸名)
storage_attr: rawptr[AST] = Attribute(ps.pool,
Name(ps.pool, "t", ASTCtx.Load),
"State" if is_extern != 0 else "CExport",
ASTCtx.Load)
annotation: rawptr[AST] = BinOp(ps.pool, ann, OpKind.BitOr, storage_attr)
value: rawptr[AST] = None
if _match_op(ps, TokOp.Equal):
if is_extern != 0:
# extern 变量是外部定义:本模块不允许带初始值。
_RecordDescriptorErr("extern 变量声明不能带初始值(符号定义在外部)",
lineno, col)
ps.error_count += 1
return None
_advance(ps)
value = _parse_test(ps)
if value is None:
ps.error_count += 1
_record_parse_err(ps)
return None
target: rawptr[AST] = Name(ps.pool, name, ASTCtx.Store)
_set_pos(target, lineno, col, 0, 0)
node: rawptr[AST] = AnnAssign(ps.pool, target, annotation, value,
1 if value is not None else 0)
_set_pos(node, lineno, col, 0, 0)
_skip_nl(ps)
if _cur_type(ps) != TokenType.NewLine and _cur_type(ps) != TokenType.EndMarker:
_record_parse_err(ps)
ps.error_count += 1
return None
if _cur_type(ps) == TokenType.NewLine:
_advance(ps)
return node
def _parse_enumdef(ps: rawptr[Parser]) -> rawptr[AST]:
"""enum NAME: 语法糖 → 与 `class NAME(t.CEnum):` 逐字段同形的 ClassDef。