rapydscript-ns 0.8.1 → 0.8.3
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/CHANGELOG.md +31 -0
- package/CONTRIBUTORS +3 -2
- package/PYTHON_DIFFERENCES_REPORT.md +291 -0
- package/PYTHON_FEATURE_COVERAGE.md +200 -0
- package/README.md +480 -79
- package/TODO.md +6 -318
- package/hack_demo.pyj +112 -0
- package/language-service/index.js +4474 -0
- package/language-service/language-service.d.ts +40 -0
- package/package.json +9 -10
- package/src/ast.pyj +30 -6
- package/src/baselib-builtins.pyj +181 -11
- package/src/baselib-containers.pyj +154 -5
- package/src/baselib-errors.pyj +3 -0
- package/src/baselib-internal.pyj +40 -1
- package/src/baselib-str.pyj +42 -1
- package/src/lib/collections.pyj +1 -1
- package/src/lib/numpy.pyj +10 -10
- package/src/monaco-language-service/analyzer.js +132 -22
- package/src/monaco-language-service/builtins.js +22 -2
- package/src/monaco-language-service/completions.js +224 -3
- package/src/monaco-language-service/diagnostics.js +55 -5
- package/src/monaco-language-service/index.js +26 -5
- package/src/monaco-language-service/scope.js +3 -0
- package/src/output/classes.pyj +20 -3
- package/src/output/codegen.pyj +38 -3
- package/src/output/functions.pyj +35 -25
- package/src/output/loops.pyj +64 -11
- package/src/output/modules.pyj +1 -4
- package/src/output/operators.pyj +67 -1
- package/src/output/statements.pyj +7 -3
- package/src/output/stream.pyj +6 -13
- package/src/parse.pyj +94 -14
- package/src/tokenizer.pyj +1 -0
- package/test/baselib.pyj +4 -4
- package/test/classes.pyj +56 -17
- package/test/collections.pyj +5 -5
- package/test/python_compat.pyj +326 -0
- package/test/python_features.pyj +1271 -0
- package/test/slice.pyj +105 -0
- package/test/str.pyj +25 -0
- package/test/unit/fixtures/fibonacci_expected.js +1 -1
- package/test/unit/index.js +119 -7
- package/test/unit/language-service-builtins.js +70 -0
- package/test/unit/language-service-bundle.js +83 -0
- package/test/unit/language-service-completions.js +289 -0
- package/test/unit/language-service-index.js +350 -0
- package/test/unit/language-service-scope.js +255 -0
- package/test/unit/language-service.js +158 -1
- package/test/unit/run-language-service.js +2 -0
- package/test/unit/web-repl.js +134 -0
- package/tools/build-language-service.js +2 -2
- package/tools/compiler.js +0 -24
- package/tools/export.js +3 -37
- package/tools/lint.js +1 -1
- package/tools/self.js +1 -9
- package/web-repl/rapydscript.js +6 -40
- package/web-repl/language-service.js +0 -4084
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
// language-service.d.ts — Ambient type declarations for language-service.js
|
|
2
|
+
// The actual implementation lives in language-service.js (bundled, do not edit directly).
|
|
3
|
+
|
|
4
|
+
export interface WebReplInstance {
|
|
5
|
+
in_block_mode: boolean;
|
|
6
|
+
compile(code: string, opts?: Record<string, unknown>): string;
|
|
7
|
+
compile_mapped(code: string, opts?: Record<string, unknown>): { code: string; sourceMap: string };
|
|
8
|
+
runjs(code: string): unknown;
|
|
9
|
+
replace_print(writeLine: (...args: unknown[]) => void): void;
|
|
10
|
+
is_input_complete(source: string): boolean;
|
|
11
|
+
init_completions(completelib: unknown): void;
|
|
12
|
+
find_completions(line: string): unknown;
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
export interface RapydScriptOptions {
|
|
16
|
+
compiler?: unknown;
|
|
17
|
+
virtualFiles?: Record<string, string>;
|
|
18
|
+
stdlibFiles?: Record<string, string>;
|
|
19
|
+
dtsFiles?: Array<{ name: string; content: string }>;
|
|
20
|
+
parseDelay?: number;
|
|
21
|
+
extraBuiltins?: Record<string, true>;
|
|
22
|
+
pythonFlags?: string;
|
|
23
|
+
pythonize_strings?: boolean;
|
|
24
|
+
loadDts?: (name: string) => string | Promise<string>;
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
export declare class RapydScriptLanguageService {
|
|
28
|
+
constructor(monaco: unknown, options: RapydScriptOptions);
|
|
29
|
+
|
|
30
|
+
setVirtualFiles(files: Record<string, string>): void;
|
|
31
|
+
removeVirtualFile(name: string): void;
|
|
32
|
+
addGlobals(names: string[]): void;
|
|
33
|
+
getScopeMap(model: unknown): unknown | null;
|
|
34
|
+
addDts(name: string, dtsText: string): void;
|
|
35
|
+
loadDts(name: string): Promise<void>;
|
|
36
|
+
dispose(): void;
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
export declare function registerRapydScript(monaco: unknown, options?: RapydScriptOptions): RapydScriptLanguageService;
|
|
40
|
+
export declare function web_repl(): WebReplInstance;
|
package/package.json
CHANGED
|
@@ -2,6 +2,11 @@
|
|
|
2
2
|
"name": "rapydscript-ns",
|
|
3
3
|
"description": "Pythonic JavaScript that doesn't suck",
|
|
4
4
|
"homepage": "https://github.com/ficocelliguy/rapydscript-ns",
|
|
5
|
+
"version": "0.8.3",
|
|
6
|
+
"license": "BSD-2-Clause",
|
|
7
|
+
"engines": {
|
|
8
|
+
"node": ">=0.12.0"
|
|
9
|
+
},
|
|
5
10
|
"keywords": [
|
|
6
11
|
"javascript",
|
|
7
12
|
"rapydscript",
|
|
@@ -11,7 +16,10 @@
|
|
|
11
16
|
"main": "tools/compiler.js",
|
|
12
17
|
"exports": {
|
|
13
18
|
".": "./tools/compiler.js",
|
|
14
|
-
"./language-service":
|
|
19
|
+
"./language-service": {
|
|
20
|
+
"types": "./language-service/language-service.d.ts",
|
|
21
|
+
"default": "./language-service/index.js"
|
|
22
|
+
}
|
|
15
23
|
},
|
|
16
24
|
"scripts": {
|
|
17
25
|
"test": "node bin/rapydscript self --complete --test && npm run test:unit && npm run test:ls",
|
|
@@ -23,11 +31,6 @@
|
|
|
23
31
|
"build": "rm -rf **/*pyj-cached dev && node bin/rapydscript self --complete && node bin/web-repl-export web-repl && node tools/build-language-service.js",
|
|
24
32
|
"prepublishOnly": "node tools/build-language-service.js"
|
|
25
33
|
},
|
|
26
|
-
"version": "0.8.1",
|
|
27
|
-
"license": "BSD-2-Clause",
|
|
28
|
-
"engines": {
|
|
29
|
-
"node": ">=0.12.0"
|
|
30
|
-
},
|
|
31
34
|
"maintainers": [
|
|
32
35
|
{
|
|
33
36
|
"name": "Michael Ficocelli",
|
|
@@ -39,12 +42,8 @@
|
|
|
39
42
|
"url": "https://github.com/ficocelliguy/rapydscript-ns.git"
|
|
40
43
|
},
|
|
41
44
|
"dependencies": {
|
|
42
|
-
"regenerator": ">= 0.12.1",
|
|
43
45
|
"uglify-js": ">= 3.0.15"
|
|
44
46
|
},
|
|
45
|
-
"optionalDependencies": {
|
|
46
|
-
"v8-profiler": ">= 5.2.9"
|
|
47
|
-
},
|
|
48
47
|
"bin": {
|
|
49
48
|
"rapydscript": "bin/rapydscript"
|
|
50
49
|
}
|
package/src/ast.pyj
CHANGED
|
@@ -159,6 +159,7 @@ class AST_Assert(AST_Statement):
|
|
|
159
159
|
properties = {
|
|
160
160
|
'condition': "[AST_Node] the expression that should be tested",
|
|
161
161
|
'message': "[AST_Node*] the expression that is the error message or None",
|
|
162
|
+
'python_truthiness': "[bool] Whether to use Python truthiness (from __python__ import truthiness)"
|
|
162
163
|
}
|
|
163
164
|
|
|
164
165
|
def _walk(self, visitor):
|
|
@@ -213,7 +214,8 @@ class AST_StatementWithBody(AST_Statement):
|
|
|
213
214
|
class AST_DWLoop(AST_StatementWithBody):
|
|
214
215
|
"Base class for do/while statements"
|
|
215
216
|
properties = {
|
|
216
|
-
'condition': "[AST_Node] the loop condition. Should not be instanceof AST_Statement"
|
|
217
|
+
'condition': "[AST_Node] the loop condition. Should not be instanceof AST_Statement",
|
|
218
|
+
'python_truthiness': "[bool] Whether to use Python truthiness (from __python__ import truthiness)"
|
|
217
219
|
}
|
|
218
220
|
|
|
219
221
|
def _walk(self, visitor):
|
|
@@ -227,13 +229,25 @@ class AST_Do(AST_DWLoop):
|
|
|
227
229
|
|
|
228
230
|
class AST_While(AST_DWLoop):
|
|
229
231
|
"A `while` statement"
|
|
232
|
+
properties = {
|
|
233
|
+
'belse': "[AST_Else?] the `else` clause, run when loop exits without `break`"
|
|
234
|
+
}
|
|
235
|
+
|
|
236
|
+
def _walk(self, visitor):
|
|
237
|
+
return visitor._visit(self, def():
|
|
238
|
+
self.condition._walk(visitor)
|
|
239
|
+
self.body._walk(visitor)
|
|
240
|
+
if self.belse:
|
|
241
|
+
self.belse._walk(visitor)
|
|
242
|
+
)
|
|
230
243
|
|
|
231
244
|
class AST_ForIn(AST_StatementWithBody):
|
|
232
245
|
"A `for ... in` statement"
|
|
233
246
|
properties = {
|
|
234
247
|
'init': "[AST_Node] the `for/in` initialization code",
|
|
235
248
|
'name': "[AST_SymbolRef?] the loop variable, only if `init` is AST_Var",
|
|
236
|
-
'object': "[AST_Node] the object that we're looping through"
|
|
249
|
+
'object': "[AST_Node] the object that we're looping through",
|
|
250
|
+
'belse': "[AST_Else?] the `else` clause, run when no break occurred"
|
|
237
251
|
}
|
|
238
252
|
|
|
239
253
|
def _walk(self, visitor):
|
|
@@ -243,6 +257,8 @@ class AST_ForIn(AST_StatementWithBody):
|
|
|
243
257
|
self.object._walk(visitor)
|
|
244
258
|
if self.body:
|
|
245
259
|
self.body._walk(visitor)
|
|
260
|
+
if self.belse:
|
|
261
|
+
self.belse._walk(visitor)
|
|
246
262
|
)
|
|
247
263
|
|
|
248
264
|
class AST_ForJS(AST_StatementWithBody):
|
|
@@ -647,6 +663,9 @@ class AST_Await(AST_Node):
|
|
|
647
663
|
|
|
648
664
|
class AST_Throw(AST_Exit):
|
|
649
665
|
"A `throw` statement"
|
|
666
|
+
properties = {
|
|
667
|
+
'cause': "[AST_Node?] the __cause__ of the exception (from `raise X from Y`); may be None"
|
|
668
|
+
}
|
|
650
669
|
|
|
651
670
|
class AST_LoopControl(AST_Jump):
|
|
652
671
|
"Base class for loop control statements (`break` and `continue`)"
|
|
@@ -663,7 +682,8 @@ class AST_If(AST_StatementWithBody):
|
|
|
663
682
|
"A `if` statement"
|
|
664
683
|
properties = {
|
|
665
684
|
'condition': "[AST_Node] the `if` condition",
|
|
666
|
-
'alternative': "[AST_Statement?] the `else` part, or null if not present"
|
|
685
|
+
'alternative': "[AST_Statement?] the `else` part, or null if not present",
|
|
686
|
+
'python_truthiness': "[bool] Whether to use Python truthiness (from __python__ import truthiness)"
|
|
667
687
|
}
|
|
668
688
|
|
|
669
689
|
def _walk(self, visitor):
|
|
@@ -767,7 +787,8 @@ class AST_BaseCall(AST_Node):
|
|
|
767
787
|
class AST_Call(AST_BaseCall):
|
|
768
788
|
"A function call expression"
|
|
769
789
|
properties = {
|
|
770
|
-
'expression': "[AST_Node] expression to invoke as function"
|
|
790
|
+
'expression': "[AST_Node] expression to invoke as function",
|
|
791
|
+
'python_truthiness': "[bool] Whether to use Python truthiness for __call__ dispatch"
|
|
771
792
|
}
|
|
772
793
|
|
|
773
794
|
def _walk(self, visitor):
|
|
@@ -941,7 +962,8 @@ class AST_Unary(AST_Node):
|
|
|
941
962
|
'operator': "[string] the operator",
|
|
942
963
|
'expression': "[AST_Node] expression that this unary operator applies to",
|
|
943
964
|
'parenthesized': "[bool] Whether this unary expression was parenthesized",
|
|
944
|
-
'overloaded': "[bool] Whether to use Python-style operator overloading dispatch"
|
|
965
|
+
'overloaded': "[bool] Whether to use Python-style operator overloading dispatch",
|
|
966
|
+
'python_truthiness': "[bool] Whether to use Python truthiness (from __python__ import truthiness)"
|
|
945
967
|
}
|
|
946
968
|
|
|
947
969
|
def _walk(self, visitor):
|
|
@@ -958,7 +980,8 @@ class AST_Binary(AST_Node):
|
|
|
958
980
|
'left': "[AST_Node] left-hand side expression",
|
|
959
981
|
'operator': "[string] the operator",
|
|
960
982
|
'right': "[AST_Node] right-hand side expression",
|
|
961
|
-
'overloaded': "[bool] Whether to use Python-style operator overloading dispatch"
|
|
983
|
+
'overloaded': "[bool] Whether to use Python-style operator overloading dispatch",
|
|
984
|
+
'python_truthiness': "[bool] Whether to use Python truthiness (from __python__ import truthiness)"
|
|
962
985
|
}
|
|
963
986
|
|
|
964
987
|
def _walk(self, visitor):
|
|
@@ -987,6 +1010,7 @@ class AST_Conditional(AST_Node):
|
|
|
987
1010
|
'condition': "[AST_Node]",
|
|
988
1011
|
'consequent': "[AST_Node]",
|
|
989
1012
|
'alternative': "[AST_Node]",
|
|
1013
|
+
'python_truthiness': "[bool] Whether to use Python truthiness (from __python__ import truthiness)"
|
|
990
1014
|
}
|
|
991
1015
|
|
|
992
1016
|
def _walk(self, visitor):
|
package/src/baselib-builtins.pyj
CHANGED
|
@@ -5,7 +5,21 @@
|
|
|
5
5
|
# globals: exports, console, ρσ_iterator_symbol, ρσ_kwargs_symbol, ρσ_arraylike, ρσ_list_contains
|
|
6
6
|
|
|
7
7
|
def ρσ_bool(val):
|
|
8
|
-
|
|
8
|
+
# Python truthiness — written entirely with verbatim JS so that the
|
|
9
|
+
# compiled ρσ_bool function never calls itself (no bootstrap recursion).
|
|
10
|
+
v'if (val === null || val === undefined) return false'
|
|
11
|
+
v'var ρσ_bool_t = typeof val'
|
|
12
|
+
v'if (ρσ_bool_t === "boolean") return val'
|
|
13
|
+
v'if (ρσ_bool_t === "number") return val !== 0'
|
|
14
|
+
v'if (ρσ_bool_t === "string") return val.length > 0'
|
|
15
|
+
v'if (ρσ_bool_t === "function") return true'
|
|
16
|
+
v'if (val.constructor && val.constructor.prototype === val) return true'
|
|
17
|
+
v'if (typeof val.__bool__ === "function") return !!val.__bool__()'
|
|
18
|
+
v'if (Array.isArray(val)) return val.length > 0'
|
|
19
|
+
v'if (typeof val.__len__ === "function") return val.__len__() > 0'
|
|
20
|
+
v'if ((typeof Set === "function" && val instanceof Set) || (typeof Map === "function" && val instanceof Map)) return val.size > 0'
|
|
21
|
+
v'if (!val.constructor || val.constructor === Object) return Object.keys(val).length > 0'
|
|
22
|
+
return True
|
|
9
23
|
|
|
10
24
|
def ρσ_print(*args, **kwargs):
|
|
11
25
|
if v'typeof console' is 'object':
|
|
@@ -73,7 +87,24 @@ def ρσ_chr(code):
|
|
|
73
87
|
return String.fromCharCode(0xD800+(code>>10), 0xDC00+(code&0x3FF))
|
|
74
88
|
|
|
75
89
|
def ρσ_callable(x):
|
|
76
|
-
return v'typeof x === "function"'
|
|
90
|
+
return v'typeof x === "function" || (x !== null && x !== undefined && typeof x.__call__ === "function")'
|
|
91
|
+
|
|
92
|
+
def ρσ_callable_call(fn):
|
|
93
|
+
# Dispatch fn(args): check __call__ first (callable objects), then direct call.
|
|
94
|
+
# __call__ is checked first so callable objects (not functions) are dispatched correctly.
|
|
95
|
+
args = v'Array.prototype.slice.call(arguments, 1)'
|
|
96
|
+
if v'fn !== null && fn !== undefined && typeof fn.__call__ === "function"':
|
|
97
|
+
return v'fn.__call__.apply(fn, args)'
|
|
98
|
+
if v'typeof fn === "function"':
|
|
99
|
+
return v'fn.apply(undefined, args)'
|
|
100
|
+
raise TypeError('object is not callable')
|
|
101
|
+
|
|
102
|
+
|
|
103
|
+
def ρσ_round(x, ndigits):
|
|
104
|
+
if ndigits is undefined or ndigits is 0:
|
|
105
|
+
return Math.round(x)
|
|
106
|
+
factor = Math.pow(10, ndigits)
|
|
107
|
+
return Math.round(x * factor) / factor
|
|
77
108
|
|
|
78
109
|
def ρσ_bin(x):
|
|
79
110
|
if jstype(x) is not 'number' or x % 1 is not 0:
|
|
@@ -95,15 +126,17 @@ def ρσ_hex(x):
|
|
|
95
126
|
ans = '0x' + ans
|
|
96
127
|
return ans
|
|
97
128
|
|
|
98
|
-
def ρσ_enumerate(iterable):
|
|
99
|
-
|
|
129
|
+
def ρσ_enumerate(iterable, start):
|
|
130
|
+
offset = 0 if start is undefined else start
|
|
131
|
+
ans = {'_i': offset - 1}
|
|
100
132
|
ans[ρσ_iterator_symbol] = def():
|
|
101
133
|
return this
|
|
102
134
|
if ρσ_arraylike(iterable):
|
|
103
135
|
ans['next'] = def():
|
|
104
136
|
this._i += 1
|
|
105
|
-
|
|
106
|
-
|
|
137
|
+
idx = this._i - offset
|
|
138
|
+
if idx < iterable.length:
|
|
139
|
+
return {'done': False, 'value': [this._i, iterable[idx]]}
|
|
107
140
|
return v"{'done':true}"
|
|
108
141
|
return ans
|
|
109
142
|
if jstype(iterable[ρσ_iterator_symbol]) is 'function':
|
|
@@ -114,9 +147,9 @@ def ρσ_enumerate(iterable):
|
|
|
114
147
|
if r.done:
|
|
115
148
|
return v"{'done':true}"
|
|
116
149
|
this._i += 1
|
|
117
|
-
return
|
|
150
|
+
return {'done': False, 'value': [this._i, r.value]}
|
|
118
151
|
return ans
|
|
119
|
-
return ρσ_enumerate(Object.keys(iterable))
|
|
152
|
+
return ρσ_enumerate(Object.keys(iterable), start)
|
|
120
153
|
|
|
121
154
|
def ρσ_reversed(iterable):
|
|
122
155
|
if ρσ_arraylike(iterable):
|
|
@@ -131,7 +164,22 @@ def ρσ_reversed(iterable):
|
|
|
131
164
|
return ans
|
|
132
165
|
raise TypeError('reversed() can only be called on arrays or strings')
|
|
133
166
|
|
|
134
|
-
def ρσ_iter(iterable):
|
|
167
|
+
def ρσ_iter(iterable, sentinel):
|
|
168
|
+
# Two-argument form: iter(callable, sentinel) — call repeatedly until sentinel is returned
|
|
169
|
+
if arguments.length >= 2:
|
|
170
|
+
callable_ = iterable
|
|
171
|
+
ans = v'{"_callable":callable_,"_sentinel":sentinel,"_done":false}'
|
|
172
|
+
ans[ρσ_iterator_symbol] = def():
|
|
173
|
+
return this
|
|
174
|
+
ans['next'] = def():
|
|
175
|
+
if this._done:
|
|
176
|
+
return v"{'done':true}"
|
|
177
|
+
val = ρσ_callable_call(this._callable)
|
|
178
|
+
if v'val === this._sentinel':
|
|
179
|
+
this._done = True
|
|
180
|
+
return v"{'done':true}"
|
|
181
|
+
return v"{'done':false,'value':val}"
|
|
182
|
+
return ans
|
|
135
183
|
# Generate a JavaScript iterator object from iterable
|
|
136
184
|
if jstype(iterable[ρσ_iterator_symbol]) is 'function':
|
|
137
185
|
return iterable.keys() if jstype(Map) is 'function' and v'iterable instanceof Map' else iterable[ρσ_iterator_symbol]()
|
|
@@ -244,6 +292,67 @@ def ρσ_type(x):
|
|
|
244
292
|
return x.constructor
|
|
245
293
|
|
|
246
294
|
|
|
295
|
+
def ρσ_issubclass(cls, base):
|
|
296
|
+
if Array.isArray(base):
|
|
297
|
+
for b in base:
|
|
298
|
+
if ρσ_issubclass(cls, b):
|
|
299
|
+
return True
|
|
300
|
+
return False
|
|
301
|
+
if jstype(cls) is not 'function':
|
|
302
|
+
raise TypeError('issubclass() arg 1 must be a class')
|
|
303
|
+
if jstype(base) is not 'function':
|
|
304
|
+
raise TypeError('issubclass() arg 2 must be a class')
|
|
305
|
+
if cls is base:
|
|
306
|
+
return True
|
|
307
|
+
v'var proto = cls.prototype; while (proto !== null && proto !== undefined) { if (proto === base.prototype) return true; proto = Object.getPrototypeOf(proto); }'
|
|
308
|
+
return False
|
|
309
|
+
|
|
310
|
+
|
|
311
|
+
v'var ρσ_hash_id_counter = 0'
|
|
312
|
+
|
|
313
|
+
def ρσ_hash(obj):
|
|
314
|
+
v'var ρσ_t = typeof obj'
|
|
315
|
+
v'if (obj === null || obj === undefined) return 0'
|
|
316
|
+
v'if (ρσ_t === "boolean") return obj ? 1 : 0'
|
|
317
|
+
v'if (ρσ_t === "number") { return (obj === Math.floor(obj)) ? (obj | 0) : ((obj * 2654435761) | 0); }'
|
|
318
|
+
v'''if (ρσ_t === "string") {
|
|
319
|
+
var ρσ_h = 5381;
|
|
320
|
+
for (var ρσ_i = 0; ρσ_i < obj.length; ρσ_i++) {
|
|
321
|
+
ρσ_h = (((ρσ_h << 5) + ρσ_h) ^ obj.charCodeAt(ρσ_i)) | 0;
|
|
322
|
+
}
|
|
323
|
+
return ρσ_h;
|
|
324
|
+
}'''
|
|
325
|
+
v'if (typeof obj.__hash__ === "function") return obj.__hash__()'
|
|
326
|
+
v'if (Array.isArray(obj)) throw new TypeError("unhashable type: \'list\'")'
|
|
327
|
+
v'if (typeof ρσ_set === "function" && obj instanceof ρσ_set) throw new TypeError("unhashable type: \'set\'")'
|
|
328
|
+
v'if (typeof Set === "function" && obj instanceof Set) throw new TypeError("unhashable type: \'set\'")'
|
|
329
|
+
v'if (typeof ρσ_dict === "function" && obj instanceof ρσ_dict) throw new TypeError("unhashable type: \'dict\'")'
|
|
330
|
+
v'if (typeof Map === "function" && obj instanceof Map) throw new TypeError("unhashable type: \'dict\'")'
|
|
331
|
+
v'if (!obj.constructor || obj.constructor === Object) throw new TypeError("unhashable type: \'dict\'")'
|
|
332
|
+
v'if (obj.ρσ_object_id === undefined) obj.ρσ_object_id = ++ρσ_hash_id_counter'
|
|
333
|
+
v'return obj.ρσ_object_id'
|
|
334
|
+
|
|
335
|
+
|
|
336
|
+
def ρσ_next(iterator, defval):
|
|
337
|
+
if iterator is None or iterator is undefined:
|
|
338
|
+
raise TypeError('object is not an iterator')
|
|
339
|
+
if jstype(iterator.next) is 'function':
|
|
340
|
+
r = iterator.next()
|
|
341
|
+
if r.done:
|
|
342
|
+
if arguments.length > 1:
|
|
343
|
+
return defval
|
|
344
|
+
raise StopIteration()
|
|
345
|
+
return r.value
|
|
346
|
+
if jstype(iterator.__next__) is 'function':
|
|
347
|
+
try:
|
|
348
|
+
return iterator.__next__()
|
|
349
|
+
except StopIteration:
|
|
350
|
+
if arguments.length > 1:
|
|
351
|
+
return defval
|
|
352
|
+
raise
|
|
353
|
+
raise TypeError("object is not an iterator")
|
|
354
|
+
|
|
355
|
+
|
|
247
356
|
def ρσ_divmod(x, y):
|
|
248
357
|
if y is 0:
|
|
249
358
|
raise ZeroDivisionError('integer division or modulo by zero')
|
|
@@ -269,11 +378,72 @@ def ρσ_max(*args, **kwargs):
|
|
|
269
378
|
raise TypeError('expected at least one argument')
|
|
270
379
|
|
|
271
380
|
|
|
381
|
+
class ρσ_slice:
|
|
382
|
+
def __init__(self, start_or_stop, stop, step):
|
|
383
|
+
if arguments.length is 1:
|
|
384
|
+
self.start = None
|
|
385
|
+
self.stop = start_or_stop
|
|
386
|
+
self.step = None
|
|
387
|
+
elif arguments.length is 2:
|
|
388
|
+
self.start = start_or_stop
|
|
389
|
+
self.stop = stop
|
|
390
|
+
self.step = None
|
|
391
|
+
else:
|
|
392
|
+
self.start = start_or_stop
|
|
393
|
+
self.stop = stop
|
|
394
|
+
self.step = step
|
|
395
|
+
|
|
396
|
+
def indices(self, length):
|
|
397
|
+
step = 1 if self.step is None else self.step
|
|
398
|
+
if step is 0:
|
|
399
|
+
raise ValueError('slice step cannot be zero')
|
|
400
|
+
if step > 0:
|
|
401
|
+
lower = 0
|
|
402
|
+
upper = length
|
|
403
|
+
start = lower if self.start is None else self.start
|
|
404
|
+
stop = upper if self.stop is None else self.stop
|
|
405
|
+
else:
|
|
406
|
+
lower = -1
|
|
407
|
+
upper = length - 1
|
|
408
|
+
start = upper if self.start is None else self.start
|
|
409
|
+
stop = lower if self.stop is None else self.stop
|
|
410
|
+
# Only clamp values that were explicitly provided (None defaults are already correct).
|
|
411
|
+
if self.start is not None:
|
|
412
|
+
if start < 0:
|
|
413
|
+
start = max(start + length, lower)
|
|
414
|
+
if start > upper:
|
|
415
|
+
start = upper
|
|
416
|
+
if self.stop is not None:
|
|
417
|
+
if stop < 0:
|
|
418
|
+
stop = max(stop + length, lower)
|
|
419
|
+
if stop > upper:
|
|
420
|
+
stop = upper
|
|
421
|
+
return start, stop, step
|
|
422
|
+
|
|
423
|
+
def __repr__(self):
|
|
424
|
+
s = 'None' if self.start is None else String(self.start)
|
|
425
|
+
stop = 'None' if self.stop is None else String(self.stop)
|
|
426
|
+
step = 'None' if self.step is None else String(self.step)
|
|
427
|
+
return 'slice(' + s + ', ' + stop + ', ' + step + ')'
|
|
428
|
+
|
|
429
|
+
def __str__(self):
|
|
430
|
+
return self.__repr__()
|
|
431
|
+
|
|
432
|
+
def __eq__(self, other):
|
|
433
|
+
if not v'other instanceof ρσ_slice':
|
|
434
|
+
return False
|
|
435
|
+
return self.start is other.start and self.stop is other.stop and self.step is other.step
|
|
436
|
+
|
|
437
|
+
def __hash__(self):
|
|
438
|
+
raise TypeError("unhashable type: 'slice'")
|
|
439
|
+
|
|
440
|
+
|
|
272
441
|
v'var abs = Math.abs, max = ρσ_max.bind(Math.max), min = ρσ_max.bind(Math.min), bool = ρσ_bool, type = ρσ_type'
|
|
273
442
|
v'var float = ρσ_float, int = ρσ_int, arraylike = ρσ_arraylike_creator(), ρσ_arraylike = arraylike'
|
|
274
443
|
v'var id = ρσ_id, get_module = ρσ_get_module, pow = ρσ_pow, divmod = ρσ_divmod'
|
|
275
|
-
v'var dir = ρσ_dir, ord = ρσ_ord, chr = ρσ_chr, bin = ρσ_bin, hex = ρσ_hex, callable = ρσ_callable'
|
|
444
|
+
v'var dir = ρσ_dir, ord = ρσ_ord, chr = ρσ_chr, bin = ρσ_bin, hex = ρσ_hex, callable = ρσ_callable, round = ρσ_round'
|
|
276
445
|
v'var enumerate = ρσ_enumerate, iter = ρσ_iter, reversed = ρσ_reversed, len = ρσ_len'
|
|
277
|
-
v'var range = ρσ_range, getattr = ρσ_getattr, setattr = ρσ_setattr, hasattr = ρσ_hasattr'
|
|
446
|
+
v'var range = ρσ_range, getattr = ρσ_getattr, setattr = ρσ_setattr, hasattr = ρσ_hasattr, issubclass = ρσ_issubclass, hash = ρσ_hash, next = ρσ_next'
|
|
278
447
|
v'var ρσ_Ellipsis = Object.freeze({toString: function(){return "Ellipsis";}, __repr__: function(){return "Ellipsis";}})'
|
|
279
448
|
v'var Ellipsis = ρσ_Ellipsis'
|
|
449
|
+
v'var slice = ρσ_slice'
|
|
@@ -140,7 +140,7 @@ def ρσ_list_sort(key=None, reverse=False):
|
|
|
140
140
|
k = this[i] # noqa:undef
|
|
141
141
|
keymap.set(k, key(k))
|
|
142
142
|
posmap.set(k, i)
|
|
143
|
-
|
|
143
|
+
Array.prototype.sort.call(this, def (a, b): return mult * ρσ_list_sort_cmp(keymap.get(a), keymap.get(b), posmap.get(a), posmap.get(b));)
|
|
144
144
|
|
|
145
145
|
def ρσ_list_concat(): # ensure concat() returns an object of type list
|
|
146
146
|
ans = Array.prototype.concat.apply(this, arguments)
|
|
@@ -190,14 +190,18 @@ def ρσ_list_decorate(ans):
|
|
|
190
190
|
ans.inspect = ρσ_list_to_string
|
|
191
191
|
ans.extend = ρσ_list_extend
|
|
192
192
|
ans.index = ρσ_list_index
|
|
193
|
-
ans.
|
|
193
|
+
ans.jspop = Array.prototype.pop # native JS pop (no bounds check, ignores args)
|
|
194
|
+
ans.pop = ρσ_list_pop # Python pop (bounds-checked, supports negative index)
|
|
195
|
+
ans.pypop = ρσ_list_pop # backward-compat alias for pop
|
|
194
196
|
ans.remove = ρσ_list_remove
|
|
195
197
|
ans.insert = ρσ_list_insert
|
|
196
198
|
ans.copy = ρσ_list_copy
|
|
197
199
|
ans.clear = ρσ_list_clear
|
|
198
200
|
ans.count = ρσ_list_count
|
|
199
201
|
ans.concat = ρσ_list_concat
|
|
200
|
-
ans.
|
|
202
|
+
ans.jssort = Array.prototype.sort # native JS sort (lexicographic by default)
|
|
203
|
+
ans.sort = ρσ_list_sort # Python sort (numeric by default)
|
|
204
|
+
ans.pysort = ρσ_list_sort # backward-compat alias for sort
|
|
201
205
|
ans.slice = ρσ_list_slice
|
|
202
206
|
ans.as_array = ρσ_list_as_array
|
|
203
207
|
ans.__len__ = ρσ_list_len
|
|
@@ -235,7 +239,7 @@ v'var list = ρσ_list_constructor, list_wrap = ρσ_list_decorate'
|
|
|
235
239
|
|
|
236
240
|
def sorted(iterable, key=None, reverse=False):
|
|
237
241
|
ans = ρσ_list_constructor(iterable)
|
|
238
|
-
ans.
|
|
242
|
+
ans.sort(key, reverse)
|
|
239
243
|
return ans
|
|
240
244
|
# }}}
|
|
241
245
|
|
|
@@ -468,7 +472,7 @@ Object.defineProperties(ρσ_set.prototype, {
|
|
|
468
472
|
return '{' + list(this).join(', ') + '}'
|
|
469
473
|
|
|
470
474
|
ρσ_set.prototype.__eq__ = def(other):
|
|
471
|
-
if
|
|
475
|
+
if v'!other || !other.jsset':
|
|
472
476
|
return False
|
|
473
477
|
if other.size is not this.size:
|
|
474
478
|
return False
|
|
@@ -490,6 +494,142 @@ def ρσ_set_wrap(x):
|
|
|
490
494
|
v'var set = ρσ_set, set_wrap = ρσ_set_wrap'
|
|
491
495
|
# }}}
|
|
492
496
|
|
|
497
|
+
# frozenset {{{
|
|
498
|
+
def ρσ_frozenset(iterable):
|
|
499
|
+
if v'this instanceof ρσ_frozenset':
|
|
500
|
+
this.jsset = new ρσ_set_implementation() # noqa:undef
|
|
501
|
+
ans = this
|
|
502
|
+
if iterable is undefined:
|
|
503
|
+
return ans
|
|
504
|
+
s = ans.jsset
|
|
505
|
+
if ρσ_arraylike(iterable):
|
|
506
|
+
for v'var i = 0; i < iterable.length; i++':
|
|
507
|
+
s.add(iterable[i])
|
|
508
|
+
elif jstype(iterable[ρσ_iterator_symbol]) is 'function':
|
|
509
|
+
iterator = iterable.keys() if jstype(Map) is 'function' and v'iterable instanceof Map' else iterable[ρσ_iterator_symbol]()
|
|
510
|
+
result = iterator.next()
|
|
511
|
+
while not result.done:
|
|
512
|
+
s.add(result.value)
|
|
513
|
+
result = iterator.next()
|
|
514
|
+
else:
|
|
515
|
+
keys = Object.keys(iterable)
|
|
516
|
+
for v'var j=0; j < keys.length; j++':
|
|
517
|
+
s.add(keys[j])
|
|
518
|
+
return ans
|
|
519
|
+
else:
|
|
520
|
+
return new ρσ_frozenset(iterable)
|
|
521
|
+
ρσ_frozenset.prototype.__name__ = 'frozenset'
|
|
522
|
+
|
|
523
|
+
Object.defineProperties(ρσ_frozenset.prototype, {
|
|
524
|
+
'length': { 'get': def(): return this.jsset.size; },
|
|
525
|
+
'size': { 'get': def(): return this.jsset.size; },
|
|
526
|
+
})
|
|
527
|
+
|
|
528
|
+
ρσ_frozenset.prototype.__len__ = def(): return this.jsset.size
|
|
529
|
+
ρσ_frozenset.prototype.has = ρσ_frozenset.prototype.__contains__ = def(x): return this.jsset.has(x)
|
|
530
|
+
ρσ_frozenset.prototype.copy = def(): return ρσ_frozenset(this)
|
|
531
|
+
ρσ_frozenset.prototype[ρσ_iterator_symbol] = def(): return this.jsset.values()
|
|
532
|
+
|
|
533
|
+
ρσ_frozenset.prototype.difference = def():
|
|
534
|
+
ans = new ρσ_frozenset()
|
|
535
|
+
s = ans.jsset
|
|
536
|
+
iterator = this.jsset.values()
|
|
537
|
+
r = iterator.next()
|
|
538
|
+
while not r.done:
|
|
539
|
+
x = r.value
|
|
540
|
+
has = False
|
|
541
|
+
for v'var i = 0; i < arguments.length; i++':
|
|
542
|
+
if arguments[i].has(x): # noqa:undef
|
|
543
|
+
has = True
|
|
544
|
+
break
|
|
545
|
+
if not has:
|
|
546
|
+
s.add(x)
|
|
547
|
+
r = iterator.next()
|
|
548
|
+
return ans
|
|
549
|
+
|
|
550
|
+
ρσ_frozenset.prototype.intersection = def():
|
|
551
|
+
ans = new ρσ_frozenset()
|
|
552
|
+
s = ans.jsset
|
|
553
|
+
iterator = this.jsset.values()
|
|
554
|
+
r = iterator.next()
|
|
555
|
+
while not r.done:
|
|
556
|
+
x = r.value
|
|
557
|
+
has = True
|
|
558
|
+
for v'var i = 0; i < arguments.length; i++':
|
|
559
|
+
if not arguments[i].has(x): # noqa:undef
|
|
560
|
+
has = False
|
|
561
|
+
break
|
|
562
|
+
if has:
|
|
563
|
+
s.add(x)
|
|
564
|
+
r = iterator.next()
|
|
565
|
+
return ans
|
|
566
|
+
|
|
567
|
+
ρσ_frozenset.prototype.isdisjoint = def(other):
|
|
568
|
+
iterator = this.jsset.values()
|
|
569
|
+
r = iterator.next()
|
|
570
|
+
while not r.done:
|
|
571
|
+
x = r.value
|
|
572
|
+
if other.has(x):
|
|
573
|
+
return False
|
|
574
|
+
r = iterator.next()
|
|
575
|
+
return True
|
|
576
|
+
|
|
577
|
+
ρσ_frozenset.prototype.issubset = def(other):
|
|
578
|
+
iterator = this.jsset.values()
|
|
579
|
+
r = iterator.next()
|
|
580
|
+
while not r.done:
|
|
581
|
+
x = r.value
|
|
582
|
+
if not other.has(x):
|
|
583
|
+
return False
|
|
584
|
+
r = iterator.next()
|
|
585
|
+
return True
|
|
586
|
+
|
|
587
|
+
ρσ_frozenset.prototype.issuperset = def(other):
|
|
588
|
+
s = this.jsset
|
|
589
|
+
iterator = other[ρσ_iterator_symbol]()
|
|
590
|
+
r = iterator.next()
|
|
591
|
+
while not r.done:
|
|
592
|
+
x = r.value
|
|
593
|
+
if not s.has(x):
|
|
594
|
+
return False
|
|
595
|
+
r = iterator.next()
|
|
596
|
+
return True
|
|
597
|
+
|
|
598
|
+
ρσ_frozenset.prototype.symmetric_difference = def(other):
|
|
599
|
+
return this.union(other).difference(this.intersection(other))
|
|
600
|
+
|
|
601
|
+
ρσ_frozenset.prototype.union = def():
|
|
602
|
+
ans = ρσ_frozenset(this)
|
|
603
|
+
s = ans.jsset
|
|
604
|
+
for v'var i=0; i < arguments.length; i++':
|
|
605
|
+
iterator = arguments[i][ρσ_iterator_symbol]() # noqa:undef
|
|
606
|
+
r = iterator.next()
|
|
607
|
+
while not r.done:
|
|
608
|
+
s.add(r.value)
|
|
609
|
+
r = iterator.next()
|
|
610
|
+
return ans
|
|
611
|
+
|
|
612
|
+
ρσ_frozenset.prototype.toString = ρσ_frozenset.prototype.__repr__ = ρσ_frozenset.prototype.__str__ = ρσ_frozenset.prototype.inspect = def():
|
|
613
|
+
return 'frozenset({' + list(this).join(', ') + '})'
|
|
614
|
+
|
|
615
|
+
ρσ_frozenset.prototype.__eq__ = def(other):
|
|
616
|
+
if v'!other || !other.jsset':
|
|
617
|
+
return False
|
|
618
|
+
if other.size is not this.size:
|
|
619
|
+
return False
|
|
620
|
+
if other.size is 0:
|
|
621
|
+
return True
|
|
622
|
+
iterator = other[ρσ_iterator_symbol]()
|
|
623
|
+
r = iterator.next()
|
|
624
|
+
while not r.done:
|
|
625
|
+
if not this.has(r.value):
|
|
626
|
+
return False
|
|
627
|
+
r = iterator.next()
|
|
628
|
+
return True
|
|
629
|
+
|
|
630
|
+
v'var frozenset = ρσ_frozenset'
|
|
631
|
+
# }}}
|
|
632
|
+
|
|
493
633
|
# dict {{{
|
|
494
634
|
v'var ρσ_dict_implementation'
|
|
495
635
|
|
|
@@ -704,6 +844,15 @@ Object.defineProperties(ρσ_dict.prototype, {
|
|
|
704
844
|
r = iterator.next()
|
|
705
845
|
return True
|
|
706
846
|
|
|
847
|
+
ρσ_dict.prototype.__or__ = def(other):
|
|
848
|
+
result = ρσ_dict(this)
|
|
849
|
+
result.update(other)
|
|
850
|
+
return result
|
|
851
|
+
|
|
852
|
+
ρσ_dict.prototype.__ior__ = def(other):
|
|
853
|
+
this.update(other)
|
|
854
|
+
return this
|
|
855
|
+
|
|
707
856
|
ρσ_dict.prototype.as_object = def(other):
|
|
708
857
|
ans = {}
|
|
709
858
|
iterator = this.jsmap.entries()
|