Skip to content

Implement support of spread operator #745

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 1 commit into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
32 changes: 29 additions & 3 deletions src/jsonata.js
Original file line number Diff line number Diff line change
Expand Up @@ -558,6 +558,10 @@ var jsonata = (function() {
result = await evaluateGroupExpression(expr, input, environment);
break;

case '...':
result = await evaluate(expr.expression, input, environment);
break;

}
return result;
}
Expand Down Expand Up @@ -915,8 +919,29 @@ var jsonata = (function() {
for(var pairIndex = 0; pairIndex < expr.lhs.length; pairIndex++) {
var pair = expr.lhs[pairIndex];
var key = await evaluate(pair[0], reduce ? item['@'] : item, env);
if(key === undefined) {
continue;
}
// handle spread operator
if (pair[0].value === '...') {
if(typeof key !== 'object' || key === null || Array.isArray(key)) {
throw {
code: "T2015",
stack: (new Error()).stack,
position: expr.position,
value: key
}
}
for (const [_key, _value] of Object.entries(key)) {
groups[_key] = {
data: _value,
exprIndex: pairIndex,
canOverride: true
};
}
}
// key has to be a string
if (typeof key !== 'string' && key !== undefined) {
else if (typeof key !== 'string') {
throw {
code: "T1003",
stack: (new Error()).stack,
Expand All @@ -925,9 +950,9 @@ var jsonata = (function() {
};
}

if (key !== undefined) {
else {
var entry = {data: item, exprIndex: pairIndex};
if (groups.hasOwnProperty(key)) {
if (groups.hasOwnProperty(key) && !groups[key].canOverride) {
// a value already exists in this slot
if(groups[key].exprIndex !== pairIndex) {
// this key has been generated by another expression in this group
Expand Down Expand Up @@ -2005,6 +2030,7 @@ var jsonata = (function() {
"T2012": "The delete clause of the transform expression must evaluate to a string or array of strings: {{value}}",
"T2013": "The transform expression clones the input object using the $clone() function. This has been overridden in the current scope by a non-function.",
"D2014": "The size of the sequence allocated by the range operator (..) must not exceed 1e6. Attempted to allocate {{value}}.",
"T2015": "The right side of the spread operator must evaluate to an object: {{value}}",
"D3001": "Attempting to invoke string function on Infinity or NaN",
"D3010": "Second argument of replace function cannot be an empty string",
"D3011": "Fourth argument of replace function must evaluate to a positive number",
Expand Down
17 changes: 14 additions & 3 deletions src/parser.js
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,7 @@ const parser = (() => {
'^': 40,
'**': 60,
'..': 20,
'...': 50,
':=': 10,
'!=': 40,
'<=': 40,
Expand Down Expand Up @@ -164,6 +165,11 @@ const parser = (() => {
}
// handle double-char operators
if (currentChar === '.' && path.charAt(position + 1) === '.') {
// triple-dot ... spread operator
if (path.charAt(position + 2) === '.') {
position += 3;
return create('operator', '...');
}
// double-dot .. range operator
position += 2;
return create('operator', '..');
Expand Down Expand Up @@ -565,6 +571,7 @@ const parser = (() => {
terminal("in"); //
prefix("-"); // unary numeric negation
infix("~>"); // function application
prefix("..."); // spread operator

infixr("(error)", 10, function (left) {
this.lhs = left;
Expand Down Expand Up @@ -759,9 +766,13 @@ const parser = (() => {
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 (n.id === '...') { // Spread operator
a.push([n, {type: 'variable', value: ''}]); // the value is an identity expression
} else {
advance(":");
var v = expression(0);
a.push([n, v]); // holds an array of name/value expression pairs
}
if (node.id !== ",") {
break;
}
Expand Down
11 changes: 11 additions & 0 deletions test/test-suite/groups/spread/case000.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
{
"expr": "{...Address, \"hello\":\"world\"}",
"dataset": "dataset1",
"bindings": {},
"result": {
"Street": "Hursley Park",
"City": "Winchester",
"Postcode": "SO21 2JN",
"hello": "world"
}
}
10 changes: 10 additions & 0 deletions test/test-suite/groups/spread/case001.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
{
"expr": "{...Address, \"Street\":\"world\"}",
"dataset": "dataset1",
"bindings": {},
"result": {
"City": "Winchester",
"Postcode": "SO21 2JN",
"Street": "world"
}
}
10 changes: 10 additions & 0 deletions test/test-suite/groups/spread/case002.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
{
"expr": "{\"Street\": \"test\", ...Address}",
"dataset": "dataset1",
"bindings": {},
"result": {
"Street": "Hursley Park",
"City": "Winchester",
"Postcode": "SO21 2JN"
}
}
11 changes: 11 additions & 0 deletions test/test-suite/groups/spread/case003.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
{
"expr": "{...Address, \"Test\": FirstName}",
"dataset": "dataset1",
"bindings": {},
"result": {
"Street": "Hursley Park",
"City": "Winchester",
"Postcode": "SO21 2JN",
"Test": "Fred"
}
}
11 changes: 11 additions & 0 deletions test/test-suite/groups/spread/case004.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
{
"expr": "['yo', ...foo.blah.baz.fud, 'whats up']",
"dataset": "dataset0",
"bindings": {},
"result": [
"yo",
"hello",
"world",
"whats up"
]
}
8 changes: 8 additions & 0 deletions test/test-suite/groups/spread/case005.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
{
"expr": "{...Wrong.Address, \"hello\":\"world\"}",
"dataset": "dataset1",
"bindings": {},
"result": {
"hello": "world"
}
}
6 changes: 6 additions & 0 deletions test/test-suite/groups/spread/case006.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
{
"expr": "{...null, \"hello\":\"world\"}",
"dataset": "dataset1",
"bindings": {},
"code": "T2015"
}
6 changes: 6 additions & 0 deletions test/test-suite/groups/spread/case007.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
{
"expr": "{...Phone, \"hello\":\"world\"}",
"dataset": "dataset1",
"bindings": {},
"code": "T2015"
}
6 changes: 6 additions & 0 deletions test/test-suite/groups/spread/case008.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
{
"expr": "{...Surname, \"hello\":\"world\"}",
"dataset": "dataset1",
"bindings": {},
"code": "T2015"
}