@@ -399,6 +399,14 @@ private partial def getOrCreateBuiltinTypeClass (name : String) : InterpM Value
399399 "hex" , "fromhex" , "decode" , "join"
400400 ] do
401401 ns := ns.insert dunder (.builtin s! "bytes.{ dunder} " )
402+ else if name == "dict" then
403+ for dunder in [
404+ "__new__" , "__init__" , "__len__" , "__getitem__" , "__setitem__" , "__delitem__" ,
405+ "__contains__" , "__iter__" , "__eq__" , "__ne__" , "__or__" ,
406+ "__repr__" , "__str__" , "__bool__" ,
407+ "get" , "keys" , "values" , "items" , "pop" , "update" , "clear" , "copy" , "setdefault"
408+ ] do
409+ ns := ns.insert dunder (.builtin s! "dict.{ dunder} " )
402410 -- Create the class with object as base
403411 let cls ← allocClassObj { name := name, bases := #[objectCls], mro := #[], ns := ns, slots := none }
404412 -- Compute MRO: [self, object]
@@ -414,7 +422,7 @@ private def resolveClassBases (bases : List Value) : InterpM (List Value) := do
414422 bases.mapM fun base =>
415423 match base with
416424 | .builtin name =>
417- if name == "int" || name == "bytes" || name == "object" || name == "bool" then
425+ if name == "int" || name == "bytes" || name == "object" || name == "bool" || name == "dict" then
418426 getOrCreateBuiltinTypeClass name
419427 else pure base
420428 | _ => pure base
@@ -1527,6 +1535,51 @@ partial def callValueDispatch (callee : Value) (args : List Value)
15271535 | none =>
15281536 allocInstance { cls := cls, attrs := {}, wrappedValue := some (.bytes ByteArray.empty) }
15291537 | some other => throwTypeError s! "bytes.__new__: cannot convert { typeName other} to bytes"
1538+ else if name == "dict.__new__" then do
1539+ -- dict.__new__ (cls) — create an instance with an empty wrapped dict
1540+ let cls ← match args with
1541+ | [cls@(.classObj _)] => pure cls
1542+ | [_, cls@(.classObj _)] => pure cls -- super() prepends inst
1543+ | [cls@(.classObj _), _] => pure cls -- cls, init_arg (init_arg handled by __init__ )
1544+ | [_, cls@(.classObj _), _] => pure cls -- super() prepends inst + init_arg
1545+ | _ => throwTypeError "dict.__new__(cls) requires a class"
1546+ let dictRef ← heapAlloc (.dictObj #[])
1547+ allocInstance { cls := cls, attrs := {}, wrappedValue := some (.dict dictRef) }
1548+ else if name == "dict.__init__" then do
1549+ -- dict.__init__ (self, source?, **kwargs) — populate the wrapped dict
1550+ let (selfVal, initArgs, initKwargs) ← match args with
1551+ | [s] => pure (s, none, kwargs)
1552+ | [s, arg] => pure (s, some arg, kwargs)
1553+ | _ => pure (args.head!, none, kwargs)
1554+ -- Get the wrapped dict ref from self
1555+ let dictRef ← match selfVal with
1556+ | .instance iref => do
1557+ let id_ ← heapGetInstanceData iref
1558+ match id_.wrappedValue with
1559+ | some (.dict ref) => pure ref
1560+ | _ => throwTypeError "dict.__init__: self has no wrapped dict"
1561+ | _ => throwTypeError "dict.__init__: expected instance"
1562+ -- Copy entries from source argument
1563+ match initArgs with
1564+ | some (.dict srcRef) => do
1565+ let srcPairs ← heapGetDict srcRef
1566+ heapSetDict dictRef srcPairs
1567+ | some (.instance iref) => do
1568+ let id_ ← heapGetInstanceData iref
1569+ match id_.wrappedValue with
1570+ | some (.dict srcRef) => do
1571+ let srcPairs ← heapGetDict srcRef
1572+ heapSetDict dictRef srcPairs
1573+ | _ => throwTypeError "dict.__init__: cannot convert to dict"
1574+ | some _ => throwTypeError "dict.__init__: cannot convert to dict"
1575+ | none => pure ()
1576+ -- Add kwargs entries
1577+ if !initKwargs.isEmpty then
1578+ let mut pairs ← heapGetDict dictRef
1579+ for (k, v) in initKwargs do
1580+ pairs := pairs.push (.str k, v)
1581+ heapSetDict dictRef pairs
1582+ return .none
15301583 else if name.startsWith "int." then do
15311584 -- Dispatch int dunder methods: extract wrapped int values
15321585 let methodName := String.ofList (name.toList.drop "int." .length)
@@ -1881,6 +1934,127 @@ partial def callValueDispatch (callee : Value) (args : List Value)
18811934 return .bytes result
18821935 | _ => throwTypeError "bytes.join takes 1 argument"
18831936 | _ => throwTypeError s! "bytes.{ methodName} is not implemented"
1937+ else if name.startsWith "dict." then do
1938+ -- Dispatch dict dunder/methods: extract wrapped dict ref
1939+ let methodName := String.ofList (name.toList.drop "dict." .length)
1940+ let extractDictRef : Value → InterpM HeapRef := fun v =>
1941+ match v with
1942+ | .dict ref => pure ref
1943+ | .instance iref => do
1944+ let id_ ← heapGetInstanceData iref
1945+ match id_.wrappedValue with
1946+ | some (.dict ref) => pure ref
1947+ | _ => throwTypeError s! "dict.{ methodName} : expected dict, got { typeName v} "
1948+ | _ => throwTypeError s! "dict.{ methodName} : expected dict, got { typeName v} "
1949+ match methodName with
1950+ | "__len__" =>
1951+ let ref ← extractDictRef args.head!
1952+ return .int (← heapGetDict ref).size
1953+ | "__getitem__" =>
1954+ let ref ← extractDictRef args.head!
1955+ let idx := args.tail.head!
1956+ let pairs ← heapGetDict ref
1957+ for (k, v) in pairs do
1958+ if ← valueEq k idx then return v
1959+ throwKeyError (← valueRepr idx)
1960+ | "__setitem__" =>
1961+ let ref ← extractDictRef args.head!
1962+ let idx := args.tail.head!
1963+ let value := args.tail.tail.head!
1964+ let pairs ← heapGetDict ref
1965+ let mut newPairs := pairs
1966+ let mut found := false
1967+ for i in [:pairs.size] do
1968+ if ← valueEq pairs[i]!.1 idx then
1969+ newPairs := newPairs.set! i (idx, value); found := true ; break
1970+ if !found then newPairs := newPairs.push (idx, value)
1971+ heapSetDict ref newPairs
1972+ return .none
1973+ | "__delitem__" =>
1974+ let ref ← extractDictRef args.head!
1975+ let idx := args.tail.head!
1976+ let pairs ← heapGetDict ref
1977+ let mut newPairs : Array (Value × Value) := #[]
1978+ let mut found := false
1979+ for (k, v) in pairs do
1980+ if !found && (← valueEq k idx) then found := true
1981+ else newPairs := newPairs.push (k, v)
1982+ if !found then throwKeyError (← valueRepr idx)
1983+ heapSetDict ref newPairs
1984+ return .none
1985+ | "__contains__" =>
1986+ let ref ← extractDictRef args.head!
1987+ let elem := args.tail.head!
1988+ let pairs ← heapGetDict ref
1989+ for (k, _) in pairs do
1990+ if ← valueEq k elem then return .bool true
1991+ return .bool false
1992+ | "__iter__" =>
1993+ let ref ← extractDictRef args.head!
1994+ let pairs ← heapGetDict ref
1995+ let keys := pairs.map Prod.fst
1996+ allocGenerator keys
1997+ | "__eq__" =>
1998+ let refA ← extractDictRef args.head!
1999+ let pairsA ← heapGetDict refA
2000+ match args.tail.head! with
2001+ | .dict refB => do
2002+ let pairsB ← heapGetDict refB
2003+ if pairsA.size != pairsB.size then return .bool false
2004+ for (k, v) in pairsA do
2005+ let mut found := false
2006+ for (k2, v2) in pairsB do
2007+ if ← valueEq k k2 then
2008+ if ← valueEq v v2 then found := true
2009+ break
2010+ if !found then return .bool false
2011+ return .bool true
2012+ | .instance iref => do
2013+ let id_ ← heapGetInstanceData iref
2014+ match id_.wrappedValue with
2015+ | some (.dict refB) => do
2016+ let pairsB ← heapGetDict refB
2017+ if pairsA.size != pairsB.size then return .bool false
2018+ for (k, v) in pairsA do
2019+ let mut found := false
2020+ for (k2, v2) in pairsB do
2021+ if ← valueEq k k2 then
2022+ if ← valueEq v v2 then found := true
2023+ break
2024+ if !found then return .bool false
2025+ return .bool true
2026+ | _ => return .bool false
2027+ | _ => return .bool false
2028+ | "__ne__" =>
2029+ let eqResult ← callValueDispatch (.builtin "dict.__eq__" ) args kwargs
2030+ match eqResult with
2031+ | .bool b => return .bool (!b)
2032+ | _ => return .bool true
2033+ | "__or__" =>
2034+ let refA ← extractDictRef args.head!
2035+ let pairsA ← heapGetDict refA
2036+ let refB ← extractDictRef args.tail.head!
2037+ let pairsB ← heapGetDict refB
2038+ let mut result := pairsA
2039+ for (k, v) in pairsB do
2040+ let mut found := false
2041+ for i in [:result.size] do
2042+ if ← valueEq result[i]!.1 k then
2043+ result := result.set! i (k, v); found := true ; break
2044+ if !found then result := result.push (k, v)
2045+ allocDict result
2046+ | "__bool__" =>
2047+ let ref ← extractDictRef args.head!
2048+ return .bool !(← heapGetDict ref).isEmpty
2049+ | "__repr__" | "__str__" =>
2050+ let ref ← extractDictRef args.head!
2051+ let s ← valueRepr (.dict ref)
2052+ return .str s
2053+ | "get" | "keys" | "values" | "items" | "pop" | "update" |
2054+ "clear" | "copy" | "setdefault" =>
2055+ let ref ← extractDictRef args.head!
2056+ callDictMethod ref methodName args.tail
2057+ | _ => throwTypeError s! "dict.{ methodName} is not implemented"
18842058 -- ============================================================
18852059 -- functools.singledispatch dispatch and register
18862060 -- ============================================================
@@ -2644,6 +2818,7 @@ partial def getAttributeValue (obj : Value) (attr : String) : InterpM Value := d
26442818 | .function _ => return .boundMethod obj attr
26452819 | .staticMethod fn => return fn
26462820 | .classMethod _ => return .boundMethod id_.cls attr
2821+ | .builtin _ => return .boundMethod obj attr
26472822 | _ => return v
26482823 | _ => pure ()
26492824 -- Fallback: try __getattr__ hook before raising
@@ -2776,7 +2951,7 @@ partial def evalSubscriptValue (obj idx : Value) : InterpM Value := do
27762951 -- Allow subscripting on builtin type names for type annotations (list[ int ] , dict[str, int], etc.)
27772952 match name with
27782953 | "list" | "dict" | "set" | "tuple" | "frozenset" | "type"
2779- | "memoryview" | "complex" => return .none
2954+ | "memoryview" | "complex" => return (.builtin name)
27802955 | _ => throwTypeError s! "'{ typeName obj} ' object is not subscriptable"
27812956 | _ => throwTypeError s! "'{ typeName obj} ' object is not subscriptable"
27822957
0 commit comments