#include <flp2_stdio_h>
#include <flp2_qdos_h>
#include "flp2_definities_h"
#include "flp2_strukturen_h"
#include "flp2_rampen_h"
#include "flp2_invoer_h"
#include "flp2_scanner_h"
#include "flp2_boomkwekerij_h"
#include "flp2_bibliotheek_h"
#include "flp2_bibliotheek2_h"
#include "flp2_normalisatie_h"
#include "flp2_reduktie_h"

/* Een parser voor de volgende grammatika:
** <programma>          =   <>      |    <claus><programma>
** <claus>              =   <term>. |    <term> <- <term_lijst>.
** <term_lijst>         =   <>      |    <term> | <term>, <termlijst>
** <term>               =   <faktor_0>
** <faktor_i>           =   atoom <wederhelft_i> | <vrijgezel_i>
** <wederhelft_i>       =   <> | <op_j><faktor_j><wederhelft_i> (j >= i) 
** <vrijgezel_i>        =   <op_j><faktor_k><wederhelfi_i> (k = max(i,j))  
** <atoom>              =   <konstante> | <variabele> | <getal> | <string> |
**                          [<lijst>] | (<term>) | {<term_lijst>}
** <lijst>              =   <> | <term>, <lijst> | <term> "|" <term>
** deze formulering is niet perfekt, legaal is b.v [0,]
** maar links-rekursieve regels ( bv <lijst> =  <lijst>,<term) kan 
** dit soort 'recursive descent'-parsers niet aan.
*/


int            parse(SYMBOOL, int);
KNOP           *lees_nt (SYMBOOL); 
int            programma(void), claus(void), term_lijst(void), term(void),
               atoom(void),lijst(void);
KNOP           *faktor(int);
void           wederhelft (KNOP **, int);
void           vraag(SYMBOOL);
int            geef_nummer(char *,int *);
int            syntaxfout;
int            paniek;     /* WAAR tijdens foutafhandeling       */
int            clauskop;   /* WAAR  tijdens parsen van kop v.e. claus  */

struct nonterminal 
{ int   (*parser)();   /*  pointer naar parse-routine                    */
  char  start[TERMINALS], /*  start[<sym>] = WAAR als <sym> legale starter  */
        volgt[TERMINALS]; /*  idem legale volger                            */
}tabel[ ] =
/*     c  V  8  "  [  (  {  +  ,  |  )  }  ]  <-  .  ?  onzin EOF      */
{ {   &programma,
      {1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0,  1, 1 , 0,  1},
      {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,  1, 1,  0,  1}},
  {   &claus,
      {1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, -2,-1,  0, -1},
      {1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0,  1, 1,  0,  1}},
  {   &term_lijst,
      {1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, -1,-1,  0, -1},
      {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0,  1, 1,  0,  1}},
  {   &term,
      {1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0,-1, -1,-1,  0, -1},
      {0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1,  1, 1,  0,  1}},
  {   &atoom,                                 
      {1, 1, 1, 1, 1, 0, 1, 0, 0, 0, 0, 0, 0,-1, -1,-1,  0, -1},
      {0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1,  1, 1,  0,  1}},
  {   &lijst,
      {1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 1,-1, -1,-1,  0, -1},    
      {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1,  1, 1,  0,  1}}
}; 

int parse(nt, groei)
SYMBOOL nt;
BOOL groei;
{
    int ariteit, c;
    if (chkstack())  {
          fout (WAARSCHUWING, PARSER, "parse stack overflow");
          return 0;
    }
    while ((c = tabel [nt - TERMINALS].start [gluur() -> type]) != 1)   {
          if (NIET paniek++){     
               fout(WAARSCHUWING, PARSER, "%s starting a %s.",
                    geef_naam(gluur() -> type), geef_naam(nt));   }
          switch (c)  {
               case 0:  lees(0); continue;
               case -1: return -1;
               case -2: lees(0); return -1;
          }
    }
    paniek = ONWAAR;
    if (groei)    {
          neer();
          bevestig (HIER, nt);
    }
    ariteit = (*tabel[nt - TERMINALS].parser) ();     /* parse!!     */

    if (groei)
          op();                                 
    while (NIET tabel[nt - TERMINALS].volgt[gluur() -> type] )   {
          if (NIET paniek++)     
               fout(WAARSCHUWING, PARSER, "%s following a %s.",
                    geef_naam(gluur() -> type), geef_naam(nt));
          lees (0);
    }
    return ariteit;
}

/* om stack te sparen is deze funktie niet rekursief geschreven  */
int programma()       /* <programma>=<>  |  <claus><programma>      */
{
   unsigned int klaus = 0;     /* telt aantal klauzen  +1 */
   while (++klaus)  {
          switch (gluur() -> type)   {
               case PUNT:
               case VRAAGTEKEN:
               case END_OF_FILE:   break;
               default:            parse(CLAUS, 1);
                                   neer();
                                   bevestig (HIER, PROGRAMMA);
                                   continue;
          }
          break;
    }
    bevestig (HIER, NIL); 
    while (--klaus)
           op();
    return 0;
}

int claus()          /* <claus>=<term>.  |  <term>-><term_lijst>.    */
{
     clauskop = WAAR;
     parse(TERM, 0);
     clauskop = ONWAAR;
     switch(gluur() -> type)  {
          case PIJLTJE:  lees(0);
                         parse(TERM_LIJST, 1); 
          default:       vraag(PUNT);
      }
      onthoud_aantal_variabelen(geef_nummer(0, (int *)0));
      return 0;
}

int term_lijst()    /* <term_lijst>=  <term>  |  <term>,<term_lijst>   */
{
     bevestig (HIER, LIJST);
     parse(TERM, 0);
     switch (gluur() -> type)  {
          default: vraag (KOMMA);
                   return parse (TERM_LIJST, 1) + 1; 
          case PUNT:     neer();
                         bevestig (HIER, LIJST);
                         op();
          case HAAKJE_SLUITEN :
          case AKKOLADE_SLUITEN: return  1;
      }
}

int term ()        /* <term> = <faktor_0>          */
{
     KNOP *vader, *ptr;

     onthoud (&vader, &ptr);
     ent (vader, ptr, faktor (0));
     return 0;
}

KNOP *faktor (i)     /* <faktor_i> = <atoom><wederhelft_i>   */
int i;
{
     KNOP *p = 0;
     switch (gluur() -> type) {
          case HAAKJE_OPEN:
               lees(0);
               p = faktor (0);
               markeer_haakjes (p);
               vraag (HAAKJE_SLUITEN);
               break;
          case OPERATOR:
               break;
          default:
               p = stek();
               if (parse (ATOOM, 1) == -1) 
                    p = 0;    /* anders wordt p later voor b.v. kombineer()
                                 gebruikt en  krijgen we cykels         */
     }
     wederhelft (&p, i);
     return  p;
}

/* <wederhelft_i> = <> | <operator_j><faktor_j><wederhelft_i> (j >= i^1) */
/* <vrijgezel_i>  = <operator_j><faktor_k><wederhelft_i> (k = max (j, i) */

void wederhelft (eega, i) /* vrijgezel(i) is wederhelft (0, i)  */
KNOP **eega;
int i;
{
     TOKEN token;
     int j;

     if (gluur() -> type != OPERATOR) 
          return;
     j = gluur() -> voorrang;
     if (eega) 
          if (j < (i^1)) return;   /* dus even rang associeert rechts   */
     else
          j = max (j, i);
     lees (&token);
     *eega = kombineer (&token, *eega, faktor (j));
     wederhelft (eega, i);
}

int atoom()
{
     TOKEN token;
     int ariteit, varno, is_globaal;
     KNOP *p;

     lees (&token);
     switch (token.type)  {
          case GETAL:         bevestig (HIER, GETAL, &token);  break;
          case STRING:        bevestig (HIER, STRING, &token); break;
          case HOEKJE_OPEN:   bevestig (HIER, LIJST);
                              parse (LIJST, 0);
                              vraag (HOEKJE_SLUITEN);
                              break;
          case VARIABELE:     varno = geef_nummer(token.naam, &is_globaal);
                              bevestig (HIER, VARIABELE, &token,
                                   0, varno, is_globaal);
                              break;
          case KONSTANTE:     if (gluur() -> type != HAAKJE_OPEN) {
                                 bevestig (HIER, KONSTANTE, &token, 0,0,0); 
                                 break;
                               } else {
                                 bevestig (HIER, TERM);
                                 p = neer();
                                 op();
                                 lees(0);
                                 ariteit = parse (TERM_LIJST,1);
                                 vraag (HAAKJE_SLUITEN);
                                 bevestig (p, KONSTANTE, &token, ariteit, 0,0);
                                 break;
                                }
           default:             fout (WAARSCHUWING, PARSER, "%s in atom",
                                        geef_naam (token.type));
     }          /* eind switch  */
     return 0;
}

int lijst()
{
     if (gluur() -> type == HOEKJE_SLUITEN) 
          return 0;
     parse (TERM, 0);
     switch (gluur() -> type) {
          case KRAS:               lees (0);
                                   parse (TERM, 0);
                                   return 1; 
          default:                 vraag (KOMMA);       
          case HOEKJE_SLUITEN:     return parse (LIJST, 1) + 1;
     }
}

KNOP *lees_nt (nt)
SYMBOOL nt;
{
     KNOP *p = stek();

     syntaxfout = ONWAAR;         
     paniek = ONWAAR;                           
     if (gluur() -> type == PUNT)
          return 0;
     if (nt == TERM)
          p = faktor (0);
     else
          parse (nt, 1);
     if (syntaxfout)    {
          geef_terug (p);
          return 0;
     } else return p;
}  


static void vraag(t)
SYMBOOL t;
{
     if (gluur() -> type  == t) 
          lees(0);
     else
          if (NIET paniek++)
             fout(WAARSCHUWING, PARSER, "%s expected", geef_naam(t));
}

int geef_nummer (varptr, globptr)
char *varptr;
int *globptr;
{
     static char *varlijst[MAXVARS];
     static int  globlijst[MAXVARS];
     static int teller = 0;
     int i;

     if (NIET varptr) {
          i = teller;
          teller =0;                      /* reset teller          */
          return i;
     }
     for (i=0; i<teller; i++)
          if (varlijst[i] == varptr) break;
     if (i < teller) {                    /* gevonden               */
          *globptr = globlijst[i];
          return i;
     } else  {                            /* onthouden              */
          if (++teller == MAXVARS)        /* overflow               */
               fout(ERR_BO, PARSER, "too many (>%d) variables in one clause",MAXVARS);
          varlijst [i] = (*varptr == '_' ? 0 : varptr);   
                                          /* '_' krijgt altijd nieuw nummer */

          *globptr = globlijst [i] = clauskop;
          return i;
     }
}
