snail-lang 0.3.6__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 (66) hide show
  1. {snail_lang-0.3.6 → snail_lang-0.3.7}/Cargo.lock +7 -7
  2. {snail_lang-0.3.6 → snail_lang-0.3.7}/PKG-INFO +18 -10
  3. {snail_lang-0.3.6 → snail_lang-0.3.7}/README.md +17 -9
  4. {snail_lang-0.3.6 → snail_lang-0.3.7}/crates/snail-ast/Cargo.toml +1 -1
  5. {snail_lang-0.3.6 → snail_lang-0.3.7}/crates/snail-ast/src/ast.rs +3 -0
  6. {snail_lang-0.3.6 → snail_lang-0.3.7}/crates/snail-core/Cargo.toml +1 -1
  7. {snail_lang-0.3.6 → snail_lang-0.3.7}/crates/snail-error/Cargo.toml +1 -1
  8. {snail_lang-0.3.6 → snail_lang-0.3.7}/crates/snail-error/src/lib.rs +15 -7
  9. {snail_lang-0.3.6 → snail_lang-0.3.7}/crates/snail-lower/Cargo.toml +1 -1
  10. {snail_lang-0.3.6 → snail_lang-0.3.7}/crates/snail-lower/src/constants.rs +2 -3
  11. {snail_lang-0.3.6 → snail_lang-0.3.7}/crates/snail-lower/src/expr.rs +517 -49
  12. {snail_lang-0.3.6 → snail_lang-0.3.7}/crates/snail-parser/Cargo.toml +1 -1
  13. {snail_lang-0.3.6 → snail_lang-0.3.7}/crates/snail-parser/src/expr.rs +6 -0
  14. {snail_lang-0.3.6 → snail_lang-0.3.7}/crates/snail-parser/src/lib.rs +1 -0
  15. {snail_lang-0.3.6 → snail_lang-0.3.7}/crates/snail-parser/src/snail.pest +4 -0
  16. {snail_lang-0.3.6 → snail_lang-0.3.7}/crates/snail-parser/src/string.rs +1 -0
  17. {snail_lang-0.3.6 → snail_lang-0.3.7}/crates/snail-parser/src/util.rs +1 -0
  18. snail_lang-0.3.7/crates/snail-parser/tests/common.rs +84 -0
  19. snail_lang-0.3.7/crates/snail-parser/tests/errors.rs +195 -0
  20. snail_lang-0.3.7/crates/snail-parser/tests/parser.rs +634 -0
  21. snail_lang-0.3.7/crates/snail-parser/tests/statements.rs +409 -0
  22. snail_lang-0.3.7/crates/snail-parser/tests/syntax_expressions.rs +369 -0
  23. snail_lang-0.3.7/crates/snail-parser/tests/syntax_strings.rs +92 -0
  24. {snail_lang-0.3.6 → snail_lang-0.3.7}/crates/snail-python/Cargo.toml +1 -1
  25. {snail_lang-0.3.6 → snail_lang-0.3.7}/crates/snail-python/src/lib.rs +16 -10
  26. {snail_lang-0.3.6 → snail_lang-0.3.7}/pyproject.toml +1 -1
  27. {snail_lang-0.3.6 → snail_lang-0.3.7}/python/snail/cli.py +2 -0
  28. snail_lang-0.3.7/python/snail/runtime/__init__.py +59 -0
  29. snail_lang-0.3.7/python/snail/runtime/structured_accessor.py +75 -0
  30. {snail_lang-0.3.6 → snail_lang-0.3.7}/python/snail/runtime/subprocess.py +2 -2
  31. snail_lang-0.3.6/crates/snail-parser/tests/parser.rs +0 -837
  32. snail_lang-0.3.6/python/snail/runtime/__init__.py +0 -27
  33. snail_lang-0.3.6/python/snail/runtime/structured_accessor.py +0 -115
  34. {snail_lang-0.3.6 → snail_lang-0.3.7}/Cargo.toml +0 -0
  35. {snail_lang-0.3.6 → snail_lang-0.3.7}/LICENSE +0 -0
  36. {snail_lang-0.3.6 → snail_lang-0.3.7}/crates/snail-ast/README.md +0 -0
  37. {snail_lang-0.3.6 → snail_lang-0.3.7}/crates/snail-ast/src/awk.rs +0 -0
  38. {snail_lang-0.3.6 → snail_lang-0.3.7}/crates/snail-ast/src/lib.rs +0 -0
  39. {snail_lang-0.3.6 → snail_lang-0.3.7}/crates/snail-core/README.md +0 -0
  40. {snail_lang-0.3.6 → snail_lang-0.3.7}/crates/snail-core/src/lib.rs +0 -0
  41. {snail_lang-0.3.6 → snail_lang-0.3.7}/crates/snail-error/README.md +0 -0
  42. {snail_lang-0.3.6 → snail_lang-0.3.7}/crates/snail-lower/README.md +0 -0
  43. {snail_lang-0.3.6 → snail_lang-0.3.7}/crates/snail-lower/src/awk.rs +0 -0
  44. {snail_lang-0.3.6 → snail_lang-0.3.7}/crates/snail-lower/src/helpers.rs +0 -0
  45. {snail_lang-0.3.6 → snail_lang-0.3.7}/crates/snail-lower/src/lib.rs +0 -0
  46. {snail_lang-0.3.6 → snail_lang-0.3.7}/crates/snail-lower/src/operators.rs +0 -0
  47. {snail_lang-0.3.6 → snail_lang-0.3.7}/crates/snail-lower/src/program.rs +0 -0
  48. {snail_lang-0.3.6 → snail_lang-0.3.7}/crates/snail-lower/src/py_ast.rs +0 -0
  49. {snail_lang-0.3.6 → snail_lang-0.3.7}/crates/snail-lower/src/stmt.rs +0 -0
  50. {snail_lang-0.3.6 → snail_lang-0.3.7}/crates/snail-parser/README.md +0 -0
  51. {snail_lang-0.3.6 → snail_lang-0.3.7}/crates/snail-parser/src/awk.rs +0 -0
  52. {snail_lang-0.3.6 → snail_lang-0.3.7}/crates/snail-parser/src/literal.rs +0 -0
  53. {snail_lang-0.3.6 → snail_lang-0.3.7}/crates/snail-parser/src/stmt.rs +0 -0
  54. {snail_lang-0.3.6 → snail_lang-0.3.7}/python/snail/__init__.py +0 -0
  55. {snail_lang-0.3.6 → snail_lang-0.3.7}/python/snail/runtime/compact_try.py +0 -0
  56. {snail_lang-0.3.6 → snail_lang-0.3.7}/python/snail/runtime/regex.py +0 -0
  57. {snail_lang-0.3.6 → snail_lang-0.3.7}/python/snail/vendor/__init__.py +0 -0
  58. {snail_lang-0.3.6 → snail_lang-0.3.7}/python/snail/vendor/jmespath/LICENSE +0 -0
  59. {snail_lang-0.3.6 → snail_lang-0.3.7}/python/snail/vendor/jmespath/__init__.py +0 -0
  60. {snail_lang-0.3.6 → snail_lang-0.3.7}/python/snail/vendor/jmespath/ast.py +0 -0
  61. {snail_lang-0.3.6 → snail_lang-0.3.7}/python/snail/vendor/jmespath/compat.py +0 -0
  62. {snail_lang-0.3.6 → snail_lang-0.3.7}/python/snail/vendor/jmespath/exceptions.py +0 -0
  63. {snail_lang-0.3.6 → snail_lang-0.3.7}/python/snail/vendor/jmespath/functions.py +0 -0
  64. {snail_lang-0.3.6 → snail_lang-0.3.7}/python/snail/vendor/jmespath/lexer.py +0 -0
  65. {snail_lang-0.3.6 → snail_lang-0.3.7}/python/snail/vendor/jmespath/parser.py +0 -0
  66. {snail_lang-0.3.6 → snail_lang-0.3.7}/python/snail/vendor/jmespath/visitor.py +0 -0
@@ -485,11 +485,11 @@ checksum = "67b1b7a3b5fe4f1376887184045fcf45c69e92af734b7aaddc05fb777b6fbd03"
485
485
 
486
486
  [[package]]
487
487
  name = "snail-ast"
488
- version = "0.3.6"
488
+ version = "0.3.7"
489
489
 
490
490
  [[package]]
491
491
  name = "snail-core"
492
- version = "0.3.6"
492
+ version = "0.3.7"
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.3.6"
503
+ version = "0.3.7"
504
504
  dependencies = [
505
505
  "snail-ast",
506
506
  ]
507
507
 
508
508
  [[package]]
509
509
  name = "snail-lower"
510
- version = "0.3.6"
510
+ version = "0.3.7"
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.3.6"
519
+ version = "0.3.7"
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.3.6"
529
+ version = "0.3.7"
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.3.6"
543
+ version = "0.3.7"
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.6
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,29 +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
118
 
119
- # Join list items with a separator
120
- joined = ["a", "b"] | join(" ") # yields "a b"
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")
121
123
  ```
122
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
+
123
131
  ### JSON Queries with JMESPath
124
132
 
125
- 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:
126
134
 
127
135
  ```snail
128
136
  # Parse JSON and query with $[jmespath]
129
- data = json($(curl -s api.example.com/users))
137
+ data = js($(curl -s api.example.com/users))
130
138
  names = data | $[users[*].name]
131
139
  first_email = data | $[users[0].email]
132
140
 
133
141
  # Inline parsing and querying
134
- result = json('{"foo": 12}') | $[foo]
142
+ result = js('{"foo": 12}') | $[foo]
135
143
 
136
144
  # JSONL parsing returns a list
137
- names = json('{"name": "Ada"}\n{"name": "Lin"}') | $[[*].name]
145
+ names = js('{"name": "Ada"}\n{"name": "Lin"}') | $[[*].name]
138
146
  ```
139
147
 
140
148
  ### Full Python Interoperability
@@ -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,29 +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
107
 
108
- # Join list items with a separator
109
- joined = ["a", "b"] | join(" ") # yields "a b"
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")
110
112
  ```
111
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
+
112
120
  ### JSON Queries with JMESPath
113
121
 
114
- 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:
115
123
 
116
124
  ```snail
117
125
  # Parse JSON and query with $[jmespath]
118
- data = json($(curl -s api.example.com/users))
126
+ data = js($(curl -s api.example.com/users))
119
127
  names = data | $[users[*].name]
120
128
  first_email = data | $[users[0].email]
121
129
 
122
130
  # Inline parsing and querying
123
- result = json('{"foo": 12}') | $[foo]
131
+ result = js('{"foo": 12}') | $[foo]
124
132
 
125
133
  # JSONL parsing returns a list
126
- names = json('{"name": "Ada"}\n{"name": "Lin"}') | $[[*].name]
134
+ names = js('{"name": "Ada"}\n{"name": "Lin"}') | $[[*].name]
127
135
  ```
128
136
 
129
137
  ### Full Python Interoperability
@@ -1,6 +1,6 @@
1
1
  [package]
2
2
  name = "snail-ast"
3
- version = "0.3.6"
3
+ version = "0.3.7"
4
4
  edition = "2024"
5
5
  readme = "README.md"
6
6
 
@@ -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,6 +1,6 @@
1
1
  [package]
2
2
  name = "snail-core"
3
- version = "0.3.6"
3
+ version = "0.3.7"
4
4
  edition.workspace = true
5
5
  readme = "README.md"
6
6
 
@@ -1,6 +1,6 @@
1
1
  [package]
2
2
  name = "snail-error"
3
- version = "0.3.6"
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,6 +1,6 @@
1
1
  [package]
2
2
  name = "snail-lower"
3
- version = "0.3.6"
3
+ version = "0.3.7"
4
4
  edition = "2024"
5
5
  readme = "README.md"
6
6
 
@@ -5,9 +5,8 @@ pub const SNAIL_SUBPROCESS_CAPTURE_CLASS: &str = "__SnailSubprocessCapture";
5
5
  pub const SNAIL_SUBPROCESS_STATUS_CLASS: &str = "__SnailSubprocessStatus";
6
6
  pub const SNAIL_REGEX_SEARCH: &str = "__snail_regex_search";
7
7
  pub const SNAIL_REGEX_COMPILE: &str = "__snail_regex_compile";
8
- pub const SNAIL_STRUCTURED_ACCESSOR_CLASS: &str = "__SnailStructuredAccessor";
9
- pub const SNAIL_JSON_OBJECT_CLASS: &str = "__SnailJsonObject";
10
- pub const SNAIL_JSON_PIPELINE_WRAPPER_CLASS: &str = "__SnailJsonPipelineWrapper";
8
+ pub const SNAIL_JMESPATH_QUERY: &str = "__snail_jmespath_query";
9
+ pub const SNAIL_PARTIAL_HELPER: &str = "__snail_partial";
11
10
 
12
11
  // Awk-related constants (public within crate)
13
12
  pub(crate) const SNAIL_AWK_LINE: &str = "$l";