koatl 0.1.23__tar.gz → 0.1.25__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (118) hide show
  1. {koatl-0.1.23 → koatl-0.1.25}/Cargo.lock +1 -1
  2. {koatl-0.1.23 → koatl-0.1.25}/PKG-INFO +1 -1
  3. {koatl-0.1.23 → koatl-0.1.25}/koatl/Cargo.toml +1 -1
  4. {koatl-0.1.23 → koatl-0.1.25/koatl}/python/koatl/prelude/functional/memo.tl +23 -10
  5. {koatl-0.1.23 → koatl-0.1.25/koatl}/python/koatl/prelude/functional/monad.tl +4 -1
  6. koatl-0.1.25/koatl/python/koatl/prelude/functional/result.tl +147 -0
  7. {koatl-0.1.23 → koatl-0.1.25}/koatl/python/koatl/prelude/iterable.tl +2 -2
  8. {koatl-0.1.23 → koatl-0.1.25/koatl}/python/koatl/runtime/__init__.py +16 -7
  9. {koatl-0.1.23 → koatl-0.1.25/koatl}/python/koatl/runtime/helpers.py +14 -6
  10. {koatl-0.1.23 → koatl-0.1.25}/koatl/src/emit_py.rs +20 -3
  11. {koatl-0.1.23 → koatl-0.1.25}/koatl/tests/e2e/base/coal.tl +0 -12
  12. koatl-0.1.25/koatl/tests/e2e/base/loops.tl +25 -0
  13. {koatl-0.1.23 → koatl-0.1.25}/koatl/tests/e2e/base/match.tl +0 -2
  14. koatl-0.1.25/koatl/tests/e2e/base/short_circuit.tl +42 -0
  15. {koatl-0.1.23 → koatl-0.1.25}/koatl/tests/e2e/prelude/memo.tl +1 -1
  16. {koatl-0.1.23/koatl/tests/e2e/base → koatl-0.1.25/koatl/tests/e2e/prelude}/try.tl +11 -4
  17. {koatl-0.1.23 → koatl-0.1.25}/koatl-core/parser/src/ast.rs +1 -1
  18. {koatl-0.1.23 → koatl-0.1.25}/koatl-core/src/resolve_scopes.rs +149 -42
  19. {koatl-0.1.23 → koatl-0.1.25}/koatl-core/src/transform.rs +249 -174
  20. {koatl-0.1.23/koatl → koatl-0.1.25}/python/koatl/prelude/functional/memo.tl +23 -10
  21. {koatl-0.1.23/koatl → koatl-0.1.25}/python/koatl/prelude/functional/monad.tl +4 -1
  22. koatl-0.1.25/python/koatl/prelude/functional/result.tl +147 -0
  23. {koatl-0.1.23 → koatl-0.1.25}/python/koatl/prelude/iterable.tl +2 -2
  24. {koatl-0.1.23/koatl → koatl-0.1.25}/python/koatl/runtime/__init__.py +16 -7
  25. {koatl-0.1.23/koatl → koatl-0.1.25}/python/koatl/runtime/helpers.py +14 -6
  26. koatl-0.1.23/koatl/python/koatl/prelude/functional/result.tl +0 -90
  27. koatl-0.1.23/koatl/tests/e2e/base/loops.tl +0 -14
  28. koatl-0.1.23/python/koatl/prelude/functional/result.tl +0 -90
  29. {koatl-0.1.23 → koatl-0.1.25}/Cargo.toml +0 -0
  30. {koatl-0.1.23 → koatl-0.1.25}/README.md +0 -0
  31. {koatl-0.1.23 → koatl-0.1.25}/koatl/.github/workflows/CI.yml +0 -0
  32. {koatl-0.1.23 → koatl-0.1.25}/koatl/.gitignore +0 -0
  33. {koatl-0.1.23 → koatl-0.1.25}/koatl/LICENSE +0 -0
  34. {koatl-0.1.23 → koatl-0.1.25}/koatl/README.md +0 -0
  35. {koatl-0.1.23 → koatl-0.1.25}/koatl/python/koatl/__init__.py +0 -0
  36. {koatl-0.1.23 → koatl-0.1.25}/koatl/python/koatl/__main__.py +0 -0
  37. {koatl-0.1.23 → koatl-0.1.25}/koatl/python/koatl/cli.py +0 -0
  38. {koatl-0.1.23 → koatl-0.1.25}/koatl/python/koatl/notebook/__init__.py +0 -0
  39. {koatl-0.1.23 → koatl-0.1.25}/koatl/python/koatl/notebook/magic.py +0 -0
  40. {koatl-0.1.23 → koatl-0.1.25}/koatl/python/koatl/prelude/__init__.tl +0 -0
  41. {koatl-0.1.23 → koatl-0.1.25}/koatl/python/koatl/prelude/functional/__init__.tl +0 -0
  42. {koatl-0.1.23 → koatl-0.1.25}/koatl/python/koatl/prelude/functional/async.tl +0 -0
  43. {koatl-0.1.23 → koatl-0.1.25}/koatl/python/koatl/prelude/functional/async_util.py +0 -0
  44. {koatl-0.1.23 → koatl-0.1.25}/koatl/python/koatl/prelude/functional/list.tl +0 -0
  45. {koatl-0.1.23 → koatl-0.1.25}/koatl/python/koatl/prelude/functional/reader.tl +0 -0
  46. {koatl-0.1.23 → koatl-0.1.25}/koatl/python/koatl/runtime/meta_finder.py +0 -0
  47. {koatl-0.1.23 → koatl-0.1.25}/koatl/python/koatl/runtime/record.py +0 -0
  48. {koatl-0.1.23 → koatl-0.1.25}/koatl/python/koatl/runtime/virtual.py +0 -0
  49. {koatl-0.1.23 → koatl-0.1.25}/koatl/requirements.txt +0 -0
  50. {koatl-0.1.23 → koatl-0.1.25}/koatl/src/lib.rs +0 -0
  51. {koatl-0.1.23 → koatl-0.1.25}/koatl/tests/e2e/base/containers.tl +0 -0
  52. {koatl-0.1.23 → koatl-0.1.25}/koatl/tests/e2e/base/decorators.tl +0 -0
  53. {koatl-0.1.23 → koatl-0.1.25}/koatl/tests/e2e/base/destructure-for-and-fn.tl +0 -0
  54. {koatl-0.1.23 → koatl-0.1.25}/koatl/tests/e2e/base/destructure.tl +0 -0
  55. {koatl-0.1.23 → koatl-0.1.25}/koatl/tests/e2e/base/escape_ident.tl +0 -0
  56. {koatl-0.1.23 → koatl-0.1.25}/koatl/tests/e2e/base/fstr.tl +0 -0
  57. {koatl-0.1.23 → koatl-0.1.25}/koatl/tests/e2e/base/functions.tl +0 -0
  58. {koatl-0.1.23 → koatl-0.1.25}/koatl/tests/e2e/base/generator.tl +0 -0
  59. {koatl-0.1.23 → koatl-0.1.25}/koatl/tests/e2e/base/if_expr.tl +0 -0
  60. {koatl-0.1.23 → koatl-0.1.25}/koatl/tests/e2e/base/imports.tl +0 -0
  61. {koatl-0.1.23 → koatl-0.1.25}/koatl/tests/e2e/base/nary-list.tl +0 -0
  62. {koatl-0.1.23 → koatl-0.1.25}/koatl/tests/e2e/base/placeholder.tl +0 -0
  63. {koatl-0.1.23 → koatl-0.1.25}/koatl/tests/e2e/base/precedence.tl +0 -0
  64. {koatl-0.1.23 → koatl-0.1.25}/koatl/tests/e2e/base/scopes.tl +0 -0
  65. {koatl-0.1.23 → koatl-0.1.25}/koatl/tests/e2e/base/semantic_whitespace.tl +0 -0
  66. {koatl-0.1.23 → koatl-0.1.25}/koatl/tests/e2e/destructure.tl +0 -0
  67. {koatl-0.1.23 → koatl-0.1.25}/koatl/tests/e2e/prelude/async.tl +0 -0
  68. {koatl-0.1.23 → koatl-0.1.25}/koatl/tests/e2e/prelude/iterables.tl +0 -0
  69. {koatl-0.1.23 → koatl-0.1.25}/koatl/tests/e2e/prelude/list.tl +0 -0
  70. {koatl-0.1.23 → koatl-0.1.25}/koatl/tests/e2e/prelude/reader.tl +0 -0
  71. {koatl-0.1.23 → koatl-0.1.25}/koatl/tests/e2e/prelude/result.tl +0 -0
  72. {koatl-0.1.23/koatl/tests/e2e/base → koatl-0.1.25/koatl/tests/e2e/prelude}/slice.tl +0 -0
  73. {koatl-0.1.23 → koatl-0.1.25}/koatl/tests/e2e/prelude/virtual.tl +0 -0
  74. {koatl-0.1.23 → koatl-0.1.25}/koatl/tests/e2e/util/__init__.py +0 -0
  75. {koatl-0.1.23 → koatl-0.1.25}/koatl/tests/e2e/util/module0.tl +0 -0
  76. {koatl-0.1.23 → koatl-0.1.25}/koatl/tests/e2e/util/module1.tl +0 -0
  77. {koatl-0.1.23 → koatl-0.1.25}/koatl/tests/e2e/util/module2.tl +0 -0
  78. {koatl-0.1.23 → koatl-0.1.25}/koatl/tests/parse/arith.tl +0 -0
  79. {koatl-0.1.23 → koatl-0.1.25}/koatl/tests/parse/assign.tl +0 -0
  80. {koatl-0.1.23 → koatl-0.1.25}/koatl/tests/parse/block-comments.tl +0 -0
  81. {koatl-0.1.23 → koatl-0.1.25}/koatl/tests/parse/deco.tl +0 -0
  82. {koatl-0.1.23 → koatl-0.1.25}/koatl/tests/parse/func.tl +0 -0
  83. {koatl-0.1.23 → koatl-0.1.25}/koatl/tests/parse/matches.tl +0 -0
  84. {koatl-0.1.23 → koatl-0.1.25}/koatl/tests/test_e2e.py +0 -0
  85. {koatl-0.1.23 → koatl-0.1.25}/koatl/tests/test_parse.py +0 -0
  86. {koatl-0.1.23 → koatl-0.1.25}/koatl-core/Cargo.toml +0 -0
  87. {koatl-0.1.23 → koatl-0.1.25}/koatl-core/parser/Cargo.toml +0 -0
  88. {koatl-0.1.23 → koatl-0.1.25}/koatl-core/parser/src/lexer.rs +0 -0
  89. {koatl-0.1.23 → koatl-0.1.25}/koatl-core/parser/src/lib.rs +0 -0
  90. {koatl-0.1.23 → koatl-0.1.25}/koatl-core/parser/src/parser.rs +0 -0
  91. {koatl-0.1.23 → koatl-0.1.25}/koatl-core/parser/src/util.rs +0 -0
  92. {koatl-0.1.23 → koatl-0.1.25}/koatl-core/parser/tests/lexer.rs +0 -0
  93. {koatl-0.1.23 → koatl-0.1.25}/koatl-core/src/inference.rs +0 -0
  94. {koatl-0.1.23 → koatl-0.1.25}/koatl-core/src/lib.rs +0 -0
  95. {koatl-0.1.23 → koatl-0.1.25}/koatl-core/src/main.rs +0 -0
  96. {koatl-0.1.23 → koatl-0.1.25}/koatl-core/src/parse_timer.rs +0 -0
  97. {koatl-0.1.23 → koatl-0.1.25}/koatl-core/src/parser.rs +0 -0
  98. {koatl-0.1.23 → koatl-0.1.25}/koatl-core/src/py/ast.rs +0 -0
  99. {koatl-0.1.23 → koatl-0.1.25}/koatl-core/src/py/emit.rs +0 -0
  100. {koatl-0.1.23 → koatl-0.1.25}/koatl-core/src/py/mod.rs +0 -0
  101. {koatl-0.1.23 → koatl-0.1.25}/koatl-core/src/py/util.rs +0 -0
  102. {koatl-0.1.23 → koatl-0.1.25}/koatl-core/src/types.rs +0 -0
  103. {koatl-0.1.23 → koatl-0.1.25}/koatl-core/src/util.rs +0 -0
  104. {koatl-0.1.23 → koatl-0.1.25}/pyproject.toml +0 -0
  105. {koatl-0.1.23 → koatl-0.1.25}/python/koatl/__init__.py +0 -0
  106. {koatl-0.1.23 → koatl-0.1.25}/python/koatl/__main__.py +0 -0
  107. {koatl-0.1.23 → koatl-0.1.25}/python/koatl/cli.py +0 -0
  108. {koatl-0.1.23 → koatl-0.1.25}/python/koatl/notebook/__init__.py +0 -0
  109. {koatl-0.1.23 → koatl-0.1.25}/python/koatl/notebook/magic.py +0 -0
  110. {koatl-0.1.23 → koatl-0.1.25}/python/koatl/prelude/__init__.tl +0 -0
  111. {koatl-0.1.23 → koatl-0.1.25}/python/koatl/prelude/functional/__init__.tl +0 -0
  112. {koatl-0.1.23 → koatl-0.1.25}/python/koatl/prelude/functional/async.tl +0 -0
  113. {koatl-0.1.23 → koatl-0.1.25}/python/koatl/prelude/functional/async_util.py +0 -0
  114. {koatl-0.1.23 → koatl-0.1.25}/python/koatl/prelude/functional/list.tl +0 -0
  115. {koatl-0.1.23 → koatl-0.1.25}/python/koatl/prelude/functional/reader.tl +0 -0
  116. {koatl-0.1.23 → koatl-0.1.25}/python/koatl/runtime/meta_finder.py +0 -0
  117. {koatl-0.1.23 → koatl-0.1.25}/python/koatl/runtime/record.py +0 -0
  118. {koatl-0.1.23 → koatl-0.1.25}/python/koatl/runtime/virtual.py +0 -0
@@ -99,7 +99,7 @@ checksum = "f4c7245a08504955605670dbf141fceab975f15ca21570696aebe9d2e71576bd"
99
99
 
100
100
  [[package]]
101
101
  name = "koatl"
102
- version = "0.1.23"
102
+ version = "0.1.25"
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.23
3
+ Version: 0.1.25
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.23"
3
+ version = "0.1.25"
4
4
  edition = "2021"
5
5
 
6
6
  [lib]
@@ -1,6 +1,6 @@
1
1
  import collections.defaultdict
2
2
  import functools.wraps
3
- import .result.Ok
3
+ import .result.(Result, Ok)
4
4
  import .Monad
5
5
 
6
6
  export Memo = class(Monad):
@@ -11,7 +11,8 @@ export Memo = class(Monad):
11
11
  __repr__ = self => f"Memo.Cache({self.cache})"
12
12
 
13
13
  try_get = (self, name, deps) =>
14
- try self.cache[name][deps] except KeyError()
14
+ # TODO should try operator return a Result directly?
15
+ Result(try self.cache[name][deps] except KeyError())
15
16
 
16
17
  update = (self, name, deps, value) =>
17
18
  self.cache[name][deps] = value
@@ -21,9 +22,20 @@ export Memo = class(Monad):
21
22
 
22
23
  __repr__ = self => f"Memo(...)"
23
24
 
25
+ # This needs to be special cased because if we try to use Memo.value
26
+ # directly, the *generator* itself gets memoized. We want to memoize
27
+ # the thing that that comes out of the generator.
28
+ bind_value = staticmethod& (id, deps, f) =>
29
+ Memo(ctx =>
30
+ if ctx.try_get(id, tuple(deps)) matches Ok(value):
31
+ return value
32
+
33
+ ctx.update(id, tuple(deps), f().f(ctx))
34
+ )
35
+
24
36
  value = staticmethod& (id, deps, f) =>
25
37
  Memo(ctx =>
26
- if ctx.try_get(id, tuple(deps)) matches Ok() as value:
38
+ if ctx.try_get(id, tuple(deps)) matches Ok(value):
27
39
  return value
28
40
 
29
41
  ctx.update(id, tuple(deps), f())
@@ -34,12 +46,12 @@ export Memo = class(Monad):
34
46
  let deps = (tuple(args), tuple(kwargs.items()))
35
47
 
36
48
  Memo(ctx =>
37
- if ctx.try_get(id, deps) matches Ok() as value:
49
+ if ctx.try_get(id, deps) matches Ok(value):
38
50
  return value
39
51
 
40
52
  let v = f(*args, **kwargs)
41
53
  if v matches Memo():
42
- v = v.run(ctx)
54
+ v = v.f(ctx)
43
55
 
44
56
  ctx.update(id, deps, v)
45
57
  )
@@ -52,21 +64,22 @@ export Memo = class(Monad):
52
64
  pure = staticmethod& value => Memo(ctx => value)
53
65
 
54
66
  bind_once = (self, f) => Memo(ctx =>
55
- let value = f(self.run(ctx))
67
+ let value = f(self.f(ctx))
56
68
  if value matches Memo():
57
- value = value.run(ctx)
69
+ value = value.f(ctx)
58
70
  value
59
71
  )
60
72
 
61
73
  bind_gen = (self, gen) => Memo(ctx =>
62
- self = self.run(ctx)
74
+ self = self.f(ctx)
63
75
  try:
64
76
  while True:
65
77
  self = gen.send(self)
66
78
  if self matches Memo():
67
- self = self.run(ctx)
79
+ self = self.f(ctx)
68
80
  except StopIteration(value=value):
69
81
  return value
70
82
  )
71
83
 
72
- __tl__.memo = Memo.value
84
+ __tl__.memo_value = Memo.value
85
+ __tl__.bind_memo_value = Memo.bind_value
@@ -1,6 +1,9 @@
1
1
  import abc
2
2
 
3
+ # TODO: this should be really called MonadOnce.
3
4
  export Monad = class(abc.ABC):
5
+ __slots__ = ()
6
+
4
7
  # The default implementation required for `@` syntax that should be overridden by subclasses.
5
8
  bind_once = abc.abstractmethod& (self, f) => None
6
9
 
@@ -11,7 +14,7 @@ export Monad = class(abc.ABC):
11
14
 
12
15
  # An optional, optimized implementation of `bind` that skips deep recursion.
13
16
  # TODO: can this be automatically generated?
14
- bind_gen = (self, gen) => raise NotImplementedError()
17
+ # bind_gen = (self, gen) => ...
15
18
 
16
19
  # Automatically generated implementations.
17
20
  map = (self, f) => self.bind(x => self.pure(f(x)))
@@ -0,0 +1,147 @@
1
+ import functools.wraps
2
+ import .monad.Monad
3
+ import koatl.runtime.virtual.register_global_attr
4
+
5
+ export Result = class:
6
+ __slots__ = ()
7
+ __match_args__ = ("value",)
8
+
9
+ __new__ = (cls, value) =>
10
+ if isinstance(value, Result):
11
+ return value
12
+ value.ok then Ok(value) else Err(value)
13
+
14
+ __init__ = (self, *args, **kwargs) =>
15
+ raise ValueError("Result should not be instantiated directly, use Ok or Err")
16
+
17
+ checked = staticmethod& f =>
18
+ """
19
+ Runs the function in a try block and returns a Result.
20
+ If the function raises an exception, it will be wrapped in an Err.
21
+ """
22
+ try:
23
+ return Ok(f())
24
+ except BaseException() as e:
25
+ return Err(e)
26
+
27
+ bind_once = (self, f) =>
28
+ self = Result(self)
29
+ if not self.ok:
30
+ return self
31
+
32
+ let v = Result(f(self.value))
33
+
34
+ bind = bind_once
35
+
36
+ bind_gen = (self, gen) =>
37
+ self = Result(self)
38
+ try:
39
+ while True:
40
+ if not self.ok:
41
+ return self
42
+
43
+ self = Result(gen.send(self.value))
44
+ except StopIteration(value=value):
45
+ return Result.pure(value)
46
+
47
+ pure = staticmethod& x => Ok(x)
48
+
49
+ apply = (self, f) =>
50
+ self = Result(self)
51
+ f = Result(f)
52
+
53
+ if not self.ok:
54
+ return self
55
+ if not f.ok:
56
+ return f
57
+
58
+ Ok(f(self))
59
+
60
+ map = (self, f) =>
61
+ self = Result(self)
62
+ if self.ok:
63
+ return Ok(f(self.value))
64
+ return self
65
+
66
+ map_err = (self, f) =>
67
+ self = Result(self)
68
+ if not self.ok:
69
+ return Err(f(self.value))
70
+ return self
71
+
72
+ map_none = (self, f) =>
73
+ self = Result(self)
74
+ if not self.ok and self.value === None:
75
+ return Err(f())
76
+ return self
77
+
78
+ export Ok = class(Result):
79
+ __slots__ = ("value",)
80
+ ok = True
81
+ __new__ = (cls, value) => object.__new__(cls)
82
+ __init__ = (self, value) =>
83
+ if hasattr(self, "value"):
84
+ # This is necessary to prevent overwriting the value
85
+ # since Python's Result.__new__ will call Ok.__init__ again
86
+ # if the object is already created.
87
+ return None
88
+ self.value = value
89
+
90
+ __eq__ = (self, other) =>
91
+ if not isinstance(other, Ok):
92
+ return False
93
+ return self.value == other.value
94
+
95
+ __repr__ = self => f"Ok({repr(self.value)})"
96
+ unwrap = self => self.value
97
+ coalesce = (self, f) => self.value
98
+
99
+ export Err = class(Result):
100
+ __slots__ = ("value",)
101
+ ok = False
102
+ __new__ = (cls, value) => object.__new__(cls)
103
+ __init__ = (self, value) =>
104
+ if hasattr(self, "value"):
105
+ return None
106
+ self.value = value
107
+
108
+ __eq__ = (self, other) =>
109
+ if not isinstance(other, Err):
110
+ return False
111
+ return self.value == other.value
112
+
113
+ __repr__ = self => f"Err({repr(self.value)})"
114
+ unwrap = self =>
115
+ if self.value matches BaseException():
116
+ raise self.value
117
+ raise ValueError(f"Expected Ok, got {repr(self.value)}")
118
+ coalesce = (self, f) => f()
119
+
120
+
121
+ register_global_attr(type(None), "ok", ExtensionProperty(_ => False))
122
+ register_global_attr(BaseException, "ok", ExtensionProperty(_ => False))
123
+ register_global_attr(object, "ok", ExtensionProperty(_ => True))
124
+
125
+ register_global_attr(object, "result", ExtensionProperty(Result))
126
+
127
+ # Enables @ operator for bare objects.
128
+ register_global_attr(object, "bind_gen", Result.bind_gen)
129
+
130
+ __tl__.Ok = Ok
131
+ __tl__.Err = Err
132
+
133
+ __tl__.op_coal = (x, f) =>
134
+ if x matches Result():
135
+ return x.coalesce(f)
136
+
137
+ if not x.ok:
138
+ return f()
139
+ return x
140
+
141
+ __tl__.op_map = (x, f) =>
142
+ if x matches Result():
143
+ return x.map(f)
144
+
145
+ if x.ok:
146
+ return f(x)
147
+ return x
@@ -21,13 +21,13 @@ methods = {
21
21
  if not hasattr(m, "apply"):
22
22
  # special case for bare types - slightly more efficient
23
23
  # ...also required since bare types don't have .map
24
- if not __tl__.ok(m):
24
+ if not m.ok:
25
25
  return m
26
26
 
27
27
  let acc = [m]
28
28
  for v in it:
29
29
  let fv = f(v)
30
- if not __tl__.ok(fv):
30
+ if not fv.ok:
31
31
  return fv
32
32
  acc.append(fv)
33
33
  return acc
@@ -21,10 +21,13 @@ from .record import *
21
21
  from .helpers import *
22
22
 
23
23
 
24
- def dummy_memo(*args, **kwargs):
25
- raise RuntimeError(
26
- "memo is not available without the prelude. Please import koatl.prelude."
27
- )
24
+ def dummy(name):
25
+ def wrapper(*args, **kwargs):
26
+ raise RuntimeError(
27
+ f"{name} is not available without the prelude. Please import koatl.prelude."
28
+ )
29
+
30
+ return wrapper
28
31
 
29
32
 
30
33
  __tl__ = SimpleNamespace(
@@ -32,15 +35,21 @@ __tl__ = SimpleNamespace(
32
35
  slice=slice,
33
36
  vget=virtual.vget,
34
37
  vhas=virtual.vhas,
35
- memo=dummy_memo,
36
38
  unpack_record=helpers.unpack_record,
37
39
  set_exports=helpers.set_exports,
38
40
  do=helpers.do,
39
- ok=helpers.ok,
40
41
  partial=functools.partial,
42
+ # These require more complex logic and require the prelude.
43
+ # The runtime provides dummy implementations that raise if used without the prelude.
44
+ memo_value=dummy("memo"),
45
+ bind_memo_value=dummy("memo"),
46
+ op_map=dummy("?"),
47
+ op_coal=("??"),
48
+ Ok=dummy("try-expr"),
49
+ Err=dummy("try-expr"),
41
50
  **{name: helpers.__dict__[name] for name in helpers.__all__},
42
51
  **{name: record.__dict__[name] for name in record.__all__},
43
- **{name: virtual.__dict__[name] for name in virtual.__all__}
52
+ **{name: virtual.__dict__[name] for name in virtual.__all__},
44
53
  )
45
54
 
46
55
 
@@ -46,6 +46,11 @@ def unpack_record(obj):
46
46
 
47
47
 
48
48
  def ok(obj):
49
+ try:
50
+ return obj.ok
51
+ except AttributeError:
52
+ pass
53
+
49
54
  if obj is None:
50
55
  return False
51
56
  if isinstance(obj, BaseException):
@@ -61,6 +66,10 @@ def do(f):
61
66
  try:
62
67
  m = gen.send(None)
63
68
  except StopIteration as e:
69
+ if not hasattr(e.value, "bind_once"):
70
+ raise ValueError(
71
+ "This do-block returned a bare value before it could infer the monadic type. Wrap the value in pure()."
72
+ ) from None
64
73
  return e.value
65
74
 
66
75
  def recurse(v):
@@ -69,17 +78,16 @@ def do(f):
69
78
  m = gen.send(v)
70
79
  return vget(m, "bind_once")(recurse)
71
80
  except StopIteration as e:
72
- try:
73
- return vget(m, "pure")(e.value)
74
- except AttributeError:
75
- return e.value
81
+ return vget(m, "pure")(e.value)
76
82
 
77
83
  try:
78
84
  # TODO: this is a workaround to avoid recursion.
79
85
  # is it possible to derive bind_gen directly from bind_once?
80
86
 
81
- return vget(m, "bind_gen")(gen)
82
- except (NotImplementedError, AttributeError):
87
+ bind_gen = vget(m, "bind_gen")
88
+ except AttributeError:
83
89
  return vget(m, "bind_once")(recurse)
84
90
 
91
+ return bind_gen(gen)
92
+
85
93
  return impl
@@ -502,7 +502,7 @@ impl<'src> PyExprExt<'src> for SPyExpr<'src> {
502
502
  ctx.ast_node("Name", (ident, c.emit_py(ctx)?), &self.tl_span)?
503
503
  }
504
504
  PyExpr::Binary(op, left, right) => {
505
- let py_op_str = match op {
505
+ let py_bin_op = match op {
506
506
  PyBinaryOp::Add => Some("Add"),
507
507
  PyBinaryOp::Sub => Some("Sub"),
508
508
  PyBinaryOp::Mult => Some("Mult"),
@@ -512,18 +512,35 @@ impl<'src> PyExprExt<'src> for SPyExpr<'src> {
512
512
  _ => None,
513
513
  };
514
514
 
515
- if let Some(py_op_str) = py_op_str {
515
+ if let Some(py_bin_op) = py_bin_op {
516
516
  return ctx.ast_node(
517
517
  "BinOp",
518
518
  (
519
519
  left.emit_py(ctx)?,
520
- ctx.ast_cls(py_op_str, ())?,
520
+ ctx.ast_cls(py_bin_op, ())?,
521
521
  right.emit_py(ctx)?,
522
522
  ),
523
523
  &self.tl_span,
524
524
  );
525
525
  }
526
526
 
527
+ let py_bool_op = match op {
528
+ PyBinaryOp::And => Some("And"),
529
+ PyBinaryOp::Or => Some("Or"),
530
+ _ => None,
531
+ };
532
+
533
+ if let Some(py_bool_op) = py_bool_op {
534
+ return ctx.ast_node(
535
+ "BoolOp",
536
+ (
537
+ ctx.ast_cls(py_bool_op, ())?,
538
+ [left.emit_py(ctx)?, right.emit_py(ctx)?],
539
+ ),
540
+ &self.tl_span,
541
+ );
542
+ }
543
+
527
544
  let py_cmp_op = match op {
528
545
  PyBinaryOp::Lt => Some("Lt"),
529
546
  PyBinaryOp::Gt => Some("Gt"),
@@ -17,15 +17,3 @@ assert_eq(1?.($ + 1)(), 2)
17
17
 
18
18
  assert_eq(None ?? 1, 1)
19
19
  assert_eq(int(5) ?? 1, 5)
20
-
21
-
22
- # try exprs
23
-
24
- assert_eq(type(try x), NameError)
25
- assert_eq(type(try int(1)[1]), TypeError)
26
- assert_eq(type(try [0][5]), IndexError)
27
- assert_eq(try 5, 5)
28
-
29
-
30
- assert_eq((try 5)?.($+1)?(), 6)
31
- assert_eq(type((try x)?.($+1)?()), NameError)
@@ -0,0 +1,25 @@
1
+ import util.assert_eq
2
+
3
+ x = []
4
+ for i in [1, 2, 3]:
5
+ x = [*x, i]
6
+
7
+ assert_eq(x, [1, 2, 3])
8
+
9
+ i = True
10
+ while (x => x)(i):
11
+ if i === False:
12
+ assert False
13
+
14
+ i = False
15
+
16
+ counts = []
17
+ counter = 0
18
+ while (
19
+ counter = counter + 1
20
+ counter < 5
21
+ ):
22
+ counts.append(counter)
23
+
24
+ assert_eq(counter, 5)
25
+ assert_eq(counts, [1, 2, 3, 4])
@@ -73,8 +73,6 @@ x = 1
73
73
  assert_eq([1, 2, 3] matches [1, 2, 3], True)
74
74
  assert_eq([1, 2, 3] matches [.x, _, 3], True)
75
75
 
76
- assert_eq(try 1 matches 1, True)
77
-
78
76
  f = () =>
79
77
  if [1, 2, 3] matches [1, 2, x]:
80
78
  assert_eq(x, 3)
@@ -0,0 +1,42 @@
1
+ import util.assert_eq
2
+
3
+ assert_eq(
4
+ True and False
5
+ False
6
+ )
7
+
8
+ # No short-circuiting here.
9
+
10
+ flag = 0
11
+ assert_eq(
12
+ True and (
13
+ flag = 1
14
+ True
15
+ )
16
+ True
17
+ )
18
+ assert_eq(flag, 1)
19
+
20
+ # This needs to short-circuit.
21
+
22
+ flag = 0
23
+ assert_eq(
24
+ False and (
25
+ flag = 1
26
+ True
27
+ )
28
+ False
29
+ )
30
+ assert_eq(flag, 0)
31
+
32
+ # Same with or
33
+
34
+ flag = 0
35
+ assert_eq(
36
+ True or (
37
+ flag = 1
38
+ True
39
+ )
40
+ True
41
+ )
42
+ assert_eq(flag, 0)
@@ -4,7 +4,7 @@ counts = 0
4
4
 
5
5
  fib = Memo.fn& x =>
6
6
  if x < 2:
7
- return 1
7
+ return Memo.pure(1)
8
8
 
9
9
  counts = counts + 1
10
10
 
@@ -3,12 +3,14 @@ import util.assert_eq
3
3
  # precedence
4
4
  assert_eq(try 1 ?? 2, 1)
5
5
  assert_eq(try z ?? 2, 2)
6
- assert_eq(type(try z), NameError)
6
+ assert_eq(try z matches Err(), True)
7
+ assert_eq((try z).value matches NameError(), True)
7
8
  assert_eq(try z ?? try 1 ?? 2, 1)
8
- assert_eq(try z ?? try None ?? 2, 2)
9
+ assert_eq(try z ?? try None ?? 2, None)
10
+ assert_eq(try z ?? Err(None) ?? 2, 2)
9
11
 
10
12
  err_type = NameError
11
- assert_eq(type(try a except err_type()), err_type)
13
+ assert_eq(try a except err_type() matches Err(), True)
12
14
 
13
15
  # handlers
14
16
  assert_eq(try 1 except ValueError() ?? 2, 1)
@@ -19,4 +21,9 @@ try:
19
21
  try a except (ValueError() | StopIteration())
20
22
  assert False
21
23
  except:
22
- None
24
+ None
25
+
26
+ assert_eq(try 1 matches Ok(1), True)
27
+
28
+ assert_eq((try 5)?.($+1)?(), Ok(6))
29
+ assert_eq((try x)?.($+1)?() matches Err(), True)
@@ -30,7 +30,7 @@ impl<T> IntoIndirect<T> for T {
30
30
  }
31
31
  }
32
32
 
33
- #[derive(Debug, Copy, Clone)]
33
+ #[derive(Debug, Copy, Clone, PartialEq, Eq)]
34
34
  pub enum BinaryOp {
35
35
  Add,
36
36
  Sub,