%{
#include "global.h"
#include <stdio.h>

extern int numligne;
ScriptProp *scriptprop;
int nbobj=-1;			/* Nombre d'objets */
int HasPosition,HasSize,HasType=0;
TabObj *tabobj;		/* Tableau d'objets, limite=30 */
int TabIdObj[31]; 	/* Tableau d'indice des objets */
Bloc **TabIObj;		/* TabIObj[Obj][Case] -> bloc attache au case */
Bloc *PileBloc[10];	/* Au maximum 10 imbrications de boucle conditionnelle */
int TopPileB=0;		/* Sommet de la pile des blocs */
CaseObj *TabCObj;	/* Struct pour enregistrer les valeurs des cases et leur nb */
int CurrCase;
int i;
char **TabNVar;		/* Tableau des noms de variables */
char **TabVVar;		/* Tableau des valeurs de variables */
int NbVar;
long BuffArg[6][20];	/* Les arguments s'ajoute par couche pour chaque fonction imbriquee */
int NbArg[6];		/* Tableau: nb d'args pour chaque couche */
int SPileArg;		/* Taille de la pile d'arguments */
long l;

/* Initialisation globale */
void InitVarGlob()
{
 scriptprop=(ScriptProp*) calloc(1,sizeof(ScriptProp));
 scriptprop->x=-1;
 scriptprop->y=-1;
 scriptprop->initbloc=NULL;

 tabobj=(TabObj*) calloc(1,sizeof(TabObj));
 for (i=0;i<30;i++) 
  (*tabobj)[i].state=Actif;
 for (i=0;i<31;i++)
  TabIdObj[i]=-1;
 TabNVar=NULL;
 TabVVar=NULL;
 NbVar=-1;

 SPileArg=-1;
}

/* Initialisation pour un objet */
void InitObjTabCase(int HasMainLoop)
{
 if (nbobj==0)
 {
  TabIObj=(Bloc**)calloc(1,sizeof(long));
  TabCObj=(CaseObj*)calloc(1,sizeof(CaseObj));
 }
 else
 {
  TabIObj=(Bloc**)realloc(TabIObj,sizeof(long)*(nbobj+1));
  TabCObj=(CaseObj*)realloc(TabCObj,sizeof(CaseObj)*(nbobj+1));
 }

 if (!HasMainLoop)
  TabIObj[nbobj]=NULL;
 CurrCase=-1;
 TabCObj[nbobj].NbCase=-1;
}

/* Ajout d'un case dans la table TabCase */
/* Initialisation d'un case of: agrandissement de la table */
void InitCase(int cond)
{
 CurrCase++;

 /* On enregistre la condition du case */
 TabCObj[nbobj].NbCase++;
 if (TabCObj[nbobj].NbCase==0)
  TabCObj[nbobj].LstCase=(int*)malloc(1,sizeof(int));
 else
  TabCObj[nbobj].LstCase=(int*)realloc(TabCObj[nbobj].LstCase,sizeof(int)*CurrCase+1);
 TabCObj[nbobj].LstCase[CurrCase]=cond;

 if (CurrCase==0)
  TabIObj[nbobj]=(Bloc*)calloc(1,sizeof(Bloc));
 else
  TabIObj[nbobj]=(Bloc*)realloc(TabIObj[nbobj],sizeof(Bloc)*(CurrCase+1));

 TabIObj[nbobj][CurrCase].NbInstr=-1;
 TabIObj[nbobj][CurrCase].TabInstr=NULL;

 /* Ce case correspond au bloc courant d'instruction: on l'empile */
 PileBloc[0]=&TabIObj[nbobj][CurrCase];
 TopPileB=0; 
}

/* Enleve un niveau d'args dans la pile BuffArg */
void RmLevelBufArg()
{
  SPileArg--;
}

/* Fonction de concatenation des n derniers etage de la pile */
/* Retourne les elts trie et depile et la taille */
long *Depile(int NbLevelArg, int *s)
{
 long *Temp;
 int j;
 int i;
 int size;

 if (NbLevelArg>0)
 {
  Temp=(long*)calloc(1,sizeof(long));
  size=0;
  for (i=SPileArg-NbLevelArg+1;i<=SPileArg;i++)
  {
   size=NbArg[i]+size+1;
   Temp=(long*)realloc (Temp,sizeof(long)*size);
   for (j=0;j<=NbArg[i];j++)
   {
    Temp[j+size-NbArg[i]-1]=BuffArg[i][j];
   }
  }
  *s=size;
  for (i=0;i<NbLevelArg;i++)	/* On depile les couches d'arguments */
   RmLevelBufArg();
  return Temp;
 }
 else
 {
  return NULL;
  *s=0;
 }
}

/* Ajout d'une commande */
void AddCom(int Type, int NbLevelArg)
{
 int CurrInstr;
 int i;
 int size;
 int j;


 PileBloc[TopPileB]->NbInstr++;
 CurrInstr=PileBloc[TopPileB]->NbInstr;

 if (CurrInstr==0)
  PileBloc[TopPileB]->TabInstr=(Instr*)calloc(1,sizeof(Instr)*(CurrInstr+1));
 else
  PileBloc[TopPileB]->TabInstr=(Instr*)realloc(PileBloc[TopPileB]->TabInstr,
				sizeof(Instr)*(CurrInstr+1));
 /* Rangement des instructions dans le bloc */
 PileBloc[TopPileB]->TabInstr[CurrInstr].Type=Type;
 /* On enleve la derniere couche d'argument et on la range dans la commande */

 PileBloc[TopPileB]->TabInstr[CurrInstr].TabArg=Depile(NbLevelArg,
		&PileBloc[TopPileB]->TabInstr[CurrInstr].NbArg);
}

/* Initialisation du buffer contenant les arguments de la commande courante */
/* Ajout d'une couche d'argument dans la pile*/
void AddLevelBufArg()
{
 /* Agrandissment de la pile */
 SPileArg++;
 NbArg[SPileArg]=-1;
}

/* Ajout d'un arg dans la couche arg qui est au sommet de la pile TabArg */ 
void AddBufArg(long *TabLong,int NbLong)
{
 int i;

 for (i=0;i<NbLong;i++)
 {
  BuffArg[SPileArg][i+NbArg[SPileArg]+1]=TabLong[i];
 }
 NbArg[SPileArg]=NbArg[SPileArg]+NbLong;
}

/* Recheche d'un nom de var dans TabVar, s'il n'existe pas il le cree */
/* Retourne un Id */
void AddVar(char *Name)		/* ajout de variable a la fin de la derniere commande pointee */
{
 int i;

 /* Comparaison avec les variables deja existante */
 for (i=0;i<=NbVar;i++)
  if (strcmp(TabNVar[i],Name)==0)
  {
   l=(long)i;
   AddBufArg(&l,1);
   return ;
  }

 if (NbVar>58) 	
 {
  fprintf(stderr,"Line %d: too many variables (>60)\n",numligne);
  exit(1);
 }

 /* La variable n'a pas ete trouvee: creation */
 NbVar++;

 if (NbVar==0)
 {
  TabNVar=(char**)calloc(1,sizeof(long));
  TabVVar=(char**)calloc(1,sizeof(long));
 }
 else
 {
  TabNVar=(char**)realloc(TabNVar,sizeof(long)*(NbVar+1));
  TabVVar=(char**)realloc(TabVVar,sizeof(long)*(NbVar+1));
 }

 TabNVar[NbVar]=(char*)strdup(Name);
 TabVVar[NbVar]=(char*)calloc(1,sizeof(char));
 TabVVar[NbVar][0]='\0';


 /* Ajout de la variable dans le buffer Arg */
 l=(long)NbVar;
 AddBufArg(&l,1);
 return ;
}

/* Ajout d'une constante str comme argument */
void AddConstStr(char *Name)	
{
 /* On cree une nouvelle variable et on range la constante dedans */
 NbVar++;
 if (NbVar==0)
 {
  TabVVar=(char**)calloc(1,sizeof(long));
  TabNVar=(char**)calloc(1,sizeof(long));
 }
 else
 {
  TabVVar=(char**)realloc(TabVVar,sizeof(long)*(NbVar+1));
  TabNVar=(char**)realloc(TabNVar,sizeof(long)*(NbVar+1));
 }

 TabNVar[NbVar]=(char*)calloc(1,sizeof(char));
 TabNVar[NbVar][0]='\0';
 TabVVar[NbVar]=(char*)strdup(Name);

 /* Ajout de l'id de la constante dans la liste courante des arguments */
 l=(long)NbVar;
 AddBufArg(&l,1);
}

/* Ajout d'une constante numerique comme argument */
void AddConstNum(long num)	
{

 /* On ne cree pas de nouvelle variable */
 /* On code la valeur numerique afin de le ranger sous forme d'id */
 l=num+200000;
 /* Ajout de la constante dans la liste courante des arguments */
 AddBufArg(&l,1);
}

/* Ajout d'une fonction comme argument */
/* Enleve les args de func de la pile, */
/* le concate, et les range dans la pile */
void AddFunct(int code,int NbLevelArg)	
{
 int size;
 long *l;
 int i;

 /* Methode: depiler BuffArg et completer le niveau inferieur de BuffArg */
 l=Depile(NbLevelArg, &size);

 size++;
 if (size==1)
  l=(long*)calloc(1,sizeof(long));
 else
 {
  l=(long*)realloc(l,sizeof(long)*(size));
  for (i=size-2;i>-1;i--)	/* Deplacement des args */
  {
   l[i+1]=l[i];
  }
 }
 l[0]=(long)code-150000;

 AddBufArg(l,size);
}

/* Ajout d'une instruction de test pour executer un ou plusieurs blocs */
/* enregistre l'instruction et le champs de ces blocs = NULL */
void AddComBloc(int TypeCond, int NbLevelArg, int NbBloc)
{
 int size;	/* Taille des args */
 long *l;
 int i;
 int OldNA;
 int CurrInstr;

 /* Ajout de l'instruction de teste comme d'une commande */
 AddCom(TypeCond, NbLevelArg);

 /* On initialise ensuite les deux champs reserve  bloc1 et bloc2 */
 CurrInstr=PileBloc[TopPileB]->NbInstr;
 /* Attention NbArg peur changer si on utilise en arg une fonction */
 OldNA=PileBloc[TopPileB]->TabInstr[CurrInstr].NbArg;

 PileBloc[TopPileB]->TabInstr[CurrInstr].TabArg=(long*)realloc( 
		PileBloc[TopPileB]->TabInstr[CurrInstr].TabArg,sizeof(long)*(OldNA+NbBloc));
 for (i=0;i<NbBloc;i++)
 {
  PileBloc[TopPileB]->TabInstr[CurrInstr].TabArg[OldNA+i]=0;
 }
 PileBloc[TopPileB]->TabInstr[CurrInstr].NbArg=OldNA+NbBloc;
}

/* Creer un nouveau bloc, et l'empile: il devient le bloc courant */
void EmpilerBloc()
{
 Bloc *TmpBloc;

 TmpBloc=(Bloc*)calloc(1,sizeof(Bloc));
 TmpBloc->NbInstr=-1;
 TmpBloc->TabInstr=NULL;
 TopPileB++; 
 PileBloc[TopPileB]=TmpBloc;

}

/* Depile le bloc d'initialisation du script et le range a sa place speciale */
void DepilerBloc(int IdBloc)
{
 Bloc *Bloc1;
 Instr *IfInstr;

 Bloc1=PileBloc[TopPileB];
 TopPileB--; 
 IfInstr=&PileBloc[TopPileB]->TabInstr[PileBloc[TopPileB]->NbInstr];
 IfInstr->TabArg[IfInstr->NbArg-IdBloc]=(long)Bloc1;
}

%}

/* Declaration des types des tokens, tous les types sont assembls dans union */ 
/* Le type est celui de yyval, yyval est utilis dans lex explicitement */
/* Dans bison, il est utlise implicitement avec $1, $2... */
%union {  char *str;
          int number;
       }

/* Declaration des symboles terminaux */
%token <str> STR GSTR VAR
%token <number> NUMBER	/* Nombre pour communiquer les dimensions */

%token WINDOWTITLE WINDOWSIZE WINDOWPOSITION FONT
%token FORECOLOR BACKCOLOR SHADCOLOR LICOLOR
%token OBJECT INIT MAIN END PROP
%token TYPE SIZE POSITION VALUE TITLE ICON STATE WARP
%token HIDEN INACTIF ACTIF
%token CASE CLIC BEG POINT
%token EXEC HIDE SHOW VALUE POSITION SIZE TITLE ICON FONT COLOR GETVALUE GETTITLE  QUIT
%token GET SET SENDSIGN
%token IF THEN ELSE FOR TO DO WHILE
%token BEGF ENDF
%token EQUAL INFEQ SUPEQ INF SUP DIFF

%%
script: initvar head initbloc object ;

/* Initialisation des variables */
initvar: 			{ InitVarGlob(); }
       ;

/* Entete du scripte decrivant les options par defaut */
head:				/* vide: dans ce cas on utilise les valeurs par dfaut */	
    | WINDOWTITLE GSTR head	{		/* Titre de la fenetre */
				 scriptprop->titlewin=$2;
				}
    | ICON STR head		{
				 scriptprop->icon=$2;
				}
    | WINDOWPOSITION NUMBER NUMBER head
				{		/* Position et taille de la fenetre */
				 scriptprop->x=$2;
				 scriptprop->y=$3;
				}
    | WINDOWSIZE NUMBER NUMBER head
				{		/* Position et taille de la fenetre */
				 scriptprop->width=$2;
				 scriptprop->height=$3;
				}
    | BACKCOLOR STR head	{ 		/* Couleur de fond */
				 scriptprop->backcolor=$2;
				}
    | FORECOLOR STR head	{ 		/* Couleur des lignes */
				 scriptprop->forecolor=$2;
				}
    | SHADCOLOR STR head	{ 		/* Couleur des lignes */
				 scriptprop->shadcolor=$2;
				}
    | LICOLOR STR head	{ 		/* Couleur des lignes */
				 scriptprop->licolor=$2;
				}
    | FONT STR head		{
				 scriptprop->font=$2;
				}
   ;

/* Bloc d'initialisation du script */
initbloc:		/* cas ou il n'y pas de bloc d'initialisation du script */
	| INIT creerbloc BEG instr END {
				 scriptprop->initbloc=PileBloc[TopPileB];
				 TopPileB--; 
				}

/* Desciption d'un objet */
object :			/* Vide */
    | OBJECT id PROP init verify mainloop object
    ;

id: NUMBER			{ nbobj++;
				  if (nbobj>30)
				  { yyerror("Too many items\n");
				    exit(1);}
				  if (($1<1)||($1>30))
				  { yyerror("Choose item id between 1 and 30\n");
				    exit(1);} 
				  if (TabIdObj[$1]!=-1) 
				  { i=$1; fprintf(stderr,"Line %d: item id %d already used:\n",numligne,$1);
				    exit(1);}
			          TabIdObj[$1]=nbobj;
				  (*tabobj)[nbobj].id=$1;
				}
  ;

init:				/* vide */
    | TYPE STR init		{
				 (*tabobj)[nbobj].type=$2;
				 HasType=1;
				}
    | SIZE NUMBER NUMBER init	{
				 (*tabobj)[nbobj].width=$2;
				 (*tabobj)[nbobj].height=$3;
				 HasSize=1;
				}
    | POSITION NUMBER NUMBER init {
				 (*tabobj)[nbobj].x=$2;
				 (*tabobj)[nbobj].y=$3;
				 HasPosition=1;
				}
    | VALUE NUMBER init		{
				 (*tabobj)[nbobj].value=$2;
				}
    | TITLE GSTR init		{
				 (*tabobj)[nbobj].title=$2;
				}
    | ICON STR init		{
				 (*tabobj)[nbobj].icon=$2;
				}
    | BACKCOLOR STR init	{
				 (*tabobj)[nbobj].backcolor=$2;
				}
    | FORECOLOR STR init	{
				 (*tabobj)[nbobj].forecolor=$2;
				}
    | SHADCOLOR STR init	{
				 (*tabobj)[nbobj].shadcolor=$2;
				}
    | LICOLOR STR init	{
				 (*tabobj)[nbobj].licolor=$2;
				}
    | FONT STR init		{
				 (*tabobj)[nbobj].font=$2;
				}
    | STATE state init		
    ;
state: HIDEN			{
				 (*tabobj)[nbobj].state=Hiden;
				}
    | INACTIF			{
				 (*tabobj)[nbobj].state=Inactif;
				}
    | ACTIF			{
				 (*tabobj)[nbobj].state=Actif;
				}
    ; 


verify:				 { 
				  if (!HasPosition)
				   { yyerror("No position for object");
				     exit(1);}
				  if (!HasSize)
				   { yyerror("No size for object");
				     exit(1);}
				  if (!HasType)
				   { yyerror("No type for object");
				     exit(1);}
				  HasSize=0;
				  HasPosition=0;
				  HasType=0;
				 }

mainloop: END			{ InitObjTabCase(0); }
	| MAIN addtabcase CASE case END
	;

addtabcase:			{ InitObjTabCase(1); }

case:
    | modif POINT bloc case
    | number POINT bloc case
    ;

modif :  CLIC			{ InitCase(0); }
      ;

number :  NUMBER		{ InitCase($1); }
       ;

bloc: BEG instr END
    ;

				
/* instruction: commande */
instr:				{/* vide donc fin d'une serie d'instruction d'un case */
				}
    |	EXEC exec instr
    |   WARP warp instr
    |	HIDE hide instr
    |	SHOW show instr
    |	VALUE value instr
    |	POSITION position instr
    |	SIZE size instr
    |	TITLE title instr
    |	ICON icon instr
    |	FONT font instr
    |	COLOR color instr
    |   SET set instr
    |   SENDSIGN sendsign instr
    |	QUIT quit instr
    |	IF ifthenelse instr
    |	FOR loop instr
    |	WHILE while instr
    ;

exec: addlbuff args			{ AddCom(1,1); }
    ;
hide: addlbuff numarg			{ AddCom(2,1);}
    ;
show: addlbuff numarg			{ AddCom(3,1);}
    ;
value: addlbuff numarg addlbuff numarg	{ AddCom(4,2);}
     ;
position: addlbuff numarg addlbuff numarg addlbuff numarg	{ AddCom(5,3);}
        ;
size: addlbuff numarg addlbuff numarg addlbuff numarg		{ AddCom(6,3);}
    ;
icon: addlbuff numarg addlbuff strarg	{ AddCom(7,2);}
    ;
title: addlbuff numarg addlbuff gstrarg	{ AddCom(8,2);}
     ;
font: addlbuff numarg addlbuff strarg	{ AddCom(9,2);}
    ;
color: addlbuff numarg addlbuff strarg	{ AddCom(10,2);}
     ;
set: addlbuff vararg GET addlbuff arg	{ AddCom(11,2);}
   ;
sendsign: addlbuff numarg addlbuff numarg{ AddCom(12,2);}
	;
quit: 					{ AddCom(13,0);}
    ;
warp: addlbuff numarg			{ AddCom(17,1);}
    ;
ifthenelse: headif creerbloc bloc1 else
          ;
loop: headloop creerbloc bloc2
    ;
while: headwhile creerbloc bloc2
    ;

/* Boucle conditionnelle: compare n'importe quel type d'argument */
headif: addlbuff arg addlbuff compare addlbuff arg THEN 	{ AddComBloc(14,3,2); }
      ;
else: 				/* Le else est optionnel */
    | ELSE creerbloc bloc2
    ;
creerbloc:			{ EmpilerBloc(); }
         ;
bloc1: BEG instr END		{ DepilerBloc(2); }
     ;
bloc2: BEG instr END		{ DepilerBloc(1); }
     ;

/* Boucle sur une variable */
headloop: addlbuff vararg GET addlbuff arg TO addlbuff arg DO	{ AddComBloc(15,3,1); }
        ;

/* Boucle conditionnelle while */
headwhile: addlbuff arg addlbuff compare addlbuff arg DO	{ AddComBloc(16,3,1); }
        ;

/* Argument de commandes */
/* Argument elementaire */
var	: VAR			{ AddVar($1); }
	;
str	: STR			{ AddConstStr($1); }
	;
gstr	: GSTR			{ AddConstStr($1); }
	;
num	: NUMBER		{ AddConstNum($1); }
	;
clic	: CLIC			{ AddConstNum(0); }
	;
addlbuff:			{ AddLevelBufArg(); }
	;
function: GETVALUE numarg	{ AddFunct(1,1); }
	| GETTITLE numarg	{ AddFunct(2,1); }
	;


/* Plusieurs arguments de type differents */
args	:			{ }
	| clic args
	| var args
	| gstr args
	| str args
	| num args
	| BEGF addlbuff function ENDF args
	;


/* Argument unique de n'importe quel type */
arg	: var
	| clic
	| gstr
	| str
	| num
	| BEGF addlbuff function ENDF
	;

/* Argument unique de type numerique */
numarg	: clic
	| num
	| var
	| BEGF addlbuff function ENDF
	;

/* Argument unique de type str */
strarg	: var
	| str
	| BEGF addlbuff function ENDF
	;

/* Argument unique de type gstr */
gstrarg	: var
	| gstr
	| BEGF addlbuff function ENDF
	;

/* Argument unique de type var, pas de fonction */
vararg	: var
	;

/* element de comparaison entre deux variables numerique */
compare	: INF			 { l=1-250000; AddBufArg(&l,1); }
	| INFEQ			 { l=2-250000; AddBufArg(&l,1); }
	| EQUAL			 { l=3-250000; AddBufArg(&l,1); }
	| SUPEQ			 { l=4-250000; AddBufArg(&l,1); }
	| SUP			 { l=5-250000; AddBufArg(&l,1); }
	| DIFF			 { l=6-250000; AddBufArg(&l,1); }
	;

%%
