Permalink
Feb 1, 2020
Jul 21, 2020
Dec 28, 2020
Dec 28, 2020
Dec 28, 2020
May 17, 2019
Feb 1, 2020
Jan 15, 2018
Apr 3, 2019
Apr 3, 2019
Feb 1, 2020
Apr 3, 2019
Dec 13, 2018
Oct 5, 2020
Nov 6, 2019
Dec 10, 2018
Dec 10, 2018
Mar 7, 2018
Feb 26, 2018
Feb 1, 2020
Dec 10, 2018
Jan 29, 2021
Dec 28, 2020
Dec 10, 2018
Jan 29, 2021
Jul 30, 2020
Jul 14, 2020
Jul 31, 2018
Jul 31, 2018
Dec 10, 2018
Nov 2, 2018
Jul 31, 2018
Jun 11, 2020
Dec 10, 2018
Jun 11, 2020
Apr 18, 2020
Jun 11, 2020
May 27, 2018
Apr 18, 2020
Feb 1, 2020
Feb 3, 2018
Feb 1, 2020
Jul 4, 2020
Jul 4, 2020
Jul 30, 2020
Jul 30, 2020
Mar 7, 2018
Mar 7, 2018
Nov 9, 2015
May 17, 2019
Feb 26, 2018
Jul 21, 2020
Dec 28, 2020
Feb 26, 2018
Dec 28, 2020
Dec 13, 2018
Mar 7, 2018
Jan 29, 2021
Mar 7, 2018
Feb 26, 2018
Apr 14, 2019
Dec 13, 2018
May 17, 2019
Feb 26, 2018
Dec 28, 2020
Dec 1, 2018
Jun 29, 2020
Jun 28, 2018
May 16, 2020
May 16, 2020
Feb 1, 2020
Mar 7, 2018
Aug 2, 2018
Mar 16, 2021
Newer
100644
1235 lines (1114 sloc)
36.1 KB
3
/*
4
Implementation of Python dictionaries
5
6
We can't use Javascript's Map here, because the behaviour is not exactly the
7
same (eg with keys that are instances of classes with a __hash__ method...)
8
and because Map is much slower than regular Javascript objects.
9
10
A Python dictionary is implemented as a Javascript objects with these
11
attributes:
12
. $version: an integer with an initial value of 0, incremented at each
13
insertion
14
. $numeric_dict: for keys of type int
15
. $string_dict and $str_hash: for keys of type str
16
. $object_dict: for keys of other types
17
18
The value associated to a key in $numeric_dict and $string_dict is a pair
19
[value, rank] where "value" is the value associated with the key and "rank"
20
is the value of the dict attribute $version when the pair is inserted. This
21
is required to keep track of the insertion order, mandatory since Python 3.7.
22
23
For keys that are not str or int, their hash value is computed. Since several
34
var set_ops = ["eq", "le", "lt", "ge", "gt",
35
"sub", "rsub", "and", "or", "xor"]
36
37
// methods to compare non set-like views
38
function is_sublist(t1, t2){
39
// Return true if all elements of t1 are in t2
40
for(var i = 0, ilen = t1.length; i < ilen; i++){
41
var x = t1[i],
42
flag = false
43
for(var j = 0, jlen = t2.length; j < jlen; j++){
44
if($B.rich_comp("__eq__", x, t2[j])){
45
t2.splice(j, 1)
46
flag = true
47
break
48
}
49
}
50
if(! flag){
51
return false
52
}
53
}
54
return true
55
}
56
57
dict_view_op = {
58
__eq__: function(t1, t2){
59
return t1.length == t2.length && is_sublist(t1, t2)
60
},
61
__ne__: function(t1, t2){
62
return ! dict_view_op.__eq__(t1, t2)
63
},
64
__lt__: function(t1, t2){
65
return t1.length < t2.length && is_sublist(t1, t2)
66
},
67
__gt__: function(t1, t2){
68
return dict_view_op.__lt__(t2, t1)
69
},
70
__le__: function(t1, t2){
71
return t1.length <= t2.length && is_sublist(t1, t2)
72
},
73
__ge__: function(t1, t2){
74
return dict_view_op.__le__(t2, t1)
75
},
76
__and__: function(t1, t2){
77
var items = []
78
for(var i = 0, ilen = t1.length; i < ilen; i++){
79
var x = t1[i]
80
flag = false
81
for(var j = 0, jlen = t2.length; j < jlen; j++){
82
if($B.rich_comp("__eq__", x, t2[j])){
83
t2.splice(j, 1)
84
items.push(x)
85
break
86
}
87
}
88
}
89
return items
90
},
91
__or__: function(t1, t2){
92
var items = t1
93
for(var j = 0, jlen = t2.length; j < jlen; j++){
94
var y = t2[j],
95
flag = false
96
for(var i = 0, ilen = t1.length; i < ilen; i++){
97
if($B.rich_comp("__eq__", y, t1[i])){
98
t2.splice(j, 1)
99
flag = true
100
break
101
}
102
}
103
if(! flag){
104
items.push(y)
105
}
106
}
107
return items
108
}
125
for(var i = 0, len = set_ops.length; i < len; i++){
126
var op = "__" + set_ops[i] + "__"
127
klass[op] = (function(op){
128
return function(self, other){
129
// compare set of items to other
130
if(self.set_like){
131
return _b_.set[op](_b_.set.$factory(self),
132
_b_.set.$factory(other))
133
}else{
134
// Non-set like views can only be compared to
135
// instances of the same class
136
if(other.__class__ !== klass){
137
return false
139
var other_items = _b_.list.$factory(other)
140
return dict_view_op[op](self.items, other_items)
148
it.test_change = function(){
149
return self.dict.$version != self.dict_version
150
}
158
klass.__repr__ = function(self){
159
return klass.$infos.__name__ + '(' + _b_.repr(self.items) + ')'
160
}
161
177
dict.$to_obj = function(d){
178
// Function applied to dictionary that only have string keys,
179
// return a Javascript objects with the kays mapped to the value,
180
// excluding the insertion rank
181
var res = {}
182
for(var key in d.$string_dict){
183
res[key] = d.$string_dict[key][0]
184
}
185
return res
186
}
187
197
if(val === undefined){val = _b_.NotImplemented}
198
else if(val === null){val = $N}
203
for(var k in d.$numeric_dict){
204
items.push([parseFloat(k), d.$numeric_dict[k]])
205
}
211
for(var k in d.$object_dict){
212
d.$object_dict[k].forEach(function(item){
213
items.push(item)
214
})
215
}
217
// sort by insertion order
218
items.sort(function(a, b){
219
return a[1][1] - b[1][1]
220
})
221
items = items.map(function(item){return [item[0], item[1][0]]})
226
}else{
227
items.__class__ = _b_.tuple
228
return items.map(function(item){
229
item.__class__ = _b_.tuple; return item}
230
)
231
}
242
si(left, _l[i][0], _l[i][1])
243
if(right.$version != right_version){
244
throw _b_.RuntimeError.$factory("dict mutated during update")
245
}
246
}
249
function rank(self, hash, key){
250
// Search if object key, with hash = hash(key), is in
251
// self.$object_dict
252
var pairs = self.$object_dict[hash]
253
if(pairs !== undefined){
254
for(var i = 0, len = pairs.length; i < len; i++){
255
if($B.rich_comp("__eq__", key, pairs[i][0])){
256
return i
257
}
258
}
259
}
260
return -1
261
}
262
269
dict.__class_getitem__ = function(cls, item){
270
// PEP 585
271
// Set as a classmethod at the end of this script, after $B.set_func_names()
272
if(! Array.isArray(item)){
273
item = [item]
274
}
275
return $B.GenericAlias.$factory(cls, item)
276
}
277
280
var $ = $B.args("__contains__", 2, {self: null, key: null},
281
["self", "key"], arguments, {}, null, null),
284
if(self.$is_namespace){key = $B.to_alias(key)} // issue 1244
285
286
if(self.$jsobj){
287
return self.$jsobj[key] !== undefined
288
}
294
return self.$numeric_dict[key] !== undefined
295
}
296
297
var hash = _b_.hash(key)
298
if(self.$str_hash[hash] !== undefined &&
299
$B.rich_comp("__eq__", key, self.$str_hash[hash])){return true}
300
if(self.$numeric_dict[hash] !== undefined &&
301
$B.rich_comp("__eq__", key, hash)){return true}
302
return rank(self, hash, key) > -1
307
var $ = $B.args("__eq__", 2, {self: null, arg: null},
308
["self", "arg"], arguments, {}, null, null),
317
switch(typeof arg){
318
case "string":
319
if(self.$string_dict[arg] === undefined){
339
if((ix = rank(self, hash, arg)) > -1){
340
self.$object_dict[hash].splice(ix, 1)
341
}else{
350
var $ = $B.args("__eq__", 2, {self: null, other: null},
351
["self", "other"], arguments, {}, null, null),
357
if(self.$jsobj){self = jsobj2dict(self.$jsobj)}
358
if(other.$jsobj){other = jsobj2dict(other.$jsobj)}
369
if(!$B.rich_comp("__eq__", other.$numeric_dict[k][0],
370
self.$numeric_dict[k][0])){
374
var pairs = other.$object_dict[k],
375
flag = false
376
for(var i = 0, len = pairs.length; i < len; i++){
377
if($B.rich_comp("__eq__", k, pairs[i][0]) &&
378
$B.rich_comp("__eq__", self.$numeric_dict[k],
379
pairs[i][1])){
380
flag = true
381
break
382
}
397
var pairs = self.$object_dict[hash]
398
// Get all (key, value) pairs in other that have the same hash
399
var other_pairs = []
400
if(other.$numeric_dict[hash] !== undefined){
401
other_pairs.push([hash, other.$numeric_dict[hash]])
402
}
404
other_pairs = other_pairs.concat(other.$object_dict[hash])
405
}
406
if(other_pairs.length == 0){
407
return false
408
}
409
for(var i = 0, len_i = pairs.length; i < len_i; i++){
410
var flag = false
411
var key = pairs[i][0],
413
for(var j = 0, len_j = other_pairs.length; j < len_j; j++){
414
if($B.rich_comp("__eq__", key, other_pairs[j][0]) &&
429
var $ = $B.args("__getitem__", 2, {self: null, arg: null},
430
["self", "arg"], arguments, {}, null, null),
438
dict.$getitem = function(self, arg, ignore_missing){
439
// ignore_missing is only set in dict.setdefault
452
var x = self.$string_dict[arg]
453
if(x !== undefined){
454
$B.string_count++
455
return x[0]
463
break
464
}
465
466
// since the key is more complex use 'default' method of getting item
467
492
if(! ignore_missing){
493
if(self.__class__ !== dict && ! ignore_missing){
494
try{
495
var missing_method = getattr(self.__class__, "__missing__",
496
_b_.None)
497
}catch(err){
498
console.log(err)
524
if(item[0] != 0 && item[0] != 1){
525
self.$numeric_dict[item[0]] = [item[1], self.$order++]
526
self.$version++
527
break
528
}
541
for(var key in first){
542
self.$string_dict[key] = [first[key], self.$order++]
543
}
544
return _b_.None
545
}else if(first.$jsobj){
546
self.$jsobj = {}
547
for(var attr in first.$jsobj){
548
self.$jsobj[attr] = first.$jsobj[attr]
558
arguments, {}, "first", "second")
559
var args = $.first
560
if(args.length > 1){
561
throw _b_.TypeError.$factory("dict expected at most 1 argument" +
562
", got 2")
563
}else if(args.length == 1){
564
args = args[0]
565
if(args.__class__ === dict){
566
['$string_dict', '$str_hash', '$numeric_dict', '$object_dict'].
567
forEach(function(d){
568
for(key in args[d]){self[d][key] = args[d][key]}
569
})
573
var keys = $B.$getattr(args, "keys", null)
574
if(keys !== null){
575
var gi = $B.$getattr(args, "__getitem__", null)
576
if(gi !== null){
577
// has keys and __getitem__ : it's a mapping, iterate on
578
// keys and values
579
gi = $B.$call(gi)
580
var kiter = _b_.iter($B.$call(keys)())
581
while(true){
582
try{
583
var key = _b_.next(kiter),
584
value = gi(key)
585
dict.__setitem__(self, key, value)
586
}catch(err){
587
if(err.__class__ === _b_.StopIteration){
588
break
589
}
590
throw err
591
}
592
}
593
return $N
594
}
595
}
596
if(! Array.isArray(args)){
597
args = _b_.list.$factory(args)
598
}
599
// Form "dict([[key1, value1], [key2,value2], ...])"
625
dict.__ior__ = function(self, other){
626
// PEP 584
627
dict.update(self, other)
628
return self
629
}
630
639
for(var k in self.$numeric_dict){_count++}
640
for(var k in self.$string_dict){_count++}
641
for(var hash in self.$object_dict){
642
_count += self.$object_dict[hash].length
643
}
669
dict.__or__ = function(self, other){
670
// PEP 584
671
if(! _b_.isinstance(other, dict)){
672
return _b_.NotImplemented
673
}
674
var res = dict.copy(self)
675
dict.update(res, other)
676
return res
677
}
678
679
function __newobj__(){
680
// __newobj__ is called with a generator as only argument
681
var $ = $B.args('__newobj__', 0, {}, [], arguments, {}, 'args', null),
682
args = $.args
683
var res = $B.empty_dict()
684
res.__class__ = args[0]
685
return res
686
}
687
688
dict.__reduce_ex__ = function(self, protocol){
689
return $B.fast_tuple([
690
__newobj__,
691
$B.fast_tuple([self.__class__]),
692
_b_.None,
693
_b_.None,
694
dict.items(self)])
695
}
696
707
try{
708
res.push(repr(item[0]) + ": " + repr(item[1]))
709
}catch(err){
710
throw err
711
}
717
dict.__ror__ = function(self, other){
718
// PEP 584
719
if(! _b_.isinstance(other, dict)){
720
return _b_.NotImplemented
721
}
722
var res = dict.copy(other)
723
dict.update(res, self)
724
return res
725
}
726
729
["self", "key", "value"], arguments, {}, null, null)
730
return dict.$setitem($.self, $.key, $.value)
731
}
736
// If key is a string, set:
737
// - $string_dict[key] = [value, order] where "order" is an auto-increment
738
// unique id to keep track of insertion order
739
// - $str_hash[hash(key)] to key
740
//
741
// If key is a number, set $numeric_dict[key] = value
742
//
743
// If key is another object, compute its hash value:
744
// - if the hash is a key of $str_hash, and key == $str_hash[hash],
745
// replace $string_dict[$str_hash[hash]] by value
746
// - if the hash is a key of $numeric_dict, and hash == key, replace
747
// $numeric_dict[hash] by value
748
// - if the hash is a key of $object_dict: $object_dict[hash] is a list
749
// of [k, v] pairs. If key is equal to one of the "k", replace the
750
// matching v by value. Otherwise, add [key, value] to the list
751
// - else set $object_dict[hash] = [[key, value]]
752
//
753
// In all cases, increment attribute $version, used to detect dictionary
756
// Parameter $hash is only set if this method is called by setdefault.
757
// In this case the hash of key has already been computed and we
758
// know that the key is not present in the dictionary, so it's no
759
// use computing hash(key) again, nor testing equality of keys
768
// If class attribute __init__ or __new__ are reset,
769
// the factory function has to change
770
self.$jsobj.$factory = $B.$instance_creator(self.$jsobj)
771
}
772
}else{
780
if(self.$string_dict === undefined){
781
console.log("pas de string dict", self, key, value)
782
}
783
if(self.$string_dict[key] !== undefined){
784
self.$string_dict[key][0] = value
785
}else{
786
self.$string_dict[key] = [value, self.$order++]
787
self.$str_hash[str_hash(key)] = key
788
self.$version++
789
}
792
if(self.$numeric_dict[key] !== undefined){
793
// existing key: preserve order
794
self.$numeric_dict[key][0] = value
795
}else{
796
// special case for 0 and 1 if True or False are keys
797
var done = false
798
if((key == 0 || key == 1) &&
799
self.$object_dict[key] !== undefined){
800
for(const item of self.$object_dict[key]){
801
if((key == 0 && item[0] === false) ||
802
(key == 1 && item[0] === true)){
803
// replace value
804
item[1][0] = value
805
done = true
806
}
807
}
808
}
809
if(! done){
810
// new key
811
self.$numeric_dict[key] = [value, self.$order++]
812
}
816
case "boolean":
817
// true replaces 1 and false replaces 0
818
var num = key ? 1 : 0
819
if(self.$numeric_dict[num] !== undefined){
820
var order = self.$numeric_dict[num][1] // preserve order
821
self.$numeric_dict[num] = [value, order]
822
return
823
}
824
if(self.$object_dict[num] !== undefined){
825
self.$object_dict[num].push([key, [value, self.$order++]])
826
}else{
827
self.$object_dict[num] = [[key, [value, self.$order++]]]
828
}
848
// If $setitem is called from setdefault, don't test equality of key
849
// with any object
850
if($hash){
851
if(self.$object_dict[$hash] !== undefined){
855
}
856
self.$version++
857
return $N
858
}
859
var ix = rank(self, hash, key)
860
if(ix > -1){
861
// reset value
916
var $ = $B.args("fromkeys", 3, {cls: null, keys: null, value: null},
917
["cls", "keys", "value"], arguments, {value: _b_.None}, null, null),
929
if(klass === dict){dict.$setitem(res, key, value)}
930
else{$B.$getattr(res, "__setitem__")(key, value)}
941
var $ = $B.args("get", 3, {self: null, key: null, _default: null},
942
["self", "key", "_default"], arguments, {_default: $N}, null, null)
945
catch(err){
946
if(_b_.isinstance(err, _b_.KeyError)){return $._default}
947
else{throw err}
948
}
949
}
950
951
var dict_items = $B.make_view("dict_items", true)
952
dict_items.$iterator = $B.make_iterator_class("dict_itemiterator")
956
var _len = arguments.length - 1,
957
_msg = "items() takes no arguments (" + _len + " given)"
960
var items = to_list(self),
961
set_like = true
962
// Check if all values are hashable
963
for(var i = 0, len = items.length; i < len; i++){
964
try{
965
_b_.hash(items[i][1])
966
}catch(err){
967
set_like = false
968
break
969
}
970
}
971
var values = to_list(self)
972
var it = dict_items.$factory(self, values, set_like)
973
it.dict_version = self.$version
983
var _len = arguments.length - 1,
984
_msg = "keys() takes no arguments (" + _len + " given)"
987
var it = dict_keys.$factory(self, to_list(self, 0), true)
988
it.dict_version = self.$version
994
var missing = {},
995
$ = $B.args("pop", 3, {self: null, key: null, _default: null},
996
["self", "key", "_default"], arguments, {_default: missing}, null, null),
1028
var $ = $B.args("setdefault", 3, {self: null, key: null, _default: null},
1029
["self", "key", "_default"], arguments, {_default: $N}, null, null),
1033
try{
1034
// Pass 3rd argument to dict.$getitem to avoid using __missing__
1035
// Cf. issue #1598
1036
return dict.$getitem(self, key, true)
1037
}catch(err){
1042
var hash = key.$hash
1043
key.$hash = undefined
1044
dict.$setitem(self, key, _default, hash)
1051
var $ = $B.args("update", 1, {"self": null}, ["self"], arguments,
1052
{}, "args", "kw"),
1053
self = $.self,
1054
args = $.args,
1055
kw = $.kw
1056
if(args.length > 0){
1057
var o = args[0]
1064
var _keys = _b_.list.$factory($B.$call($B.$getattr(o, "keys"))())
1065
for(var i = 0, len = _keys.length; i < len; i++){
1067
dict.$setitem(self, _keys[i], _value)
1068
}
1069
}else{
1070
var it = _b_.iter(o),
1071
i = 0
1072
while(true){
1073
try{
1074
var item = _b_.next(it)
1075
}catch(err){
1076
if(err.__class__ === _b_.StopIteration){break}
1077
throw err
1078
}
1079
try{
1080
key_value = _b_.list.$factory(item)
1081
}catch(err){
1082
throw _b_.TypeError.$factory("cannot convert dictionary" +
1083
" update sequence element #" + i + " to a sequence")
1084
}
1085
if(key_value.length !== 2){
1086
throw _b_.ValueError.$factory("dictionary update " +
1087
"sequence element #" + i + " has length " +
1088
key_value.length + "; 2 is required")
1089
}
1090
dict.$setitem(self, key_value[0], key_value[1])
1091
i++
1100
var dict_values = $B.make_view("dict_values")
1101
dict_values.$iterator = $B.make_iterator_class("dict_valueiterator")
1105
var _len = arguments.length - 1,
1106
_msg = "values() takes no arguments (" + _len + " given)"
1110
var it = dict_values.$factory(self, values, false)
1111
it.dict_version = self.$version
1117
var args = [res]
1118
for(var i = 0, len = arguments.length; i < len ; i++){
1119
args.push(arguments[i])
1120
}
1121
dict.__init__.apply(null, args)
1131
$B.empty_dict = function(){
1132
return {
1133
__class__: dict,
1134
$numeric_dict : {},
1135
$object_dict : {},
1136
$string_dict : {},
1137
$str_hash: {},
1143
// This must be done after set_func_names, otherwise dict.fromkeys doesn't
1144
// have the attribute $infos
1145
dict.fromkeys = _b_.classmethod.$factory(dict.fromkeys)
1146
1147
$B.getset_descriptor = $B.make_class("getset_descriptor",
1148
function(klass, attr){
1149
return {
1150
__class__: $B.getset_descriptor,
1152
cls: klass,
1153
attr: attr
1154
}
1155
}
1156
)
1157
1158
$B.getset_descriptor.__repr__ = $B.getset_descriptor.__str__ = function(self){
1159
return `<attribute '${self.attr}' of '${self.cls.$infos.__name__}' objects>`
1160
}
1161
1162
$B.set_func_names($B.getset_descriptor, "builtins")
1163
1168
// obj is a dictionary, with $string_dict table such that
1169
// obj.$string_dict[key] = [value, rank]
1170
// Transform it into an object with attribute $jsobj such that
1171
// res.$jsobj[key] = value
1172
var res = $B.obj_dict(dict.$to_obj(obj))
1182
throw _b_.TypeError.$factory("'mappingproxy' object does not support " +
1183
"item assignment")
1186
for(var attr in dict){
1187
if(mappingproxy[attr] !== undefined ||
1188
["__class__", "__mro__", "__new__", "__init__", "__delitem__",
1189
"clear", "fromkeys", "pop", "popitem", "setdefault",
1190
"update"].indexOf(attr) > -1){
1191
continue
1192
}
1193
if(typeof dict[attr] == "function"){
1194
mappingproxy[attr] = (function(key){
1195
return function(){
1196
return dict[key].apply(null, arguments)
1197
}
1198
})(attr)
1199
}else{
1200
mappingproxy[attr] = dict[attr]
1201
}
1202
}
1203
1227
throw _b_.AttributeError.$factory("'" + $B.class_name(obj) +
1228
"' object has no attribute '__dict__'")}