feat(vast/ast): 新增 extern 软关键字声明 FFI 函数(等价 t.State)

TPC 侧 vast:
- extern [def] A(...) [-> T] [: pass] 四种语法解析为 FunctionDef
- 返回注解改写为 <原类型> | t.State,复用 TPC 的 IsState 外部函数路径
- 设 Extern 标记,Unparser 还原 extern 语法(round-trip 一致)

TPV 侧 includes/ast(自举前端):
- Keyword.Extern 枚举与 intern,_is_funcdef_kw 软判定
  (extern def / extern Name+'( 或 '[')
- _parse_funcdef 支持无冒号声明、FLAG_IS_EXTERN 标记、
  returns 改写为 <原类型> | t.State(vpi 含 State → 定义端裸名 + 不生成函数体)
This commit is contained in:
TermiNexus committed 2026-08-17 10:41:44 +08:00
1 parent aa9042173e
commit ce568e10b7
6 files changed
+114 -12

No files matched your search

+1
View File
@@ -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
# ============================================================
+47 -7
View File
@@ -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
+4 -1
View File
@@ -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)
# ============================================================
+50
View File
@@ -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('[')
+1 -1
View File
@@ -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')
+11 -3
View File
@@ -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}'