Expressions
Expression are given in a simple Python- or JavaScript-like expression language. Its data types are limited to JSON types plus function objects.
Literals
Literals are similar to those for JSON Numeric literals, but only accept integer and decimal notation.
template:
- {$eval: "1.3"}
context: {}
result:
- 1.3
Strings do not support any kind of escaping, but can be enclosed in either "
or in '
. To avoid confusion when writing JSON-e expressions in YAML or JSON
documents, remember that JSON-e operates on the data structure that results
after YAML or JSON parsing is complete.
template:
- {$eval: "'abc'"}
- {$eval: '"abc"'}
context: {}
result:
- "abc"
- "abc"
Array and object literals also look much like JSON, with bare identifiers allowed as keys like in Javascript:
template:
- {$eval: '[1, 2, "three"]'}
- {$eval: '{foo: 1, "bar": 2}'}
context: {}
result:
- [1, 2, "three"]
- {"foo": 1, "bar": 2}
Context References
Bare identifiers refer to items from the context or to built-ins (described below).
template: {$eval: '[x, z, x+z]'}
context: {x: 'quick', z: 'sort'}
result: ['quick', 'sort', 'quicksort']
Valid identifiers for context references follow the requirements of many programming languages: identifiers may only contain letters, underscores, and numbers, but may not start with a number. This does imply that the context object may only have keys that meet these requirements.
Arithmetic Operations
The usual arithmetic operators are all defined, with typical associativity and precedence:
template:
- {$eval: 'x + z'}
- {$eval: 's + t'}
- {$eval: 'z - x'}
- {$eval: 'x * z'}
- {$eval: 'z / x'}
- {$eval: 'z ** 2'}
- {$eval: '(z / x) ** 2'}
context: {x: 10, z: 20, s: "face", t: "plant"}
result:
- 30
- "faceplant"
- 10
- 200
- 2
- 400
- 4
Note that strings can be concatenated with +
, but none of the other operators
apply.
Comparison Operations
Comparisons work as expected. Equality is "deep" in the sense of doing comparisons of the contents of data structures.
template:
- {$eval: 'x < z'}
- {$eval: 'x <= z'}
- {$eval: 'x > z'}
- {$eval: 'x >= z'}
- {$eval: 'deep == [1, [3, {a: 5}]]'}
- {$eval: 'deep != [1, [3, {a: 5}]]'}
context: {x: -10, z: 10, deep: [1, [3, {a: 5}]]}
result: [true, true, false, false, true, false]
Boolean Operations
Boolean operations use C- and Javascript-style symbols ||
, &&
, and !
:
template: {$eval: '!(false || false) && true'}
context: {}
result: true
Json-e supports short-circuit evaluation, so if in ||
left operand is true
returning value will be true no matter what right operand is:
template: {$eval: "true || b"}
context: {}
result: true
And if in &&
left operand is false returning value will be false no matter
what right operand is:
template: {$eval: "false && b"}
context: {}
result: false
Object Property Access
Like Javascript, object properties can be accessed either with array-index
syntax or with dot syntax. Unlike Javascript, obj.prop
is an error if obj
does not have prop
, while obj['prop']
will evaluate to null
.
template: {$eval: 'v.a + v["b"]'}
context: {v: {a: 'apple', b: 'banana', c: 'carrot'}}
result: 'applebanana'
Note that the object can be a literal expression:
template: {$eval: '{ENOMEM:"Out of memory", ENOCPU:"Out of CPUs"}[msgid]'}
context: {msgid: ENOMEM}
result: 'Out of memory'
When using the dot-syntax, e.g. .prop
, identifiers may only contain letters,
underscores, and numbers, but may not start with a number (just like for context
references). While the context object may only have keys that meet these
requirements, nested objects may have keys in any format. Keys that are not in
the identifier format must be accessed using the bracket-name syntax, e.g.
['my-prop']
.
Indexing and Slicing
Strings and arrays can be indexed and sliced using a Python-like indexing scheme. Negative indexes are counted from the end of the value. Slices are treated as "half-open", meaning that the result contains the first index and does not contain the second index. A "backward" slice with the start index greater than the end index is treated as empty.
Strings are treated as a sequence of Unicode codepoints.
template:
- {$eval: '[array[1], string[1]]'}
- {$eval: '[array[1:4], string[1:4]]'}
- {$eval: '[array[2:], string[2:]]'}
- {$eval: '[array[:2], string[:2]]'}
- {$eval: '[array[4:2], string[4:2]]'}
- {$eval: '[array[-2], string[-2]]'}
- {$eval: '[array[-2:], string[-2:]]'}
- {$eval: '[array[:-3], string[:-3]]'}
context: {array: ['a', 'b', '☪', 'd', 'e'], string: 'ab☪de'}
result:
- ['b', 'b']
- [['b', '☪', 'd'], 'b☪d']
- [['☪', 'd', 'e'], '☪de']
- [['a', 'b'], 'ab']
- [[], '']
- ['d', 'd']
- [['d', 'e'], 'de']
- [['a', 'b'], 'ab']
Containment Operation
The in
keyword can be used to check for containment: a property in an object,
an element in an array, or a substring in a string.
template:
- {$eval: '"foo" in {foo: 1, bar: 2}'}
- {$eval: '"foo" in ["foo", "bar"]'}
- {$eval: '"foo" in "foobar"'}
context: {}
result: [true, true, true]
Function Invocation
Function calls are made with the usual fn(arg1, arg2)
syntax. Functions are
not JSON data, so they cannot be created in JSON-e, but they can be provided as
built-ins or supplied in the context and called from JSON-e.