jmespath-ruby 0.1.2 → 0.1.3

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 (45) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +19 -1
  3. data/README.md +177 -4
  4. data/completions/_jmespath +50 -0
  5. data/completions/jmespath.bash +178 -0
  6. data/docs/guides/compilation.md +310 -0
  7. data/docs/guides/completion.md +69 -0
  8. data/docs/guides/errors.md +200 -3
  9. data/docs/guides/functions.md +33 -0
  10. data/docs/guides/search.md +23 -0
  11. data/lib/jmespath/ast.rb +6 -1
  12. data/lib/jmespath/cli/argument_parser.rb +31 -0
  13. data/lib/jmespath/cli/arguments.rb +80 -0
  14. data/lib/jmespath/cli/failure_diagnostic.rb +1 -0
  15. data/lib/jmespath/cli/function_listing.rb +48 -0
  16. data/lib/jmespath/cli/help.rb +43 -0
  17. data/lib/jmespath/cli/interactive_session.rb +106 -0
  18. data/lib/jmespath/cli.rb +112 -16
  19. data/lib/jmespath/data_access.rb +12 -9
  20. data/lib/jmespath/diagnostic.rb +4 -3
  21. data/lib/jmespath/errors.rb +14 -0
  22. data/lib/jmespath/explanation.rb +95 -0
  23. data/lib/jmespath/expression.rb +93 -7
  24. data/lib/jmespath/failure_capture.rb +65 -0
  25. data/lib/jmespath/functions/coercions.rb +12 -5
  26. data/lib/jmespath/functions/conversion.rb +19 -1
  27. data/lib/jmespath/functions/function_call.rb +1 -1
  28. data/lib/jmespath/functions/json_value.rb +50 -0
  29. data/lib/jmespath/functions/references.rb +11 -10
  30. data/lib/jmespath/functions/signature.rb +6 -4
  31. data/lib/jmespath/functions.rb +12 -6
  32. data/lib/jmespath/input.rb +37 -0
  33. data/lib/jmespath/normalizer.rb +22 -5
  34. data/lib/jmespath/parser/projections.rb +5 -5
  35. data/lib/jmespath/parser.rb +2 -2
  36. data/lib/jmespath/plan.rb +17 -2
  37. data/lib/jmespath/search_result.rb +23 -0
  38. data/lib/jmespath/syntax_diagnostic.rb +32 -0
  39. data/lib/jmespath/tracing/collector.rb +109 -0
  40. data/lib/jmespath/tracing/evaluator.rb +113 -0
  41. data/lib/jmespath/tracing/field_lookup.rb +49 -0
  42. data/lib/jmespath/version.rb +1 -1
  43. data/lib/jmespath.rb +43 -8
  44. data/sig/jmespath.rbs +47 -3
  45. metadata +17 -1
@@ -13,6 +13,316 @@ The expression owns a frozen copy of the query text.
13
13
  Changing the caller's original string does not change the compiled expression.
14
14
  Mutable literal results are copied for each evaluation.
15
15
 
16
+ ## Failure results
17
+
18
+ `Expression#try_search` reuses compiled syntax and saved evaluation options, with per-call overrides handled independently.
19
+ Calls return separate frozen results and isolate capture state across nested calls, threads, and fibers.
20
+ The returned value or error retains its identity and independent mutability.
21
+ See [search results](search.md#search-results) for the result fields.
22
+
23
+ A custom function can handle an inner query failure explicitly:
24
+
25
+ ```ruby
26
+ inner = JMESPath.compile("length(@)")
27
+ functions = JMESPath::Functions.default.with("checked", signature: [[:any]]) do |value|
28
+ result = inner.try_search(value)
29
+ result.success? ? result.value : "invalid"
30
+ end
31
+ outer = JMESPath.compile("checked(@)", functions: functions)
32
+ outer.try_search(4).value #=> "invalid"
33
+ outer.try_search([1, 2]).value #=> 2
34
+ ```
35
+
36
+ An exception escaping the custom function propagates from the outer call, even when it came from an inner query.
37
+
38
+ ## Required results
39
+
40
+ Call `Expression#fetch` when a Ruby caller requires a non-null final result.
41
+ It accepts the same inputs, positional options, and keyword options as `search`, including saved defaults and per-call overrides.
42
+ Each call evaluates once and returns any non-nil result unchanged, preserving selected object identity and mutability.
43
+ False, zero, empty strings, empty arrays, and empty hashes succeed.
44
+
45
+ Use these calls in Ruby code:
46
+
47
+ ```ruby
48
+ query = JMESPath.compile("user.name")
49
+ query.fetch({user: {name: "Ada"}}) #=> "Ada"
50
+ query.fetch({user: {name: false}}) #=> false
51
+ query.fetch({}) # raises JMESPath::Errors::NullResultError
52
+ query.fetch({user: {name: nil}}) # raises JMESPath::Errors::NullResultError
53
+ ```
54
+
55
+ Missing fields, explicit null, and out-of-range indexes produce the same error.
56
+ Only the final result must be non-null; intermediate null values can be replaced by the query.
57
+ With `disable_visit_errors: true`, suppressed type or arity failures producing nil also raise `NullResultError`.
58
+ Other failures propagate unchanged.
59
+ The error exposes frozen query text through `expression` and uses the message `Expression returned null`.
60
+ It contains no input document or explanation of the null result.
61
+ Nullable `search` and `call` behavior is unchanged.
62
+
63
+ ## Query inspection
64
+
65
+ Call `Expression#explain` to inspect query structure without providing input data.
66
+ Inspection neither resolves function names nor executes callbacks.
67
+ Unknown and custom function names remain valid structure; inspection cannot establish availability, argument types, or callback behavior.
68
+
69
+ ```ruby
70
+ query = JMESPath.compile("user.name")
71
+ query.explain[:root][:operation] #=> "subexpression"
72
+ query.explain[:root][:children].map { |child| child[:node][:details][:name] } #=> ["user", "name"]
73
+ query.explain[:root][:source_range] #=> [0, 9]
74
+ ```
75
+
76
+ The returned hash has two keys: `:source`, containing the complete UTF-8 query text, and `:root`, containing a logical operation.
77
+ Every operation has these keys:
78
+
79
+ | Key | Value |
80
+ | --- | --- |
81
+ | `:id` | Integer identifying the operation within this explanation |
82
+ | `:operation` | String from the operation table below |
83
+ | `:source_range` | Two byte offsets, `[start_byte, end_byte]`, or nil |
84
+ | `:details` | Hash containing operation-specific values |
85
+ | `:children` | Ordered array of `{role: String, node: operation_hash}` entries |
86
+
87
+ All hashes, arrays, and strings are frozen, including literal object keys and nested literal values.
88
+ Repeated inspection produces equal data; object identity is unspecified.
89
+ Saved evaluation options and cache settings do not affect the explanation.
90
+ Compiled expressions support concurrent inspection with separate traversal state.
91
+ Inspection exposes neither private syntax nodes nor optimizer operations.
92
+ Ordinary compilation and evaluation do not construct explanations.
93
+
94
+ ### Operations and child roles
95
+
96
+ Keys use symbols; operation names and child roles use strings.
97
+ Decoded names and literal values retain their Ruby representations.
98
+ Literal contents remain values, without additional operation nodes.
99
+ Empty details are represented by `{}`.
100
+
101
+ | Operation | Details | Child roles in traversal order |
102
+ | --- | --- | --- |
103
+ | `field` | `name: String` | None |
104
+ | `current` | Empty | None |
105
+ | `literal` | `value: decoded Ruby value` | None |
106
+ | `group` | Empty | `expression` |
107
+ | `subexpression` | Empty | Repeated `step` |
108
+ | `pipe` | Empty | Repeated `step` |
109
+ | `index` | `index: Integer` | `input` |
110
+ | `slice` | `start`, `stop`, `step`, each Integer or nil | `input` |
111
+ | `flatten` | Empty | `input` |
112
+ | `projection` | Empty | `input`, `expression` |
113
+ | `value_projection` | Empty | `input`, `expression` |
114
+ | `filter_projection` | Empty | `input`, `condition`, `expression` |
115
+ | `expression_reference` | Empty | `expression` |
116
+ | `function` | `name: String` | Repeated `argument` |
117
+ | `and`, `or` | Empty | Repeated `operand` |
118
+ | `not` | Empty | `operand` |
119
+ | `comparison` | `operator: String`, one of `==`, `!=`, `<`, `<=`, `>`, `>=` | `left`, `right` |
120
+ | `multi_select_list` | Empty | Repeated `element` |
121
+ | `multi_select_hash` | Empty | Repeated `entry` |
122
+ | `entry` | `name: String` | `expression` |
123
+
124
+ Repeated roles preserve source order, including duplicate hash entries.
125
+ Omitted slice components remain nil, including an omitted step.
126
+ Flattening and slicing can appear as inputs to projections.
127
+ Explicit parentheses produce `group` nodes; implicit identities produce `current` nodes.
128
+ Adjacent unparenthesized subexpressions, pipes, and identical boolean operators form ordered nodes with multiple children.
129
+
130
+ ### Operation IDs and evaluation rules
131
+
132
+ IDs start at zero and increase consecutively in preorder: each operation, then its children in the table's order.
133
+ Filter conditions precede projected expressions in this traversal.
134
+ IDs include structural groups and hash entries.
135
+ Identical query text produces identical IDs under this contract, independently of evaluation options or cache use.
136
+ IDs are local to an explanation and do not identify operations across query edits.
137
+
138
+ Traversal order describes structure, with these logical evaluation rules:
139
+
140
+ | Operation family | Evaluation rule |
141
+ | --- | --- |
142
+ | Subexpressions and pipes | Evaluate steps left to right, passing each result to the next step. |
143
+ | Function calls | Evaluate arguments left to right against the same input, then invoke the function. |
144
+ | Expression references | Defer the referenced expression until a function requests evaluation against its chosen value. |
145
+ | Projections | Evaluate input, then project each element; omit nil results and retain false. |
146
+ | Value projections | Project object values without promising sorted object keys. |
147
+ | Filters | Evaluate input, evaluate conditions for all elements, then project selected elements; omit nil projected results. |
148
+ | Boolean operators | Evaluate operands left to right against the same input, stopping according to JMESPath truth rules. |
149
+ | Comparisons | Evaluate both operands against the same input before comparing, left before right. |
150
+ | Multiselects | Evaluate entries left to right against the same input; nil input returns nil without evaluating entries. |
151
+ | Groups and entries | Evaluate their contained expression. |
152
+ | Indexes, slices, flattening | Evaluate input before applying the operation; flattening removes one array level. |
153
+ | Fields, current, literals, negation | Apply standard JMESPath semantics; negation evaluates its operand first. |
154
+
155
+ The explanation contains no input values, evaluation events, or optimizer details.
156
+
157
+ ### Source coordinates and serialization
158
+
159
+ Ranges include the start byte and exclude the end byte in the returned UTF-8 `:source`.
160
+ They use the same coordinate units as lexer offsets and function-error byte ranges.
161
+ Use `source.byteslice(start_byte...end_byte)` to extract the text.
162
+ Inspection retains all source whitespace, including trailing newlines.
163
+ Convertible non-UTF-8 query text is encoded as UTF-8 in the explanation; `Expression#source` retains its original encoding.
164
+
165
+ Explicit operation ranges cover their entire syntactic expression, including delimiters and contained expressions.
166
+ They exclude surrounding whitespace and retain internal whitespace.
167
+ Quoted fields include quotes, groups include parentheses, and references include `&`.
168
+ Projections include their projected expression; filters also include the input, brackets, and condition.
169
+ Index, slice, and flatten ranges include their input when present.
170
+ Hash-entry ranges include the key and value expression, excluding separators.
171
+ Parent and child ranges can overlap.
172
+
173
+ Implicit `current` operations have nil ranges; an explicit `@` has its token range.
174
+ Every non-nil range stays within the source and ends on UTF-8 character boundaries.
175
+ Line and column fields are omitted; derive them using the [syntax diagnostic conventions](errors.md).
176
+
177
+ For finite numeric values, `JSON.generate(explanation)` serializes directly, with string keys and JSON values.
178
+ Numeric overflow literals preserve their decoded non-finite Ruby floats, including values inside literal arrays and objects.
179
+ Strict JSON cannot represent these values; generation raises `JSON::GeneratorError`, even with `max_nesting: false`.
180
+ Inspection preserves those values and compilation behavior without substituting strings or nulls.
181
+ Deep explanation trees can exceed JSON's default nesting limit before reaching the query's supported depth limit.
182
+ Use `JSON.generate(explanation, max_nesting: false)` for these trees.
183
+ Use the corresponding option when parsing the serialized explanation.
184
+ For example, this Ruby code reads the root operation after serialization:
185
+
186
+ ```ruby
187
+ explanation = JMESPath.compile("user.name").explain
188
+ JSON.parse(JSON.generate(explanation))["root"]["operation"] #=> "subexpression"
189
+ ```
190
+
191
+ This Ruby example serializes an explanation with nested groups:
192
+
193
+ ```ruby
194
+ explanation = JMESPath.compile("(" * 128 + "@" + ")" * 128).explain
195
+ serialized = JSON.generate(explanation, max_nesting: false)
196
+ JSON.parse(serialized, max_nesting: false)["root"]["operation"] #=> "group"
197
+ ```
198
+
199
+ This Ruby example inspects an overflow literal without serializing it:
200
+
201
+ ```ruby
202
+ query = JMESPath.compile("`1e400`")
203
+ query.explain[:root][:details][:value].infinite? #=> 1
204
+ query.search(nil).infinite? #=> 1
205
+ ```
206
+
207
+ ## Evaluation tracing
208
+
209
+ Call `Expression#trace` to evaluate once and explain null fields, filter exclusions, and omitted projection results.
210
+ It accepts the same input forms and evaluation options as `search`, including saved defaults and per-call overrides.
211
+ The keyword `limit:` sets a positive Integer event limit, defaulting to 1,000.
212
+ Invalid limits raise `ArgumentError` before consuming input; positional `:limit` is not an evaluation option.
213
+
214
+ This Ruby example traces a projection while retaining false values:
215
+
216
+ ```ruby
217
+ query = JMESPath.compile("items[*].name")
218
+ report = query.trace({items: [{name: "Ada"}, {}, {name: false}]})
219
+ report[:value] #=> ["Ada", false]
220
+ report[:events].map { |event| event[:type] } #=> ["field_null", "projection_omission"]
221
+ report[:events].first[:context] #=> [{operation_id: 0, index: 1}]
222
+ report[:truncated] #=> false
223
+ ```
224
+
225
+ ### Report and event format
226
+
227
+ The frozen report has exactly three keys:
228
+
229
+ | Key | Value |
230
+ | --- | --- |
231
+ | `:value` | The ordinary query result, including nil or false |
232
+ | `:events` | A deeply frozen array of diagnostic events |
233
+ | `:truncated` | True when a diagnostic event was discarded because the limit was reached |
234
+
235
+ The result retains ordinary mutability and selected input object identity.
236
+ Freezing the report does not freeze its `:value`.
237
+ Diagnostics contain no input objects, intermediate query values, or document snapshots.
238
+
239
+ Every event has exactly four keys:
240
+
241
+ | Key | Value |
242
+ | --- | --- |
243
+ | `:type` | A string from the event table below |
244
+ | `:operation_id` | The responsible operation's ID in `query.explain` |
245
+ | `:context` | An ordered array of enclosing iteration frames |
246
+ | `:details` | A hash of event-specific details |
247
+
248
+ Event and detail keys are symbols; type and reason values are strings.
249
+
250
+ | Type | Responsible operation | Details |
251
+ | --- | --- | --- |
252
+ | `field_null` | The field access returning nil | `name: String` and `reason: String` |
253
+ | `filter_exclusion` | The filter rejecting an item | Empty hash |
254
+ | `projection_omission` | The projection omitting a nil result | Empty hash |
255
+
256
+ Field reasons are `missing`, `null`, `non_object`, and `unknown`.
257
+ `missing` means lookup established absence under string-key and symbol-key fallback rules.
258
+ `null` means lookup established an explicitly present nil under those rules.
259
+ `non_object` means ordinary conversion produced no object.
260
+ `unknown` means classification would require additional observable operations.
261
+ Custom lookup methods and identity-based hashes receive conservative `unknown` classifications.
262
+ Tracing adds no conversions, application callbacks, or application-observable field lookups to refine a reason.
263
+
264
+ This Ruby example finds the source of an omission through its operation ID:
265
+
266
+ ```ruby
267
+ query = JMESPath.compile("items[*].name")
268
+ report = query.trace({items: [{}]})
269
+ event = report[:events].last
270
+ query.explain[:root][:id] == event[:operation_id] #=> true
271
+ query.explain[:root][:source_range] #=> [0, 13]
272
+ ```
273
+
274
+ Successful fields, retained values, out-of-range indexes, and unvisited branches produce no diagnostics.
275
+ Tracing explains the three event types above; it does not explain every possible nil result.
276
+ Field events precede any exclusion or omission they cause.
277
+ Filters evaluate all conditions before projecting selected items.
278
+
279
+ ### Local item context
280
+
281
+ Each context frame has exactly `:operation_id` and `:index`.
282
+ Frames run from the outermost iteration to the innermost.
283
+ Indexes are zero-based positions in the immediate array or object-values sequence.
284
+ Filter projections retain original input indexes, including gaps left by rejected items.
285
+ An exclusion or omission includes the frame of its affected item.
286
+ Events outside iteration have an empty context.
287
+
288
+ Object projections use ordinal values positions without extra key enumeration.
289
+ Pipes, slicing, flattening, and functions can establish local sequences.
290
+ Context does not provide object keys or paths back through transformations to the original document.
291
+
292
+ Expression-reference frames identify the reference operation and its zero-based invocation count within that trace call.
293
+ The count describes callback invocation order, not positions in a function's input.
294
+ Synchronous reference calls retain enclosing iteration frames.
295
+ Concurrent reference calls keep separate thread contexts; application scheduling determines event order.
296
+ References saved by callbacks remain callable after tracing finishes, with collection closed.
297
+
298
+ ### Limits, errors, and isolation
299
+
300
+ Tracing retains the first `limit` diagnostic events in observed evaluation order.
301
+ Exactly `limit` events leave `:truncated` false; the next diagnostic sets it to true.
302
+ Truncation adds no sentinel event.
303
+ Evaluation continues normally after the limit, preserving callback counts, order, and the complete result.
304
+
305
+ This Ruby example limits diagnostics without changing the result:
306
+
307
+ ```ruby
308
+ query = JMESPath.compile("items[*].name")
309
+ limited = query.trace({items: [{}, {name: false}]}, limit: 1)
310
+ limited[:value] #=> [false]
311
+ limited[:events].length #=> 1
312
+ limited[:truncated] #=> true
313
+ ```
314
+
315
+ The limit bounds retained diagnostics, not evaluation memory.
316
+ The operation map uses memory proportional to query size; retained events contain copies of their enclosing frames.
317
+ Filters retain selected original indexes until projection finishes.
318
+ Collection avoids further event snapshots after truncation.
319
+
320
+ Trace state is separate for each call, including concurrent calls and nested calls from callbacks.
321
+ Ordinary search builds no trace collector or operation map.
322
+ Tracing propagates original evaluation and input exceptions without attaching a report or retrying evaluation.
323
+ Suppression retains its ordinary behavior; a suppressed nil result can produce a downstream omission.
324
+ There is no dedicated validation-failure event, automatic logging, module-level trace method, or CLI trace flag.
325
+
16
326
  ## Saved options
17
327
 
18
328
  Pass `functions:` and `disable_visit_errors:` to `compile` to save defaults for repeated searches.
@@ -0,0 +1,69 @@
1
+ # Shell completion
2
+
3
+ The gem includes Bash and Zsh completion definitions for `jmespath` and `jmespath.rb`.
4
+ They suggest option names and filesystem paths after `--file` and `--query-file`.
5
+ Both `--file PATH` and `--file=PATH` forms support completion, including spaces in filenames.
6
+
7
+ ## Bash setup
8
+
9
+ Run these commands in the Bash session and Ruby environment where you use the gem:
10
+
11
+ ```bash
12
+ jmespath_gem_root="$(ruby -rrubygems -e 'print Gem::Specification.find_by_name("jmespath-ruby").full_gem_path')"
13
+ source "$jmespath_gem_root/completions/jmespath.bash"
14
+ ```
15
+
16
+ The definition uses Bash built-ins and supports Bash 3.2 or later.
17
+ It requires no completion framework.
18
+ Unquoted `~/`, `$HOME/`, and `${HOME}/` prefixes use your home directory.
19
+ Quoted or escaped tildes remain literal, with `./` added when necessary to preserve the selected path.
20
+ Bash completion leaves named-user prefixes such as `~other/` unchanged.
21
+ Completion inside a word follows Readline's insertion rules, which can repeat existing trailing text.
22
+ Move the cursor to the word's end to avoid that behavior.
23
+
24
+ ## Zsh setup
25
+
26
+ Add the gem's completion directory to `fpath` before initializing completion:
27
+
28
+ ```zsh
29
+ jmespath_gem_root="$(ruby -rrubygems -e 'print Gem::Specification.find_by_name("jmespath-ruby").full_gem_path')"
30
+ fpath=("$jmespath_gem_root/completions" $fpath)
31
+ autoload -Uz compinit
32
+ compinit
33
+ ```
34
+
35
+ If completion is initialized in the session, run these commands again to register the added definition.
36
+ The definition uses Zsh's standard completion system.
37
+
38
+ ## Persistent setup
39
+
40
+ To enable completion in later sessions, add the relevant setup commands to your shell startup file yourself.
41
+ For Zsh, place the `fpath` assignment before your startup file's `compinit` call.
42
+ Installation does not edit shell configuration.
43
+
44
+ When the gem is available only through Bundler, replace `ruby` in the lookup command with `bundle exec ruby`.
45
+ Use the Ruby environment and gem version that supply your executable.
46
+ After changing Ruby environments or gem versions, load the matching completion definition again.
47
+
48
+ ## Usage and boundaries
49
+
50
+ Press Tab while entering an option or path.
51
+ These examples show the key as `<Tab>`:
52
+
53
+ ```text
54
+ jmespath --que<Tab>
55
+ jmespath --query-file queries/rep<Tab>
56
+ jmespath.rb --file=my<Tab>
57
+ ```
58
+
59
+ Directories remain available for further path completion, without filtering by file extension.
60
+ Paths containing spaces receive shell quoting, so the completed path remains one argument.
61
+ Hyphen-leading filenames follow the CLI's literal-path rules, such as `--file=--document.json` or `--file ./--document.json`.
62
+
63
+ Options remain available before or after an expression until a standalone `--` delimiter.
64
+ Repeated or conflicting options remain visible; the CLI validates combinations when you run the command.
65
+ Completion does not suggest JMESPath expressions or complete arguments after the delimiter.
66
+ It lists filesystem names without running the CLI, evaluating queries, or reading document or query contents.
67
+ Ruby runs only during the installation-path lookup shown above.
68
+
69
+ Fish support and automatic installation are outside this feature.
@@ -10,6 +10,35 @@ SyntaxError derives from that base; evaluation errors derive from RuntimeError.
10
10
  | `InvalidArityError` | Incorrect number of function arguments |
11
11
  | `InvalidValueError` | Invalid slice step or cyclic value during structural processing |
12
12
  | `UnknownFunctionError` | Function absent from the supplied registry |
13
+ | `NullResultError` | `Expression#fetch` received a null final result |
14
+
15
+ ## Failure results
16
+
17
+ `Expression#try_search` returns library-owned evaluation, JSON parsing, and native input I/O failures in a `JMESPath::SearchResult`.
18
+ Its `error` reader contains the original exception; `value` is nil and `success?` is false.
19
+ Successful null results have `success?` true and `error` nil.
20
+ Compilation happens before this method and can still raise syntax errors.
21
+ A valid per-call nesting limit that rejects the compiled expression produces a failure result.
22
+
23
+ ```ruby
24
+ query = JMESPath.compile("length(@)")
25
+ query.try_search(4).error.function_name #=> "length"
26
+ query.try_search(4, disable_visit_errors: true).success? #=> true
27
+ query.try_search(4, disable_visit_errors: true).value #=> nil
28
+ JMESPath.compile("@").try_search(StringIO.new("invalid")).error.is_a?(JSON::ParserError) #=> true
29
+ ```
30
+
31
+ Invalid options raise `ArgumentError` before reading input.
32
+ Suppressed type and arity failures remain successful null results; unknown-function failures remain failures.
33
+ Caller-owned streams remain open, and library-opened files close after input processing.
34
+ Failures from native reads are captured; exceptions from application overrides of input methods propagate.
35
+
36
+ Exceptions escaping custom functions, registry overrides, and application conversions propagate unchanged, including JSON and library error classes.
37
+ This includes failures from ordinary queries called inside application code.
38
+ Use an inner `try_search` to handle those failures explicitly, as shown in [compiled result handling](compilation.md#failure-results).
39
+ Ordinary `search`, `call`, `fetch`, and CLI error handling retain their contracts.
40
+
41
+ ## Syntax diagnostics
13
42
 
14
43
  SyntaxError exposes the expression, a zero-based byte position, and one-based line and character column.
15
44
  Character columns count Unicode code points, independently of byte offsets and terminal display widths.
@@ -68,6 +97,47 @@ Only library argument-type and arity failures are suppressed by `disable_visit_e
68
97
  Syntax, unknown-function, invalid-value, adapter, and callback failures remain visible.
69
98
  Missing fields and out-of-range indices normally return nil.
70
99
 
100
+ `Expression#fetch` raises `NullResultError` for a nil final result, including explicit null and suppressed validation failures.
101
+ The error derives from `RuntimeError`, exposes frozen query text through `expression`, and has the message `Expression returned null`.
102
+ It retains no input document and does not distinguish missing fields from explicit null.
103
+ Non-nil values, including false and empty collections, succeed.
104
+ Other evaluation and application exceptions propagate unchanged.
105
+ See [required results](compilation.md#required-results) for executable Ruby examples.
106
+
107
+ ## Syntax validation
108
+
109
+ Use `JMESPath.validate` in Ruby to check syntax without an input document:
110
+
111
+ ```ruby
112
+ JMESPath.validate("items[*].name") #=> []
113
+ diagnostics = JMESPath.validate("items[?active")
114
+ failure = diagnostics.fetch(0)
115
+ failure.reason #=> "Expected rbracket, received eof"
116
+ failure.expression #=> "items[?active"
117
+ failure.position #=> 13
118
+ failure.line #=> 1
119
+ failure.column #=> 14
120
+ failure.location_known? #=> true
121
+ JMESPath::Diagnostic.format(failure) #=> "Expected rbracket, received eof at line 1, column 14\nitems[?active\n ^"
122
+ ```
123
+
124
+ The result is a frozen array containing zero or one immutable `JMESPath::SyntaxDiagnostic` record.
125
+ Parsing stops at the first syntax failure.
126
+ Records contain frozen copies of the reason and expression, without an exception or backtrace.
127
+ Source text and coordinates match compilation errors, including the lexer's conversion to UTF-8.
128
+ Positions are zero-based byte offsets; lines and Unicode character columns are one-based.
129
+ For unknown locations, `position`, `line`, and `column` are nil, and `location_known?` is false.
130
+ Invalid encodings produce an unlocated diagnostic with an empty stored expression.
131
+ The formatter accepts both records and syntax errors and applies the same excerpt and caret rules.
132
+
133
+ Validation neither evaluates data nor invokes callbacks, checks function availability, builds execution plans, or uses the compilation cache.
134
+ Unknown functions, incorrect argument counts, and unsupported runtime argument types remain valid syntax.
135
+ Validation cannot establish whether a query will succeed for a function registry or input document.
136
+ Parser failures other than `SyntaxError` propagate, including `InvalidValueError` for a zero slice step, matching compilation.
137
+
138
+ The only option is `max_depth:`, an Integer from 1 through 128, defaulting to 128.
139
+ Invalid expression types, depth values, and unsupported keywords raise `ArgumentError` before parsing.
140
+
71
141
  ## Function validation data
72
142
 
73
143
  Library-owned `InvalidTypeError` and `InvalidArityError` failures expose immutable metadata.
@@ -172,11 +242,138 @@ Invalid options raise ArgumentError.
172
242
 
173
243
  ## CLI diagnostics
174
244
 
175
- Both `jmespath` and `jmespath.rb` print expected input and query failures to stderr and exit with status 1.
245
+ Both `jmespath` and `jmespath.rb` print expected input and query failures to stderr.
246
+ Noninteractive queries exit with status 1 on these failures; interactive sessions recover from query failures as described below.
176
247
  Failures before output starts leave stdout empty.
177
- Successful queries print one JSON result to stdout.
248
+ Successful document queries print one JSON result to stdout.
249
+ With `--raw-output`, string results print without JSON quotes or escape encoding; other types retain compact JSON encoding.
250
+ Raw output appends one newline while preserving every embedded and trailing newline in the string.
251
+ An empty string produces one newline; a string ending with one newline produces two.
252
+ Raw output preserves diagnostics, source coordinates, failure statuses, and empty stdout on failures before output starts.
253
+ Repeated `--raw-output` options and combinations with `--pretty` produce usage failures before accessing input.
254
+ Output is compact by default; `--pretty` selects two-space indentation with one final newline.
255
+ Both formats emit valid JSON, including scalar results and Unicode text.
256
+ Pretty formatting preserves all diagnostics, source coordinates, exit statuses, and empty stdout on failures before output starts.
257
+ Repeated `--pretty` options produce usage failures.
178
258
  Usage failures print usage to stderr and exit with status 2 without reading stdin.
179
259
  Help and version requests exit with status 0 without reading stdin.
260
+ With `--file PATH`, usage failures and informational requests do not open the document.
261
+ Missing or unreadable document files produce escaped operating-system messages on stderr with status 1.
262
+ File failures leave stdin unused; single-document files close after parsing, including when parsing fails.
263
+ Query diagnostics retain their source locations when JSON comes from a file.
264
+
265
+ ### JSON Lines failures
266
+
267
+ Use `--json-lines` to read independent records from stdin or `--file PATH`:
268
+
269
+ ```sh
270
+ jmespath --json-lines 'length(@)' < records.jsonl
271
+ jmespath --json-lines --file records.jsonl --query-file query.jmespath
272
+ ```
273
+
274
+ The query compiles once before document input is accessed, including for empty input.
275
+ A valid query with empty input returns status 0 without output; an invalid query returns status 1.
276
+ Each physical line must contain one complete JSON value.
277
+ Blank and whitespace-only lines fail with `Invalid JSON input`.
278
+ LF, CRLF, and a final unterminated record are accepted.
279
+ Results use compact JSON with one final newline per record, including null and false values.
280
+
281
+ If the input contains `[]` on line 1 and `4` on line 2, the first command prints `0` before failing.
282
+ Its stderr identifies both the input record and the query location:
283
+
284
+ ```text
285
+ Input line 2:
286
+ Invalid argument 1 for length: expected string, array, or object; received number at line 1, column 1
287
+ length(@)
288
+ ^
289
+ ```
290
+
291
+ Processing stops at the failing record with status 1, preserving earlier results and omitting the failed result.
292
+ Check the exit status before treating stdout as complete.
293
+ Parsing failures retain the generic message without document fragments.
294
+ Results that cannot be encoded as JSON report `Invalid JSON result` without result fragments.
295
+ Query excerpts and function context remain available after the input-line prefix.
296
+ Compilation and file-open failures have no record number.
297
+
298
+ The CLI reads the next line after writing the preceding result, using normal output buffering.
299
+ Memory is proportional to the current record, its evaluation, and its serialized result, plus the compiled query.
300
+ Owned files close after processing succeeds or fails; caller-owned streams remain open.
301
+ Injected input needs `gets` for JSON Lines and `read` for document mode; injected output needs only `puts`.
302
+ Repeated flags or combinations with `--pretty`, `--raw-output`, or validation fail with status 2 before input access.
303
+ The exact two-argument invocation `--validate --json-lines` validates literal query text.
304
+
305
+ ### Interactive sessions
306
+
307
+ Use `jmespath --interactive --file document.json` to load a document once and enter successive UTF-8 queries through stdin.
308
+ The file closes before the session reads commands.
309
+ Prompts appear only for terminal stdin, including when stderr is redirected.
310
+ Help, prompts, and diagnostics use stderr; successful results use compact JSON on stdout with one final newline.
311
+ Results and diagnostics are flushed when supported; terminal prompts always flush before input reads.
312
+
313
+ Syntax failures retain query excerpts and carets; type, arity, and unknown-function failures retain their diagnostic context.
314
+ Each failure omits its result and permits the next query.
315
+ An invalid UTF-8 line or unknown colon command also produces a recoverable diagnostic.
316
+ Empty lines and lines containing only spaces or tabs are ignored; LF, CRLF, and a final unterminated query are accepted.
317
+ `:help` prints instructions, while `:quit` and EOF return status 0, including after recovered failures.
318
+ Earlier output remains available if a later stream failure terminates the session with status 1.
319
+
320
+ Missing, unreadable, or malformed documents terminate startup with status 1 before commands or prompts.
321
+ Malformed JSON reports `Invalid JSON input` without exposing document contents.
322
+ Input and output stream failures terminate with status 1; failed diagnostic writes are not retried.
323
+ Missing `--file`, repeated flags, queries in arguments, and combinations with `--query-file` or other modes return usage status 2.
324
+ Information flags must remain standalone.
325
+ The exact two-argument form `--validate --interactive` still validates the literal query `--interactive`.
326
+
327
+ Injected readers require `gets`; readers without `tty?` receive no prompts.
328
+ Output and diagnostic streams require `puts` and may support `flush`.
329
+ Terminal prompts additionally require `print` and `flush` on the diagnostic stream.
330
+ Caller-owned streams remain open after success or failure.
331
+
332
+ ### Saved queries
333
+
334
+ Select a saved UTF-8 query with `--query-file PATH` and supply the document through stdin or `--file PATH`.
335
+ Query files retain their source text, including Unicode, line breaks, and trailing newlines.
336
+ Syntax and function diagnostics use that text for their excerpts, coordinates, and carets.
337
+ An empty file produces an empty-expression syntax diagnostic with status 1.
338
+ Missing or unreadable query files produce escaped operating-system messages with status 1, leaving document input untouched.
339
+ Query files close after reading, including read failures, before document input is accessed.
340
+ In single-document mode, malformed JSON takes precedence over query syntax errors after the query file is read.
341
+ Absent or conflicting query sources, repeated options, and missing values produce usage failures before opening files.
342
+
343
+ To execute a saved query, run this command in a shell:
344
+
345
+ ```sh
346
+ jmespath --query-file query.jmespath --file document.json
347
+ jmespath --pretty --query-file query.jmespath --file document.json
348
+ ```
349
+
350
+ To query a UTF-8 document file, run this command in a shell:
351
+
352
+ ```sh
353
+ jmespath --file document.json 'length(@)'
354
+ ```
355
+
356
+ Both executables also support validation without reading stdin.
357
+ Run this command in a shell:
358
+
359
+ ```sh
360
+ jmespath --validate 'items[*].name'
361
+ jmespath --validate --query-file query.jmespath
362
+ ```
363
+
364
+ Valid syntax leaves both output streams empty and exits with status 0.
365
+ Unknown functions remain valid syntax; function availability and runtime types require evaluation.
366
+ For invalid syntax, the command prints a formatted diagnostic to stderr and exits with status 1.
367
+ Parser value failures, including a zero slice step, also exit with status 1.
368
+ Missing or excess validation arguments print usage to stderr and exit with status 2.
369
+ Every validation outcome leaves stdout empty and does not read stdin.
370
+ In the exact two-argument form `--validate EXPRESSION`, the second argument is query text, even when it resembles a flag.
371
+ Thus `--validate --query-file` validates the literal expression `--query-file`.
372
+ File validation also accepts `--query-file PATH --validate` and rejects `--file`.
373
+ Combining `--pretty` with validation produces a usage failure before opening input files or reading stdin.
374
+ Combining `--raw-output` with validation follows the same rule.
375
+ The exact two-argument call `--validate --pretty` validates the literal expression `--pretty`.
376
+ Likewise, `--validate --raw-output` validates the literal expression `--raw-output`.
180
377
 
181
378
  To inspect a function type failure, run this command in a shell:
182
379
 
@@ -205,7 +402,7 @@ Missing or unusable source ranges omit coordinates, the query line, and the care
205
402
  Incomplete validation metadata retains the escaped exception message and any usable source location.
206
403
  Unknown-function diagnostics retain suggestions; invalid-value failures use message-only diagnostics.
207
404
 
208
- Malformed stdin JSON prints `Invalid JSON input` without parser messages or document fragments.
405
+ Malformed JSON from stdin or a file prints `Invalid JSON input` without parser messages or document fragments.
209
406
  For example, run this command in a shell:
210
407
 
211
408
  ```sh
@@ -45,6 +45,39 @@ JMESPath.search("sort_by(@, &age)[*].name", [{name: "Lin", age: 20}, {name: "Ada
45
45
  JMESPath.search("avg(@)", [1, 2, 3]) #=> 2.0
46
46
  ```
47
47
 
48
+ ## Command-line reference
49
+
50
+ Run either executable from the shell to list built-in signatures and descriptions without reading stdin:
51
+
52
+ ```sh
53
+ jmespath --functions
54
+ jmespath.rb --functions
55
+ ```
56
+
57
+ Each built-in appears once in alphabetical order, using public function metadata.
58
+ For example, the listing includes these entries:
59
+
60
+ ```text
61
+ join(string, array<string>) Join strings with a separator.
62
+ length(string|array|object) Character or element count.
63
+ map(expref, array) Map an expression over an array, including null results.
64
+ merge(object...) Merge objects, with later values winning.
65
+ sort(array<number>|array<string>) Stable sorted array of numbers or strings.
66
+ ```
67
+
68
+ `A|B` accepts either type; `any` accepts any value.
69
+ `array<T>` requires every element to have type `T`, while `array` accepts elements of any type.
70
+ Thus `array<number>|array<string>` accepts a homogeneous array, including an empty array.
71
+ `expref` denotes an expression reference written as `&expression`.
72
+ The final `...` marks a required argument that repeats, so `merge(object...)` needs at least one object.
73
+ The listing includes a notation legend and describes declared types; runtime checks can impose narrower constraints.
74
+ Return types are not inferred.
75
+
76
+ Use `--functions` alone, without queries, paths, other modes, repeated flags, or values.
77
+ The exact two-argument form `--validate --functions` validates the literal query text under the validation rules.
78
+ Listing returns status 0 with empty stderr; output failures return status 1.
79
+ Usage failures return status 2 with empty stdout and usage guidance on stderr, before accessing input.
80
+
48
81
  ## Custom signatures
49
82
 
50
83
  Start with `Functions.default` to retain the built-ins, or `Functions.new` for an empty registry.