%{
#include <string>
#include <utility>
#include <vector>
#include <ola/Logging.h>
#include "tools/ola_trigger/ConfigCommon.h"
#include "tools/ola_trigger/ParserActions.h"
int yyerror(const char *s);
int yylex(void);

%}

%union{
  unsigned int int_val;
  std::string *str_val;
  std::vector<std::string> *str_list;
  class Action *action;
  IntervalList *interval_list;
  ValueInterval *interval;
  std::pair<Action*, Action*> *action_pair;
}

%start  config

%token NEW_LINE
%token WHITESPACE
%token <int_val> INTEGER_LITERAL
%token <str_val> QUOTED_VALUE
%token <str_val> WORD_LITERAL
%type <str_val> value
%type <str_val> program
%type <str_val> full_path
%type <str_list> arguments
%type <str_val> argument
%type <str_list> assignment
%type <action> action
%type <action_pair> actions
%type <action> falling_action
%type <action> optional_falling_action
%type <interval_list> slot_values_match
%type <interval_list> slot_values
%type <interval> slot_value_range

%%
config :
      /* empty */
    | config line
;

line :
      optional_whitespace NEW_LINE
    | expression optional_whitespace NEW_LINE
;

expression :
      assignment { SetDefaultValue($1); }
    | action_directive
;

assignment :
      WORD_LITERAL optional_whitespace '=' optional_whitespace value {
        $$ = new std::vector<std::string>;
        $$->push_back(*$1);
        $$->push_back(*$5);
        delete $1;
        delete $5;
      }
;

value :
      WORD_LITERAL
    | QUOTED_VALUE
;

optional_whitespace:
      /* empty */
    | WHITESPACE
;

action_directive:
      INTEGER_LITERAL WHITESPACE slot_values_match WHITESPACE actions {
        if ($3 == NULL) {
          SetDefaultAction($1, $5->first, $5->second);
        } else {
          SetSlotAction($1, $3, $5->first, $5->second);
        }
        delete $5;
      }
;

slot_values_match:
      '%' { $$ = NULL; }
    | slot_values { $$ = $1; }
;

slot_values:
      slot_value_range { $$ = new IntervalList(); $$->push_back($1); }
    | slot_values ',' slot_value_range { $$ = $1; $$->push_back($3); }
;

slot_value_range:
      INTEGER_LITERAL { $$ = CreateInterval($1, $1); }
    | INTEGER_LITERAL '-' INTEGER_LITERAL { $$ = CreateInterval($1, $3); }
;

actions:
      action { $$ = new ActionPair($1, $1); }
    | falling_action { $$ = new ActionPair(NULL, $1); }
    | '+' action optional_falling_action {
        $$ = new std::pair<Action*, Action*>($2, $3);
      }
;

falling_action:
    '-' action { $$ = $2; }
;

optional_falling_action:
      /* empty */ { $$ = NULL; }
    | WHITESPACE falling_action { $$ = $2; }
;

action:
      assignment { $$ = CreateAssignmentAction($1); }
    | '`' program arguments '`' optional_whitespace {
        $$ = CreateCommandAction(*$2, $3);
        delete $2;
      }
;

arguments:
      /* empty */ { $$ = new std::vector<std::string>(); }
    | arguments WHITESPACE argument {
        $$ = $1; $$->push_back(std::string(*$3));
        delete $3;
    }
;

argument:
      WORD_LITERAL
    | QUOTED_VALUE
;

program:
      WORD_LITERAL
    | QUOTED_VALUE
    | full_path
;

full_path:
    '/' WORD_LITERAL {
      $$ = new std::string("/"); $$->append(*$2);
      delete $2;
    }
    | full_path '/' WORD_LITERAL  {
        $$ = new std::string(*$1);
        $$->append("/");
        $$->append(*$3);
        delete $1;
        delete $3;
      }
;

%%
extern int column;

int yyerror(std::string s) {
  extern int yylineno;  // defined and maintained in lex.yy.cpp
  extern char *yytext;  // defined and maintained in lex.yy.cpp

  OLA_FATAL << "ERROR: " << s << " at symbol \"" << yytext <<
      "\" on line " << yylineno << " column " << column;
  exit(1);
}

int yyerror(const char *s) {
  return yyerror(std::string(s));
}
