koatl 0.1.26__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 (117) hide show
  1. {koatl-0.1.26 → koatl-0.1.27}/Cargo.lock +1 -1
  2. {koatl-0.1.26 → koatl-0.1.27}/PKG-INFO +1 -1
  3. {koatl-0.1.26 → koatl-0.1.27}/koatl/Cargo.toml +1 -1
  4. {koatl-0.1.26 → koatl-0.1.27}/koatl/src/emit_py.rs +28 -2
  5. {koatl-0.1.26 → koatl-0.1.27}/koatl/tests/e2e/base/destructure.tl +1 -7
  6. koatl-0.1.27/koatl/tests/e2e/base/record.tl +24 -0
  7. {koatl-0.1.26 → koatl-0.1.27}/koatl/tests/e2e/base/scopes.tl +12 -0
  8. koatl-0.1.27/koatl/tests/e2e/prelude/aug_assign.tl +13 -0
  9. {koatl-0.1.26 → koatl-0.1.27}/koatl/tests/e2e/prelude/memo.tl +17 -0
  10. {koatl-0.1.26 → koatl-0.1.27}/koatl/tests/e2e/prelude/reader.tl +1 -1
  11. {koatl-0.1.26 → koatl-0.1.27}/koatl-core/parser/src/ast.rs +3 -1
  12. {koatl-0.1.26 → koatl-0.1.27}/koatl-core/parser/src/lexer.rs +2 -1
  13. {koatl-0.1.26 → koatl-0.1.27}/koatl-core/parser/src/parser.rs +91 -64
  14. {koatl-0.1.26 → koatl-0.1.27}/koatl-core/parser/src/util.rs +2 -2
  15. {koatl-0.1.26 → koatl-0.1.27}/koatl-core/src/inference.rs +5 -1
  16. {koatl-0.1.26 → koatl-0.1.27}/koatl-core/src/py/ast.rs +1 -1
  17. {koatl-0.1.26 → koatl-0.1.27}/koatl-core/src/py/emit.rs +7 -2
  18. {koatl-0.1.26 → koatl-0.1.27}/koatl-core/src/py/util.rs +10 -1
  19. {koatl-0.1.26 → koatl-0.1.27}/koatl-core/src/resolve_scopes.rs +98 -105
  20. {koatl-0.1.26 → koatl-0.1.27}/koatl-core/src/transform.rs +192 -399
  21. {koatl-0.1.26 → koatl-0.1.27}/Cargo.toml +0 -0
  22. {koatl-0.1.26 → koatl-0.1.27}/README.md +0 -0
  23. {koatl-0.1.26 → koatl-0.1.27}/koatl/.github/workflows/CI.yml +0 -0
  24. {koatl-0.1.26 → koatl-0.1.27}/koatl/.gitignore +0 -0
  25. {koatl-0.1.26 → koatl-0.1.27}/koatl/LICENSE +0 -0
  26. {koatl-0.1.26 → koatl-0.1.27}/koatl/README.md +0 -0
  27. {koatl-0.1.26 → koatl-0.1.27}/koatl/python/koatl/__init__.py +0 -0
  28. {koatl-0.1.26 → koatl-0.1.27}/koatl/python/koatl/__main__.py +0 -0
  29. {koatl-0.1.26 → koatl-0.1.27}/koatl/python/koatl/cli.py +0 -0
  30. {koatl-0.1.26 → koatl-0.1.27}/koatl/python/koatl/notebook/__init__.py +0 -0
  31. {koatl-0.1.26 → koatl-0.1.27}/koatl/python/koatl/notebook/magic.py +0 -0
  32. {koatl-0.1.26 → koatl-0.1.27}/koatl/python/koatl/prelude/__init__.tl +0 -0
  33. {koatl-0.1.26 → koatl-0.1.27}/koatl/python/koatl/prelude/functional/__init__.tl +0 -0
  34. {koatl-0.1.26 → koatl-0.1.27}/koatl/python/koatl/prelude/functional/algebra.tl +0 -0
  35. {koatl-0.1.26 → koatl-0.1.27}/koatl/python/koatl/prelude/functional/async.tl +0 -0
  36. {koatl-0.1.26 → koatl-0.1.27}/koatl/python/koatl/prelude/functional/async_util.py +0 -0
  37. {koatl-0.1.26 → koatl-0.1.27}/koatl/python/koatl/prelude/functional/list.tl +0 -0
  38. {koatl-0.1.26 → koatl-0.1.27}/koatl/python/koatl/prelude/functional/memo.tl +0 -0
  39. {koatl-0.1.26 → koatl-0.1.27}/koatl/python/koatl/prelude/functional/reader.tl +0 -0
  40. {koatl-0.1.26 → koatl-0.1.27}/koatl/python/koatl/prelude/functional/result.tl +0 -0
  41. {koatl-0.1.26 → koatl-0.1.27}/koatl/python/koatl/prelude/iterable.tl +0 -0
  42. {koatl-0.1.26 → koatl-0.1.27}/koatl/python/koatl/runtime/__init__.py +0 -0
  43. {koatl-0.1.26 → koatl-0.1.27}/koatl/python/koatl/runtime/helpers.py +0 -0
  44. {koatl-0.1.26 → koatl-0.1.27}/koatl/python/koatl/runtime/meta_finder.py +0 -0
  45. {koatl-0.1.26 → koatl-0.1.27}/koatl/python/koatl/runtime/record.py +0 -0
  46. {koatl-0.1.26 → koatl-0.1.27}/koatl/python/koatl/runtime/virtual.py +0 -0
  47. {koatl-0.1.26 → koatl-0.1.27}/koatl/requirements.txt +0 -0
  48. {koatl-0.1.26 → koatl-0.1.27}/koatl/src/lib.rs +0 -0
  49. {koatl-0.1.26 → koatl-0.1.27}/koatl/tests/e2e/base/coal.tl +0 -0
  50. {koatl-0.1.26 → koatl-0.1.27}/koatl/tests/e2e/base/containers.tl +0 -0
  51. {koatl-0.1.26 → koatl-0.1.27}/koatl/tests/e2e/base/decorators.tl +0 -0
  52. {koatl-0.1.26 → koatl-0.1.27}/koatl/tests/e2e/base/destructure-for-and-fn.tl +0 -0
  53. {koatl-0.1.26 → koatl-0.1.27}/koatl/tests/e2e/base/escape_ident.tl +0 -0
  54. {koatl-0.1.26 → koatl-0.1.27}/koatl/tests/e2e/base/fstr.tl +0 -0
  55. {koatl-0.1.26 → koatl-0.1.27}/koatl/tests/e2e/base/functions.tl +0 -0
  56. {koatl-0.1.26 → koatl-0.1.27}/koatl/tests/e2e/base/generator.tl +0 -0
  57. {koatl-0.1.26 → koatl-0.1.27}/koatl/tests/e2e/base/if_expr.tl +0 -0
  58. {koatl-0.1.26 → koatl-0.1.27}/koatl/tests/e2e/base/imports.tl +0 -0
  59. {koatl-0.1.26 → koatl-0.1.27}/koatl/tests/e2e/base/loops.tl +0 -0
  60. {koatl-0.1.26 → koatl-0.1.27}/koatl/tests/e2e/base/match.tl +0 -0
  61. {koatl-0.1.26 → koatl-0.1.27}/koatl/tests/e2e/base/nary-list.tl +0 -0
  62. {koatl-0.1.26 → koatl-0.1.27}/koatl/tests/e2e/base/placeholder.tl +0 -0
  63. {koatl-0.1.26 → koatl-0.1.27}/koatl/tests/e2e/base/precedence.tl +0 -0
  64. {koatl-0.1.26 → koatl-0.1.27}/koatl/tests/e2e/base/semantic_whitespace.tl +0 -0
  65. {koatl-0.1.26 → koatl-0.1.27}/koatl/tests/e2e/base/short_circuit.tl +0 -0
  66. {koatl-0.1.26 → koatl-0.1.27}/koatl/tests/e2e/destructure.tl +0 -0
  67. {koatl-0.1.26 → koatl-0.1.27}/koatl/tests/e2e/prelude/async.tl +0 -0
  68. {koatl-0.1.26 → koatl-0.1.27}/koatl/tests/e2e/prelude/iterables.tl +0 -0
  69. {koatl-0.1.26 → koatl-0.1.27}/koatl/tests/e2e/prelude/list.tl +0 -0
  70. {koatl-0.1.26 → koatl-0.1.27}/koatl/tests/e2e/prelude/result.tl +0 -0
  71. {koatl-0.1.26 → koatl-0.1.27}/koatl/tests/e2e/prelude/slice.tl +0 -0
  72. {koatl-0.1.26 → koatl-0.1.27}/koatl/tests/e2e/prelude/try.tl +0 -0
  73. {koatl-0.1.26 → koatl-0.1.27}/koatl/tests/e2e/prelude/virtual.tl +0 -0
  74. {koatl-0.1.26 → koatl-0.1.27}/koatl/tests/e2e/util/__init__.py +0 -0
  75. {koatl-0.1.26 → koatl-0.1.27}/koatl/tests/e2e/util/module0.tl +0 -0
  76. {koatl-0.1.26 → koatl-0.1.27}/koatl/tests/e2e/util/module1.tl +0 -0
  77. {koatl-0.1.26 → koatl-0.1.27}/koatl/tests/e2e/util/module2.tl +0 -0
  78. {koatl-0.1.26 → koatl-0.1.27}/koatl/tests/parse/arith.tl +0 -0
  79. {koatl-0.1.26 → koatl-0.1.27}/koatl/tests/parse/assign.tl +0 -0
  80. {koatl-0.1.26 → koatl-0.1.27}/koatl/tests/parse/block-comments.tl +0 -0
  81. {koatl-0.1.26 → koatl-0.1.27}/koatl/tests/parse/deco.tl +0 -0
  82. {koatl-0.1.26 → koatl-0.1.27}/koatl/tests/parse/func.tl +0 -0
  83. {koatl-0.1.26 → koatl-0.1.27}/koatl/tests/parse/matches.tl +0 -0
  84. {koatl-0.1.26 → koatl-0.1.27}/koatl/tests/test_e2e.py +0 -0
  85. {koatl-0.1.26 → koatl-0.1.27}/koatl/tests/test_parse.py +0 -0
  86. {koatl-0.1.26 → koatl-0.1.27}/koatl-core/Cargo.toml +0 -0
  87. {koatl-0.1.26 → koatl-0.1.27}/koatl-core/parser/Cargo.toml +0 -0
  88. {koatl-0.1.26 → koatl-0.1.27}/koatl-core/parser/src/lib.rs +0 -0
  89. {koatl-0.1.26 → koatl-0.1.27}/koatl-core/parser/tests/lexer.rs +0 -0
  90. {koatl-0.1.26 → koatl-0.1.27}/koatl-core/src/lib.rs +0 -0
  91. {koatl-0.1.26 → koatl-0.1.27}/koatl-core/src/main.rs +0 -0
  92. {koatl-0.1.26 → koatl-0.1.27}/koatl-core/src/parse_timer.rs +0 -0
  93. {koatl-0.1.26 → koatl-0.1.27}/koatl-core/src/parser.rs +0 -0
  94. {koatl-0.1.26 → koatl-0.1.27}/koatl-core/src/py/mod.rs +0 -0
  95. {koatl-0.1.26 → koatl-0.1.27}/koatl-core/src/types.rs +0 -0
  96. {koatl-0.1.26 → koatl-0.1.27}/koatl-core/src/util.rs +0 -0
  97. {koatl-0.1.26 → koatl-0.1.27}/pyproject.toml +0 -0
  98. {koatl-0.1.26 → koatl-0.1.27}/python/koatl/__init__.py +0 -0
  99. {koatl-0.1.26 → koatl-0.1.27}/python/koatl/__main__.py +0 -0
  100. {koatl-0.1.26 → koatl-0.1.27}/python/koatl/cli.py +0 -0
  101. {koatl-0.1.26 → koatl-0.1.27}/python/koatl/notebook/__init__.py +0 -0
  102. {koatl-0.1.26 → koatl-0.1.27}/python/koatl/notebook/magic.py +0 -0
  103. {koatl-0.1.26 → koatl-0.1.27}/python/koatl/prelude/__init__.tl +0 -0
  104. {koatl-0.1.26 → koatl-0.1.27}/python/koatl/prelude/functional/__init__.tl +0 -0
  105. {koatl-0.1.26 → koatl-0.1.27}/python/koatl/prelude/functional/algebra.tl +0 -0
  106. {koatl-0.1.26 → koatl-0.1.27}/python/koatl/prelude/functional/async.tl +0 -0
  107. {koatl-0.1.26 → koatl-0.1.27}/python/koatl/prelude/functional/async_util.py +0 -0
  108. {koatl-0.1.26 → koatl-0.1.27}/python/koatl/prelude/functional/list.tl +0 -0
  109. {koatl-0.1.26 → koatl-0.1.27}/python/koatl/prelude/functional/memo.tl +0 -0
  110. {koatl-0.1.26 → koatl-0.1.27}/python/koatl/prelude/functional/reader.tl +0 -0
  111. {koatl-0.1.26 → koatl-0.1.27}/python/koatl/prelude/functional/result.tl +0 -0
  112. {koatl-0.1.26 → koatl-0.1.27}/python/koatl/prelude/iterable.tl +0 -0
  113. {koatl-0.1.26 → koatl-0.1.27}/python/koatl/runtime/__init__.py +0 -0
  114. {koatl-0.1.26 → koatl-0.1.27}/python/koatl/runtime/helpers.py +0 -0
  115. {koatl-0.1.26 → koatl-0.1.27}/python/koatl/runtime/meta_finder.py +0 -0
  116. {koatl-0.1.26 → koatl-0.1.27}/python/koatl/runtime/record.py +0 -0
  117. {koatl-0.1.26 → koatl-0.1.27}/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.26"
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.26
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.26"
3
+ version = "0.1.27"
4
4
  edition = "2021"
5
5
 
6
6
  [lib]
@@ -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
 
@@ -230,9 +220,9 @@ where
230
220
  .map(|id| Expr::Literal(Literal::Str(id.value.0).spanned(id.span)))
231
221
  .spanned_expr(),
232
222
  literal.clone().map(Expr::Literal).spanned_expr(),
233
- sexpr
223
+ qualified_ident
234
224
  .clone()
235
- .delimited_by_with_eol(symbol("("), symbol(")")),
225
+ .delimited_by(symbol("("), symbol(")")),
236
226
  ))
237
227
  .then_ignore(symbol(":"))
238
228
  .then(pattern.clone())
@@ -275,6 +265,7 @@ where
275
265
  let closed_pattern = choice((
276
266
  literal_pattern,
277
267
  class_pattern,
268
+ capture_pattern,
278
269
  value_pattern.clone(),
279
270
  group_pattern,
280
271
  sequence_pattern,
@@ -476,12 +467,23 @@ where
476
467
  })
477
468
  }
478
469
 
479
- 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
+ >(
480
481
  expr_or_inline_stmt_or_block: PBody,
481
482
  nary_tuple: PTuple,
482
483
  expr: PExpr,
483
484
  ident: PIdent,
484
- nary_pattern: PPattern,
485
+ nary_pattern: PNaryPattern,
486
+ pattern: PPattern,
485
487
  ) -> (
486
488
  impl Parser<'tokens, TInput, SStmt<'src>, TExtra<'tokens, 'src>> + Clone,
487
489
  impl Parser<'tokens, TInput, SStmt<'src>, TExtra<'tokens, 'src>> + Clone,
@@ -492,6 +494,7 @@ where
492
494
  PTuple: Parser<'tokens, TInput, SExpr<'src>, TExtra<'tokens, 'src>> + Clone + 'tokens,
493
495
  PExpr: Parser<'tokens, TInput, SExpr<'src>, TExtra<'tokens, 'src>> + Clone + 'tokens,
494
496
  PIdent: Parser<'tokens, TInput, SIdent<'src>, TExtra<'tokens, 'src>> + Clone + 'tokens,
497
+ PNaryPattern: Parser<'tokens, TInput, SPattern<'src>, TExtra<'tokens, 'src>> + Clone + 'tokens,
495
498
  PPattern: Parser<'tokens, TInput, SPattern<'src>, TExtra<'tokens, 'src>> + Clone + 'tokens,
496
499
  {
497
500
  let mut stmt = Recursive::<chumsky::recursive::Indirect<TInput, SStmt, TExtra>>::declare();
@@ -511,33 +514,55 @@ where
511
514
  .map(|(decl, idents)| SStmtInner::Decl(idents, decl))
512
515
  .boxed();
513
516
 
514
- let assign_lhs = nary_tuple.clone();
515
- let inline_assign_lhs = expr.clone();
516
-
517
- let assign_stmt = group((
517
+ let pattern_assign_stmt = group((
518
518
  decl_mod.clone().or_not(),
519
- assign_lhs.clone(),
519
+ nary_pattern.clone(),
520
520
  symbol("=").ignore_then(nary_tuple.clone()),
521
521
  ))
522
- .map(|(decl, lhs, rhs)| SStmtInner::Assign(lhs.indirect(), rhs.indirect(), decl))
522
+ .map(|(decl, pat, rhs)| SStmtInner::PatternAssign(pat.indirect(), rhs.indirect(), decl))
523
523
  .boxed();
524
524
 
525
- let inline_assign_stmt = group((
525
+ let inline_pattern_assign_stmt = group((
526
526
  decl_mod.clone().or_not(),
527
- inline_assign_lhs.clone(),
527
+ pattern.clone(),
528
528
  symbol("=").ignore_then(expr.clone()),
529
529
  ))
530
- .map(|(decl, lhs, rhs)| SStmtInner::Assign(lhs.indirect(), rhs.indirect(), decl))
530
+ .map(|(decl, pat, rhs)| SStmtInner::PatternAssign(pat.indirect(), rhs.indirect(), decl))
531
531
  .boxed();
532
532
 
533
- let expr_stmt = assign_lhs
534
- .clone()
535
- .map(|x| SStmtInner::Expr(x.indirect()))
536
- .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);
537
542
 
538
- let inline_expr_stmt = inline_assign_lhs
539
- .clone()
540
- .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
+ })
541
566
  .boxed();
542
567
 
543
568
  let while_stmt = just(Token::Kw("while"))
@@ -689,8 +714,8 @@ where
689
714
  stmt.define(
690
715
  choice((
691
716
  decl_stmt.then_ignore(just(Token::Eol)),
717
+ pattern_assign_stmt.then_ignore(just(Token::Eol)),
692
718
  assign_stmt.then_ignore(just(Token::Eol)),
693
- expr_stmt.then_ignore(just(Token::Eol)),
694
719
  while_stmt.clone().then_ignore(just(Token::Eol)),
695
720
  for_stmt.clone().then_ignore(just(Token::Eol)),
696
721
  return_stmt.then_ignore(just(Token::Eol)),
@@ -708,8 +733,8 @@ where
708
733
 
709
734
  inline_stmt.define(
710
735
  choice((
736
+ inline_pattern_assign_stmt,
711
737
  inline_assign_stmt,
712
- inline_expr_stmt,
713
738
  while_stmt,
714
739
  for_stmt,
715
740
  inline_return_stmt,
@@ -861,6 +886,25 @@ where
861
886
  .boxed();
862
887
  // .memoized();
863
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
+
864
908
  let mapping = enumeration(
865
909
  choice((
866
910
  symbol("**")
@@ -872,7 +916,7 @@ where
872
916
  .map(|id| Expr::Literal(Literal::Str(id.value.0).spanned(id.span)))
873
917
  .spanned_expr(),
874
918
  literal_expr.clone(),
875
- expr.clone().delimited_by_with_eol(symbol("("), symbol(")")),
919
+ round_brackets.clone(),
876
920
  ))
877
921
  .then_ignore(symbol(":"))
878
922
  .then(expr.clone())
@@ -973,12 +1017,8 @@ where
973
1017
  )
974
1018
  .boxed();
975
1019
 
976
- let (closed_pattern, as_pattern, nary_pattern) = match_pattern(
977
- ident.clone(),
978
- qualified_ident.clone(),
979
- expr.clone(),
980
- literal.clone(),
981
- );
1020
+ let (closed_pattern, as_pattern, nary_pattern) =
1021
+ match_pattern(ident.clone(), qualified_ident.clone(), literal.clone());
982
1022
 
983
1023
  let classic_match = just(Token::Kw("match"))
984
1024
  .ignore_then(expr.clone())
@@ -990,17 +1030,6 @@ where
990
1030
  .as_context()
991
1031
  .boxed();
992
1032
 
993
- let tuple = choice((
994
- symbol("(")
995
- .then(symbol(")"))
996
- .map(|_| Expr::Tuple(vec![]))
997
- .spanned_expr(),
998
- nary_tuple
999
- .clone()
1000
- .delimited_by_with_eol(just(Token::Symbol("(")), just(Token::Symbol(")"))),
1001
- ))
1002
- .boxed();
1003
-
1004
1033
  enum ControlKw {
1005
1034
  Await,
1006
1035
  Yield,
@@ -1053,10 +1082,7 @@ where
1053
1082
  list.clone(),
1054
1083
  mapping,
1055
1084
  fstr,
1056
- tuple.clone(),
1057
- block
1058
- .clone()
1059
- .delimited_by_with_eol(symbol("("), symbol(")")),
1085
+ round_brackets.clone(),
1060
1086
  ))
1061
1087
  .labelled("atom"),
1062
1088
  );
@@ -1438,6 +1464,7 @@ where
1438
1464
  expr.clone(),
1439
1465
  ident.clone(),
1440
1466
  nary_pattern.clone(),
1467
+ as_pattern.clone(),
1441
1468
  );
1442
1469
 
1443
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>>),
@@ -572,10 +572,15 @@ impl SPyStmt<'_> {
572
572
  expr.emit_to(ctx, LOW_PREC)?;
573
573
  ctx.emit_endl();
574
574
  }
575
- PyStmt::Assign(target, value) => {
575
+ PyStmt::Assign(target, value, op) => {
576
576
  ctx.emit_indent();
577
577
  target.emit_to(ctx, LOW_PREC)?;
578
- ctx.emit(" = ");
578
+ ctx.emit(" ");
579
+ if let Some(op) = op {
580
+ op.emit_to(ctx);
581
+ } else {
582
+ }
583
+ ctx.emit("= ");
579
584
  value.emit_to(ctx, LOW_PREC)?;
580
585
  ctx.emit_endl();
581
586
  }
@@ -18,7 +18,16 @@ impl PyAstBuilder {
18
18
  }
19
19
 
20
20
  pub fn assign<'src>(&self, target: SPyExpr<'src>, value: SPyExpr<'src>) -> SPyStmt<'src> {
21
- (PyStmt::Assign(target, value), self.span).into()
21
+ (PyStmt::Assign(target, value, None), self.span).into()
22
+ }
23
+
24
+ pub fn assign_modified<'src>(
25
+ &self,
26
+ target: SPyExpr<'src>,
27
+ value: SPyExpr<'src>,
28
+ op: Option<PyBinaryOp>,
29
+ ) -> SPyStmt<'src> {
30
+ (PyStmt::Assign(target, value, op), self.span).into()
22
31
  }
23
32
 
24
33
  pub fn return_<'src>(&self, expr: SPyExpr<'src>) -> SPyStmt<'src> {