Skip to content

Commit c572771

Browse files
authored
Merge branch 'main' into jep/arithmetic-expressions
2 parents 40562c6 + c2d4e49 commit c572771

26 files changed

+1720
-40
lines changed

GRAMMAR.ABNF

Lines changed: 67 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,7 @@
1919
;; The result of applying a JMESPath expression against a JSON document will always result in valid JSON, provided there
2020
;; are no errors during the evaluation process. Structured data in, structured data out.
2121
;;
22-
;; This also means that, with the exception of JMESPath expression types, JMESPath only supports the same types support by JSON:
22+
;; This also means that, with the exception of JMESPath expression types, JMESPath only supports the same types supported by JSON:
2323
;;
2424
;; - number (integers and double-precision floating-point format in JSON)
2525
;; - string
@@ -62,7 +62,8 @@ sub-expression = expression "." ( identifier / multi-select-list / multi-sele
6262
;; In pseudocode:
6363
;; ```
6464
;; left-evaluation = search(left-expression, original-json-document)
65-
;; result = search(right-expression, left-evaluation)
65+
;; if left-evaluation is `null` then result = `null`
66+
;; else result = search(right-expression, left-evaluation)
6667
;; ```
6768
;; A sub-expression is itself an expression, so there can be multiple levels of sub-expressions: grandparent.parent.child.
6869
;; Examples
@@ -252,36 +253,36 @@ bracket-specifier =/ "[]" ;; ## Flatten Operator
252253
;; ```
253254

254255
slice-expression = [number] ":" [number] [ ":" [number] ] ;; ## Slices
255-
;; A slice expression allows you to select a subset of an array. A slice has a start, stop, and step value. The
256-
;; general form of a slice is [start:stop:step], but each component is optional and can be omitted.
256+
;; A slice expression allows you to select a subset of an array or a string. A slice has a `start`, `stop`, and `step` value. The
257+
;; general form of a slice is `[start:stop:step]`, but each component is optional and can be omitted.
257258
;;
258259
;; ```note
259260
;; Slices in JMESPath have the same semantics as python slices. If you're familiar with python slices, you're familiar with
260261
;; JMESPath slices.
261262
;; ```
262263
;;
263-
;; Given a start, stop, and step value, the sub elements in an array are extracted as follows:
264+
;; Given a `start`, `stop`, and `step` value, the sub elements in an array or characters in a string are extracted as follows:
264265
;;
265-
;; - The first element in the extracted array is the index denoted by start.
266-
;; - The last element in the extracted array is the index denoted by end - 1.
267-
;; - The step value determines how many indices to skip after each element is selected from the array.
268-
;; The default step value of 1 will not skip any indices and will return a contiguous subset of the original array.
269-
;; A step value greater than 1 will skip indices while extracting elements from an array. For instance, a step value of 2 will
270-
;; skip every other index.
271-
;; Negative step values start from the end of the array and extract elements in reverse order.
266+
;; - The first element in the extracted array or first character in the extracted string is the index denoted by `start`.
267+
;; - The last element in the extracted array or last character in the extracted string is the index denoted by `end - 1`.
268+
;; - The `step` value determines how many indices to skip after each element is selected from the array or each character is selected from the string.
269+
;; The default `step` value of `1` will not skip any indices and will return a contiguous subset of the original array or a substring of the original string.
270+
;; A `step` value greater than `1` will skip indices while extracting elements from an array or characters from a string. For instance, a `step` value of `2` will
271+
;; skip every other element or character.
272+
;; Negative `step` values start from the end of the array or string and extract elements or characters in reverse order.
272273
;;
273274
;; Slice expressions adhere to the following rules:
274275
;;
275-
;; - If a negative start position is given, it is calculated as the total length of the array plus the given start position.
276-
;; - If no start position is given, it is assumed to be 0 if the given step is greater than 0 or the end of the array if
277-
;; the given step is less than 0.
278-
;; - If a negative stop position is given, it is calculated as the total length of the array plus the given stop position.
279-
;; - If no stop position is given, it is assumed to be the length of the array if the given step is greater than 0 or 0 if
280-
;; the given step is less than 0.
281-
;; - If the given step is omitted, it it assumed to be 1.
282-
;; - If the given step is 0, an error MUST be raised.
283-
;; - If the element being sliced is not an array, the result is null.
284-
;; - If the element being sliced is an array and yields no results, the result MUST be an empty array.
276+
;; - If a negative `start` position is given, it is calculated as the total length of the array or string plus the given `start` position.
277+
;; - If no `start` position is given, it is assumed to be `0` if the given `step` is greater than 0 or the end of the array or string if
278+
;; the given `step` is less than `0`.
279+
;; - If a negative `stop` position is given, it is calculated as the total length of the array or string plus the given `stop` position.
280+
;; - If no `stop` position is given, it is assumed to be the length of the array or string if the given `step` is greater than `0` or `0` if
281+
;; the given `step` is less than `0`.
282+
;; - If the given `step` is omitted, it it assumed to be `1`.
283+
;; - If the given `step` is `0`, an error MUST be raised.
284+
;; - If the element being sliced is not an array or a string, the result is `null`.
285+
;; - If the element being sliced is an array or string and yields no results, the result MUST be an empty array.
285286
;;
286287
;; ### Examples
287288
;; ```
@@ -293,6 +294,12 @@ slice-expression = [number] ":" [number] [ ":" [number] ] ;; ## Slices
293294
;; search([::-1], [0, 1, 2, 3]) -> [3, 2, 1, 0]
294295
;; search([-2:], [0, 1, 2, 3]) -> [2, 3]
295296
;; ```
297+
;; Slicing operates on strings exactly as if a string were thought of as an array of characters.
298+
;; ```
299+
;; search(foo[0:4], {"foo": "hello, world!"}) -> "hell"
300+
;; search([::], 'raw-string') -> "raw-string"
301+
;; search([::2], 'raw-string') -> "rwsrn"
302+
;; search([::-1], 'raw-string') -> "gnirts-war"
296303

297304
multi-select-list = "[" ( expression *( "," expression ) ) "]" ;; # MultiSelect List
298305
;; A multiselect expression is used to extract a subset of elements from a JSON hash. There are two version of multiselect,
@@ -601,7 +608,7 @@ unquoted-string = (%x41-5A / %x61-7A / %x5F) *( ; A-Za-z_
601608
%x5F / ; _
602609
%x61-7A) ; a-z
603610
quoted-string = quote 1*(unescaped-char / escaped-char) quote
604-
unescaped-char = %x20-21 / %x23-5B / %x5D-10FFFF
611+
unescaped-char = %x20-21 / %x23-2E / %30-5B / %x5D-10FFFF
605612
escape = "\"
606613
quote = %x22 ; Double quote: '"'
607614
escaped-char = escape (
@@ -705,4 +712,40 @@ arithmetic-expression =/ expression ( "/" / "÷" ) expression ; / %x47 ÷
705712
;; ```
706713
;; search({ab: a.b, cd: c.d} | ab + cd, {"a": {"b": 1}, "c": {"d": 2}}) -> 3
707714
;; ```
708-
;;
715+
;;
716+
717+
expression =/ root-node ;; ## Root Reference
718+
719+
root-node = "$"
720+
721+
;; ## root-node
722+
;;
723+
;; The `root-node` token can be used to represent the original input JSON document.
724+
;;
725+
;; As a JMESPath expression is being evaluated, the current scope changes.
726+
;; Given a simple sub expression such as `foo.bar`, first the `foo` expression is evaluated
727+
;; with the starting input JSON document, and the result of that expression is then used as
728+
;; the current scope when the `bar` element is evaluated.
729+
;;
730+
;; Once we’ve drilled down to a specific scope, the `root-node` token can be used
731+
;; to refer to the original JSON document.
732+
;;
733+
;; Example
734+
;;
735+
;; Given a JSON document:
736+
;;
737+
;; ```json
738+
;; {
739+
;; "first_choice": "WA",
740+
;; "states": [
741+
;; {"name": "WA", "cities": ["Seattle", "Bellevue", "Olympia"]},
742+
;; {"name": "CA", "cities": ["Los Angeles", "San Francisco"]},
743+
;; {"name": "NY", "cities": ["New York City", "Albany"]}
744+
;; ]
745+
;; }
746+
;; ```
747+
;; We can retrieve the list of cities of the state corresponding to the `first_choice` key
748+
;; using the following expression:
749+
;;
750+
;; ` states[? name == $.first_choice ].cities[] `
751+
;;

function_schema.yml

Lines changed: 42 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,37 @@ $schema: https://json-schema.org/draft/2020-12/schema
33
$id: https://jmespath.site/function_schema.yml
44
title: JMESPath function
55
description: JMESPath function definition and tests
6+
$defs:
7+
unnamed_arg: &unnamed_arg
8+
type: object
9+
properties:
10+
type: &type
11+
description: ""
12+
oneOf:
13+
- enum: &types
14+
- any
15+
- number
16+
- string
17+
- boolean
18+
- array
19+
- object
20+
- "null"
21+
- expression
22+
- "array[number]"
23+
- "array[string]"
24+
- "array[boolean]"
25+
- "array[object]"
26+
- "array[any]"
27+
- "array[array[any]]"
28+
- expression->any
29+
- expression->number
30+
- expression->string
31+
- type: array
32+
items:
33+
enum: *types
34+
desc:
35+
type: string
36+
description: ""
637
type: object
738
required: [name, topic, args, returns, desc, examples]
839
properties:
@@ -20,24 +51,19 @@ properties:
2051
type: array
2152
description: ""
2253
items:
23-
type: object
54+
allOf:
55+
- $ref: "#/$defs/unnamed_arg"
56+
- type: object
57+
required:
58+
- name
59+
properties:
60+
name:
61+
type: string
62+
description: ""
2463
description: ""
25-
additionalProperties: false
26-
properties:
27-
name:
28-
type: string
29-
description: ""
30-
type: &type
31-
description: ""
32-
oneOf:
33-
- enum: &types [any, number, string, boolean, array, object, "null", expression, "array[number]", "array[string]", "array[boolean]", "array[object]", "array[any]", "expression->number", "expression->string"]
34-
- type: array
35-
items:
36-
enum: *types
37-
desc:
38-
type: string
39-
description: ""
64+
unevaluatedProperties: false
4065
optional: *arg
66+
variadic: *unnamed_arg
4167
returns:
4268
type: object
4369
description: ""

functions/find_first.yml

Lines changed: 64 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,64 @@
1+
name: find_first
2+
topic: strings
3+
args:
4+
required:
5+
- name: subject
6+
type: [string]
7+
desc: 'Subject string'
8+
- name: sub
9+
type: [string]
10+
desc: 'Substring'
11+
optional:
12+
- name: start
13+
type: [number]
14+
desc: 'Position in the subject string where searching should start.'
15+
- name: end
16+
type: [number]
17+
desc: 'Position in the subject string where searching should end.'
18+
returns:
19+
type: number
20+
desc: ''
21+
desc: |
22+
Given the `subject` string, `find_first()` returns the index of the first occurence where the `sub` substring appears in `subject` or `null`.
23+
24+
The `start` and `end` parameters are optional and allow to select where `find_first()` must perform its search within `subject`.
25+
26+
* If `start` is omitted, it defaults to `0` which is the start of the `subject` string.
27+
* If `end` is omitted, it defaults to `length(subject) - 1` which is is the end of the `subject` string.
28+
examples:
29+
find_first:
30+
context: &subject subject string
31+
args: ['@', 'string']
32+
returns: 8.0
33+
find_first_start:
34+
context: *subject
35+
args: ['@', 'string', '`0`']
36+
returns: 8.0
37+
find_first_start_end:
38+
context: *subject
39+
args: ['@', 'string', '`0`', '`14`']
40+
returns: 8.0
41+
find_first_start_before_end_after:
42+
context: *subject
43+
args: ['@', 'string', '`-99`', '`100`']
44+
returns: 8.0
45+
find_first_ends_prematurely:
46+
context: *subject
47+
args: ['@', 'string', '`0`', '`13`']
48+
returns: null
49+
find_first_start_boundary:
50+
context: *subject
51+
args: ['@', 'string', '`8`']
52+
returns: 8.0
53+
find_first_incomplete:
54+
context: *subject
55+
args: ['@', 'string', '`9`']
56+
returns: null
57+
find_first_first_occurrence:
58+
context: *subject
59+
args: ['@', 's', '`0`']
60+
returns: 0.0
61+
find_first_second_occurrence:
62+
context: *subject
63+
args: ['@', 's', '`1`']
64+
returns: 8.0

functions/find_last.yml

Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,44 @@
1+
name: find_last
2+
topic: strings
3+
args:
4+
required:
5+
- name: subject
6+
type: [string]
7+
desc: 'Subject string'
8+
- name: sub
9+
type: [string]
10+
desc: 'Substring'
11+
optional:
12+
- name: pos
13+
type: [number]
14+
desc: 'Position in the subject string where searching should start.'
15+
returns:
16+
type: number
17+
desc: ''
18+
desc: |
19+
Given the `subject` string, `find_last()` returns the index of the last occurence where the `sub` substring appears in `subject` or `null`.
20+
21+
The `pos` parameter is optional and allow to select where `find_first()` must perform its search within `subject`.
22+
If this is parameter omitted, it defaults to `length(subject) - 1` which is is the end of the `subject` string.
23+
`find_last()` then searches backwards in the `subject` string for the first occurrence of the `sub` substring.
24+
examples:
25+
find_last:
26+
context: &subject subject string
27+
args: ['@', 'string']
28+
returns: 8.0
29+
find_last_pos:
30+
context: *subject
31+
args: ['@', 'string', '`8`']
32+
returns: 8.0
33+
find_last_pos_ends_prematurely:
34+
context: *subject
35+
args: ['@', 'string', '`7`']
36+
returns: null
37+
find_last_second_occurrence:
38+
context: *subject
39+
args: ['@', 's', '`8`']
40+
returns: 8.0
41+
find_last_first_occurrence:
42+
context: *subject
43+
args: ['@', 's', '`7`']
44+
returns: 0.0

functions/from_items.yml

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
name: from_items
2+
topic: misc
3+
args:
4+
required:
5+
- name: arg
6+
type: ['array[any]']
7+
desc: ''
8+
optional: []
9+
returns:
10+
type: [object]
11+
desc: ''
12+
desc: |2
13+
Returns an object from the provided array of key value pairs.
14+
This function is the inversed of the `items()` function.
15+
examples:
16+
from_items:
17+
context: [["one", 1], ["two", 2]]
18+
args: ['@']
19+
returns: {"one": 1, "two": 2}
20+
from_items_duplicate_entry:
21+
context: [["one", 1], ["two", 2], ["one", 3]]
22+
args: ['@']
23+
returns: {"one": 3, "two": 2}

0 commit comments

Comments
 (0)