%{

/*
 * © Copyright 1996-2012 ECMWF.
 * 
 * This software is licensed under the terms of the Apache Licence Version 2.0
 * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. 
 * In applying this licence, ECMWF does not waive the privileges and immunities 
 * granted to it by virtue of its status as an intergovernmental organisation nor
 * does it submit to any jurisdiction.
 */

#include "mars.h"
#include "lang.h"

extern int yylex();
extern void yyerror(const char*);

static request *reverse(request*);
static value *reverse_value(value *r,value *s);
static value *reverse_other(value *r,value *s);

request *parser_lang;
request *parser_reqs;
rule    *parser_ruls;

%}

%union {
    char       *str;
	request    *req;
	parameter  *par;
	value      *val;
	rule       *rul;
	action     *act;
	condition  *con;
	testop      top;
}

%start all

%token IF
%token AND
%token NOT
%token OR
%token THEN
%token WARNING
%token INFO
%token MAIL
%token MAILUSER
%token ERROR
%token EXIT
%token SET
%token UNSET
%token IN
%token FAIL

%token <str>WORD

%type  <req>verb
%type  <req>verbs
%type  <req>requests
%type  <req>request
%type  <req>intf

%type  <rul>rule;
%type  <rul>rules;

%type  <act>action;
%type  <act>actions;

%type  <top>op;
%type  <con>term;
%type  <con>factor;
%type  <con>atom;
%type  <con>func;
%type  <con>test;

%type  <par>parameter_list
%type  <par>parameter_desc
%type  <par>parameters
%type  <par>parameter

%type  <str>info

%type  <val>value_list
%type  <val>value_desc
%type  <val>reference

%type  <val>aliases
%type  <val>alias

%type  <val>values
%type  <val>value

%%

all        : empty
           | verbs                      { parser_lang  = reverse($1);  }
           | requests                   { parser_reqs  = reverse($1);  }
		   | rules                      { parser_ruls  = $1;           }
		   ;

requests   : request 
           | requests request           { $$ = $2; $2->next = $1;   }
           ;


request    : WORD                       { $$ = new_request($1,NULL);     }
           | WORD ',' parameters        { $$ = new_request($1,$3);       }
           | WORD '.'                   { $$ = new_request($1,NULL);     }
           | WORD ',' parameters '.'    { $$ = new_request($1,$3);       }
           | ':' parameters ':'         { $$ = new_request(0,$2);        }
           ;

parameters : parameter
           | parameters ',' parameter   { $$ = $3; $3->next = $1;   }
           ;

parameter  : WORD intf '=' values            
					{ $$ = new_parameter($1,$4); $$->interface = $2; }
           | WORD intf '=' '(' requests ')'  
					{   $$ = new_parameter($1,NULL);
						$$->interface = $2;
						$$->subrequest = reverse($5);    }

			/* used for marsgen ... */

           | WORD '@' WORD '=' values   {
											$$ = new_parameter($3,$5);      
											$$->default_values=new_value($1);
										}
		   ;

values     : value
           | values '/' value           { $$ = $3; $3->next = $1;   }

value      : WORD                       { $$ = new_value($1);             }     
           | '\"'                       { $$ = new_value(strcache("\"")); }
		   | '*'  '[' WORD ']'          { 
											char buf[1024];
											sprintf(buf,"*%s",$3);
											$$ = new_value(strcache(buf));  
											strfree($3);
										}
		   | '*'                        { $$ = new_value(strcache("*"));  }
		   | '/'                        { $$ = new_value(strcache("/"));  }
		   | '@'                        { $$ = new_value(strcache("@"));  }
		   ;

empty      :
           ;

/*-------------------------------------------------------------------*/


verbs            : verb 
                 | verbs verb            { $$ = $2; $2->next = $1;   }
                 ;

info             : ';' WORD { $$ =  $2; }
				 | empty    { $$ =  NULL; }
				 ;

verb             : WORD info info '{' parameter_list '}' 
					{ 
					  $$ = new_request($1,$5); 
					  $$->info = $2;
					  $$->kind = $3;
					}
                 | WORD info info  '{' '}'                
					{ 
					  $$ = new_request($1,NULL); 
					  $$->info = $2;
					  $$->kind = $3;
					}

parameter_list   : parameter_desc
                 | parameter_list parameter_desc { $$ = $2; $2->next = $1;   }

parameter_desc   : WORD intf '{' value_list '}' { $$ = new_parameter($1,$4); 
					   $$->interface = $2; }
			     | WORD intf '{' value_list '}' '=' values 
				 	{ 
						$$ = new_parameter($1,$4); 
						$$->interface = $2;
						$$->default_values = $7;
					}
				 ;

intf             : empty { $$ = NULL; }
				 | '[' parameters ']' { $$ = new_request(NULL,$2); }
				 ;

value_list       : value_desc
			     | value_list value_desc { $$ = $2; $2->next = $1;   }
				 ;


value_desc       : alias
				 | alias ';' aliases { $$ = $1; 
						  	$1->other_names = reverse_other($3,NULL);
						  }
				 | reference 
				 ;

aliases          : alias
				 | aliases ';' alias { $$ = $3; $3->other_names = $1;  }
				 ;

alias            : value
				 | value '=' '(' values ')' { $$ = $1; $1->expand = reverse_value($4,0); }
				 ;


reference        : '&' WORD '&' WORD { $$ = new_reference($2,$4); }
				 ;

/*-------------------------------------------------------------------*/

rules  : rule
	   | rule rules { $$ = $1; $1->next = $2;}
	   ;


rule   : IF term THEN actions { $$ = new_rule($2,$4);}
	   ;

actions : action 
		| action actions { $$ = $1; $1->next = $2;}
		;

term   : factor 
	   | factor OR  term   { $$ = new_condition(t_or,$1,$3);}
	   ;

factor : test   
	   | test   AND factor { $$ = new_condition(t_and,$1,$3);}
	   ; 

test   : atom op atom     { $$ = new_condition($2,$1,$3); }
	   | NOT term         { $$ = new_condition(t_not,$2,NULL); }
	   | '(' term ')'     { $$ = $2; }
	   | atom             
	   | func
	   ;

atom   : values           { $$ = new_condition(t_val,(condition*)$1,NULL);} 
	   ;

func:    WORD '(' WORD ')' { $$ = new_condition(t_func,(condition*)strcache($1),(condition*)strcache($3));}
	| WORD '(' ')' { $$ = new_condition(t_func,(condition*)strcache($1),NULL);}
    ;


op     : '='      { $$ = t_eq;}
	   | '>'      { $$ = t_gt;}
	   | '<'      { $$ = t_lt;}
	   | '>' '='  { $$ = t_ge;}
	   | '<' '='  { $$ = t_le;}
	   | '<' '>'  { $$ = t_ne;}
	   | IN       { $$ = t_in;}
	   ;

action : SET parameter { $$ = new_action(a_set,$2); 
			           $2->values = reverse_value($2->values,NULL);  }
	   | UNSET WORD    { $$ = new_action(a_unset,$2); }
	   | WARNING WORD  { $$ = new_action(a_warning,$2); }
	   | MAIL    WORD  { $$ = new_action(a_mail,$2); }
	   | MAILUSER WORD { $$ = new_action(a_mailuser,$2); }
	   | INFO    WORD  { $$ = new_action(a_info,$2); }
	   | ERROR   WORD  { $$ = new_action(a_error,$2); }
	   | EXIT    WORD  { $$ = new_action(a_exit,$2); }
	   | FAIL          { $$ = new_action(a_fail,NULL); }
	   ;

%%

#include "langl.c"

static value *reverse_other(value *r,value *s)
{
	value *v;

	if(r == NULL) return s;

	v              = r->other_names;
	r->other_names = s;
	return reverse_other(v,r); 
}

static value *reverse_value(value *r,value *s)
{
	value *v;

	if(r == NULL) return s;

	v              = r->next;
	r->next        = s;
	return reverse_value(v,r); 
}

static parameter *reverse_parameter(parameter *r,parameter *s)
{
	parameter *v;

	if(r == NULL) return s;

	r->values         = reverse_value(r->values,NULL);
	r->default_values = reverse_value(r->default_values,NULL);
	v                 = r->next;
	r->next           = s;

	return reverse_parameter(v,r); 
}

static request *reverse_request(request *r,request *s)
{
	request *v;

	if(r == NULL) return s;

	r->params = reverse_parameter(r->params,NULL);
	v         = r->next;
	r->next   = s;
	return reverse_request(v,r); 
}

static request *reverse(request *r)
{
	return reverse_request(r,NULL);
}
