rapydscript-ns 0.8.2 → 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/PYTHON_DIFFERENCES_REPORT.md +291 -0
- package/PYTHON_FEATURE_COVERAGE.md +96 -5
- package/README.md +161 -46
- package/TODO.md +2 -283
- package/language-service/index.js +4474 -0
- package/language-service/language-service.d.ts +40 -0
- package/package.json +9 -7
- package/src/baselib-builtins.pyj +77 -1
- package/src/baselib-containers.pyj +8 -4
- package/src/baselib-internal.pyj +30 -1
- package/src/baselib-str.pyj +8 -1
- package/src/lib/collections.pyj +1 -1
- package/src/lib/numpy.pyj +10 -10
- package/src/monaco-language-service/analyzer.js +131 -9
- package/src/monaco-language-service/builtins.js +12 -2
- package/src/monaco-language-service/completions.js +170 -1
- package/src/monaco-language-service/diagnostics.js +1 -1
- package/src/monaco-language-service/index.js +17 -0
- package/src/monaco-language-service/scope.js +3 -0
- package/src/output/classes.pyj +20 -3
- package/src/output/codegen.pyj +1 -1
- package/src/output/functions.pyj +4 -16
- package/src/output/loops.pyj +0 -9
- package/src/output/modules.pyj +1 -4
- package/src/output/operators.pyj +14 -0
- package/src/output/stream.pyj +0 -13
- package/src/parse.pyj +17 -1
- 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 +110 -23
- 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 +5 -5
- package/test/unit/language-service-completions.js +180 -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 +35 -0
- package/test/unit/run-language-service.js +1 -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/web-repl/rapydscript.js +6 -40
- package/web-repl/language-service.js +0 -4187
|
@@ -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.2",
|
|
27
|
-
"license": "BSD-2-Clause",
|
|
28
|
-
"engines": {
|
|
29
|
-
"node": ">=0.12.0"
|
|
30
|
-
},
|
|
31
34
|
"maintainers": [
|
|
32
35
|
{
|
|
33
36
|
"name": "Michael Ficocelli",
|
|
@@ -39,7 +42,6 @@
|
|
|
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
47
|
"bin": {
|
package/src/baselib-builtins.pyj
CHANGED
|
@@ -164,7 +164,22 @@ def ρσ_reversed(iterable):
|
|
|
164
164
|
return ans
|
|
165
165
|
raise TypeError('reversed() can only be called on arrays or strings')
|
|
166
166
|
|
|
167
|
-
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
|
|
168
183
|
# Generate a JavaScript iterator object from iterable
|
|
169
184
|
if jstype(iterable[ρσ_iterator_symbol]) is 'function':
|
|
170
185
|
return iterable.keys() if jstype(Map) is 'function' and v'iterable instanceof Map' else iterable[ρσ_iterator_symbol]()
|
|
@@ -363,6 +378,66 @@ def ρσ_max(*args, **kwargs):
|
|
|
363
378
|
raise TypeError('expected at least one argument')
|
|
364
379
|
|
|
365
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
|
+
|
|
366
441
|
v'var abs = Math.abs, max = ρσ_max.bind(Math.max), min = ρσ_max.bind(Math.min), bool = ρσ_bool, type = ρσ_type'
|
|
367
442
|
v'var float = ρσ_float, int = ρσ_int, arraylike = ρσ_arraylike_creator(), ρσ_arraylike = arraylike'
|
|
368
443
|
v'var id = ρσ_id, get_module = ρσ_get_module, pow = ρσ_pow, divmod = ρσ_divmod'
|
|
@@ -371,3 +446,4 @@ v'var enumerate = ρσ_enumerate, iter = ρσ_iter, reversed = ρσ_reversed, le
|
|
|
371
446
|
v'var range = ρσ_range, getattr = ρσ_getattr, setattr = ρσ_setattr, hasattr = ρσ_hasattr, issubclass = ρσ_issubclass, hash = ρσ_hash, next = ρσ_next'
|
|
372
447
|
v'var ρσ_Ellipsis = Object.freeze({toString: function(){return "Ellipsis";}, __repr__: function(){return "Ellipsis";}})'
|
|
373
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
|
|
package/src/baselib-internal.pyj
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
# vim:fileencoding=utf-8
|
|
2
2
|
# License: BSD
|
|
3
3
|
|
|
4
|
-
# globals: exports, console, ρσ_iterator_symbol, ρσ_kwargs_symbol, ρσ_arraylike, ρσ_list_contains, ρσ_list_constructor, ρσ_str, ρσ_int, ρσ_float
|
|
4
|
+
# globals: exports, console, ρσ_iterator_symbol, ρσ_kwargs_symbol, ρσ_arraylike, ρσ_list_contains, ρσ_list_constructor, ρσ_str, ρσ_int, ρσ_float, ρσ_slice
|
|
5
5
|
|
|
6
6
|
def ρσ_eslice(arr, step, start, end):
|
|
7
7
|
if jstype(arr) is 'string' or v'arr instanceof String':
|
|
@@ -179,6 +179,11 @@ def ρσ_interpolate_kwargs_constructor(apply, f, supplied_args):
|
|
|
179
179
|
def ρσ_getitem(obj, key):
|
|
180
180
|
if obj.__getitem__:
|
|
181
181
|
return obj.__getitem__(key)
|
|
182
|
+
if v'typeof ρσ_slice !== "undefined" && key instanceof ρσ_slice':
|
|
183
|
+
return ρσ_eslice(obj,
|
|
184
|
+
v'(key.step !== null && key.step !== undefined) ? key.step : 1',
|
|
185
|
+
v'(key.start !== null && key.start !== undefined) ? key.start : undefined',
|
|
186
|
+
v'(key.stop !== null && key.stop !== undefined) ? key.stop : undefined')
|
|
182
187
|
if jstype(key) is 'number' and key < 0:
|
|
183
188
|
key += obj.length
|
|
184
189
|
return obj[key]
|
|
@@ -186,6 +191,10 @@ def ρσ_getitem(obj, key):
|
|
|
186
191
|
def ρσ_setitem(obj, key, val):
|
|
187
192
|
if obj.__setitem__:
|
|
188
193
|
obj.__setitem__(key, val)
|
|
194
|
+
elif v'typeof ρσ_slice !== "undefined" && key instanceof ρσ_slice':
|
|
195
|
+
ρσ_splice(obj, val,
|
|
196
|
+
v'(key.start !== null && key.start !== undefined) ? key.start : 0',
|
|
197
|
+
v'(key.stop !== null && key.stop !== undefined) ? key.stop : obj.length')
|
|
189
198
|
else:
|
|
190
199
|
if jstype(key) is 'number' and key < 0:
|
|
191
200
|
key += obj.length
|
|
@@ -195,6 +204,11 @@ def ρσ_setitem(obj, key, val):
|
|
|
195
204
|
def ρσ_delitem(obj, key):
|
|
196
205
|
if obj.__delitem__:
|
|
197
206
|
obj.__delitem__(key)
|
|
207
|
+
elif v'typeof ρσ_slice !== "undefined" && key instanceof ρσ_slice':
|
|
208
|
+
ρσ_delslice(obj,
|
|
209
|
+
v'(key.step !== null && key.step !== undefined) ? key.step : 1',
|
|
210
|
+
v'(key.start !== null && key.start !== undefined) ? key.start : undefined',
|
|
211
|
+
v'(key.stop !== null && key.stop !== undefined) ? key.stop : undefined')
|
|
198
212
|
elif jstype(obj.splice) is 'function':
|
|
199
213
|
obj.splice(key, 1)
|
|
200
214
|
else:
|
|
@@ -339,6 +353,21 @@ def ρσ_op_rshift(a, b):
|
|
|
339
353
|
if b is not None and jstype(b.__rrshift__) is 'function': return b.__rrshift__(a)
|
|
340
354
|
return a >> b
|
|
341
355
|
|
|
356
|
+
# List-concatenation helpers (always available, no overload_operators required).
|
|
357
|
+
# ρσ_list_add: used for the + operator; creates a new list when both sides are arrays.
|
|
358
|
+
# NOTE: the fallback uses v'a + b' (verbatim JS) to avoid recursive self-compilation.
|
|
359
|
+
def ρσ_list_add(a, b):
|
|
360
|
+
if Array.isArray(a) and Array.isArray(b):
|
|
361
|
+
return ρσ_list_constructor(a.concat(b))
|
|
362
|
+
return v'a + b'
|
|
363
|
+
|
|
364
|
+
# ρσ_list_iadd: used for +=; extends a in-place when both sides are arrays (Python semantics).
|
|
365
|
+
def ρσ_list_iadd(a, b):
|
|
366
|
+
if Array.isArray(a) and Array.isArray(b):
|
|
367
|
+
v'Array.prototype.push.apply(a, b)'
|
|
368
|
+
return a
|
|
369
|
+
return v'a + b'
|
|
370
|
+
|
|
342
371
|
# Unary operator overloading helpers
|
|
343
372
|
def ρσ_op_neg(a):
|
|
344
373
|
if a is not None and jstype(a.__neg__) is 'function': return a.__neg__()
|
package/src/baselib-str.pyj
CHANGED
|
@@ -829,4 +829,11 @@ define_str_func('zfill', def(width):
|
|
|
829
829
|
ρσ_str.whitespace = ' \t\n\r\x0b\x0c'
|
|
830
830
|
|
|
831
831
|
v'define_str_func = undefined'
|
|
832
|
-
|
|
832
|
+
def ρσ_format(value, spec):
|
|
833
|
+
if value is not None and value is not undefined and v'typeof value.__format__ === "function"':
|
|
834
|
+
return value.__format__(spec if spec is not undefined else '')
|
|
835
|
+
if spec is undefined or spec is '':
|
|
836
|
+
return ρσ_str(value)
|
|
837
|
+
return str.format('{:' + spec + '}', value)
|
|
838
|
+
|
|
839
|
+
v'var str = ρσ_str, repr = ρσ_repr, format = ρσ_format'
|
package/src/lib/collections.pyj
CHANGED
|
@@ -353,7 +353,7 @@ class Counter:
|
|
|
353
353
|
|
|
354
354
|
def most_common(self, n=None):
|
|
355
355
|
items = [[k, self._data[k]] for k in self._data]
|
|
356
|
-
items.
|
|
356
|
+
items.sort(key=def(pair): return -pair[1];)
|
|
357
357
|
if n is not None:
|
|
358
358
|
return items[:n]
|
|
359
359
|
return items
|
package/src/lib/numpy.pyj
CHANGED
|
@@ -1055,7 +1055,7 @@ def sort(a, axis=-1):
|
|
|
1055
1055
|
a = asarray(a)
|
|
1056
1056
|
if a.ndim is 1 or axis is None:
|
|
1057
1057
|
data = a._data.slice()
|
|
1058
|
-
data.
|
|
1058
|
+
data.jssort(def(x, y): return x - y;)
|
|
1059
1059
|
return ndarray(a.shape.slice(), a.dtype, data, True)
|
|
1060
1060
|
# 2D sort along last axis (axis=1 or axis=-1)
|
|
1061
1061
|
if axis < 0:
|
|
@@ -1066,7 +1066,7 @@ def sort(a, axis=-1):
|
|
|
1066
1066
|
if axis is 1:
|
|
1067
1067
|
for i in range(rows):
|
|
1068
1068
|
row = a._data.slice(i * cols, (i + 1) * cols)
|
|
1069
|
-
row.
|
|
1069
|
+
row.jssort(def(x, y): return x - y;)
|
|
1070
1070
|
for v in row:
|
|
1071
1071
|
new_data.push(v)
|
|
1072
1072
|
elif axis is 0:
|
|
@@ -1074,7 +1074,7 @@ def sort(a, axis=-1):
|
|
|
1074
1074
|
col = []
|
|
1075
1075
|
for i in range(rows):
|
|
1076
1076
|
col.push(a._data[i * cols + j])
|
|
1077
|
-
col.
|
|
1077
|
+
col.jssort(def(x, y): return x - y;)
|
|
1078
1078
|
for i in range(rows):
|
|
1079
1079
|
new_data[i * cols + j] = col[i]
|
|
1080
1080
|
if new_data.length is 0:
|
|
@@ -1089,14 +1089,14 @@ def argsort(a, axis=-1):
|
|
|
1089
1089
|
for i in range(a.size):
|
|
1090
1090
|
indices.push(i)
|
|
1091
1091
|
data = a._data.slice()
|
|
1092
|
-
indices.
|
|
1092
|
+
indices.jssort(def(i, j): return data[i] - data[j];)
|
|
1093
1093
|
return ndarray([a.size], 'int32', indices, True)
|
|
1094
1094
|
# Flatten and sort
|
|
1095
1095
|
data = a._data.slice()
|
|
1096
1096
|
indices = []
|
|
1097
1097
|
for i in range(data.length):
|
|
1098
1098
|
indices.push(i)
|
|
1099
|
-
indices.
|
|
1099
|
+
indices.jssort(def(i, j): return data[i] - data[j];)
|
|
1100
1100
|
return ndarray([indices.length], 'int32', indices, True)
|
|
1101
1101
|
|
|
1102
1102
|
def searchsorted(a, v, side='left'):
|
|
@@ -1157,7 +1157,7 @@ def where(condition, x=None, y=None):
|
|
|
1157
1157
|
def median(a, axis=None):
|
|
1158
1158
|
a = asarray(a)
|
|
1159
1159
|
data = a._data.slice()
|
|
1160
|
-
data.
|
|
1160
|
+
data.jssort(def(x, y): return x - y;)
|
|
1161
1161
|
n = data.length
|
|
1162
1162
|
if n % 2 is 1:
|
|
1163
1163
|
return data[Math.floor(n / 2)]
|
|
@@ -1166,7 +1166,7 @@ def median(a, axis=None):
|
|
|
1166
1166
|
def percentile(a, q, axis=None):
|
|
1167
1167
|
a = asarray(a)
|
|
1168
1168
|
data = a._data.slice()
|
|
1169
|
-
data.
|
|
1169
|
+
data.jssort(def(x, y): return x - y;)
|
|
1170
1170
|
n = data.length
|
|
1171
1171
|
idx = q / 100.0 * (n - 1)
|
|
1172
1172
|
lo = Math.floor(idx)
|
|
@@ -1497,7 +1497,7 @@ def union1d(ar1, ar2):
|
|
|
1497
1497
|
if not seen[key]:
|
|
1498
1498
|
seen[key] = True
|
|
1499
1499
|
unique.push(v)
|
|
1500
|
-
unique.
|
|
1500
|
+
unique.jssort(def(a, b): return a - b;)
|
|
1501
1501
|
return ndarray([unique.length], 'float64', unique, True)
|
|
1502
1502
|
|
|
1503
1503
|
def intersect1d(ar1, ar2):
|
|
@@ -1513,7 +1513,7 @@ def intersect1d(ar1, ar2):
|
|
|
1513
1513
|
if s[key] and not seen[key]:
|
|
1514
1514
|
seen[key] = True
|
|
1515
1515
|
result.push(v)
|
|
1516
|
-
result.
|
|
1516
|
+
result.jssort(def(a, b): return a - b;)
|
|
1517
1517
|
return ndarray([result.length], 'float64', result, True)
|
|
1518
1518
|
|
|
1519
1519
|
def setdiff1d(ar1, ar2):
|
|
@@ -1529,7 +1529,7 @@ def setdiff1d(ar1, ar2):
|
|
|
1529
1529
|
if not s[key] and not seen[key]:
|
|
1530
1530
|
seen[key] = True
|
|
1531
1531
|
result.push(v)
|
|
1532
|
-
result.
|
|
1532
|
+
result.jssort(def(a, b): return a - b;)
|
|
1533
1533
|
return ndarray([result.length], 'float64', result, True)
|
|
1534
1534
|
|
|
1535
1535
|
def in1d(ar1, ar2):
|
|
@@ -46,6 +46,95 @@ function dot_path_from_node(node, RS) {
|
|
|
46
46
|
return null;
|
|
47
47
|
}
|
|
48
48
|
|
|
49
|
+
/**
|
|
50
|
+
* Infer the type of a single return-value expression.
|
|
51
|
+
* Returns a type string ('list', 'dict', 'str', 'number', 'bool', or a class name),
|
|
52
|
+
* or null if the type cannot be determined from this expression alone.
|
|
53
|
+
*
|
|
54
|
+
* @param {object} node - The AST expression node (return value)
|
|
55
|
+
* @param {ScopeFrame} scope - The function's own scope frame (fully populated)
|
|
56
|
+
* @param {object} RS - RapydScript compiler object
|
|
57
|
+
* @returns {string|null}
|
|
58
|
+
*/
|
|
59
|
+
function infer_expr_type(node, scope, RS) {
|
|
60
|
+
if (node instanceof RS.AST_Array) return 'list';
|
|
61
|
+
if (node instanceof RS.AST_Object) return 'dict';
|
|
62
|
+
if (node instanceof RS.AST_String) return 'str';
|
|
63
|
+
if (node instanceof RS.AST_Number) return 'number';
|
|
64
|
+
if ((RS.AST_True && node instanceof RS.AST_True) ||
|
|
65
|
+
(RS.AST_False && node instanceof RS.AST_False)) return 'bool';
|
|
66
|
+
if (RS.AST_Null && node instanceof RS.AST_Null) return null; // None → not counted
|
|
67
|
+
|
|
68
|
+
if (node instanceof RS.AST_SymbolRef) {
|
|
69
|
+
const sym = scope ? scope.getSymbol(node.name) : null;
|
|
70
|
+
return (sym && sym.inferred_class) ? sym.inferred_class : null;
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
if (node instanceof RS.AST_BaseCall && node.expression instanceof RS.AST_SymbolRef) {
|
|
74
|
+
const name = node.expression.name;
|
|
75
|
+
// PascalCase → assume class constructor, use name as type
|
|
76
|
+
if (/^[A-Z]/.test(name)) return name;
|
|
77
|
+
// Known local function with an already-resolved return_type
|
|
78
|
+
const sym = scope ? scope.getSymbol(name) : null;
|
|
79
|
+
if (sym && sym.return_type) return sym.return_type;
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
return null;
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
/**
|
|
86
|
+
* Walk a function node's body, collect all return-expression types, and return
|
|
87
|
+
* the single inferred type if every non-None return agrees on one type.
|
|
88
|
+
* Skips nested function bodies (their returns don't belong to this function).
|
|
89
|
+
* Returns null if returns disagree, are unrecognised, or the body has no typed returns.
|
|
90
|
+
*
|
|
91
|
+
* @param {object} func_node - AST_Lambda node
|
|
92
|
+
* @param {ScopeFrame} func_scope - The function's own scope frame (already populated)
|
|
93
|
+
* @param {object} RS - RapydScript compiler object
|
|
94
|
+
* @returns {string|null}
|
|
95
|
+
*/
|
|
96
|
+
function collect_return_type(func_node, func_scope, RS) {
|
|
97
|
+
const types = new Set();
|
|
98
|
+
// Use a plain visitor object (same pattern as ScopeBuilder): _visit(node, descend)
|
|
99
|
+
// receives the node and a closure that walks its children; call descend() to recurse.
|
|
100
|
+
func_node.walk({
|
|
101
|
+
_visit: function (node, descend) {
|
|
102
|
+
// Stop descent into nested functions — their returns are not ours.
|
|
103
|
+
if (node !== func_node && node instanceof RS.AST_Lambda) return;
|
|
104
|
+
if (node instanceof RS.AST_Return && node.value) {
|
|
105
|
+
const t = infer_expr_type(node.value, func_scope, RS);
|
|
106
|
+
if (t) types.add(t);
|
|
107
|
+
}
|
|
108
|
+
if (descend) descend(); // recurse into children
|
|
109
|
+
},
|
|
110
|
+
});
|
|
111
|
+
return types.size === 1 ? [...types][0] : null;
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
/**
|
|
115
|
+
* Extract the return type annotation from a function node as a normalized string.
|
|
116
|
+
* Maps `-> [X]` to 'list', `-> {k: v}` to 'dict', `-> str` to 'str', etc.
|
|
117
|
+
* Returns null if no annotation or unrecognised shape.
|
|
118
|
+
* @param {object} func_node
|
|
119
|
+
* @param {object} RS
|
|
120
|
+
* @returns {string|null}
|
|
121
|
+
*/
|
|
122
|
+
function extract_return_type(func_node, RS) {
|
|
123
|
+
const ann = func_node.return_annotation;
|
|
124
|
+
if (!ann) return null;
|
|
125
|
+
if (ann instanceof RS.AST_Array) return 'list';
|
|
126
|
+
if (ann instanceof RS.AST_Object) return 'dict';
|
|
127
|
+
if (ann instanceof RS.AST_SymbolRef ||
|
|
128
|
+
(RS.AST_SymbolVar && ann instanceof RS.AST_SymbolVar)) {
|
|
129
|
+
const n = ann.name;
|
|
130
|
+
if (n === 'str' || n === 'string') return 'str';
|
|
131
|
+
if (n === 'int' || n === 'float' || n === 'number') return 'number';
|
|
132
|
+
if (n === 'bool' || n === 'boolean') return 'bool';
|
|
133
|
+
return n; // user-defined class name or 'list'/'dict' spelled out
|
|
134
|
+
}
|
|
135
|
+
return null;
|
|
136
|
+
}
|
|
137
|
+
|
|
49
138
|
/**
|
|
50
139
|
* Collect parameter descriptors from an AST_Lambda node.
|
|
51
140
|
* Handles regular args, positional-only (/), keyword-only (*), *args, and **kwargs.
|
|
@@ -94,10 +183,11 @@ function extract_params(lambda_node) {
|
|
|
94
183
|
|
|
95
184
|
class ScopeBuilder {
|
|
96
185
|
constructor(RS, map) {
|
|
97
|
-
this._RS
|
|
98
|
-
this._map
|
|
99
|
-
this._scopes
|
|
100
|
-
this.current_node
|
|
186
|
+
this._RS = RS;
|
|
187
|
+
this._map = map;
|
|
188
|
+
this._scopes = []; // stack of ScopeFrame
|
|
189
|
+
this.current_node = null;
|
|
190
|
+
this._current_from_module = null; // set while inside an AST_Import with argnames
|
|
101
191
|
}
|
|
102
192
|
|
|
103
193
|
_current_scope() {
|
|
@@ -143,6 +233,9 @@ class ScopeBuilder {
|
|
|
143
233
|
params: opts.params || null,
|
|
144
234
|
inferred_class: opts.inferred_class || null,
|
|
145
235
|
dts_call_path: opts.dts_call_path || null,
|
|
236
|
+
return_type: opts.return_type || null,
|
|
237
|
+
source_module: opts.source_module || null,
|
|
238
|
+
original_name: opts.original_name || null,
|
|
146
239
|
});
|
|
147
240
|
scope.addSymbol(sym);
|
|
148
241
|
return sym;
|
|
@@ -176,6 +269,7 @@ class ScopeBuilder {
|
|
|
176
269
|
scope_depth: parent.depth,
|
|
177
270
|
doc: extract_doc(node),
|
|
178
271
|
params: extract_params(node),
|
|
272
|
+
return_type: extract_return_type(node, RS),
|
|
179
273
|
});
|
|
180
274
|
parent.addSymbol(sym);
|
|
181
275
|
}
|
|
@@ -239,18 +333,28 @@ class ScopeBuilder {
|
|
|
239
333
|
defined_at: pos_from_token(node.start),
|
|
240
334
|
});
|
|
241
335
|
|
|
336
|
+
} else if (node instanceof RS.AST_Import && node.argnames) {
|
|
337
|
+
// `from X import name [as alias], ...` — record module name so children can use it.
|
|
338
|
+
const mod_node = node.module;
|
|
339
|
+
this._current_from_module = mod_node
|
|
340
|
+
? (mod_node.name || mod_node.value || null)
|
|
341
|
+
: null;
|
|
342
|
+
|
|
242
343
|
} else if (node instanceof RS.AST_ImportedVar) {
|
|
243
344
|
// `from X import name` or `from X import name as alias`
|
|
244
|
-
const
|
|
245
|
-
if (
|
|
345
|
+
const alias = (node.alias && node.alias.name) ? node.alias.name : node.name;
|
|
346
|
+
if (alias) {
|
|
246
347
|
this._add_symbol({
|
|
247
|
-
name,
|
|
248
|
-
kind:
|
|
249
|
-
defined_at:
|
|
348
|
+
name: alias,
|
|
349
|
+
kind: 'import',
|
|
350
|
+
defined_at: pos_from_token(node.start),
|
|
351
|
+
source_module: this._current_from_module || null,
|
|
352
|
+
original_name: (node.name !== alias) ? node.name : null,
|
|
250
353
|
});
|
|
251
354
|
}
|
|
252
355
|
|
|
253
356
|
} else if (node instanceof RS.AST_Import && !node.argnames) {
|
|
357
|
+
this._current_from_module = null;
|
|
254
358
|
// `import X` or `import X as Y` (no from-clause)
|
|
255
359
|
const name = (node.alias && node.alias.name)
|
|
256
360
|
? node.alias.name
|
|
@@ -456,6 +560,24 @@ class ScopeBuilder {
|
|
|
456
560
|
|
|
457
561
|
if (cont) cont();
|
|
458
562
|
|
|
563
|
+
// ------------------------------------------------------------------
|
|
564
|
+
// 3b. Post-traversal: infer return type for unannotated functions.
|
|
565
|
+
// The function's own scope is fully populated at this point, so
|
|
566
|
+
// local variable inferred_class values are available for lookup.
|
|
567
|
+
// ------------------------------------------------------------------
|
|
568
|
+
|
|
569
|
+
if (node instanceof RS.AST_Lambda && this._scopes.length > prev_depth) {
|
|
570
|
+
const func_scope = this._current_scope();
|
|
571
|
+
const parent_frame = func_scope ? func_scope.parent : null;
|
|
572
|
+
const fname = node.name ? node.name.name : null;
|
|
573
|
+
if (fname && parent_frame) {
|
|
574
|
+
const sym = parent_frame.getSymbol(fname);
|
|
575
|
+
if (sym && !sym.return_type) {
|
|
576
|
+
sym.return_type = collect_return_type(node, func_scope, RS);
|
|
577
|
+
}
|
|
578
|
+
}
|
|
579
|
+
}
|
|
580
|
+
|
|
459
581
|
// ------------------------------------------------------------------
|
|
460
582
|
// 4. Pop the scope we pushed (if any).
|
|
461
583
|
// ------------------------------------------------------------------
|
|
@@ -104,6 +104,11 @@ const STUBS = [
|
|
|
104
104
|
return_type: 'iterable',
|
|
105
105
|
doc: 'Return an iterator of elements from iterable for which function returns true.' }),
|
|
106
106
|
|
|
107
|
+
new BuiltinInfo({ name: 'format',
|
|
108
|
+
params: [p('value'), p('spec', { type: 'str', optional: true })],
|
|
109
|
+
return_type: 'str',
|
|
110
|
+
doc: 'Return value formatted according to the format spec string.\n\nEquivalent to calling `value.__format__(spec)` or applying spec as a `str.format()` format-spec field. The spec mini-language is the same as what follows `:` in f-strings and `str.format()` fields: alignment (`<>^=`), sign (`+-`), width, grouping (`,_`), precision (`.N`), and type (`bcdoxXeEfFgGns%`).\n\nExamples:\n\n format(42, \'08b\') # \'00101010\'\n format(3.14, \'.2f\') # \'3.14\'\n format(\'hi\', \'>10\') # \' hi\'\n format(42) # \'42\'' }),
|
|
111
|
+
|
|
107
112
|
new BuiltinInfo({ name: 'float', kind: 'class',
|
|
108
113
|
params: [p('x', { optional: true })],
|
|
109
114
|
return_type: 'float',
|
|
@@ -150,9 +155,9 @@ const STUBS = [
|
|
|
150
155
|
doc: 'Return True if cls is a subclass of classinfo. classinfo may be a class or tuple of classes.' }),
|
|
151
156
|
|
|
152
157
|
new BuiltinInfo({ name: 'iter',
|
|
153
|
-
params: [p('obj')],
|
|
158
|
+
params: [p('obj'), p('sentinel', { optional: true })],
|
|
154
159
|
return_type: 'iterator',
|
|
155
|
-
doc: '
|
|
160
|
+
doc: 'iter(iterable) → iterator over iterable. iter(callable, sentinel) → calls callable repeatedly until it returns sentinel.' }),
|
|
156
161
|
|
|
157
162
|
new BuiltinInfo({ name: 'len',
|
|
158
163
|
params: [p('s')],
|
|
@@ -219,6 +224,11 @@ const STUBS = [
|
|
|
219
224
|
return_type: 'set',
|
|
220
225
|
doc: 'Create a new set, optionally populated from an iterable.' }),
|
|
221
226
|
|
|
227
|
+
new BuiltinInfo({ name: 'slice', kind: 'class',
|
|
228
|
+
params: [p('start_or_stop', { type: 'int' }), p('stop', { type: 'int', optional: true }), p('step', { type: 'int', optional: true })],
|
|
229
|
+
return_type: 'slice',
|
|
230
|
+
doc: 'Create a slice object representing the set of indices specified by range(start, stop, step).\n\nForms:\n- `slice(stop)` — equivalent to `slice(None, stop, None)`\n- `slice(start, stop)` — equivalent to `slice(start, stop, None)`\n- `slice(start, stop, step)` — full form\n\nAttributes: `.start`, `.stop`, `.step` (each may be `None`).\n\nMethod: `.indices(length)` — returns `(start, stop, step)` normalized for a sequence of the given length.\n\nExample:\n\n s = slice(1, 5)\n lst[s] # same as lst[1:5]\n s.indices(10) # (1, 5, 1)' }),
|
|
231
|
+
|
|
222
232
|
new BuiltinInfo({ name: 'setattr',
|
|
223
233
|
params: [p('obj'), p('name', { type: 'str' }), p('value')],
|
|
224
234
|
return_type: 'None',
|