/*                                                               -*- C -*-
 *  RULES.C
 *
 *  (c)Copyright 1991-93 by Tobias Ferber,  All Rights Reserved
 */

#include <ctype.h>
#include <stdio.h>
#include <stdarg.h>
#include <stdlib.h>

#include "cvt.h"


/*** / GLOBALS / ***/

crule_t **crules;           /* global crule_t * array of size global_numrules */

long global_numrules= 0;     /* total #of rules; counted by readcrule() */

int global_maxlhs= 0;       /* current maximum length of a lhs in 'crules'.
                             * This value is updated with each call of
                             * addcrule() */


/*** / NEW / ***/

crule_t *new(void)
/* allokiert eine neue crule und initialisiert die members */
{ crule_t *rp= (crule_t *)malloc(sizeof(crule_t));
  if(rp)
  { rp->next= NIL(crule_t);
    rp->lhs= rp->rhs= (char *)0L;
    rp->l= rp->r= 0;
    rp->ln= 0;
  }
  return rp;
}


/*** / DISPOSE / ***/

crule_t *dispose(crule_t *rp)
/* gibt eine crule frei und ihren Nachfolger zur"uck */
{ crule_t *succ= rp->next;
  if(rp->lhs) { free(rp->lhs); rp->lhs= (char *)0L; rp->l= 0; }
  if(rp->rhs) { free(rp->rhs); rp->rhs= (char *)0L; rp->r= 0; }
  rp->ln= 0;
  free(rp);
  return succ;
}


/*** / INIT_RULES / ***/

int init_rules(void)
/* richtet das 'crules' array ein und initialisiert es auf NIL(crule_t).
 * Es wird hier nicht "uberpr"uft ob die ANSI Grenze von 64k f"ur
 * malloc() "uberschritten wird, was aber auch "au\3erst unwahrscheinlich
 * ist. */
{
#ifdef DEBUG
  if(debuglevel >= 1)
    printf("> init_rules() entered to malloc() %ld bytes of memory\n",
      global_numchars * sizeof(crule_t *));
#endif

  crules= (crule_t **)malloc(global_numchars * sizeof(crule_t *));

  if(crules)
  { long i;
    for(i=0; i<global_numchars; i++)
      crules[i]= NIL(crule_t);
    return 0;
  }
  else return 1;
}


/*** / EXIT_RULES / ***/

void exit_rules(void)
/* Hier werden alle Eintr"age in 'crules' aufgel"ost und deren Speicher
 * freigegeben.  Au\3erdem wird 'crules' selbst abgebaut. */
{
#ifdef DEBUG
  if(debuglevel >= 1)
    printf("> exit_rules() entered to free() %ld rules for %d chars\n",
      global_numrules, global_numchars);
#endif

  if(crules)
  { long i;
    for(i=0; i<global_numchars; i++)
    { crule_t *rp= crules[i];
      while(rp) rp= dispose(rp);
      crules[i]= NIL(crule_t);
    }
    free(crules);
  }
  global_numrules= 0;
}


/*** / ADDCRULE / ***/

int addcrule(crule_t *rp)
/* Hier wird eine crule 'rp' in das globale 'crules' array eingef"ugt.
 * Der erste Buchstabe der lhs von 'rp' bestimmt den index in 'crules'.
 * Wenn dort schon ein Eintrag ist, wird 'rp' so eingesetzt, da\3 jeder
 * Vorg"anger von 'rp' eine l"angere lhs hat und jeder Nachfolger eine
 * k"urzere.  Bei gleicher L"ange wird nach den ASCII Werten in lhs
 * absteigend sortiert, d.h. Vorg"anger haben gr"o\3ere ASCII Werte
 * als die Nachfolger.
 * Der R"uckgabewert ist 0 wenn alles geklappt hat, 1, wenn die gleiche
 * lhs schon vorhanden war und 2, wenn 'rp' oder dessen lhs leer war. */
{ if(rp && rp->lhs)
  { int r= (int)((rp->lhs)[0] & 0377);
    crule_t *m= NIL(crule_t), *n= crules[r];
    while(n && n->l > rp->l) { m=n; n=n->next; }
    if(n && n->l == rp->l)
    { char *s= n->lhs, *t= rp->lhs;
      int l= n->l; /* == rp->l */   
      while(l-- && *s==*t) { ++s; ++t; }
      if(l<0) return 1; /* ambiguous! */
      else if(*s>*t) m=n;
    }
    if(m) { rp->next=m->next; m->next=rp; }
    else { rp->next= crules[r]; crules[r]= rp; }
    if(rp->l > global_maxlhs) global_maxlhs= rp->l;
    return 0;
  }
  return 2;
}


#ifdef DEBUG

/*** / MAXLHS / ***/

int maxlhs(void)
/* Ermittelt den l"angsten linken Kontext in 'crules' */
{ int r, m=0;
  for(r=0; r<global_numchars; r++)
  { crule_t *rp= crules[r];
    while(rp)
    { if(rp->l > m) m= rp->l;
      rp= rp->next;
    }
  }
  return m;
}


/*** / PRINT_CRULE / ***/

void print_crule(crule_t *rp)
{
  if(rp->l && rp->lhs)
  { int i,q;
    for(i=q=0; i<rp->l; i++)
    { int c= (rp->lhs)[i]&0xFF;
      if(isprint(c))
      { if(!q)
        { putchar('\"');
          q=1;
        }
        if(c=='\\' || c=='\"')
          putchar('\\');
        putchar(c);
      }
      else /* !isprint(c) */
      { if(q)
        { printf("\",");
          q=0;
        }
        printf("$%02x",c);
      }
      if(rp->l>i+1 && q==0)
        putchar(',');
    }
    if(q) putchar('\"');
    printf("->");
    if(rp->r && rp->rhs)
    { for(i=q=0; i<rp->r; i++)
      { int c= (rp->rhs)[i]&0xFF;
        if(isprint(c))
        { if(!q)
          { putchar('\"');
            q=1;
          }
          if(c=='\\' || c=='\"')
            putchar('\\');
          putchar(c);

        }
        else /* !isprint(c) */
        { if(q)
          { printf("\",");
            q=0;
          }
          printf("$%02x",c);
        }
        if(rp->r>i+1 && q==0)
          putchar(',');
      }
      if(q) putchar('\"');
    }
    printf("; // line %d\n",rp->ln);
  }
  else printf("// empty rule in line %d\n",rp->ln);
}


/*** / DUMP_CRULES / ***/

void dump_crules(void)
/* prints all rules in 'crules' */
{ crule_t *rp;
  int r;
  printf("/*** / CONVERSION RULES / ***/\n");
  for(r=0; r<global_numchars; r++)
  { rp= crules[r];
    while(rp)
    { print_crule(rp);
      rp= rp->next;
    }
  }
  printf("// %ld rules; maximum lhs is %d\n",global_numrules, maxlhs());
}

#endif /* DEBUG */
