diff --git a/includes/ast/base.vp b/includes/ast/base.vp index fcf145e..dd2d38d 100644 --- a/includes/ast/base.vp +++ b/includes/ast/base.vp @@ -155,6 +155,7 @@ FLAG_IS_ASYNC: t.CDefine = 1 FLAG_SIMPLE: t.CDefine = 2 FLAG_HAS_STAR: t.CDefine = 4 FLAG_IS_EXTERN: t.CDefine = 8 +FLAG_IS_EXPORT: t.CDefine = 16 # ============================================================ diff --git a/includes/ast/parser.vp b/includes/ast/parser.vp index 9ee5c85..44aa098 100644 --- a/includes/ast/parser.vp +++ b/includes/ast/parser.vp @@ -10,7 +10,7 @@ from .base import ( _set_pos, _binop_from_op, _augop_from_op, _cmpop_from_op, _copy_str, _inherit_pos, _init_ast, CONST_INT, CONST_FLOAT, CONST_STR, CONST_BOOL, CONST_NONE, CONST_CHAR, - FLAG_IS_ASYNC, FLAG_IS_EXTERN, + FLAG_IS_ASYNC, FLAG_IS_EXTERN, FLAG_IS_EXPORT, ) from .exprs import ( BoolOp, BinOp, UnaryOp, Lambda, IfExp, Dict, Set, @@ -95,18 +95,20 @@ def _cur_kw(ps: Parser | t.CPtr) -> t.CInt: def _is_funcdef_kw(ps: Parser | t.CPtr, kw: t.CInt) -> int: """判断当前 token 是否为函数定义关键字(def 及其别名 fn/fun/func/function, - 以及 extern 声明关键字) + 以及 extern/export 声明关键字) - def 是硬关键字直接判定;fn/fun/func/function/extern 是软关键字, + def 是硬关键字直接判定;fn/fun/func/function/extern/export 是软关键字, 仅当下一个 token 是 Name 且再下一个是 '('(或泛型 '[T]' 前的 '[')时 - 判定为函数定义。extern 还允许 'extern def A(...)' 形式(下一个是 def)。 + 判定为函数定义。extern 还允许 'extern def A(...)' 形式(下一个是 def), + export 同样允许 'export def A(...)' 形式。 否则按普通标识符处理,兼容既有代码把 func 用作 字段/参数名(如 AST 节点 func 字段、Call.func 注解 `func: AST | t.CPtr`)。 """ if kw == Keyword.Def: return 1 - if kw == Keyword.Extern: - # extern def A(...) 或 extern A(...)(extern 后是 def 或 Name+'(/'[') + if kw == Keyword.Extern or kw == Keyword.Export: + # extern/export def A(...) 或 extern/export A(...) + # (extern/export 后是 def 或 Name+'(/'[') nxt: Token | t.CPtr = ps.cur.next if nxt is None: return 0 @@ -2229,12 +2231,19 @@ def _parse_funcdef(ps: Parser | t.CPtr, is_async: t.CInt) -> AST | t.CPtr: lineno: t.CInt = _cur_lineno(ps) col: t.CInt = _cur_col(ps) is_extern_fd: t.CInt = 0 + is_export_fd: t.CInt = 0 if _cur_kw(ps) == Keyword.Extern: # extern 声明:extern [def] A(...) [-> T] [": pass" 可选] is_extern_fd = 1 _advance(ps) # extern if _cur_kw(ps) == Keyword.Def: _advance(ps) + elif _cur_kw(ps) == Keyword.Export: + # export 声明:export [def] A(...) [-> T] [": pass" 可选] + is_export_fd = 1 + _advance(ps) # export + if _cur_kw(ps) == Keyword.Def: + _advance(ps) else: _advance(ps) # def / fn / ... if _cur_type(ps) != TokenType.Name: @@ -2280,16 +2289,28 @@ def _parse_funcdef(ps: Parser | t.CPtr, is_async: t.CInt) -> AST | t.CPtr: returns = BinOp(ps.pool, returns, OpKind.BitOr, state_attr_fd) else: returns = state_attr_fd + elif is_export_fd: + # export 声明:返回注解改写为 <原类型> | t.CExport,复用既有 + # CExport 识别路径(vpi 注解含 "CExport" → 定义端裸名 + 不加模块 SHA1 头) + export_attr_fd: AST | t.CPtr = Attribute(ps.pool, + Name(ps.pool, "t", ASTCtx.Load), + "CExport", ASTCtx.Load) + if returns != None: + returns = BinOp(ps.pool, returns, OpKind.BitOr, export_attr_fd) + else: + returns = export_attr_fd dec_list: list[AST | t.CPtr] | t.CPtr = ps.pending_decorators ps.pending_decorators = None node: FunctionDef | t.CPtr = FunctionDef(ps.pool, name, args, dec_list, returns, is_async, type_params_fd) if is_extern_fd: node.flags = node.flags | FLAG_IS_EXTERN - if is_extern_fd == 0 or _match_op(ps, TokOp.Colon): + if is_export_fd: + node.flags = node.flags | FLAG_IS_EXPORT + if (is_extern_fd == 0 and is_export_fd == 0) or _match_op(ps, TokOp.Colon): _expect_op(ps, TokOp.Colon) _parse_suite_into_children(ps, node) else: - # extern 无 ': pass' 形式(extern A(...) -> int):body 为空,仅声明。 + # extern/export 无 ': pass' 形式(extern A(...) -> int):body 为空,仅声明。 # 必须消费本行 NewLine:模块级/类体语句循环只 _skip_nl(Nl), # 残留 NewLine 会被下一轮当表达式解析(_parse_atom → error_count++) # → parse_tokens 返回 None(TPV 编译 extern 文件报 7 个扫描失败的根因) diff --git a/includes/ast/tokens.vp b/includes/ast/tokens.vp index d9c07c5..54a316b 100644 --- a/includes/ast/tokens.vp +++ b/includes/ast/tokens.vp @@ -79,6 +79,8 @@ class Keyword(t.CEnum): Function: t.State # extern 函数声明关键字(FFI,等价 t.State 标记;软判定见 _is_funcdef_kw) Extern: t.State + # export 函数声明关键字(等价 t.CExport,裸名导出不加 SHA1 头;软判定见 _is_funcdef_kw) + Export: t.State # ============================================================ @@ -258,7 +260,8 @@ def _init_keywords(pool: memhub.MemManager | t.CPtr): _kw_intern(pool, "fun", Keyword.Fun) _kw_intern(pool, "func", Keyword.Func) _kw_intern(pool, "function", Keyword.Function) - _kw_intern(pool, "extern", Keyword.Extern) + _kw_intern(pool, "extern", Keyword.Extern) + _kw_intern(pool, "export", Keyword.Export) # ============================================================ diff --git a/lib/vast/Parser.py b/lib/vast/Parser.py index c82234f..ea88009 100644 --- a/lib/vast/Parser.py +++ b/lib/vast/Parser.py @@ -57,6 +57,10 @@ class Parser(ExprParserMixin): # extern 函数声明(FFI,等价 t.State)——必须在软关键字 def 分派之前 if v == 'extern': return self._ParseExternDef() + # export 函数声明(等价 t.CExport,裸名导出不增加 SHA1 头)—— + # 必须在软关键字 def 分派之前 + if v == 'export': + return self._ParseExportDef() # 软关键字函数定义:fn/fun/func/function + 标识符 if v in SOFT_KEYWORDS and self._Peek(1).Type == NAME: return self._ParseFunctionDef() @@ -498,6 +502,8 @@ class Parser(ExprParserMixin): tok: Token = self._Peek() if tok.Type == NAME and tok.Value == 'extern': return self._ParseExternDef(decorators=decorator_list) + if tok.Type == NAME and tok.Value == 'export': + return self._ParseExportDef(decorators=decorator_list) if tok.Type == NAME and tok.Value == 'def': return self._ParseFunctionDef(decorators=decorator_list) if tok.Type == NAME and tok.Value == 'async': @@ -582,6 +588,52 @@ class Parser(ExprParserMixin): self._FixLoc(node, start) return node + def _ParseExportDef(self, decorators: list[N.expr] | None = None) -> N.FunctionDef: + """解析 export 函数声明(等价 t.CExport,导出裸名、不加模块 SHA1 头)。 + + 允许语法(与 extern 对称,配不配 def 都一样): + export def A(x: int) -> int: pass + export A(x: int) -> int: pass + export def A(x: int) -> int + export A(x: int) -> int + + 产出标准 FunctionDef 节点,返回注解改写为 ``<原类型> | t.CExport`` + 以复用 TPC 的 CExport 路径(符号名不增加 SHA1 前缀);并设自定义 + 属性 ``Export = True`` 供 Unparser 还原 export 语法。 + """ + start: Token = self._Next() # 'export' + if self._AtName('def'): + self._Next() + name_tok: Token = self._ExpectName() + type_params: list[N.AST] = [] + if self._AtOp('['): + type_params = self._ParseTypeParams() + args: N.arguments = self._ParseArguments() + returns: N.expr | None = None + if self._AtOp('->'): + self._Next() + returns = self.ParseExpr() + body: list[N.stmt] = [] + if self._AtOp(':'): + self._Next() + body = self._ParseSuite() + node: N.FunctionDef = N.FunctionDef( + name=name_tok.Value, args=args, body=body, + decorator_list=decorators or [], returns=returns, + type_comment=None, type_params=type_params, + ) + node.Export = True + # 返回注解改写为 <原类型> | t.CExport,使 TPC 识别为 CExport 函数 + # (导出裸名,不增加模块 SHA1 头;有条件情况下导出) + export_attr: N.expr = N.Attribute(value=N.Name(id='t', ctx=N.Load()), + attr='CExport', ctx=N.Load()) + if returns is not None: + node.returns = N.BinOp(left=returns, op=N.BitOr(), right=export_attr) + else: + node.returns = export_attr + self._FixLoc(node, start) + return node + def _ParseTypeParams(self) -> list[N.AST]: """解析 PEP 695 类型参数:[T, *Ts, **P, U: Bound]""" self._ExpectOp('[') diff --git a/lib/vast/Unparser.py b/lib/vast/Unparser.py index 21c514b..0de3389 100644 --- a/lib/vast/Unparser.py +++ b/lib/vast/Unparser.py @@ -73,7 +73,13 @@ class Unparser: def _FunctionDef(self, node: N.FunctionDef) -> str: is_extern: bool = bool(getattr(node, 'Extern', False)) - kw: str = 'async def' if isinstance(node, N.AsyncFunctionDef) else ('extern def' if is_extern else 'def') + is_export: bool = bool(getattr(node, 'Export', False)) + if is_extern: + kw: str = 'extern def' + elif is_export: + kw = 'export def' + else: + kw = 'async def' if isinstance(node, N.AsyncFunctionDef) else 'def' decos: str = ''.join(f'@{self._expr(d)}\n' for d in node.decorator_list) params: str = self._Arguments(node.args) ret: str = '' @@ -84,6 +90,12 @@ class Unparser: if (isinstance(right, N.Attribute) and right.attr == 'State' and isinstance(right.value, N.Name) and right.value.id == 't'): returns = returns.left + if is_export and isinstance(returns, N.BinOp) and isinstance(returns.op, N.BitOr): + # 还原 export 原始注解:去掉改写时追加的 | t.CExport + right = returns.right + if (isinstance(right, N.Attribute) and right.attr == 'CExport' + and isinstance(right.value, N.Name) and right.value.id == 't'): + returns = returns.left if returns is not None: ret = f' -> {self._expr(returns)}' body: str = self._Block(node.body)