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.
Files changed (109) hide show
  1. {koatl-0.1.13 → koatl-0.1.14}/Cargo.lock +1 -1
  2. {koatl-0.1.13 → koatl-0.1.14}/PKG-INFO +1 -1
  3. {koatl-0.1.13 → koatl-0.1.14}/koatl/Cargo.toml +1 -1
  4. {koatl-0.1.13 → koatl-0.1.14/koatl}/python/koatl/cli.py +2 -2
  5. {koatl-0.1.13 → koatl-0.1.14/koatl}/python/koatl/prelude/functional/__init__.tl +3 -2
  6. koatl-0.1.14/koatl/python/koatl/prelude/functional/async.tl +42 -0
  7. koatl-0.1.14/koatl/python/koatl/prelude/functional/monad.tl +12 -0
  8. koatl-0.1.14/koatl/python/koatl/prelude/functional/reader.tl +33 -0
  9. koatl-0.1.14/koatl/python/koatl/prelude/functional/result.tl +80 -0
  10. {koatl-0.1.13 → koatl-0.1.14/koatl}/python/koatl/runtime/__init__.py +2 -1
  11. {koatl-0.1.13 → koatl-0.1.14/koatl}/python/koatl/runtime/helpers.py +8 -4
  12. {koatl-0.1.13 → koatl-0.1.14}/koatl/python/koatl/runtime/meta_finder.py +6 -2
  13. {koatl-0.1.13 → koatl-0.1.14/koatl}/python/koatl/runtime/virtual.py +27 -0
  14. koatl-0.1.13/koatl/tests/e2e/prelude/ok.tl → koatl-0.1.14/koatl/tests/e2e/prelude/result.tl +11 -1
  15. koatl-0.1.14/koatl/tests/parse/deco.tl +2 -0
  16. {koatl-0.1.13 → koatl-0.1.14}/koatl-core/parser/src/ast.rs +1 -1
  17. {koatl-0.1.13 → koatl-0.1.14}/koatl-core/parser/src/parser.rs +16 -17
  18. {koatl-0.1.13 → koatl-0.1.14}/koatl-core/src/transform.rs +10 -12
  19. {koatl-0.1.13/koatl → koatl-0.1.14}/python/koatl/cli.py +2 -2
  20. {koatl-0.1.13/koatl → koatl-0.1.14}/python/koatl/prelude/functional/__init__.tl +3 -2
  21. koatl-0.1.14/python/koatl/prelude/functional/async.tl +42 -0
  22. koatl-0.1.14/python/koatl/prelude/functional/monad.tl +12 -0
  23. koatl-0.1.14/python/koatl/prelude/functional/reader.tl +33 -0
  24. koatl-0.1.14/python/koatl/prelude/functional/result.tl +80 -0
  25. {koatl-0.1.13/koatl → koatl-0.1.14}/python/koatl/runtime/__init__.py +2 -1
  26. {koatl-0.1.13/koatl → koatl-0.1.14}/python/koatl/runtime/helpers.py +8 -4
  27. {koatl-0.1.13 → koatl-0.1.14}/python/koatl/runtime/meta_finder.py +6 -2
  28. {koatl-0.1.13/koatl → koatl-0.1.14}/python/koatl/runtime/virtual.py +27 -0
  29. koatl-0.1.13/koatl/python/koatl/prelude/functional/async.tl +0 -38
  30. koatl-0.1.13/koatl/python/koatl/prelude/functional/ok.tl +0 -51
  31. koatl-0.1.13/koatl/python/koatl/prelude/functional/reader.tl +0 -24
  32. koatl-0.1.13/koatl/tests/parse/deco.tl +0 -2
  33. koatl-0.1.13/python/koatl/prelude/functional/async.tl +0 -38
  34. koatl-0.1.13/python/koatl/prelude/functional/ok.tl +0 -51
  35. koatl-0.1.13/python/koatl/prelude/functional/reader.tl +0 -24
  36. {koatl-0.1.13 → koatl-0.1.14}/Cargo.toml +0 -0
  37. {koatl-0.1.13 → koatl-0.1.14}/README.md +0 -0
  38. {koatl-0.1.13 → koatl-0.1.14}/koatl/.github/workflows/CI.yml +0 -0
  39. {koatl-0.1.13 → koatl-0.1.14}/koatl/.gitignore +0 -0
  40. {koatl-0.1.13 → koatl-0.1.14}/koatl/LICENSE +0 -0
  41. {koatl-0.1.13 → koatl-0.1.14}/koatl/README.md +0 -0
  42. {koatl-0.1.13 → koatl-0.1.14}/koatl/python/koatl/__init__.py +0 -0
  43. {koatl-0.1.13 → koatl-0.1.14}/koatl/python/koatl/__main__.py +0 -0
  44. {koatl-0.1.13 → koatl-0.1.14}/koatl/python/koatl/notebook/__init__.py +0 -0
  45. {koatl-0.1.13 → koatl-0.1.14}/koatl/python/koatl/notebook/magic.py +0 -0
  46. {koatl-0.1.13 → koatl-0.1.14}/koatl/python/koatl/prelude/__init__.tl +0 -0
  47. {koatl-0.1.13 → koatl-0.1.14}/koatl/python/koatl/prelude/functional/async_util.py +0 -0
  48. {koatl-0.1.13 → koatl-0.1.14}/koatl/python/koatl/prelude/iterable.tl +0 -0
  49. {koatl-0.1.13 → koatl-0.1.14}/koatl/python/koatl/runtime/record.py +0 -0
  50. {koatl-0.1.13 → koatl-0.1.14}/koatl/requirements.txt +0 -0
  51. {koatl-0.1.13 → koatl-0.1.14}/koatl/src/emit_py.rs +0 -0
  52. {koatl-0.1.13 → koatl-0.1.14}/koatl/src/lib.rs +0 -0
  53. {koatl-0.1.13 → koatl-0.1.14}/koatl/tests/e2e/base/coal.tl +0 -0
  54. {koatl-0.1.13 → koatl-0.1.14}/koatl/tests/e2e/base/containers.tl +0 -0
  55. {koatl-0.1.13 → koatl-0.1.14}/koatl/tests/e2e/base/decorators.tl +0 -0
  56. {koatl-0.1.13 → koatl-0.1.14}/koatl/tests/e2e/base/destructure-for-and-fn.tl +0 -0
  57. {koatl-0.1.13 → koatl-0.1.14}/koatl/tests/e2e/base/destructure.tl +0 -0
  58. {koatl-0.1.13 → koatl-0.1.14}/koatl/tests/e2e/base/escape_ident.tl +0 -0
  59. {koatl-0.1.13 → koatl-0.1.14}/koatl/tests/e2e/base/fstr.tl +0 -0
  60. {koatl-0.1.13 → koatl-0.1.14}/koatl/tests/e2e/base/functions.tl +0 -0
  61. {koatl-0.1.13 → koatl-0.1.14}/koatl/tests/e2e/base/generator.tl +0 -0
  62. {koatl-0.1.13 → koatl-0.1.14}/koatl/tests/e2e/base/if_expr.tl +0 -0
  63. {koatl-0.1.13 → koatl-0.1.14}/koatl/tests/e2e/base/imports.tl +0 -0
  64. {koatl-0.1.13 → koatl-0.1.14}/koatl/tests/e2e/base/iterables.tl +0 -0
  65. {koatl-0.1.13 → koatl-0.1.14}/koatl/tests/e2e/base/loops.tl +0 -0
  66. {koatl-0.1.13 → koatl-0.1.14}/koatl/tests/e2e/base/match.tl +0 -0
  67. {koatl-0.1.13 → koatl-0.1.14}/koatl/tests/e2e/base/nary-list.tl +0 -0
  68. {koatl-0.1.13 → koatl-0.1.14}/koatl/tests/e2e/base/placeholder.tl +0 -0
  69. {koatl-0.1.13 → koatl-0.1.14}/koatl/tests/e2e/base/precedence.tl +0 -0
  70. {koatl-0.1.13 → koatl-0.1.14}/koatl/tests/e2e/base/semantic_whitespace.tl +0 -0
  71. {koatl-0.1.13 → koatl-0.1.14}/koatl/tests/e2e/base/slice.tl +0 -0
  72. {koatl-0.1.13 → koatl-0.1.14}/koatl/tests/e2e/base/try.tl +0 -0
  73. {koatl-0.1.13 → koatl-0.1.14}/koatl/tests/e2e/destructure.tl +0 -0
  74. {koatl-0.1.13 → koatl-0.1.14}/koatl/tests/e2e/prelude/async.tl +0 -0
  75. {koatl-0.1.13 → koatl-0.1.14}/koatl/tests/e2e/prelude/reader.tl +0 -0
  76. {koatl-0.1.13 → koatl-0.1.14}/koatl/tests/e2e/prelude/virtual.tl +0 -0
  77. {koatl-0.1.13 → koatl-0.1.14}/koatl/tests/e2e/util/__init__.py +0 -0
  78. {koatl-0.1.13 → koatl-0.1.14}/koatl/tests/e2e/util/module0.tl +0 -0
  79. {koatl-0.1.13 → koatl-0.1.14}/koatl/tests/e2e/util/module1.tl +0 -0
  80. {koatl-0.1.13 → koatl-0.1.14}/koatl/tests/e2e/util/module2.tl +0 -0
  81. {koatl-0.1.13 → koatl-0.1.14}/koatl/tests/parse/arith.tl +0 -0
  82. {koatl-0.1.13 → koatl-0.1.14}/koatl/tests/parse/assign.tl +0 -0
  83. {koatl-0.1.13 → koatl-0.1.14}/koatl/tests/parse/func.tl +0 -0
  84. {koatl-0.1.13 → koatl-0.1.14}/koatl/tests/parse/matches.tl +0 -0
  85. {koatl-0.1.13 → koatl-0.1.14}/koatl/tests/test_e2e.py +0 -0
  86. {koatl-0.1.13 → koatl-0.1.14}/koatl/tests/test_parse.py +0 -0
  87. {koatl-0.1.13 → koatl-0.1.14}/koatl-core/Cargo.toml +0 -0
  88. {koatl-0.1.13 → koatl-0.1.14}/koatl-core/parser/Cargo.toml +0 -0
  89. {koatl-0.1.13 → koatl-0.1.14}/koatl-core/parser/src/lexer.rs +0 -0
  90. {koatl-0.1.13 → koatl-0.1.14}/koatl-core/parser/src/lib.rs +0 -0
  91. {koatl-0.1.13 → koatl-0.1.14}/koatl-core/parser/src/util.rs +0 -0
  92. {koatl-0.1.13 → koatl-0.1.14}/koatl-core/parser/tests/lexer.rs +0 -0
  93. {koatl-0.1.13 → koatl-0.1.14}/koatl-core/src/lib.rs +0 -0
  94. {koatl-0.1.13 → koatl-0.1.14}/koatl-core/src/linecol.rs +0 -0
  95. {koatl-0.1.13 → koatl-0.1.14}/koatl-core/src/main.rs +0 -0
  96. {koatl-0.1.13 → koatl-0.1.14}/koatl-core/src/parser.rs +0 -0
  97. {koatl-0.1.13 → koatl-0.1.14}/koatl-core/src/py/ast.rs +0 -0
  98. {koatl-0.1.13 → koatl-0.1.14}/koatl-core/src/py/emit.rs +0 -0
  99. {koatl-0.1.13 → koatl-0.1.14}/koatl-core/src/py/mod.rs +0 -0
  100. {koatl-0.1.13 → koatl-0.1.14}/koatl-core/src/py/util.rs +0 -0
  101. {koatl-0.1.13 → koatl-0.1.14}/pyproject.toml +0 -0
  102. {koatl-0.1.13 → koatl-0.1.14}/python/koatl/__init__.py +0 -0
  103. {koatl-0.1.13 → koatl-0.1.14}/python/koatl/__main__.py +0 -0
  104. {koatl-0.1.13 → koatl-0.1.14}/python/koatl/notebook/__init__.py +0 -0
  105. {koatl-0.1.13 → koatl-0.1.14}/python/koatl/notebook/magic.py +0 -0
  106. {koatl-0.1.13 → koatl-0.1.14}/python/koatl/prelude/__init__.tl +0 -0
  107. {koatl-0.1.13 → koatl-0.1.14}/python/koatl/prelude/functional/async_util.py +0 -0
  108. {koatl-0.1.13 → koatl-0.1.14}/python/koatl/prelude/iterable.tl +0 -0
  109. {koatl-0.1.13 → koatl-0.1.14}/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.13"
102
+ version = "0.1.14"
103
103
  dependencies = [
104
104
  "ariadne",
105
105
  "koatl-core",
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: koatl
3
- Version: 0.1.13
3
+ Version: 0.1.14
4
4
  Classifier: Programming Language :: Rust
5
5
  Classifier: Programming Language :: Python :: Implementation :: CPython
6
6
  Classifier: Programming Language :: Python :: Implementation :: PyPy
@@ -1,6 +1,6 @@
1
1
  [package]
2
2
  name = "koatl"
3
- version = "0.1.13"
3
+ version = "0.1.14"
4
4
  edition = "2021"
5
5
 
6
6
  [lib]
@@ -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 .ok.*
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 = &[staticmethod] (*args) =>
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
- coro = f(*args, **kwargs)
59
+ gen = f(*args, **kwargs)
60
60
 
61
61
  try:
62
- m = coro.send(None)
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 = coro.send(v)
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
- return vget(m, "bind_once")(recurse)
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(source_code, mode="prelude")
56
+ transpiled_code = transpile(
57
+ source_code, mode="prelude", filename=self.filepath
58
+ )
57
59
  else:
58
- transpiled_code = transpile(source_code, mode="module")
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])
@@ -0,0 +1,2 @@
1
+ wraps(f)& x => x
2
+ y = wraps(f)& x => x
@@ -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(Vec<SExpr<'a>>, Box<SExpr<'a>>),
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 extension = symbol("!")
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, extension)))
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((postfix, checked.clone())),
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.extend(deco);
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(decorators, expr) => {
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
- for item in decorators.iter().rev() {
2293
- node = (
2294
- PyExpr::Call(
2295
- Box::new(bind_pre(&mut pre, item.transform(ctx)?)),
2296
- vec![PyCallItem::Arg(node)],
2297
- ),
2298
- item.1,
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 .ok.*
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 = &[staticmethod] (*args) =>
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
+ )