koatl 0.1.13__tar.gz → 0.1.15__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.15}/Cargo.lock +2 -1
- {koatl-0.1.13 → koatl-0.1.15}/PKG-INFO +1 -1
- {koatl-0.1.13 → koatl-0.1.15}/koatl/Cargo.toml +1 -1
- {koatl-0.1.13 → koatl-0.1.15/koatl}/python/koatl/cli.py +2 -2
- {koatl-0.1.13 → koatl-0.1.15}/koatl/python/koatl/notebook/__init__.py +14 -5
- {koatl-0.1.13 → koatl-0.1.15/koatl}/python/koatl/prelude/functional/__init__.tl +5 -4
- koatl-0.1.15/koatl/python/koatl/prelude/functional/async.tl +43 -0
- koatl-0.1.15/koatl/python/koatl/prelude/functional/monad.tl +12 -0
- koatl-0.1.15/koatl/python/koatl/prelude/functional/reader.tl +31 -0
- koatl-0.1.15/koatl/python/koatl/prelude/functional/result.tl +80 -0
- {koatl-0.1.13 → koatl-0.1.15}/koatl/python/koatl/prelude/iterable.tl +3 -3
- {koatl-0.1.13 → koatl-0.1.15}/koatl/python/koatl/runtime/__init__.py +3 -1
- {koatl-0.1.13 → koatl-0.1.15}/koatl/python/koatl/runtime/helpers.py +10 -4
- {koatl-0.1.13 → koatl-0.1.15/koatl}/python/koatl/runtime/meta_finder.py +6 -2
- {koatl-0.1.13 → koatl-0.1.15/koatl}/python/koatl/runtime/virtual.py +27 -0
- {koatl-0.1.13 → koatl-0.1.15}/koatl/src/emit_py.rs +24 -11
- {koatl-0.1.13 → koatl-0.1.15}/koatl/src/lib.rs +1 -1
- {koatl-0.1.13 → koatl-0.1.15}/koatl/tests/e2e/base/coal.tl +5 -4
- {koatl-0.1.13 → koatl-0.1.15}/koatl/tests/e2e/base/if_expr.tl +1 -1
- koatl-0.1.15/koatl/tests/e2e/base/scopes.tl +49 -0
- {koatl-0.1.13 → koatl-0.1.15}/koatl/tests/e2e/base/semantic_whitespace.tl +2 -2
- koatl-0.1.13/koatl/tests/e2e/prelude/ok.tl → koatl-0.1.15/koatl/tests/e2e/prelude/result.tl +11 -1
- koatl-0.1.15/koatl/tests/parse/deco.tl +2 -0
- {koatl-0.1.13 → koatl-0.1.15}/koatl/tests/test_e2e.py +26 -9
- {koatl-0.1.13 → koatl-0.1.15}/koatl-core/Cargo.toml +1 -0
- {koatl-0.1.13 → koatl-0.1.15}/koatl-core/parser/Cargo.toml +1 -1
- {koatl-0.1.13 → koatl-0.1.15}/koatl-core/parser/src/ast.rs +56 -40
- {koatl-0.1.13 → koatl-0.1.15}/koatl-core/parser/src/lexer.rs +2 -2
- {koatl-0.1.13 → koatl-0.1.15}/koatl-core/parser/src/parser.rs +235 -169
- {koatl-0.1.13 → koatl-0.1.15}/koatl-core/parser/src/util.rs +33 -36
- {koatl-0.1.13 → koatl-0.1.15}/koatl-core/src/lib.rs +2 -2
- {koatl-0.1.13 → koatl-0.1.15}/koatl-core/src/main.rs +1 -0
- {koatl-0.1.13 → koatl-0.1.15}/koatl-core/src/py/ast.rs +19 -12
- {koatl-0.1.13 → koatl-0.1.15}/koatl-core/src/py/emit.rs +16 -2
- {koatl-0.1.13 → koatl-0.1.15}/koatl-core/src/py/util.rs +18 -2
- {koatl-0.1.13 → koatl-0.1.15}/koatl-core/src/transform.rs +1596 -1189
- {koatl-0.1.13/koatl → koatl-0.1.15}/python/koatl/cli.py +2 -2
- {koatl-0.1.13 → koatl-0.1.15}/python/koatl/notebook/__init__.py +14 -5
- {koatl-0.1.13/koatl → koatl-0.1.15}/python/koatl/prelude/functional/__init__.tl +5 -4
- koatl-0.1.15/python/koatl/prelude/functional/async.tl +43 -0
- koatl-0.1.15/python/koatl/prelude/functional/monad.tl +12 -0
- koatl-0.1.15/python/koatl/prelude/functional/reader.tl +31 -0
- koatl-0.1.15/python/koatl/prelude/functional/result.tl +80 -0
- {koatl-0.1.13 → koatl-0.1.15}/python/koatl/prelude/iterable.tl +3 -3
- {koatl-0.1.13 → koatl-0.1.15}/python/koatl/runtime/__init__.py +3 -1
- {koatl-0.1.13 → koatl-0.1.15}/python/koatl/runtime/helpers.py +10 -4
- {koatl-0.1.13/koatl → koatl-0.1.15}/python/koatl/runtime/meta_finder.py +6 -2
- {koatl-0.1.13/koatl → koatl-0.1.15}/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.15}/Cargo.toml +0 -0
- {koatl-0.1.13 → koatl-0.1.15}/README.md +0 -0
- {koatl-0.1.13 → koatl-0.1.15}/koatl/.github/workflows/CI.yml +0 -0
- {koatl-0.1.13 → koatl-0.1.15}/koatl/.gitignore +0 -0
- {koatl-0.1.13 → koatl-0.1.15}/koatl/LICENSE +0 -0
- {koatl-0.1.13 → koatl-0.1.15}/koatl/README.md +0 -0
- {koatl-0.1.13 → koatl-0.1.15}/koatl/python/koatl/__init__.py +0 -0
- {koatl-0.1.13 → koatl-0.1.15}/koatl/python/koatl/__main__.py +0 -0
- {koatl-0.1.13 → koatl-0.1.15}/koatl/python/koatl/notebook/magic.py +0 -0
- {koatl-0.1.13 → koatl-0.1.15}/koatl/python/koatl/prelude/__init__.tl +0 -0
- {koatl-0.1.13 → koatl-0.1.15}/koatl/python/koatl/prelude/functional/async_util.py +0 -0
- {koatl-0.1.13 → koatl-0.1.15}/koatl/python/koatl/runtime/record.py +0 -0
- {koatl-0.1.13 → koatl-0.1.15}/koatl/requirements.txt +0 -0
- {koatl-0.1.13 → koatl-0.1.15}/koatl/tests/e2e/base/containers.tl +0 -0
- {koatl-0.1.13 → koatl-0.1.15}/koatl/tests/e2e/base/decorators.tl +0 -0
- {koatl-0.1.13 → koatl-0.1.15}/koatl/tests/e2e/base/destructure-for-and-fn.tl +0 -0
- {koatl-0.1.13 → koatl-0.1.15}/koatl/tests/e2e/base/destructure.tl +0 -0
- {koatl-0.1.13 → koatl-0.1.15}/koatl/tests/e2e/base/escape_ident.tl +0 -0
- {koatl-0.1.13 → koatl-0.1.15}/koatl/tests/e2e/base/fstr.tl +0 -0
- {koatl-0.1.13 → koatl-0.1.15}/koatl/tests/e2e/base/functions.tl +0 -0
- {koatl-0.1.13 → koatl-0.1.15}/koatl/tests/e2e/base/generator.tl +0 -0
- {koatl-0.1.13 → koatl-0.1.15}/koatl/tests/e2e/base/imports.tl +0 -0
- {koatl-0.1.13 → koatl-0.1.15}/koatl/tests/e2e/base/iterables.tl +0 -0
- {koatl-0.1.13 → koatl-0.1.15}/koatl/tests/e2e/base/loops.tl +0 -0
- {koatl-0.1.13 → koatl-0.1.15}/koatl/tests/e2e/base/match.tl +0 -0
- {koatl-0.1.13 → koatl-0.1.15}/koatl/tests/e2e/base/nary-list.tl +0 -0
- {koatl-0.1.13 → koatl-0.1.15}/koatl/tests/e2e/base/placeholder.tl +0 -0
- {koatl-0.1.13 → koatl-0.1.15}/koatl/tests/e2e/base/precedence.tl +0 -0
- {koatl-0.1.13 → koatl-0.1.15}/koatl/tests/e2e/base/slice.tl +0 -0
- {koatl-0.1.13 → koatl-0.1.15}/koatl/tests/e2e/base/try.tl +0 -0
- {koatl-0.1.13 → koatl-0.1.15}/koatl/tests/e2e/destructure.tl +0 -0
- {koatl-0.1.13 → koatl-0.1.15}/koatl/tests/e2e/prelude/async.tl +0 -0
- {koatl-0.1.13 → koatl-0.1.15}/koatl/tests/e2e/prelude/reader.tl +0 -0
- {koatl-0.1.13 → koatl-0.1.15}/koatl/tests/e2e/prelude/virtual.tl +0 -0
- {koatl-0.1.13 → koatl-0.1.15}/koatl/tests/e2e/util/__init__.py +0 -0
- {koatl-0.1.13 → koatl-0.1.15}/koatl/tests/e2e/util/module0.tl +0 -0
- {koatl-0.1.13 → koatl-0.1.15}/koatl/tests/e2e/util/module1.tl +0 -0
- {koatl-0.1.13 → koatl-0.1.15}/koatl/tests/e2e/util/module2.tl +0 -0
- {koatl-0.1.13 → koatl-0.1.15}/koatl/tests/parse/arith.tl +0 -0
- {koatl-0.1.13 → koatl-0.1.15}/koatl/tests/parse/assign.tl +0 -0
- {koatl-0.1.13 → koatl-0.1.15}/koatl/tests/parse/func.tl +0 -0
- {koatl-0.1.13 → koatl-0.1.15}/koatl/tests/parse/matches.tl +0 -0
- {koatl-0.1.13 → koatl-0.1.15}/koatl/tests/test_parse.py +0 -0
- {koatl-0.1.13 → koatl-0.1.15}/koatl-core/parser/src/lib.rs +0 -0
- {koatl-0.1.13 → koatl-0.1.15}/koatl-core/parser/tests/lexer.rs +0 -0
- {koatl-0.1.13 → koatl-0.1.15}/koatl-core/src/linecol.rs +0 -0
- {koatl-0.1.13 → koatl-0.1.15}/koatl-core/src/parser.rs +0 -0
- {koatl-0.1.13 → koatl-0.1.15}/koatl-core/src/py/mod.rs +0 -0
- {koatl-0.1.13 → koatl-0.1.15}/pyproject.toml +0 -0
- {koatl-0.1.13 → koatl-0.1.15}/python/koatl/__init__.py +0 -0
- {koatl-0.1.13 → koatl-0.1.15}/python/koatl/__main__.py +0 -0
- {koatl-0.1.13 → koatl-0.1.15}/python/koatl/notebook/magic.py +0 -0
- {koatl-0.1.13 → koatl-0.1.15}/python/koatl/prelude/__init__.tl +0 -0
- {koatl-0.1.13 → koatl-0.1.15}/python/koatl/prelude/functional/async_util.py +0 -0
- {koatl-0.1.13 → koatl-0.1.15}/python/koatl/runtime/record.py +0 -0
|
@@ -99,7 +99,7 @@ checksum = "f4c7245a08504955605670dbf141fceab975f15ca21570696aebe9d2e71576bd"
|
|
|
99
99
|
|
|
100
100
|
[[package]]
|
|
101
101
|
name = "koatl"
|
|
102
|
-
version = "0.1.
|
|
102
|
+
version = "0.1.15"
|
|
103
103
|
dependencies = [
|
|
104
104
|
"ariadne",
|
|
105
105
|
"koatl-core",
|
|
@@ -112,6 +112,7 @@ name = "koatl-core"
|
|
|
112
112
|
version = "0.1.0"
|
|
113
113
|
dependencies = [
|
|
114
114
|
"ariadne",
|
|
115
|
+
"once_cell",
|
|
115
116
|
"parser",
|
|
116
117
|
]
|
|
117
118
|
|
|
@@ -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__"}
|
|
@@ -21,14 +21,23 @@ def source_code_transformer(lines):
|
|
|
21
21
|
return lines
|
|
22
22
|
|
|
23
23
|
|
|
24
|
-
def
|
|
25
|
-
|
|
26
|
-
|
|
24
|
+
def try_import_prelude(namespace):
|
|
25
|
+
try:
|
|
26
|
+
exec("from koatl.runtime import *", namespace)
|
|
27
|
+
exec("from koatl.prelude import *", namespace)
|
|
28
|
+
except Exception as e:
|
|
29
|
+
import traceback
|
|
30
|
+
|
|
31
|
+
print(
|
|
32
|
+
"There was an error importing the Koatl prelude. Some features may not work."
|
|
33
|
+
)
|
|
34
|
+
traceback.print_exc()
|
|
27
35
|
|
|
28
36
|
|
|
29
37
|
def load_ipython_extension(ipython):
|
|
30
|
-
|
|
31
|
-
|
|
38
|
+
print("Switched notebook to Koatl.")
|
|
39
|
+
|
|
40
|
+
try_import_prelude(ipython.user_ns)
|
|
32
41
|
|
|
33
42
|
ttm = ipython.input_transformer_manager
|
|
34
43
|
|
|
@@ -1,15 +1,16 @@
|
|
|
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
|
|
10
11
|
[*fs] =>
|
|
11
|
-
composed = (*args, **kwargs) =>
|
|
12
|
-
value = fs[-1](*args, **kwargs)
|
|
12
|
+
let composed = (*args, **kwargs) =>
|
|
13
|
+
let value = fs[-1](*args, **kwargs)
|
|
13
14
|
for f in fs[..-1..-1]:
|
|
14
15
|
value = f(value)
|
|
15
16
|
value
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
import functools.wraps
|
|
2
|
+
import asyncio
|
|
3
|
+
|
|
4
|
+
import .async_util
|
|
5
|
+
import .monad.Monad
|
|
6
|
+
|
|
7
|
+
export Async = class(Monad):
|
|
8
|
+
__init__ = (self, awaitable) => self.generator = awaitable.__await__()
|
|
9
|
+
|
|
10
|
+
__await__ = self => self.generator
|
|
11
|
+
|
|
12
|
+
__repr__ = self => "Async(...)"
|
|
13
|
+
|
|
14
|
+
from_generator_fn = staticmethod& (generator_fn, *args, **kwargs) =>
|
|
15
|
+
let m = object.__new__(Async)
|
|
16
|
+
m.generator = generator_fn(*args, **kwargs)
|
|
17
|
+
return m
|
|
18
|
+
|
|
19
|
+
run = self => asyncio.run(async_util.to_coro(self))
|
|
20
|
+
|
|
21
|
+
bind_once = (self, f) => Async.from_generator_fn& () =>
|
|
22
|
+
let result = f(yield from self.__await__())
|
|
23
|
+
|
|
24
|
+
if hasattr(result, "__await__"):
|
|
25
|
+
return yield from result.__await__()
|
|
26
|
+
|
|
27
|
+
return result
|
|
28
|
+
|
|
29
|
+
bind_gen = (self, gen) => Async.from_generator_fn& () =>
|
|
30
|
+
try:
|
|
31
|
+
while True:
|
|
32
|
+
self = gen.send(yield from self.__await__())
|
|
33
|
+
except StopIteration(value=value):
|
|
34
|
+
return value
|
|
35
|
+
|
|
36
|
+
pure = staticmethod& x => Async.from_generator_fn& () =>
|
|
37
|
+
return x
|
|
38
|
+
yield None
|
|
39
|
+
|
|
40
|
+
sleep = staticmethod& x => Async(asyncio.sleep(x))
|
|
41
|
+
|
|
42
|
+
#- TODO: Why is asyncio.gather eager? -#
|
|
43
|
+
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,31 @@
|
|
|
1
|
+
import functools.wraps
|
|
2
|
+
import .monad.Monad
|
|
3
|
+
|
|
4
|
+
export Reader = class(Monad):
|
|
5
|
+
__init__ = (self, fn) => self.fn = fn
|
|
6
|
+
|
|
7
|
+
__repr__ = self => "Reader(...)"
|
|
8
|
+
|
|
9
|
+
run = (self, ctx) => self.fn(ctx)
|
|
10
|
+
|
|
11
|
+
bind_once = (self, f) => Reader& ctx =>
|
|
12
|
+
let v = f(self.fn(ctx))
|
|
13
|
+
if v matches Reader():
|
|
14
|
+
v.fn(ctx)
|
|
15
|
+
else:
|
|
16
|
+
v
|
|
17
|
+
|
|
18
|
+
bind_gen = (self, gen) => Reader& ctx =>
|
|
19
|
+
try:
|
|
20
|
+
while True:
|
|
21
|
+
self = gen.send(self.fn(ctx))
|
|
22
|
+
except StopIteration(value=value):
|
|
23
|
+
return value
|
|
24
|
+
|
|
25
|
+
NoKey = object()
|
|
26
|
+
|
|
27
|
+
ask = staticmethod& (key=NoKey) => Reader(
|
|
28
|
+
key === Reader.NoKey then ctx => ctx else ctx => ctx[key]
|
|
29
|
+
)
|
|
30
|
+
|
|
31
|
+
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
|
+
)
|
|
@@ -13,7 +13,7 @@ methods = {
|
|
|
13
13
|
yield from f(i)
|
|
14
14
|
|
|
15
15
|
fold: (x, init, f) =>
|
|
16
|
-
acc = init
|
|
16
|
+
let acc = init
|
|
17
17
|
for i in x:
|
|
18
18
|
acc = f(acc, i)
|
|
19
19
|
acc
|
|
@@ -25,7 +25,7 @@ methods = {
|
|
|
25
25
|
return None
|
|
26
26
|
|
|
27
27
|
last: (x, f) =>
|
|
28
|
-
result = None
|
|
28
|
+
let result = None
|
|
29
29
|
for i in x:
|
|
30
30
|
if f(i):
|
|
31
31
|
result = i
|
|
@@ -38,7 +38,7 @@ methods = {
|
|
|
38
38
|
raise IndexError("Index out of range")
|
|
39
39
|
|
|
40
40
|
sum: x =>
|
|
41
|
-
acc = 0
|
|
41
|
+
let acc = 0
|
|
42
42
|
for i in x:
|
|
43
43
|
acc = acc + i
|
|
44
44
|
acc
|
|
@@ -22,10 +22,12 @@ from .helpers import *
|
|
|
22
22
|
|
|
23
23
|
|
|
24
24
|
__tl__ = SimpleNamespace(
|
|
25
|
+
slice=slice,
|
|
26
|
+
vget=virtual.vget,
|
|
27
|
+
vhas=virtual.vhas,
|
|
25
28
|
unpack_record=helpers.unpack_record,
|
|
26
29
|
set_exports=helpers.set_exports,
|
|
27
30
|
do=helpers.do,
|
|
28
|
-
vget=helpers.vget,
|
|
29
31
|
ok=helpers.ok,
|
|
30
32
|
partial=functools.partial,
|
|
31
33
|
**{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,12 @@ def do(f):
|
|
|
74
74
|
except AttributeError:
|
|
75
75
|
return e.value
|
|
76
76
|
|
|
77
|
-
|
|
77
|
+
try:
|
|
78
|
+
# TODO: this is a workaround to avoid recursion.
|
|
79
|
+
# is it possible to get bind_gen directly from bind_once?
|
|
80
|
+
|
|
81
|
+
return vget(m, "bind_gen")(gen)
|
|
82
|
+
except (NotImplementedError, AttributeError):
|
|
83
|
+
return vget(m, "bind_once")(recurse)
|
|
78
84
|
|
|
79
85
|
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="no_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
|
|
@@ -333,18 +333,31 @@ impl<'src> PyStmtExt<'src> for SPyStmt<'src> {
|
|
|
333
333
|
&self.tl_span,
|
|
334
334
|
)
|
|
335
335
|
}
|
|
336
|
-
PyStmt::FnDef(
|
|
337
|
-
let arguments = args.emit_py(ctx)?;
|
|
338
|
-
let body_ast = body.emit_py(ctx)?;
|
|
339
|
-
let decorators = decorators.emit_py(ctx)?;
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
|
|
336
|
+
PyStmt::FnDef(fndef) => {
|
|
337
|
+
let arguments = fndef.args.emit_py(ctx)?;
|
|
338
|
+
let body_ast = fndef.body.emit_py(ctx)?;
|
|
339
|
+
let decorators = fndef.decorators.emit_py(ctx)?;
|
|
340
|
+
|
|
341
|
+
if fndef.async_ {
|
|
342
|
+
ctx.ast_node(
|
|
343
|
+
"AsyncFunctionDef",
|
|
344
|
+
(fndef.name.as_ref(), arguments, body_ast, decorators),
|
|
345
|
+
&self.tl_span,
|
|
346
|
+
)
|
|
347
|
+
} else {
|
|
348
|
+
ctx.ast_node(
|
|
349
|
+
"FunctionDef",
|
|
350
|
+
(fndef.name.as_ref(), arguments, body_ast, decorators),
|
|
351
|
+
&self.tl_span,
|
|
352
|
+
)
|
|
353
|
+
}
|
|
346
354
|
}
|
|
347
|
-
PyStmt::ClassDef(
|
|
355
|
+
PyStmt::ClassDef(PyClassDef {
|
|
356
|
+
name,
|
|
357
|
+
bases,
|
|
358
|
+
body,
|
|
359
|
+
decorators,
|
|
360
|
+
}) => {
|
|
348
361
|
let mut bases_ast = Vec::new();
|
|
349
362
|
let mut keywords_ast = Vec::new();
|
|
350
363
|
|
|
@@ -14,7 +14,7 @@ use pyo3::{
|
|
|
14
14
|
fn get_option(mode: &str) -> PyResult<TranspileOptions> {
|
|
15
15
|
Ok(match mode {
|
|
16
16
|
"module" => TranspileOptions::module(),
|
|
17
|
-
"
|
|
17
|
+
"no_prelude" => TranspileOptions::no_prelude(),
|
|
18
18
|
"interactive" => TranspileOptions::interactive(),
|
|
19
19
|
"script" => TranspileOptions::script(),
|
|
20
20
|
_ => {
|
|
@@ -1,9 +1,10 @@
|
|
|
1
1
|
import util.assert_eq
|
|
2
2
|
|
|
3
|
-
|
|
4
|
-
assert_eq(
|
|
5
|
-
assert_eq(
|
|
6
|
-
assert_eq(
|
|
3
|
+
none = None
|
|
4
|
+
assert_eq(none?(1)?(2), None)
|
|
5
|
+
assert_eq(none?[1]?.a, None)
|
|
6
|
+
assert_eq(none?.a, None)
|
|
7
|
+
assert_eq(none?.(a), None)
|
|
7
8
|
|
|
8
9
|
obj = (class:
|
|
9
10
|
a = 1
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
import util.assert_eq
|
|
2
|
+
|
|
3
|
+
test = 1
|
|
4
|
+
if True:
|
|
5
|
+
test = 2
|
|
6
|
+
assert_eq(test, 2)
|
|
7
|
+
|
|
8
|
+
let test = 3
|
|
9
|
+
assert_eq(test, 3)
|
|
10
|
+
|
|
11
|
+
test = 4
|
|
12
|
+
assert_eq(test, 4)
|
|
13
|
+
|
|
14
|
+
assert_eq(test, 2)
|
|
15
|
+
|
|
16
|
+
f = () =>
|
|
17
|
+
let test = 5
|
|
18
|
+
assert_eq(test, 5)
|
|
19
|
+
|
|
20
|
+
f()
|
|
21
|
+
assert_eq(test, 2)
|
|
22
|
+
|
|
23
|
+
f = () =>
|
|
24
|
+
test = 5
|
|
25
|
+
let g = () =>
|
|
26
|
+
test = 6
|
|
27
|
+
g()
|
|
28
|
+
assert_eq(test, 6)
|
|
29
|
+
|
|
30
|
+
f()
|
|
31
|
+
|
|
32
|
+
|
|
33
|
+
f = () =>
|
|
34
|
+
test = 5
|
|
35
|
+
let g = () =>
|
|
36
|
+
let test = 6
|
|
37
|
+
g()
|
|
38
|
+
assert_eq(test, 5)
|
|
39
|
+
|
|
40
|
+
f()
|
|
41
|
+
|
|
42
|
+
test = 0
|
|
43
|
+
|
|
44
|
+
f = test =>
|
|
45
|
+
test = 1
|
|
46
|
+
assert_eq(test, 1)
|
|
47
|
+
|
|
48
|
+
f(1)
|
|
49
|
+
assert_eq(test, 0)
|
|
@@ -64,7 +64,7 @@ assert_eq([
|
|
|
64
64
|
apply = (x, y) => x(y)
|
|
65
65
|
assert_eq(apply(
|
|
66
66
|
x =>
|
|
67
|
-
y = x * 2
|
|
67
|
+
let y = x * 2
|
|
68
68
|
y * 2
|
|
69
69
|
8
|
|
70
70
|
), 8 * 2 * 2)
|
|
@@ -72,7 +72,7 @@ assert_eq(apply(
|
|
|
72
72
|
assert_eq(
|
|
73
73
|
apply(
|
|
74
74
|
x =>
|
|
75
|
-
y = x * 2
|
|
75
|
+
let y = x * 2
|
|
76
76
|
y * 2
|
|
77
77
|
8
|
|
78
78
|
)
|
|
@@ -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])
|
|
@@ -6,10 +6,9 @@ from pathlib import Path
|
|
|
6
6
|
sys.path.append(str(Path(__file__).parent / "e2e"))
|
|
7
7
|
|
|
8
8
|
|
|
9
|
-
def get_test_data():
|
|
9
|
+
def get_test_data(dirs):
|
|
10
10
|
data_dirs = [
|
|
11
|
-
Path(__file__).parent / "e2e" /
|
|
12
|
-
Path(__file__).parent / "e2e" / "prelude",
|
|
11
|
+
Path(__file__).parent / "e2e" / dirs,
|
|
13
12
|
]
|
|
14
13
|
|
|
15
14
|
test_cases = []
|
|
@@ -20,13 +19,32 @@ def get_test_data():
|
|
|
20
19
|
return test_cases
|
|
21
20
|
|
|
22
21
|
|
|
23
|
-
@pytest.mark.parametrize("test_file", get_test_data())
|
|
24
|
-
def
|
|
22
|
+
@pytest.mark.parametrize("test_file", get_test_data("base"))
|
|
23
|
+
def test_e2e_native_emit_base(test_file):
|
|
24
|
+
e2e_native_emit(test_file, "no_prelude")
|
|
25
|
+
|
|
26
|
+
|
|
27
|
+
@pytest.mark.parametrize("test_file", get_test_data("prelude"))
|
|
28
|
+
def test_e2e_native_emit_prelude(test_file):
|
|
29
|
+
e2e_native_emit(test_file, "script")
|
|
30
|
+
|
|
31
|
+
|
|
32
|
+
@pytest.mark.parametrize("test_file", get_test_data("base"))
|
|
33
|
+
def test_e2e_base(test_file):
|
|
34
|
+
e2e(test_file, "no_prelude")
|
|
35
|
+
|
|
36
|
+
|
|
37
|
+
@pytest.mark.parametrize("test_file", get_test_data("prelude"))
|
|
38
|
+
def test_e2e_prelude(test_file):
|
|
39
|
+
e2e(test_file, "script")
|
|
40
|
+
|
|
41
|
+
|
|
42
|
+
def e2e_native_emit(test_file, mode):
|
|
25
43
|
import linecache
|
|
26
44
|
|
|
27
45
|
with open(test_file, "r") as f:
|
|
28
46
|
source = f.read()
|
|
29
|
-
source, source_map = koatl.transpile_raw(source, mode=
|
|
47
|
+
source, source_map = koatl.transpile_raw(source, mode=mode)
|
|
30
48
|
|
|
31
49
|
global_dict = {}
|
|
32
50
|
|
|
@@ -46,6 +64,5 @@ def test_e2e_native_emit(test_file):
|
|
|
46
64
|
print("end", test_file)
|
|
47
65
|
|
|
48
66
|
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
koatl.cli.run_from_path(test_file, mode="script")
|
|
67
|
+
def e2e(test_file, mode):
|
|
68
|
+
koatl.cli.run_from_path(test_file, mode=mode)
|