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
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:
|
|
@@ -282,6 +296,16 @@ def ρσ_op_mul(a, b):
|
|
|
282
296
|
if b is not None and jstype(b.__rmul__) is 'function': return b.__rmul__(a)
|
|
283
297
|
if (jstype(a) is 'string' or v'a instanceof String') and jstype(b) is 'number': return a.repeat(b)
|
|
284
298
|
if (jstype(b) is 'string' or v'b instanceof String') and jstype(a) is 'number': return b.repeat(a)
|
|
299
|
+
if Array.isArray(a) and jstype(b) is 'number':
|
|
300
|
+
result = v'[]'
|
|
301
|
+
for v'var ρσ_mi = 0; ρσ_mi < b; ρσ_mi++':
|
|
302
|
+
result = result.concat(a) # noqa:undef
|
|
303
|
+
return ρσ_list_constructor(result)
|
|
304
|
+
if Array.isArray(b) and jstype(a) is 'number':
|
|
305
|
+
result = v'[]'
|
|
306
|
+
for v'var ρσ_mi = 0; ρσ_mi < a; ρσ_mi++':
|
|
307
|
+
result = result.concat(b) # noqa:undef
|
|
308
|
+
return ρσ_list_constructor(result)
|
|
285
309
|
return a * b
|
|
286
310
|
|
|
287
311
|
def ρσ_op_truediv(a, b):
|
|
@@ -329,6 +353,21 @@ def ρσ_op_rshift(a, b):
|
|
|
329
353
|
if b is not None and jstype(b.__rrshift__) is 'function': return b.__rrshift__(a)
|
|
330
354
|
return a >> b
|
|
331
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
|
+
|
|
332
371
|
# Unary operator overloading helpers
|
|
333
372
|
def ρσ_op_neg(a):
|
|
334
373
|
if a is not None and jstype(a.__neg__) is 'function': return a.__neg__()
|
package/src/baselib-str.pyj
CHANGED
|
@@ -525,6 +525,40 @@ define_str_func('isspace', def():
|
|
|
525
525
|
return this.length > 0 and /^\s+$/.test(this)
|
|
526
526
|
)
|
|
527
527
|
|
|
528
|
+
define_str_func('isalpha', def():
|
|
529
|
+
return this.length > 0 and /^[a-zA-Z]+$/.test(this)
|
|
530
|
+
)
|
|
531
|
+
|
|
532
|
+
define_str_func('isdigit', def():
|
|
533
|
+
return this.length > 0 and /^\d+$/.test(this)
|
|
534
|
+
)
|
|
535
|
+
|
|
536
|
+
define_str_func('isalnum', def():
|
|
537
|
+
return this.length > 0 and /^[a-zA-Z0-9]+$/.test(this)
|
|
538
|
+
)
|
|
539
|
+
|
|
540
|
+
define_str_func('isidentifier', def():
|
|
541
|
+
return this.length > 0 and /^[a-zA-Z_][a-zA-Z0-9_]*$/.test(this)
|
|
542
|
+
)
|
|
543
|
+
|
|
544
|
+
define_str_func('casefold', def():
|
|
545
|
+
return this.toLowerCase()
|
|
546
|
+
)
|
|
547
|
+
|
|
548
|
+
define_str_func('removeprefix', def(prefix):
|
|
549
|
+
s = this.toString()
|
|
550
|
+
if s.startsWith(prefix):
|
|
551
|
+
return s.slice(prefix.length)
|
|
552
|
+
return s
|
|
553
|
+
)
|
|
554
|
+
|
|
555
|
+
define_str_func('removesuffix', def(suffix):
|
|
556
|
+
s = this.toString()
|
|
557
|
+
if suffix.length and s.endsWith(suffix):
|
|
558
|
+
return s.slice(0, s.length - suffix.length)
|
|
559
|
+
return s
|
|
560
|
+
)
|
|
561
|
+
|
|
528
562
|
define_str_func('join', def(iterable):
|
|
529
563
|
if Array.isArray(iterable):
|
|
530
564
|
return iterable.join(this)
|
|
@@ -795,4 +829,11 @@ define_str_func('zfill', def(width):
|
|
|
795
829
|
ρσ_str.whitespace = ' \t\n\r\x0b\x0c'
|
|
796
830
|
|
|
797
831
|
v'define_str_func = undefined'
|
|
798
|
-
|
|
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):
|
|
@@ -6,24 +6,12 @@
|
|
|
6
6
|
// const scopeMap = analyzer.analyze(sourceCode, { virtualFiles: {...} });
|
|
7
7
|
|
|
8
8
|
import { ScopeMap, ScopeFrame, SymbolInfo } from './scope.js';
|
|
9
|
+
import { build_scoped_flags } from './diagnostics.js';
|
|
9
10
|
|
|
10
11
|
// ---------------------------------------------------------------------------
|
|
11
12
|
// Helpers
|
|
12
13
|
// ---------------------------------------------------------------------------
|
|
13
14
|
|
|
14
|
-
function build_scoped_flags(flags_str) {
|
|
15
|
-
const result = Object.create(null);
|
|
16
|
-
if (!flags_str) return result;
|
|
17
|
-
flags_str.split(',').forEach(flag => {
|
|
18
|
-
flag = flag.trim();
|
|
19
|
-
if (!flag) return;
|
|
20
|
-
let val = true;
|
|
21
|
-
if (flag.startsWith('no_')) { val = false; flag = flag.slice(3); }
|
|
22
|
-
result[flag] = val;
|
|
23
|
-
});
|
|
24
|
-
return result;
|
|
25
|
-
}
|
|
26
|
-
|
|
27
15
|
/** Convert AST token position {line (1-indexed), col (0-indexed)} → {line, column} (both 1-indexed). */
|
|
28
16
|
function pos_from_token(tok) {
|
|
29
17
|
if (!tok) return { line: 1, column: 1 };
|
|
@@ -58,6 +46,95 @@ function dot_path_from_node(node, RS) {
|
|
|
58
46
|
return null;
|
|
59
47
|
}
|
|
60
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
|
+
|
|
61
138
|
/**
|
|
62
139
|
* Collect parameter descriptors from an AST_Lambda node.
|
|
63
140
|
* Handles regular args, positional-only (/), keyword-only (*), *args, and **kwargs.
|
|
@@ -106,10 +183,11 @@ function extract_params(lambda_node) {
|
|
|
106
183
|
|
|
107
184
|
class ScopeBuilder {
|
|
108
185
|
constructor(RS, map) {
|
|
109
|
-
this._RS
|
|
110
|
-
this._map
|
|
111
|
-
this._scopes
|
|
112
|
-
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
|
|
113
191
|
}
|
|
114
192
|
|
|
115
193
|
_current_scope() {
|
|
@@ -155,6 +233,9 @@ class ScopeBuilder {
|
|
|
155
233
|
params: opts.params || null,
|
|
156
234
|
inferred_class: opts.inferred_class || null,
|
|
157
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,
|
|
158
239
|
});
|
|
159
240
|
scope.addSymbol(sym);
|
|
160
241
|
return sym;
|
|
@@ -188,6 +269,7 @@ class ScopeBuilder {
|
|
|
188
269
|
scope_depth: parent.depth,
|
|
189
270
|
doc: extract_doc(node),
|
|
190
271
|
params: extract_params(node),
|
|
272
|
+
return_type: extract_return_type(node, RS),
|
|
191
273
|
});
|
|
192
274
|
parent.addSymbol(sym);
|
|
193
275
|
}
|
|
@@ -251,18 +333,28 @@ class ScopeBuilder {
|
|
|
251
333
|
defined_at: pos_from_token(node.start),
|
|
252
334
|
});
|
|
253
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
|
+
|
|
254
343
|
} else if (node instanceof RS.AST_ImportedVar) {
|
|
255
344
|
// `from X import name` or `from X import name as alias`
|
|
256
|
-
const
|
|
257
|
-
if (
|
|
345
|
+
const alias = (node.alias && node.alias.name) ? node.alias.name : node.name;
|
|
346
|
+
if (alias) {
|
|
258
347
|
this._add_symbol({
|
|
259
|
-
name,
|
|
260
|
-
kind:
|
|
261
|
-
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,
|
|
262
353
|
});
|
|
263
354
|
}
|
|
264
355
|
|
|
265
356
|
} else if (node instanceof RS.AST_Import && !node.argnames) {
|
|
357
|
+
this._current_from_module = null;
|
|
266
358
|
// `import X` or `import X as Y` (no from-clause)
|
|
267
359
|
const name = (node.alias && node.alias.name)
|
|
268
360
|
? node.alias.name
|
|
@@ -468,6 +560,24 @@ class ScopeBuilder {
|
|
|
468
560
|
|
|
469
561
|
if (cont) cont();
|
|
470
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
|
+
|
|
471
581
|
// ------------------------------------------------------------------
|
|
472
582
|
// 4. Pop the scope we pushed (if any).
|
|
473
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',
|
|
@@ -114,6 +119,11 @@ const STUBS = [
|
|
|
114
119
|
return_type: 'any',
|
|
115
120
|
doc: 'Return the value of the named attribute of obj. If not found, return default (or raise AttributeError).' }),
|
|
116
121
|
|
|
122
|
+
new BuiltinInfo({ name: 'hash',
|
|
123
|
+
params: [p('obj')],
|
|
124
|
+
return_type: 'int',
|
|
125
|
+
doc: 'Return the hash value of obj. Strings and numbers are hashed by value; class instances by identity. Raises TypeError for unhashable types (list, set, dict). Objects with __hash__ dispatch to it.' }),
|
|
126
|
+
|
|
117
127
|
new BuiltinInfo({ name: 'hasattr',
|
|
118
128
|
params: [p('obj'), p('name', { type: 'str' })],
|
|
119
129
|
return_type: 'bool',
|
|
@@ -139,10 +149,15 @@ const STUBS = [
|
|
|
139
149
|
return_type: 'bool',
|
|
140
150
|
doc: 'Return True if obj is an instance of classinfo (or a subclass thereof).' }),
|
|
141
151
|
|
|
152
|
+
new BuiltinInfo({ name: 'issubclass',
|
|
153
|
+
params: [p('cls'), p('classinfo')],
|
|
154
|
+
return_type: 'bool',
|
|
155
|
+
doc: 'Return True if cls is a subclass of classinfo. classinfo may be a class or tuple of classes.' }),
|
|
156
|
+
|
|
142
157
|
new BuiltinInfo({ name: 'iter',
|
|
143
|
-
params: [p('obj')],
|
|
158
|
+
params: [p('obj'), p('sentinel', { optional: true })],
|
|
144
159
|
return_type: 'iterator',
|
|
145
|
-
doc: '
|
|
160
|
+
doc: 'iter(iterable) → iterator over iterable. iter(callable, sentinel) → calls callable repeatedly until it returns sentinel.' }),
|
|
146
161
|
|
|
147
162
|
new BuiltinInfo({ name: 'len',
|
|
148
163
|
params: [p('s')],
|
|
@@ -209,6 +224,11 @@ const STUBS = [
|
|
|
209
224
|
return_type: 'set',
|
|
210
225
|
doc: 'Create a new set, optionally populated from an iterable.' }),
|
|
211
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
|
+
|
|
212
232
|
new BuiltinInfo({ name: 'setattr',
|
|
213
233
|
params: [p('obj'), p('name', { type: 'str' }), p('value')],
|
|
214
234
|
return_type: 'None',
|