feat(ast): 支持 volatile 调用前缀与注解位置 __attribute__(...) 修饰符

- Call 新增 IsVolatile 字段(末尾追加,既有字段偏移不变),承载值位置 volatile f() / volatile S() 前缀标记。

- parser 新增 _IsVolatileCallPrefix + _parse_test 消费点:吃掉 volatile 后对解析出的 Call 置 IsVolatile=1。

- parser 注解位置新增 _IsMagicAttributeCall:描述符收集循环扩为 _IsDescriptorKeyword or _IsMagicAttributeCall,把 __attribute__(...) 的实参经 _MakeTAttrMarker 合成为 t.attr.<name> 标记贴到类型联合最左,与 const/volatile 同族语义。
This commit is contained in:
TermiNexus committed 2026-09-27 12:01:15 +08:00
1 parent 622ae03c9f
commit c392390e0d
2 files changed
+72 -1

No files matched your search

+5
View File
@@ -284,6 +284,10 @@ class Call(ExprNode):
func: rawptr[AST]
args: rawptr[list[rawptr[AST]]]
keywords: rawptr[list[rawptr[AST]]]
# 值位置 `volatile f()` / `volatile S()` 前缀标记(见 parser.vp _IsVolatileCallPrefix):
# 1=本次调用的结果槽(赋值目标/临时槽)按 volatile 存取,结构体构造的写入亦带
# LLVM volatile。追加在末尾:既有字段偏移不变,__sizeof__() 自动纳入。
IsVolatile: int
def __new__(self, pool: rawptr[memhub.MemManager],
func: rawptr[AST],
@@ -301,6 +305,7 @@ class Call(ExprNode):
self.func = func
self.args = args
self.keywords = keywords
self.IsVolatile = 0
_set_parent_list(args, self)
_set_parent_list(keywords, self)
+67 -1
View File
@@ -375,6 +375,24 @@ def _IsDescriptorKeyword(ps: rawptr[Parser]) -> int:
return 0
def _IsMagicAttributeCall(ps: rawptr[Parser]) -> int:
"""注解位置的 `__attribute__(...)` 前缀判定,1=是 / 0=否
`__attribute__` 是软关键字(不进关键字表):只在**注解位置**、且紧跟 '(' 时
由本处消费;作普通标识符/装饰器名(`@__attribute__(...)` 走装饰器通道)都不受影响。
语义与 const / volatile 描述符同族:顺序自由、可混写。
"""
if _cur_type(ps) != TokenType.Name:
return 0
s: str = _cur_str(ps)
if s is None or string.strcmp(s, "__attribute__") != 0:
return 0
nxt: rawptr[Token] = ps.cur.next
if nxt is None or nxt.type != TokenType.Op or nxt.op_subtype != TokOp.LPar:
return 0
return 1
def _MakeDescriptorMarker(ps: rawptr[Parser]) -> rawptr[AST]:
"""由当前描述符关键字构造标记节点 Attribute(Name('t'), 'CConst'/'CVolatile')。
@@ -980,10 +998,37 @@ def _parse_namedexpr(ps: rawptr[Parser]) -> rawptr[AST]:
return left
def _IsVolatileCallPrefix(ps: rawptr[Parser]) -> int:
"""值位置的 `volatile 函数()` / `volatile 结构体()` 前缀判定,1=是 / 0=否
`volatile` 是软关键字:只在"值位置 + 紧跟 调用(Name 后随 '(')"时消费,
作注解描述符(`a: volatile int`)与作标识符的写法都不受影响。
"""
if _cur_kw(ps) != Keyword.Volatile:
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 or nxt2.type != TokenType.Op or nxt2.op_subtype != TokOp.LPar:
return 0
return 1
def _parse_test(ps: rawptr[Parser]) -> rawptr[AST]:
"""test: or_test ['if' or_test 'else' test] | lambdef"""
if _cur_kw(ps) == Keyword.Lambda:
return _parse_lambda(ps)
# 值位置 volatile 调用前缀:`a: int = volatile f()` / `volatile S()`
# ⇒ 消费 volatile 并把调用节点标记为 volatile(结果槽按 volatile 存取;
# 结构体构造的写入亦带 LLVM volatile)。LLVM 的 call 无 volatile 限定符,
# 故语义只落在内存存取上(见 Call.IsVolatile 注释)。
if _IsVolatileCallPrefix(ps) != 0:
_advance(ps) # 消费 volatile
vinner: rawptr[AST] = _parse_test(ps)
if vinner is not None and vinner.kind() == ast.ASTKind.Call:
(ast.Call*(vinner)).IsVolatile = 1
return vinner
left: rawptr[AST] = _parse_or_test(ps)
if left is None: return None
if _cur_kw(ps) == Keyword.If:
@@ -2371,11 +2416,32 @@ def _parse_type_ann(ps: rawptr[Parser]) -> rawptr[AST]:
mod_count: int = 0
mod_lineno: int = _cur_lineno(ps)
mod_col: int = _cur_col(ps)
while _IsDescriptorKeyword(ps) != 0:
while _IsDescriptorKeyword(ps) != 0 or _IsMagicAttributeCall(ps) != 0:
if mods is None:
# 元素大小显式传 8:list[T](pool, elem_size) 按字节步长寻址,传 4 会让
# 两个及以上描述符(如 `const volatile`)的第 2 个 8 字节指针越位写入。
mods = list[rawptr[AST]](ps.pool, 8)
if _IsMagicAttributeCall(ps) != 0:
# C 风格 `a: __attribute__(section(".text.wo"), aligned(16)) const int = ...`:
# 与 const / volatile 同位置、同顺序自由、可混写;每个实参 desugar 成
# `t.attr.<name>`(带调用则保留实参)后并入同一 markers 列表,由
# _PrependDescriptorMarkers 贴到类型联合最左(与 TPC 文本展开同形)。
ac_node: rawptr[AST] = _parse_test(ps)
if ac_node is None:
break
if ac_node.kind() != ast.ASTKind.Call:
break
ac: ast.Call* = ast.Call*(ac_node)
if ac.args is not None:
acn: sizet = len(ac.args)
aci: sizet = 0
while aci < acn:
mk: rawptr[AST] = _MakeTAttrMarker(ps, ac.args.get(aci))
if mk is not None:
mods.append(mk)
mod_count += 1
aci += 1
continue
mods.append(_MakeDescriptorMarker(ps))
mod_count += 1
_advance(ps)