/** * @constructor * @param {Array.} L */ Sk.builtin.dict = function dict (L) { var v; var it, k; var i; if (!(this instanceof Sk.builtin.dict)) { return new Sk.builtin.dict(L); } if (L === undefined) { L = []; } this.size = 0; this.buckets = {}; if (Object.prototype.toString.apply(L) === "[object Array]") { // Handle dictionary literals for (i = 0; i < L.length; i += 2) { this.mp$ass_subscript(L[i], L[i + 1]); } } else if (L instanceof Sk.builtin.dict) { // Handle calls of type "dict(mapping)" from Python code for (it = Sk.abstr.iter(L), k = it.tp$iternext(); k !== undefined; k = it.tp$iternext()) { v = L.mp$subscript(k); if (v === undefined) { //print(k, "had undefined v"); v = null; } this.mp$ass_subscript(k, v); } } else if (Sk.builtin.checkIterable(L)) { // Handle calls of type "dict(iterable)" from Python code for (it = Sk.abstr.iter(L), i = it.tp$iternext(); i !== undefined; i = it.tp$iternext()) { if (i.mp$subscript) { this.mp$ass_subscript(i.mp$subscript(0), i.mp$subscript(1)); } else { throw new Sk.builtin.TypeError("element " + this.size + " is not a sequence"); } } } else { throw new Sk.builtin.TypeError("object is not iterable"); } this.__class__ = Sk.builtin.dict; return this; }; Sk.abstr.setUpInheritance("dict", Sk.builtin.dict, Sk.builtin.object); Sk.abstr.markUnhashable(Sk.builtin.dict); var kf = Sk.builtin.hash; Sk.builtin.dict.prototype.key$lookup = function (bucket, key) { var item; var eq; var i; for (i = 0; i < bucket.items.length; i++) { item = bucket.items[i]; eq = Sk.misceval.richCompareBool(item.lhs, key, "Eq"); if (eq) { return item; } } return null; }; Sk.builtin.dict.prototype.key$pop = function (bucket, key) { var item; var eq; var i; for (i = 0; i < bucket.items.length; i++) { item = bucket.items[i]; eq = Sk.misceval.richCompareBool(item.lhs, key, "Eq"); if (eq) { bucket.items.splice(i, 1); this.size -= 1; return item; } } return undefined; }; // Perform dictionary lookup, either return value or undefined if key not in dictionary Sk.builtin.dict.prototype.mp$lookup = function (key) { var k = kf(key); var bucket = this.buckets[k.v]; var item; // todo; does this need to go through mp$ma_lookup if (bucket !== undefined) { item = this.key$lookup(bucket, key); if (item) { return item.rhs; } } // Not found in dictionary return undefined; }; Sk.builtin.dict.prototype.mp$subscript = function (key) { Sk.builtin.pyCheckArgs("[]", arguments, 1, 2, false, false); var s; var res = this.mp$lookup(key); if (res !== undefined) { // Found in dictionary return res; } else { // Not found in dictionary s = new Sk.builtin.str(key); throw new Sk.builtin.KeyError(s.v); } }; Sk.builtin.dict.prototype.sq$contains = function (ob) { Sk.builtin.pyCheckArgs("__contains__()", arguments, 1, 1, false, false); var res = this.mp$lookup(ob); return (res !== undefined); }; Sk.builtin.dict.prototype.mp$ass_subscript = function (key, w) { var k = kf(key); var bucket = this.buckets[k.v]; var item; if (bucket === undefined) { // New bucket bucket = {$hash: k, items: [ {lhs: key, rhs: w} ]}; this.buckets[k.v] = bucket; this.size += 1; return; } item = this.key$lookup(bucket, key); if (item) { item.rhs = w; return; } // Not found in dictionary bucket.items.push({lhs: key, rhs: w}); this.size += 1; }; Sk.builtin.dict.prototype.mp$del_subscript = function (key) { Sk.builtin.pyCheckArgs("del", arguments, 1, 1, false, false); var k = kf(key); var bucket = this.buckets[k.v]; var item; var s; // todo; does this need to go through mp$ma_lookup if (bucket !== undefined) { item = this.key$pop(bucket, key); if (item !== undefined) { return; } } // Not found in dictionary s = new Sk.builtin.str(key); throw new Sk.builtin.KeyError(s.v); }; Sk.builtin.dict.prototype["$r"] = function () { var v; var iter, k; var ret = []; for (iter = Sk.abstr.iter(this), k = iter.tp$iternext(); k !== undefined; k = iter.tp$iternext()) { v = this.mp$subscript(k); if (v === undefined) { //print(k, "had undefined v"); v = null; } // we need to check if value is same as object // otherwise it would cause an stack overflow if(v === this) { ret.push(Sk.misceval.objectRepr(k).v + ": {...}"); } else { ret.push(Sk.misceval.objectRepr(k).v + ": " + Sk.misceval.objectRepr(v).v); } } return new Sk.builtin.str("{" + ret.join(", ") + "}"); }; Sk.builtin.dict.prototype.mp$length = function () { return this.size; }; Sk.builtin.dict.prototype["get"] = new Sk.builtin.func(function (self, k, d) { Sk.builtin.pyCheckArgs("get()", arguments, 1, 2, false, true); var ret; if (d === undefined) { d = Sk.builtin.none.none$; } ret = self.mp$lookup(k); if (ret === undefined) { ret = d; } return ret; }); Sk.builtin.dict.prototype["pop"] = new Sk.builtin.func(function (self, key, d) { Sk.builtin.pyCheckArgs("pop()", arguments, 1, 2, false, true); var k = kf(key); var bucket = self.buckets[k.v]; var item; var s; // todo; does this need to go through mp$ma_lookup if (bucket !== undefined) { item = self.key$pop(bucket, key); if (item !== undefined) { return item.rhs; } } // Not found in dictionary if (d !== undefined) { return d; } s = new Sk.builtin.str(key); throw new Sk.builtin.KeyError(s.v); }); Sk.builtin.dict.prototype["has_key"] = new Sk.builtin.func(function (self, k) { Sk.builtin.pyCheckArgs("has_key()", arguments, 1, 1, false, true); return new Sk.builtin.bool( self.sq$contains(k)); }); Sk.builtin.dict.prototype["items"] = new Sk.builtin.func(function (self) { Sk.builtin.pyCheckArgs("items()", arguments, 0, 0, false, true); var v; var iter, k; var ret = []; for (iter = Sk.abstr.iter(self), k = iter.tp$iternext(); k !== undefined; k = iter.tp$iternext()) { v = self.mp$subscript(k); if (v === undefined) { //print(k, "had undefined v"); v = null; } ret.push(new Sk.builtin.tuple([k, v])); } return new Sk.builtin.list(ret); }); Sk.builtin.dict.prototype["keys"] = new Sk.builtin.func(function (self) { Sk.builtin.pyCheckArgs("keys()", arguments, 0, 0, false, true); var iter, k; var ret = []; for (iter = Sk.abstr.iter(self), k = iter.tp$iternext(); k !== undefined; k = iter.tp$iternext()) { ret.push(k); } return new Sk.builtin.list(ret); }); Sk.builtin.dict.prototype["values"] = new Sk.builtin.func(function (self) { Sk.builtin.pyCheckArgs("values()", arguments, 0, 0, false, true); var v; var iter, k; var ret = []; for (iter = Sk.abstr.iter(self), k = iter.tp$iternext(); k !== undefined; k = iter.tp$iternext()) { v = self.mp$subscript(k); if (v === undefined) { v = null; } ret.push(v); } return new Sk.builtin.list(ret); }); Sk.builtin.dict.prototype["clear"] = new Sk.builtin.func(function (self) { Sk.builtin.pyCheckArgs("clear()", arguments, 0, 0, false, true); var k; var iter; for (iter = Sk.abstr.iter(self), k = iter.tp$iternext(); k !== undefined; k = iter.tp$iternext()) { self.mp$del_subscript(k); } }); Sk.builtin.dict.prototype["setdefault"] = new Sk.builtin.func(function (self, key, default_) { try { return self.mp$subscript(key); } catch (e) { if (default_ === undefined) { default_ = Sk.builtin.none.none$; } self.mp$ass_subscript(key, default_); return default_; } }); /* this function mimics the cpython implementation, which is also the reason for the almost similar code, this may be changed in future */ Sk.builtin.dict.prototype.dict_merge = function(b) { var iter; var k, v; if(b instanceof Sk.builtin.dict) { // fast way for (iter = b.tp$iter(), k = iter.tp$iternext(); k !== undefined; k = iter.tp$iternext()) { v = b.mp$subscript(k); if (v === undefined) { throw new Sk.builtin.AttributeError("cannot get item for key: " + k.v); } this.mp$ass_subscript(k, v); } } else { // generic slower way var keys = Sk.misceval.callsim(b["keys"], b); for (iter = Sk.abstr.iter(keys), k = iter.tp$iternext(); k !== undefined; k = iter.tp$iternext()) { v = b.tp$getitem(k); // get value if (v === undefined) { throw new Sk.builtin.AttributeError("cannot get item for key: " + k.v); } this.mp$ass_subscript(k, v); } } }; /** * update() accepts either another dictionary object or an iterable of key/value pairs (as tuples or other iterables of length two). * If keyword arguments are specified, the dictionary is then updated with those key/value pairs: d.update(red=1, blue=2). * https://hg.python.org/cpython/file/4ff865976bb9/Objects/dictobject.c */ var update_f = function (kwargs, self, other) { // case another dict or obj with keys and getitem has been provided if(other !== undefined && (other.tp$name === "dict" || other["keys"])) { self.dict_merge(other); // we merge with override } else if(other !== undefined && Sk.builtin.checkIterable(other)) { // 2nd case, we expect an iterable that contains another iterable of length 2 var iter; var k, v; var seq_i = 0; // index of current sequence item for (iter = Sk.abstr.iter(other), k = iter.tp$iternext(); k !== undefined; k = iter.tp$iternext(), seq_i++) { // check if value is iter if (!Sk.builtin.checkIterable(k)) { throw new Sk.builtin.TypeError("cannot convert dictionary update sequence element #" + seq_i + " to a sequence"); } // cpython impl. would transform iterable into sequence // we just call iternext twice if k has length of 2 if(k.sq$length() === 2) { var k_iter = Sk.abstr.iter(k); var k_key = k_iter.tp$iternext(); var k_value = k_iter.tp$iternext(); self.mp$ass_subscript(k_key, k_value); } else { // throw exception throw new Sk.builtin.ValueError("dictionary update sequence element #" + seq_i + " has length " + k.sq$length() + "; 2 is required"); } } } else if(other !== undefined) { // other is not a dict or iterable throw new Sk.builtin.TypeError("'" +Sk.abstr.typeName(other) + "' object is not iterable"); } // apply all key/value pairs of kwargs // create here kwargs_dict, there could be exceptions in other cases before var kwargs_dict = new Sk.builtins.dict(kwargs); self.dict_merge(kwargs_dict); // returns none, when successful or throws exception return Sk.builtin.none.none$; }; update_f.co_kwargs = true; Sk.builtin.dict.prototype.update = new Sk.builtin.func(update_f); Sk.builtin.dict.prototype.__contains__ = new Sk.builtin.func(function (self, item) { Sk.builtin.pyCheckArgs("__contains__", arguments, 1, 1, false, true); return Sk.builtin.dict.prototype.sq$contains.call(self, item); }); Sk.builtin.dict.prototype.__cmp__ = new Sk.builtin.func(function (self, other, op) { // __cmp__ cannot be supported until dict lt/le/gt/ge operations are supported return Sk.builtin.NotImplemented.NotImplemented$; }); Sk.builtin.dict.prototype.__delitem__ = new Sk.builtin.func(function (self, item) { Sk.builtin.pyCheckArgs("__delitem__", arguments, 1, 1, false, true); return Sk.builtin.dict.prototype.mp$del_subscript.call(self, item); }); Sk.builtin.dict.prototype.__getitem__ = new Sk.builtin.func(function (self, item) { Sk.builtin.pyCheckArgs("__getitem__", arguments, 1, 1, false, true); return Sk.builtin.dict.prototype.mp$subscript.call(self, item); }); Sk.builtin.dict.prototype.__setitem__ = new Sk.builtin.func(function (self, item, value) { Sk.builtin.pyCheckArgs("__setitem__", arguments, 2, 2, false, true); return Sk.builtin.dict.prototype.mp$ass_subscript.call(self, item, value); }); Sk.builtin.dict.prototype.__hash__ = new Sk.builtin.func(function (self) { Sk.builtin.pyCheckArgs("__hash__", arguments, 0, 0, false, true); return Sk.builtin.dict.prototype.tp$hash.call(self); }); Sk.builtin.dict.prototype.__len__ = new Sk.builtin.func(function (self) { Sk.builtin.pyCheckArgs("__len__", arguments, 0, 0, false, true); return Sk.builtin.dict.prototype.mp$length.call(self); }); Sk.builtin.dict.prototype.__getattr__ = new Sk.builtin.func(function (self, attr) { Sk.builtin.pyCheckArgs("__getattr__", arguments, 1, 1, false, true); if (!Sk.builtin.checkString(attr)) { throw new Sk.builtin.TypeError("__getattr__ requires a string"); } return Sk.builtin.dict.prototype.tp$getattr.call(self, Sk.ffi.remapToJs(attr)); }); Sk.builtin.dict.prototype.__iter__ = new Sk.builtin.func(function (self) { Sk.builtin.pyCheckArgs("__iter__", arguments, 0, 0, false, true); return new Sk.builtin.dict_iter_(self); }); Sk.builtin.dict.prototype.tp$iter = function () { return new Sk.builtin.dict_iter_(this); }; Sk.builtin.dict.prototype.__repr__ = new Sk.builtin.func(function (self) { Sk.builtin.pyCheckArgs("__repr__", arguments, 0, 0, false, true); return Sk.builtin.dict.prototype["$r"].call(self); }); /* python3 recommends implementing simple ops */ Sk.builtin.dict.prototype.ob$eq = function (other) { var iter, k, v, otherv; if (this === other) { return Sk.builtin.bool.true$; } if (!(other instanceof Sk.builtin.dict)) { return Sk.builtin.NotImplemented.NotImplemented$; } if (this.size !== other.size) { return Sk.builtin.bool.false$; } for (iter = this.tp$iter(), k = iter.tp$iternext(); k !== undefined; k = iter.tp$iternext()) { v = this.mp$subscript(k); otherv = other.mp$subscript(k); if (!Sk.misceval.richCompareBool(v, otherv, "Eq")) { return Sk.builtin.bool.false$; } } return Sk.builtin.bool.true$; }; Sk.builtin.dict.prototype.ob$ne = function (other) { var isEqual = this.ob$eq(other); if (isEqual instanceof Sk.builtin.NotImplemented) { return isEqual; } else if (isEqual.v) { return Sk.builtin.bool.false$; } else { return Sk.builtin.bool.true$; } }; Sk.builtin.dict.prototype["copy"] = new Sk.builtin.func(function (self) { throw new Sk.builtin.NotImplementedError("dict.copy is not yet implemented in Skulpt"); }); Sk.builtin.dict.prototype["fromkeys"] = new Sk.builtin.func(function (seq, value) { throw new Sk.builtin.NotImplementedError("dict.fromkeys is not yet implemented in Skulpt"); }); Sk.builtin.dict.prototype["iteritems"] = new Sk.builtin.func(function (self) { throw new Sk.builtin.NotImplementedError("dict.iteritems is not yet implemented in Skulpt"); }); Sk.builtin.dict.prototype["iterkeys"] = new Sk.builtin.func(function (self) { throw new Sk.builtin.NotImplementedError("dict.iterkeys is not yet implemented in Skulpt"); }); Sk.builtin.dict.prototype["itervalues"] = new Sk.builtin.func(function (self) { throw new Sk.builtin.NotImplementedError("dict.itervalues is not yet implemented in Skulpt"); }); Sk.builtin.dict.prototype["popitem"] = new Sk.builtin.func(function (self) { throw new Sk.builtin.NotImplementedError("dict.popitem is not yet implemented in Skulpt"); }); Sk.builtin.dict.prototype["viewitems"] = new Sk.builtin.func(function (self) { throw new Sk.builtin.NotImplementedError("dict.viewitems is not yet implemented in Skulpt"); }); Sk.builtin.dict.prototype["viewkeys"] = new Sk.builtin.func(function (self) { throw new Sk.builtin.NotImplementedError("dict.viewkeys is not yet implemented in Skulpt"); }); Sk.builtin.dict.prototype["viewvalues"] = new Sk.builtin.func(function (self) { throw new Sk.builtin.NotImplementedError("dict.viewvalues is not yet implemented in Skulpt"); }); goog.exportSymbol("Sk.builtin.dict", Sk.builtin.dict); /** * @constructor * @param {Object} obj */ Sk.builtin.dict_iter_ = function (obj) { var k, i, bucket, allkeys, buckets; if (!(this instanceof Sk.builtin.dict_iter_)) { return new Sk.builtin.dict_iter_(obj); } this.$index = 0; this.$obj = obj; allkeys = []; buckets = obj.buckets; for (k in buckets) { if (buckets.hasOwnProperty(k)) { bucket = buckets[k]; if (bucket && bucket.$hash !== undefined && bucket.items !== undefined) { // skip internal stuff. todo; merge pyobj and this for (i = 0; i < bucket.items.length; i++) { allkeys.push(bucket.items[i].lhs); } } } } this.$keys = allkeys; this.tp$iter = this; this.tp$iternext = function () { // todo; StopIteration if (this.$index >= this.$keys.length) { return undefined; } return this.$keys[this.$index++]; // return this.$obj[this.$keys[this.$index++]].lhs; }; this.$r = function () { return new Sk.builtin.str("dictionary-keyiterator"); }; return this; }; Sk.abstr.setUpInheritance("dictionary-keyiterator", Sk.builtin.dict_iter_, Sk.builtin.object); Sk.builtin.dict_iter_.prototype.__class__ = Sk.builtin.dict_iter_; Sk.builtin.dict_iter_.prototype.__iter__ = new Sk.builtin.func(function (self) { return self; }); Sk.builtin.dict_iter_.prototype["next"] = new Sk.builtin.func(function (self) { var ret = self.tp$iternext(); if (ret === undefined) { throw new Sk.builtin.StopIteration(); } return ret; });