diff --git a/py/dict.go b/py/dict.go index 1c54a3be..dfc9bfc0 100644 --- a/py/dict.go +++ b/py/dict.go @@ -60,6 +60,36 @@ func init() { } return nil, ExceptionNewf(KeyError, "%v", args[0]) }, 0, "gets(key, default) -> If there is a val corresponding to key, return val, otherwise default") + + DictType.Dict["items"] = MustNewMethod("items", func(self Object, args Tuple) (Object, error) { + dict := self.(Dict) + o := make([]Object, 0, len(dict.keys)) + for _, item := range dict.items { + if item[0] != nil { + o = append(o, Tuple{item[0], item[1]}) + } + } + return NewIterator(o), nil + }, 0, "items() -> list of D's (key, value) pairs, as 2-tuples") + + DictType.Dict["get"] = MustNewMethod("get", func(self Object, args Tuple) (Object, error) { + var length = len(args) + switch { + case length == 0: + return nil, ExceptionNewf(TypeError, "%s expected at least 1 arguments, got %d", "items()", length) + case length > 2: + return nil, ExceptionNewf(TypeError, "%s expected at most 2 arguments, got %d", "items()", length) + } + dict := self.(Dict) + if res, ok := dict.keys[args[0]]; ok { + return dict.items[res][1], nil + } + + if length == 2 { + return args[1], nil + } + return None, nil + }, 0, "gets(key, default) -> If there is a val corresponding to key, return val, otherwise default") } // String to object dictionary @@ -67,21 +97,44 @@ func init() { // Used for variables etc where the keys can only be strings type StringDict map[string]Object +type Dict struct { + keys map[Object]int + items [][2]Object // key-value pair +} + // Type of this StringDict object func (o StringDict) Type() *Type { return StringDictType } +// Type of this Dict object +func (o Dict) Type() *Type { + return DictType +} + // Make a new dictionary func NewStringDict() StringDict { return make(StringDict) } +// Make a new dictionary +func NewDict() *Dict { + return &Dict{} +} + // Make a new dictionary with reservation for n entries func NewStringDictSized(n int) StringDict { return make(StringDict, n) } +// Make a new dictionary with reservation for n entries +func NewDictSized(n int) *Dict { + return &Dict{ + keys: make(map[Object]int, n), + items: make([][2]Object, n), + } +} + // Checks that obj is exactly a dictionary and returns an error if not func DictCheckExact(obj Object) (StringDict, error) { dict, ok := obj.(StringDict) @@ -91,12 +144,27 @@ func DictCheckExact(obj Object) (StringDict, error) { return dict, nil } +// Checks that obj is exactly a dictionary and returns an error if not +func _DictCheckExact(obj Object) (*Dict, error) { + dict, ok := obj.(Dict) + if !ok { + return nil, expectingDict + } + return &dict, nil +} + // Checks that obj is exactly a dictionary and returns an error if not func DictCheck(obj Object) (StringDict, error) { // FIXME should be checking subclasses return DictCheckExact(obj) } +// Checks that obj is exactly a dictionary and returns an error if not +func _DictCheck(obj Object) (*Dict, error) { + // FIXME should be checking subclasses + return _DictCheckExact(obj) +} + // Copy a dictionary func (d StringDict) Copy() StringDict { e := make(StringDict, len(d)) @@ -106,10 +174,26 @@ func (d StringDict) Copy() StringDict { return e } +// Copy a dictionary +func (d Dict) Copy() *Dict { + e := NewDictSized(len(d.keys)) + for k, v := range d.keys { + e.keys[k] = v + e.items[v][0] = d.items[v][0] + e.items[v][1] = d.items[v][1] + } + return e +} + func (a StringDict) M__str__() (Object, error) { return a.M__repr__() } + +func (a Dict) M__str__() (Object, error) { + return a.M__repr__() +} + func (a StringDict) M__repr__() (Object, error) { var out bytes.Buffer out.WriteRune('{') @@ -135,6 +219,33 @@ func (a StringDict) M__repr__() (Object, error) { return String(out.String()), nil } +func (a Dict) M__repr__() (Object, error) { + var out bytes.Buffer + out.WriteRune('{') + spacer := false + for _, item := range a.items { + if item[0] != nil { + if spacer { + out.WriteString(", ") + } + keyStr, err := ReprAsString(item[0]) + if err != nil { + return nil, err + } + valueStr, err := ReprAsString(item[1]) + if err != nil { + return nil, err + } + out.WriteString(keyStr) + out.WriteString(": ") + out.WriteString(valueStr) + spacer = true + } + } + out.WriteRune('}') + return String(out.String()), nil +} + // Returns a list of keys from the dict func (d StringDict) M__iter__() (Object, error) { o := make([]Object, 0, len(d)) @@ -144,6 +255,17 @@ func (d StringDict) M__iter__() (Object, error) { return NewIterator(o), nil } +// Returns a list of keys from the dict +func (d Dict) M__iter__() (Object, error) { + o := make([]Object, 0, len(d.keys)) + for _, item := range d.items { + if item[0] != nil { + o = append(o, item[0]) + } + } + return NewIterator(o), nil +} + func (d StringDict) M__getitem__(key Object) (Object, error) { str, ok := key.(String) if ok { @@ -155,6 +277,15 @@ func (d StringDict) M__getitem__(key Object) (Object, error) { return nil, ExceptionNewf(KeyError, "%v", key) } +func (d Dict) M__getitem__(key Object) (Object, error) { + // FIXME should be checking hash of Object + res, ok := d.keys[key] + if ok { + return d.items[res][1], nil + } + return nil, ExceptionNewf(KeyError, "%v", key) +} + func (d StringDict) M__setitem__(key, value Object) (Object, error) { str, ok := key.(String) if !ok { @@ -164,6 +295,13 @@ func (d StringDict) M__setitem__(key, value Object) (Object, error) { return None, nil } +func (d Dict) M__setitem__(key, value Object) (Object, error) { + // FIXME should be checking hash of Object + d.keys[key] = len(d.items) + d.items = append(d.items, [2]Object{key, value}) + return None, nil +} + func (a StringDict) M__eq__(other Object) (Object, error) { b, ok := other.(StringDict) if !ok { @@ -188,6 +326,41 @@ func (a StringDict) M__eq__(other Object) (Object, error) { return True, nil } +func (a Dict) M__eq__(other Object) (Object, error) { + b, ok := other.(Dict) + if !ok { + return NotImplemented, nil + } + if len(a.keys) != len(b.keys) { + return False, nil + } + for k, ai := range a.keys { + // FIXME should be checking hash of Object + bi, ok := b.keys[k] + if !ok || len(a.keys) < ai || len(b.keys) < bi { + return False, nil + } + aitem := a.items[ai] + bitem := b.items[bi] + + res, err := Eq(aitem[0], bitem[0]) + if err != nil { + return nil, err + } + if res == False { + return False, nil + } + res, err = Eq(aitem[1], bitem[1]) + if err != nil { + return nil, err + } + if res == False { + return False, nil + } + } + return True, nil +} + func (a StringDict) M__ne__(other Object) (Object, error) { res, err := a.M__eq__(other) if err != nil { @@ -202,6 +375,10 @@ func (a StringDict) M__ne__(other Object) (Object, error) { return True, nil } +func (a Dict) M__ne__(other Object) (Object, error) { + return notEq(a.M__eq__(other)) +} + func (a StringDict) M__contains__(other Object) (Object, error) { key, ok := other.(String) if !ok { @@ -213,3 +390,11 @@ func (a StringDict) M__contains__(other Object) (Object, error) { } return False, nil } + +func (a Dict) M__contains__(other Object) (Object, error) { + // FIXME should be checking hash of Object + if i, ok := a.keys[other]; ok { + return Eq(other, a.items[i][0]) + } + return False, nil +}