snail-lang 0.7.4__tar.gz → 0.7.5__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 (59) hide show
  1. {snail_lang-0.7.4 → snail_lang-0.7.5}/Cargo.lock +5 -5
  2. {snail_lang-0.7.4 → snail_lang-0.7.5}/PKG-INFO +2 -2
  3. {snail_lang-0.7.4 → snail_lang-0.7.5}/README.md +1 -1
  4. {snail_lang-0.7.4 → snail_lang-0.7.5}/crates/snail-ast/Cargo.toml +1 -1
  5. {snail_lang-0.7.4 → snail_lang-0.7.5}/crates/snail-error/Cargo.toml +1 -1
  6. {snail_lang-0.7.4 → snail_lang-0.7.5}/crates/snail-parser/Cargo.toml +1 -1
  7. {snail_lang-0.7.4 → snail_lang-0.7.5}/crates/snail-python/Cargo.toml +1 -1
  8. {snail_lang-0.7.4 → snail_lang-0.7.5}/crates/snail-python/src/lib.rs +23 -2
  9. {snail_lang-0.7.4 → snail_lang-0.7.5}/crates/snail-python/src/lower/awk.rs +29 -16
  10. {snail_lang-0.7.4 → snail_lang-0.7.5}/crates/snail-python/src/lower/constants.rs +3 -0
  11. {snail_lang-0.7.4 → snail_lang-0.7.5}/pyproject.toml +1 -1
  12. {snail_lang-0.7.4 → snail_lang-0.7.5}/python/snail/cli.py +27 -2
  13. {snail_lang-0.7.4 → snail_lang-0.7.5}/python/snail/runtime/__init__.py +27 -0
  14. {snail_lang-0.7.4 → snail_lang-0.7.5}/Cargo.toml +0 -0
  15. {snail_lang-0.7.4 → snail_lang-0.7.5}/LICENSE +0 -0
  16. {snail_lang-0.7.4 → snail_lang-0.7.5}/crates/snail-ast/README.md +0 -0
  17. {snail_lang-0.7.4 → snail_lang-0.7.5}/crates/snail-ast/src/ast.rs +0 -0
  18. {snail_lang-0.7.4 → snail_lang-0.7.5}/crates/snail-ast/src/awk.rs +0 -0
  19. {snail_lang-0.7.4 → snail_lang-0.7.5}/crates/snail-ast/src/lib.rs +0 -0
  20. {snail_lang-0.7.4 → snail_lang-0.7.5}/crates/snail-error/README.md +0 -0
  21. {snail_lang-0.7.4 → snail_lang-0.7.5}/crates/snail-error/src/lib.rs +0 -0
  22. {snail_lang-0.7.4 → snail_lang-0.7.5}/crates/snail-parser/README.md +0 -0
  23. {snail_lang-0.7.4 → snail_lang-0.7.5}/crates/snail-parser/src/awk.rs +0 -0
  24. {snail_lang-0.7.4 → snail_lang-0.7.5}/crates/snail-parser/src/expr.rs +0 -0
  25. {snail_lang-0.7.4 → snail_lang-0.7.5}/crates/snail-parser/src/lib.rs +0 -0
  26. {snail_lang-0.7.4 → snail_lang-0.7.5}/crates/snail-parser/src/literal.rs +0 -0
  27. {snail_lang-0.7.4 → snail_lang-0.7.5}/crates/snail-parser/src/snail.pest +0 -0
  28. {snail_lang-0.7.4 → snail_lang-0.7.5}/crates/snail-parser/src/stmt.rs +0 -0
  29. {snail_lang-0.7.4 → snail_lang-0.7.5}/crates/snail-parser/src/string.rs +0 -0
  30. {snail_lang-0.7.4 → snail_lang-0.7.5}/crates/snail-parser/src/util.rs +0 -0
  31. {snail_lang-0.7.4 → snail_lang-0.7.5}/crates/snail-parser/tests/common.rs +0 -0
  32. {snail_lang-0.7.4 → snail_lang-0.7.5}/crates/snail-parser/tests/errors.rs +0 -0
  33. {snail_lang-0.7.4 → snail_lang-0.7.5}/crates/snail-parser/tests/parser.rs +0 -0
  34. {snail_lang-0.7.4 → snail_lang-0.7.5}/crates/snail-parser/tests/statements.rs +0 -0
  35. {snail_lang-0.7.4 → snail_lang-0.7.5}/crates/snail-parser/tests/syntax_expressions.rs +0 -0
  36. {snail_lang-0.7.4 → snail_lang-0.7.5}/crates/snail-parser/tests/syntax_strings.rs +0 -0
  37. {snail_lang-0.7.4 → snail_lang-0.7.5}/crates/snail-python/build.rs +0 -0
  38. {snail_lang-0.7.4 → snail_lang-0.7.5}/crates/snail-python/src/compiler.rs +0 -0
  39. {snail_lang-0.7.4 → snail_lang-0.7.5}/crates/snail-python/src/linecache.rs +0 -0
  40. {snail_lang-0.7.4 → snail_lang-0.7.5}/crates/snail-python/src/lower/desugar.rs +0 -0
  41. {snail_lang-0.7.4 → snail_lang-0.7.5}/crates/snail-python/src/lower/expr.rs +0 -0
  42. {snail_lang-0.7.4 → snail_lang-0.7.5}/crates/snail-python/src/lower/helpers.rs +0 -0
  43. {snail_lang-0.7.4 → snail_lang-0.7.5}/crates/snail-python/src/lower/map.rs +0 -0
  44. {snail_lang-0.7.4 → snail_lang-0.7.5}/crates/snail-python/src/lower/mod.rs +0 -0
  45. {snail_lang-0.7.4 → snail_lang-0.7.5}/crates/snail-python/src/lower/operators.rs +0 -0
  46. {snail_lang-0.7.4 → snail_lang-0.7.5}/crates/snail-python/src/lower/program.rs +0 -0
  47. {snail_lang-0.7.4 → snail_lang-0.7.5}/crates/snail-python/src/lower/py_ast.rs +0 -0
  48. {snail_lang-0.7.4 → snail_lang-0.7.5}/crates/snail-python/src/lower/stmt.rs +0 -0
  49. {snail_lang-0.7.4 → snail_lang-0.7.5}/crates/snail-python/src/lower/validate.rs +0 -0
  50. {snail_lang-0.7.4 → snail_lang-0.7.5}/crates/snail-python/src/profiling.rs +0 -0
  51. {snail_lang-0.7.4 → snail_lang-0.7.5}/python/snail/__init__.py +0 -0
  52. {snail_lang-0.7.4 → snail_lang-0.7.5}/python/snail/runtime/augmented.py +0 -0
  53. {snail_lang-0.7.4 → snail_lang-0.7.5}/python/snail/runtime/compact_try.py +0 -0
  54. {snail_lang-0.7.4 → snail_lang-0.7.5}/python/snail/runtime/env.py +0 -0
  55. {snail_lang-0.7.4 → snail_lang-0.7.5}/python/snail/runtime/lazy_file.py +0 -0
  56. {snail_lang-0.7.4 → snail_lang-0.7.5}/python/snail/runtime/lazy_text.py +0 -0
  57. {snail_lang-0.7.4 → snail_lang-0.7.5}/python/snail/runtime/regex.py +0 -0
  58. {snail_lang-0.7.4 → snail_lang-0.7.5}/python/snail/runtime/structured_accessor.py +0 -0
  59. {snail_lang-0.7.4 → snail_lang-0.7.5}/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.4"
315
+ version = "0.7.5"
316
316
 
317
317
  [[package]]
318
318
  name = "snail-error"
319
- version = "0.7.4"
319
+ version = "0.7.5"
320
320
  dependencies = [
321
321
  "snail-ast",
322
322
  ]
323
323
 
324
324
  [[package]]
325
325
  name = "snail-lower"
326
- version = "0.7.4"
326
+ version = "0.7.5"
327
327
  dependencies = [
328
328
  "snail-python",
329
329
  ]
330
330
 
331
331
  [[package]]
332
332
  name = "snail-parser"
333
- version = "0.7.4"
333
+ version = "0.7.5"
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.4"
343
+ version = "0.7.5"
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.4
3
+ Version: 0.7.5
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 syntax highlighting, formatting, and run commands:
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 syntax highlighting, formatting, and run commands:
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' }
@@ -1,6 +1,6 @@
1
1
  [package]
2
2
  name = "snail-ast"
3
- version = "0.7.4"
3
+ version = "0.7.5"
4
4
  edition = "2024"
5
5
  readme = "README.md"
6
6
 
@@ -1,6 +1,6 @@
1
1
  [package]
2
2
  name = "snail-error"
3
- version = "0.7.4"
3
+ version = "0.7.5"
4
4
  edition = "2024"
5
5
  readme = "README.md"
6
6
 
@@ -1,6 +1,6 @@
1
1
  [package]
2
2
  name = "snail-parser"
3
- version = "0.7.4"
3
+ version = "0.7.5"
4
4
  edition = "2024"
5
5
  readme = "README.md"
6
6
 
@@ -1,6 +1,6 @@
1
1
  [package]
2
2
  name = "snail-python"
3
- version = "0.7.4"
3
+ version = "0.7.5"
4
4
  edition.workspace = true
5
5
  build = "build.rs"
6
6
 
@@ -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(py, strip_display_prefix(filename), &argv, auto_import)?;
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
- builder
274
- .call_node(
275
- "Attribute",
276
- vec![
277
- name_expr(
278
- builder,
279
- SNAIL_AWK_LINE_PYVAR,
280
- span,
281
- builder.load_ctx().map_err(py_err_to_lower)?,
282
- )?,
283
- "split".to_string().into_py(builder.py()),
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
- span,
287
- )
288
- .map_err(py_err_to_lower)?,
289
- PyList::empty_bound(builder.py()).into_py(builder.py()),
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";
@@ -4,7 +4,7 @@ build-backend = "maturin"
4
4
 
5
5
  [project]
6
6
  name = "snail-lang"
7
- version = "0.7.4"
7
+ version = "0.7.5"
8
8
  description = "Snail programming language interpreter"
9
9
  readme = "README.md"
10
10
  requires-python = ">=3.8"
@@ -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("amPIvh")
12
- _VALUE_FLAGS = frozenset("fbe")
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