koatl 0.1.33__tar.gz → 0.1.34__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.34}/Cargo.lock +1 -1
  2. {koatl-0.1.33 → koatl-0.1.34}/PKG-INFO +1 -1
  3. {koatl-0.1.33 → koatl-0.1.34}/koatl/Cargo.toml +1 -1
  4. {koatl-0.1.33 → koatl-0.1.34/koatl}/python/koatl/prelude/functional/result.tl +1 -1
  5. {koatl-0.1.33 → koatl-0.1.34/koatl}/python/koatl/prelude/iterable.tl +63 -13
  6. {koatl-0.1.33 → koatl-0.1.34/koatl}/python/koatl/runtime/record.py +10 -0
  7. {koatl-0.1.33 → koatl-0.1.34}/koatl/python/koatl/runtime/virtual.py +1 -1
  8. {koatl-0.1.33 → koatl-0.1.34}/koatl/src/emit_py.rs +3 -0
  9. {koatl-0.1.33 → koatl-0.1.34}/koatl/tests/e2e/base/coal.tl +1 -1
  10. {koatl-0.1.33 → koatl-0.1.34}/koatl/tests/e2e/prelude/try.tl +2 -2
  11. {koatl-0.1.33 → koatl-0.1.34}/koatl-core/parser/src/ast.rs +9 -5
  12. {koatl-0.1.33 → koatl-0.1.34}/koatl-core/parser/src/lexer.rs +40 -18
  13. {koatl-0.1.33 → koatl-0.1.34}/koatl-core/parser/src/parser.rs +37 -23
  14. {koatl-0.1.33 → koatl-0.1.34}/koatl-core/src/py/ast.rs +4 -1
  15. {koatl-0.1.33 → koatl-0.1.34}/koatl-core/src/py/emit.rs +43 -18
  16. {koatl-0.1.33 → koatl-0.1.34}/koatl-core/src/py/util.rs +1 -1
  17. {koatl-0.1.33 → koatl-0.1.34}/koatl-core/src/resolve_scopes.rs +1 -1
  18. {koatl-0.1.33 → koatl-0.1.34}/koatl-core/src/transform.rs +10 -6
  19. {koatl-0.1.33/koatl → koatl-0.1.34}/python/koatl/prelude/functional/result.tl +1 -1
  20. {koatl-0.1.33/koatl → koatl-0.1.34}/python/koatl/prelude/iterable.tl +63 -13
  21. {koatl-0.1.33/koatl → koatl-0.1.34}/python/koatl/runtime/record.py +10 -0
  22. {koatl-0.1.33 → koatl-0.1.34}/python/koatl/runtime/virtual.py +1 -1
  23. {koatl-0.1.33 → koatl-0.1.34}/Cargo.toml +0 -0
  24. {koatl-0.1.33 → koatl-0.1.34}/README.md +0 -0
  25. {koatl-0.1.33 → koatl-0.1.34}/koatl/.github/workflows/CI.yml +0 -0
  26. {koatl-0.1.33 → koatl-0.1.34}/koatl/.gitignore +0 -0
  27. {koatl-0.1.33 → koatl-0.1.34}/koatl/LICENSE +0 -0
  28. {koatl-0.1.33 → koatl-0.1.34}/koatl/README.md +0 -0
  29. {koatl-0.1.33 → koatl-0.1.34}/koatl/python/koatl/__init__.py +0 -0
  30. {koatl-0.1.33 → koatl-0.1.34}/koatl/python/koatl/__main__.py +0 -0
  31. {koatl-0.1.33 → koatl-0.1.34}/koatl/python/koatl/cli.py +0 -0
  32. {koatl-0.1.33 → koatl-0.1.34}/koatl/python/koatl/notebook/__init__.py +0 -0
  33. {koatl-0.1.33 → koatl-0.1.34}/koatl/python/koatl/notebook/magic.py +0 -0
  34. {koatl-0.1.33 → koatl-0.1.34}/koatl/python/koatl/prelude/__init__.tl +0 -0
  35. {koatl-0.1.33 → koatl-0.1.34}/koatl/python/koatl/prelude/functional/__init__.tl +0 -0
  36. {koatl-0.1.33 → koatl-0.1.34}/koatl/python/koatl/prelude/functional/algebra.tl +0 -0
  37. {koatl-0.1.33 → koatl-0.1.34}/koatl/python/koatl/prelude/functional/async.tl +0 -0
  38. {koatl-0.1.33 → koatl-0.1.34}/koatl/python/koatl/prelude/functional/env.tl +0 -0
  39. {koatl-0.1.33 → koatl-0.1.34}/koatl/python/koatl/prelude/functional/list.tl +0 -0
  40. {koatl-0.1.33 → koatl-0.1.34}/koatl/python/koatl/prelude/functional/memo.tl +0 -0
  41. {koatl-0.1.33 → koatl-0.1.34}/koatl/python/koatl/prelude/io.tl +0 -0
  42. {koatl-0.1.33 → koatl-0.1.34}/koatl/python/koatl/prelude/regex.tl +0 -0
  43. {koatl-0.1.33 → koatl-0.1.34}/koatl/python/koatl/runtime/__init__.py +0 -0
  44. {koatl-0.1.33 → koatl-0.1.34}/koatl/python/koatl/runtime/classes.py +0 -0
  45. {koatl-0.1.33 → koatl-0.1.34}/koatl/python/koatl/runtime/helpers.py +0 -0
  46. {koatl-0.1.33 → koatl-0.1.34}/koatl/python/koatl/runtime/meta_finder.py +0 -0
  47. {koatl-0.1.33 → koatl-0.1.34}/koatl/requirements.txt +0 -0
  48. {koatl-0.1.33 → koatl-0.1.34}/koatl/src/lib.rs +0 -0
  49. {koatl-0.1.33 → koatl-0.1.34}/koatl/tests/e2e/base/containers.tl +0 -0
  50. {koatl-0.1.33 → koatl-0.1.34}/koatl/tests/e2e/base/data.txt +0 -0
  51. {koatl-0.1.33 → koatl-0.1.34}/koatl/tests/e2e/base/decorators.tl +0 -0
  52. {koatl-0.1.33 → koatl-0.1.34}/koatl/tests/e2e/base/destructure-for-and-fn.tl +0 -0
  53. {koatl-0.1.33 → koatl-0.1.34}/koatl/tests/e2e/base/destructure.tl +0 -0
  54. {koatl-0.1.33 → koatl-0.1.34}/koatl/tests/e2e/base/escape_ident.tl +0 -0
  55. {koatl-0.1.33 → koatl-0.1.34}/koatl/tests/e2e/base/fstr.tl +0 -0
  56. {koatl-0.1.33 → koatl-0.1.34}/koatl/tests/e2e/base/functions.tl +0 -0
  57. {koatl-0.1.33 → koatl-0.1.34}/koatl/tests/e2e/base/generator.tl +0 -0
  58. {koatl-0.1.33 → koatl-0.1.34}/koatl/tests/e2e/base/if_expr.tl +0 -0
  59. {koatl-0.1.33 → koatl-0.1.34}/koatl/tests/e2e/base/imports.tl +0 -0
  60. {koatl-0.1.33 → koatl-0.1.34}/koatl/tests/e2e/base/loops.tl +0 -0
  61. {koatl-0.1.33 → koatl-0.1.34}/koatl/tests/e2e/base/match.tl +0 -0
  62. {koatl-0.1.33 → koatl-0.1.34}/koatl/tests/e2e/base/nary-list.tl +0 -0
  63. {koatl-0.1.33 → koatl-0.1.34}/koatl/tests/e2e/base/placeholder.tl +0 -0
  64. {koatl-0.1.33 → koatl-0.1.34}/koatl/tests/e2e/base/precedence.tl +0 -0
  65. {koatl-0.1.33 → koatl-0.1.34}/koatl/tests/e2e/base/record.tl +0 -0
  66. {koatl-0.1.33 → koatl-0.1.34}/koatl/tests/e2e/base/scopes.tl +0 -0
  67. {koatl-0.1.33 → koatl-0.1.34}/koatl/tests/e2e/base/semantic_whitespace.tl +0 -0
  68. {koatl-0.1.33 → koatl-0.1.34}/koatl/tests/e2e/base/short_circuit.tl +0 -0
  69. {koatl-0.1.33 → koatl-0.1.34}/koatl/tests/e2e/base/with.tl +0 -0
  70. {koatl-0.1.33 → koatl-0.1.34}/koatl/tests/e2e/prelude/async.tl +0 -0
  71. {koatl-0.1.33 → koatl-0.1.34}/koatl/tests/e2e/prelude/aug_assign.tl +0 -0
  72. {koatl-0.1.33 → koatl-0.1.34}/koatl/tests/e2e/prelude/env.tl +0 -0
  73. {koatl-0.1.33 → koatl-0.1.34}/koatl/tests/e2e/prelude/iterables.tl +0 -0
  74. {koatl-0.1.33 → koatl-0.1.34}/koatl/tests/e2e/prelude/list.tl +0 -0
  75. {koatl-0.1.33 → koatl-0.1.34}/koatl/tests/e2e/prelude/memo.tl +0 -0
  76. {koatl-0.1.33 → koatl-0.1.34}/koatl/tests/e2e/prelude/result.tl +0 -0
  77. {koatl-0.1.33 → koatl-0.1.34}/koatl/tests/e2e/prelude/slice.tl +0 -0
  78. {koatl-0.1.33 → koatl-0.1.34}/koatl/tests/e2e/prelude/virtual.tl +0 -0
  79. {koatl-0.1.33 → koatl-0.1.34}/koatl/tests/e2e/util/__init__.py +0 -0
  80. {koatl-0.1.33 → koatl-0.1.34}/koatl/tests/e2e/util/module0.tl +0 -0
  81. {koatl-0.1.33 → koatl-0.1.34}/koatl/tests/e2e/util/module1.tl +0 -0
  82. {koatl-0.1.33 → koatl-0.1.34}/koatl/tests/e2e/util/module2.tl +0 -0
  83. {koatl-0.1.33 → koatl-0.1.34}/koatl/tests/parse/arith.tl +0 -0
  84. {koatl-0.1.33 → koatl-0.1.34}/koatl/tests/parse/assign.tl +0 -0
  85. {koatl-0.1.33 → koatl-0.1.34}/koatl/tests/parse/block-comments.tl +0 -0
  86. {koatl-0.1.33 → koatl-0.1.34}/koatl/tests/parse/deco.tl +0 -0
  87. {koatl-0.1.33 → koatl-0.1.34}/koatl/tests/parse/func.tl +0 -0
  88. {koatl-0.1.33 → koatl-0.1.34}/koatl/tests/parse/matches.tl +0 -0
  89. {koatl-0.1.33 → koatl-0.1.34}/koatl/tests/test_e2e.py +0 -0
  90. {koatl-0.1.33 → koatl-0.1.34}/koatl/tests/test_parse.py +0 -0
  91. {koatl-0.1.33 → koatl-0.1.34}/koatl-core/Cargo.toml +0 -0
  92. {koatl-0.1.33 → koatl-0.1.34}/koatl-core/parser/Cargo.toml +0 -0
  93. {koatl-0.1.33 → koatl-0.1.34}/koatl-core/parser/src/lib.rs +0 -0
  94. {koatl-0.1.33 → koatl-0.1.34}/koatl-core/parser/src/util.rs +0 -0
  95. {koatl-0.1.33 → koatl-0.1.34}/koatl-core/src/inference.rs +0 -0
  96. {koatl-0.1.33 → koatl-0.1.34}/koatl-core/src/lib.rs +0 -0
  97. {koatl-0.1.33 → koatl-0.1.34}/koatl-core/src/main.rs +0 -0
  98. {koatl-0.1.33 → koatl-0.1.34}/koatl-core/src/parse_timer.rs +0 -0
  99. {koatl-0.1.33 → koatl-0.1.34}/koatl-core/src/parser.rs +0 -0
  100. {koatl-0.1.33 → koatl-0.1.34}/koatl-core/src/py/mod.rs +0 -0
  101. {koatl-0.1.33 → koatl-0.1.34}/koatl-core/src/types.rs +0 -0
  102. {koatl-0.1.33 → koatl-0.1.34}/koatl-core/src/util.rs +0 -0
  103. {koatl-0.1.33 → koatl-0.1.34}/pyproject.toml +0 -0
  104. {koatl-0.1.33 → koatl-0.1.34}/python/koatl/__init__.py +0 -0
  105. {koatl-0.1.33 → koatl-0.1.34}/python/koatl/__main__.py +0 -0
  106. {koatl-0.1.33 → koatl-0.1.34}/python/koatl/cli.py +0 -0
  107. {koatl-0.1.33 → koatl-0.1.34}/python/koatl/notebook/__init__.py +0 -0
  108. {koatl-0.1.33 → koatl-0.1.34}/python/koatl/notebook/magic.py +0 -0
  109. {koatl-0.1.33 → koatl-0.1.34}/python/koatl/prelude/__init__.tl +0 -0
  110. {koatl-0.1.33 → koatl-0.1.34}/python/koatl/prelude/functional/__init__.tl +0 -0
  111. {koatl-0.1.33 → koatl-0.1.34}/python/koatl/prelude/functional/algebra.tl +0 -0
  112. {koatl-0.1.33 → koatl-0.1.34}/python/koatl/prelude/functional/async.tl +0 -0
  113. {koatl-0.1.33 → koatl-0.1.34}/python/koatl/prelude/functional/env.tl +0 -0
  114. {koatl-0.1.33 → koatl-0.1.34}/python/koatl/prelude/functional/list.tl +0 -0
  115. {koatl-0.1.33 → koatl-0.1.34}/python/koatl/prelude/functional/memo.tl +0 -0
  116. {koatl-0.1.33 → koatl-0.1.34}/python/koatl/prelude/io.tl +0 -0
  117. {koatl-0.1.33 → koatl-0.1.34}/python/koatl/prelude/regex.tl +0 -0
  118. {koatl-0.1.33 → koatl-0.1.34}/python/koatl/runtime/__init__.py +0 -0
  119. {koatl-0.1.33 → koatl-0.1.34}/python/koatl/runtime/classes.py +0 -0
  120. {koatl-0.1.33 → koatl-0.1.34}/python/koatl/runtime/helpers.py +0 -0
  121. {koatl-0.1.33 → koatl-0.1.34}/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.34"
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.34
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.34"
4
4
  edition = "2021"
5
5
 
6
6
  [lib]
@@ -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,6 +1,6 @@
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
6
  iter = Abstract
@@ -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 =>
@@ -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)
@@ -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)]
@@ -5,7 +5,10 @@ use parser::{
5
5
  lexer::{py_escape_fstr, py_escape_str},
6
6
  };
7
7
 
8
- use crate::{py::ast::*, util::TlResult};
8
+ use crate::{
9
+ py::ast::*,
10
+ util::{TlErrBuilder, TlResult},
11
+ };
9
12
 
10
13
  const LOW_PREC: f32 = -100.0;
11
14
  const HIGH_PREC: f32 = 100.0;
@@ -258,10 +261,16 @@ impl PyBinaryOp {
258
261
  | PyBinaryOp::Geq
259
262
  | PyBinaryOp::Is
260
263
  | PyBinaryOp::Nis
264
+ | PyBinaryOp::In
265
+ | PyBinaryOp::Nin
261
266
  | PyBinaryOp::And
262
267
  | PyBinaryOp::Or => 0.0,
263
268
  PyBinaryOp::Add | PyBinaryOp::Sub => 1.0,
264
- PyBinaryOp::Mult | PyBinaryOp::Div | PyBinaryOp::Mod | PyBinaryOp::MatMult => 2.0,
269
+ PyBinaryOp::Mult
270
+ | PyBinaryOp::Div
271
+ | PyBinaryOp::Mod
272
+ | PyBinaryOp::MatMult
273
+ | PyBinaryOp::FloorDiv => 2.0,
265
274
  PyBinaryOp::Pow => 3.0,
266
275
  }
267
276
  }
@@ -272,6 +281,7 @@ impl PyBinaryOp {
272
281
  PyBinaryOp::Sub => "-",
273
282
  PyBinaryOp::Mult => "*",
274
283
  PyBinaryOp::Div => "/",
284
+ PyBinaryOp::FloorDiv => "//",
275
285
  PyBinaryOp::Mod => "%",
276
286
  PyBinaryOp::Pow => "**",
277
287
  PyBinaryOp::MatMult => "@",
@@ -283,6 +293,8 @@ impl PyBinaryOp {
283
293
  PyBinaryOp::Geq => ">=",
284
294
  PyBinaryOp::Is => "is",
285
295
  PyBinaryOp::Nis => "is not",
296
+ PyBinaryOp::In => "in",
297
+ PyBinaryOp::Nin => "not in",
286
298
  PyBinaryOp::And => "and",
287
299
  PyBinaryOp::Or => "or",
288
300
  });
@@ -476,22 +488,7 @@ impl SPyExpr<'_> {
476
488
  PyExpr::Literal(literal) => literal.emit_to(ctx, parent_precendence)?,
477
489
  PyExpr::Fstr(fstr_parts) => {
478
490
  ctx.emit("f\"");
479
- for part in fstr_parts.iter_mut() {
480
- match part {
481
- PyFstrPart::Str(s) => {
482
- ctx.emit_escaped_fstr(s);
483
- }
484
- PyFstrPart::Expr(expr, fmt) => {
485
- ctx.emit("{");
486
- expr.emit_sided_to(ctx, HIGH_PREC, true)?;
487
- if let Some(fmt) = fmt {
488
- ctx.emit(":");
489
- ctx.emit(fmt);
490
- }
491
- ctx.emit("}");
492
- }
493
- }
494
- }
491
+ emit_fstr_inner(ctx, fstr_parts)?;
495
492
  ctx.emit("\"");
496
493
  }
497
494
  PyExpr::Slice(start, stop, step) => {
@@ -547,6 +544,34 @@ impl SPyExpr<'_> {
547
544
  }
548
545
  }
549
546
 
547
+ fn emit_fstr_inner(ctx: &mut EmitCtx, fstr_parts: &mut [PyFstrPart<'_>]) -> TlResult<()> {
548
+ for part in fstr_parts.iter_mut() {
549
+ match part {
550
+ PyFstrPart::Str(s) => {
551
+ ctx.emit_escaped_fstr(s);
552
+ }
553
+ PyFstrPart::Expr(expr, fmt) => {
554
+ ctx.emit("{");
555
+ expr.emit_sided_to(ctx, HIGH_PREC, true)?;
556
+
557
+ if let Some(fmt) = fmt {
558
+ ctx.emit(":");
559
+ let PyExpr::Fstr(fmt_parts) = &mut fmt.value else {
560
+ return Err(TlErrBuilder::new()
561
+ .message("Format specifier must be an fstr")
562
+ .span(fmt.tl_span)
563
+ .build());
564
+ };
565
+ emit_fstr_inner(ctx, fmt_parts)?;
566
+ }
567
+ ctx.emit("}");
568
+ }
569
+ }
570
+ }
571
+
572
+ Ok(())
573
+ }
574
+
550
575
  impl PyBlock<'_> {
551
576
  pub fn emit_to(&mut self, ctx: &mut EmitCtx, delta_indentation: i32) -> TlResult<()> {
552
577
  let old_indentation = ctx.indentation;
@@ -376,7 +376,7 @@ impl PyAstBuilder {
376
376
  pub fn fstr_expr<'src>(
377
377
  &self,
378
378
  expr: SPyExpr<'src>,
379
- format_spec: impl Into<Option<PyIdent<'src>>>,
379
+ format_spec: impl Into<Option<SPyExpr<'src>>>,
380
380
  ) -> PyFstrPart<'src> {
381
381
  PyFstrPart::Expr(expr, format_spec.into())
382
382
  }
@@ -1319,7 +1319,7 @@ impl<'src> SExprExt<'src> for Indirect<SExpr<'src>> {
1319
1319
  (
1320
1320
  FmtExpr {
1321
1321
  expr: expr.expr.traverse(state),
1322
- fmt: expr.fmt,
1322
+ fmt: expr.fmt.map(|x| x.traverse(state)),
1323
1323
  },
1324
1324
  suffix,
1325
1325
  )
@@ -1348,10 +1348,7 @@ fn transform_postfix_expr<'src, 'ast>(
1348
1348
  }
1349
1349
  Expr::ScopedAttribute(_, rhs) | Expr::MappedScopedAttribute(_, rhs) => {
1350
1350
  let t = inner_pre.bind(rhs.transform(ctx)?);
1351
- a.call(
1352
- a.tl_builtin("partial"),
1353
- vec![PyCallItem::Arg(t), PyCallItem::Arg(lhs)],
1354
- )
1351
+ a.call(t, vec![PyCallItem::Arg(lhs)])
1355
1352
  }
1356
1353
  Expr::RawAttribute(_, attr) | Expr::MappedRawAttribute(_, attr) => {
1357
1354
  a.attribute(lhs, attr.value.escape(), access_ctx)
@@ -2168,10 +2165,14 @@ impl<'src, 'ast> SExprExt<'src, 'ast> for SExpr<'src> {
2168
2165
  nodes.push(PyFstrPart::Str(begin.value.clone().into()));
2169
2166
 
2170
2167
  for (fmt_expr, str_part) in parts {
2171
- // TODO format specifiers?
2172
2168
  let expr = pre.bind(fmt_expr.expr.transform(ctx)?);
2169
+ let fmt = fmt_expr
2170
+ .fmt
2171
+ .as_ref()
2172
+ .map(|x| -> TlResult<_> { Ok(pre.bind(x.transform(ctx)?)) })
2173
+ .transpose()?;
2173
2174
 
2174
- nodes.push(PyFstrPart::Expr(expr, None));
2175
+ nodes.push(PyFstrPart::Expr(expr, fmt));
2175
2176
  nodes.push(PyFstrPart::Str(str_part.value.clone().into()));
2176
2177
  }
2177
2178
 
@@ -2225,6 +2226,7 @@ fn map_py_binary_op(op: BinaryOp, span: Span) -> TlResult<PyBinaryOp> {
2225
2226
  BinaryOp::Sub => PyBinaryOp::Sub,
2226
2227
  BinaryOp::Mul => PyBinaryOp::Mult,
2227
2228
  BinaryOp::Div => PyBinaryOp::Div,
2229
+ BinaryOp::FloorDiv => PyBinaryOp::FloorDiv,
2228
2230
  BinaryOp::Mod => PyBinaryOp::Mod,
2229
2231
  BinaryOp::Exp => PyBinaryOp::Pow,
2230
2232
  BinaryOp::MatMul => PyBinaryOp::MatMult,
@@ -2237,6 +2239,8 @@ fn map_py_binary_op(op: BinaryOp, span: Span) -> TlResult<PyBinaryOp> {
2237
2239
  BinaryOp::Neq => PyBinaryOp::Neq,
2238
2240
  BinaryOp::Is => PyBinaryOp::Is,
2239
2241
  BinaryOp::Nis => PyBinaryOp::Nis,
2242
+ BinaryOp::In => PyBinaryOp::In,
2243
+ BinaryOp::Nin => PyBinaryOp::Nin,
2240
2244
 
2241
2245
  _ => return Err(simple_err("Internal error: Unsupported binary op", span)),
2242
2246
  })
@@ -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,6 +1,6 @@
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
6
  iter = Abstract
@@ -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 =>
@@ -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
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes