/* logica 0.0.1 */

%lex

%%
\#[^\n]*   /* skip comment */
","\s*|\s+     /* commas and whitespace are delimitors */
"("       return '(';
")"       return ')';

/* combinators */
"AND"|"and"    return 'AND';
"OR"|"or"      return 'OR';
"NOT"|"not"     return 'NOT';

/* comparators */
"<="|"LTE"|"lte"       return 'LTE';
">="|"GTE"|"gte"       return 'GTE';
">"|"GT"|"gt"          return 'GT';
"<"|"LT"|"lt"          return 'LT';
"="|"EQ"|"eq"          return 'EQ';
"IN"|"in"              return 'IN';

/* literal types */
\'.*?\'|\".*?\"               return 'STRING';
[0-9]+("."[0-9]+)?\b          return 'NUMBER';
"true"|"TRUE"|"false"|"FALSE" return 'BOOLEAN';
"NULL"|"null"                 return 'NULL';

[a-zA-Z]+[a-zA-Z0-9]*         return 'SYMBOL';

<<EOF>>                       return 'EOF';

/lex

%%

Program
  : Operation EOF
                  { yy.AST = $$; }
  | BOOLEAN EOF
                  {{ yy.AST = {type: 'BOOLEAN', val: $$ } }}
  ;

Operation
  : '(' Complement BoolOperand ')'  -> {type: 'COMBINATION', combinator:$2, operands: [$3]}
  | '(' Combinator BoolOperands ')' -> {type: 'COMBINATION', combinator:$2, operands: $3}
  | '(' Value Operator Operands ')' -> {type: 'OPERATION', headOperand: $2, operator:$3, tailOperands: $4}
  | '(' Value ')'

  /* backwards compatibility with operator-first notation for builtin comparators */
  | '(' Comparator Operand Operands ')' -> {type: 'OPERATION', headOperand: $3, operator:$2, tailOperands: $4}
  ;

Operands
  : Operands Operand -> [$1, $2]
  | Operand -> [$1]
  ;

Complement
  : "NOT" -> 'NOT'
  ;

Combinator
  : "AND" -> 'AND'
  | "OR" -> 'OR'
  ;

Comparator
  : "EQ"  -> 'EQ'
  | "GT"  -> 'GT'
  | "LT"  -> 'LT'
  | "GTE" -> 'GTE'
  | "LTE" -> 'LTE'
  | "IN"  -> 'IN'
  ;

Operator
  : Comparator
  | SYMBOL
  ;

Boolean
  : BOOLEAN   -> {type: 'BOOLEAN', val: JSON.parse($1)}
  ;

Literal
  : NUMBER      -> {type: 'NUMBER', val: $1}
  | STRING      -> {type: 'STRING', val: $1}
  | Boolean
  | NULL        -> {type: 'NULL', val: null}
  ;

Value
  : Literal
  | SYMBOL      -> {type: 'SYMBOL', val: $1}
  ;

BoolOperands
  : BoolOperands BoolOperand -> [$1, $2]
  | BoolOperand -> [$1]
  ;

BoolOperand
  : SYMBOL    -> {type: 'SYMBOL', val: $1}
  | Boolean
  | Operation
  ;

Operand
  : Value
  | Operation
  ;