koatl 0.1.21__tar.gz → 0.1.23__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.21 → koatl-0.1.23}/Cargo.lock +1 -1
- {koatl-0.1.21 → koatl-0.1.23}/PKG-INFO +1 -1
- {koatl-0.1.21 → koatl-0.1.23}/koatl/Cargo.toml +1 -1
- {koatl-0.1.21 → koatl-0.1.23}/koatl/python/koatl/prelude/functional/__init__.tl +16 -3
- koatl-0.1.23/koatl/python/koatl/prelude/functional/list.tl +26 -0
- {koatl-0.1.21 → koatl-0.1.23}/koatl/python/koatl/prelude/functional/monad.tl +10 -4
- {koatl-0.1.21 → koatl-0.1.23/koatl}/python/koatl/prelude/functional/result.tl +38 -41
- koatl-0.1.23/koatl/python/koatl/prelude/iterable.tl +83 -0
- {koatl-0.1.21 → koatl-0.1.23}/koatl/python/koatl/runtime/helpers.py +1 -1
- {koatl-0.1.21 → koatl-0.1.23}/koatl/python/koatl/runtime/virtual.py +2 -2
- {koatl-0.1.21 → koatl-0.1.23}/koatl/src/lib.rs +23 -8
- koatl-0.1.23/koatl/tests/e2e/prelude/list.tl +18 -0
- {koatl-0.1.21 → koatl-0.1.23}/koatl-core/parser/src/parser.rs +11 -1
- {koatl-0.1.21 → koatl-0.1.23}/koatl-core/src/resolve_scopes.rs +0 -14
- {koatl-0.1.21 → koatl-0.1.23}/koatl-core/src/transform.rs +22 -8
- {koatl-0.1.21 → koatl-0.1.23}/python/koatl/prelude/functional/__init__.tl +16 -3
- koatl-0.1.23/python/koatl/prelude/functional/list.tl +26 -0
- {koatl-0.1.21 → koatl-0.1.23}/python/koatl/prelude/functional/monad.tl +10 -4
- {koatl-0.1.21/koatl → koatl-0.1.23}/python/koatl/prelude/functional/result.tl +38 -41
- koatl-0.1.23/python/koatl/prelude/iterable.tl +83 -0
- {koatl-0.1.21 → koatl-0.1.23}/python/koatl/runtime/helpers.py +1 -1
- {koatl-0.1.21 → koatl-0.1.23}/python/koatl/runtime/virtual.py +2 -2
- koatl-0.1.21/koatl/python/koatl/prelude/iterable.tl +0 -56
- koatl-0.1.21/python/koatl/prelude/iterable.tl +0 -56
- {koatl-0.1.21 → koatl-0.1.23}/Cargo.toml +0 -0
- {koatl-0.1.21 → koatl-0.1.23}/README.md +0 -0
- {koatl-0.1.21 → koatl-0.1.23}/koatl/.github/workflows/CI.yml +0 -0
- {koatl-0.1.21 → koatl-0.1.23}/koatl/.gitignore +0 -0
- {koatl-0.1.21 → koatl-0.1.23}/koatl/LICENSE +0 -0
- {koatl-0.1.21 → koatl-0.1.23}/koatl/README.md +0 -0
- {koatl-0.1.21 → koatl-0.1.23}/koatl/python/koatl/__init__.py +0 -0
- {koatl-0.1.21 → koatl-0.1.23}/koatl/python/koatl/__main__.py +0 -0
- {koatl-0.1.21 → koatl-0.1.23}/koatl/python/koatl/cli.py +0 -0
- {koatl-0.1.21 → koatl-0.1.23}/koatl/python/koatl/notebook/__init__.py +0 -0
- {koatl-0.1.21 → koatl-0.1.23}/koatl/python/koatl/notebook/magic.py +0 -0
- {koatl-0.1.21 → koatl-0.1.23}/koatl/python/koatl/prelude/__init__.tl +0 -0
- {koatl-0.1.21 → koatl-0.1.23}/koatl/python/koatl/prelude/functional/async.tl +0 -0
- {koatl-0.1.21 → koatl-0.1.23}/koatl/python/koatl/prelude/functional/async_util.py +0 -0
- {koatl-0.1.21 → koatl-0.1.23}/koatl/python/koatl/prelude/functional/memo.tl +0 -0
- {koatl-0.1.21 → koatl-0.1.23}/koatl/python/koatl/prelude/functional/reader.tl +0 -0
- {koatl-0.1.21 → koatl-0.1.23}/koatl/python/koatl/runtime/__init__.py +0 -0
- {koatl-0.1.21 → koatl-0.1.23}/koatl/python/koatl/runtime/meta_finder.py +0 -0
- {koatl-0.1.21 → koatl-0.1.23}/koatl/python/koatl/runtime/record.py +0 -0
- {koatl-0.1.21 → koatl-0.1.23}/koatl/requirements.txt +0 -0
- {koatl-0.1.21 → koatl-0.1.23}/koatl/src/emit_py.rs +0 -0
- {koatl-0.1.21 → koatl-0.1.23}/koatl/tests/e2e/base/coal.tl +0 -0
- {koatl-0.1.21 → koatl-0.1.23}/koatl/tests/e2e/base/containers.tl +0 -0
- {koatl-0.1.21 → koatl-0.1.23}/koatl/tests/e2e/base/decorators.tl +0 -0
- {koatl-0.1.21 → koatl-0.1.23}/koatl/tests/e2e/base/destructure-for-and-fn.tl +0 -0
- {koatl-0.1.21 → koatl-0.1.23}/koatl/tests/e2e/base/destructure.tl +0 -0
- {koatl-0.1.21 → koatl-0.1.23}/koatl/tests/e2e/base/escape_ident.tl +0 -0
- {koatl-0.1.21 → koatl-0.1.23}/koatl/tests/e2e/base/fstr.tl +0 -0
- {koatl-0.1.21 → koatl-0.1.23}/koatl/tests/e2e/base/functions.tl +0 -0
- {koatl-0.1.21 → koatl-0.1.23}/koatl/tests/e2e/base/generator.tl +0 -0
- {koatl-0.1.21 → koatl-0.1.23}/koatl/tests/e2e/base/if_expr.tl +0 -0
- {koatl-0.1.21 → koatl-0.1.23}/koatl/tests/e2e/base/imports.tl +0 -0
- {koatl-0.1.21 → koatl-0.1.23}/koatl/tests/e2e/base/loops.tl +0 -0
- {koatl-0.1.21 → koatl-0.1.23}/koatl/tests/e2e/base/match.tl +0 -0
- {koatl-0.1.21 → koatl-0.1.23}/koatl/tests/e2e/base/nary-list.tl +0 -0
- {koatl-0.1.21 → koatl-0.1.23}/koatl/tests/e2e/base/placeholder.tl +0 -0
- {koatl-0.1.21 → koatl-0.1.23}/koatl/tests/e2e/base/precedence.tl +0 -0
- {koatl-0.1.21 → koatl-0.1.23}/koatl/tests/e2e/base/scopes.tl +0 -0
- {koatl-0.1.21 → koatl-0.1.23}/koatl/tests/e2e/base/semantic_whitespace.tl +0 -0
- {koatl-0.1.21 → koatl-0.1.23}/koatl/tests/e2e/base/slice.tl +0 -0
- {koatl-0.1.21 → koatl-0.1.23}/koatl/tests/e2e/base/try.tl +0 -0
- {koatl-0.1.21 → koatl-0.1.23}/koatl/tests/e2e/destructure.tl +0 -0
- {koatl-0.1.21 → koatl-0.1.23}/koatl/tests/e2e/prelude/async.tl +0 -0
- {koatl-0.1.21 → koatl-0.1.23}/koatl/tests/e2e/prelude/iterables.tl +0 -0
- {koatl-0.1.21 → koatl-0.1.23}/koatl/tests/e2e/prelude/memo.tl +0 -0
- {koatl-0.1.21 → koatl-0.1.23}/koatl/tests/e2e/prelude/reader.tl +0 -0
- {koatl-0.1.21 → koatl-0.1.23}/koatl/tests/e2e/prelude/result.tl +0 -0
- {koatl-0.1.21 → koatl-0.1.23}/koatl/tests/e2e/prelude/virtual.tl +0 -0
- {koatl-0.1.21 → koatl-0.1.23}/koatl/tests/e2e/util/__init__.py +0 -0
- {koatl-0.1.21 → koatl-0.1.23}/koatl/tests/e2e/util/module0.tl +0 -0
- {koatl-0.1.21 → koatl-0.1.23}/koatl/tests/e2e/util/module1.tl +0 -0
- {koatl-0.1.21 → koatl-0.1.23}/koatl/tests/e2e/util/module2.tl +0 -0
- {koatl-0.1.21 → koatl-0.1.23}/koatl/tests/parse/arith.tl +0 -0
- {koatl-0.1.21 → koatl-0.1.23}/koatl/tests/parse/assign.tl +0 -0
- {koatl-0.1.21 → koatl-0.1.23}/koatl/tests/parse/block-comments.tl +0 -0
- {koatl-0.1.21 → koatl-0.1.23}/koatl/tests/parse/deco.tl +0 -0
- {koatl-0.1.21 → koatl-0.1.23}/koatl/tests/parse/func.tl +0 -0
- {koatl-0.1.21 → koatl-0.1.23}/koatl/tests/parse/matches.tl +0 -0
- {koatl-0.1.21 → koatl-0.1.23}/koatl/tests/test_e2e.py +0 -0
- {koatl-0.1.21 → koatl-0.1.23}/koatl/tests/test_parse.py +0 -0
- {koatl-0.1.21 → koatl-0.1.23}/koatl-core/Cargo.toml +0 -0
- {koatl-0.1.21 → koatl-0.1.23}/koatl-core/parser/Cargo.toml +0 -0
- {koatl-0.1.21 → koatl-0.1.23}/koatl-core/parser/src/ast.rs +0 -0
- {koatl-0.1.21 → koatl-0.1.23}/koatl-core/parser/src/lexer.rs +0 -0
- {koatl-0.1.21 → koatl-0.1.23}/koatl-core/parser/src/lib.rs +0 -0
- {koatl-0.1.21 → koatl-0.1.23}/koatl-core/parser/src/util.rs +0 -0
- {koatl-0.1.21 → koatl-0.1.23}/koatl-core/parser/tests/lexer.rs +0 -0
- {koatl-0.1.21 → koatl-0.1.23}/koatl-core/src/inference.rs +0 -0
- {koatl-0.1.21 → koatl-0.1.23}/koatl-core/src/lib.rs +0 -0
- {koatl-0.1.21 → koatl-0.1.23}/koatl-core/src/main.rs +0 -0
- {koatl-0.1.21 → koatl-0.1.23}/koatl-core/src/parse_timer.rs +0 -0
- {koatl-0.1.21 → koatl-0.1.23}/koatl-core/src/parser.rs +0 -0
- {koatl-0.1.21 → koatl-0.1.23}/koatl-core/src/py/ast.rs +0 -0
- {koatl-0.1.21 → koatl-0.1.23}/koatl-core/src/py/emit.rs +0 -0
- {koatl-0.1.21 → koatl-0.1.23}/koatl-core/src/py/mod.rs +0 -0
- {koatl-0.1.21 → koatl-0.1.23}/koatl-core/src/py/util.rs +0 -0
- {koatl-0.1.21 → koatl-0.1.23}/koatl-core/src/types.rs +0 -0
- {koatl-0.1.21 → koatl-0.1.23}/koatl-core/src/util.rs +0 -0
- {koatl-0.1.21 → koatl-0.1.23}/pyproject.toml +0 -0
- {koatl-0.1.21 → koatl-0.1.23}/python/koatl/__init__.py +0 -0
- {koatl-0.1.21 → koatl-0.1.23}/python/koatl/__main__.py +0 -0
- {koatl-0.1.21 → koatl-0.1.23}/python/koatl/cli.py +0 -0
- {koatl-0.1.21 → koatl-0.1.23}/python/koatl/notebook/__init__.py +0 -0
- {koatl-0.1.21 → koatl-0.1.23}/python/koatl/notebook/magic.py +0 -0
- {koatl-0.1.21 → koatl-0.1.23}/python/koatl/prelude/__init__.tl +0 -0
- {koatl-0.1.21 → koatl-0.1.23}/python/koatl/prelude/functional/async.tl +0 -0
- {koatl-0.1.21 → koatl-0.1.23}/python/koatl/prelude/functional/async_util.py +0 -0
- {koatl-0.1.21 → koatl-0.1.23}/python/koatl/prelude/functional/memo.tl +0 -0
- {koatl-0.1.21 → koatl-0.1.23}/python/koatl/prelude/functional/reader.tl +0 -0
- {koatl-0.1.21 → koatl-0.1.23}/python/koatl/runtime/__init__.py +0 -0
- {koatl-0.1.21 → koatl-0.1.23}/python/koatl/runtime/meta_finder.py +0 -0
- {koatl-0.1.21 → koatl-0.1.23}/python/koatl/runtime/record.py +0 -0
|
@@ -3,9 +3,17 @@ export import .result.*
|
|
|
3
3
|
export import .async.*
|
|
4
4
|
export import .reader.*
|
|
5
5
|
export import .memo.*
|
|
6
|
+
export import .list.*
|
|
6
7
|
|
|
7
|
-
|
|
8
|
-
|
|
8
|
+
named = name => fn =>
|
|
9
|
+
fn.__name__ = name
|
|
10
|
+
fn.__qualname__ = name
|
|
11
|
+
fn
|
|
12
|
+
|
|
13
|
+
methods = {
|
|
14
|
+
id: named("id")& x => x
|
|
15
|
+
|
|
16
|
+
compose: named("compose")& (*args) =>
|
|
9
17
|
args match:
|
|
10
18
|
[] => raise ValueError("At least one function is required for composition")
|
|
11
19
|
[f] => f
|
|
@@ -21,4 +29,9 @@ export Fn = class:
|
|
|
21
29
|
composed.signature = fs[-1].signature
|
|
22
30
|
|
|
23
31
|
composed
|
|
24
|
-
default raise ValueError("Invalid arguments for Fn.compose()")
|
|
32
|
+
default raise ValueError("Invalid arguments for Fn.compose()")
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
globals().update(methods)
|
|
36
|
+
|
|
37
|
+
__all__ = methods.keys() | tuple
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
import collections.UserList
|
|
2
|
+
import itertools
|
|
3
|
+
|
|
4
|
+
import .monad.Monad
|
|
5
|
+
import ..iterable.Iterable
|
|
6
|
+
|
|
7
|
+
export List = class(UserList, Monad):
|
|
8
|
+
bind_once = (self, f) =>
|
|
9
|
+
raise NotImplementedError(
|
|
10
|
+
"Binding List in a do-block doesn't work in Koatl " +
|
|
11
|
+
"due to generator limitations. " +
|
|
12
|
+
"Wrap in Ok to use the regular Result monad."
|
|
13
|
+
)
|
|
14
|
+
|
|
15
|
+
bind = (self, f) => List(self.flat_map(f))
|
|
16
|
+
|
|
17
|
+
pure = staticmethod& x => [x]
|
|
18
|
+
|
|
19
|
+
traverse = Iterable.traverse
|
|
20
|
+
|
|
21
|
+
|
|
22
|
+
for name, method in List.__dict__:
|
|
23
|
+
if name.startswith("_"):
|
|
24
|
+
continue
|
|
25
|
+
|
|
26
|
+
register_global_attr(list, name, method)
|
|
@@ -1,12 +1,18 @@
|
|
|
1
1
|
import abc
|
|
2
2
|
|
|
3
3
|
export Monad = class(abc.ABC):
|
|
4
|
+
# The default implementation required for `@` syntax that should be overridden by subclasses.
|
|
5
|
+
bind_once = abc.abstractmethod& (self, f) => None
|
|
6
|
+
|
|
7
|
+
pure = staticmethod& abc.abstractmethod& value => None
|
|
8
|
+
|
|
9
|
+
# Automatically given by bind_once.
|
|
4
10
|
bind = (self, f) => self.bind_once(f)
|
|
5
11
|
|
|
6
12
|
# An optional, optimized implementation of `bind` that skips deep recursion.
|
|
13
|
+
# TODO: can this be automatically generated?
|
|
7
14
|
bind_gen = (self, gen) => raise NotImplementedError()
|
|
8
15
|
|
|
9
|
-
#
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
pure = staticmethod& abc.abstractmethod& value => None
|
|
16
|
+
# Automatically generated implementations.
|
|
17
|
+
map = (self, f) => self.bind(x => self.pure(f(x)))
|
|
18
|
+
apply = (self, f) => self.bind(x => f.map(fn => fn(x)))
|
|
@@ -8,6 +8,8 @@ export Result = class(Monad):
|
|
|
8
8
|
Ok() => f(self)
|
|
9
9
|
default => self
|
|
10
10
|
|
|
11
|
+
bind = bind_once
|
|
12
|
+
|
|
11
13
|
bind_gen = (self, gen) =>
|
|
12
14
|
try:
|
|
13
15
|
while True:
|
|
@@ -23,6 +25,28 @@ export Result = class(Monad):
|
|
|
23
25
|
return Ok(None)
|
|
24
26
|
return value
|
|
25
27
|
|
|
28
|
+
pure = staticmethod& x => Ok(x)
|
|
29
|
+
|
|
30
|
+
apply = (self, f) =>
|
|
31
|
+
if not __tl__.ok(self):
|
|
32
|
+
return self
|
|
33
|
+
if not __tl__.ok(f):
|
|
34
|
+
return f
|
|
35
|
+
|
|
36
|
+
# unwrap the Oks - this is a bit weird
|
|
37
|
+
# but it allows bare types to be Results
|
|
38
|
+
if self matches Ok(value):
|
|
39
|
+
self = self.value
|
|
40
|
+
if f matches Ok(value):
|
|
41
|
+
f = f.value
|
|
42
|
+
|
|
43
|
+
f(self)
|
|
44
|
+
|
|
45
|
+
# Map must not be defined since it will override any special map
|
|
46
|
+
# implementation for traits, specifically for Iterable -
|
|
47
|
+
# is there a better way around this (in the vtable implementation)?
|
|
48
|
+
# map = ...
|
|
49
|
+
|
|
26
50
|
map_err = (self, f) =>
|
|
27
51
|
if self matches Err():
|
|
28
52
|
return f(self)
|
|
@@ -44,50 +68,23 @@ export Ok = class(metaclass=OkMeta):
|
|
|
44
68
|
__init__ = (self, value) =>
|
|
45
69
|
self.value = value
|
|
46
70
|
|
|
47
|
-
# Since we are using a custom metaclass, we can't derive from Result
|
|
48
|
-
# so need to copy the methods over manually from Result
|
|
49
|
-
|
|
50
|
-
# We could just use the object fallback methods, but this avoids
|
|
51
|
-
# the manual __tl__.vget so is slightly faster
|
|
52
|
-
bind_once = Result.bind_once
|
|
53
|
-
bind_gen = Result.bind_gen
|
|
54
|
-
map_err = Result.map_err
|
|
55
|
-
map_none = Result.map_none
|
|
56
|
-
|
|
57
71
|
assert = staticmethod& value =>
|
|
58
72
|
value match:
|
|
59
73
|
BaseException() as e => raise e
|
|
60
74
|
None => raise ValueError("Expected a value, got None")
|
|
61
75
|
default value
|
|
62
76
|
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
register_global_attr(
|
|
78
|
-
object,
|
|
79
|
-
"bind",
|
|
80
|
-
Result.bind_once
|
|
81
|
-
)
|
|
82
|
-
|
|
83
|
-
register_global_attr(
|
|
84
|
-
object,
|
|
85
|
-
"bind_once",
|
|
86
|
-
Result.bind_once
|
|
87
|
-
)
|
|
88
|
-
|
|
89
|
-
register_global_attr(
|
|
90
|
-
object,
|
|
91
|
-
"bind_gen",
|
|
92
|
-
Result.bind_gen
|
|
93
|
-
)
|
|
77
|
+
|
|
78
|
+
# Since we are using a custom metaclass, we can't derive from Result
|
|
79
|
+
# so need to copy the methods over manually from Result
|
|
80
|
+
|
|
81
|
+
# We could just use the object fallback methods, but this avoids
|
|
82
|
+
# the vget so is slightly faster
|
|
83
|
+
for name, method in Result.__dict__:
|
|
84
|
+
if name.startswith("_"):
|
|
85
|
+
continue
|
|
86
|
+
|
|
87
|
+
setattr(Ok, name, method)
|
|
88
|
+
register_global_attr(object, name, method)
|
|
89
|
+
|
|
90
|
+
export Err = BaseException
|
|
@@ -0,0 +1,83 @@
|
|
|
1
|
+
import itertools
|
|
2
|
+
import builtins
|
|
3
|
+
|
|
4
|
+
methods = {
|
|
5
|
+
map: (x, f) => builtins.map(f, x.iter)
|
|
6
|
+
|
|
7
|
+
filter: (x, f) => builtins.filter(f, x.iter)
|
|
8
|
+
|
|
9
|
+
flat_map: (x, f) => itertools.chain.from_iterable(x.map(f))
|
|
10
|
+
|
|
11
|
+
traverse: (x, f) =>
|
|
12
|
+
let it = x.iter
|
|
13
|
+
let v
|
|
14
|
+
try:
|
|
15
|
+
v = next(it)
|
|
16
|
+
except StopIteration():
|
|
17
|
+
return []
|
|
18
|
+
|
|
19
|
+
let m = f(v)
|
|
20
|
+
|
|
21
|
+
if not hasattr(m, "apply"):
|
|
22
|
+
# special case for bare types - slightly more efficient
|
|
23
|
+
# ...also required since bare types don't have .map
|
|
24
|
+
if not __tl__.ok(m):
|
|
25
|
+
return m
|
|
26
|
+
|
|
27
|
+
let acc = [m]
|
|
28
|
+
for v in it:
|
|
29
|
+
let fv = f(v)
|
|
30
|
+
if not __tl__.ok(fv):
|
|
31
|
+
return fv
|
|
32
|
+
acc.append(fv)
|
|
33
|
+
return acc
|
|
34
|
+
|
|
35
|
+
let acc = m.map([$])
|
|
36
|
+
for v in it:
|
|
37
|
+
m = f(v)
|
|
38
|
+
# acc = liftA2((list, value) => [*list, value])(acc, f(v))
|
|
39
|
+
acc = acc.apply(m.map(x => acc => [*acc, x]))
|
|
40
|
+
acc
|
|
41
|
+
|
|
42
|
+
fold: (x, init, f) =>
|
|
43
|
+
let acc = init
|
|
44
|
+
for i in x:
|
|
45
|
+
acc = f(acc, i)
|
|
46
|
+
acc
|
|
47
|
+
|
|
48
|
+
first: (x, f) =>
|
|
49
|
+
for i in x:
|
|
50
|
+
if f(i):
|
|
51
|
+
return i
|
|
52
|
+
return None
|
|
53
|
+
|
|
54
|
+
last: (x, f) =>
|
|
55
|
+
let result = None
|
|
56
|
+
for i in x:
|
|
57
|
+
if f(i):
|
|
58
|
+
result = i
|
|
59
|
+
return result
|
|
60
|
+
|
|
61
|
+
at: (x, index) =>
|
|
62
|
+
for i in x:
|
|
63
|
+
if i == index:
|
|
64
|
+
return i
|
|
65
|
+
raise IndexError("Index out of range")
|
|
66
|
+
|
|
67
|
+
sum: x =>
|
|
68
|
+
let acc = 0
|
|
69
|
+
for i in x:
|
|
70
|
+
acc = acc + i
|
|
71
|
+
acc
|
|
72
|
+
|
|
73
|
+
list: x =>
|
|
74
|
+
list(x.iter)
|
|
75
|
+
|
|
76
|
+
record: x =>
|
|
77
|
+
Record(x.iter)
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
# TODO merge this with typing.Iterable?
|
|
81
|
+
|
|
82
|
+
export Iterable = Trait(__name__, "Iterable", methods, requires=["iter"])
|
|
83
|
+
register_global_trait(Iterable)
|
|
@@ -76,7 +76,7 @@ def do(f):
|
|
|
76
76
|
|
|
77
77
|
try:
|
|
78
78
|
# TODO: this is a workaround to avoid recursion.
|
|
79
|
-
# is it possible to
|
|
79
|
+
# is it possible to derive bind_gen directly from bind_once?
|
|
80
80
|
|
|
81
81
|
return vget(m, "bind_gen")(gen)
|
|
82
82
|
except (NotImplementedError, AttributeError):
|
|
@@ -9,7 +9,7 @@ def ExtensionProperty(f):
|
|
|
9
9
|
return f
|
|
10
10
|
|
|
11
11
|
|
|
12
|
-
def vget(obj, name):
|
|
12
|
+
def vget(obj, name, ignore_traits=False):
|
|
13
13
|
try:
|
|
14
14
|
return getattr(obj, name)
|
|
15
15
|
except:
|
|
@@ -34,7 +34,7 @@ def vget(obj, name):
|
|
|
34
34
|
except TypeError:
|
|
35
35
|
pass
|
|
36
36
|
|
|
37
|
-
v = fast_vget(obj, name)
|
|
37
|
+
v = fast_vget(obj, name, ignore_traits)
|
|
38
38
|
if v is not None:
|
|
39
39
|
if hasattr(v, "ext_prop"):
|
|
40
40
|
return v(obj)
|
|
@@ -79,27 +79,42 @@ struct TraitAttr {
|
|
|
79
79
|
static VTBL2: Lazy<Mutex<HashMap<String, Vec<TraitAttr>>>> =
|
|
80
80
|
Lazy::new(|| Mutex::new(HashMap::new()));
|
|
81
81
|
|
|
82
|
-
#[pyfunction(signature=(obj, name))]
|
|
82
|
+
#[pyfunction(signature=(obj, name, ignore_traits))]
|
|
83
83
|
fn fast_vget<'py, 'ptr>(
|
|
84
84
|
obj: &'ptr Bound<'py, PyAny>,
|
|
85
85
|
name: &'ptr Bound<'py, PyString>,
|
|
86
|
+
ignore_traits: bool,
|
|
86
87
|
) -> PyResult<PyObject> {
|
|
87
88
|
let name = name.to_string();
|
|
88
89
|
|
|
89
90
|
let py = obj.py();
|
|
90
91
|
|
|
91
|
-
|
|
92
|
+
{
|
|
93
|
+
// This block is necessary to ensure the lock is released
|
|
94
|
+
// before we possibly re-enter fast_vget through __tl__.vget below.
|
|
92
95
|
|
|
93
|
-
|
|
94
|
-
let mro = obj.get_type().mro();
|
|
96
|
+
let vtbl = VTBL.lock().unwrap();
|
|
95
97
|
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
98
|
+
if let Some(types) = vtbl.get(&name) {
|
|
99
|
+
let mro = obj.get_type().mro();
|
|
100
|
+
|
|
101
|
+
for typ in mro {
|
|
102
|
+
if let Some(attr) = types.get(&(typ.as_ptr() as usize)) {
|
|
103
|
+
return Ok((*attr).clone_ref(py));
|
|
104
|
+
}
|
|
99
105
|
}
|
|
100
106
|
}
|
|
101
107
|
}
|
|
102
108
|
|
|
109
|
+
if ignore_traits {
|
|
110
|
+
// This prevents a deadlock from fast_vget being called
|
|
111
|
+
// *again* from inside __tl_.vget while vtbl2 is locked.
|
|
112
|
+
|
|
113
|
+
// Also, prevents a trait from being dependent on other traits,
|
|
114
|
+
// which might cause an infinite loop sometimes.
|
|
115
|
+
return Ok(py.None().into_any());
|
|
116
|
+
}
|
|
117
|
+
|
|
103
118
|
let vtbl2 = VTBL2.lock().unwrap();
|
|
104
119
|
|
|
105
120
|
if let Some(traits) = vtbl2.get(&name) {
|
|
@@ -109,7 +124,7 @@ fn fast_vget<'py, 'ptr>(
|
|
|
109
124
|
for t in traits {
|
|
110
125
|
let mut ok = true;
|
|
111
126
|
for r in t.requirements.iter() {
|
|
112
|
-
if vget.call1((obj, r)).is_err() {
|
|
127
|
+
if vget.call1((obj, r, true)).is_err() {
|
|
113
128
|
ok = false;
|
|
114
129
|
break;
|
|
115
130
|
}
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
import util.assert_eq
|
|
2
|
+
|
|
3
|
+
# Traverse makes sure that apply and map implementations are correct.
|
|
4
|
+
|
|
5
|
+
assert_eq(
|
|
6
|
+
[1, 2, 3].traverse(x => @Async.pure(x)).run() # run in a new event loop
|
|
7
|
+
[1, 2, 3]
|
|
8
|
+
)
|
|
9
|
+
|
|
10
|
+
assert_eq(
|
|
11
|
+
[1, 2, 3].traverse(x => x + 1)
|
|
12
|
+
[2, 3, 4]
|
|
13
|
+
)
|
|
14
|
+
|
|
15
|
+
assert_eq(
|
|
16
|
+
[1, 2, 3].traverse(x => None)
|
|
17
|
+
None
|
|
18
|
+
)
|
|
@@ -640,7 +640,17 @@ where
|
|
|
640
640
|
.or_not()
|
|
641
641
|
.then_ignore(just(Token::Kw("import")))
|
|
642
642
|
.then(group((
|
|
643
|
-
|
|
643
|
+
// TODO this looks extremely cursed
|
|
644
|
+
symbol("..")
|
|
645
|
+
.to(2)
|
|
646
|
+
.repeated()
|
|
647
|
+
.foldr(
|
|
648
|
+
symbol(".")
|
|
649
|
+
.or_not()
|
|
650
|
+
.map(|x| if x.is_some() { 1 } else { 0 }),
|
|
651
|
+
|acc, sum| acc + sum,
|
|
652
|
+
)
|
|
653
|
+
.boxed(),
|
|
644
654
|
ident
|
|
645
655
|
.clone()
|
|
646
656
|
.then_ignore(symbol("."))
|
|
@@ -213,13 +213,6 @@ impl<'src> ResolveState<'src> {
|
|
|
213
213
|
let placeholder_ctx = self.placeholder_stack.pop().unwrap();
|
|
214
214
|
|
|
215
215
|
if placeholder_ctx.activated {
|
|
216
|
-
if ph_fn_ctx.is_async || ph_fn_ctx.is_generator || ph_fn_ctx.is_do {
|
|
217
|
-
self.errors.extend(simple_err(
|
|
218
|
-
"Await, bind, and yield are not allowed in placeholder functions",
|
|
219
|
-
placeholder_ctx.span,
|
|
220
|
-
));
|
|
221
|
-
}
|
|
222
|
-
|
|
223
216
|
ph_fn_ctx.is_placeholder = true;
|
|
224
217
|
|
|
225
218
|
ph_fn_ctx.arg_names.push(placeholder_ctx.arg_decl);
|
|
@@ -1323,13 +1316,6 @@ impl<'src> SExprExt<'src> for Indirect<SExpr<'src>> {
|
|
|
1323
1316
|
|
|
1324
1317
|
let fn_ctx = state.fn_stack.pop().unwrap();
|
|
1325
1318
|
|
|
1326
|
-
if fn_ctx.is_async || fn_ctx.is_generator || fn_ctx.is_do {
|
|
1327
|
-
state.errors.extend(simple_err(
|
|
1328
|
-
"Memo expressions cannot be async, generator, or do",
|
|
1329
|
-
span,
|
|
1330
|
-
));
|
|
1331
|
-
}
|
|
1332
|
-
|
|
1333
1319
|
state.memo_captures.insert(inner.as_ref().into(), fn_ctx);
|
|
1334
1320
|
|
|
1335
1321
|
Expr::Memo(inner)
|
|
@@ -15,6 +15,7 @@ use crate::{
|
|
|
15
15
|
use once_cell::sync::Lazy;
|
|
16
16
|
use parser::ast::*;
|
|
17
17
|
use slotmap::SlotMap;
|
|
18
|
+
use std::hash::{Hash, Hasher};
|
|
18
19
|
|
|
19
20
|
static PY_KWS: &[&str] = &[
|
|
20
21
|
"and", "as", "assert", "break", "class", "continue", "def", "del", "elif", "else", "except",
|
|
@@ -2279,20 +2280,26 @@ impl<'src, 'ast> SExprExt<'src, 'ast> for SExpr<'src> {
|
|
|
2279
2280
|
|
|
2280
2281
|
let callback = pre.bind(make_fn_exp(
|
|
2281
2282
|
ctx,
|
|
2282
|
-
FnDef::PyFnDef(vec![], py_body,
|
|
2283
|
+
FnDef::PyFnDef(vec![], py_body, memo_captures.is_do, memo_captures.is_async),
|
|
2283
2284
|
&span,
|
|
2284
2285
|
)?);
|
|
2285
2286
|
|
|
2286
2287
|
let linecol = ctx.line_cache.linecol(span.start);
|
|
2287
2288
|
|
|
2288
|
-
a.
|
|
2289
|
+
let memo_call = a.call(
|
|
2289
2290
|
a.tl_builtin("memo"),
|
|
2290
2291
|
vec![
|
|
2291
|
-
PyCallItem::Arg(
|
|
2292
|
-
|
|
2293
|
-
|
|
2294
|
-
|
|
2295
|
-
|
|
2292
|
+
PyCallItem::Arg(a.str(format!(
|
|
2293
|
+
"{}:{}:{}:{:08x}",
|
|
2294
|
+
ctx.filename,
|
|
2295
|
+
linecol.0,
|
|
2296
|
+
linecol.1,
|
|
2297
|
+
{
|
|
2298
|
+
let mut hasher = std::collections::hash_map::DefaultHasher::new();
|
|
2299
|
+
ctx.source[span.start..span.end].hash(&mut hasher);
|
|
2300
|
+
hasher.finish() as u32
|
|
2301
|
+
}
|
|
2302
|
+
))),
|
|
2296
2303
|
PyCallItem::Arg(
|
|
2297
2304
|
a.tuple(
|
|
2298
2305
|
nonlocals
|
|
@@ -2304,7 +2311,14 @@ impl<'src, 'ast> SExprExt<'src, 'ast> for SExpr<'src> {
|
|
|
2304
2311
|
),
|
|
2305
2312
|
PyCallItem::Arg(callback),
|
|
2306
2313
|
],
|
|
2307
|
-
)
|
|
2314
|
+
);
|
|
2315
|
+
|
|
2316
|
+
if memo_captures.is_do {
|
|
2317
|
+
// TODO should we really automatically bind a monadic expression for convenience?
|
|
2318
|
+
a.yield_(a.yield_(memo_call))
|
|
2319
|
+
} else {
|
|
2320
|
+
a.yield_(memo_call)
|
|
2321
|
+
}
|
|
2308
2322
|
}
|
|
2309
2323
|
Expr::Await(expr) => a.await_(pre.bind(expr.transform(ctx)?)),
|
|
2310
2324
|
Expr::Yield(expr) => a.yield_(pre.bind(expr.transform(ctx)?)),
|
|
@@ -3,9 +3,17 @@ export import .result.*
|
|
|
3
3
|
export import .async.*
|
|
4
4
|
export import .reader.*
|
|
5
5
|
export import .memo.*
|
|
6
|
+
export import .list.*
|
|
6
7
|
|
|
7
|
-
|
|
8
|
-
|
|
8
|
+
named = name => fn =>
|
|
9
|
+
fn.__name__ = name
|
|
10
|
+
fn.__qualname__ = name
|
|
11
|
+
fn
|
|
12
|
+
|
|
13
|
+
methods = {
|
|
14
|
+
id: named("id")& x => x
|
|
15
|
+
|
|
16
|
+
compose: named("compose")& (*args) =>
|
|
9
17
|
args match:
|
|
10
18
|
[] => raise ValueError("At least one function is required for composition")
|
|
11
19
|
[f] => f
|
|
@@ -21,4 +29,9 @@ export Fn = class:
|
|
|
21
29
|
composed.signature = fs[-1].signature
|
|
22
30
|
|
|
23
31
|
composed
|
|
24
|
-
default raise ValueError("Invalid arguments for Fn.compose()")
|
|
32
|
+
default raise ValueError("Invalid arguments for Fn.compose()")
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
globals().update(methods)
|
|
36
|
+
|
|
37
|
+
__all__ = methods.keys() | tuple
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
import collections.UserList
|
|
2
|
+
import itertools
|
|
3
|
+
|
|
4
|
+
import .monad.Monad
|
|
5
|
+
import ..iterable.Iterable
|
|
6
|
+
|
|
7
|
+
export List = class(UserList, Monad):
|
|
8
|
+
bind_once = (self, f) =>
|
|
9
|
+
raise NotImplementedError(
|
|
10
|
+
"Binding List in a do-block doesn't work in Koatl " +
|
|
11
|
+
"due to generator limitations. " +
|
|
12
|
+
"Wrap in Ok to use the regular Result monad."
|
|
13
|
+
)
|
|
14
|
+
|
|
15
|
+
bind = (self, f) => List(self.flat_map(f))
|
|
16
|
+
|
|
17
|
+
pure = staticmethod& x => [x]
|
|
18
|
+
|
|
19
|
+
traverse = Iterable.traverse
|
|
20
|
+
|
|
21
|
+
|
|
22
|
+
for name, method in List.__dict__:
|
|
23
|
+
if name.startswith("_"):
|
|
24
|
+
continue
|
|
25
|
+
|
|
26
|
+
register_global_attr(list, name, method)
|
|
@@ -1,12 +1,18 @@
|
|
|
1
1
|
import abc
|
|
2
2
|
|
|
3
3
|
export Monad = class(abc.ABC):
|
|
4
|
+
# The default implementation required for `@` syntax that should be overridden by subclasses.
|
|
5
|
+
bind_once = abc.abstractmethod& (self, f) => None
|
|
6
|
+
|
|
7
|
+
pure = staticmethod& abc.abstractmethod& value => None
|
|
8
|
+
|
|
9
|
+
# Automatically given by bind_once.
|
|
4
10
|
bind = (self, f) => self.bind_once(f)
|
|
5
11
|
|
|
6
12
|
# An optional, optimized implementation of `bind` that skips deep recursion.
|
|
13
|
+
# TODO: can this be automatically generated?
|
|
7
14
|
bind_gen = (self, gen) => raise NotImplementedError()
|
|
8
15
|
|
|
9
|
-
#
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
pure = staticmethod& abc.abstractmethod& value => None
|
|
16
|
+
# Automatically generated implementations.
|
|
17
|
+
map = (self, f) => self.bind(x => self.pure(f(x)))
|
|
18
|
+
apply = (self, f) => self.bind(x => f.map(fn => fn(x)))
|