/*
 *
 *  (c) COPYRIGHT INRIA, Grif, 1996.
 *  Please first read the full copyright statement in file COPYRIGHT.
 *
 */

/*
 * Warning:
 * This module is part of the Thot library, which was originally
 * developed in French. That's why some comments are still in
 * French, but their translation is in progress and the full module
 * will be available in English in the next release.
 * 
 */

/*
 * Ce module effectue l'analyse syntaxique d'un texte source dont il
 * recoit les mots un par un.
 * Il est parametre' par une grammaire qui lui est fournie dans la table
 * GramRule ou dans un fichier de type .GRM
 *
 * Author: V. Quint (INRIA)
 *
 */

#include "thot_sys.h"
#include "constgrm.h"
#include "typegrm.h"
#include "constmedia.h"
#include "typemedia.h"
#include "compilmsg.h"
#include "message.h"
#include "registry.h"
#include "fileaccess.h"

typedef struct _ParserStackItem
  {
     int                 StRule;	/* numero de la regle */
     int                 StRuleInd;	/* pointeur dans la regle */
     boolean             Option;	/* option en cours */
     boolean             Alt;	        /* on peut chercher une alternative */
     boolean             Tested;	/* regle en cours de test avec le mot courant */
  }
ParserStackItem;

#define THOT_EXPORT extern
#include "compil_tv.h"
#undef THOT_EXPORT
#define THOT_EXPORT
#include "analsynt_tv.h"
#include "compilmsg_f.h"

extern int          LineNum;	/* Numero de la ligne courante dans le fichier

				   en cours de compilation */

static boolean      Comment;	/* on est dans un commentaire */

#define STACKSIZE 40		/* taille de la pile */
static int          level;	/* niveau courant dans la pile */
static ParserStackItem Stack[STACKSIZE];	/* pile d'analyse */

#include "fileaccess_f.h"
#include "parser_f.h"
#include "registry_f.h"
/*----------------------------------------------------------------------
   InitParser initialise les donnees de l'analyseur syntaxique.    
  ----------------------------------------------------------------------*/

#ifdef __STDC__
void                InitParser ()

#else  /* __STDC__ */
void                InitParser ()
#endif				/* __STDC__ */

{
   Comment = False;		/* pas de commentaire en cours */
   level = 0;			/* initialise la pile */
   Stack[0].StRule = 0;
   Stack[0].StRuleInd = 1;	/* au debut de la regle initiale */
}


/*----------------------------------------------------------------------
   CheckShortKeyword teste si le caractere qui est a` la position  
   index dans la ligne courante est un mot-cle court.	
   Retourne dans ret le code de ce mot-cle ou 0 si ce	
   n'est pas un mot-cle court.				
  ----------------------------------------------------------------------*/

#ifdef __STDC__
static void         CheckShortKeyword (indLine index, SyntacticCode * ret)

#else  /* __STDC__ */
static void         CheckShortKeyword (index, ret)
indLine             index;
SyntacticCode      *ret;

#endif /* __STDC__ */

{
   int                 i;

   *ret = 0;
   i = 0;
   do
     {
	if (inputLine[index - 1] == Keywords[i].SrcKeyword[0])
	   *ret = Keywords[i].SrcKeywordCode;
	i++;
     }
   while (*ret == 0 && i < LastShortKeyword);
}


/*----------------------------------------------------------------------
   CheckLongKeyword teste si le mot de longueur len qui commence a` 
   la position index dans la ligne courante est un mot-cle 
   long. Rend dans ret le code de ce mot-cle' ou 0 si ce   
   n'est pas un mot cle long.                              
  ----------------------------------------------------------------------*/

#ifdef __STDC__
static void         CheckLongKeyword (indLine index, indLine len, SyntacticCode * ret)

#else  /* __STDC__ */
static void         CheckLongKeyword (index, len, ret)
indLine             index;
indLine             len;
SyntacticCode      *ret;

#endif /* __STDC__ */

{
   int                 i;

   *ret = 0;
   i = LastShortKeyword;
   do
     {
	if (Keywords[i].SrcKeywordLen == len)
	   if (strncasecmp (&inputLine[index - 1], Keywords[i].SrcKeyword, len) == 0)
	      *ret = Keywords[i].SrcKeywordCode;
	i++;
     }
   while (*ret == 0 && i < NKeywords);
}


/*----------------------------------------------------------------------
   CheckIdent teste si le mot de longueur len qui commence a` la   
   position index dans la ligne courante est dans la table 
   des identificateurs. Rend dans ret le code du type      
   grammatical de cet identificateur ou 0 s'il n'est       
   pas dans la table. Rend dans rank le rang de              
   l'identificateur dans la table Identifier.              
  ----------------------------------------------------------------------*/

#ifdef __STDC__
static void         CheckIdent (indLine index, indLine len, SyntacticCode * ret, int *rank)

#else  /* __STDC__ */
static void         CheckIdent (index, len, ret, rank)
indLine             index;
indLine             len;
SyntacticCode      *ret;
int                *rank;

#endif /* __STDC__ */

{
   int                 i;

   *ret = 0;
   *rank = 0;
   i = 0;
   do
     {
	if (Identifier[i].SrcIdentLen == len)
	   if (strncmp (&inputLine[index - 1], Identifier[i].SrcIdentifier, len) == 0)
	     {
		*rank = i + 1;
		*ret = Identifier[i].SrcIdentCode;
	     }
	i++;
     }
   while (*ret == 0 && i < NIdentifiers);
}


/*----------------------------------------------------------------------
   NewIdent ajoute a` la table des identificateurs le mot de       
   longueur len qui commence a` la position index dans la  
   ligne courante et qui est de type syntaxique code.      
   Rend dans rank le rang de cet identificateur dans la    
   table Identifier.                                       
  ----------------------------------------------------------------------*/

#ifdef __STDC__
static void         NewIdent (indLine index, indLine len, SyntacticCode code, int *rank)

#else  /* __STDC__ */
static void         NewIdent (index, len, code, rank)
indLine             index;
indLine             len;
SyntacticCode       code;
int                *rank;

#endif /* __STDC__ */

{
   indLine             i;

   *rank = 0;
   if (NIdentifiers >= MAX_IDENTIFIERS)
      CompilerMessage (index, COMPIL, FATAL, NO_SPACE_LEFT_IN_INDENT_TABLE,
		     inputLine, LineNum);
   else if (len > IDENTIFIER_LENGTH)
      CompilerMessage (index, COMPIL, FATAL, INVALID_WORD_SIZE,
		     inputLine, LineNum);
   else
     {
	Identifier[NIdentifiers].SrcIdentLen = len;
	Identifier[NIdentifiers].SrcIdentDefRule = 0;
	Identifier[NIdentifiers].SrcIdentRefRule = 0;
	Identifier[NIdentifiers].SrcIdentCode = code;
	for (i = 0; i < len; i++)
	   Identifier[NIdentifiers].SrcIdentifier[i] = inputLine[index + i - 1];
	NIdentifiers++;
	*rank = NIdentifiers;
     }
}

/*----------------------------------------------------------------------
   AsciiToInt traduit le nombre qui est sous sa forme ASCII a` la  
   position index de la ligne courante et qui est de       
   longueur len.                                            
  ----------------------------------------------------------------------*/

#ifdef __STDC__
int                 AsciiToInt (indLine index, indLine len)

#else  /* __STDC__ */
int                 AsciiToInt (index, len)
indLine             index;
indLine             len;

#endif /* __STDC__ */

{
   int                 num;

   sscanf (&inputLine[index - 1], "%d", &num);
   if (num > 65535)
     {
	CompilerMessage (index, COMPIL, FATAL, NUMBER_OVERFLOW, inputLine, LineNum);
	num = 0;
     }
   return num;
}

/*----------------------------------------------------------------------
   OctalToChar remplace dans le buffer d'entree inputLine les      
   sequences \nn par le caractere dont le code octal est   
   nn. Remplace aussi \\ par \.                            
  ----------------------------------------------------------------------*/

#ifdef __STDC__
void                OctalToChar ()

#else  /* __STDC__ */
void                OctalToChar ()
#endif				/* __STDC__ */

{
   int                 i, shift, k, n;

   i = 0;
   while (inputLine[i] != '\0')
     {
	if (inputLine[i] == '\\')
	  {
	     shift = 0;
	     if (inputLine[i + 1] == '\\')
		shift = 1;
	     else if (inputLine[i + 1] >= '0' && inputLine[i + 1] <= '7')
	       {
		  /* \ suivi d'un chiffre octal */
		  k = i + 1;
		  n = 0;
		  while (inputLine[k] >= '0' && inputLine[k] <= '7' && k < i + 4)
		     n = n * 8 + ((int) inputLine[k++]) - ((int) '0');
		  if (n < 1 || n > 255)
		     CompilerMessage (i, COMPIL, FATAL, INVALID_CHAR,
				    inputLine, LineNum);
		  else
		    {
		       inputLine[i] = (unsigned char) n;
		       shift = k - i - 1;
		    }
	       }
	     if (shift > 0)
		/* decale la fin de la ligne de shift caracteres vers la gauche */
	       {
		  k = i + 1;
		  do
		    {
		       inputLine[k] = inputLine[k + shift];
		       k++;
		    }
		  while (inputLine[k - 1] != '\0');
	       }
	  }
	else if (inputLine[i] < ' ')
	   inputLine[i] = ' ';
	i++;
     }
}


/*----------------------------------------------------------------------
   GetNextToken cherche le prochain mot a` partir de la position   
   start dans la ligne courante. Au retour:                
   - wi: position dans la ligne du debut du mot trouve, ou 
   si pas trouve, 0.                                       
   - wl: longueur du mot trouve, ou 0 si pas trouve. (len+1
   si wn=SynString).                                           
   - wn: SyntacticType du mot trouve, ou SynError si pas trouve.       
  ----------------------------------------------------------------------*/

#ifdef __STDC__
void                GetNextToken (indLine start, indLine * wi, indLine * wl, SyntacticType * wn)

#else  /* __STDC__ */
void                GetNextToken (start, wi, wl, wn)
indLine             start;
indLine            *wi;
indLine            *wl;
SyntacticType      *wn;

#endif /* __STDC__ */

{
   indLine             j, k;
   boolean             stop;

   *wi = 0;
   *wl = 0;
   *wn = SynError;
   stop = False;
   j = start - 1;
   do
     {
	/* saute les caracteres vides et commentaires */
	/* les commentaires sont delimites par des accolades exclusivement */
	if (Comment)
	   /* cherche la fin du commentaire ou de la ligne */
	  {
	     while (inputLine[j] != '}' && inputLine[j] != '\0')
		j++;
	     if (inputLine[j] == '}')
	       {
		  Comment = False;
		  j++;
	       }
	  }
	if (inputLine[j] != ' ')
	   if (inputLine[j] == '\0')
	      stop = True;
	   else if (inputLine[j] == '{')
	      /* debut de commentaire */
	     {
		Comment = True;
		while (inputLine[j] != '}' && inputLine[j] != '\0')
		   j++;
		if (inputLine[j] == '\0')
		   stop = True;
		else
		   Comment = False;
	     }
	   else
	     {
		stop = True;
		*wi = j + 1;
	     }
	j++;
     }
   while (!stop);

   /* analyse les caracteres significatifs */
   if (*wi > 0 && !error)
     {
	stop = False;
	j = *wi - 1;
	*wl = 1;
	/* SyntacticType du mot trouve, d'apres son premier caractere */
	if (inputLine[j] >= '0' && inputLine[j] <= '9')
	   *wn = SynInteger;
	else if ((inputLine[j] >= 'A' && inputLine[j] <= 'Z')
		 || inputLine[j] == (unsigned char) '\240'	/*nobreakspace */
		 || (inputLine[j] >= 'a' && inputLine[j] <= 'z')

		 || (((int) inputLine[j]) >= 192 &&
		     ((int) inputLine[j]) <= 255))
	   *wn = SynIdentifier;
	else if (inputLine[j] == '\'')
	   *wn = SynString;
	else
	   *wn = SynShortKeyword;
	j++;
	if (*wn == SynInteger || *wn == SynIdentifier)
	   /* verifie que le mot est bien forme et cherche la fin */
	   do
	     {
		if (inputLine[j] == '\0'
		    || inputLine[j] == ' '
		    || (inputLine[j] >= '!' && inputLine[j] <= '/')
		    || (inputLine[j] >= ':' && inputLine[j] <= '@')
		    || (inputLine[j] >= '[' && inputLine[j] <= '^')
		    || (inputLine[j] >= '{' && inputLine[j] <= '~')
		    || inputLine[j] == (char) (127)
		    || inputLine[j] == '`')
		   /* on a trouve un separateur */
		   stop = True;	/* verifie l'homogeneite */
		else
		  {
		     switch (*wn)
			   {
			      case SynInteger:
				 if (inputLine[j] < '0' || inputLine[j] > '9')
				   {
				      CompilerMessage (j + 1, COMPIL, FATAL, BAD_NUMBER,
						     inputLine, LineNum);
				      *wn = SynError;
				      stop = True;
				   }
				 break;
			      case SynIdentifier:
				 if (!((inputLine[j] >= 'A' && inputLine[j] <= 'Z')
				       || (inputLine[j] == (unsigned char) '\240' /*nobreakspace */ )

				       || (inputLine[j] >= 'a' && inputLine[j] <= 'z')
				       || (inputLine[j] >= '0' && inputLine[j] <= '9')
				       || (((int) inputLine[j]) >= 192 &&
					   ((int) inputLine[j]) <= 255)
				 /* lettre accentuee */
				       || inputLine[j] == '_'))
				   {
				      CompilerMessage (j + 1, COMPIL, FATAL, BAD_WORD,
						     inputLine, LineNum);
				      *wn = SynError;
				      stop = True;
				   }
				 break;
			      default:
				 break;
			   }
		     (*wl)++;
		  }
		j++;
	     }
	   while (!stop);
	else if (*wn == SynString)
	   /* chaine de caracteres */
	  {
	     /* saute le quote initial */
	     (*wi)++;
	     *wl = 0;
	     do
		if (inputLine[j] == '\0')
		  {
		     CompilerMessage (*wi,
				 COMPIL, FATAL, MULTIPLE_LINE_STRINGS_ERROR,
				    inputLine, LineNum);
		     *wn = SynError;
		     stop = True;
		  }
		else
		  {
		     if (inputLine[j] == '\'')
			if (inputLine[j + 1] == '\'')
			   /* represente un seul quote */
			  {
			     k = j;
			     do
				/* ecrase le quote double */
			       {
				  k++;
				  inputLine[k] = inputLine[k + 1];
			       }
			     while (inputLine[k] != '\0');
			     j++;
			  }
			else
			   stop = True;		/* quote final */
		     else
			j++;
		     (*wl)++;
		  }
	     while (!stop);
	  }
     }
}


/*----------------------------------------------------------------------
   TokenMatch retourne vrai si dans la ligne courante le mot       
   commencant a` l'index wi de longueur wl et de SyntacticType wn 
   correspond a` l'element de code c qui apparait dans la  
   regle r de la grammaire. Rend dans rank le rang du mot  
   dans la table Identifier si c'est un identificateur.    
  ----------------------------------------------------------------------*/

#ifdef __STDC__
static boolean      TokenMatch (indLine wi, indLine wl, SyntacticType wn, SyntacticCode c, SyntacticCode r, int *rank)

#else  /* __STDC__ */
static boolean      TokenMatch (wi, wl, wn, c, r, rank)
indLine             wi;
indLine             wl;
SyntacticType       wn;
SyntacticCode       c;
SyntacticCode       r;
int                *rank;

#endif /* __STDC__ */

{
   SyntacticCode       code;
   boolean             match;

   match = False;
   if (c < 1000)
      /* identificateur attendu */
      /* a priori ca marche */
      match = True;
   else if (c >= 1000 && c < 1100)
      /* mot-cle court attendu */
     {
	if (wl == 1 && wn == SynShortKeyword)
	   /* c'est un mot-cle court */
	  {
	     /* est-il valide ? */
	     CheckShortKeyword (wi, &code);
	     if (code > 0)
		/* mot-cle court valide */
	       {
		  if (code == c)
		     /* c'est le mot-cle court attendu */
		     match = True;
	       }
	     else
		/* mot-cle court invalide */
		CompilerMessage (wi, COMPIL, FATAL, BAD_SYMBOL, inputLine, LineNum);
	  }
     }
   else if (c >= 1100 && c < 2000)
      /* mot-cle long attendu */
     {
	CheckLongKeyword (wi, wl, &code);
	/* est-ce un mot-cle long valide ? */
	if (code == c)
	   /* c'est le mot-cle long attendu */
	   match = True;
     }
   else if (c > 3000)
      /* type de base attendu */
     {
	if (c == 3001 && wn == SynIdentifier)
	   /* on attend un nom et c'est un nom */
	  {
	     CheckIdent (wi, wl, &code, rank);
	     /* est-il dans la table ? */
	     if (code > 0)
		/* il y est */
	       {
		  if (code == r)
		     match = True;
		  /* oui, et avec le type attendu */
	       }
	     else
		/* non, il n'est pas encore connu */
	       {
		  CheckLongKeyword (wi, wl, &code);
		  /* est-ce un mot-cle long ? */
		  if (code == 0)
		     /* ce n'est pas un mot-cle long, c'est */
		     /* donc un identificateur */
		    {
		       NewIdent (wi, wl, r, rank);
		       /* on l'ajoute dans la table */
		       match = True;	/* et c'est bon... */
		    }
	       }
	  }
	if (c == 3002 && wn == SynInteger)
	   match = True;
	if (c == 3003 && wn == SynString)
	   match = True;
     }
   return match;
}


/*----------------------------------------------------------------------
   AnalyzeToken procede a` l'analyse du mot commencant a la postion
   wi de la ligne courante (inputLine), de longueur wl et de
   SyntacticType wn. Rend dans c le code grammatical du mot, dans 
   r le numero de la derniere regle ou` il a ete trouve et 
   dans rank son rang dans Identifier, si c'est un         
   identificateur. Dans pr se trouve le numero de l'avant  
   derniere regle appliquee.                               
  ----------------------------------------------------------------------*/

#ifdef __STDC__
void                AnalyzeToken (indLine wi, indLine wl, SyntacticType wn, SyntacticCode * c, SyntRuleNum * r, int *rank, SyntRuleNum * pr)

#else  /* __STDC__ */
void                AnalyzeToken (wi, wl, wn, c, r, rank, pr)
indLine             wi;
indLine             wl;
SyntacticType       wn;
SyntacticCode      *c;
SyntRuleNum        *r;
int                *rank;
SyntRuleNum        *pr;

#endif /* __STDC__ */

{
   boolean             stop;
   boolean             st1;
   boolean             ok;
   boolean             meta;
   int                 s;

   *pr = 0;
   if (level < 0)
      CompilerMessage (wi, COMPIL, FATAL, END_HAS_BEEN_DETECTED,
		     inputLine, LineNum);
   else
     {
	ok = False;
	stop = False;
	for (s = 0; s < level; s++)
	   Stack[s].Tested = False;
	/* aucune regle de la pile n'a ete testee avec ce mot, sauf celle
	   du haut, en cours */
	do
	   /* cherche une regle a laquelle le mot correspond */
	  {
	     meta = True;
	     /* traite les meta symboles de la regle courante */
	     do
	       {
		  if (GramRule[Stack[level].StRule][Stack[level].StRuleInd] >= 2000
		      && GramRule[Stack[level].StRule][Stack[level].StRuleInd] <= 2005)
		     switch (GramRule[Stack[level].StRule][Stack[level].StRuleInd])
			   {
			      case 2001:
				 /* debut d'option */
				 Stack[level].Option = True;
				 Stack[level].StRuleInd++;
				 break;
			      case 2002:
				 /* fin d'option */
				 Stack[level].Option = False;
				 Stack[level].StRuleInd++;
				 break;
			      case 2000:
			      case 2003:
				 /* fin de regle ou alternative */
				 level--;	/* depile une regle */
				 if (level < 0)
				    stop = True;
				 /* on est a la fin de la regle initiale */
				 else
				   {
				      if (Stack[level].Alt)
					 /* essaie une alternative de la nvelle regle */
					{
					   do
					      Stack[level].StRuleInd++;
					   while (GramRule[Stack[level].StRule][Stack[level].StRuleInd] != 2003
						  && GramRule[Stack[level].StRule][Stack[level].StRuleInd] != 2000);
					   if (GramRule[Stack[level].StRule][Stack[level].StRuleInd] == 2003)
					      Stack[level].StRuleInd++;
					}
				      else
					 Stack[level].StRuleInd++;
				   }
				 break;
			      case 2004:
				 /* debut de repetition */
				 Stack[level].StRuleInd++;
				 Stack[level].Option = True;
				 break;
			      case 2005:
				 /* fin de repetition */
				 if (Stack[level].Tested)
				    /* on a deja essaye ce mot, on saute */
				   {
				      Stack[level].StRuleInd++;
				      Stack[level].Option = False;
				   }
				 else
				    /* ce mot n'a pas ete essaye, on y va */
				   {
				      do
					 Stack[level].StRuleInd--;
				      while (GramRule[Stack[level].StRule][Stack[level].StRuleInd] != 2004);
				      Stack[level].StRuleInd++;
				      Stack[level].Option = True;
				      Stack[level].Tested = True;
				   }
				 break;
			   }

		  else
		     meta = False;
	       }
	     while (meta && level >= 0);
	     if (level >= 0)
	       {
		  *c = GramRule[Stack[level].StRule][Stack[level].StRuleInd];
		  if (TokenMatch (wi, wl, wn, *c, Stack[level].StRule + 1, rank))
		     /* ca correspond */
		     if (*c < 1000)
			/* symbole non terminal */
			if (level >= STACKSIZE)
			   CompilerMessage (wi, COMPIL, FATAL, NO_SPACE_LEFT_IN_STACK,
					  inputLine, LineNum);
			else
			   /* empile la regle definissant ce symbole */
			  {
			     level++;
			     Stack[level].StRule = *c - 1;
			     Stack[level].StRuleInd = 1;
			     Stack[level].Option = False;
			     Stack[level].Alt = True;
			     Stack[level].Tested = True;
			  }
		     else
		       {
			  ok = True;
			  *r = Stack[level].StRule + 1;
			  if (level > 0)
			     *pr = Stack[level - 1].StRule + 1;
			  for (s = 0; s < level; s++)
			    {
			       /* sur toute la pile */
			       Stack[s].Alt = False;
			       /* s'il y a une alternative, on ne peut */
			       /* plus en changer */
			       Stack[s].Option = False;
			    }
			  do
			     /* si on etait dans une partie */
			     /* optionnelle, on ne peut plus la sauter */
			     /* traite les meta symboles suivant le mot ok */
			    {
			       Stack[level].StRuleInd++;
			       if (GramRule[Stack[level].StRule][Stack[level].StRuleInd] >= 2000
				   && GramRule[Stack[level].StRule][Stack[level].StRuleInd] <= 2005)
				  switch (GramRule[Stack[level].StRule][Stack[level].StRuleInd])
					{
					   case 2001:
					      stop = True;	/* debut d'option */
					      break;
					   case 2002:
					      Stack[level].Option = False;	/* fin d'option */
					      break;
					   case 2000:
					   case 2003:
					      /* fin de regle ou alternative */
					      level--;	/* regle epuisee */
					      if (level < 0)
						 stop = True;
					      /* on est a la fin de la regle initiale */
					      break;
					   case 2004:
					      stop = True;	/* debut de repetition */
					      break;
					   case 2005:
					      /* fin de repetition */
					      do
						 Stack[level].StRuleInd--;
					      while (GramRule[Stack[level].StRule][Stack[level].StRuleInd] != 2004);
					      Stack[level].StRuleInd++;
					      Stack[level].Option = True;
					      stop = True;
					      break;
					}

			       else
				  stop = True;
			    }
			  while (!stop && level >= 0);
		       }
		  else
		    {
		       /* le mot ne correspond pas */
		       if (Stack[level].Option)		/* saute la partie optionnelle */
			  /* cherche la fin de la partie a option */
			 {
			    do
			       Stack[level].StRuleInd++;
			    while (GramRule[Stack[level].StRule][Stack[level].StRuleInd] != 2002
				   && GramRule[Stack[level].StRule][Stack[level].StRuleInd] != 2005);
			    Stack[level].StRuleInd++;
			    Stack[level].Option = False;
			    if (GramRule[Stack[level].StRule][Stack[level].StRuleInd] == 2000
				|| GramRule[Stack[level].StRule][Stack[level].StRuleInd] == 2003)
			       /* fin regle */
			      {
				 level--;	/* depile une regle */
				 if (level < 0)
				    stop = True;	/* pile vide */
				 else
				    Stack[level].StRuleInd++;
			      }
			 }
		       else
			  /* ce n'est pas un element optionnel */
			 {
			    st1 = False;
			    do
			       /* cherche une alternative dans la regle */
			      {
				 do
				    Stack[level].StRuleInd++;
				 while (GramRule[Stack[level].StRule][Stack[level].StRuleInd] != 2003
					&& GramRule[Stack[level].StRule][Stack[level].StRuleInd] != 2000);
				 if (GramRule[Stack[level].StRule][Stack[level].StRuleInd] == 2003)
				    /* une alternative dans la regle */
				   {
				      if (Stack[level].Alt)
					 /* alternative autorisee */
					{
					   Stack[level].StRuleInd++;	/* essaie l'alternative */
					   st1 = True;
					}
				   }
				 else
				    /* pas d'alternative dans la regle */
				    /* depile les regles jusqu'a en trouver une ou on est */
				    /* dans une partie optionnelle ou un choix */
				   {
				      while (level >= 0 && !st1)
					{
					   level--;
					   if (level >= 0)
					     {
						if (Stack[level].Option)
						   /* saute la partie optionnelle */
						   /* cherche la fin de la partie a option */
						  {
						     do
							Stack[level].StRuleInd++;
						     while (GramRule[Stack[level].StRule][Stack[level].StRuleInd] != 2002
							    && GramRule[Stack[level].StRule][Stack[level].StRuleInd] != 2005);
						     Stack[level].StRuleInd++;
						     Stack[level].Option = False;
						     st1 = True;
						  }
						else if (Stack[level].Alt)
						   /* cherche une alternative */
						  {
						     do
							Stack[level].StRuleInd++;
						     while (GramRule[Stack[level].StRule][Stack[level].StRuleInd] != 2003
							    && GramRule[Stack[level].StRule][Stack[level].StRuleInd] != 2000);
						     if (GramRule[Stack[level].StRule][Stack[level].StRuleInd] == 2003)
						       {
							  Stack[level].StRuleInd++;
							  st1 = True;
						       }
						  }
					     }
					}
				      st1 = True;
				   }
			      }
			    while (!st1);
			 }
		    }
	       }
	  }
	while (!stop && level >= 0);	/* mot ok ou fin de regle */
	if (!ok)
	   CompilerMessage (wi, COMPIL, FATAL, SYNTAX_ERROR, inputLine, LineNum);
     }
}


/*----------------------------------------------------------------------
   ParserEnd verifie, en fin de fichier source, que tout est correct
   du point de vue syntaxique.                             
  ----------------------------------------------------------------------*/

#ifdef __STDC__
void                ParserEnd ()

#else  /* __STDC__ */
void                ParserEnd ()
#endif				/* __STDC__ */

{
   if (level >= 0)
      /* la pile n'est pas vide */
      CompilerMessage (1, COMPIL, FATAL, ABNORMAL_END, inputLine, LineNum);
   else
     {
	/* la pile est vide */
	if (GramRule[Stack[0].StRule][Stack[0].StRuleInd] != 2000)
	   /* la regle initiale n'est pas terminee */
	   CompilerMessage (1, COMPIL, FATAL, ABNORMAL_END, inputLine, LineNum);
     }
}


/*----------------------------------------------------------------------
   InitSyntax initialise la table des mots-cles et la table des	
   regles a` partir d'un fichier grammaire de type GRM.		
   fileName est le nom du fichier grammaire, avec le suffixe .GRM.	
  ----------------------------------------------------------------------*/

#ifdef __STDC__
void                InitSyntax (char *fileName)

#else  /* __STDC__ */
void                InitSyntax (fileName)
char               *fileName;

#endif /* __STDC__ */

{
   indLine             j, wind, wlen;
   SyntacticType       wnat;
   int                 l;
   boolean             readingKeywordTable;
   int                 ruleptr;
   int                 currule;
   char                pgrname[200];
   char                pnomcourt[200];
   SrcKeywordDesc     *pkw1;
   BinFile             grmFile;
   boolean             fileOK;

   strcpy (pnomcourt, fileName);
   /* cherche dans le directory compil si le fichier grammaire existe */
   if (SearchFile (pnomcourt, 3, pgrname) == 0)
      CompilerMessage (0, COMPIL, FATAL, GRM_FILE_NOT_FOUND,
		     inputLine, LineNum);
   else
     {
	/* ouvre le fichier grammaire */
	grmFile = TtaReadOpen (pgrname);
	NKeywords = 0;
	LastShortKeyword = 0;
	/* on commence par la lecture de la table des mots-cles */
	readingKeywordTable = True;
	NGramRules = 0;
	ruleptr = 0;
	fileOK = True;
	while (fileOK)
	  {
	     /* lit une ligne */
	     j = 0;
	     do
	       {
		  fileOK = TtaReadByte (grmFile, &inputLine[j]);
		  j++;
	       }
	     while (j < LINE_LENGTH && inputLine[j - 1] != '\n' && fileOK);
	     /* marque la fin reelle de la ligne */
	     inputLine[j - 1] = '\0';
	     /* traite la ligne */
	     j = 1;
	     do
	       {
		  /* accede au mot suivant de la ligne */
		  GetNextToken (j, &wind, &wlen, &wnat);
		  if (readingKeywordTable)
		     /* lecture de la table des mots-cles */
		    {
		       if (wnat == SynIdentifier && LastShortKeyword == 0)
			  LastShortKeyword = NKeywords;
		       /* on passe des courts aux longs */
		       if (wnat == SynIdentifier || wnat == SynShortKeyword)
			 {
			    if (NKeywords >= MAX_KEYWORDS)
			       /* table saturee */
			       CompilerMessage (wind, COMPIL, FATAL, NO_SPACE_LEFT_IN_KEYWORD_TABLE,
					      inputLine, LineNum);
			    else
			       NKeywords++;
			    /* entree suivante de la table */
			    pkw1 = &Keywords[NKeywords - 1];
			    /* remplit cette nouvelle entree */
			    if (wlen > KEWWORD_LENGTH)
			      {
				 wlen = KEWWORD_LENGTH;
				 CompilerMessage (wind, COMPIL, FATAL, INVALID_KEYWORD_SIZE,
						inputLine, LineNum);
			      }
			    pkw1->SrcKeywordLen = wlen;
			    for (l = 0; l < wlen; l++)
			      {
				 pkw1->SrcKeyword[l] = inputLine[wind + l - 1];
				 if ((char) (((int) pkw1->SrcKeyword[l]) - 32) >= 'A'
				     && (char) (((int) pkw1->SrcKeyword[l]) - 32) <= 'Z')
				    pkw1->SrcKeyword[l] = (char) (((int) pkw1->SrcKeyword[l]) - 32);
			      }
			    /* traduit le mot-cle en majuscules */
			    j = wind + wlen;
			    GetNextToken (j, &wind, &wlen, &wnat);
			    /* lit le code */
			    /* grammatical du mot-cle */
			    if (wnat == SynInteger)
			       pkw1->SrcKeywordCode = AsciiToInt (wind, wlen);
			    else
			       /* fichier incorrect */
			       CompilerMessage (wind, COMPIL, FATAL, INCORR_GRAMMAR_FILE_GRM, inputLine, LineNum);
			 }
		       else if (wnat == SynInteger)
			  /* fin de la table mots-cles */
			  readingKeywordTable = False;
		    }
		  if (!readingKeywordTable)
		     /* lecture de la table des regles */
		     if (wnat == SynInteger)
			if (ruleptr == 0)
			   /* nouvelle regle */
			  {
			     currule = AsciiToInt (wind, wlen);		/* numero de regle */
			     if (currule > MAX_RULES)
			       {
				  /* table des regles saturee */
				  CompilerMessage (wind, COMPIL, FATAL, NO_SPACE_LEFT_IN_GRAMMAR_TABLE, inputLine, LineNum);
				  currule = MAX_RULES;
			       }
			     GramRule[currule - 1][0] = 0;
			     ruleptr = 1;
			     if (currule > NGramRules)
				NGramRules = currule;
			  }
			else
			  {
			     /* on est dans une regle */
			     GramRule[currule - 1][ruleptr] = AsciiToInt (wind, wlen);
			     if (GramRule[currule - 1][ruleptr] == 2000)
				ruleptr = 0;	/* fin regle */
			     else if (ruleptr >= RULE_LENGTH)
				/* regle trop longue */
				CompilerMessage (wind, COMPIL, FATAL, GRAMMAR_RULE_SIZE_EXCEEDED, inputLine, LineNum);
			     else
				ruleptr++;
			  }
		     else
			/* ce n'est pas un nombre */
		     if (wind > 0)
			/* fichier incorrect */
			CompilerMessage (wind, COMPIL, FATAL, INCORR_GRAMMAR_FILE_GRM, inputLine, LineNum);
		  j = wind + wlen;	/* fin du mot */
	       }
	     while (wind != 0);	/* plus de mot dans la ligne */
	  }
	TtaReadClose (grmFile);
     }
}
