koatl 0.1.23__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.23 → koatl-0.1.25}/Cargo.lock +1 -1
- {koatl-0.1.23 → koatl-0.1.25}/PKG-INFO +1 -1
- {koatl-0.1.23 → koatl-0.1.25}/koatl/Cargo.toml +1 -1
- {koatl-0.1.23 → koatl-0.1.25/koatl}/python/koatl/prelude/functional/memo.tl +23 -10
- {koatl-0.1.23 → koatl-0.1.25/koatl}/python/koatl/prelude/functional/monad.tl +4 -1
- koatl-0.1.25/koatl/python/koatl/prelude/functional/result.tl +147 -0
- {koatl-0.1.23 → koatl-0.1.25}/koatl/python/koatl/prelude/iterable.tl +2 -2
- {koatl-0.1.23 → koatl-0.1.25/koatl}/python/koatl/runtime/__init__.py +16 -7
- {koatl-0.1.23 → koatl-0.1.25/koatl}/python/koatl/runtime/helpers.py +14 -6
- {koatl-0.1.23 → koatl-0.1.25}/koatl/src/emit_py.rs +20 -3
- {koatl-0.1.23 → 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.23 → 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.23 → koatl-0.1.25}/koatl/tests/e2e/prelude/memo.tl +1 -1
- {koatl-0.1.23/koatl/tests/e2e/base → koatl-0.1.25/koatl/tests/e2e/prelude}/try.tl +11 -4
- {koatl-0.1.23 → koatl-0.1.25}/koatl-core/parser/src/ast.rs +1 -1
- {koatl-0.1.23 → koatl-0.1.25}/koatl-core/src/resolve_scopes.rs +149 -42
- {koatl-0.1.23 → koatl-0.1.25}/koatl-core/src/transform.rs +249 -174
- {koatl-0.1.23/koatl → koatl-0.1.25}/python/koatl/prelude/functional/memo.tl +23 -10
- {koatl-0.1.23/koatl → koatl-0.1.25}/python/koatl/prelude/functional/monad.tl +4 -1
- koatl-0.1.25/python/koatl/prelude/functional/result.tl +147 -0
- {koatl-0.1.23 → koatl-0.1.25}/python/koatl/prelude/iterable.tl +2 -2
- {koatl-0.1.23/koatl → koatl-0.1.25}/python/koatl/runtime/__init__.py +16 -7
- {koatl-0.1.23/koatl → koatl-0.1.25}/python/koatl/runtime/helpers.py +14 -6
- koatl-0.1.23/koatl/python/koatl/prelude/functional/result.tl +0 -90
- koatl-0.1.23/koatl/tests/e2e/base/loops.tl +0 -14
- koatl-0.1.23/python/koatl/prelude/functional/result.tl +0 -90
- {koatl-0.1.23 → koatl-0.1.25}/Cargo.toml +0 -0
- {koatl-0.1.23 → koatl-0.1.25}/README.md +0 -0
- {koatl-0.1.23 → koatl-0.1.25}/koatl/.github/workflows/CI.yml +0 -0
- {koatl-0.1.23 → koatl-0.1.25}/koatl/.gitignore +0 -0
- {koatl-0.1.23 → koatl-0.1.25}/koatl/LICENSE +0 -0
- {koatl-0.1.23 → koatl-0.1.25}/koatl/README.md +0 -0
- {koatl-0.1.23 → koatl-0.1.25}/koatl/python/koatl/__init__.py +0 -0
- {koatl-0.1.23 → koatl-0.1.25}/koatl/python/koatl/__main__.py +0 -0
- {koatl-0.1.23 → koatl-0.1.25}/koatl/python/koatl/cli.py +0 -0
- {koatl-0.1.23 → koatl-0.1.25}/koatl/python/koatl/notebook/__init__.py +0 -0
- {koatl-0.1.23 → koatl-0.1.25}/koatl/python/koatl/notebook/magic.py +0 -0
- {koatl-0.1.23 → koatl-0.1.25}/koatl/python/koatl/prelude/__init__.tl +0 -0
- {koatl-0.1.23 → koatl-0.1.25}/koatl/python/koatl/prelude/functional/__init__.tl +0 -0
- {koatl-0.1.23 → koatl-0.1.25}/koatl/python/koatl/prelude/functional/async.tl +0 -0
- {koatl-0.1.23 → koatl-0.1.25}/koatl/python/koatl/prelude/functional/async_util.py +0 -0
- {koatl-0.1.23 → koatl-0.1.25}/koatl/python/koatl/prelude/functional/list.tl +0 -0
- {koatl-0.1.23 → koatl-0.1.25}/koatl/python/koatl/prelude/functional/reader.tl +0 -0
- {koatl-0.1.23 → koatl-0.1.25}/koatl/python/koatl/runtime/meta_finder.py +0 -0
- {koatl-0.1.23 → koatl-0.1.25}/koatl/python/koatl/runtime/record.py +0 -0
- {koatl-0.1.23 → koatl-0.1.25}/koatl/python/koatl/runtime/virtual.py +0 -0
- {koatl-0.1.23 → koatl-0.1.25}/koatl/requirements.txt +0 -0
- {koatl-0.1.23 → koatl-0.1.25}/koatl/src/lib.rs +0 -0
- {koatl-0.1.23 → koatl-0.1.25}/koatl/tests/e2e/base/containers.tl +0 -0
- {koatl-0.1.23 → koatl-0.1.25}/koatl/tests/e2e/base/decorators.tl +0 -0
- {koatl-0.1.23 → koatl-0.1.25}/koatl/tests/e2e/base/destructure-for-and-fn.tl +0 -0
- {koatl-0.1.23 → koatl-0.1.25}/koatl/tests/e2e/base/destructure.tl +0 -0
- {koatl-0.1.23 → koatl-0.1.25}/koatl/tests/e2e/base/escape_ident.tl +0 -0
- {koatl-0.1.23 → koatl-0.1.25}/koatl/tests/e2e/base/fstr.tl +0 -0
- {koatl-0.1.23 → koatl-0.1.25}/koatl/tests/e2e/base/functions.tl +0 -0
- {koatl-0.1.23 → koatl-0.1.25}/koatl/tests/e2e/base/generator.tl +0 -0
- {koatl-0.1.23 → koatl-0.1.25}/koatl/tests/e2e/base/if_expr.tl +0 -0
- {koatl-0.1.23 → koatl-0.1.25}/koatl/tests/e2e/base/imports.tl +0 -0
- {koatl-0.1.23 → koatl-0.1.25}/koatl/tests/e2e/base/nary-list.tl +0 -0
- {koatl-0.1.23 → koatl-0.1.25}/koatl/tests/e2e/base/placeholder.tl +0 -0
- {koatl-0.1.23 → koatl-0.1.25}/koatl/tests/e2e/base/precedence.tl +0 -0
- {koatl-0.1.23 → koatl-0.1.25}/koatl/tests/e2e/base/scopes.tl +0 -0
- {koatl-0.1.23 → koatl-0.1.25}/koatl/tests/e2e/base/semantic_whitespace.tl +0 -0
- {koatl-0.1.23 → koatl-0.1.25}/koatl/tests/e2e/destructure.tl +0 -0
- {koatl-0.1.23 → koatl-0.1.25}/koatl/tests/e2e/prelude/async.tl +0 -0
- {koatl-0.1.23 → koatl-0.1.25}/koatl/tests/e2e/prelude/iterables.tl +0 -0
- {koatl-0.1.23 → koatl-0.1.25}/koatl/tests/e2e/prelude/list.tl +0 -0
- {koatl-0.1.23 → koatl-0.1.25}/koatl/tests/e2e/prelude/reader.tl +0 -0
- {koatl-0.1.23 → koatl-0.1.25}/koatl/tests/e2e/prelude/result.tl +0 -0
- {koatl-0.1.23/koatl/tests/e2e/base → koatl-0.1.25/koatl/tests/e2e/prelude}/slice.tl +0 -0
- {koatl-0.1.23 → koatl-0.1.25}/koatl/tests/e2e/prelude/virtual.tl +0 -0
- {koatl-0.1.23 → koatl-0.1.25}/koatl/tests/e2e/util/__init__.py +0 -0
- {koatl-0.1.23 → koatl-0.1.25}/koatl/tests/e2e/util/module0.tl +0 -0
- {koatl-0.1.23 → koatl-0.1.25}/koatl/tests/e2e/util/module1.tl +0 -0
- {koatl-0.1.23 → koatl-0.1.25}/koatl/tests/e2e/util/module2.tl +0 -0
- {koatl-0.1.23 → koatl-0.1.25}/koatl/tests/parse/arith.tl +0 -0
- {koatl-0.1.23 → koatl-0.1.25}/koatl/tests/parse/assign.tl +0 -0
- {koatl-0.1.23 → koatl-0.1.25}/koatl/tests/parse/block-comments.tl +0 -0
- {koatl-0.1.23 → koatl-0.1.25}/koatl/tests/parse/deco.tl +0 -0
- {koatl-0.1.23 → koatl-0.1.25}/koatl/tests/parse/func.tl +0 -0
- {koatl-0.1.23 → koatl-0.1.25}/koatl/tests/parse/matches.tl +0 -0
- {koatl-0.1.23 → koatl-0.1.25}/koatl/tests/test_e2e.py +0 -0
- {koatl-0.1.23 → koatl-0.1.25}/koatl/tests/test_parse.py +0 -0
- {koatl-0.1.23 → koatl-0.1.25}/koatl-core/Cargo.toml +0 -0
- {koatl-0.1.23 → koatl-0.1.25}/koatl-core/parser/Cargo.toml +0 -0
- {koatl-0.1.23 → koatl-0.1.25}/koatl-core/parser/src/lexer.rs +0 -0
- {koatl-0.1.23 → koatl-0.1.25}/koatl-core/parser/src/lib.rs +0 -0
- {koatl-0.1.23 → koatl-0.1.25}/koatl-core/parser/src/parser.rs +0 -0
- {koatl-0.1.23 → koatl-0.1.25}/koatl-core/parser/src/util.rs +0 -0
- {koatl-0.1.23 → koatl-0.1.25}/koatl-core/parser/tests/lexer.rs +0 -0
- {koatl-0.1.23 → koatl-0.1.25}/koatl-core/src/inference.rs +0 -0
- {koatl-0.1.23 → koatl-0.1.25}/koatl-core/src/lib.rs +0 -0
- {koatl-0.1.23 → koatl-0.1.25}/koatl-core/src/main.rs +0 -0
- {koatl-0.1.23 → koatl-0.1.25}/koatl-core/src/parse_timer.rs +0 -0
- {koatl-0.1.23 → koatl-0.1.25}/koatl-core/src/parser.rs +0 -0
- {koatl-0.1.23 → koatl-0.1.25}/koatl-core/src/py/ast.rs +0 -0
- {koatl-0.1.23 → koatl-0.1.25}/koatl-core/src/py/emit.rs +0 -0
- {koatl-0.1.23 → koatl-0.1.25}/koatl-core/src/py/mod.rs +0 -0
- {koatl-0.1.23 → koatl-0.1.25}/koatl-core/src/py/util.rs +0 -0
- {koatl-0.1.23 → koatl-0.1.25}/koatl-core/src/types.rs +0 -0
- {koatl-0.1.23 → koatl-0.1.25}/koatl-core/src/util.rs +0 -0
- {koatl-0.1.23 → koatl-0.1.25}/pyproject.toml +0 -0
- {koatl-0.1.23 → koatl-0.1.25}/python/koatl/__init__.py +0 -0
- {koatl-0.1.23 → koatl-0.1.25}/python/koatl/__main__.py +0 -0
- {koatl-0.1.23 → koatl-0.1.25}/python/koatl/cli.py +0 -0
- {koatl-0.1.23 → koatl-0.1.25}/python/koatl/notebook/__init__.py +0 -0
- {koatl-0.1.23 → koatl-0.1.25}/python/koatl/notebook/magic.py +0 -0
- {koatl-0.1.23 → koatl-0.1.25}/python/koatl/prelude/__init__.tl +0 -0
- {koatl-0.1.23 → koatl-0.1.25}/python/koatl/prelude/functional/__init__.tl +0 -0
- {koatl-0.1.23 → koatl-0.1.25}/python/koatl/prelude/functional/async.tl +0 -0
- {koatl-0.1.23 → koatl-0.1.25}/python/koatl/prelude/functional/async_util.py +0 -0
- {koatl-0.1.23 → koatl-0.1.25}/python/koatl/prelude/functional/list.tl +0 -0
- {koatl-0.1.23 → koatl-0.1.25}/python/koatl/prelude/functional/reader.tl +0 -0
- {koatl-0.1.23 → koatl-0.1.25}/python/koatl/runtime/meta_finder.py +0 -0
- {koatl-0.1.23 → koatl-0.1.25}/python/koatl/runtime/record.py +0 -0
- {koatl-0.1.23 → koatl-0.1.25}/python/koatl/runtime/virtual.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import collections.defaultdict
|
|
2
2
|
import functools.wraps
|
|
3
|
-
import .result.Ok
|
|
3
|
+
import .result.(Result, Ok)
|
|
4
4
|
import .Monad
|
|
5
5
|
|
|
6
6
|
export Memo = class(Monad):
|
|
@@ -11,7 +11,8 @@ export Memo = class(Monad):
|
|
|
11
11
|
__repr__ = self => f"Memo.Cache({self.cache})"
|
|
12
12
|
|
|
13
13
|
try_get = (self, name, deps) =>
|
|
14
|
-
try
|
|
14
|
+
# TODO should try operator return a Result directly?
|
|
15
|
+
Result(try self.cache[name][deps] except KeyError())
|
|
15
16
|
|
|
16
17
|
update = (self, name, deps, value) =>
|
|
17
18
|
self.cache[name][deps] = value
|
|
@@ -21,9 +22,20 @@ export Memo = class(Monad):
|
|
|
21
22
|
|
|
22
23
|
__repr__ = self => f"Memo(...)"
|
|
23
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
|
+
|
|
24
36
|
value = staticmethod& (id, deps, f) =>
|
|
25
37
|
Memo(ctx =>
|
|
26
|
-
if ctx.try_get(id, tuple(deps)) matches Ok()
|
|
38
|
+
if ctx.try_get(id, tuple(deps)) matches Ok(value):
|
|
27
39
|
return value
|
|
28
40
|
|
|
29
41
|
ctx.update(id, tuple(deps), f())
|
|
@@ -34,12 +46,12 @@ export Memo = class(Monad):
|
|
|
34
46
|
let deps = (tuple(args), tuple(kwargs.items()))
|
|
35
47
|
|
|
36
48
|
Memo(ctx =>
|
|
37
|
-
if ctx.try_get(id, deps) matches Ok()
|
|
49
|
+
if ctx.try_get(id, deps) matches Ok(value):
|
|
38
50
|
return value
|
|
39
51
|
|
|
40
52
|
let v = f(*args, **kwargs)
|
|
41
53
|
if v matches Memo():
|
|
42
|
-
v = v.
|
|
54
|
+
v = v.f(ctx)
|
|
43
55
|
|
|
44
56
|
ctx.update(id, deps, v)
|
|
45
57
|
)
|
|
@@ -52,21 +64,22 @@ export Memo = class(Monad):
|
|
|
52
64
|
pure = staticmethod& value => Memo(ctx => value)
|
|
53
65
|
|
|
54
66
|
bind_once = (self, f) => Memo(ctx =>
|
|
55
|
-
let value = f(self.
|
|
67
|
+
let value = f(self.f(ctx))
|
|
56
68
|
if value matches Memo():
|
|
57
|
-
value = value.
|
|
69
|
+
value = value.f(ctx)
|
|
58
70
|
value
|
|
59
71
|
)
|
|
60
72
|
|
|
61
73
|
bind_gen = (self, gen) => Memo(ctx =>
|
|
62
|
-
self = self.
|
|
74
|
+
self = self.f(ctx)
|
|
63
75
|
try:
|
|
64
76
|
while True:
|
|
65
77
|
self = gen.send(self)
|
|
66
78
|
if self matches Memo():
|
|
67
|
-
self = self.
|
|
79
|
+
self = self.f(ctx)
|
|
68
80
|
except StopIteration(value=value):
|
|
69
81
|
return value
|
|
70
82
|
)
|
|
71
83
|
|
|
72
|
-
__tl__.
|
|
84
|
+
__tl__.memo_value = Memo.value
|
|
85
|
+
__tl__.bind_memo_value = Memo.bind_value
|
|
@@ -1,6 +1,9 @@
|
|
|
1
1
|
import abc
|
|
2
2
|
|
|
3
|
+
# TODO: this should be really called MonadOnce.
|
|
3
4
|
export Monad = class(abc.ABC):
|
|
5
|
+
__slots__ = ()
|
|
6
|
+
|
|
4
7
|
# The default implementation required for `@` syntax that should be overridden by subclasses.
|
|
5
8
|
bind_once = abc.abstractmethod& (self, f) => None
|
|
6
9
|
|
|
@@ -11,7 +14,7 @@ export Monad = class(abc.ABC):
|
|
|
11
14
|
|
|
12
15
|
# An optional, optimized implementation of `bind` that skips deep recursion.
|
|
13
16
|
# TODO: can this be automatically generated?
|
|
14
|
-
bind_gen = (self, gen) =>
|
|
17
|
+
# bind_gen = (self, gen) => ...
|
|
15
18
|
|
|
16
19
|
# Automatically generated implementations.
|
|
17
20
|
map = (self, f) => self.bind(x => self.pure(f(x)))
|
|
@@ -0,0 +1,147 @@
|
|
|
1
|
+
import functools.wraps
|
|
2
|
+
import .monad.Monad
|
|
3
|
+
import koatl.runtime.virtual.register_global_attr
|
|
4
|
+
|
|
5
|
+
export Result = class:
|
|
6
|
+
__slots__ = ()
|
|
7
|
+
__match_args__ = ("value",)
|
|
8
|
+
|
|
9
|
+
__new__ = (cls, value) =>
|
|
10
|
+
if isinstance(value, Result):
|
|
11
|
+
return value
|
|
12
|
+
value.ok then Ok(value) else Err(value)
|
|
13
|
+
|
|
14
|
+
__init__ = (self, *args, **kwargs) =>
|
|
15
|
+
raise ValueError("Result should not be instantiated directly, use Ok or Err")
|
|
16
|
+
|
|
17
|
+
checked = staticmethod& f =>
|
|
18
|
+
"""
|
|
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
|
+
"""
|
|
22
|
+
try:
|
|
23
|
+
return Ok(f())
|
|
24
|
+
except BaseException() as e:
|
|
25
|
+
return Err(e)
|
|
26
|
+
|
|
27
|
+
bind_once = (self, f) =>
|
|
28
|
+
self = Result(self)
|
|
29
|
+
if not self.ok:
|
|
30
|
+
return self
|
|
31
|
+
|
|
32
|
+
let v = Result(f(self.value))
|
|
33
|
+
|
|
34
|
+
bind = bind_once
|
|
35
|
+
|
|
36
|
+
bind_gen = (self, gen) =>
|
|
37
|
+
self = Result(self)
|
|
38
|
+
try:
|
|
39
|
+
while True:
|
|
40
|
+
if not self.ok:
|
|
41
|
+
return self
|
|
42
|
+
|
|
43
|
+
self = Result(gen.send(self.value))
|
|
44
|
+
except StopIteration(value=value):
|
|
45
|
+
return Result.pure(value)
|
|
46
|
+
|
|
47
|
+
pure = staticmethod& x => Ok(x)
|
|
48
|
+
|
|
49
|
+
apply = (self, f) =>
|
|
50
|
+
self = Result(self)
|
|
51
|
+
f = Result(f)
|
|
52
|
+
|
|
53
|
+
if not self.ok:
|
|
54
|
+
return self
|
|
55
|
+
if not f.ok:
|
|
56
|
+
return f
|
|
57
|
+
|
|
58
|
+
Ok(f(self))
|
|
59
|
+
|
|
60
|
+
map = (self, f) =>
|
|
61
|
+
self = Result(self)
|
|
62
|
+
if self.ok:
|
|
63
|
+
return Ok(f(self.value))
|
|
64
|
+
return self
|
|
65
|
+
|
|
66
|
+
map_err = (self, f) =>
|
|
67
|
+
self = Result(self)
|
|
68
|
+
if not self.ok:
|
|
69
|
+
return Err(f(self.value))
|
|
70
|
+
return self
|
|
71
|
+
|
|
72
|
+
map_none = (self, f) =>
|
|
73
|
+
self = Result(self)
|
|
74
|
+
if not self.ok and self.value === None:
|
|
75
|
+
return Err(f())
|
|
76
|
+
return self
|
|
77
|
+
|
|
78
|
+
export Ok = class(Result):
|
|
79
|
+
__slots__ = ("value",)
|
|
80
|
+
ok = True
|
|
81
|
+
__new__ = (cls, value) => object.__new__(cls)
|
|
82
|
+
__init__ = (self, value) =>
|
|
83
|
+
if hasattr(self, "value"):
|
|
84
|
+
# This is necessary to prevent overwriting the value
|
|
85
|
+
# since Python's Result.__new__ will call Ok.__init__ again
|
|
86
|
+
# if the object is already created.
|
|
87
|
+
return None
|
|
88
|
+
self.value = value
|
|
89
|
+
|
|
90
|
+
__eq__ = (self, other) =>
|
|
91
|
+
if not isinstance(other, Ok):
|
|
92
|
+
return False
|
|
93
|
+
return self.value == other.value
|
|
94
|
+
|
|
95
|
+
__repr__ = self => f"Ok({repr(self.value)})"
|
|
96
|
+
unwrap = self => self.value
|
|
97
|
+
coalesce = (self, f) => self.value
|
|
98
|
+
|
|
99
|
+
export Err = class(Result):
|
|
100
|
+
__slots__ = ("value",)
|
|
101
|
+
ok = False
|
|
102
|
+
__new__ = (cls, value) => object.__new__(cls)
|
|
103
|
+
__init__ = (self, value) =>
|
|
104
|
+
if hasattr(self, "value"):
|
|
105
|
+
return None
|
|
106
|
+
self.value = value
|
|
107
|
+
|
|
108
|
+
__eq__ = (self, other) =>
|
|
109
|
+
if not isinstance(other, Err):
|
|
110
|
+
return False
|
|
111
|
+
return self.value == other.value
|
|
112
|
+
|
|
113
|
+
__repr__ = self => f"Err({repr(self.value)})"
|
|
114
|
+
unwrap = self =>
|
|
115
|
+
if self.value matches BaseException():
|
|
116
|
+
raise self.value
|
|
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)
|
|
129
|
+
|
|
130
|
+
__tl__.Ok = Ok
|
|
131
|
+
__tl__.Err = Err
|
|
132
|
+
|
|
133
|
+
__tl__.op_coal = (x, f) =>
|
|
134
|
+
if x matches Result():
|
|
135
|
+
return x.coalesce(f)
|
|
136
|
+
|
|
137
|
+
if not x.ok:
|
|
138
|
+
return f()
|
|
139
|
+
return x
|
|
140
|
+
|
|
141
|
+
__tl__.op_map = (x, f) =>
|
|
142
|
+
if x matches Result():
|
|
143
|
+
return x.map(f)
|
|
144
|
+
|
|
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
|
|
|
@@ -46,6 +46,11 @@ def unpack_record(obj):
|
|
|
46
46
|
|
|
47
47
|
|
|
48
48
|
def ok(obj):
|
|
49
|
+
try:
|
|
50
|
+
return obj.ok
|
|
51
|
+
except AttributeError:
|
|
52
|
+
pass
|
|
53
|
+
|
|
49
54
|
if obj is None:
|
|
50
55
|
return False
|
|
51
56
|
if isinstance(obj, BaseException):
|
|
@@ -61,6 +66,10 @@ def do(f):
|
|
|
61
66
|
try:
|
|
62
67
|
m = gen.send(None)
|
|
63
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
|
|
64
73
|
return e.value
|
|
65
74
|
|
|
66
75
|
def recurse(v):
|
|
@@ -69,17 +78,16 @@ def do(f):
|
|
|
69
78
|
m = gen.send(v)
|
|
70
79
|
return vget(m, "bind_once")(recurse)
|
|
71
80
|
except StopIteration as e:
|
|
72
|
-
|
|
73
|
-
return vget(m, "pure")(e.value)
|
|
74
|
-
except AttributeError:
|
|
75
|
-
return e.value
|
|
81
|
+
return vget(m, "pure")(e.value)
|
|
76
82
|
|
|
77
83
|
try:
|
|
78
84
|
# TODO: this is a workaround to avoid recursion.
|
|
79
85
|
# is it possible to derive bind_gen directly from bind_once?
|
|
80
86
|
|
|
81
|
-
|
|
82
|
-
except
|
|
87
|
+
bind_gen = vget(m, "bind_gen")
|
|
88
|
+
except AttributeError:
|
|
83
89
|
return vget(m, "bind_once")(recurse)
|
|
84
90
|
|
|
91
|
+
return bind_gen(gen)
|
|
92
|
+
|
|
85
93
|
return impl
|
|
@@ -502,7 +502,7 @@ impl<'src> PyExprExt<'src> for SPyExpr<'src> {
|
|
|
502
502
|
ctx.ast_node("Name", (ident, c.emit_py(ctx)?), &self.tl_span)?
|
|
503
503
|
}
|
|
504
504
|
PyExpr::Binary(op, left, right) => {
|
|
505
|
-
let
|
|
505
|
+
let py_bin_op = match op {
|
|
506
506
|
PyBinaryOp::Add => Some("Add"),
|
|
507
507
|
PyBinaryOp::Sub => Some("Sub"),
|
|
508
508
|
PyBinaryOp::Mult => Some("Mult"),
|
|
@@ -512,18 +512,35 @@ impl<'src> PyExprExt<'src> for SPyExpr<'src> {
|
|
|
512
512
|
_ => None,
|
|
513
513
|
};
|
|
514
514
|
|
|
515
|
-
if let Some(
|
|
515
|
+
if let Some(py_bin_op) = py_bin_op {
|
|
516
516
|
return ctx.ast_node(
|
|
517
517
|
"BinOp",
|
|
518
518
|
(
|
|
519
519
|
left.emit_py(ctx)?,
|
|
520
|
-
ctx.ast_cls(
|
|
520
|
+
ctx.ast_cls(py_bin_op, ())?,
|
|
521
521
|
right.emit_py(ctx)?,
|
|
522
522
|
),
|
|
523
523
|
&self.tl_span,
|
|
524
524
|
);
|
|
525
525
|
}
|
|
526
526
|
|
|
527
|
+
let py_bool_op = match op {
|
|
528
|
+
PyBinaryOp::And => Some("And"),
|
|
529
|
+
PyBinaryOp::Or => Some("Or"),
|
|
530
|
+
_ => None,
|
|
531
|
+
};
|
|
532
|
+
|
|
533
|
+
if let Some(py_bool_op) = py_bool_op {
|
|
534
|
+
return ctx.ast_node(
|
|
535
|
+
"BoolOp",
|
|
536
|
+
(
|
|
537
|
+
ctx.ast_cls(py_bool_op, ())?,
|
|
538
|
+
[left.emit_py(ctx)?, right.emit_py(ctx)?],
|
|
539
|
+
),
|
|
540
|
+
&self.tl_span,
|
|
541
|
+
);
|
|
542
|
+
}
|
|
543
|
+
|
|
527
544
|
let py_cmp_op = match op {
|
|
528
545
|
PyBinaryOp::Lt => Some("Lt"),
|
|
529
546
|
PyBinaryOp::Gt => Some("Gt"),
|
|
@@ -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)
|