snail-lang 0.7.4__tar.gz → 0.7.6__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.7.4 → snail_lang-0.7.6}/Cargo.lock +5 -5
- {snail_lang-0.7.4 → snail_lang-0.7.6}/PKG-INFO +2 -2
- {snail_lang-0.7.4 → snail_lang-0.7.6}/README.md +1 -1
- {snail_lang-0.7.4 → snail_lang-0.7.6}/crates/snail-ast/Cargo.toml +1 -1
- {snail_lang-0.7.4 → snail_lang-0.7.6}/crates/snail-error/Cargo.toml +1 -1
- {snail_lang-0.7.4 → snail_lang-0.7.6}/crates/snail-parser/Cargo.toml +1 -1
- {snail_lang-0.7.4 → snail_lang-0.7.6}/crates/snail-python/Cargo.toml +1 -1
- {snail_lang-0.7.4 → snail_lang-0.7.6}/crates/snail-python/src/lib.rs +23 -2
- {snail_lang-0.7.4 → snail_lang-0.7.6}/crates/snail-python/src/lower/awk.rs +29 -16
- {snail_lang-0.7.4 → snail_lang-0.7.6}/crates/snail-python/src/lower/constants.rs +3 -0
- {snail_lang-0.7.4 → snail_lang-0.7.6}/crates/snail-python/src/lower/desugar.rs +12 -207
- {snail_lang-0.7.4 → snail_lang-0.7.6}/crates/snail-python/src/lower/expr.rs +3 -81
- {snail_lang-0.7.4 → snail_lang-0.7.6}/crates/snail-python/src/lower/validate.rs +2 -2
- {snail_lang-0.7.4 → snail_lang-0.7.6}/pyproject.toml +1 -1
- {snail_lang-0.7.4 → snail_lang-0.7.6}/python/snail/cli.py +27 -2
- {snail_lang-0.7.4 → snail_lang-0.7.6}/python/snail/runtime/__init__.py +27 -0
- {snail_lang-0.7.4 → snail_lang-0.7.6}/Cargo.toml +0 -0
- {snail_lang-0.7.4 → snail_lang-0.7.6}/LICENSE +0 -0
- {snail_lang-0.7.4 → snail_lang-0.7.6}/crates/snail-ast/README.md +0 -0
- {snail_lang-0.7.4 → snail_lang-0.7.6}/crates/snail-ast/src/ast.rs +0 -0
- {snail_lang-0.7.4 → snail_lang-0.7.6}/crates/snail-ast/src/awk.rs +0 -0
- {snail_lang-0.7.4 → snail_lang-0.7.6}/crates/snail-ast/src/lib.rs +0 -0
- {snail_lang-0.7.4 → snail_lang-0.7.6}/crates/snail-error/README.md +0 -0
- {snail_lang-0.7.4 → snail_lang-0.7.6}/crates/snail-error/src/lib.rs +0 -0
- {snail_lang-0.7.4 → snail_lang-0.7.6}/crates/snail-parser/README.md +0 -0
- {snail_lang-0.7.4 → snail_lang-0.7.6}/crates/snail-parser/src/awk.rs +0 -0
- {snail_lang-0.7.4 → snail_lang-0.7.6}/crates/snail-parser/src/expr.rs +0 -0
- {snail_lang-0.7.4 → snail_lang-0.7.6}/crates/snail-parser/src/lib.rs +0 -0
- {snail_lang-0.7.4 → snail_lang-0.7.6}/crates/snail-parser/src/literal.rs +0 -0
- {snail_lang-0.7.4 → snail_lang-0.7.6}/crates/snail-parser/src/snail.pest +0 -0
- {snail_lang-0.7.4 → snail_lang-0.7.6}/crates/snail-parser/src/stmt.rs +0 -0
- {snail_lang-0.7.4 → snail_lang-0.7.6}/crates/snail-parser/src/string.rs +0 -0
- {snail_lang-0.7.4 → snail_lang-0.7.6}/crates/snail-parser/src/util.rs +0 -0
- {snail_lang-0.7.4 → snail_lang-0.7.6}/crates/snail-parser/tests/common.rs +0 -0
- {snail_lang-0.7.4 → snail_lang-0.7.6}/crates/snail-parser/tests/errors.rs +0 -0
- {snail_lang-0.7.4 → snail_lang-0.7.6}/crates/snail-parser/tests/parser.rs +0 -0
- {snail_lang-0.7.4 → snail_lang-0.7.6}/crates/snail-parser/tests/statements.rs +0 -0
- {snail_lang-0.7.4 → snail_lang-0.7.6}/crates/snail-parser/tests/syntax_expressions.rs +0 -0
- {snail_lang-0.7.4 → snail_lang-0.7.6}/crates/snail-parser/tests/syntax_strings.rs +0 -0
- {snail_lang-0.7.4 → snail_lang-0.7.6}/crates/snail-python/build.rs +0 -0
- {snail_lang-0.7.4 → snail_lang-0.7.6}/crates/snail-python/src/compiler.rs +0 -0
- {snail_lang-0.7.4 → snail_lang-0.7.6}/crates/snail-python/src/linecache.rs +0 -0
- {snail_lang-0.7.4 → snail_lang-0.7.6}/crates/snail-python/src/lower/helpers.rs +0 -0
- {snail_lang-0.7.4 → snail_lang-0.7.6}/crates/snail-python/src/lower/map.rs +0 -0
- {snail_lang-0.7.4 → snail_lang-0.7.6}/crates/snail-python/src/lower/mod.rs +0 -0
- {snail_lang-0.7.4 → snail_lang-0.7.6}/crates/snail-python/src/lower/operators.rs +0 -0
- {snail_lang-0.7.4 → snail_lang-0.7.6}/crates/snail-python/src/lower/program.rs +0 -0
- {snail_lang-0.7.4 → snail_lang-0.7.6}/crates/snail-python/src/lower/py_ast.rs +0 -0
- {snail_lang-0.7.4 → snail_lang-0.7.6}/crates/snail-python/src/lower/stmt.rs +0 -0
- {snail_lang-0.7.4 → snail_lang-0.7.6}/crates/snail-python/src/profiling.rs +0 -0
- {snail_lang-0.7.4 → snail_lang-0.7.6}/python/snail/__init__.py +0 -0
- {snail_lang-0.7.4 → snail_lang-0.7.6}/python/snail/runtime/augmented.py +0 -0
- {snail_lang-0.7.4 → snail_lang-0.7.6}/python/snail/runtime/compact_try.py +0 -0
- {snail_lang-0.7.4 → snail_lang-0.7.6}/python/snail/runtime/env.py +0 -0
- {snail_lang-0.7.4 → snail_lang-0.7.6}/python/snail/runtime/lazy_file.py +0 -0
- {snail_lang-0.7.4 → snail_lang-0.7.6}/python/snail/runtime/lazy_text.py +0 -0
- {snail_lang-0.7.4 → snail_lang-0.7.6}/python/snail/runtime/regex.py +0 -0
- {snail_lang-0.7.4 → snail_lang-0.7.6}/python/snail/runtime/structured_accessor.py +0 -0
- {snail_lang-0.7.4 → snail_lang-0.7.6}/python/snail/runtime/subprocess.py +0 -0
|
@@ -312,25 +312,25 @@ checksum = "67b1b7a3b5fe4f1376887184045fcf45c69e92af734b7aaddc05fb777b6fbd03"
|
|
|
312
312
|
|
|
313
313
|
[[package]]
|
|
314
314
|
name = "snail-ast"
|
|
315
|
-
version = "0.7.
|
|
315
|
+
version = "0.7.6"
|
|
316
316
|
|
|
317
317
|
[[package]]
|
|
318
318
|
name = "snail-error"
|
|
319
|
-
version = "0.7.
|
|
319
|
+
version = "0.7.6"
|
|
320
320
|
dependencies = [
|
|
321
321
|
"snail-ast",
|
|
322
322
|
]
|
|
323
323
|
|
|
324
324
|
[[package]]
|
|
325
325
|
name = "snail-lower"
|
|
326
|
-
version = "0.7.
|
|
326
|
+
version = "0.7.6"
|
|
327
327
|
dependencies = [
|
|
328
328
|
"snail-python",
|
|
329
329
|
]
|
|
330
330
|
|
|
331
331
|
[[package]]
|
|
332
332
|
name = "snail-parser"
|
|
333
|
-
version = "0.7.
|
|
333
|
+
version = "0.7.6"
|
|
334
334
|
dependencies = [
|
|
335
335
|
"pest",
|
|
336
336
|
"pest_derive",
|
|
@@ -340,7 +340,7 @@ dependencies = [
|
|
|
340
340
|
|
|
341
341
|
[[package]]
|
|
342
342
|
name = "snail-python"
|
|
343
|
-
version = "0.7.
|
|
343
|
+
version = "0.7.6"
|
|
344
344
|
dependencies = [
|
|
345
345
|
"pyo3",
|
|
346
346
|
"snail-ast",
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: snail-lang
|
|
3
|
-
Version: 0.7.
|
|
3
|
+
Version: 0.7.6
|
|
4
4
|
Requires-Dist: astunparse>=1.6.3 ; python_full_version < '3.9'
|
|
5
5
|
Requires-Dist: jmespath>=1.0.1
|
|
6
6
|
Requires-Dist: maturin>=1.5 ; extra == 'dev'
|
|
@@ -323,7 +323,7 @@ Documentation is WIP
|
|
|
323
323
|
|
|
324
324
|
## 🔌 Editor Support
|
|
325
325
|
|
|
326
|
-
Vim/Neovim plugin with
|
|
326
|
+
Vim/Neovim plugin with Tree-sitter-based highlighting (Neovim), formatting, and run commands:
|
|
327
327
|
|
|
328
328
|
```vim
|
|
329
329
|
Plug 'sudonym1/snail', { 'rtp': 'extras/vim' }
|
|
@@ -310,7 +310,7 @@ Documentation is WIP
|
|
|
310
310
|
|
|
311
311
|
## 🔌 Editor Support
|
|
312
312
|
|
|
313
|
-
Vim/Neovim plugin with
|
|
313
|
+
Vim/Neovim plugin with Tree-sitter-based highlighting (Neovim), formatting, and run commands:
|
|
314
314
|
|
|
315
315
|
```vim
|
|
316
316
|
Plug 'sudonym1/snail', { 'rtp': 'extras/vim' }
|
|
@@ -112,6 +112,8 @@ fn prepare_globals<'py>(
|
|
|
112
112
|
filename: &str,
|
|
113
113
|
argv: &[String],
|
|
114
114
|
auto_import: bool,
|
|
115
|
+
awk_field_separators: Option<String>,
|
|
116
|
+
awk_include_whitespace: Option<bool>,
|
|
115
117
|
) -> PyResult<Bound<'py, PyAny>> {
|
|
116
118
|
let runtime = py.import_bound("snail.runtime")?;
|
|
117
119
|
|
|
@@ -131,6 +133,16 @@ fn prepare_globals<'py>(
|
|
|
131
133
|
sys.setattr("argv", PyList::new_bound(py, argv))?;
|
|
132
134
|
|
|
133
135
|
runtime.call_method1("install_helpers", (&globals,))?;
|
|
136
|
+
let separators = awk_field_separators
|
|
137
|
+
.as_deref()
|
|
138
|
+
.filter(|value| !value.is_empty());
|
|
139
|
+
let separators_value = match separators {
|
|
140
|
+
Some(separators) => separators.into_py(py),
|
|
141
|
+
None => py.None().into_py(py),
|
|
142
|
+
};
|
|
143
|
+
globals.set_item("__snail_awk_field_separators", separators_value)?;
|
|
144
|
+
let include_whitespace = awk_include_whitespace.unwrap_or(separators.is_none());
|
|
145
|
+
globals.set_item("__snail_awk_include_whitespace", include_whitespace)?;
|
|
134
146
|
|
|
135
147
|
Ok(globals)
|
|
136
148
|
}
|
|
@@ -201,7 +213,7 @@ fn compile_ast_py(
|
|
|
201
213
|
}
|
|
202
214
|
|
|
203
215
|
#[pyfunction(name = "exec")]
|
|
204
|
-
#[pyo3(signature = (source, *, argv = Vec::new(), mode = "snail", auto_print = true, auto_import = true, filename = "<snail>", begin_code = Vec::new(), end_code = Vec::new()))]
|
|
216
|
+
#[pyo3(signature = (source, *, argv = Vec::new(), mode = "snail", auto_print = true, auto_import = true, filename = "<snail>", begin_code = Vec::new(), end_code = Vec::new(), field_separators = None, include_whitespace = None))]
|
|
205
217
|
#[allow(clippy::too_many_arguments)]
|
|
206
218
|
fn exec_py(
|
|
207
219
|
py: Python<'_>,
|
|
@@ -213,6 +225,8 @@ fn exec_py(
|
|
|
213
225
|
filename: &str,
|
|
214
226
|
begin_code: Vec<String>,
|
|
215
227
|
end_code: Vec<String>,
|
|
228
|
+
field_separators: Option<String>,
|
|
229
|
+
include_whitespace: Option<bool>,
|
|
216
230
|
) -> PyResult<i32> {
|
|
217
231
|
let profile = profile_enabled();
|
|
218
232
|
let total_start = Instant::now();
|
|
@@ -241,7 +255,14 @@ fn exec_py(
|
|
|
241
255
|
log_profile("py_compile", compile_start.elapsed());
|
|
242
256
|
}
|
|
243
257
|
let globals_start = Instant::now();
|
|
244
|
-
let globals = prepare_globals(
|
|
258
|
+
let globals = prepare_globals(
|
|
259
|
+
py,
|
|
260
|
+
strip_display_prefix(filename),
|
|
261
|
+
&argv,
|
|
262
|
+
auto_import,
|
|
263
|
+
field_separators,
|
|
264
|
+
include_whitespace,
|
|
265
|
+
)?;
|
|
245
266
|
if profile {
|
|
246
267
|
log_profile("prepare_globals", globals_start.elapsed());
|
|
247
268
|
}
|
|
@@ -270,23 +270,36 @@ pub(crate) fn lower_awk_line_loop_with_auto_print(
|
|
|
270
270
|
.call_node(
|
|
271
271
|
"Call",
|
|
272
272
|
vec![
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
273
|
+
name_expr(
|
|
274
|
+
builder,
|
|
275
|
+
SNAIL_AWK_SPLIT_HELPER,
|
|
276
|
+
span,
|
|
277
|
+
builder.load_ctx().map_err(py_err_to_lower)?,
|
|
278
|
+
)?,
|
|
279
|
+
PyList::new_bound(
|
|
280
|
+
builder.py(),
|
|
281
|
+
vec![
|
|
282
|
+
name_expr(
|
|
283
|
+
builder,
|
|
284
|
+
SNAIL_AWK_LINE_PYVAR,
|
|
285
|
+
span,
|
|
284
286
|
builder.load_ctx().map_err(py_err_to_lower)?,
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
287
|
+
)?,
|
|
288
|
+
name_expr(
|
|
289
|
+
builder,
|
|
290
|
+
SNAIL_AWK_FIELD_SEPARATORS_PYVAR,
|
|
291
|
+
span,
|
|
292
|
+
builder.load_ctx().map_err(py_err_to_lower)?,
|
|
293
|
+
)?,
|
|
294
|
+
name_expr(
|
|
295
|
+
builder,
|
|
296
|
+
SNAIL_AWK_INCLUDE_WHITESPACE_PYVAR,
|
|
297
|
+
span,
|
|
298
|
+
builder.load_ctx().map_err(py_err_to_lower)?,
|
|
299
|
+
)?,
|
|
300
|
+
],
|
|
301
|
+
)
|
|
302
|
+
.into_py(builder.py()),
|
|
290
303
|
PyList::empty_bound(builder.py()).into_py(builder.py()),
|
|
291
304
|
],
|
|
292
305
|
span,
|
|
@@ -13,6 +13,7 @@ pub const SNAIL_INCR_ATTR: &str = "__snail_incr_attr";
|
|
|
13
13
|
pub const SNAIL_INCR_INDEX: &str = "__snail_incr_index";
|
|
14
14
|
pub const SNAIL_AUG_ATTR: &str = "__snail_aug_attr";
|
|
15
15
|
pub const SNAIL_AUG_INDEX: &str = "__snail_aug_index";
|
|
16
|
+
pub(crate) const SNAIL_AWK_SPLIT_HELPER: &str = "__snail_awk_split";
|
|
16
17
|
pub(crate) const SNAIL_LET_VALUE: &str = "__snail_let_value";
|
|
17
18
|
pub(crate) const SNAIL_LET_OK: &str = "__snail_let_ok";
|
|
18
19
|
pub(crate) const SNAIL_LET_KEEP: &str = "__snail_let_keep";
|
|
@@ -28,6 +29,8 @@ pub(crate) const SNAIL_AWK_MATCH: &str = "$m";
|
|
|
28
29
|
pub(crate) const SNAIL_AWK_FIELDS: &str = "$f";
|
|
29
30
|
pub(crate) const SNAIL_AWK_LINE_PYVAR: &str = "__snail_line";
|
|
30
31
|
pub(crate) const SNAIL_AWK_FIELDS_PYVAR: &str = "__snail_fields";
|
|
32
|
+
pub(crate) const SNAIL_AWK_FIELD_SEPARATORS_PYVAR: &str = "__snail_awk_field_separators";
|
|
33
|
+
pub(crate) const SNAIL_AWK_INCLUDE_WHITESPACE_PYVAR: &str = "__snail_awk_include_whitespace";
|
|
31
34
|
pub(crate) const SNAIL_AWK_NR_PYVAR: &str = "__snail_nr_user";
|
|
32
35
|
pub(crate) const SNAIL_AWK_FNR_PYVAR: &str = "__snail_fnr_user";
|
|
33
36
|
pub(crate) const SNAIL_AWK_PATH_PYVAR: &str = "__snail_path_user";
|
|
@@ -553,26 +553,18 @@ impl LambdaHoister {
|
|
|
553
553
|
span: span.clone(),
|
|
554
554
|
},
|
|
555
555
|
Expr::Lambda { params, body, span } => {
|
|
556
|
-
|
|
557
|
-
|
|
558
|
-
|
|
559
|
-
|
|
560
|
-
|
|
561
|
-
|
|
562
|
-
|
|
563
|
-
|
|
564
|
-
|
|
565
|
-
|
|
566
|
-
|
|
567
|
-
|
|
568
|
-
span: span.clone(),
|
|
569
|
-
}
|
|
570
|
-
} else {
|
|
571
|
-
Expr::Lambda {
|
|
572
|
-
params: params.clone(),
|
|
573
|
-
body: body.clone(),
|
|
574
|
-
span: span.clone(),
|
|
575
|
-
}
|
|
556
|
+
let params = self.desugar_params(params, prelude);
|
|
557
|
+
let body = self.desugar_block(body);
|
|
558
|
+
let name = self.next_lambda_name();
|
|
559
|
+
prelude.push(Stmt::Def {
|
|
560
|
+
name: name.clone(),
|
|
561
|
+
params,
|
|
562
|
+
body,
|
|
563
|
+
span: span.clone(),
|
|
564
|
+
});
|
|
565
|
+
Expr::Name {
|
|
566
|
+
name,
|
|
567
|
+
span: span.clone(),
|
|
576
568
|
}
|
|
577
569
|
}
|
|
578
570
|
Expr::Compound { expressions, span } => Expr::Compound {
|
|
@@ -717,190 +709,3 @@ impl LambdaHoister {
|
|
|
717
709
|
}
|
|
718
710
|
}
|
|
719
711
|
}
|
|
720
|
-
|
|
721
|
-
fn lambda_requires_def(params: &[Parameter], body: &[Stmt]) -> bool {
|
|
722
|
-
if body.iter().any(|stmt| !matches!(stmt, Stmt::Expr { .. })) {
|
|
723
|
-
return true;
|
|
724
|
-
}
|
|
725
|
-
|
|
726
|
-
for param in params {
|
|
727
|
-
if let Parameter::Regular { default, .. } = param
|
|
728
|
-
&& let Some(default) = default
|
|
729
|
-
&& expr_contains_complex_lambda(default)
|
|
730
|
-
{
|
|
731
|
-
return true;
|
|
732
|
-
}
|
|
733
|
-
}
|
|
734
|
-
|
|
735
|
-
for stmt in body {
|
|
736
|
-
if let Stmt::Expr { value, .. } = stmt
|
|
737
|
-
&& expr_contains_complex_lambda(value)
|
|
738
|
-
{
|
|
739
|
-
return true;
|
|
740
|
-
}
|
|
741
|
-
}
|
|
742
|
-
|
|
743
|
-
false
|
|
744
|
-
}
|
|
745
|
-
|
|
746
|
-
fn expr_contains_complex_lambda(expr: &Expr) -> bool {
|
|
747
|
-
match expr {
|
|
748
|
-
Expr::Yield { value, .. } => value
|
|
749
|
-
.as_ref()
|
|
750
|
-
.is_some_and(|expr| expr_contains_complex_lambda(expr)),
|
|
751
|
-
Expr::YieldFrom { expr, .. } => expr_contains_complex_lambda(expr),
|
|
752
|
-
Expr::Lambda { params, body, .. } => lambda_requires_def(params, body),
|
|
753
|
-
Expr::Unary { expr, .. } => expr_contains_complex_lambda(expr),
|
|
754
|
-
Expr::Binary { left, right, .. } => {
|
|
755
|
-
expr_contains_complex_lambda(left) || expr_contains_complex_lambda(right)
|
|
756
|
-
}
|
|
757
|
-
Expr::AugAssign { target, value, .. } => {
|
|
758
|
-
assign_target_contains_complex_lambda(target) || expr_contains_complex_lambda(value)
|
|
759
|
-
}
|
|
760
|
-
Expr::PrefixIncr { target, .. } | Expr::PostfixIncr { target, .. } => {
|
|
761
|
-
assign_target_contains_complex_lambda(target)
|
|
762
|
-
}
|
|
763
|
-
Expr::Compare {
|
|
764
|
-
left, comparators, ..
|
|
765
|
-
} => {
|
|
766
|
-
if expr_contains_complex_lambda(left) {
|
|
767
|
-
return true;
|
|
768
|
-
}
|
|
769
|
-
comparators.iter().any(expr_contains_complex_lambda)
|
|
770
|
-
}
|
|
771
|
-
Expr::IfExpr {
|
|
772
|
-
test, body, orelse, ..
|
|
773
|
-
} => {
|
|
774
|
-
expr_contains_complex_lambda(test)
|
|
775
|
-
|| expr_contains_complex_lambda(body)
|
|
776
|
-
|| expr_contains_complex_lambda(orelse)
|
|
777
|
-
}
|
|
778
|
-
Expr::TryExpr { expr, fallback, .. } => {
|
|
779
|
-
expr_contains_complex_lambda(expr)
|
|
780
|
-
|| fallback
|
|
781
|
-
.as_ref()
|
|
782
|
-
.is_some_and(|expr| expr_contains_complex_lambda(expr))
|
|
783
|
-
}
|
|
784
|
-
Expr::Compound { expressions, .. } => expressions.iter().any(expr_contains_complex_lambda),
|
|
785
|
-
Expr::Regex { pattern, .. } => regex_pattern_contains_complex_lambda(pattern),
|
|
786
|
-
Expr::RegexMatch { value, pattern, .. } => {
|
|
787
|
-
expr_contains_complex_lambda(value) || regex_pattern_contains_complex_lambda(pattern)
|
|
788
|
-
}
|
|
789
|
-
Expr::Subprocess { parts, .. } => parts.iter().any(|part| match part {
|
|
790
|
-
SubprocessPart::Expr(expr) => expr_contains_complex_lambda(expr),
|
|
791
|
-
SubprocessPart::Text(_) => false,
|
|
792
|
-
}),
|
|
793
|
-
Expr::Call { func, args, .. } => {
|
|
794
|
-
if expr_contains_complex_lambda(func) {
|
|
795
|
-
return true;
|
|
796
|
-
}
|
|
797
|
-
args.iter().any(|arg| match arg {
|
|
798
|
-
Argument::Positional { value, .. }
|
|
799
|
-
| Argument::Keyword { value, .. }
|
|
800
|
-
| Argument::Star { value, .. }
|
|
801
|
-
| Argument::KwStar { value, .. } => expr_contains_complex_lambda(value),
|
|
802
|
-
})
|
|
803
|
-
}
|
|
804
|
-
Expr::Attribute { value, .. } => expr_contains_complex_lambda(value),
|
|
805
|
-
Expr::Index { value, index, .. } => {
|
|
806
|
-
expr_contains_complex_lambda(value) || expr_contains_complex_lambda(index)
|
|
807
|
-
}
|
|
808
|
-
Expr::Paren { expr, .. } => expr_contains_complex_lambda(expr),
|
|
809
|
-
Expr::List { elements, .. } | Expr::Tuple { elements, .. } => {
|
|
810
|
-
elements.iter().any(expr_contains_complex_lambda)
|
|
811
|
-
}
|
|
812
|
-
Expr::Set { elements, .. } => elements.iter().any(expr_contains_complex_lambda),
|
|
813
|
-
Expr::Dict { entries, .. } => entries.iter().any(|(key, value)| {
|
|
814
|
-
expr_contains_complex_lambda(key) || expr_contains_complex_lambda(value)
|
|
815
|
-
}),
|
|
816
|
-
Expr::Slice { start, end, .. } => {
|
|
817
|
-
start
|
|
818
|
-
.as_ref()
|
|
819
|
-
.is_some_and(|expr| expr_contains_complex_lambda(expr))
|
|
820
|
-
|| end
|
|
821
|
-
.as_ref()
|
|
822
|
-
.is_some_and(|expr| expr_contains_complex_lambda(expr))
|
|
823
|
-
}
|
|
824
|
-
Expr::ListComp {
|
|
825
|
-
element, iter, ifs, ..
|
|
826
|
-
} => {
|
|
827
|
-
expr_contains_complex_lambda(element)
|
|
828
|
-
|| expr_contains_complex_lambda(iter)
|
|
829
|
-
|| ifs.iter().any(expr_contains_complex_lambda)
|
|
830
|
-
}
|
|
831
|
-
Expr::DictComp {
|
|
832
|
-
key,
|
|
833
|
-
value,
|
|
834
|
-
iter,
|
|
835
|
-
ifs,
|
|
836
|
-
..
|
|
837
|
-
} => {
|
|
838
|
-
expr_contains_complex_lambda(key)
|
|
839
|
-
|| expr_contains_complex_lambda(value)
|
|
840
|
-
|| expr_contains_complex_lambda(iter)
|
|
841
|
-
|| ifs.iter().any(expr_contains_complex_lambda)
|
|
842
|
-
}
|
|
843
|
-
Expr::FString { parts, .. } => parts.iter().any(fstring_part_contains_complex_lambda),
|
|
844
|
-
Expr::Name { .. }
|
|
845
|
-
| Expr::Placeholder { .. }
|
|
846
|
-
| Expr::Number { .. }
|
|
847
|
-
| Expr::String { .. }
|
|
848
|
-
| Expr::Bool { .. }
|
|
849
|
-
| Expr::None { .. }
|
|
850
|
-
| Expr::StructuredAccessor { .. }
|
|
851
|
-
| Expr::FieldIndex { .. } => false,
|
|
852
|
-
}
|
|
853
|
-
}
|
|
854
|
-
|
|
855
|
-
fn assign_target_contains_complex_lambda(target: &AssignTarget) -> bool {
|
|
856
|
-
match target {
|
|
857
|
-
AssignTarget::Name { .. } => false,
|
|
858
|
-
AssignTarget::Attribute { value, .. } => expr_contains_complex_lambda(value),
|
|
859
|
-
AssignTarget::Index { value, index, .. } => {
|
|
860
|
-
expr_contains_complex_lambda(value) || expr_contains_complex_lambda(index)
|
|
861
|
-
}
|
|
862
|
-
AssignTarget::Starred { target, .. } => assign_target_contains_complex_lambda(target),
|
|
863
|
-
AssignTarget::Tuple { elements, .. } | AssignTarget::List { elements, .. } => {
|
|
864
|
-
elements.iter().any(assign_target_contains_complex_lambda)
|
|
865
|
-
}
|
|
866
|
-
}
|
|
867
|
-
}
|
|
868
|
-
|
|
869
|
-
fn regex_pattern_contains_complex_lambda(pattern: &RegexPattern) -> bool {
|
|
870
|
-
match pattern {
|
|
871
|
-
RegexPattern::Literal(_) => false,
|
|
872
|
-
RegexPattern::Interpolated(parts) => parts.iter().any(fstring_part_contains_complex_lambda),
|
|
873
|
-
}
|
|
874
|
-
}
|
|
875
|
-
|
|
876
|
-
fn fstring_part_contains_complex_lambda(part: &FStringPart) -> bool {
|
|
877
|
-
match part {
|
|
878
|
-
FStringPart::Text(_) => false,
|
|
879
|
-
FStringPart::Expr(expr) => fstring_expr_contains_complex_lambda(expr),
|
|
880
|
-
}
|
|
881
|
-
}
|
|
882
|
-
|
|
883
|
-
fn fstring_expr_contains_complex_lambda(expr: &FStringExpr) -> bool {
|
|
884
|
-
if expr_contains_complex_lambda(expr.expr.as_ref()) {
|
|
885
|
-
return true;
|
|
886
|
-
}
|
|
887
|
-
expr.format_spec
|
|
888
|
-
.as_ref()
|
|
889
|
-
.is_some_and(|parts| parts.iter().any(fstring_part_contains_complex_lambda))
|
|
890
|
-
}
|
|
891
|
-
|
|
892
|
-
fn ensure_lambda_return(mut body: Vec<Stmt>) -> Vec<Stmt> {
|
|
893
|
-
let Some(last) = body.pop() else {
|
|
894
|
-
return body;
|
|
895
|
-
};
|
|
896
|
-
match last {
|
|
897
|
-
Stmt::Expr { value, span, .. } => {
|
|
898
|
-
body.push(Stmt::Return {
|
|
899
|
-
value: Some(value),
|
|
900
|
-
span,
|
|
901
|
-
});
|
|
902
|
-
}
|
|
903
|
-
other => body.push(other),
|
|
904
|
-
}
|
|
905
|
-
body
|
|
906
|
-
}
|
|
@@ -10,7 +10,6 @@ use super::operators::{
|
|
|
10
10
|
lower_unary_op,
|
|
11
11
|
};
|
|
12
12
|
use super::py_ast::{AstBuilder, py_err_to_lower};
|
|
13
|
-
use super::stmt::lower_parameters;
|
|
14
13
|
|
|
15
14
|
pub(crate) fn lower_expr(builder: &AstBuilder<'_>, expr: &Expr) -> Result<PyObject, LowerError> {
|
|
16
15
|
lower_expr_with_exception(builder, expr, None)
|
|
@@ -782,9 +781,9 @@ pub(crate) fn lower_expr_with_exception(
|
|
|
782
781
|
.call_node("YieldFrom", vec![value], span)
|
|
783
782
|
.map_err(py_err_to_lower)
|
|
784
783
|
}
|
|
785
|
-
Expr::Lambda {
|
|
786
|
-
|
|
787
|
-
|
|
784
|
+
Expr::Lambda { .. } => Err(LowerError::new(
|
|
785
|
+
"def expressions should be desugared before lowering",
|
|
786
|
+
)),
|
|
788
787
|
Expr::Compound { expressions, span } => {
|
|
789
788
|
let mut lowered = Vec::new();
|
|
790
789
|
for expr in expressions {
|
|
@@ -1431,83 +1430,6 @@ fn lower_subprocess_parts(
|
|
|
1431
1430
|
Ok(lowered_parts)
|
|
1432
1431
|
}
|
|
1433
1432
|
|
|
1434
|
-
fn lower_lambda_expr(
|
|
1435
|
-
builder: &AstBuilder<'_>,
|
|
1436
|
-
params: &[Parameter],
|
|
1437
|
-
body: &[Stmt],
|
|
1438
|
-
span: &SourceSpan,
|
|
1439
|
-
exception_name: Option<&str>,
|
|
1440
|
-
) -> Result<PyObject, LowerError> {
|
|
1441
|
-
let args = lower_parameters(builder, params, exception_name)?;
|
|
1442
|
-
let body_expr = lower_lambda_body_expr(builder, body, span, exception_name)?;
|
|
1443
|
-
builder
|
|
1444
|
-
.call_node("Lambda", vec![args, body_expr], span)
|
|
1445
|
-
.map_err(py_err_to_lower)
|
|
1446
|
-
}
|
|
1447
|
-
|
|
1448
|
-
fn lower_lambda_body_expr(
|
|
1449
|
-
builder: &AstBuilder<'_>,
|
|
1450
|
-
body: &[Stmt],
|
|
1451
|
-
span: &SourceSpan,
|
|
1452
|
-
exception_name: Option<&str>,
|
|
1453
|
-
) -> Result<PyObject, LowerError> {
|
|
1454
|
-
let mut lowered = Vec::new();
|
|
1455
|
-
for stmt in body {
|
|
1456
|
-
match stmt {
|
|
1457
|
-
Stmt::Expr { value, .. } => {
|
|
1458
|
-
lowered.push(lower_expr_with_exception(builder, value, exception_name)?);
|
|
1459
|
-
}
|
|
1460
|
-
_ => {
|
|
1461
|
-
return Err(LowerError::new(
|
|
1462
|
-
"def expression bodies must contain only expression statements",
|
|
1463
|
-
));
|
|
1464
|
-
}
|
|
1465
|
-
}
|
|
1466
|
-
}
|
|
1467
|
-
|
|
1468
|
-
if lowered.is_empty() {
|
|
1469
|
-
return builder
|
|
1470
|
-
.call_node(
|
|
1471
|
-
"Constant",
|
|
1472
|
-
vec![builder.py().None().into_py(builder.py())],
|
|
1473
|
-
span,
|
|
1474
|
-
)
|
|
1475
|
-
.map_err(py_err_to_lower);
|
|
1476
|
-
}
|
|
1477
|
-
|
|
1478
|
-
if lowered.len() == 1 {
|
|
1479
|
-
return Ok(lowered.pop().unwrap());
|
|
1480
|
-
}
|
|
1481
|
-
|
|
1482
|
-
let tuple_expr = builder
|
|
1483
|
-
.call_node(
|
|
1484
|
-
"Tuple",
|
|
1485
|
-
vec![
|
|
1486
|
-
PyList::new_bound(builder.py(), lowered).into_py(builder.py()),
|
|
1487
|
-
builder.load_ctx().map_err(py_err_to_lower)?,
|
|
1488
|
-
],
|
|
1489
|
-
span,
|
|
1490
|
-
)
|
|
1491
|
-
.map_err(py_err_to_lower)?;
|
|
1492
|
-
let index_expr = builder
|
|
1493
|
-
.call_node(
|
|
1494
|
-
"UnaryOp",
|
|
1495
|
-
vec![
|
|
1496
|
-
lower_unary_op(builder, UnaryOp::Minus)?,
|
|
1497
|
-
number_expr(builder, "1", span)?,
|
|
1498
|
-
],
|
|
1499
|
-
span,
|
|
1500
|
-
)
|
|
1501
|
-
.map_err(py_err_to_lower)?;
|
|
1502
|
-
subscript_expr(
|
|
1503
|
-
builder,
|
|
1504
|
-
tuple_expr,
|
|
1505
|
-
index_expr,
|
|
1506
|
-
builder.load_ctx().map_err(py_err_to_lower)?,
|
|
1507
|
-
span,
|
|
1508
|
-
)
|
|
1509
|
-
}
|
|
1510
|
-
|
|
1511
1433
|
fn lower_call_arguments(
|
|
1512
1434
|
builder: &AstBuilder<'_>,
|
|
1513
1435
|
args: &[Argument],
|
|
@@ -269,8 +269,8 @@ fn check_expr(expr: &Expr, in_function: bool) -> Result<(), LowerError> {
|
|
|
269
269
|
for param in params {
|
|
270
270
|
check_param(param)?;
|
|
271
271
|
}
|
|
272
|
-
//
|
|
273
|
-
check_stmts(body,
|
|
272
|
+
// Anonymous defs are hoisted to defs; allow yield in their bodies.
|
|
273
|
+
check_stmts(body, true)?;
|
|
274
274
|
}
|
|
275
275
|
Expr::Compound { expressions, .. } => {
|
|
276
276
|
for expr in expressions {
|
|
@@ -8,8 +8,8 @@ from . import __build_info__, compile_ast, exec
|
|
|
8
8
|
|
|
9
9
|
_USAGE = "snail [options] -f <file> [args]...\n snail [options] <code> [args]..."
|
|
10
10
|
_DESCRIPTION = "Snail programming language interpreter"
|
|
11
|
-
_BOOLEAN_FLAGS = frozenset("
|
|
12
|
-
_VALUE_FLAGS = frozenset("
|
|
11
|
+
_BOOLEAN_FLAGS = frozenset("amPIvhW")
|
|
12
|
+
_VALUE_FLAGS = frozenset("fbeF")
|
|
13
13
|
|
|
14
14
|
|
|
15
15
|
def _display_filename(filename: str) -> str:
|
|
@@ -101,6 +101,8 @@ class _Args:
|
|
|
101
101
|
self.help = False
|
|
102
102
|
self.begin_code: list[str] = []
|
|
103
103
|
self.end_code: list[str] = []
|
|
104
|
+
self.field_separators: list[str] = []
|
|
105
|
+
self.include_whitespace = False
|
|
104
106
|
self.args: list[str] = []
|
|
105
107
|
|
|
106
108
|
|
|
@@ -123,6 +125,11 @@ def _print_help(file=None) -> None:
|
|
|
123
125
|
" -e, --end <code> end block code (repeatable)",
|
|
124
126
|
file=file,
|
|
125
127
|
)
|
|
128
|
+
print(
|
|
129
|
+
" -F, --field-separator <chars> field separator characters (repeatable)",
|
|
130
|
+
file=file,
|
|
131
|
+
)
|
|
132
|
+
print(" -W, --whitespace include whitespace as a separator", file=file)
|
|
126
133
|
print(
|
|
127
134
|
" -P, --no-print disable auto-print of implicit return value",
|
|
128
135
|
file=file,
|
|
@@ -244,6 +251,11 @@ def _parse_args(argv: list[str]) -> _Args:
|
|
|
244
251
|
code_found = True
|
|
245
252
|
idx += 2
|
|
246
253
|
continue
|
|
254
|
+
if token in ("-W", "--whitespace"):
|
|
255
|
+
args.include_whitespace = True
|
|
256
|
+
args.awk = True
|
|
257
|
+
idx += 1
|
|
258
|
+
continue
|
|
247
259
|
if token in ("-b", "--begin"):
|
|
248
260
|
if idx + 1 >= len(argv):
|
|
249
261
|
raise ValueError(f"option {token} requires an argument")
|
|
@@ -256,6 +268,13 @@ def _parse_args(argv: list[str]) -> _Args:
|
|
|
256
268
|
args.end_code.append(argv[idx + 1])
|
|
257
269
|
idx += 2
|
|
258
270
|
continue
|
|
271
|
+
if token in ("-F", "--field-separator"):
|
|
272
|
+
if idx + 1 >= len(argv):
|
|
273
|
+
raise ValueError(f"option {token} requires an argument")
|
|
274
|
+
args.field_separators.append(argv[idx + 1])
|
|
275
|
+
args.awk = True
|
|
276
|
+
idx += 2
|
|
277
|
+
continue
|
|
259
278
|
raise ValueError(f"unknown option: {token}")
|
|
260
279
|
return args
|
|
261
280
|
|
|
@@ -394,6 +413,10 @@ def main(argv: Optional[list[str]] = None) -> int:
|
|
|
394
413
|
print(output)
|
|
395
414
|
return 0
|
|
396
415
|
|
|
416
|
+
separators = "".join(namespace.field_separators)
|
|
417
|
+
field_separators = separators if separators else None
|
|
418
|
+
include_whitespace = namespace.include_whitespace or field_separators is None
|
|
419
|
+
|
|
397
420
|
return exec(
|
|
398
421
|
source,
|
|
399
422
|
argv=args,
|
|
@@ -403,6 +426,8 @@ def main(argv: Optional[list[str]] = None) -> int:
|
|
|
403
426
|
filename=filename,
|
|
404
427
|
begin_code=namespace.begin_code,
|
|
405
428
|
end_code=namespace.end_code,
|
|
429
|
+
field_separators=field_separators,
|
|
430
|
+
include_whitespace=include_whitespace,
|
|
406
431
|
)
|
|
407
432
|
|
|
408
433
|
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
from __future__ import annotations
|
|
2
2
|
|
|
3
3
|
import importlib
|
|
4
|
+
import re
|
|
4
5
|
from typing import Optional
|
|
5
6
|
|
|
6
7
|
__all__ = ["install_helpers", "AutoImportDict", "AUTO_IMPORT_NAMES"]
|
|
@@ -49,6 +50,7 @@ _incr_index = None
|
|
|
49
50
|
_aug_attr = None
|
|
50
51
|
_aug_index = None
|
|
51
52
|
_env_map = None
|
|
53
|
+
_awk_split_cache: dict[tuple[str, bool], re.Pattern[str]] = {}
|
|
52
54
|
|
|
53
55
|
|
|
54
56
|
def _get_compact_try():
|
|
@@ -221,6 +223,28 @@ def _lazy_aug_index(obj, index, value, op: str):
|
|
|
221
223
|
return _get_aug_index()(obj, index, value, op)
|
|
222
224
|
|
|
223
225
|
|
|
226
|
+
def __snail_awk_split(line: str, separators: Optional[str], include_whitespace: bool):
|
|
227
|
+
if not separators:
|
|
228
|
+
return line.split()
|
|
229
|
+
if not include_whitespace:
|
|
230
|
+
if len(separators) == 1:
|
|
231
|
+
return line.split(separators)
|
|
232
|
+
regex = _awk_split_cache.get((separators, False))
|
|
233
|
+
if regex is None:
|
|
234
|
+
regex = re.compile(f"[{re.escape(separators)}]")
|
|
235
|
+
_awk_split_cache[(separators, False)] = regex
|
|
236
|
+
return regex.split(line)
|
|
237
|
+
|
|
238
|
+
stripped = line.strip()
|
|
239
|
+
if not stripped:
|
|
240
|
+
return []
|
|
241
|
+
regex = _awk_split_cache.get((separators, True))
|
|
242
|
+
if regex is None:
|
|
243
|
+
regex = re.compile(f"(?:\\s+|[{re.escape(separators)}])")
|
|
244
|
+
_awk_split_cache[(separators, True)] = regex
|
|
245
|
+
return regex.split(stripped)
|
|
246
|
+
|
|
247
|
+
|
|
224
248
|
def __snail_partial(func, /, *args, **kwargs):
|
|
225
249
|
import functools
|
|
226
250
|
|
|
@@ -255,6 +279,9 @@ def install_helpers(globals_dict: dict) -> None:
|
|
|
255
279
|
globals_dict["__snail_incr_index"] = _lazy_incr_index
|
|
256
280
|
globals_dict["__snail_aug_attr"] = _lazy_aug_attr
|
|
257
281
|
globals_dict["__snail_aug_index"] = _lazy_aug_index
|
|
282
|
+
globals_dict["__snail_awk_split"] = __snail_awk_split
|
|
283
|
+
globals_dict["__snail_awk_field_separators"] = None
|
|
284
|
+
globals_dict["__snail_awk_include_whitespace"] = False
|
|
258
285
|
globals_dict["__snail_env"] = _get_env_map()
|
|
259
286
|
globals_dict["js"] = _lazy_js
|
|
260
287
|
globals_dict["__SnailLazyText"] = _get_lazy_text_class()
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|