|
528 | 528 | },
|
529 | 529 |
|
530 | 530 | 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(); |
538 | 533 | } else if (this.lookahead(0) === "Star" &&
|
539 | 534 | this.lookahead(1) === "Rbracket") {
|
540 | 535 | this.advance();
|
|
547 | 542 | }
|
548 | 543 | },
|
549 | 544 |
|
| 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 | + |
550 | 587 | nudLbrace: function() {
|
551 | 588 | return this.parseMultiselectHash();
|
552 | 589 | },
|
|
613 | 650 | ledLbracket: function(left) {
|
614 | 651 | var token = this.lookaheadToken(0);
|
615 | 652 | 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(); |
620 | 655 | return {type: "IndexExpression", children: [left, right]};
|
621 | 656 | } else {
|
622 | 657 | this.match("Star");
|
|
802 | 837 | return result;
|
803 | 838 | },
|
804 | 839 |
|
| 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 | + |
805 | 906 | visitProjection: function(node, value) {
|
806 | 907 | // Evaluate left child.
|
807 | 908 | var base = this.visit(node.children[0], value);
|
|
992 | 1093 | contains: {
|
993 | 1094 | func: this.functionContains,
|
994 | 1095 | signature: [{types: ["string", "array"]}, {types: ["any"]}]},
|
995 |
| - ends_with: { |
| 1096 | + "ends_with": { |
996 | 1097 | func: this.functionEndsWith,
|
997 | 1098 | signature: [{types: ["string"]}, {types: ["string"]}]},
|
998 | 1099 | floor: {func: this.functionFloor, signature: [{types: ["number"]}]},
|
|
1007 | 1108 | signature: [{types: ["array"]}, {types: ["expref"]}]
|
1008 | 1109 | },
|
1009 | 1110 | sum: {func: this.functionSum, signature: [{types: ["array-number"]}]},
|
1010 |
| - starts_with: { |
| 1111 | + "starts_with": { |
1011 | 1112 | func: this.functionStartsWith,
|
1012 | 1113 | signature: [{types: ["string"]}, {types: ["string"]}]},
|
1013 | 1114 | min: {
|
|
1164 | 1265 | var originalStr = resolvedArgs[0];
|
1165 | 1266 | var reversedStr = "";
|
1166 | 1267 | for (var i = originalStr.length - 1; i >= 0; i--) {
|
1167 |
| - reversedStr += originalStr[i] |
| 1268 | + reversedStr += originalStr[i]; |
1168 | 1269 | }
|
1169 | 1270 | return reversedStr;
|
1170 | 1271 | } else {
|
|
1376 | 1477 | functionMaxBy: function(resolvedArgs) {
|
1377 | 1478 | var exprefNode = resolvedArgs[1];
|
1378 | 1479 | var resolvedArray = resolvedArgs[0];
|
1379 |
| - var keyFunction = this.createKeyFunction(exprefNode, ["number"]); |
| 1480 | + var keyFunction = this.createKeyFunction(exprefNode, ["number", "string"]); |
1380 | 1481 | var maxNumber = -Infinity;
|
1381 | 1482 | var maxRecord;
|
1382 | 1483 | var current;
|
|
1393 | 1494 | functionMinBy: function(resolvedArgs) {
|
1394 | 1495 | var exprefNode = resolvedArgs[1];
|
1395 | 1496 | var resolvedArray = resolvedArgs[0];
|
1396 |
| - var keyFunction = this.createKeyFunction(exprefNode, ["number"]); |
| 1497 | + var keyFunction = this.createKeyFunction(exprefNode, ["number", "string"]); |
1397 | 1498 | var minNumber = Infinity;
|
1398 | 1499 | var minRecord;
|
1399 | 1500 | var current;
|
|
0 commit comments