Files
Viper_VRTE/_dict.vp
T
TermiNexus 0212bd942c fix(includes): 槽视图改为 cast 到声明类型(地址 → 二级指针不再隐式转换)
【类别 c 源码违规】`slot: <A> = t.CPtr(addr)` 把一级指针(i8*)隐式塞进二级注解
(i8** / X**),违反 2026-09-28 收紧的「指针级数必须相同」。TransPyV 侧 coerce
没有「值侧是显式 cast」豁免(ExprGenBase._ptr_cast_reason 先比层数),故回归测试
在 includes/llvmlite/__function.vp:950 首爆:
  指针隐式转指针已禁止:指针层数不同(源 1 级 → 目标 2 级);源类型 i8* → 目标 i8**。

判据(按注解层数分类,不枚举调用点):
  · str / bytes 本身即 i8*(含一层),再 | t.CPtr ⇒ i8**(槽里存的是 str)
  · str* / bytes* 同 str | t.CPtr(i8**)
  · T | t.CPtr:T 为指针特化时 ⇒ X**(如 list[X*]、list[str])
  · A | t.CPtr | t.CPtr:逐层叠加 ⇒ 二级(如 hashtable 的 value 槽)
  一级注解(int | t.CPtr = i32*、t.CUInt64T | t.CPtr = i64*、类名 | t.CPtr = X*)
  与 i8* 同级且 i8* 是通用指针通配 ⇒ 本可编译,一律保持原样不动。

修法:RHS 改为**cast 到声明类型**,即「注解写什么就 cast 什么」——
  `t.CPtr(addr)` / `(t.CPtr)(addr)` / `rawptr(addr)` → `(A)(addr)`;
  `str*` 注解沿用 sys.vp 既有写法 `str*(addr)`(等价于 `str | t.CPtr` 强转)。
显式写明目标类型后,层数与被指类型均由程序员给出,编译器无需任何豁免。
与 TransPyV/App CallGenCBuiltin.vp 的 `cl_ptr: str | t.CPtr = (str | t.CPtr)(cl_addr)`
以及 includes/vector.vp 的 `dst: T* = (T*)offset` 同一写法。

本仓改动 27 处 / 5 文件:
  _list.vp 7
  _dict.vp 9(8 处槽视图 + L63 的 `key_ptr: str* = rawptr(...)`)
  hashtable.vp 6(含 3 处 …|t.CPtr|t.CPtr)
  llvmlite/__function.vp 2
  mlirlite/__function.vp 3(2 处槽视图 + _mlir_slotp 的 return:该函数返回注解
  即 str | t.CPtr,其 return 同属「i8* → 二级」)

未动其它一级注解站点,例如 _dict.vp 的 int/float/t.CDouble/t.CInt | t.CPtr、
hashtable.vp 的 t.CUInt64T | t.CPtr、argparse.vp:168 的 Argument | t.CPtr。
2026-09-29 15:10:33 +08:00

272 lines
12 KiB
Plaintext

import t, c
import memhub
import string
import json
from stdint import *
# Variant type tags (consistent with _variant.py)
define VARIANT_NONE = 0 # Untyped / null value
define VARIANT_INT = 1 # int (i64)
define VARIANT_FLOAT = 2 # float (f32)
define VARIANT_DOUBLE = 3 # double (f64)
define VARIANT_STR = 4 # str (i8*)
define VARIANT_DICT = 5 # dict*
define VARIANT_LIST = 6 # list*
define VARIANT_BOOL = 7 # bool (i1/i32)
define VARIANT_PTR = 8 # Custom pointer type
# Variant structure size: vtype(i32, 4 bytes) + padding(4 bytes) + ptr(i8*, 8 bytes) = 16 bytes
define _VARIANT_SIZE = 16
class dict:
"""Heap-allocated dynamic dictionary container based on mbuddy allocator.
Key type is fixed to str (i8*), values are stored as Variant (type tag + untyped pointer).
Supports nested dict/list structures and JSON serialization.
Usage:
mb: memhub.MemBuddy = memhub.MemBuddy(arena, arena_size)
d: dict = dict(mb)
d.set_int("x", 42)
p: t.CPtr = d["x"]
v: int = c.Deref((int | t.CPtr)(p)) # int 值按 i32 存,读回须给 int* 视图
# Nested dict
inner: dict = dict(mb)
inner.set_int("a", 1)
d.set_dict("sub", inner)
sub: dict = c.Deref(d["sub"])
av: int = c.Deref((int | t.CPtr)(sub["a"]))
"""
__keys__: rawptr
__values__: rawptr
__count__: sizet = 0
__capacity__: sizet = 8
__mbuddy__: memhub.MemBuddy*
__iter_index__: sizet = 0
def __new__(self, mb: memhub.MemBuddy*):
# Heap allocation for dict object
return mb.alloc(dict.__sizeof__())
def __init__(self, mb: memhub.MemBuddy*):
self.__mbuddy__ = mb
self.__keys__ = mb.alloc(self.__capacity__ * 8)
self.__values__ = mb.alloc(self.__capacity__ * _VARIANT_SIZE)
def __len__(self) -> sizet:
return self.__count__
def _find(self, key: str) -> sizet:
"""Find index of target key; returns __count__ if not found"""
for i in range(self.__count__):
key_ptr: str* = str*(uint64(self.__keys__) + i * 8)
if string.strcmp(key_ptr[0], key) == 0: return i
return self.__count__
def __getitem__(self, key: str) -> t.CPtr:
"""Return untyped pointer to value. Use c.Deref to resolve the actual value.
Example: p: t.CPtr = d["x"]; v: int = c.Deref((int | t.CPtr)(p))"""
idx: sizet = self._find(key)
if idx >= self.__count__: return None
# Variant.ptr is located at offset 8 within Variant structure
val_ptr: str | t.CPtr = (str | t.CPtr)(uint64(self.__values__) + idx * _VARIANT_SIZE + 8)
return val_ptr[0]
def __setitem__(self, key: str, val: t.CNeedPtr):
"""Store value as VARIANT_PTR. Address is automatically resolved via t.CNeedPtr.
Note: Lifecycle of the data pointed to by val is managed by caller.
Use typed methods such as set_int / set_str for persistent storage."""
self._set_variant_raw(key, VARIANT_PTR, val)
def _set_variant_raw(self, key: str, vtype: t.CInt, ptr: t.CPtr):
"""Internal method: store Variant entry (vtype + ptr). No automatic address resolution."""
idx: sizet = self._find(key)
if idx < self.__count__:
# Update existing key
vtype_ptr: t.CInt | t.CPtr = t.CPtr(uint64(self.__values__) + idx * _VARIANT_SIZE)
vtype_ptr[0] = vtype
val_ptr: str | t.CPtr = (str | t.CPtr)(uint64(self.__values__) + idx * _VARIANT_SIZE + 8)
val_ptr[0] = ptr
return
if self.__count__ >= self.__capacity__:
new_cap: sizet = self.__capacity__ * 2
new_keys: t.CPtr = self.__mbuddy__.alloc(new_cap * 8)
new_values: t.CPtr = self.__mbuddy__.alloc(new_cap * _VARIANT_SIZE)
if new_keys is None or new_values is None: return
string.memcpy(new_keys, self.__keys__, self.__count__ * 8)
string.memcpy(new_values, self.__values__, self.__count__ * _VARIANT_SIZE)
self.__keys__ = new_keys
self.__values__ = new_values
self.__capacity__ = new_cap
key_ptr: str | t.CPtr = (str | t.CPtr)(uint64(self.__keys__) + self.__count__ * 8)
key_ptr[0] = key
vtype_ptr: t.CInt | t.CPtr = t.CPtr(uint64(self.__values__) + self.__count__ * _VARIANT_SIZE)
vtype_ptr[0] = vtype
val_ptr: str | t.CPtr = (str | t.CPtr)(uint64(self.__values__) + self.__count__ * _VARIANT_SIZE + 8)
val_ptr[0] = ptr
self.__count__ += 1
# ============================================================
# Typed setters — allocate persistent storage inside __mbuddy__
# ============================================================
def set_int(self, key: str, val: int):
"""Store integer value (VARIANT_INT). Value is copied to persistent storage within __mbuddy__."""
storage: t.CPtr = self.__mbuddy__.alloc(8)
int_ptr: int | t.CPtr = t.CPtr(uint64(storage))
int_ptr[0] = val
self._set_variant_raw(key, VARIANT_INT, storage)
def set_str(self, key: str, val: str):
"""Store string value (VARIANT_STR). Raw string pointer is stored directly."""
self._set_variant_raw(key, VARIANT_STR, val)
def set_float(self, key: str, val: float):
"""Store float value (VARIANT_FLOAT). Value is copied to persistent storage within __mbuddy__."""
storage: t.CPtr = self.__mbuddy__.alloc(8)
float_ptr: float | t.CPtr = t.CPtr(uint64(storage))
float_ptr[0] = val
self._set_variant_raw(key, VARIANT_FLOAT, storage)
def set_double(self, key: str, val: t.CDouble):
"""Store double value (VARIANT_DOUBLE). Value is copied to persistent storage within __mbuddy__."""
storage: t.CPtr = self.__mbuddy__.alloc(8)
dbl_ptr: t.CDouble | t.CPtr = t.CPtr(uint64(storage))
dbl_ptr[0] = val
self._set_variant_raw(key, VARIANT_DOUBLE, storage)
def set_dict(self, key: str, val: dict | t.CPtr):
"""Store nested dictionary pointer (VARIANT_DICT)."""
self._set_variant_raw(key, VARIANT_DICT, val)
def set_list(self, key: str, val: t.CPtr):
"""Store nested list pointer (VARIANT_LIST)."""
self._set_variant_raw(key, VARIANT_LIST, val)
def set_bool(self, key: str, val: t.CInt):
"""Store boolean value (VARIANT_BOOL). Value is copied to persistent storage within __mbuddy__."""
storage: t.CPtr = self.__mbuddy__.alloc(8)
bool_ptr: t.CInt | t.CPtr = t.CPtr(uint64(storage))
bool_ptr[0] = val
self._set_variant_raw(key, VARIANT_BOOL, storage)
# ============================================================
# Query methods
# ============================================================
def get_vtype(self, key: str) -> t.CInt:
"""Return Variant type tag associated with specified key."""
idx: sizet = self._find(key)
if idx >= self.__count__:
return VARIANT_NONE
vtype_ptr: t.CInt | t.CPtr = t.CPtr(uint64(self.__values__) + idx * _VARIANT_SIZE)
return vtype_ptr[0]
def get(self, key: str, default: t.CPtr) -> t.CPtr:
"""Return value pointer; return default pointer if key does not exist."""
idx: sizet = self._find(key)
if idx >= self.__count__:
return default
val_ptr: str | t.CPtr = (str | t.CPtr)(uint64(self.__values__) + idx * _VARIANT_SIZE + 8)
return val_ptr[0]
def contains(self, key: str) -> t.CInt:
"""Check whether target key exists."""
idx: sizet = self._find(key)
if idx < self.__count__:
return 1
return 0
def __iter__(self) -> dict | t.CPtr:
self.__iter_index__ = 0
return self
def __next__(self) -> str:
if self.__iter_index__ >= self.__count__:
raise StopIteration
idx: sizet = self.__iter_index__
self.__iter_index__ = idx + 1
key_ptr: str | t.CPtr = (str | t.CPtr)(uint64(self.__keys__) + idx * 8)
return key_ptr[0]
# ============================================================
# JSON utilities: serialization / deserialization via includes/json library
# ============================================================
def to_json(self, pool: rawptr[memhub.MemManager]) -> rawptr[json.JsonValue]:
"""Convert dict instance to JsonValue object (JSON_OBJECT).
Automatically select corresponding JSON value type according to Variant tag."""
obj: rawptr[json.JsonValue] = json.object(pool)
i: sizet = 0
while i < self.__count__:
key_ptr: str | t.CPtr = (str | t.CPtr)(uint64(self.__keys__) + i * 8)
vtype_ptr: t.CInt | t.CPtr = t.CPtr(uint64(self.__values__) + i * _VARIANT_SIZE)
val_ptr: str | t.CPtr = (str | t.CPtr)(uint64(self.__values__) + i * _VARIANT_SIZE + 8)
vt: t.CInt = vtype_ptr[0]
vp: t.CPtr = val_ptr[0]
jv: rawptr[json.JsonValue] = json.null(pool)
if jv != None:
if vt == VARIANT_INT:
jv.vtype = 2 # JSON_INT
ip: int | t.CPtr = t.CPtr(uint64(vp))
jv.int_val = ip[0]
elif vt == VARIANT_STR:
jv.vtype = 4 # JSON_STRING
jv.str_val = vp
elif vt == VARIANT_BOOL:
jv.vtype = 1 # JSON_BOOL
bp: t.CInt | t.CPtr = t.CPtr(uint64(vp))
jv.bool_val = bp[0]
elif vt == VARIANT_FLOAT:
jv.vtype = 3 # JSON_FLOAT
fp: float | t.CPtr = t.CPtr(uint64(vp))
jv.float_val = fp[0]
elif vt == VARIANT_PTR:
# Fallback: treat raw pointer as integer
jv.vtype = 2 # JSON_INT
ip: int | t.CPtr = t.CPtr(uint64(vp))
jv.int_val = ip[0]
json.object_set(pool, obj, key_ptr[0], jv)
i += 1
return obj
def from_json(self, pool: rawptr[memhub.MemManager], root: rawptr[json.JsonValue]):
"""Populate dict from JsonValue object.
Extract values and store them as matching Variant types based on JSON type tag."""
if root == None:
return
if root.vtype != 6: # JSON_OBJECT
return
cur: rawptr[json.JsonValue] = root.child
while cur != None:
if cur.key != None:
if cur.vtype == 2: # JSON_INT
storage: t.CPtr = self.__mbuddy__.alloc(8)
ip: int | t.CPtr = t.CPtr(uint64(storage))
ip[0] = cur.int_val
self._set_variant_raw(cur.key, VARIANT_INT, storage)
elif cur.vtype == 4: # JSON_STRING
self._set_variant_raw(cur.key, VARIANT_STR, cur.str_val)
elif cur.vtype == 1: # JSON_BOOL
storage: t.CPtr = self.__mbuddy__.alloc(8)
bp: t.CInt | t.CPtr = t.CPtr(uint64(storage))
bp[0] = cur.bool_val
self._set_variant_raw(cur.key, VARIANT_BOOL, storage)
elif cur.vtype == 3: # JSON_FLOAT
storage: t.CPtr = self.__mbuddy__.alloc(8)
fp: float | t.CPtr = t.CPtr(uint64(storage))
fp[0] = cur.float_val
self._set_variant_raw(cur.key, VARIANT_FLOAT, storage)
cur = cur.next
def dumps(self, pool: rawptr[memhub.MemManager]) -> str:
"""Serialize dict to JSON string."""
obj: rawptr[json.JsonValue] = self.to_json(pool)
return json.write(pool, obj, False)
def loads(self, pool: rawptr[memhub.MemManager], json_str: str):
"""Parse JSON string and populate dict instance."""
root: rawptr[json.JsonValue] = json.parse(pool, json_str)
self.from_json(pool, root)