koatl 0.1.13__tar.gz → 0.1.14__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.13 → koatl-0.1.14}/Cargo.lock +1 -1
- {koatl-0.1.13 → koatl-0.1.14}/PKG-INFO +1 -1
- {koatl-0.1.13 → koatl-0.1.14}/koatl/Cargo.toml +1 -1
- {koatl-0.1.13 → koatl-0.1.14/koatl}/python/koatl/cli.py +2 -2
- {koatl-0.1.13 → koatl-0.1.14/koatl}/python/koatl/prelude/functional/__init__.tl +3 -2
- koatl-0.1.14/koatl/python/koatl/prelude/functional/async.tl +42 -0
- koatl-0.1.14/koatl/python/koatl/prelude/functional/monad.tl +12 -0
- koatl-0.1.14/koatl/python/koatl/prelude/functional/reader.tl +33 -0
- koatl-0.1.14/koatl/python/koatl/prelude/functional/result.tl +80 -0
- {koatl-0.1.13 → koatl-0.1.14/koatl}/python/koatl/runtime/__init__.py +2 -1
- {koatl-0.1.13 → koatl-0.1.14/koatl}/python/koatl/runtime/helpers.py +8 -4
- {koatl-0.1.13 → koatl-0.1.14}/koatl/python/koatl/runtime/meta_finder.py +6 -2
- {koatl-0.1.13 → koatl-0.1.14/koatl}/python/koatl/runtime/virtual.py +27 -0
- koatl-0.1.13/koatl/tests/e2e/prelude/ok.tl → koatl-0.1.14/koatl/tests/e2e/prelude/result.tl +11 -1
- koatl-0.1.14/koatl/tests/parse/deco.tl +2 -0
- {koatl-0.1.13 → koatl-0.1.14}/koatl-core/parser/src/ast.rs +1 -1
- {koatl-0.1.13 → koatl-0.1.14}/koatl-core/parser/src/parser.rs +16 -17
- {koatl-0.1.13 → koatl-0.1.14}/koatl-core/src/transform.rs +10 -12
- {koatl-0.1.13/koatl → koatl-0.1.14}/python/koatl/cli.py +2 -2
- {koatl-0.1.13/koatl → koatl-0.1.14}/python/koatl/prelude/functional/__init__.tl +3 -2
- koatl-0.1.14/python/koatl/prelude/functional/async.tl +42 -0
- koatl-0.1.14/python/koatl/prelude/functional/monad.tl +12 -0
- koatl-0.1.14/python/koatl/prelude/functional/reader.tl +33 -0
- koatl-0.1.14/python/koatl/prelude/functional/result.tl +80 -0
- {koatl-0.1.13/koatl → koatl-0.1.14}/python/koatl/runtime/__init__.py +2 -1
- {koatl-0.1.13/koatl → koatl-0.1.14}/python/koatl/runtime/helpers.py +8 -4
- {koatl-0.1.13 → koatl-0.1.14}/python/koatl/runtime/meta_finder.py +6 -2
- {koatl-0.1.13/koatl → koatl-0.1.14}/python/koatl/runtime/virtual.py +27 -0
- koatl-0.1.13/koatl/python/koatl/prelude/functional/async.tl +0 -38
- koatl-0.1.13/koatl/python/koatl/prelude/functional/ok.tl +0 -51
- koatl-0.1.13/koatl/python/koatl/prelude/functional/reader.tl +0 -24
- koatl-0.1.13/koatl/tests/parse/deco.tl +0 -2
- koatl-0.1.13/python/koatl/prelude/functional/async.tl +0 -38
- koatl-0.1.13/python/koatl/prelude/functional/ok.tl +0 -51
- koatl-0.1.13/python/koatl/prelude/functional/reader.tl +0 -24
- {koatl-0.1.13 → koatl-0.1.14}/Cargo.toml +0 -0
- {koatl-0.1.13 → koatl-0.1.14}/README.md +0 -0
- {koatl-0.1.13 → koatl-0.1.14}/koatl/.github/workflows/CI.yml +0 -0
- {koatl-0.1.13 → koatl-0.1.14}/koatl/.gitignore +0 -0
- {koatl-0.1.13 → koatl-0.1.14}/koatl/LICENSE +0 -0
- {koatl-0.1.13 → koatl-0.1.14}/koatl/README.md +0 -0
- {koatl-0.1.13 → koatl-0.1.14}/koatl/python/koatl/__init__.py +0 -0
- {koatl-0.1.13 → koatl-0.1.14}/koatl/python/koatl/__main__.py +0 -0
- {koatl-0.1.13 → koatl-0.1.14}/koatl/python/koatl/notebook/__init__.py +0 -0
- {koatl-0.1.13 → koatl-0.1.14}/koatl/python/koatl/notebook/magic.py +0 -0
- {koatl-0.1.13 → koatl-0.1.14}/koatl/python/koatl/prelude/__init__.tl +0 -0
- {koatl-0.1.13 → koatl-0.1.14}/koatl/python/koatl/prelude/functional/async_util.py +0 -0
- {koatl-0.1.13 → koatl-0.1.14}/koatl/python/koatl/prelude/iterable.tl +0 -0
- {koatl-0.1.13 → koatl-0.1.14}/koatl/python/koatl/runtime/record.py +0 -0
- {koatl-0.1.13 → koatl-0.1.14}/koatl/requirements.txt +0 -0
- {koatl-0.1.13 → koatl-0.1.14}/koatl/src/emit_py.rs +0 -0
- {koatl-0.1.13 → koatl-0.1.14}/koatl/src/lib.rs +0 -0
- {koatl-0.1.13 → koatl-0.1.14}/koatl/tests/e2e/base/coal.tl +0 -0
- {koatl-0.1.13 → koatl-0.1.14}/koatl/tests/e2e/base/containers.tl +0 -0
- {koatl-0.1.13 → koatl-0.1.14}/koatl/tests/e2e/base/decorators.tl +0 -0
- {koatl-0.1.13 → koatl-0.1.14}/koatl/tests/e2e/base/destructure-for-and-fn.tl +0 -0
- {koatl-0.1.13 → koatl-0.1.14}/koatl/tests/e2e/base/destructure.tl +0 -0
- {koatl-0.1.13 → koatl-0.1.14}/koatl/tests/e2e/base/escape_ident.tl +0 -0
- {koatl-0.1.13 → koatl-0.1.14}/koatl/tests/e2e/base/fstr.tl +0 -0
- {koatl-0.1.13 → koatl-0.1.14}/koatl/tests/e2e/base/functions.tl +0 -0
- {koatl-0.1.13 → koatl-0.1.14}/koatl/tests/e2e/base/generator.tl +0 -0
- {koatl-0.1.13 → koatl-0.1.14}/koatl/tests/e2e/base/if_expr.tl +0 -0
- {koatl-0.1.13 → koatl-0.1.14}/koatl/tests/e2e/base/imports.tl +0 -0
- {koatl-0.1.13 → koatl-0.1.14}/koatl/tests/e2e/base/iterables.tl +0 -0
- {koatl-0.1.13 → koatl-0.1.14}/koatl/tests/e2e/base/loops.tl +0 -0
- {koatl-0.1.13 → koatl-0.1.14}/koatl/tests/e2e/base/match.tl +0 -0
- {koatl-0.1.13 → koatl-0.1.14}/koatl/tests/e2e/base/nary-list.tl +0 -0
- {koatl-0.1.13 → koatl-0.1.14}/koatl/tests/e2e/base/placeholder.tl +0 -0
- {koatl-0.1.13 → koatl-0.1.14}/koatl/tests/e2e/base/precedence.tl +0 -0
- {koatl-0.1.13 → koatl-0.1.14}/koatl/tests/e2e/base/semantic_whitespace.tl +0 -0
- {koatl-0.1.13 → koatl-0.1.14}/koatl/tests/e2e/base/slice.tl +0 -0
- {koatl-0.1.13 → koatl-0.1.14}/koatl/tests/e2e/base/try.tl +0 -0
- {koatl-0.1.13 → koatl-0.1.14}/koatl/tests/e2e/destructure.tl +0 -0
- {koatl-0.1.13 → koatl-0.1.14}/koatl/tests/e2e/prelude/async.tl +0 -0
- {koatl-0.1.13 → koatl-0.1.14}/koatl/tests/e2e/prelude/reader.tl +0 -0
- {koatl-0.1.13 → koatl-0.1.14}/koatl/tests/e2e/prelude/virtual.tl +0 -0
- {koatl-0.1.13 → koatl-0.1.14}/koatl/tests/e2e/util/__init__.py +0 -0
- {koatl-0.1.13 → koatl-0.1.14}/koatl/tests/e2e/util/module0.tl +0 -0
- {koatl-0.1.13 → koatl-0.1.14}/koatl/tests/e2e/util/module1.tl +0 -0
- {koatl-0.1.13 → koatl-0.1.14}/koatl/tests/e2e/util/module2.tl +0 -0
- {koatl-0.1.13 → koatl-0.1.14}/koatl/tests/parse/arith.tl +0 -0
- {koatl-0.1.13 → koatl-0.1.14}/koatl/tests/parse/assign.tl +0 -0
- {koatl-0.1.13 → koatl-0.1.14}/koatl/tests/parse/func.tl +0 -0
- {koatl-0.1.13 → koatl-0.1.14}/koatl/tests/parse/matches.tl +0 -0
- {koatl-0.1.13 → koatl-0.1.14}/koatl/tests/test_e2e.py +0 -0
- {koatl-0.1.13 → koatl-0.1.14}/koatl/tests/test_parse.py +0 -0
- {koatl-0.1.13 → koatl-0.1.14}/koatl-core/Cargo.toml +0 -0
- {koatl-0.1.13 → koatl-0.1.14}/koatl-core/parser/Cargo.toml +0 -0
- {koatl-0.1.13 → koatl-0.1.14}/koatl-core/parser/src/lexer.rs +0 -0
- {koatl-0.1.13 → koatl-0.1.14}/koatl-core/parser/src/lib.rs +0 -0
- {koatl-0.1.13 → koatl-0.1.14}/koatl-core/parser/src/util.rs +0 -0
- {koatl-0.1.13 → koatl-0.1.14}/koatl-core/parser/tests/lexer.rs +0 -0
- {koatl-0.1.13 → koatl-0.1.14}/koatl-core/src/lib.rs +0 -0
- {koatl-0.1.13 → koatl-0.1.14}/koatl-core/src/linecol.rs +0 -0
- {koatl-0.1.13 → koatl-0.1.14}/koatl-core/src/main.rs +0 -0
- {koatl-0.1.13 → koatl-0.1.14}/koatl-core/src/parser.rs +0 -0
- {koatl-0.1.13 → koatl-0.1.14}/koatl-core/src/py/ast.rs +0 -0
- {koatl-0.1.13 → koatl-0.1.14}/koatl-core/src/py/emit.rs +0 -0
- {koatl-0.1.13 → koatl-0.1.14}/koatl-core/src/py/mod.rs +0 -0
- {koatl-0.1.13 → koatl-0.1.14}/koatl-core/src/py/util.rs +0 -0
- {koatl-0.1.13 → koatl-0.1.14}/pyproject.toml +0 -0
- {koatl-0.1.13 → koatl-0.1.14}/python/koatl/__init__.py +0 -0
- {koatl-0.1.13 → koatl-0.1.14}/python/koatl/__main__.py +0 -0
- {koatl-0.1.13 → koatl-0.1.14}/python/koatl/notebook/__init__.py +0 -0
- {koatl-0.1.13 → koatl-0.1.14}/python/koatl/notebook/magic.py +0 -0
- {koatl-0.1.13 → koatl-0.1.14}/python/koatl/prelude/__init__.tl +0 -0
- {koatl-0.1.13 → koatl-0.1.14}/python/koatl/prelude/functional/async_util.py +0 -0
- {koatl-0.1.13 → koatl-0.1.14}/python/koatl/prelude/iterable.tl +0 -0
- {koatl-0.1.13 → koatl-0.1.14}/python/koatl/runtime/record.py +0 -0
|
@@ -2,7 +2,7 @@ def transpile_from_source(source, mode="script", script_path="<string>"):
|
|
|
2
2
|
from koatl import transpile
|
|
3
3
|
import ast
|
|
4
4
|
|
|
5
|
-
transpiled_code = transpile(source, mode=mode)
|
|
5
|
+
transpiled_code = transpile(source, mode=mode, filename=str(script_path))
|
|
6
6
|
|
|
7
7
|
return ast.unparse(transpiled_code)
|
|
8
8
|
|
|
@@ -10,7 +10,7 @@ def transpile_from_source(source, mode="script", script_path="<string>"):
|
|
|
10
10
|
def run_from_source(source, mode="script", script_path="<string>"):
|
|
11
11
|
from koatl import transpile
|
|
12
12
|
|
|
13
|
-
transpiled_code = transpile(source, mode=mode)
|
|
13
|
+
transpiled_code = transpile(source, mode=mode, filename=str(script_path))
|
|
14
14
|
code_obj = compile(transpiled_code, script_path, "exec")
|
|
15
15
|
|
|
16
16
|
script_globals = {"__name__": "__main__"}
|
|
@@ -1,9 +1,10 @@
|
|
|
1
|
-
export import .
|
|
1
|
+
export import .monad.*
|
|
2
|
+
export import .result.*
|
|
2
3
|
export import .async.*
|
|
3
4
|
export import .reader.*
|
|
4
5
|
|
|
5
6
|
export Fn = class:
|
|
6
|
-
compose = &
|
|
7
|
+
compose = staticmethod& (*args) =>
|
|
7
8
|
args match:
|
|
8
9
|
[] => raise ValueError("At least one function is required for composition")
|
|
9
10
|
[f] => f
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
import functools.wraps
|
|
2
|
+
import asyncio
|
|
3
|
+
import .async_util
|
|
4
|
+
|
|
5
|
+
export Async = class:
|
|
6
|
+
__init__ = (self, awaitable) => self.generator = awaitable.__await__()
|
|
7
|
+
|
|
8
|
+
__await__ = self => self.generator
|
|
9
|
+
|
|
10
|
+
__repr__ = self => "Async(...)"
|
|
11
|
+
|
|
12
|
+
from_generator_fn = staticmethod& (generator_fn, *args, **kwargs) =>
|
|
13
|
+
m = object.__new__(Async)
|
|
14
|
+
m.generator = generator_fn(*args, **kwargs)
|
|
15
|
+
return m
|
|
16
|
+
|
|
17
|
+
run = self => asyncio.run(async_util.to_coro(self))
|
|
18
|
+
|
|
19
|
+
bind_once = (self, f) => Async.from_generator_fn& () =>
|
|
20
|
+
result = f(yield from self.__await__())
|
|
21
|
+
|
|
22
|
+
if hasattr(result, "__await__"):
|
|
23
|
+
return yield from result.__await__()
|
|
24
|
+
|
|
25
|
+
return result
|
|
26
|
+
|
|
27
|
+
bind_gen = (self, gen) => Async.from_generator_fn& () =>
|
|
28
|
+
nonlocal self = self
|
|
29
|
+
try:
|
|
30
|
+
while True:
|
|
31
|
+
self = gen.send(yield from self.__await__())
|
|
32
|
+
except StopIteration(value=value):
|
|
33
|
+
return value
|
|
34
|
+
|
|
35
|
+
pure = staticmethod& x => Async.from_generator_fn& () =>
|
|
36
|
+
return x
|
|
37
|
+
yield None
|
|
38
|
+
|
|
39
|
+
sleep = staticmethod& x => Async(asyncio.sleep(x))
|
|
40
|
+
|
|
41
|
+
#- TODO: Why is asyncio.gather eager? -#
|
|
42
|
+
gather = staticmethod& (*args) => Async.from_generator_fn& () => yield from asyncio.gather(*args)
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import abc
|
|
2
|
+
|
|
3
|
+
export Monad = class(abc.ABC):
|
|
4
|
+
bind = (self, f) => self.bind_once(f)
|
|
5
|
+
|
|
6
|
+
# An optional, optimized implementation of `bind` that skips deep recursion.
|
|
7
|
+
bind_gen = (self, gen) => raise NotImplementedError()
|
|
8
|
+
|
|
9
|
+
# The default implementation required for `@` syntax that should be overridden by subclasses.
|
|
10
|
+
bind_once = abc.abstractmethod& (self, f) => None
|
|
11
|
+
|
|
12
|
+
pure = staticmethod& abc.abstractmethod& value => None
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
import functools.wraps
|
|
2
|
+
|
|
3
|
+
export Reader = class:
|
|
4
|
+
__init__ = (self, fn) => self.fn = fn
|
|
5
|
+
|
|
6
|
+
__repr__ = self => "Reader(...)"
|
|
7
|
+
|
|
8
|
+
run = (self, ctx) => self.fn(ctx)
|
|
9
|
+
|
|
10
|
+
bind_once = (self, f) => Reader& ctx =>
|
|
11
|
+
v = f(self.fn(ctx))
|
|
12
|
+
if v matches Reader():
|
|
13
|
+
v.fn(ctx)
|
|
14
|
+
else:
|
|
15
|
+
v
|
|
16
|
+
|
|
17
|
+
# TODO: this is a workaround to avoid recursion.
|
|
18
|
+
# how to get bind_gen directly from bind_once?
|
|
19
|
+
bind_gen = (self, gen) => Reader& ctx =>
|
|
20
|
+
nonlocal self = self
|
|
21
|
+
try:
|
|
22
|
+
while True:
|
|
23
|
+
self = gen.send(self.fn(ctx))
|
|
24
|
+
except StopIteration(value=value):
|
|
25
|
+
return value
|
|
26
|
+
|
|
27
|
+
NoKey = object()
|
|
28
|
+
|
|
29
|
+
ask = staticmethod& (key=NoKey) => Reader(
|
|
30
|
+
key === Reader.NoKey then ctx => ctx else ctx => ctx[key]
|
|
31
|
+
)
|
|
32
|
+
|
|
33
|
+
pure = staticmethod& value => Reader(ctx => value)
|
|
@@ -0,0 +1,80 @@
|
|
|
1
|
+
import functools.wraps
|
|
2
|
+
import .monad.Monad
|
|
3
|
+
import koatl.runtime.virtual.register_global_attr
|
|
4
|
+
|
|
5
|
+
export Result = class(Monad):
|
|
6
|
+
bind_once = (self, f) => self match:
|
|
7
|
+
Ok(value) => f(value)
|
|
8
|
+
Ok() => f(self)
|
|
9
|
+
default => self
|
|
10
|
+
|
|
11
|
+
bind_gen = (self, gen) =>
|
|
12
|
+
try:
|
|
13
|
+
while True:
|
|
14
|
+
if not __tl__.ok(self):
|
|
15
|
+
return self
|
|
16
|
+
|
|
17
|
+
if self matches Ok(value):
|
|
18
|
+
self = value
|
|
19
|
+
|
|
20
|
+
self = gen.send(self)
|
|
21
|
+
except StopIteration(value=value):
|
|
22
|
+
if value === None:
|
|
23
|
+
return Ok(None)
|
|
24
|
+
return value
|
|
25
|
+
|
|
26
|
+
map_err = (self, f) =>
|
|
27
|
+
if self matches Err():
|
|
28
|
+
return f(self)
|
|
29
|
+
else:
|
|
30
|
+
return self
|
|
31
|
+
|
|
32
|
+
OkMeta = class(type):
|
|
33
|
+
__instancecheck__ = (cls, instance) => __tl__.ok(instance)
|
|
34
|
+
|
|
35
|
+
export Ok = class(metaclass=OkMeta):
|
|
36
|
+
__match_args__ = ("value",)
|
|
37
|
+
|
|
38
|
+
__init__ = (self, value) =>
|
|
39
|
+
self.value = value
|
|
40
|
+
|
|
41
|
+
# Since we are using a custom metaclass, we can't derive from Result
|
|
42
|
+
# so need to copy the methods over manually from Result
|
|
43
|
+
|
|
44
|
+
# We could just use the object fallback methods, but this avoids
|
|
45
|
+
# the manual __tl__.vget
|
|
46
|
+
bind_once = Result.bind_once
|
|
47
|
+
bind_gen = Result.bind_gen
|
|
48
|
+
map_err = Result.map_err
|
|
49
|
+
|
|
50
|
+
assert = staticmethod& value =>
|
|
51
|
+
value match:
|
|
52
|
+
BaseException() as e => raise e
|
|
53
|
+
None => raise ValueError("Expected a value, got None")
|
|
54
|
+
default value
|
|
55
|
+
|
|
56
|
+
export Err = BaseException
|
|
57
|
+
|
|
58
|
+
register_global_attr(
|
|
59
|
+
object,
|
|
60
|
+
"map_err",
|
|
61
|
+
Result.map_err
|
|
62
|
+
)
|
|
63
|
+
|
|
64
|
+
register_global_attr(
|
|
65
|
+
object,
|
|
66
|
+
"bind",
|
|
67
|
+
Result.bind_once
|
|
68
|
+
)
|
|
69
|
+
|
|
70
|
+
register_global_attr(
|
|
71
|
+
object,
|
|
72
|
+
"bind_once",
|
|
73
|
+
Result.bind_once
|
|
74
|
+
)
|
|
75
|
+
|
|
76
|
+
register_global_attr(
|
|
77
|
+
object,
|
|
78
|
+
"bind_gen",
|
|
79
|
+
Result.bind_gen
|
|
80
|
+
)
|
|
@@ -22,10 +22,11 @@ from .helpers import *
|
|
|
22
22
|
|
|
23
23
|
|
|
24
24
|
__tl__ = SimpleNamespace(
|
|
25
|
+
vget=virtual.vget,
|
|
26
|
+
vhas=virtual.vhas,
|
|
25
27
|
unpack_record=helpers.unpack_record,
|
|
26
28
|
set_exports=helpers.set_exports,
|
|
27
29
|
do=helpers.do,
|
|
28
|
-
vget=helpers.vget,
|
|
29
30
|
ok=helpers.ok,
|
|
30
31
|
partial=functools.partial,
|
|
31
32
|
**{name: helpers.__dict__[name] for name in helpers.__all__},
|
|
@@ -56,17 +56,17 @@ def ok(obj):
|
|
|
56
56
|
def do(f):
|
|
57
57
|
@wraps(f)
|
|
58
58
|
def impl(*args, **kwargs):
|
|
59
|
-
|
|
59
|
+
gen = f(*args, **kwargs)
|
|
60
60
|
|
|
61
61
|
try:
|
|
62
|
-
m =
|
|
62
|
+
m = gen.send(None)
|
|
63
63
|
except StopIteration as e:
|
|
64
64
|
return e.value
|
|
65
65
|
|
|
66
66
|
def recurse(v):
|
|
67
67
|
nonlocal m
|
|
68
68
|
try:
|
|
69
|
-
m =
|
|
69
|
+
m = gen.send(v)
|
|
70
70
|
return vget(m, "bind_once")(recurse)
|
|
71
71
|
except StopIteration as e:
|
|
72
72
|
try:
|
|
@@ -74,6 +74,10 @@ def do(f):
|
|
|
74
74
|
except AttributeError:
|
|
75
75
|
return e.value
|
|
76
76
|
|
|
77
|
-
|
|
77
|
+
try:
|
|
78
|
+
print(m, gen)
|
|
79
|
+
return vget(m, "bind_gen")(gen)
|
|
80
|
+
except (NotImplementedError, AttributeError):
|
|
81
|
+
return vget(m, "bind_once")(recurse)
|
|
78
82
|
|
|
79
83
|
return impl
|
|
@@ -53,9 +53,13 @@ class TlLoader(Loader):
|
|
|
53
53
|
)
|
|
54
54
|
|
|
55
55
|
if module.__name__.startswith("koatl.prelude"):
|
|
56
|
-
transpiled_code = transpile(
|
|
56
|
+
transpiled_code = transpile(
|
|
57
|
+
source_code, mode="prelude", filename=self.filepath
|
|
58
|
+
)
|
|
57
59
|
else:
|
|
58
|
-
transpiled_code = transpile(
|
|
60
|
+
transpiled_code = transpile(
|
|
61
|
+
source_code, mode="module", filename=self.filepath
|
|
62
|
+
)
|
|
59
63
|
|
|
60
64
|
code = compile(transpiled_code, self.filepath, "exec")
|
|
61
65
|
|
|
@@ -46,6 +46,33 @@ def vget(obj, name):
|
|
|
46
46
|
) from None
|
|
47
47
|
|
|
48
48
|
|
|
49
|
+
def vhas(obj, name):
|
|
50
|
+
if hasattr(obj, name):
|
|
51
|
+
return True
|
|
52
|
+
|
|
53
|
+
if name == "iter":
|
|
54
|
+
if isinstance(obj, slice):
|
|
55
|
+
return True
|
|
56
|
+
|
|
57
|
+
try:
|
|
58
|
+
obj.items()
|
|
59
|
+
return True
|
|
60
|
+
except AttributeError:
|
|
61
|
+
pass
|
|
62
|
+
|
|
63
|
+
try:
|
|
64
|
+
iter(obj)
|
|
65
|
+
return True
|
|
66
|
+
except TypeError:
|
|
67
|
+
pass
|
|
68
|
+
|
|
69
|
+
v = fast_vget(obj, name)
|
|
70
|
+
if v is not None:
|
|
71
|
+
return True
|
|
72
|
+
|
|
73
|
+
return False
|
|
74
|
+
|
|
75
|
+
|
|
49
76
|
def Trait(module, name, methods, *, requires=[]):
|
|
50
77
|
def fix_methods(type_name, methods):
|
|
51
78
|
import inspect
|
|
@@ -16,4 +16,14 @@ fn = () =>
|
|
|
16
16
|
v.append(@321)
|
|
17
17
|
fn()
|
|
18
18
|
|
|
19
|
-
assert_eq(v, [123, 222, 321])
|
|
19
|
+
assert_eq(v, [123, 222, 321])
|
|
20
|
+
|
|
21
|
+
v = []
|
|
22
|
+
fn = () =>
|
|
23
|
+
v.append(@123)
|
|
24
|
+
v.append(@Ok(None))
|
|
25
|
+
v.append(@ValueError())
|
|
26
|
+
v.append(@321)
|
|
27
|
+
fn()
|
|
28
|
+
|
|
29
|
+
assert_eq(v, [123, None])
|
|
@@ -189,7 +189,7 @@ pub enum Expr<'a> {
|
|
|
189
189
|
Fn(Vec<ArgDefItem<'a>>, Box<SExpr<'a>>),
|
|
190
190
|
Fstr(Spanned<String>, Vec<(SFmtExpr<'a>, Spanned<String>)>),
|
|
191
191
|
|
|
192
|
-
Decorated(
|
|
192
|
+
Decorated(Box<SExpr<'a>>, Box<SExpr<'a>>),
|
|
193
193
|
|
|
194
194
|
Block(Vec<SStmt<'a>>),
|
|
195
195
|
}
|
|
@@ -620,19 +620,6 @@ where
|
|
|
620
620
|
))
|
|
621
621
|
.boxed();
|
|
622
622
|
|
|
623
|
-
let deco_list = enumeration(expr.clone(), symbol(","))
|
|
624
|
-
.delimited_by_with_eol(just(Token::Symbol("[")), just(Token::Symbol("]")))
|
|
625
|
-
.labelled("decorators")
|
|
626
|
-
.boxed();
|
|
627
|
-
|
|
628
|
-
let decorators = symbol("&").ignore_then(deco_list.clone());
|
|
629
|
-
|
|
630
|
-
let decorated = decorators
|
|
631
|
-
.then(below_pipe.clone())
|
|
632
|
-
.map(|(decorators, expr)| Expr::Decorated(decorators, Box::new(expr)))
|
|
633
|
-
.spanned()
|
|
634
|
-
.labelled("fn");
|
|
635
|
-
|
|
636
623
|
enum ControlKw {
|
|
637
624
|
Await,
|
|
638
625
|
Yield,
|
|
@@ -665,7 +652,6 @@ where
|
|
|
665
652
|
|
|
666
653
|
atom.define(
|
|
667
654
|
choice((
|
|
668
|
-
decorated,
|
|
669
655
|
ident_expr.clone(),
|
|
670
656
|
classic_if,
|
|
671
657
|
classic_match,
|
|
@@ -751,7 +737,7 @@ where
|
|
|
751
737
|
.labelled("then-attribute")
|
|
752
738
|
.boxed();
|
|
753
739
|
|
|
754
|
-
let
|
|
740
|
+
let raw_attr = symbol("!")
|
|
755
741
|
.ignore_then(ident.clone())
|
|
756
742
|
.map(Postfix::RawAttribute)
|
|
757
743
|
.labelled("raw-attribute")
|
|
@@ -763,7 +749,7 @@ where
|
|
|
763
749
|
symbol("?")
|
|
764
750
|
.to(1)
|
|
765
751
|
.or_not()
|
|
766
|
-
.then(choice((call, subscript, attribute, then,
|
|
752
|
+
.then(choice((call, subscript, attribute, then, raw_attr)))
|
|
767
753
|
.repeated(),
|
|
768
754
|
|expr, (coal, op), e| -> SExpr {
|
|
769
755
|
(
|
|
@@ -812,6 +798,19 @@ where
|
|
|
812
798
|
.labelled("postfix")
|
|
813
799
|
.boxed();
|
|
814
800
|
|
|
801
|
+
let decorator = postfix
|
|
802
|
+
.clone()
|
|
803
|
+
.then(symbol("&").ignore_then(expr.clone()).or_not())
|
|
804
|
+
.map_with(|(lhs, rhs), e| {
|
|
805
|
+
if let Some(rhs) = rhs {
|
|
806
|
+
(Expr::Decorated(Box::new(lhs), Box::new(rhs)), e.span())
|
|
807
|
+
} else {
|
|
808
|
+
lhs
|
|
809
|
+
}
|
|
810
|
+
})
|
|
811
|
+
.labelled("decorator")
|
|
812
|
+
.boxed();
|
|
813
|
+
|
|
815
814
|
let mut checked = Recursive::<Indirect<TInput, SExpr, TExtra>>::declare();
|
|
816
815
|
|
|
817
816
|
unary.define(
|
|
@@ -823,7 +822,7 @@ where
|
|
|
823
822
|
}
|
|
824
823
|
.repeated()
|
|
825
824
|
.foldr_with(
|
|
826
|
-
choice((
|
|
825
|
+
choice((decorator, checked.clone())),
|
|
827
826
|
|op: UnaryOp, rhs: SExpr, e| (Expr::Unary(op, Box::new(rhs)), e.span()),
|
|
828
827
|
)
|
|
829
828
|
.labelled("unary-expression")
|
|
@@ -663,7 +663,7 @@ fn transform_assignment<'src, 'ast>(
|
|
|
663
663
|
}
|
|
664
664
|
Expr::Decorated(deco, right) => {
|
|
665
665
|
cur_node = &right.0;
|
|
666
|
-
decorators.
|
|
666
|
+
decorators.push(deco);
|
|
667
667
|
}
|
|
668
668
|
Expr::Call(left, right) => {
|
|
669
669
|
if right.len() != 1 {
|
|
@@ -2285,20 +2285,18 @@ impl<'src> SExprExt<'src> for SExpr<'src> {
|
|
|
2285
2285
|
pre: aux_stmts,
|
|
2286
2286
|
})
|
|
2287
2287
|
}
|
|
2288
|
-
Expr::Decorated(
|
|
2288
|
+
Expr::Decorated(deco, expr) => {
|
|
2289
2289
|
let mut pre = PyBlock::new();
|
|
2290
2290
|
let mut node = bind_pre(&mut pre, expr.transform(ctx)?);
|
|
2291
2291
|
|
|
2292
|
-
|
|
2293
|
-
|
|
2294
|
-
|
|
2295
|
-
|
|
2296
|
-
|
|
2297
|
-
|
|
2298
|
-
|
|
2299
|
-
)
|
|
2300
|
-
.into();
|
|
2301
|
-
}
|
|
2292
|
+
node = (
|
|
2293
|
+
PyExpr::Call(
|
|
2294
|
+
Box::new(bind_pre(&mut pre, deco.transform(ctx)?)),
|
|
2295
|
+
vec![PyCallItem::Arg(node)],
|
|
2296
|
+
),
|
|
2297
|
+
*span,
|
|
2298
|
+
)
|
|
2299
|
+
.into();
|
|
2302
2300
|
|
|
2303
2301
|
Ok(SPyExprWithPre { value: node, pre })
|
|
2304
2302
|
}
|
|
@@ -2,7 +2,7 @@ def transpile_from_source(source, mode="script", script_path="<string>"):
|
|
|
2
2
|
from koatl import transpile
|
|
3
3
|
import ast
|
|
4
4
|
|
|
5
|
-
transpiled_code = transpile(source, mode=mode)
|
|
5
|
+
transpiled_code = transpile(source, mode=mode, filename=str(script_path))
|
|
6
6
|
|
|
7
7
|
return ast.unparse(transpiled_code)
|
|
8
8
|
|
|
@@ -10,7 +10,7 @@ def transpile_from_source(source, mode="script", script_path="<string>"):
|
|
|
10
10
|
def run_from_source(source, mode="script", script_path="<string>"):
|
|
11
11
|
from koatl import transpile
|
|
12
12
|
|
|
13
|
-
transpiled_code = transpile(source, mode=mode)
|
|
13
|
+
transpiled_code = transpile(source, mode=mode, filename=str(script_path))
|
|
14
14
|
code_obj = compile(transpiled_code, script_path, "exec")
|
|
15
15
|
|
|
16
16
|
script_globals = {"__name__": "__main__"}
|
|
@@ -1,9 +1,10 @@
|
|
|
1
|
-
export import .
|
|
1
|
+
export import .monad.*
|
|
2
|
+
export import .result.*
|
|
2
3
|
export import .async.*
|
|
3
4
|
export import .reader.*
|
|
4
5
|
|
|
5
6
|
export Fn = class:
|
|
6
|
-
compose = &
|
|
7
|
+
compose = staticmethod& (*args) =>
|
|
7
8
|
args match:
|
|
8
9
|
[] => raise ValueError("At least one function is required for composition")
|
|
9
10
|
[f] => f
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
import functools.wraps
|
|
2
|
+
import asyncio
|
|
3
|
+
import .async_util
|
|
4
|
+
|
|
5
|
+
export Async = class:
|
|
6
|
+
__init__ = (self, awaitable) => self.generator = awaitable.__await__()
|
|
7
|
+
|
|
8
|
+
__await__ = self => self.generator
|
|
9
|
+
|
|
10
|
+
__repr__ = self => "Async(...)"
|
|
11
|
+
|
|
12
|
+
from_generator_fn = staticmethod& (generator_fn, *args, **kwargs) =>
|
|
13
|
+
m = object.__new__(Async)
|
|
14
|
+
m.generator = generator_fn(*args, **kwargs)
|
|
15
|
+
return m
|
|
16
|
+
|
|
17
|
+
run = self => asyncio.run(async_util.to_coro(self))
|
|
18
|
+
|
|
19
|
+
bind_once = (self, f) => Async.from_generator_fn& () =>
|
|
20
|
+
result = f(yield from self.__await__())
|
|
21
|
+
|
|
22
|
+
if hasattr(result, "__await__"):
|
|
23
|
+
return yield from result.__await__()
|
|
24
|
+
|
|
25
|
+
return result
|
|
26
|
+
|
|
27
|
+
bind_gen = (self, gen) => Async.from_generator_fn& () =>
|
|
28
|
+
nonlocal self = self
|
|
29
|
+
try:
|
|
30
|
+
while True:
|
|
31
|
+
self = gen.send(yield from self.__await__())
|
|
32
|
+
except StopIteration(value=value):
|
|
33
|
+
return value
|
|
34
|
+
|
|
35
|
+
pure = staticmethod& x => Async.from_generator_fn& () =>
|
|
36
|
+
return x
|
|
37
|
+
yield None
|
|
38
|
+
|
|
39
|
+
sleep = staticmethod& x => Async(asyncio.sleep(x))
|
|
40
|
+
|
|
41
|
+
#- TODO: Why is asyncio.gather eager? -#
|
|
42
|
+
gather = staticmethod& (*args) => Async.from_generator_fn& () => yield from asyncio.gather(*args)
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import abc
|
|
2
|
+
|
|
3
|
+
export Monad = class(abc.ABC):
|
|
4
|
+
bind = (self, f) => self.bind_once(f)
|
|
5
|
+
|
|
6
|
+
# An optional, optimized implementation of `bind` that skips deep recursion.
|
|
7
|
+
bind_gen = (self, gen) => raise NotImplementedError()
|
|
8
|
+
|
|
9
|
+
# The default implementation required for `@` syntax that should be overridden by subclasses.
|
|
10
|
+
bind_once = abc.abstractmethod& (self, f) => None
|
|
11
|
+
|
|
12
|
+
pure = staticmethod& abc.abstractmethod& value => None
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
import functools.wraps
|
|
2
|
+
|
|
3
|
+
export Reader = class:
|
|
4
|
+
__init__ = (self, fn) => self.fn = fn
|
|
5
|
+
|
|
6
|
+
__repr__ = self => "Reader(...)"
|
|
7
|
+
|
|
8
|
+
run = (self, ctx) => self.fn(ctx)
|
|
9
|
+
|
|
10
|
+
bind_once = (self, f) => Reader& ctx =>
|
|
11
|
+
v = f(self.fn(ctx))
|
|
12
|
+
if v matches Reader():
|
|
13
|
+
v.fn(ctx)
|
|
14
|
+
else:
|
|
15
|
+
v
|
|
16
|
+
|
|
17
|
+
# TODO: this is a workaround to avoid recursion.
|
|
18
|
+
# how to get bind_gen directly from bind_once?
|
|
19
|
+
bind_gen = (self, gen) => Reader& ctx =>
|
|
20
|
+
nonlocal self = self
|
|
21
|
+
try:
|
|
22
|
+
while True:
|
|
23
|
+
self = gen.send(self.fn(ctx))
|
|
24
|
+
except StopIteration(value=value):
|
|
25
|
+
return value
|
|
26
|
+
|
|
27
|
+
NoKey = object()
|
|
28
|
+
|
|
29
|
+
ask = staticmethod& (key=NoKey) => Reader(
|
|
30
|
+
key === Reader.NoKey then ctx => ctx else ctx => ctx[key]
|
|
31
|
+
)
|
|
32
|
+
|
|
33
|
+
pure = staticmethod& value => Reader(ctx => value)
|
|
@@ -0,0 +1,80 @@
|
|
|
1
|
+
import functools.wraps
|
|
2
|
+
import .monad.Monad
|
|
3
|
+
import koatl.runtime.virtual.register_global_attr
|
|
4
|
+
|
|
5
|
+
export Result = class(Monad):
|
|
6
|
+
bind_once = (self, f) => self match:
|
|
7
|
+
Ok(value) => f(value)
|
|
8
|
+
Ok() => f(self)
|
|
9
|
+
default => self
|
|
10
|
+
|
|
11
|
+
bind_gen = (self, gen) =>
|
|
12
|
+
try:
|
|
13
|
+
while True:
|
|
14
|
+
if not __tl__.ok(self):
|
|
15
|
+
return self
|
|
16
|
+
|
|
17
|
+
if self matches Ok(value):
|
|
18
|
+
self = value
|
|
19
|
+
|
|
20
|
+
self = gen.send(self)
|
|
21
|
+
except StopIteration(value=value):
|
|
22
|
+
if value === None:
|
|
23
|
+
return Ok(None)
|
|
24
|
+
return value
|
|
25
|
+
|
|
26
|
+
map_err = (self, f) =>
|
|
27
|
+
if self matches Err():
|
|
28
|
+
return f(self)
|
|
29
|
+
else:
|
|
30
|
+
return self
|
|
31
|
+
|
|
32
|
+
OkMeta = class(type):
|
|
33
|
+
__instancecheck__ = (cls, instance) => __tl__.ok(instance)
|
|
34
|
+
|
|
35
|
+
export Ok = class(metaclass=OkMeta):
|
|
36
|
+
__match_args__ = ("value",)
|
|
37
|
+
|
|
38
|
+
__init__ = (self, value) =>
|
|
39
|
+
self.value = value
|
|
40
|
+
|
|
41
|
+
# Since we are using a custom metaclass, we can't derive from Result
|
|
42
|
+
# so need to copy the methods over manually from Result
|
|
43
|
+
|
|
44
|
+
# We could just use the object fallback methods, but this avoids
|
|
45
|
+
# the manual __tl__.vget
|
|
46
|
+
bind_once = Result.bind_once
|
|
47
|
+
bind_gen = Result.bind_gen
|
|
48
|
+
map_err = Result.map_err
|
|
49
|
+
|
|
50
|
+
assert = staticmethod& value =>
|
|
51
|
+
value match:
|
|
52
|
+
BaseException() as e => raise e
|
|
53
|
+
None => raise ValueError("Expected a value, got None")
|
|
54
|
+
default value
|
|
55
|
+
|
|
56
|
+
export Err = BaseException
|
|
57
|
+
|
|
58
|
+
register_global_attr(
|
|
59
|
+
object,
|
|
60
|
+
"map_err",
|
|
61
|
+
Result.map_err
|
|
62
|
+
)
|
|
63
|
+
|
|
64
|
+
register_global_attr(
|
|
65
|
+
object,
|
|
66
|
+
"bind",
|
|
67
|
+
Result.bind_once
|
|
68
|
+
)
|
|
69
|
+
|
|
70
|
+
register_global_attr(
|
|
71
|
+
object,
|
|
72
|
+
"bind_once",
|
|
73
|
+
Result.bind_once
|
|
74
|
+
)
|
|
75
|
+
|
|
76
|
+
register_global_attr(
|
|
77
|
+
object,
|
|
78
|
+
"bind_gen",
|
|
79
|
+
Result.bind_gen
|
|
80
|
+
)
|