/* description: jio grammar */
/* lexical grammar */
%lex

%x letsquote
%x endquote

%%

"\""               {this.begin("letsquote"); return "QUOTE";}
<letsquote>(\\\"|[^"])*     {this.popState(); this.begin("endquote"); return "QUOTED_STRING";}
<endquote>"\""       {this.popState(); return "QUOTE";}

[^\S]+                     /* skip whitespace */
"("                        {return "LEFT_PARENTHESE";}
")"                        {return "RIGHT_PARENTHESE";}
"AND"                      {return "AND";}
"OR"                       {return "OR";}
"NOT"                      {return "NOT";}
":"                        {return "DEFINITION";}


(\!?\=|\<\=?|\>\=?)        {return 'OPERATOR';}
[^\s\n"():><!=]+           {return 'WORD';}

<<EOF>>                    {return 'EOF';}

/lex

/* operator associations and precedence */

%start begin

%% /* language grammar */

begin
    : search_text end { return $1; }
    ;

end
    :
    | EOF
    | NEWLINE
    ;

search_text
    : and_expression                { $$ = $1; }
    | and_expression search_text    { $$ = mkComplexQuery('AND', [$1, $2]); }
    | and_expression OR search_text { $$ = mkComplexQuery('OR', [$1, $3]); }
    ;

and_expression
    : boolean_expression                    { $$ = $1; }
    | boolean_expression AND and_expression { $$ = mkComplexQuery('AND', [$1, $3]); }
    ;

boolean_expression
    : NOT expression { $$ = mkNotQuery($2); }
    | expression     { $$ = $1; }
    ;

expression
    : LEFT_PARENTHESE search_text RIGHT_PARENTHESE { $$ = $2; }
    | WORD DEFINITION expression                   { querySetKey($3, $1); $$ = $3; }
    | value                                        { $$ = $1; }
    ;

value
    : OPERATOR string { $2.operator = $1 ; $$ = $2; }
    | string          { $$ = $1; }
    ;

string
    : WORD   { $$ = mkSimpleQuery('', $1); }
    | QUOTE QUOTED_STRING QUOTE { $$ = mkSimpleQuery('', $2); }
    ;

