Permalink
Feb 1, 2020
Jul 21, 2020
Jul 21, 2020
May 17, 2019
Jul 21, 2020
May 17, 2019
May 17, 2019
May 17, 2019
May 17, 2019
Feb 1, 2020
Jan 15, 2018
Apr 3, 2019
Apr 3, 2019
Feb 1, 2020
Apr 3, 2019
Apr 3, 2019
Apr 3, 2019
Dec 13, 2018
Nov 6, 2019
Dec 10, 2018
Dec 10, 2018
Mar 7, 2018
Feb 26, 2018
Feb 1, 2020
Dec 10, 2018
Dec 10, 2018
Dec 10, 2018
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
Feb 26, 2018
Dec 13, 2018
Mar 7, 2018
Mar 7, 2018
Feb 26, 2018
Apr 14, 2019
Dec 13, 2018
May 17, 2019
Feb 26, 2018
Jul 21, 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
Feb 26, 2018
Newer
100644
1213 lines (1095 sloc)
35.8 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
}
110
}
111
112
$B.make_view = function(name){
113
var klass = $B.make_class(name, function(items, set_like){
121
}
122
})
123
124
for(var i = 0, len = set_ops.length; i < len; i++){
125
var op = "__" + set_ops[i] + "__"
126
klass[op] = (function(op){
127
return function(self, other){
128
// compare set of items to other
129
if(self.set_like){
130
return _b_.set[op](_b_.set.$factory(self),
131
_b_.set.$factory(other))
132
}else{
133
// Non-set like views can only be compared to
134
// instances of the same class
135
if(other.__class__ !== klass){
136
return false
137
}
138
var other_items = _b_.list.$factory(other)
139
return dict_view_op[op](self.items, other_items)
140
}
141
}
142
})(op)
143
}
144
klass.__iter__ = function(self){
145
var it = klass.$iterator.$factory(self.items)
146
it.len_func = self.len_func
147
return it
148
}
154
klass.__repr__ = function(self){
155
return klass.$infos.__name__ + '(' + _b_.repr(self.items) + ')'
156
}
157
159
return klass
160
}
161
162
// Special version of __next__ for iterators on dict keys / values / items.
163
// Checks that the dictionary size didn't change during iteration.
164
function dict_iterator_next(self){
165
if(self.len_func() != self.len){
167
}
168
self.counter++
169
if(self.counter < self.items.length){
170
return self.items[self.counter]
171
}
172
throw _b_.StopIteration.$factory("StopIteration")
173
}
174
186
dict.$to_obj = function(d){
187
// Function applied to dictionary that only have string keys,
188
// return a Javascript objects with the kays mapped to the value,
189
// excluding the insertion rank
190
var res = {}
191
for(var key in d.$string_dict){
192
res[key] = d.$string_dict[key][0]
193
}
194
return res
195
}
196
206
if(val === undefined){val = _b_.NotImplemented}
207
else if(val === null){val = $N}
211
}else{
212
for(var k in d.$numeric_dict){
213
items.push([parseFloat(k), d.$numeric_dict[k]])
214
}
218
for(var k in d.$object_dict){
219
d.$object_dict[k].forEach(function(item){
220
items.push(item)
221
})
222
}
223
// sort by insertion order
224
items.sort(function(a, b){
225
return a[1][1] - b[1][1]
226
})
227
items = items.map(function(item){return [item[0], item[1][0]]})
230
if(ix !== undefined){
231
return items.map(function(item){return item[ix]})
232
}else{
233
items.__class__ = _b_.tuple
234
return items.map(function(item){
235
item.__class__ = _b_.tuple; return item}
236
)
237
}
240
$B.dict_to_list = to_list // used in py_types.js
241
242
// Special version of __next__ for iterators on dict keys / values / items.
243
// Checks that the dictionary size didn't change during iteration.
244
function dict_iterator_next(self){
245
if(self.len_func() != self.len){
247
}
248
self.counter++
249
if(self.counter < self.items.length){
250
return self.items[self.counter]
261
si(left, _l[i][0], _l[i][1])
262
if(right.$version != right_version){
263
throw _b_.RuntimeError.$factory("dict mutated during update")
264
}
265
}
268
function rank(self, hash, key){
269
// Search if object key, with hash = hash(key), is in
270
// self.$object_dict
271
var pairs = self.$object_dict[hash]
272
if(pairs !== undefined){
273
for(var i = 0, len = pairs.length; i < len; i++){
274
if($B.rich_comp("__eq__", key, pairs[i][0])){
275
return i
276
}
277
}
278
}
279
return -1
280
}
281
290
var $ = $B.args("__contains__", 2, {self: null, key: null},
291
["self", "key"], arguments, {}, null, null),
294
if(self.$is_namespace){key = $B.to_alias(key)} // issue 1244
295
296
if(self.$jsobj){
297
return self.$jsobj[key] !== undefined
298
}
304
return self.$numeric_dict[key] !== undefined
305
}
306
307
var hash = _b_.hash(key)
308
if(self.$str_hash[hash] !== undefined &&
309
$B.rich_comp("__eq__", key, self.$str_hash[hash])){return true}
310
if(self.$numeric_dict[hash] !== undefined &&
311
$B.rich_comp("__eq__", key, hash)){return true}
312
return rank(self, hash, key) > -1
317
var $ = $B.args("__eq__", 2, {self: null, arg: null},
318
["self", "arg"], arguments, {}, null, null),
327
switch(typeof arg){
328
case "string":
329
if(self.$string_dict[arg] === undefined){
349
if((ix = rank(self, hash, arg)) > -1){
350
self.$object_dict[hash].splice(ix, 1)
351
}else{
360
var $ = $B.args("__eq__", 2, {self: null, other: null},
361
["self", "other"], arguments, {}, null, null),
367
if(self.$jsobj){self = jsobj2dict(self.$jsobj)}
368
if(other.$jsobj){other = jsobj2dict(other.$jsobj)}
379
if(!$B.rich_comp("__eq__", other.$numeric_dict[k][0],
380
self.$numeric_dict[k][0])){
384
var pairs = other.$object_dict[k],
385
flag = false
386
for(var i = 0, len = pairs.length; i < len; i++){
387
if($B.rich_comp("__eq__", k, pairs[i][0]) &&
388
$B.rich_comp("__eq__", self.$numeric_dict[k],
389
pairs[i][1])){
390
flag = true
391
break
392
}
407
var pairs = self.$object_dict[hash]
408
// Get all (key, value) pairs in other that have the same hash
409
var other_pairs = []
410
if(other.$numeric_dict[hash] !== undefined){
411
other_pairs.push([hash, other.$numeric_dict[hash]])
412
}
414
other_pairs = other_pairs.concat(other.$object_dict[hash])
415
}
416
if(other_pairs.length == 0){
417
return false
418
}
419
for(var i = 0, len_i = pairs.length; i < len_i; i++){
420
var flag = false
421
var key = pairs[i][0],
423
for(var j = 0, len_j = other_pairs.length; j < len_j; j++){
424
if($B.rich_comp("__eq__", key, other_pairs[j][0]) &&
439
var $ = $B.args("__getitem__", 2, {self: null, arg: null},
440
["self", "arg"], arguments, {}, null, null),
456
457
switch(typeof arg){
458
case "string":
459
if(self.$string_dict[arg] !== undefined){
467
break
468
}
469
470
// since the key is more complex use 'default' method of getting item
471
526
if(item[0] != 0 && item[0] != 1){
527
self.$numeric_dict[item[0]] = [item[1], self.$order++]
528
self.$version++
529
break
530
}
543
for(var key in first){
544
self.$string_dict[key] = [first[key], self.$order++]
545
}
546
return _b_.None
547
}else if(first.$jsobj){
548
self.$jsobj = {}
549
for(var attr in first.$jsobj){
550
self.$jsobj[attr] = first.$jsobj[attr]
560
arguments, {}, "first", "second")
561
var args = $.first
562
if(args.length > 1){
563
throw _b_.TypeError.$factory("dict expected at most 1 argument" +
564
", got 2")
565
}else if(args.length == 1){
566
args = args[0]
567
if(args.__class__ === dict){
568
['$string_dict', '$str_hash', '$numeric_dict', '$object_dict'].
569
forEach(function(d){
570
for(key in args[d]){self[d][key] = args[d][key]}
571
})
575
var keys = $B.$getattr(args, "keys", null)
576
if(keys !== null){
577
var gi = $B.$getattr(args, "__getitem__", null)
578
if(gi !== null){
579
// has keys and __getitem__ : it's a mapping, iterate on
580
// keys and values
581
gi = $B.$call(gi)
582
var kiter = _b_.iter($B.$call(keys)())
583
while(true){
584
try{
585
var key = _b_.next(kiter),
586
value = gi(key)
587
dict.__setitem__(self, key, value)
588
}catch(err){
589
if(err.__class__ === _b_.StopIteration){
590
break
591
}
592
throw err
593
}
594
}
595
return $N
596
}
597
}
598
if(! Array.isArray(args)){
599
args = _b_.list.$factory(args)
600
}
601
// Form "dict([[key1, value1], [key2,value2], ...])"
627
dict.__ior__ = function(self, other){
628
// PEP 584
629
dict.update(self, other)
630
return self
631
}
632
641
for(var k in self.$numeric_dict){_count++}
642
for(var k in self.$string_dict){_count++}
643
for(var hash in self.$object_dict){
644
_count += self.$object_dict[hash].length
645
}
671
dict.__or__ = function(self, other){
672
// PEP 584
673
if(! _b_.isinstance(other, dict)){
674
return _b_.NotImplemented
675
}
676
var res = dict.copy(self)
677
dict.update(res, other)
678
return res
679
}
680
691
try{
692
res.push(repr(item[0]) + ": " + repr(item[1]))
693
}catch(err){
694
throw err
695
}
701
dict.__ror__ = function(self, other){
702
// PEP 584
703
if(! _b_.isinstance(other, dict)){
704
return _b_.NotImplemented
705
}
706
var res = dict.copy(other)
707
dict.update(res, self)
708
return res
709
}
710
713
["self", "key", "value"], arguments, {}, null, null)
714
return dict.$setitem($.self, $.key, $.value)
715
}
720
// If key is a string, set:
721
// - $string_dict[key] = [value, order] where "order" is an auto-increment
722
// unique id to keep track of insertion order
723
// - $str_hash[hash(key)] to key
724
//
725
// If key is a number, set $numeric_dict[key] = value
726
//
727
// If key is another object, compute its hash value:
728
// - if the hash is a key of $str_hash, and key == $str_hash[hash],
729
// replace $string_dict[$str_hash[hash]] by value
730
// - if the hash is a key of $numeric_dict, and hash == key, replace
731
// $numeric_dict[hash] by value
732
// - if the hash is a key of $object_dict: $object_dict[hash] is a list
733
// of [k, v] pairs. If key is equal to one of the "k", replace the
734
// matching v by value. Otherwise, add [key, value] to the list
735
// - else set $object_dict[hash] = [[key, value]]
736
//
737
// In all cases, increment attribute $version, used to detect dictionary
740
// Parameter $hash is only set if this method is called by setdefault.
741
// In this case the hash of key has already been computed and we
742
// know that the key is not present in the dictionary, so it's no
743
// use computing hash(key) again, nor testing equality of keys
752
// If class attribute __init__ or __new__ are reset,
753
// the factory function has to change
754
self.$jsobj.$factory = $B.$instance_creator(self.$jsobj)
755
}
756
}else{
764
if(self.$string_dict === undefined){
765
console.log("pas de string dict", self, key, value)
766
}
767
if(self.$string_dict[key] !== undefined){
768
self.$string_dict[key][0] = value
769
}else{
770
self.$string_dict[key] = [value, self.$order++]
771
self.$str_hash[str_hash(key)] = key
772
self.$version++
773
}
776
if(self.$numeric_dict[key] !== undefined){
777
// existing key: preserve order
778
self.$numeric_dict[key][0] = value
779
}else{
780
// special case for 0 and 1 if True or False are keys
781
var done = false
782
if((key == 0 || key == 1) &&
783
self.$object_dict[key] !== undefined){
784
for(const item of self.$object_dict[key]){
785
if((key == 0 && item[0] === false) ||
786
(key == 1 && item[0] === true)){
787
// replace value
788
item[1][0] = value
789
done = true
790
}
791
}
792
}
793
if(! done){
794
// new key
795
self.$numeric_dict[key] = [value, self.$order++]
796
}
800
case "boolean":
801
// true replaces 1 and false replaces 0
802
var num = key ? 1 : 0
803
if(self.$numeric_dict[num] !== undefined){
804
var order = self.$numeric_dict[num][1] // preserve order
805
self.$numeric_dict[num] = [value, order]
806
return
807
}
808
if(self.$object_dict[num] !== undefined){
809
self.$object_dict[num].push([key, [value, self.$order++]])
810
}else{
811
self.$object_dict[num] = [[key, [value, self.$order++]]]
812
}
832
// If $setitem is called from setdefault, don't test equality of key
833
// with any object
834
if($hash){
835
if(self.$object_dict[$hash] !== undefined){
839
}
840
self.$version++
841
return $N
842
}
843
var ix = rank(self, hash, key)
844
if(ix > -1){
845
// reset value
900
var $ = $B.args("fromkeys", 3, {cls: null, keys: null, value: null},
901
["cls", "keys", "value"], arguments, {value: _b_.None}, null, null),
913
if(klass === dict){dict.$setitem(res, key, value)}
914
else{$B.$getattr(res, "__setitem__")(key, value)}
925
var $ = $B.args("get", 3, {self: null, key: null, _default: null},
926
["self", "key", "_default"], arguments, {_default: $N}, null, null)
929
catch(err){
930
if(_b_.isinstance(err, _b_.KeyError)){return $._default}
931
else{throw err}
932
}
933
}
934
935
var dict_items = $B.make_view("dict_items", true)
936
dict_items.$iterator = $B.make_iterator_class("dict_itemiterator")
940
var _len = arguments.length - 1,
941
_msg = "items() takes no arguments (" + _len + " given)"
944
var items = to_list(self),
945
set_like = true
946
// Check if all values are hashable
947
for(var i = 0, len = items.length; i < len; i++){
948
try{
949
_b_.hash(items[i][1])
950
}catch(err){
951
set_like = false
952
break
953
}
954
}
955
var it = dict_items.$factory(to_list(self), set_like)
965
var _len = arguments.length - 1,
966
_msg = "keys() takes no arguments (" + _len + " given)"
976
var missing = {},
977
$ = $B.args("pop", 3, {self: null, key: null, _default: null},
978
["self", "key", "_default"], arguments, {_default: missing}, null, null),
1010
var $ = $B.args("setdefault", 3, {self: null, key: null, _default: null},
1011
["self", "key", "_default"], arguments, {_default: $N}, null, null),
1022
var hash = key.$hash
1023
key.$hash = undefined
1024
dict.$setitem(self, key, _default, hash)
1031
var $ = $B.args("update", 1, {"self": null}, ["self"], arguments,
1032
{}, "args", "kw"),
1033
self = $.self,
1034
args = $.args,
1035
kw = $.kw
1036
if(args.length > 0){
1037
var o = args[0]
1044
var _keys = _b_.list.$factory($B.$call($B.$getattr(o, "keys"))())
1045
for(var i = 0, len = _keys.length; i < len; i++){
1047
dict.$setitem(self, _keys[i], _value)
1048
}
1049
}else{
1050
var it = _b_.iter(o),
1051
i = 0
1052
while(true){
1053
try{
1054
var item = _b_.next(it)
1055
}catch(err){
1056
if(err.__class__ === _b_.StopIteration){break}
1057
throw err
1058
}
1059
try{
1060
key_value = _b_.list.$factory(item)
1061
}catch(err){
1062
throw _b_.TypeError.$factory("cannot convert dictionary" +
1063
" update sequence element #" + i + " to a sequence")
1064
}
1065
if(key_value.length !== 2){
1066
throw _b_.ValueError.$factory("dictionary update " +
1067
"sequence element #" + i + " has length " +
1068
key_value.length + "; 2 is required")
1069
}
1070
dict.$setitem(self, key_value[0], key_value[1])
1071
i++
1080
var dict_values = $B.make_view("dict_values")
1081
dict_values.$iterator = $B.make_iterator_class("dict_valueiterator")
1085
var _len = arguments.length - 1,
1086
_msg = "values() takes no arguments (" + _len + " given)"
1089
var values = to_list(self, 1)
1090
var it = dict_values.$factory(to_list(self, 1), false)
1097
var args = [res]
1098
for(var i = 0, len = arguments.length; i < len ; i++){
1099
args.push(arguments[i])
1100
}
1101
dict.__init__.apply(null, args)
1109
$B.empty_dict = function(){
1110
return {
1111
__class__: dict,
1112
$numeric_dict : {},
1113
$object_dict : {},
1114
$string_dict : {},
1115
$str_hash: {},
1121
// This must be done after set_func_names, otherwise dict.fromkeys doesn't
1122
// have the attribute $infos
1123
dict.fromkeys = _b_.classmethod.$factory(dict.fromkeys)
1124
1125
$B.getset_descriptor = $B.make_class("getset_descriptor",
1126
function(klass, attr){
1127
return {
1128
__class__: $B.getset_descriptor,
1130
cls: klass,
1131
attr: attr
1132
}
1133
}
1134
)
1135
1136
$B.getset_descriptor.__repr__ = $B.getset_descriptor.__str__ = function(self){
1137
return `<attribute '${self.attr}' of '${self.cls.$infos.__name__}' objects>`
1138
}
1139
1140
$B.set_func_names($B.getset_descriptor, "builtins")
1141
1146
// obj is a dictionary, with $string_dict table such that
1147
// obj.$string_dict[key] = [value, rank]
1148
// Transform it into an object with attribute $jsobj such that
1149
// res.$jsobj[key] = value
1150
var res = $B.obj_dict(dict.$to_obj(obj))
1160
throw _b_.TypeError.$factory("'mappingproxy' object does not support " +
1161
"item assignment")
1164
for(var attr in dict){
1165
if(mappingproxy[attr] !== undefined ||
1166
["__class__", "__mro__", "__new__", "__init__", "__delitem__",
1167
"clear", "fromkeys", "pop", "popitem", "setdefault",
1168
"update"].indexOf(attr) > -1){
1169
continue
1170
}
1171
if(typeof dict[attr] == "function"){
1172
mappingproxy[attr] = (function(key){
1173
return function(){
1174
return dict[key].apply(null, arguments)
1175
}
1176
})(attr)
1177
}else{
1178
mappingproxy[attr] = dict[attr]
1179
}
1180
}
1181
1204
if(klass !== undefined && klass.$native){
1205
throw _b_.AttributeError.$factory(klass.__name__ +