koatl 0.3.20__tar.gz → 0.4.0__tar.gz
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.
- {koatl-0.3.20 → koatl-0.4.0}/Cargo.lock +1 -1
- {koatl-0.3.20 → koatl-0.4.0}/PKG-INFO +1 -1
- {koatl-0.3.20 → koatl-0.4.0}/koatl/Cargo.toml +1 -1
- {koatl-0.3.20 → koatl-0.4.0}/koatl/python/koatl/runtime/__init__.py +10 -0
- koatl-0.4.0/koatl/tests/e2e/base/elif.tl +92 -0
- koatl-0.4.0/koatl/tests/e2e/base/locals.tl +68 -0
- {koatl-0.3.20 → koatl-0.4.0}/koatl-core/src/ast.rs +1 -1
- {koatl-0.3.20 → koatl-0.4.0}/koatl-core/src/ast_builder.rs +1 -1
- {koatl-0.3.20 → koatl-0.4.0}/koatl-core/src/lift_cst.rs +19 -4
- {koatl-0.3.20 → koatl-0.4.0}/koatl-core/src/py/ast_builder.rs +5 -1
- {koatl-0.3.20 → koatl-0.4.0}/koatl-core/src/resolve_scopes.rs +66 -29
- {koatl-0.3.20 → koatl-0.4.0}/koatl-core/src/transform.rs +133 -20
- {koatl-0.3.20 → koatl-0.4.0}/koatl-parser/src/cst.rs +1 -0
- {koatl-0.3.20 → koatl-0.4.0}/koatl-parser/src/lexer.rs +4 -4
- {koatl-0.3.20 → koatl-0.4.0}/koatl-parser/src/parser.rs +19 -0
- {koatl-0.3.20 → koatl-0.4.0}/koatl-parser/src/simple_fmt.rs +19 -10
- {koatl-0.3.20 → koatl-0.4.0}/python/koatl/runtime/__init__.py +10 -0
- {koatl-0.3.20 → koatl-0.4.0}/Cargo.toml +0 -0
- {koatl-0.3.20 → koatl-0.4.0}/README.md +0 -0
- {koatl-0.3.20 → koatl-0.4.0}/koatl/.github/workflows/CI.yml +0 -0
- {koatl-0.3.20 → koatl-0.4.0}/koatl/.gitignore +0 -0
- {koatl-0.3.20 → koatl-0.4.0}/koatl/LICENSE +0 -0
- {koatl-0.3.20 → koatl-0.4.0}/koatl/README.md +0 -0
- {koatl-0.3.20 → koatl-0.4.0}/koatl/python/koatl/__init__.py +0 -0
- {koatl-0.3.20 → koatl-0.4.0}/koatl/python/koatl/__main__.py +0 -0
- {koatl-0.3.20 → koatl-0.4.0}/koatl/python/koatl/cli.py +0 -0
- {koatl-0.3.20 → koatl-0.4.0}/koatl/python/koatl/notebook/__init__.py +0 -0
- {koatl-0.3.20 → koatl-0.4.0}/koatl/python/koatl/notebook/magic.py +0 -0
- {koatl-0.3.20 → koatl-0.4.0}/koatl/python/koatl/prelude/__init__.tl +0 -0
- {koatl-0.3.20 → koatl-0.4.0}/koatl/python/koatl/runtime/meta_finder.py +0 -0
- {koatl-0.3.20 → koatl-0.4.0}/koatl/python/koatl/runtime/record.py +0 -0
- {koatl-0.3.20 → koatl-0.4.0}/koatl/python/koatl/runtime/vattr.py +0 -0
- {koatl-0.3.20 → koatl-0.4.0}/koatl/python/koatl/std/__init__.tl +0 -0
- {koatl-0.3.20 → koatl-0.4.0}/koatl/python/koatl/std/control/__init__.tl +0 -0
- {koatl-0.3.20 → koatl-0.4.0}/koatl/python/koatl/std/control/async.tl +0 -0
- {koatl-0.3.20 → koatl-0.4.0}/koatl/python/koatl/std/control/base.tl +0 -0
- {koatl-0.3.20 → koatl-0.4.0}/koatl/python/koatl/std/control/do.tl +0 -0
- {koatl-0.3.20 → koatl-0.4.0}/koatl/python/koatl/std/control/env.tl +0 -0
- {koatl-0.3.20 → koatl-0.4.0}/koatl/python/koatl/std/control/memo.tl +0 -0
- {koatl-0.3.20 → koatl-0.4.0}/koatl/python/koatl/std/control/result.tl +0 -0
- {koatl-0.3.20 → koatl-0.4.0}/koatl/python/koatl/std/data/__init__.tl +0 -0
- {koatl-0.3.20 → koatl-0.4.0}/koatl/python/koatl/std/data/list.tl +0 -0
- {koatl-0.3.20 → koatl-0.4.0}/koatl/python/koatl/std/data/record.tl +0 -0
- {koatl-0.3.20 → koatl-0.4.0}/koatl/python/koatl/std/ext.tl +0 -0
- {koatl-0.3.20 → koatl-0.4.0}/koatl/python/koatl/std/io.tl +0 -0
- {koatl-0.3.20 → koatl-0.4.0}/koatl/python/koatl/std/iter.tl +0 -0
- {koatl-0.3.20 → koatl-0.4.0}/koatl/python/koatl/std/json.tl +0 -0
- {koatl-0.3.20 → koatl-0.4.0}/koatl/python/koatl/std/lazy_module.tl +0 -0
- {koatl-0.3.20 → koatl-0.4.0}/koatl/python/koatl/std/pickle.tl +0 -0
- {koatl-0.3.20 → koatl-0.4.0}/koatl/python/koatl/std/re.tl +0 -0
- {koatl-0.3.20 → koatl-0.4.0}/koatl/python/koatl/std/trait.py +0 -0
- {koatl-0.3.20 → koatl-0.4.0}/koatl/requirements.txt +0 -0
- {koatl-0.3.20 → koatl-0.4.0}/koatl/src/emit_py.rs +0 -0
- {koatl-0.3.20 → koatl-0.4.0}/koatl/src/lib.rs +0 -0
- {koatl-0.3.20 → koatl-0.4.0}/koatl/tests/conftest.py +0 -0
- {koatl-0.3.20 → koatl-0.4.0}/koatl/tests/e2e/base/arguments.tl +0 -0
- {koatl-0.3.20 → koatl-0.4.0}/koatl/tests/e2e/base/containers.tl +0 -0
- {koatl-0.3.20 → koatl-0.4.0}/koatl/tests/e2e/base/data.txt +0 -0
- {koatl-0.3.20 → koatl-0.4.0}/koatl/tests/e2e/base/decorators.tl +0 -0
- {koatl-0.3.20 → koatl-0.4.0}/koatl/tests/e2e/base/destructure-for-and-fn.tl +0 -0
- {koatl-0.3.20 → koatl-0.4.0}/koatl/tests/e2e/base/destructure.tl +0 -0
- {koatl-0.3.20 → koatl-0.4.0}/koatl/tests/e2e/base/escape_ident.tl +0 -0
- {koatl-0.3.20 → koatl-0.4.0}/koatl/tests/e2e/base/fstr.tl +0 -0
- {koatl-0.3.20 → koatl-0.4.0}/koatl/tests/e2e/base/functions.tl +0 -0
- {koatl-0.3.20 → koatl-0.4.0}/koatl/tests/e2e/base/generator.tl +0 -0
- {koatl-0.3.20 → koatl-0.4.0}/koatl/tests/e2e/base/if_expr.tl +0 -0
- {koatl-0.3.20 → koatl-0.4.0}/koatl/tests/e2e/base/imports.tl +0 -0
- {koatl-0.3.20 → koatl-0.4.0}/koatl/tests/e2e/base/imports2.tl +0 -0
- {koatl-0.3.20 → koatl-0.4.0}/koatl/tests/e2e/base/loops.tl +0 -0
- {koatl-0.3.20 → koatl-0.4.0}/koatl/tests/e2e/base/match.tl +0 -0
- {koatl-0.3.20 → koatl-0.4.0}/koatl/tests/e2e/base/nary-list.tl +0 -0
- {koatl-0.3.20 → koatl-0.4.0}/koatl/tests/e2e/base/placeholder.tl +0 -0
- {koatl-0.3.20 → koatl-0.4.0}/koatl/tests/e2e/base/precedence.tl +0 -0
- {koatl-0.3.20 → koatl-0.4.0}/koatl/tests/e2e/base/scopes.tl +0 -0
- {koatl-0.3.20 → koatl-0.4.0}/koatl/tests/e2e/base/semantic_whitespace.tl +0 -0
- {koatl-0.3.20 → koatl-0.4.0}/koatl/tests/e2e/base/short_circuit.tl +0 -0
- {koatl-0.3.20 → koatl-0.4.0}/koatl/tests/e2e/base/with.tl +0 -0
- {koatl-0.3.20 → koatl-0.4.0}/koatl/tests/e2e/prelude/async.tl +0 -0
- {koatl-0.3.20 → koatl-0.4.0}/koatl/tests/e2e/prelude/aug_assign.tl +0 -0
- {koatl-0.3.20 → koatl-0.4.0}/koatl/tests/e2e/prelude/coal.tl +0 -0
- {koatl-0.3.20 → koatl-0.4.0}/koatl/tests/e2e/prelude/env.tl +0 -0
- {koatl-0.3.20 → koatl-0.4.0}/koatl/tests/e2e/prelude/iterables.tl +0 -0
- {koatl-0.3.20 → koatl-0.4.0}/koatl/tests/e2e/prelude/list.tl +0 -0
- {koatl-0.3.20 → koatl-0.4.0}/koatl/tests/e2e/prelude/memo.tl +0 -0
- {koatl-0.3.20 → koatl-0.4.0}/koatl/tests/e2e/prelude/record.tl +0 -0
- {koatl-0.3.20 → koatl-0.4.0}/koatl/tests/e2e/prelude/result.tl +0 -0
- {koatl-0.3.20 → koatl-0.4.0}/koatl/tests/e2e/prelude/slice.tl +0 -0
- {koatl-0.3.20 → koatl-0.4.0}/koatl/tests/e2e/prelude/try.tl +0 -0
- {koatl-0.3.20 → koatl-0.4.0}/koatl/tests/e2e/prelude/virtual.tl +0 -0
- {koatl-0.3.20 → koatl-0.4.0}/koatl/tests/e2e/test_modules/module0.tl +0 -0
- {koatl-0.3.20 → koatl-0.4.0}/koatl/tests/e2e/test_modules/module1.tl +0 -0
- {koatl-0.3.20 → koatl-0.4.0}/koatl/tests/e2e/test_modules/module2.tl +0 -0
- {koatl-0.3.20 → koatl-0.4.0}/koatl/tests/fail-parse/arguments_mixed_defaults.tl +0 -0
- {koatl-0.3.20 → koatl-0.4.0}/koatl/tests/fail-parse/arguments_multiple_kwargs.tl +0 -0
- {koatl-0.3.20 → koatl-0.4.0}/koatl/tests/fail-parse/arguments_multiple_kwonly_markers.tl +0 -0
- {koatl-0.3.20 → koatl-0.4.0}/koatl/tests/fail-parse/arguments_multiple_posonly_markers.tl +0 -0
- {koatl-0.3.20 → koatl-0.4.0}/koatl/tests/fail-parse/arguments_multiple_varargs.tl +0 -0
- {koatl-0.3.20 → koatl-0.4.0}/koatl/tests/fail-parse/arguments_posonly_after_kwonly.tl +0 -0
- {koatl-0.3.20 → koatl-0.4.0}/koatl/tests/fail-parse/arguments_vararg_and_kwonly_marker.tl +0 -0
- {koatl-0.3.20 → koatl-0.4.0}/koatl/tests/fail-parse/unmatched_closing_delimiter.tl +0 -0
- {koatl-0.3.20 → koatl-0.4.0}/koatl/tests/parse/arith.tl +0 -0
- {koatl-0.3.20 → koatl-0.4.0}/koatl/tests/parse/assign.tl +0 -0
- {koatl-0.3.20 → koatl-0.4.0}/koatl/tests/parse/block-comments.tl +0 -0
- {koatl-0.3.20 → koatl-0.4.0}/koatl/tests/parse/deco.tl +0 -0
- {koatl-0.3.20 → koatl-0.4.0}/koatl/tests/parse/func.tl +0 -0
- {koatl-0.3.20 → koatl-0.4.0}/koatl/tests/parse/matches.tl +0 -0
- {koatl-0.3.20 → koatl-0.4.0}/koatl/tests/parse/mutual-recursion.tl +0 -0
- {koatl-0.3.20 → koatl-0.4.0}/koatl/tests/parse/numbers.tl +0 -0
- {koatl-0.3.20 → koatl-0.4.0}/koatl/tests/parse/operators.tl +0 -0
- {koatl-0.3.20 → koatl-0.4.0}/koatl/tests/test_e2e.py +0 -0
- {koatl-0.3.20 → koatl-0.4.0}/koatl/tests/test_iterable.tl +0 -0
- {koatl-0.3.20 → koatl-0.4.0}/koatl/tests/test_parse.py +0 -0
- {koatl-0.3.20 → koatl-0.4.0}/koatl/tests/test_parse_fail.py +0 -0
- {koatl-0.3.20 → koatl-0.4.0}/koatl/tests/test_re.tl +0 -0
- {koatl-0.3.20 → koatl-0.4.0}/koatl/tests/util/__init__.py +0 -0
- {koatl-0.3.20 → koatl-0.4.0}/koatl-core/Cargo.toml +0 -0
- {koatl-0.3.20 → koatl-0.4.0}/koatl-core/src/inference.rs +0 -0
- {koatl-0.3.20 → koatl-0.4.0}/koatl-core/src/lib.rs +0 -0
- {koatl-0.3.20 → koatl-0.4.0}/koatl-core/src/main.rs +0 -0
- {koatl-0.3.20 → koatl-0.4.0}/koatl-core/src/py/ast.rs +0 -0
- {koatl-0.3.20 → koatl-0.4.0}/koatl-core/src/py/emit.rs +0 -0
- {koatl-0.3.20 → koatl-0.4.0}/koatl-core/src/py/mod.rs +0 -0
- {koatl-0.3.20 → koatl-0.4.0}/koatl-core/src/types.rs +0 -0
- {koatl-0.3.20 → koatl-0.4.0}/koatl-core/src/util.rs +0 -0
- {koatl-0.3.20 → koatl-0.4.0}/koatl-parser/Cargo.toml +0 -0
- {koatl-0.3.20 → koatl-0.4.0}/koatl-parser/src/lib.rs +0 -0
- {koatl-0.3.20 → koatl-0.4.0}/pyproject.toml +0 -0
- {koatl-0.3.20 → koatl-0.4.0}/python/koatl/__init__.py +0 -0
- {koatl-0.3.20 → koatl-0.4.0}/python/koatl/__main__.py +0 -0
- {koatl-0.3.20 → koatl-0.4.0}/python/koatl/cli.py +0 -0
- {koatl-0.3.20 → koatl-0.4.0}/python/koatl/notebook/__init__.py +0 -0
- {koatl-0.3.20 → koatl-0.4.0}/python/koatl/notebook/magic.py +0 -0
- {koatl-0.3.20 → koatl-0.4.0}/python/koatl/prelude/__init__.tl +0 -0
- {koatl-0.3.20 → koatl-0.4.0}/python/koatl/runtime/meta_finder.py +0 -0
- {koatl-0.3.20 → koatl-0.4.0}/python/koatl/runtime/record.py +0 -0
- {koatl-0.3.20 → koatl-0.4.0}/python/koatl/runtime/vattr.py +0 -0
- {koatl-0.3.20 → koatl-0.4.0}/python/koatl/std/__init__.tl +0 -0
- {koatl-0.3.20 → koatl-0.4.0}/python/koatl/std/control/__init__.tl +0 -0
- {koatl-0.3.20 → koatl-0.4.0}/python/koatl/std/control/async.tl +0 -0
- {koatl-0.3.20 → koatl-0.4.0}/python/koatl/std/control/base.tl +0 -0
- {koatl-0.3.20 → koatl-0.4.0}/python/koatl/std/control/do.tl +0 -0
- {koatl-0.3.20 → koatl-0.4.0}/python/koatl/std/control/env.tl +0 -0
- {koatl-0.3.20 → koatl-0.4.0}/python/koatl/std/control/memo.tl +0 -0
- {koatl-0.3.20 → koatl-0.4.0}/python/koatl/std/control/result.tl +0 -0
- {koatl-0.3.20 → koatl-0.4.0}/python/koatl/std/data/__init__.tl +0 -0
- {koatl-0.3.20 → koatl-0.4.0}/python/koatl/std/data/list.tl +0 -0
- {koatl-0.3.20 → koatl-0.4.0}/python/koatl/std/data/record.tl +0 -0
- {koatl-0.3.20 → koatl-0.4.0}/python/koatl/std/ext.tl +0 -0
- {koatl-0.3.20 → koatl-0.4.0}/python/koatl/std/io.tl +0 -0
- {koatl-0.3.20 → koatl-0.4.0}/python/koatl/std/iter.tl +0 -0
- {koatl-0.3.20 → koatl-0.4.0}/python/koatl/std/json.tl +0 -0
- {koatl-0.3.20 → koatl-0.4.0}/python/koatl/std/lazy_module.tl +0 -0
- {koatl-0.3.20 → koatl-0.4.0}/python/koatl/std/pickle.tl +0 -0
- {koatl-0.3.20 → koatl-0.4.0}/python/koatl/std/re.tl +0 -0
- {koatl-0.3.20 → koatl-0.4.0}/python/koatl/std/trait.py +0 -0
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import functools
|
|
2
2
|
import importlib
|
|
3
|
+
import builtins
|
|
3
4
|
from types import SimpleNamespace
|
|
4
5
|
|
|
5
6
|
from koatl.runtime.record import Record
|
|
@@ -33,6 +34,13 @@ def set_exports(package_name, globals_dict, exports, module_star_exports):
|
|
|
33
34
|
globals_dict["__all__"] = tuple(set(globals_dict["__all__"]) | exports)
|
|
34
35
|
|
|
35
36
|
|
|
37
|
+
def redirect_locals(locals_name_map, py_locals_dict):
|
|
38
|
+
new_locals = {}
|
|
39
|
+
for tl_name, py_name in locals_name_map.items():
|
|
40
|
+
new_locals[tl_name] = py_locals_dict[py_name]
|
|
41
|
+
return new_locals
|
|
42
|
+
|
|
43
|
+
|
|
36
44
|
__tl__ = SimpleNamespace(
|
|
37
45
|
Exception=Exception,
|
|
38
46
|
slice=slice,
|
|
@@ -42,6 +50,8 @@ __tl__ = SimpleNamespace(
|
|
|
42
50
|
record_literal=Record.from_dict_ref,
|
|
43
51
|
#
|
|
44
52
|
set_exports=set_exports,
|
|
53
|
+
redirect_locals=redirect_locals,
|
|
54
|
+
builtins=builtins,
|
|
45
55
|
#
|
|
46
56
|
vget=vget,
|
|
47
57
|
vhas=vhas,
|
|
@@ -0,0 +1,92 @@
|
|
|
1
|
+
import util.assert_eq
|
|
2
|
+
|
|
3
|
+
# Test basic elif
|
|
4
|
+
x = 2
|
|
5
|
+
let result
|
|
6
|
+
if x == 1:
|
|
7
|
+
result = "one"
|
|
8
|
+
elif x == 2:
|
|
9
|
+
result = "two"
|
|
10
|
+
else:
|
|
11
|
+
result = "other"
|
|
12
|
+
assert_eq(result, "two")
|
|
13
|
+
|
|
14
|
+
# Test multiple elif clauses
|
|
15
|
+
score = 75
|
|
16
|
+
let grade = if score >= 90:
|
|
17
|
+
"A"
|
|
18
|
+
elif score >= 80:
|
|
19
|
+
"B"
|
|
20
|
+
elif score >= 70:
|
|
21
|
+
"C"
|
|
22
|
+
elif score >= 60:
|
|
23
|
+
"D"
|
|
24
|
+
else:
|
|
25
|
+
"F"
|
|
26
|
+
assert_eq(grade, "C")
|
|
27
|
+
|
|
28
|
+
# Test elif without else
|
|
29
|
+
value = 5
|
|
30
|
+
output = "default"
|
|
31
|
+
if value < 0:
|
|
32
|
+
output = "negative"
|
|
33
|
+
elif value == 0:
|
|
34
|
+
output = "zero"
|
|
35
|
+
elif value > 0:
|
|
36
|
+
output = "positive"
|
|
37
|
+
assert_eq(output, "positive")
|
|
38
|
+
|
|
39
|
+
# Test elif with expressions
|
|
40
|
+
a, b = 10, 20
|
|
41
|
+
let relation
|
|
42
|
+
if a > b:
|
|
43
|
+
relation = "greater"
|
|
44
|
+
elif a < b:
|
|
45
|
+
relation = "less"
|
|
46
|
+
elif a == b:
|
|
47
|
+
relation = "equal"
|
|
48
|
+
assert_eq(relation, "less")
|
|
49
|
+
|
|
50
|
+
# Test nested if with elif
|
|
51
|
+
outer_val = 2
|
|
52
|
+
inner_val = 3
|
|
53
|
+
let result2
|
|
54
|
+
if outer_val == 1:
|
|
55
|
+
result2 = "outer1"
|
|
56
|
+
elif outer_val == 2:
|
|
57
|
+
if inner_val == 1:
|
|
58
|
+
result2 = "inner1"
|
|
59
|
+
elif inner_val == 2:
|
|
60
|
+
result2 = "inner2"
|
|
61
|
+
elif inner_val == 3:
|
|
62
|
+
result2 = "inner3"
|
|
63
|
+
else:
|
|
64
|
+
result2 = "inner_else"
|
|
65
|
+
else:
|
|
66
|
+
result2 = "outer_else"
|
|
67
|
+
assert_eq(result2, "inner3")
|
|
68
|
+
|
|
69
|
+
# Test elif in expression context (as part of block expression)
|
|
70
|
+
num = 15
|
|
71
|
+
msg = if num < 10:
|
|
72
|
+
"small"
|
|
73
|
+
elif num < 20:
|
|
74
|
+
"medium"
|
|
75
|
+
else:
|
|
76
|
+
"large"
|
|
77
|
+
assert_eq(msg, "medium")
|
|
78
|
+
|
|
79
|
+
# Test elif with complex conditions
|
|
80
|
+
x, y = 5, 11
|
|
81
|
+
let result3
|
|
82
|
+
if x > 10 and y > 10:
|
|
83
|
+
result3 = "both large"
|
|
84
|
+
elif x > 10 or y > 10:
|
|
85
|
+
result3 = "one large"
|
|
86
|
+
elif x == y:
|
|
87
|
+
result3 = "equal"
|
|
88
|
+
else:
|
|
89
|
+
result3 = "both small"
|
|
90
|
+
assert_eq(result3, "one large")
|
|
91
|
+
|
|
92
|
+
print("All elif tests passed!")
|
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
import util.assert_eq
|
|
2
|
+
|
|
3
|
+
# Test __locals__ in function scope with let declarations
|
|
4
|
+
test_function_locals = () =>
|
|
5
|
+
let y = 1
|
|
6
|
+
let z = 2
|
|
7
|
+
assert_eq(__locals__, {y: 1, z: 2})
|
|
8
|
+
|
|
9
|
+
test_function_locals()
|
|
10
|
+
|
|
11
|
+
# Test __locals__ in nested function
|
|
12
|
+
test_nested = () =>
|
|
13
|
+
let outer = 1
|
|
14
|
+
let inner = () =>
|
|
15
|
+
let inner_var = 2
|
|
16
|
+
assert_eq(__locals__, {inner_var: 2})
|
|
17
|
+
inner()
|
|
18
|
+
|
|
19
|
+
test_nested()
|
|
20
|
+
|
|
21
|
+
# Test __locals__ with function arguments (not mangled)
|
|
22
|
+
test_with_args = (arg1, arg2) =>
|
|
23
|
+
let local = 100
|
|
24
|
+
assert_eq(__locals__, {local: 100, arg1: arg1, arg2: arg2})
|
|
25
|
+
|
|
26
|
+
test_with_args(10, 20)
|
|
27
|
+
|
|
28
|
+
# Test __captures__ in nested function
|
|
29
|
+
test_nested_captures = () =>
|
|
30
|
+
let outer_var = 1
|
|
31
|
+
let another_outer = 2
|
|
32
|
+
let inner = () =>
|
|
33
|
+
let inner_var = 3
|
|
34
|
+
outer_var, another_outer
|
|
35
|
+
|
|
36
|
+
# Should capture outer_var and another_outer but not inner_var
|
|
37
|
+
assert_eq(__captures__, {assert_eq, inner, outer_var, another_outer})
|
|
38
|
+
inner()
|
|
39
|
+
|
|
40
|
+
test_nested_captures()
|
|
41
|
+
|
|
42
|
+
# Test __captures__ with multiple nesting levels
|
|
43
|
+
test_deep_nesting = () =>
|
|
44
|
+
let level1 = 1
|
|
45
|
+
let middle = () =>
|
|
46
|
+
let level2 = 2
|
|
47
|
+
let inner = () =>
|
|
48
|
+
let level3 = 3
|
|
49
|
+
# Should capture level1 and level2 but not level3
|
|
50
|
+
assert_eq(__captures__, {assert_eq, inner, middle, level1, level2})
|
|
51
|
+
inner()
|
|
52
|
+
middle()
|
|
53
|
+
|
|
54
|
+
test_deep_nesting()
|
|
55
|
+
|
|
56
|
+
test_no_captures = () =>
|
|
57
|
+
assert_eq(__captures__, {test_no_captures, assert_eq})
|
|
58
|
+
|
|
59
|
+
test_no_captures()
|
|
60
|
+
|
|
61
|
+
# Test __captures__ with function arguments in outer scope
|
|
62
|
+
test_args_captured = (arg1, arg2) =>
|
|
63
|
+
let local_outer = 100
|
|
64
|
+
let inner = () =>
|
|
65
|
+
assert_eq(__captures__, {assert_eq, inner, arg1, arg2, local_outer})
|
|
66
|
+
inner()
|
|
67
|
+
|
|
68
|
+
test_args_captured(10, 20)
|
|
@@ -74,7 +74,7 @@ pub enum ImportLeaf<'a> {
|
|
|
74
74
|
#[derive(Debug, Clone)]
|
|
75
75
|
pub struct ImportTree<'a> {
|
|
76
76
|
pub trunk: Vec<SIdent<'a>>,
|
|
77
|
-
pub leaf: Spanned<ImportLeaf<'a
|
|
77
|
+
pub leaf: Indirect<Spanned<ImportLeaf<'a>>>,
|
|
78
78
|
|
|
79
79
|
// number of dots prepending the trunk
|
|
80
80
|
pub level: usize,
|
|
@@ -208,15 +208,30 @@ impl<'src, 'tok> Lift<Indirect<ast::SExpr<'src>>> for cst::SExpr<'src, 'tok> {
|
|
|
208
208
|
body,
|
|
209
209
|
else_clause,
|
|
210
210
|
..
|
|
211
|
+
} => {
|
|
212
|
+
let else_expr = else_clause.as_ref().map(|(_, expr)| expr.lift());
|
|
213
|
+
ast::Expr::If(cond.lift(), body.lift(), else_expr)
|
|
211
214
|
}
|
|
212
|
-
|
|
215
|
+
cst::Expr::ClassicIf {
|
|
213
216
|
cond,
|
|
214
217
|
body,
|
|
218
|
+
elif_clauses,
|
|
215
219
|
else_clause,
|
|
216
220
|
..
|
|
217
221
|
} => {
|
|
218
|
-
|
|
219
|
-
|
|
222
|
+
// Convert elif chain into nested if-else
|
|
223
|
+
let mut result_else = else_clause.as_ref().map(|(_, expr)| expr.lift());
|
|
224
|
+
|
|
225
|
+
// Process elif clauses in reverse order to build nested structure
|
|
226
|
+
for (_, elif_cond, elif_body) in elif_clauses.iter().rev() {
|
|
227
|
+
let elif_body_lifted = elif_body.lift();
|
|
228
|
+
let span = elif_body_lifted.span;
|
|
229
|
+
let elif_expr = ast::Expr::If(elif_cond.lift(), elif_body_lifted, result_else)
|
|
230
|
+
.spanned(span);
|
|
231
|
+
result_else = Some(elif_expr.indirect());
|
|
232
|
+
}
|
|
233
|
+
|
|
234
|
+
ast::Expr::If(cond.lift(), body.lift(), result_else)
|
|
220
235
|
}
|
|
221
236
|
cst::Expr::Parenthesized { expr, .. } => return expr.lift(),
|
|
222
237
|
cst::Expr::Slice {
|
|
@@ -637,7 +652,7 @@ impl<'src, 'tok> Lift<ast::ImportTree<'src>> for cst::ImportTree<cst::STree<'src
|
|
|
637
652
|
.iter()
|
|
638
653
|
.map(|(ident, _)| ident.lift_as_ident())
|
|
639
654
|
.collect(),
|
|
640
|
-
leaf: self.leaf.value.lift().spanned(self.leaf.span),
|
|
655
|
+
leaf: self.leaf.value.lift().spanned(self.leaf.span).indirect(),
|
|
641
656
|
}
|
|
642
657
|
}
|
|
643
658
|
}
|
|
@@ -252,7 +252,11 @@ impl PyAstBuilder {
|
|
|
252
252
|
}
|
|
253
253
|
|
|
254
254
|
pub fn py_builtin<'src>(&self, name: &'static str) -> SPyExpr<'src> {
|
|
255
|
-
self.attribute(
|
|
255
|
+
self.attribute(
|
|
256
|
+
self.attribute(self.load_ident("__tl__"), "builtins", PyAccessCtx::Load),
|
|
257
|
+
name,
|
|
258
|
+
PyAccessCtx::Load,
|
|
259
|
+
)
|
|
256
260
|
}
|
|
257
261
|
|
|
258
262
|
pub fn subscript<'src>(
|
|
@@ -38,6 +38,7 @@ pub struct ResolveState<'src> {
|
|
|
38
38
|
pub export_stars: Vec<SIdent<'src>>,
|
|
39
39
|
|
|
40
40
|
pub resolutions: HashMap<RefHash, DeclarationKey>,
|
|
41
|
+
pub locals_scope_refs: HashMap<RefHash, ScopeKey>,
|
|
41
42
|
pub patterns: HashMap<RefHash, PatternInfo>,
|
|
42
43
|
|
|
43
44
|
// TODO: these should all be collapsed into the same thing...
|
|
@@ -62,7 +63,7 @@ pub struct ResolveState<'src> {
|
|
|
62
63
|
errors: TlErrs,
|
|
63
64
|
|
|
64
65
|
placeholder_stack: Vec<PlaceholderGuard>,
|
|
65
|
-
|
|
66
|
+
phantom_fn_stack: Vec<FnInfo>,
|
|
66
67
|
scope_stack: Vec<ScopeKey>,
|
|
67
68
|
}
|
|
68
69
|
|
|
@@ -99,6 +100,7 @@ impl<'src> ResolveState<'src> {
|
|
|
99
100
|
source,
|
|
100
101
|
|
|
101
102
|
resolutions: HashMap::new(),
|
|
103
|
+
locals_scope_refs: HashMap::new(),
|
|
102
104
|
functions: HashMap::new(),
|
|
103
105
|
patterns: HashMap::new(),
|
|
104
106
|
memo_fninfo: HashMap::new(),
|
|
@@ -114,7 +116,7 @@ impl<'src> ResolveState<'src> {
|
|
|
114
116
|
errors: TlErrs::new(),
|
|
115
117
|
|
|
116
118
|
placeholder_stack: Vec::new(),
|
|
117
|
-
|
|
119
|
+
phantom_fn_stack: Vec::new(),
|
|
118
120
|
scope_stack: Vec::new(),
|
|
119
121
|
}
|
|
120
122
|
}
|
|
@@ -137,14 +139,14 @@ impl<'src> ResolveState<'src> {
|
|
|
137
139
|
return Ok(found.decl);
|
|
138
140
|
}
|
|
139
141
|
|
|
140
|
-
let Some(fn_ctx) = self.
|
|
142
|
+
let Some(fn_ctx) = self.phantom_fn_stack.last_mut() else {
|
|
141
143
|
// This should never happen since if not fn_local, there must be at least one function context
|
|
142
144
|
return Err(simple_err("Internal error: no function context", ident.span).into());
|
|
143
145
|
};
|
|
144
146
|
|
|
145
147
|
fn_ctx.captures.insert(found.decl.clone().into());
|
|
146
148
|
|
|
147
|
-
for fn_ctx in self.
|
|
149
|
+
for fn_ctx in self.phantom_fn_stack.iter_mut().rev().skip(1) {
|
|
148
150
|
if fn_ctx.decls.contains(&found.decl) {
|
|
149
151
|
break;
|
|
150
152
|
}
|
|
@@ -160,7 +162,7 @@ impl<'src> ResolveState<'src> {
|
|
|
160
162
|
if lhs {
|
|
161
163
|
if scope.is_class || scope.is_global {
|
|
162
164
|
let decl = self.declarations.insert_declaration(
|
|
163
|
-
&mut self.
|
|
165
|
+
&mut self.phantom_fn_stack,
|
|
164
166
|
ident.clone(),
|
|
165
167
|
scope_key,
|
|
166
168
|
DeclType::Let,
|
|
@@ -174,7 +176,7 @@ impl<'src> ResolveState<'src> {
|
|
|
174
176
|
|
|
175
177
|
if !lhs {
|
|
176
178
|
let decl = self.declarations.insert_declaration(
|
|
177
|
-
&mut self.
|
|
179
|
+
&mut self.phantom_fn_stack,
|
|
178
180
|
ident.clone(),
|
|
179
181
|
self.root_scope,
|
|
180
182
|
DeclType::Let,
|
|
@@ -227,7 +229,7 @@ impl<'src> ResolveState<'src> {
|
|
|
227
229
|
let temp_scope_key = self.scopes.insert(dummy_scope);
|
|
228
230
|
|
|
229
231
|
let ph_decl = self.declarations.insert_declaration(
|
|
230
|
-
&mut self.
|
|
232
|
+
&mut self.phantom_fn_stack,
|
|
231
233
|
Ident("x".into()).spanned(span),
|
|
232
234
|
temp_scope_key,
|
|
233
235
|
DeclType::Let,
|
|
@@ -242,11 +244,11 @@ impl<'src> ResolveState<'src> {
|
|
|
242
244
|
self.placeholder_stack
|
|
243
245
|
.push(PlaceholderGuard::new(ph_decl, span));
|
|
244
246
|
|
|
245
|
-
self.
|
|
247
|
+
self.phantom_fn_stack.push(FnInfo::new());
|
|
246
248
|
|
|
247
249
|
let result = f(self);
|
|
248
250
|
|
|
249
|
-
let mut ph_fn_ctx = self.
|
|
251
|
+
let mut ph_fn_ctx = self.phantom_fn_stack.pop().unwrap();
|
|
250
252
|
let placeholder_ctx = self.placeholder_stack.pop().unwrap();
|
|
251
253
|
|
|
252
254
|
if placeholder_ctx.activated {
|
|
@@ -292,7 +294,7 @@ impl<'src> ResolveState<'src> {
|
|
|
292
294
|
self.set_generator(span);
|
|
293
295
|
}
|
|
294
296
|
|
|
295
|
-
if let Some(cur_fn_ctx) = self.
|
|
297
|
+
if let Some(cur_fn_ctx) = self.phantom_fn_stack.last_mut() {
|
|
296
298
|
cur_fn_ctx.captures.extend(ph_fn_ctx.captures);
|
|
297
299
|
}
|
|
298
300
|
|
|
@@ -352,9 +354,12 @@ impl<'src> ResolveState<'src> {
|
|
|
352
354
|
|
|
353
355
|
let key = *self.scope_stack.last().unwrap();
|
|
354
356
|
|
|
355
|
-
let decl =
|
|
356
|
-
self.
|
|
357
|
-
|
|
357
|
+
let decl = self.declarations.insert_declaration(
|
|
358
|
+
&mut self.phantom_fn_stack,
|
|
359
|
+
name.clone(),
|
|
360
|
+
key,
|
|
361
|
+
modifier,
|
|
362
|
+
);
|
|
358
363
|
|
|
359
364
|
if lifted {
|
|
360
365
|
self.scopes[key].lifted_decls.push(decl.clone());
|
|
@@ -366,7 +371,7 @@ impl<'src> ResolveState<'src> {
|
|
|
366
371
|
}
|
|
367
372
|
|
|
368
373
|
fn set_async(&mut self, span: Span) -> () {
|
|
369
|
-
if let Some(fn_ctx) = self.
|
|
374
|
+
if let Some(fn_ctx) = self.phantom_fn_stack.last_mut() {
|
|
370
375
|
fn_ctx.is_async = true;
|
|
371
376
|
} else if !self.allow_top_level_await {
|
|
372
377
|
self.errors.extend(simple_err(
|
|
@@ -377,7 +382,7 @@ impl<'src> ResolveState<'src> {
|
|
|
377
382
|
}
|
|
378
383
|
|
|
379
384
|
fn set_do(&mut self, span: Span) -> () {
|
|
380
|
-
if let Some(fn_ctx) = self.
|
|
385
|
+
if let Some(fn_ctx) = self.phantom_fn_stack.last_mut() {
|
|
381
386
|
fn_ctx.is_do = true;
|
|
382
387
|
} else {
|
|
383
388
|
self.errors.extend(simple_err(
|
|
@@ -388,7 +393,7 @@ impl<'src> ResolveState<'src> {
|
|
|
388
393
|
}
|
|
389
394
|
|
|
390
395
|
fn set_generator(&mut self, span: Span) -> () {
|
|
391
|
-
if let Some(fn_ctx) = self.
|
|
396
|
+
if let Some(fn_ctx) = self.phantom_fn_stack.last_mut() {
|
|
392
397
|
fn_ctx.is_generator = true;
|
|
393
398
|
} else {
|
|
394
399
|
self.errors.extend(simple_err(
|
|
@@ -650,7 +655,7 @@ fn error_ident_and_decl<'src>(
|
|
|
650
655
|
let expr = Expr::Ident(ident.clone()).spanned(span).indirect();
|
|
651
656
|
|
|
652
657
|
let decl = state.declarations.insert_declaration(
|
|
653
|
-
&mut state.
|
|
658
|
+
&mut state.phantom_fn_stack,
|
|
654
659
|
ident.clone(),
|
|
655
660
|
state.err_scope,
|
|
656
661
|
DeclType::Let,
|
|
@@ -901,7 +906,7 @@ fn pattern_scoped<'src>(
|
|
|
901
906
|
.iter()
|
|
902
907
|
.map(|x| {
|
|
903
908
|
state.declarations.insert_declaration(
|
|
904
|
-
&mut state.
|
|
909
|
+
&mut state.phantom_fn_stack,
|
|
905
910
|
x.clone().spanned(pattern.span),
|
|
906
911
|
scope_key,
|
|
907
912
|
DeclType::Let,
|
|
@@ -928,11 +933,11 @@ where
|
|
|
928
933
|
{
|
|
929
934
|
let fn_ctx = FnInfo::new();
|
|
930
935
|
|
|
931
|
-
state.
|
|
936
|
+
state.phantom_fn_stack.push(fn_ctx);
|
|
932
937
|
|
|
933
938
|
let ret = f(state);
|
|
934
939
|
|
|
935
|
-
let fn_ctx = state.
|
|
940
|
+
let fn_ctx = state.phantom_fn_stack.pop().unwrap();
|
|
936
941
|
|
|
937
942
|
if fn_ctx.is_async {
|
|
938
943
|
state.set_async(span);
|
|
@@ -989,6 +994,21 @@ impl<'src> SExprExt<'src> for Indirect<SExpr<'src>> {
|
|
|
989
994
|
let t = match self.value {
|
|
990
995
|
Expr::Literal(_) => return self,
|
|
991
996
|
Expr::Ident(ident) => {
|
|
997
|
+
// Check for special __locals__ identifier
|
|
998
|
+
if ident.value.0.as_ref() == "__locals__" {
|
|
999
|
+
let expr = Expr::Ident(ident).spanned(span).indirect();
|
|
1000
|
+
// Store the current scope for __locals__
|
|
1001
|
+
let current_scope = *state.scope_stack.last().unwrap();
|
|
1002
|
+
state
|
|
1003
|
+
.locals_scope_refs
|
|
1004
|
+
.insert(expr.as_ref().into(), current_scope);
|
|
1005
|
+
return expr;
|
|
1006
|
+
} else if ident.value.0.as_ref() == "__captures__" {
|
|
1007
|
+
let expr = Expr::Ident(ident).spanned(span).indirect();
|
|
1008
|
+
|
|
1009
|
+
return expr;
|
|
1010
|
+
}
|
|
1011
|
+
|
|
992
1012
|
let decl = match state.resolve(&ident, false) {
|
|
993
1013
|
Ok(decl) => decl,
|
|
994
1014
|
Err(errs) => {
|
|
@@ -1240,7 +1260,7 @@ impl<'src> SExprExt<'src> for Indirect<SExpr<'src>> {
|
|
|
1240
1260
|
let cap = capture.spanned(pattern.span);
|
|
1241
1261
|
|
|
1242
1262
|
let decl = state.declarations.insert_declaration(
|
|
1243
|
-
&mut state.
|
|
1263
|
+
&mut state.phantom_fn_stack,
|
|
1244
1264
|
cap,
|
|
1245
1265
|
scope,
|
|
1246
1266
|
DeclType::Let,
|
|
@@ -1264,7 +1284,7 @@ impl<'src> SExprExt<'src> for Indirect<SExpr<'src>> {
|
|
|
1264
1284
|
}
|
|
1265
1285
|
ArgDefItem::ArgSpread(arg) => {
|
|
1266
1286
|
let decl = state.declarations.insert_declaration(
|
|
1267
|
-
&mut state.
|
|
1287
|
+
&mut state.phantom_fn_stack,
|
|
1268
1288
|
arg.clone(),
|
|
1269
1289
|
scope,
|
|
1270
1290
|
DeclType::Let,
|
|
@@ -1276,7 +1296,7 @@ impl<'src> SExprExt<'src> for Indirect<SExpr<'src>> {
|
|
|
1276
1296
|
}
|
|
1277
1297
|
ArgDefItem::KwargSpread(arg) => {
|
|
1278
1298
|
let decl = state.declarations.insert_declaration(
|
|
1279
|
-
&mut state.
|
|
1299
|
+
&mut state.phantom_fn_stack,
|
|
1280
1300
|
arg.clone(),
|
|
1281
1301
|
scope,
|
|
1282
1302
|
DeclType::Let,
|
|
@@ -1312,7 +1332,7 @@ impl<'src> SExprExt<'src> for Indirect<SExpr<'src>> {
|
|
|
1312
1332
|
|
|
1313
1333
|
state.scopes[scope].locals.extend(decls);
|
|
1314
1334
|
|
|
1315
|
-
state.
|
|
1335
|
+
state.phantom_fn_stack.push(fn_info);
|
|
1316
1336
|
|
|
1317
1337
|
let n_scopes = state.scope_stack.len();
|
|
1318
1338
|
|
|
@@ -1341,7 +1361,9 @@ impl<'src> SExprExt<'src> for Indirect<SExpr<'src>> {
|
|
|
1341
1361
|
(n_fndef_lifted, n_classdef_lifted)
|
|
1342
1362
|
};
|
|
1343
1363
|
|
|
1344
|
-
let body = state
|
|
1364
|
+
let body = state
|
|
1365
|
+
.scoped(scope, |state| body.traverse_expecting_scope(state))
|
|
1366
|
+
.value;
|
|
1345
1367
|
|
|
1346
1368
|
{
|
|
1347
1369
|
// put back
|
|
@@ -1362,7 +1384,7 @@ impl<'src> SExprExt<'src> for Indirect<SExpr<'src>> {
|
|
|
1362
1384
|
}
|
|
1363
1385
|
}
|
|
1364
1386
|
|
|
1365
|
-
let fn_ctx = state.
|
|
1387
|
+
let fn_ctx = state.phantom_fn_stack.pop().unwrap();
|
|
1366
1388
|
let expr = Expr::Fn(items, body).spanned(span).indirect();
|
|
1367
1389
|
|
|
1368
1390
|
state.functions.insert(expr.as_ref().into(), fn_ctx);
|
|
@@ -1700,7 +1722,7 @@ impl<'src> SStmtExt<'src> for Indirect<SStmt<'src>> {
|
|
|
1700
1722
|
let scope = &mut state.scopes[scope_key];
|
|
1701
1723
|
|
|
1702
1724
|
let decl = state.declarations.insert_declaration(
|
|
1703
|
-
&mut state.
|
|
1725
|
+
&mut state.phantom_fn_stack,
|
|
1704
1726
|
alias.clone().unwrap_or(ident.clone()),
|
|
1705
1727
|
scope_key,
|
|
1706
1728
|
if reexport {
|
|
@@ -1712,6 +1734,10 @@ impl<'src> SStmtExt<'src> for Indirect<SStmt<'src>> {
|
|
|
1712
1734
|
|
|
1713
1735
|
state.declarations[decl].is_import = true;
|
|
1714
1736
|
|
|
1737
|
+
state
|
|
1738
|
+
.resolutions
|
|
1739
|
+
.insert(tree.leaf.as_ref().into(), decl.clone());
|
|
1740
|
+
|
|
1715
1741
|
scope.locals.push(decl);
|
|
1716
1742
|
}
|
|
1717
1743
|
ImportLeaf::This(alias) => {
|
|
@@ -1727,7 +1753,7 @@ impl<'src> SStmtExt<'src> for Indirect<SStmt<'src>> {
|
|
|
1727
1753
|
}
|
|
1728
1754
|
|
|
1729
1755
|
let decl = state.declarations.insert_declaration(
|
|
1730
|
-
&mut state.
|
|
1756
|
+
&mut state.phantom_fn_stack,
|
|
1731
1757
|
alias.clone().unwrap_or(trunk_accum.last().unwrap().clone()),
|
|
1732
1758
|
scope_key,
|
|
1733
1759
|
if reexport {
|
|
@@ -1739,9 +1765,20 @@ impl<'src> SStmtExt<'src> for Indirect<SStmt<'src>> {
|
|
|
1739
1765
|
|
|
1740
1766
|
state.declarations[decl].is_import = true;
|
|
1741
1767
|
|
|
1768
|
+
state
|
|
1769
|
+
.resolutions
|
|
1770
|
+
.insert(tree.leaf.as_ref().into(), decl.clone());
|
|
1771
|
+
|
|
1742
1772
|
scope.locals.push(decl);
|
|
1743
1773
|
}
|
|
1744
1774
|
ImportLeaf::Star => {
|
|
1775
|
+
if state.scope_stack.len() > 1 {
|
|
1776
|
+
state.errors.extend(simple_err(
|
|
1777
|
+
"Wildcard imports are only allowed in the global scope",
|
|
1778
|
+
tree.leaf.span,
|
|
1779
|
+
));
|
|
1780
|
+
}
|
|
1781
|
+
|
|
1745
1782
|
if reexport {
|
|
1746
1783
|
state
|
|
1747
1784
|
.export_stars
|
|
@@ -1777,7 +1814,7 @@ pub fn resolve_names<'src>(
|
|
|
1777
1814
|
})
|
|
1778
1815
|
.value;
|
|
1779
1816
|
|
|
1780
|
-
if !state.
|
|
1817
|
+
if !state.phantom_fn_stack.is_empty() {
|
|
1781
1818
|
panic!("Function context stack is not empty after resolving names");
|
|
1782
1819
|
}
|
|
1783
1820
|
|