/* -*- C++ -*- */
%{
#include <malloc.h>
#include "SkelRep.h"
#include "Options.h"   // See if we have something verbose...

int yyerror(char *ErrorMsg);
int yylex();

SkelData *SkelParserResult = 0;
extern const char *SkelFilename;          /* Name of file being parsed */
extern int Skellineno;                    /* Current line number of lexed stuff */


%}

%union {
	SkelData        *Data;
        char            *StrData;
        VTExp           *Exp;
        VTListExp       *ExpList;
        SkelSettingList *SList;
}

%token MSPLUGIN MSINSERT MSIF MSELSEIF MSELSE MSENDIF
%token <StrData> RAWDATA LITSTRING

/* Tokens for parsing expressions */
%token EQ NEQ
%token <StrData> VARIABLE

%type <Data> DataLink File ElseIfSeq
%type <Exp>  Exp FuncExp
%type <ExpList> ExpList
%type <SList> SettingList

%start File

%left '&' '|'          /* Logical And and Or  */
%left EQ NEQ           /* Equality comparison */
%left '-' '+'          /* Concatenate strings */
%right '!'             /* Negate truth value  */
%left '(' ')'          /* Explicit ordering   */
%%

/* File rule, capture the end result in the Result variable... */
File : DataLink {
    $$ = SkelParserResult = $1;
  } 

/* OptEquals - Match an optional equals sign... either = or nothing. */
OptEquals : '=' |  /* Empty */

/* ElseIfSeq - Match the MSElse and MSElseIf* sequences of tags in the
   MSIf statement.  This tag will match the pattern 
   "(<MSELSEIF>)* (<MSELSE>)?" converting the elseif's into if then else
   form, for simple evaluation later.  
 */
ElseIfSeq : /* Empty */ {
    $$ = 0;  /* <MSIF> tag has no MSELSE or MSELSEIF tags */
  }

  /* MSELSEIF token is followed by a list of more MSELSEIF's, 
   * an MSELSE, or nothing... 
   */
  | MSELSEIF Exp '>' DataLink ElseIfSeq {
    $$ = new SkelIfThenElseData(Skellineno, $2, $4, $5, 0);
  }

  /* MSELSE tag cannot be followed by any other tags... */
  | MSELSE DataLink {
    $$ = $2;
  }

DataLink : /* Empty */ { 
    $$ = 0; 
  }

  | RAWDATA DataLink {
    $$ = new SkelRawData(Skellineno, $1, $2);
    free($1);
  }

  | MSINSERT OptEquals Exp SettingList '>' DataLink {
    $$ = new SkelInsertData(Skellineno, $3, $4, $6);
  }

  | MSPLUGIN OptEquals LITSTRING SettingList '>' DataLink {
    $$ = new SkelPluginData(Skellineno, $3, $4, $6);
    free($3);
  }

  | MSIF Exp '>' DataLink ElseIfSeq MSENDIF DataLink {
    $$ = new SkelIfThenElseData(Skellineno, $2, $4, $5, $7);
  }

/* Exp is an Expression */
Exp : VARIABLE {
      $$ = new SkelVarExp(Skellineno, $1);
      free($1);
    }
    | FuncExp {
      $$ = $1;
    }
    | Exp EQ Exp {
      $$ = new SkelBinExp(Skellineno, SkelBinExp::Equals, $1, $3);
    }
    | Exp NEQ Exp {
      $$ = new SkelBinExp(Skellineno, SkelBinExp::NEquals, $1, $3);
    }
    | Exp '|' Exp {
      $$ = new SkelBinExp(Skellineno, SkelBinExp::Or, $1, $3);
    }
    | Exp '&' Exp {
      $$ = new SkelBinExp(Skellineno, SkelBinExp::And, $1, $3);
    }
    | '!' Exp {
      $$ = new SkelUnaryExp(Skellineno, SkelUnaryExp::Not, $2); 
    }
    | '[' ']' {
      $$ = new VTListExp(Skellineno);
    }
    | '[' ExpList ']' {
      $$ = $2;
    }

FuncExp : LITSTRING {
      $$ = new VTStrExp(Skellineno, $1);
      free($1);
    }
    | Exp '+' Exp {
      $$ = new SkelBinExp(Skellineno, SkelBinExp::Plus, $1, $3);
    }
    | '(' Exp ')' {
      $$ = $2;
    }
    | Exp '{' ExpList '}' {
      $$ = new SkelFunctionExp(Skellineno, $1, $3);
    }
    | Exp '{' '}' {
      $$ = new SkelFunctionExp(Skellineno, $1, new VTListExp(Skellineno));
    }


ExpList : Exp {
      $$ = new VTListExp(Skellineno);
      $$->AddToTail($1);
    } 
    | ExpList ',' Exp {
      $$ = $1;
      $$->AddToTail($3);
    }


SettingList : /* Empty */ {
      $$ = 0;
    }
    | LITSTRING '=' Exp SettingList {
      $$ = new SkelSettingList(Skellineno, $1, $3, $4);
      free($1);
    }

%%
int yyerror(char *ErrorMsg) { 
  cout << "SkelFile Parse Error" << SkelFilename << ":" 
       << Skellineno << ": " << ErrorMsg << endl;
  return 0; 
}
