################################################################################ # ### Specification for json-e # This document specifies the json-e template language through a series of # test cases on the form: `{title, context, template, result, error, panic}` # where `jsone.render(template, context) = result` and title is short name # for the test case. If the `error` property is specified as `true`, no result # needs to be specified, as the test case must throw an error. If the `panic` # property is specified as `true`, the test case is expected to throw an error # about invalid usage, typically invalid context. # # Comments explaining the test cases is encouraged, additionally entries with # {section: '...'} is encouraged to separate sections. # # Test conditions: # 1) Time at test time is expected to be '2017-01-19T16:27:20.974Z' ################################################################################ --- section: Initial Minimal Subset --- title: Identity context: {} template: {key: [1,2,{key2: 'val', key3: 1}, true], f: false} result: {key: [1,2,{key2: 'val', key3: 1}, true], f: false} --- title: fromNow context: {} template: {$fromNow: ''} result: '2017-01-19T16:27:20.974Z' --- title: fromNow 2 days 3 hours context: {} template: {$fromNow: '2 days 3 hours'} result: '2017-01-21T19:27:20.974Z' --- title: $eval without context context: {} template: {$eval: '1 + 2'} result: 3 --- title: $eval context: {key: ['value', 1, 2, true, {}]} template: {$eval: 'key'} result: ['value', 1, 2, true, {}] --- title: $json context: {} template: {$json: [1,2,true,{},[]]} result: '[1,2,true,{},[]]' --- title: $json alphabetical context: {} template: {$json: {'Z':null,'q':1,'a':{'c':45,'b':['9',8,{}]},'r':true}} result: '{"Z":null,"a":{"b":["9",8,{}],"c":45},"q":1,"r":true}' --- title: string interpolation context: {key: 'world', num: 1} template: {message: 'hello ${key}', 'k-${num}': true} result: {message: 'hello world', 'k-1': true} --- title: string interpolation with object evaluation context: {key: 'world', num: 1} template: {message: 'hello ${{a: "world"}.a}', 'k-${{a: 1}.a + {a: 0}.a}': true} result: {message: 'hello world', 'k-1': true} --- title: multiple string interpolation (1) context: {a: 'hello', b: 'world'} template: {message: '${a} ${b}'} result: {message: 'hello world'} --- title: multiple string interpolation (2) context: {a: 'hello', b: 'world'} template: {message: '${a}$$${b}'} result: {message: 'hello$${b}'} --- title: multiple string interpolation (3) context: {a: 'hello', b: 'world'} template: {message: '${a}##${b}'} result: {message: 'hello##world'} --- title: string interpolation escapes context: {} template: {message: 'a literal $${in a string}'} result: {message: 'a literal ${in a string}'} --- title: string interpolation escapes with more $ context: {x: 'a'} template: ['${x}', '$${x}', '$$${x}', '$$$${x}', '$$$$${x}'] result: ['a', '${x}', '$${x}', '$$${x}', '$$$${x}'] --- title: string interpolation of keys context: {name: 'foo', value: 'bar'} template: {"tc_${name}": "${value}"} result: {"tc_foo": "bar"} --- title: string interpolation with unbalanced } context: {} template: {message: 'tricky ${"}}}}"}'} result: {message: 'tricky }}}}'} --- title: can't interpolate arrays context: {key: [1,2,3]} template: {message: 'hello ${key}'} error: "TemplateError at template.message: interpolation of 'key' produced an array or object" --- title: can't interpolate objects context: {key: {}} template: 'hello ${key}' error: "TemplateError: interpolation of 'key' produced an array or object" --- title: booleans interpolate context: {t: true, f: false} template: '${t} or ${f}: yeast is a bacterium' result: 'true or false: yeast is a bacterium' --- title: numbers interpolate context: {round: 3, decimal: 3.75} template: '${round}, really ${decimal}' result: '3, really 3.75' --- title: nulls interpolate context: {nothing: null} template: 'big pile of ${nothing}' result: 'big pile of ' --- title: invalid context context: {'a b c': 1} template: {} error: 'TemplateError: top level keys of context must follow /[a-zA-Z_][a-zA-Z0-9_]*/' --- title: operator nested in an object context: {} template: { key: {$reverse: [1,2,3]}} result: {key: [3,2,1]} ################################################################################ --- section: $if operator --- title: $if-then-else, true context: {cond: true} template: {$if: 'cond', then: 1, else: 2} result: 1 --- title: $if-then-else, false context: {cond: false} template: {$if: 'cond', then: 1, else: 2} result: 2 --- title: $if-then in array, true context: {cond: true} template: [0, {$if: 'cond', then: 1}] result: [0, 1] --- title: $if-then in array, false context: {cond: false} template: [0, {$if: 'cond', then: 1}] result: [0] # missing else branch should return a delete-marker --- title: $if-then in object, true context: {cond: true} template: {key: {$if: 'cond', then: 1}, k2: 3} result: {key: 1, k2: 3} --- title: $if-then in object, false context: {cond: false} template: {key: {$if: 'cond', then: 1}, k2: 3} result: {k2: 3} # missing else branch should return a delete-marker --- title: $if -> delete-marker, true context: {cond: true} template: {key: {$if: 'cond'}, k2: 3} result: {k2: 3} # missing then/else branches should return a delete-marker --- title: $if -> delete-marker, false context: {cond: false} template: {key: {$if: 'cond'}, k2: 3} result: {k2: 3} # missing then/else branches should return a delete-marker --- title: $if->then, then => $eval, true context: {key: {b: 1}} template: {$if: 'true', then: {$eval: 'key'}} result: {b: 1} --- title: $if->else, else => $eval, false context: {key: {b: 1}} template: {$if: 'false', else: {$eval: 'key'}} result: {b: 1} --- title: $if->then, then => ${}, true context: {key: 'one'} template: {$if: 'true', then: '${key}'} result: 'one' --- title: $if->else, else => ${}, false context: {key: 'one'} template: {$if: 'false', else: '${key}'} result: 'one' --- title: $if->then, then => object, true context: {cond: true} template: {$if: 'cond', then: {key: 'hello world'}} result: {key: 'hello world'} --- title: $if->else, else => object, false context: {cond: false} template: {$if: 'cond', else: {key: 'hello world'}} result: {key: 'hello world'} --- title: $if->then, then => object, $eval, true context: {cond: true, key: 'hello world'} template: {$if: 'cond', then: {key: {$eval: 'key'}}} result: {key: 'hello world'} --- title: $if->else, else => object, $eval, false context: {cond: false, key: 'hello world'} template: {$if: 'cond', else: {key: {$eval: 'key'}}} result: {key: 'hello world'} --- title: $if->then, then => object, interpolation, true context: {cond: true, key: 'world'} template: {$if: 'cond', then: {key: 'hello ${key}'}} result: {key: 'hello world'} --- title: $if->else, else => object, interpolation, false context: {cond: false, key: 'world'} template: {$if: 'cond', else: {key: 'hello ${key}'}} result: {key: 'hello world'} --- title: $if->then->else, empty string context: {cond: "", key: 'world'} template: {$if: 'cond', then: "t", else: "f"} result: f --- title: $if->then->else, nonempty string context: {cond: "stuff", key: 'world'} template: {$if: 'cond', then: "t", else: "f"} result: t --- title: $if->then->else, string "0" # once upon a time, this was false in PHP.. maybe still is context: {cond: "0", key: 'world'} template: {$if: 'cond', then: "t", else: "f"} result: t --- title: $if->then->else, zero context: {cond: 0, key: 'world'} template: {$if: 'cond', then: "t", else: "f"} result: f --- title: $if->then->else, one context: {cond: 1, key: 'world'} template: {$if: 'cond', then: "t", else: "f"} result: t --- title: $if->then->else, null context: {cond: null, key: 'world'} template: {$if: 'cond', then: "t", else: "f"} result: f --- title: $if->then->else, empty array context: {cond: [], key: 'world'} template: {$if: 'cond', then: "t", else: "f"} result: f --- title: $if->then->else, nonempty array context: {cond: [1, 2], key: 'world'} template: {$if: 'cond', then: "t", else: "f"} result: t --- title: $if->then->else, empty object context: {cond: {}, key: 'world'} template: {$if: 'cond', then: "t", else: "f"} result: f --- title: $if->then->else, nonempty object context: {cond: {a: 2}, key: 'world'} template: {$if: 'cond', then: "t", else: "f"} result: t --- title: $if->then->else, function context: {} template: {$if: 'min', then: "t", else: "f"} result: t --- title: $if->then evaluating to nothing at the top level is null context: {cond: false} template: {$if: 'cond', then: "t"} result: null --- title: $if-then-else with undefined properties context: {cond: true} template: {$if: 'cond', then: 1, foo: 'bar', else: 2, bing: 'baz'} error: 'TemplateError: $if has undefined properties: bing foo' ################################################################################ --- section: escape operators --- title: escape $eval context: {} template: {$$eval: "1+2"} result: {$eval: "1+2"} --- title: escape $flatten context: {} template: {$$flatten: [[1, 2], [3, 4], [5]]} result: {$flatten: [[1, 2], [3, 4], [5]]} --- title: escape $fromNow context: {} template: {$$fromNow: "2 days"} result: {$fromNow: "2 days"} --- title: escape $if context: {} template: {$$if: false} result: {$if: false} --- title: escape $json context: {} template: {$$json: [1, 2, 3]} result: {$json: [1, 2, 3]} --- title: escape $let context: {} template: {$$let: {ts: 100, foo: 200}, in: [{$$eval: "ts+foo"}]} result: {$let: {ts: 100, foo: 200}, in: [{$eval: "ts+foo"}]} --- title: escape $map context: {} template: {$$map: [2, 4, 6], each(x): {$$eval: "x + a"}} result: {$map: [2, 4, 6], each(x): {$eval: "x + a"}} --- title: escape $match context: {x: 1} template: {$$match: [{'x == 1': 2}]} result: {$match: [{'x == 1': 2}]} --- title: escape $switch context: {x: 1} template: {$$switch: [{'x == 1': 2}]} result: {$switch: [{'x == 1': 2}]} --- title: escape $merge context: {} template: {$$merge: [{a: 1}, {a: 2}]} result: {$merge: [{a: 1}, {a: 2}]} --- title: escape $reverse context: {} template: {$$reverse: [3, 2, 1]} result: {$reverse: [3, 2, 1]} --- title: escape $sort context: {} template: {$$sort: [3, 2, 1]} result: {$sort: [3, 2, 1]} --- title: reserved $-keywords context: {} template: {$nothing: 1} error: 'TemplateError: $ is reserved; use $$' --- title: escape non-json-e operator context: {} template: {$$nothing: 1} result: {$nothing: 1} --- title: two escaped operators context: {} template: {$$map: 1, $$let: 2} result: {$map: 1, $let: 2} ################################################################################ --- section: $flatten --- title: simple flatten context: {} template: {$flatten: [[1, 2], [3, 4], [5]]} result: [1, 2, 3, 4, 5] --- title: flatten empty array context: {} template: {$flatten: []} result: [] --- title: flatten empty array of arrays context: {} template: {$flatten: [[]]} result: [] --- title: flatten evaluated value context: {} template: {$flatten: {$eval: '[[1], [2, 3]]'}} result: [1, 2, 3] --- title: flatten null context: {} template: {$flatten: null} error: 'TemplateError: $flatten value must evaluate to an array' --- title: flatten an object context: {} template: {$flatten: {a: 1, b: 2}} error: 'TemplateError: $flatten value must evaluate to an array' --- title: flatten an array of strings context: {} template: {$flatten: ["a", "b"]} result: ["a", "b"] --- title: flatten an array of numbers context: {} template: {$flatten: [1, 2]} result: [1, 2] --- title: flatten mixed types context: {} template: {$flatten: [[1, 2], [['a', 'b']], [{}], [[]]]} result: [1, 2, ['a', 'b'], {}, []] --- title: flatten mixed levels context: {} template: {$flatten: [[1, [2, 3]], [4], 5]} result: [1, [2, 3], 4, 5] --- title: flatten with undefined properties context: {} template: {$flatten: {$eval: '[[1], [2, 3]]'}, foo: 'bar'} error: 'TemplateError: $flatten has undefined properties: foo' ################################################################################ --- section: $flattenDeep --- title: simple flattenDeep context: {} template: {$flattenDeep: [[1, [2, [3]]]]} result: [1, 2, 3] --- title: flattenDeep empty array context: {} template: {$flattenDeep: []} result: [] --- title: evaluated value context: {} template: {$flattenDeep: {$eval: '[[1], [2, [3]]]'}} result: [1, 2, 3] --- title: flattenDeep empty array of arrays context: {} template: {$flattenDeep: [[]]} result: [] --- title: flattenDeep null context: {} template: {$flattenDeep: null} error: 'TemplateError: $flattenDeep value must evaluate to an array' --- title: flattenDeep an object context: {} template: {$flattenDeep: {a: 1, b: 2}} error: 'TemplateError: $flattenDeep value must evaluate to an array' --- title: flattenDeep an array of strings context: {} template: {$flattenDeep: ["a", "b"]} result: ["a", "b"] --- title: flattenDeep an array of numbers context: {} template: {$flattenDeep: [1, 2]} result: [1, 2] --- title: flattenDeep mixed types context: {} template: {$flattenDeep: [[1, 2], [['a', 'b']], [{}], [[]]]} result: [1, 2, 'a', 'b', {}] --- title: flattenDeep mixed levels context: {} template: {$flattenDeep: [[1, [2, 3]], [4], 5]} result: [1, 2, 3, 4, 5] --- title: flattenDeep with undefined properties context: {} template: {$flattenDeep: [[1, [2, 3]], [4], 5], foo: 'bar'} error: 'TemplateError: $flattenDeep has undefined properties: foo' ################################################################################ --- section: $fromNow --- title: $fromNow 1 years context: {} template: {$fromNow: '1 years'} result: '2018-01-19T16:27:20.974Z' --- title: $fromNow 1 year context: {} template: {$fromNow: '1 year'} result: '2018-01-19T16:27:20.974Z' --- title: $fromNow 1 yr context: {} template: {$fromNow: '1 yr'} result: '2018-01-19T16:27:20.974Z' --- title: $fromNow 1 y context: {} template: {$fromNow: '1 y'} result: '2018-01-19T16:27:20.974Z' --- title: $fromNow 1 months context: {} template: {$fromNow: '1 months'} result: '2017-02-18T16:27:20.974Z' --- title: $fromNow 1 month context: {} template: {$fromNow: '1 month'} result: '2017-02-18T16:27:20.974Z' --- title: $fromNow 30 mo context: {} template: {$fromNow: '30 mo'} result: '2019-07-08T16:27:20.974Z' --- title: $fromNow -30 mo context: {} template: {$fromNow: '-30 mo'} result: '2014-08-03T16:27:20.974Z' --- title: $fromNow 1 weeks context: {} template: {$fromNow: '1 weeks'} result: '2017-01-26T16:27:20.974Z' --- title: $fromNow 1 week context: {} template: {$fromNow: '1 week'} result: '2017-01-26T16:27:20.974Z' --- title: $fromNow 1 wk context: {} template: {$fromNow: '1 wk'} result: '2017-01-26T16:27:20.974Z' --- title: $fromNow 1 w context: {} template: {$fromNow: '1 w'} result: '2017-01-26T16:27:20.974Z' --- title: $fromNow 2 days context: {} template: {$fromNow: '2 days'} result: '2017-01-21T16:27:20.974Z' --- title: $fromNow 2 day context: {} template: {$fromNow: '2 day'} result: '2017-01-21T16:27:20.974Z' --- title: $fromNow 2 d context: {} template: {$fromNow: '2 d'} result: '2017-01-21T16:27:20.974Z' --- title: $fromNow 2 hours context: {} template: {$fromNow: '2 hours'} result: '2017-01-19T18:27:20.974Z' --- title: $fromNow -1 hour context: {} template: {$fromNow: '-1 hour'} result: '2017-01-19T15:27:20.974Z' --- title: $fromNow 1 hour context: {} template: {$fromNow: '1 hour'} result: '2017-01-19T17:27:20.974Z' --- title: $fromNow 3h context: {} template: {$fromNow: '3h'} result: '2017-01-19T19:27:20.974Z' --- title: $fromNow 1 minutes context: {} template: {$fromNow: '1 minutes'} result: '2017-01-19T16:28:20.974Z' --- title: $fromNow 1 m context: {} template: {$fromNow: '1 minute'} result: '2017-01-19T16:28:20.974Z' --- title: $fromNow 1 m context: {} template: {$fromNow: '1 minute'} result: '2017-01-19T16:28:20.974Z' --- title: $fromNow 12 min context: {} template: {$fromNow: '12 min'} result: '2017-01-19T16:39:20.974Z' --- title: $fromNow 12min context: {} template: {$fromNow: '12min'} result: '2017-01-19T16:39:20.974Z' --- title: $fromNow 1m context: {} template: {$fromNow: '1m'} result: '2017-01-19T16:28:20.974Z' --- title: $fromNow 11m context: {} template: {$fromNow: '11m'} result: '2017-01-19T16:38:20.974Z' --- title: $fromNow 11 m context: {} template: {$fromNow: '11 m'} result: '2017-01-19T16:38:20.974Z' --- title: $fromNow 1 seconds context: {} template: {$fromNow: '1 seconds'} result: '2017-01-19T16:27:21.974Z' --- title: $fromNow 1 second context: {} template: {$fromNow: '1 second'} result: '2017-01-19T16:27:21.974Z' --- title: $fromNow 1 sec context: {} template: {$fromNow: '1 sec'} result: '2017-01-19T16:27:21.974Z' --- title: $fromNow 1 s context: {} template: {$fromNow: '1 s'} result: '2017-01-19T16:27:21.974Z' --- title: $fromNow multiple units context: {} template: {$fromNow: '1y2mo3w4d5h6m7s'} result: '2018-04-14T21:33:27.974Z' --- title: $fromNow multiple units with whitespace context: {} template: {$fromNow: '1 y 2 mo 3 w 4 d 5 h 6 m 7 s'} result: '2018-04-14T21:33:27.974Z' --- title: $fromNow multiple units positive context: {} template: {$fromNow: '+1y2mo3w4d5h6m7s'} result: '2018-04-14T21:33:27.974Z' --- title: $fromNow multiple units negative context: {} template: {$fromNow: '-1y2mo3w4d5h6m7s'} result: '2015-10-27T11:21:13.974Z' --- title: $fromNow multiple units out of order context: {} template: {$fromNow: '1s 1 y'} error: true --- title: $fromNow of non-string context: {} template: {$fromNow: 13} error: 'TemplateError: $fromNow expects a string' --- title: $fromNow with eval context: {ttl: 24} template: {$fromNow: {$eval: 'str(ttl) + " hours"'}} result: '2017-01-20T16:27:20.974Z' --- title: $fromNow with redefined `now` context: {} template: {$let: {now: '2019-01-01T01:00:00.123Z'}, in: {$fromNow: '3 mo'}} result: '2019-04-01T01:00:00.123Z' --- title: $fromNow with reference context: {when: '2019-01-01T01:00:00.123Z'} template: {$fromNow: '3 mo', from: {$eval: when}} result: '2019-04-01T01:00:00.123Z' --- title: $fromNow with undefined properties context: {now: '2019-01-01T01:00:00.123Z'} template: {$fromNow: '3 mo', from: {$eval: now}, foo: 'bar'} error: 'TemplateError: $fromNow has undefined properties: foo' ################################################################################ --- section: $json --- title: wrap expressions with $eval context: {a: 1, b: 2} template: {$json: {$eval: 'a + b + 7'}} result: '10' --- title: unwrap expressions not evaluated context: {a: 1, b: 2} template: {$json: 'a + b + 7'} result: '"a + b + 7"' --- title: $eval inside array context: {a: 1, b: 2} template: {$json: [2, 5, {$eval: 'a + b + 7'}]} result: '[2,5,10]' --- title: ASCII non-printable character is u-escaped context: {} template: {$json: ["\x10"]} result: "[\"\\u0010\"]" --- title: ASCII printable character is not escaped context: {} template: {$json: ["Z"]} result: "[\"Z\"]" --- title: Unicode BMP character is not escaped context: {} template: {$json: ["ă"]} result: "[\"ă\"]" --- title: sorting pairs by unicode key strings sorts lexically by codepoint context: {} # Note that these `\u` escapes are interpreted into characters by YAML, not by JSON-e template: {$json: {"\u3347\u0050": 1, "\uE1FF\u0100": 2, "\u0055\u0045": 3}} result: "{\"\u0055\u0045\":3,\"\u3347\u0050\":1,\"\uE1FF\u0100\":2}" --- title: sorting pairs by unicode key strings sorts lexically by codepoint, even with chars above base plane context: {} template: {$json: {"\U0001F809\u732b":'hey', "Z\U00010000P": 1, "\U000E1FFF\u0103": 2, "UaE": 3}} result: "{\"UaE\":3,\"Z\U00010000P\":1,\"\U0001F809\u732b\":\"hey\",\"\U000E1FFF\u0103\":2}" --- title: $json with undefined properties context: {} template: {$json: [1,2,true,{},[]], foo: 'bar'} error: 'TemplateError: $json has undefined properties: foo' --- title: $json can be evaluated to null without an error context: {input: null} template: {$json: { '$eval': 'input' } } result: 'null' ################################################################################ --- section: accessing nested objects => context --- title: $eval context: {key: {key2: ['value', 1, 2, true, {}]}} template: {$eval: 'key.key2'} result: ['value', 1, 2, true, {}] --- title: $json context: {key: {key2: [1,2,true,{},[]]}} template: {$json: {$eval: 'key.key2'}} result: '[1,2,true,{},[]]' ################################################################################ --- section: string interpolation --- title: string interpolation context: {key: {key2: 'world', key3: 1}} template: {message: 'hello ${key.key2}', 'k-${key.key3}': true} result: {message: 'hello world', 'k-1': true} --- title: string interpolation, first character context: {somevalue: 13} template: {message: '${somevalue}'} result: {message: '13'} --- title: no string interpolation with $$ context: {somevalue: 13} template: {message: '$${somevalue}'} result: {message: '${somevalue}'} --- title: unterminated string interpolation context: {} template: {message: '${somevalue'} error: "SyntaxError at template.message: unterminated ${..} expression" ################################################################################ --- section: $let --- title: simple let template: {$let: {ts: 100, foo: 200,}, in: [{$eval: "ts+foo"}, {$eval: "ts-foo"}, {$eval: "ts*foo"}]} context: {} result: [300, -100, 20000] --- title: let with evaluated values template: {$let: {thirty: {$eval: "10 + 20"}}, in: {$eval: "thirty+5"}} context: {} result: 35 --- title: let with evaluated context template: {$let: {$eval: '{"thirty": 10 + 20}'}, in: {$eval: "thirty+5"}} context: {} result: 35 --- title: context overriding template: {$let: {x: 20}, in: {$eval: "x + y"}} context: {x: 10, 'y': 1} result: 21 --- title: nested let template: {$let: {x: 20,'y': 10}, in: {$let: {x: 30}, in: {$eval: "x + y + z"}}} context: {x: 1,'y': 2, z: 3} result: 43 --- title: let with undefined properties template: {$let: {x: 1,'y': 2}, a: {$eval: "x + y"}} context: {} error: 'TemplateError: $let has undefined properties: a' --- title: let without in template: {$let: {x: 1,'y': 2}} context: {} error: 'TemplateError: $let operator requires an `in` clause' --- title: let array template: {$let: [1, 2], in: {$eval: "1 + 2"}} context: {} error: 'TemplateError: $let value must be an object' --- title: let null template: {$let: null, in: {$eval: "1 + 2"}} context: {} error: 'TemplateError: $let value must be an object' --- title: checking name begin with number context: {} template: {$let: {ab: 100, bb: 200, 2b: 5}, in: [{$eval: "ab+bb"}, {$eval: "ab-bb"}, {$eval: "ab*bb"}]} error: 'TemplateError: top level keys of $let must follow /[a-zA-Z_][a-zA-Z0-9_]*/' --- title: checking space in variable name context: {} template: {$let: {ab: 100, bb: 200, "a b": 10}, in: [{$eval: "ab+bb"}, {$eval: "ab-bb"}, {$eval: "ab*bb"}]} error: 'TemplateError: top level keys of $let must follow /[a-zA-Z_][a-zA-Z0-9_]*/' --- title: checking special character in variable name context: {} template: {$let: {ab: 100, bb: 200, "a.b": 10}, in: [{$eval: "ab+bb"}, {$eval: "ab-bb"}, {$eval: "ab*bb"}]} error: 'TemplateError: top level keys of $let must follow /[a-zA-Z_][a-zA-Z0-9_]*/' --- title: checking empty name context: {} template: {$let: {"": 10}, in: []} error: 'TemplateError: top level keys of $let must follow /[a-zA-Z_][a-zA-Z0-9_]*/' --- title: checking integrity of operation $let with exact result context: {} template: {$let: {ts: 100, foo: 200}, in: [{$eval: "ts+foo"}, {$eval: "ts-foo"}, {$eval: "ts*foo"}]} result: [300, -100, 20000] --- title: checking $let and $reverse operator integrity context: {} template: {$let: {x: [1, 2, 3]}, in: [$reverse: {$eval: "x"}]} result: [[ 3, 2, 1 ]] --- title: checking $let with $if without else context: {c: 'itm'} template: {$let: {a: {$if: 'c == "item"', then: 'value'}}, in: {a: '${a}'}} error: 'InterpreterError at template.a: unknown context value a' --- title: let with a value of $if-then-else template: {$let: {$if: something == 3, then: {a: 10, b: 20}, else: {a: 20, b: 10}}, in: {$eval: 'a + b'}} context: {'something': 3} result: 30 --- title: let with a rendered key template: {$let: {"first_${name}": 1, "second_${name}": 2}, in: {$eval: "first_prize + second_prize"}} context: {name: "prize"} result: 3 --- title: let using values from the parent context template: {$let: {"b": {$eval: "a + 10"}}, in: {$eval: "a + b"}} context: {a: 5} result: 20 --- title: let with a non-object rendered value (array) template: {$let: {$eval: "[1, 2]"}, in: "uhoh"} context: {} error: 'TemplateError: $let value must be an object' --- title: let with a non-object rendered value (string) template: {$let: "${name}", in: "uhoh"} context: {name: "fido"} error: 'TemplateError: $let value must be an object' --- title: let with a non-object rendered value (boolean) template: {$let: {$eval: "true"}, in: "uhoh"} context: {} error: 'TemplateError: $let value must be an object' --- title: fromNow with redefined `now` context: {} template: {$let: {now: '2017-01-01T01:00:00.123Z'}, in: {$eval: "fromNow('1 day')"}} result: '2017-01-02T01:00:00.123Z' --- title: defined with empty variable from a let expression context: {} template: {$let: {var: 10}, in: {$if: "defined('var')", then: 't', else: 'f'}} result: t --- title: $let with nested redefinitions of builtins context: {} template: $let: {lowercase: {$eval: uppercase}} in: $let: {var: 10} in: $if: "defined('var')" then: "${lowercase('t')}" # lowercase should be redefined as uppercase else: "${lowercase('f')}" result: T --- title: $let with nested redefinitions of builtins context: {} template: $let: {fromNow: {$eval: uppercase}} in: $let: {var: 10} in: $if: "defined('var')" then: "${fromNow('1 hour')}" else: "f" result: '1 HOUR' --- title: $let doesn't leak variables context: {} template: - $let: foo: 'bar' baz: 'qux' in: {$eval: 'foo'} - $eval: 'foo' error: 'InterpreterError at template[1]: unknown context value foo' ################################################################################ --- section: $map --- title: simple map context: {a: 1} template: $map: [2, 4, 6] each(x): {$eval: 'x + a'} result: [3, 5, 7] --- title: map to objects context: {a: 1} template: $map: [2, 4, 6] each(x): asText: '${x + a}' integer: {$eval: 'x + a'} result: - {asText: '3', integer: 3} - {asText: '5', integer: 5} - {asText: '7', integer: 7} --- title: complex identifier context: {a: 1} template: $map: [2, 4, 6] each(my_identifier97): {$eval: 'my_identifier97 + a'} result: [3, 5, 7] --- title: must wrap expressions with $eval context: {a: 1} template: $map: [2, 4, 6] each(my_identifier97): 'my_identifier97 + a' result: ['my_identifier97 + a', 'my_identifier97 + a', 'my_identifier97 + a'] --- title: can take from objects context: {a: 1} template: $map: [{k: 1}, {k: 2}, {k: 3}] each(y): {$eval: 'y.k'} result: [1, 2, 3] --- title: value is rendered context: {a: 1} template: $map: {$eval: '[{k: 1}, {k: 2}, {k: 3}]'} each(y): {$eval: 'y.k'} result: [1, 2, 3] --- title: can make objects too context: {a: 1} template: $map: [{k: 1}, {k: 2}, {k: 3}] each(y): k: {$eval: 'y.k + 1'} v: 'before=${y.k}' result: - k: 2 v: 'before=1' - k: 3 v: 'before=2' - k: 4 v: 'before=3' --- title: respects delete-marker from $if context: {a: 1} template: $map: [{k: 1}, {k: 2}, {k: 3}] each(y): $if: 'y.k != 2' then: {$eval: 'y.k'} result: [1, 3] --- title: $map works on object context: {} template: $map: {a: 1, b: 2, c: 3} each(y): {'${y.key}x': {$eval: 'y.val + 1'}} result: {ax: 2, bx: 3, cx: 4} --- title: $map each(value,key) on object context: {} template: $map: {a: 1, b: 2, c: 3} each(v,k): {'${k}x': {$eval: 'v + 1'}} result: {ax: 2, bx: 3, cx: 4} --- title: $map can add new keys to object context: {} template: $map: {a: 1, b: 2, c: 3} each(y): {'${y.key}': {$eval: 'y.val'}, '${y.key}x': {$eval: 'y.val + 1'}} result: {a: 1, b: 2, c: 3, ax: 2, bx: 3, cx: 4} --- title: $map array index variable context: {} template: $map: ['a', 'b', 'c'] each(y,i): '${y}-${i}' result: ['a-0','b-1','c-2'] --- title: $map no args context: {} template: $map: "a, b, c" each(): {$eval: 'y.k'} error: 'TemplateError: $map has undefined properties: each()' --- title: $map too many args context: {} template: $map: "a, b, c" each(k,v,i): {$eval: 'y.k'} error: 'TemplateError: $map has undefined properties: each(k,v,i)' --- title: $map requires an array, not string context: {} template: $map: "a, b, c" each(y): {$eval: 'y.k'} error: 'TemplateError: $map value must evaluate to an array or object' --- title: $map requires an array, not number context: {} template: $map: 10.1 each(y): {$eval: 'y.k'} error: 'TemplateError: $map value must evaluate to an array or object' --- title: $map requires an array, not null context: {} template: $map: null each(y): {$eval: 'y.k'} error: 'TemplateError: $map value must evaluate to an array or object' --- title: map with undefined properties context: {a: 1} template: $map: [2, 4, 6] each(x): {$eval: 'x + a'} foo: 'bar' error: 'TemplateError: $map has undefined properties: foo' --- title: $map as object and each(identifier) as array context: {} template: $map: {"hello": 5, "test": 9} each(x): ['${x.key}', {$eval: 'x.val+1'}] error: 'TemplateError: $map on objects expects each(x) to evaluate to an object' --- title: $map over object, two vars context: {} template: { $map: {a: 1, b: 2, c: 3}, 'each(v,k)': {'${k}x': {$eval: 'v + 1'}} } result: {ax: 2, bx: 3, cx: 4} --- title: $map over object, one var context: {} template: {$map: {a: 1, b: 2, c: 3}, 'each(y)': {'${y.key}x': {$eval: 'y.val + 1'}}} result: {ax: 2, bx: 3, cx: 4} ################################################################################ --- section: range --- title: range without step creates list of numbers context: {} template: $map: {$eval: 'range(1, 5)'} each(x): {$eval: 'x'} result: [1, 2, 3, 4] --- title: range with end > start creates empty list context: {} template: {$eval: 'len(range(5, 1))'} result: 0 --- title: range with step creates list of numbers context: {} template: $map: {$eval: 'range(1, 10, 2)'} each(x): {$eval: 'x'} result: [1, 3, 5, 7, 9] --- title: range with negative step creates list of numbers context: {} template: $map: {$eval: 'range(1, -2, -1)'} each(x): {$eval: 'x'} result: [1, 0, -1] --- title: range with negative step and negative start and end creates list of numbers context: {} template: $map: {$eval: 'range(-7, -10, -1)'} each(x): {$eval: 'x'} result: [-7, -8, -9] --- title: range with negative step that does not divide evenly context: {} template: $map: {$eval: 'range(20, 0, -7)'} each(x): {$eval: 'x'} result: [20, 13, 6] --- title: range with step=0 throws error context: {} template: $map: {$eval: 'range(1, 5, 0)'} each(x): {$eval: 'x'} error: true --- title: range with float step throws error context: {} template: $map: {$eval: 'range(1, 5, 1.2)'} each(x): {$eval: 'x'} error: true --- title: range with float start throws error context: {} template: $map: {$eval: 'range(1.3, 5)'} each(x): {$eval: 'x'} error: true --- title: range with string start throws error context: {} template: $map: {$eval: 'range("1", 5)'} each(x): {$eval: 'x'} error: true --- title: range without step creates list of objects context: {} template: $map: {$eval: 'range(1, 4)'} each(x): asText: '${x}' integer: {$eval: 'x'} result: - {asText: '1', integer: 1} - {asText: '2', integer: 2} - {asText: '3', integer: 3} --- title: range without step creates list of strings context: {} template: $map: {$eval: 'range(10, 12)'} each(x,i): 'id_${x}_${i}' result: ['id_10_0', 'id_11_1'] --- title: range with step creates list of strings context: {} template: $map: {$eval: 'range(10, 20, 5)'} each(x,i): 'id_${x}_${i}' result: ['id_10_0', 'id_15_1'] --- title: range without step creates objects context: {} template: $map: {$eval: 'range(1, 3)'} each(y): k: {$eval: 'y + 1'} v: 'before=${y}' result: - k: 2 v: 'before=1' - k: 3 v: 'before=2' ################################################################################ --- section: $reduce operator --- title: reduce array into number context: {} template: $reduce: [2, 4, 6] each(acc, x): {$eval: 'acc + x'} initial: 0 result: 12 --- title: reduce array with indexes into number context: {} template: $reduce: [2, 4, 6] each(acc, x, i): {$eval: 'acc + x * 10 ** i'} initial: 0 result: 642 --- title: reduce array into string context: {} template: $reduce: [2, 4, 6] each(acc, x): "${acc}${x}" initial: 'pre ' result: 'pre 246' --- title: reduce array into boolean context: {} template: $reduce: [2, 4, 6] each(acc, x): {$eval: 'acc || x > 5'} initial: false result: true --- title: reduce array into object # This is a contrived example since using $merge and $map would be more appropriate. # But it demonstrates that the accumulator can be an object context: {} template: $reduce: [2, 4, 6] each(acc, x, i): $merge: [{$eval: 'acc'}, {'k${i}': {$eval: 'x'}}] initial: {a: 0} result: { a: 0, k0: 2, k1: 4, k2: 6 } --- title: reduce over an object context: {} template: $reduce: {a: 2, b: 4, c: 6} each(acc, v, k): {$eval: 'acc + v'} initial: 0 error: 'TemplateError: $reduce value must evaluate to an array' --- title: reduce skips delete-marker from $if context: {} template: $reduce: [2, 4, 6] initial: 0 each(acc, v, i): $if: 'v != 4' then: {$eval: 'acc + v'} result: 8 --- title: reduce with undefined properties context: {} template: $reduce: [2, 4, 6] each(acc, x): {$eval: 'acc + x'} initial: 0 foo: 'bar' error: 'TemplateError: $reduce has undefined properties: foo' --- title: reduce with missing initial context: {} template: $reduce: [2, 4, 6] each(acc, x): {$eval: 'acc + x'} error: 'TemplateError: $reduce must have exactly three properties' --- title: reduce with missing initial with undefined properties context: {} template: $reduce: [2, 4, 6] each(acc, x): {$eval: 'acc + x'} init: 9 error: 'TemplateError: $reduce has undefined properties: init' --- title: reduce with missing each context: {} template: $reduce: [2, 4, 6] initial: 0 error: 'TemplateError: $reduce must have exactly three properties' --- title: reduce with missing each with undefined properties context: {} template: $reduce: [2, 4, 6] each: 'acc + x' initial: 0 error: 'TemplateError: $reduce has undefined properties: each' --- title: reduce over a number context: {} template: $reduce: 10 each(acc, x): {$eval: 'acc + x'} initial: 0 error: 'TemplateError: $reduce value must evaluate to an array' --- title: reduce over a string context: {} template: $reduce: A Thing that is a String each(acc, x): {$eval: 'acc + x'} initial: 0 error: 'TemplateError: $reduce value must evaluate to an array' --- ################################################################################ section: $find operator --- title: $find, simple find context: {} template: {$find: [2, 4, 6], each(x): 'x == 2' } result: 2 --- title: $find, simple find with context context: {a: 2} template: {$find: [2, 4, 6], each(x): 'x == a' } result: 2 --- title: $find respects delete marker context: {} template: {a: 1, b: {$find: [1,2,3], 'each(x)': 'x > 4' }} result: {a: 1} --- title: $find, complicated find with $let context: {} template: $let: needle: $find: [1, 2, 3] each(entry): entry == 2 in: $eval: needle result: 2 --- title: $find complex identifier context: {a: 1} template: $find: [2, 4, 6] each(my_identifier97): 'my_identifier97 == 1 || my_identifier97 == 4' result: 4 --- title: $find array index variable context: {} template: $find: [1, 2, 3] each(y,i): 'i == 2' result: 3 --- title: $find no args context: {} template: $find: [1, 2, 3] each(): 'y.k' error: 'TemplateError: $find has undefined properties: each()' --- title: $find too many args context: {} template: $find: [1, 2, 3] each(k,v,i): 'y.k' error: 'TemplateError: $find has undefined properties: each(k,v,i)' --- title: $find requires an array, not string context: {} template: $find: "a, b, c" each(y): 'y.k' error: 'TemplateError: $find value must evaluate to an array' --- title: $find requires an array, not number context: {} template: $find: 10.1 each(y): 'y.k' error: 'TemplateError: $find value must evaluate to an array' --- title: $find requires an array, not null context: {} template: $find: null each(y): 'y.k' error: 'TemplateError: $find value must evaluate to an array' --- title: $find requires an array, not object context: {} template: $find: {a: 1} each(y): 'y.k' error: 'TemplateError: $find value must evaluate to an array' --- title: $find with undefined properties context: {a: 1} template: $find: [2, 4, 6] each(x): 'x + a' foo: 'bar' error: 'TemplateError: $find has undefined properties: foo' --- title: $find each must be string, not number context: {} template: $find: [1,2,3] each(a): 1 error: 'TemplateError: each can evaluate string expressions only' --- title: $find each must be string, not object context: {} template: $find: [1,2,3] each(a): {a: 1} error: 'TemplateError: each can evaluate string expressions only' ################################################################################ --- section: $match operator --- title: $match, 2 matches, ordered by lexcial sorting of property names context: {cond: 3} template: {$match: {'cond == 3': 2, 'cond == 5': 3, 'cond < 5 && cond > 0': 4, 'true': 17}} result: [4, 2, 17] --- title: $match, no matches context: {cond: 3} template: {$match: {'cond > 5': 2, 'cond == 5': 3, 'cond == 4 || cond < 1': 4}} result: [] --- title: $match, 1 match in array context: {cond: 3} template: [0, $match: {'cond == 3': 2, 'cond == 5': 3}] result: [0, [2]] --- title: $match, 0 matches in array context: {cond: 3} template: [0, $match: {'cond > 3': 2, 'cond == 5': 3}] result: [0, []] --- title: $match, 1 match in object context: {cond: 3} template: {key: 0, banana: {$match: {'cond == 3': 2, 'cond == 5': 3}}} result: {key: 0, banana: [2]} --- title: $match, 0 matches in object context: {cond: 3} template: {key: 0, banana: {$match: {'cond > 3': 2, 'cond == 5': 3}}} result: {key: 0, banana: []} --- title: $match, is not an object context: {cond: 3} template: {$match: [{'cond < 5 && cond > 0': 2}]} error: 'TemplateError: $match can evaluate objects only' --- title: $match, value that also needs evaluation context: {cond: 3, ifcond: false} template: {$match: {'cond == 3': {$if: 'ifcond', then: "t", else: "f"}}} result: ["f"] ################################################################################ --- section: $switch operator --- title: $switch, 1 match context: {cond: 3} template: {$switch: {'cond == 3': 2, 'cond == 5': 3}} result: 2 --- title: $switch, 2 matches context: {cond: 3} template: {$switch: {'cond == 3': 2, 'cond > 1': 3}} error: 'TemplateError: $switch can only have one truthy condition' --- title: $switch, no matches context: {cond: 3} template: {$switch: {'cond > 5': 2, 'cond == 5': 3, 'cond == 4 || cond < 1': 4}} result: null --- title: $switch, 1 match in array context: {cond: 3} template: [0, $switch: {'cond == 3': 2, 'cond == 5': 3}] result: [0, 2] --- title: $switch, 0 matches in array context: {cond: 3} template: [0, $switch: {'cond > 3': 2, 'cond == 5': 3}] result: [0] --- title: $switch, 1 match in object context: {cond: 3} template: {key: 0, banana: {$switch: {'cond == 3': 2, 'cond == 5': 3}}} result: {key: 0, banana: 2} --- title: $switch, 0 matches in object context: {cond: 3} template: {key: 0, banana: {$switch: {'cond > 3': 2, 'cond == 5': 3}}} result: {key: 0} --- title: $switch, is not an object context: {cond: 3} template: {$switch: [{'cond < 5 && cond > 0': 2}]} error: 'TemplateError: $switch can evaluate objects only' --- title: $switch, value that also needs evaluation context: {cond: 3, ifcond: false} template: {$switch: {'cond == 3': {$if: 'ifcond', then: "t", else: "f"}}} result: "f" --- title: $switch, fallback to $default context: {cond: 4} template: {$switch: {'cond == 3': 1, $default: 'fallback'}} result: "fallback" --- title: $switch, $default value that also needs evaluation context: {cond: 4, ifcond: false} template: {$switch: {'cond == 3': 1, $default: {$if: 'ifcond', then: "t", else: "f"}}} result: "f" --- title: $switch, $default with 1 match in object context: {cond: 3} template: {$switch: {'cond == 3': 1, $default: 'fallback'}} result: 1 ############################################################################### --- section: $merge --- title: simple merge context: {} template: {$merge: [{a: 1, b: 1}, {b: 2, c: 3}, {d: 4}]} result: {a: 1, b: 2, c: 3, d: 4} --- title: value is evaluated context: {defaults: {d: 4}} template: {$merge: [{a: 1, b: 1}, {b: 2, c: 3}, {$eval: defaults}]} result: {a: 1, b: 2, c: 3, d: 4} --- title: merge empty array context: {} template: {$merge: []} result: {} --- title: merge empty array with empty object context: {} template: {$merge: [{}]} result: {} --- title: merge null context: {} template: {$merge: null} error: 'TemplateError: $merge value must evaluate to an array of objects' --- title: $merge [null] template: {$merge: [null]} context: {} error: 'TemplateError: $merge value must evaluate to an array of objects' --- title: merge with undefined properties template: {$merge: [{a: 1}, {b: 2, c: 3}, {d: 4}], foo: "bar", bing: "baz"} context: {} error: 'TemplateError: $merge has undefined properties: bing foo' ################################################################################ --- section: $mergeDeep --- title: simple non-deep mergeDeep matches merge context: {} template: {$mergeDeep: [{a: 1, b: 1}, {b: 2, c: 3}, {d: 4}]} result: {a: 1, b: 2, c: 3, d: 4} --- title: deep merge of lists appends context: {} template: {$mergeDeep: [{a: [1, 2]}, {a: [3, 4]}]} result: {a: [1, 2, 3, 4]} --- title: deep merge of objects merges context: {} template: {$mergeDeep: [{a: {x: 1,'y': 2}}, {a: {'y': 3, z: 4}}]} result: {a: {x: 1,'y': 3, z: 4}} --- title: very deep merge context: {} template: $mergeDeep: - task: payload: command: [a, b] - task: extra: foo: bar - task: payload: command: [c] result: task: extra: foo: bar payload: command: [a, b, c] --- title: value is evaluated context: {defaults: {d: 4}} template: {$mergeDeep: [{a: 1, b: 1}, {b: 2, c: 3}, {$eval: defaults}]} result: {a: 1, b: 2, c: 3, d: 4} --- title: mergeDeep empty array context: {} template: {$mergeDeep: []} result: {} --- title: mergeDeep empty array with empty object context: {} template: {$mergeDeep: [{}]} result: {} --- title: mergeDeep null context: {} template: {$mergeDeep: null} error: 'TemplateError: $mergeDeep value must evaluate to an array of objects' --- title: $mergeDeep [null] template: {$mergeDeep: [null]} context: {} error: 'TemplateError: $mergeDeep value must evaluate to an array of objects' --- title: mergeDeep with undefined properties template: {$mergeDeep: [{a: {x: 1,'y': 2}}, {a: {'y': 3, z: 4}}], foo: "bar", bing: "baz"} context: {} error: 'TemplateError: $mergeDeep has undefined properties: bing foo' ################################################################################ --- section: $sort --- title: simple sort context: {} template: {$sort: [3, 4, 1, 2]} result: [1, 2, 3, 4] --- title: simple sort of strings (shortest first) context: {} template: {$sort: ['android', 'aardvark', 'add', 'adderall']} result: ['aardvark', 'add', 'adderall', 'android'] --- title: simple sort of multi-digit numbers context: {} template: {$sort: [111, 22, 3]} result: [3, 22, 111] --- title: sort of mixed array context: {} template: {$sort: [1, "2", 2, 3, "111", "112"]} error: true --- title: simple sort with $eval context: {array: [3, 4, 1, 2]} template: {$sort: {$eval: 'array'}} result: [1, 2, 3, 4] --- title: sort by, returning number context: {} template: $sort: [{a: 20}, {a: 10, b: []}, {a: 3}] by(x): 'x.a' result: [{a: 3}, {a: 10, b: []}, {a: 20}] --- title: sort by, returning string context: {} template: $sort: [{s: 'android'}, {s: 'add'}, {s: 'adderall'}] by(x): 'x.s' result: [{s: 'add'}, {s: 'adderall'}, {s: 'android'}] --- title: cannot sort objects without by context: {} template: $sort: [{a: 2}, {a: 1, b: []}, {a: 3}] error: 'TemplateError: $sorted values to be sorted must have the same type' --- title: by cannot return objects context: {} template: $sort: [{a: 2}, {a: 1, b: []}, {a: 3}] by(x): x error: 'TemplateError: $sorted values to be sorted must have the same type' --- title: cannot sort arrays without by context: {} template: $sort: [[1,2,3], [4,5], [], [8,9,10]] error: 'TemplateError: $sorted values to be sorted must have the same type' --- title: by cannot return arrays context: {} template: $sort: [[1,2,3], [4,5], [], [8,9,10]] by(x): x error: 'TemplateError: $sorted values to be sorted must have the same type' --- title: cannot sort nulls context: {} template: {$sort: [null, null]} error: 'TemplateError: $sorted values to be sorted must have the same type' --- title: cannot sort nulls, even with by context: {} template: {$sort: [null, null], 'by(x)': 'x'} error: 'TemplateError: $sorted values to be sorted must have the same type' --- title: cannot sort booleans context: {} template: {$sort: [false, true]} error: 'TemplateError: $sorted values to be sorted must have the same type' --- title: cannot sort booleans, even with by context: {} template: {$sort: [false, true], 'by(x)': 'x'} error: 'TemplateError: $sorted values to be sorted must have the same type' --- title: cannot sort numbers and strings together context: {} template: {$sort: [1, 'a']} error: 'TemplateError: $sorted values to be sorted must have the same type' --- title: cannot sort numbers and strings together even with by context: {} template: {$sort: [1, 'a'], 'by(x)': 'x'} error: 'TemplateError: $sorted values to be sorted must have the same type' --- title: sort requires an array (string) context: {} template: $sort: "some string" error: 'TemplateError: $sorted values to be sorted must have the same type' --- title: sort requires an array (number) context: {} template: $sort: 34 error: 'TemplateError: $sorted values to be sorted must have the same type' --- title: sort requires an array (object) context: {} template: $sort: {k: 1, b: 4, x: 8} error: 'TemplateError: $sorted values to be sorted must have the same type' --- title: sort requires an array (null) context: {} template: $sort: null error: 'TemplateError: $sorted values to be sorted must have the same type' --- title: sort with undefined properties context: {} template: $sort: [{s: 'android'}, {s: 'add'}, {s: 'adderall'}] by(x): 'x.s' foo: 'bar' error: 'TemplateError: $sort has undefined properties: foo' --- title: $sort variable not used (in context) context: { foo: 1 } template: { $sort: [2,1,3], by(x): foo} result: [2,1,3] --- title: $sort variable not used (not in context) context: {} template: { $sort: [2,1,3], by(x): foo} error: 'InterpreterError: unknown context value foo' --- title: $sort is stable template: $sort: ["aa", "dd", "ac", "ba", "ab"] by(x): 'x[0]' context: {} result: ["aa", "ac", "ab", "ba", "dd"] --- title: $sort of very large integers template: $sort: [1111111111111111111111, 22222222222222222222, 3333333, 444] context: {} result: [444, 3333333, 22222222222222222222, 1111111111111111111111] ################################################################################ --- section: $reverse --- title: simple reverse context: {} template: {$reverse: [3, 4, 1, 2]} result: [2, 1, 4, 3] --- title: value is rendered context: {} template: {$reverse: {$eval: '[99, 999, 9]'}} result: [9, 999, 99] --- title: reverse of an object context: {} template: {$reverse: {a: 1}} error: 'TemplateError: $reverse value must evaluate to an array of objects' --- title: reverse of null context: {} template: {$reverse: null} error: 'TemplateError: $reverse value must evaluate to an array of objects' --- title: reverse of a string context: {} template: {$reverse: 'foo'} error: 'TemplateError: $reverse value must evaluate to an array of objects' --- title: reverse of a number context: {} template: {$reverse: 1} error: 'TemplateError: $reverse value must evaluate to an array of objects' --- title: reverse of a boolean context: {} template: {$reverse: true} error: 'TemplateError: $reverse value must evaluate to an array of objects' --- title: simple reverse with $eval context: {key: [3, 4, 1, 2]} template: {$reverse: {$eval: 'key'}} result: [2, 1, 4, 3] --- title: simple reverse + $sort # with this we can sort in reverse order context: {} template: {$reverse: {$sort: [3, 4, 1, 2]}} result: [4, 3, 2, 1] --- title: reverse with undefined properties template: {$reverse: [3, 4, 1, 2], foo: "bar", bing: "baz"} context: {} error: 'TemplateError: $reverse has undefined properties: bing foo' ################################################################################ --- section: $eval --- title: $eval of $eval context: {} template: {$eval: {$eval: '"13"'}} error: 'TemplateError: $eval must be given a string expression' --- title: $eval with trailing characters context: {} template: {$eval: '"13" 13'} error: 'SyntaxError: Found: 13 token, expected one of: !=, &&, (, *, **, +, -, ., /, <, <=, ==, >, >=, [, in, ||' --- title: $eval with undefined properties context: {} template: {$eval: '13', wrong: 'a + b'} error: 'TemplateError: $eval has undefined properties: wrong' --- title: $eval of an array context: {key1: 1, key2: 2} template: {$eval: ['key1', 'key2']} error: 'TemplateError: $eval must be given a string expression' --- title: $eval of an object context: {key1: 1, key2: 2} template: {$eval: {k: 'key1'}} error: 'TemplateError: $eval must be given a string expression' --- title: $eval of null context: {} template: {$eval: null} error: 'TemplateError: $eval must be given a string expression' --- title: $eval of a number context: {} template: {$eval: 11} error: 'TemplateError: $eval must be given a string expression' --- title: $eval of a boolean context: {} template: {$eval: true} error: 'TemplateError: $eval must be given a string expression' ################################################################################ --- section: builtins --- title: min (1) context: {key1: 1, key2: 2} template: {$eval: 'min(key1, key2)'} result: 1 --- title: min (2) context: {key1: 1, key2: 2} template: {$eval: 'min(1, key2)'} result: 1 --- title: min (3) context: {key1: 1, key2: 2} template: {$eval: 'min(key1, 2)'} result: 1 --- title: min (4) context: {key1: 1, key2: 2} template: {$eval: 'min(1, 2)'} result: 1 --- title: min (5) context: {key1: 1, key2: 2} template: {$eval: 'min(2, 1)'} result: 1 --- title: min (6) context: {key1: 1, key2: 2} template: {$eval: 'min(2, 1, 3, 4, 5)'} result: 1 --- title: min - TypeError context: {} template: {$eval: 'min("11")'} error: 'BuiltinError: invalid arguments to builtin: min' --- title: min (7) context: {} template: {$eval: 'min()'} error: 'BuiltinError: invalid arguments to builtin: min: expected at least 1 arguments' --- title: max (1) context: {key1: 1, key2: 2} template: {$eval: 'max(key1, key2)'} result: 2 --- title: max (2) context: {key1: 1, key2: 2} template: {$eval: 'max(1, key2)'} result: 2 --- title: max (3) context: {key1: 1, key2: 2} template: {$eval: 'max(key1, 2)'} result: 2 --- title: max (4) context: {key1: 1, key2: 2} template: {$eval: 'max(1, 2)'} result: 2 --- title: max (5) context: {key1: 1, key2: 2} template: {$eval: 'max(2, 1)'} result: 2 --- title: max (6) context: {key1: 1, key2: 2} template: {$eval: 'max(2, 1, 3, 4, 5)'} result: 5 --- title: max - TypeError context: {} template: {$eval: 'max("12")'} error: 'BuiltinError: invalid arguments to builtin: max' --- title: max (7) context: {} template: {$eval: 'max()'} error: 'BuiltinError: invalid arguments to builtin: max: expected at least 1 arguments' --- title: sqrt (1) context: {key: 4} template: {$eval: 'sqrt(key)'} result: 2 --- title: sqrt (2) context: {} template: {$eval: 'sqrt(9)'} result: 3 --- title: sqrt - TypeError context: {} template: {$eval: 'sqrt("nine")'} error: 'BuiltinError: invalid arguments to builtin: sqrt' --- title: ceil (1) context: {key: 1.1} template: {$eval: 'ceil(key)'} result: 2 --- title: ceil (2) context: {} template: {$eval: 'ceil(2.003)'} result: 3 --- title: ceil (3) context: {key: 1} template: {$eval: 'ceil(key)'} result: 1 --- title: ceil (4) context: {key: 1.0} template: {$eval: 'ceil(key)'} result: 1 --- title: ceil - TypeError context: {} template: {$eval: 'ceil(true)'} error: 'BuiltinError: invalid arguments to builtin: ceil' --- title: floor (1) context: {key: 1.1} template: {$eval: 'floor(key)'} result: 1 --- title: floor (2) context: {} template: {$eval: 'floor(2.003)'} result: 2 --- title: floor (3) context: {key: 1} template: {$eval: 'floor(key)'} result: 1 --- title: floor (4) context: {key: 1.0} template: {$eval: 'floor(key)'} result: 1 --- title: floor - TypeError context: {key: null} template: {$eval: 'floor(key)'} error: 'BuiltinError: invalid arguments to builtin: floor' --- title: abs (1) context: {key: -1.1} template: {$eval: 'abs(key)'} result: 1.1 --- title: abs (2) context: {} template: {$eval: 'abs(-1.1)'} result: 1.1 --- title: abs (3) context: {key: -1} template: {$eval: 'abs(key)'} result: 1 --- title: abs (4) context: {key: -1.0} template: {$eval: 'abs(key)'} result: 1 --- title: abs (5) context: {} template: {$eval: 'abs(-1)'} result: 1 --- title: abs (6) context: {} template: {$eval: 'abs(-1.0)'} result: 1 --- title: abs (7) context: {key: 1.1} template: {$eval: 'abs(key)'} result: 1.1 --- title: abs (8) context: {} template: {$eval: 'abs(1.1)'} result: 1.1 --- title: abs (9) context: {key: 1} template: {$eval: 'abs(key)'} result: 1 --- title: abs (10) context: {key: 1.0} template: {$eval: 'abs(key)'} result: 1 --- title: abs (11) context: {} template: {$eval: 'abs(1)'} result: 1 --- title: abs (12) context: {} template: {$eval: 'abs(1.0)'} result: 1 --- title: abs - TypeError context: {} template: {$eval: 'abs({})'} error: 'BuiltinError: invalid arguments to builtin: abs' --- title: lowercase (1) context: {key: 'HEllo'} template: {$eval: 'lowercase(key)'} result: 'hello' --- title: lowercase (2) context: {} template: {$eval: 'lowercase("HEllo")'} result: 'hello' --- title: TypeError lowercase (1) context: {key: {}} template: {$eval: 'lowercase(key)'} error: 'BuiltinError: invalid arguments to builtin: lowercase' --- title: TypeError lowercase (2) context: {} template: {$eval: 'lowercase({})'} error: 'BuiltinError: invalid arguments to builtin: lowercase' --- title: TypeError lowercase (3) context: {key: []} template: {$eval: 'lowercase(key)'} error: 'BuiltinError: invalid arguments to builtin: lowercase' --- title: TypeError lowercase (4) context: {} template: {$eval: 'lowercase([])'} error: 'BuiltinError: invalid arguments to builtin: lowercase' --- title: TypeError lowercase (5) context: {key: 1} template: {$eval: 'lowercase(key)'} error: 'BuiltinError: invalid arguments to builtin: lowercase' --- title: TypeError lowercase (6) context: {} template: {$eval: 'lowercase(1)'} error: 'BuiltinError: invalid arguments to builtin: lowercase' --- title: uppercase (1) context: {key: 'HEllo'} template: {$eval: 'uppercase(key)'} result: 'HELLO' --- title: uppercase (2) context: {} template: {$eval: 'uppercase("HEllo")'} result: 'HELLO' --- title: TypeError uppercase (1) context: {key: {}} template: {$eval: 'uppercase(key)'} error: 'BuiltinError: invalid arguments to builtin: uppercase' --- title: TypeError uppercase (2) context: {} template: {$eval: 'uppercase({})'} error: 'BuiltinError: invalid arguments to builtin: uppercase' --- title: TypeError uppercase (3) context: {key: []} template: {$eval: 'uppercase(key)'} error: 'BuiltinError: invalid arguments to builtin: uppercase' --- title: TypeError uppercase (4) context: {} template: {$eval: 'uppercase([])'} error: 'BuiltinError: invalid arguments to builtin: uppercase' --- title: TypeError uppercase (5) context: {key: 1} template: {$eval: 'uppercase(key)'} error: 'BuiltinError: invalid arguments to builtin: uppercase' --- title: TypeError uppercase (6) context: {} template: {$eval: 'uppercase(1)'} error: 'BuiltinError: invalid arguments to builtin: uppercase' --- title: len (1) context: {key: [1,2,3]} template: {$eval: 'len(key)'} result: 3 --- title: len (2) context: {key: [1,2,3]} template: {$eval: 'len([1, 2, 3])'} result: 3 --- title: len (3) context: {key: 'abc'} template: {$eval: 'len(key)'} result: 3 --- title: len of grinning face unicode character context: {key: "\U0001F601"} template: {$eval: 'len(key)'} result: 1 --- title: len (4) context: {key: [1,2,3]} template: {$eval: 'len("abc")'} result: 3 --- title: TypeError len (1) context: {key: {}} template: {$eval: 'len(key)'} error: 'BuiltinError: invalid arguments to builtin: len' --- title: TypeError len (2) context: {} template: {$eval: 'len({})'} error: 'BuiltinError: invalid arguments to builtin: len' --- title: TypeError len (3) context: {key: 1} template: {$eval: 'len(key)'} error: 'BuiltinError: invalid arguments to builtin: len' --- title: TypeError len (4) context: {} template: {$eval: 'len(1)'} error: 'BuiltinError: invalid arguments to builtin: len' --- title: str of num via context context: {key: 1} template: {$eval: 'str(key)'} result: '1' --- title: str of num context: {} template: {$eval: 'str(1)'} result: '1' --- title: str of string via context context: {key: 'hello'} template: {$eval: 'str(key)'} result: 'hello' --- title: str of string context: {} template: {$eval: 'str("hello")'} result: 'hello' --- title: str of bool via context context: {key: true} template: {$eval: 'str(key)'} result: 'true' --- title: str of true context: {} template: {$eval: 'str(true)'} result: 'true' --- title: str (key) with null context: {x: null} template: {$eval: 'str(x)'} result: 'null' --- title: str of bool via context context: {key: false} template: {$eval: 'str(key)'} result: 'false' --- title: str of false context: {} template: {$eval: 'str(false)'} result: 'false' --- title: str of floating-point 4.25 context: {val: 4.25} template: {$eval: 'str(val)'} result: '4.25' --- title: str of floating-point 4.2 context: {val: 4.2} template: {$eval: 'str(val)'} result: '4.2' --- title: str of unicode context: {val: "let's make a ☃"} template: {$eval: 'str(val)'} result: "let's make a ☃" --- title: str of empty object context: {} template: {$eval: 'str({})'} error: 'BuiltinError: invalid arguments to builtin: str' --- title: str of object context: {} template: {$eval: 'str({a:10})'} error: 'BuiltinError: invalid arguments to builtin: str' --- title: str of array context: {} template: {$eval: 'str([1, 2, 3])'} error: 'BuiltinError: invalid arguments to builtin: str' --- title: str(key) with array context: {key: [1, 2, 3]} template: {$eval: 'str(key)'} error: 'BuiltinError: invalid arguments to builtin: str' --- title: number context: {round: "3"} template: {foo: {$eval: number(round)}} result: {foo: 3} --- title: number (with point) context: {round: "3.5"} template: {foo: {$eval: number(round)}} result: {foo: 3.5} --- title: negative number context: {round: "-14"} template: {foo: {$eval: number(round)}} result: {foo: -14} --- title: negative number (with point) context: {round: "-14.15"} template: {foo: {$eval: number(round)}} result: {foo: -14.15} --- title: number zero context: {round: "0"} template: {foo: {$eval: number(round)}} result: {foo: 0} --- title: giving a number to number fails context: {round: 0} template: {foo: {$eval: number(round)}} error: true --- title: giving a bool to number fails context: {round: false} template: {foo: {$eval: number(round)}} error: true --- title: giving null to number fails context: {round: null} template: {foo: {$eval: number(round)}} error: true --- title: giving an array to number fails context: {round: [1]} template: {foo: {$eval: number(round)}} error: true --- title: giving an object to number fails context: {round: {one: 1}} template: {foo: {$eval: number(round)}} error: true --- title: strip space context: {} template: {$eval: "strip(' abc ')"} result: 'abc' --- title: strip mixed whitspace context: {} template: {$eval: "strip(' \f\n\r\t\vabc \f\n\r\t\v')"} result: 'abc' --- title: rstrip space context: {} template: {$eval: "rstrip(' abc ')"} result: ' abc' --- title: rstrip mixed whitspace context: {} template: {$eval: "rstrip(' \f\n\r\t\vabc \f\n\r\t\v')"} result: " \f\n\r\t\vabc" --- title: lstrip space context: {} template: {$eval: "lstrip(' abc ')"} result: 'abc ' --- title: lstrip mixed whitspace context: {} template: {$eval: "lstrip(' \f\n\r\t\vabc \f\n\r\t\v')"} result: "abc \f\n\r\t\v" --- title: split string with delimiter context: {} template: {$eval: "split('left:right', ':')"} result: ['left', 'right'] --- title: split string without delimiter context: {} template: {$eval: "split('spellme', '')"} result: ['s', 'p', 'e', 'l', 'l', 'm', 'e'] --- title: split with empty string context: {} template: {$eval: "split('', ':')"} result: [''] --- title: split string with no match context: {} template: {$eval: "split('no semicolon', ':')"} result: ['no semicolon'] --- title: split with number delimiter context: {} template: {$eval: "split('left1right', 1)"} result: ['left', 'right'] --- title: split error with object context: {} template: {$eval: "split({})"} error: 'BuiltinError: invalid arguments to builtin: split' --- title: split error with array context: {} template: {$eval: "split([])"} error: 'BuiltinError: invalid arguments to builtin: split' --- title: split with only one argument context: {} template: {$eval: "split('abc')"} error: 'BuiltinError: invalid arguments to builtin: split' --- title: join with string separator context: {} template: {$eval: "join(['join', 'me'], ' ')"} result: "join me" --- title: join with same context: {} template: {$eval: "join([',', ','], ',')"} result: ",,," --- title: join with number separator context: {} template: {$eval: "join([1, 3], 2)"} result: "123" --- title: join with strings separated with number context: {} template: {$eval: "join(['one', 'three'], 2)"} result: "one2three" --- title: join with numbers separated with string context: {} template: {$eval: "join([1, 3], 'two')"} result: "1two3" --- title: join with string context: {} template: {$eval: "join('cannot join string')"} error: "BuiltinError: invalid arguments to builtin: join" --- title: join with object context: {} template: {$eval: "join({})"} error: "BuiltinError: invalid arguments to builtin: join" --- title: join with boolean context: {} template: {$eval: "join(false)"} error: "BuiltinError: invalid arguments to builtin: join" --- title: fromNow context: {} template: {$eval: fromNow("")} result: '2017-01-19T16:27:20.974Z' --- title: fromNow - 2 days 3 hours context: {} template: {$eval: fromNow("2 days 3 hours")} result: '2017-01-21T19:27:20.974Z' --- title: fromNow with reference context: {} template: {$eval: "fromNow('1 day', '2017-01-01T01:00:00.123Z')"} result: '2017-01-02T01:00:00.123Z' --- title: fromNow - TypeError context: {} template: {$eval: fromNow(13)} error: 'BuiltinError: invalid arguments to builtin: fromNow' --- title: typeof str context: {} template: {$eval: typeof('abc')} result: string --- title: typeof number (int) context: {} template: {$eval: typeof(4)} result: number --- title: typeof number (float) context: {} template: {$eval: typeof(4.0)} result: number --- title: typeof boolean (true) context: {} template: {$eval: typeof(true)} result: boolean --- title: typeof boolean (false) context: {} template: {$eval: typeof(false)} result: boolean --- title: typeof array (empty) context: {} template: {$eval: 'typeof([])'} result: array --- title: typeof array context: {} template: {$eval: 'typeof([1, 2, 3])'} result: array --- title: typeof object context: {} template: {$eval: 'typeof({})'} result: object --- title: typeof null context: {x: null} template: {$eval: 'typeof(x)'} result: 'null' --- title: typeof null, interpolated context: {x: null} template: "${typeof(x)}" result: 'null' --- title: typeof null in if context: {} template: {$if: 'typeof(null) == "null"', then: 'foo', else: 'bar'} result: foo --- title: typeof function context: {} template: {$eval: 'typeof(typeof)'} result: function --- title: override builtin (1), uppercase context: {uppercase: 1} template: {$eval: 'uppercase + 1'} result: 2 --- title: override builtin (2), min context: {min: 'hello world!'} template: {$eval: 'min'} result: 'hello world!' --- title: 'defined without context variable' context: {} template: {$if: "defined('var')", then: 't', else: 'f'} result: f --- title: 'defined with empty string as context variable' context: {var: ""} template: {$if: "defined('var')", then: 't', else: 'f'} result: t ################################################################################ --- section: expression language - basics --- title: missing right square bracket context: {bar: "mystring"} template: {foo: '${bar[2:}'} error: "SyntaxError at template.foo: Found: } token, expected one of: !, (, +, -, [, false, identifier, null, number, string, true, {" --- title: decimal literal context: {} template: {$eval: '10.5'} result: 10.5 --- title: hex literal context: {} template: {$eval: '0xff'} error: 'SyntaxError: Found: xff token, expected one of: !=, &&, (, *, **, +, -, ., /, <, <=, ==, >, >=, [, in, ||' --- title: string literal with single quote context: {} template: {$eval: "'three!'"} result: 'three!' --- title: string literal with double quote context: {} template: {$eval: '"three!"'} result: 'three!' --- title: string literal escape with backslash (not supported) context: {} template: {$eval: '"backslash\\"maybe"'} error: 'SyntaxError: Found: maybe token, expected one of: !=, &&, (, *, **, +, -, ., /, <, <=, ==, >, >=, [, in, ||' --- title: string literal escape with doubling (not supported) context: {} template: {$eval: '"doubled""maybe"'} error: 'SyntaxError: Found: "maybe" token, expected one of: !=, &&, (, *, **, +, -, ., /, <, <=, ==, >, >=, [, in, ||' --- title: boolean literals context: {} template: {$eval: '[true, false]'} result: [true, false] --- title: null literal context: {} template: {$eval: '[null, null]'} result: [null, null] --- title: leading zeroes (not octal) context: {} template: {$eval: '0777'} result: 777 --- title: addition context: {a: 1, b: 2} template: {$eval: 'a + b + 7'} result: 10 --- title: multiplication context: {a: 3, b: 2} template: {$eval: 'a * b * 3'} result: 18 --- title: string concatenation context: {a: 3, b: 2} template: {$eval: '"a" + "b"'} result: 'ab' --- title: $eval must take string context: {a: 3, b: 2} template: {$eval: ['a', 'b']} error: 'TemplateError: $eval must be given a string expression' --- title: array access [index] context: {a: [1,2,3,4]} template: {$eval: 'a[0]'} result: 1 --- title: array access [index] context: {a: [1,2,3,4]} template: {$eval: 'a[2]'} result: 3 --- title: array access [-index] context: {a: [1,2,3,4]} template: {$eval: 'a[-1]'} result: 4 --- title: array access [-index] context: {a: [1,2,3,4]} template: {$eval: 'a[-2]'} result: 3 --- title: array access [index expression] context: {a: [10,11,12], b: 1} template: {$eval: 'a[b]'} result: 11 --- title: array access [index expression with +] context: {a: [10,11,12], b: 1} template: {$eval: 'a[b+1]'} result: 12 --- title: array slicing [begin:end], [begin, end) context: {a: [1,2,3,4]} template: {$eval: 'a[0:2]'} result: [1,2] --- title: array slicing [begin:], [begin, end) context: {a: [1,2,3,4]} template: {$eval: 'a[0:]'} result: [1,2,3,4] --- title: array slicing [begin:], [begin, end) context: {a: [1,2,3,4]} template: {$eval: 'a[1:]'} result: [2,3,4] --- title: array slicing [-begin:], [begin, end) context: {a: [1,2,3,4]} template: {$eval: 'a[-3:]'} result: [2,3,4] --- title: array slicing [-begin:-end], [begin, end) context: {a: [1,2,3,4]} template: {$eval: 'a[-3:-1]'} result: [2,3] --- title: array slicing [:end], [begin, end) context: {a: [1,2,3,4]} template: {$eval: 'a[:3]'} result: [1,2,3] --- title: array slicing [:-end], [begin, end) context: {a: [1,2,3,4]} template: {$eval: 'a[:-2]'} result: [1,2] --- title: array slice partially out of range context: {a: [1,2,3,4]} template: {$eval: 'a[2:10]'} result: [3,4] --- title: array slice completely out of range context: {a: [1,2,3,4]} template: {$eval: 'a[8:10]'} result: [] --- title: array slice negative out of range context: {a: [1,2,3,4]} template: {$eval: 'a[2:-10]'} result: [] --- title: array slice negative range context: {a: [1,2,3,4]} template: {$eval: 'a[2:-3]'} result: [] --- title: array slice reverse range context: {a: [1,2,3,4]} template: {$eval: 'a[3:2]'} result: [] --- title: function min(contextValue, contextValue) context: {a: 1, b: 2} template: {$eval: 'min(a, b)'} result: 1 --- title: function max(contextValue, contextValue) context: {a: 1, b: 2} template: {$eval: 'max(a, b)'} result: 2 --- title: function min(contextValue, value) context: {a: 1, b: 2} template: {$eval: 'min(a, -1)'} result: -1 --- title: function max(contextValue, value) context: {a: 1, b: 2} template: {$eval: 'max(a, 3)'} result: 3 --- title: identifier beginning with in context: {input: 6} template: {$eval: 'input + 1'} result: 7 --- title: identifier beginning with true / false context: {true_facts: true, false_lies: false} template: {$eval: 'true_facts || false_lies'} result: true --- title: identifier beginning with null context: {null_hypothesis: "no difference"} template: {$eval: 'null_hypothesis'} result: "no difference" ################################################################################ --- section: expression language - arithmetic --- title: 'addition' context: {a: 1, b: 2} template: {$eval: 'a + b + 7'} result: 10 --- title: 'multiplication' context: {a: 3, b: 2} template: {$eval: 'a * b * 3'} result: 18 --- title: 'multiplication (2)' context: {a: 180000000000.0} template: {$eval: '(a * -1000)'} result: -180000000000000.0 --- title: 'multiplication with addition' context: {a: 2} template: {$eval: 'a * 3 + 4'} result: 10 --- title: 'multiplication with subtraction' context: {a: 2} template: {$eval: 'a * 3 - 4'} result: 2 --- title: 'multiplication with division' context: {a: 4} template: {$eval: 'a * 3 / 2'} result: 6 --- title: 'multiplication with exponentiation' context: {a: 2} template: {$eval: 'a * 3 ** 4'} result: 162 --- title: 'multiplication with grouped addition' context: {a: 2} template: {$eval: 'a * (3 + 4)'} result: 14 --- title: 'multiplication with grouped subtraction' context: {a: 2} template: {$eval: 'a * (3 - 4)'} result: -2 --- title: 'multiplication with grouped division' context: {a: 4} template: {$eval: 'a * (4 / 2)'} result: 8 --- title: 'grouped multiplication with exponentiation' context: {a: 4} template: {$eval: '(a * 3) ** 2'} result: 144 --- title: 'division (1)' context: {a: 3, b: 2} template: {$eval: 'a / b'} result: 1.5 --- title: 'division (2)' context: {a: 3.0, b: 2.0} template: {$eval: 'a / b'} result: 1.5 --- title: 'division (3)' context: {a: 3, b: 2} template: {$eval: 'b / a'} result: 0.6666666666666666 --- title: 'division (4)' context: {a: 3.0, b: 2.0} template: {$eval: 'b / a'} result: 0.6666666666666666 --- title: 'division by zero' context: {} template: {$eval: '1 / 0'} error: "InterpreterError: division by zero" --- title: 'division with addition' context: {a: 12} template: {$eval: 'a / 3 + 4'} result: 8 --- title: 'division with subtraction' context: {a: 6} template: {$eval: 'a / 3 - 4'} result: -2 --- title: 'division with division' context: {a: 12} template: {$eval: 'a / 3 / 2'} result: 2 --- title: 'division with exponentiation' context: {a: 162} template: {$eval: 'a / 3 ** 4'} result: 2 --- title: 'division with grouped addition' context: {a: 14} template: {$eval: 'a / (3 + 4)'} result: 2 --- title: 'division with grouped subtraction' context: {a: 6} template: {$eval: 'a / (3 - 4)'} result: -6 --- title: 'division with grouped division' context: {a: 12} template: {$eval: 'a / (6 / 2)'} result: 4 --- title: 'grouped division with exponentiation' context: {a: 12} template: {$eval: '(a / 3) ** 4'} result: 256 --- title: 'exponentiation (1)' context: {a: 2, b: 3} template: {$eval: 'a ** b'} result: 8 --- title: 'exponentiation (2)' context: {a: 2, b: 3} template: {$eval: 'a ** b + 2'} result: 10 --- title: 'exponentiation (3)' context: {a: 2, b: 3} template: {$eval: 'a ** b * 2'} result: 16 --- title: 'exponentiation (4)' context: {a: 2, b: 3} template: {$eval: 'a * a ** b'} result: 16 --- title: 'exponentiation, right associativity (1)' context: {a: 2, b: 3} template: {$eval: 'a ** a ** b == a ** (a ** b)'} result: true --- title: 'exponentiation, right associativity (2)' context: {a: 2, b: 3} template: {$eval: 'a ** a ** b'} result: 256 --- title: 'exponentiation, right associativity (3)' context: {a: 2, b: 3} template: {$eval: '2 ** 2 ** 3 == 2 ** (2 ** 3)'} result: true --- title: 'exponentiation, right associativity (4)' context: {a: 2, b: 3} template: {$eval: '2 ** 2 ** 3'} result: 256 --- title: 'unary negation' context: {a: 2} template: {$eval: '-a'} result: -2 --- title: 'unary plus' context: {a: 2} template: {$eval: '+a'} result: 2 ################################################################################ --- section: expression language - operator type errors --- title: 'addition of strings to numbers' context: {a: 'a', b: 1} template: {$eval: 'a + b'} error: 'InterpreterError: infix: + expects numbers/strings + numbers/strings' --- title: 'addition of strings to null' context: {a: 'a', b: null} template: {$eval: 'a + b'} error: 'InterpreterError: infix: + expects numbers/strings + numbers/strings' --- title: 'addition of strings to booleans' context: {a: 'a', b: true} template: {$eval: 'a + b'} error: 'InterpreterError: infix: + expects numbers/strings + numbers/strings' --- title: 'addition of strings to objects' context: {a: 'a', b: {x: 1}} template: {$eval: 'a + b'} error: 'InterpreterError: infix: + expects numbers/strings + numbers/strings' --- title: 'addition of strings to arrays' context: {a: 'a', b: [1, 2]} template: {$eval: 'a + b'} error: 'InterpreterError: infix: + expects numbers/strings + numbers/strings' --- title: 'subtraction of strings from strings' context: {a: 'a', b: 'b'} template: {$eval: 'a-b'} error: 'InterpreterError: infix: - expects number - number' --- title: 'subtraction of strings from numbers' context: {a: 'a', b: 1} template: {$eval: 'a - b'} error: 'InterpreterError: infix: - expects number - number' --- title: 'subtraction of strings from null' context: {a: 'a', b: null} template: {$eval: 'a - b'} error: 'InterpreterError: infix: - expects number - number' --- title: 'subtraction of strings from booleans' context: {a: 'a', b: true} template: {$eval: 'a - b'} error: 'InterpreterError: infix: - expects number - number' --- title: 'subtraction of strings from objects' context: {a: 'a', b: {x: 1}} template: {$eval: 'a - b'} error: 'InterpreterError: infix: - expects number - number' --- title: 'subtraction of strings from arrays' context: {a: 'a', b: [1, 2]} template: {$eval: 'a - b'} error: 'InterpreterError: infix: - expects number - number' --- title: 'multiplication of strings to strings' context: {a: 'a', b: 'b'} template: {$eval: 'a * b'} error: 'InterpreterError: infix: * expects number * number' --- title: 'multiplication of strings to numbers' context: {a: 'a', b: 1} template: {$eval: 'a * b'} error: 'InterpreterError: infix: * expects number * number' --- title: 'multiplication of strings to null' context: {a: 'a', b: null} template: {$eval: 'a * b'} error: 'InterpreterError: infix: * expects number * number' --- title: 'multiplication of strings to booleans' context: {a: 'a', b: true} template: {$eval: 'a * b'} error: 'InterpreterError: infix: * expects number * number' --- title: 'multiplication of strings to objects' context: {a: 'a', b: {x: 1}} template: {$eval: 'a * b'} error: 'InterpreterError: infix: * expects number * number' --- title: 'multiplication of strings to arrays' context: {a: 'a', b: [1, 2]} template: {$eval: 'a * b'} error: 'InterpreterError: infix: * expects number * number' --- title: 'division of strings by strings' context: {a: 'a', b: 'b'} template: {$eval: 'a / b'} error: 'InterpreterError: infix: / expects number / number' --- title: 'division of strings by numbers' context: {a: 'a', b: 1} template: {$eval: 'a / b'} error: 'InterpreterError: infix: / expects number / number' --- title: 'division of strings by null' context: {a: 'a', b: null} template: {$eval: 'a / b'} error: 'InterpreterError: infix: / expects number / number' --- title: 'division of strings by booleans' context: {a: 'a', b: true} template: {$eval: 'a / b'} error: 'InterpreterError: infix: / expects number / number' --- title: 'division of strings by objects' context: {a: 'a', b: {x: 1}} template: {$eval: 'a / b'} error: 'InterpreterError: infix: / expects number / number' --- title: 'division of strings by arrays' context: {a: 'a', b: [1, 2]} template: {$eval: 'a / b'} error: 'InterpreterError: infix: / expects number / number' --- title: 'exponentiation of strings by strings' context: {a: 'a', b: 'b'} template: {$eval: 'a ** b'} error: 'InterpreterError: infix: ** expects number ** number' --- title: 'exponentiation of strings by numbers' context: {a: 'a', b: 1} template: {$eval: 'a ** b'} error: 'InterpreterError: infix: ** expects number ** number' --- title: 'exponentiation of strings by null' context: {a: 'a', b: null} template: {$eval: 'a ** b'} error: 'InterpreterError: infix: ** expects number ** number' --- title: 'exponentiation of strings by booleans' context: {a: 'a', b: true} template: {$eval: 'a ** b'} error: 'InterpreterError: infix: ** expects number ** number' --- title: 'exponentiation of strings by objects' context: {a: 'a', b: {x: 1}} template: {$eval: 'a ** b'} error: 'InterpreterError: infix: ** expects number ** number' --- title: 'exponentiation of strings by arrays' context: {a: 'a', b: [1, 2]} template: {$eval: 'a ** b'} error: 'InterpreterError: infix: ** expects number ** number' --- title: 'unary negation of string' context: {a: 'a'} template: {$eval: '-a'} error: 'InterpreterError: unary - expects number' --- title: 'unary plus of string' context: {a: 'a'} template: {$eval: '+a'} error: 'InterpreterError: unary + expects number' --- title: 'unary negation of true' template: {$eval: '-true'} context: {} error: 'InterpreterError: unary - expects number' --- title: 'unary plus of true' template: {$eval: '+true'} context: {} error: 'InterpreterError: unary + expects number' --- title: 'unary negation of false' template: {$eval: '-false'} context: {} error: 'InterpreterError: unary - expects number' --- title: 'unary plus of false' template: {$eval: '+false'} context: {} error: 'InterpreterError: unary + expects number' --- title: 'unary negation of null' template: {$eval: '-null'} context: {} error: 'InterpreterError: unary - expects number' --- title: 'unary plus of null' template: {$eval: '+null'} context: {} error: 'InterpreterError: unary + expects number' --- title: 'addition of numbers to strings' context: {a: 13, b: 'b'} template: {$eval: 'a + b'} error: 'InterpreterError: infix: + expects numbers/strings + numbers/strings' --- title: 'addition of numbers to null' context: {a: 13, b: null} template: {$eval: 'a + b'} error: 'InterpreterError: infix: + expects numbers/strings + numbers/strings' --- title: 'addition of numbers to booleans' context: {a: 13, b: true} template: {$eval: 'a + b'} error: 'InterpreterError: infix: + expects numbers/strings + numbers/strings' --- title: 'addition of numbers to objects' context: {a: 13, b: {x: 1}} template: {$eval: 'a + b'} error: 'InterpreterError: infix: + expects numbers/strings + numbers/strings' --- title: 'addition of numbers to arrays' context: {a: 13, b: [1, 2]} template: {$eval: 'a + b'} error: 'InterpreterError: infix: + expects numbers/strings + numbers/strings' --- title: 'subtraction of numbers from strings' context: {a: 13, b: 'b'} template: {$eval: 'a-b'} error: 'InterpreterError: infix: - expects number - number' --- title: 'subtraction of numbers from null' context: {a: 13, b: null} template: {$eval: 'a - b'} error: 'InterpreterError: infix: - expects number - number' --- title: 'subtraction of numbers from booleans' context: {a: 13, b: true} template: {$eval: 'a - b'} error: 'InterpreterError: infix: - expects number - number' --- title: 'subtraction of numbers from objects' context: {a: 13, b: {x: 1}} template: {$eval: 'a - b'} error: 'InterpreterError: infix: - expects number - number' --- title: 'subtraction of numbers from arrays' context: {a: 13, b: [1, 2]} template: {$eval: 'a - b'} error: 'InterpreterError: infix: - expects number - number' --- title: 'multiplication of numbers to strings' context: {a: 13, b: 'b'} template: {$eval: 'a * b'} error: 'InterpreterError: infix: * expects number * number' --- title: 'multiplication of numbers to null' context: {a: 13, b: null} template: {$eval: 'a * b'} error: 'InterpreterError: infix: * expects number * number' --- title: 'multiplication of numbers to booleans' context: {a: 13, b: true} template: {$eval: 'a * b'} error: 'InterpreterError: infix: * expects number * number' --- title: 'multiplication of numbers to objects' context: {a: 13, b: {x: 1}} template: {$eval: 'a * b'} error: 'InterpreterError: infix: * expects number * number' --- title: 'multiplication of numbers to arrays' context: {a: 13, b: [1, 2]} template: {$eval: 'a * b'} error: 'InterpreterError: infix: * expects number * number' --- title: 'division of numbers by strings' context: {a: 13, b: 'b'} template: {$eval: 'a / b'} error: 'InterpreterError: infix: / expects number / number' --- title: 'division of numbers by null' context: {a: 13, b: null} template: {$eval: 'a / b'} error: 'InterpreterError: infix: / expects number / number' --- title: 'division of numbers by booleans' context: {a: 13, b: true} template: {$eval: 'a / b'} error: 'InterpreterError: infix: / expects number / number' --- title: 'division of numbers by objects' context: {a: 13, b: {x: 1}} template: {$eval: 'a / b'} error: 'InterpreterError: infix: / expects number / number' --- title: 'division of numbers by arrays' context: {a: 13, b: [1, 2]} template: {$eval: 'a / b'} error: 'InterpreterError: infix: / expects number / number' --- title: 'exponentiation of numbers by strings' context: {a: 13, b: 'b'} template: {$eval: 'a ** b'} error: 'InterpreterError: infix: ** expects number ** number' --- title: 'exponentiation of numbers by null' context: {a: 13, b: null} template: {$eval: 'a ** b'} error: 'InterpreterError: infix: ** expects number ** number' --- title: 'exponentiation of numbers by booleans' context: {a: 13, b: true} template: {$eval: 'a ** b'} error: 'InterpreterError: infix: ** expects number ** number' --- title: 'exponentiation of numbers by objects' context: {a: 13, b: {x: 1}} template: {$eval: 'a ** b'} error: 'InterpreterError: infix: ** expects number ** number' --- title: 'exponentiation of numbers by arrays' context: {a: 13, b: [1, 2]} template: {$eval: 'a ** b'} error: 'InterpreterError: infix: ** expects number ** number' --- title: 'addition of null to strings' context: {a: null, b: 'a'} template: {$eval: 'a + b'} error: 'InterpreterError: infix: + expects numbers/strings + numbers/strings' --- title: 'addition of null to numbers' context: {a: null, b: 1} template: {$eval: 'a + b'} error: 'InterpreterError: infix: + expects numbers/strings + numbers/strings' --- title: 'addition of null to null' context: {a: null, b: null} template: {$eval: 'a + b'} error: 'InterpreterError: infix: + expects numbers/strings + numbers/strings' --- title: 'addition of null to booleans' context: {a: null, b: true} template: {$eval: 'a + b'} error: 'InterpreterError: infix: + expects numbers/strings + numbers/strings' --- title: 'addition of null to objects' context: {a: null, b: {x: 1}} template: {$eval: 'a + b'} error: 'InterpreterError: infix: + expects numbers/strings + numbers/strings' --- title: 'addition of null to arrays' context: {a: null, b: [1, 2]} template: {$eval: 'a + b'} error: 'InterpreterError: infix: + expects numbers/strings + numbers/strings' --- title: 'subtraction of null from strings' context: {a: null, b: 'b'} template: {$eval: 'a-b'} error: 'InterpreterError: infix: - expects number - number' --- title: 'subtraction of null from numbers' context: {a: null, b: 1} template: {$eval: 'a - b'} error: 'InterpreterError: infix: - expects number - number' --- title: 'subtraction of null from null' context: {a: null, b: null} template: {$eval: 'a - b'} error: 'InterpreterError: infix: - expects number - number' --- title: 'subtraction of null from booleans' context: {a: null, b: true} template: {$eval: 'a - b'} error: 'InterpreterError: infix: - expects number - number' --- title: 'subtraction of null from objects' context: {a: null, b: {x: 1}} template: {$eval: 'a - b'} error: 'InterpreterError: infix: - expects number - number' --- title: 'subtraction of null from arrays' context: {a: null, b: [1, 2]} template: {$eval: 'a - b'} error: 'InterpreterError: infix: - expects number - number' --- title: 'multiplication of null to strings' context: {a: null, b: 'b'} template: {$eval: 'a * b'} error: 'InterpreterError: infix: * expects number * number' --- title: 'multiplication of null to numbers' context: {a: null, b: 1} template: {$eval: 'a * b'} error: 'InterpreterError: infix: * expects number * number' --- title: 'multiplication of null to null' context: {a: null, b: null} template: {$eval: 'a * b'} error: 'InterpreterError: infix: * expects number * number' --- title: 'multiplication of null to booleans' context: {a: null, b: true} template: {$eval: 'a * b'} error: 'InterpreterError: infix: * expects number * number' --- title: 'multiplication of null to objects' context: {a: null, b: {x: 1}} template: {$eval: 'a * b'} error: 'InterpreterError: infix: * expects number * number' --- title: 'multiplication of null to arrays' context: {a: null, b: [1, 2]} template: {$eval: 'a * b'} error: 'InterpreterError: infix: * expects number * number' --- title: 'division of null by strings' context: {a: null, b: 'b'} template: {$eval: 'a / b'} error: 'InterpreterError: infix: / expects number / number' --- title: 'division of null by numbers' context: {a: null, b: 1} template: {$eval: 'a / b'} error: 'InterpreterError: infix: / expects number / number' --- title: 'division of null by null' context: {a: null, b: null} template: {$eval: 'a / b'} error: 'InterpreterError: infix: / expects number / number' --- title: 'division of null by booleans' context: {a: null, b: true} template: {$eval: 'a / b'} error: 'InterpreterError: infix: / expects number / number' --- title: 'division of null by objects' context: {a: null, b: {x: 1}} template: {$eval: 'a / b'} error: 'InterpreterError: infix: / expects number / number' --- title: 'division of null by arrays' context: {a: null, b: [1, 2]} template: {$eval: 'a / b'} error: 'InterpreterError: infix: / expects number / number' --- title: 'exponentiation of null by strings' context: {a: null, b: 'b'} template: {$eval: 'a ** b'} error: 'InterpreterError: infix: ** expects number ** number' --- title: 'exponentiation of null by numbers' context: {a: null, b: 1} template: {$eval: 'a ** b'} error: 'InterpreterError: infix: ** expects number ** number' --- title: 'exponentiation of null by null' context: {a: null, b: null} template: {$eval: 'a ** b'} error: 'InterpreterError: infix: ** expects number ** number' --- title: 'exponentiation of null by booleans' context: {a: null, b: true} template: {$eval: 'a ** b'} error: 'InterpreterError: infix: ** expects number ** number' --- title: 'exponentiation of null by objects' context: {a: null, b: {x: 1}} template: {$eval: 'a ** b'} error: 'InterpreterError: infix: ** expects number ** number' --- title: 'exponentiation of null by arrays' context: {a: null, b: [1, 2]} template: {$eval: 'a ** b'} error: 'InterpreterError: infix: ** expects number ** number' --- title: 'unary negation of null' context: {a: null} template: {$eval: '-a'} error: 'InterpreterError: unary - expects number' --- title: 'unary plus of null' context: {a: null} template: {$eval: '+a'} error: 'InterpreterError: unary + expects number' --- title: 'addition of boolean to strings' context: {a: true, b: 'a'} template: {$eval: 'a + b'} error: 'InterpreterError: infix: + expects numbers/strings + numbers/strings' --- title: 'addition of boolean to numbers' context: {a: true, b: 1} template: {$eval: 'a + b'} error: 'InterpreterError: infix: + expects numbers/strings + numbers/strings' --- title: 'addition of boolean to null' context: {a: true, b: null} template: {$eval: 'a + b'} error: 'InterpreterError: infix: + expects numbers/strings + numbers/strings' --- title: 'addition of boolean to booleans' context: {a: true, b: true} template: {$eval: 'a + b'} error: 'InterpreterError: infix: + expects numbers/strings + numbers/strings' --- title: 'addition of boolean to objects' context: {a: true, b: {x: 1}} template: {$eval: 'a + b'} error: 'InterpreterError: infix: + expects numbers/strings + numbers/strings' --- title: 'addition of boolean to arrays' context: {a: true, b: [1, 2]} template: {$eval: 'a + b'} error: 'InterpreterError: infix: + expects numbers/strings + numbers/strings' --- title: 'subtraction of boolean from strings' context: {a: true, b: 'b'} template: {$eval: 'a-b'} error: 'InterpreterError: infix: - expects number - number' --- title: 'subtraction of boolean from numbers' context: {a: true, b: 1} template: {$eval: 'a - b'} error: 'InterpreterError: infix: - expects number - number' --- title: 'subtraction of boolean from null' context: {a: true, b: null} template: {$eval: 'a - b'} error: 'InterpreterError: infix: - expects number - number' --- title: 'subtraction of boolean from booleans' context: {a: true, b: true} template: {$eval: 'a - b'} error: 'InterpreterError: infix: - expects number - number' --- title: 'subtraction of boolean from objects' context: {a: true, b: {x: 1}} template: {$eval: 'a - b'} error: 'InterpreterError: infix: - expects number - number' --- title: 'subtraction of boolean from arrays' context: {a: true, b: [1, 2]} template: {$eval: 'a - b'} error: 'InterpreterError: infix: - expects number - number' --- title: 'multiplication of boolean to strings' context: {a: true, b: 'b'} template: {$eval: 'a * b'} error: 'InterpreterError: infix: * expects number * number' --- title: 'multiplication of boolean to numbers' context: {a: true, b: 1} template: {$eval: 'a * b'} error: 'InterpreterError: infix: * expects number * number' --- title: 'multiplication of boolean to null' context: {a: true, b: null} template: {$eval: 'a * b'} error: 'InterpreterError: infix: * expects number * number' --- title: 'multiplication of boolean to booleans' context: {a: true, b: true} template: {$eval: 'a * b'} error: 'InterpreterError: infix: * expects number * number' --- title: 'multiplication of boolean to objects' context: {a: true, b: {x: 1}} template: {$eval: 'a * b'} error: 'InterpreterError: infix: * expects number * number' --- title: 'multiplication of boolean to arrays' context: {a: true, b: [1, 2]} template: {$eval: 'a * b'} error: 'InterpreterError: infix: * expects number * number' --- title: 'division of boolean by strings' context: {a: true, b: 'b'} template: {$eval: 'a / b'} error: 'InterpreterError: infix: / expects number / number' --- title: 'division of boolean by numbers' context: {a: true, b: 1} template: {$eval: 'a / b'} error: 'InterpreterError: infix: / expects number / number' --- title: 'division of boolean by null' context: {a: true, b: null} template: {$eval: 'a / b'} error: 'InterpreterError: infix: / expects number / number' --- title: 'division of boolean by booleans' context: {a: true, b: true} template: {$eval: 'a / b'} error: 'InterpreterError: infix: / expects number / number' --- title: 'division of boolean by objects' context: {a: true, b: {x: 1}} template: {$eval: 'a / b'} error: 'InterpreterError: infix: / expects number / number' --- title: 'division of boolean by arrays' context: {a: true, b: [1, 2]} template: {$eval: 'a / b'} error: 'InterpreterError: infix: / expects number / number' --- title: 'exponentiation of boolean by strings' context: {a: true, b: 'b'} template: {$eval: 'a ** b'} error: 'InterpreterError: infix: ** expects number ** number' --- title: 'exponentiation of boolean by numbers' context: {a: true, b: 1} template: {$eval: 'a ** b'} error: 'InterpreterError: infix: ** expects number ** number' --- title: 'exponentiation of boolean by null' context: {a: true, b: null} template: {$eval: 'a ** b'} error: 'InterpreterError: infix: ** expects number ** number' --- title: 'exponentiation of boolean by booleans' context: {a: true, b: true} template: {$eval: 'a ** b'} error: 'InterpreterError: infix: ** expects number ** number' --- title: 'exponentiation of boolean by objects' context: {a: true, b: {x: 1}} template: {$eval: 'a ** b'} error: 'InterpreterError: infix: ** expects number ** number' --- title: 'exponentiation of boolean by arrays' context: {a: true, b: [1, 2]} template: {$eval: 'a ** b'} error: 'InterpreterError: infix: ** expects number ** number' --- title: 'unary negation of boolean' context: {a: true} template: {$eval: '-a'} error: 'InterpreterError: unary - expects number' --- title: 'unary plus of boolean' context: {a: true} template: {$eval: '+a'} error: 'InterpreterError: unary + expects number' --- title: 'addition of object to strings' context: {a: {x: 10}, b: 'a'} template: {$eval: 'a + b'} error: 'InterpreterError: infix: + expects numbers/strings + numbers/strings' --- title: 'addition of object to numbers' context: {a: {x: 10}, b: 1} template: {$eval: 'a + b'} error: 'InterpreterError: infix: + expects numbers/strings + numbers/strings' --- title: 'addition of object to null' context: {a: {x: 10}, b: null} template: {$eval: 'a + b'} error: 'InterpreterError: infix: + expects numbers/strings + numbers/strings' --- title: 'addition of object to booleans' context: {a: {x: 10}, b: true} template: {$eval: 'a + b'} error: 'InterpreterError: infix: + expects numbers/strings + numbers/strings' --- title: 'addition of object to objects' context: {a: {x: 10}, b: {x: 1}} template: {$eval: 'a + b'} error: 'InterpreterError: infix: + expects numbers/strings + numbers/strings' --- title: 'addition of object to arrays' context: {a: {x: 10}, b: [1, 2]} template: {$eval: 'a + b'} error: 'InterpreterError: infix: + expects numbers/strings + numbers/strings' --- title: 'subtraction of object from strings' context: {a: {x: 10}, b: 'b'} template: {$eval: 'a-b'} error: 'InterpreterError: infix: - expects number - number' --- title: 'subtraction of object from numbers' context: {a: {x: 10}, b: 1} template: {$eval: 'a - b'} error: 'InterpreterError: infix: - expects number - number' --- title: 'subtraction of object from null' context: {a: {x: 10}, b: null} template: {$eval: 'a - b'} error: 'InterpreterError: infix: - expects number - number' --- title: 'subtraction of object from booleans' context: {a: {x: 10}, b: true} template: {$eval: 'a - b'} error: 'InterpreterError: infix: - expects number - number' --- title: 'subtraction of object from objects' context: {a: {x: 10}, b: {x: 1}} template: {$eval: 'a - b'} error: 'InterpreterError: infix: - expects number - number' --- title: 'subtraction of object from arrays' context: {a: {x: 10}, b: [1, 2]} template: {$eval: 'a - b'} error: 'InterpreterError: infix: - expects number - number' --- title: 'multiplication of object to strings' context: {a: {x: 10}, b: 'b'} template: {$eval: 'a * b'} error: 'InterpreterError: infix: * expects number * number' --- title: 'multiplication of object to numbers' context: {a: {x: 10}, b: 1} template: {$eval: 'a * b'} error: 'InterpreterError: infix: * expects number * number' --- title: 'multiplication of object to null' context: {a: {x: 10}, b: null} template: {$eval: 'a * b'} error: 'InterpreterError: infix: * expects number * number' --- title: 'multiplication of object to booleans' context: {a: {x: 10}, b: true} template: {$eval: 'a * b'} error: 'InterpreterError: infix: * expects number * number' --- title: 'multiplication of object to objects' context: {a: {x: 10}, b: {x: 1}} template: {$eval: 'a * b'} error: 'InterpreterError: infix: * expects number * number' --- title: 'multiplication of object to arrays' context: {a: {x: 10}, b: [1, 2]} template: {$eval: 'a * b'} error: 'InterpreterError: infix: * expects number * number' --- title: 'division of object by strings' context: {a: {x: 10}, b: 'b'} template: {$eval: 'a / b'} error: 'InterpreterError: infix: / expects number / number' --- title: 'division of object by numbers' context: {a: {x: 10}, b: 1} template: {$eval: 'a / b'} error: 'InterpreterError: infix: / expects number / number' --- title: 'division of object by null' context: {a: {x: 10}, b: null} template: {$eval: 'a / b'} error: 'InterpreterError: infix: / expects number / number' --- title: 'division of object by booleans' context: {a: {x: 10}, b: true} template: {$eval: 'a / b'} error: 'InterpreterError: infix: / expects number / number' --- title: 'division of object by objects' context: {a: {x: 10}, b: {x: 1}} template: {$eval: 'a / b'} error: 'InterpreterError: infix: / expects number / number' --- title: 'division of object by arrays' context: {a: {x: 10}, b: [1, 2]} template: {$eval: 'a / b'} error: 'InterpreterError: infix: / expects number / number' --- title: 'exponentiation of object by strings' context: {a: {x: 10}, b: 'b'} template: {$eval: 'a ** b'} error: 'InterpreterError: infix: ** expects number ** number' --- title: 'exponentiation of object by numbers' context: {a: {x: 10}, b: 1} template: {$eval: 'a ** b'} error: 'InterpreterError: infix: ** expects number ** number' --- title: 'exponentiation of object by null' context: {a: {x: 10}, b: null} template: {$eval: 'a ** b'} error: 'InterpreterError: infix: ** expects number ** number' --- title: 'exponentiation of object by booleans' context: {a: {x: 10}, b: true} template: {$eval: 'a ** b'} error: 'InterpreterError: infix: ** expects number ** number' --- title: 'exponentiation of object by objects' context: {a: {x: 10}, b: {x: 1}} template: {$eval: 'a ** b'} error: 'InterpreterError: infix: ** expects number ** number' --- title: 'exponentiation of object by arrays' context: {a: {x: 10}, b: [1, 2]} template: {$eval: 'a ** b'} error: 'InterpreterError: infix: ** expects number ** number' --- title: 'unary negation of object' context: {a: {x: 10}} template: {$eval: '-a'} error: 'InterpreterError: unary - expects number' --- title: 'unary plus of object' context: {a: {x: 10}} template: {$eval: '+a'} error: 'InterpreterError: unary + expects number' --- title: 'addition of array to strings' context: {a: [1, 2], b: 'a'} template: {$eval: 'a + b'} error: 'InterpreterError: infix: + expects numbers/strings + numbers/strings' --- title: 'addition of array to numbers' context: {a: [1, 2], b: 1} template: {$eval: 'a + b'} error: 'InterpreterError: infix: + expects numbers/strings + numbers/strings' --- title: 'addition of array to null' context: {a: [1, 2], b: null} template: {$eval: 'a + b'} error: 'InterpreterError: infix: + expects numbers/strings + numbers/strings' --- title: 'addition of array to booleans' context: {a: [1, 2], b: true} template: {$eval: 'a + b'} error: 'InterpreterError: infix: + expects numbers/strings + numbers/strings' --- title: 'addition of array to objects' context: {a: [1, 2], b: {x: 1}} template: {$eval: 'a + b'} error: 'InterpreterError: infix: + expects numbers/strings + numbers/strings' --- title: 'addition of array to arrays' context: {a: [1, 2], b: [1, 2]} template: {$eval: 'a + b'} error: 'InterpreterError: infix: + expects numbers/strings + numbers/strings' --- title: 'subtraction of array from strings' context: {a: [1, 2], b: 'b'} template: {$eval: 'a-b'} error: 'InterpreterError: infix: - expects number - number' --- title: 'subtraction of array from numbers' context: {a: [1, 2], b: 1} template: {$eval: 'a - b'} error: 'InterpreterError: infix: - expects number - number' --- title: 'subtraction of array from null' context: {a: [1, 2], b: null} template: {$eval: 'a - b'} error: 'InterpreterError: infix: - expects number - number' --- title: 'subtraction of array from booleans' context: {a: [1, 2], b: true} template: {$eval: 'a - b'} error: 'InterpreterError: infix: - expects number - number' --- title: 'subtraction of array from objects' context: {a: [1, 2], b: {x: 1}} template: {$eval: 'a - b'} error: 'InterpreterError: infix: - expects number - number' --- title: 'subtraction of array from arrays' context: {a: [1, 2], b: [1, 2]} template: {$eval: 'a - b'} error: 'InterpreterError: infix: - expects number - number' --- title: 'multiplication of array to strings' context: {a: [1, 2], b: 'b'} template: {$eval: 'a * b'} error: 'InterpreterError: infix: * expects number * number' --- title: 'multiplication of array to numbers' context: {a: [1, 2], b: 1} template: {$eval: 'a * b'} error: 'InterpreterError: infix: * expects number * number' --- title: 'multiplication of array to null' context: {a: [1, 2], b: null} template: {$eval: 'a * b'} error: 'InterpreterError: infix: * expects number * number' --- title: 'multiplication of array to booleans' context: {a: [1, 2], b: true} template: {$eval: 'a * b'} error: 'InterpreterError: infix: * expects number * number' --- title: 'multiplication of array to objects' context: {a: [1, 2], b: {x: 1}} template: {$eval: 'a * b'} error: 'InterpreterError: infix: * expects number * number' --- title: 'multiplication of array to arrays' context: {a: [1, 2], b: [1, 2]} template: {$eval: 'a * b'} error: 'InterpreterError: infix: * expects number * number' --- title: 'division of array by strings' context: {a: [1, 2], b: 'b'} template: {$eval: 'a / b'} error: 'InterpreterError: infix: / expects number / number' --- title: 'division of array by numbers' context: {a: [1, 2], b: 1} template: {$eval: 'a / b'} error: 'InterpreterError: infix: / expects number / number' --- title: 'division of array by null' context: {a: [1, 2], b: null} template: {$eval: 'a / b'} error: 'InterpreterError: infix: / expects number / number' --- title: 'division of array by booleans' context: {a: [1, 2], b: true} template: {$eval: 'a / b'} error: 'InterpreterError: infix: / expects number / number' --- title: 'division of array by objects' context: {a: [1, 2], b: {x: 1}} template: {$eval: 'a / b'} error: 'InterpreterError: infix: / expects number / number' --- title: 'division of array by arrays' context: {a: [1, 2], b: [1, 2]} template: {$eval: 'a / b'} error: 'InterpreterError: infix: / expects number / number' --- title: 'exponentiation of array by strings' context: {a: [1, 2], b: 'b'} template: {$eval: 'a ** b'} error: 'InterpreterError: infix: ** expects number ** number' --- title: 'exponentiation of array by numbers' context: {a: [1, 2], b: 1} template: {$eval: 'a ** b'} error: 'InterpreterError: infix: ** expects number ** number' --- title: 'exponentiation of array by null' context: {a: [1, 2], b: null} template: {$eval: 'a ** b'} error: 'InterpreterError: infix: ** expects number ** number' --- title: 'exponentiation of array by booleans' context: {a: [1, 2], b: true} template: {$eval: 'a ** b'} error: 'InterpreterError: infix: ** expects number ** number' --- title: 'exponentiation of array by objects' context: {a: [1, 2], b: {x: 1}} template: {$eval: 'a ** b'} error: 'InterpreterError: infix: ** expects number ** number' --- title: 'exponentiation of array by arrays' context: {a: [1, 2], b: [1, 2]} template: {$eval: 'a ** b'} error: 'InterpreterError: infix: ** expects number ** number' --- title: 'unary negation of array' context: {a: [1, 2]} template: {$eval: '-a'} error: 'InterpreterError: unary - expects number' --- title: 'unary plus of array' context: {a: [1, 2]} template: {$eval: '+a'} error: 'InterpreterError: unary + expects number' ################################################################################ --- section: expression language - logic --- title: 'logical not (1)' context: {tt: true, ff: false} template: {$eval: '!tt'} result: false --- title: 'logical not (2)' context: {tt: true, ff: false} template: {$eval: '!ff'} result: true --- title: 'or operator (1)' context: {tt: true, ff: false} template: {$eval: 'tt || tt'} result: true --- title: 'or operator (2)' context: {tt: true, ff: false} template: {$eval: 'tt || ff'} result: true --- title: 'or operator (3)' context: {tt: true, ff: false} template: {$eval: 'ff || tt'} result: true --- title: 'or operator (4)' context: {tt: true, ff: false} template: {$eval: 'ff || ff'} result: false --- title: 'and operator (1)' context: {tt: true, ff: false} template: {$eval: 'tt && tt'} result: true --- title: 'and operator (2)' context: {tt: true, ff: false} template: {$eval: 'tt && ff'} result: false --- title: 'and operator (3)' context: {tt: true, ff: false} template: {$eval: 'ff && tt'} result: false --- title: 'and operator (4)' context: {tt: true, ff: false} template: {$eval: 'ff && ff'} result: false --- title: 'and operator with not operator' context: {tt: true, ff: false} template: {$eval: 'tt && !ff'} result: true --- title: 'or operator with not operator' context: {tt: true, ff: false} template: {$eval: 'ff || !ff'} result: true --- title: 'complex logical operation (1)' context: {} template: {$eval: "4 < 6 && 2 == 1 + 1"} result: true --- title: 'complex logical operation (2)' context: {} template: {$eval: "!(4 < 6) && 2 == 1 + 1"} result: false --- title: 'complex logical operation (3)' context: {} template: {$eval: "2 == 1 + 1 && 4 < 6"} result: true --- title: 'complex logical operation (4)' context: {} template: {$eval: "4 >= 6 || 2 == 1 + 1"} result: true --- title: 'string not' context: {a: 'abc'} template: {$eval: "!a"} result: false --- title: 'empty string not' context: {a: ''} template: {$eval: "!a"} result: true --- title: 'number not' context: {a: 123} template: {$eval: "!a"} result: false --- title: 'zero not' context: {a: 0} template: {$eval: "!a"} result: true --- title: 'null not' context: {a: null} template: {$eval: "!a"} result: true --- title: 'object not' context: {a: {x: 1}} template: {$eval: "!a"} result: false --- title: 'empty object not' context: {a: {}} template: {$eval: "!a"} result: true --- title: 'array not' context: {a: [1, 2]} template: {$eval: "!a"} result: false --- title: 'empty array not' context: {a: []} template: {$eval: "!a"} result: true --- title: 'function not' context: {} template: {$eval: "!min"} result: false --- title: 'string and string' context: {a: 'abc', b: 'abc'} template: {$eval: "a && b"} result: true --- title: 'string or string' context: {a: 'abc', b: 'abc'} template: {$eval: "a || b"} result: true --- title: 'string and number' context: {a: 'abc', b: 123} template: {$eval: "a && b"} result: true --- title: 'string or number' context: {a: 'abc', b: 123} template: {$eval: "a || b"} result: true --- title: 'string and null' context: {a: 'abc', b: null} template: {$eval: "a && b"} result: false --- title: 'string or null' context: {a: 'abc', b: null} template: {$eval: "a || b"} result: true --- title: 'string and boolean' context: {a: 'abc', b: true} template: {$eval: "a && b"} result: true --- title: 'string or boolean' context: {a: 'abc', b: true} template: {$eval: "a || b"} result: true --- title: 'string and object' context: {a: 'abc', b: {}} template: {$eval: "a && b"} result: false --- title: 'string or object' context: {a: 'abc', b: {}} template: {$eval: "a || b"} result: true --- title: 'string and array' context: {a: 'abc', b: []} template: {$eval: "a && b"} result: false --- title: 'string or array' context: {a: 'abc', b: []} template: {$eval: "a || b"} result: true --- title: 'number and string' context: {a: 345, b: 'abc'} template: {$eval: "a && b"} result: true --- title: 'number or string' context: {a: 345, b: 'abc'} template: {$eval: "a || b"} result: true --- title: 'number and number' context: {a: 345, b: 123} template: {$eval: "a && b"} result: true --- title: 'number or number' context: {a: 345, b: 123} template: {$eval: "a || b"} result: true --- title: 'number and null' context: {a: 345, b: null} template: {$eval: "a && b"} result: false --- title: 'number or null' context: {a: 345, b: null} template: {$eval: "a || b"} result: true --- title: 'number and boolean' context: {a: 345, b: true} template: {$eval: "a && b"} result: true --- title: 'number or boolean' context: {a: 345, b: true} template: {$eval: "a || b"} result: true --- title: 'number and object' context: {a: 345, b: {}} template: {$eval: "a && b"} result: false --- title: 'number or object' context: {a: 345, b: {}} template: {$eval: "a || b"} result: true --- title: 'number and array' context: {a: 345, b: []} template: {$eval: "a && b"} result: false --- title: 'number or array' context: {a: 345, b: []} template: {$eval: "a || b"} result: true --- title: 'null and string' context: {a: null, b: 'abc'} template: {$eval: "a && b"} result: false --- title: 'null or string' context: {a: null, b: 'abc'} template: {$eval: "a || b"} result: true --- title: 'null and number' context: {a: null, b: 123} template: {$eval: "a && b"} result: false --- title: 'null or number' context: {a: null, b: 123} template: {$eval: "a || b"} result: true --- title: 'null and null' context: {a: null, b: null} template: {$eval: "a && b"} result: false --- title: 'null or null' context: {a: null, b: null} template: {$eval: "a || b"} result: false --- title: 'null and boolean' context: {a: null, b: true} template: {$eval: "a && b"} result: false --- title: 'null or boolean' context: {a: null, b: true} template: {$eval: "a || b"} result: true --- title: 'null and object' context: {a: null, b: {}} template: {$eval: "a && b"} result: false --- title: 'null or object' context: {a: null, b: {}} template: {$eval: "a || b"} result: false --- title: 'null and array' context: {a: null, b: []} template: {$eval: "a && b"} result: false --- title: 'null or array' context: {a: null, b: []} template: {$eval: "a || b"} result: false --- title: 'boolean and string' context: {a: true, b: 'abc'} template: {$eval: "a && b"} result: true --- title: 'boolean or string' context: {a: true, b: 'abc'} template: {$eval: "a || b"} result: true --- title: 'boolean and number' context: {a: true, b: 123} template: {$eval: "a && b"} result: true --- title: 'boolean or number' context: {a: true, b: 123} template: {$eval: "a || b"} result: true --- title: 'boolean and null' context: {a: true, b: null} template: {$eval: "a && b"} result: false --- title: 'boolean or null' context: {a: true, b: null} template: {$eval: "a || b"} result: true --- title: 'boolean and boolean' context: {a: true, b: true} template: {$eval: "a && b"} result: true --- title: 'boolean or boolean' context: {a: true, b: true} template: {$eval: "a || b"} result: true --- title: 'boolean and object' context: {a: true, b: {}} template: {$eval: "a && b"} result: false --- title: 'boolean or object' context: {a: true, b: {}} template: {$eval: "a || b"} result: true --- title: 'boolean and array' context: {a: true, b: []} template: {$eval: "a && b"} result: false --- title: 'boolean or array' context: {a: true, b: []} template: {$eval: "a || b"} result: true --- title: 'object and string' context: {a: {}, b: 'abc'} template: {$eval: "a && b"} result: false --- title: 'object or string' context: {a: {}, b: 'abc'} template: {$eval: "a || b"} result: true --- title: 'object and number' context: {a: {}, b: 123} template: {$eval: "a && b"} result: false --- title: 'object or number' context: {a: {}, b: 123} template: {$eval: "a || b"} result: true --- title: 'object and null' context: {a: {}, b: null} template: {$eval: "a && b"} result: false --- title: 'object or null' context: {a: {}, b: null} template: {$eval: "a || b"} result: false --- title: 'object and boolean' context: {a: {}, b: true} template: {$eval: "a && b"} result: false --- title: 'object or boolean' context: {a: {}, b: true} template: {$eval: "a || b"} result: true --- title: 'object and object' context: {a: {}, b: {}} template: {$eval: "a && b"} result: false --- title: 'object or object' context: {a: {}, b: {}} template: {$eval: "a || b"} result: false --- title: 'object and array' context: {a: {}, b: []} template: {$eval: "a && b"} result: false --- title: 'object or array' context: {a: {}, b: []} template: {$eval: "a || b"} result: false --- title: 'array and string' context: {a: [], b: 'abc'} template: {$eval: "a && b"} result: false --- title: 'array or string' context: {a: [], b: 'abc'} template: {$eval: "a || b"} result: true --- title: 'array and number' context: {a: [], b: 123} template: {$eval: "a && b"} result: false --- title: 'array or number' context: {a: [], b: 123} template: {$eval: "a || b"} result: true --- title: 'array and null' context: {a: [], b: null} template: {$eval: "a && b"} result: false --- title: 'array or null' context: {a: [], b: null} template: {$eval: "a || b"} result: false --- title: 'array and boolean' context: {a: [], b: true} template: {$eval: "a && b"} result: false --- title: 'array or boolean' context: {a: [], b: true} template: {$eval: "a || b"} result: true --- title: 'array and object' context: {a: [], b: {}} template: {$eval: "a && b"} result: false --- title: 'array or object' context: {a: [], b: {}} template: {$eval: "a || b"} result: false --- title: 'array and array' context: {a: [], b: []} template: {$eval: "a && b"} result: false --- title: 'array or array' context: {a: [], b: []} template: {$eval: "a || b"} result: false --- title: 'short-circuit evaluation for and' context: {} template: {$eval: "false && b"} result: false --- title: 'short-circuit evaluation for or' context: {} template: {$eval: "true || b"} result: true ################################################################################ --- section: expression language - string operations --- title: 'string concatenation (1)' context: {a: 3, b: 2} template: {$eval: '"a" + "b"'} result: 'ab' --- title: 'TypeError: string + number (1)' context: {a: 3, b: 2} template: {$eval: '"a" + b'} error: 'InterpreterError: infix: + expects numbers/strings + numbers/strings' --- title: 'TypeError: string + number (2)' context: {a: 3, b: 2} template: {$eval: '"" + a + b'} error: 'InterpreterError: infix: + expects numbers/strings + numbers/strings' --- title: 'TypeError: string + number (3)' context: {a: 3, b: 2} template: {$eval: '"" + (a + b)'} error: 'InterpreterError: infix: + expects numbers/strings + numbers/strings' --- title: 'TypeError: number + string (4)' context: {a: 3, b: 2} template: {$eval: '(a + b) + "abc"'} error: 'InterpreterError: infix: + expects numbers/strings + numbers/strings' --- title: 'string indexing (1)' context: {key: '12345'} template: {$eval: 'key[1]'} result: '2' --- title: 'string indexing (2)' context: {key: '12345'} template: {$eval: 'key[-2]'} result: '4' --- title: 'string indexing (3)' context: {} template: {$eval: '"12345"[1]'} result: '2' --- title: 'string indexing (4)' context: {} template: {$eval: '"12345"[-2]'} result: '4' --- title: 'string indexing (unicode)' context: {key: "\u2622abc\U0001F601"} template: {$eval: '[key[0], key[2], key[-1]]'} result: ["\u2622", "b", "\U0001F601"] --- title: 'string indexing (noninteger)' context: {} template: {$eval: '"12345"[2.5]'} error: 'InterpreterError: should only use integers to access arrays or strings' --- title: 'string slicing (1)' context: {key: '12345'} template: {$eval: 'key[1:-1]'} result: '234' --- title: 'string slicing (2)' context: {key: '12345'} template: {$eval: 'key[-2:]'} result: '45' --- title: 'string slicing (noninteger first index)' context: {key: '12345'} template: {$eval: 'key[1.5:3]'} error: 'InterpreterError: cannot perform interval access with non-integers' --- title: 'string slicing (noninteger second index)' context: {key: '12345'} template: {$eval: 'key[1:3.5]'} error: 'InterpreterError: cannot perform interval access with non-integers' --- title: 'string slicing (noninteger indexes)' context: {key: '12345'} template: {$eval: 'key[1.5:3.5]'} error: 'InterpreterError: cannot perform interval access with non-integers' --- title: 'string slicing type error (1)' context: {key: '12345'} template: {$eval: 'key["a":]'} error: 'InterpreterError: cannot perform interval access with non-integers' --- title: 'string slicing type error (2)' context: {key: '12345'} template: {$eval: 'key[:"a"]'} error: 'InterpreterError: cannot perform interval access with non-integers' --- title: 'string length attribute is an error' context: {key: '12345'} template: {$eval: 'key.length'} error: 'InterpreterError: infix: . expects objects' --- title: 'string length attribute is an error even by index' context: {key: '12345'} template: {$eval: 'key["length"]'} error: 'InterpreterError: should only use integers to access arrays or strings' --- title: 'other string attributes are not set' context: {key: 'abc'} template: {$eval: 'key.toUpperCase()'} error: 'InterpreterError: infix: . expects objects' --- title: 'non-identifier after dot' context: {key: {}} template: {$eval: 'key.{'} error: 'SyntaxError: Found: { token, expected one of: identifier' ################################################################################ --- section: expression language - property access --- title: 'property with numeric value' context: {key: 1} template: {$eval: 'key'} result: 1 --- title: 'property with string value' context: {key: 'a'} template: {$eval: 'key'} result: 'a' --- title: 'property with boolean value' context: {key: true} template: {$eval: 'key'} result: true --- title: 'property with object value' context: {key: {a: 1}} template: {$eval: 'key'} result: {a: 1} --- title: 'property with null value' context: {key: 'null'} template: {$eval: 'key'} result: 'null' --- title: 'property that is also a function name' context: {min: abc} template: {$eval: 'min'} result: abc --- title: 'missing property' context: {key: {a: 1}} template: {$eval: 'key.b'} error: 'InterpreterError: object has no property "b"' --- title: 'missing property by name' context: {key: {a: 1}} template: {$eval: 'key["b"]'} result: null --- title: 'single property access with object value' context: {key: {key2: 3}} template: {$eval: 'key.key2'} result: 3 --- title: 'nested property access with object value' context: {key: {key2: {key3: {a: 1}}}} template: {$eval: 'key.key2.key3'} result: {a: 1} --- title: 'nested property access with numeric value' context: {key: {key2: {key3: 1}}} template: {$eval: 'key["key2"]["key3"]'} result: 1 --- title: 'nested property access with dot following ]' context: {key: {key2: {key3: 1}}} template: {$eval: 'key["key2"].key3'} result: 1 --- title: 'nested property with object in parentheses' context: {key: {key2: true}} template: {$eval: '(key)["key2"]'} result: true --- title: 'nested property with object expression' context: {keyid: 1, keys: [false, {key2: true}]} template: {$eval: '(keys[keyid])["key2"]'} result: true --- title: 'property access with expression value' context: {key: abc, values: {abc: 2, def: 3}} template: {$eval: 'values[key]'} result: 2 --- title: 'property starting with underscore' context: {_key: abc} template: {$eval: '_key'} result: abc --- title: 'property containing underscore' context: {my_key: abc} template: {$eval: 'my_key'} result: abc --- title: 'property containing hyphen' context: {my-key: abc} template: {$eval: 'my-key'} error: 'TemplateError: top level keys of context must follow /[a-zA-Z_][a-zA-Z0-9_]*/' --- title: 'property starting with capital' context: {Key: abc} template: {$eval: 'Key'} result: abc --- title: 'property containing capital' context: {kEy: abc} template: {$eval: 'kEy'} result: abc --- title: 'case sensitivity of keys' context: {key: abc} template: {$eval: 'Key'} error: 'InterpreterError: unknown context value Key' --- title: 'property starting with number' context: {'2fast': abc} template: {$eval: '2fast'} error: 'TemplateError: top level keys of context must follow /[a-zA-Z_][a-zA-Z0-9_]*/' --- title: 'property containing number' context: {furious5: abc} template: {$eval: 'furious5'} result: abc --- title: 'nested property starting with underscore' context: {key: {_foo: abc}} template: {$eval: 'key._foo'} result: abc --- title: 'nested property containing underscore' context: {key: {foo_bar: abc}} template: {$eval: 'key.foo_bar'} result: abc --- title: 'nested property containing hyphen' context: {key: {my-foo: abc}} template: {$eval: 'key.my-foo'} error: 'InterpreterError: object has no property "my"' --- title: 'nested property containing hyphen using brackets' context: {key: {my-foo: abc}} template: {$eval: "key['my-foo']"} result: abc --- title: 'nested property starting with capital' context: {key: {Foo: abc}} template: {$eval: 'key.Foo'} result: abc --- title: 'nested property containing capital' context: {key: {fOo: abc}} template: {$eval: 'key.fOo'} result: abc --- title: 'case sensitivity of nested keys' context: {key: {foo: abc}} template: {$eval: 'key.Foo'} error: 'InterpreterError: object has no property "Foo"' --- title: 'property access with number' context: obj: {'13': 15} key: 13 # note that this is the number 13, yet the object key is a string as required by JSON template: {$eval: 'obj[key]'} error: 'InterpreterError: object keys must be strings' --- title: 'property of number' context: {key: 123} template: {$eval: 'key.valueOf'} error: 'InterpreterError: infix: . expects objects' --- title: 'property of null' context: {key: null} template: {$eval: 'key.foo'} error: 'InterpreterError: infix: . expects objects' --- title: 'property of boolean' context: {key: null} template: {$eval: 'key.valueOf'} error: 'InterpreterError: infix: . expects objects' --- title: 'property of array' context: {key: []} template: {$eval: 'key.valueOf'} error: 'InterpreterError: infix: . expects objects' --- title: 'non-identifier after dot' context: {key: {}} template: {$eval: 'key.{'} error: 'SyntaxError: Found: { token, expected one of: identifier' --- title: 'non-identifier after dot (2)' context: {key: {}} template: {$eval: 'key.{'} error: 'SyntaxError: Found: { token, expected one of: identifier' ################################################################################ --- section: expression language - array access --- title: 'numeric' context: {key: [1,2,3,4,5]} template: {$eval: 'key[0]'} result: 1 --- title: 'numeric, nonzero index' context: {key: [1,2,3,4,5]} template: {$eval: 'key[2]'} result: 3 --- title: 'numeric, noninteger index' context: {key: [1,2,3,4,5]} template: {$eval: 'key[2.5]'} error: 'InterpreterError: should only use integers to access arrays or strings' --- title: 'arithemtic with results' context: {key: [1,2,3,4,5]} template: {$eval: 'key[0] + key[1] + key[2] + key[3] + key[4]'} result: 15 --- title: 'string index' context: {key: [1,2,3,4,5]} template: {$eval: 'key["a"]'} error: 'InterpreterError: should only use integers to access arrays or strings' --- title: 'too-large index' context: {key: [1,2,3,4,5]} template: {$eval: 'key[999]'} error: 'InterpreterError: index out of bounds' --- title: 'negative index' context: {key: [1,2,3,4,5]} template: {$eval: 'key[-1]'} result: 5 --- title: 'nested, in property accesses' context: {key: {key2: {key3: [1,2,3,4,5]}}} template: {$eval: 'key.key2.key3[0]'} result: 1 --- title: 'nested, in property access by string' context: {key: {key2: {key3: [1,2,3,4,5]}}} template: {$eval: 'key["key2"]["key3"][0]'} result: 1 --- title: 'nested, nonzero index' context: {key: {key2: {key3: [1,2,3,4,5]}}} template: {$eval: 'key.key2.key3[2]'} result: 3 --- title: 'nested, nonzero index, in property access by string' context: {key: {key2: {key3: [1,2,3,4,5]}}} template: {$eval: 'key["key2"]["key3"][2]'} result: 3 --- title: 'nested, with arithmetic' context: {key: {key2: {key3: [1,2,3,4,5]}}} template: {$eval: 'key.key2.key3[0] + key.key2.key3[1] + key.key2.key3[2] + key.key2.key3[3] + key.key2.key3[4]'} result: 15 --- title: 'nested, with arithmetic, in property access by string' context: {key: {key2: {key3: [1,2,3,4,5]}}} template: {$eval: 'key["key2"]["key3"][0] + key["key2"]["key3"][1] + key["key2"]["key3"][2] + key["key2"]["key3"][3] + key["key2"]["key3"][4]'} result: 15 --- title: 'indexing number' context: {key: 123} template: {$eval: 'key[2]'} error: 'InterpreterError: infix: "[..]" expects object, array, or string' --- title: 'indexing null' context: {key: null} template: {$eval: 'key[2]'} error: 'InterpreterError: infix: "[..]" expects object, array, or string' --- title: 'indexing boolean' context: {key: true} template: {$eval: 'key[2]'} error: 'InterpreterError: infix: "[..]" expects object, array, or string' --- title: 'indexing object' context: {key: {x: 10}} template: {$eval: 'key[2]'} error: 'InterpreterError: object keys must be strings' --- title: 'array length property is an error' context: {key: [1, 3, 5]} template: {$eval: 'key.length'} error: 'InterpreterError: infix: . expects objects' --- title: 'array length property is an error even by index' context: {key: [1, 3, 5]} template: {$eval: 'key["length"]'} error: 'InterpreterError: should only use integers to access arrays or strings' --- title: 'other array attributes are not available' context: {key: [5, 3, 1]} template: {$eval: 'key.sort'} error: 'InterpreterError: infix: . expects objects' ################################################################################ --- section: expression language - slicing --- title: 'array slicing (full slice)' context: {key: [1,2,3,4,5]} template: {$eval: 'key[0:5]'} result: [1,2,3,4,5] --- title: 'array slicing (prefix)' context: {key: [1,2,3,4,5]} template: {$eval: 'key[0:3]'} result: [1,2,3] --- title: 'array slicing (out of order)' context: {key: [1,2,3,4,5]} template: {$eval: 'key[3:2]'} result: [] --- title: 'array slicing (negative indices)' context: {key: [1,2,3,4,5]} template: {$eval: 'key[-5:-1]'} result: [1,2,3,4] --- title: 'array slicing (negative indices larger than length)' context: {key: [1,2,3,4,5]} template: {$eval: 'key[-10:-1]'} result: [1,2,3,4] --- title: 'array slicing (noninteger first index)' context: {key: [1,2,3,4,5]} template: {$eval: 'key[2.5: 4]'} error: true --- title: 'array slicing (noninteger last index)' context: {key: [1,2,3,4,5]} template: {$eval: 'key[2: 3.5]'} error: true --- title: 'array slicing (noninteger indexes)' context: {key: [1,2,3,4,5]} template: {$eval: 'key[2.5: 3.5]'} error: true --- title: 'array slicing (floor)' context: {key: [1,2,3,4,5]} template: {$eval: 'key[:floor(len(key)/2)]'} result: [1, 2] --- title: 'array slicing (no end index)' context: {key: [1,2,3,4,5]} template: {$eval: 'key[-4:]'} result: [2,3,4,5] --- title: 'array slicing (no start index)' context: {key: [1,2,3,4,5]} template: {$eval: 'key[:-3]'} result: [1,2] --- title: 'array slicing type error (1)' context: {key: [1,2,3,4,5]} template: {$eval: 'key["a":]'} error: true --- title: 'array slicing type error (2)' context: {key: [1,2,3,4,5]} template: {$eval: 'key[:"a"]'} error: true --- title: 'array slicing type error (3)' context: {key: [1,2,3,4,5]} template: {$eval: 'key["a":"b"]'} error: true --- title: 'array slicing type error (4)' context: {key: [1,2,3,4,5]} template: {$eval: 'key[1:"a"]'} error: true --- title: 'array slicing type error (5)' context: {key: [1,2,3,4,5]} template: {$eval: 'key["a":2]'} error: true --- title: 'slicing number' context: {key: 123} template: {$eval: 'key[2:]'} error: true --- title: 'slicing null' context: {key: null} template: {$eval: 'key[2:]'} error: true --- title: 'slicing boolean' context: {key: true} template: {$eval: 'key[2:]'} error: true --- title: 'slicing object' context: {key: {x: 10}} template: {$eval: 'key[2:]'} error: true --- title: 'string slicing - full' context: {key: "hello"} template: {$eval: 'key[0:6]'} result: "hello" --- title: 'string slicing - prefix' context: {key: "hello"} template: {$eval: 'key[0:3]'} result: "hel" --- title: 'string slicing - omitted first index' context: {key: "hello"} template: {$eval: 'key[:3]'} result: "hel" --- title: 'string slicing - omitted second index' context: {key: "hello"} template: {$eval: 'key[3:]'} result: "lo" --- title: 'string slicing - negative index, omitted second index' context: {key: "hello"} template: {$eval: 'key[-3:]'} result: "llo" --- title: 'string slicing - negative index' context: {key: "hello"} template: {$eval: 'key[-3:4]'} result: "ll" --- title: 'string slicing - negative greater than length' context: {key: "hello"} template: {$eval: 'key[-9:]'} result: "hello" --- title: 'string slicing - unicode indices' context: {key: "\u2622abc\U0001F601"} template: {$eval: '[key[0:1], key[1:4], key[-1:]]'} result: ["\u2622", "abc", "\U0001F601"] ################################################################################ --- section: expression language - function calls --- title: 'function call (1)' context: {a: 3, b: 2} template: {$eval: 'min(a, b)'} result: 2 --- title: 'function call (2)' context: {a: 3, b: 2} template: {$eval: 'min(3, b)'} result: 2 --- title: 'function call (3)' context: {a: 3, b: 2} template: {$eval: 'min(a, 2)'} result: 2 --- title: 'function call (4)' context: {a: 3, b: 2} template: {$eval: 'min(3, 2)'} result: 2 --- title: 'function call (5)' context: {a: 3, b: 2} template: {$eval: 'max(a, b)'} result: 3 --- title: 'function call (6)' context: {a: 3, b: 2} template: {$eval: 'max(3, b)'} result: 3 --- title: 'function call (7)' context: {a: 3, b: 2} template: {$eval: 'max(a, 2)'} result: 3 --- title: 'function call (8)' context: {a: 3, b: 2} template: {$eval: 'max(3, 2)'} result: 3 --- title: 'function call (9)' context: {a: 3, b: 2} template: {$eval: 'min(a, b) + max(a, b)'} result: 5 --- title: 'function call (10)' context: {a: 3, b: 2} template: {$eval: 'min(3, b) + max(3, b)'} result: 5 --- title: 'function call (11)' context: {a: 3, b: 2} template: {$eval: 'min(a, 2) + max(a, 2)'} result: 5 --- title: 'function call (12)' context: {a: 3, b: 2} template: {$eval: 'min(3, 2) + max(3, 2)'} result: 5 --- title: 'function call (13)' context: {} template: {$let: {functions: [{$eval: min}, {$eval: max}]}, in: {$eval: 'functions[1](2, 3)'}} result: 3 --- title: 'function call (14)' context: {} template: {$eval: '[max,min][1](5, 1)'} result: 1 --- title: 'function name is not callable' context: {function: "hi"} template: {$eval: "function(10)"} error: "InterpreterError: hi is not callable" --- title: 'parenthesized function expression' context: {} template: {$eval: "(min)(10, 20)"} result: 10 --- title: 'dotted function expression' context: {} template: {$let: {negated: {max: {$eval: min}, min: {$eval: max}}}, in: {$eval: "(negated.min)(10, 20)"}} result: 20 --- title: accessor after function call context: {} template: {$eval: 'split("left:right", ":")[1]'} result: right ################################################################################ --- section: expression language - in operator --- title: 'in operator on string, success (1)' context: {} template: {$eval: '"abc" in "aabc"'} result: true --- title: 'in operator on string, failure (2)' context: {} template: {$eval: '"efg" in "aabc"'} result: false --- title: 'in operator on string, success (3)' context: {key: 'aabc'} template: {$eval: '"abc" in key'} result: true --- title: 'in operator on string, failure (4)' context: {key: 'aabc'} template: {$eval: '"efg" in key'} result: false --- title: 'in operator on string, failure (5)' context: {key: 'aabc'} template: {$eval: '"2" in key'} result: false --- title: 'in operator on array, success (1)' context: {} template: {$eval: '"abc" in [1, "abc", "def"]'} result: true --- title: 'in operator on array, success (2)' context: {} template: {$eval: '1 in [1, "abc", "def"]'} result: true --- title: 'in operator on array, success (3)' context: {key: [1, 'abc', 'def']} template: {$eval: '"abc" in key'} result: true --- title: 'in operator on array, success (3)' context: {key: [1, 'abc', 'def']} template: {$eval: '1 in key'} result: true --- title: 'in operator on array, failure (1)' context: {} template: {$eval: '"efg" in [1, "abc", "def"]'} result: false --- title: 'in operator on array, failure (2)' context: {} template: {$eval: '2 in [1, "abc", "def"]'} result: false --- title: 'in operator on array, failure (2)' context: {key: [1, 'abc', 'def']} template: {$eval: '"efg" in key'} result: false --- title: 'in operator on array, failure (4)' context: {key: [1, 'abc', 'def']} template: {$eval: '2 in key'} result: false --- title: 'in operator on object, success (1)' context: {} template: {$eval: '"a" in {"a": 1, b: 2}'} result: true --- title: 'in operator on object, success (2)' context: {key: {a: 1, b: 2}} template: {$eval: '"a" in key'} result: true --- title: 'in operator on object, success (3)' context: {} template: {$eval: '"3.4" in {"3.4": 1, "b": 2}'} result: true --- title: 'in operator on object, failure (1)' context: {} template: {$eval: '"c" in {a: 1, "b": 2}'} result: false --- title: 'in operator on object, failure (2)' context: {key: {a: 1, b: 2}} template: {$eval: '"c" in key'} result: false --- title: 'in operator on object, failure (3)' context: {} template: {$eval: '"a" in {"3": 1, "b": 2}'} result: false --- title: 'in operator on object, failure (4)' context: {} template: {$eval: '"3.4" in {"3": 1, "b": 2}'} result: false --- title: 'TypeError: in operator on Object, error (1)' context: {key: {a: 1, b: 2}} template: {$eval: '[] in key'} error: true --- title: 'TypeError: in operator on Object, error (2)' context: {key: {a: 1, b: 2}} template: {$eval: '{} in key'} error: true --- title: 'TypeError: in operator on Object, error (3)' context: {key: {a: 1, b: 2}} template: {$eval: '1 in key'} error: true --- title: 'TypeError: in operator on Array, Array not found' context: {key: [2,3,4]} template: {$eval: '[] in key'} result: false --- title: 'TypeError: in operator on String, error (1)' context: {key: 'hello world!'} template: {$eval: '[] in key'} error: true --- title: 'TypeError: in operator on String, error (2)' context: {key: 'hello world!'} template: {$eval: '{} in key'} error: true --- title: 'TypeError: in operator on String, error (3)' context: {key: 'hello world!'} template: {$eval: '1 in key'} error: true --- title: 'TypeError: in operator on Array, object not found' context: {key: [2,3,4]} template: {$eval: '{} in key'} result: false --- title: 'no type conversion from num to string in array' context: {} template: {$eval: '5 in ["5", "five"]'} result: false --- title: 'no type conversion from string to num in array' context: {} template: {$eval: '"5" in [5]'} result: false --- title: 'cannot search for num in object' context: {} template: {$eval: '5 in {"5": "five"}'} error: true --- title: 'deep equality, object on left side, found' context: {} template: {$eval: '{a: 45} in ["string", 46, {a: 45}]'} result: true --- title: 'deep equality, object on left side, not found' context: {} template: {$eval: '{a: {b: 13}} in ["string", 46, {a: {b: 14}}]'} result: false --- title: 'deep equality, Array on left side, found' context: {} template: {$eval: '[44, 45] in ["string", 46, [44, 45]]'} result: true --- title: 'deep equality, Array on left side, not found' context: {} template: {$eval: '[44, 45] in ["string", 46, [44]]'} result: false --- title: 'precedence of in over ||' context: {a: 'a', b: 'ab'} template: {$eval: "a in b || true"} result: true --- title: 'precedence of in over &&' context: {a: 'a', b: 'bc'} template: {$eval: "a in b && true"} result: false --- title: 'compare precedence of in and ==' context: {a: 'a', b: 'ab', x: 5,'y': 7 } template: {$eval: "a in b || x==y"} result: true ################################################################################ --- section: expression language - comparisons --- title: 'equality (1)' context: {a: 1, b: 1} template: {$eval: '1 == 1'} result: true --- title: 'equality (2)' context: {a: 1, b: 1} template: {$eval: '1 == 2'} result: false --- title: equality (3) context: {} template: {$eval: '4 == 3.2 + 0.8'} result: true --- title: 'equality (4)' context: {a: 1, b: 1} template: {$eval: 'a == b'} result: true --- title: 'equality (5)' context: {a: 1, b: 2} template: {$eval: 'a == b'} result: false --- title: object equality, true context: {a: {x: 1,'y': 2}, b: {x: 1,'y': 2}} template: {$eval: 'a == b'} result: true --- title: object equality, false context: {a: {x: 1,'y': 2}, b: {x: 2,'y': 2}} template: {$eval: 'a == b'} result: false --- title: object in-equality, true context: {a: {x: 1,'y': 2}, b: {x: 2,'y': 2}} template: {$eval: 'a != b'} result: true --- title: object in-equality, false context: {a: {x: 1,'y': 2}, b: {x: 1,'y': 2}} template: {$eval: 'a != b'} result: false --- title: complex object equality, true context: {a: {x: 1,'y': {e: {g: {one: 1}}}}, b: {x: 1,'y': {e: {g: {one: 1}}}}} template: {$eval: 'a == b'} result: true --- title: complex object equality, false context: {a: {x: 1,'y': {e: {g: {one: 1}}}}, b: {x: 1,'y': {e: {g: {two: 2}}}}} template: {$eval: 'a == b'} result: false --- title: complex object in-equality, true context: {a: {x: 1,'y': {e: {g: {one: 1}}}}, b: {x: 1,'y': {e: {g: {two: 2}}}}} template: {$eval: 'a != b'} result: true --- title: complex object in-equality, false context: {a: {x: 1,'y': {e: {g: {one: 1}}}}, b: {x: 1,'y': {e: {g: {one: 1}}}}} template: {$eval: 'a != b'} result: false --- title: 'in-equality (1)' context: {a: 1, b: 1} template: {$eval: '1 != 1'} result: false --- title: 'in-equality (2)' context: {a: 1, b: 1} template: {$eval: '1 != 2'} result: true --- title: 'in-equality (3)' context: {a: 1, b: 1} template: {$eval: 'a != b'} result: false --- title: 'in-equality (4)' context: {a: 1, b: 2} template: {$eval: 'a != b'} result: true --- title: 'less than (1)' context: {a: 1, b: 2} template: {$eval: '1 < 2'} result: true --- title: 'less than (2)' context: {a: 1, b: 2} template: {$eval: '2 < 1'} result: false --- title: 'less than (3)' context: {a: 1, b: 2} template: {$eval: '"a" < "b"'} result: true --- title: 'less than (4)' context: {a: 1, b: 2} template: {$eval: '"b" < "a"'} result: false --- title: 'less than (5)' context: {a: 1, b: 2} template: {$eval: 'a < b'} result: true --- title: 'less than (6)' context: {a: 1, b: 2} template: {$eval: 'b < a'} result: false --- title: 'greater than (1)' context: {a: 1, b: 2} template: {$eval: '2 > 1'} result: true --- title: 'greater than (2)' context: {a: 1, b: 2} template: {$eval: '1 > 2'} result: false --- title: 'greater than (3)' context: {a: 1, b: 2} template: {$eval: '"b" > "a"'} result: true --- title: 'greater than (4)' context: {a: 1, b: 2} template: {$eval: '"a" > "b"'} result: false --- title: 'greater than (5)' context: {a: 1, b: 2} template: {$eval: 'b > a'} result: true --- title: 'greater than (6)' context: {a: 1, b: 2} template: {$eval: 'a > b'} result: false --- title: 'less than equal (1)' context: {a: 1, b: 2} template: {$eval: "a <= b"} result: true --- title: 'less than equal (2)' context: {a: 1, b: 1} template: {$eval: "a <= b"} result: true --- title: 'less than equal (3)' context: {a: 1, b: 2} template: {$eval: "b <= a"} result: false --- title: 'less than equal (4)' context: {a: 1, b: 1} template: {$eval: "b <= a"} result: true --- title: 'greater than equal (1)' context: {a: 1, b: 2} template: {$eval: "b >= a"} result: true --- title: 'greater than equal (2)' context: {a: 1, b: 1} template: {$eval: "a >= b"} result: true --- title: 'greater than equal (3)' context: {a: 1, b: 2} template: {$eval: "a >= b"} result: false --- title: 'greater than equal (4)' context: {a: 1, b: 1} template: {$eval: "a >= b"} result: true --- title: 'deep object equality' context: a: {left: 1, right: {left: 2, right: 3}} b: {left: 1, right: {left: 2, right: 3}} template: {$eval: "a == b"} result: true --- title: 'boolean equality' context: {} template: {$eval: "true == true"} result: true --- title: 'null equality' context: {a: null, b: null} template: {$eval: "a == b"} result: true --- title: 'string equality' context: {a: 'xyz', b: 'xyz'} template: {$eval: "a == b"} result: true --- title: 'deep object inequality' context: a: {left: 1, right: {left: 2, right: 3}} b: {left: 1, right: {left: 3, right: 2}} template: {$eval: "a != b"} result: true --- title: 'deep array equality' context: a: [1, [2, 3]] b: [1, [2, 3]] template: {$eval: "a == b"} result: true --- title: 'deep array inequality' context: a: [1, [2, 3]] b: [1, [3, 2]] template: {$eval: "a != b"} result: true --- title: ordering of string and string context: {a: 'abc', b: 'def'} template: {$eval: 'a < b'} result: true --- title: ordering of string and number context: {a: 'abc', b: 27} template: {$eval: 'a < b'} error: true --- title: ordering of string and null context: {a: 'abc', b: null} template: {$eval: 'a < b'} error: true --- title: ordering of string and boolean context: {a: 'abc', b: true} template: {$eval: 'a < b'} error: true --- title: ordering of string and object context: {a: 'abc', b: {}} template: {$eval: 'a < b'} error: true --- title: ordering of string and array context: {a: 'abc', b: []} template: {$eval: 'a < b'} error: true --- title: ordering of number and string context: {a: 123, b: 'def'} template: {$eval: 'a < b'} error: true --- title: ordering of number and number context: {a: 123, b: 27} template: {$eval: 'a < b'} result: false --- title: ordering of number and null context: {a: 123, b: null} template: {$eval: 'a < b'} error: true --- title: ordering of number and boolean context: {a: 123, b: true} template: {$eval: 'a < b'} error: true --- title: ordering of number and object context: {a: 123, b: {}} template: {$eval: 'a < b'} error: true --- title: ordering of number and array context: {a: 123, b: []} template: {$eval: 'a < b'} error: true --- title: ordering of null and string context: {a: null, b: 'def'} template: {$eval: 'a < b'} error: true --- title: ordering of null and number context: {a: null, b: 27} template: {$eval: 'a < b'} error: true --- title: ordering of null and null context: {a: null, b: null} template: {$eval: 'a < b'} error: true --- title: ordering of null and boolean context: {a: null, b: true} template: {$eval: 'a < b'} error: true --- title: ordering of null and object context: {a: null, b: {}} template: {$eval: 'a < b'} error: true --- title: ordering of null and array context: {a: null, b: []} template: {$eval: 'a < b'} error: true --- title: ordering of boolean and string context: {a: true, b: 'def'} template: {$eval: 'a < b'} error: true --- title: ordering of boolean and number context: {a: true, b: 27} template: {$eval: 'a < b'} error: true --- title: ordering of boolean and null context: {a: true, b: null} template: {$eval: 'a < b'} error: true --- title: ordering of boolean and boolean context: {a: true, b: true} template: {$eval: 'a < b'} error: true --- title: ordering of boolean and object context: {a: true, b: {}} template: {$eval: 'a < b'} error: true --- title: ordering of boolean and array context: {a: true, b: []} template: {$eval: 'a < b'} error: true --- title: ordering of object and string context: {a: {}, b: 'def'} template: {$eval: 'a < b'} error: true --- title: ordering of object and number context: {a: {}, b: 27} template: {$eval: 'a < b'} error: true --- title: ordering of object and null context: {a: {}, b: null} template: {$eval: 'a < b'} error: true --- title: ordering of object and boolean context: {a: {}, b: true} template: {$eval: 'a < b'} error: true --- title: ordering of object and object context: {a: {}, b: {}} template: {$eval: 'a < b'} error: true --- title: ordering of object and array context: {a: {}, b: []} template: {$eval: 'a < b'} error: true --- title: ordering of array and string context: {a: [], b: 'def'} template: {$eval: 'a < b'} error: true --- title: ordering of array and number context: {a: [], b: 27} template: {$eval: 'a < b'} error: true --- title: ordering of array and null context: {a: [], b: null} template: {$eval: 'a < b'} error: true --- title: ordering of array and boolean context: {a: [], b: true} template: {$eval: 'a < b'} error: true --- title: ordering of array and array context: {a: [], b: {}} template: {$eval: 'a < b'} error: true --- title: ordering of array and array context: {a: [], b: []} template: {$eval: 'a < b'} error: true --- title: equality of string and string context: {a: 'abc', b: 'def'} template: {$eval: 'a == b'} result: false --- title: equality of string and number context: {a: 'abc', b: 27} template: {$eval: 'a == b'} result: false --- title: equality of string and null context: {a: 'abc', b: null} template: {$eval: 'a == b'} result: false --- title: equality of string and boolean context: {a: 'abc', b: true} template: {$eval: 'a == b'} result: false --- title: equality of string and object context: {a: 'abc', b: {}} template: {$eval: 'a == b'} result: false --- title: equality of string and array context: {a: 'abc', b: []} template: {$eval: 'a == b'} result: false --- title: equality of number and string context: {a: 123, b: 'def'} template: {$eval: 'a == b'} result: false --- title: equality of number and number context: {a: 123, b: 27} template: {$eval: 'a == b'} result: false --- title: equality of number and null context: {a: 123, b: null} template: {$eval: 'a == b'} result: false --- title: equality of number and boolean context: {a: 123, b: true} template: {$eval: 'a == b'} result: false --- title: equality of number and object context: {a: 123, b: {}} template: {$eval: 'a == b'} result: false --- title: equality of number and array context: {a: 123, b: []} template: {$eval: 'a == b'} result: false --- title: equality of null and string context: {a: null, b: 'def'} template: {$eval: 'a == b'} result: false --- title: equality of null and number context: {a: null, b: 27} template: {$eval: 'a == b'} result: false --- title: equality of null and null context: {a: null, b: null} template: {$eval: 'a == b'} result: true --- title: equality of null and boolean context: {a: null, b: true} template: {$eval: 'a == b'} result: false --- title: equality of null and object context: {a: null, b: {}} template: {$eval: 'a == b'} result: false --- title: equality of null and array context: {a: null, b: []} template: {$eval: 'a == b'} result: false --- title: equality of boolean and string context: {a: true, b: 'def'} template: {$eval: 'a == b'} result: false --- title: equality of boolean and number context: {a: true, b: 27} template: {$eval: 'a == b'} result: false --- title: equality of boolean and null context: {a: true, b: null} template: {$eval: 'a == b'} result: false --- title: equality of boolean and boolean context: {a: true, b: true} template: {$eval: 'a == b'} result: true --- title: equality of boolean and object context: {a: true, b: {}} template: {$eval: 'a == b'} result: false --- title: equality of boolean and array context: {a: true, b: []} template: {$eval: 'a == b'} result: false --- title: equality of object and string context: {a: {}, b: 'def'} template: {$eval: 'a == b'} result: false --- title: equality of object and number context: {a: {}, b: 27} template: {$eval: 'a == b'} result: false --- title: equality of object and null context: {a: {}, b: null} template: {$eval: 'a == b'} result: false --- title: equality of object and boolean context: {a: {}, b: true} template: {$eval: 'a == b'} result: false --- title: equality of object and object context: {a: {}, b: {}} template: {$eval: 'a == b'} result: true --- title: equality of object and array context: {a: {}, b: []} template: {$eval: 'a == b'} result: false --- title: equality of array and string context: {a: [], b: 'def'} template: {$eval: 'a == b'} result: false --- title: equality of array and number context: {a: [], b: 27} template: {$eval: 'a == b'} result: false --- title: equality of array and null context: {a: [], b: null} template: {$eval: 'a == b'} result: false --- title: equality of array and boolean context: {a: [], b: true} template: {$eval: 'a == b'} result: false --- title: equality of array and object context: {a: [], b: {}} template: {$eval: 'a == b'} result: false --- title: equality of array and array context: {a: [], b: []} template: {$eval: 'a == b'} result: true --- title: equality of functions context: {} template: {$eval: 'min == min'} result: true --- title: inequality of functions context: {} template: {$eval: 'min == max'} result: false --- title: equality of function and string context: {} template: {$eval: 'min == "min"'} result: false --- title: equality of function and number context: {} template: {$eval: 'min == 13'} result: false ################################################################################ --- section: expression language - compound literals --- title: 'parse list (1)' context: {} template: {$eval: '[1,2,3,4]'} result: [1,2,3,4] --- title: 'parse list (2)' context: {} template: {$eval: '[1,2,3,4][0]'} result: 1 --- title: 'parse list (3)' context: {} template: {$eval: '[1,2,3,4][0:-1]'} result: [1,2,3] --- title: 'parse list (4)' context: {} template: {$eval: '[1,2,3,4'} error: true --- title: 'parse object (1)' context: {key: 1} template: {$eval: '{a: key, "b": key + 1}'} result: {a: 1, b: 2} --- title: 'parse object (2)' context: {key: 1} template: {$eval: '{"a": key, b: key + 1}'} result: {a: 1, b: 2} --- title: 'parse object (3)' context: {key: 1} template: {$eval: '{"a": key, "b": key + 1}'} result: {a: 1, b: 2} --- title: 'parse object (4)' context: {key: 1} template: {$eval: '{"a": key, b: [key,key+1,key+2]}'} result: {a: 1, b: [1,2,3]} --- title: 'parse object (5)' context: {key: 1} template: {$eval: '{a: key, "b": [key,key+1,key+2]}'} result: {a: 1, b: [1,2,3]} --- title: 'parse object (6)' context: {key: 1} template: {$eval: '{"a": key, "b": [key,key+1,key+2]}'} result: {a: 1, b: [1,2,3]} --- title: 'parse object (7)' context: {key: 1} template: {$eval: '{a: key, b: [key,key+1,key+2]}'} result: {a: 1, b: [1,2,3]} --- title: 'parse object (8)' context: {key: 1} template: {$eval: '{a: key, b: min(key + 1, 1)}'} result: {a: 1, b: 1} --- title: 'parse object (9)' context: {key: 1} template: {$eval: '{"a": key, b: min(key + 1, 1)}'} result: {a: 1, b: 1} --- title: 'parse object (10)' context: {key: 1} template: {$eval: '{a: key, "b": min(key + 1, 1)}'} result: {a: 1, b: 1} --- title: 'parse object (11)' context: {key: 1} template: {$eval: '{"a": key, "b": min(key + 1, 1)}'} result: {a: 1, b: 1} --- title: 'parse object (12)' context: {key: 1} template: {$eval: '{a: key, b: key + 1'} error: true ################################################################################ --- section: expression language - errors --- title: 'unary - type error' context: {key: 'hello'} template: {$eval: '-key'} error: 'InterpreterError: unary - expects number' --- title: 'unary + type error' context: {key: 'hello'} template: {$eval: '+key'} error: 'InterpreterError: unary + expects number' --- title: 'undefined variable' context: {key: 'hello'} template: {$eval: 'key2'} error: 'InterpreterError: unknown context value key2' --- title: 'missing closing parenthesis' context: {key1: 1, key2: 1} template: {$eval: 'key1 + (key2 + key1'} error: true --- title: 'missing opening parenthesis' context: {key1: 1, key2: 1} template: {$eval: 'key1 + key2 + key1)'} error: true --- title: 'missing closing "' context: {} template: {$eval: '"hello'} error: true --- title: 'missing opening "' context: {} template: {$eval: 'hello"'} error: "SyntaxError: Unexpected input for 'hello\"' at '\"'" --- title: 'Infix + type error' context: {} template: {$eval: 'true + false'} error: 'InterpreterError: infix: + expects numbers/strings + numbers/strings' --- title: 'Infix - type error' context: {} template: {$eval: '"hello" - 3'} error: 'InterpreterError: infix: - expects number - number' --- title: 'Infix * type error' context: {} template: {$eval: '"hello" * 3'} error: 'InterpreterError: infix: * expects number * number' --- title: 'Infix / type error' context: {} template: {$eval: '"hello" / 3'} error: 'InterpreterError: infix: / expects number / number' --- title: 'Infix - type error' context: {} template: {$eval: '3 - "hello"'} error: 'InterpreterError: infix: - expects number - number' --- title: 'Infix * type error' context: {} template: {$eval: '3 * "hello"'} error: 'InterpreterError: infix: * expects number * number' --- title: 'Infix / type error' context: {} template: {$eval: '3 / "hello"'} error: 'InterpreterError: infix: / expects number / number' --- title: 'Infix ** type error' context: {} template: {$eval: '"hello" ** 3'} error: 'InterpreterError: infix: ** expects number ** number' --- title: 'Infix ** type error' context: {} template: {$eval: '3 ** "hello"'} error: 'InterpreterError: infix: ** expects number ** number' --- title: 'Infix < type error' context: {} template: {$eval: '"hello" < 3'} error: 'InterpreterError: infix: < expects numbers/strings < numbers/strings' --- title: 'Infix > type error' context: {} template: {$eval: '"hello" > 3'} error: 'InterpreterError: infix: > expects numbers/strings > numbers/strings' --- title: 'Infix <= type error' context: {} template: {$eval: '"hello" <= 3'} error: 'InterpreterError: infix: <= expects numbers/strings <= numbers/strings' --- title: 'Infix >= type error' context: {} template: {$eval: '"hello" >= 3'} error: 'InterpreterError: infix: >= expects numbers/strings >= numbers/strings' --- title: 'Infix < type error' context: {} template: {$eval: '3 < "hello"'} error: 'InterpreterError: infix: < expects numbers/strings < numbers/strings' --- title: 'Infix > type error' context: {} template: {$eval: '3 > "hello"'} error: 'InterpreterError: infix: > expects numbers/strings > numbers/strings' --- title: 'Infix <= type error' context: {} template: {$eval: '3 <= "hello"'} error: 'InterpreterError: infix: <= expects numbers/strings <= numbers/strings' --- title: 'Infix >= type error' context: {} template: {$eval: '3 >= "hello"'} error: 'InterpreterError: infix: >= expects numbers/strings >= numbers/strings' --- title: 'Infix access array or string with non-integer type error' context: {extra: {attributes: 'a string!'}} template: {$if: "extra.attributes['someattr'] == 'x'", then: true} error: 'InterpreterError: should only use integers to access arrays or strings' --- title: 'Nested error has appropriate message' context: {} template: {foo: [1, {"123": [{$eval: 10}]}]} error: 'TemplateError at template.foo[1]["123"][0]: $eval must be given a string expression' --- title: 'object with missing value ' context: {} template: {$eval: '{a: 1, "b": }'} error: "SyntaxError: Found: } token, expected one of: !, (, +, -, [, false, identifier, null, number, string, true, {" --- title: 'object missing key and value after comma' context: {} template: {$eval: '{a: 1,'} error: "SyntaxError: Unexpected end of input" --- title: 'array missing value after comma' context: {} template: { $eval: '[2,1,]'} error: true --- title: 'list with missing first value' context: {} template: {$eval: '[,2,3]'} error: "SyntaxError: Found: , token, expected one of: !, (, +, -, [, false, identifier, null, number, string, true, {" --- title: 'list with missing value' context: {} template: {$eval: '[1,,3]'} error: "SyntaxError: Found: , token, expected one of: !, (, +, -, [, false, identifier, null, number, string, true, {" --- title: 'empty brackets in property access' context: {a: "0123456789"} template: {$eval: 'a[]'} error: "SyntaxError: Found: ] token, expected one of: !, (, +, -, [, false, identifier, null, number, string, true, {" --- title: 'function call with missing closing bracket' context: {} template: {$eval: 'max(2, 0'} error: "SyntaxError: Unexpected end of input" --- title: 'function call with missing argument in brackets' context: {} template: {$eval: 'max(, 0)'} error: "SyntaxError: Found: , token, expected one of: !, (, +, -, [, false, identifier, null, number, string, true, {" --- title: 'uncalled function' context: {} template: {$eval: 'fromNow'} error: "TemplateError: evaluated template contained uncalled functions" --- title: 'list expression with uncalled functions' context: {} template: {$eval: '[min, max]'} error: "TemplateError: evaluated template contained uncalled functions" --- title: 'list with uncalled function inside' context: {} template: [{$eval: 'min'}] error: "TemplateError: evaluated template contained uncalled functions" --- title: 'object containing uncalled function' context: {} template: {maximum: {$eval: 'max'}} error: "TemplateError: evaluated template contained uncalled functions" --- title: 'deep data structure with uncalled functions' context: {} template: [{minimum: {$eval: 'min'}}, {maximum: {$eval: 'max'}}] error: "TemplateError: evaluated template contained uncalled functions" --- title: '$json with an uncalled function' context: {} template: {$json: {$eval: 'lowercase'}} error: "TemplateError: evaluated template contained uncalled functions" --- title: '$json with deep uncalled functions' context: {} template: {$json: [{minimum: {$eval: 'min'}}, {maximum: {$eval: 'max'}}]} error: "TemplateError: evaluated template contained uncalled functions" ################################################################################ --- section: Regression tests --- title: issue 354 template: {$eval: 'max(3, 2)'} context: {} result: 3