From 52f3ff7f56bf48bf4efd167f48143fedf5e36e97 Mon Sep 17 00:00:00 2001 From: TermiNexus Date: Sat, 24 Oct 2026 00:14:08 +0800 Subject: [PATCH] =?UTF-8?q?feat(define):=20=E6=94=AF=E6=8C=81=E5=AD=97?= =?UTF-8?q?=E7=AC=A6=E4=B8=B2=20list=20=E5=AE=8F=E5=B8=B8=E9=87=8F?= =?UTF-8?q?=E4=B8=8E=20*list=20=E8=A7=A3=E5=8C=85?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - AssignGenMixin:_register_cdefine_list 校验元素为字符串常量或 *other_list,\n 连接,不建全局 - TypeGenBase/TypeGenCDef:新增宏 list 常量表(register/lookup/clear),与 DefineT 同池 --- App/lib/core/Gen/AssignGenMixin.vp | 92 +++++++++++++++++++++++++++- App/lib/core/Gen/Type/TypeGenBase.vp | 5 ++ App/lib/core/Gen/Type/TypeGenCDef.vp | 51 ++++++++++++++- 3 files changed, 146 insertions(+), 2 deletions(-) diff --git a/App/lib/core/Gen/AssignGenMixin.vp b/App/lib/core/Gen/AssignGenMixin.vp index 8ad98b9..6dc9a21 100644 --- a/App/lib/core/Gen/AssignGenMixin.vp +++ b/App/lib/core/Gen/AssignGenMixin.vp @@ -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: diff --git a/App/lib/core/Gen/Type/TypeGenBase.vp b/App/lib/core/Gen/Type/TypeGenBase.vp index a0a0dbf..f7528d7 100644 --- a/App/lib/core/Gen/Type/TypeGenBase.vp +++ b/App/lib/core/Gen/Type/TypeGenBase.vp @@ -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. diff --git a/App/lib/core/Gen/Type/TypeGenCDef.vp b/App/lib/core/Gen/Type/TypeGenCDef.vp index d96681d..489414d 100644 --- a/App/lib/core/Gen/Type/TypeGenCDef.vp +++ b/App/lib/core/Gen/Type/TypeGenCDef.vp @@ -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: