Skip to content

Commit 6a36245

Browse files
committed
Merge branch 'function-and-slices'
* function-and-slices: Implement slices Update functions to pass latest compliance test updates Remove scratch file Switch to jmespath.org in README
2 parents d75aa7e + 538eb93 commit 6a36245

File tree

6 files changed

+392
-40
lines changed

6 files changed

+392
-40
lines changed

README.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,4 +2,4 @@
22

33
This is a javascript implementation of JMESPath.
44

5-
See http://jmespath.readthedocs.org/en/latest/ for more info.
5+
See http://jmespath.org for more info.

demo.js

Lines changed: 0 additions & 14 deletions
This file was deleted.

jmespath.js

Lines changed: 181 additions & 17 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,17 +1093,27 @@
9921093
contains: {
9931094
func: this.functionContains,
9941095
signature: [{types: ["string", "array"]}, {types: ["any"]}]},
1096+
"ends_with": {
1097+
func: this.functionEndsWith,
1098+
signature: [{types: ["string"]}, {types: ["string"]}]},
9951099
floor: {func: this.functionFloor, signature: [{types: ["number"]}]},
9961100
length: {
9971101
func: this.functionLength,
9981102
signature: [{types: ["string", "array", "object"]}]},
999-
max: {func: this.functionMax, signature: [{types: ["array-number"]}]},
1103+
max: {
1104+
func: this.functionMax,
1105+
signature: [{types: ["array-number", "array-string"]}]},
10001106
"max_by": {
10011107
func: this.functionMaxBy,
10021108
signature: [{types: ["array"]}, {types: ["expref"]}]
10031109
},
10041110
sum: {func: this.functionSum, signature: [{types: ["array-number"]}]},
1005-
min: {func: this.functionMin, signature: [{types: ["array-number"]}]},
1111+
"starts_with": {
1112+
func: this.functionStartsWith,
1113+
signature: [{types: ["string"]}, {types: ["string"]}]},
1114+
min: {
1115+
func: this.functionMin,
1116+
signature: [{types: ["array-number", "array-string"]}]},
10061117
"min_by": {
10071118
func: this.functionMinBy,
10081119
signature: [{types: ["array"]}, {types: ["expref"]}]
@@ -1022,6 +1133,9 @@
10221133
{types: ["array-string"]}
10231134
]
10241135
},
1136+
reverse: {
1137+
func: this.functionReverse,
1138+
signature: [{types: ["string", "array"]}]},
10251139
"to_string": {func: this.functionToString, signature: [{types: ["any"]}]},
10261140
"to_number": {func: this.functionToNumber, signature: [{types: ["any"]}]},
10271141
"not_null": {
@@ -1135,6 +1249,32 @@
11351249
}
11361250
},
11371251

1252+
functionStartsWith: function(resolvedArgs) {
1253+
return resolvedArgs[0].lastIndexOf(resolvedArgs[1]) === 0;
1254+
},
1255+
1256+
functionEndsWith: function(resolvedArgs) {
1257+
var search = resolvedArgs[0];
1258+
var suffix = resolvedArgs[1];
1259+
return search.indexOf(suffix, search.length - suffix.length) !== -1;
1260+
},
1261+
1262+
functionReverse: function(resolvedArgs) {
1263+
var typeName = this.getTypeName(resolvedArgs[0]);
1264+
if (typeName === "string") {
1265+
var originalStr = resolvedArgs[0];
1266+
var reversedStr = "";
1267+
for (var i = originalStr.length - 1; i >= 0; i--) {
1268+
reversedStr += originalStr[i];
1269+
}
1270+
return reversedStr;
1271+
} else {
1272+
var reversedArray = resolvedArgs[0].slice(0);
1273+
reversedArray.reverse();
1274+
return reversedArray;
1275+
}
1276+
},
1277+
11381278
functionAbs: function(resolvedArgs) {
11391279
return Math.abs(resolvedArgs[0]);
11401280
},
@@ -1172,15 +1312,39 @@
11721312

11731313
functionMax: function(resolvedArgs) {
11741314
if (resolvedArgs[0].length > 0) {
1175-
return Math.max.apply(Math, resolvedArgs[0]);
1315+
var typeName = this.getTypeName(resolvedArgs[0][0]);
1316+
if (typeName === "number") {
1317+
return Math.max.apply(Math, resolvedArgs[0]);
1318+
} else {
1319+
var elements = resolvedArgs[0];
1320+
var maxElement = elements[0];
1321+
for (var i = 1; i < elements.length; i++) {
1322+
if (maxElement.localeCompare(elements[i]) < 0) {
1323+
maxElement = elements[i];
1324+
}
1325+
}
1326+
return maxElement;
1327+
}
11761328
} else {
11771329
return null;
11781330
}
11791331
},
11801332

11811333
functionMin: function(resolvedArgs) {
11821334
if (resolvedArgs[0].length > 0) {
1183-
return Math.min.apply(Math, resolvedArgs[0]);
1335+
var typeName = this.getTypeName(resolvedArgs[0][0]);
1336+
if (typeName === "number") {
1337+
return Math.min.apply(Math, resolvedArgs[0]);
1338+
} else {
1339+
var elements = resolvedArgs[0];
1340+
var minElement = elements[0];
1341+
for (var i = 1; i < elements.length; i++) {
1342+
if (elements[i].localeCompare(minElement) < 0) {
1343+
minElement = elements[i];
1344+
}
1345+
}
1346+
return minElement;
1347+
}
11841348
} else {
11851349
return null;
11861350
}
@@ -1313,7 +1477,7 @@
13131477
functionMaxBy: function(resolvedArgs) {
13141478
var exprefNode = resolvedArgs[1];
13151479
var resolvedArray = resolvedArgs[0];
1316-
var keyFunction = this.createKeyFunction(exprefNode, ["number"]);
1480+
var keyFunction = this.createKeyFunction(exprefNode, ["number", "string"]);
13171481
var maxNumber = -Infinity;
13181482
var maxRecord;
13191483
var current;
@@ -1330,7 +1494,7 @@
13301494
functionMinBy: function(resolvedArgs) {
13311495
var exprefNode = resolvedArgs[1];
13321496
var resolvedArray = resolvedArgs[0];
1333-
var keyFunction = this.createKeyFunction(exprefNode, ["number"]);
1497+
var keyFunction = this.createKeyFunction(exprefNode, ["number", "string"]);
13341498
var minNumber = Infinity;
13351499
var minRecord;
13361500
var current;

test/compliance.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -43,7 +43,7 @@ function addTestSuitesFromFile(filename) {
4343
})(testcase, given);
4444
} else {
4545
(function(testcase, given) {
46-
it('should pass test ' + j, function() {
46+
it('should pass test ' + j + " expression: " + testcase.expression, function() {
4747
assert.deepEqual(search(given, testcase.expression),
4848
testcase.result);
4949
});

0 commit comments

Comments
 (0)