rapydscript-ns 0.9.3 → 0.9.4
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.agignore +1 -1
- package/.github/workflows/ci.yml +38 -38
- package/=template.pyj +5 -5
- package/CHANGELOG.md +9 -0
- package/HACKING.md +103 -103
- package/LICENSE +24 -24
- package/PYTHON_GAPS.md +48 -116
- package/README.md +35 -15
- package/TODO.md +1 -26
- package/add-toc-to-readme +2 -2
- package/bin/export +75 -75
- package/bin/rapydscript +0 -0
- package/bin/web-repl-export +102 -102
- package/build +2 -2
- package/language-service/index.js +9 -7
- package/package.json +1 -1
- package/publish.py +37 -37
- package/session.vim +4 -4
- package/setup.cfg +2 -2
- package/src/ast.pyj +6 -0
- package/src/baselib-containers.pyj +23 -1
- package/src/baselib-str.pyj +13 -2
- package/src/compiler.pyj +36 -36
- package/src/errors.pyj +30 -30
- package/src/lib/aes.pyj +646 -646
- package/src/lib/collections.pyj +227 -3
- package/src/lib/copy.pyj +120 -120
- package/src/lib/elementmaker.pyj +83 -83
- package/src/lib/encodings.pyj +126 -126
- package/src/lib/gettext.pyj +569 -569
- package/src/lib/itertools.pyj +580 -580
- package/src/lib/math.pyj +193 -193
- package/src/lib/operator.pyj +11 -11
- package/src/lib/pprint.pyj +455 -0
- package/src/lib/random.pyj +118 -118
- package/src/lib/react.pyj +74 -74
- package/src/lib/statistics.pyj +0 -0
- package/src/lib/traceback.pyj +63 -63
- package/src/lib/uuid.pyj +77 -77
- package/src/monaco-language-service/diagnostics.js +2 -2
- package/src/monaco-language-service/dts.js +550 -550
- package/src/output/codegen.pyj +4 -1
- package/src/output/comments.pyj +45 -45
- package/src/output/exceptions.pyj +201 -201
- package/src/output/jsx.pyj +164 -164
- package/src/output/treeshake.pyj +182 -182
- package/src/output/utils.pyj +72 -72
- package/src/parse.pyj +28 -7
- package/src/string_interpolation.pyj +72 -72
- package/src/tokenizer.pyj +18 -2
- package/src/unicode_aliases.pyj +576 -576
- package/src/utils.pyj +192 -192
- package/test/_import_one.pyj +37 -37
- package/test/_import_two/__init__.pyj +11 -11
- package/test/_import_two/level2/deep.pyj +4 -4
- package/test/_import_two/other.pyj +6 -6
- package/test/_import_two/sub.pyj +13 -13
- package/test/aes_vectors.pyj +421 -421
- package/test/annotations.pyj +80 -80
- package/test/baselib.pyj +23 -0
- package/test/chainmap.pyj +185 -0
- package/test/decorators.pyj +77 -77
- package/test/docstrings.pyj +39 -39
- package/test/elementmaker_test.pyj +45 -45
- package/test/functions.pyj +151 -151
- package/test/generators.pyj +41 -41
- package/test/generic.pyj +370 -370
- package/test/internationalization.pyj +73 -73
- package/test/lint.pyj +164 -164
- package/test/loops.pyj +85 -85
- package/test/numpy.pyj +734 -734
- package/test/pprint.pyj +232 -0
- package/test/repl.pyj +121 -121
- package/test/scoped_flags.pyj +76 -76
- package/test/statistics.pyj +224 -0
- package/test/unit/index.js +80 -0
- package/test/unit/language-service-completions.js +2 -0
- package/test/unit/language-service-dts.js +543 -543
- package/test/unit/language-service-hover.js +455 -455
- package/test/unit/language-service.js +63 -2
- package/test/unit/web-repl.js +323 -0
- package/tools/compiler.d.ts +367 -367
- package/tools/completer.js +131 -131
- package/tools/export.js +4 -2
- package/tools/gettext.js +185 -185
- package/tools/ini.js +65 -65
- package/tools/msgfmt.js +187 -187
- package/tools/repl.js +223 -223
- package/tools/test.js +118 -118
- package/tools/utils.js +128 -128
- package/tools/web_repl.js +95 -95
- package/try +41 -41
- package/web-repl/env.js +196 -196
- package/web-repl/index.html +163 -163
- package/web-repl/prism.css +139 -139
- package/web-repl/prism.js +113 -113
- package/web-repl/rapydscript.js +228 -226
- package/web-repl/sha1.js +25 -25
package/src/lib/collections.pyj
CHANGED
|
@@ -2,10 +2,11 @@
|
|
|
2
2
|
# License: BSD
|
|
3
3
|
# RapydScript implementation of Python's collections standard library.
|
|
4
4
|
#
|
|
5
|
-
# Supported: namedtuple, deque, Counter, OrderedDict, defaultdict
|
|
5
|
+
# Supported: namedtuple, deque, Counter, OrderedDict, defaultdict, ChainMap
|
|
6
6
|
#
|
|
7
|
-
# Note: Counter, OrderedDict, and
|
|
8
|
-
# For Python-compatible subscript syntax (obj[key])
|
|
7
|
+
# Note: Counter, OrderedDict, defaultdict, and ChainMap use
|
|
8
|
+
# __getitem__/__setitem__. For Python-compatible subscript syntax (obj[key])
|
|
9
|
+
# in user code, add:
|
|
9
10
|
# from __python__ import overload_getitem
|
|
10
11
|
|
|
11
12
|
|
|
@@ -693,3 +694,226 @@ class defaultdict:
|
|
|
693
694
|
if self._data[k] != src[k]:
|
|
694
695
|
return False
|
|
695
696
|
return True
|
|
697
|
+
|
|
698
|
+
|
|
699
|
+
class ChainMap:
|
|
700
|
+
"""Combine several mappings into a single, updateable view.
|
|
701
|
+
|
|
702
|
+
Lookups search the underlying mappings in order, so the first mapping
|
|
703
|
+
wins. Writes, updates, and deletions affect only the first mapping.
|
|
704
|
+
Mirrors Python's collections.ChainMap.
|
|
705
|
+
"""
|
|
706
|
+
|
|
707
|
+
def __init__(self, *maps):
|
|
708
|
+
if maps.length == 0:
|
|
709
|
+
self.maps = [{}]
|
|
710
|
+
else:
|
|
711
|
+
self.maps = list(maps)
|
|
712
|
+
|
|
713
|
+
# ── internal mapping-access helpers ───────────────────────────────────
|
|
714
|
+
# A "map" may be a plain JS object, a RapydScript dict (ρσ_dict, used for
|
|
715
|
+
# {...} literals in the web-repl), or any RapydScript mapping defining
|
|
716
|
+
# __getitem__/__setitem__/__contains__ (OrderedDict, defaultdict, Counter,
|
|
717
|
+
# or a nested ChainMap).
|
|
718
|
+
|
|
719
|
+
def _is_rdict(self, m):
|
|
720
|
+
return v'!!(m && m.jsmap && typeof m.jsmap.get === "function")'
|
|
721
|
+
|
|
722
|
+
def _is_mapping(self, m):
|
|
723
|
+
return v'!!(m && typeof m.__getitem__ === "function" && typeof m.__contains__ === "function")'
|
|
724
|
+
|
|
725
|
+
def _mkeys(self, m):
|
|
726
|
+
if self._is_rdict(m):
|
|
727
|
+
return v'Array.from(m.jsmap.keys())'
|
|
728
|
+
if self._is_mapping(m):
|
|
729
|
+
return list(m.keys())
|
|
730
|
+
return Object.keys(m)
|
|
731
|
+
|
|
732
|
+
def _mhas(self, m, key):
|
|
733
|
+
if self._is_rdict(m):
|
|
734
|
+
return v'm.jsmap.has(key)'
|
|
735
|
+
if self._is_mapping(m):
|
|
736
|
+
return m.__contains__(key)
|
|
737
|
+
return v'Object.prototype.hasOwnProperty.call(m, key)'
|
|
738
|
+
|
|
739
|
+
def _mget(self, m, key):
|
|
740
|
+
if self._is_rdict(m):
|
|
741
|
+
return v'm.jsmap.get(key)'
|
|
742
|
+
if self._is_mapping(m):
|
|
743
|
+
return m.__getitem__(key)
|
|
744
|
+
return v'm[key]'
|
|
745
|
+
|
|
746
|
+
def _mset(self, m, key, value):
|
|
747
|
+
if self._is_rdict(m):
|
|
748
|
+
v'm.jsmap.set(key, value)'
|
|
749
|
+
elif self._is_mapping(m):
|
|
750
|
+
m.__setitem__(key, value)
|
|
751
|
+
else:
|
|
752
|
+
v'm[key] = value'
|
|
753
|
+
|
|
754
|
+
def _mdel(self, m, key):
|
|
755
|
+
if self._is_rdict(m):
|
|
756
|
+
v'm.jsmap["delete"](key)'
|
|
757
|
+
elif self._is_mapping(m):
|
|
758
|
+
m.__delitem__(key)
|
|
759
|
+
else:
|
|
760
|
+
v'delete m[key]'
|
|
761
|
+
|
|
762
|
+
def _all_keys(self):
|
|
763
|
+
# Unique keys across every map. Maps are scanned last-to-first so that
|
|
764
|
+
# earlier maps keep their key positions, matching Python's iteration.
|
|
765
|
+
seen = {}
|
|
766
|
+
result = []
|
|
767
|
+
i = self.maps.length - 1
|
|
768
|
+
while i >= 0:
|
|
769
|
+
for k in self._mkeys(self.maps[i]):
|
|
770
|
+
if k not in seen:
|
|
771
|
+
seen[k] = True
|
|
772
|
+
result.push(k)
|
|
773
|
+
i -= 1
|
|
774
|
+
return result
|
|
775
|
+
|
|
776
|
+
def __getitem__(self, key):
|
|
777
|
+
for m in self.maps:
|
|
778
|
+
if self._mhas(m, key):
|
|
779
|
+
return self._mget(m, key)
|
|
780
|
+
raise KeyError(repr(key))
|
|
781
|
+
|
|
782
|
+
def __setitem__(self, key, value):
|
|
783
|
+
self._mset(self.maps[0], key, value)
|
|
784
|
+
|
|
785
|
+
def __delitem__(self, key):
|
|
786
|
+
if not self._mhas(self.maps[0], key):
|
|
787
|
+
raise KeyError('Key not found in the first mapping: ' + repr(key))
|
|
788
|
+
self._mdel(self.maps[0], key)
|
|
789
|
+
|
|
790
|
+
def __contains__(self, key):
|
|
791
|
+
for m in self.maps:
|
|
792
|
+
if self._mhas(m, key):
|
|
793
|
+
return True
|
|
794
|
+
return False
|
|
795
|
+
|
|
796
|
+
def __len__(self):
|
|
797
|
+
return self._all_keys().length
|
|
798
|
+
|
|
799
|
+
def __iter__(self):
|
|
800
|
+
return iter(self._all_keys())
|
|
801
|
+
|
|
802
|
+
def __bool__(self):
|
|
803
|
+
for m in self.maps:
|
|
804
|
+
if self._mkeys(m).length > 0:
|
|
805
|
+
return True
|
|
806
|
+
return False
|
|
807
|
+
|
|
808
|
+
def _map_repr(self, m):
|
|
809
|
+
pairs = [repr(k) + ': ' + repr(self._mget(m, k)) for k in self._mkeys(m)]
|
|
810
|
+
return '{' + pairs.join(', ') + '}'
|
|
811
|
+
|
|
812
|
+
def __repr__(self):
|
|
813
|
+
parts = [self._map_repr(m) for m in self.maps]
|
|
814
|
+
return 'ChainMap(' + parts.join(', ') + ')'
|
|
815
|
+
|
|
816
|
+
def __eq__(self, other):
|
|
817
|
+
if other is None:
|
|
818
|
+
return False
|
|
819
|
+
is_mapish = self._is_rdict(other) or self._is_mapping(other) or (jstype(other) is 'object' and not Array.isArray(other))
|
|
820
|
+
if not is_mapish:
|
|
821
|
+
return False
|
|
822
|
+
a = self._all_keys()
|
|
823
|
+
b = self._mkeys(other)
|
|
824
|
+
if a.length != b.length:
|
|
825
|
+
return False
|
|
826
|
+
for k in a:
|
|
827
|
+
if not self._mhas(other, k):
|
|
828
|
+
return False
|
|
829
|
+
if self.__getitem__(k) != self._mget(other, k):
|
|
830
|
+
return False
|
|
831
|
+
return True
|
|
832
|
+
|
|
833
|
+
def get(self, key, dflt=None):
|
|
834
|
+
if self.__contains__(key):
|
|
835
|
+
return self.__getitem__(key)
|
|
836
|
+
return dflt
|
|
837
|
+
|
|
838
|
+
def keys(self):
|
|
839
|
+
return self._all_keys()
|
|
840
|
+
|
|
841
|
+
def values(self):
|
|
842
|
+
return [self.__getitem__(k) for k in self._all_keys()]
|
|
843
|
+
|
|
844
|
+
def items(self):
|
|
845
|
+
return [[k, self.__getitem__(k)] for k in self._all_keys()]
|
|
846
|
+
|
|
847
|
+
@property
|
|
848
|
+
def parents(self):
|
|
849
|
+
cm = ChainMap()
|
|
850
|
+
cm.maps = self.maps.slice(1)
|
|
851
|
+
return cm
|
|
852
|
+
|
|
853
|
+
def new_child(self, m=None):
|
|
854
|
+
if m is None:
|
|
855
|
+
m = {}
|
|
856
|
+
cm = ChainMap()
|
|
857
|
+
cm.maps = [m].concat(self.maps)
|
|
858
|
+
return cm
|
|
859
|
+
|
|
860
|
+
def _copy_map(self, m):
|
|
861
|
+
if jstype(m.copy) is 'function':
|
|
862
|
+
return m.copy()
|
|
863
|
+
result = {}
|
|
864
|
+
for k in Object.keys(m):
|
|
865
|
+
result[k] = m[k]
|
|
866
|
+
return result
|
|
867
|
+
|
|
868
|
+
def copy(self):
|
|
869
|
+
cm = ChainMap()
|
|
870
|
+
cm.maps = [self._copy_map(self.maps[0])].concat(self.maps.slice(1))
|
|
871
|
+
return cm
|
|
872
|
+
|
|
873
|
+
def pop(self, key, *rest):
|
|
874
|
+
first = self.maps[0]
|
|
875
|
+
if self._mhas(first, key):
|
|
876
|
+
val = self._mget(first, key)
|
|
877
|
+
self._mdel(first, key)
|
|
878
|
+
return val
|
|
879
|
+
if len(rest) > 0:
|
|
880
|
+
return rest[0]
|
|
881
|
+
raise KeyError('Key not found in the first mapping: ' + repr(key))
|
|
882
|
+
|
|
883
|
+
def popitem(self):
|
|
884
|
+
first = self.maps[0]
|
|
885
|
+
keys = self._mkeys(first)
|
|
886
|
+
if keys.length == 0:
|
|
887
|
+
raise KeyError('No keys found in the first mapping.')
|
|
888
|
+
k = keys[keys.length - 1]
|
|
889
|
+
val = self._mget(first, k)
|
|
890
|
+
self._mdel(first, k)
|
|
891
|
+
return [k, val]
|
|
892
|
+
|
|
893
|
+
def setdefault(self, key, dflt=None):
|
|
894
|
+
if not self.__contains__(key):
|
|
895
|
+
self._mset(self.maps[0], key, dflt)
|
|
896
|
+
return self.__getitem__(key)
|
|
897
|
+
|
|
898
|
+
def clear(self):
|
|
899
|
+
first = self.maps[0]
|
|
900
|
+
if jstype(first.clear) is 'function':
|
|
901
|
+
first.clear()
|
|
902
|
+
else:
|
|
903
|
+
for k in Object.keys(first):
|
|
904
|
+
v'delete first[k]'
|
|
905
|
+
|
|
906
|
+
def update(self, other=None, **kwargs):
|
|
907
|
+
first = self.maps[0]
|
|
908
|
+
if other is not None:
|
|
909
|
+
if self._is_rdict(other) or self._is_mapping(other):
|
|
910
|
+
for k in self._mkeys(other):
|
|
911
|
+
self._mset(first, k, self._mget(other, k))
|
|
912
|
+
elif jstype(other) is 'object' and not Array.isArray(other):
|
|
913
|
+
for k in Object.keys(other):
|
|
914
|
+
self._mset(first, k, other[k])
|
|
915
|
+
else:
|
|
916
|
+
for pair in other:
|
|
917
|
+
self._mset(first, pair[0], pair[1])
|
|
918
|
+
for k in kwargs:
|
|
919
|
+
self._mset(first, k, kwargs[k])
|
package/src/lib/copy.pyj
CHANGED
|
@@ -1,120 +1,120 @@
|
|
|
1
|
-
# vim:fileencoding=utf-8
|
|
2
|
-
# License: BSD
|
|
3
|
-
# RapydScript implementation of Python's copy standard library.
|
|
4
|
-
#
|
|
5
|
-
# Supported: copy, deepcopy
|
|
6
|
-
# Classes may define __copy__() and __deepcopy__(memo) for custom behaviour.
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
def _is_primitive(x):
|
|
10
|
-
t = jstype(x)
|
|
11
|
-
return x is None or t is 'number' or t is 'boolean' or t is 'string' or t is 'undefined'
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
def copy(x):
|
|
15
|
-
"""Return a shallow copy of x.
|
|
16
|
-
|
|
17
|
-
For immutable primitives (numbers, strings, booleans, None) the object
|
|
18
|
-
itself is returned unchanged. For containers a new container of the same
|
|
19
|
-
type is created whose top-level items are the same objects as in the
|
|
20
|
-
original.
|
|
21
|
-
|
|
22
|
-
Dispatch order:
|
|
23
|
-
1. Primitive → return as-is.
|
|
24
|
-
2. ``__copy__`` method → call and return.
|
|
25
|
-
3. list → ``list(x)`` (slice).
|
|
26
|
-
4. set → ``set(x)``.
|
|
27
|
-
5. frozenset → ``frozenset(x)``.
|
|
28
|
-
6. dict (ρσ_dict) → ``x.copy()``.
|
|
29
|
-
7. Plain JS object (constructor is Object or null-proto) → Object.assign.
|
|
30
|
-
8. Other object (class instance) → Object.create + Object.assign.
|
|
31
|
-
"""
|
|
32
|
-
if _is_primitive(x):
|
|
33
|
-
return x
|
|
34
|
-
if jstype(x.__copy__) is 'function':
|
|
35
|
-
return x.__copy__()
|
|
36
|
-
if Array.isArray(x):
|
|
37
|
-
return list(x)
|
|
38
|
-
if isinstance(x, set):
|
|
39
|
-
return set(x)
|
|
40
|
-
if isinstance(x, frozenset):
|
|
41
|
-
return frozenset(x)
|
|
42
|
-
if isinstance(x, dict):
|
|
43
|
-
return x.copy()
|
|
44
|
-
proto = Object.getPrototypeOf(x)
|
|
45
|
-
if x.constructor is Object or proto is None:
|
|
46
|
-
return Object.assign({}, x)
|
|
47
|
-
# Class instance: create a same-prototype object and copy own properties.
|
|
48
|
-
result = Object.create(proto)
|
|
49
|
-
Object.assign(result, x)
|
|
50
|
-
return result
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
def deepcopy(x, memo=None):
|
|
54
|
-
"""Return a deep (recursive) copy of x.
|
|
55
|
-
|
|
56
|
-
Circular references are handled via the *memo* mapping (a JS Map), which
|
|
57
|
-
stores already-copied objects so that they are only copied once.
|
|
58
|
-
|
|
59
|
-
Dispatch order (same structure as ``copy`` but recursive):
|
|
60
|
-
1. Primitive → return as-is.
|
|
61
|
-
2. Memo hit → return the previously copied object.
|
|
62
|
-
3. ``__deepcopy__(memo)`` method → call and return.
|
|
63
|
-
4. list → recurse into elements.
|
|
64
|
-
5. set → recurse into elements, build new set.
|
|
65
|
-
6. frozenset → recurse into elements, build new frozenset.
|
|
66
|
-
7. dict → recurse into keys and values.
|
|
67
|
-
8. Plain JS object → recurse into own-enumerable properties.
|
|
68
|
-
9. Class instance → recurse into own-enumerable properties.
|
|
69
|
-
"""
|
|
70
|
-
if memo is None:
|
|
71
|
-
memo = v'new Map()'
|
|
72
|
-
if _is_primitive(x):
|
|
73
|
-
return x
|
|
74
|
-
if memo.has(x):
|
|
75
|
-
return memo.get(x)
|
|
76
|
-
if jstype(x.__deepcopy__) is 'function':
|
|
77
|
-
result = x.__deepcopy__(memo)
|
|
78
|
-
memo.set(x, result)
|
|
79
|
-
return result
|
|
80
|
-
if Array.isArray(x):
|
|
81
|
-
result = []
|
|
82
|
-
memo.set(x, result)
|
|
83
|
-
for i in range(x.length):
|
|
84
|
-
result.push(deepcopy(x[i], memo))
|
|
85
|
-
return result
|
|
86
|
-
if isinstance(x, set):
|
|
87
|
-
result = set()
|
|
88
|
-
memo.set(x, result)
|
|
89
|
-
iterator = x[ρσ_iterator_symbol]()
|
|
90
|
-
r = iterator.next()
|
|
91
|
-
while not r.done:
|
|
92
|
-
result.add(deepcopy(r.value, memo))
|
|
93
|
-
r = iterator.next()
|
|
94
|
-
return result
|
|
95
|
-
if isinstance(x, frozenset):
|
|
96
|
-
items = []
|
|
97
|
-
iterator = x[ρσ_iterator_symbol]()
|
|
98
|
-
r = iterator.next()
|
|
99
|
-
while not r.done:
|
|
100
|
-
items.push(deepcopy(r.value, memo))
|
|
101
|
-
r = iterator.next()
|
|
102
|
-
result = frozenset(items)
|
|
103
|
-
memo.set(x, result)
|
|
104
|
-
return result
|
|
105
|
-
if isinstance(x, dict):
|
|
106
|
-
result = dict()
|
|
107
|
-
memo.set(x, result)
|
|
108
|
-
iterator = x.items()
|
|
109
|
-
r = iterator.next()
|
|
110
|
-
while not r.done:
|
|
111
|
-
result.set(deepcopy(r.value[0], memo), deepcopy(r.value[1], memo))
|
|
112
|
-
r = iterator.next()
|
|
113
|
-
return result
|
|
114
|
-
proto = Object.getPrototypeOf(x)
|
|
115
|
-
result = Object.create(proto)
|
|
116
|
-
memo.set(x, result)
|
|
117
|
-
keys = Object.keys(x)
|
|
118
|
-
for i in range(keys.length):
|
|
119
|
-
result[keys[i]] = deepcopy(x[keys[i]], memo)
|
|
120
|
-
return result
|
|
1
|
+
# vim:fileencoding=utf-8
|
|
2
|
+
# License: BSD
|
|
3
|
+
# RapydScript implementation of Python's copy standard library.
|
|
4
|
+
#
|
|
5
|
+
# Supported: copy, deepcopy
|
|
6
|
+
# Classes may define __copy__() and __deepcopy__(memo) for custom behaviour.
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
def _is_primitive(x):
|
|
10
|
+
t = jstype(x)
|
|
11
|
+
return x is None or t is 'number' or t is 'boolean' or t is 'string' or t is 'undefined'
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
def copy(x):
|
|
15
|
+
"""Return a shallow copy of x.
|
|
16
|
+
|
|
17
|
+
For immutable primitives (numbers, strings, booleans, None) the object
|
|
18
|
+
itself is returned unchanged. For containers a new container of the same
|
|
19
|
+
type is created whose top-level items are the same objects as in the
|
|
20
|
+
original.
|
|
21
|
+
|
|
22
|
+
Dispatch order:
|
|
23
|
+
1. Primitive → return as-is.
|
|
24
|
+
2. ``__copy__`` method → call and return.
|
|
25
|
+
3. list → ``list(x)`` (slice).
|
|
26
|
+
4. set → ``set(x)``.
|
|
27
|
+
5. frozenset → ``frozenset(x)``.
|
|
28
|
+
6. dict (ρσ_dict) → ``x.copy()``.
|
|
29
|
+
7. Plain JS object (constructor is Object or null-proto) → Object.assign.
|
|
30
|
+
8. Other object (class instance) → Object.create + Object.assign.
|
|
31
|
+
"""
|
|
32
|
+
if _is_primitive(x):
|
|
33
|
+
return x
|
|
34
|
+
if jstype(x.__copy__) is 'function':
|
|
35
|
+
return x.__copy__()
|
|
36
|
+
if Array.isArray(x):
|
|
37
|
+
return list(x)
|
|
38
|
+
if isinstance(x, set):
|
|
39
|
+
return set(x)
|
|
40
|
+
if isinstance(x, frozenset):
|
|
41
|
+
return frozenset(x)
|
|
42
|
+
if isinstance(x, dict):
|
|
43
|
+
return x.copy()
|
|
44
|
+
proto = Object.getPrototypeOf(x)
|
|
45
|
+
if x.constructor is Object or proto is None:
|
|
46
|
+
return Object.assign({}, x)
|
|
47
|
+
# Class instance: create a same-prototype object and copy own properties.
|
|
48
|
+
result = Object.create(proto)
|
|
49
|
+
Object.assign(result, x)
|
|
50
|
+
return result
|
|
51
|
+
|
|
52
|
+
|
|
53
|
+
def deepcopy(x, memo=None):
|
|
54
|
+
"""Return a deep (recursive) copy of x.
|
|
55
|
+
|
|
56
|
+
Circular references are handled via the *memo* mapping (a JS Map), which
|
|
57
|
+
stores already-copied objects so that they are only copied once.
|
|
58
|
+
|
|
59
|
+
Dispatch order (same structure as ``copy`` but recursive):
|
|
60
|
+
1. Primitive → return as-is.
|
|
61
|
+
2. Memo hit → return the previously copied object.
|
|
62
|
+
3. ``__deepcopy__(memo)`` method → call and return.
|
|
63
|
+
4. list → recurse into elements.
|
|
64
|
+
5. set → recurse into elements, build new set.
|
|
65
|
+
6. frozenset → recurse into elements, build new frozenset.
|
|
66
|
+
7. dict → recurse into keys and values.
|
|
67
|
+
8. Plain JS object → recurse into own-enumerable properties.
|
|
68
|
+
9. Class instance → recurse into own-enumerable properties.
|
|
69
|
+
"""
|
|
70
|
+
if memo is None:
|
|
71
|
+
memo = v'new Map()'
|
|
72
|
+
if _is_primitive(x):
|
|
73
|
+
return x
|
|
74
|
+
if memo.has(x):
|
|
75
|
+
return memo.get(x)
|
|
76
|
+
if jstype(x.__deepcopy__) is 'function':
|
|
77
|
+
result = x.__deepcopy__(memo)
|
|
78
|
+
memo.set(x, result)
|
|
79
|
+
return result
|
|
80
|
+
if Array.isArray(x):
|
|
81
|
+
result = []
|
|
82
|
+
memo.set(x, result)
|
|
83
|
+
for i in range(x.length):
|
|
84
|
+
result.push(deepcopy(x[i], memo))
|
|
85
|
+
return result
|
|
86
|
+
if isinstance(x, set):
|
|
87
|
+
result = set()
|
|
88
|
+
memo.set(x, result)
|
|
89
|
+
iterator = x[ρσ_iterator_symbol]()
|
|
90
|
+
r = iterator.next()
|
|
91
|
+
while not r.done:
|
|
92
|
+
result.add(deepcopy(r.value, memo))
|
|
93
|
+
r = iterator.next()
|
|
94
|
+
return result
|
|
95
|
+
if isinstance(x, frozenset):
|
|
96
|
+
items = []
|
|
97
|
+
iterator = x[ρσ_iterator_symbol]()
|
|
98
|
+
r = iterator.next()
|
|
99
|
+
while not r.done:
|
|
100
|
+
items.push(deepcopy(r.value, memo))
|
|
101
|
+
r = iterator.next()
|
|
102
|
+
result = frozenset(items)
|
|
103
|
+
memo.set(x, result)
|
|
104
|
+
return result
|
|
105
|
+
if isinstance(x, dict):
|
|
106
|
+
result = dict()
|
|
107
|
+
memo.set(x, result)
|
|
108
|
+
iterator = x.items()
|
|
109
|
+
r = iterator.next()
|
|
110
|
+
while not r.done:
|
|
111
|
+
result.set(deepcopy(r.value[0], memo), deepcopy(r.value[1], memo))
|
|
112
|
+
r = iterator.next()
|
|
113
|
+
return result
|
|
114
|
+
proto = Object.getPrototypeOf(x)
|
|
115
|
+
result = Object.create(proto)
|
|
116
|
+
memo.set(x, result)
|
|
117
|
+
keys = Object.keys(x)
|
|
118
|
+
for i in range(keys.length):
|
|
119
|
+
result[keys[i]] = deepcopy(x[keys[i]], memo)
|
|
120
|
+
return result
|
package/src/lib/elementmaker.pyj
CHANGED
|
@@ -1,83 +1,83 @@
|
|
|
1
|
-
# vim:fileencoding=utf-8
|
|
2
|
-
# License: GPL v3 Copyright: 2015, Kovid Goyal <kovid at kovidgoyal.net>
|
|
3
|
-
|
|
4
|
-
html_elements = {
|
|
5
|
-
'a', 'abbr', 'acronym', 'address', 'area',
|
|
6
|
-
'article', 'aside', 'audio', 'b', 'base', 'big', 'body', 'blockquote', 'br', 'button',
|
|
7
|
-
'canvas', 'caption', 'center', 'cite', 'code', 'col', 'colgroup',
|
|
8
|
-
'command', 'datagrid', 'datalist', 'dd', 'del', 'details', 'dfn',
|
|
9
|
-
'dialog', 'dir', 'div', 'dl', 'dt', 'em', 'event-source', 'fieldset',
|
|
10
|
-
'figcaption', 'figure', 'footer', 'font', 'form', 'header', 'h1',
|
|
11
|
-
'h2', 'h3', 'h4', 'h5', 'h6', 'hr', 'head', 'i', 'iframe', 'img', 'input', 'ins',
|
|
12
|
-
'keygen', 'kbd', 'label', 'legend', 'li', 'm', 'map', 'menu', 'meter',
|
|
13
|
-
'multicol', 'nav', 'nextid', 'ol', 'output', 'optgroup', 'option',
|
|
14
|
-
'p', 'pre', 'progress', 'q', 's', 'samp', 'script', 'section', 'select',
|
|
15
|
-
'small', 'sound', 'source', 'spacer', 'span', 'strike', 'strong', 'style',
|
|
16
|
-
'sub', 'sup', 'table', 'tbody', 'td', 'textarea', 'time', 'tfoot',
|
|
17
|
-
'th', 'thead', 'tr', 'tt', 'u', 'ul', 'var', 'video'
|
|
18
|
-
}
|
|
19
|
-
|
|
20
|
-
mathml_elements = {
|
|
21
|
-
'maction', 'math', 'merror', 'mfrac', 'mi',
|
|
22
|
-
'mmultiscripts', 'mn', 'mo', 'mover', 'mpadded', 'mphantom',
|
|
23
|
-
'mprescripts', 'mroot', 'mrow', 'mspace', 'msqrt', 'mstyle', 'msub',
|
|
24
|
-
'msubsup', 'msup', 'mtable', 'mtd', 'mtext', 'mtr', 'munder',
|
|
25
|
-
'munderover', 'none'
|
|
26
|
-
}
|
|
27
|
-
|
|
28
|
-
svg_elements = {
|
|
29
|
-
'a', 'animate', 'animateColor', 'animateMotion',
|
|
30
|
-
'animateTransform', 'clipPath', 'circle', 'defs', 'desc', 'ellipse',
|
|
31
|
-
'font-face', 'font-face-name', 'font-face-src', 'g', 'glyph', 'hkern',
|
|
32
|
-
'linearGradient', 'line', 'marker', 'metadata', 'missing-glyph',
|
|
33
|
-
'mpath', 'path', 'polygon', 'polyline', 'radialGradient', 'rect',
|
|
34
|
-
'set', 'stop', 'svg', 'switch', 'text', 'title', 'tspan', 'use'
|
|
35
|
-
}
|
|
36
|
-
|
|
37
|
-
html5_tags = html_elements.union(mathml_elements).union(svg_elements)
|
|
38
|
-
|
|
39
|
-
def _makeelement(tag, *args, **kwargs):
|
|
40
|
-
ans = this.createElement(tag)
|
|
41
|
-
|
|
42
|
-
for attr in kwargs:
|
|
43
|
-
vattr = str.replace(str.rstrip(attr, '_'), '_', '-')
|
|
44
|
-
val = kwargs[attr]
|
|
45
|
-
if callable(val):
|
|
46
|
-
if str.startswith(attr, 'on'):
|
|
47
|
-
attr = attr[2:]
|
|
48
|
-
ans.addEventListener(attr, val)
|
|
49
|
-
elif val is True:
|
|
50
|
-
ans.setAttribute(vattr, vattr)
|
|
51
|
-
elif jstype(val) is 'string':
|
|
52
|
-
ans.setAttribute(vattr, val)
|
|
53
|
-
|
|
54
|
-
for arg in args:
|
|
55
|
-
if jstype(arg) is 'string':
|
|
56
|
-
arg = this.createTextNode(arg)
|
|
57
|
-
ans.appendChild(arg)
|
|
58
|
-
return ans
|
|
59
|
-
|
|
60
|
-
def maker_for_document(document):
|
|
61
|
-
# Create an elementmaker to be used with the specified document
|
|
62
|
-
E = _makeelement.bind(document)
|
|
63
|
-
Object.defineProperties(E, {
|
|
64
|
-
tag: {
|
|
65
|
-
'value':_makeelement.bind(document, tag)
|
|
66
|
-
} for tag in html5_tags
|
|
67
|
-
})
|
|
68
|
-
return E
|
|
69
|
-
|
|
70
|
-
if jstype(document) is 'undefined':
|
|
71
|
-
E = maker_for_document({
|
|
72
|
-
'createTextNode': def(value): return value;,
|
|
73
|
-
'createElement': def(name):
|
|
74
|
-
return {
|
|
75
|
-
'name':name,
|
|
76
|
-
'children':[],
|
|
77
|
-
'attributes':{},
|
|
78
|
-
'setAttribute': def(name, val): this.attributes[name] = val;,
|
|
79
|
-
'appendChild': def(child): this.children.push(child);,
|
|
80
|
-
}
|
|
81
|
-
})
|
|
82
|
-
else:
|
|
83
|
-
E = maker_for_document(document)
|
|
1
|
+
# vim:fileencoding=utf-8
|
|
2
|
+
# License: GPL v3 Copyright: 2015, Kovid Goyal <kovid at kovidgoyal.net>
|
|
3
|
+
|
|
4
|
+
html_elements = {
|
|
5
|
+
'a', 'abbr', 'acronym', 'address', 'area',
|
|
6
|
+
'article', 'aside', 'audio', 'b', 'base', 'big', 'body', 'blockquote', 'br', 'button',
|
|
7
|
+
'canvas', 'caption', 'center', 'cite', 'code', 'col', 'colgroup',
|
|
8
|
+
'command', 'datagrid', 'datalist', 'dd', 'del', 'details', 'dfn',
|
|
9
|
+
'dialog', 'dir', 'div', 'dl', 'dt', 'em', 'event-source', 'fieldset',
|
|
10
|
+
'figcaption', 'figure', 'footer', 'font', 'form', 'header', 'h1',
|
|
11
|
+
'h2', 'h3', 'h4', 'h5', 'h6', 'hr', 'head', 'i', 'iframe', 'img', 'input', 'ins',
|
|
12
|
+
'keygen', 'kbd', 'label', 'legend', 'li', 'm', 'map', 'menu', 'meter',
|
|
13
|
+
'multicol', 'nav', 'nextid', 'ol', 'output', 'optgroup', 'option',
|
|
14
|
+
'p', 'pre', 'progress', 'q', 's', 'samp', 'script', 'section', 'select',
|
|
15
|
+
'small', 'sound', 'source', 'spacer', 'span', 'strike', 'strong', 'style',
|
|
16
|
+
'sub', 'sup', 'table', 'tbody', 'td', 'textarea', 'time', 'tfoot',
|
|
17
|
+
'th', 'thead', 'tr', 'tt', 'u', 'ul', 'var', 'video'
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
mathml_elements = {
|
|
21
|
+
'maction', 'math', 'merror', 'mfrac', 'mi',
|
|
22
|
+
'mmultiscripts', 'mn', 'mo', 'mover', 'mpadded', 'mphantom',
|
|
23
|
+
'mprescripts', 'mroot', 'mrow', 'mspace', 'msqrt', 'mstyle', 'msub',
|
|
24
|
+
'msubsup', 'msup', 'mtable', 'mtd', 'mtext', 'mtr', 'munder',
|
|
25
|
+
'munderover', 'none'
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
svg_elements = {
|
|
29
|
+
'a', 'animate', 'animateColor', 'animateMotion',
|
|
30
|
+
'animateTransform', 'clipPath', 'circle', 'defs', 'desc', 'ellipse',
|
|
31
|
+
'font-face', 'font-face-name', 'font-face-src', 'g', 'glyph', 'hkern',
|
|
32
|
+
'linearGradient', 'line', 'marker', 'metadata', 'missing-glyph',
|
|
33
|
+
'mpath', 'path', 'polygon', 'polyline', 'radialGradient', 'rect',
|
|
34
|
+
'set', 'stop', 'svg', 'switch', 'text', 'title', 'tspan', 'use'
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
html5_tags = html_elements.union(mathml_elements).union(svg_elements)
|
|
38
|
+
|
|
39
|
+
def _makeelement(tag, *args, **kwargs):
|
|
40
|
+
ans = this.createElement(tag)
|
|
41
|
+
|
|
42
|
+
for attr in kwargs:
|
|
43
|
+
vattr = str.replace(str.rstrip(attr, '_'), '_', '-')
|
|
44
|
+
val = kwargs[attr]
|
|
45
|
+
if callable(val):
|
|
46
|
+
if str.startswith(attr, 'on'):
|
|
47
|
+
attr = attr[2:]
|
|
48
|
+
ans.addEventListener(attr, val)
|
|
49
|
+
elif val is True:
|
|
50
|
+
ans.setAttribute(vattr, vattr)
|
|
51
|
+
elif jstype(val) is 'string':
|
|
52
|
+
ans.setAttribute(vattr, val)
|
|
53
|
+
|
|
54
|
+
for arg in args:
|
|
55
|
+
if jstype(arg) is 'string':
|
|
56
|
+
arg = this.createTextNode(arg)
|
|
57
|
+
ans.appendChild(arg)
|
|
58
|
+
return ans
|
|
59
|
+
|
|
60
|
+
def maker_for_document(document):
|
|
61
|
+
# Create an elementmaker to be used with the specified document
|
|
62
|
+
E = _makeelement.bind(document)
|
|
63
|
+
Object.defineProperties(E, {
|
|
64
|
+
tag: {
|
|
65
|
+
'value':_makeelement.bind(document, tag)
|
|
66
|
+
} for tag in html5_tags
|
|
67
|
+
})
|
|
68
|
+
return E
|
|
69
|
+
|
|
70
|
+
if jstype(document) is 'undefined':
|
|
71
|
+
E = maker_for_document({
|
|
72
|
+
'createTextNode': def(value): return value;,
|
|
73
|
+
'createElement': def(name):
|
|
74
|
+
return {
|
|
75
|
+
'name':name,
|
|
76
|
+
'children':[],
|
|
77
|
+
'attributes':{},
|
|
78
|
+
'setAttribute': def(name, val): this.attributes[name] = val;,
|
|
79
|
+
'appendChild': def(child): this.children.push(child);,
|
|
80
|
+
}
|
|
81
|
+
})
|
|
82
|
+
else:
|
|
83
|
+
E = maker_for_document(document)
|