diff --git a/src/jsonata.js b/src/jsonata.js index 8b4a68ca..c3ee7b3e 100644 --- a/src/jsonata.js +++ b/src/jsonata.js @@ -482,6 +482,9 @@ var jsonata = (function() { case '>=': result = evaluateComparisonExpression(lhs, rhs, op); break; + case '?:': + result = evaluateDefault(lhs, rhs, op); + break; case '&': result = evaluateStringConcat(lhs, rhs); break; @@ -810,6 +813,16 @@ var jsonata = (function() { return result; } + /** + * Evaluate default-value expression + * @param {Object} lhs - LHS value + * @param {Object} rhs - RHS value + * @returns {*} Result + */ + function evaluateDefault(lhs, rhs) { + return lhs ? lhs : rhs; + } + /** * Inclusion operator - in * diff --git a/src/parser.js b/src/parser.js index 629ff82c..c6f13f3a 100644 --- a/src/parser.js +++ b/src/parser.js @@ -40,6 +40,7 @@ const parser = (() => { '<=': 40, '>=': 40, '~>': 40, + '?:': 40, 'and': 30, 'or': 25, 'in': 40, @@ -198,6 +199,11 @@ const parser = (() => { position += 2; return create('operator', '~>'); } + if (currentChar === '?' && path.charAt(position + 1) === ':') { + // ?: default / elvis operator + position += 2; + return create('operator', '?:'); + } // test for single char operators if (Object.prototype.hasOwnProperty.call(operators, currentChar)) { position++; @@ -565,6 +571,7 @@ const parser = (() => { terminal("in"); // prefix("-"); // unary numeric negation infix("~>"); // function application + infix("?:"); // default value infixr("(error)", 10, function (left) { this.lhs = left; diff --git a/test/test-suite/groups/default-operator/case000.json b/test/test-suite/groups/default-operator/case000.json new file mode 100644 index 00000000..89f21c56 --- /dev/null +++ b/test/test-suite/groups/default-operator/case000.json @@ -0,0 +1,7 @@ +{ + "description": "property missing on object uses default string on lhs", + "expr": "order?:'the usual'", + "dataset": "dataset0", + "bindings": {}, + "result": "the usual" +} \ No newline at end of file diff --git a/test/test-suite/groups/default-operator/case001.json b/test/test-suite/groups/default-operator/case001.json new file mode 100644 index 00000000..16806281 --- /dev/null +++ b/test/test-suite/groups/default-operator/case001.json @@ -0,0 +1,7 @@ +{ + "description": "property missing on object uses default number on lhs", + "expr": "age?:42", + "dataset": "dataset0", + "bindings": {}, + "result": 42 +} \ No newline at end of file diff --git a/test/test-suite/groups/default-operator/case003.json b/test/test-suite/groups/default-operator/case003.json new file mode 100644 index 00000000..d070d53f --- /dev/null +++ b/test/test-suite/groups/default-operator/case003.json @@ -0,0 +1,7 @@ +{ + "description": "property present on object is used", + "expr": "bar ?: 0", + "dataset": "dataset0", + "bindings": {}, + "result": 98 +} \ No newline at end of file diff --git a/test/test-suite/groups/default-operator/case004.json b/test/test-suite/groups/default-operator/case004.json new file mode 100644 index 00000000..9a125739 --- /dev/null +++ b/test/test-suite/groups/default-operator/case004.json @@ -0,0 +1,7 @@ +{ + "description": "operator can be chained", + "expr": "nope ?: foo.bar ?: 0", + "dataset": "dataset0", + "bindings": {}, + "result": 42 +} \ No newline at end of file