how to parse a search query that uses the structure of a polish notation?

Viewed 51

My application has a search box that supports queries with the following structure:

Input: a string with the following structure:

<operand><phrase><category>  <operand><phrase><category>  ...


<operand>: can be + or - to denote include or exclude and is optional

<phrase>: can be a single word or a word or sentence surrounded by quotes

<category>: is surrounded by square brackets and can be either [c1], [c2], or [c3]

An example of one query:

+car "tart berries"[fruit]  -"broccoli"[vegetable] "green onion"[vegetable] pepper[vegetable]  -"my keys"[object]

Desired output :

[
["+", "car",         ""],
["" , "tart berries","fruit"],
["-", "broccoli".,   "vegetable"],
["" , "green onion", "vegetable"],
["" , "pepper",      "vegetable"]
]

My code:

I use the following regex to capture groups:

let re = /([+-]?)(\w+|".+?")(\[fruit\]|\[vegetable\]|\[object\])?/gi
var str = '+car "tart berries"[fruit]  -"broccoli"[vegetable] "green onion"[vegetable] pepper[vegetable]  -"my keys"[object]'

for (const match of str.matchAll(re)) {

    console.log(match);
}

Issue:

  1. Items get repeated multiple time:
["+car", "+", "car"]
[""tart berries"[fruit]", "", ""tart berries"", "[fruit]"]
  1. is there a way to exclude quotes from the results.
"tart berries" instead of ""tart berries"" 
1 Answers

You can't get rid of the quotes in the regex, JS regexp does not support branch reset group, so you will have to post-process the matches.

Also, to capture just c1/c2/c3 values without square brackets, you need to replace (\[a]|\[b]|\[c])? with an optional non-capturing group and re-group the atlernatives as (?:\[(a|b|c)]).

Note that "[^"]*" is more efficient than ".+?".

You can use

let re = /([+-]?)(\w+|"[^"]*")(?:\[(fruit|vegetable|object)])?/gi
const rx_quotes = /^"|"$/g;
var str = '+car "tart berries"[fruit]  -"broccoli"[vegetable] "green onion"[vegetable] pepper[vegetable]  -"my keys"[object]'
let result = [];
for (const match of str.matchAll(re)) {
    let [_,x,y,z] = match;
    y = y.replace(rx_quotes,'');
    if (z === undefined) z = "";
    result.push([x,y,z]);
}
console.log(result);

Regex details

  • ([+-]?) - Group 1: an optional + or - char
  • (\w+|"[^"]*") - Group 2: one or more word chars or a string between double quotation marks
  • (?:\[(fruit|vegetable|object)])? - an optional non-capturing group matching 1 or 0 occurrences of
    • \[ - a [ char
    • (fruit|vegetable|object) - Group 3: any of the substring alternatives
    • ] - a ] char.
Related