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 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
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
1184 lines (1065 sloc)
34.6 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]
262
si(left, _l[i][0], _l[i][1])
263
if(right.$version != right_version){
264
throw _b_.RuntimeError.$factory("dict mutated during update")
265
}
266
}
269
function rank(self, hash, key){
270
// Search if object key, with hash = hash(key), is in
271
// self.$object_dict
272
var pairs = self.$object_dict[hash]
273
if(pairs !== undefined){
274
for(var i = 0, len = pairs.length; i < len; i++){
275
if($B.rich_comp("__eq__", key, pairs[i][0])){
276
return i
277
}
278
}
279
}
280
return -1
281
}
282
291
var $ = $B.args("__contains__", 2, {self: null, key: null},
292
["self", "key"], arguments, {}, null, null),
295
if(self.$is_namespace){key = $B.to_alias(key)} // issue 1244
296
297
if(self.$jsobj){
298
return self.$jsobj[key] !== undefined
299
}
305
return self.$numeric_dict[key] !== undefined
306
}
307
308
var hash = _b_.hash(key)
309
if(self.$str_hash[hash] !== undefined &&
310
$B.rich_comp("__eq__", key, self.$str_hash[hash])){return true}
311
if(self.$numeric_dict[hash] !== undefined &&
312
$B.rich_comp("__eq__", key, hash)){return true}
313
return rank(self, hash, key) > -1
318
var $ = $B.args("__eq__", 2, {self: null, arg: null},
319
["self", "arg"], arguments, {}, null, null),
328
switch(typeof arg){
329
case "string":
330
if(self.$string_dict[arg] === undefined){
350
if((ix = rank(self, hash, arg)) > -1){
351
self.$object_dict[hash].splice(ix, 1)
352
}else{
361
var $ = $B.args("__eq__", 2, {self: null, other: null},
362
["self", "other"], arguments, {}, null, null),
368
if(self.$jsobj){self = jsobj2dict(self.$jsobj)}
369
if(other.$jsobj){other = jsobj2dict(other.$jsobj)}
380
if(!$B.rich_comp("__eq__", other.$numeric_dict[k][0],
381
self.$numeric_dict[k][0])){
385
var pairs = other.$object_dict[k],
386
flag = false
387
for(var i = 0, len = pairs.length; i < len; i++){
388
if($B.rich_comp("__eq__", k, pairs[i][0]) &&
389
$B.rich_comp("__eq__", self.$numeric_dict[k],
390
pairs[i][1])){
391
flag = true
392
break
393
}
408
var pairs = self.$object_dict[hash]
409
// Get all (key, value) pairs in other that have the same hash
410
var other_pairs = []
411
if(other.$numeric_dict[hash] !== undefined){
412
other_pairs.push([hash, other.$numeric_dict[hash]])
413
}
415
other_pairs = other_pairs.concat(other.$object_dict[hash])
416
}
417
if(other_pairs.length == 0){
418
return false
419
}
420
for(var i = 0, len_i = pairs.length; i < len_i; i++){
421
var flag = false
422
var key = pairs[i][0],
424
for(var j = 0, len_j = other_pairs.length; j < len_j; j++){
425
if($B.rich_comp("__eq__", key, other_pairs[j][0]) &&
440
var $ = $B.args("__getitem__", 2, {self: null, arg: null},
441
["self", "arg"], arguments, {}, null, null),
457
458
switch(typeof arg){
459
case "string":
460
if(self.$string_dict[arg] !== undefined){
468
break
469
}
470
471
// since the key is more complex use 'default' method of getting item
472
542
for(var key in first){
543
self.$string_dict[key] = [first[key], self.$order++]
544
}
545
return _b_.None
546
}else if(first.$jsobj){
547
self.$jsobj = {}
548
for(var attr in first.$jsobj){
549
self.$jsobj[attr] = first.$jsobj[attr]
559
arguments, {}, "first", "second")
560
var args = $.first
561
if(args.length > 1){
562
throw _b_.TypeError.$factory("dict expected at most 1 argument" +
563
", got 2")
564
}else if(args.length == 1){
565
args = args[0]
566
if(args.__class__ === dict){
567
['$string_dict', '$str_hash', '$numeric_dict', '$object_dict'].
568
forEach(function(d){
569
for(key in args[d]){self[d][key] = args[d][key]}
570
})
574
var keys = $B.$getattr(args, "keys", null)
575
if(keys !== null){
576
var gi = $B.$getattr(args, "__getitem__", null)
577
if(gi !== null){
578
// has keys and __getitem__ : it's a mapping, iterate on
579
// keys and values
580
gi = $B.$call(gi)
581
var kiter = _b_.iter($B.$call(keys)())
582
while(true){
583
try{
584
var key = _b_.next(kiter),
585
value = gi(key)
586
dict.__setitem__(self, key, value)
587
}catch(err){
588
if(err.__class__ === _b_.StopIteration){
589
break
590
}
591
throw err
592
}
593
}
594
return $N
595
}
596
}
597
if(! Array.isArray(args)){
598
args = _b_.list.$factory(args)
599
}
600
// Form "dict([[key1, value1], [key2,value2], ...])"
626
dict.__ior__ = function(self, other){
627
// PEP 584
628
dict.update(self, other)
629
return self
630
}
631
640
for(var k in self.$numeric_dict){_count++}
641
for(var k in self.$string_dict){_count++}
642
for(var hash in self.$object_dict){
643
_count += self.$object_dict[hash].length
644
}
670
dict.__or__ = function(self, other){
671
// PEP 584
672
if(! _b_.isinstance(other, dict)){
673
return _b_.NotImplemented
674
}
675
var res = dict.copy(self)
676
dict.update(res, other)
677
return res
678
}
679
690
try{
691
res.push(repr(item[0]) + ": " + repr(item[1]))
692
}catch(err){
693
throw err
694
}
700
dict.__ror__ = function(self, other){
701
// PEP 584
702
if(! _b_.isinstance(other, dict)){
703
return _b_.NotImplemented
704
}
705
var res = dict.copy(other)
706
dict.update(res, self)
707
return res
708
}
709
712
["self", "key", "value"], arguments, {}, null, null)
713
return dict.$setitem($.self, $.key, $.value)
714
}
719
// If key is a string, set:
720
// - $string_dict[key] = [value, order] where "order" is an auto-increment
721
// unique id to keep track of insertion order
722
// - $str_hash[hash(key)] to key
723
//
724
// If key is a number, set $numeric_dict[key] = value
725
//
726
// If key is another object, compute its hash value:
727
// - if the hash is a key of $str_hash, and key == $str_hash[hash],
728
// replace $string_dict[$str_hash[hash]] by value
729
// - if the hash is a key of $numeric_dict, and hash == key, replace
730
// $numeric_dict[hash] by value
731
// - if the hash is a key of $object_dict: $object_dict[hash] is a list
732
// of [k, v] pairs. If key is equal to one of the "k", replace the
733
// matching v by value. Otherwise, add [key, value] to the list
734
// - else set $object_dict[hash] = [[key, value]]
735
//
736
// In all cases, increment attribute $version, used to detect dictionary
739
// Parameter $hash is only set if this method is called by setdefault.
740
// In this case the hash of key has already been computed and we
741
// know that the key is not present in the dictionary, so it's no
742
// use computing hash(key) again, nor testing equality of keys
751
// If class attribute __init__ or __new__ are reset,
752
// the factory function has to change
753
self.$jsobj.$factory = $B.$instance_creator(self.$jsobj)
754
}
755
}else{
763
if(self.$string_dict === undefined){
764
console.log("pas de string dict", self, key, value)
765
}
766
if(self.$string_dict[key] !== undefined){
767
self.$string_dict[key][0] = value
768
}else{
769
self.$string_dict[key] = [value, self.$order++]
770
self.$str_hash[str_hash(key)] = key
771
self.$version++
772
}
775
if(self.$numeric_dict[key] !== undefined){
776
// existing key: preserve order
777
self.$numeric_dict[key][0] = value
778
}else{
779
// new key
780
self.$numeric_dict[key] = [value, self.$order++]
781
self.$version++
782
}
803
// If $setitem is called from setdefault, don't test equality of key
804
// with any object
805
if($hash){
806
if(self.$object_dict[$hash] !== undefined){
810
}
811
self.$version++
812
return $N
813
}
814
var ix = rank(self, hash, key)
815
if(ix > -1){
816
// reset value
871
var $ = $B.args("fromkeys", 3, {cls: null, keys: null, value: null},
872
["cls", "keys", "value"], arguments, {value: _b_.None}, null, null),
884
if(klass === dict){dict.$setitem(res, key, value)}
885
else{$B.$getattr(res, "__setitem__")(key, value)}
896
var $ = $B.args("get", 3, {self: null, key: null, _default: null},
897
["self", "key", "_default"], arguments, {_default: $N}, null, null)
900
catch(err){
901
if(_b_.isinstance(err, _b_.KeyError)){return $._default}
902
else{throw err}
903
}
904
}
905
906
var dict_items = $B.make_view("dict_items", true)
907
dict_items.$iterator = $B.make_iterator_class("dict_itemiterator")
911
var _len = arguments.length - 1,
912
_msg = "items() takes no arguments (" + _len + " given)"
915
var items = to_list(self),
916
set_like = true
917
// Check if all values are hashable
918
for(var i = 0, len = items.length; i < len; i++){
919
try{
920
_b_.hash(items[i][1])
921
}catch(err){
922
set_like = false
923
break
924
}
925
}
926
var it = dict_items.$factory(to_list(self), set_like)
936
var _len = arguments.length - 1,
937
_msg = "keys() takes no arguments (" + _len + " given)"
947
var missing = {},
948
$ = $B.args("pop", 3, {self: null, key: null, _default: null},
949
["self", "key", "_default"], arguments, {_default: missing}, null, null),
981
var $ = $B.args("setdefault", 3, {self: null, key: null, _default: null},
982
["self", "key", "_default"], arguments, {_default: $N}, null, null),
993
var hash = key.$hash
994
key.$hash = undefined
995
dict.$setitem(self, key, _default, hash)
1002
var $ = $B.args("update", 1, {"self": null}, ["self"], arguments,
1003
{}, "args", "kw"),
1004
self = $.self,
1005
args = $.args,
1006
kw = $.kw
1007
if(args.length > 0){
1008
var o = args[0]
1015
var _keys = _b_.list.$factory($B.$call($B.$getattr(o, "keys"))())
1016
for(var i = 0, len = _keys.length; i < len; i++){
1018
dict.$setitem(self, _keys[i], _value)
1019
}
1020
}else{
1021
var it = _b_.iter(o),
1022
i = 0
1023
while(true){
1024
try{
1025
var item = _b_.next(it)
1026
}catch(err){
1027
if(err.__class__ === _b_.StopIteration){break}
1028
throw err
1029
}
1030
try{
1031
key_value = _b_.list.$factory(item)
1032
}catch(err){
1033
throw _b_.TypeError.$factory("cannot convert dictionary" +
1034
" update sequence element #" + i + " to a sequence")
1035
}
1036
if(key_value.length !== 2){
1037
throw _b_.ValueError.$factory("dictionary update " +
1038
"sequence element #" + i + " has length " +
1039
key_value.length + "; 2 is required")
1040
}
1041
dict.$setitem(self, key_value[0], key_value[1])
1042
i++
1051
var dict_values = $B.make_view("dict_values")
1052
dict_values.$iterator = $B.make_iterator_class("dict_valueiterator")
1056
var _len = arguments.length - 1,
1057
_msg = "values() takes no arguments (" + _len + " given)"
1060
var values = to_list(self, 1)
1061
var it = dict_values.$factory(to_list(self, 1), false)
1068
var args = [res]
1069
for(var i = 0, len = arguments.length; i < len ; i++){
1070
args.push(arguments[i])
1071
}
1072
dict.__init__.apply(null, args)
1080
$B.empty_dict = function(){
1081
return {
1082
__class__: dict,
1083
$numeric_dict : {},
1084
$object_dict : {},
1085
$string_dict : {},
1086
$str_hash: {},
1092
// This must be done after set_func_names, otherwise dict.fromkeys doesn't
1093
// have the attribute $infos
1094
dict.fromkeys = _b_.classmethod.$factory(dict.fromkeys)
1095
1096
$B.getset_descriptor = $B.make_class("getset_descriptor",
1097
function(klass, attr){
1098
return {
1099
__class__: $B.getset_descriptor,
1101
cls: klass,
1102
attr: attr
1103
}
1104
}
1105
)
1106
1107
$B.getset_descriptor.__repr__ = $B.getset_descriptor.__str__ = function(self){
1108
return `<attribute '${self.attr}' of '${self.cls.$infos.__name__}' objects>`
1109
}
1110
1111
$B.set_func_names($B.getset_descriptor, "builtins")
1112
1117
// obj is a dictionary, with $string_dict table such that
1118
// obj.$string_dict[key] = [value, rank]
1119
// Transform it into an object with attribute $jsobj such that
1120
// res.$jsobj[key] = value
1121
var res = $B.obj_dict(dict.$to_obj(obj))
1131
throw _b_.TypeError.$factory("'mappingproxy' object does not support " +
1132
"item assignment")
1135
for(var attr in dict){
1136
if(mappingproxy[attr] !== undefined ||
1137
["__class__", "__mro__", "__new__", "__init__", "__delitem__",
1138
"clear", "fromkeys", "pop", "popitem", "setdefault",
1139
"update"].indexOf(attr) > -1){
1140
continue
1141
}
1142
if(typeof dict[attr] == "function"){
1143
mappingproxy[attr] = (function(key){
1144
return function(){
1145
return dict[key].apply(null, arguments)
1146
}
1147
})(attr)
1148
}else{
1149
mappingproxy[attr] = dict[attr]
1150
}
1151
}
1152
1175
if(klass !== undefined && klass.$native){
1176
throw _b_.AttributeError.$factory(klass.__name__ +