From 5a651922ea7798892039be67bc15411f47739134 Mon Sep 17 00:00:00 2001 From: 289Adam289 Date: Wed, 30 Oct 2024 15:04:54 +0100 Subject: [PATCH] improve parser and unit tests --- src/libs/SearchParser/searchParser.js | 366 +++++++++++++-------- src/libs/SearchParser/searchParser.peggy | 17 +- tests/unit/SearchAutocompleteParserTest.ts | 14 + tests/unit/SearchParserTest.ts | 56 ++++ 4 files changed, 313 insertions(+), 140 deletions(-) diff --git a/src/libs/SearchParser/searchParser.js b/src/libs/SearchParser/searchParser.js index 1e8d12f16a32..c94222f3a6e9 100644 --- a/src/libs/SearchParser/searchParser.js +++ b/src/libs/SearchParser/searchParser.js @@ -208,50 +208,52 @@ function peg$parse(input, options) { var peg$c25 = "<"; var peg$c26 = "\""; - var peg$r0 = /^[:=]/; - var peg$r1 = /^[^ ,"\t\n\r]/; - var peg$r2 = /^[^"\r\n]/; - var peg$r3 = /^[^ ,\t\n\r]/; - var peg$r4 = /^[ \t\r\n]/; - - var peg$e0 = peg$otherExpectation("key"); - var peg$e1 = peg$literalExpectation("date", false); - var peg$e2 = peg$literalExpectation("amount", false); - var peg$e3 = peg$literalExpectation("merchant", false); - var peg$e4 = peg$literalExpectation("description", false); - var peg$e5 = peg$literalExpectation("reportID", false); - var peg$e6 = peg$literalExpectation("keyword", false); - var peg$e7 = peg$literalExpectation("in", false); - var peg$e8 = peg$literalExpectation("currency", false); - var peg$e9 = peg$literalExpectation("tag", false); - var peg$e10 = peg$literalExpectation("category", false); - var peg$e11 = peg$literalExpectation("to", false); - var peg$e12 = peg$literalExpectation("taxRate", false); - var peg$e13 = peg$literalExpectation("cardID", false); - var peg$e14 = peg$literalExpectation("from", false); - var peg$e15 = peg$literalExpectation("expenseType", false); - var peg$e16 = peg$otherExpectation("default key"); - var peg$e17 = peg$literalExpectation("type", false); - var peg$e18 = peg$literalExpectation("status", false); - var peg$e19 = peg$literalExpectation("sortBy", false); - var peg$e20 = peg$literalExpectation("sortOrder", false); - var peg$e21 = peg$literalExpectation("policyID", false); - var peg$e22 = peg$literalExpectation(",", false); - var peg$e23 = peg$otherExpectation("operator"); - var peg$e24 = peg$classExpectation([":", "="], false, false); - var peg$e25 = peg$literalExpectation("!=", false); - var peg$e26 = peg$literalExpectation(">=", false); - var peg$e27 = peg$literalExpectation(">", false); - var peg$e28 = peg$literalExpectation("<=", false); - var peg$e29 = peg$literalExpectation("<", false); - var peg$e30 = peg$otherExpectation("quote"); - var peg$e31 = peg$classExpectation([" ", ",", "\"", "\t", "\n", "\r"], true, false); - var peg$e32 = peg$literalExpectation("\"", false); - var peg$e33 = peg$classExpectation(["\"", "\r", "\n"], true, false); - var peg$e34 = peg$classExpectation([" ", ",", "\t", "\n", "\r"], true, false); - var peg$e35 = peg$otherExpectation("word"); - var peg$e36 = peg$otherExpectation("whitespace"); - var peg$e37 = peg$classExpectation([" ", "\t", "\r", "\n"], false, false); + var peg$r0 = /^[^ \t\r\n]/; + var peg$r1 = /^[:=]/; + var peg$r2 = /^[^ ,"\t\n\r]/; + var peg$r3 = /^[^"\r\n]/; + var peg$r4 = /^[^ ,\t\n\r]/; + var peg$r5 = /^[ \t\r\n]/; + + var peg$e0 = peg$classExpectation([" ", "\t", "\r", "\n"], true, false); + var peg$e1 = peg$otherExpectation("key"); + var peg$e2 = peg$literalExpectation("date", false); + var peg$e3 = peg$literalExpectation("amount", false); + var peg$e4 = peg$literalExpectation("merchant", false); + var peg$e5 = peg$literalExpectation("description", false); + var peg$e6 = peg$literalExpectation("reportID", false); + var peg$e7 = peg$literalExpectation("keyword", false); + var peg$e8 = peg$literalExpectation("in", false); + var peg$e9 = peg$literalExpectation("currency", false); + var peg$e10 = peg$literalExpectation("tag", false); + var peg$e11 = peg$literalExpectation("category", false); + var peg$e12 = peg$literalExpectation("to", false); + var peg$e13 = peg$literalExpectation("taxRate", false); + var peg$e14 = peg$literalExpectation("cardID", false); + var peg$e15 = peg$literalExpectation("from", false); + var peg$e16 = peg$literalExpectation("expenseType", false); + var peg$e17 = peg$otherExpectation("default key"); + var peg$e18 = peg$literalExpectation("type", false); + var peg$e19 = peg$literalExpectation("status", false); + var peg$e20 = peg$literalExpectation("sortBy", false); + var peg$e21 = peg$literalExpectation("sortOrder", false); + var peg$e22 = peg$literalExpectation("policyID", false); + var peg$e23 = peg$literalExpectation(",", false); + var peg$e24 = peg$otherExpectation("operator"); + var peg$e25 = peg$classExpectation([":", "="], false, false); + var peg$e26 = peg$literalExpectation("!=", false); + var peg$e27 = peg$literalExpectation(">=", false); + var peg$e28 = peg$literalExpectation(">", false); + var peg$e29 = peg$literalExpectation("<=", false); + var peg$e30 = peg$literalExpectation("<", false); + var peg$e31 = peg$otherExpectation("quote"); + var peg$e32 = peg$classExpectation([" ", ",", "\"", "\t", "\n", "\r"], true, false); + var peg$e33 = peg$literalExpectation("\"", false); + var peg$e34 = peg$classExpectation(["\"", "\r", "\n"], true, false); + var peg$e35 = peg$classExpectation([" ", ",", "\t", "\n", "\r"], true, false); + var peg$e36 = peg$otherExpectation("word"); + var peg$e37 = peg$otherExpectation("whitespace"); + var peg$e38 = peg$classExpectation([" ", "\t", "\r", "\n"], false, false); var peg$f0 = function(filters) { return applyDefaults(filters); }; var peg$f1 = function(head, tail) { @@ -286,16 +288,21 @@ function peg$parse(input, options) { var peg$f2 = function(key, op, value) { updateDefaultValues(key, value); }; - var peg$f3 = function(value) { return buildFilter("eq", "keyword", value); }; + var peg$f3 = function(value) { + if (Array.isArray(value)) { + return buildFilter("eq", "keyword", value.join("")); + } + return buildFilter("eq", "keyword", value); + }; var peg$f4 = function(field, op, values) { return buildFilter(op, field, values); }; - var peg$f5 = function(parts) { + var peg$f5 = function(parts, empty) { const value = parts.flat().map((word) => { - if (word.startsWith('"') && word.endsWith('"') && word.length >= 2) { - return word.slice(1,-1); - } - return word; + if (word.startsWith('"') && word.endsWith('"') && word.length >= 2) { + return word.slice(1, -1); + } + return word; }); if (value.length > 1) { return value.filter((word) => word.length > 0); @@ -588,7 +595,31 @@ function peg$parse(input, options) { s0 = peg$currPos; s1 = peg$parse_(); - s2 = peg$parseidentifier(); + s2 = peg$parsequotedString(); + if (s2 === peg$FAILED) { + s2 = []; + s3 = input.charAt(peg$currPos); + if (peg$r0.test(s3)) { + peg$currPos++; + } else { + s3 = peg$FAILED; + if (peg$silentFails === 0) { peg$fail(peg$e0); } + } + if (s3 !== peg$FAILED) { + while (s3 !== peg$FAILED) { + s2.push(s3); + s3 = input.charAt(peg$currPos); + if (peg$r0.test(s3)) { + peg$currPos++; + } else { + s3 = peg$FAILED; + if (peg$silentFails === 0) { peg$fail(peg$e0); } + } + } + } else { + s2 = peg$FAILED; + } + } if (s2 !== peg$FAILED) { s3 = peg$parse_(); peg$savedPos = s0; @@ -642,7 +673,7 @@ function peg$parse(input, options) { peg$currPos += 4; } else { s1 = peg$FAILED; - if (peg$silentFails === 0) { peg$fail(peg$e1); } + if (peg$silentFails === 0) { peg$fail(peg$e2); } } if (s1 === peg$FAILED) { if (input.substr(peg$currPos, 6) === peg$c1) { @@ -650,7 +681,7 @@ function peg$parse(input, options) { peg$currPos += 6; } else { s1 = peg$FAILED; - if (peg$silentFails === 0) { peg$fail(peg$e2); } + if (peg$silentFails === 0) { peg$fail(peg$e3); } } if (s1 === peg$FAILED) { if (input.substr(peg$currPos, 8) === peg$c2) { @@ -658,7 +689,7 @@ function peg$parse(input, options) { peg$currPos += 8; } else { s1 = peg$FAILED; - if (peg$silentFails === 0) { peg$fail(peg$e3); } + if (peg$silentFails === 0) { peg$fail(peg$e4); } } if (s1 === peg$FAILED) { if (input.substr(peg$currPos, 11) === peg$c3) { @@ -666,7 +697,7 @@ function peg$parse(input, options) { peg$currPos += 11; } else { s1 = peg$FAILED; - if (peg$silentFails === 0) { peg$fail(peg$e4); } + if (peg$silentFails === 0) { peg$fail(peg$e5); } } if (s1 === peg$FAILED) { if (input.substr(peg$currPos, 8) === peg$c4) { @@ -674,7 +705,7 @@ function peg$parse(input, options) { peg$currPos += 8; } else { s1 = peg$FAILED; - if (peg$silentFails === 0) { peg$fail(peg$e5); } + if (peg$silentFails === 0) { peg$fail(peg$e6); } } if (s1 === peg$FAILED) { if (input.substr(peg$currPos, 7) === peg$c5) { @@ -682,7 +713,7 @@ function peg$parse(input, options) { peg$currPos += 7; } else { s1 = peg$FAILED; - if (peg$silentFails === 0) { peg$fail(peg$e6); } + if (peg$silentFails === 0) { peg$fail(peg$e7); } } if (s1 === peg$FAILED) { if (input.substr(peg$currPos, 2) === peg$c6) { @@ -690,7 +721,7 @@ function peg$parse(input, options) { peg$currPos += 2; } else { s1 = peg$FAILED; - if (peg$silentFails === 0) { peg$fail(peg$e7); } + if (peg$silentFails === 0) { peg$fail(peg$e8); } } if (s1 === peg$FAILED) { if (input.substr(peg$currPos, 8) === peg$c7) { @@ -698,7 +729,7 @@ function peg$parse(input, options) { peg$currPos += 8; } else { s1 = peg$FAILED; - if (peg$silentFails === 0) { peg$fail(peg$e8); } + if (peg$silentFails === 0) { peg$fail(peg$e9); } } if (s1 === peg$FAILED) { if (input.substr(peg$currPos, 3) === peg$c8) { @@ -706,7 +737,7 @@ function peg$parse(input, options) { peg$currPos += 3; } else { s1 = peg$FAILED; - if (peg$silentFails === 0) { peg$fail(peg$e9); } + if (peg$silentFails === 0) { peg$fail(peg$e10); } } if (s1 === peg$FAILED) { if (input.substr(peg$currPos, 8) === peg$c9) { @@ -714,7 +745,7 @@ function peg$parse(input, options) { peg$currPos += 8; } else { s1 = peg$FAILED; - if (peg$silentFails === 0) { peg$fail(peg$e10); } + if (peg$silentFails === 0) { peg$fail(peg$e11); } } if (s1 === peg$FAILED) { if (input.substr(peg$currPos, 2) === peg$c10) { @@ -722,7 +753,7 @@ function peg$parse(input, options) { peg$currPos += 2; } else { s1 = peg$FAILED; - if (peg$silentFails === 0) { peg$fail(peg$e11); } + if (peg$silentFails === 0) { peg$fail(peg$e12); } } if (s1 === peg$FAILED) { if (input.substr(peg$currPos, 7) === peg$c11) { @@ -730,7 +761,7 @@ function peg$parse(input, options) { peg$currPos += 7; } else { s1 = peg$FAILED; - if (peg$silentFails === 0) { peg$fail(peg$e12); } + if (peg$silentFails === 0) { peg$fail(peg$e13); } } if (s1 === peg$FAILED) { if (input.substr(peg$currPos, 6) === peg$c12) { @@ -738,7 +769,7 @@ function peg$parse(input, options) { peg$currPos += 6; } else { s1 = peg$FAILED; - if (peg$silentFails === 0) { peg$fail(peg$e13); } + if (peg$silentFails === 0) { peg$fail(peg$e14); } } if (s1 === peg$FAILED) { if (input.substr(peg$currPos, 4) === peg$c13) { @@ -746,7 +777,7 @@ function peg$parse(input, options) { peg$currPos += 4; } else { s1 = peg$FAILED; - if (peg$silentFails === 0) { peg$fail(peg$e14); } + if (peg$silentFails === 0) { peg$fail(peg$e15); } } if (s1 === peg$FAILED) { if (input.substr(peg$currPos, 11) === peg$c14) { @@ -754,7 +785,7 @@ function peg$parse(input, options) { peg$currPos += 11; } else { s1 = peg$FAILED; - if (peg$silentFails === 0) { peg$fail(peg$e15); } + if (peg$silentFails === 0) { peg$fail(peg$e16); } } } } @@ -779,7 +810,7 @@ function peg$parse(input, options) { peg$silentFails--; if (s0 === peg$FAILED) { s1 = peg$FAILED; - if (peg$silentFails === 0) { peg$fail(peg$e0); } + if (peg$silentFails === 0) { peg$fail(peg$e1); } } return s0; @@ -795,7 +826,7 @@ function peg$parse(input, options) { peg$currPos += 4; } else { s1 = peg$FAILED; - if (peg$silentFails === 0) { peg$fail(peg$e17); } + if (peg$silentFails === 0) { peg$fail(peg$e18); } } if (s1 === peg$FAILED) { if (input.substr(peg$currPos, 6) === peg$c16) { @@ -803,7 +834,7 @@ function peg$parse(input, options) { peg$currPos += 6; } else { s1 = peg$FAILED; - if (peg$silentFails === 0) { peg$fail(peg$e18); } + if (peg$silentFails === 0) { peg$fail(peg$e19); } } if (s1 === peg$FAILED) { if (input.substr(peg$currPos, 6) === peg$c17) { @@ -811,7 +842,7 @@ function peg$parse(input, options) { peg$currPos += 6; } else { s1 = peg$FAILED; - if (peg$silentFails === 0) { peg$fail(peg$e19); } + if (peg$silentFails === 0) { peg$fail(peg$e20); } } if (s1 === peg$FAILED) { if (input.substr(peg$currPos, 9) === peg$c18) { @@ -819,7 +850,7 @@ function peg$parse(input, options) { peg$currPos += 9; } else { s1 = peg$FAILED; - if (peg$silentFails === 0) { peg$fail(peg$e20); } + if (peg$silentFails === 0) { peg$fail(peg$e21); } } if (s1 === peg$FAILED) { if (input.substr(peg$currPos, 8) === peg$c19) { @@ -827,7 +858,7 @@ function peg$parse(input, options) { peg$currPos += 8; } else { s1 = peg$FAILED; - if (peg$silentFails === 0) { peg$fail(peg$e21); } + if (peg$silentFails === 0) { peg$fail(peg$e22); } } } } @@ -842,58 +873,125 @@ function peg$parse(input, options) { peg$silentFails--; if (s0 === peg$FAILED) { s1 = peg$FAILED; - if (peg$silentFails === 0) { peg$fail(peg$e16); } + if (peg$silentFails === 0) { peg$fail(peg$e17); } } return s0; } function peg$parseidentifier() { - var s0, s1, s2, s3, s4; + var s0, s1, s2, s3, s4, s5, s6; s0 = peg$currPos; - s1 = peg$currPos; - s2 = []; - s3 = peg$parsequotedString(); - if (s3 === peg$FAILED) { - s3 = peg$parsealphanumeric(); + s1 = []; + if (input.charCodeAt(peg$currPos) === 44) { + s2 = peg$c20; + peg$currPos++; + } else { + s2 = peg$FAILED; + if (peg$silentFails === 0) { peg$fail(peg$e23); } } - while (s3 !== peg$FAILED) { - s2.push(s3); - s3 = peg$currPos; + if (s2 !== peg$FAILED) { + while (s2 !== peg$FAILED) { + s1.push(s2); + if (input.charCodeAt(peg$currPos) === 44) { + s2 = peg$c20; + peg$currPos++; + } else { + s2 = peg$FAILED; + if (peg$silentFails === 0) { peg$fail(peg$e23); } + } + } + } else { + s1 = peg$FAILED; + } + if (s1 === peg$FAILED) { + s1 = null; + } + s2 = peg$currPos; + s3 = []; + s4 = peg$parsequotedString(); + if (s4 === peg$FAILED) { + s4 = peg$parsealphanumeric(); + } + while (s4 !== peg$FAILED) { + s3.push(s4); + s4 = peg$currPos; + s5 = []; if (input.charCodeAt(peg$currPos) === 44) { - s4 = peg$c20; + s6 = peg$c20; peg$currPos++; } else { - s4 = peg$FAILED; - if (peg$silentFails === 0) { peg$fail(peg$e22); } + s6 = peg$FAILED; + if (peg$silentFails === 0) { peg$fail(peg$e23); } } - if (s4 !== peg$FAILED) { - s4 = peg$parsequotedString(); - if (s4 === peg$FAILED) { - s4 = peg$parsealphanumeric(); + if (s6 !== peg$FAILED) { + while (s6 !== peg$FAILED) { + s5.push(s6); + if (input.charCodeAt(peg$currPos) === 44) { + s6 = peg$c20; + peg$currPos++; + } else { + s6 = peg$FAILED; + if (peg$silentFails === 0) { peg$fail(peg$e23); } + } } - if (s4 === peg$FAILED) { - peg$currPos = s3; - s3 = peg$FAILED; + } else { + s5 = peg$FAILED; + } + if (s5 !== peg$FAILED) { + s5 = peg$parsequotedString(); + if (s5 === peg$FAILED) { + s5 = peg$parsealphanumeric(); + } + if (s5 === peg$FAILED) { + peg$currPos = s4; + s4 = peg$FAILED; } else { - s3 = s4; + s4 = s5; } } else { - s3 = s4; + s4 = s5; } } - if (s2.length < 1) { - peg$currPos = s1; - s1 = peg$FAILED; + if (s3.length < 1) { + peg$currPos = s2; + s2 = peg$FAILED; } else { - s1 = s2; + s2 = s3; } - if (s1 !== peg$FAILED) { + if (s2 !== peg$FAILED) { + s3 = []; + if (input.charCodeAt(peg$currPos) === 44) { + s4 = peg$c20; + peg$currPos++; + } else { + s4 = peg$FAILED; + if (peg$silentFails === 0) { peg$fail(peg$e23); } + } + if (s4 !== peg$FAILED) { + while (s4 !== peg$FAILED) { + s3.push(s4); + if (input.charCodeAt(peg$currPos) === 44) { + s4 = peg$c20; + peg$currPos++; + } else { + s4 = peg$FAILED; + if (peg$silentFails === 0) { peg$fail(peg$e23); } + } + } + } else { + s3 = peg$FAILED; + } + if (s3 === peg$FAILED) { + s3 = null; + } peg$savedPos = s0; - s1 = peg$f5(s1); + s0 = peg$f5(s2, s3); + } else { + peg$currPos = s0; + s0 = peg$FAILED; } - s0 = s1; return s0; } @@ -904,11 +1002,11 @@ function peg$parse(input, options) { peg$silentFails++; s0 = peg$currPos; s1 = input.charAt(peg$currPos); - if (peg$r0.test(s1)) { + if (peg$r1.test(s1)) { peg$currPos++; } else { s1 = peg$FAILED; - if (peg$silentFails === 0) { peg$fail(peg$e24); } + if (peg$silentFails === 0) { peg$fail(peg$e25); } } if (s1 !== peg$FAILED) { peg$savedPos = s0; @@ -922,7 +1020,7 @@ function peg$parse(input, options) { peg$currPos += 2; } else { s1 = peg$FAILED; - if (peg$silentFails === 0) { peg$fail(peg$e25); } + if (peg$silentFails === 0) { peg$fail(peg$e26); } } if (s1 !== peg$FAILED) { peg$savedPos = s0; @@ -936,7 +1034,7 @@ function peg$parse(input, options) { peg$currPos += 2; } else { s1 = peg$FAILED; - if (peg$silentFails === 0) { peg$fail(peg$e26); } + if (peg$silentFails === 0) { peg$fail(peg$e27); } } if (s1 !== peg$FAILED) { peg$savedPos = s0; @@ -950,7 +1048,7 @@ function peg$parse(input, options) { peg$currPos++; } else { s1 = peg$FAILED; - if (peg$silentFails === 0) { peg$fail(peg$e27); } + if (peg$silentFails === 0) { peg$fail(peg$e28); } } if (s1 !== peg$FAILED) { peg$savedPos = s0; @@ -964,7 +1062,7 @@ function peg$parse(input, options) { peg$currPos += 2; } else { s1 = peg$FAILED; - if (peg$silentFails === 0) { peg$fail(peg$e28); } + if (peg$silentFails === 0) { peg$fail(peg$e29); } } if (s1 !== peg$FAILED) { peg$savedPos = s0; @@ -978,7 +1076,7 @@ function peg$parse(input, options) { peg$currPos++; } else { s1 = peg$FAILED; - if (peg$silentFails === 0) { peg$fail(peg$e29); } + if (peg$silentFails === 0) { peg$fail(peg$e30); } } if (s1 !== peg$FAILED) { peg$savedPos = s0; @@ -993,7 +1091,7 @@ function peg$parse(input, options) { peg$silentFails--; if (s0 === peg$FAILED) { s1 = peg$FAILED; - if (peg$silentFails === 0) { peg$fail(peg$e23); } + if (peg$silentFails === 0) { peg$fail(peg$e24); } } return s0; @@ -1006,20 +1104,20 @@ function peg$parse(input, options) { s0 = peg$currPos; s1 = []; s2 = input.charAt(peg$currPos); - if (peg$r1.test(s2)) { + if (peg$r2.test(s2)) { peg$currPos++; } else { s2 = peg$FAILED; - if (peg$silentFails === 0) { peg$fail(peg$e31); } + if (peg$silentFails === 0) { peg$fail(peg$e32); } } while (s2 !== peg$FAILED) { s1.push(s2); s2 = input.charAt(peg$currPos); - if (peg$r1.test(s2)) { + if (peg$r2.test(s2)) { peg$currPos++; } else { s2 = peg$FAILED; - if (peg$silentFails === 0) { peg$fail(peg$e31); } + if (peg$silentFails === 0) { peg$fail(peg$e32); } } } if (input.charCodeAt(peg$currPos) === 34) { @@ -1027,25 +1125,25 @@ function peg$parse(input, options) { peg$currPos++; } else { s2 = peg$FAILED; - if (peg$silentFails === 0) { peg$fail(peg$e32); } + if (peg$silentFails === 0) { peg$fail(peg$e33); } } if (s2 !== peg$FAILED) { s3 = []; s4 = input.charAt(peg$currPos); - if (peg$r2.test(s4)) { + if (peg$r3.test(s4)) { peg$currPos++; } else { s4 = peg$FAILED; - if (peg$silentFails === 0) { peg$fail(peg$e33); } + if (peg$silentFails === 0) { peg$fail(peg$e34); } } while (s4 !== peg$FAILED) { s3.push(s4); s4 = input.charAt(peg$currPos); - if (peg$r2.test(s4)) { + if (peg$r3.test(s4)) { peg$currPos++; } else { s4 = peg$FAILED; - if (peg$silentFails === 0) { peg$fail(peg$e33); } + if (peg$silentFails === 0) { peg$fail(peg$e34); } } } if (input.charCodeAt(peg$currPos) === 34) { @@ -1053,25 +1151,25 @@ function peg$parse(input, options) { peg$currPos++; } else { s4 = peg$FAILED; - if (peg$silentFails === 0) { peg$fail(peg$e32); } + if (peg$silentFails === 0) { peg$fail(peg$e33); } } if (s4 !== peg$FAILED) { s5 = []; s6 = input.charAt(peg$currPos); - if (peg$r3.test(s6)) { + if (peg$r4.test(s6)) { peg$currPos++; } else { s6 = peg$FAILED; - if (peg$silentFails === 0) { peg$fail(peg$e34); } + if (peg$silentFails === 0) { peg$fail(peg$e35); } } while (s6 !== peg$FAILED) { s5.push(s6); s6 = input.charAt(peg$currPos); - if (peg$r3.test(s6)) { + if (peg$r4.test(s6)) { peg$currPos++; } else { s6 = peg$FAILED; - if (peg$silentFails === 0) { peg$fail(peg$e34); } + if (peg$silentFails === 0) { peg$fail(peg$e35); } } } peg$savedPos = s0; @@ -1087,7 +1185,7 @@ function peg$parse(input, options) { peg$silentFails--; if (s0 === peg$FAILED) { s1 = peg$FAILED; - if (peg$silentFails === 0) { peg$fail(peg$e30); } + if (peg$silentFails === 0) { peg$fail(peg$e31); } } return s0; @@ -1100,21 +1198,21 @@ function peg$parse(input, options) { s0 = peg$currPos; s1 = []; s2 = input.charAt(peg$currPos); - if (peg$r3.test(s2)) { + if (peg$r4.test(s2)) { peg$currPos++; } else { s2 = peg$FAILED; - if (peg$silentFails === 0) { peg$fail(peg$e34); } + if (peg$silentFails === 0) { peg$fail(peg$e35); } } if (s2 !== peg$FAILED) { while (s2 !== peg$FAILED) { s1.push(s2); s2 = input.charAt(peg$currPos); - if (peg$r3.test(s2)) { + if (peg$r4.test(s2)) { peg$currPos++; } else { s2 = peg$FAILED; - if (peg$silentFails === 0) { peg$fail(peg$e34); } + if (peg$silentFails === 0) { peg$fail(peg$e35); } } } } else { @@ -1128,7 +1226,7 @@ function peg$parse(input, options) { peg$silentFails--; if (s0 === peg$FAILED) { s1 = peg$FAILED; - if (peg$silentFails === 0) { peg$fail(peg$e35); } + if (peg$silentFails === 0) { peg$fail(peg$e36); } } return s0; @@ -1152,25 +1250,25 @@ function peg$parse(input, options) { peg$silentFails++; s0 = []; s1 = input.charAt(peg$currPos); - if (peg$r4.test(s1)) { + if (peg$r5.test(s1)) { peg$currPos++; } else { s1 = peg$FAILED; - if (peg$silentFails === 0) { peg$fail(peg$e37); } + if (peg$silentFails === 0) { peg$fail(peg$e38); } } while (s1 !== peg$FAILED) { s0.push(s1); s1 = input.charAt(peg$currPos); - if (peg$r4.test(s1)) { + if (peg$r5.test(s1)) { peg$currPos++; } else { s1 = peg$FAILED; - if (peg$silentFails === 0) { peg$fail(peg$e37); } + if (peg$silentFails === 0) { peg$fail(peg$e38); } } } peg$silentFails--; s1 = peg$FAILED; - if (peg$silentFails === 0) { peg$fail(peg$e36); } + if (peg$silentFails === 0) { peg$fail(peg$e37); } return s0; } diff --git a/src/libs/SearchParser/searchParser.peggy b/src/libs/SearchParser/searchParser.peggy index 7d5815d41459..d3aaf5ddf97b 100644 --- a/src/libs/SearchParser/searchParser.peggy +++ b/src/libs/SearchParser/searchParser.peggy @@ -84,7 +84,12 @@ defaultFilter } freeTextFilter - = _ value:identifier _ { return buildFilter("eq", "keyword", value); } + = _ value:(quotedString / [^ \t\r\n]+) _ { + if (Array.isArray(value)) { + return buildFilter("eq", "keyword", value.join("")); + } + return buildFilter("eq", "keyword", value); + } standardFilter = _ field:key _ op:operator _ values:identifier { @@ -114,12 +119,12 @@ defaultKey "default key" = @("type" / "status" / "sortBy" / "sortOrder" / "policyID") identifier - = parts:(quotedString / alphanumeric)|1.., ","| { + = (","+)? parts:(quotedString / alphanumeric)|1.., ","+| empty:(","+)? { const value = parts.flat().map((word) => { - if (word.startsWith('"') && word.endsWith('"') && word.length >= 2) { - return word.slice(1,-1); - } - return word; + if (word.startsWith('"') && word.endsWith('"') && word.length >= 2) { + return word.slice(1, -1); + } + return word; }); if (value.length > 1) { return value.filter((word) => word.length > 0); diff --git a/tests/unit/SearchAutocompleteParserTest.ts b/tests/unit/SearchAutocompleteParserTest.ts index 97db970bbb43..2571b03089b1 100644 --- a/tests/unit/SearchAutocompleteParserTest.ts +++ b/tests/unit/SearchAutocompleteParserTest.ts @@ -9,6 +9,20 @@ const tests = [ ranges: [], }, }, + { + query: ',', + expected: { + autocomplete: null, + ranges: [], + }, + }, + { + query: 'tag:,,', + expected: { + autocomplete: null, + ranges: [], + }, + }, { query: 'type:expense status:all', expected: { diff --git a/tests/unit/SearchParserTest.ts b/tests/unit/SearchParserTest.ts index 1f2a97771bf3..2964e406b512 100644 --- a/tests/unit/SearchParserTest.ts +++ b/tests/unit/SearchParserTest.ts @@ -12,6 +12,62 @@ const tests = [ filters: null, }, }, + { + query: ',', + expected: { + type: 'expense', + status: 'all', + sortBy: 'date', + sortOrder: 'desc', + filters: { + operator: 'eq', + left: 'keyword', + right: [','], + }, + }, + }, + { + query: 'currency:,', + expected: { + type: 'expense', + status: 'all', + sortBy: 'date', + sortOrder: 'desc', + filters: { + operator: 'eq', + left: 'keyword', + right: ['currency:,'], + }, + }, + }, + { + query: 'tag:,,travel,', + expected: { + type: 'expense', + status: 'all', + sortBy: 'date', + sortOrder: 'desc', + filters: { + operator: 'eq', + left: 'tag', + right: 'travel', + }, + }, + }, + { + query: 'category:', + expected: { + type: 'expense', + status: 'all', + sortBy: 'date', + sortOrder: 'desc', + filters: { + operator: 'eq', + left: 'keyword', + right: ['category:'], + }, + }, + }, { query: 'in:123333 currency:USD merchant:marriott', expected: {