koatl 0.1.24__tar.gz → 0.1.25__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.1.24 → koatl-0.1.25}/Cargo.lock +1 -1
- {koatl-0.1.24 → koatl-0.1.25}/PKG-INFO +1 -1
- {koatl-0.1.24 → koatl-0.1.25}/koatl/Cargo.toml +1 -1
- {koatl-0.1.24 → koatl-0.1.25}/koatl/python/koatl/prelude/functional/memo.tl +18 -6
- {koatl-0.1.24 → koatl-0.1.25/koatl}/python/koatl/prelude/functional/monad.tl +2 -1
- {koatl-0.1.24 → koatl-0.1.25/koatl}/python/koatl/prelude/functional/result.tl +43 -27
- {koatl-0.1.24 → koatl-0.1.25/koatl}/python/koatl/prelude/iterable.tl +2 -2
- {koatl-0.1.24 → koatl-0.1.25/koatl}/python/koatl/runtime/__init__.py +16 -7
- {koatl-0.1.24 → koatl-0.1.25}/koatl/python/koatl/runtime/helpers.py +9 -6
- {koatl-0.1.24 → koatl-0.1.25}/koatl/tests/e2e/base/coal.tl +0 -12
- koatl-0.1.25/koatl/tests/e2e/base/loops.tl +25 -0
- {koatl-0.1.24 → koatl-0.1.25}/koatl/tests/e2e/base/match.tl +0 -2
- koatl-0.1.25/koatl/tests/e2e/base/short_circuit.tl +42 -0
- {koatl-0.1.24 → koatl-0.1.25}/koatl/tests/e2e/prelude/memo.tl +1 -1
- {koatl-0.1.24/koatl/tests/e2e/base → koatl-0.1.25/koatl/tests/e2e/prelude}/try.tl +11 -4
- {koatl-0.1.24 → koatl-0.1.25}/koatl-core/parser/src/ast.rs +1 -1
- {koatl-0.1.24 → koatl-0.1.25}/koatl-core/src/resolve_scopes.rs +124 -29
- {koatl-0.1.24 → koatl-0.1.25}/koatl-core/src/transform.rs +203 -138
- {koatl-0.1.24 → koatl-0.1.25}/python/koatl/prelude/functional/memo.tl +18 -6
- {koatl-0.1.24/koatl → koatl-0.1.25}/python/koatl/prelude/functional/monad.tl +2 -1
- {koatl-0.1.24/koatl → koatl-0.1.25}/python/koatl/prelude/functional/result.tl +43 -27
- {koatl-0.1.24/koatl → koatl-0.1.25}/python/koatl/prelude/iterable.tl +2 -2
- {koatl-0.1.24/koatl → koatl-0.1.25}/python/koatl/runtime/__init__.py +16 -7
- {koatl-0.1.24 → koatl-0.1.25}/python/koatl/runtime/helpers.py +9 -6
- koatl-0.1.24/koatl/tests/e2e/base/loops.tl +0 -14
- {koatl-0.1.24 → koatl-0.1.25}/Cargo.toml +0 -0
- {koatl-0.1.24 → koatl-0.1.25}/README.md +0 -0
- {koatl-0.1.24 → koatl-0.1.25}/koatl/.github/workflows/CI.yml +0 -0
- {koatl-0.1.24 → koatl-0.1.25}/koatl/.gitignore +0 -0
- {koatl-0.1.24 → koatl-0.1.25}/koatl/LICENSE +0 -0
- {koatl-0.1.24 → koatl-0.1.25}/koatl/README.md +0 -0
- {koatl-0.1.24 → koatl-0.1.25}/koatl/python/koatl/__init__.py +0 -0
- {koatl-0.1.24 → koatl-0.1.25}/koatl/python/koatl/__main__.py +0 -0
- {koatl-0.1.24 → koatl-0.1.25}/koatl/python/koatl/cli.py +0 -0
- {koatl-0.1.24 → koatl-0.1.25}/koatl/python/koatl/notebook/__init__.py +0 -0
- {koatl-0.1.24 → koatl-0.1.25}/koatl/python/koatl/notebook/magic.py +0 -0
- {koatl-0.1.24 → koatl-0.1.25}/koatl/python/koatl/prelude/__init__.tl +0 -0
- {koatl-0.1.24 → koatl-0.1.25}/koatl/python/koatl/prelude/functional/__init__.tl +0 -0
- {koatl-0.1.24 → koatl-0.1.25}/koatl/python/koatl/prelude/functional/async.tl +0 -0
- {koatl-0.1.24 → koatl-0.1.25}/koatl/python/koatl/prelude/functional/async_util.py +0 -0
- {koatl-0.1.24 → koatl-0.1.25}/koatl/python/koatl/prelude/functional/list.tl +0 -0
- {koatl-0.1.24 → koatl-0.1.25}/koatl/python/koatl/prelude/functional/reader.tl +0 -0
- {koatl-0.1.24 → koatl-0.1.25}/koatl/python/koatl/runtime/meta_finder.py +0 -0
- {koatl-0.1.24 → koatl-0.1.25}/koatl/python/koatl/runtime/record.py +0 -0
- {koatl-0.1.24 → koatl-0.1.25}/koatl/python/koatl/runtime/virtual.py +0 -0
- {koatl-0.1.24 → koatl-0.1.25}/koatl/requirements.txt +0 -0
- {koatl-0.1.24 → koatl-0.1.25}/koatl/src/emit_py.rs +0 -0
- {koatl-0.1.24 → koatl-0.1.25}/koatl/src/lib.rs +0 -0
- {koatl-0.1.24 → koatl-0.1.25}/koatl/tests/e2e/base/containers.tl +0 -0
- {koatl-0.1.24 → koatl-0.1.25}/koatl/tests/e2e/base/decorators.tl +0 -0
- {koatl-0.1.24 → koatl-0.1.25}/koatl/tests/e2e/base/destructure-for-and-fn.tl +0 -0
- {koatl-0.1.24 → koatl-0.1.25}/koatl/tests/e2e/base/destructure.tl +0 -0
- {koatl-0.1.24 → koatl-0.1.25}/koatl/tests/e2e/base/escape_ident.tl +0 -0
- {koatl-0.1.24 → koatl-0.1.25}/koatl/tests/e2e/base/fstr.tl +0 -0
- {koatl-0.1.24 → koatl-0.1.25}/koatl/tests/e2e/base/functions.tl +0 -0
- {koatl-0.1.24 → koatl-0.1.25}/koatl/tests/e2e/base/generator.tl +0 -0
- {koatl-0.1.24 → koatl-0.1.25}/koatl/tests/e2e/base/if_expr.tl +0 -0
- {koatl-0.1.24 → koatl-0.1.25}/koatl/tests/e2e/base/imports.tl +0 -0
- {koatl-0.1.24 → koatl-0.1.25}/koatl/tests/e2e/base/nary-list.tl +0 -0
- {koatl-0.1.24 → koatl-0.1.25}/koatl/tests/e2e/base/placeholder.tl +0 -0
- {koatl-0.1.24 → koatl-0.1.25}/koatl/tests/e2e/base/precedence.tl +0 -0
- {koatl-0.1.24 → koatl-0.1.25}/koatl/tests/e2e/base/scopes.tl +0 -0
- {koatl-0.1.24 → koatl-0.1.25}/koatl/tests/e2e/base/semantic_whitespace.tl +0 -0
- {koatl-0.1.24 → koatl-0.1.25}/koatl/tests/e2e/destructure.tl +0 -0
- {koatl-0.1.24 → koatl-0.1.25}/koatl/tests/e2e/prelude/async.tl +0 -0
- {koatl-0.1.24 → koatl-0.1.25}/koatl/tests/e2e/prelude/iterables.tl +0 -0
- {koatl-0.1.24 → koatl-0.1.25}/koatl/tests/e2e/prelude/list.tl +0 -0
- {koatl-0.1.24 → koatl-0.1.25}/koatl/tests/e2e/prelude/reader.tl +0 -0
- {koatl-0.1.24 → koatl-0.1.25}/koatl/tests/e2e/prelude/result.tl +0 -0
- {koatl-0.1.24/koatl/tests/e2e/base → koatl-0.1.25/koatl/tests/e2e/prelude}/slice.tl +0 -0
- {koatl-0.1.24 → koatl-0.1.25}/koatl/tests/e2e/prelude/virtual.tl +0 -0
- {koatl-0.1.24 → koatl-0.1.25}/koatl/tests/e2e/util/__init__.py +0 -0
- {koatl-0.1.24 → koatl-0.1.25}/koatl/tests/e2e/util/module0.tl +0 -0
- {koatl-0.1.24 → koatl-0.1.25}/koatl/tests/e2e/util/module1.tl +0 -0
- {koatl-0.1.24 → koatl-0.1.25}/koatl/tests/e2e/util/module2.tl +0 -0
- {koatl-0.1.24 → koatl-0.1.25}/koatl/tests/parse/arith.tl +0 -0
- {koatl-0.1.24 → koatl-0.1.25}/koatl/tests/parse/assign.tl +0 -0
- {koatl-0.1.24 → koatl-0.1.25}/koatl/tests/parse/block-comments.tl +0 -0
- {koatl-0.1.24 → koatl-0.1.25}/koatl/tests/parse/deco.tl +0 -0
- {koatl-0.1.24 → koatl-0.1.25}/koatl/tests/parse/func.tl +0 -0
- {koatl-0.1.24 → koatl-0.1.25}/koatl/tests/parse/matches.tl +0 -0
- {koatl-0.1.24 → koatl-0.1.25}/koatl/tests/test_e2e.py +0 -0
- {koatl-0.1.24 → koatl-0.1.25}/koatl/tests/test_parse.py +0 -0
- {koatl-0.1.24 → koatl-0.1.25}/koatl-core/Cargo.toml +0 -0
- {koatl-0.1.24 → koatl-0.1.25}/koatl-core/parser/Cargo.toml +0 -0
- {koatl-0.1.24 → koatl-0.1.25}/koatl-core/parser/src/lexer.rs +0 -0
- {koatl-0.1.24 → koatl-0.1.25}/koatl-core/parser/src/lib.rs +0 -0
- {koatl-0.1.24 → koatl-0.1.25}/koatl-core/parser/src/parser.rs +0 -0
- {koatl-0.1.24 → koatl-0.1.25}/koatl-core/parser/src/util.rs +0 -0
- {koatl-0.1.24 → koatl-0.1.25}/koatl-core/parser/tests/lexer.rs +0 -0
- {koatl-0.1.24 → koatl-0.1.25}/koatl-core/src/inference.rs +0 -0
- {koatl-0.1.24 → koatl-0.1.25}/koatl-core/src/lib.rs +0 -0
- {koatl-0.1.24 → koatl-0.1.25}/koatl-core/src/main.rs +0 -0
- {koatl-0.1.24 → koatl-0.1.25}/koatl-core/src/parse_timer.rs +0 -0
- {koatl-0.1.24 → koatl-0.1.25}/koatl-core/src/parser.rs +0 -0
- {koatl-0.1.24 → koatl-0.1.25}/koatl-core/src/py/ast.rs +0 -0
- {koatl-0.1.24 → koatl-0.1.25}/koatl-core/src/py/emit.rs +0 -0
- {koatl-0.1.24 → koatl-0.1.25}/koatl-core/src/py/mod.rs +0 -0
- {koatl-0.1.24 → koatl-0.1.25}/koatl-core/src/py/util.rs +0 -0
- {koatl-0.1.24 → koatl-0.1.25}/koatl-core/src/types.rs +0 -0
- {koatl-0.1.24 → koatl-0.1.25}/koatl-core/src/util.rs +0 -0
- {koatl-0.1.24 → koatl-0.1.25}/pyproject.toml +0 -0
- {koatl-0.1.24 → koatl-0.1.25}/python/koatl/__init__.py +0 -0
- {koatl-0.1.24 → koatl-0.1.25}/python/koatl/__main__.py +0 -0
- {koatl-0.1.24 → koatl-0.1.25}/python/koatl/cli.py +0 -0
- {koatl-0.1.24 → koatl-0.1.25}/python/koatl/notebook/__init__.py +0 -0
- {koatl-0.1.24 → koatl-0.1.25}/python/koatl/notebook/magic.py +0 -0
- {koatl-0.1.24 → koatl-0.1.25}/python/koatl/prelude/__init__.tl +0 -0
- {koatl-0.1.24 → koatl-0.1.25}/python/koatl/prelude/functional/__init__.tl +0 -0
- {koatl-0.1.24 → koatl-0.1.25}/python/koatl/prelude/functional/async.tl +0 -0
- {koatl-0.1.24 → koatl-0.1.25}/python/koatl/prelude/functional/async_util.py +0 -0
- {koatl-0.1.24 → koatl-0.1.25}/python/koatl/prelude/functional/list.tl +0 -0
- {koatl-0.1.24 → koatl-0.1.25}/python/koatl/prelude/functional/reader.tl +0 -0
- {koatl-0.1.24 → koatl-0.1.25}/python/koatl/runtime/meta_finder.py +0 -0
- {koatl-0.1.24 → koatl-0.1.25}/python/koatl/runtime/record.py +0 -0
- {koatl-0.1.24 → koatl-0.1.25}/python/koatl/runtime/virtual.py +0 -0
|
@@ -22,6 +22,17 @@ export Memo = class(Monad):
|
|
|
22
22
|
|
|
23
23
|
__repr__ = self => f"Memo(...)"
|
|
24
24
|
|
|
25
|
+
# This needs to be special cased because if we try to use Memo.value
|
|
26
|
+
# directly, the *generator* itself gets memoized. We want to memoize
|
|
27
|
+
# the thing that that comes out of the generator.
|
|
28
|
+
bind_value = staticmethod& (id, deps, f) =>
|
|
29
|
+
Memo(ctx =>
|
|
30
|
+
if ctx.try_get(id, tuple(deps)) matches Ok(value):
|
|
31
|
+
return value
|
|
32
|
+
|
|
33
|
+
ctx.update(id, tuple(deps), f().f(ctx))
|
|
34
|
+
)
|
|
35
|
+
|
|
25
36
|
value = staticmethod& (id, deps, f) =>
|
|
26
37
|
Memo(ctx =>
|
|
27
38
|
if ctx.try_get(id, tuple(deps)) matches Ok(value):
|
|
@@ -40,7 +51,7 @@ export Memo = class(Monad):
|
|
|
40
51
|
|
|
41
52
|
let v = f(*args, **kwargs)
|
|
42
53
|
if v matches Memo():
|
|
43
|
-
v = v.
|
|
54
|
+
v = v.f(ctx)
|
|
44
55
|
|
|
45
56
|
ctx.update(id, deps, v)
|
|
46
57
|
)
|
|
@@ -53,21 +64,22 @@ export Memo = class(Monad):
|
|
|
53
64
|
pure = staticmethod& value => Memo(ctx => value)
|
|
54
65
|
|
|
55
66
|
bind_once = (self, f) => Memo(ctx =>
|
|
56
|
-
let value = f(self.
|
|
67
|
+
let value = f(self.f(ctx))
|
|
57
68
|
if value matches Memo():
|
|
58
|
-
value = value.
|
|
69
|
+
value = value.f(ctx)
|
|
59
70
|
value
|
|
60
71
|
)
|
|
61
72
|
|
|
62
73
|
bind_gen = (self, gen) => Memo(ctx =>
|
|
63
|
-
self = self.
|
|
74
|
+
self = self.f(ctx)
|
|
64
75
|
try:
|
|
65
76
|
while True:
|
|
66
77
|
self = gen.send(self)
|
|
67
78
|
if self matches Memo():
|
|
68
|
-
self = self.
|
|
79
|
+
self = self.f(ctx)
|
|
69
80
|
except StopIteration(value=value):
|
|
70
81
|
return value
|
|
71
82
|
)
|
|
72
83
|
|
|
73
|
-
__tl__.
|
|
84
|
+
__tl__.memo_value = Memo.value
|
|
85
|
+
__tl__.bind_memo_value = Memo.bind_value
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import abc
|
|
2
2
|
|
|
3
|
+
# TODO: this should be really called MonadOnce.
|
|
3
4
|
export Monad = class(abc.ABC):
|
|
4
5
|
__slots__ = ()
|
|
5
6
|
|
|
@@ -13,7 +14,7 @@ export Monad = class(abc.ABC):
|
|
|
13
14
|
|
|
14
15
|
# An optional, optimized implementation of `bind` that skips deep recursion.
|
|
15
16
|
# TODO: can this be automatically generated?
|
|
16
|
-
bind_gen = (self, gen) =>
|
|
17
|
+
# bind_gen = (self, gen) => ...
|
|
17
18
|
|
|
18
19
|
# Automatically generated implementations.
|
|
19
20
|
map = (self, f) => self.bind(x => self.pure(f(x)))
|
|
@@ -14,24 +14,15 @@ export Result = class:
|
|
|
14
14
|
__init__ = (self, *args, **kwargs) =>
|
|
15
15
|
raise ValueError("Result should not be instantiated directly, use Ok or Err")
|
|
16
16
|
|
|
17
|
-
|
|
17
|
+
checked = staticmethod& f =>
|
|
18
18
|
"""
|
|
19
|
-
|
|
20
|
-
|
|
19
|
+
Runs the function in a try block and returns a Result.
|
|
20
|
+
If the function raises an exception, it will be wrapped in an Err.
|
|
21
21
|
"""
|
|
22
|
-
|
|
23
|
-
return
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
if self.value.ok:
|
|
27
|
-
return self.value
|
|
28
|
-
else:
|
|
29
|
-
return self
|
|
30
|
-
else:
|
|
31
|
-
if self.value.ok:
|
|
32
|
-
return self
|
|
33
|
-
else:
|
|
34
|
-
return self.value
|
|
22
|
+
try:
|
|
23
|
+
return Ok(f())
|
|
24
|
+
except BaseException() as e:
|
|
25
|
+
return Err(e)
|
|
35
26
|
|
|
36
27
|
bind_once = (self, f) =>
|
|
37
28
|
self = Result(self)
|
|
@@ -96,8 +87,14 @@ export Ok = class(Result):
|
|
|
96
87
|
return None
|
|
97
88
|
self.value = value
|
|
98
89
|
|
|
90
|
+
__eq__ = (self, other) =>
|
|
91
|
+
if not isinstance(other, Ok):
|
|
92
|
+
return False
|
|
93
|
+
return self.value == other.value
|
|
94
|
+
|
|
99
95
|
__repr__ = self => f"Ok({repr(self.value)})"
|
|
100
96
|
unwrap = self => self.value
|
|
97
|
+
coalesce = (self, f) => self.value
|
|
101
98
|
|
|
102
99
|
export Err = class(Result):
|
|
103
100
|
__slots__ = ("value",)
|
|
@@ -108,24 +105,43 @@ export Err = class(Result):
|
|
|
108
105
|
return None
|
|
109
106
|
self.value = value
|
|
110
107
|
|
|
108
|
+
__eq__ = (self, other) =>
|
|
109
|
+
if not isinstance(other, Err):
|
|
110
|
+
return False
|
|
111
|
+
return self.value == other.value
|
|
112
|
+
|
|
111
113
|
__repr__ = self => f"Err({repr(self.value)})"
|
|
112
114
|
unwrap = self =>
|
|
113
115
|
if self.value matches BaseException():
|
|
114
116
|
raise self.value
|
|
115
117
|
raise ValueError(f"Expected Ok, got {repr(self.value)}")
|
|
118
|
+
coalesce = (self, f) => f()
|
|
119
|
+
|
|
120
|
+
|
|
121
|
+
register_global_attr(type(None), "ok", ExtensionProperty(_ => False))
|
|
122
|
+
register_global_attr(BaseException, "ok", ExtensionProperty(_ => False))
|
|
123
|
+
register_global_attr(object, "ok", ExtensionProperty(_ => True))
|
|
124
|
+
|
|
125
|
+
register_global_attr(object, "result", ExtensionProperty(Result))
|
|
126
|
+
|
|
127
|
+
# Enables @ operator for bare objects.
|
|
128
|
+
register_global_attr(object, "bind_gen", Result.bind_gen)
|
|
116
129
|
|
|
130
|
+
__tl__.Ok = Ok
|
|
131
|
+
__tl__.Err = Err
|
|
117
132
|
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
for name, method in Result.__dict__:
|
|
122
|
-
if name.startswith("_"):
|
|
123
|
-
continue
|
|
124
|
-
if name == "map":
|
|
125
|
-
continue
|
|
133
|
+
__tl__.op_coal = (x, f) =>
|
|
134
|
+
if x matches Result():
|
|
135
|
+
return x.coalesce(f)
|
|
126
136
|
|
|
127
|
-
|
|
137
|
+
if not x.ok:
|
|
138
|
+
return f()
|
|
139
|
+
return x
|
|
128
140
|
|
|
129
|
-
|
|
141
|
+
__tl__.op_map = (x, f) =>
|
|
142
|
+
if x matches Result():
|
|
143
|
+
return x.map(f)
|
|
130
144
|
|
|
131
|
-
|
|
145
|
+
if x.ok:
|
|
146
|
+
return f(x)
|
|
147
|
+
return x
|
|
@@ -21,13 +21,13 @@ methods = {
|
|
|
21
21
|
if not hasattr(m, "apply"):
|
|
22
22
|
# special case for bare types - slightly more efficient
|
|
23
23
|
# ...also required since bare types don't have .map
|
|
24
|
-
if not
|
|
24
|
+
if not m.ok:
|
|
25
25
|
return m
|
|
26
26
|
|
|
27
27
|
let acc = [m]
|
|
28
28
|
for v in it:
|
|
29
29
|
let fv = f(v)
|
|
30
|
-
if not
|
|
30
|
+
if not fv.ok:
|
|
31
31
|
return fv
|
|
32
32
|
acc.append(fv)
|
|
33
33
|
return acc
|
|
@@ -21,10 +21,13 @@ from .record import *
|
|
|
21
21
|
from .helpers import *
|
|
22
22
|
|
|
23
23
|
|
|
24
|
-
def
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
24
|
+
def dummy(name):
|
|
25
|
+
def wrapper(*args, **kwargs):
|
|
26
|
+
raise RuntimeError(
|
|
27
|
+
f"{name} is not available without the prelude. Please import koatl.prelude."
|
|
28
|
+
)
|
|
29
|
+
|
|
30
|
+
return wrapper
|
|
28
31
|
|
|
29
32
|
|
|
30
33
|
__tl__ = SimpleNamespace(
|
|
@@ -32,15 +35,21 @@ __tl__ = SimpleNamespace(
|
|
|
32
35
|
slice=slice,
|
|
33
36
|
vget=virtual.vget,
|
|
34
37
|
vhas=virtual.vhas,
|
|
35
|
-
memo=dummy_memo,
|
|
36
38
|
unpack_record=helpers.unpack_record,
|
|
37
39
|
set_exports=helpers.set_exports,
|
|
38
40
|
do=helpers.do,
|
|
39
|
-
ok=helpers.ok,
|
|
40
41
|
partial=functools.partial,
|
|
42
|
+
# These require more complex logic and require the prelude.
|
|
43
|
+
# The runtime provides dummy implementations that raise if used without the prelude.
|
|
44
|
+
memo_value=dummy("memo"),
|
|
45
|
+
bind_memo_value=dummy("memo"),
|
|
46
|
+
op_map=dummy("?"),
|
|
47
|
+
op_coal=("??"),
|
|
48
|
+
Ok=dummy("try-expr"),
|
|
49
|
+
Err=dummy("try-expr"),
|
|
41
50
|
**{name: helpers.__dict__[name] for name in helpers.__all__},
|
|
42
51
|
**{name: record.__dict__[name] for name in record.__all__},
|
|
43
|
-
**{name: virtual.__dict__[name] for name in virtual.__all__}
|
|
52
|
+
**{name: virtual.__dict__[name] for name in virtual.__all__},
|
|
44
53
|
)
|
|
45
54
|
|
|
46
55
|
|
|
@@ -66,6 +66,10 @@ def do(f):
|
|
|
66
66
|
try:
|
|
67
67
|
m = gen.send(None)
|
|
68
68
|
except StopIteration as e:
|
|
69
|
+
if not hasattr(e.value, "bind_once"):
|
|
70
|
+
raise ValueError(
|
|
71
|
+
"This do-block returned a bare value before it could infer the monadic type. Wrap the value in pure()."
|
|
72
|
+
) from None
|
|
69
73
|
return e.value
|
|
70
74
|
|
|
71
75
|
def recurse(v):
|
|
@@ -74,17 +78,16 @@ def do(f):
|
|
|
74
78
|
m = gen.send(v)
|
|
75
79
|
return vget(m, "bind_once")(recurse)
|
|
76
80
|
except StopIteration as e:
|
|
77
|
-
|
|
78
|
-
return vget(m, "pure")(e.value)
|
|
79
|
-
except AttributeError:
|
|
80
|
-
return e.value
|
|
81
|
+
return vget(m, "pure")(e.value)
|
|
81
82
|
|
|
82
83
|
try:
|
|
83
84
|
# TODO: this is a workaround to avoid recursion.
|
|
84
85
|
# is it possible to derive bind_gen directly from bind_once?
|
|
85
86
|
|
|
86
|
-
|
|
87
|
-
except
|
|
87
|
+
bind_gen = vget(m, "bind_gen")
|
|
88
|
+
except AttributeError:
|
|
88
89
|
return vget(m, "bind_once")(recurse)
|
|
89
90
|
|
|
91
|
+
return bind_gen(gen)
|
|
92
|
+
|
|
90
93
|
return impl
|
|
@@ -17,15 +17,3 @@ assert_eq(1?.($ + 1)(), 2)
|
|
|
17
17
|
|
|
18
18
|
assert_eq(None ?? 1, 1)
|
|
19
19
|
assert_eq(int(5) ?? 1, 5)
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
# try exprs
|
|
23
|
-
|
|
24
|
-
assert_eq(type(try x), NameError)
|
|
25
|
-
assert_eq(type(try int(1)[1]), TypeError)
|
|
26
|
-
assert_eq(type(try [0][5]), IndexError)
|
|
27
|
-
assert_eq(try 5, 5)
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
assert_eq((try 5)?.($+1)?(), 6)
|
|
31
|
-
assert_eq(type((try x)?.($+1)?()), NameError)
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
import util.assert_eq
|
|
2
|
+
|
|
3
|
+
x = []
|
|
4
|
+
for i in [1, 2, 3]:
|
|
5
|
+
x = [*x, i]
|
|
6
|
+
|
|
7
|
+
assert_eq(x, [1, 2, 3])
|
|
8
|
+
|
|
9
|
+
i = True
|
|
10
|
+
while (x => x)(i):
|
|
11
|
+
if i === False:
|
|
12
|
+
assert False
|
|
13
|
+
|
|
14
|
+
i = False
|
|
15
|
+
|
|
16
|
+
counts = []
|
|
17
|
+
counter = 0
|
|
18
|
+
while (
|
|
19
|
+
counter = counter + 1
|
|
20
|
+
counter < 5
|
|
21
|
+
):
|
|
22
|
+
counts.append(counter)
|
|
23
|
+
|
|
24
|
+
assert_eq(counter, 5)
|
|
25
|
+
assert_eq(counts, [1, 2, 3, 4])
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
import util.assert_eq
|
|
2
|
+
|
|
3
|
+
assert_eq(
|
|
4
|
+
True and False
|
|
5
|
+
False
|
|
6
|
+
)
|
|
7
|
+
|
|
8
|
+
# No short-circuiting here.
|
|
9
|
+
|
|
10
|
+
flag = 0
|
|
11
|
+
assert_eq(
|
|
12
|
+
True and (
|
|
13
|
+
flag = 1
|
|
14
|
+
True
|
|
15
|
+
)
|
|
16
|
+
True
|
|
17
|
+
)
|
|
18
|
+
assert_eq(flag, 1)
|
|
19
|
+
|
|
20
|
+
# This needs to short-circuit.
|
|
21
|
+
|
|
22
|
+
flag = 0
|
|
23
|
+
assert_eq(
|
|
24
|
+
False and (
|
|
25
|
+
flag = 1
|
|
26
|
+
True
|
|
27
|
+
)
|
|
28
|
+
False
|
|
29
|
+
)
|
|
30
|
+
assert_eq(flag, 0)
|
|
31
|
+
|
|
32
|
+
# Same with or
|
|
33
|
+
|
|
34
|
+
flag = 0
|
|
35
|
+
assert_eq(
|
|
36
|
+
True or (
|
|
37
|
+
flag = 1
|
|
38
|
+
True
|
|
39
|
+
)
|
|
40
|
+
True
|
|
41
|
+
)
|
|
42
|
+
assert_eq(flag, 0)
|
|
@@ -3,12 +3,14 @@ import util.assert_eq
|
|
|
3
3
|
# precedence
|
|
4
4
|
assert_eq(try 1 ?? 2, 1)
|
|
5
5
|
assert_eq(try z ?? 2, 2)
|
|
6
|
-
assert_eq(
|
|
6
|
+
assert_eq(try z matches Err(), True)
|
|
7
|
+
assert_eq((try z).value matches NameError(), True)
|
|
7
8
|
assert_eq(try z ?? try 1 ?? 2, 1)
|
|
8
|
-
assert_eq(try z ?? try None ?? 2,
|
|
9
|
+
assert_eq(try z ?? try None ?? 2, None)
|
|
10
|
+
assert_eq(try z ?? Err(None) ?? 2, 2)
|
|
9
11
|
|
|
10
12
|
err_type = NameError
|
|
11
|
-
assert_eq(
|
|
13
|
+
assert_eq(try a except err_type() matches Err(), True)
|
|
12
14
|
|
|
13
15
|
# handlers
|
|
14
16
|
assert_eq(try 1 except ValueError() ?? 2, 1)
|
|
@@ -19,4 +21,9 @@ try:
|
|
|
19
21
|
try a except (ValueError() | StopIteration())
|
|
20
22
|
assert False
|
|
21
23
|
except:
|
|
22
|
-
None
|
|
24
|
+
None
|
|
25
|
+
|
|
26
|
+
assert_eq(try 1 matches Ok(1), True)
|
|
27
|
+
|
|
28
|
+
assert_eq((try 5)?.($+1)?(), Ok(6))
|
|
29
|
+
assert_eq((try x)?.($+1)?() matches Err(), True)
|
|
@@ -36,9 +36,20 @@ pub struct ResolveState<'src> {
|
|
|
36
36
|
pub export_stars: Vec<SIdent<'src>>,
|
|
37
37
|
|
|
38
38
|
pub resolutions: HashMap<RefHash, DeclarationKey>,
|
|
39
|
-
pub functions: HashMap<RefHash, FnInfo>,
|
|
40
39
|
pub patterns: HashMap<RefHash, PatternInfo>,
|
|
41
|
-
|
|
40
|
+
|
|
41
|
+
// TODO: these should all be collapsed into the same thing...
|
|
42
|
+
pub functions: HashMap<RefHash, FnInfo>,
|
|
43
|
+
pub memo_fninfo: HashMap<RefHash, FnInfo>,
|
|
44
|
+
// ...but before that, mapped_fninfo needs to be fixed.
|
|
45
|
+
// CallItems and ListItems nodes inside MappedCall and MappedSubscript
|
|
46
|
+
// are not boxed, so I've been pointing to the upper-level MappedX nodes
|
|
47
|
+
// instead, but that will cause a conflict with memo_fninfo, specifically
|
|
48
|
+
// "memo a?.x" will try to use the same refhash for both.
|
|
49
|
+
// So, is it safe to just point to the reference of the bare CallItem/ListItem?
|
|
50
|
+
// Or do we just box it?
|
|
51
|
+
pub mapped_fninfo: HashMap<RefHash, FnInfo>,
|
|
52
|
+
pub coal_fninfo: HashMap<RefHash, FnInfo>,
|
|
42
53
|
|
|
43
54
|
pub declarations: SlotMap<DeclarationKey, Declaration<'src>>,
|
|
44
55
|
pub scopes: SlotMap<ScopeKey, Scope>,
|
|
@@ -88,7 +99,9 @@ impl<'src> ResolveState<'src> {
|
|
|
88
99
|
resolutions: HashMap::new(),
|
|
89
100
|
functions: HashMap::new(),
|
|
90
101
|
patterns: HashMap::new(),
|
|
91
|
-
|
|
102
|
+
memo_fninfo: HashMap::new(),
|
|
103
|
+
mapped_fninfo: HashMap::new(),
|
|
104
|
+
coal_fninfo: HashMap::new(),
|
|
92
105
|
|
|
93
106
|
declarations: SlotMap::with_key(),
|
|
94
107
|
scopes,
|
|
@@ -388,6 +401,7 @@ pub struct FnInfo {
|
|
|
388
401
|
pub is_generator: bool,
|
|
389
402
|
pub is_placeholder: bool,
|
|
390
403
|
pub is_memo: bool,
|
|
404
|
+
pub is_mapped_rhs: bool,
|
|
391
405
|
|
|
392
406
|
pub arg_names: Vec<DeclarationKey>,
|
|
393
407
|
pub captures: HashSet<DeclarationKey>,
|
|
@@ -401,6 +415,7 @@ impl FnInfo {
|
|
|
401
415
|
is_generator: false,
|
|
402
416
|
is_placeholder: false,
|
|
403
417
|
is_memo: false,
|
|
418
|
+
is_mapped_rhs: false,
|
|
404
419
|
arg_names: Vec::new(),
|
|
405
420
|
captures: HashSet::new(),
|
|
406
421
|
}
|
|
@@ -836,6 +851,31 @@ fn pattern_scoped<'src>(
|
|
|
836
851
|
(pattern, scope_key, meta)
|
|
837
852
|
}
|
|
838
853
|
|
|
854
|
+
fn with_phantom_fninfo<'src, F, O>(state: &mut ResolveState<'src>, span: Span, f: F) -> (O, FnInfo)
|
|
855
|
+
where
|
|
856
|
+
F: FnOnce(&mut ResolveState<'src>) -> O,
|
|
857
|
+
{
|
|
858
|
+
let fn_ctx = FnInfo::new();
|
|
859
|
+
|
|
860
|
+
state.fn_stack.push(fn_ctx);
|
|
861
|
+
|
|
862
|
+
let ret = f(state);
|
|
863
|
+
|
|
864
|
+
let fn_ctx = state.fn_stack.pop().unwrap();
|
|
865
|
+
|
|
866
|
+
if fn_ctx.is_async {
|
|
867
|
+
state.set_async(span);
|
|
868
|
+
}
|
|
869
|
+
if fn_ctx.is_generator {
|
|
870
|
+
state.set_generator(span);
|
|
871
|
+
}
|
|
872
|
+
if fn_ctx.is_do {
|
|
873
|
+
state.set_do(span);
|
|
874
|
+
}
|
|
875
|
+
|
|
876
|
+
(ret, fn_ctx)
|
|
877
|
+
}
|
|
878
|
+
|
|
839
879
|
trait SExprExt<'src> {
|
|
840
880
|
fn traverse(self, state: &mut ResolveState<'src>) -> Indirect<SExpr<'src>>;
|
|
841
881
|
fn traverse_expecting_scope(self, state: &mut ResolveState<'src>) -> Indirect<SExpr<'src>>;
|
|
@@ -1239,42 +1279,99 @@ impl<'src> SExprExt<'src> for Indirect<SExpr<'src>> {
|
|
|
1239
1279
|
),
|
|
1240
1280
|
|
|
1241
1281
|
// special case for pipe
|
|
1242
|
-
Expr::Binary(binary_op,
|
|
1243
|
-
|
|
1244
|
-
|
|
1245
|
-
|
|
1246
|
-
|
|
1247
|
-
|
|
1248
|
-
|
|
1249
|
-
|
|
1282
|
+
Expr::Binary(binary_op, x, y) => {
|
|
1283
|
+
let (x, y) = match binary_op {
|
|
1284
|
+
BinaryOp::Pipe => (x.traverse_guarded(state), y.traverse_guarded(state)),
|
|
1285
|
+
BinaryOp::Coalesce => {
|
|
1286
|
+
let (y, fn_ctx) =
|
|
1287
|
+
with_phantom_fninfo(state, span, |state| y.traverse(state));
|
|
1288
|
+
|
|
1289
|
+
state.coal_fninfo.insert(y.as_ref().into(), fn_ctx);
|
|
1290
|
+
|
|
1291
|
+
(x.traverse(state), y)
|
|
1292
|
+
}
|
|
1293
|
+
_ => (x.traverse(state), y.traverse(state)),
|
|
1294
|
+
};
|
|
1250
1295
|
|
|
1251
1296
|
Expr::Binary(binary_op, x, y)
|
|
1252
1297
|
}
|
|
1253
1298
|
|
|
1254
1299
|
// postfix
|
|
1255
1300
|
Expr::Attribute(expr, attr) => Expr::Attribute(expr.traverse(state), attr.clone()),
|
|
1256
|
-
Expr::MappedAttribute(expr, attr) =>
|
|
1301
|
+
Expr::MappedAttribute(expr, attr) => {
|
|
1302
|
+
let traversed = Expr::MappedAttribute(expr.traverse(state), attr)
|
|
1303
|
+
.spanned(span)
|
|
1304
|
+
.indirect();
|
|
1305
|
+
|
|
1306
|
+
state
|
|
1307
|
+
.mapped_fninfo
|
|
1308
|
+
.insert(traversed.as_ref().into(), FnInfo::new());
|
|
1309
|
+
|
|
1310
|
+
return traversed;
|
|
1311
|
+
}
|
|
1257
1312
|
Expr::RawAttribute(expr, attr) => Expr::RawAttribute(expr.traverse(state), attr),
|
|
1258
1313
|
Expr::MappedRawAttribute(expr, spanned) => {
|
|
1259
|
-
Expr::MappedRawAttribute(expr.traverse(state), spanned)
|
|
1314
|
+
let traversed = Expr::MappedRawAttribute(expr.traverse(state), spanned)
|
|
1315
|
+
.spanned(span)
|
|
1316
|
+
.indirect();
|
|
1317
|
+
|
|
1318
|
+
state
|
|
1319
|
+
.mapped_fninfo
|
|
1320
|
+
.insert(traversed.as_ref().into(), FnInfo::new());
|
|
1321
|
+
|
|
1322
|
+
return traversed;
|
|
1260
1323
|
}
|
|
1261
1324
|
Expr::ScopedAttribute(expr, value) => {
|
|
1262
1325
|
Expr::ScopedAttribute(expr.traverse(state), value.traverse_guarded(state))
|
|
1263
1326
|
}
|
|
1264
1327
|
Expr::MappedScopedAttribute(expr, value) => {
|
|
1265
|
-
|
|
1328
|
+
let (rhs, fn_ctx) =
|
|
1329
|
+
with_phantom_fninfo(state, span, |state| value.traverse_guarded(state));
|
|
1330
|
+
|
|
1331
|
+
let traversed = Expr::MappedScopedAttribute(expr.traverse(state), rhs)
|
|
1332
|
+
.spanned(span)
|
|
1333
|
+
.indirect();
|
|
1334
|
+
|
|
1335
|
+
state
|
|
1336
|
+
.mapped_fninfo
|
|
1337
|
+
.insert(traversed.as_ref().into(), fn_ctx);
|
|
1338
|
+
|
|
1339
|
+
return traversed;
|
|
1266
1340
|
}
|
|
1267
1341
|
Expr::Call(a, items) => {
|
|
1268
1342
|
Expr::Call(a.traverse(state), traverse_call_items(state, items))
|
|
1269
1343
|
}
|
|
1270
1344
|
Expr::MappedCall(a, call_items) => {
|
|
1271
|
-
|
|
1345
|
+
let (rhs, fn_ctx) = with_phantom_fninfo(state, span, |state| {
|
|
1346
|
+
traverse_call_items(state, call_items)
|
|
1347
|
+
});
|
|
1348
|
+
|
|
1349
|
+
let traversed = Expr::MappedCall(a.traverse(state), rhs)
|
|
1350
|
+
.spanned(span)
|
|
1351
|
+
.indirect();
|
|
1352
|
+
|
|
1353
|
+
state
|
|
1354
|
+
.mapped_fninfo
|
|
1355
|
+
.insert(traversed.as_ref().into(), fn_ctx);
|
|
1356
|
+
|
|
1357
|
+
return traversed;
|
|
1272
1358
|
}
|
|
1273
1359
|
Expr::Subscript(x, list_items) => {
|
|
1274
1360
|
Expr::Subscript(x.traverse(state), traverse_list_items(state, list_items))
|
|
1275
1361
|
}
|
|
1276
1362
|
Expr::MappedSubscript(expr, list_items) => {
|
|
1277
|
-
|
|
1363
|
+
let (rhs, fn_ctx) = with_phantom_fninfo(state, span, |state| {
|
|
1364
|
+
traverse_list_items(state, list_items)
|
|
1365
|
+
});
|
|
1366
|
+
let traversed = Expr::MappedSubscript(expr.traverse(state), rhs)
|
|
1367
|
+
.spanned(span)
|
|
1368
|
+
.indirect();
|
|
1369
|
+
|
|
1370
|
+
state
|
|
1371
|
+
.mapped_fninfo
|
|
1372
|
+
.insert(traversed.as_ref().into(), fn_ctx);
|
|
1373
|
+
|
|
1374
|
+
return traversed;
|
|
1278
1375
|
}
|
|
1279
1376
|
Expr::Tuple(items) => Expr::Tuple(traverse_list_items(state, items)),
|
|
1280
1377
|
Expr::List(items) => Expr::List(traverse_list_items(state, items)),
|
|
@@ -1305,29 +1402,27 @@ impl<'src> SExprExt<'src> for Indirect<SExpr<'src>> {
|
|
|
1305
1402
|
Expr::Memo(inner) => {
|
|
1306
1403
|
state.set_do(span);
|
|
1307
1404
|
|
|
1308
|
-
let mut fn_ctx = FnInfo::new();
|
|
1309
|
-
fn_ctx.is_memo = true;
|
|
1310
|
-
|
|
1311
1405
|
let mut scope = Scope::new(state.top_scope_key());
|
|
1312
|
-
scope.is_fn = true;
|
|
1313
|
-
|
|
1314
1406
|
// TODO it's confusing that we need to
|
|
1315
1407
|
// set scope.is_fn = true all the time in order
|
|
1316
1408
|
// for captures to be found properly.
|
|
1317
1409
|
// is there any better way?
|
|
1318
|
-
|
|
1410
|
+
scope.is_fn = true;
|
|
1319
1411
|
let scope = state.scopes.insert(scope);
|
|
1320
|
-
state.fn_stack.push(fn_ctx);
|
|
1321
1412
|
|
|
1322
|
-
let
|
|
1323
|
-
state
|
|
1413
|
+
let (inner, mut fn_ctx) = with_phantom_fninfo(state, span, |state| {
|
|
1414
|
+
state
|
|
1415
|
+
.scoped(scope, |state| {
|
|
1416
|
+
state.placeholder_guarded(span, |state| {
|
|
1417
|
+
inner.traverse_expecting_scope(state)
|
|
1418
|
+
})
|
|
1419
|
+
})
|
|
1420
|
+
.value
|
|
1324
1421
|
});
|
|
1325
1422
|
|
|
1326
|
-
|
|
1327
|
-
|
|
1328
|
-
let fn_ctx = state.fn_stack.pop().unwrap();
|
|
1423
|
+
fn_ctx.is_memo = true;
|
|
1329
1424
|
|
|
1330
|
-
state.
|
|
1425
|
+
state.memo_fninfo.insert(inner.as_ref().into(), fn_ctx);
|
|
1331
1426
|
|
|
1332
1427
|
Expr::Memo(inner)
|
|
1333
1428
|
}
|