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
Dec 10, 2018
Dec 10, 2018
Mar 7, 2018
Feb 26, 2018
Feb 1, 2020
Dec 10, 2018
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
Jul 24, 2021
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
1243 lines (1123 sloc)
36.5 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
178
dict.$to_obj = function(d){
179
// Function applied to dictionary that only have string keys,
180
// return a Javascript objects with the kays mapped to the value,
181
// excluding the insertion rank
182
var res = {}
183
for(var key in d.$string_dict){
184
res[key] = d.$string_dict[key][0]
185
}
186
return res
187
}
188
198
if(val === undefined){val = _b_.NotImplemented}
199
else if(val === null){val = $N}
204
for(var k in d.$numeric_dict){
205
items.push([parseFloat(k), d.$numeric_dict[k]])
206
}
212
for(var k in d.$object_dict){
213
d.$object_dict[k].forEach(function(item){
214
items.push(item)
215
})
216
}
218
// sort by insertion order
219
items.sort(function(a, b){
220
return a[1][1] - b[1][1]
221
})
222
items = items.map(function(item){return [item[0], item[1][0]]})
227
}else{
228
items.__class__ = _b_.tuple
229
return items.map(function(item){
230
item.__class__ = _b_.tuple; return item}
231
)
232
}
243
si(left, _l[i][0], _l[i][1])
244
if(right.$version != right_version){
245
throw _b_.RuntimeError.$factory("dict mutated during update")
246
}
247
}
250
function rank(self, hash, key){
251
// Search if object key, with hash = hash(key), is in
252
// self.$object_dict
253
var pairs = self.$object_dict[hash]
254
if(pairs !== undefined){
255
for(var i = 0, len = pairs.length; i < len; i++){
256
if($B.rich_comp("__eq__", key, pairs[i][0])){
257
return i
258
}
259
}
260
}
261
return -1
262
}
263
270
dict.__class_getitem__ = function(cls, item){
271
// PEP 585
272
// Set as a classmethod at the end of this script, after $B.set_func_names()
273
if(! Array.isArray(item)){
274
item = [item]
275
}
276
return $B.GenericAlias.$factory(cls, item)
277
}
278
280
var $ = $B.args("__contains__", 2, {self: null, key: null},
281
["self", "key"], arguments, {}, null, null),
296
return self.$numeric_dict[key] !== undefined
297
}
298
299
var hash = _b_.hash(key)
300
if(self.$str_hash[hash] !== undefined &&
313
var $ = $B.args("__eq__", 2, {self: null, arg: null},
314
["self", "arg"], arguments, {}, null, null),
323
switch(typeof arg){
324
case "string":
325
if(self.$string_dict[arg] === undefined){
345
if((ix = rank(self, hash, arg)) > -1){
346
self.$object_dict[hash].splice(ix, 1)
347
}else{
356
var $ = $B.args("__eq__", 2, {self: null, other: null},
357
["self", "other"], arguments, {}, null, null),
363
if(self.$jsobj){self = jsobj2dict(self.$jsobj)}
364
if(other.$jsobj){other = jsobj2dict(other.$jsobj)}
375
if(!$B.rich_comp("__eq__", other.$numeric_dict[k][0],
376
self.$numeric_dict[k][0])){
380
var pairs = other.$object_dict[k],
381
flag = false
382
for(var i = 0, len = pairs.length; i < len; i++){
383
if($B.rich_comp("__eq__", k, pairs[i][0]) &&
384
$B.rich_comp("__eq__", self.$numeric_dict[k],
385
pairs[i][1])){
386
flag = true
387
break
388
}
403
var pairs = self.$object_dict[hash]
404
// Get all (key, value) pairs in other that have the same hash
405
var other_pairs = []
406
if(other.$numeric_dict[hash] !== undefined){
407
other_pairs.push([hash, other.$numeric_dict[hash]])
408
}
410
other_pairs = other_pairs.concat(other.$object_dict[hash])
411
}
412
if(other_pairs.length == 0){
413
return false
414
}
415
for(var i = 0, len_i = pairs.length; i < len_i; i++){
416
var flag = false
417
var key = pairs[i][0],
419
for(var j = 0, len_j = other_pairs.length; j < len_j; j++){
420
if($B.rich_comp("__eq__", key, other_pairs[j][0]) &&
435
var $ = $B.args("__getitem__", 2, {self: null, arg: null},
436
["self", "arg"], arguments, {}, null, null),
465
break
466
}
467
468
// since the key is more complex use 'default' method of getting item
469
494
if(! ignore_missing){
495
if(self.__class__ !== dict && ! ignore_missing){
496
try{
497
var missing_method = getattr(self.__class__, "__missing__",
498
_b_.None)
499
}catch(err){
500
console.log(err)
519
if(item.length != 2){
520
throw _b_.ValueError.$factory("dictionary " +
521
`update sequence element #${i} has length 1; 2 is required`)
522
}
530
if(item[0] != 0 && item[0] != 1){
531
self.$numeric_dict[item[0]] = [item[1], self.$order++]
532
self.$version++
533
break
534
}
548
for(var key in first){
549
self.$string_dict[key] = [first[key], self.$order++]
550
}
551
return _b_.None
552
}else if(first.$jsobj){
553
self.$jsobj = {}
554
for(var attr in first.$jsobj){
555
self.$jsobj[attr] = first.$jsobj[attr]
565
arguments, {}, "first", "second")
566
var args = $.first
567
if(args.length > 1){
568
throw _b_.TypeError.$factory("dict expected at most 1 argument" +
569
", got 2")
570
}else if(args.length == 1){
571
args = args[0]
572
if(args.__class__ === dict){
573
['$string_dict', '$str_hash', '$numeric_dict', '$object_dict'].
574
forEach(function(d){
575
for(key in args[d]){self[d][key] = args[d][key]}
576
})
580
var keys = $B.$getattr(args, "keys", null)
581
if(keys !== null){
582
var gi = $B.$getattr(args, "__getitem__", null)
583
if(gi !== null){
584
// has keys and __getitem__ : it's a mapping, iterate on
585
// keys and values
586
gi = $B.$call(gi)
587
var kiter = _b_.iter($B.$call(keys)())
588
while(true){
589
try{
590
var key = _b_.next(kiter),
591
value = gi(key)
592
dict.__setitem__(self, key, value)
593
}catch(err){
594
if(err.__class__ === _b_.StopIteration){
595
break
596
}
597
throw err
598
}
599
}
600
return $N
601
}
602
}
603
if(! Array.isArray(args)){
604
args = _b_.list.$factory(args)
605
}
606
// Form "dict([[key1, value1], [key2,value2], ...])"
632
dict.__ior__ = function(self, other){
633
// PEP 584
634
dict.update(self, other)
635
return self
636
}
637
646
for(var k in self.$numeric_dict){_count++}
647
for(var k in self.$string_dict){_count++}
648
for(var hash in self.$object_dict){
649
_count += self.$object_dict[hash].length
650
}
676
dict.__or__ = function(self, other){
677
// PEP 584
678
if(! _b_.isinstance(other, dict)){
679
return _b_.NotImplemented
680
}
681
var res = dict.copy(self)
682
dict.update(res, other)
683
return res
684
}
685
686
function __newobj__(){
687
// __newobj__ is called with a generator as only argument
688
var $ = $B.args('__newobj__', 0, {}, [], arguments, {}, 'args', null),
689
args = $.args
690
var res = $B.empty_dict()
691
res.__class__ = args[0]
692
return res
693
}
694
695
dict.__reduce_ex__ = function(self, protocol){
696
return $B.fast_tuple([
697
__newobj__,
698
$B.fast_tuple([self.__class__]),
699
_b_.None,
700
_b_.None,
701
dict.items(self)])
702
}
703
715
try{
716
res.push(repr(item[0]) + ": " + repr(item[1]))
717
}catch(err){
718
throw err
719
}
725
dict.__ror__ = function(self, other){
726
// PEP 584
727
if(! _b_.isinstance(other, dict)){
728
return _b_.NotImplemented
729
}
730
var res = dict.copy(other)
731
dict.update(res, self)
732
return res
733
}
734
737
["self", "key", "value"], arguments, {}, null, null)
738
return dict.$setitem($.self, $.key, $.value)
739
}
744
// If key is a string, set:
745
// - $string_dict[key] = [value, order] where "order" is an auto-increment
746
// unique id to keep track of insertion order
747
// - $str_hash[hash(key)] to key
748
//
749
// If key is a number, set $numeric_dict[key] = value
750
//
751
// If key is another object, compute its hash value:
752
// - if the hash is a key of $str_hash, and key == $str_hash[hash],
753
// replace $string_dict[$str_hash[hash]] by value
754
// - if the hash is a key of $numeric_dict, and hash == key, replace
755
// $numeric_dict[hash] by value
756
// - if the hash is a key of $object_dict: $object_dict[hash] is a list
757
// of [k, v] pairs. If key is equal to one of the "k", replace the
758
// matching v by value. Otherwise, add [key, value] to the list
759
// - else set $object_dict[hash] = [[key, value]]
760
//
761
// In all cases, increment attribute $version, used to detect dictionary
764
// Parameter $hash is only set if this method is called by setdefault.
765
// In this case the hash of key has already been computed and we
766
// know that the key is not present in the dictionary, so it's no
767
// use computing hash(key) again, nor testing equality of keys
776
// If class attribute __init__ or __new__ are reset,
777
// the factory function has to change
778
self.$jsobj.$factory = $B.$instance_creator(self.$jsobj)
779
}
780
}else{
788
if(self.$string_dict === undefined){
789
console.log("pas de string dict", self, key, value)
790
}
791
if(self.$string_dict[key] !== undefined){
792
self.$string_dict[key][0] = value
793
}else{
794
self.$string_dict[key] = [value, self.$order++]
795
self.$str_hash[str_hash(key)] = key
796
self.$version++
797
}
800
if(self.$numeric_dict[key] !== undefined){
801
// existing key: preserve order
802
self.$numeric_dict[key][0] = value
803
}else{
804
// special case for 0 and 1 if True or False are keys
805
var done = false
806
if((key == 0 || key == 1) &&
807
self.$object_dict[key] !== undefined){
808
for(const item of self.$object_dict[key]){
809
if((key == 0 && item[0] === false) ||
810
(key == 1 && item[0] === true)){
811
// replace value
812
item[1][0] = value
813
done = true
814
}
815
}
816
}
817
if(! done){
818
// new key
819
self.$numeric_dict[key] = [value, self.$order++]
820
}
824
case "boolean":
825
// true replaces 1 and false replaces 0
826
var num = key ? 1 : 0
827
if(self.$numeric_dict[num] !== undefined){
828
var order = self.$numeric_dict[num][1] // preserve order
829
self.$numeric_dict[num] = [value, order]
830
return
831
}
832
if(self.$object_dict[num] !== undefined){
833
self.$object_dict[num].push([key, [value, self.$order++]])
834
}else{
835
self.$object_dict[num] = [[key, [value, self.$order++]]]
836
}
856
// If $setitem is called from setdefault, don't test equality of key
857
// with any object
858
if($hash){
859
if(self.$object_dict[$hash] !== undefined){
863
}
864
self.$version++
865
return $N
866
}
867
var ix = rank(self, hash, key)
868
if(ix > -1){
869
// reset value
920
var $ = $B.args("fromkeys", 3, {cls: null, keys: null, value: null},
921
["cls", "keys", "value"], arguments, {value: _b_.None}, null, null),
933
if(klass === dict){dict.$setitem(res, key, value)}
934
else{$B.$getattr(res, "__setitem__")(key, value)}
945
var $ = $B.args("get", 3, {self: null, key: null, _default: null},
946
["self", "key", "_default"], arguments, {_default: $N}, null, null)
948
try{
949
// call $getitem with ignore_missign set to true
950
return dict.$getitem($.self, $.key, true)
951
}catch(err){
952
if(_b_.isinstance(err, _b_.KeyError)){return $._default}
953
else{throw err}
954
}
955
}
956
957
var dict_items = $B.make_view("dict_items", true)
958
dict_items.$iterator = $B.make_iterator_class("dict_itemiterator")
962
var _len = arguments.length - 1,
963
_msg = "items() takes no arguments (" + _len + " given)"
966
var items = to_list(self),
967
set_like = true
968
// Check if all values are hashable
969
for(var i = 0, len = items.length; i < len; i++){
970
try{
971
_b_.hash(items[i][1])
972
}catch(err){
973
set_like = false
974
break
975
}
976
}
977
var values = to_list(self)
978
var it = dict_items.$factory(self, values, set_like)
979
it.dict_version = self.$version
989
var _len = arguments.length - 1,
990
_msg = "keys() takes no arguments (" + _len + " given)"
993
var it = dict_keys.$factory(self, to_list(self, 0), true)
994
it.dict_version = self.$version
1000
var missing = {},
1001
$ = $B.args("pop", 3, {self: null, key: null, _default: null},
1002
["self", "key", "_default"], arguments, {_default: missing}, null, null),
1034
var $ = $B.args("setdefault", 3, {self: null, key: null, _default: null},
1035
["self", "key", "_default"], arguments, {_default: $N}, null, null),
1039
try{
1040
// Pass 3rd argument to dict.$getitem to avoid using __missing__
1041
// Cf. issue #1598
1042
return dict.$getitem(self, key, true)
1043
}catch(err){
1048
var hash = key.$hash
1049
key.$hash = undefined
1050
dict.$setitem(self, key, _default, hash)
1057
var $ = $B.args("update", 1, {"self": null}, ["self"], arguments,
1058
{}, "args", "kw"),
1059
self = $.self,
1060
args = $.args,
1061
kw = $.kw
1062
if(args.length > 0){
1063
var o = args[0]
1070
var _keys = _b_.list.$factory($B.$call($B.$getattr(o, "keys"))())
1071
for(var i = 0, len = _keys.length; i < len; i++){
1073
dict.$setitem(self, _keys[i], _value)
1074
}
1075
}else{
1076
var it = _b_.iter(o),
1077
i = 0
1078
while(true){
1079
try{
1080
var item = _b_.next(it)
1081
}catch(err){
1082
if(err.__class__ === _b_.StopIteration){break}
1083
throw err
1084
}
1085
try{
1086
key_value = _b_.list.$factory(item)
1087
}catch(err){
1088
throw _b_.TypeError.$factory("cannot convert dictionary" +
1089
" update sequence element #" + i + " to a sequence")
1090
}
1091
if(key_value.length !== 2){
1092
throw _b_.ValueError.$factory("dictionary update " +
1093
"sequence element #" + i + " has length " +
1094
key_value.length + "; 2 is required")
1095
}
1096
dict.$setitem(self, key_value[0], key_value[1])
1097
i++
1106
var dict_values = $B.make_view("dict_values")
1107
dict_values.$iterator = $B.make_iterator_class("dict_valueiterator")
1111
var _len = arguments.length - 1,
1112
_msg = "values() takes no arguments (" + _len + " given)"
1116
var it = dict_values.$factory(self, values, false)
1117
it.dict_version = self.$version
1123
var args = [res]
1124
for(var i = 0, len = arguments.length; i < len ; i++){
1125
args.push(arguments[i])
1126
}
1127
dict.__init__.apply(null, args)
1137
$B.empty_dict = function(){
1138
return {
1139
__class__: dict,
1140
$numeric_dict : {},
1141
$object_dict : {},
1142
$string_dict : {},
1143
$str_hash: {},
1149
// This must be done after set_func_names, otherwise dict.fromkeys doesn't
1150
// have the attribute $infos
1151
dict.fromkeys = _b_.classmethod.$factory(dict.fromkeys)
1152
1153
$B.getset_descriptor = $B.make_class("getset_descriptor",
1154
function(klass, attr){
1155
return {
1156
__class__: $B.getset_descriptor,
1158
cls: klass,
1159
attr: attr
1160
}
1161
}
1162
)
1163
1164
$B.getset_descriptor.__repr__ = $B.getset_descriptor.__str__ = function(self){
1165
return `<attribute '${self.attr}' of '${self.cls.$infos.__name__}' objects>`
1166
}
1167
1168
$B.set_func_names($B.getset_descriptor, "builtins")
1169
1174
// obj is a dictionary, with $string_dict table such that
1175
// obj.$string_dict[key] = [value, rank]
1176
// Transform it into an object with attribute $jsobj such that
1177
// res.$jsobj[key] = value
1178
var res = $B.obj_dict(dict.$to_obj(obj))
1190
throw _b_.TypeError.$factory("'mappingproxy' object does not support " +
1191
"item assignment")
1194
for(var attr in dict){
1195
if(mappingproxy[attr] !== undefined ||
1196
["__class__", "__mro__", "__new__", "__init__", "__delitem__",
1197
"clear", "fromkeys", "pop", "popitem", "setdefault",
1198
"update"].indexOf(attr) > -1){
1199
continue
1200
}
1201
if(typeof dict[attr] == "function"){
1202
mappingproxy[attr] = (function(key){
1203
return function(){
1204
return dict[key].apply(null, arguments)
1205
}
1206
})(attr)
1207
}else{
1208
mappingproxy[attr] = dict[attr]
1209
}
1210
}
1211
1235
throw _b_.AttributeError.$factory("'" + $B.class_name(obj) +
1236
"' object has no attribute '__dict__'")}