Skip to content

Commit 538eb93

Browse files
committed
Implement slices
1 parent a67a33e commit 538eb93

File tree

1 file changed

+117
-16
lines changed

1 file changed

+117
-16
lines changed

jmespath.js

Lines changed: 117 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -528,13 +528,8 @@
528528
},
529529

530530
nudLbracket: function() {
531-
if (this.lookahead(0) === "Number") {
532-
var node = {
533-
type: "Index",
534-
value: this.lookaheadToken(0).value};
535-
this.advance();
536-
this.match("Rbracket");
537-
return node;
531+
if (this.lookahead(0) === "Number" || this.lookahead(0) === "Colon") {
532+
return this.parseIndexExpression();
538533
} else if (this.lookahead(0) === "Star" &&
539534
this.lookahead(1) === "Rbracket") {
540535
this.advance();
@@ -547,6 +542,48 @@
547542
}
548543
},
549544

545+
parseIndexExpression: function() {
546+
if (this.lookahead(0) === "Colon" || this.lookahead(1) === "Colon") {
547+
return this.parseSliceExpression();
548+
} else {
549+
var node = {
550+
type: "Index",
551+
value: this.lookaheadToken(0).value};
552+
this.advance();
553+
this.match("Rbracket");
554+
return node;
555+
}
556+
},
557+
558+
parseSliceExpression: function() {
559+
// [start:end:step] where each part is optional, as well as the last
560+
// colon.
561+
var parts = [null, null, null];
562+
var index = 0;
563+
var currentToken = this.lookahead(0);
564+
while (currentToken !== "Rbracket" && index < 3) {
565+
if (currentToken === "Colon") {
566+
index++;
567+
this.advance();
568+
} else if (currentToken === "Number") {
569+
parts[index] = this.lookaheadToken(0).value;
570+
this.advance();
571+
} else {
572+
var t = this.lookahead(0);
573+
var error = new Error("Syntax error, unexpected token: " +
574+
t.value + "(" + t.type + ")");
575+
error.name = "Parsererror";
576+
throw error;
577+
}
578+
currentToken = this.lookahead(0);
579+
}
580+
this.match("Rbracket");
581+
return {
582+
type: "Slice",
583+
children: parts
584+
};
585+
},
586+
550587
nudLbrace: function() {
551588
return this.parseMultiselectHash();
552589
},
@@ -613,10 +650,8 @@
613650
ledLbracket: function(left) {
614651
var token = this.lookaheadToken(0);
615652
var right;
616-
if (token.type === "Number") {
617-
this.match("Number");
618-
right = {type: "Index", value: token.value};
619-
this.match("Rbracket");
653+
if (token.type === "Number" || token.type === "Colon") {
654+
right = this.parseIndexExpression();
620655
return {type: "IndexExpression", children: [left, right]};
621656
} else {
622657
this.match("Star");
@@ -802,6 +837,72 @@
802837
return result;
803838
},
804839

840+
visitSlice: function(node, value) {
841+
if (!isArray(value)) {
842+
return null;
843+
}
844+
var sliceParams = node.children.slice(0);
845+
var computed = this.computeSliceParams(value.length, sliceParams);
846+
var start = computed[0];
847+
var stop = computed[1];
848+
var step = computed[2];
849+
var result = [];
850+
var i;
851+
if (step > 0) {
852+
for (i = start; i < stop; i += step) {
853+
result.push(value[i]);
854+
}
855+
} else {
856+
for (i = start; i > stop; i += step) {
857+
result.push(value[i]);
858+
}
859+
}
860+
return result;
861+
},
862+
863+
computeSliceParams: function(arrayLength, sliceParams) {
864+
var start = sliceParams[0];
865+
var stop = sliceParams[1];
866+
var step = sliceParams[2];
867+
var computed = [null, null, null];
868+
if (step === null) {
869+
step = 1;
870+
} else if (step === 0) {
871+
var error = new Error("Invalid slice, step cannot be 0");
872+
error.name = "RuntimeError";
873+
throw error;
874+
}
875+
var stepValueNegative = step < 0 ? true : false;
876+
877+
if (start === null) {
878+
start = stepValueNegative ? arrayLength - 1 : 0;
879+
} else {
880+
start = this.capSliceRange(arrayLength, start, step);
881+
}
882+
883+
if (stop === null) {
884+
stop = stepValueNegative ? -1 : arrayLength;
885+
} else {
886+
stop = this.capSliceRange(arrayLength, stop, step);
887+
}
888+
computed[0] = start;
889+
computed[1] = stop;
890+
computed[2] = step;
891+
return computed;
892+
},
893+
894+
capSliceRange: function(arrayLength, actualValue, step) {
895+
if (actualValue < 0) {
896+
actualValue += arrayLength;
897+
if (actualValue < 0) {
898+
actualValue = step < 0 ? -1 : 0;
899+
}
900+
} else if (actualValue >= arrayLength) {
901+
actualValue = step < 0 ? arrayLength - 1 : arrayLength;
902+
}
903+
return actualValue;
904+
},
905+
805906
visitProjection: function(node, value) {
806907
// Evaluate left child.
807908
var base = this.visit(node.children[0], value);
@@ -992,7 +1093,7 @@
9921093
contains: {
9931094
func: this.functionContains,
9941095
signature: [{types: ["string", "array"]}, {types: ["any"]}]},
995-
ends_with: {
1096+
"ends_with": {
9961097
func: this.functionEndsWith,
9971098
signature: [{types: ["string"]}, {types: ["string"]}]},
9981099
floor: {func: this.functionFloor, signature: [{types: ["number"]}]},
@@ -1007,7 +1108,7 @@
10071108
signature: [{types: ["array"]}, {types: ["expref"]}]
10081109
},
10091110
sum: {func: this.functionSum, signature: [{types: ["array-number"]}]},
1010-
starts_with: {
1111+
"starts_with": {
10111112
func: this.functionStartsWith,
10121113
signature: [{types: ["string"]}, {types: ["string"]}]},
10131114
min: {
@@ -1164,7 +1265,7 @@
11641265
var originalStr = resolvedArgs[0];
11651266
var reversedStr = "";
11661267
for (var i = originalStr.length - 1; i >= 0; i--) {
1167-
reversedStr += originalStr[i]
1268+
reversedStr += originalStr[i];
11681269
}
11691270
return reversedStr;
11701271
} else {
@@ -1376,7 +1477,7 @@
13761477
functionMaxBy: function(resolvedArgs) {
13771478
var exprefNode = resolvedArgs[1];
13781479
var resolvedArray = resolvedArgs[0];
1379-
var keyFunction = this.createKeyFunction(exprefNode, ["number"]);
1480+
var keyFunction = this.createKeyFunction(exprefNode, ["number", "string"]);
13801481
var maxNumber = -Infinity;
13811482
var maxRecord;
13821483
var current;
@@ -1393,7 +1494,7 @@
13931494
functionMinBy: function(resolvedArgs) {
13941495
var exprefNode = resolvedArgs[1];
13951496
var resolvedArray = resolvedArgs[0];
1396-
var keyFunction = this.createKeyFunction(exprefNode, ["number"]);
1497+
var keyFunction = this.createKeyFunction(exprefNode, ["number", "string"]);
13971498
var minNumber = Infinity;
13981499
var minRecord;
13991500
var current;

0 commit comments

Comments
 (0)