snail-lang 0.3.4__tar.gz → 0.3.7__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 (83) hide show
  1. {snail_lang-0.3.4 → snail_lang-0.3.7}/Cargo.lock +10 -27
  2. {snail_lang-0.3.4 → snail_lang-0.3.7}/PKG-INFO +27 -15
  3. {snail_lang-0.3.4 → snail_lang-0.3.7}/README.md +26 -14
  4. {snail_lang-0.3.4 → snail_lang-0.3.7}/crates/snail-ast/Cargo.toml +1 -1
  5. {snail_lang-0.3.4 → snail_lang-0.3.7}/crates/snail-ast/README.md +1 -3
  6. {snail_lang-0.3.4 → snail_lang-0.3.7}/crates/snail-ast/src/ast.rs +3 -0
  7. {snail_lang-0.3.4 → snail_lang-0.3.7}/crates/snail-core/Cargo.toml +2 -3
  8. {snail_lang-0.3.4 → snail_lang-0.3.7}/crates/snail-core/README.md +5 -9
  9. snail_lang-0.3.7/crates/snail-core/src/lib.rs +36 -0
  10. {snail_lang-0.3.4 → snail_lang-0.3.7}/crates/snail-error/Cargo.toml +1 -1
  11. {snail_lang-0.3.4 → snail_lang-0.3.7}/crates/snail-error/src/lib.rs +15 -7
  12. {snail_lang-0.3.4 → snail_lang-0.3.7}/crates/snail-lower/Cargo.toml +2 -2
  13. {snail_lang-0.3.4 → snail_lang-0.3.7}/crates/snail-lower/README.md +7 -8
  14. snail_lang-0.3.7/crates/snail-lower/src/awk.rs +470 -0
  15. {snail_lang-0.3.4 → snail_lang-0.3.7}/crates/snail-lower/src/constants.rs +2 -3
  16. snail_lang-0.3.7/crates/snail-lower/src/expr.rs +1391 -0
  17. snail_lang-0.3.7/crates/snail-lower/src/helpers.rs +103 -0
  18. {snail_lang-0.3.4 → snail_lang-0.3.7}/crates/snail-lower/src/lib.rs +5 -2
  19. snail_lang-0.3.7/crates/snail-lower/src/operators.rs +69 -0
  20. snail_lang-0.3.7/crates/snail-lower/src/program.rs +176 -0
  21. snail_lang-0.3.7/crates/snail-lower/src/py_ast.rs +80 -0
  22. snail_lang-0.3.7/crates/snail-lower/src/stmt.rs +686 -0
  23. {snail_lang-0.3.4 → snail_lang-0.3.7}/crates/snail-parser/Cargo.toml +1 -1
  24. {snail_lang-0.3.4 → snail_lang-0.3.7}/crates/snail-parser/src/expr.rs +6 -0
  25. snail_lang-0.3.7/crates/snail-parser/src/lib.rs +418 -0
  26. {snail_lang-0.3.4 → snail_lang-0.3.7}/crates/snail-parser/src/snail.pest +4 -0
  27. {snail_lang-0.3.4 → snail_lang-0.3.7}/crates/snail-parser/src/string.rs +1 -0
  28. {snail_lang-0.3.4 → snail_lang-0.3.7}/crates/snail-parser/src/util.rs +1 -0
  29. snail_lang-0.3.7/crates/snail-parser/tests/common.rs +84 -0
  30. snail_lang-0.3.7/crates/snail-parser/tests/errors.rs +195 -0
  31. snail_lang-0.3.7/crates/snail-parser/tests/parser.rs +634 -0
  32. snail_lang-0.3.7/crates/snail-parser/tests/statements.rs +409 -0
  33. snail_lang-0.3.7/crates/snail-parser/tests/syntax_expressions.rs +369 -0
  34. snail_lang-0.3.7/crates/snail-parser/tests/syntax_strings.rs +92 -0
  35. {snail_lang-0.3.4 → snail_lang-0.3.7}/crates/snail-python/Cargo.toml +1 -1
  36. {snail_lang-0.3.4 → snail_lang-0.3.7}/crates/snail-python/src/lib.rs +29 -17
  37. {snail_lang-0.3.4 → snail_lang-0.3.7}/pyproject.toml +1 -1
  38. {snail_lang-0.3.4 → snail_lang-0.3.7}/python/snail/cli.py +2 -0
  39. snail_lang-0.3.7/python/snail/runtime/__init__.py +59 -0
  40. snail_lang-0.3.7/python/snail/runtime/structured_accessor.py +75 -0
  41. {snail_lang-0.3.4 → snail_lang-0.3.7}/python/snail/runtime/subprocess.py +2 -2
  42. snail_lang-0.3.4/crates/snail-codegen/Cargo.toml +0 -9
  43. snail_lang-0.3.4/crates/snail-codegen/README.md +0 -41
  44. snail_lang-0.3.4/crates/snail-codegen/src/expr.rs +0 -483
  45. snail_lang-0.3.4/crates/snail-codegen/src/lib.rs +0 -58
  46. snail_lang-0.3.4/crates/snail-codegen/src/writer.rs +0 -217
  47. snail_lang-0.3.4/crates/snail-core/src/lib.rs +0 -33
  48. snail_lang-0.3.4/crates/snail-lower/src/awk.rs +0 -360
  49. snail_lang-0.3.4/crates/snail-lower/src/expr.rs +0 -560
  50. snail_lang-0.3.4/crates/snail-lower/src/helpers.rs +0 -49
  51. snail_lang-0.3.4/crates/snail-lower/src/operators.rs +0 -42
  52. snail_lang-0.3.4/crates/snail-lower/src/program.rs +0 -87
  53. snail_lang-0.3.4/crates/snail-lower/src/span.rs +0 -65
  54. snail_lang-0.3.4/crates/snail-lower/src/stmt.rs +0 -270
  55. snail_lang-0.3.4/crates/snail-parser/src/lib.rs +0 -85
  56. snail_lang-0.3.4/crates/snail-parser/tests/parser.rs +0 -819
  57. snail_lang-0.3.4/crates/snail-python-ast/Cargo.toml +0 -8
  58. snail_lang-0.3.4/crates/snail-python-ast/README.md +0 -32
  59. snail_lang-0.3.4/crates/snail-python-ast/src/lib.rs +0 -311
  60. snail_lang-0.3.4/python/snail/runtime/__init__.py +0 -25
  61. snail_lang-0.3.4/python/snail/runtime/structured_accessor.py +0 -71
  62. {snail_lang-0.3.4 → snail_lang-0.3.7}/Cargo.toml +0 -0
  63. {snail_lang-0.3.4 → snail_lang-0.3.7}/LICENSE +0 -0
  64. {snail_lang-0.3.4 → snail_lang-0.3.7}/crates/snail-ast/src/awk.rs +0 -0
  65. {snail_lang-0.3.4 → snail_lang-0.3.7}/crates/snail-ast/src/lib.rs +0 -0
  66. {snail_lang-0.3.4 → snail_lang-0.3.7}/crates/snail-error/README.md +0 -0
  67. {snail_lang-0.3.4 → snail_lang-0.3.7}/crates/snail-parser/README.md +0 -0
  68. {snail_lang-0.3.4 → snail_lang-0.3.7}/crates/snail-parser/src/awk.rs +0 -0
  69. {snail_lang-0.3.4 → snail_lang-0.3.7}/crates/snail-parser/src/literal.rs +0 -0
  70. {snail_lang-0.3.4 → snail_lang-0.3.7}/crates/snail-parser/src/stmt.rs +0 -0
  71. {snail_lang-0.3.4 → snail_lang-0.3.7}/python/snail/__init__.py +0 -0
  72. {snail_lang-0.3.4 → snail_lang-0.3.7}/python/snail/runtime/compact_try.py +0 -0
  73. {snail_lang-0.3.4 → snail_lang-0.3.7}/python/snail/runtime/regex.py +0 -0
  74. {snail_lang-0.3.4 → snail_lang-0.3.7}/python/snail/vendor/__init__.py +0 -0
  75. {snail_lang-0.3.4 → snail_lang-0.3.7}/python/snail/vendor/jmespath/LICENSE +0 -0
  76. {snail_lang-0.3.4 → snail_lang-0.3.7}/python/snail/vendor/jmespath/__init__.py +0 -0
  77. {snail_lang-0.3.4 → snail_lang-0.3.7}/python/snail/vendor/jmespath/ast.py +0 -0
  78. {snail_lang-0.3.4 → snail_lang-0.3.7}/python/snail/vendor/jmespath/compat.py +0 -0
  79. {snail_lang-0.3.4 → snail_lang-0.3.7}/python/snail/vendor/jmespath/exceptions.py +0 -0
  80. {snail_lang-0.3.4 → snail_lang-0.3.7}/python/snail/vendor/jmespath/functions.py +0 -0
  81. {snail_lang-0.3.4 → snail_lang-0.3.7}/python/snail/vendor/jmespath/lexer.py +0 -0
  82. {snail_lang-0.3.4 → snail_lang-0.3.7}/python/snail/vendor/jmespath/parser.py +0 -0
  83. {snail_lang-0.3.4 → snail_lang-0.3.7}/python/snail/vendor/jmespath/visitor.py +0 -0
@@ -485,47 +485,38 @@ checksum = "67b1b7a3b5fe4f1376887184045fcf45c69e92af734b7aaddc05fb777b6fbd03"
485
485
 
486
486
  [[package]]
487
487
  name = "snail-ast"
488
- version = "0.3.4"
489
-
490
- [[package]]
491
- name = "snail-codegen"
492
- version = "0.3.4"
493
- dependencies = [
494
- "snail-ast",
495
- "snail-python-ast",
496
- ]
488
+ version = "0.3.7"
497
489
 
498
490
  [[package]]
499
491
  name = "snail-core"
500
- version = "0.3.4"
492
+ version = "0.3.7"
501
493
  dependencies = [
494
+ "pyo3",
502
495
  "snail-ast",
503
- "snail-codegen",
504
496
  "snail-error",
505
497
  "snail-lower",
506
498
  "snail-parser",
507
- "snail-python-ast",
508
499
  ]
509
500
 
510
501
  [[package]]
511
502
  name = "snail-error"
512
- version = "0.3.4"
503
+ version = "0.3.7"
513
504
  dependencies = [
514
505
  "snail-ast",
515
506
  ]
516
507
 
517
508
  [[package]]
518
509
  name = "snail-lower"
519
- version = "0.3.4"
510
+ version = "0.3.7"
520
511
  dependencies = [
512
+ "pyo3",
521
513
  "snail-ast",
522
514
  "snail-error",
523
- "snail-python-ast",
524
515
  ]
525
516
 
526
517
  [[package]]
527
518
  name = "snail-parser"
528
- version = "0.3.4"
519
+ version = "0.3.7"
529
520
  dependencies = [
530
521
  "pest",
531
522
  "pest_derive",
@@ -535,34 +526,26 @@ dependencies = [
535
526
 
536
527
  [[package]]
537
528
  name = "snail-proptest"
538
- version = "0.3.4"
529
+ version = "0.3.7"
539
530
  dependencies = [
540
531
  "proptest",
532
+ "pyo3",
541
533
  "snail-ast",
542
- "snail-codegen",
543
534
  "snail-core",
544
535
  "snail-error",
545
536
  "snail-lower",
546
537
  "snail-parser",
547
- "snail-python-ast",
548
538
  "tempfile",
549
539
  ]
550
540
 
551
541
  [[package]]
552
542
  name = "snail-python"
553
- version = "0.3.4"
543
+ version = "0.3.7"
554
544
  dependencies = [
555
545
  "pyo3",
556
546
  "snail-core",
557
547
  ]
558
548
 
559
- [[package]]
560
- name = "snail-python-ast"
561
- version = "0.3.4"
562
- dependencies = [
563
- "snail-ast",
564
- ]
565
-
566
549
  [[package]]
567
550
  name = "syn"
568
551
  version = "2.0.114"
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: snail-lang
3
- Version: 0.3.4
3
+ Version: 0.3.7
4
4
  Requires-Dist: maturin>=1.5 ; extra == 'dev'
5
5
  Requires-Dist: pytest ; extra == 'dev'
6
6
  Provides-Extra: dev
@@ -64,7 +64,7 @@ The `?` operator makes error handling terse yet expressive:
64
64
  err = risky_operation()?
65
65
 
66
66
  # Provide a fallback value (exception available as $e)
67
- value = parse_json(data):{}?
67
+ value = js(data):{}?
68
68
  details = fetch_url(url):"Error: {$e}"?
69
69
 
70
70
  # Access attributes directly
@@ -101,7 +101,7 @@ Built-in variables: `$l` (line), `$f` (fields), `$n` (line number), `$fn` (per-f
101
101
 
102
102
  ### Pipeline Operator
103
103
 
104
- The `|` operator enables data pipelining through objects that implement `__pipeline__`:
104
+ The `|` operator enables data pipelining through pipeline-aware callables:
105
105
 
106
106
  ```snail
107
107
  # Pipe data to subprocess stdin
@@ -112,23 +112,37 @@ output = "foo\nbar" | $(grep foo) | $(wc -l)
112
112
 
113
113
  # Custom pipeline handlers
114
114
  class Doubler {
115
- def __pipeline__(self, x) { return x * 2 }
115
+ def __call__(self, x) { return x * 2 }
116
116
  }
117
117
  doubled = 21 | Doubler() # yields 42
118
+
119
+ # Use placeholders to control where piped values land in calls
120
+ greeting = "World" | greet("Hello ", _) # greet("Hello ", "World")
121
+ excited = "World" | greet(_, "!") # greet("World", "!")
122
+ formal = "World" | greet("Hello ", suffix=_) # greet("Hello ", "World")
118
123
  ```
119
124
 
125
+ When a pipeline targets a call expression, the left-hand value is passed to the
126
+ resulting callable. If the call includes a single `_` placeholder, Snail substitutes
127
+ the piped value at that position (including keyword arguments). Only one
128
+ placeholder is allowed in a piped call. Outside of pipeline calls, `_` remains a
129
+ normal identifier.
130
+
120
131
  ### JSON Queries with JMESPath
121
132
 
122
- Parse and query JSON data with the `json()` function and structured pipeline accessor:
133
+ Parse and query JSON data with the `js()` function and structured pipeline accessor:
123
134
 
124
135
  ```snail
125
136
  # Parse JSON and query with $[jmespath]
126
- data = json($(curl -s api.example.com/users))
137
+ data = js($(curl -s api.example.com/users))
127
138
  names = data | $[users[*].name]
128
139
  first_email = data | $[users[0].email]
129
140
 
130
141
  # Inline parsing and querying
131
- result = json('{"foo": 12}') | $[foo]
142
+ result = js('{"foo": 12}') | $[foo]
143
+
144
+ # JSONL parsing returns a list
145
+ names = js('{"name": "Ada"}\n{"name": "Lin"}') | $[[*].name]
132
146
  ```
133
147
 
134
148
  ### Full Python Interoperability
@@ -147,7 +161,7 @@ filtered = df[df["value"] > 100]
147
161
 
148
162
  ```bash
149
163
  # Install from PyPI
150
- pip install snail
164
+ pip install snail-lang
151
165
 
152
166
  # Run a one-liner
153
167
  snail "print('Hello, Snail!')"
@@ -179,10 +193,9 @@ flowchart TB
179
193
  C2[crates/snail-ast/src/awk.rs<br/>AwkProgram AST]
180
194
  end
181
195
 
182
- subgraph Lowering["Lowering & Code Generation"]
196
+ subgraph Lowering["Lowering"]
183
197
  D1[crates/snail-lower/<br/>AST → Python AST Transform]
184
198
  D2[python/snail/runtime/<br/>Runtime Helpers]
185
- D3[crates/snail-codegen/<br/>Python AST → Source Code]
186
199
  end
187
200
 
188
201
  subgraph Execution
@@ -198,9 +211,8 @@ flowchart TB
198
211
  C1 --> D1
199
212
  C2 --> D1
200
213
  D1 --> D2
201
- D1 --> D3
202
- D2 --> D3
203
- D3 --> E1
214
+ D1 --> E1
215
+ D2 --> E1
204
216
  E1 --> E2
205
217
  E2 --> F[Python Execution]
206
218
 
@@ -218,7 +230,7 @@ flowchart TB
218
230
  - `$(cmd)` subprocess capture → `__SnailSubprocessCapture`
219
231
  - `@(cmd)` subprocess status → `__SnailSubprocessStatus`
220
232
  - Regex literals → `__snail_regex_search` and `__snail_regex_compile`
221
- - **Code Generation**: Converts Python AST to Python source for in-process execution
233
+ - **Execution**: Compiles Python AST directly for in-process execution
222
234
  - **CLI**: Python wrapper (`python/snail/cli.py`) that executes via the extension module
223
235
 
224
236
  ## 📚 Documentation
@@ -333,7 +345,7 @@ python3 -m venv myenv
333
345
  source myenv/bin/activate # On Windows: myenv\Scripts\activate
334
346
 
335
347
  # Install and run
336
- pip install snail
348
+ pip install snail-lang
337
349
  snail "import sys; print(sys.prefix)"
338
350
  ```
339
351
 
@@ -53,7 +53,7 @@ The `?` operator makes error handling terse yet expressive:
53
53
  err = risky_operation()?
54
54
 
55
55
  # Provide a fallback value (exception available as $e)
56
- value = parse_json(data):{}?
56
+ value = js(data):{}?
57
57
  details = fetch_url(url):"Error: {$e}"?
58
58
 
59
59
  # Access attributes directly
@@ -90,7 +90,7 @@ Built-in variables: `$l` (line), `$f` (fields), `$n` (line number), `$fn` (per-f
90
90
 
91
91
  ### Pipeline Operator
92
92
 
93
- The `|` operator enables data pipelining through objects that implement `__pipeline__`:
93
+ The `|` operator enables data pipelining through pipeline-aware callables:
94
94
 
95
95
  ```snail
96
96
  # Pipe data to subprocess stdin
@@ -101,23 +101,37 @@ output = "foo\nbar" | $(grep foo) | $(wc -l)
101
101
 
102
102
  # Custom pipeline handlers
103
103
  class Doubler {
104
- def __pipeline__(self, x) { return x * 2 }
104
+ def __call__(self, x) { return x * 2 }
105
105
  }
106
106
  doubled = 21 | Doubler() # yields 42
107
+
108
+ # Use placeholders to control where piped values land in calls
109
+ greeting = "World" | greet("Hello ", _) # greet("Hello ", "World")
110
+ excited = "World" | greet(_, "!") # greet("World", "!")
111
+ formal = "World" | greet("Hello ", suffix=_) # greet("Hello ", "World")
107
112
  ```
108
113
 
114
+ When a pipeline targets a call expression, the left-hand value is passed to the
115
+ resulting callable. If the call includes a single `_` placeholder, Snail substitutes
116
+ the piped value at that position (including keyword arguments). Only one
117
+ placeholder is allowed in a piped call. Outside of pipeline calls, `_` remains a
118
+ normal identifier.
119
+
109
120
  ### JSON Queries with JMESPath
110
121
 
111
- Parse and query JSON data with the `json()` function and structured pipeline accessor:
122
+ Parse and query JSON data with the `js()` function and structured pipeline accessor:
112
123
 
113
124
  ```snail
114
125
  # Parse JSON and query with $[jmespath]
115
- data = json($(curl -s api.example.com/users))
126
+ data = js($(curl -s api.example.com/users))
116
127
  names = data | $[users[*].name]
117
128
  first_email = data | $[users[0].email]
118
129
 
119
130
  # Inline parsing and querying
120
- result = json('{"foo": 12}') | $[foo]
131
+ result = js('{"foo": 12}') | $[foo]
132
+
133
+ # JSONL parsing returns a list
134
+ names = js('{"name": "Ada"}\n{"name": "Lin"}') | $[[*].name]
121
135
  ```
122
136
 
123
137
  ### Full Python Interoperability
@@ -136,7 +150,7 @@ filtered = df[df["value"] > 100]
136
150
 
137
151
  ```bash
138
152
  # Install from PyPI
139
- pip install snail
153
+ pip install snail-lang
140
154
 
141
155
  # Run a one-liner
142
156
  snail "print('Hello, Snail!')"
@@ -168,10 +182,9 @@ flowchart TB
168
182
  C2[crates/snail-ast/src/awk.rs<br/>AwkProgram AST]
169
183
  end
170
184
 
171
- subgraph Lowering["Lowering & Code Generation"]
185
+ subgraph Lowering["Lowering"]
172
186
  D1[crates/snail-lower/<br/>AST → Python AST Transform]
173
187
  D2[python/snail/runtime/<br/>Runtime Helpers]
174
- D3[crates/snail-codegen/<br/>Python AST → Source Code]
175
188
  end
176
189
 
177
190
  subgraph Execution
@@ -187,9 +200,8 @@ flowchart TB
187
200
  C1 --> D1
188
201
  C2 --> D1
189
202
  D1 --> D2
190
- D1 --> D3
191
- D2 --> D3
192
- D3 --> E1
203
+ D1 --> E1
204
+ D2 --> E1
193
205
  E1 --> E2
194
206
  E2 --> F[Python Execution]
195
207
 
@@ -207,7 +219,7 @@ flowchart TB
207
219
  - `$(cmd)` subprocess capture → `__SnailSubprocessCapture`
208
220
  - `@(cmd)` subprocess status → `__SnailSubprocessStatus`
209
221
  - Regex literals → `__snail_regex_search` and `__snail_regex_compile`
210
- - **Code Generation**: Converts Python AST to Python source for in-process execution
222
+ - **Execution**: Compiles Python AST directly for in-process execution
211
223
  - **CLI**: Python wrapper (`python/snail/cli.py`) that executes via the extension module
212
224
 
213
225
  ## 📚 Documentation
@@ -322,7 +334,7 @@ python3 -m venv myenv
322
334
  source myenv/bin/activate # On Windows: myenv\Scripts\activate
323
335
 
324
336
  # Install and run
325
- pip install snail
337
+ pip install snail-lang
326
338
  snail "import sys; print(sys.prefix)"
327
339
  ```
328
340
 
@@ -1,6 +1,6 @@
1
1
  [package]
2
2
  name = "snail-ast"
3
- version = "0.3.4"
3
+ version = "0.3.7"
4
4
  edition = "2024"
5
5
  readme = "README.md"
6
6
 
@@ -21,11 +21,9 @@ None - this crate has no dependencies and provides pure data structures.
21
21
 
22
22
  ## Used By
23
23
 
24
- - **snail-python-ast**: Uses `SourceSpan` and `StringDelimiter` for its own AST nodes
25
24
  - **snail-error**: Uses `SourceSpan` for error reporting with source locations
26
25
  - **snail-parser**: Produces `Program` and `AwkProgram` as output
27
- - **snail-lower**: Consumes Snail AST and transforms it to Python AST
28
- - **snail-codegen**: Uses `StringDelimiter` for string literal formatting
26
+ - **snail-lower**: Consumes Snail AST and transforms it to Python `ast` nodes via pyo3
29
27
  - **snail-core**: Re-exports all types for the unified API
30
28
 
31
29
  ## Design
@@ -156,6 +156,9 @@ pub enum Expr {
156
156
  name: String,
157
157
  span: SourceSpan,
158
158
  },
159
+ Placeholder {
160
+ span: SourceSpan,
161
+ },
159
162
  Number {
160
163
  value: String,
161
164
  span: SourceSpan,
@@ -1,13 +1,12 @@
1
1
  [package]
2
2
  name = "snail-core"
3
- version = "0.3.4"
3
+ version = "0.3.7"
4
4
  edition.workspace = true
5
5
  readme = "README.md"
6
6
 
7
7
  [dependencies]
8
8
  snail-ast = { path = "../snail-ast" }
9
- snail-python-ast = { path = "../snail-python-ast" }
10
9
  snail-error = { path = "../snail-error" }
11
10
  snail-parser = { path = "../snail-parser" }
12
11
  snail-lower = { path = "../snail-lower" }
13
- snail-codegen = { path = "../snail-codegen" }
12
+ pyo3 = { version = "0.21", features = ["abi3-py310"] }
@@ -4,27 +4,24 @@ Unified compilation API for the Snail programming language.
4
4
 
5
5
  ## Purpose
6
6
 
7
- This crate serves as the main entry point for Snail compilation. It re-exports all types from the workspace crates and provides high-level compilation functions that orchestrate the complete pipeline from source code to executable Python.
7
+ This crate serves as the main entry point for Snail compilation. It re-exports types from the workspace crates and provides high-level compilation functions that orchestrate the complete pipeline from source code to Python `ast` nodes.
8
8
 
9
9
  ## Key Components
10
10
 
11
- - **compile_snail_source()**: One-step compilation from Snail source to Python source
11
+ - **compile_snail_source()**: One-step compilation from Snail source to Python `ast` module
12
12
  - **compile_snail_source_with_auto_print()**: Compilation with optional auto-print for CLI usage
13
- - Re-exports from all workspace crates:
13
+ - Re-exports from workspace crates:
14
14
  - `snail-ast`: All AST types
15
- - `snail-python-ast`: All Python AST types
16
15
  - `snail-error`: Error types and formatting
17
16
  - `snail-parser`: Parsing functions
18
17
  - `snail-lower`: Lowering functions
19
- - `snail-codegen`: Code generation functions
20
18
 
21
19
  ## Compilation Pipeline
22
20
 
23
21
  The `compile_snail_source()` function orchestrates the complete compilation:
24
22
 
25
23
  1. **Parse**: `parse_program()` or `parse_awk_program()` → Snail AST
26
- 2. **Lower**: `lower_program()` or `lower_awk_program()` → Python AST
27
- 3. **Codegen**: `python_source()` → Python source string
24
+ 2. **Lower**: `lower_program()` or `lower_awk_program()` → Python `ast.Module`
28
25
 
29
26
  Each stage can fail with appropriate error types (`ParseError` or `LowerError`), wrapped in the unified `SnailError` type.
30
27
 
@@ -45,11 +42,10 @@ This enables REPL-like behavior for interactive use and CLI one-liners.
45
42
  ## Dependencies
46
43
 
47
44
  - **snail-ast**: Core AST types
48
- - **snail-python-ast**: Python AST types
49
45
  - **snail-error**: Error handling
50
46
  - **snail-parser**: Parsing logic
51
47
  - **snail-lower**: AST transformation
52
- - **snail-codegen**: Python code generation
48
+ - **pyo3**: Python AST construction
53
49
 
54
50
  ## Used By
55
51
 
@@ -0,0 +1,36 @@
1
+ use pyo3::prelude::*;
2
+
3
+ // Re-export all workspace crates for unified API
4
+ pub use snail_ast::*;
5
+ pub use snail_error::*;
6
+ pub use snail_lower::*;
7
+ pub use snail_parser::*;
8
+
9
+ /// Compilation API
10
+ pub fn compile_snail_source(
11
+ py: Python<'_>,
12
+ source: &str,
13
+ mode: CompileMode,
14
+ ) -> Result<PyObject, SnailError> {
15
+ compile_snail_source_with_auto_print(py, source, mode, false)
16
+ }
17
+
18
+ pub fn compile_snail_source_with_auto_print(
19
+ py: Python<'_>,
20
+ source: &str,
21
+ mode: CompileMode,
22
+ auto_print_last: bool,
23
+ ) -> Result<PyObject, SnailError> {
24
+ match mode {
25
+ CompileMode::Snail => {
26
+ let program = parse_program(source)?;
27
+ let module = lower_program_with_auto_print(py, &program, auto_print_last)?;
28
+ Ok(module)
29
+ }
30
+ CompileMode::Awk => {
31
+ let program = parse_awk_program(source)?;
32
+ let module = lower_awk_program_with_auto_print(py, &program, auto_print_last)?;
33
+ Ok(module)
34
+ }
35
+ }
36
+ }
@@ -1,6 +1,6 @@
1
1
  [package]
2
2
  name = "snail-error"
3
- version = "0.3.4"
3
+ version = "0.3.7"
4
4
  edition = "2024"
5
5
  readme = "README.md"
6
6
 
@@ -42,21 +42,29 @@ impl fmt::Display for ParseError {
42
42
  impl Error for ParseError {}
43
43
 
44
44
  #[derive(Debug, Clone, PartialEq, Eq)]
45
- pub struct LowerError {
46
- pub message: String,
45
+ pub enum LowerError {
46
+ Message(String),
47
+ MultiplePlaceholders { span: SourceSpan },
47
48
  }
48
49
 
49
50
  impl LowerError {
50
51
  pub fn new(message: impl Into<String>) -> Self {
51
- Self {
52
- message: message.into(),
53
- }
52
+ Self::Message(message.into())
53
+ }
54
+
55
+ pub fn multiple_placeholders(span: SourceSpan) -> Self {
56
+ Self::MultiplePlaceholders { span }
54
57
  }
55
58
  }
56
59
 
57
60
  impl fmt::Display for LowerError {
58
61
  fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
59
- write!(f, "{}", self.message)
62
+ match self {
63
+ LowerError::Message(message) => write!(f, "{message}"),
64
+ LowerError::MultiplePlaceholders { .. } => {
65
+ write!(f, "pipeline calls may include at most one placeholder")
66
+ }
67
+ }
60
68
  }
61
69
  }
62
70
 
@@ -101,7 +109,7 @@ impl From<LowerError> for SnailError {
101
109
  pub fn format_snail_error(err: &SnailError, filename: &str) -> String {
102
110
  match err {
103
111
  SnailError::Parse(parse) => format_parse_error(parse, filename),
104
- SnailError::Lower(lower) => format!("error: {}", lower.message),
112
+ SnailError::Lower(lower) => format!("error: {lower}"),
105
113
  }
106
114
  }
107
115
 
@@ -1,10 +1,10 @@
1
1
  [package]
2
2
  name = "snail-lower"
3
- version = "0.3.4"
3
+ version = "0.3.7"
4
4
  edition = "2024"
5
5
  readme = "README.md"
6
6
 
7
7
  [dependencies]
8
8
  snail-ast = { path = "../snail-ast" }
9
- snail-python-ast = { path = "../snail-python-ast" }
10
9
  snail-error = { path = "../snail-error" }
10
+ pyo3 = { version = "0.21", features = ["abi3-py310"] }
@@ -1,15 +1,15 @@
1
1
  # snail-lower
2
2
 
3
- AST transformation layer that lowers Snail AST to Python AST.
3
+ AST transformation layer that lowers Snail AST to Python `ast` nodes via pyo3.
4
4
 
5
5
  ## Purpose
6
6
 
7
- This crate is the semantic transformation core of the Snail compiler. It takes Snail AST nodes and transforms them into equivalent Python AST nodes, handling all Snail-specific features by generating appropriate Python code patterns and helper function calls.
7
+ This crate is the semantic transformation core of the Snail compiler. It takes Snail AST nodes and transforms them into equivalent Python `ast` nodes, handling all Snail-specific features by generating appropriate Python AST patterns and helper function calls.
8
8
 
9
9
  ## Key Components
10
10
 
11
- - **lower_program()**: Transforms a regular `Program` to `PyModule`
12
- - **lower_awk_program()**: Transforms an `AwkProgram` to `PyModule` with awk runtime
11
+ - **lower_program()**: Transforms a regular `Program` to a Python `ast.Module`
12
+ - **lower_awk_program()**: Transforms an `AwkProgram` to a Python `ast.Module` with awk runtime
13
13
  - **lower_awk_program_with_auto_print()**: Awk lowering with optional auto-print
14
14
  - Helper constants for generated Python code:
15
15
  - `SNAIL_TRY_HELPER`: Name for the `?` operator helper function
@@ -31,7 +31,7 @@ This crate is the semantic transformation core of the Snail compiler. It takes S
31
31
 
32
32
  ## Awk Mode Lowering
33
33
 
34
- When lowering awk programs, generates a complete Python program that:
34
+ When lowering awk programs, generates a complete Python AST that:
35
35
  1. Imports `sys` for accessing command-line arguments and stdin
36
36
  2. Executes BEGIN blocks before processing input
37
37
  3. Creates a main loop that reads lines from files or stdin
@@ -42,15 +42,14 @@ When lowering awk programs, generates a complete Python program that:
42
42
  ## Dependencies
43
43
 
44
44
  - **snail-ast**: Consumes Snail `Program` and `AwkProgram`
45
- - **snail-python-ast**: Produces Python `PyModule` and related nodes
45
+ - **pyo3**: Constructs Python `ast` nodes
46
46
  - **snail-error**: Returns `LowerError` on transformation failures
47
47
 
48
48
  ## Used By
49
49
 
50
- - **snail-codegen**: Consumes the Python AST to generate source code
51
50
  - **snail-core**: Calls lowering functions as part of the compilation pipeline
52
51
  - Tests validate transformation correctness
53
52
 
54
53
  ## Design
55
54
 
56
- The lowering process preserves Python semantics exactly - only syntax differs. All `SourceSpan` information is preserved through the transformation to enable accurate error reporting in the generated Python code.
55
+ The lowering process preserves Python semantics exactly - only syntax differs. `SourceSpan` information is used to populate Python AST location metadata for accurate error reporting.