snail-lang 0.4.1__tar.gz → 0.5.1__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.
- {snail_lang-0.4.1 → snail_lang-0.5.1}/Cargo.lock +7 -7
- {snail_lang-0.4.1 → snail_lang-0.5.1}/PKG-INFO +27 -18
- {snail_lang-0.4.1 → snail_lang-0.5.1}/README.md +26 -17
- {snail_lang-0.4.1 → snail_lang-0.5.1}/crates/snail-ast/Cargo.toml +1 -1
- {snail_lang-0.4.1 → snail_lang-0.5.1}/crates/snail-ast/src/ast.rs +26 -3
- {snail_lang-0.4.1 → snail_lang-0.5.1}/crates/snail-core/Cargo.toml +1 -1
- {snail_lang-0.4.1 → snail_lang-0.5.1}/crates/snail-error/Cargo.toml +1 -1
- {snail_lang-0.4.1 → snail_lang-0.5.1}/crates/snail-lower/Cargo.toml +1 -1
- {snail_lang-0.4.1 → snail_lang-0.5.1}/crates/snail-lower/src/constants.rs +7 -0
- {snail_lang-0.4.1 → snail_lang-0.5.1}/crates/snail-lower/src/expr.rs +225 -36
- {snail_lang-0.4.1 → snail_lang-0.5.1}/crates/snail-lower/src/stmt.rs +365 -11
- {snail_lang-0.4.1 → snail_lang-0.5.1}/crates/snail-parser/Cargo.toml +1 -1
- {snail_lang-0.4.1 → snail_lang-0.5.1}/crates/snail-parser/src/expr.rs +15 -0
- {snail_lang-0.4.1 → snail_lang-0.5.1}/crates/snail-parser/src/lib.rs +29 -3
- {snail_lang-0.4.1 → snail_lang-0.5.1}/crates/snail-parser/src/snail.pest +19 -5
- {snail_lang-0.4.1 → snail_lang-0.5.1}/crates/snail-parser/src/stmt.rs +249 -60
- {snail_lang-0.4.1 → snail_lang-0.5.1}/crates/snail-parser/tests/common.rs +13 -1
- {snail_lang-0.4.1 → snail_lang-0.5.1}/crates/snail-parser/tests/parser.rs +167 -4
- {snail_lang-0.4.1 → snail_lang-0.5.1}/crates/snail-parser/tests/statements.rs +1 -1
- {snail_lang-0.4.1 → snail_lang-0.5.1}/crates/snail-python/Cargo.toml +2 -1
- snail_lang-0.5.1/crates/snail-python/build.rs +84 -0
- {snail_lang-0.4.1 → snail_lang-0.5.1}/crates/snail-python/src/lib.rs +34 -1
- {snail_lang-0.4.1 → snail_lang-0.5.1}/pyproject.toml +1 -1
- {snail_lang-0.4.1 → snail_lang-0.5.1}/python/snail/__init__.py +2 -2
- {snail_lang-0.4.1 → snail_lang-0.5.1}/python/snail/cli.py +40 -5
- {snail_lang-0.4.1 → snail_lang-0.5.1}/python/snail/runtime/__init__.py +16 -0
- snail_lang-0.5.1/python/snail/runtime/regex.py +37 -0
- snail_lang-0.4.1/python/snail/runtime/regex.py +0 -11
- {snail_lang-0.4.1 → snail_lang-0.5.1}/Cargo.toml +0 -0
- {snail_lang-0.4.1 → snail_lang-0.5.1}/LICENSE +0 -0
- {snail_lang-0.4.1 → snail_lang-0.5.1}/crates/snail-ast/README.md +0 -0
- {snail_lang-0.4.1 → snail_lang-0.5.1}/crates/snail-ast/src/awk.rs +0 -0
- {snail_lang-0.4.1 → snail_lang-0.5.1}/crates/snail-ast/src/lib.rs +0 -0
- {snail_lang-0.4.1 → snail_lang-0.5.1}/crates/snail-core/README.md +0 -0
- {snail_lang-0.4.1 → snail_lang-0.5.1}/crates/snail-core/src/lib.rs +0 -0
- {snail_lang-0.4.1 → snail_lang-0.5.1}/crates/snail-error/README.md +0 -0
- {snail_lang-0.4.1 → snail_lang-0.5.1}/crates/snail-error/src/lib.rs +0 -0
- {snail_lang-0.4.1 → snail_lang-0.5.1}/crates/snail-lower/README.md +0 -0
- {snail_lang-0.4.1 → snail_lang-0.5.1}/crates/snail-lower/src/awk.rs +0 -0
- {snail_lang-0.4.1 → snail_lang-0.5.1}/crates/snail-lower/src/helpers.rs +0 -0
- {snail_lang-0.4.1 → snail_lang-0.5.1}/crates/snail-lower/src/lib.rs +0 -0
- {snail_lang-0.4.1 → snail_lang-0.5.1}/crates/snail-lower/src/operators.rs +0 -0
- {snail_lang-0.4.1 → snail_lang-0.5.1}/crates/snail-lower/src/program.rs +0 -0
- {snail_lang-0.4.1 → snail_lang-0.5.1}/crates/snail-lower/src/py_ast.rs +0 -0
- {snail_lang-0.4.1 → snail_lang-0.5.1}/crates/snail-parser/README.md +0 -0
- {snail_lang-0.4.1 → snail_lang-0.5.1}/crates/snail-parser/src/awk.rs +0 -0
- {snail_lang-0.4.1 → snail_lang-0.5.1}/crates/snail-parser/src/literal.rs +0 -0
- {snail_lang-0.4.1 → snail_lang-0.5.1}/crates/snail-parser/src/string.rs +0 -0
- {snail_lang-0.4.1 → snail_lang-0.5.1}/crates/snail-parser/src/util.rs +0 -0
- {snail_lang-0.4.1 → snail_lang-0.5.1}/crates/snail-parser/tests/errors.rs +0 -0
- {snail_lang-0.4.1 → snail_lang-0.5.1}/crates/snail-parser/tests/syntax_expressions.rs +0 -0
- {snail_lang-0.4.1 → snail_lang-0.5.1}/crates/snail-parser/tests/syntax_strings.rs +0 -0
- {snail_lang-0.4.1 → snail_lang-0.5.1}/python/snail/runtime/compact_try.py +0 -0
- {snail_lang-0.4.1 → snail_lang-0.5.1}/python/snail/runtime/structured_accessor.py +0 -0
- {snail_lang-0.4.1 → snail_lang-0.5.1}/python/snail/runtime/subprocess.py +0 -0
|
@@ -485,11 +485,11 @@ checksum = "67b1b7a3b5fe4f1376887184045fcf45c69e92af734b7aaddc05fb777b6fbd03"
|
|
|
485
485
|
|
|
486
486
|
[[package]]
|
|
487
487
|
name = "snail-ast"
|
|
488
|
-
version = "0.
|
|
488
|
+
version = "0.5.1"
|
|
489
489
|
|
|
490
490
|
[[package]]
|
|
491
491
|
name = "snail-core"
|
|
492
|
-
version = "0.
|
|
492
|
+
version = "0.5.1"
|
|
493
493
|
dependencies = [
|
|
494
494
|
"pyo3",
|
|
495
495
|
"snail-ast",
|
|
@@ -500,14 +500,14 @@ dependencies = [
|
|
|
500
500
|
|
|
501
501
|
[[package]]
|
|
502
502
|
name = "snail-error"
|
|
503
|
-
version = "0.
|
|
503
|
+
version = "0.5.1"
|
|
504
504
|
dependencies = [
|
|
505
505
|
"snail-ast",
|
|
506
506
|
]
|
|
507
507
|
|
|
508
508
|
[[package]]
|
|
509
509
|
name = "snail-lower"
|
|
510
|
-
version = "0.
|
|
510
|
+
version = "0.5.1"
|
|
511
511
|
dependencies = [
|
|
512
512
|
"pyo3",
|
|
513
513
|
"snail-ast",
|
|
@@ -516,7 +516,7 @@ dependencies = [
|
|
|
516
516
|
|
|
517
517
|
[[package]]
|
|
518
518
|
name = "snail-parser"
|
|
519
|
-
version = "0.
|
|
519
|
+
version = "0.5.1"
|
|
520
520
|
dependencies = [
|
|
521
521
|
"pest",
|
|
522
522
|
"pest_derive",
|
|
@@ -526,7 +526,7 @@ dependencies = [
|
|
|
526
526
|
|
|
527
527
|
[[package]]
|
|
528
528
|
name = "snail-proptest"
|
|
529
|
-
version = "0.
|
|
529
|
+
version = "0.5.1"
|
|
530
530
|
dependencies = [
|
|
531
531
|
"proptest",
|
|
532
532
|
"pyo3",
|
|
@@ -540,7 +540,7 @@ dependencies = [
|
|
|
540
540
|
|
|
541
541
|
[[package]]
|
|
542
542
|
name = "snail-python"
|
|
543
|
-
version = "0.
|
|
543
|
+
version = "0.5.1"
|
|
544
544
|
dependencies = [
|
|
545
545
|
"pyo3",
|
|
546
546
|
"snail-core",
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: snail-lang
|
|
3
|
-
Version: 0.
|
|
3
|
+
Version: 0.5.1
|
|
4
4
|
Requires-Dist: jmespath>=1.0.1
|
|
5
5
|
Requires-Dist: maturin>=1.5 ; extra == 'dev'
|
|
6
6
|
Requires-Dist: pytest ; extra == 'dev'
|
|
@@ -59,7 +59,6 @@ END { print("Sum:", total); assert total == 15}
|
|
|
59
59
|
|
|
60
60
|
Built-in variables: `$0` (line), `$1`, `$2` etc (access fields), `$n` (line number), `$fn` (per-file line number), `$p` (file path), `$m` (last match).
|
|
61
61
|
|
|
62
|
-
|
|
63
62
|
### Compact Error Handling
|
|
64
63
|
|
|
65
64
|
The `?` operator makes error handling terse yet expressive:
|
|
@@ -81,6 +80,25 @@ name = risky("")?.__class__.__name__
|
|
|
81
80
|
args = risky("becomes a list"):[1,2,3]?[0]
|
|
82
81
|
```
|
|
83
82
|
|
|
83
|
+
### Destructuring + `if let` / `while let`
|
|
84
|
+
|
|
85
|
+
Unpack tuples and lists directly, including Python-style rest bindings:
|
|
86
|
+
|
|
87
|
+
```snail
|
|
88
|
+
x, *xs = [1, 2, 3]
|
|
89
|
+
|
|
90
|
+
if let [head, *tail] = [1, 2, 3]; head > 0 {
|
|
91
|
+
print(head, tail)
|
|
92
|
+
}
|
|
93
|
+
```
|
|
94
|
+
|
|
95
|
+
`if let`/`while let` only enter the block when the destructuring succeeds. A guard
|
|
96
|
+
after `;` lets you add a boolean check that runs after the bindings are created.
|
|
97
|
+
|
|
98
|
+
Note that this syntax is more powerful than the walrus operator as that does
|
|
99
|
+
not allow for destructuring.
|
|
100
|
+
|
|
101
|
+
|
|
84
102
|
### Pipeline Operator
|
|
85
103
|
|
|
86
104
|
The `|` operator enables data pipelining as syntactic sugar for nested
|
|
@@ -144,9 +162,12 @@ if bad_email in /^[\w.]+@[\w.]+$/ {
|
|
|
144
162
|
# Compiled regex for reuse
|
|
145
163
|
pattern = /\d{3}-\d{4}/
|
|
146
164
|
match = pattern.search(phone)
|
|
165
|
+
match2 = "555-1212" in pattern
|
|
147
166
|
```
|
|
148
167
|
|
|
149
|
-
|
|
168
|
+
Snail regexes don't return a match object, rather they return a tuple
|
|
169
|
+
containing all of the match groups, including group 0. Both `search` and `in`
|
|
170
|
+
return the same tuple (or `()` when there is no match).
|
|
150
171
|
|
|
151
172
|
### JSON Queries with JMESPath
|
|
152
173
|
|
|
@@ -184,28 +205,16 @@ snail 'js($(curl -s https://api.github.com/repos/sudonym1/snail)) | $[stargazers
|
|
|
184
205
|
snail 'result = int("oops"):"bad int {$e}"?; print(result)'
|
|
185
206
|
|
|
186
207
|
# Regex match and capture
|
|
187
|
-
snail '
|
|
208
|
+
snail 'if let [_, user, domain] = "user@example.com" in /^[\w.]+@([\w.]+)$/ { print(domain) }'
|
|
188
209
|
|
|
189
210
|
# Awk mode: print line numbers for matches
|
|
190
211
|
rg -n "TODO" README.md | snail --awk '/TODO/ { print("{$n}: {$0}") }'
|
|
191
212
|
```
|
|
192
213
|
|
|
193
|
-
## 🏗️ Architecture
|
|
194
|
-
|
|
195
|
-
**Key Components:**
|
|
196
|
-
|
|
197
|
-
- **Parser**: Uses [Pest](https://pest.rs/) parser generator with PEG grammar defined in `src/snail.pest`
|
|
198
|
-
- **AST**: Separate representations for regular Snail (`Program`) and awk mode (`AwkProgram`) with source spans for error reporting
|
|
199
|
-
- **Lowering**: Transforms Snail AST into Python AST, emitting helper calls backed by `snail.runtime`
|
|
200
|
-
- `?` operator → `__snail_compact_try`
|
|
201
|
-
- `$(cmd)` subprocess capture → `__SnailSubprocessCapture`
|
|
202
|
-
- `@(cmd)` subprocess status → `__SnailSubprocessStatus`
|
|
203
|
-
- Regex literals → `__snail_regex_search` and `__snail_regex_compile`
|
|
204
|
-
- **Execution**: Compiles Python AST directly for in-process execution
|
|
205
|
-
- **CLI**: Python wrapper (`python/snail/cli.py`) that executes via the extension module
|
|
206
|
-
|
|
207
214
|
## 📚 Documentation
|
|
208
215
|
|
|
216
|
+
Documentation is WIP
|
|
217
|
+
|
|
209
218
|
- **[Language Reference](docs/REFERENCE.md)** — Complete syntax and semantics
|
|
210
219
|
- **[examples/all_syntax.snail](examples/all_syntax.snail)** — Every feature in one file
|
|
211
220
|
- **[examples/awk.snail](examples/awk.snail)** — Awk mode examples
|
|
@@ -47,7 +47,6 @@ END { print("Sum:", total); assert total == 15}
|
|
|
47
47
|
|
|
48
48
|
Built-in variables: `$0` (line), `$1`, `$2` etc (access fields), `$n` (line number), `$fn` (per-file line number), `$p` (file path), `$m` (last match).
|
|
49
49
|
|
|
50
|
-
|
|
51
50
|
### Compact Error Handling
|
|
52
51
|
|
|
53
52
|
The `?` operator makes error handling terse yet expressive:
|
|
@@ -69,6 +68,25 @@ name = risky("")?.__class__.__name__
|
|
|
69
68
|
args = risky("becomes a list"):[1,2,3]?[0]
|
|
70
69
|
```
|
|
71
70
|
|
|
71
|
+
### Destructuring + `if let` / `while let`
|
|
72
|
+
|
|
73
|
+
Unpack tuples and lists directly, including Python-style rest bindings:
|
|
74
|
+
|
|
75
|
+
```snail
|
|
76
|
+
x, *xs = [1, 2, 3]
|
|
77
|
+
|
|
78
|
+
if let [head, *tail] = [1, 2, 3]; head > 0 {
|
|
79
|
+
print(head, tail)
|
|
80
|
+
}
|
|
81
|
+
```
|
|
82
|
+
|
|
83
|
+
`if let`/`while let` only enter the block when the destructuring succeeds. A guard
|
|
84
|
+
after `;` lets you add a boolean check that runs after the bindings are created.
|
|
85
|
+
|
|
86
|
+
Note that this syntax is more powerful than the walrus operator as that does
|
|
87
|
+
not allow for destructuring.
|
|
88
|
+
|
|
89
|
+
|
|
72
90
|
### Pipeline Operator
|
|
73
91
|
|
|
74
92
|
The `|` operator enables data pipelining as syntactic sugar for nested
|
|
@@ -132,9 +150,12 @@ if bad_email in /^[\w.]+@[\w.]+$/ {
|
|
|
132
150
|
# Compiled regex for reuse
|
|
133
151
|
pattern = /\d{3}-\d{4}/
|
|
134
152
|
match = pattern.search(phone)
|
|
153
|
+
match2 = "555-1212" in pattern
|
|
135
154
|
```
|
|
136
155
|
|
|
137
|
-
|
|
156
|
+
Snail regexes don't return a match object, rather they return a tuple
|
|
157
|
+
containing all of the match groups, including group 0. Both `search` and `in`
|
|
158
|
+
return the same tuple (or `()` when there is no match).
|
|
138
159
|
|
|
139
160
|
### JSON Queries with JMESPath
|
|
140
161
|
|
|
@@ -172,28 +193,16 @@ snail 'js($(curl -s https://api.github.com/repos/sudonym1/snail)) | $[stargazers
|
|
|
172
193
|
snail 'result = int("oops"):"bad int {$e}"?; print(result)'
|
|
173
194
|
|
|
174
195
|
# Regex match and capture
|
|
175
|
-
snail '
|
|
196
|
+
snail 'if let [_, user, domain] = "user@example.com" in /^[\w.]+@([\w.]+)$/ { print(domain) }'
|
|
176
197
|
|
|
177
198
|
# Awk mode: print line numbers for matches
|
|
178
199
|
rg -n "TODO" README.md | snail --awk '/TODO/ { print("{$n}: {$0}") }'
|
|
179
200
|
```
|
|
180
201
|
|
|
181
|
-
## 🏗️ Architecture
|
|
182
|
-
|
|
183
|
-
**Key Components:**
|
|
184
|
-
|
|
185
|
-
- **Parser**: Uses [Pest](https://pest.rs/) parser generator with PEG grammar defined in `src/snail.pest`
|
|
186
|
-
- **AST**: Separate representations for regular Snail (`Program`) and awk mode (`AwkProgram`) with source spans for error reporting
|
|
187
|
-
- **Lowering**: Transforms Snail AST into Python AST, emitting helper calls backed by `snail.runtime`
|
|
188
|
-
- `?` operator → `__snail_compact_try`
|
|
189
|
-
- `$(cmd)` subprocess capture → `__SnailSubprocessCapture`
|
|
190
|
-
- `@(cmd)` subprocess status → `__SnailSubprocessStatus`
|
|
191
|
-
- Regex literals → `__snail_regex_search` and `__snail_regex_compile`
|
|
192
|
-
- **Execution**: Compiles Python AST directly for in-process execution
|
|
193
|
-
- **CLI**: Python wrapper (`python/snail/cli.py`) that executes via the extension module
|
|
194
|
-
|
|
195
202
|
## 📚 Documentation
|
|
196
203
|
|
|
204
|
+
Documentation is WIP
|
|
205
|
+
|
|
197
206
|
- **[Language Reference](docs/REFERENCE.md)** — Complete syntax and semantics
|
|
198
207
|
- **[examples/all_syntax.snail](examples/all_syntax.snail)** — Every feature in one file
|
|
199
208
|
- **[examples/awk.snail](examples/awk.snail)** — Awk mode examples
|
|
@@ -20,14 +20,14 @@ pub struct Program {
|
|
|
20
20
|
#[derive(Debug, Clone, PartialEq)]
|
|
21
21
|
pub enum Stmt {
|
|
22
22
|
If {
|
|
23
|
-
cond:
|
|
23
|
+
cond: Condition,
|
|
24
24
|
body: Vec<Stmt>,
|
|
25
|
-
elifs: Vec<(
|
|
25
|
+
elifs: Vec<(Condition, Vec<Stmt>)>,
|
|
26
26
|
else_body: Option<Vec<Stmt>>,
|
|
27
27
|
span: SourceSpan,
|
|
28
28
|
},
|
|
29
29
|
While {
|
|
30
|
-
cond:
|
|
30
|
+
cond: Condition,
|
|
31
31
|
body: Vec<Stmt>,
|
|
32
32
|
else_body: Option<Vec<Stmt>>,
|
|
33
33
|
span: SourceSpan,
|
|
@@ -148,6 +148,29 @@ pub enum AssignTarget {
|
|
|
148
148
|
index: Box<Expr>,
|
|
149
149
|
span: SourceSpan,
|
|
150
150
|
},
|
|
151
|
+
Starred {
|
|
152
|
+
target: Box<AssignTarget>,
|
|
153
|
+
span: SourceSpan,
|
|
154
|
+
},
|
|
155
|
+
Tuple {
|
|
156
|
+
elements: Vec<AssignTarget>,
|
|
157
|
+
span: SourceSpan,
|
|
158
|
+
},
|
|
159
|
+
List {
|
|
160
|
+
elements: Vec<AssignTarget>,
|
|
161
|
+
span: SourceSpan,
|
|
162
|
+
},
|
|
163
|
+
}
|
|
164
|
+
|
|
165
|
+
#[derive(Debug, Clone, PartialEq)]
|
|
166
|
+
pub enum Condition {
|
|
167
|
+
Expr(Box<Expr>),
|
|
168
|
+
Let {
|
|
169
|
+
target: Box<AssignTarget>,
|
|
170
|
+
value: Box<Expr>,
|
|
171
|
+
guard: Option<Box<Expr>>,
|
|
172
|
+
span: SourceSpan,
|
|
173
|
+
},
|
|
151
174
|
}
|
|
152
175
|
|
|
153
176
|
#[derive(Debug, Clone, PartialEq)]
|
|
@@ -7,6 +7,13 @@ pub const SNAIL_REGEX_SEARCH: &str = "__snail_regex_search";
|
|
|
7
7
|
pub const SNAIL_REGEX_COMPILE: &str = "__snail_regex_compile";
|
|
8
8
|
pub const SNAIL_JMESPATH_QUERY: &str = "__snail_jmespath_query";
|
|
9
9
|
pub const SNAIL_PARTIAL_HELPER: &str = "__snail_partial";
|
|
10
|
+
pub const SNAIL_CONTAINS_HELPER: &str = "__snail_contains__";
|
|
11
|
+
pub const SNAIL_CONTAINS_NOT_HELPER: &str = "__snail_contains_not__";
|
|
12
|
+
pub(crate) const SNAIL_LET_VALUE: &str = "__snail_let_value";
|
|
13
|
+
pub(crate) const SNAIL_LET_OK: &str = "__snail_let_ok";
|
|
14
|
+
pub(crate) const SNAIL_LET_KEEP: &str = "__snail_let_keep";
|
|
15
|
+
pub(crate) const SNAIL_COMPARE_LEFT: &str = "__snail_compare_left";
|
|
16
|
+
pub(crate) const SNAIL_COMPARE_RIGHT: &str = "__snail_compare_right";
|
|
10
17
|
|
|
11
18
|
// Awk-related constants (public within crate)
|
|
12
19
|
pub(crate) const SNAIL_AWK_NR: &str = "$n";
|
|
@@ -36,6 +36,44 @@ pub(crate) fn lower_assign_target(
|
|
|
36
36
|
.call_node("Subscript", vec![value_expr, index_expr, store_ctx], span)
|
|
37
37
|
.map_err(py_err_to_lower)
|
|
38
38
|
}
|
|
39
|
+
AssignTarget::Starred { target, span } => {
|
|
40
|
+
let value = lower_assign_target(builder, target)?;
|
|
41
|
+
builder
|
|
42
|
+
.call_node("Starred", vec![value, store_ctx.clone()], span)
|
|
43
|
+
.map_err(py_err_to_lower)
|
|
44
|
+
}
|
|
45
|
+
AssignTarget::Tuple { elements, span } => {
|
|
46
|
+
let mut lowered = Vec::with_capacity(elements.len());
|
|
47
|
+
for element in elements {
|
|
48
|
+
lowered.push(lower_assign_target(builder, element)?);
|
|
49
|
+
}
|
|
50
|
+
builder
|
|
51
|
+
.call_node(
|
|
52
|
+
"Tuple",
|
|
53
|
+
vec![
|
|
54
|
+
PyList::new_bound(builder.py(), lowered).into_py(builder.py()),
|
|
55
|
+
store_ctx,
|
|
56
|
+
],
|
|
57
|
+
span,
|
|
58
|
+
)
|
|
59
|
+
.map_err(py_err_to_lower)
|
|
60
|
+
}
|
|
61
|
+
AssignTarget::List { elements, span } => {
|
|
62
|
+
let mut lowered = Vec::with_capacity(elements.len());
|
|
63
|
+
for element in elements {
|
|
64
|
+
lowered.push(lower_assign_target(builder, element)?);
|
|
65
|
+
}
|
|
66
|
+
builder
|
|
67
|
+
.call_node(
|
|
68
|
+
"List",
|
|
69
|
+
vec![
|
|
70
|
+
PyList::new_bound(builder.py(), lowered).into_py(builder.py()),
|
|
71
|
+
store_ctx,
|
|
72
|
+
],
|
|
73
|
+
span,
|
|
74
|
+
)
|
|
75
|
+
.map_err(py_err_to_lower)
|
|
76
|
+
}
|
|
39
77
|
}
|
|
40
78
|
}
|
|
41
79
|
|
|
@@ -63,6 +101,41 @@ pub(crate) fn lower_delete_target(
|
|
|
63
101
|
.call_node("Subscript", vec![value_expr, index_expr, del_ctx], span)
|
|
64
102
|
.map_err(py_err_to_lower)
|
|
65
103
|
}
|
|
104
|
+
AssignTarget::Starred { .. } => Err(LowerError::new(
|
|
105
|
+
"starred targets are not valid in del statements",
|
|
106
|
+
)),
|
|
107
|
+
AssignTarget::Tuple { elements, span } => {
|
|
108
|
+
let mut lowered = Vec::with_capacity(elements.len());
|
|
109
|
+
for element in elements {
|
|
110
|
+
lowered.push(lower_delete_target(builder, element)?);
|
|
111
|
+
}
|
|
112
|
+
builder
|
|
113
|
+
.call_node(
|
|
114
|
+
"Tuple",
|
|
115
|
+
vec![
|
|
116
|
+
PyList::new_bound(builder.py(), lowered).into_py(builder.py()),
|
|
117
|
+
del_ctx,
|
|
118
|
+
],
|
|
119
|
+
span,
|
|
120
|
+
)
|
|
121
|
+
.map_err(py_err_to_lower)
|
|
122
|
+
}
|
|
123
|
+
AssignTarget::List { elements, span } => {
|
|
124
|
+
let mut lowered = Vec::with_capacity(elements.len());
|
|
125
|
+
for element in elements {
|
|
126
|
+
lowered.push(lower_delete_target(builder, element)?);
|
|
127
|
+
}
|
|
128
|
+
builder
|
|
129
|
+
.call_node(
|
|
130
|
+
"List",
|
|
131
|
+
vec![
|
|
132
|
+
PyList::new_bound(builder.py(), lowered).into_py(builder.py()),
|
|
133
|
+
del_ctx,
|
|
134
|
+
],
|
|
135
|
+
span,
|
|
136
|
+
)
|
|
137
|
+
.map_err(py_err_to_lower)
|
|
138
|
+
}
|
|
66
139
|
}
|
|
67
140
|
}
|
|
68
141
|
|
|
@@ -116,6 +189,145 @@ fn lower_regex_pattern_expr(
|
|
|
116
189
|
}
|
|
117
190
|
}
|
|
118
191
|
|
|
192
|
+
fn lower_contains_call(
|
|
193
|
+
builder: &AstBuilder<'_>,
|
|
194
|
+
helper_name: &str,
|
|
195
|
+
left: PyObject,
|
|
196
|
+
right: PyObject,
|
|
197
|
+
span: &SourceSpan,
|
|
198
|
+
) -> Result<PyObject, LowerError> {
|
|
199
|
+
let func = name_expr(
|
|
200
|
+
builder,
|
|
201
|
+
helper_name,
|
|
202
|
+
span,
|
|
203
|
+
builder.load_ctx().map_err(py_err_to_lower)?,
|
|
204
|
+
)?;
|
|
205
|
+
let args = vec![left, right];
|
|
206
|
+
builder
|
|
207
|
+
.call_node(
|
|
208
|
+
"Call",
|
|
209
|
+
vec![
|
|
210
|
+
func,
|
|
211
|
+
PyList::new_bound(builder.py(), args).into_py(builder.py()),
|
|
212
|
+
PyList::empty_bound(builder.py()).into_py(builder.py()),
|
|
213
|
+
],
|
|
214
|
+
span,
|
|
215
|
+
)
|
|
216
|
+
.map_err(py_err_to_lower)
|
|
217
|
+
}
|
|
218
|
+
|
|
219
|
+
fn lower_compare_pair(
|
|
220
|
+
builder: &AstBuilder<'_>,
|
|
221
|
+
left: PyObject,
|
|
222
|
+
op: CompareOp,
|
|
223
|
+
right: PyObject,
|
|
224
|
+
span: &SourceSpan,
|
|
225
|
+
) -> Result<PyObject, LowerError> {
|
|
226
|
+
match op {
|
|
227
|
+
CompareOp::In => lower_contains_call(builder, SNAIL_CONTAINS_HELPER, left, right, span),
|
|
228
|
+
CompareOp::NotIn => {
|
|
229
|
+
lower_contains_call(builder, SNAIL_CONTAINS_NOT_HELPER, left, right, span)
|
|
230
|
+
}
|
|
231
|
+
_ => {
|
|
232
|
+
let op = lower_compare_op(builder, op)?;
|
|
233
|
+
builder
|
|
234
|
+
.call_node(
|
|
235
|
+
"Compare",
|
|
236
|
+
vec![
|
|
237
|
+
left,
|
|
238
|
+
PyList::new_bound(builder.py(), vec![op]).into_py(builder.py()),
|
|
239
|
+
PyList::new_bound(builder.py(), vec![right]).into_py(builder.py()),
|
|
240
|
+
],
|
|
241
|
+
span,
|
|
242
|
+
)
|
|
243
|
+
.map_err(py_err_to_lower)
|
|
244
|
+
}
|
|
245
|
+
}
|
|
246
|
+
}
|
|
247
|
+
|
|
248
|
+
fn named_expr(
|
|
249
|
+
builder: &AstBuilder<'_>,
|
|
250
|
+
name: &str,
|
|
251
|
+
value: PyObject,
|
|
252
|
+
span: &SourceSpan,
|
|
253
|
+
) -> Result<PyObject, LowerError> {
|
|
254
|
+
let target = name_expr(
|
|
255
|
+
builder,
|
|
256
|
+
name,
|
|
257
|
+
span,
|
|
258
|
+
builder.store_ctx().map_err(py_err_to_lower)?,
|
|
259
|
+
)?;
|
|
260
|
+
builder
|
|
261
|
+
.call_node("NamedExpr", vec![target, value], span)
|
|
262
|
+
.map_err(py_err_to_lower)
|
|
263
|
+
}
|
|
264
|
+
|
|
265
|
+
fn lower_compare_chain(
|
|
266
|
+
builder: &AstBuilder<'_>,
|
|
267
|
+
left: &Expr,
|
|
268
|
+
ops: &[CompareOp],
|
|
269
|
+
comparators: &[Expr],
|
|
270
|
+
span: &SourceSpan,
|
|
271
|
+
exception_name: Option<&str>,
|
|
272
|
+
) -> Result<PyObject, LowerError> {
|
|
273
|
+
if ops.len() != comparators.len() {
|
|
274
|
+
return Err(LowerError::new(
|
|
275
|
+
"comparison ops must match comparator count",
|
|
276
|
+
));
|
|
277
|
+
}
|
|
278
|
+
if ops.is_empty() {
|
|
279
|
+
return Err(LowerError::new("comparison missing operator"));
|
|
280
|
+
}
|
|
281
|
+
|
|
282
|
+
let left_expr = lower_expr_with_exception(builder, left, exception_name)?;
|
|
283
|
+
let right_expr = lower_expr_with_exception(builder, &comparators[0], exception_name)?;
|
|
284
|
+
let left_named = named_expr(builder, SNAIL_COMPARE_LEFT, left_expr, span)?;
|
|
285
|
+
let right_named = named_expr(builder, SNAIL_COMPARE_RIGHT, right_expr, span)?;
|
|
286
|
+
let mut comparisons = Vec::with_capacity(ops.len());
|
|
287
|
+
comparisons.push(lower_compare_pair(
|
|
288
|
+
builder,
|
|
289
|
+
left_named,
|
|
290
|
+
ops[0],
|
|
291
|
+
right_named,
|
|
292
|
+
span,
|
|
293
|
+
)?);
|
|
294
|
+
|
|
295
|
+
for (index, op) in ops.iter().enumerate().skip(1) {
|
|
296
|
+
let prev_right = name_expr(
|
|
297
|
+
builder,
|
|
298
|
+
SNAIL_COMPARE_RIGHT,
|
|
299
|
+
span,
|
|
300
|
+
builder.load_ctx().map_err(py_err_to_lower)?,
|
|
301
|
+
)?;
|
|
302
|
+
let left_named = named_expr(builder, SNAIL_COMPARE_LEFT, prev_right, span)?;
|
|
303
|
+
let right_expr = lower_expr_with_exception(builder, &comparators[index], exception_name)?;
|
|
304
|
+
let right_named = named_expr(builder, SNAIL_COMPARE_RIGHT, right_expr, span)?;
|
|
305
|
+
comparisons.push(lower_compare_pair(
|
|
306
|
+
builder,
|
|
307
|
+
left_named,
|
|
308
|
+
*op,
|
|
309
|
+
right_named,
|
|
310
|
+
span,
|
|
311
|
+
)?);
|
|
312
|
+
}
|
|
313
|
+
|
|
314
|
+
if comparisons.len() == 1 {
|
|
315
|
+
return Ok(comparisons.remove(0));
|
|
316
|
+
}
|
|
317
|
+
|
|
318
|
+
let op = lower_bool_op(builder, BinaryOp::And)?;
|
|
319
|
+
builder
|
|
320
|
+
.call_node(
|
|
321
|
+
"BoolOp",
|
|
322
|
+
vec![
|
|
323
|
+
op,
|
|
324
|
+
PyList::new_bound(builder.py(), comparisons).into_py(builder.py()),
|
|
325
|
+
],
|
|
326
|
+
span,
|
|
327
|
+
)
|
|
328
|
+
.map_err(py_err_to_lower)
|
|
329
|
+
}
|
|
330
|
+
|
|
119
331
|
fn lower_fstring_parts(
|
|
120
332
|
builder: &AstBuilder<'_>,
|
|
121
333
|
parts: &[FStringPart],
|
|
@@ -404,26 +616,14 @@ pub(crate) fn lower_expr_with_exception(
|
|
|
404
616
|
comparators,
|
|
405
617
|
span,
|
|
406
618
|
} => {
|
|
407
|
-
|
|
408
|
-
|
|
409
|
-
|
|
410
|
-
|
|
411
|
-
|
|
412
|
-
|
|
413
|
-
|
|
414
|
-
|
|
415
|
-
.collect::<Result<Vec<_>, _>>()?;
|
|
416
|
-
builder
|
|
417
|
-
.call_node(
|
|
418
|
-
"Compare",
|
|
419
|
-
vec![
|
|
420
|
-
left_expr,
|
|
421
|
-
PyList::new_bound(builder.py(), ops).into_py(builder.py()),
|
|
422
|
-
PyList::new_bound(builder.py(), comparators).into_py(builder.py()),
|
|
423
|
-
],
|
|
424
|
-
span,
|
|
425
|
-
)
|
|
426
|
-
.map_err(py_err_to_lower)
|
|
619
|
+
if ops.len() == 1 {
|
|
620
|
+
let left_expr = lower_expr_with_exception(builder, left, exception_name)?;
|
|
621
|
+
let right_expr =
|
|
622
|
+
lower_expr_with_exception(builder, &comparators[0], exception_name)?;
|
|
623
|
+
lower_compare_pair(builder, left_expr, ops[0], right_expr, span)
|
|
624
|
+
} else {
|
|
625
|
+
lower_compare_chain(builder, left, ops, comparators, span, exception_name)
|
|
626
|
+
}
|
|
427
627
|
}
|
|
428
628
|
Expr::IfExpr {
|
|
429
629
|
test,
|
|
@@ -613,31 +813,20 @@ pub(crate) fn lower_expr_with_exception(
|
|
|
613
813
|
Expr::Attribute { value, attr, span } => {
|
|
614
814
|
let value = lower_expr_with_exception(builder, value, exception_name)?;
|
|
615
815
|
if attr.chars().all(|ch| ch.is_ascii_digit()) {
|
|
616
|
-
let
|
|
816
|
+
let index = attr
|
|
617
817
|
.parse::<i32>()
|
|
618
818
|
.map_err(|_| LowerError::new(format!("Invalid match group index: .{attr}")))?;
|
|
619
|
-
let
|
|
819
|
+
let index_expr = number_expr(builder, &index.to_string(), span)?;
|
|
820
|
+
return builder
|
|
620
821
|
.call_node(
|
|
621
|
-
"
|
|
822
|
+
"Subscript",
|
|
622
823
|
vec![
|
|
623
824
|
value,
|
|
624
|
-
|
|
825
|
+
index_expr,
|
|
625
826
|
builder.load_ctx().map_err(py_err_to_lower)?,
|
|
626
827
|
],
|
|
627
828
|
span,
|
|
628
829
|
)
|
|
629
|
-
.map_err(py_err_to_lower)?;
|
|
630
|
-
let index_expr = number_expr(builder, &group_index.to_string(), span)?;
|
|
631
|
-
return builder
|
|
632
|
-
.call_node(
|
|
633
|
-
"Call",
|
|
634
|
-
vec![
|
|
635
|
-
group_attr,
|
|
636
|
-
PyList::new_bound(builder.py(), vec![index_expr]).into_py(builder.py()),
|
|
637
|
-
PyList::empty_bound(builder.py()).into_py(builder.py()),
|
|
638
|
-
],
|
|
639
|
-
span,
|
|
640
|
-
)
|
|
641
830
|
.map_err(py_err_to_lower);
|
|
642
831
|
}
|
|
643
832
|
builder
|