|
119 | 119 | }
|
120 | 120 |
|
121 | 121 |
|
122 |
| - // The "[", "<", ">" tokens |
| 122 | + // The "&", "[", "<", ">" tokens |
123 | 123 | // are not in basicToken because
|
124 | 124 | // there are two token variants
|
125 |
| - // ("[?", "<=", ">="). This is specially handled |
| 125 | + // ("&&", "[?", "<=", ">="). This is specially handled |
126 | 126 | // below.
|
127 | 127 |
|
128 | 128 | var basicTokens = {
|
|
135 | 135 | "]": "Rbracket",
|
136 | 136 | "(": "Lparen",
|
137 | 137 | ")": "Rparen",
|
138 |
| - "@": "Current", |
139 |
| - "&": "Expref" |
| 138 | + "@": "Current" |
140 | 139 | };
|
141 | 140 |
|
142 | 141 | var identifierStart = {
|
|
214 | 213 | tokens.push({type: "QuotedIdentifier",
|
215 | 214 | value: identifier,
|
216 | 215 | start: start});
|
| 216 | + } else if (stream[this.current] === "'") { |
| 217 | + start = this.current; |
| 218 | + identifier = this.consumeRawStringLiteral(stream); |
| 219 | + tokens.push({type: "Literal", |
| 220 | + value: identifier, |
| 221 | + start: start}); |
217 | 222 | } else if (stream[this.current] === "`") {
|
218 | 223 | start = this.current;
|
219 | 224 | var literal = this.consumeLiteral(stream);
|
|
225 | 230 | } else if (skipChars[stream[this.current]] !== undefined) {
|
226 | 231 | // Ignore whitespace.
|
227 | 232 | this.current++;
|
| 233 | + } else if (stream[this.current] === "&") { |
| 234 | + start = this.current; |
| 235 | + this.current++; |
| 236 | + if (stream[this.current] === "&") { |
| 237 | + this.current++; |
| 238 | + tokens.push({type: "And", value: "&&", start: start}); |
| 239 | + } else { |
| 240 | + tokens.push({type: "Expref", value: "&", start: start}); |
| 241 | + } |
228 | 242 | } else if (stream[this.current] === "|") {
|
229 | 243 | start = this.current;
|
230 | 244 | this.current++;
|
|
271 | 285 | return JSON.parse(stream.slice(start, this.current));
|
272 | 286 | },
|
273 | 287 |
|
| 288 | + consumeRawStringLiteral: function(stream) { |
| 289 | + var start = this.current; |
| 290 | + this.current++; |
| 291 | + var maxLength = stream.length; |
| 292 | + while (stream[this.current] !== "'" && this.current < maxLength) { |
| 293 | + // You can escape a single quote and you can escape an escape. |
| 294 | + var current = this.current; |
| 295 | + if (stream[current] === "\\" && (stream[current + 1] === "\\" || |
| 296 | + stream[current + 1] === "'")) { |
| 297 | + current += 2; |
| 298 | + } else { |
| 299 | + current++; |
| 300 | + } |
| 301 | + this.current = current; |
| 302 | + } |
| 303 | + this.current++; |
| 304 | + var literal = stream.slice(start + 1, this.current - 1); |
| 305 | + return literal.replace("\\'", "'"); |
| 306 | + }, |
| 307 | + |
274 | 308 | consumeNumber: function(stream) {
|
275 | 309 | var start = this.current;
|
276 | 310 | this.current++;
|
|
304 | 338 | if (stream[this.current] === "=") {
|
305 | 339 | this.current++;
|
306 | 340 | return {type: "NE", value: "!=", start: start};
|
| 341 | + } else { |
| 342 | + return {type: "Not", value: "!", start: start}; |
307 | 343 | }
|
308 | 344 | } else if (startingChar === "<") {
|
309 | 345 | if (stream[this.current] === "=") {
|
|
394 | 430 | "Current": 0,
|
395 | 431 | "Expref": 0,
|
396 | 432 | "Pipe": 1,
|
397 |
| - "EQ": 2, |
398 |
| - "GT": 2, |
399 |
| - "LT": 2, |
400 |
| - "GTE": 2, |
401 |
| - "LTE": 2, |
402 |
| - "NE": 2, |
403 |
| - "Or": 5, |
404 |
| - "Flatten": 6, |
| 433 | + "Or": 2, |
| 434 | + "And": 3, |
| 435 | + "EQ": 5, |
| 436 | + "GT": 5, |
| 437 | + "LT": 5, |
| 438 | + "GTE": 5, |
| 439 | + "LTE": 5, |
| 440 | + "NE": 5, |
| 441 | + "Flatten": 9, |
405 | 442 | "Star": 20,
|
406 |
| - "Filter": 20, |
| 443 | + "Filter": 21, |
407 | 444 | "Dot": 40,
|
| 445 | + "Not": 45, |
408 | 446 | "Lbrace": 50,
|
409 | 447 | "Lbracket": 55,
|
410 | 448 | "Lparen": 60
|
|
505 | 543 | }
|
506 | 544 | },
|
507 | 545 |
|
| 546 | + nudNot: function() { |
| 547 | + var right = this.expression(this.bindingPower.Not); |
| 548 | + return {type: "NotExpression", children: [right]}; |
| 549 | + }, |
| 550 | + |
508 | 551 | ledOr: function(left) {
|
509 | 552 | var right = this.expression(this.bindingPower.Or);
|
510 | 553 | return {type: "OrExpression", children: [left, right]};
|
511 | 554 | },
|
512 | 555 |
|
| 556 | + ledAnd: function(left) { |
| 557 | + var right = this.expression(this.bindingPower.And); |
| 558 | + return {type: "AndExpression", children: [left, right]}; |
| 559 | + }, |
| 560 | + |
513 | 561 | ledPipe: function(left) {
|
514 | 562 | var right = this.expression(this.bindingPower.Pipe);
|
515 | 563 | return {type: "Pipe", children: [left, right]};
|
|
692 | 740 | return {type: "Projection", children: [leftNode, rightNode]};
|
693 | 741 | },
|
694 | 742 |
|
| 743 | + nudLparen: function() { |
| 744 | + var args = []; |
| 745 | + var expression; |
| 746 | + while (this.lookahead(0) !== "Rparen") { |
| 747 | + if (this.lookahead(0) === "Current") { |
| 748 | + expression = {type: "Current"}; |
| 749 | + this.advance(); |
| 750 | + } else { |
| 751 | + expression = this.expression(0); |
| 752 | + } |
| 753 | + args.push(expression); |
| 754 | + } |
| 755 | + this.match("Rparen"); |
| 756 | + return args[0]; |
| 757 | + }, |
| 758 | + |
695 | 759 | ledLparen: function(left) {
|
696 | 760 | var name = left.name;
|
697 | 761 | var args = [];
|
|
965 | 1029 | var matched, current;
|
966 | 1030 | for (var i = 0; i < base.length; i++) {
|
967 | 1031 | matched = this.visit(node.children[2], base[i]);
|
968 |
| - if (matched === true) { |
| 1032 | + if (!isFalse(matched)) { |
969 | 1033 | filtered.push(base[i]);
|
970 | 1034 | }
|
971 | 1035 | }
|
|
1060 | 1124 | return matched;
|
1061 | 1125 | },
|
1062 | 1126 |
|
| 1127 | + visitAndExpression: function(node, value) { |
| 1128 | + var first = this.visit(node.children[0], value); |
| 1129 | + |
| 1130 | + if (isFalse(first) === true) { |
| 1131 | + return first; |
| 1132 | + } |
| 1133 | + return this.visit(node.children[1], value); |
| 1134 | + }, |
| 1135 | + |
| 1136 | + visitNotExpression: function(node, value) { |
| 1137 | + var first = this.visit(node.children[0], value); |
| 1138 | + return isFalse(first); |
| 1139 | + }, |
| 1140 | + |
1063 | 1141 | visitLiteral: function(node) {
|
1064 | 1142 | return node.value;
|
1065 | 1143 | },
|
|
1119 | 1197 | length: {
|
1120 | 1198 | func: this.functionLength,
|
1121 | 1199 | signature: [{types: ["string", "array", "object"]}]},
|
| 1200 | + map: { |
| 1201 | + func: this.functionMap, |
| 1202 | + signature: [{types: ["expref"]}, {types: ["array"]}]}, |
1122 | 1203 | max: {
|
1123 | 1204 | func: this.functionMax,
|
1124 | 1205 | signature: [{types: ["array-number", "array-string"]}]},
|
|
1334 | 1415 | }
|
1335 | 1416 | },
|
1336 | 1417 |
|
| 1418 | + functionMap: function(resolvedArgs) { |
| 1419 | + var mapped = []; |
| 1420 | + var interpreter = this.interpreter; |
| 1421 | + var exprefNode = resolvedArgs[0]; |
| 1422 | + var elements = resolvedArgs[1]; |
| 1423 | + for (var i = 0; i < elements.length; i++) { |
| 1424 | + mapped.push(interpreter.visit(exprefNode, elements[i])); |
| 1425 | + } |
| 1426 | + return mapped; |
| 1427 | + }, |
| 1428 | + |
1337 | 1429 | functionMerge: function(resolvedArgs) {
|
1338 | 1430 | var merged = {};
|
1339 | 1431 | for (var i = 0; i < resolvedArgs.length; i++) {
|
|
0 commit comments