constructor() {
        const TYPE = {
            OR: "OR",
            AND: "AND",
            NOT: "NOT",
            PAREN_OPEN: "PAREN_OPEN",
            PAREN_CLOSE: "PAREN_CLOSE",
            KEYWORD: "KEYWORD",
            PHRASE: "PHRASE",
            REGEXP: "REGEXP",
            QUALIFIER: "QUALIFIER",
        }
        this.TYPE = TYPE
        const { OR, AND, NOT, QUALIFIER, PAREN_OPEN, PAREN_CLOSE } = TYPE
        this.INVALID_POSITION = {
            FIRST: new Set([OR, AND, PAREN_CLOSE]),
            LAST: new Set([OR, AND, NOT, PAREN_OPEN, QUALIFIER]),
            FOLLOW: {
                [OR]: new Set([OR, AND, PAREN_CLOSE]),
                [AND]: new Set([OR, AND, PAREN_CLOSE]),
                [NOT]: new Set([OR, AND, NOT, PAREN_CLOSE]),
                [PAREN_OPEN]: new Set([OR, AND, PAREN_CLOSE]),
                [QUALIFIER]: new Set([OR, AND, NOT, PAREN_CLOSE, QUALIFIER]),
            },
            AND: {
                PREV: new Set([OR, AND, NOT, PAREN_OPEN, QUALIFIER]),
                NEXT: new Set([OR, AND, NOT, PAREN_CLOSE]),
            },
        }
        this.setQualifier()
    }
grammar:
  <query> ::= <expression>
  <expression> ::= <term> ( <or> <term> )*
  <term> ::= <factor> ( <conjunction> <factor> )*
  <factor> ::= <qualifier>? <match>
  <qualifier> ::= <scope> <operator>
  <match> ::= <keyword> | '"'<keyword>'"' | '/'<regexp>'/' | '('<expression>')'
  <conjunction> ::= <and> | <not>
  <and> ::= 'AND' | ' '
  <or> ::= 'OR' | '|'
  <not> ::= 'NOT' | '-'
  <keyword> ::= [^\\s"()|]+
  <regexp> ::= [^/]+
  <operator> ::= ':' | '=' | '>=' | '<=' | '>' | '<'
  <scope> ::= 'default' | 'file' | 'path' | 'ext' | 'content' | 'size' | 'time'