koatl 0.1.25__tar.gz → 0.1.27__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 (119) hide show
  1. {koatl-0.1.25 → koatl-0.1.27}/Cargo.lock +1 -1
  2. {koatl-0.1.25 → koatl-0.1.27}/PKG-INFO +1 -1
  3. {koatl-0.1.25 → koatl-0.1.27}/koatl/Cargo.toml +1 -1
  4. {koatl-0.1.25 → koatl-0.1.27}/koatl/python/koatl/prelude/functional/__init__.tl +1 -1
  5. koatl-0.1.27/koatl/python/koatl/prelude/functional/algebra.tl +46 -0
  6. {koatl-0.1.25 → koatl-0.1.27}/koatl/python/koatl/prelude/functional/async.tl +3 -3
  7. {koatl-0.1.25 → koatl-0.1.27/koatl}/python/koatl/prelude/functional/list.tl +1 -1
  8. {koatl-0.1.25 → koatl-0.1.27}/koatl/python/koatl/prelude/functional/memo.tl +3 -5
  9. {koatl-0.1.25 → koatl-0.1.27/koatl}/python/koatl/prelude/functional/reader.tl +2 -2
  10. {koatl-0.1.25 → koatl-0.1.27/koatl}/python/koatl/prelude/functional/result.tl +2 -2
  11. {koatl-0.1.25 → koatl-0.1.27/koatl}/python/koatl/runtime/virtual.py +2 -1
  12. {koatl-0.1.25 → koatl-0.1.27}/koatl/src/emit_py.rs +28 -2
  13. {koatl-0.1.25 → koatl-0.1.27}/koatl/tests/e2e/base/destructure.tl +1 -7
  14. koatl-0.1.27/koatl/tests/e2e/base/record.tl +24 -0
  15. {koatl-0.1.25 → koatl-0.1.27}/koatl/tests/e2e/base/scopes.tl +12 -0
  16. koatl-0.1.27/koatl/tests/e2e/prelude/aug_assign.tl +13 -0
  17. {koatl-0.1.25 → koatl-0.1.27}/koatl/tests/e2e/prelude/memo.tl +17 -0
  18. {koatl-0.1.25 → koatl-0.1.27}/koatl/tests/e2e/prelude/reader.tl +1 -1
  19. {koatl-0.1.25 → koatl-0.1.27}/koatl-core/parser/src/ast.rs +3 -1
  20. {koatl-0.1.25 → koatl-0.1.27}/koatl-core/parser/src/lexer.rs +2 -1
  21. {koatl-0.1.25 → koatl-0.1.27}/koatl-core/parser/src/parser.rs +91 -65
  22. {koatl-0.1.25 → koatl-0.1.27}/koatl-core/parser/src/util.rs +2 -2
  23. {koatl-0.1.25 → koatl-0.1.27}/koatl-core/src/inference.rs +5 -1
  24. {koatl-0.1.25 → koatl-0.1.27}/koatl-core/src/py/ast.rs +1 -1
  25. {koatl-0.1.25 → koatl-0.1.27}/koatl-core/src/py/emit.rs +9 -8
  26. {koatl-0.1.25 → koatl-0.1.27}/koatl-core/src/py/util.rs +10 -1
  27. {koatl-0.1.25 → koatl-0.1.27}/koatl-core/src/resolve_scopes.rs +112 -120
  28. {koatl-0.1.25 → koatl-0.1.27}/koatl-core/src/transform.rs +197 -407
  29. {koatl-0.1.25 → koatl-0.1.27}/python/koatl/prelude/functional/__init__.tl +1 -1
  30. koatl-0.1.27/python/koatl/prelude/functional/algebra.tl +46 -0
  31. {koatl-0.1.25 → koatl-0.1.27}/python/koatl/prelude/functional/async.tl +3 -3
  32. {koatl-0.1.25/koatl → koatl-0.1.27}/python/koatl/prelude/functional/list.tl +1 -1
  33. {koatl-0.1.25 → koatl-0.1.27}/python/koatl/prelude/functional/memo.tl +3 -5
  34. {koatl-0.1.25/koatl → koatl-0.1.27}/python/koatl/prelude/functional/reader.tl +2 -2
  35. {koatl-0.1.25/koatl → koatl-0.1.27}/python/koatl/prelude/functional/result.tl +2 -2
  36. {koatl-0.1.25/koatl → koatl-0.1.27}/python/koatl/runtime/virtual.py +2 -1
  37. koatl-0.1.25/koatl/python/koatl/prelude/functional/monad.tl +0 -21
  38. koatl-0.1.25/python/koatl/prelude/functional/monad.tl +0 -21
  39. {koatl-0.1.25 → koatl-0.1.27}/Cargo.toml +0 -0
  40. {koatl-0.1.25 → koatl-0.1.27}/README.md +0 -0
  41. {koatl-0.1.25 → koatl-0.1.27}/koatl/.github/workflows/CI.yml +0 -0
  42. {koatl-0.1.25 → koatl-0.1.27}/koatl/.gitignore +0 -0
  43. {koatl-0.1.25 → koatl-0.1.27}/koatl/LICENSE +0 -0
  44. {koatl-0.1.25 → koatl-0.1.27}/koatl/README.md +0 -0
  45. {koatl-0.1.25 → koatl-0.1.27}/koatl/python/koatl/__init__.py +0 -0
  46. {koatl-0.1.25 → koatl-0.1.27}/koatl/python/koatl/__main__.py +0 -0
  47. {koatl-0.1.25 → koatl-0.1.27}/koatl/python/koatl/cli.py +0 -0
  48. {koatl-0.1.25 → koatl-0.1.27}/koatl/python/koatl/notebook/__init__.py +0 -0
  49. {koatl-0.1.25 → koatl-0.1.27}/koatl/python/koatl/notebook/magic.py +0 -0
  50. {koatl-0.1.25 → koatl-0.1.27}/koatl/python/koatl/prelude/__init__.tl +0 -0
  51. {koatl-0.1.25 → koatl-0.1.27}/koatl/python/koatl/prelude/functional/async_util.py +0 -0
  52. {koatl-0.1.25 → koatl-0.1.27}/koatl/python/koatl/prelude/iterable.tl +0 -0
  53. {koatl-0.1.25 → koatl-0.1.27}/koatl/python/koatl/runtime/__init__.py +0 -0
  54. {koatl-0.1.25 → koatl-0.1.27}/koatl/python/koatl/runtime/helpers.py +0 -0
  55. {koatl-0.1.25 → koatl-0.1.27}/koatl/python/koatl/runtime/meta_finder.py +0 -0
  56. {koatl-0.1.25 → koatl-0.1.27}/koatl/python/koatl/runtime/record.py +0 -0
  57. {koatl-0.1.25 → koatl-0.1.27}/koatl/requirements.txt +0 -0
  58. {koatl-0.1.25 → koatl-0.1.27}/koatl/src/lib.rs +0 -0
  59. {koatl-0.1.25 → koatl-0.1.27}/koatl/tests/e2e/base/coal.tl +0 -0
  60. {koatl-0.1.25 → koatl-0.1.27}/koatl/tests/e2e/base/containers.tl +0 -0
  61. {koatl-0.1.25 → koatl-0.1.27}/koatl/tests/e2e/base/decorators.tl +0 -0
  62. {koatl-0.1.25 → koatl-0.1.27}/koatl/tests/e2e/base/destructure-for-and-fn.tl +0 -0
  63. {koatl-0.1.25 → koatl-0.1.27}/koatl/tests/e2e/base/escape_ident.tl +0 -0
  64. {koatl-0.1.25 → koatl-0.1.27}/koatl/tests/e2e/base/fstr.tl +0 -0
  65. {koatl-0.1.25 → koatl-0.1.27}/koatl/tests/e2e/base/functions.tl +0 -0
  66. {koatl-0.1.25 → koatl-0.1.27}/koatl/tests/e2e/base/generator.tl +0 -0
  67. {koatl-0.1.25 → koatl-0.1.27}/koatl/tests/e2e/base/if_expr.tl +0 -0
  68. {koatl-0.1.25 → koatl-0.1.27}/koatl/tests/e2e/base/imports.tl +0 -0
  69. {koatl-0.1.25 → koatl-0.1.27}/koatl/tests/e2e/base/loops.tl +0 -0
  70. {koatl-0.1.25 → koatl-0.1.27}/koatl/tests/e2e/base/match.tl +0 -0
  71. {koatl-0.1.25 → koatl-0.1.27}/koatl/tests/e2e/base/nary-list.tl +0 -0
  72. {koatl-0.1.25 → koatl-0.1.27}/koatl/tests/e2e/base/placeholder.tl +0 -0
  73. {koatl-0.1.25 → koatl-0.1.27}/koatl/tests/e2e/base/precedence.tl +0 -0
  74. {koatl-0.1.25 → koatl-0.1.27}/koatl/tests/e2e/base/semantic_whitespace.tl +0 -0
  75. {koatl-0.1.25 → koatl-0.1.27}/koatl/tests/e2e/base/short_circuit.tl +0 -0
  76. {koatl-0.1.25 → koatl-0.1.27}/koatl/tests/e2e/destructure.tl +0 -0
  77. {koatl-0.1.25 → koatl-0.1.27}/koatl/tests/e2e/prelude/async.tl +0 -0
  78. {koatl-0.1.25 → koatl-0.1.27}/koatl/tests/e2e/prelude/iterables.tl +0 -0
  79. {koatl-0.1.25 → koatl-0.1.27}/koatl/tests/e2e/prelude/list.tl +0 -0
  80. {koatl-0.1.25 → koatl-0.1.27}/koatl/tests/e2e/prelude/result.tl +0 -0
  81. {koatl-0.1.25 → koatl-0.1.27}/koatl/tests/e2e/prelude/slice.tl +0 -0
  82. {koatl-0.1.25 → koatl-0.1.27}/koatl/tests/e2e/prelude/try.tl +0 -0
  83. {koatl-0.1.25 → koatl-0.1.27}/koatl/tests/e2e/prelude/virtual.tl +0 -0
  84. {koatl-0.1.25 → koatl-0.1.27}/koatl/tests/e2e/util/__init__.py +0 -0
  85. {koatl-0.1.25 → koatl-0.1.27}/koatl/tests/e2e/util/module0.tl +0 -0
  86. {koatl-0.1.25 → koatl-0.1.27}/koatl/tests/e2e/util/module1.tl +0 -0
  87. {koatl-0.1.25 → koatl-0.1.27}/koatl/tests/e2e/util/module2.tl +0 -0
  88. {koatl-0.1.25 → koatl-0.1.27}/koatl/tests/parse/arith.tl +0 -0
  89. {koatl-0.1.25 → koatl-0.1.27}/koatl/tests/parse/assign.tl +0 -0
  90. {koatl-0.1.25 → koatl-0.1.27}/koatl/tests/parse/block-comments.tl +0 -0
  91. {koatl-0.1.25 → koatl-0.1.27}/koatl/tests/parse/deco.tl +0 -0
  92. {koatl-0.1.25 → koatl-0.1.27}/koatl/tests/parse/func.tl +0 -0
  93. {koatl-0.1.25 → koatl-0.1.27}/koatl/tests/parse/matches.tl +0 -0
  94. {koatl-0.1.25 → koatl-0.1.27}/koatl/tests/test_e2e.py +0 -0
  95. {koatl-0.1.25 → koatl-0.1.27}/koatl/tests/test_parse.py +0 -0
  96. {koatl-0.1.25 → koatl-0.1.27}/koatl-core/Cargo.toml +0 -0
  97. {koatl-0.1.25 → koatl-0.1.27}/koatl-core/parser/Cargo.toml +0 -0
  98. {koatl-0.1.25 → koatl-0.1.27}/koatl-core/parser/src/lib.rs +0 -0
  99. {koatl-0.1.25 → koatl-0.1.27}/koatl-core/parser/tests/lexer.rs +0 -0
  100. {koatl-0.1.25 → koatl-0.1.27}/koatl-core/src/lib.rs +0 -0
  101. {koatl-0.1.25 → koatl-0.1.27}/koatl-core/src/main.rs +0 -0
  102. {koatl-0.1.25 → koatl-0.1.27}/koatl-core/src/parse_timer.rs +0 -0
  103. {koatl-0.1.25 → koatl-0.1.27}/koatl-core/src/parser.rs +0 -0
  104. {koatl-0.1.25 → koatl-0.1.27}/koatl-core/src/py/mod.rs +0 -0
  105. {koatl-0.1.25 → koatl-0.1.27}/koatl-core/src/types.rs +0 -0
  106. {koatl-0.1.25 → koatl-0.1.27}/koatl-core/src/util.rs +0 -0
  107. {koatl-0.1.25 → koatl-0.1.27}/pyproject.toml +0 -0
  108. {koatl-0.1.25 → koatl-0.1.27}/python/koatl/__init__.py +0 -0
  109. {koatl-0.1.25 → koatl-0.1.27}/python/koatl/__main__.py +0 -0
  110. {koatl-0.1.25 → koatl-0.1.27}/python/koatl/cli.py +0 -0
  111. {koatl-0.1.25 → koatl-0.1.27}/python/koatl/notebook/__init__.py +0 -0
  112. {koatl-0.1.25 → koatl-0.1.27}/python/koatl/notebook/magic.py +0 -0
  113. {koatl-0.1.25 → koatl-0.1.27}/python/koatl/prelude/__init__.tl +0 -0
  114. {koatl-0.1.25 → koatl-0.1.27}/python/koatl/prelude/functional/async_util.py +0 -0
  115. {koatl-0.1.25 → koatl-0.1.27}/python/koatl/prelude/iterable.tl +0 -0
  116. {koatl-0.1.25 → koatl-0.1.27}/python/koatl/runtime/__init__.py +0 -0
  117. {koatl-0.1.25 → koatl-0.1.27}/python/koatl/runtime/helpers.py +0 -0
  118. {koatl-0.1.25 → koatl-0.1.27}/python/koatl/runtime/meta_finder.py +0 -0
  119. {koatl-0.1.25 → koatl-0.1.27}/python/koatl/runtime/record.py +0 -0
@@ -99,7 +99,7 @@ checksum = "f4c7245a08504955605670dbf141fceab975f15ca21570696aebe9d2e71576bd"
99
99
 
100
100
  [[package]]
101
101
  name = "koatl"
102
- version = "0.1.25"
102
+ version = "0.1.27"
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.25
3
+ Version: 0.1.27
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.25"
3
+ version = "0.1.27"
4
4
  edition = "2021"
5
5
 
6
6
  [lib]
@@ -1,4 +1,4 @@
1
- export import .monad.*
1
+ export import .algebra.*
2
2
  export import .result.*
3
3
  export import .async.*
4
4
  export import .reader.*
@@ -0,0 +1,46 @@
1
+ import abc
2
+
3
+ export Monad = class(abc.ABC):
4
+ """
5
+ Abstract base class for monads.
6
+
7
+ Subclasses must implement `bind` and `pure`.
8
+ Automatically provides standard monadic operations: bind_once, map, and apply.
9
+ """
10
+ __slots__ = ()
11
+
12
+ bind = abc.abstractmethod& (self, f) => None
13
+
14
+ pure = staticmethod& abc.abstractmethod& value => None
15
+
16
+ # Automatically generated implementations.
17
+ map = (self, f) => self.bind(x => self.pure(f(x)))
18
+ apply = (self, f) => self.bind(x => f.map(fn => fn(x)))
19
+
20
+
21
+ # TODO: should this inherit from Monad?
22
+ export MonadOnce = class(abc.ABC):
23
+ """
24
+ Abstract base class for single-use monads.
25
+
26
+ A MonadOnce can only be bound once, which is suitable for
27
+ use with Python generators that cannot be cloned or reused.
28
+
29
+ Subclasses must implement `bind_once` and `pure`.
30
+ Automatically provides (likely suboptimal) standard monadic operations: bind, map, and apply.
31
+ """
32
+ __slots__ = ()
33
+
34
+ # The default implementation required for `@` syntax that should be overridden by subclasses.
35
+ bind_once = abc.abstractmethod& (self, f) => None
36
+
37
+ pure = staticmethod& abc.abstractmethod& value => None
38
+
39
+ # An optional, optimized implementation of `bind` that skips deep recursion.
40
+ # TODO: can this be automatically generated from bind_once?
41
+ # bind_gen = (self, gen) => ...
42
+
43
+ # Automatically generated implementations.
44
+ bind = (self, f) => self.bind_once(f)
45
+ map = (self, f) => self.bind(x => self.pure(f(x)))
46
+ apply = (self, f) => self.bind(x => f.map(fn => fn(x)))
@@ -2,9 +2,9 @@ import functools.wraps
2
2
  import asyncio
3
3
 
4
4
  import .async_util
5
- import .monad.Monad
5
+ import .MonadOnce
6
6
 
7
- export Async = class(Monad):
7
+ export Async = class(MonadOnce):
8
8
  __init__ = (self, awaitable) => self.generator = awaitable.__await__()
9
9
 
10
10
  __await__ = self => self.generator
@@ -39,5 +39,5 @@ export Async = class(Monad):
39
39
 
40
40
  sleep = staticmethod& x => Async(asyncio.sleep(x))
41
41
 
42
- #- TODO: Why is asyncio.gather eager? -#
42
+ # TODO: Why is asyncio.gather eager?
43
43
  gather = staticmethod& (*args) => Async.from_generator_fn& () => yield from asyncio.gather(*args)
@@ -1,7 +1,7 @@
1
1
  import collections.UserList
2
2
  import itertools
3
3
 
4
- import .monad.Monad
4
+ import .Monad
5
5
  import ..iterable.Iterable
6
6
 
7
7
  export List = class(UserList, Monad):
@@ -1,18 +1,16 @@
1
1
  import collections.defaultdict
2
2
  import functools.wraps
3
3
  import .result.(Result, Ok)
4
- import .Monad
4
+ import .MonadOnce
5
5
 
6
- export Memo = class(Monad):
6
+ export Memo = class(MonadOnce):
7
7
  Cache = class:
8
8
  __init__ = self =>
9
9
  self.cache = defaultdict(dict)
10
10
 
11
11
  __repr__ = self => f"Memo.Cache({self.cache})"
12
12
 
13
- try_get = (self, name, deps) =>
14
- # TODO should try operator return a Result directly?
15
- Result(try self.cache[name][deps] except KeyError())
13
+ try_get = (self, name, deps) => try self.cache[name][deps] except KeyError()
16
14
 
17
15
  update = (self, name, deps, value) =>
18
16
  self.cache[name][deps] = value
@@ -1,7 +1,7 @@
1
1
  import functools.wraps
2
- import .monad.Monad
2
+ import .MonadOnce
3
3
 
4
- export Reader = class(Monad):
4
+ export Reader = class(MonadOnce):
5
5
  __init__ = (self, fn) => self.fn = fn
6
6
 
7
7
  __repr__ = self => "Reader(...)"
@@ -1,8 +1,8 @@
1
1
  import functools.wraps
2
- import .monad.Monad
3
2
  import koatl.runtime.virtual.register_global_attr
3
+ import .algebra.MonadOnce
4
4
 
5
- export Result = class:
5
+ export Result = class(MonadOnce):
6
6
  __slots__ = ()
7
7
  __match_args__ = ("value",)
8
8
 
@@ -1,3 +1,4 @@
1
+ import abc
1
2
  from functools import partial
2
3
  from itertools import count
3
4
  from types import new_class
@@ -105,7 +106,7 @@ def Trait(module, name, methods, *, requires=[]):
105
106
 
106
107
  meta = type(
107
108
  f"{name}Meta",
108
- (type,),
109
+ (abc.ABCMeta,),
109
110
  {"__instancecheck__": instancecheck, "__module__": "types"},
110
111
  )
111
112
 
@@ -279,10 +279,36 @@ impl<'src> PyStmtExt<'src> for SPyStmt<'src> {
279
279
  .collect();
280
280
  ctx.ast_node("Match", (subject_ast, cases_ast?), &self.tl_span)
281
281
  }
282
- PyStmt::Assign(target, value) => {
282
+ PyStmt::Assign(target, value, op) => {
283
283
  let target_ast = target.emit_py(ctx)?;
284
284
  let value_ast = value.emit_py(ctx)?;
285
- ctx.ast_node("Assign", ([target_ast], value_ast), &self.tl_span)
285
+
286
+ if let Some(op) = op {
287
+ let py_op = match op {
288
+ PyBinaryOp::Add => "Add",
289
+ PyBinaryOp::Sub => "Sub",
290
+ PyBinaryOp::Mult => "Mult",
291
+ PyBinaryOp::Div => "Div",
292
+ _ => {
293
+ return Err(PyTlErr {
294
+ message: format!(
295
+ "Unsupported augmented assignment operator: {:?}",
296
+ op
297
+ ),
298
+ py_err: None,
299
+ span: Some(self.tl_span),
300
+ });
301
+ }
302
+ };
303
+
304
+ ctx.ast_node(
305
+ "AugAssign",
306
+ (target_ast, ctx.ast_cls(py_op, ())?, value_ast),
307
+ &self.tl_span,
308
+ )
309
+ } else {
310
+ ctx.ast_node("Assign", ([target_ast], value_ast), &self.tl_span)
311
+ }
286
312
  }
287
313
  PyStmt::Return(expr) => {
288
314
  let expr_ast = expr.emit_py(ctx)?;
@@ -48,11 +48,5 @@ assert_eq([a, b, c], ["a", [2, 3], {2: "2", 3: "3"}])
48
48
  {a, **b} = {a: 42, b: 43}
49
49
  assert_eq([a, b], [42, {b: 43}])
50
50
 
51
- a, *b, c = 1, 2, 3, 4
52
- assert_eq([a, b, c], [1, (2, 3), 4])
53
-
54
51
  a, [*b], c, *d = 1, [2, 3], 4, 5, 6
55
- assert_eq([a, b, c, d], [1, [2, 3], 4, (5, 6)])
56
-
57
- # destructuring modules
58
- {assert_eq} = util
52
+ assert_eq([a, b, c, d], [1, [2, 3], 4, [5, 6]])
@@ -0,0 +1,24 @@
1
+ import util.assert_eq
2
+
3
+ assert_eq(
4
+ {a: 0}.keys() | list
5
+ ["a"]
6
+ )
7
+
8
+ assert_eq(
9
+ {(): 0}.keys() | list
10
+ [()]
11
+ )
12
+
13
+ assert_eq(
14
+ {(1): 0}.keys() | list
15
+ [1]
16
+ )
17
+
18
+ assert_eq(
19
+ {(
20
+ let x = 1
21
+ x
22
+ ): 0}.keys() | list
23
+ [1]
24
+ )
@@ -89,4 +89,16 @@ f = () =>
89
89
 
90
90
  A().f()
91
91
  assert_eq(A, 2)
92
+ f()
93
+
94
+ # Shadowing should work
95
+ f = () =>
96
+ let x = 1
97
+ let x = x + 1
98
+ assert_eq(x, 2)
99
+
100
+ # but recursion should also work
101
+ let x = 3
102
+ let x = () => x.__name__
103
+ assert_eq(type(x()), str)
92
104
  f()
@@ -0,0 +1,13 @@
1
+ import util.assert_eq
2
+
3
+ x = 1
4
+ x += 2
5
+ assert_eq(x, 3)
6
+
7
+ x = None
8
+ x ??= 2
9
+ assert_eq(x, 2)
10
+
11
+ x = 4
12
+ x |= $ + 2
13
+ assert_eq(x, 6)
@@ -29,3 +29,20 @@ assert_eq(f(10).run(ctx), 20)
29
29
  assert_eq(f(5).run(ctx), 10)
30
30
  assert_eq(f(5).run(ctx), 10)
31
31
  assert_eq(counts, 2)
32
+
33
+ # Indirect dependencies should be tracked as well.
34
+
35
+ counts = 0
36
+
37
+ f = (x, y) =>
38
+ memo:
39
+ counts = counts + 1
40
+ let other = () => y
41
+ x * other()
42
+
43
+ ctx = Memo.Cache()
44
+ assert_eq(f(10, 20).run(ctx), 200)
45
+ assert_eq(f(10, 20).run(ctx), 200)
46
+ assert_eq(f(10, 30).run(ctx), 300)
47
+ assert_eq(f(10, 30).run(ctx), 300)
48
+ assert_eq(counts, 2)
@@ -1,6 +1,6 @@
1
1
  import util.assert_eq
2
2
 
3
- {ask} = Reader
3
+ ask = Reader.ask
4
4
 
5
5
  g = () =>
6
6
  @ask("c")
@@ -121,7 +121,9 @@ pub enum DeclType {
121
121
  #[derive(Debug, Clone)]
122
122
  pub enum Stmt<'a, TTree: Tree> {
123
123
  Decl(Vec<SIdent<'a>>, DeclType),
124
- Assign(TTree::Expr, TTree::Expr, Option<DeclType>),
124
+ Assign(TTree::Expr, TTree::Expr, Option<BinaryOp>),
125
+ PatternAssign(TTree::Pattern, TTree::Expr, Option<DeclType>),
126
+
125
127
  Expr(TTree::Expr),
126
128
 
127
129
  Return(TTree::Expr),
@@ -272,7 +272,8 @@ where
272
272
 
273
273
  fn parse_symbol(&mut self) -> TResult<'src, Spanned<Token<'src>>> {
274
274
  const POLYGRAMS: &[&str] = &[
275
- "===", "<=>", "=>", "..", "==", "<>", "<=", ">=", "//", "**", "??", ".=",
275
+ "+=", "-=", "*=", "/=", "|=", "??=", "===", "<=>", "=>", "..", "==", "<>", "<=", ">=",
276
+ "//", "**", "??", ".=",
276
277
  ];
277
278
  const MONOGRAMS: &str = "[](){}<>.,;:!?@$%^&*+-=|\\/`~";
278
279
 
@@ -123,10 +123,9 @@ where
123
123
  just(Token::Symbol(symbol))
124
124
  }
125
125
 
126
- pub fn match_pattern<'tokens, 'src: 'tokens, TInput, PIdent, PQualIdent, PExpr, PLiteral>(
126
+ pub fn match_pattern<'tokens, 'src: 'tokens, TInput, PIdent, PQualIdent, PLiteral>(
127
127
  ident: PIdent,
128
128
  qualified_ident: PQualIdent,
129
- sexpr: PExpr,
130
129
  literal: PLiteral,
131
130
  ) -> (
132
131
  impl Parser<'tokens, TInput, SPattern<'src>, TExtra<'tokens, 'src>> + Clone,
@@ -137,7 +136,6 @@ where
137
136
  TInput: ValueInput<'tokens, Token = Token<'src>, Span = Span>,
138
137
  PIdent: Parser<'tokens, TInput, SIdent<'src>, TExtra<'tokens, 'src>> + Clone + 'tokens,
139
138
  PQualIdent: Parser<'tokens, TInput, SExpr<'src>, TExtra<'tokens, 'src>> + Clone + 'tokens,
140
- PExpr: Parser<'tokens, TInput, SExpr<'src>, TExtra<'tokens, 'src>> + Clone + 'tokens,
141
139
  PLiteral: Parser<'tokens, TInput, SLiteral<'src>, TExtra<'tokens, 'src>> + Clone + 'tokens,
142
140
  {
143
141
  let mut pattern =
@@ -153,24 +151,16 @@ where
153
151
  if id.value.0 == "_" { None } else { Some(id) }
154
152
  }
155
153
 
154
+ let capture_pattern = ident
155
+ .clone()
156
+ .map(to_wildcard)
157
+ .map(Pattern::Capture)
158
+ .spanned_pattern()
159
+ .boxed();
160
+
156
161
  let value_pattern = symbol(".")
157
- .to(1)
158
- .or_not()
159
- .then(qualified_ident.clone())
160
- .try_map(|(q, value), _e| {
161
- Ok(if let Expr::RawAttribute(..) = value.value {
162
- Pattern::Value(value.indirect())
163
- } else if q.is_some() {
164
- Pattern::Value(value.indirect())
165
- } else if let Expr::Ident(id) = value.value {
166
- Pattern::Capture(to_wildcard(id))
167
- } else {
168
- return Err(Rich::custom(
169
- value.span,
170
- "Internal error: value pattern must be an identifier or attribute",
171
- ));
172
- })
173
- })
162
+ .ignore_then(qualified_ident.clone())
163
+ .map(|value| Pattern::Value(value.indirect()))
174
164
  .spanned_pattern()
175
165
  .boxed();
176
166
 
@@ -210,7 +200,6 @@ where
210
200
  if items.len() == 1 && last_comma.is_none() {
211
201
  let item = items.into_iter().next().unwrap();
212
202
  if let PatternSequenceItem::Item(inner) = item {
213
- // TODO ???
214
203
  inner.extract()
215
204
  } else {
216
205
  Pattern::Sequence(vec![item]).spanned(e.span())
@@ -231,9 +220,9 @@ where
231
220
  .map(|id| Expr::Literal(Literal::Str(id.value.0).spanned(id.span)))
232
221
  .spanned_expr(),
233
222
  literal.clone().map(Expr::Literal).spanned_expr(),
234
- sexpr
223
+ qualified_ident
235
224
  .clone()
236
- .delimited_by_with_eol(symbol("("), symbol(")")),
225
+ .delimited_by(symbol("("), symbol(")")),
237
226
  ))
238
227
  .then_ignore(symbol(":"))
239
228
  .then(pattern.clone())
@@ -276,6 +265,7 @@ where
276
265
  let closed_pattern = choice((
277
266
  literal_pattern,
278
267
  class_pattern,
268
+ capture_pattern,
279
269
  value_pattern.clone(),
280
270
  group_pattern,
281
271
  sequence_pattern,
@@ -477,12 +467,23 @@ where
477
467
  })
478
468
  }
479
469
 
480
- pub fn statement<'tokens, 'src: 'tokens, TInput, PBody, PTuple, PExpr, PIdent, PPattern>(
470
+ pub fn statement<
471
+ 'tokens,
472
+ 'src: 'tokens,
473
+ TInput,
474
+ PBody,
475
+ PTuple,
476
+ PExpr,
477
+ PIdent,
478
+ PNaryPattern,
479
+ PPattern,
480
+ >(
481
481
  expr_or_inline_stmt_or_block: PBody,
482
482
  nary_tuple: PTuple,
483
483
  expr: PExpr,
484
484
  ident: PIdent,
485
- nary_pattern: PPattern,
485
+ nary_pattern: PNaryPattern,
486
+ pattern: PPattern,
486
487
  ) -> (
487
488
  impl Parser<'tokens, TInput, SStmt<'src>, TExtra<'tokens, 'src>> + Clone,
488
489
  impl Parser<'tokens, TInput, SStmt<'src>, TExtra<'tokens, 'src>> + Clone,
@@ -493,6 +494,7 @@ where
493
494
  PTuple: Parser<'tokens, TInput, SExpr<'src>, TExtra<'tokens, 'src>> + Clone + 'tokens,
494
495
  PExpr: Parser<'tokens, TInput, SExpr<'src>, TExtra<'tokens, 'src>> + Clone + 'tokens,
495
496
  PIdent: Parser<'tokens, TInput, SIdent<'src>, TExtra<'tokens, 'src>> + Clone + 'tokens,
497
+ PNaryPattern: Parser<'tokens, TInput, SPattern<'src>, TExtra<'tokens, 'src>> + Clone + 'tokens,
496
498
  PPattern: Parser<'tokens, TInput, SPattern<'src>, TExtra<'tokens, 'src>> + Clone + 'tokens,
497
499
  {
498
500
  let mut stmt = Recursive::<chumsky::recursive::Indirect<TInput, SStmt, TExtra>>::declare();
@@ -512,33 +514,55 @@ where
512
514
  .map(|(decl, idents)| SStmtInner::Decl(idents, decl))
513
515
  .boxed();
514
516
 
515
- let assign_lhs = nary_tuple.clone();
516
- let inline_assign_lhs = expr.clone();
517
-
518
- let assign_stmt = group((
517
+ let pattern_assign_stmt = group((
519
518
  decl_mod.clone().or_not(),
520
- assign_lhs.clone(),
519
+ nary_pattern.clone(),
521
520
  symbol("=").ignore_then(nary_tuple.clone()),
522
521
  ))
523
- .map(|(decl, lhs, rhs)| SStmtInner::Assign(lhs.indirect(), rhs.indirect(), decl))
522
+ .map(|(decl, pat, rhs)| SStmtInner::PatternAssign(pat.indirect(), rhs.indirect(), decl))
524
523
  .boxed();
525
524
 
526
- let inline_assign_stmt = group((
525
+ let inline_pattern_assign_stmt = group((
527
526
  decl_mod.clone().or_not(),
528
- inline_assign_lhs.clone(),
527
+ pattern.clone(),
529
528
  symbol("=").ignore_then(expr.clone()),
530
529
  ))
531
- .map(|(decl, lhs, rhs)| SStmtInner::Assign(lhs.indirect(), rhs.indirect(), decl))
530
+ .map(|(decl, pat, rhs)| SStmtInner::PatternAssign(pat.indirect(), rhs.indirect(), decl))
532
531
  .boxed();
533
532
 
534
- let expr_stmt = assign_lhs
535
- .clone()
536
- .map(|x| SStmtInner::Expr(x.indirect()))
537
- .boxed();
533
+ let aug_op = choice((
534
+ symbol("+=").to(BinaryOp::Add),
535
+ symbol("-=").to(BinaryOp::Sub),
536
+ symbol("*=").to(BinaryOp::Mul),
537
+ symbol("/=").to(BinaryOp::Div),
538
+ symbol("|=").to(BinaryOp::Pipe),
539
+ symbol("??=").to(BinaryOp::Coalesce),
540
+ ))
541
+ .map(Some);
538
542
 
539
- let inline_expr_stmt = inline_assign_lhs
540
- .clone()
541
- .map(|x| SStmtInner::Expr(x.indirect()))
543
+ let assign_op = choice((aug_op, symbol("=").to(None)));
544
+
545
+ let assign_stmt = group((
546
+ nary_tuple.clone(),
547
+ assign_op.clone().then(nary_tuple.clone()).or_not(),
548
+ ))
549
+ .map(|(lhs, rhs)| {
550
+ if let Some((op, rhs)) = rhs {
551
+ SStmtInner::Assign(lhs.indirect(), rhs.indirect(), op)
552
+ } else {
553
+ SStmtInner::Expr(lhs.indirect())
554
+ }
555
+ })
556
+ .boxed();
557
+
558
+ let inline_assign_stmt = group((expr.clone(), assign_op.clone().then(expr.clone()).or_not()))
559
+ .map(|(lhs, rhs)| {
560
+ if let Some((op, rhs)) = rhs {
561
+ SStmtInner::Assign(lhs.indirect(), rhs.indirect(), op)
562
+ } else {
563
+ SStmtInner::Expr(lhs.indirect())
564
+ }
565
+ })
542
566
  .boxed();
543
567
 
544
568
  let while_stmt = just(Token::Kw("while"))
@@ -690,8 +714,8 @@ where
690
714
  stmt.define(
691
715
  choice((
692
716
  decl_stmt.then_ignore(just(Token::Eol)),
717
+ pattern_assign_stmt.then_ignore(just(Token::Eol)),
693
718
  assign_stmt.then_ignore(just(Token::Eol)),
694
- expr_stmt.then_ignore(just(Token::Eol)),
695
719
  while_stmt.clone().then_ignore(just(Token::Eol)),
696
720
  for_stmt.clone().then_ignore(just(Token::Eol)),
697
721
  return_stmt.then_ignore(just(Token::Eol)),
@@ -709,8 +733,8 @@ where
709
733
 
710
734
  inline_stmt.define(
711
735
  choice((
736
+ inline_pattern_assign_stmt,
712
737
  inline_assign_stmt,
713
- inline_expr_stmt,
714
738
  while_stmt,
715
739
  for_stmt,
716
740
  inline_return_stmt,
@@ -862,6 +886,25 @@ where
862
886
  .boxed();
863
887
  // .memoized();
864
888
 
889
+ let tuple = choice((
890
+ symbol("(")
891
+ .then(symbol(")"))
892
+ .map(|_| Expr::Tuple(vec![]))
893
+ .spanned_expr(),
894
+ nary_tuple
895
+ .clone()
896
+ .delimited_by_with_eol(just(Token::Symbol("(")), just(Token::Symbol(")"))),
897
+ ))
898
+ .boxed();
899
+
900
+ let round_brackets = choice((
901
+ tuple.clone(),
902
+ block
903
+ .clone()
904
+ .delimited_by_with_eol(symbol("("), symbol(")")),
905
+ ))
906
+ .boxed();
907
+
865
908
  let mapping = enumeration(
866
909
  choice((
867
910
  symbol("**")
@@ -873,7 +916,7 @@ where
873
916
  .map(|id| Expr::Literal(Literal::Str(id.value.0).spanned(id.span)))
874
917
  .spanned_expr(),
875
918
  literal_expr.clone(),
876
- expr.clone().delimited_by_with_eol(symbol("("), symbol(")")),
919
+ round_brackets.clone(),
877
920
  ))
878
921
  .then_ignore(symbol(":"))
879
922
  .then(expr.clone())
@@ -974,12 +1017,8 @@ where
974
1017
  )
975
1018
  .boxed();
976
1019
 
977
- let (closed_pattern, as_pattern, nary_pattern) = match_pattern(
978
- ident.clone(),
979
- qualified_ident.clone(),
980
- expr.clone(),
981
- literal.clone(),
982
- );
1020
+ let (closed_pattern, as_pattern, nary_pattern) =
1021
+ match_pattern(ident.clone(), qualified_ident.clone(), literal.clone());
983
1022
 
984
1023
  let classic_match = just(Token::Kw("match"))
985
1024
  .ignore_then(expr.clone())
@@ -991,17 +1030,6 @@ where
991
1030
  .as_context()
992
1031
  .boxed();
993
1032
 
994
- let tuple = choice((
995
- symbol("(")
996
- .then(symbol(")"))
997
- .map(|_| Expr::Tuple(vec![]))
998
- .spanned_expr(),
999
- nary_tuple
1000
- .clone()
1001
- .delimited_by_with_eol(just(Token::Symbol("(")), just(Token::Symbol(")"))),
1002
- ))
1003
- .boxed();
1004
-
1005
1033
  enum ControlKw {
1006
1034
  Await,
1007
1035
  Yield,
@@ -1054,10 +1082,7 @@ where
1054
1082
  list.clone(),
1055
1083
  mapping,
1056
1084
  fstr,
1057
- tuple.clone(),
1058
- block
1059
- .clone()
1060
- .delimited_by_with_eol(symbol("("), symbol(")")),
1085
+ round_brackets.clone(),
1061
1086
  ))
1062
1087
  .labelled("atom"),
1063
1088
  );
@@ -1439,6 +1464,7 @@ where
1439
1464
  expr.clone(),
1440
1465
  ident.clone(),
1441
1466
  nary_pattern.clone(),
1467
+ as_pattern.clone(),
1442
1468
  );
1443
1469
 
1444
1470
  stmt.define(stmt_.labelled("statement").boxed());
@@ -33,9 +33,9 @@ impl AstBuilder {
33
33
  &self,
34
34
  target: impl Into<Indirect<SExpr<'src>>>,
35
35
  value: impl Into<Indirect<SExpr<'src>>>,
36
- modifier: DeclType,
36
+ aug_op: Option<BinaryOp>,
37
37
  ) -> SStmt<'src> {
38
- Stmt::Assign(target.into(), value.into(), Some(modifier)).spanned(self.span)
38
+ Stmt::Assign(target.into(), value.into(), aug_op).spanned(self.span)
39
39
  }
40
40
 
41
41
  pub fn return_<'src>(&self, expr: impl IntoIndirect<SExpr<'src>>) -> SStmt<'src> {
@@ -22,7 +22,11 @@ impl<'src, 'ast> SStmtExt<'src, 'ast> for Indirect<SStmt<'src>> {
22
22
  fn traverse(&'ast self, ctx: &mut InferenceCtx<'src, '_>) -> Type {
23
23
  match &self.value {
24
24
  Stmt::Expr(expr) => expr.traverse(ctx),
25
- Stmt::Assign(lhs, rhs, _decl_type) => {
25
+ Stmt::PatternAssign(_lhs, rhs, _decl_type) => {
26
+ rhs.traverse(ctx);
27
+ Type::NoReturn
28
+ }
29
+ Stmt::Assign(lhs, rhs, _op) => {
26
30
  lhs.traverse(ctx);
27
31
  rhs.traverse(ctx);
28
32
  Type::NoReturn
@@ -66,7 +66,7 @@ pub enum PyStmt<'a> {
66
66
  Expr(SPyExpr<'a>),
67
67
  If(SPyExpr<'a>, PyBlock<'a>, Option<PyBlock<'a>>),
68
68
  Match(SPyExpr<'a>, Vec<PyMatchCase<'a>>),
69
- Assign(SPyExpr<'a>, SPyExpr<'a>),
69
+ Assign(SPyExpr<'a>, SPyExpr<'a>, Option<PyBinaryOp>),
70
70
  Return(SPyExpr<'a>),
71
71
  Raise(Option<SPyExpr<'a>>),
72
72
  Assert(SPyExpr<'a>, Option<SPyExpr<'a>>),