koatl 0.1.33__tar.gz → 0.1.35__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 (121) hide show
  1. {koatl-0.1.33 → koatl-0.1.35}/Cargo.lock +1 -1
  2. {koatl-0.1.33 → koatl-0.1.35}/PKG-INFO +1 -1
  3. {koatl-0.1.33 → koatl-0.1.35}/koatl/Cargo.toml +1 -1
  4. {koatl-0.1.33 → koatl-0.1.35}/koatl/python/koatl/prelude/functional/algebra.tl +4 -4
  5. {koatl-0.1.33 → koatl-0.1.35/koatl}/python/koatl/prelude/functional/result.tl +1 -1
  6. {koatl-0.1.33 → koatl-0.1.35/koatl}/python/koatl/prelude/iterable.tl +64 -14
  7. {koatl-0.1.33 → koatl-0.1.35}/koatl/python/koatl/runtime/classes.py +5 -4
  8. {koatl-0.1.33 → koatl-0.1.35/koatl}/python/koatl/runtime/record.py +10 -0
  9. {koatl-0.1.33 → koatl-0.1.35}/koatl/python/koatl/runtime/virtual.py +1 -1
  10. {koatl-0.1.33 → koatl-0.1.35}/koatl/src/emit_py.rs +3 -0
  11. {koatl-0.1.33 → koatl-0.1.35}/koatl/tests/e2e/base/coal.tl +1 -1
  12. {koatl-0.1.33 → koatl-0.1.35}/koatl/tests/e2e/prelude/try.tl +2 -2
  13. {koatl-0.1.33 → koatl-0.1.35}/koatl/tests/e2e/prelude/virtual.tl +1 -1
  14. {koatl-0.1.33 → koatl-0.1.35}/koatl-core/parser/src/ast.rs +9 -5
  15. {koatl-0.1.33 → koatl-0.1.35}/koatl-core/parser/src/lexer.rs +40 -18
  16. {koatl-0.1.33 → koatl-0.1.35}/koatl-core/parser/src/parser.rs +37 -23
  17. {koatl-0.1.33 → koatl-0.1.35}/koatl-core/src/py/ast.rs +4 -1
  18. {koatl-0.1.33 → koatl-0.1.35}/koatl-core/src/py/emit.rs +43 -18
  19. {koatl-0.1.33 → koatl-0.1.35}/koatl-core/src/py/util.rs +1 -1
  20. {koatl-0.1.33 → koatl-0.1.35}/koatl-core/src/resolve_scopes.rs +1 -1
  21. {koatl-0.1.33 → koatl-0.1.35}/koatl-core/src/transform.rs +10 -6
  22. {koatl-0.1.33 → koatl-0.1.35}/python/koatl/prelude/functional/algebra.tl +4 -4
  23. {koatl-0.1.33/koatl → koatl-0.1.35}/python/koatl/prelude/functional/result.tl +1 -1
  24. {koatl-0.1.33/koatl → koatl-0.1.35}/python/koatl/prelude/iterable.tl +64 -14
  25. {koatl-0.1.33 → koatl-0.1.35}/python/koatl/runtime/classes.py +5 -4
  26. {koatl-0.1.33/koatl → koatl-0.1.35}/python/koatl/runtime/record.py +10 -0
  27. {koatl-0.1.33 → koatl-0.1.35}/python/koatl/runtime/virtual.py +1 -1
  28. {koatl-0.1.33 → koatl-0.1.35}/Cargo.toml +0 -0
  29. {koatl-0.1.33 → koatl-0.1.35}/README.md +0 -0
  30. {koatl-0.1.33 → koatl-0.1.35}/koatl/.github/workflows/CI.yml +0 -0
  31. {koatl-0.1.33 → koatl-0.1.35}/koatl/.gitignore +0 -0
  32. {koatl-0.1.33 → koatl-0.1.35}/koatl/LICENSE +0 -0
  33. {koatl-0.1.33 → koatl-0.1.35}/koatl/README.md +0 -0
  34. {koatl-0.1.33 → koatl-0.1.35}/koatl/python/koatl/__init__.py +0 -0
  35. {koatl-0.1.33 → koatl-0.1.35}/koatl/python/koatl/__main__.py +0 -0
  36. {koatl-0.1.33 → koatl-0.1.35}/koatl/python/koatl/cli.py +0 -0
  37. {koatl-0.1.33 → koatl-0.1.35}/koatl/python/koatl/notebook/__init__.py +0 -0
  38. {koatl-0.1.33 → koatl-0.1.35}/koatl/python/koatl/notebook/magic.py +0 -0
  39. {koatl-0.1.33 → koatl-0.1.35}/koatl/python/koatl/prelude/__init__.tl +0 -0
  40. {koatl-0.1.33 → koatl-0.1.35}/koatl/python/koatl/prelude/functional/__init__.tl +0 -0
  41. {koatl-0.1.33 → koatl-0.1.35}/koatl/python/koatl/prelude/functional/async.tl +0 -0
  42. {koatl-0.1.33 → koatl-0.1.35}/koatl/python/koatl/prelude/functional/env.tl +0 -0
  43. {koatl-0.1.33 → koatl-0.1.35}/koatl/python/koatl/prelude/functional/list.tl +0 -0
  44. {koatl-0.1.33 → koatl-0.1.35}/koatl/python/koatl/prelude/functional/memo.tl +0 -0
  45. {koatl-0.1.33 → koatl-0.1.35}/koatl/python/koatl/prelude/io.tl +0 -0
  46. {koatl-0.1.33 → koatl-0.1.35}/koatl/python/koatl/prelude/regex.tl +0 -0
  47. {koatl-0.1.33 → koatl-0.1.35}/koatl/python/koatl/runtime/__init__.py +0 -0
  48. {koatl-0.1.33 → koatl-0.1.35}/koatl/python/koatl/runtime/helpers.py +0 -0
  49. {koatl-0.1.33 → koatl-0.1.35}/koatl/python/koatl/runtime/meta_finder.py +0 -0
  50. {koatl-0.1.33 → koatl-0.1.35}/koatl/requirements.txt +0 -0
  51. {koatl-0.1.33 → koatl-0.1.35}/koatl/src/lib.rs +0 -0
  52. {koatl-0.1.33 → koatl-0.1.35}/koatl/tests/e2e/base/containers.tl +0 -0
  53. {koatl-0.1.33 → koatl-0.1.35}/koatl/tests/e2e/base/data.txt +0 -0
  54. {koatl-0.1.33 → koatl-0.1.35}/koatl/tests/e2e/base/decorators.tl +0 -0
  55. {koatl-0.1.33 → koatl-0.1.35}/koatl/tests/e2e/base/destructure-for-and-fn.tl +0 -0
  56. {koatl-0.1.33 → koatl-0.1.35}/koatl/tests/e2e/base/destructure.tl +0 -0
  57. {koatl-0.1.33 → koatl-0.1.35}/koatl/tests/e2e/base/escape_ident.tl +0 -0
  58. {koatl-0.1.33 → koatl-0.1.35}/koatl/tests/e2e/base/fstr.tl +0 -0
  59. {koatl-0.1.33 → koatl-0.1.35}/koatl/tests/e2e/base/functions.tl +0 -0
  60. {koatl-0.1.33 → koatl-0.1.35}/koatl/tests/e2e/base/generator.tl +0 -0
  61. {koatl-0.1.33 → koatl-0.1.35}/koatl/tests/e2e/base/if_expr.tl +0 -0
  62. {koatl-0.1.33 → koatl-0.1.35}/koatl/tests/e2e/base/imports.tl +0 -0
  63. {koatl-0.1.33 → koatl-0.1.35}/koatl/tests/e2e/base/loops.tl +0 -0
  64. {koatl-0.1.33 → koatl-0.1.35}/koatl/tests/e2e/base/match.tl +0 -0
  65. {koatl-0.1.33 → koatl-0.1.35}/koatl/tests/e2e/base/nary-list.tl +0 -0
  66. {koatl-0.1.33 → koatl-0.1.35}/koatl/tests/e2e/base/placeholder.tl +0 -0
  67. {koatl-0.1.33 → koatl-0.1.35}/koatl/tests/e2e/base/precedence.tl +0 -0
  68. {koatl-0.1.33 → koatl-0.1.35}/koatl/tests/e2e/base/record.tl +0 -0
  69. {koatl-0.1.33 → koatl-0.1.35}/koatl/tests/e2e/base/scopes.tl +0 -0
  70. {koatl-0.1.33 → koatl-0.1.35}/koatl/tests/e2e/base/semantic_whitespace.tl +0 -0
  71. {koatl-0.1.33 → koatl-0.1.35}/koatl/tests/e2e/base/short_circuit.tl +0 -0
  72. {koatl-0.1.33 → koatl-0.1.35}/koatl/tests/e2e/base/with.tl +0 -0
  73. {koatl-0.1.33 → koatl-0.1.35}/koatl/tests/e2e/prelude/async.tl +0 -0
  74. {koatl-0.1.33 → koatl-0.1.35}/koatl/tests/e2e/prelude/aug_assign.tl +0 -0
  75. {koatl-0.1.33 → koatl-0.1.35}/koatl/tests/e2e/prelude/env.tl +0 -0
  76. {koatl-0.1.33 → koatl-0.1.35}/koatl/tests/e2e/prelude/iterables.tl +0 -0
  77. {koatl-0.1.33 → koatl-0.1.35}/koatl/tests/e2e/prelude/list.tl +0 -0
  78. {koatl-0.1.33 → koatl-0.1.35}/koatl/tests/e2e/prelude/memo.tl +0 -0
  79. {koatl-0.1.33 → koatl-0.1.35}/koatl/tests/e2e/prelude/result.tl +0 -0
  80. {koatl-0.1.33 → koatl-0.1.35}/koatl/tests/e2e/prelude/slice.tl +0 -0
  81. {koatl-0.1.33 → koatl-0.1.35}/koatl/tests/e2e/util/__init__.py +0 -0
  82. {koatl-0.1.33 → koatl-0.1.35}/koatl/tests/e2e/util/module0.tl +0 -0
  83. {koatl-0.1.33 → koatl-0.1.35}/koatl/tests/e2e/util/module1.tl +0 -0
  84. {koatl-0.1.33 → koatl-0.1.35}/koatl/tests/e2e/util/module2.tl +0 -0
  85. {koatl-0.1.33 → koatl-0.1.35}/koatl/tests/parse/arith.tl +0 -0
  86. {koatl-0.1.33 → koatl-0.1.35}/koatl/tests/parse/assign.tl +0 -0
  87. {koatl-0.1.33 → koatl-0.1.35}/koatl/tests/parse/block-comments.tl +0 -0
  88. {koatl-0.1.33 → koatl-0.1.35}/koatl/tests/parse/deco.tl +0 -0
  89. {koatl-0.1.33 → koatl-0.1.35}/koatl/tests/parse/func.tl +0 -0
  90. {koatl-0.1.33 → koatl-0.1.35}/koatl/tests/parse/matches.tl +0 -0
  91. {koatl-0.1.33 → koatl-0.1.35}/koatl/tests/test_e2e.py +0 -0
  92. {koatl-0.1.33 → koatl-0.1.35}/koatl/tests/test_parse.py +0 -0
  93. {koatl-0.1.33 → koatl-0.1.35}/koatl-core/Cargo.toml +0 -0
  94. {koatl-0.1.33 → koatl-0.1.35}/koatl-core/parser/Cargo.toml +0 -0
  95. {koatl-0.1.33 → koatl-0.1.35}/koatl-core/parser/src/lib.rs +0 -0
  96. {koatl-0.1.33 → koatl-0.1.35}/koatl-core/parser/src/util.rs +0 -0
  97. {koatl-0.1.33 → koatl-0.1.35}/koatl-core/src/inference.rs +0 -0
  98. {koatl-0.1.33 → koatl-0.1.35}/koatl-core/src/lib.rs +0 -0
  99. {koatl-0.1.33 → koatl-0.1.35}/koatl-core/src/main.rs +0 -0
  100. {koatl-0.1.33 → koatl-0.1.35}/koatl-core/src/parse_timer.rs +0 -0
  101. {koatl-0.1.33 → koatl-0.1.35}/koatl-core/src/parser.rs +0 -0
  102. {koatl-0.1.33 → koatl-0.1.35}/koatl-core/src/py/mod.rs +0 -0
  103. {koatl-0.1.33 → koatl-0.1.35}/koatl-core/src/types.rs +0 -0
  104. {koatl-0.1.33 → koatl-0.1.35}/koatl-core/src/util.rs +0 -0
  105. {koatl-0.1.33 → koatl-0.1.35}/pyproject.toml +0 -0
  106. {koatl-0.1.33 → koatl-0.1.35}/python/koatl/__init__.py +0 -0
  107. {koatl-0.1.33 → koatl-0.1.35}/python/koatl/__main__.py +0 -0
  108. {koatl-0.1.33 → koatl-0.1.35}/python/koatl/cli.py +0 -0
  109. {koatl-0.1.33 → koatl-0.1.35}/python/koatl/notebook/__init__.py +0 -0
  110. {koatl-0.1.33 → koatl-0.1.35}/python/koatl/notebook/magic.py +0 -0
  111. {koatl-0.1.33 → koatl-0.1.35}/python/koatl/prelude/__init__.tl +0 -0
  112. {koatl-0.1.33 → koatl-0.1.35}/python/koatl/prelude/functional/__init__.tl +0 -0
  113. {koatl-0.1.33 → koatl-0.1.35}/python/koatl/prelude/functional/async.tl +0 -0
  114. {koatl-0.1.33 → koatl-0.1.35}/python/koatl/prelude/functional/env.tl +0 -0
  115. {koatl-0.1.33 → koatl-0.1.35}/python/koatl/prelude/functional/list.tl +0 -0
  116. {koatl-0.1.33 → koatl-0.1.35}/python/koatl/prelude/functional/memo.tl +0 -0
  117. {koatl-0.1.33 → koatl-0.1.35}/python/koatl/prelude/io.tl +0 -0
  118. {koatl-0.1.33 → koatl-0.1.35}/python/koatl/prelude/regex.tl +0 -0
  119. {koatl-0.1.33 → koatl-0.1.35}/python/koatl/runtime/__init__.py +0 -0
  120. {koatl-0.1.33 → koatl-0.1.35}/python/koatl/runtime/helpers.py +0 -0
  121. {koatl-0.1.33 → koatl-0.1.35}/python/koatl/runtime/meta_finder.py +0 -0
@@ -99,7 +99,7 @@ checksum = "f4c7245a08504955605670dbf141fceab975f15ca21570696aebe9d2e71576bd"
99
99
 
100
100
  [[package]]
101
101
  name = "koatl"
102
- version = "0.1.33"
102
+ version = "0.1.35"
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.33
3
+ Version: 0.1.35
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.33"
3
+ version = "0.1.35"
4
4
  edition = "2021"
5
5
 
6
6
  [lib]
@@ -8,9 +8,9 @@ export Monad = class(Trait):
8
8
  """
9
9
  __slots__ = ()
10
10
 
11
- bind = Abstract& (self, f) => None
11
+ bind = Trait.abstract& (self, f) => None
12
12
 
13
- pure = staticmethod& Abstract& value => None
13
+ pure = staticmethod& Trait.abstract& value => None
14
14
 
15
15
  # Automatically generated implementations.
16
16
  map = (self, f) => self.bind(x => self.pure(f(x)))
@@ -33,7 +33,7 @@ export MonadOnce = class(Monad, Trait):
33
33
  __slots__ = ()
34
34
 
35
35
  # The default implementation required for `@` syntax that should be overridden by subclasses.
36
- bind_once = Abstract& (self, f) => None
36
+ bind_once = Trait.abstract& (self, f) => None
37
37
 
38
38
  # An optional, optimized implementation of `bind` that skips deep recursion.
39
39
  # TODO: can this be automatically generated from bind_once?
@@ -69,4 +69,4 @@ export Traversable = class(Trait):
69
69
  """
70
70
  __slots__ = ()
71
71
 
72
- traverse = Abstract& (self, f) => None
72
+ traverse = Trait.abstract& (self, f) => None
@@ -160,7 +160,7 @@ export Err = class(Result):
160
160
  raise ValueError(f"Expected Ok, got {repr(self)}")
161
161
  coalesce = (self, f) => f()
162
162
 
163
- Extension.property(object, "result")& Result
163
+ Extension.property(object, "result")& self => Result
164
164
 
165
165
  __tl__.Ok = Ok
166
166
  __tl__.Err = Err
@@ -1,9 +1,9 @@
1
1
  import itertools
2
2
  import builtins
3
- import .functional.(Traversable, Ok, Err, Memo, Async, AsyncMemo)
3
+ import .functional.(Traversable, Ok, Err, Memo, Async, AsyncMemo, Result)
4
4
 
5
5
  export Iterable = Extension.trait& class(Traversable, Trait):
6
- iter = Abstract
6
+ iter = Trait.abstract& self => None
7
7
 
8
8
  skip = (self, n) =>
9
9
  let it = self.iter
@@ -50,6 +50,22 @@ export Iterable = Extension.trait& class(Traversable, Trait):
50
50
 
51
51
  filter = (self, f) => builtins.filter(f, self.iter)
52
52
 
53
+ filter_map = (self, f=None) =>
54
+ let impl = () =>
55
+ if f === None:
56
+ for i in self.iter:
57
+ if Result(i) matches Ok(value):
58
+ yield value
59
+ else:
60
+ for i in self.iter:
61
+ if Result(f(i)) matches Ok(value):
62
+ yield value
63
+
64
+ impl.__name__ = f"filter_map"
65
+ impl.__qualname__ = f"Iterable.filter_map"
66
+
67
+ impl()
68
+
53
69
  flat_map = (self, f) => itertools.chain.from_iterable(self.map(f))
54
70
 
55
71
  reverse = self => reversed(list(self.iter))
@@ -59,8 +75,16 @@ export Iterable = Extension.trait& class(Traversable, Trait):
59
75
 
60
76
  copy = self => itertools.tee(self.iter)
61
77
 
62
-
63
- count = self => len(self.iter)
78
+ count = (self, f=None) =>
79
+ let acc = 0
80
+ if f === None:
81
+ for _ in self:
82
+ acc += 1
83
+ else:
84
+ for i in self:
85
+ if f(i):
86
+ acc += 1
87
+ acc
64
88
 
65
89
  fold = (self, init, f) =>
66
90
  let acc = init
@@ -163,30 +187,56 @@ export Iterable = Extension.trait& class(Traversable, Trait):
163
187
  raise ValueError("mean of empty iterable")
164
188
  acc / count
165
189
 
166
- max = self =>
190
+ max = (self, key=None) =>
167
191
  let it = self.iter
168
- let v
192
+ let v, m
169
193
  try:
170
194
  v = next(it)
195
+ if key === None:
196
+ m = v
197
+ else:
198
+ m = key(v)
171
199
  except StopIteration(value=value):
172
200
  raise ValueError("max of empty iterable")
173
201
 
174
- for i in it:
175
- if i > v:
176
- v = i
202
+
203
+ if key === None:
204
+ for i in it:
205
+ if i > m:
206
+ v = i
207
+ m = i
208
+ else:
209
+ for i in it:
210
+ let ki = key(i)
211
+ if ki > m:
212
+ v = i
213
+ m = ki
214
+
177
215
  v
178
216
 
179
- min = self =>
217
+ min = (self, key=None) =>
180
218
  let it = self.iter
181
- let v
219
+ let v, m
182
220
  try:
183
221
  v = next(it)
222
+ if key === None:
223
+ m = v
224
+ else:
225
+ m = key(v)
184
226
  except StopIteration(value=value):
185
227
  raise ValueError("min of empty iterable")
186
228
 
187
- for i in it:
188
- if i < v:
189
- v = i
229
+ if key === None:
230
+ for i in it:
231
+ if i < m:
232
+ v = i
233
+ m = i
234
+ else:
235
+ for i in it:
236
+ let ki = key(i)
237
+ if ki < m:
238
+ v = i
239
+ m = ki
190
240
  v
191
241
 
192
242
  list = self =>
@@ -40,6 +40,7 @@ class MappingMeta(type):
40
40
  return [(k, v) for k, v in inspect.getmembers(self) if not k.startswith("_")]
41
41
 
42
42
 
43
+ # A utility base class to inherit from to enable __getitem__ method lookup on types and thus destructuring.
43
44
  class Class(metaclass=MappingMeta):
44
45
  pass
45
46
 
@@ -104,9 +105,9 @@ class Trait(metaclass=TraitMeta):
104
105
  fn._property = True
105
106
  return property(fn)
106
107
 
107
-
108
- Abstract = lambda value: abc.abstractmethod(value)
109
- Abstract.__isabstractmethod__ = True
108
+ @staticmethod
109
+ def abstract(value):
110
+ return abc.abstractmethod(value)
110
111
 
111
112
 
112
- __all__ = ["Class", "Trait", "Abstract"]
113
+ __all__ = ["Class", "Trait"]
@@ -21,6 +21,16 @@ class Record(dict):
21
21
 
22
22
  return attr
23
23
 
24
+ def __hash__(self):
25
+ return hash(tuple(sorted(self.items())))
26
+
27
+ def __eq__(self, other):
28
+ return super().__eq__(other)
29
+
30
+ @property
31
+ def len(self):
32
+ return len(self)
33
+
24
34
  @staticmethod
25
35
  def method(fn):
26
36
  fn._method = True
@@ -7,7 +7,7 @@ from .._rs import fast_vget, fast_vset, fast_vset_trait
7
7
  def vget(obj, name, ignore_traits=False):
8
8
  try:
9
9
  return getattr(obj, name)
10
- except:
10
+ except AttributeError:
11
11
  pass
12
12
 
13
13
  # special case for iter - this could be implemented using types and trait vtbls
@@ -553,6 +553,7 @@ impl<'src> PyExprExt<'src> for SPyExpr<'src> {
553
553
  PyBinaryOp::Sub => Some("Sub"),
554
554
  PyBinaryOp::Mult => Some("Mult"),
555
555
  PyBinaryOp::Div => Some("Div"),
556
+ PyBinaryOp::FloorDiv => Some("FloorDiv"),
556
557
  PyBinaryOp::Mod => Some("Mod"),
557
558
  PyBinaryOp::Pow => Some("Pow"),
558
559
  _ => None,
@@ -596,6 +597,8 @@ impl<'src> PyExprExt<'src> for SPyExpr<'src> {
596
597
  PyBinaryOp::Neq => Some("NotEq"),
597
598
  PyBinaryOp::Is => Some("Is"),
598
599
  PyBinaryOp::Nis => Some("IsNot"),
600
+ PyBinaryOp::In => Some("In"),
601
+ PyBinaryOp::Nin => Some("NotIn"),
599
602
  _ => None,
600
603
  };
601
604
 
@@ -13,7 +13,7 @@ obj = (class:
13
13
  assert_eq((x => x)?(1), 1)
14
14
  assert_eq([1]?[0], 1)
15
15
  assert_eq(obj?.a, 1)
16
- assert_eq(1?.($ + 1)(), 2)
16
+ assert_eq(1?.($ + 1), 2)
17
17
 
18
18
  assert_eq(None ?? 1, 1)
19
19
  assert_eq(int(5) ?? 1, 5)
@@ -25,5 +25,5 @@ except:
25
25
 
26
26
  assert_eq(try 1 matches Ok(1), True)
27
27
 
28
- assert_eq((try 5)?.($+1)?(), Ok(6))
29
- assert_eq((try x)?.($+1)?() matches Err(), True)
28
+ assert_eq((try 5)?.($+1), Ok(6))
29
+ assert_eq((try x)?.($+1) matches Err(), True)
@@ -15,7 +15,7 @@ assert_eq(x.a, 1)
15
15
 
16
16
 
17
17
  SomeTrait = Extension.trait& class(Trait):
18
- required_method = Abstract
18
+ required_method = Trait.abstract& self => ()
19
19
 
20
20
  derived_method = self => self.required_method()
21
21
  derived_property = Trait.property& self => self.required_method()
@@ -35,11 +35,15 @@ pub enum BinaryOp {
35
35
  Add,
36
36
  Sub,
37
37
  Mul,
38
- Mod,
39
38
  MatMul,
40
39
  Div,
41
40
  Exp,
42
41
 
42
+ FloorDiv,
43
+ Mod,
44
+
45
+ In,
46
+ Nin,
43
47
  Lt,
44
48
  Leq,
45
49
  Gt,
@@ -138,9 +142,9 @@ pub enum Stmt<'a, TTree: Tree> {
138
142
  }
139
143
 
140
144
  #[derive(Debug, Clone)]
141
- pub struct FmtExpr<'a, TTree: Tree> {
145
+ pub struct FmtExpr<TTree: Tree> {
142
146
  pub expr: TTree::Expr,
143
- pub fmt: Option<Ident<'a>>,
147
+ pub fmt: Option<TTree::Expr>,
144
148
  }
145
149
 
146
150
  #[derive(Debug, Clone)]
@@ -229,7 +233,7 @@ pub enum Expr<'a, TTree: Tree> {
229
233
  Block(Vec<TTree::Stmt>),
230
234
 
231
235
  Fn(Vec<ArgDefItem<'a, TTree>>, TTree::Expr),
232
- Fstr(Spanned<String>, Vec<(FmtExpr<'a, TTree>, Spanned<String>)>),
236
+ Fstr(Spanned<String>, Vec<(FmtExpr<TTree>, Spanned<String>)>),
233
237
 
234
238
  // these are removed during desugaring
235
239
  Decorated(TTree::Expr, TTree::Expr),
@@ -347,4 +351,4 @@ pub type SMappingItem<'a> = MappingItem<STree<'a>>;
347
351
  pub type SMatchCase<'a> = MatchCase<STree<'a>>;
348
352
  pub type SCallItem<'a> = CallItem<'a, STree<'a>>;
349
353
  pub type SArgDefItem<'a> = ArgDefItem<'a, STree<'a>>;
350
- pub type SFmtExpr<'a> = FmtExpr<'a, STree<'a>>;
354
+ pub type SFmtExpr<'a> = FmtExpr<STree<'a>>;
@@ -5,7 +5,10 @@ use chumsky::{
5
5
  label::LabelError,
6
6
  prelude::*,
7
7
  };
8
- use std::{collections::HashSet, fmt};
8
+ use std::{
9
+ collections::HashSet,
10
+ fmt::{self},
11
+ };
9
12
 
10
13
  use crate::ast::{Span, Spannable, Spanned};
11
14
 
@@ -321,7 +324,7 @@ where
321
324
  fn parse_symbol(&mut self) -> TResult<'src, Spanned<Token<'src>>> {
322
325
  const POLYGRAMS: &[&str] = &[
323
326
  "+=", "-=", "*=", "/=", "|=", "??=", "===", "<=>", "=>", "..", "==", "<>", "<=", ">=",
324
- "//", "**", "??", ".=",
327
+ "//", "**", "??", ".=", "::",
325
328
  ];
326
329
  const MONOGRAMS: &str = "[](){}<>.,;:!?@$%^&*+-=|\\/`~";
327
330
 
@@ -612,23 +615,13 @@ where
612
615
  }
613
616
  }
614
617
 
615
- fn parse_fstr(&mut self, verbatim: bool) -> TResult<'src, TokenList<'src>> {
618
+ fn parse_fstr_inner(&mut self, ending: &str, verbatim: bool) -> TResult<'src, TokenList<'src>> {
616
619
  let mut marker = self.cursor();
617
-
618
- if verbatim {
619
- self.parse_seq("f\"\"\"")?;
620
- } else {
621
- self.parse_seq("f\"")?;
622
- }
623
-
624
620
  let mut tokens = vec![];
625
621
  let mut current_str = String::new();
626
622
 
627
623
  loop {
628
- if self
629
- .try_parse(|x| x.parse_seq(if verbatim { "\"\"\"" } else { "\"" }))
630
- .is_ok()
631
- {
624
+ if self.look_ahead(|x| x.parse_seq(ending)).is_ok() {
632
625
  if tokens.len() == 0 {
633
626
  tokens.push(Token::FstrBegin(current_str).spanned(self.span_since(&marker)));
634
627
  } else {
@@ -667,12 +660,19 @@ where
667
660
 
668
661
  self.parse_nonsemantic()?;
669
662
  let _ = self.try_parse(|x| x.parse_newline());
670
-
671
663
  let sexpr = self.try_parse(|x| x.parse_block(0, NewBlockType::BeginInput))?;
672
-
673
664
  let _ = self.try_parse(|x| x.parse_newline());
674
- self.try_parse(|x| x.parse_indentation())?;
675
- self.try_parse(|x| x.parse_seq("}"))?;
665
+
666
+ self.parse_indentation()?;
667
+
668
+ marker = self.cursor();
669
+ let mut format_tokens = vec![];
670
+ if self.try_parse(|x| x.parse_seq("!")).is_ok() {
671
+ format_tokens.push(Token::Symbol("!").spanned(self.span_since(&marker)));
672
+ format_tokens.extend(self.parse_fstr_inner("}", verbatim)?);
673
+ }
674
+
675
+ self.parse_seq("}")?;
676
676
 
677
677
  tokens.push(Token::Indent.spanned(Span::new(
678
678
  sexpr.span.context,
@@ -687,6 +687,7 @@ where
687
687
  sexpr.span.context,
688
688
  sexpr.span.end..sexpr.span.end,
689
689
  )));
690
+ tokens.extend(format_tokens);
690
691
 
691
692
  marker = self.cursor();
692
693
 
@@ -703,6 +704,20 @@ where
703
704
  }
704
705
  }
705
706
 
707
+ fn parse_fstr(&mut self, verbatim: bool) -> TResult<'src, TokenList<'src>> {
708
+ if verbatim {
709
+ self.parse_seq("f\"\"\"")?;
710
+ let inner = self.parse_fstr_inner("\"\"\"", true)?;
711
+ self.parse_seq("\"\"\"")?;
712
+ return Ok(inner);
713
+ } else {
714
+ self.parse_seq("f\"")?;
715
+ let inner = self.parse_fstr_inner("\"", false)?;
716
+ self.parse_seq("\"")?;
717
+ return Ok(inner);
718
+ }
719
+ }
720
+
706
721
  fn parse_str_start(&mut self) -> TResult<'src, ()> {
707
722
  let start = self.cursor();
708
723
 
@@ -818,6 +833,13 @@ where
818
833
  break;
819
834
  }
820
835
 
836
+ if let Token::Symbol("!") = &tok.value {
837
+ // Format specifier delimiter; end block.
838
+ self.input.rewind(saved);
839
+ end_block = true;
840
+ break;
841
+ }
842
+
821
843
  if let Token::Symbol(s) = &tok.value {
822
844
  let char = s.chars().next().unwrap_or('\0');
823
845
  if OPEN_DELIMS.contains(&char) {
@@ -525,6 +525,8 @@ where
525
525
  symbol("-=").to(BinaryOp::Sub),
526
526
  symbol("*=").to(BinaryOp::Mul),
527
527
  symbol("/=").to(BinaryOp::Div),
528
+ symbol("//=").to(BinaryOp::FloorDiv),
529
+ symbol("%=").to(BinaryOp::Mod),
528
530
  symbol("|=").to(BinaryOp::Pipe),
529
531
  symbol("??=").to(BinaryOp::Coalesce),
530
532
  ))
@@ -543,6 +545,7 @@ where
543
545
  SStmtInner::Expr(lhs.indirect())
544
546
  }
545
547
  })
548
+ .labelled("assignment")
546
549
  .boxed();
547
550
 
548
551
  let inline_assign_stmt = group((expr.clone(), assign_op.clone().then(expr.clone()).or_not()))
@@ -553,6 +556,7 @@ where
553
556
  SStmtInner::Expr(lhs.indirect())
554
557
  }
555
558
  })
559
+ .labelled("assignment")
556
560
  .boxed();
557
561
 
558
562
  let while_stmt = just(Token::Kw("while"))
@@ -955,27 +959,32 @@ where
955
959
  }
956
960
  .map_with(|x, e| x.spanned(e.span()));
957
961
 
958
- let fstr = fstr_begin
959
- .then(
960
- expr_or_block
961
- .clone()
962
- .then(fstr_continue)
963
- .map(|(block, cont)| {
964
- (
965
- FmtExpr {
966
- expr: block.indirect(),
967
- fmt: None,
968
- },
969
- cont,
970
- )
971
- })
972
- .repeated()
973
- .collect::<Vec<_>>(),
974
- )
975
- .map(|(begin, parts)| SExprInner::Fstr(begin, parts))
976
- .spanned_expr()
977
- .labelled("f-string")
978
- .boxed();
962
+ let mut fstr = Recursive::<chumsky::recursive::Indirect<TInput, SExpr, TExtra>>::declare();
963
+
964
+ fstr.define(
965
+ fstr_begin
966
+ .then(
967
+ expr_or_block
968
+ .clone()
969
+ .then(symbol("!").ignore_then(fstr.clone()).or_not())
970
+ .then(fstr_continue)
971
+ .map(|((block, fmt), cont)| {
972
+ (
973
+ FmtExpr {
974
+ expr: block.indirect(),
975
+ fmt: fmt.map(|x| x.indirect()),
976
+ },
977
+ cont,
978
+ )
979
+ })
980
+ .repeated()
981
+ .collect::<Vec<_>>(),
982
+ )
983
+ .map(|(begin, parts)| SExprInner::Fstr(begin, parts))
984
+ .spanned_expr()
985
+ .labelled("f-string")
986
+ .boxed(),
987
+ );
979
988
 
980
989
  let class_ = just(Token::Kw("class"))
981
990
  .ignore_then(
@@ -1183,7 +1192,7 @@ where
1183
1192
  .labelled("then-attribute")
1184
1193
  .boxed();
1185
1194
 
1186
- let raw_attr = symbol("!")
1195
+ let raw_attr = symbol("::")
1187
1196
  .ignore_then(ident.clone())
1188
1197
  .map(Postfix::RawAttribute)
1189
1198
  .labelled("raw-attribute")
@@ -1307,6 +1316,7 @@ where
1307
1316
  select! {
1308
1317
  Token::Symbol("*") => BinaryOp::Mul,
1309
1318
  Token::Symbol("/") => BinaryOp::Div,
1319
+ Token::Symbol("//") => BinaryOp::FloorDiv,
1310
1320
  Token::Symbol("%") => BinaryOp::Mod,
1311
1321
  Token::Symbol("@") => BinaryOp::MatMul,
1312
1322
  },
@@ -1325,6 +1335,7 @@ where
1325
1335
  let binary3 = make_binary_op(
1326
1336
  binary2.clone(),
1327
1337
  select! {
1338
+ Token::Kw("in") => BinaryOp::In,
1328
1339
  Token::Symbol("<") => BinaryOp::Lt,
1329
1340
  Token::Symbol("<=") => BinaryOp::Leq,
1330
1341
  Token::Symbol(">") => BinaryOp::Gt,
@@ -1333,7 +1344,10 @@ where
1333
1344
  Token::Symbol("<>") => BinaryOp::Neq,
1334
1345
  Token::Symbol("===") => BinaryOp::Is,
1335
1346
  Token::Symbol("<=>") => BinaryOp::Nis,
1336
- },
1347
+ }
1348
+ .or(just(Token::Kw("not"))
1349
+ .then(just(Token::Kw("in")))
1350
+ .to(BinaryOp::Nin)),
1337
1351
  false,
1338
1352
  );
1339
1353
 
@@ -146,6 +146,7 @@ pub enum PyBinaryOp {
146
146
  Mult,
147
147
  Div,
148
148
  Mod,
149
+ FloorDiv,
149
150
  Pow,
150
151
  MatMult,
151
152
 
@@ -160,6 +161,8 @@ pub enum PyBinaryOp {
160
161
  Geq,
161
162
  Is,
162
163
  Nis,
164
+ In,
165
+ Nin,
163
166
  }
164
167
 
165
168
  #[derive(Debug, Clone)]
@@ -200,7 +203,7 @@ pub enum PyDictItem<'a> {
200
203
  #[derive(Debug, Clone)]
201
204
  pub enum PyFstrPart<'a> {
202
205
  Str(PyIdent<'a>),
203
- Expr(SPyExpr<'a>, Option<PyIdent<'a>>),
206
+ Expr(SPyExpr<'a>, Option<SPyExpr<'a>>),
204
207
  }
205
208
 
206
209
  #[derive(Debug, Clone)]