feat(ast): 支持 inline def 与 @__attribute__(...) 语法糖(均 desugar 到既有装饰器)

- parser.vp:新增 inline 修饰符(软关键字,语句起始 + 后随函数定义时生效),合成 @t.attr.always_inline;inline 不进关键字表,作标识符的既有写法不受影响

- parser.vp:新增 @__attribute__(section(...), always_inline) desugar ⇒ @c.Attribute(t.attr.section(...), t.attr.always_inline);带/不带调用的属性名都支持,class/def 共用装饰器链,产出与手写形态逐字段同形
This commit is contained in:
TermiNexus committed 2026-09-27 11:41:09 +08:00
1 parent 7025982944
commit 622ae03c9f
1 file changed
+182
+182
View File
@@ -611,6 +611,86 @@ def _ParseVirtualModified(ps: rawptr[Parser], parent: rawptr[AST]):
parent.append(stmt)
# ============================================================
# inline 修饰符(软关键字):`inline def f(...)` ⇒ 合成 @t.attr.always_inline
#
# 与 virtual/novirtual 完全同构:只在「语句起始 + inline + 紧跟函数定义」时生效,
# 其余位置的 inline 仍是普通标识符(故 `inline = 5` / 字段名 inline 等既有写法不受
# 影响)。合成的装饰器与**手写** `@t.attr.always_inline` 逐字段同形,下游
# FuncGenBase.extract_func_attrs → _append_llvm_attr 的 "always_inline" → LLVM
# "alwaysinline" 映射原样复用;StubGen 跨模块 alwaysinline 合并规则同样原样生效。
#
# 为什么不进关键字表:`inline` 在 TPV/TPC 既有源码里可能是普通标识符,进表会把
# 全局词法口径改掉;此处按软关键字文本判定,作用域只限"语句起始后随函数定义"。
# ============================================================
def _IsInlineModifier(ps: rawptr[Parser]) -> int:
"""当前 token 是否为 `inline`(软关键字,且后随函数定义),1=是 / 0=否"""
if _cur_type(ps) != TokenType.Name:
return 0
s: str = _cur_str(ps)
if s is None or string.strcmp(s, "inline") != 0:
return 0
nxt: rawptr[Token] = ps.cur.next
if nxt is None or nxt.type != TokenType.Name:
return 0
nxt2: rawptr[Token] = nxt.next
if nxt2 is None:
return 0
# 后随 def(硬关键字):`inline def f(...)`
if nxt.kw_subtype == Keyword.Def:
return 1
# 后随函数定义软关键字别名(fn/fun/func/function):与 _is_funcdef_kw 同口径
# ——必须是 Name + '(' 或 '[T]',避免把 `inline func` 这种普通标识符组合误判。
if nxt.kw_subtype == Keyword.Fn or nxt.kw_subtype == Keyword.Fun \
or nxt.kw_subtype == Keyword.Func or nxt.kw_subtype == Keyword.Function:
if nxt2.type == TokenType.Op \
and (nxt2.op_subtype == TokOp.LPar or nxt2.op_subtype == TokOp.Lsqb):
return 1
return 0
def _MakeInlineMarker(ps: rawptr[Parser]) -> rawptr[AST]:
"""由当前 inline token 构造标记节点 Attribute(Attribute(Name('t'),'attr'), 'always_inline')
形态与**手写** `@t.attr.always_inline` 装饰器逐字段同形。取属性名的既有逻辑
(FuncGenBase._get_attr_name_from_node)对 Attribute 节点取最外层 attr,
故 t.attr.always_inline → "always_inline" → _append_llvm_attr 映射为 LLVM
"alwaysinline"。**刻意不用** `@c.Attribute(t.attr.x)` 形态:该形态的提取器
要求本源文件 `import c`,而 includes/asm.vp 之类文件没有 import,会静默不内联。
"""
lineno: int = _cur_lineno(ps)
col: int = _cur_col(ps)
t_name: rawptr[AST] = Name(ps.pool, "t", ASTCtx.Load)
attr_at: rawptr[AST] = Attribute(ps.pool, t_name, "attr", ASTCtx.Load)
marker: rawptr[AST] = Attribute(ps.pool, attr_at, "always_inline", ASTCtx.Load)
_set_pos(marker, lineno, col, 0, 0)
return marker
def _ParseInlineModified(ps: rawptr[Parser], parent: rawptr[AST]):
"""`inline def f(...)`:消费 inline,合成 @t.attr.always_inline 后交给 _parse_funcdef"""
marker: rawptr[AST] = _MakeInlineMarker(ps)
if marker is None:
return
_advance(ps) # 消费 inline 修饰符
# 合并既有装饰器(`@deco` + inline def 组合),标记追加在末尾;位置无关
# (下游按装饰器名判定)。与 _ParseVirtualModified 同一套 pending_decorators 通道。
merged: rawptr[list[rawptr[AST]]] = list[rawptr[AST]](ps.pool, 8)
if merged is None:
return
if ps.pending_decorators is not None:
dn: sizet = len(ps.pending_decorators)
di: sizet = 0
while di < dn:
merged.append(ps.pending_decorators.get(di))
di += 1
merged.append(marker)
ps.pending_decorators = merged
stmt: rawptr[AST] = _parse_funcdef(ps, 0)
if stmt is not None:
parent.append(stmt)
def _IsBareDescriptor(ps: rawptr[Parser]) -> int:
"""裸用描述符判定:整条语句只有一个描述符串(const[: volatile]),1=是 / 0=否。
@@ -2469,6 +2549,12 @@ def _parse_statement_into(ps: rawptr[Parser], parent: rawptr[AST]):
if _IsVirtualModifier(ps) != 0:
_ParseVirtualModified(ps, parent)
return
# inline 修饰符(软关键字):只在"语句起始 + inline + 紧跟函数定义"时生效,
# 合成既有装饰器 @t.always_inline 后照常走 def 解析(见 _ParseInlineModified)。
# 与 virtual/novirtual 同一位置、同一套 pending_decorators 通道。
if _IsInlineModifier(ps) != 0:
_ParseInlineModified(ps, parent)
return
# 类型描述符裸用:整条语句只有 `const` / `volatile`(缺类型)⇒ 响亮失败。
# 只在"描述符串后直接换行/文件尾/分号"时命中(软关键字边界),故
# `const = 5`(作标识符的赋值)/ `t.attr.const()` 等写法不命中、语义不变。
@@ -3908,6 +3994,100 @@ def _parse_match_case(ps: rawptr[Parser]) -> rawptr[AST]:
return node
# ============================================================
# __attribute__ 装饰器 desugar(C 风格属性书写法)
#
# @__attribute__(section(".text.wo"), always_inline)
# def f(): ...
#
# 等价于手写 `@c.Attribute(t.attr.section(".text.wo"), t.attr.always_inline)`:
# 实参用**无前缀**属性名,逐个 desugar 为 `t.attr.<name>`(带调用时保留实参),
# 外层换成 `c.Attribute(...)`。产出的装饰器与手写形态逐字段同形 ⇒
# ClassGenBase._has_decorator / FuncGenBase.extract_func_attrs / _append_llvm_attr
# 原样识别(零新增判据);也即同时适用于 class 与 def(装饰器链共用本函数)。
# 容错:实参已是 `t.attr.X` / `c.Attribute(...)` 形态时原样保留(允许混写)。
# ============================================================
def _IsMagicAttributeCallee(node: ast.AST*) -> int:
"""节点是否为 `__attribute__`(Name 形态,或 X.__attribute__ 的尾段形态)"""
if node is None:
return 0
k: int = node.kind()
if k == ast.ASTKind.Name:
nm: ast.Name* = ast.Name*(node)
if nm.id is not None and string.strcmp(nm.id, "__attribute__") == 0:
return 1
return 0
if k == ast.ASTKind.Attribute:
at: ast.Attribute* = ast.Attribute*(node)
if at.attr is not None and string.strcmp(at.attr, "__attribute__") == 0:
return 1
return 0
def _MakeTAttrMarker(ps: rawptr[Parser], arg: ast.AST*) -> ast.AST*:
"""把一个 __attribute__ 实参 desugar 为 `t.attr.<name>`(带调用则保留实参)
- `always_inline`(Name) → `t.attr.always_inline`
- `section(".text.wo")`(Call)→ `t.attr.section(".text.wo")`
- 已含 `t.attr.X` 形态(value 也是 Attribute)→ 原样返回
"""
if arg is None:
return None
k: int = arg.kind()
if k == ast.ASTKind.Attribute:
at0: ast.Attribute* = ast.Attribute*(arg)
if at0.value is not None and at0.value.kind() == ast.ASTKind.Attribute:
return arg
return arg
attr_root: ast.AST* = Attribute(ps.pool, Name(ps.pool, "t", ASTCtx.Load),
"attr", ASTCtx.Load)
if k == ast.ASTKind.Name:
nm2: ast.Name* = ast.Name*(arg)
if nm2.id is None:
return arg
mk: ast.AST* = Attribute(ps.pool, attr_root, nm2.id, ASTCtx.Load)
_set_pos(mk, nm2.lineno, nm2.col_offset, 0, 0)
return mk
if k == ast.ASTKind.Call:
cl2: ast.Call* = ast.Call*(arg)
if cl2.func is None or cl2.func.kind() != ast.ASTKind.Name:
return arg
fnm: ast.Name* = ast.Name*(cl2.func)
if fnm.id is None:
return arg
inner: ast.AST* = Attribute(ps.pool, attr_root, fnm.id, ASTCtx.Load)
_set_pos(inner, fnm.lineno, fnm.col_offset, 0, 0)
mk2: ast.AST* = Call(ps.pool, inner, cl2.args, cl2.keywords)
_set_pos(mk2, cl2.lineno, cl2.col_offset, 0, 0)
return mk2
return arg
def _DesugarMagicAttribute(ps: rawptr[Parser], expr: ast.AST*) -> ast.AST*:
"""`@__attribute__(...)` → `@c.Attribute(t.attr.x, ...)`;其它装饰器原样返回"""
if expr is None or expr.kind() != ast.ASTKind.Call:
return expr
cl: ast.Call* = ast.Call*(expr)
if _IsMagicAttributeCallee(cl.func) == 0:
return expr
c_func: ast.AST* = Attribute(ps.pool, Name(ps.pool, "c", ASTCtx.Load),
"Attribute", ASTCtx.Load)
new_args: rawptr[list[rawptr[AST]]] = list[rawptr[AST]](ps.pool, 8)
if new_args is None:
return expr
if cl.args is not None:
an: sizet = len(cl.args)
ai: sizet = 0
while ai < an:
mk: ast.AST* = _MakeTAttrMarker(ps, cl.args.get(ai))
if mk is not None:
new_args.append(mk)
ai += 1
new_call: ast.AST* = Call(ps.pool, c_func, new_args, None)
_set_pos(new_call, cl.lineno, cl.col_offset, 0, 0)
return new_call
def _parse_decorators(ps: rawptr[Parser]) -> rawptr[list[rawptr[AST]]]:
"""解析装饰器链,返回 list。"""
decs: rawptr[list[rawptr[AST]]] = list[rawptr[AST]](ps.pool, 8)
@@ -3916,6 +4096,8 @@ def _parse_decorators(ps: rawptr[Parser]) -> rawptr[list[rawptr[AST]]]:
col: int = _cur_col(ps)
_advance(ps) # @
expr: rawptr[AST] = _parse_namedexpr(ps)
# C 风格 `@__attribute__(...)` desugar 成既有的 `@c.Attribute(t.attr.<x>...)`
expr = _DesugarMagicAttribute(ps, expr)
_skip_nl(ps)
if _cur_type(ps) == TokenType.NewLine:
_advance(ps)