Search code examples
javascripttypescriptpattern-matchingpeg

How can I create a PEG.js pattern for my DB queries (X "AND" Y "AND" ...)


So far my parser looks like this.

const parser = peg.generate(`

eq
  = left:attribute "=" right:value { return left == right; }

and
  = left:eq "AND" right:eq { return left && right; }

It is able to read queries like id = 2 AND createdOn = 193242. I want to be able to read id = 2 AND secondId = 444 AND createdOn = 193242 and so on... (Any number of "AND"s). How can I achieve this via PEG.js?


Solution

  • This should do it:

    and
      = left:eq right:and_eq+ {
          if ( right.length === 1 ) return left && right;
          // handle more then 1 'AND eq' here
        }
    
    and_eq
      = "AND" e:eq { return e; }
    

    Generic solution for any number of operators with any precedence (creates AST):

    {
    function leftAssotiative(left, tail) {
      let r = left;
      for (let i = 0; i < tail.length; ++i) {
        r = { kind: tail[i][0], left: r, right: tail[i][1] };
      }
      return r;
    }
    function rightAssotiative(left, tail) {
      if (tail.length > 1) {
        let r = tail[tail.length-1][1];
        for (let i = tail.length-2; i >= 0; --i) {
          r = { kind: tail[i+1][0], left: tail[i][1], right: r };
        }
        return { kind: tail[0][0], left: left, right: r };
      }
      return left;
    }
    }
    
    // Expressions
    Expr// minimal priority
      = l:Expr0 t:(p0 Expr0)* { return rightAssotiative(l, t); };
    Expr0
      = l:Expr1 t:(p1 Expr1)* { return leftAssotiative(l, t); };
    ...
    ExprN// maximal priority
      = '(' @Expr ')';
    
    // Operators
    p0 = [+-];
    p1 = [*/];
    ...