diff --git a/includes/ast/base.vp b/includes/ast/base.vp index fb3134c..fcf145e 100644 --- a/includes/ast/base.vp +++ b/includes/ast/base.vp @@ -154,6 +154,7 @@ CONST_CHAR: t.CDefine = 6 FLAG_IS_ASYNC: t.CDefine = 1 FLAG_SIMPLE: t.CDefine = 2 FLAG_HAS_STAR: t.CDefine = 4 +FLAG_IS_EXTERN: t.CDefine = 8 # ============================================================ diff --git a/includes/ast/parser.vp b/includes/ast/parser.vp index 114b96b..3f49e0e 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_ASYNC, FLAG_IS_EXTERN, ) from .exprs import ( BoolOp, BinOp, UnaryOp, Lambda, IfExp, Dict, Set, @@ -94,15 +94,33 @@ 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) + """判断当前 token 是否为函数定义关键字(def 及其别名 fn/fun/func/function, + 以及 extern 声明关键字) - def 是硬关键字直接判定;fn/fun/func/function 是软关键字, + def 是硬关键字直接判定;fn/fun/func/function/extern 是软关键字, 仅当下一个 token 是 Name 且再下一个是 '('(或泛型 '[T]' 前的 '[')时 - 判定为函数定义。否则按普通标识符处理,兼容既有代码把 func 用作 + 判定为函数定义。extern 还允许 'extern def A(...)' 形式(下一个是 def)。 + 否则按普通标识符处理,兼容既有代码把 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+'(/'[') + nxt: Token | t.CPtr = ps.cur.next + if nxt is None: + return 0 + if nxt.kw_subtype == Keyword.Def: + return 1 + if nxt.type != TokenType.Name: + return 0 + nxt2: Token | t.CPtr = nxt.next + if nxt2 is None: + return 0 + if nxt2.type == TokenType.Op \ + and (nxt2.op_subtype == TokOp.LPar or nxt2.op_subtype == TokOp.Lsqb): + return 1 + return 0 if kw == Keyword.Fn or kw == Keyword.Fun \ or kw == Keyword.Func or kw == Keyword.Function: # 软关键字 lookahead: 下个是 Name,再下个是 '(' 或 '[T...]' 泛型参数(PEP 695) @@ -2210,7 +2228,15 @@ def _parse_with_item(ps: Parser | t.CPtr) -> AST | t.CPtr: 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) - _advance(ps) # def + is_extern_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) + else: + _advance(ps) # def / fn / ... if _cur_type(ps) != TokenType.Name: ps.error_count += 1 return None @@ -2244,11 +2270,25 @@ def _parse_funcdef(ps: Parser | t.CPtr, is_async: t.CInt) -> AST | t.CPtr: returns: AST | t.CPtr = None if _accept_op(ps, TokOp.RArrow): returns = _parse_test(ps) - _expect_op(ps, TokOp.Colon) + if is_extern_fd: + # extern 声明:返回注解改写为 <原类型> | t.State,复用既有 + # State/外部 C 函数识别路径(vpi 注解含 "State" → 定义端裸名 + 不生成函数体) + state_attr_fd: AST | t.CPtr = Attribute(ps.pool, + Name(ps.pool, "t", ASTCtx.Load), + "State", ASTCtx.Load) + if returns != None: + returns = BinOp(ps.pool, returns, OpKind.BitOr, state_attr_fd) + else: + returns = state_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) - _parse_suite_into_children(ps, node) + if is_extern_fd: + node.flags = node.flags | FLAG_IS_EXTERN + if is_extern_fd == 0 or _match_op(ps, TokOp.Colon): + _expect_op(ps, TokOp.Colon) + _parse_suite_into_children(ps, node) + # extern 无 ': pass' 形式(extern A(...) -> int):body 为空,仅声明 _set_pos(node, lineno, col, 0, 0) return node diff --git a/includes/ast/tokens.vp b/includes/ast/tokens.vp index 5f4fc29..d9c07c5 100644 --- a/includes/ast/tokens.vp +++ b/includes/ast/tokens.vp @@ -77,6 +77,8 @@ class Keyword(t.CEnum): Fun: t.State Func: t.State Function: t.State + # extern 函数声明关键字(FFI,等价 t.State 标记;软判定见 _is_funcdef_kw) + Extern: t.State # ============================================================ @@ -255,7 +257,8 @@ def _init_keywords(pool: memhub.MemManager | t.CPtr): _kw_intern(pool, "fn", Keyword.Fn) _kw_intern(pool, "fun", Keyword.Fun) _kw_intern(pool, "func", Keyword.Func) - _kw_intern(pool, "function", Keyword.Function) + _kw_intern(pool, "function", Keyword.Function) + _kw_intern(pool, "extern", Keyword.Extern) # ============================================================ diff --git a/lib/vast/Parser.py b/lib/vast/Parser.py index e11f83c..c82234f 100644 --- a/lib/vast/Parser.py +++ b/lib/vast/Parser.py @@ -54,6 +54,9 @@ class Parser(ExprParserMixin): tok: Token = self._Peek() if tok.Type == NAME: v: str = tok.Value + # extern 函数声明(FFI,等价 t.State)——必须在软关键字 def 分派之前 + if v == 'extern': + return self._ParseExternDef() # 软关键字函数定义:fn/fun/func/function + 标识符 if v in SOFT_KEYWORDS and self._Peek(1).Type == NAME: return self._ParseFunctionDef() @@ -493,6 +496,8 @@ class Parser(ExprParserMixin): self._ExpectNEWLINE() self._SkipNEWLINE() 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 == 'def': return self._ParseFunctionDef(decorators=decorator_list) if tok.Type == NAME and tok.Value == 'async': @@ -532,6 +537,51 @@ class Parser(ExprParserMixin): self._FixLoc(node, start) return node + def _ParseExternDef(self, decorators: list[N.expr] | None = None) -> N.FunctionDef: + """解析 extern 函数声明(FFI,与 t.State 作用相当)。 + + 允许语法(均声明不定义,不生成函数体): + extern def A(x: int) -> int: pass + extern A(x: int) -> int: pass + extern def A(x: int) -> int + extern A(x: int) -> int + + 产出标准 FunctionDef 节点,返回注解改写为 ``<原类型> | t.State`` + 以复用 TPC 的 IsState 路径;并设自定义属性 ``Extern = True`` + 供 Unparser 还原 extern 语法。 + """ + start: Token = self._Next() # 'extern' + 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.Extern = True + # 返回注解改写为 <原类型> | t.State,使 TPC 识别为 State 函数(仅声明) + state_attr: N.expr = N.Attribute(value=N.Name(id='t', ctx=N.Load()), + attr='State', ctx=N.Load()) + if returns is not None: + node.returns = N.BinOp(left=returns, op=N.BitOr(), right=state_attr) + else: + node.returns = state_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/Tokenizer.py b/lib/vast/Tokenizer.py index 44f7298..73dab55 100644 --- a/lib/vast/Tokenizer.py +++ b/lib/vast/Tokenizer.py @@ -43,7 +43,7 @@ _OPERATORS: tuple[str, ...] = ( ) # 行首空白 + 关键字 + 空白 + 标识符 → 软关键字函数定义(Parser 层处理) -SOFT_KEYWORDS: frozenset[str] = frozenset({'fn', 'fun', 'func', 'function'}) +SOFT_KEYWORDS: frozenset[str] = frozenset({'fn', 'fun', 'func', 'function', 'extern'}) # 字符串前缀字母集合(允许任意顺序/大小写组合,如 fr、Rb、BF) _STRING_PREFIX_CHARS: frozenset[str] = frozenset('rRbBuUfF') diff --git a/lib/vast/Unparser.py b/lib/vast/Unparser.py index 0a179ea..21c514b 100644 --- a/lib/vast/Unparser.py +++ b/lib/vast/Unparser.py @@ -72,12 +72,20 @@ class Unparser: return '\n'.join(lines) def _FunctionDef(self, node: N.FunctionDef) -> str: - kw: str = 'async def' if isinstance(node, N.AsyncFunctionDef) else 'def' + 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') decos: str = ''.join(f'@{self._expr(d)}\n' for d in node.decorator_list) params: str = self._Arguments(node.args) ret: str = '' - if node.returns is not None: - ret = f' -> {self._expr(node.returns)}' + returns: N.expr | None = node.returns + if is_extern and isinstance(returns, N.BinOp) and isinstance(returns.op, N.BitOr): + # 还原 extern 原始注解:去掉改写时追加的 | t.State + right: N.expr = returns.right + if (isinstance(right, N.Attribute) and right.attr == 'State' + 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) return f'{decos}{kw} {node.name}({params}){ret}:\n{body}'