refactor(parser): 删除 T1 | T2 注解组合,指针/描述符/存储标记改为下标包装

This commit is contained in:
TermiNexus committed 2026-10-18 13:39:56 +08:00
1 parent 9696cf8569
commit cc6f56c4e7
1 file changed
+89 -65
+89 -65
View File
@@ -428,42 +428,74 @@ def _MakeDescriptorMarker(ps: Parser*) -> AST*:
return marker
def _MakeTypeWrapper(ps: Parser*, marker: AST*, inner: AST*) -> AST*:
"""Wrap a type in a builtin marker subscript: marker[inner] (e.g. CPtr[T], CConst[T])."""
node: AST* = Subscript(ps.pool, marker, inner, ASTCtx.Load)
if inner is not None:
_set_pos(node, inner.lineno, inner.col_offset, 0, 0)
return node
def _PrependDescriptorMarkers(ps: Parser*,
base: AST*,
mods: list[AST*]*,
mod_count: int) -> AST*:
"""Prepend descriptor markers to the leftmost operand of the type union (same shape as TPC text).
"""Wrap the type in the descriptor markers collected before it.
TPC expands `const volatile int | rawptr` to the left-associative `(((t.CConst | t.CVolatile) | int) | rawptr)`;
this function produces the identical tree rather than a differently-shaped one.
`const volatile int` becomes `CConst[CVolatile[int]]`; the marker order is irrelevant to the
backend, which only asks whether a given descriptor is present.
"""
slot_parent: BinOp* = None # leftmost BitOr node: its left is the leftmost operand
slot_node: AST* = base
cur: AST* = base
while cur is not None and cur.kind() == ASTKind.BinOp:
b: BinOp* = (BinOp*)(cur)
if b.op != OpKind.BitOr or b.left is None:
break
nxt: AST* = b.left
if nxt.kind() == ASTKind.BinOp:
nb: BinOp* = (BinOp*)(nxt)
if nb.op == OpKind.BitOr and nb.left is not None:
cur = nxt
continue
slot_parent = b
slot_node = nxt
break
# Descriptor chain left-associates in source order: `const volatile` -> (t.CConst | t.CVolatile)
chain: AST* = mods.get(0)
i: int = 1
while i < mod_count:
chain = BinOp(ps.pool, chain, OpKind.BitOr, mods.get(i))
i += 1
newnode: AST* = BinOp(ps.pool, chain, OpKind.BitOr, slot_node)
if slot_parent is None:
return newnode
slot_parent.left = newnode
return base
out: AST* = base
i: int = mod_count - 1
while i >= 0:
out = _MakeTypeWrapper(ps, mods.get(i), out)
i -= 1
return out
def _RecordTypeUnionErr(detail: str, lineno: int, col: int):
"""Record a retired `T1 | T2` type union (fail-fast; first only).
All former union forms have dedicated syntax: T* / CPtr[T] for pointers, const/volatile/bigend/
littlend for descriptors, &T / &temp T for value references, export/extern/static for storage.
"""
global _g_parse_err_line, _g_parse_err_col, _g_parse_err_msg
if _g_parse_err_line != 0:
return
_g_parse_err_line = lineno
_g_parse_err_col = col
buf: str = stdlib.malloc(1024)
if buf is not None:
stdio.snprintf(buf, 1024,
"`T1 | T2` type unions were removed (%s). Use the dedicated syntax instead: "
"pointer -> `T*`; const/volatile/bigend/littlend -> the prefix descriptor; "
"value reference -> `&T` / `&temp T`; storage -> `export` / `extern` / `static` / `def`; "
"fixed array -> `[N]T` or `CArray[T, N]`.", detail)
_g_parse_err_msg = buf
else:
_g_parse_err_msg = detail
def _RejectTypeUnion(ps: Parser*, node: AST*) -> int:
"""Fail-fast when an annotation contains a retired `|` type union; 1=rejected."""
if node is None: return 0
k: int = node.kind()
if k == ASTKind.BinOp:
bo: BinOp* = (BinOp*)(node)
if bo.op == OpKind.BitOr:
_RecordTypeUnionErr("`|` is not allowed in a type annotation", node.lineno, node.col_offset)
ps.error_count += 1
return 1
return 0
if k == ASTKind.Subscript:
sub_u: Subscript* = (Subscript*)(node)
return _RejectTypeUnion(ps, sub_u.slice)
if k == ASTKind.Tuple:
tup_u: Tuple* = (Tuple*)(node)
if tup_u.elts is not None:
for ui in range(int(len(tup_u.elts))):
if _RejectTypeUnion(ps, tup_u.elts.get(ui)) != 0: return 1
return 0
def _RecordDescriptorErr(detail: str, lineno: int, col: int):
@@ -1491,9 +1523,9 @@ def _at_ptr_suffix(ps: Parser*, base: AST*) -> int:
def _make_ptr_type(ps: Parser*, base: AST*) -> AST*:
"""Build one pointer wrapper layer: base | CPtr (builtin pointer marker)."""
"""Build one pointer wrapper layer: CPtr[base] (builtin pointer marker)."""
cptr: AST* = Name(ps.pool, "CPtr", ASTCtx.Load)
node: AST* = BinOp(ps.pool, base, OpKind.BitOr, cptr)
node: AST* = _MakeTypeWrapper(ps, cptr, base)
_set_pos(node, base.lineno, base.col_offset, 0, 0)
return node
@@ -2611,27 +2643,16 @@ def _parse_array_prefix_declarator(ps: Parser*) -> AST*:
def _IsCTypedefAnnotation(ann: AST*) -> int:
"""1 if the annotation is a typedef marker (bare CTypedef / t.CTypedef / a union containing it).
"""1 if the annotation is the bare CTypedef marker.
Used to parse a typedef's right-hand side as a **type**: `X: t.CTypedef = const uint16*`
Used to parse a typedef's right-hand side as a **type**: `typedef X = const uint16*`
synthesizes descriptors and the `*` suffix through _parse_type_ann (the annotation rule),
so both sides agree. TPC's vast Parser._IsCTypedefAnnotation uses the same name-based test.
"""
if ann is None: return 0
kd: int = ann.kind()
if kd == ASTKind.Attribute:
at: Attribute* = Attribute*(ann)
if at.attr is not None and string.strcmp(at.attr, "CTypedef") == 0: return 1
return 0
if kd == ASTKind.Name:
nm: Name* = Name*(ann)
if nm.id is not None and string.strcmp(nm.id, "CTypedef") == 0: return 1
return 0
if kd == ASTKind.BinOp:
bo: BinOp* = BinOp*(ann)
if bo.left is not None and _IsCTypedefAnnotation(bo.left) != 0: return 1
if bo.right is not None and _IsCTypedefAnnotation(bo.right) != 0: return 1
return 0
if ann.kind() != ASTKind.Name: return 0
nm: Name* = Name*(ann)
if nm.id is not None and string.strcmp(nm.id, "CTypedef") == 0: return 1
return 0
@@ -2742,6 +2763,9 @@ def _parse_type_ann(ps: Parser*) -> AST*:
_RecordDescriptorErr("missing type after descriptor (type parse failed)", mod_lineno, mod_col)
_record_parse_err(ps) # annotation parse failed: record position (error_count already bumped in _parse_test)
return None
# `T1 | T2` unions were retired: every former union form has dedicated syntax.
if _RejectTypeUnion(ps, base) != 0:
return None
if mod_count > 0 and base.kind() == ASTKind.Constant:
# Non-type follower (`a: const 1` / `a: const "x"` / `a: const True`): fail loudly, since
# descriptors modify types only and constants are not types -- never silently degrade or drop.
@@ -2781,17 +2805,17 @@ def _parse_type_ann(ps: Parser*) -> AST*:
while _match_op(ps, TokOp.Star):
_advance(ps)
ptr_level += 1
# Build n nested base | CPtr layers
# Build n nested CPtr[base] layers
for li_an in range(int(ptr_level)):
cptr_an: AST* = Name(ps.pool, "CPtr", ASTCtx.Load)
base = BinOp(ps.pool, base, OpKind.BitOr, cptr_an)
# Paste descriptors last (same shape as TPC text expansion; see _PrependDescriptorMarkers)
base = _MakeTypeWrapper(ps, cptr_an, base)
# Paste descriptors last (outermost wrappers)
if mod_count > 0:
base = _PrependDescriptorMarkers(ps, base, mods, mod_count)
# Append the value-reference marker as the outermost union member: `base | CRef` / `base | CTempRef`.
# Append the value-reference marker as the outermost wrapper: CRef[base] / CTempRef[base].
if ref_marker is not None:
ref_marker_node: AST* = Name(ps.pool, ref_marker, ASTCtx.Load)
base = BinOp(ps.pool, base, OpKind.BitOr, ref_marker_node)
base = _MakeTypeWrapper(ps, ref_marker_node, base)
return base
@@ -3770,27 +3794,27 @@ def _parse_funcdef(ps: Parser*, is_async: int) -> AST*:
if _accept_op(ps, TokOp.RArrow):
returns = _parse_type_ann(ps)
if is_extern_fd:
# extern declaration: rewrite the return annotation to <orig type> | State, reusing the existing
# State/external-C-function path (vpi annotation containing "State" -> bare name, no function body)
# extern declaration: wrap the return annotation as State[T], reusing the existing
# State/external-C-function path (a lone State marker -> Void, no function body)
state_attr_fd: AST* = Name(ps.pool, "State", ASTCtx.Load)
if returns != None:
returns = BinOp(ps.pool, returns, OpKind.BitOr, state_attr_fd)
returns = _MakeTypeWrapper(ps, state_attr_fd, returns)
else:
returns = state_attr_fd
elif is_export_fd:
# export declaration: rewrite the return annotation to <orig type> | CExport, reusing the existing
# CExport path (vpi annotation containing "CExport" -> bare name, no module SHA1 header)
# export declaration: wrap the return annotation as CExport[T], reusing the existing
# CExport path (bare-name symbol, no module SHA1 header)
export_attr_fd: AST* = Name(ps.pool, "CExport", ASTCtx.Load)
if returns != None:
returns = BinOp(ps.pool, returns, OpKind.BitOr, export_attr_fd)
returns = _MakeTypeWrapper(ps, export_attr_fd, returns)
else:
returns = export_attr_fd
elif is_define_fd:
# define macro function: rewrite the return annotation to <orig type> | CDefine, reusing the
# existing CDefine path (StubGen keeps it as-is -> C #define / alwaysinline)
# define macro function: wrap the return annotation as CDefine[T], reusing the existing
# CDefine path (StubGen keeps it as-is -> C #define / alwaysinline)
define_attr_fd: AST* = Name(ps.pool, "CDefine", ASTCtx.Load)
if returns != None:
returns = BinOp(ps.pool, returns, OpKind.BitOr, define_attr_fd)
returns = _MakeTypeWrapper(ps, define_attr_fd, returns)
else:
returns = define_attr_fd
dec_list: list[AST*]* = ps.pending_decorators
@@ -3977,7 +4001,7 @@ def _parse_define_const(ps: Parser*) -> AST*:
cdefine_marker: AST* = Name(ps.pool, "CDefine", ASTCtx.Load)
annotation: AST* = cdefine_marker
if explicit_ann is not None:
annotation = BinOp(ps.pool, explicit_ann, OpKind.BitOr, cdefine_marker)
annotation = _MakeTypeWrapper(ps, cdefine_marker, explicit_ann)
target: AST* = Name(ps.pool, name, ASTCtx.Store)
_set_pos(target, lineno, col, 0, 0)
node: AST* = AnnAssign(ps.pool, target, annotation, value, 1)
@@ -4062,9 +4086,9 @@ def _parse_export_extern_var(ps: Parser*) -> AST*:
ps.error_count += 1
_record_parse_err(ps)
return None
# Storage-class marker: export -> CExport (bare-name export); extern -> State (declaration + bare name)
# Storage-class marker: export -> CExport[T] (bare-name export); extern -> State[T] (declaration + bare name)
storage_attr: AST* = Name(ps.pool, "State" if is_extern != 0 else "CExport", ASTCtx.Load)
annotation: AST* = BinOp(ps.pool, ann, OpKind.BitOr, storage_attr)
annotation: AST* = _MakeTypeWrapper(ps, storage_attr, ann)
value: AST* = None
if _match_op(ps, TokOp.Equal):
if is_extern != 0: