koatl 0.1.24__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 (116) hide show
  1. {koatl-0.1.24 → koatl-0.1.25}/Cargo.lock +1 -1
  2. {koatl-0.1.24 → koatl-0.1.25}/PKG-INFO +1 -1
  3. {koatl-0.1.24 → koatl-0.1.25}/koatl/Cargo.toml +1 -1
  4. {koatl-0.1.24 → koatl-0.1.25}/koatl/python/koatl/prelude/functional/memo.tl +18 -6
  5. {koatl-0.1.24 → koatl-0.1.25/koatl}/python/koatl/prelude/functional/monad.tl +2 -1
  6. {koatl-0.1.24 → koatl-0.1.25/koatl}/python/koatl/prelude/functional/result.tl +43 -27
  7. {koatl-0.1.24 → koatl-0.1.25/koatl}/python/koatl/prelude/iterable.tl +2 -2
  8. {koatl-0.1.24 → koatl-0.1.25/koatl}/python/koatl/runtime/__init__.py +16 -7
  9. {koatl-0.1.24 → koatl-0.1.25}/koatl/python/koatl/runtime/helpers.py +9 -6
  10. {koatl-0.1.24 → koatl-0.1.25}/koatl/tests/e2e/base/coal.tl +0 -12
  11. koatl-0.1.25/koatl/tests/e2e/base/loops.tl +25 -0
  12. {koatl-0.1.24 → koatl-0.1.25}/koatl/tests/e2e/base/match.tl +0 -2
  13. koatl-0.1.25/koatl/tests/e2e/base/short_circuit.tl +42 -0
  14. {koatl-0.1.24 → koatl-0.1.25}/koatl/tests/e2e/prelude/memo.tl +1 -1
  15. {koatl-0.1.24/koatl/tests/e2e/base → koatl-0.1.25/koatl/tests/e2e/prelude}/try.tl +11 -4
  16. {koatl-0.1.24 → koatl-0.1.25}/koatl-core/parser/src/ast.rs +1 -1
  17. {koatl-0.1.24 → koatl-0.1.25}/koatl-core/src/resolve_scopes.rs +124 -29
  18. {koatl-0.1.24 → koatl-0.1.25}/koatl-core/src/transform.rs +203 -138
  19. {koatl-0.1.24 → koatl-0.1.25}/python/koatl/prelude/functional/memo.tl +18 -6
  20. {koatl-0.1.24/koatl → koatl-0.1.25}/python/koatl/prelude/functional/monad.tl +2 -1
  21. {koatl-0.1.24/koatl → koatl-0.1.25}/python/koatl/prelude/functional/result.tl +43 -27
  22. {koatl-0.1.24/koatl → koatl-0.1.25}/python/koatl/prelude/iterable.tl +2 -2
  23. {koatl-0.1.24/koatl → koatl-0.1.25}/python/koatl/runtime/__init__.py +16 -7
  24. {koatl-0.1.24 → koatl-0.1.25}/python/koatl/runtime/helpers.py +9 -6
  25. koatl-0.1.24/koatl/tests/e2e/base/loops.tl +0 -14
  26. {koatl-0.1.24 → koatl-0.1.25}/Cargo.toml +0 -0
  27. {koatl-0.1.24 → koatl-0.1.25}/README.md +0 -0
  28. {koatl-0.1.24 → koatl-0.1.25}/koatl/.github/workflows/CI.yml +0 -0
  29. {koatl-0.1.24 → koatl-0.1.25}/koatl/.gitignore +0 -0
  30. {koatl-0.1.24 → koatl-0.1.25}/koatl/LICENSE +0 -0
  31. {koatl-0.1.24 → koatl-0.1.25}/koatl/README.md +0 -0
  32. {koatl-0.1.24 → koatl-0.1.25}/koatl/python/koatl/__init__.py +0 -0
  33. {koatl-0.1.24 → koatl-0.1.25}/koatl/python/koatl/__main__.py +0 -0
  34. {koatl-0.1.24 → koatl-0.1.25}/koatl/python/koatl/cli.py +0 -0
  35. {koatl-0.1.24 → koatl-0.1.25}/koatl/python/koatl/notebook/__init__.py +0 -0
  36. {koatl-0.1.24 → koatl-0.1.25}/koatl/python/koatl/notebook/magic.py +0 -0
  37. {koatl-0.1.24 → koatl-0.1.25}/koatl/python/koatl/prelude/__init__.tl +0 -0
  38. {koatl-0.1.24 → koatl-0.1.25}/koatl/python/koatl/prelude/functional/__init__.tl +0 -0
  39. {koatl-0.1.24 → koatl-0.1.25}/koatl/python/koatl/prelude/functional/async.tl +0 -0
  40. {koatl-0.1.24 → koatl-0.1.25}/koatl/python/koatl/prelude/functional/async_util.py +0 -0
  41. {koatl-0.1.24 → koatl-0.1.25}/koatl/python/koatl/prelude/functional/list.tl +0 -0
  42. {koatl-0.1.24 → koatl-0.1.25}/koatl/python/koatl/prelude/functional/reader.tl +0 -0
  43. {koatl-0.1.24 → koatl-0.1.25}/koatl/python/koatl/runtime/meta_finder.py +0 -0
  44. {koatl-0.1.24 → koatl-0.1.25}/koatl/python/koatl/runtime/record.py +0 -0
  45. {koatl-0.1.24 → koatl-0.1.25}/koatl/python/koatl/runtime/virtual.py +0 -0
  46. {koatl-0.1.24 → koatl-0.1.25}/koatl/requirements.txt +0 -0
  47. {koatl-0.1.24 → koatl-0.1.25}/koatl/src/emit_py.rs +0 -0
  48. {koatl-0.1.24 → koatl-0.1.25}/koatl/src/lib.rs +0 -0
  49. {koatl-0.1.24 → koatl-0.1.25}/koatl/tests/e2e/base/containers.tl +0 -0
  50. {koatl-0.1.24 → koatl-0.1.25}/koatl/tests/e2e/base/decorators.tl +0 -0
  51. {koatl-0.1.24 → koatl-0.1.25}/koatl/tests/e2e/base/destructure-for-and-fn.tl +0 -0
  52. {koatl-0.1.24 → koatl-0.1.25}/koatl/tests/e2e/base/destructure.tl +0 -0
  53. {koatl-0.1.24 → koatl-0.1.25}/koatl/tests/e2e/base/escape_ident.tl +0 -0
  54. {koatl-0.1.24 → koatl-0.1.25}/koatl/tests/e2e/base/fstr.tl +0 -0
  55. {koatl-0.1.24 → koatl-0.1.25}/koatl/tests/e2e/base/functions.tl +0 -0
  56. {koatl-0.1.24 → koatl-0.1.25}/koatl/tests/e2e/base/generator.tl +0 -0
  57. {koatl-0.1.24 → koatl-0.1.25}/koatl/tests/e2e/base/if_expr.tl +0 -0
  58. {koatl-0.1.24 → koatl-0.1.25}/koatl/tests/e2e/base/imports.tl +0 -0
  59. {koatl-0.1.24 → koatl-0.1.25}/koatl/tests/e2e/base/nary-list.tl +0 -0
  60. {koatl-0.1.24 → koatl-0.1.25}/koatl/tests/e2e/base/placeholder.tl +0 -0
  61. {koatl-0.1.24 → koatl-0.1.25}/koatl/tests/e2e/base/precedence.tl +0 -0
  62. {koatl-0.1.24 → koatl-0.1.25}/koatl/tests/e2e/base/scopes.tl +0 -0
  63. {koatl-0.1.24 → koatl-0.1.25}/koatl/tests/e2e/base/semantic_whitespace.tl +0 -0
  64. {koatl-0.1.24 → koatl-0.1.25}/koatl/tests/e2e/destructure.tl +0 -0
  65. {koatl-0.1.24 → koatl-0.1.25}/koatl/tests/e2e/prelude/async.tl +0 -0
  66. {koatl-0.1.24 → koatl-0.1.25}/koatl/tests/e2e/prelude/iterables.tl +0 -0
  67. {koatl-0.1.24 → koatl-0.1.25}/koatl/tests/e2e/prelude/list.tl +0 -0
  68. {koatl-0.1.24 → koatl-0.1.25}/koatl/tests/e2e/prelude/reader.tl +0 -0
  69. {koatl-0.1.24 → koatl-0.1.25}/koatl/tests/e2e/prelude/result.tl +0 -0
  70. {koatl-0.1.24/koatl/tests/e2e/base → koatl-0.1.25/koatl/tests/e2e/prelude}/slice.tl +0 -0
  71. {koatl-0.1.24 → koatl-0.1.25}/koatl/tests/e2e/prelude/virtual.tl +0 -0
  72. {koatl-0.1.24 → koatl-0.1.25}/koatl/tests/e2e/util/__init__.py +0 -0
  73. {koatl-0.1.24 → koatl-0.1.25}/koatl/tests/e2e/util/module0.tl +0 -0
  74. {koatl-0.1.24 → koatl-0.1.25}/koatl/tests/e2e/util/module1.tl +0 -0
  75. {koatl-0.1.24 → koatl-0.1.25}/koatl/tests/e2e/util/module2.tl +0 -0
  76. {koatl-0.1.24 → koatl-0.1.25}/koatl/tests/parse/arith.tl +0 -0
  77. {koatl-0.1.24 → koatl-0.1.25}/koatl/tests/parse/assign.tl +0 -0
  78. {koatl-0.1.24 → koatl-0.1.25}/koatl/tests/parse/block-comments.tl +0 -0
  79. {koatl-0.1.24 → koatl-0.1.25}/koatl/tests/parse/deco.tl +0 -0
  80. {koatl-0.1.24 → koatl-0.1.25}/koatl/tests/parse/func.tl +0 -0
  81. {koatl-0.1.24 → koatl-0.1.25}/koatl/tests/parse/matches.tl +0 -0
  82. {koatl-0.1.24 → koatl-0.1.25}/koatl/tests/test_e2e.py +0 -0
  83. {koatl-0.1.24 → koatl-0.1.25}/koatl/tests/test_parse.py +0 -0
  84. {koatl-0.1.24 → koatl-0.1.25}/koatl-core/Cargo.toml +0 -0
  85. {koatl-0.1.24 → koatl-0.1.25}/koatl-core/parser/Cargo.toml +0 -0
  86. {koatl-0.1.24 → koatl-0.1.25}/koatl-core/parser/src/lexer.rs +0 -0
  87. {koatl-0.1.24 → koatl-0.1.25}/koatl-core/parser/src/lib.rs +0 -0
  88. {koatl-0.1.24 → koatl-0.1.25}/koatl-core/parser/src/parser.rs +0 -0
  89. {koatl-0.1.24 → koatl-0.1.25}/koatl-core/parser/src/util.rs +0 -0
  90. {koatl-0.1.24 → koatl-0.1.25}/koatl-core/parser/tests/lexer.rs +0 -0
  91. {koatl-0.1.24 → koatl-0.1.25}/koatl-core/src/inference.rs +0 -0
  92. {koatl-0.1.24 → koatl-0.1.25}/koatl-core/src/lib.rs +0 -0
  93. {koatl-0.1.24 → koatl-0.1.25}/koatl-core/src/main.rs +0 -0
  94. {koatl-0.1.24 → koatl-0.1.25}/koatl-core/src/parse_timer.rs +0 -0
  95. {koatl-0.1.24 → koatl-0.1.25}/koatl-core/src/parser.rs +0 -0
  96. {koatl-0.1.24 → koatl-0.1.25}/koatl-core/src/py/ast.rs +0 -0
  97. {koatl-0.1.24 → koatl-0.1.25}/koatl-core/src/py/emit.rs +0 -0
  98. {koatl-0.1.24 → koatl-0.1.25}/koatl-core/src/py/mod.rs +0 -0
  99. {koatl-0.1.24 → koatl-0.1.25}/koatl-core/src/py/util.rs +0 -0
  100. {koatl-0.1.24 → koatl-0.1.25}/koatl-core/src/types.rs +0 -0
  101. {koatl-0.1.24 → koatl-0.1.25}/koatl-core/src/util.rs +0 -0
  102. {koatl-0.1.24 → koatl-0.1.25}/pyproject.toml +0 -0
  103. {koatl-0.1.24 → koatl-0.1.25}/python/koatl/__init__.py +0 -0
  104. {koatl-0.1.24 → koatl-0.1.25}/python/koatl/__main__.py +0 -0
  105. {koatl-0.1.24 → koatl-0.1.25}/python/koatl/cli.py +0 -0
  106. {koatl-0.1.24 → koatl-0.1.25}/python/koatl/notebook/__init__.py +0 -0
  107. {koatl-0.1.24 → koatl-0.1.25}/python/koatl/notebook/magic.py +0 -0
  108. {koatl-0.1.24 → koatl-0.1.25}/python/koatl/prelude/__init__.tl +0 -0
  109. {koatl-0.1.24 → koatl-0.1.25}/python/koatl/prelude/functional/__init__.tl +0 -0
  110. {koatl-0.1.24 → koatl-0.1.25}/python/koatl/prelude/functional/async.tl +0 -0
  111. {koatl-0.1.24 → koatl-0.1.25}/python/koatl/prelude/functional/async_util.py +0 -0
  112. {koatl-0.1.24 → koatl-0.1.25}/python/koatl/prelude/functional/list.tl +0 -0
  113. {koatl-0.1.24 → koatl-0.1.25}/python/koatl/prelude/functional/reader.tl +0 -0
  114. {koatl-0.1.24 → koatl-0.1.25}/python/koatl/runtime/meta_finder.py +0 -0
  115. {koatl-0.1.24 → koatl-0.1.25}/python/koatl/runtime/record.py +0 -0
  116. {koatl-0.1.24 → 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.24"
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.24
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.24"
3
+ version = "0.1.25"
4
4
  edition = "2021"
5
5
 
6
6
  [lib]
@@ -22,6 +22,17 @@ export Memo = class(Monad):
22
22
 
23
23
  __repr__ = self => f"Memo(...)"
24
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
+
25
36
  value = staticmethod& (id, deps, f) =>
26
37
  Memo(ctx =>
27
38
  if ctx.try_get(id, tuple(deps)) matches Ok(value):
@@ -40,7 +51,7 @@ export Memo = class(Monad):
40
51
 
41
52
  let v = f(*args, **kwargs)
42
53
  if v matches Memo():
43
- v = v.run(ctx)
54
+ v = v.f(ctx)
44
55
 
45
56
  ctx.update(id, deps, v)
46
57
  )
@@ -53,21 +64,22 @@ export Memo = class(Monad):
53
64
  pure = staticmethod& value => Memo(ctx => value)
54
65
 
55
66
  bind_once = (self, f) => Memo(ctx =>
56
- let value = f(self.run(ctx))
67
+ let value = f(self.f(ctx))
57
68
  if value matches Memo():
58
- value = value.run(ctx)
69
+ value = value.f(ctx)
59
70
  value
60
71
  )
61
72
 
62
73
  bind_gen = (self, gen) => Memo(ctx =>
63
- self = self.run(ctx)
74
+ self = self.f(ctx)
64
75
  try:
65
76
  while True:
66
77
  self = gen.send(self)
67
78
  if self matches Memo():
68
- self = self.run(ctx)
79
+ self = self.f(ctx)
69
80
  except StopIteration(value=value):
70
81
  return value
71
82
  )
72
83
 
73
- __tl__.memo = Memo.value
84
+ __tl__.memo_value = Memo.value
85
+ __tl__.bind_memo_value = Memo.bind_value
@@ -1,5 +1,6 @@
1
1
  import abc
2
2
 
3
+ # TODO: this should be really called MonadOnce.
3
4
  export Monad = class(abc.ABC):
4
5
  __slots__ = ()
5
6
 
@@ -13,7 +14,7 @@ export Monad = class(abc.ABC):
13
14
 
14
15
  # An optional, optimized implementation of `bind` that skips deep recursion.
15
16
  # TODO: can this be automatically generated?
16
- bind_gen = (self, gen) => raise NotImplementedError()
17
+ # bind_gen = (self, gen) => ...
17
18
 
18
19
  # Automatically generated implementations.
19
20
  map = (self, f) => self.bind(x => self.pure(f(x)))
@@ -14,24 +14,15 @@ export Result = class:
14
14
  __init__ = (self, *args, **kwargs) =>
15
15
  raise ValueError("Result should not be instantiated directly, use Ok or Err")
16
16
 
17
- raw = self =>
17
+ checked = staticmethod& f =>
18
18
  """
19
- Unwraps the value from a Result, making it look like
20
- a non-monadic Python value.
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
21
  """
22
- if self matches not Result():
23
- return self
24
-
25
- if self.ok:
26
- if self.value.ok:
27
- return self.value
28
- else:
29
- return self
30
- else:
31
- if self.value.ok:
32
- return self
33
- else:
34
- return self.value
22
+ try:
23
+ return Ok(f())
24
+ except BaseException() as e:
25
+ return Err(e)
35
26
 
36
27
  bind_once = (self, f) =>
37
28
  self = Result(self)
@@ -96,8 +87,14 @@ export Ok = class(Result):
96
87
  return None
97
88
  self.value = value
98
89
 
90
+ __eq__ = (self, other) =>
91
+ if not isinstance(other, Ok):
92
+ return False
93
+ return self.value == other.value
94
+
99
95
  __repr__ = self => f"Ok({repr(self.value)})"
100
96
  unwrap = self => self.value
97
+ coalesce = (self, f) => self.value
101
98
 
102
99
  export Err = class(Result):
103
100
  __slots__ = ("value",)
@@ -108,24 +105,43 @@ export Err = class(Result):
108
105
  return None
109
106
  self.value = value
110
107
 
108
+ __eq__ = (self, other) =>
109
+ if not isinstance(other, Err):
110
+ return False
111
+ return self.value == other.value
112
+
111
113
  __repr__ = self => f"Err({repr(self.value)})"
112
114
  unwrap = self =>
113
115
  if self.value matches BaseException():
114
116
  raise self.value
115
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)
116
129
 
130
+ __tl__.Ok = Ok
131
+ __tl__.Err = Err
117
132
 
118
- # Provide a default implementation for *most* Result methods
119
- # ...we shouldn't provide a default for `map` since that
120
- # would prevent virtual trait lookup. TODO: fix this?
121
- for name, method in Result.__dict__:
122
- if name.startswith("_"):
123
- continue
124
- if name == "map":
125
- continue
133
+ __tl__.op_coal = (x, f) =>
134
+ if x matches Result():
135
+ return x.coalesce(f)
126
136
 
127
- let make_closure = method => (self, *args, **kwargs) => Result.raw(method(self, *args, **kwargs))
137
+ if not x.ok:
138
+ return f()
139
+ return x
128
140
 
129
- register_global_attr(object, name, make_closure(method))
141
+ __tl__.op_map = (x, f) =>
142
+ if x matches Result():
143
+ return x.map(f)
130
144
 
131
- register_global_attr(object, "ok", ExtensionProperty(__tl__.ok))
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
 
@@ -66,6 +66,10 @@ def do(f):
66
66
  try:
67
67
  m = gen.send(None)
68
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
69
73
  return e.value
70
74
 
71
75
  def recurse(v):
@@ -74,17 +78,16 @@ def do(f):
74
78
  m = gen.send(v)
75
79
  return vget(m, "bind_once")(recurse)
76
80
  except StopIteration as e:
77
- try:
78
- return vget(m, "pure")(e.value)
79
- except AttributeError:
80
- return e.value
81
+ return vget(m, "pure")(e.value)
81
82
 
82
83
  try:
83
84
  # TODO: this is a workaround to avoid recursion.
84
85
  # is it possible to derive bind_gen directly from bind_once?
85
86
 
86
- return vget(m, "bind_gen")(gen)
87
- except (NotImplementedError, AttributeError):
87
+ bind_gen = vget(m, "bind_gen")
88
+ except AttributeError:
88
89
  return vget(m, "bind_once")(recurse)
89
90
 
91
+ return bind_gen(gen)
92
+
90
93
  return impl
@@ -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,
@@ -36,9 +36,20 @@ pub struct ResolveState<'src> {
36
36
  pub export_stars: Vec<SIdent<'src>>,
37
37
 
38
38
  pub resolutions: HashMap<RefHash, DeclarationKey>,
39
- pub functions: HashMap<RefHash, FnInfo>,
40
39
  pub patterns: HashMap<RefHash, PatternInfo>,
41
- pub memo_captures: HashMap<RefHash, FnInfo>,
40
+
41
+ // TODO: these should all be collapsed into the same thing...
42
+ pub functions: HashMap<RefHash, FnInfo>,
43
+ pub memo_fninfo: HashMap<RefHash, FnInfo>,
44
+ // ...but before that, mapped_fninfo needs to be fixed.
45
+ // CallItems and ListItems nodes inside MappedCall and MappedSubscript
46
+ // are not boxed, so I've been pointing to the upper-level MappedX nodes
47
+ // instead, but that will cause a conflict with memo_fninfo, specifically
48
+ // "memo a?.x" will try to use the same refhash for both.
49
+ // So, is it safe to just point to the reference of the bare CallItem/ListItem?
50
+ // Or do we just box it?
51
+ pub mapped_fninfo: HashMap<RefHash, FnInfo>,
52
+ pub coal_fninfo: HashMap<RefHash, FnInfo>,
42
53
 
43
54
  pub declarations: SlotMap<DeclarationKey, Declaration<'src>>,
44
55
  pub scopes: SlotMap<ScopeKey, Scope>,
@@ -88,7 +99,9 @@ impl<'src> ResolveState<'src> {
88
99
  resolutions: HashMap::new(),
89
100
  functions: HashMap::new(),
90
101
  patterns: HashMap::new(),
91
- memo_captures: HashMap::new(),
102
+ memo_fninfo: HashMap::new(),
103
+ mapped_fninfo: HashMap::new(),
104
+ coal_fninfo: HashMap::new(),
92
105
 
93
106
  declarations: SlotMap::with_key(),
94
107
  scopes,
@@ -388,6 +401,7 @@ pub struct FnInfo {
388
401
  pub is_generator: bool,
389
402
  pub is_placeholder: bool,
390
403
  pub is_memo: bool,
404
+ pub is_mapped_rhs: bool,
391
405
 
392
406
  pub arg_names: Vec<DeclarationKey>,
393
407
  pub captures: HashSet<DeclarationKey>,
@@ -401,6 +415,7 @@ impl FnInfo {
401
415
  is_generator: false,
402
416
  is_placeholder: false,
403
417
  is_memo: false,
418
+ is_mapped_rhs: false,
404
419
  arg_names: Vec::new(),
405
420
  captures: HashSet::new(),
406
421
  }
@@ -836,6 +851,31 @@ fn pattern_scoped<'src>(
836
851
  (pattern, scope_key, meta)
837
852
  }
838
853
 
854
+ fn with_phantom_fninfo<'src, F, O>(state: &mut ResolveState<'src>, span: Span, f: F) -> (O, FnInfo)
855
+ where
856
+ F: FnOnce(&mut ResolveState<'src>) -> O,
857
+ {
858
+ let fn_ctx = FnInfo::new();
859
+
860
+ state.fn_stack.push(fn_ctx);
861
+
862
+ let ret = f(state);
863
+
864
+ let fn_ctx = state.fn_stack.pop().unwrap();
865
+
866
+ if fn_ctx.is_async {
867
+ state.set_async(span);
868
+ }
869
+ if fn_ctx.is_generator {
870
+ state.set_generator(span);
871
+ }
872
+ if fn_ctx.is_do {
873
+ state.set_do(span);
874
+ }
875
+
876
+ (ret, fn_ctx)
877
+ }
878
+
839
879
  trait SExprExt<'src> {
840
880
  fn traverse(self, state: &mut ResolveState<'src>) -> Indirect<SExpr<'src>>;
841
881
  fn traverse_expecting_scope(self, state: &mut ResolveState<'src>) -> Indirect<SExpr<'src>>;
@@ -1239,42 +1279,99 @@ impl<'src> SExprExt<'src> for Indirect<SExpr<'src>> {
1239
1279
  ),
1240
1280
 
1241
1281
  // special case for pipe
1242
- Expr::Binary(binary_op, mut x, mut y) => {
1243
- if let BinaryOp::Pipe = binary_op {
1244
- x = x.traverse_guarded(state);
1245
- y = y.traverse_guarded(state);
1246
- } else {
1247
- x = x.traverse(state);
1248
- y = y.traverse(state);
1249
- }
1282
+ Expr::Binary(binary_op, x, y) => {
1283
+ let (x, y) = match binary_op {
1284
+ BinaryOp::Pipe => (x.traverse_guarded(state), y.traverse_guarded(state)),
1285
+ BinaryOp::Coalesce => {
1286
+ let (y, fn_ctx) =
1287
+ with_phantom_fninfo(state, span, |state| y.traverse(state));
1288
+
1289
+ state.coal_fninfo.insert(y.as_ref().into(), fn_ctx);
1290
+
1291
+ (x.traverse(state), y)
1292
+ }
1293
+ _ => (x.traverse(state), y.traverse(state)),
1294
+ };
1250
1295
 
1251
1296
  Expr::Binary(binary_op, x, y)
1252
1297
  }
1253
1298
 
1254
1299
  // postfix
1255
1300
  Expr::Attribute(expr, attr) => Expr::Attribute(expr.traverse(state), attr.clone()),
1256
- Expr::MappedAttribute(expr, attr) => Expr::MappedAttribute(expr.traverse(state), attr),
1301
+ Expr::MappedAttribute(expr, attr) => {
1302
+ let traversed = Expr::MappedAttribute(expr.traverse(state), attr)
1303
+ .spanned(span)
1304
+ .indirect();
1305
+
1306
+ state
1307
+ .mapped_fninfo
1308
+ .insert(traversed.as_ref().into(), FnInfo::new());
1309
+
1310
+ return traversed;
1311
+ }
1257
1312
  Expr::RawAttribute(expr, attr) => Expr::RawAttribute(expr.traverse(state), attr),
1258
1313
  Expr::MappedRawAttribute(expr, spanned) => {
1259
- Expr::MappedRawAttribute(expr.traverse(state), spanned)
1314
+ let traversed = Expr::MappedRawAttribute(expr.traverse(state), spanned)
1315
+ .spanned(span)
1316
+ .indirect();
1317
+
1318
+ state
1319
+ .mapped_fninfo
1320
+ .insert(traversed.as_ref().into(), FnInfo::new());
1321
+
1322
+ return traversed;
1260
1323
  }
1261
1324
  Expr::ScopedAttribute(expr, value) => {
1262
1325
  Expr::ScopedAttribute(expr.traverse(state), value.traverse_guarded(state))
1263
1326
  }
1264
1327
  Expr::MappedScopedAttribute(expr, value) => {
1265
- Expr::MappedScopedAttribute(expr.traverse(state), value.traverse_guarded(state))
1328
+ let (rhs, fn_ctx) =
1329
+ with_phantom_fninfo(state, span, |state| value.traverse_guarded(state));
1330
+
1331
+ let traversed = Expr::MappedScopedAttribute(expr.traverse(state), rhs)
1332
+ .spanned(span)
1333
+ .indirect();
1334
+
1335
+ state
1336
+ .mapped_fninfo
1337
+ .insert(traversed.as_ref().into(), fn_ctx);
1338
+
1339
+ return traversed;
1266
1340
  }
1267
1341
  Expr::Call(a, items) => {
1268
1342
  Expr::Call(a.traverse(state), traverse_call_items(state, items))
1269
1343
  }
1270
1344
  Expr::MappedCall(a, call_items) => {
1271
- Expr::MappedCall(a.traverse(state), traverse_call_items(state, call_items))
1345
+ let (rhs, fn_ctx) = with_phantom_fninfo(state, span, |state| {
1346
+ traverse_call_items(state, call_items)
1347
+ });
1348
+
1349
+ let traversed = Expr::MappedCall(a.traverse(state), rhs)
1350
+ .spanned(span)
1351
+ .indirect();
1352
+
1353
+ state
1354
+ .mapped_fninfo
1355
+ .insert(traversed.as_ref().into(), fn_ctx);
1356
+
1357
+ return traversed;
1272
1358
  }
1273
1359
  Expr::Subscript(x, list_items) => {
1274
1360
  Expr::Subscript(x.traverse(state), traverse_list_items(state, list_items))
1275
1361
  }
1276
1362
  Expr::MappedSubscript(expr, list_items) => {
1277
- Expr::MappedSubscript(expr.traverse(state), traverse_list_items(state, list_items))
1363
+ let (rhs, fn_ctx) = with_phantom_fninfo(state, span, |state| {
1364
+ traverse_list_items(state, list_items)
1365
+ });
1366
+ let traversed = Expr::MappedSubscript(expr.traverse(state), rhs)
1367
+ .spanned(span)
1368
+ .indirect();
1369
+
1370
+ state
1371
+ .mapped_fninfo
1372
+ .insert(traversed.as_ref().into(), fn_ctx);
1373
+
1374
+ return traversed;
1278
1375
  }
1279
1376
  Expr::Tuple(items) => Expr::Tuple(traverse_list_items(state, items)),
1280
1377
  Expr::List(items) => Expr::List(traverse_list_items(state, items)),
@@ -1305,29 +1402,27 @@ impl<'src> SExprExt<'src> for Indirect<SExpr<'src>> {
1305
1402
  Expr::Memo(inner) => {
1306
1403
  state.set_do(span);
1307
1404
 
1308
- let mut fn_ctx = FnInfo::new();
1309
- fn_ctx.is_memo = true;
1310
-
1311
1405
  let mut scope = Scope::new(state.top_scope_key());
1312
- scope.is_fn = true;
1313
-
1314
1406
  // TODO it's confusing that we need to
1315
1407
  // set scope.is_fn = true all the time in order
1316
1408
  // for captures to be found properly.
1317
1409
  // is there any better way?
1318
-
1410
+ scope.is_fn = true;
1319
1411
  let scope = state.scopes.insert(scope);
1320
- state.fn_stack.push(fn_ctx);
1321
1412
 
1322
- let scoped = state.scoped(scope, |state| {
1323
- state.placeholder_guarded(span, |state| inner.traverse_expecting_scope(state))
1413
+ let (inner, mut fn_ctx) = with_phantom_fninfo(state, span, |state| {
1414
+ state
1415
+ .scoped(scope, |state| {
1416
+ state.placeholder_guarded(span, |state| {
1417
+ inner.traverse_expecting_scope(state)
1418
+ })
1419
+ })
1420
+ .value
1324
1421
  });
1325
1422
 
1326
- let inner = scoped.value;
1327
-
1328
- let fn_ctx = state.fn_stack.pop().unwrap();
1423
+ fn_ctx.is_memo = true;
1329
1424
 
1330
- state.memo_captures.insert(inner.as_ref().into(), fn_ctx);
1425
+ state.memo_fninfo.insert(inner.as_ref().into(), fn_ctx);
1331
1426
 
1332
1427
  Expr::Memo(inner)
1333
1428
  }