/* -*- C++ -*- */
%{
#include <malloc.h>
#include <string.h>
#include "ConfigRep.h"
#include "PlatformAbstraction.h"
#include "Options.h"

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

ConfigFileSection *ConfigParserResult = 0;/* Result of calling yyparse */
extern const char *ConfigFilename;        /* Name of file being parsed */
extern int Configlineno;                  /* Current line number of lexed stuff */

%}

%union {
	ConfigFileSection   *Data;
	ConfigFileStatement *SList;
        char                *StrData;
        VTExp               *Exp;
        VTListExp           *ExpList;
}

%token <StrData> LITSTRING

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

%type <Data>    FileSection WimpyFileSection File
%type <SList>   StatementList StatementList0
%type <Exp>     Exp 
%type <ExpList> ExpList
%type <StrData> LitString LitString0

%start File

%left ','              /* Lists have lowest precedence */
%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... Also, the
 * first section is allowed to be a "WimpyFileSection", which is one without
 * a section header [name:name] or [name] ....
 */
File : WimpyFileSection {
           $$ = ConfigParserResult = $1;
         }
         | FileSection {
           $$ = ConfigParserResult = $1;
	 }

/* FileSection - A file section is a section header followed by a list of
 * Statements.  The list of statements may be empty.  The section header
 * may be either a single string or a pair of strings seperated by a colon.
 */
FileSection : '[' LitString ']' StatementList0 FileSection {
           if (PlatformAbstraction::strcasecmp("VariableDefaults", $2) == 0) {
             $$ = new VarDefaultsConfigFileSection(Configlineno, $4, $5);
	   } else if (PlatformAbstraction::strcasecmp("Global", $2) == 0) {
             $$ = new GlobalConfigFileSection(Configlineno, $4, $5);
           } else {
             $$ = new PlainConfigFileSection(Configlineno, $2, $4, $5);
	   }
	   free($2);
         }
	 | '[' LitString ':' LitString ']' StatementList0 FileSection {
           /* The string after the color is ignored!! */
	   $$ = new ThemeConfigFileSection(Configlineno, $2, $6, $7);
	   free($2);
	   free($4);
	 }
	 | '[' '[' LitString ']' ']' StatementList0 FileSection {
	   $$ = new ThemeConfigFileSection(Configlineno, $3, $6, $7);
	   free($3);
	 }
         | /* Empty */ {   /* Must be the end of the file... */
           $$ = 0;
         }

/* WimpyFileSection - The very first section of the ConfigFile is allowed
 * to not have a section header.  In this case, any statments there 
 * automatically go into a new "Global" section.
 */
WimpyFileSection : StatementList FileSection {
	   $$ = new GlobalConfigFileSection(Configlineno, $1, $2);
         }

/* Exp is an Expression */
Exp : VARIABLE {                /* Variable expression */
      $$ = new SkelVarExp(Configlineno, $1);
      free($1);
    }
    | Exp EQ Exp {
      $$ = new SkelBinExp(Configlineno, SkelBinExp::Equals, $1, $3);
    }
    | Exp NEQ Exp {
      $$ = new SkelBinExp(Configlineno, SkelBinExp::NEquals, $1, $3);
    }
    | Exp '+' Exp {             /* Contatenation expression */
      $$ = new SkelBinExp(Configlineno, SkelBinExp::Plus, $1, $3);
    }
    | Exp '|' Exp {             /* Logical OR expression */
      $$ = new SkelBinExp(Configlineno, SkelBinExp::Or, $1, $3);
    }
    | Exp '&' Exp {             /* Logical AND Expression */
      $$ = new SkelBinExp(Configlineno, SkelBinExp::And, $1, $3);
    }
    | '!' Exp {                 /* Logical Negation Expression */
      $$ = new SkelUnaryExp(Configlineno, SkelUnaryExp::Not, $2); 
    }
    | '(' Exp ')' {             /* Grouping Expression */
      $$ = $2;
    }
    | '[' ExpList ']' {         /* Non-empty list of expressions */
      $$ = $2;
    }
    | '[' ']' {                 /* Empty list of expressions...  */
      $$ = new VTListExp(Configlineno);
    }
    | LitString {               /* Literal string expression */
      $$ = new VTStrExp(Configlineno, $1);
      free($1);
    }

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


    /* Possibly empty list of statements */
StatementList0 : /* Empty */ {
      $$ = 0;
    }
    | StatementList {
      $$ = $1;
    }

    /* Nonempty list of statments... A statement is of the form "name:exp;" */
StatementList : LitString ':' Exp ';' StatementList0 {
	      $$ = new ConfigFileStatement(Configlineno, $1, $3, $5);
	      free($1);
	    }

           /* LitString0 is a possibly empty literal string */
LitString0 : LitString {
             $$ = $1;
           }
           | /* EMPTY */ {
             $$ = 0;
           }

          /* LitString is required to exist, cannot default to empty. */
LitString : LITSTRING LitString0 {
             if ($2 == 0) {
	       $$ = $1;
	     } else {
	       $$ = (char*)malloc(strlen($1)+strlen($2)+1);
	       strcpy($$, $1);
	       strcat($$, $2);
	       free($1);
	       free($2);
	     }
           } 

%%
int yyerror(char *ErrorMsg) { 
  cout << "ConfigFile " << ConfigFilename << ":" << Configlineno << ": " << ErrorMsg << endl;
  return 0; 
}
