dict.js 18 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601
  1. /**
  2. * @constructor
  3. * @param {Array.<Object>} L
  4. */
  5. Sk.builtin.dict = function dict (L) {
  6. var v;
  7. var it, k;
  8. var i;
  9. if (!(this instanceof Sk.builtin.dict)) {
  10. return new Sk.builtin.dict(L);
  11. }
  12. if (L === undefined) {
  13. L = [];
  14. }
  15. this.size = 0;
  16. this.buckets = {};
  17. if (Object.prototype.toString.apply(L) === "[object Array]") {
  18. // Handle dictionary literals
  19. for (i = 0; i < L.length; i += 2) {
  20. this.mp$ass_subscript(L[i], L[i + 1]);
  21. }
  22. } else if (L instanceof Sk.builtin.dict) {
  23. // Handle calls of type "dict(mapping)" from Python code
  24. for (it = Sk.abstr.iter(L), k = it.tp$iternext();
  25. k !== undefined;
  26. k = it.tp$iternext()) {
  27. v = L.mp$subscript(k);
  28. if (v === undefined) {
  29. //print(k, "had undefined v");
  30. v = null;
  31. }
  32. this.mp$ass_subscript(k, v);
  33. }
  34. } else if (Sk.builtin.checkIterable(L)) {
  35. // Handle calls of type "dict(iterable)" from Python code
  36. for (it = Sk.abstr.iter(L), i = it.tp$iternext(); i !== undefined; i = it.tp$iternext()) {
  37. if (i.mp$subscript) {
  38. this.mp$ass_subscript(i.mp$subscript(0), i.mp$subscript(1));
  39. } else {
  40. throw new Sk.builtin.TypeError("element " + this.size + " is not a sequence");
  41. }
  42. }
  43. } else {
  44. throw new Sk.builtin.TypeError("object is not iterable");
  45. }
  46. this.__class__ = Sk.builtin.dict;
  47. return this;
  48. };
  49. Sk.abstr.setUpInheritance("dict", Sk.builtin.dict, Sk.builtin.object);
  50. Sk.abstr.markUnhashable(Sk.builtin.dict);
  51. var kf = Sk.builtin.hash;
  52. Sk.builtin.dict.prototype.key$lookup = function (bucket, key) {
  53. var item;
  54. var eq;
  55. var i;
  56. for (i = 0; i < bucket.items.length; i++) {
  57. item = bucket.items[i];
  58. eq = Sk.misceval.richCompareBool(item.lhs, key, "Eq");
  59. if (eq) {
  60. return item;
  61. }
  62. }
  63. return null;
  64. };
  65. Sk.builtin.dict.prototype.key$pop = function (bucket, key) {
  66. var item;
  67. var eq;
  68. var i;
  69. for (i = 0; i < bucket.items.length; i++) {
  70. item = bucket.items[i];
  71. eq = Sk.misceval.richCompareBool(item.lhs, key, "Eq");
  72. if (eq) {
  73. bucket.items.splice(i, 1);
  74. this.size -= 1;
  75. return item;
  76. }
  77. }
  78. return undefined;
  79. };
  80. // Perform dictionary lookup, either return value or undefined if key not in dictionary
  81. Sk.builtin.dict.prototype.mp$lookup = function (key) {
  82. var k = kf(key);
  83. var bucket = this.buckets[k.v];
  84. var item;
  85. // todo; does this need to go through mp$ma_lookup
  86. if (bucket !== undefined) {
  87. item = this.key$lookup(bucket, key);
  88. if (item) {
  89. return item.rhs;
  90. }
  91. }
  92. // Not found in dictionary
  93. return undefined;
  94. };
  95. Sk.builtin.dict.prototype.mp$subscript = function (key) {
  96. Sk.builtin.pyCheckArgs("[]", arguments, 1, 2, false, false);
  97. var s;
  98. var res = this.mp$lookup(key);
  99. if (res !== undefined) {
  100. // Found in dictionary
  101. return res;
  102. } else {
  103. // Not found in dictionary
  104. s = new Sk.builtin.str(key);
  105. throw new Sk.builtin.KeyError(s.v);
  106. }
  107. };
  108. Sk.builtin.dict.prototype.sq$contains = function (ob) {
  109. Sk.builtin.pyCheckArgs("__contains__()", arguments, 1, 1, false, false);
  110. var res = this.mp$lookup(ob);
  111. return (res !== undefined);
  112. };
  113. Sk.builtin.dict.prototype.mp$ass_subscript = function (key, w) {
  114. var k = kf(key);
  115. var bucket = this.buckets[k.v];
  116. var item;
  117. if (bucket === undefined) {
  118. // New bucket
  119. bucket = {$hash: k, items: [
  120. {lhs: key, rhs: w}
  121. ]};
  122. this.buckets[k.v] = bucket;
  123. this.size += 1;
  124. return;
  125. }
  126. item = this.key$lookup(bucket, key);
  127. if (item) {
  128. item.rhs = w;
  129. return;
  130. }
  131. // Not found in dictionary
  132. bucket.items.push({lhs: key, rhs: w});
  133. this.size += 1;
  134. };
  135. Sk.builtin.dict.prototype.mp$del_subscript = function (key) {
  136. Sk.builtin.pyCheckArgs("del", arguments, 1, 1, false, false);
  137. var k = kf(key);
  138. var bucket = this.buckets[k.v];
  139. var item;
  140. var s;
  141. // todo; does this need to go through mp$ma_lookup
  142. if (bucket !== undefined) {
  143. item = this.key$pop(bucket, key);
  144. if (item !== undefined) {
  145. return;
  146. }
  147. }
  148. // Not found in dictionary
  149. s = new Sk.builtin.str(key);
  150. throw new Sk.builtin.KeyError(s.v);
  151. };
  152. Sk.builtin.dict.prototype["$r"] = function () {
  153. var v;
  154. var iter, k;
  155. var ret = [];
  156. for (iter = Sk.abstr.iter(this), k = iter.tp$iternext();
  157. k !== undefined;
  158. k = iter.tp$iternext()) {
  159. v = this.mp$subscript(k);
  160. if (v === undefined) {
  161. //print(k, "had undefined v");
  162. v = null;
  163. }
  164. // we need to check if value is same as object
  165. // otherwise it would cause an stack overflow
  166. if(v === this) {
  167. ret.push(Sk.misceval.objectRepr(k).v + ": {...}");
  168. } else {
  169. ret.push(Sk.misceval.objectRepr(k).v + ": " + Sk.misceval.objectRepr(v).v);
  170. }
  171. }
  172. return new Sk.builtin.str("{" + ret.join(", ") + "}");
  173. };
  174. Sk.builtin.dict.prototype.mp$length = function () {
  175. return this.size;
  176. };
  177. Sk.builtin.dict.prototype["get"] = new Sk.builtin.func(function (self, k, d) {
  178. Sk.builtin.pyCheckArgs("get()", arguments, 1, 2, false, true);
  179. var ret;
  180. if (d === undefined) {
  181. d = Sk.builtin.none.none$;
  182. }
  183. ret = self.mp$lookup(k);
  184. if (ret === undefined) {
  185. ret = d;
  186. }
  187. return ret;
  188. });
  189. Sk.builtin.dict.prototype["pop"] = new Sk.builtin.func(function (self, key, d) {
  190. Sk.builtin.pyCheckArgs("pop()", arguments, 1, 2, false, true);
  191. var k = kf(key);
  192. var bucket = self.buckets[k.v];
  193. var item;
  194. var s;
  195. // todo; does this need to go through mp$ma_lookup
  196. if (bucket !== undefined) {
  197. item = self.key$pop(bucket, key);
  198. if (item !== undefined) {
  199. return item.rhs;
  200. }
  201. }
  202. // Not found in dictionary
  203. if (d !== undefined) {
  204. return d;
  205. }
  206. s = new Sk.builtin.str(key);
  207. throw new Sk.builtin.KeyError(s.v);
  208. });
  209. Sk.builtin.dict.prototype["has_key"] = new Sk.builtin.func(function (self, k) {
  210. Sk.builtin.pyCheckArgs("has_key()", arguments, 1, 1, false, true);
  211. return new Sk.builtin.bool( self.sq$contains(k));
  212. });
  213. Sk.builtin.dict.prototype["items"] = new Sk.builtin.func(function (self) {
  214. Sk.builtin.pyCheckArgs("items()", arguments, 0, 0, false, true);
  215. var v;
  216. var iter, k;
  217. var ret = [];
  218. for (iter = Sk.abstr.iter(self), k = iter.tp$iternext();
  219. k !== undefined;
  220. k = iter.tp$iternext()) {
  221. v = self.mp$subscript(k);
  222. if (v === undefined) {
  223. //print(k, "had undefined v");
  224. v = null;
  225. }
  226. ret.push(new Sk.builtin.tuple([k, v]));
  227. }
  228. return new Sk.builtin.list(ret);
  229. });
  230. Sk.builtin.dict.prototype["keys"] = new Sk.builtin.func(function (self) {
  231. Sk.builtin.pyCheckArgs("keys()", arguments, 0, 0, false, true);
  232. var iter, k;
  233. var ret = [];
  234. for (iter = Sk.abstr.iter(self), k = iter.tp$iternext();
  235. k !== undefined;
  236. k = iter.tp$iternext()) {
  237. ret.push(k);
  238. }
  239. return new Sk.builtin.list(ret);
  240. });
  241. Sk.builtin.dict.prototype["values"] = new Sk.builtin.func(function (self) {
  242. Sk.builtin.pyCheckArgs("values()", arguments, 0, 0, false, true);
  243. var v;
  244. var iter, k;
  245. var ret = [];
  246. for (iter = Sk.abstr.iter(self), k = iter.tp$iternext();
  247. k !== undefined;
  248. k = iter.tp$iternext()) {
  249. v = self.mp$subscript(k);
  250. if (v === undefined) {
  251. v = null;
  252. }
  253. ret.push(v);
  254. }
  255. return new Sk.builtin.list(ret);
  256. });
  257. Sk.builtin.dict.prototype["clear"] = new Sk.builtin.func(function (self) {
  258. Sk.builtin.pyCheckArgs("clear()", arguments, 0, 0, false, true);
  259. var k;
  260. var iter;
  261. for (iter = Sk.abstr.iter(self), k = iter.tp$iternext();
  262. k !== undefined;
  263. k = iter.tp$iternext()) {
  264. self.mp$del_subscript(k);
  265. }
  266. });
  267. Sk.builtin.dict.prototype["setdefault"] = new Sk.builtin.func(function (self, key, default_) {
  268. try {
  269. return self.mp$subscript(key);
  270. }
  271. catch (e) {
  272. if (default_ === undefined) {
  273. default_ = Sk.builtin.none.none$;
  274. }
  275. self.mp$ass_subscript(key, default_);
  276. return default_;
  277. }
  278. });
  279. /*
  280. this function mimics the cpython implementation, which is also the reason for the
  281. almost similar code, this may be changed in future
  282. */
  283. Sk.builtin.dict.prototype.dict_merge = function(b) {
  284. var iter;
  285. var k, v;
  286. if(b instanceof Sk.builtin.dict) {
  287. // fast way
  288. for (iter = b.tp$iter(), k = iter.tp$iternext(); k !== undefined; k = iter.tp$iternext()) {
  289. v = b.mp$subscript(k);
  290. if (v === undefined) {
  291. throw new Sk.builtin.AttributeError("cannot get item for key: " + k.v);
  292. }
  293. this.mp$ass_subscript(k, v);
  294. }
  295. } else {
  296. // generic slower way
  297. var keys = Sk.misceval.callsim(b["keys"], b);
  298. for (iter = Sk.abstr.iter(keys), k = iter.tp$iternext(); k !== undefined; k = iter.tp$iternext()) {
  299. v = b.tp$getitem(k); // get value
  300. if (v === undefined) {
  301. throw new Sk.builtin.AttributeError("cannot get item for key: " + k.v);
  302. }
  303. this.mp$ass_subscript(k, v);
  304. }
  305. }
  306. };
  307. /**
  308. * update() accepts either another dictionary object or an iterable of key/value pairs (as tuples or other iterables of length two).
  309. * If keyword arguments are specified, the dictionary is then updated with those key/value pairs: d.update(red=1, blue=2).
  310. * https://hg.python.org/cpython/file/4ff865976bb9/Objects/dictobject.c
  311. */
  312. var update_f = function (kwargs, self, other) {
  313. // case another dict or obj with keys and getitem has been provided
  314. if(other !== undefined && (other.tp$name === "dict" || other["keys"])) {
  315. self.dict_merge(other); // we merge with override
  316. } else if(other !== undefined && Sk.builtin.checkIterable(other)) {
  317. // 2nd case, we expect an iterable that contains another iterable of length 2
  318. var iter;
  319. var k, v;
  320. var seq_i = 0; // index of current sequence item
  321. for (iter = Sk.abstr.iter(other), k = iter.tp$iternext(); k !== undefined; k = iter.tp$iternext(), seq_i++) {
  322. // check if value is iter
  323. if (!Sk.builtin.checkIterable(k)) {
  324. throw new Sk.builtin.TypeError("cannot convert dictionary update sequence element #" + seq_i + " to a sequence");
  325. }
  326. // cpython impl. would transform iterable into sequence
  327. // we just call iternext twice if k has length of 2
  328. if(k.sq$length() === 2) {
  329. var k_iter = Sk.abstr.iter(k);
  330. var k_key = k_iter.tp$iternext();
  331. var k_value = k_iter.tp$iternext();
  332. self.mp$ass_subscript(k_key, k_value);
  333. } else {
  334. // throw exception
  335. throw new Sk.builtin.ValueError("dictionary update sequence element #" + seq_i + " has length " + k.sq$length() + "; 2 is required");
  336. }
  337. }
  338. } else if(other !== undefined) {
  339. // other is not a dict or iterable
  340. throw new Sk.builtin.TypeError("'" +Sk.abstr.typeName(other) + "' object is not iterable");
  341. }
  342. // apply all key/value pairs of kwargs
  343. // create here kwargs_dict, there could be exceptions in other cases before
  344. var kwargs_dict = new Sk.builtins.dict(kwargs);
  345. self.dict_merge(kwargs_dict);
  346. // returns none, when successful or throws exception
  347. return Sk.builtin.none.none$;
  348. };
  349. update_f.co_kwargs = true;
  350. Sk.builtin.dict.prototype.update = new Sk.builtin.func(update_f);
  351. Sk.builtin.dict.prototype.__contains__ = new Sk.builtin.func(function (self, item) {
  352. Sk.builtin.pyCheckArgs("__contains__", arguments, 1, 1, false, true);
  353. return Sk.builtin.dict.prototype.sq$contains.call(self, item);
  354. });
  355. Sk.builtin.dict.prototype.__cmp__ = new Sk.builtin.func(function (self, other, op) {
  356. // __cmp__ cannot be supported until dict lt/le/gt/ge operations are supported
  357. return Sk.builtin.NotImplemented.NotImplemented$;
  358. });
  359. Sk.builtin.dict.prototype.__delitem__ = new Sk.builtin.func(function (self, item) {
  360. Sk.builtin.pyCheckArgs("__delitem__", arguments, 1, 1, false, true);
  361. return Sk.builtin.dict.prototype.mp$del_subscript.call(self, item);
  362. });
  363. Sk.builtin.dict.prototype.__getitem__ = new Sk.builtin.func(function (self, item) {
  364. Sk.builtin.pyCheckArgs("__getitem__", arguments, 1, 1, false, true);
  365. return Sk.builtin.dict.prototype.mp$subscript.call(self, item);
  366. });
  367. Sk.builtin.dict.prototype.__setitem__ = new Sk.builtin.func(function (self, item, value) {
  368. Sk.builtin.pyCheckArgs("__setitem__", arguments, 2, 2, false, true);
  369. return Sk.builtin.dict.prototype.mp$ass_subscript.call(self, item, value);
  370. });
  371. Sk.builtin.dict.prototype.__hash__ = new Sk.builtin.func(function (self) {
  372. Sk.builtin.pyCheckArgs("__hash__", arguments, 0, 0, false, true);
  373. return Sk.builtin.dict.prototype.tp$hash.call(self);
  374. });
  375. Sk.builtin.dict.prototype.__len__ = new Sk.builtin.func(function (self) {
  376. Sk.builtin.pyCheckArgs("__len__", arguments, 0, 0, false, true);
  377. return Sk.builtin.dict.prototype.mp$length.call(self);
  378. });
  379. Sk.builtin.dict.prototype.__getattr__ = new Sk.builtin.func(function (self, attr) {
  380. Sk.builtin.pyCheckArgs("__getattr__", arguments, 1, 1, false, true);
  381. if (!Sk.builtin.checkString(attr)) { throw new Sk.builtin.TypeError("__getattr__ requires a string"); }
  382. return Sk.builtin.dict.prototype.tp$getattr.call(self, Sk.ffi.remapToJs(attr));
  383. });
  384. Sk.builtin.dict.prototype.__iter__ = new Sk.builtin.func(function (self) {
  385. Sk.builtin.pyCheckArgs("__iter__", arguments, 0, 0, false, true);
  386. return new Sk.builtin.dict_iter_(self);
  387. });
  388. Sk.builtin.dict.prototype.tp$iter = function () {
  389. return new Sk.builtin.dict_iter_(this);
  390. };
  391. Sk.builtin.dict.prototype.__repr__ = new Sk.builtin.func(function (self) {
  392. Sk.builtin.pyCheckArgs("__repr__", arguments, 0, 0, false, true);
  393. return Sk.builtin.dict.prototype["$r"].call(self);
  394. });
  395. /* python3 recommends implementing simple ops */
  396. Sk.builtin.dict.prototype.ob$eq = function (other) {
  397. var iter, k, v, otherv;
  398. if (this === other) {
  399. return Sk.builtin.bool.true$;
  400. }
  401. if (!(other instanceof Sk.builtin.dict)) {
  402. return Sk.builtin.NotImplemented.NotImplemented$;
  403. }
  404. if (this.size !== other.size) {
  405. return Sk.builtin.bool.false$;
  406. }
  407. for (iter = this.tp$iter(), k = iter.tp$iternext();
  408. k !== undefined;
  409. k = iter.tp$iternext()) {
  410. v = this.mp$subscript(k);
  411. otherv = other.mp$subscript(k);
  412. if (!Sk.misceval.richCompareBool(v, otherv, "Eq")) {
  413. return Sk.builtin.bool.false$;
  414. }
  415. }
  416. return Sk.builtin.bool.true$;
  417. };
  418. Sk.builtin.dict.prototype.ob$ne = function (other) {
  419. var isEqual = this.ob$eq(other);
  420. if (isEqual instanceof Sk.builtin.NotImplemented) {
  421. return isEqual;
  422. } else if (isEqual.v) {
  423. return Sk.builtin.bool.false$;
  424. } else {
  425. return Sk.builtin.bool.true$;
  426. }
  427. };
  428. Sk.builtin.dict.prototype["copy"] = new Sk.builtin.func(function (self) {
  429. throw new Sk.builtin.NotImplementedError("dict.copy is not yet implemented in Skulpt");
  430. });
  431. Sk.builtin.dict.prototype["fromkeys"] = new Sk.builtin.func(function (seq, value) {
  432. throw new Sk.builtin.NotImplementedError("dict.fromkeys is not yet implemented in Skulpt");
  433. });
  434. Sk.builtin.dict.prototype["iteritems"] = new Sk.builtin.func(function (self) {
  435. throw new Sk.builtin.NotImplementedError("dict.iteritems is not yet implemented in Skulpt");
  436. });
  437. Sk.builtin.dict.prototype["iterkeys"] = new Sk.builtin.func(function (self) {
  438. throw new Sk.builtin.NotImplementedError("dict.iterkeys is not yet implemented in Skulpt");
  439. });
  440. Sk.builtin.dict.prototype["itervalues"] = new Sk.builtin.func(function (self) {
  441. throw new Sk.builtin.NotImplementedError("dict.itervalues is not yet implemented in Skulpt");
  442. });
  443. Sk.builtin.dict.prototype["popitem"] = new Sk.builtin.func(function (self) {
  444. throw new Sk.builtin.NotImplementedError("dict.popitem is not yet implemented in Skulpt");
  445. });
  446. Sk.builtin.dict.prototype["viewitems"] = new Sk.builtin.func(function (self) {
  447. throw new Sk.builtin.NotImplementedError("dict.viewitems is not yet implemented in Skulpt");
  448. });
  449. Sk.builtin.dict.prototype["viewkeys"] = new Sk.builtin.func(function (self) {
  450. throw new Sk.builtin.NotImplementedError("dict.viewkeys is not yet implemented in Skulpt");
  451. });
  452. Sk.builtin.dict.prototype["viewvalues"] = new Sk.builtin.func(function (self) {
  453. throw new Sk.builtin.NotImplementedError("dict.viewvalues is not yet implemented in Skulpt");
  454. });
  455. goog.exportSymbol("Sk.builtin.dict", Sk.builtin.dict);
  456. /**
  457. * @constructor
  458. * @param {Object} obj
  459. */
  460. Sk.builtin.dict_iter_ = function (obj) {
  461. var k, i, bucket, allkeys, buckets;
  462. if (!(this instanceof Sk.builtin.dict_iter_)) {
  463. return new Sk.builtin.dict_iter_(obj);
  464. }
  465. this.$index = 0;
  466. this.$obj = obj;
  467. allkeys = [];
  468. buckets = obj.buckets;
  469. for (k in buckets) {
  470. if (buckets.hasOwnProperty(k)) {
  471. bucket = buckets[k];
  472. if (bucket && bucket.$hash !== undefined && bucket.items !== undefined) {
  473. // skip internal stuff. todo; merge pyobj and this
  474. for (i = 0; i < bucket.items.length; i++) {
  475. allkeys.push(bucket.items[i].lhs);
  476. }
  477. }
  478. }
  479. }
  480. this.$keys = allkeys;
  481. this.tp$iter = this;
  482. this.tp$iternext = function () {
  483. // todo; StopIteration
  484. if (this.$index >= this.$keys.length) {
  485. return undefined;
  486. }
  487. return this.$keys[this.$index++];
  488. // return this.$obj[this.$keys[this.$index++]].lhs;
  489. };
  490. this.$r = function () {
  491. return new Sk.builtin.str("dictionary-keyiterator");
  492. };
  493. return this;
  494. };
  495. Sk.abstr.setUpInheritance("dictionary-keyiterator", Sk.builtin.dict_iter_, Sk.builtin.object);
  496. Sk.builtin.dict_iter_.prototype.__class__ = Sk.builtin.dict_iter_;
  497. Sk.builtin.dict_iter_.prototype.__iter__ = new Sk.builtin.func(function (self) {
  498. return self;
  499. });
  500. Sk.builtin.dict_iter_.prototype["next"] = new Sk.builtin.func(function (self) {
  501. var ret = self.tp$iternext();
  502. if (ret === undefined) {
  503. throw new Sk.builtin.StopIteration();
  504. }
  505. return ret;
  506. });