rapydscript-ns 0.8.2 → 0.8.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.
Files changed (141) hide show
  1. package/.agignore +1 -1
  2. package/.github/workflows/ci.yml +38 -38
  3. package/=template.pyj +5 -5
  4. package/CHANGELOG.md +39 -0
  5. package/HACKING.md +103 -103
  6. package/LICENSE +24 -24
  7. package/PYTHON_DIFFERENCES_REPORT.md +291 -0
  8. package/PYTHON_FEATURE_COVERAGE.md +106 -15
  9. package/README.md +831 -52
  10. package/TODO.md +4 -286
  11. package/add-toc-to-readme +2 -2
  12. package/bin/export +75 -75
  13. package/bin/rapydscript +70 -70
  14. package/bin/web-repl-export +102 -102
  15. package/build +2 -2
  16. package/language-service/index.js +4623 -0
  17. package/language-service/language-service.d.ts +40 -0
  18. package/package.json +9 -7
  19. package/publish.py +37 -37
  20. package/release/baselib-plain-pretty.js +2006 -229
  21. package/release/baselib-plain-ugly.js +70 -3
  22. package/release/compiler.js +11554 -3870
  23. package/release/signatures.json +31 -29
  24. package/session.vim +4 -4
  25. package/setup.cfg +2 -2
  26. package/src/ast.pyj +93 -1
  27. package/src/baselib-builtins.pyj +99 -2
  28. package/src/baselib-containers.pyj +107 -4
  29. package/src/baselib-errors.pyj +44 -0
  30. package/src/baselib-internal.pyj +124 -5
  31. package/src/baselib-itertools.pyj +97 -97
  32. package/src/baselib-str.pyj +32 -1
  33. package/src/compiler.pyj +36 -36
  34. package/src/errors.pyj +30 -30
  35. package/src/lib/aes.pyj +646 -646
  36. package/src/lib/collections.pyj +1 -1
  37. package/src/lib/copy.pyj +120 -0
  38. package/src/lib/elementmaker.pyj +83 -83
  39. package/src/lib/encodings.pyj +126 -126
  40. package/src/lib/gettext.pyj +569 -569
  41. package/src/lib/itertools.pyj +580 -580
  42. package/src/lib/math.pyj +193 -193
  43. package/src/lib/numpy.pyj +10 -10
  44. package/src/lib/operator.pyj +11 -11
  45. package/src/lib/pythonize.pyj +20 -20
  46. package/src/lib/random.pyj +118 -118
  47. package/src/lib/re.pyj +470 -470
  48. package/src/lib/react.pyj +74 -0
  49. package/src/lib/traceback.pyj +63 -63
  50. package/src/lib/uuid.pyj +77 -77
  51. package/src/monaco-language-service/analyzer.js +131 -9
  52. package/src/monaco-language-service/builtins.js +17 -2
  53. package/src/monaco-language-service/completions.js +170 -1
  54. package/src/monaco-language-service/diagnostics.js +25 -3
  55. package/src/monaco-language-service/dts.js +550 -550
  56. package/src/monaco-language-service/index.js +17 -0
  57. package/src/monaco-language-service/scope.js +3 -0
  58. package/src/output/classes.pyj +128 -11
  59. package/src/output/codegen.pyj +17 -3
  60. package/src/output/comments.pyj +45 -45
  61. package/src/output/exceptions.pyj +201 -105
  62. package/src/output/functions.pyj +13 -16
  63. package/src/output/jsx.pyj +164 -0
  64. package/src/output/literals.pyj +28 -2
  65. package/src/output/loops.pyj +0 -9
  66. package/src/output/modules.pyj +2 -5
  67. package/src/output/operators.pyj +22 -2
  68. package/src/output/statements.pyj +2 -2
  69. package/src/output/stream.pyj +1 -13
  70. package/src/output/treeshake.pyj +182 -182
  71. package/src/output/utils.pyj +72 -72
  72. package/src/parse.pyj +434 -114
  73. package/src/string_interpolation.pyj +72 -72
  74. package/src/tokenizer.pyj +29 -0
  75. package/src/unicode_aliases.pyj +576 -576
  76. package/src/utils.pyj +192 -192
  77. package/test/_import_one.pyj +37 -37
  78. package/test/_import_two/__init__.pyj +11 -11
  79. package/test/_import_two/level2/deep.pyj +4 -4
  80. package/test/_import_two/other.pyj +6 -6
  81. package/test/_import_two/sub.pyj +13 -13
  82. package/test/aes_vectors.pyj +421 -421
  83. package/test/annotations.pyj +80 -80
  84. package/test/baselib.pyj +4 -4
  85. package/test/classes.pyj +56 -17
  86. package/test/collections.pyj +5 -5
  87. package/test/decorators.pyj +77 -77
  88. package/test/docstrings.pyj +39 -39
  89. package/test/elementmaker_test.pyj +45 -45
  90. package/test/functions.pyj +151 -151
  91. package/test/generators.pyj +41 -41
  92. package/test/generic.pyj +370 -370
  93. package/test/imports.pyj +72 -72
  94. package/test/internationalization.pyj +73 -73
  95. package/test/lint.pyj +164 -164
  96. package/test/loops.pyj +85 -85
  97. package/test/numpy.pyj +734 -734
  98. package/test/omit_function_metadata.pyj +20 -20
  99. package/test/python_compat.pyj +326 -0
  100. package/test/python_features.pyj +129 -29
  101. package/test/regexp.pyj +55 -55
  102. package/test/repl.pyj +121 -121
  103. package/test/scoped_flags.pyj +76 -76
  104. package/test/slice.pyj +105 -0
  105. package/test/str.pyj +25 -0
  106. package/test/unit/fixtures/fibonacci_expected.js +1 -1
  107. package/test/unit/index.js +2296 -71
  108. package/test/unit/language-service-builtins.js +70 -0
  109. package/test/unit/language-service-bundle.js +5 -5
  110. package/test/unit/language-service-completions.js +180 -0
  111. package/test/unit/language-service-dts.js +543 -543
  112. package/test/unit/language-service-hover.js +455 -455
  113. package/test/unit/language-service-index.js +350 -0
  114. package/test/unit/language-service-scope.js +255 -0
  115. package/test/unit/language-service.js +625 -4
  116. package/test/unit/run-language-service.js +1 -0
  117. package/test/unit/web-repl.js +437 -0
  118. package/tools/build-language-service.js +2 -2
  119. package/tools/cli.js +547 -547
  120. package/tools/compile.js +219 -219
  121. package/tools/compiler.js +0 -24
  122. package/tools/completer.js +131 -131
  123. package/tools/embedded_compiler.js +251 -251
  124. package/tools/export.js +3 -37
  125. package/tools/gettext.js +185 -185
  126. package/tools/ini.js +65 -65
  127. package/tools/msgfmt.js +187 -187
  128. package/tools/repl.js +223 -223
  129. package/tools/test.js +118 -118
  130. package/tools/utils.js +128 -128
  131. package/tools/web_repl.js +95 -95
  132. package/try +41 -41
  133. package/web-repl/env.js +196 -74
  134. package/web-repl/index.html +163 -163
  135. package/web-repl/main.js +252 -254
  136. package/web-repl/prism.css +139 -139
  137. package/web-repl/prism.js +113 -113
  138. package/web-repl/rapydscript.js +227 -139
  139. package/web-repl/sha1.js +25 -25
  140. package/hack_demo.pyj +0 -112
  141. package/web-repl/language-service.js +0 -4187
@@ -0,0 +1,74 @@
1
+ # vim:fileencoding=utf-8
2
+ # License: BSD
3
+ # RapydScript bindings for React.
4
+ #
5
+ # Supported: all standard hooks (useState, useEffect, useContext, useReducer,
6
+ # useCallback, useMemo, useRef, useImperativeHandle, useLayoutEffect,
7
+ # useDebugValue, useId, useTransition, useDeferredValue), core classes
8
+ # (Component, PureComponent), special elements (Fragment, StrictMode,
9
+ # Suspense, Profiler), and utilities (createElement, cloneElement,
10
+ # createContext, createRef, forwardRef, isValidElement, memo, lazy).
11
+ #
12
+ # Requires React to be available as a global variable. Load it via a CDN
13
+ # <script> tag, a bundler global shim, or any other mechanism before your
14
+ # compiled script runs.
15
+ #
16
+ # Usage:
17
+ # from __python__ import jsx
18
+ # from react import useState, useEffect, useRef
19
+ #
20
+ # def Counter():
21
+ # count, setCount = useState(0)
22
+ # def handleClick():
23
+ # setCount(count + 1)
24
+ # return <button onClick={handleClick}>{count}</button>
25
+ #
26
+ # For class components extend React.Component directly (no import needed):
27
+ #
28
+ # class MyComponent(React.Component):
29
+ # def render(self):
30
+ # return <div>{self.props.title}</div>
31
+
32
+ # ── Hooks (React 16.8+) ───────────────────────────────────────────────────────
33
+
34
+ useState = React.useState
35
+ useEffect = React.useEffect
36
+ useContext = React.useContext
37
+ useReducer = React.useReducer
38
+ useCallback = React.useCallback
39
+ useMemo = React.useMemo
40
+ useRef = React.useRef
41
+ useImperativeHandle = React.useImperativeHandle
42
+ useLayoutEffect = React.useLayoutEffect
43
+ useDebugValue = React.useDebugValue
44
+
45
+ # ── Hooks (React 18+) ────────────────────────────────────────────────────────
46
+
47
+ useId = React.useId
48
+ useTransition = React.useTransition
49
+ useDeferredValue = React.useDeferredValue
50
+ useSyncExternalStore = React.useSyncExternalStore
51
+ useInsertionEffect = React.useInsertionEffect
52
+
53
+ # ── Core classes ─────────────────────────────────────────────────────────────
54
+
55
+ Component = React.Component
56
+ PureComponent = React.PureComponent
57
+
58
+ # ── Special elements ─────────────────────────────────────────────────────────
59
+
60
+ Fragment = React.Fragment
61
+ StrictMode = React.StrictMode
62
+ Suspense = React.Suspense
63
+ Profiler = React.Profiler
64
+
65
+ # ── Utilities ────────────────────────────────────────────────────────────────
66
+
67
+ createElement = React.createElement
68
+ cloneElement = React.cloneElement
69
+ createContext = React.createContext
70
+ createRef = React.createRef
71
+ forwardRef = React.forwardRef
72
+ isValidElement = React.isValidElement
73
+ memo = React.memo
74
+ lazy = React.lazy
@@ -1,63 +1,63 @@
1
- # vim:fileencoding=utf-8
2
- # License: BSD Copyright: 2016, Kovid Goyal <kovid at kovidgoyal.net>
3
- # globals: ρσ_str, ρσ_last_exception
4
-
5
-
6
- def _get_internal_traceback(err):
7
- if isinstance(err, Exception) and err.stack:
8
- lines = ρσ_str.splitlines(err.stack)
9
- final_lines = v'[]'
10
- found_sentinel = False
11
- for i, line in enumerate(lines):
12
- sline = ρσ_str.strip(line)
13
- if i is 0:
14
- final_lines.push(line)
15
- continue
16
- if found_sentinel:
17
- final_lines.push(line)
18
- continue
19
- # These two conditions work on desktop Chrome and Firefox to identify the correct
20
- # line in the traceback.
21
- if sline.startsWith('at new ' + err.name) or sline.startsWith(err.name + '@'):
22
- found_sentinel = True
23
- return final_lines.join('\n')
24
- return err and err.stack
25
-
26
- def format_exception(exc, limit):
27
- if jstype(exc) is 'undefined':
28
- exc = ρσ_last_exception
29
- if not isinstance(exc, Error):
30
- if exc and exc.toString:
31
- return [exc.toString()]
32
- return []
33
- tb = _get_internal_traceback(exc)
34
- if tb:
35
- lines = ρσ_str.splitlines(tb)
36
- e = lines[0]
37
- lines = lines[1:]
38
- if limit:
39
- lines = lines[:limit+1] if limit > 0 else lines[limit:]
40
- lines.reverse()
41
- lines.push(e)
42
- lines.insert(0, 'Traceback (most recent call last):')
43
- return [l+'\n' for l in lines]
44
- return [exc.toString()]
45
-
46
- def format_exc(limit):
47
- return format_exception(ρσ_last_exception, limit).join('')
48
-
49
- def print_exc(limit):
50
- print(format_exc(limit))
51
-
52
- def format_stack(limit):
53
- stack = Error().stack
54
- if not stack:
55
- return []
56
- lines = str.splitlines(stack)[2:]
57
- lines.reverse()
58
- if limit:
59
- lines = lines[:limit+1] if limit > 0 else lines[limit:]
60
- return [l + '\n' for l in lines]
61
-
62
- def print_stack(limit):
63
- print(format_stack(limit).join(''))
1
+ # vim:fileencoding=utf-8
2
+ # License: BSD Copyright: 2016, Kovid Goyal <kovid at kovidgoyal.net>
3
+ # globals: ρσ_str, ρσ_last_exception
4
+
5
+
6
+ def _get_internal_traceback(err):
7
+ if isinstance(err, Exception) and err.stack:
8
+ lines = ρσ_str.splitlines(err.stack)
9
+ final_lines = v'[]'
10
+ found_sentinel = False
11
+ for i, line in enumerate(lines):
12
+ sline = ρσ_str.strip(line)
13
+ if i is 0:
14
+ final_lines.push(line)
15
+ continue
16
+ if found_sentinel:
17
+ final_lines.push(line)
18
+ continue
19
+ # These two conditions work on desktop Chrome and Firefox to identify the correct
20
+ # line in the traceback.
21
+ if sline.startsWith('at new ' + err.name) or sline.startsWith(err.name + '@'):
22
+ found_sentinel = True
23
+ return final_lines.join('\n')
24
+ return err and err.stack
25
+
26
+ def format_exception(exc, limit):
27
+ if jstype(exc) is 'undefined':
28
+ exc = ρσ_last_exception
29
+ if not isinstance(exc, Error):
30
+ if exc and exc.toString:
31
+ return [exc.toString()]
32
+ return []
33
+ tb = _get_internal_traceback(exc)
34
+ if tb:
35
+ lines = ρσ_str.splitlines(tb)
36
+ e = lines[0]
37
+ lines = lines[1:]
38
+ if limit:
39
+ lines = lines[:limit+1] if limit > 0 else lines[limit:]
40
+ lines.reverse()
41
+ lines.push(e)
42
+ lines.insert(0, 'Traceback (most recent call last):')
43
+ return [l+'\n' for l in lines]
44
+ return [exc.toString()]
45
+
46
+ def format_exc(limit):
47
+ return format_exception(ρσ_last_exception, limit).join('')
48
+
49
+ def print_exc(limit):
50
+ print(format_exc(limit))
51
+
52
+ def format_stack(limit):
53
+ stack = Error().stack
54
+ if not stack:
55
+ return []
56
+ lines = str.splitlines(stack)[2:]
57
+ lines.reverse()
58
+ if limit:
59
+ lines = lines[:limit+1] if limit > 0 else lines[limit:]
60
+ return [l + '\n' for l in lines]
61
+
62
+ def print_stack(limit):
63
+ print(format_stack(limit).join(''))
package/src/lib/uuid.pyj CHANGED
@@ -1,77 +1,77 @@
1
- # vim:fileencoding=utf-8
2
- # License: BSD Copyright: 2017, Kovid Goyal <kovid at kovidgoyal.net>
3
- # globals: crypto
4
- from __python__ import hash_literals
5
-
6
- from encodings import hexlify, urlsafe_b64decode, urlsafe_b64encode
7
-
8
- RFC_4122 = 1
9
-
10
- if jstype(crypto) is 'object' and crypto.getRandomValues:
11
- random_bytes = def (num):
12
- ans = Uint8Array(num or 16)
13
- crypto.getRandomValues(ans)
14
- return ans
15
- else:
16
- random_bytes = def (num):
17
- ans = Uint8Array(num or 16)
18
- for i in range(ans.length):
19
- ans[i] = Math.floor(Math.random() * 256)
20
- return ans
21
-
22
-
23
- def uuid4_bytes():
24
- data = random_bytes()
25
- data[6] = 0b01000000 | (data[6] & 0b1111)
26
- data[8] = (((data[8] >> 4) & 0b11 | 0b1000) << 4) | (data[8] & 0b1111)
27
- return data
28
-
29
-
30
- def as_str():
31
- h = this.hex
32
- return h[:8] + '-' + h[8:12] + '-' + h[12:16] + '-' + h[16:20] + '-' + h[20:]
33
-
34
-
35
- def uuid4():
36
- b = uuid4_bytes()
37
- return {
38
- 'hex': hexlify(b),
39
- 'bytes': b,
40
- 'variant': RFC_4122,
41
- 'version': 4,
42
- '__str__': as_str,
43
- 'toString': as_str,
44
- }
45
-
46
-
47
- def num_to_string(numbers, alphabet, pad_to_length):
48
- ans = v'[]'
49
- alphabet_len = alphabet.length
50
- numbers = Array.prototype.slice.call(numbers)
51
- for v'var i = 0; i < numbers.length - 1; i++':
52
- x = divmod(numbers[i], alphabet_len)
53
- numbers[i] = x[0]
54
- numbers[i+1] += x[1]
55
- for v'var i = 0; i < numbers.length; i++':
56
- number = numbers[i]
57
- while number:
58
- x = divmod(number, alphabet_len)
59
- number = x[0]
60
- ans.push(alphabet[x[1]])
61
- if pad_to_length and pad_to_length > ans.length:
62
- ans.push(alphabet[0].repeat(pad_to_length - ans.length))
63
- return ans.join('')
64
-
65
-
66
- def short_uuid():
67
- # A totally random uuid encoded using only URL and filename safe characters
68
- return urlsafe_b64encode(random_bytes(), '')
69
-
70
-
71
- def short_uuid4():
72
- # A uuid4 encoded using only URL and filename safe characters
73
- return urlsafe_b64encode(uuid4_bytes(), '')
74
-
75
-
76
- def decode_short_uuid(val):
77
- return urlsafe_b64decode(val + '==')
1
+ # vim:fileencoding=utf-8
2
+ # License: BSD Copyright: 2017, Kovid Goyal <kovid at kovidgoyal.net>
3
+ # globals: crypto
4
+ from __python__ import hash_literals
5
+
6
+ from encodings import hexlify, urlsafe_b64decode, urlsafe_b64encode
7
+
8
+ RFC_4122 = 1
9
+
10
+ if jstype(crypto) is 'object' and crypto.getRandomValues:
11
+ random_bytes = def (num):
12
+ ans = Uint8Array(num or 16)
13
+ crypto.getRandomValues(ans)
14
+ return ans
15
+ else:
16
+ random_bytes = def (num):
17
+ ans = Uint8Array(num or 16)
18
+ for i in range(ans.length):
19
+ ans[i] = Math.floor(Math.random() * 256)
20
+ return ans
21
+
22
+
23
+ def uuid4_bytes():
24
+ data = random_bytes()
25
+ data[6] = 0b01000000 | (data[6] & 0b1111)
26
+ data[8] = (((data[8] >> 4) & 0b11 | 0b1000) << 4) | (data[8] & 0b1111)
27
+ return data
28
+
29
+
30
+ def as_str():
31
+ h = this.hex
32
+ return h[:8] + '-' + h[8:12] + '-' + h[12:16] + '-' + h[16:20] + '-' + h[20:]
33
+
34
+
35
+ def uuid4():
36
+ b = uuid4_bytes()
37
+ return {
38
+ 'hex': hexlify(b),
39
+ 'bytes': b,
40
+ 'variant': RFC_4122,
41
+ 'version': 4,
42
+ '__str__': as_str,
43
+ 'toString': as_str,
44
+ }
45
+
46
+
47
+ def num_to_string(numbers, alphabet, pad_to_length):
48
+ ans = v'[]'
49
+ alphabet_len = alphabet.length
50
+ numbers = Array.prototype.slice.call(numbers)
51
+ for v'var i = 0; i < numbers.length - 1; i++':
52
+ x = divmod(numbers[i], alphabet_len)
53
+ numbers[i] = x[0]
54
+ numbers[i+1] += x[1]
55
+ for v'var i = 0; i < numbers.length; i++':
56
+ number = numbers[i]
57
+ while number:
58
+ x = divmod(number, alphabet_len)
59
+ number = x[0]
60
+ ans.push(alphabet[x[1]])
61
+ if pad_to_length and pad_to_length > ans.length:
62
+ ans.push(alphabet[0].repeat(pad_to_length - ans.length))
63
+ return ans.join('')
64
+
65
+
66
+ def short_uuid():
67
+ # A totally random uuid encoded using only URL and filename safe characters
68
+ return urlsafe_b64encode(random_bytes(), '')
69
+
70
+
71
+ def short_uuid4():
72
+ # A uuid4 encoded using only URL and filename safe characters
73
+ return urlsafe_b64encode(uuid4_bytes(), '')
74
+
75
+
76
+ def decode_short_uuid(val):
77
+ return urlsafe_b64decode(val + '==')
@@ -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 = RS;
98
- this._map = map;
99
- this._scopes = []; // stack of ScopeFrame
100
- this.current_node = null;
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 name = (node.alias && node.alias.name) ? node.alias.name : node.name;
245
- if (name) {
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: 'import',
249
- defined_at: pos_from_token(node.start),
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: 'Return an iterator object for obj.' }),
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')],
@@ -164,6 +169,11 @@ const STUBS = [
164
169
  return_type: 'list',
165
170
  doc: 'Create a list from an iterable, or an empty list.' }),
166
171
 
172
+ new BuiltinInfo({ name: 'next',
173
+ params: [p('iterator'), p('default', { optional: true })],
174
+ return_type: 'any',
175
+ doc: 'Retrieve the next item from an iterator. If the iterator is exhausted, return default; if default is not given, raise StopIteration.' }),
176
+
167
177
  new BuiltinInfo({ name: 'map',
168
178
  params: [p('function'), p('iterable'), p('*iterables', { rest: true })],
169
179
  return_type: 'iterable',
@@ -219,6 +229,11 @@ const STUBS = [
219
229
  return_type: 'set',
220
230
  doc: 'Create a new set, optionally populated from an iterable.' }),
221
231
 
232
+ new BuiltinInfo({ name: 'slice', kind: 'class',
233
+ params: [p('start_or_stop', { type: 'int' }), p('stop', { type: 'int', optional: true }), p('step', { type: 'int', optional: true })],
234
+ return_type: 'slice',
235
+ 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)' }),
236
+
222
237
  new BuiltinInfo({ name: 'setattr',
223
238
  params: [p('obj'), p('name', { type: 'str' }), p('value')],
224
239
  return_type: 'None',