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
Oct 5, 2020
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
1223 lines (1104 sloc)
36.2 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}
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]]})
229
if(ix !== undefined){
230
return items.map(function(item){return item[ix]})
231
}else{
232
items.__class__ = _b_.tuple
233
return items.map(function(item){
234
item.__class__ = _b_.tuple; return item}
235
)
236
}
239
$B.dict_to_list = to_list // used in py_types.js
240
241
// Special version of __next__ for iterators on dict keys / values / items.
242
// Checks that the dictionary size didn't change during iteration.
243
function dict_iterator_next(self){
244
if(self.len_func() != self.len){
246
}
247
self.counter++
248
if(self.counter < self.items.length){
249
return self.items[self.counter]
260
si(left, _l[i][0], _l[i][1])
261
if(right.$version != right_version){
262
throw _b_.RuntimeError.$factory("dict mutated during update")
263
}
264
}
267
function rank(self, hash, key){
268
// Search if object key, with hash = hash(key), is in
269
// self.$object_dict
270
var pairs = self.$object_dict[hash]
271
if(pairs !== undefined){
272
for(var i = 0, len = pairs.length; i < len; i++){
273
if($B.rich_comp("__eq__", key, pairs[i][0])){
274
return i
275
}
276
}
277
}
278
return -1
279
}
280
287
dict.__class_getitem__ = function(cls, item){
288
// PEP 585
289
// Set as a classmethod at the end of this script, after $B.set_func_names()
290
if(! Array.isArray(item)){
291
item = [item]
292
}
293
return $B.GenericAlias.$factory(cls, item)
294
}
295
298
var $ = $B.args("__contains__", 2, {self: null, key: null},
299
["self", "key"], arguments, {}, null, null),
302
if(self.$is_namespace){key = $B.to_alias(key)} // issue 1244
303
304
if(self.$jsobj){
305
return self.$jsobj[key] !== undefined
306
}
312
return self.$numeric_dict[key] !== undefined
313
}
314
315
var hash = _b_.hash(key)
316
if(self.$str_hash[hash] !== undefined &&
317
$B.rich_comp("__eq__", key, self.$str_hash[hash])){return true}
318
if(self.$numeric_dict[hash] !== undefined &&
319
$B.rich_comp("__eq__", key, hash)){return true}
320
return rank(self, hash, key) > -1
325
var $ = $B.args("__eq__", 2, {self: null, arg: null},
326
["self", "arg"], arguments, {}, null, null),
335
switch(typeof arg){
336
case "string":
337
if(self.$string_dict[arg] === undefined){
357
if((ix = rank(self, hash, arg)) > -1){
358
self.$object_dict[hash].splice(ix, 1)
359
}else{
368
var $ = $B.args("__eq__", 2, {self: null, other: null},
369
["self", "other"], arguments, {}, null, null),
375
if(self.$jsobj){self = jsobj2dict(self.$jsobj)}
376
if(other.$jsobj){other = jsobj2dict(other.$jsobj)}
387
if(!$B.rich_comp("__eq__", other.$numeric_dict[k][0],
388
self.$numeric_dict[k][0])){
392
var pairs = other.$object_dict[k],
393
flag = false
394
for(var i = 0, len = pairs.length; i < len; i++){
395
if($B.rich_comp("__eq__", k, pairs[i][0]) &&
396
$B.rich_comp("__eq__", self.$numeric_dict[k],
397
pairs[i][1])){
398
flag = true
399
break
400
}
415
var pairs = self.$object_dict[hash]
416
// Get all (key, value) pairs in other that have the same hash
417
var other_pairs = []
418
if(other.$numeric_dict[hash] !== undefined){
419
other_pairs.push([hash, other.$numeric_dict[hash]])
420
}
422
other_pairs = other_pairs.concat(other.$object_dict[hash])
423
}
424
if(other_pairs.length == 0){
425
return false
426
}
427
for(var i = 0, len_i = pairs.length; i < len_i; i++){
428
var flag = false
429
var key = pairs[i][0],
431
for(var j = 0, len_j = other_pairs.length; j < len_j; j++){
432
if($B.rich_comp("__eq__", key, other_pairs[j][0]) &&
447
var $ = $B.args("__getitem__", 2, {self: null, arg: null},
448
["self", "arg"], arguments, {}, null, null),
464
465
switch(typeof arg){
466
case "string":
467
if(self.$string_dict[arg] !== undefined){
475
break
476
}
477
478
// since the key is more complex use 'default' method of getting item
479
534
if(item[0] != 0 && item[0] != 1){
535
self.$numeric_dict[item[0]] = [item[1], self.$order++]
536
self.$version++
537
break
538
}
551
for(var key in first){
552
self.$string_dict[key] = [first[key], self.$order++]
553
}
554
return _b_.None
555
}else if(first.$jsobj){
556
self.$jsobj = {}
557
for(var attr in first.$jsobj){
558
self.$jsobj[attr] = first.$jsobj[attr]
568
arguments, {}, "first", "second")
569
var args = $.first
570
if(args.length > 1){
571
throw _b_.TypeError.$factory("dict expected at most 1 argument" +
572
", got 2")
573
}else if(args.length == 1){
574
args = args[0]
575
if(args.__class__ === dict){
576
['$string_dict', '$str_hash', '$numeric_dict', '$object_dict'].
577
forEach(function(d){
578
for(key in args[d]){self[d][key] = args[d][key]}
579
})
583
var keys = $B.$getattr(args, "keys", null)
584
if(keys !== null){
585
var gi = $B.$getattr(args, "__getitem__", null)
586
if(gi !== null){
587
// has keys and __getitem__ : it's a mapping, iterate on
588
// keys and values
589
gi = $B.$call(gi)
590
var kiter = _b_.iter($B.$call(keys)())
591
while(true){
592
try{
593
var key = _b_.next(kiter),
594
value = gi(key)
595
dict.__setitem__(self, key, value)
596
}catch(err){
597
if(err.__class__ === _b_.StopIteration){
598
break
599
}
600
throw err
601
}
602
}
603
return $N
604
}
605
}
606
if(! Array.isArray(args)){
607
args = _b_.list.$factory(args)
608
}
609
// Form "dict([[key1, value1], [key2,value2], ...])"
635
dict.__ior__ = function(self, other){
636
// PEP 584
637
dict.update(self, other)
638
return self
639
}
640
649
for(var k in self.$numeric_dict){_count++}
650
for(var k in self.$string_dict){_count++}
651
for(var hash in self.$object_dict){
652
_count += self.$object_dict[hash].length
653
}
679
dict.__or__ = function(self, other){
680
// PEP 584
681
if(! _b_.isinstance(other, dict)){
682
return _b_.NotImplemented
683
}
684
var res = dict.copy(self)
685
dict.update(res, other)
686
return res
687
}
688
699
try{
700
res.push(repr(item[0]) + ": " + repr(item[1]))
701
}catch(err){
702
throw err
703
}
709
dict.__ror__ = function(self, other){
710
// PEP 584
711
if(! _b_.isinstance(other, dict)){
712
return _b_.NotImplemented
713
}
714
var res = dict.copy(other)
715
dict.update(res, self)
716
return res
717
}
718
721
["self", "key", "value"], arguments, {}, null, null)
722
return dict.$setitem($.self, $.key, $.value)
723
}
728
// If key is a string, set:
729
// - $string_dict[key] = [value, order] where "order" is an auto-increment
730
// unique id to keep track of insertion order
731
// - $str_hash[hash(key)] to key
732
//
733
// If key is a number, set $numeric_dict[key] = value
734
//
735
// If key is another object, compute its hash value:
736
// - if the hash is a key of $str_hash, and key == $str_hash[hash],
737
// replace $string_dict[$str_hash[hash]] by value
738
// - if the hash is a key of $numeric_dict, and hash == key, replace
739
// $numeric_dict[hash] by value
740
// - if the hash is a key of $object_dict: $object_dict[hash] is a list
741
// of [k, v] pairs. If key is equal to one of the "k", replace the
742
// matching v by value. Otherwise, add [key, value] to the list
743
// - else set $object_dict[hash] = [[key, value]]
744
//
745
// In all cases, increment attribute $version, used to detect dictionary
748
// Parameter $hash is only set if this method is called by setdefault.
749
// In this case the hash of key has already been computed and we
750
// know that the key is not present in the dictionary, so it's no
751
// use computing hash(key) again, nor testing equality of keys
760
// If class attribute __init__ or __new__ are reset,
761
// the factory function has to change
762
self.$jsobj.$factory = $B.$instance_creator(self.$jsobj)
763
}
764
}else{
772
if(self.$string_dict === undefined){
773
console.log("pas de string dict", self, key, value)
774
}
775
if(self.$string_dict[key] !== undefined){
776
self.$string_dict[key][0] = value
777
}else{
778
self.$string_dict[key] = [value, self.$order++]
779
self.$str_hash[str_hash(key)] = key
780
self.$version++
781
}
784
if(self.$numeric_dict[key] !== undefined){
785
// existing key: preserve order
786
self.$numeric_dict[key][0] = value
787
}else{
788
// special case for 0 and 1 if True or False are keys
789
var done = false
790
if((key == 0 || key == 1) &&
791
self.$object_dict[key] !== undefined){
792
for(const item of self.$object_dict[key]){
793
if((key == 0 && item[0] === false) ||
794
(key == 1 && item[0] === true)){
795
// replace value
796
item[1][0] = value
797
done = true
798
}
799
}
800
}
801
if(! done){
802
// new key
803
self.$numeric_dict[key] = [value, self.$order++]
804
}
808
case "boolean":
809
// true replaces 1 and false replaces 0
810
var num = key ? 1 : 0
811
if(self.$numeric_dict[num] !== undefined){
812
var order = self.$numeric_dict[num][1] // preserve order
813
self.$numeric_dict[num] = [value, order]
814
return
815
}
816
if(self.$object_dict[num] !== undefined){
817
self.$object_dict[num].push([key, [value, self.$order++]])
818
}else{
819
self.$object_dict[num] = [[key, [value, self.$order++]]]
820
}
840
// If $setitem is called from setdefault, don't test equality of key
841
// with any object
842
if($hash){
843
if(self.$object_dict[$hash] !== undefined){
847
}
848
self.$version++
849
return $N
850
}
851
var ix = rank(self, hash, key)
852
if(ix > -1){
853
// reset value
908
var $ = $B.args("fromkeys", 3, {cls: null, keys: null, value: null},
909
["cls", "keys", "value"], arguments, {value: _b_.None}, null, null),
921
if(klass === dict){dict.$setitem(res, key, value)}
922
else{$B.$getattr(res, "__setitem__")(key, value)}
933
var $ = $B.args("get", 3, {self: null, key: null, _default: null},
934
["self", "key", "_default"], arguments, {_default: $N}, null, null)
937
catch(err){
938
if(_b_.isinstance(err, _b_.KeyError)){return $._default}
939
else{throw err}
940
}
941
}
942
943
var dict_items = $B.make_view("dict_items", true)
944
dict_items.$iterator = $B.make_iterator_class("dict_itemiterator")
948
var _len = arguments.length - 1,
949
_msg = "items() takes no arguments (" + _len + " given)"
952
var items = to_list(self),
953
set_like = true
954
// Check if all values are hashable
955
for(var i = 0, len = items.length; i < len; i++){
956
try{
957
_b_.hash(items[i][1])
958
}catch(err){
959
set_like = false
960
break
961
}
962
}
963
var it = dict_items.$factory(to_list(self), set_like)
973
var _len = arguments.length - 1,
974
_msg = "keys() takes no arguments (" + _len + " given)"
984
var missing = {},
985
$ = $B.args("pop", 3, {self: null, key: null, _default: null},
986
["self", "key", "_default"], arguments, {_default: missing}, null, null),
1018
var $ = $B.args("setdefault", 3, {self: null, key: null, _default: null},
1019
["self", "key", "_default"], arguments, {_default: $N}, null, null),
1030
var hash = key.$hash
1031
key.$hash = undefined
1032
dict.$setitem(self, key, _default, hash)
1039
var $ = $B.args("update", 1, {"self": null}, ["self"], arguments,
1040
{}, "args", "kw"),
1041
self = $.self,
1042
args = $.args,
1043
kw = $.kw
1044
if(args.length > 0){
1045
var o = args[0]
1052
var _keys = _b_.list.$factory($B.$call($B.$getattr(o, "keys"))())
1053
for(var i = 0, len = _keys.length; i < len; i++){
1055
dict.$setitem(self, _keys[i], _value)
1056
}
1057
}else{
1058
var it = _b_.iter(o),
1059
i = 0
1060
while(true){
1061
try{
1062
var item = _b_.next(it)
1063
}catch(err){
1064
if(err.__class__ === _b_.StopIteration){break}
1065
throw err
1066
}
1067
try{
1068
key_value = _b_.list.$factory(item)
1069
}catch(err){
1070
throw _b_.TypeError.$factory("cannot convert dictionary" +
1071
" update sequence element #" + i + " to a sequence")
1072
}
1073
if(key_value.length !== 2){
1074
throw _b_.ValueError.$factory("dictionary update " +
1075
"sequence element #" + i + " has length " +
1076
key_value.length + "; 2 is required")
1077
}
1078
dict.$setitem(self, key_value[0], key_value[1])
1079
i++
1088
var dict_values = $B.make_view("dict_values")
1089
dict_values.$iterator = $B.make_iterator_class("dict_valueiterator")
1093
var _len = arguments.length - 1,
1094
_msg = "values() takes no arguments (" + _len + " given)"
1097
var values = to_list(self, 1)
1098
var it = dict_values.$factory(to_list(self, 1), false)
1105
var args = [res]
1106
for(var i = 0, len = arguments.length; i < len ; i++){
1107
args.push(arguments[i])
1108
}
1109
dict.__init__.apply(null, args)
1119
$B.empty_dict = function(){
1120
return {
1121
__class__: dict,
1122
$numeric_dict : {},
1123
$object_dict : {},
1124
$string_dict : {},
1125
$str_hash: {},
1131
// This must be done after set_func_names, otherwise dict.fromkeys doesn't
1132
// have the attribute $infos
1133
dict.fromkeys = _b_.classmethod.$factory(dict.fromkeys)
1134
1135
$B.getset_descriptor = $B.make_class("getset_descriptor",
1136
function(klass, attr){
1137
return {
1138
__class__: $B.getset_descriptor,
1140
cls: klass,
1141
attr: attr
1142
}
1143
}
1144
)
1145
1146
$B.getset_descriptor.__repr__ = $B.getset_descriptor.__str__ = function(self){
1147
return `<attribute '${self.attr}' of '${self.cls.$infos.__name__}' objects>`
1148
}
1149
1150
$B.set_func_names($B.getset_descriptor, "builtins")
1151
1156
// obj is a dictionary, with $string_dict table such that
1157
// obj.$string_dict[key] = [value, rank]
1158
// Transform it into an object with attribute $jsobj such that
1159
// res.$jsobj[key] = value
1160
var res = $B.obj_dict(dict.$to_obj(obj))
1170
throw _b_.TypeError.$factory("'mappingproxy' object does not support " +
1171
"item assignment")
1174
for(var attr in dict){
1175
if(mappingproxy[attr] !== undefined ||
1176
["__class__", "__mro__", "__new__", "__init__", "__delitem__",
1177
"clear", "fromkeys", "pop", "popitem", "setdefault",
1178
"update"].indexOf(attr) > -1){
1179
continue
1180
}
1181
if(typeof dict[attr] == "function"){
1182
mappingproxy[attr] = (function(key){
1183
return function(){
1184
return dict[key].apply(null, arguments)
1185
}
1186
})(attr)
1187
}else{
1188
mappingproxy[attr] = dict[attr]
1189
}
1190
}
1191
1214
if(klass !== undefined && klass.$native){
1215
throw _b_.AttributeError.$factory(klass.__name__ +