feat(define): 支持字符串 list 宏常量与 *list 解包
- AssignGenMixin:_register_cdefine_list 校验元素为字符串常量或 *other_list,\n 连接,不建全局 - TypeGenBase/TypeGenCDef:新增宏 list 常量表(register/lookup/clear),与 DefineT 同池
This commit is contained in:
1 parent
8461a3da47
commit
52f3ff7f56
3 files changed
+146
-2
No files matched your search
@@ -720,7 +720,93 @@ novirtual class AssignGenMixin:
|
||||
return 0
|
||||
return 0
|
||||
|
||||
# _register_cdefine_from_value - register a CDefine constant (float or int)
|
||||
# _register_cdefine_from_value - register a DefineT constant (float or int)
|
||||
def _register_cdefine_list(self, pool: memhub.MemBuddy*, name: str, list_node: ast.AST*) -> int:
|
||||
"""Register `define NAME = [ ... ]` as a compile-time list[str] macro constant.
|
||||
|
||||
Elements are string literals, or `*OTHER` where OTHER is a macro list defined earlier
|
||||
(the parser renders a display-level `*X` as UnaryOp(Deref, X); both shapes are accepted).
|
||||
The value is stored '\n'-joined and no LLVM global is created.
|
||||
Returns 1=ok / 0=failed (failed paths already fatal-errored).
|
||||
"""
|
||||
if pool is None or name is None or list_node is None: return 0
|
||||
ln: ast.List* = ast.List*(list_node)
|
||||
if ln is None or ln.elts is None: return 0
|
||||
elts: list[ast.AST*]* = ln.elts
|
||||
n: sizet = len(elts)
|
||||
tp: memhub.MemBuddy* = pool
|
||||
|
||||
# Pass 1: total byte length (elements + one '\n' separator each).
|
||||
total: sizet = 0
|
||||
for i in range(int(n)):
|
||||
e: ast.AST* = elts.get(i)
|
||||
if e is None: return 0
|
||||
if e.kind() == ast.ASTKind.Constant:
|
||||
c: ast.Constant* = ast.Constant*(e)
|
||||
if c.const_kind != ast.CONST_STR:
|
||||
self.Gen.TypeGenErr.fatal_error(e, f"define list '{name}' elements must be string constants", "TypeError")
|
||||
return 0
|
||||
total += string.strlen(c.str_val) + 1
|
||||
elif self._cdefine_list_unpack_operand(e) is not None:
|
||||
prev: str = self._cdefine_list_lookup_unpack(e, name)
|
||||
if prev is None: return 0
|
||||
total += string.strlen(prev) + 1
|
||||
else:
|
||||
self.Gen.TypeGenErr.fatal_error(e, f"define list '{name}' elements must be string constants or `*other_list` unpacking", "TypeError")
|
||||
return 0
|
||||
|
||||
buf: str = tp.alloc(total + 1)
|
||||
if buf is None:
|
||||
self.Gen.TypeGenErr.fatal_error(None, f"define list '{name}' value buffer allocation failed", "MemoryError")
|
||||
return 0
|
||||
|
||||
# Pass 2: copy, '\n'-joined.
|
||||
pos: sizet = 0
|
||||
for i in range(int(n)):
|
||||
e = elts.get(i)
|
||||
src: str = None
|
||||
if e.kind() == ast.ASTKind.Constant:
|
||||
c2: ast.Constant* = ast.Constant*(e)
|
||||
src = c2.str_val
|
||||
else:
|
||||
src = self._cdefine_list_lookup_unpack(e, name)
|
||||
if src is None: return 0
|
||||
sl: sizet = string.strlen(src)
|
||||
if pos > 0:
|
||||
buf[pos] = char(10)
|
||||
pos += 1
|
||||
string.memcpy(buf + pos, src, sl)
|
||||
pos += sl
|
||||
buf[pos] = '\0'
|
||||
self.Gen.TypeGenCDef.register_cdefine_string_list(pool, name, buf)
|
||||
return 1
|
||||
|
||||
def _cdefine_list_unpack_operand(self, node: ast.AST*) -> ast.AST*:
|
||||
"""Operand of a define-list unpacking element (`*X` -> X); None if not an unpacking form."""
|
||||
if node is None: return None
|
||||
if node.kind() == ast.ASTKind.Starred:
|
||||
st: ast.Starred* = ast.Starred*(node)
|
||||
if st is None: return None
|
||||
return st.value
|
||||
if node.kind() == ast.ASTKind.UnaryOp:
|
||||
uo: ast.UnaryOp* = ast.UnaryOp*(node)
|
||||
if uo is not None and uo.op == ast.OpKind.Deref: return uo.operand
|
||||
return None
|
||||
|
||||
def _cdefine_list_lookup_unpack(self, node: ast.AST*, owner: str) -> str:
|
||||
"""Resolve `*OTHER` to the '\n'-joined value of an earlier macro list; None on failure."""
|
||||
op: ast.AST* = self._cdefine_list_unpack_operand(node)
|
||||
if op is None or op.kind() != ast.ASTKind.Name:
|
||||
self.Gen.TypeGenErr.fatal_error(node, f"define list '{owner}': `*` unpacking needs a macro-list name", "TypeError")
|
||||
return None
|
||||
nm: ast.Name* = ast.Name*(op)
|
||||
if nm is None or nm.id is None: return None
|
||||
prev: str = self.Gen.TypeGenCDef.lookup_cdefine_string_list(nm.id)
|
||||
if prev is None:
|
||||
self.Gen.TypeGenErr.fatal_error(op, f"define list '{owner}': '{nm.id}' is not a macro list defined earlier in this file", "NameError")
|
||||
return None
|
||||
return prev
|
||||
|
||||
def _register_cdefine_from_value(self, pool: memhub.MemBuddy*, name: str, val_node: ast.AST*, annot: ast.AST* = None) -> None:
|
||||
"""Register a CDefine constant (double for floats, else integer)."""
|
||||
if pool is None or name is None: return
|
||||
@@ -757,6 +843,10 @@ novirtual class AssignGenMixin:
|
||||
if uu_cd is not None and uu_cd.op == ast.OpKind.USub and uu_cd.operand is not None and uu_cd.operand.kind() == ast.ASTKind.Constant:
|
||||
ic_cd: ast.Constant* = ast.Constant*(uu_cd.operand)
|
||||
if ic_cd is not None and ic_cd.const_kind == ast.CONST_FLOAT: val_node = uu_cd.operand
|
||||
# Macro list: `define NAME = [ ... ]` -> compile-time list[str] constant, no global.
|
||||
if val_node is not None and val_node.kind() == ast.ASTKind.List:
|
||||
self._register_cdefine_list(pool, name, val_node)
|
||||
return
|
||||
if val_node is not None and val_node.kind() == ast.ASTKind.Constant:
|
||||
cn_fp: ast.Constant* = ast.Constant*(val_node)
|
||||
if cn_fp.const_kind == ast.CONST_STR:
|
||||
|
||||
@@ -57,6 +57,11 @@ novirtual class TypeGenBase:
|
||||
CDefineNameIdx: sizet*
|
||||
CDefineNameIdxCap: int
|
||||
CDefineNameIdxBuilt: int
|
||||
# --- `define NAME = [ ... ]` 宏 list(list[str],编译期常量,不建全局) ---
|
||||
# 值与 CDefineValues 分开存:名字不变,值是 '\n' 连接的元素串(元素内不含换行),
|
||||
# 供 `*OTHER_LIST` 解包与后续编译期消费者读取。与 CDefineNames 同池同生命周期。
|
||||
CDefineListNames: list[str]*
|
||||
CDefineListValues: list[str]*
|
||||
|
||||
# --- Cross-module CDefine lookup cache (PermPool-allocated, reused across files) ---
|
||||
# Not cleared by reset_to(mark2)/clear_cdefine_constants, so CONST_INT lookup happens once.
|
||||
|
||||
@@ -248,7 +248,52 @@ novirtual class TypeGenCDef(TypeGenBase.TypeGenBase):
|
||||
# Cross-module string CDefine reads call this + set_cdefine_found; missing the flag degrades to int 0 -> NULL crash.
|
||||
self.CDefineIsStr = 1
|
||||
self.CDefineFound = 1
|
||||
# Cross-module CDefine lookup cache (PermPool-allocated, reused across files)
|
||||
def register_cdefine_string_list(self, pool: memhub.MemBuddy*, name: str, joined: str) -> None:
|
||||
"""Register a macro-list DefineT constant (`define NAME = [...]`, '\n'-joined value).
|
||||
|
||||
The list is compile-time only: no LLVM global is created (an unannotated module-level
|
||||
string list has no representable initializer). Consumers are compile-time readers, e.g.
|
||||
`*OTHER_NAME` unpacking inside another define list.
|
||||
"""
|
||||
if pool is None or name is None or joined is None: return
|
||||
tp: memhub.MemBuddy* = self._cdefine_table_pool(pool)
|
||||
if tp is None:
|
||||
self.Gen.TypeGenErr.fatal_error(None, "DefineT list table pool unavailable", "MemoryError")
|
||||
return
|
||||
if self.CDefineListNames is None or self.CDefineListValues is None:
|
||||
ln_l: list[str]* = list[str](tp, 16)
|
||||
lv_l: list[str]* = list[str](tp, 16)
|
||||
if ln_l is None or lv_l is None:
|
||||
self.Gen.TypeGenErr.fatal_error(None, "DefineT list table allocation failed", "MemoryError")
|
||||
return
|
||||
self.CDefineListNames = ln_l
|
||||
self.CDefineListValues = lv_l
|
||||
nlen_l: sizet = string.strlen(name)
|
||||
ncopy_l: str = tp.alloc(nlen_l + 1)
|
||||
if ncopy_l is None:
|
||||
self.Gen.TypeGenErr.fatal_error(None, f"DefineT list '{name}' name buffer allocation failed", "MemoryError")
|
||||
return
|
||||
string.strcpy(ncopy_l, name)
|
||||
vlen_l: sizet = string.strlen(joined)
|
||||
vcopy_l: str = tp.alloc(vlen_l + 1)
|
||||
if vcopy_l is None:
|
||||
self.Gen.TypeGenErr.fatal_error(None, f"DefineT list '{name}' value buffer allocation failed", "MemoryError")
|
||||
return
|
||||
string.strcpy(vcopy_l, joined)
|
||||
self.CDefineListNames.append(ncopy_l)
|
||||
self.CDefineListValues.append(vcopy_l)
|
||||
|
||||
def lookup_cdefine_string_list(self, name: str) -> str:
|
||||
"""Return the '\n'-joined value of a macro-list DefineT constant; None if absent."""
|
||||
if name is None or self.CDefineListNames is None or self.CDefineListValues is None: return None
|
||||
n_l: sizet = len(self.CDefineListNames)
|
||||
for i in range(int(n_l)):
|
||||
nm_l: str = self.CDefineListNames.get(sizet(i))
|
||||
if nm_l is not None and string.strcmp(nm_l, name) == 0:
|
||||
return self.CDefineListValues.get(sizet(i))
|
||||
return None
|
||||
|
||||
# Cross-module DefineT lookup cache (PermPool-allocated, reused across files)
|
||||
def register_xmod_cdefine(self, name: str, value_str: str, is_double: int, is_str: int) -> None:
|
||||
"""Cache a cross-module CDefine lookup result in the perm_pool global cache.
|
||||
|
||||
@@ -450,6 +495,10 @@ novirtual class TypeGenCDef(TypeGenBase.TypeGenBase):
|
||||
if self.CDefineDoubleFlags is not None: self.CDefineDoubleFlags.clear()
|
||||
if self.CDefineStrFlags is not None: self.CDefineStrFlags.clear()
|
||||
if self.CDefineBits is not None: self.CDefineBits.clear()
|
||||
# Macro-list table is per-file too: a stale entry would let `*NAME` resolve a list that
|
||||
# belongs to another file's compilation unit.
|
||||
if self.CDefineListNames is not None: self.CDefineListNames.clear()
|
||||
if self.CDefineListValues is not None: self.CDefineListValues.clear()
|
||||
# Clear index slots (store index+1; 0=empty) and reset Built to 0 (empty table and index
|
||||
# in sync, matching a freshly built table before its first register).
|
||||
if self.CDefineNameIdx is not None and self.CDefineNameIdxCap > 0:
|
||||
|
||||
Reference in new issue
Block a user