From eaa4b76ede164ff84512a4dcd85c7053d3cea706 Mon Sep 17 00:00:00 2001 From: Nick O'Leary Date: Fri, 18 Nov 2016 16:38:48 +0000 Subject: [PATCH] Update jsonata version --- editor/vendor/jsonata/jsonata.js | 299 +++++++++++++++---------------- package.json | 2 +- 2 files changed, 150 insertions(+), 151 deletions(-) diff --git a/editor/vendor/jsonata/jsonata.js b/editor/vendor/jsonata/jsonata.js index 814bba28f..6c4ff877e 100644 --- a/editor/vendor/jsonata/jsonata.js +++ b/editor/vendor/jsonata/jsonata.js @@ -4,7 +4,6 @@ * This project is licensed under the MIT License, see LICENSE */ -'use strict'; /** * @module JSONata * @description JSON query and transformation language @@ -17,6 +16,8 @@ * @returns {{evaluate: evaluate, assign: assign}} Evaluated expression */ var jsonata = (function() { + 'use strict'; + var operators = { '.': 75, '[': 80, @@ -141,7 +142,7 @@ var jsonata = (function() { position += 4; } else { throw { - message: "The escape sequence \\u must be followed by 4 hex digits at column " + position, + message: "The escape sequence \\u must be followed by 4 hex digits", stack: (new Error()).stack, position: position }; @@ -149,7 +150,7 @@ var jsonata = (function() { } else { // illegal escape sequence throw { - message: 'unsupported escape sequence: \\' + currentChar + ' at column ' + position, + message: 'unsupported escape sequence: \\' + currentChar, stack: (new Error()).stack, position: position, token: currentChar @@ -165,7 +166,7 @@ var jsonata = (function() { position++; } throw { - message: 'no terminating quote found in string literal at column ' + position, + message: 'no terminating quote found in string literal', stack: (new Error()).stack, position: position }; @@ -180,7 +181,7 @@ var jsonata = (function() { return create('number', num); } else { throw { - message: 'Number out of range: ' + match[0] + ' at column ' + position, + message: 'Number out of range: ' + match[0], stack: (new Error()).stack, position: position, token: match[0] @@ -270,7 +271,7 @@ var jsonata = (function() { // unexpected end of buffer msg = "Syntax error: expected '" + id + "' before end of expression"; } else { - msg = "Syntax error: expected '" + id + "', got '" + node.id + "' at column " + node.position; + msg = "Syntax error: expected '" + id + "', got '" + node.id; } throw { message: msg , @@ -297,7 +298,7 @@ var jsonata = (function() { symbol = symbol_table[value]; if (!symbol) { throw { - message: "Unknown operator: " + value + " at column " + next_token.position, + message: "Unknown operator: " + value, stack: (new Error()).stack, position: next_token.position, token: value @@ -310,10 +311,10 @@ var jsonata = (function() { type = "literal"; symbol = symbol_table["(literal)"]; break; - /* istanbul ignore next */ + /* istanbul ignore next */ default: throw { - message: "Unexpected token:" + value + " at column " + next_token.position, + message: "Unexpected token:" + value, stack: (new Error()).stack, position: next_token.position, token: value @@ -483,28 +484,7 @@ var jsonata = (function() { return this; }); - // object constructor - prefix("{", function () { - var a = []; - if (node.id !== "}") { - for (;;) { - var n = expression(0); - advance(":"); - var v = expression(0); - a.push([n, v]); // holds an array of name/value expression pairs - if (node.id !== ",") { - break; - } - advance(","); - } - } - advance("}"); - this.lhs = a; - this.type = "unary"; - return this; - }); - - // array constructor + // array constructor prefix("[", function () { var a = []; if (node.id !== "]") { @@ -532,21 +512,57 @@ var jsonata = (function() { // filter - predicate or array index infix("[", operators['['], function (left) { - this.lhs = left; - this.rhs = expression(operators[']']); - this.type = 'binary'; - advance("]"); - return this; + if(node.id === "]") { + // empty predicate means maintain singleton arrays in the output + var step = left; + while(step && step.type === 'binary' && step.value === '[') { + step = step.lhs; + } + step.keepArray = true; + advance("]"); + return left; + } else { + this.lhs = left; + this.rhs = expression(operators[']']); + this.type = 'binary'; + advance("]"); + return this; + } }); - // aggregator - infix("{", operators['{'], function (left) { - this.lhs = left; - this.rhs = expression(operators['}']); - this.type = 'binary'; + var objectParser = function (left) { + var a = []; + if (node.id !== "}") { + for (;;) { + var n = expression(0); + advance(":"); + var v = expression(0); + a.push([n, v]); // holds an array of name/value expression pairs + if (node.id !== ",") { + break; + } + advance(","); + } + } advance("}"); + if(typeof left === 'undefined') { + // NUD - unary prefix form + this.lhs = a; + this.type = "unary"; + } else { + // LED - binary infix form + this.lhs = left; + this.rhs = a; + this.type = 'binary'; + } return this; - }); + }; + + // object constructor + prefix("{", objectParser); + + // object grouping + infix("{", operators['{'], objectParser); // if/then/else ternary operator ?: infix("?", operators['?'], function (left) { @@ -600,13 +616,16 @@ var jsonata = (function() { case 'binary': switch (expr.value) { case '.': - var step = post_parse(expr.lhs); - if (Array.isArray(step)) { - Array.prototype.push.apply(result, step); + var lstep = post_parse(expr.lhs); + if (lstep.type === 'path') { + Array.prototype.push.apply(result, lstep); } else { - result.push(step); + result.push(lstep); + } + var rest = post_parse(expr.rhs); + if(rest.type !== 'path') { + rest = [rest]; } - var rest = [post_parse(expr.rhs)]; Array.prototype.push.apply(result, rest); result.type = 'path'; break; @@ -615,31 +634,41 @@ var jsonata = (function() { // LHS is a step or a predicated step // RHS is the predicate expr result = post_parse(expr.lhs); - if (typeof result.aggregate !== 'undefined') { + var step = result; + if(result.type === 'path') { + step = result[result.length - 1]; + } + if (typeof step.group !== 'undefined') { throw { - message: 'A predicate cannot follow an aggregate in a step. Error at column: ' + expr.position, + message: 'A predicate cannot follow a grouping expression in a step. Error at column: ' + expr.position, stack: (new Error()).stack, position: expr.position }; } - if (typeof result.predicate === 'undefined') { - result.predicate = []; + if (typeof step.predicate === 'undefined') { + step.predicate = []; } - result.predicate.push(post_parse(expr.rhs)); + step.predicate.push(post_parse(expr.rhs)); break; case '{': - // aggregate + // group-by // LHS is a step or a predicated step - // RHS is the predicate expr + // RHS is the object constructor expr result = post_parse(expr.lhs); - if (typeof result.aggregate !== 'undefined') { + if (typeof result.group !== 'undefined') { throw { - message: 'Each step can only have one aggregator. Error at column: ' + expr.position, + message: 'Each step can only have one grouping expression. Error at column: ' + expr.position, stack: (new Error()).stack, position: expr.position }; } - result.aggregate = post_parse(expr.rhs); + // object constructor - process each pair + result.group = { + lhs: expr.rhs.map(function (pair) { + return [post_parse(pair[0]), post_parse(pair[1])]; + }), + position: expr.position + }; break; default: result = {type: expr.type, value: expr.value, position: expr.position}; @@ -700,6 +729,9 @@ var jsonata = (function() { // if so, need to mark the block as one that needs to create a new frame break; case 'name': + result = [expr]; + result.type = 'path'; + break; case 'literal': case 'wildcard': case 'descendant': @@ -716,7 +748,7 @@ var jsonata = (function() { result = expr; } else { throw { - message: "Syntax error: " + expr.value + " at column " + expr.position, + message: "Syntax error: " + expr.value, stack: (new Error()).stack, position: expr.position, token: expr.value @@ -724,7 +756,7 @@ var jsonata = (function() { } break; default: - var reason = "Unknown expression type: " + expr.value + " at column " + expr.position; + var reason = "Unknown expression type: " + expr.value; /* istanbul ignore else */ if (expr.id === '(end)') { reason = "Syntax error: unexpected end of expression"; @@ -747,7 +779,7 @@ var jsonata = (function() { var expr = expression(0); if (node.id !== '(end)') { throw { - message: "Syntax error: " + node.value + " at column " + node.position, + message: "Syntax error: " + node.value, stack: (new Error()).stack, position: node.position, token: node.value @@ -755,12 +787,6 @@ var jsonata = (function() { } expr = post_parse(expr); - // a single name token is a single step location path - if (expr.type === 'name') { - expr = [expr]; - expr.type = 'path'; - } - return expr; }; @@ -865,8 +891,8 @@ var jsonata = (function() { if (expr.hasOwnProperty('predicate')) { result = applyPredicates(expr.predicate, result, environment); } - if (expr.hasOwnProperty('aggregate')) { - result = applyAggregate(expr.aggregate, result, environment); + if (expr.hasOwnProperty('group')) { + result = evaluateGroupExpression(expr.group, result, environment); } var exitCallback = environment.lookup('__evaluate_exit'); @@ -887,6 +913,7 @@ var jsonata = (function() { function evaluatePath(expr, input, environment) { var result; var inputSequence; + var keepSingletonArray = false; // expr is an array of steps // if the first step is a variable reference ($...), including root reference ($$), // then the path is absolute rather than relative @@ -898,7 +925,11 @@ var jsonata = (function() { } // evaluate each step in turn - expr.forEach(function (step) { + for(var ii = 0; ii < expr.length; ii++) { + var step = expr[ii]; + if(step.keepArray === true) { + keepSingletonArray = true; + } var resultSequence = []; result = undefined; // if input is not an array, make it so @@ -913,35 +944,36 @@ var jsonata = (function() { if (expr.length > 1 && step.type === 'literal') { step.type = 'name'; } - if (step.value === '{') { - if(typeof input !== 'undefined') { - result = evaluateGroupExpression(step, inputSequence, environment); - } - } else { - inputSequence.forEach(function (item) { - var res = evaluate(step, item, environment); - if (typeof res !== 'undefined') { - if (Array.isArray(res)) { - // is res an array - if so, flatten it into the parent array - res.forEach(function (innerRes) { - if (typeof innerRes !== 'undefined') { - resultSequence.push(innerRes); - } - }); - } else { - resultSequence.push(res); - } + inputSequence.forEach(function (item) { + var res = evaluate(step, item, environment); + if (typeof res !== 'undefined') { + if (Array.isArray(res) && (step.value !== '[' )) { + // is res an array - if so, flatten it into the parent array + res.forEach(function (innerRes) { + if (typeof innerRes !== 'undefined') { + resultSequence.push(innerRes); + } + }); + } else { + resultSequence.push(res); } - }); - if (resultSequence.length == 1) { - result = resultSequence[0]; - } else if (resultSequence.length > 1) { - result = resultSequence; } + }); + if (resultSequence.length == 1) { + if(keepSingletonArray) { + result = resultSequence; + } else { + result = resultSequence[0]; + } + } else if (resultSequence.length > 1) { + result = resultSequence; } + if(typeof result === 'undefined') { + break; + } input = result; - }); + } return result; } @@ -1016,35 +1048,6 @@ var jsonata = (function() { return result; } - /** - * Apply aggregate to input data - * @param {Object} expr - JSONata expression - * @param {Object} input - Input data to evaluate against - * @param {Object} environment - Environment - * @returns {{}} Result after applying aggregate - */ - function applyAggregate(expr, input, environment) { - var result = {}; - // this is effectively a 'reduce' HOF (fold left) - // if the input is a singleton, then just return this as the result - // otherwise iterate over the input array and aggregate the result - if (Array.isArray(input)) { - // create a new frame to limit the scope - var aggEnv = createFrame(environment); - // the variable $@ will hold the aggregated value, initialize this to the first array item - aggEnv.bind('_', input[0]); - // loop over the remainder of the array - for (var index = 1; index < input.length; index++) { - var reduce = evaluate(expr, input[index], aggEnv); - aggEnv.bind('_', reduce); - } - result = aggEnv.lookup('_'); - } else { - result = input; - } - return result; - } - /** * Evaluate binary expression against input data * @param {Object} expr - JSONata expression @@ -1108,7 +1111,7 @@ var jsonata = (function() { result = -result; } else { throw { - message: "Cannot negate a non-numeric value: " + result + " at column " + expr.position, + message: "Cannot negate a non-numeric value: " + result, stack: (new Error()).stack, position: expr.position, token: expr.value, @@ -1122,11 +1125,10 @@ var jsonata = (function() { expr.lhs.forEach(function (item) { var value = evaluate(item, input, environment); if (typeof value !== 'undefined') { - if (item.value === '..') { - // array generated by the range operator - merge into results - result = functionAppend(result, value); - } else { + if(item.value === '[') { result.push(value); + } else { + result = functionAppend(result, value); } } }); @@ -1289,7 +1291,7 @@ var jsonata = (function() { if (!isNumeric(lhs)) { throw { - message: 'LHS of ' + expr.value + ' operator must evaluate to a number at column ' + expr.position, + message: 'LHS of ' + expr.value + ' operator must evaluate to a number', stack: (new Error()).stack, position: expr.position, token: expr.value, @@ -1298,7 +1300,7 @@ var jsonata = (function() { } if (!isNumeric(rhs)) { throw { - message: 'RHS of ' + expr.value + ' operator must evaluate to a number at column ' + expr.position, + message: 'RHS of ' + expr.value + ' operator must evaluate to a number', stack: (new Error()).stack, position: expr.position, token: expr.value, @@ -1413,11 +1415,11 @@ var jsonata = (function() { switch (expr.value) { case 'and': result = functionBoolean(evaluate(expr.lhs, input, environment)) && - functionBoolean(evaluate(expr.rhs, input, environment)); + functionBoolean(evaluate(expr.rhs, input, environment)); break; case 'or': result = functionBoolean(evaluate(expr.lhs, input, environment)) || - functionBoolean(evaluate(expr.rhs, input, environment)); + functionBoolean(evaluate(expr.rhs, input, environment)); break; } return result; @@ -1468,7 +1470,7 @@ var jsonata = (function() { // key has to be a string if (typeof key !== 'string') { throw { - message: 'Key in object structure must evaluate to a string. Got: ' + key + ' at column ' + expr.position, + message: 'Key in object structure must evaluate to a string. Got: ' + key, stack: (new Error()).stack, position: expr.position, value: key @@ -1518,7 +1520,7 @@ var jsonata = (function() { if (!Number.isInteger(lhs)) { throw { - message: 'LHS of range operator (..) must evaluate to an integer at column ' + expr.position, + message: 'LHS of range operator (..) must evaluate to an integer', stack: (new Error()).stack, position: expr.position, token: expr.value, @@ -1527,7 +1529,7 @@ var jsonata = (function() { } if (!Number.isInteger(rhs)) { throw { - message: 'RHS of range operator (..) must evaluate to an integer at column ' + expr.position, + message: 'RHS of range operator (..) must evaluate to an integer', stack: (new Error()).stack, position: expr.position, token: expr.value, @@ -1555,11 +1557,11 @@ var jsonata = (function() { var value = evaluate(expr.rhs, input, environment); if (expr.lhs.type !== 'variable') { throw { - message: "Left hand side of := must be a variable name (start with $) at column " + expr.position, + message: "Left hand side of := must be a variable name (start with $)", stack: (new Error()).stack, position: expr.position, token: expr.value, - value: expr.lhs.value + value: expr.lhs.type === 'path' ? expr.lhs[0].value : expr.lhs.value }; } environment.bind(expr.lhs.value, value); @@ -1645,13 +1647,13 @@ var jsonata = (function() { // evaluate it generically first, then check that it is a function. Throw error if not. var proc = evaluate(expr.procedure, input, environment); - if (typeof proc === 'undefined' && expr.procedure.type === 'name' && environment.lookup(expr.procedure.value)) { + if (typeof proc === 'undefined' && expr.procedure.type === 'path' && environment.lookup(expr.procedure[0].value)) { // help the user out here if they simply forgot the leading $ throw { - message: 'Attempted to invoke a non-function at column ' + expr.position + '. Did you mean \'$' + expr.procedure.value + '\'?', + message: 'Attempted to invoke a non-function. Did you mean \'$' + expr.procedure[0].value + '\'?', stack: (new Error()).stack, position: expr.position, - token: expr.procedure.value + token: expr.procedure[0].value }; } // apply the procedure @@ -1673,7 +1675,7 @@ var jsonata = (function() { // add the position field to the error err.position = expr.position; // and the function identifier - err.token = expr.procedure.value; + err.token = expr.procedure.type === 'path' ? expr.procedure[0].value : expr.procedure.value; throw err; } return result; @@ -1745,13 +1747,13 @@ var jsonata = (function() { }); // lookup the procedure var proc = evaluate(expr.procedure, input, environment); - if (typeof proc === 'undefined' && expr.procedure.type === 'name' && environment.lookup(expr.procedure.value)) { + if (typeof proc === 'undefined' && expr.procedure.type === 'path' && environment.lookup(expr.procedure[0].value)) { // help the user out here if they simply forgot the leading $ throw { - message: 'Attempted to partially apply a non-function at column ' + expr.position + '. Did you mean \'$' + expr.procedure.value + '\'?', + message: 'Attempted to partially apply a non-function. Did you mean \'$' + expr.procedure[0].value + '\'?', stack: (new Error()).stack, position: expr.position, - token: expr.procedure.value + token: expr.procedure[0].value }; } if (proc && proc.lambda) { @@ -1760,10 +1762,10 @@ var jsonata = (function() { result = partialApplyNativeFunction(proc, evaluatedArgs); } else { throw { - message: 'Attempted to partially apply a non-function at column ' + expr.position, + message: 'Attempted to partially apply a non-function', stack: (new Error()).stack, position: expr.position, - token: expr.procedure.value + token: expr.procedure.type === 'path' ? expr.procedure[0].value : expr.procedure.value }; } return result; @@ -2097,8 +2099,8 @@ var jsonata = (function() { } else str = JSON.stringify(arg, function (key, val) { return (typeof val !== 'undefined' && val !== null && val.toPrecision && isNumeric(val)) ? Number(val.toPrecision(13)) : - (val && isLambda(val)) ? '' : - (typeof val === 'function') ? '' : val; + (val && isLambda(val)) ? '' : + (typeof val === 'function') ? '' : val; }); return str; } @@ -2788,9 +2790,6 @@ var jsonata = (function() { }, assign: function (name, value) { environment.bind(name, value); - }, - ast: function() { - return ast; } }; } diff --git a/package.json b/package.json index 33de70e92..819697fec 100644 --- a/package.json +++ b/package.json @@ -40,7 +40,7 @@ "fs.notify":"0.0.4", "i18next":"1.10.6", "is-utf8":"0.2.1", - "jsonata":"1.0.7", + "jsonata":"1.0.10", "media-typer": "0.3.0", "mqtt": "1.14.1", "mustache": "2.2.1",