From 2eb98f5222dc5ac050fdf261eb46bb7c61005431 Mon Sep 17 00:00:00 2001 From: Caleb Walch Date: Sun, 9 Mar 2025 10:50:14 -0500 Subject: [PATCH] docs: improve formatting and content of JEP documents (jep-001, jep-003a) - Update table formatting in `jep-001-nested-expressions.md` for better readability - Update the content of `jep-001-nested-expressions.md` to improve clarity - Update the content of `jep-003a-functions.md` to improve clarity --- jep-001-nested-expressions.md | 155 +++++++--------- jep-003a-functions.md | 339 ++++++++++++++++------------------ 2 files changed, 231 insertions(+), 263 deletions(-) diff --git a/jep-001-nested-expressions.md b/jep-001-nested-expressions.md index 201ff76..bb8457e 100644 --- a/jep-001-nested-expressions.md +++ b/jep-001-nested-expressions.md @@ -1,11 +1,10 @@ # Nested Expressions -||| -|---|--- -| **JEP** | 1 -| **Author** | Michael Dowling -| **Status** | accepted -| **Created**| 27-Nov-2013 +| **JEP** | 1 | +| ----------- | --------------- | +| **Author** | Michael Dowling | +| **Status** | accepted | +| **Created** | 27-Nov-2013 | ## Abstract @@ -27,16 +26,13 @@ provide customers with a much more flexible expression DSL. Supporting arbitrarily nested expressions within other expressions requires: +- Updating the grammar to remove `non-branched-expr` -* Updating the grammar to remove `non-branched-expr` +- Updating compliance tests to add various permutations of the grammar to + ensure implementations are compliant. - -* Updating compliance tests to add various permutations of the grammar to -ensure implementations are compliant. - - -* Updating the JMESPath documentation to reflect the ability to arbitrarily -nest expressions. +- Updating the JMESPath documentation to reflect the ability to arbitrarily + nest expressions. ## Nested Expression Examples @@ -46,16 +42,17 @@ Given: ```json { - "foo": { - "baz": [ - { - "bar": "abc" - }, { - "bar": "def" - } - ], - "qux": ["zero"] - } + "foo": { + "baz": [ + { + "bar": "abc" + }, + { + "bar": "def" + } + ], + "qux": ["zero"] + } } ``` @@ -64,13 +61,7 @@ With: `foo.[baz[*].bar, qux[0]]` Result: ```json -[ - [ - "abc", - "def" - ], - "zero" -] +[["abc", "def"], "zero"] ``` ### Nested branch expressions with nested mutli-select @@ -79,20 +70,21 @@ Given: ```json { - "foo": { - "baz": [ - { - "bar": "a", - "bam": "b", - "boo": "c" - }, { - "bar": "d", - "bam": "e", - "boo": "f" - } - ], - "qux": ["zero"] - } + "foo": { + "baz": [ + { + "bar": "a", + "bam": "b", + "boo": "c" + }, + { + "bar": "d", + "bam": "e", + "boo": "f" + } + ], + "qux": ["zero"] + } } ``` @@ -102,17 +94,11 @@ Result: ```json [ - [ - [ - "a", - "c" - ], - [ - "d", - "f" - ] - ], - "zero" + [ + ["a", "c"], + ["d", "f"] + ], + "zero" ] ``` @@ -122,20 +108,21 @@ Given: ```json { - "foo": { - "baz": [ - { - "bar": "a", - "bam": "b", - "boo": "c" - }, { - "bar": "d", - "bam": "e", - "boo": "f" - } - ], - "qux": ["zero"] - } + "foo": { + "baz": [ + { + "bar": "a", + "bam": "b", + "boo": "c" + }, + { + "bar": "d", + "bam": "e", + "boo": "f" + } + ], + "qux": ["zero"] + } } ``` @@ -144,13 +131,7 @@ With: `foo.[baz[*].not_there || baz[*].bar, qux[0]]` Result: ```json -[ - [ - "a", - "d" - ], - "zero" -] +[["a", "d"], "zero"] ``` ### No breaking changes @@ -162,12 +143,12 @@ Given: ```json { - "foo": { - "baz": { - "abc": 123, - "bar": 456 - } + "foo": { + "baz": { + "abc": 123, + "bar": 456 } + } } ``` @@ -177,11 +158,11 @@ Result: ```json [ - { - "abc": 123, - "bar": 456 - }, - 456 + { + "abc": 123, + "bar": 456 + }, + 456 ] ``` diff --git a/jep-003a-functions.md b/jep-003a-functions.md index 26a0d5a..895bd19 100644 --- a/jep-003a-functions.md +++ b/jep-003a-functions.md @@ -1,19 +1,17 @@ # Functions - -||| -|---|--- -| **JEP** | 3a -| **Author** | Michael Dowling, James Saryerwinnie, Maxime Labelle -| **Status** | accepted -| **Created** | 27-Nov-2013 -| **Obsoletes** | [JEP-003](./jep-003-functions.md) +| **JEP** | 3a | +| ------------- | --------------------------------------------------- | +| **Author** | Michael Dowling, James Saryerwinnie, Maxime Labelle | +| **Status** | accepted | +| **Created** | 27-Nov-2013 | +| **Obsoletes** | [JEP-003](./jep-003-functions.md) | ## Addendum -|Date|Description -|---|---| -|16-March-2023|Clarified string data type and containment. +| Date | Description | +| ------------- | ------------------------------------------- | +| 16-March-2023 | Clarified string data type and containment. | ## Abstract @@ -35,30 +33,24 @@ powerful mechanism to perform any kind of special comparisons for things like ## Data Types -In order to support functions, a type system is needed. The JSON types are +In order to support functions, a type system is needed. The JSON types are used: +- number (integers and double-precision floating-point format in JSON) -* number (integers and double-precision floating-point format in JSON) - - -* string (a sequence of Unicode [code points](https://unicode.org/glossary/#code_point). Note that a code point is distinct to a [code unit](https://unicode.org/glossary/#code_unit)) - - -* boolean (`true` or `false`) - - -* array (an ordered, sequence of values) +- string (a sequence of Unicode [code points](https://unicode.org/glossary/#code_point). Note that a code point is distinct from a [code unit](https://unicode.org/glossary/#code_unit)) +- boolean (`true` or `false`) -* object (an unordered collection of key value pairs) +- array (an ordered, sequence of values) +- object (an unordered collection of key value pairs) -* null +- null ## Syntax Changes -Functions are defined in the `function-expression` rule below. A function +Functions are defined in the `function-expression` rule below. A function expression is an `expression` itself, and is valid any place an `expression` is allowed. @@ -84,8 +76,8 @@ expression =/ literal / function-expression ``` A function can accept any number of arguments, and each argument can be an -expression. Each function must define a signature that specifies the number -and allowed types of its expected arguments. Functions can be variadic. +expression. Each function must define a signature that specifies the number +and allowed types of its expected arguments. Functions can be variadic. ### current-node @@ -93,14 +85,14 @@ The `current-node` token can be used to represent the current node being evaluated. The `current-node` token is useful for functions that require the current node being evaluated as an argument. For example, the following expression creates an array containing the total number of elements in the -`foo` object followed by the value of `foo["bar"]`. +`foo` object followed by the value of `foo.bar`. ``` foo[].[count(@), bar] ``` JMESPath assumes that all function arguments operate on the current node unless -the argument is a `literal` or `number` token. Because of this, an +the argument is a `literal` or `number` token. Because of this, an expression such as `@.bar` would be equivalent to just `bar`, so the current node is only allowed as a bare expression. @@ -114,24 +106,24 @@ to the node currently being evaluated by the projection. ## Function Evaluation -Functions are evaluated in applicative order. Each argument must be an +Functions are evaluated in applicative order. Each argument must be an expression, each argument expression must be evaluated before evaluating the -function. The function is then called with the evaluated function arguments. +function. The function is then called with the evaluated function arguments. The result of the `function-expression` is the result returned by the -function call. If a `function-expression` is evaluated for a function that +function call. If a `function-expression` is evaluated for a function that does not exist, the JMESPath implementation must indicate to the caller that an -`unknown-function` error occurred. How and when this error is raised is +`unknown-function` error has occurred. How and when this error is raised is implementation specific, but implementations should indicate to the caller that this specific error occurred. Functions can either have a specific arity or be variadic with a minimum -number of arguments. If a `function-expression` is encountered where the +number of arguments. If a `function-expression` is encountered where the arity does not match or the minimum number of arguments for a variadic function -is not provided, then implementations must indicate to the caller than an -`invalid-arity` error occurred. How and when this error is raised is +is not provided, then implementations must indicate to the caller that an +`invalid-arity` error occurred. How and when this error is raised is implementation specific. -Each function signature declares the types of its input parameters. If any +Each function signature declares the types of its input parameters. If any type constraints are not met, implementations must indicate that an `invalid-type` error occurred. @@ -140,9 +132,9 @@ types to other types (`to_string`, `to_number`) which are defined below. No explicit type conversion happens unless a user specifically uses one of these type conversion functions. -Function expressions are also allowed as the child element of a sub expression. +Function expressions are allowed within sub expressions. This allows functions to be used with projections, which can enable functions -to be applied to every element in a projection. For example, given the input +to be applied to every element in a projection. For example, given the input data of `["1", "2", "3", "notanumber", true]`, the following expression can be used to convert (and filter) all elements to numbers: @@ -155,7 +147,7 @@ This provides a simple mechanism to explicitly convert types when needed. ## Built-in Functions JMESPath has various built-in functions that operate on different -data types, documented below. Each function below has a signature +data types, documented below. Each function below has a signature that defines the expected types of the input and the type of the returned output: @@ -165,11 +157,11 @@ return_type function_name2(type1|type2 $argname) ``` If a function can accept multiple types for an input value, then the -multiple types are separated with `|`. If the resolved arguments do not +multiple types are separated with `|`. If the resolved arguments do not match the types specified in the signature, an `invalid-type` error occurs. The `array` type can further specify requirements on the type of the elements -if they want to enforce homogeneous types. The subtype is surrounded by +to enforce homogeneous types. The subtype is surrounded by `[type]`, for example, the function signature below requires its input argument resolves to an array of numbers: @@ -181,7 +173,7 @@ As a shorthand, the type `any` is used to indicate that the argument can be of any type (`array|object|number|string|boolean|null`). The first function below, `abs` is discussed in detail to demonstrate the -above points. Subsequent function definitions will not include these details +above points. Subsequent function definitions will not include these details for brevity, but the same rules apply. **NOTE**: All string related functions are defined on the basis of Unicode code @@ -193,11 +185,11 @@ points; they do not take normalization into account. number abs(number $value) ``` -Returns the absolute value of the provided argument. The signature indicates +Returns the absolute value of the provided argument. The signature indicates that a number is returned, and that the input argument `$value` **must** resolve to a number, otherwise an `invalid-type` error is triggered. -Below is a worked example. Given: +Below is a worked example. Given: ``` {"foo": -1, "bar": "2"} @@ -206,6 +198,7 @@ Below is a worked example. Given: Evaluating `abs(foo)` works as follows: 1. Evaluate the input argument against the current data: + ``` search(foo, {"foo": -1, "bar": 2"}) -> -1 ``` @@ -214,16 +207,17 @@ Evaluating `abs(foo)` works as follows: In this case `-1` is of type `number` so it passes the type check. 3. Call the function with the resolved argument: + ``` abs(-1) -> 1 ``` 4. The value of `1` is the resolved value of the function expression `abs(foo)`. - Below is the same steps for evaluating `abs(bar)`: 1. Evaluate the input argument against the current data: + ``` search(bar, {"foo": -1, "bar": 2"}) -> "2" ``` @@ -232,14 +226,16 @@ Below is the same steps for evaluating `abs(bar)`: In this case `"2"` is of type `string` so immediately indicate that an `invalid-type` error occurred. - As a final example, here is the steps for evaluating `abs(to_number(bar))`: 1. Evaluate the input argument against the current data: + ``` search(to_number(bar), {"foo": -1, "bar": "2"}) ``` + This requires following the same process for `to_number(bar)`: + 1. Evaluate the input argument against the current data: ``` search(bar, {"foo": -1, "bar": "2"}) -> "2" @@ -257,20 +253,21 @@ As a final example, here is the steps for evaluating `abs(to_number(bar))`: In this case `2` is of type `number` so it passes the type check. 3. Call the function with the final resolved argument: + ``` abs(2) -> 2 ``` -5. The value of `2` is the resolved value of the function expression +4. The value of `2` is the resolved value of the function expression `abs(to_number(bar))`. #### Examples -|Expression|Result -|---|--- -| `` abs(`1`) `` | `1` -| `` abs(`-1`) ``| `1` -| `` abs(`"abc"`) ``| |`` +| Expression | Result | +| ---------------- | ----------------------- | +| ``abs(`1`)`` | `1` | +| ``abs(`-1`)`` | `1` | +| ``abs(`"abc"`)`` | `` | ### avg @@ -284,12 +281,12 @@ An empty array will produce a return value of null. #### Examples -|Given|Expression|Result -|---|---|--- -|`[10, 15, 20]`| `avg(@)` | 15 -| `[10, false, 20]`| `avg(@)`|`` -| `[false]` | `avg(@)` | `` -| `false` | `avg(@)` | `` +| Given | Expression | Result | +| ----------------- | ---------- | ----------------------- | +| `[10, 15, 20]` | `avg(@)` | 15 | +| `[10, false, 20]` | `avg(@)` | `` | +| `[false]` | `avg(@)` | `` | +| `false` | `avg(@)` | `` | ### ceil @@ -301,12 +298,12 @@ Returns the next highest integer value by rounding up if necessary. #### Examples -|Expression|Result -|---|---| -| ``ceil(`1.001`)`` | 2 -| ``ceil(`1.9`)`` | 2 -| ``ceil(`1`)`` | 1 -| ``ceil(`"abc"`)`` | `` +| Expression | Result | +| ----------------- | ----------------------- | +| ``ceil(`1.001`)`` | 2 | +| ``ceil(`1.9`)`` | 2 | +| ``ceil(`1`)`` | 1 | +| ``ceil(`"abc"`)`` | `` | ### contains @@ -321,22 +318,21 @@ If `$subject` is an array, this function returns true if one of the elements in the array is equal to the provided `$search` value. If the provided `$subject` is a string, this function returns true if -there exists within the `$subject` string at least one _equal_ occurrence -of the `$search` string. If the `$search` value is not a string, the -function MUST return `false`. +there exists within the `$subject` string at least one occurrence of the `$search` string. +If the `$search` value is not a string, the function MUST return `false`. #### Examples -|Given|Expression|Result -|---|---|--- -| n/a | ``contains(`"foobar"`, `"foo"`)`` | `true` -| n/a | ``contains(`"foobar"`, `"not"`)`` | `false` -| n/a | ``contains(`"foobar"`, `"bar"`)`` | `true` -| n/a | ``contains(`false`, `"bar"`)`` | `` -| n/a | ``contains(`"foobar"`, `123`)`` | `false` -| `["a", "b"]` | ``contains(@, `"a"`)`` | `true` -| `["a"]` | ``contains(@, `"a"`)`` | `true` -| `["a"]` | ``contains(@, `"b"`)`` | `false` +| Given | Expression | Result | +| ------------ | --------------------------------- | ----------------------- | +| n/a | ``contains(`"foobar"`, `"foo"`)`` | `true` | +| n/a | ``contains(`"foobar"`, `"not"`)`` | `false` | +| n/a | ``contains(`"foobar"`, `"bar"`)`` | `true` | +| n/a | ``contains(`false`, `"bar"`)`` | `` | +| n/a | ``contains(`"foobar"`, `123`)`` | `false` | +| `["a", "b"]` | ``contains(@, `"a"`)`` | `true` | +| `["a"]` | ``contains(@, `"a"`)`` | `true` | +| `["a"]` | ``contains(@, `"b"`)`` | `false` | ### floor @@ -348,11 +344,11 @@ Returns the next lowest integer value by rounding down if necessary. #### Examples -| Expression | Result -|---|--- -| ``floor(`1.001`)`` | 1 -| ``floor(`1.9`)`` | 1 -| ``floor(`1`)`` | 1 +| Expression | Result | +| ------------------ | ------ | +| ``floor(`1.001`)`` | 1 | +| ``floor(`1.9`)`` | 1 | +| ``floor(`1`)`` | 1 | ### join @@ -365,12 +361,12 @@ together using the `$glue` argument as a separator between each. #### Examples -| Given | Expression | Result -|---|---|--- -| `["a", "b"]` | ``join(`", "`, @)`` | "a, b" -| `["a", "b"]` | ```join(`""`, @) ``` | "ab" -| `["a", false, "b"]` | ``join(`", "`, @)`` | `` -| `[false]` | ``join(`", "`, @)`` | `` +| Given | Expression | Result | +| ------------------- | ------------------- | ----------------------- | +| `["a", "b"]` | ``join(`", "`, @)`` | "a, b" | +| `["a", "b"]` | ``join(`""`, @) `` | "ab" | +| `["a", false, "b"]` | ``join(`", "`, @)`` | `` | +| `[false]` | ``join(`", "`, @)`` | `` | ### keys @@ -382,12 +378,12 @@ Returns an array containing the keys of the provided object. #### Examples -| Given | Expression | Result -|---|---|--- -| `{"foo": "baz", "bar": "bam"}` | `keys(@)` | `["foo", "bar"]` -| `{}` | `keys(@)` | `[]` -| `false` | `keys(@)` | `` -| `[b, a, c]` | `keys(@)` | `` +| Given | Expression | Result | +| ------------------------------ | ---------- | ----------------------- | +| `{"foo": "baz", "bar": "bam"}` | `keys(@)` | `["foo", "bar"]` | +| `{}` | `keys(@)` | `[]` | +| `false` | `keys(@)` | `` | +| `[b, a, c]` | `keys(@)` | `` | ### length @@ -397,26 +393,23 @@ number length(string|array|object $subject) Returns the length of the given argument using the following types rules: - 1. string: returns the number of code points in the string - 2. array: returns the number of elements in the array - 3. object: returns the number of key-value pairs in the object #### Examples -| Given | Expression | Result -|---|---|--- -| n/a | ``length(`"abc"`)`` | 3 -| "current" | `length(@)` | 7 -| "current" | `length(not_there)` | `` -| `["a", "b", "c"]` | `length(@)` | 3 -| `[]` | `length(@)` | 0 -| `{}` | `length(@)` | 0 -| `{"foo": "bar", "baz": "bam"}` | `length(@)` | 2 +| Given | Expression | Result | +| ------------------------------ | ------------------- | ----------------------- | +| n/a | ``length(`"abc"`)`` | 3 | +| "current" | `length(@)` | 7 | +| "current" | `length(not_there)` | `` | +| `["a", "b", "c"]` | `length(@)` | 3 | +| `[]` | `length(@)` | 0 | +| `{}` | `length(@)` | 0 | +| `{"foo": "bar", "baz": "bam"}` | `length(@)` | 2 | ### max @@ -430,10 +423,10 @@ An empty array will produce a return value of null. #### Examples -| Given | Expression | Result -|---|---|--- -| `[10, 15]` | `max(@)` | 15 -| `[10, false, 20]` | `max(@)` | `` +| Given | Expression | Result | +| ----------------- | ---------- | ----------------------- | +| `[10, 15]` | `max(@)` | 15 | +| `[10, false, 20]` | `max(@)` | `` | ### min @@ -445,10 +438,10 @@ Returns the lowest found number in the provided `$collection` argument. #### Examples -| Given | Expression | Result -|---|--|-- -| `[10, 15]` | `min(@)` | 10 -| `[10, false, 20]` | `min(@)` | `` +| Given | Expression | Result | +| ----------------- | ---------- | ----------------------- | +| `[10, 15]` | `min(@)` | 10 | +| `[10, false, 20]` | `min(@)` | `` | ### sort @@ -459,39 +452,37 @@ array sort(array $list) This function accepts an array `$list` argument and returns the sorted elements of the `$list` as an array. -The array must be a list of strings or numbers. Sorting strings is based on -code points. Locale is not taken into account. +The array must be a list of strings or numbers. Sorting strings is based on +code points. Locale is not taken into account. #### Examples -| Given | Expression | Result -|---|---|--- -| `[b, a, c]` | `sort(@)` | `[a, b, c]` -| `[1, a, c]` | `sort(@)` | `[1, a, c]` -| `[false, [], null]` | `sort(@)` | `[[], null, false]` -| `[[], {}, false]` | `sort(@)` | `[{}, [], false]` -| `{"a": 1, "b": 2}` | `sort(@)` | `null` -| `false` | `sort(@)` | `null` - | +| Given | Expression | Result | +| ------------------- | ---------- | ------------------- | +| `[b, a, c]` | `sort(@)` | `[a, b, c]` | +| `[1, a, c]` | `sort(@)` | `[1, a, c]` | +| `[false, [], null]` | `sort(@)` | `[[], null, false]` | +| `[[], {}, false]` | `sort(@)` | `[{}, [], false]` | +| `{"a": 1, "b": 2}` | `sort(@)` | `null` | +| `false` | `sort(@)` | `null` | + ### to_string ``` string to_string(string|number|array|object|boolean $arg) ``` +- string - Returns the passed in value. -* string - Returns the passed in value. - - -* number/array/object/boolean - The JSON encoded value of the object. The -JSON encoder should emit the encoded JSON value without adding any additional -new lines. +- number/array/object/boolean - The JSON encoded value. The + JSON encoder should emit the encoded JSON value without adding any additional + new lines. #### Examples -| Given | Expression | Result -|---|---|--- -| `null` | ``to_string(`2`)`` | `"2"` +| Given | Expression | Result | +| ------ | ------------------ | ------ | +| `null` | ``to_string(`2`)`` | `"2"` | ### to_number @@ -499,13 +490,13 @@ new lines. number to_number(string|number $arg) ``` -* string - Returns the parsed number. Any string that conforms to the -`json-number` production is supported. +- string - Returns the parsed number. Any string that conforms to the + `json-number` production is supported. -* number - Returns the passed in value. -* array - null -* object - null -* boolean - null +- number - Returns the passed in value. +- array - null +- object - null +- boolean - null ### type @@ -513,30 +504,29 @@ number to_number(string|number $arg) string type(array|object|string|number|boolean|null $subject) ``` -Returns the JavaScript type of the given `$subject` argument as a string -value. +Returns the JMESPath type of the given `$subject` argument as a string value. The return value MUST be one of the following: -* number -* string -* boolean -* array -* object -* null +- number +- string +- boolean +- array +- object +- null #### Examples -| Given | Expression | Result -|---|---|--- -| "foo" | `type(@)` | "string" -| `true` | `type(@)` | "boolean" -| `false` | `type(@)` | "boolean" -| `null` | `type(@)` | "null" -| 123 | `type(@)` | number -| 123.05 | `type(@)` | number -| `["abc"]` | `type(@)` | "array" -| `{"abc": "123"}` | `type(@)` | "object" +| Given | Expression | Result | +| ---------------- | ---------- | --------- | +| "foo" | `type(@)` | "string" | +| `true` | `type(@)` | "boolean" | +| `false` | `type(@)` | "boolean" | +| `null` | `type(@)` | "null" | +| 123 | `type(@)` | number | +| 123.05 | `type(@)` | number | +| `["abc"]` | `type(@)` | "array" | +| `{"abc": "123"}` | `type(@)` | "object" | ### values @@ -548,33 +538,30 @@ Returns the values of the provided object. #### Examples -| Given | Expression | Result -|---|---|--- -| `{"foo": "baz", "bar": "bam"}` | `values(@)` | `["baz", "bam"]` -| `["a", "b"]` | `values(@)` | `` -| `false` | `values(@)` | `` +| Given | Expression | Result | +| ------------------------------ | ----------- | ----------------------- | +| `{"foo": "baz", "bar": "bam"}` | `values(@)` | `["baz", "bam"]` | +| `["a", "b"]` | `values(@)` | `` | +| `false` | `values(@)` | `` | ## Compliance Tests A `functions.json` will be added to the compliance test suite. The test suite will add the following new error types: - -* unknown-function -* invalid-arity -* invalid-type +- unknown-function +- invalid-arity +- invalid-type The compliance does not specify **when** the errors are raised, as this will -depend on implementation details. For an implementation to be compliant they -need to indicate that an error occurred while attempting to evaluate the +depend on implementation details. For an implementation to be compliant it +needs to indicate that an error occurred while attempting to evaluate the JMESPath expression. ## History +- This JEP originally proposed the literal syntax. The literal portion of this + JEP was removed and added instead to JEP-7. -* This JEP originally proposed the literal syntax. The literal portion of this -JEP was removed and added instead to JEP-7. - - -* This JEP originally specified that types matches should return null. This -has been updated to specify that an invalid type error should occur instead. +- This JEP originally specified that types matches should return null. This + has been updated to specify that an invalid type error should occur instead.