# include "ldefs.h"
void
cfoll(int v)
{
       int i,j,k;
       uchar *p;
       i = name[v];
       if(i < NCH) i = 1;      /* character */
       switch(i){
               case 1: case RSTR: case RCCL: case RNCCL: case RNULLS:
                       for(j=0;j<tptr;j++)
                               tmpstat[j] = FALSE;
                       count = 0;
                       follow(v);
# ifdef PP
                       padd(foll,v);           /* packing version */
# endif
# ifndef PP
                       add(foll,v);            /* no packing version */
# endif
                       if(i == RSTR) cfoll(left[v]);
                       else if(i == RCCL || i == RNCCL){       /* compress ccl list */
                               for(j=1; j<NCH;j++)
                                       symbol[j] = (i==RNCCL);
                               p = ptr[v];
                               while(*p)
                                       symbol[*p++] = (i == RCCL);
                               p = pcptr;
                               for(j=1;j<NCH;j++)
                                       if(symbol[j]){
                                               for(k=0;p+k < pcptr; k++)
                                                       if(cindex[j] == *(p+k))
                                                               break;
                                               if(p+k >= pcptr)*pcptr++ = cindex[j];
                                       }
                               *pcptr++ = 0;
                               if(pcptr > pchar + pchlen)
                                       error("Too many packed character classes");
                               ptr[v] = p;
                               name[v] = RCCL; /* RNCCL eliminated */
# ifdef DEBUG
                               if(debug && *p){
                                       print("ccl %d: %d",v,*p++);
                                       while(*p)
                                               print(", %d",*p++);
                                       print("\n");
                               }
# endif
                       }
                       break;
               case CARAT:
                       cfoll(left[v]);
                       break;
               case STAR: case PLUS: case QUEST: case RSCON:
                       cfoll(left[v]);
                       break;
               case BAR: case RCAT: case DIV: case RNEWE:
                       cfoll(left[v]);
                       cfoll(right[v]);
                       break;
# ifdef DEBUG
               case FINAL:
               case S1FINAL:
               case S2FINAL:
                       break;
               default:
                       warning("bad switch cfoll %d",v);
# endif
       }
}

# ifdef DEBUG
void
pfoll(void)
       {
       int i,k,*p;
       int j;
       /* print sets of chars which may follow positions */
       print("pos\tchars\n");
       for(i=0;i<tptr;i++)
               if(p=foll[i]){
                       j = *p++;
                       if(j >= 1){
                               print("%d:\t%d",i,*p++);
                               for(k=2;k<=j;k++)
                                       print(", %d",*p++);
                               print("\n");
                       }
               }
}
# endif

void
add(int **array, int n)
{
       int i, *temp;
       uchar *ctemp;

       temp = nxtpos;
       ctemp = tmpstat;
       array[n] = nxtpos;              /* note no packing is done in positions */
       *temp++ = count;
       for(i=0;i<tptr;i++)
               if(ctemp[i] == TRUE)
                       *temp++ = i;
       nxtpos = temp;
       if(nxtpos >= positions+maxpos)
               error("Too many positions %s",(maxpos== MAXPOS?"\nTry using %p num":""));
}

void
follow(int v)
{
       int p;
       if(v >= tptr-1)return;
       p = parent[v];
       if(p == 0) return;
       switch(name[p]){
                       /* will not be CHAR RNULLS FINAL S1FINAL S2FINAL RCCL RNCCL */
               case RSTR:
                       if(tmpstat[p] == FALSE){
                               count++;
                               tmpstat[p] = TRUE;
                       }
                       break;
               case STAR: case PLUS:
                       first(v);
                       follow(p);
                       break;
               case BAR: case QUEST: case RNEWE:
                       follow(p);
                       break;
               case RCAT: case DIV:
                       if(v == left[p]){
                               if(nullstr[right[p]])
                                       follow(p);
                               first(right[p]);
                       }
                       else follow(p);
                       break;
               case RSCON: case CARAT:
                       follow(p);
                       break;
# ifdef DEBUG
               default:
                       warning("bad switch follow %d",p);
# endif
       }
}

void
first(int v)    /* calculate set of positions with v as root which can be active initially */
{
       int i;
       uchar *p;
       i = name[v];
       if(i < NCH)i = 1;
       switch(i){
               case 1: case RCCL: case RNCCL: case RNULLS: case FINAL: case S1FINAL: case S2FINAL:
                       if(tmpstat[v] == FALSE){
                               count++;
                               tmpstat[v] = TRUE;
                       }
                       break;
               case BAR: case RNEWE:
                       first(left[v]);
                       first(right[v]);
                       break;
               case CARAT:
                       if(stnum % 2 == 1)
                               first(left[v]);
                       break;
               case RSCON:
                       i = stnum/2 +1;
                       p = (uchar *)right[v];
                       while(*p)
                               if(*p++ == i){
                                       first(left[v]);
                                       break;
                               }
                       break;
               case STAR: case QUEST: case PLUS:  case RSTR:
                       first(left[v]);
                       break;
               case RCAT: case DIV:
                       first(left[v]);
                       if(nullstr[left[v]])
                               first(right[v]);
                       break;
# ifdef DEBUG
               default:
                       warning("bad switch first %d",v);
# endif
       }
}

void
cgoto(void)
{
       int i, j, s;
       int npos, curpos, n;
       int tryit;
       uchar tch[NCH];
       int tst[NCH];
       uchar *q;

       /* generate initial state, for each start condition */
       Bprint(&fout,"int yyvstop[] = {\n0,\n");
       while(stnum < 2 || stnum/2 < sptr){
               for(i = 0; i<tptr; i++) tmpstat[i] = 0;
               count = 0;
               if(tptr > 0)first(tptr-1);
               add(state,stnum);
# ifdef DEBUG
               if(debug){
                       if(stnum > 1)
                               print("%s:\n",sname[stnum/2]);
                       pstate(stnum);
               }
# endif
               stnum++;
       }
       stnum--;
       /* even stnum = might not be at line begin */
       /* odd stnum  = must be at line begin */
       /* even states can occur anywhere, odd states only at line begin */
       for(s = 0; s <= stnum; s++){
               tryit = FALSE;
               cpackflg[s] = FALSE;
               sfall[s] = -1;
               acompute(s);
               for(i=0;i<NCH;i++) symbol[i] = 0;
               npos = *state[s];
               for(i = 1; i<=npos; i++){
                       curpos = *(state[s]+i);
                       if(name[curpos] < NCH) symbol[name[curpos]] = TRUE;
                       else switch(name[curpos]){
                       case RCCL:
                               tryit = TRUE;
                               q = ptr[curpos];
                               while(*q){
                                       for(j=1;j<NCH;j++)
                                               if(cindex[j] == *q)
                                                       symbol[j] = TRUE;
                                       q++;
                               }
                               break;
                       case RSTR:
                               symbol[right[curpos]] = TRUE;
                               break;
# ifdef DEBUG
                       case RNULLS:
                       case FINAL:
                       case S1FINAL:
                       case S2FINAL:
                               break;
                       default:
                               warning("bad switch cgoto %d state %d",curpos,s);
                               break;
# endif
                       }
               }
# ifdef DEBUG
               if(debug){
                       print("State %d transitions on:\n\t",s);
                       charc = 0;
                       for(i = 1; i<NCH; i++){
                               if(symbol[i]) allprint(i);
                               if(charc > LINESIZE){
                                       charc = 0;
                                       print("\n\t");
                               }
                       }
                       print("\n");
               }
# endif
               /* for each char, calculate next state */
               n = 0;
               for(i = 1; i<NCH; i++){
                       if(symbol[i]){
                               nextstate(s,i);         /* executed for each state, transition pair */
                               xstate = notin(stnum);
                               if(xstate == -2) warning("bad state  %d %o",s,i);
                               else if(xstate == -1){
                                       if(stnum >= nstates)
                                               error("Too many states %s",(nstates == NSTATES ? "\nTry using %n num":""));
                                       add(state,++stnum);
# ifdef DEBUG
                                       if(debug)pstate(stnum);
# endif
                                       tch[n] = i;
                                       tst[n++] = stnum;
                               } else {                /* xstate >= 0 ==> state exists */
                                       tch[n] = i;
                                       tst[n++] = xstate;
                               }
                       }
               }
               tch[n] = 0;
               tst[n] = -1;
               /* pack transitions into permanent array */
               if(n > 0) packtrans(s,tch,tst,n,tryit);
               else gotof[s] = -1;
       }
       Bprint(&fout,"0};\n");
}

/*      Beware -- 70% of total CPU time is spent in this subroutine -
       if you don't believe me - try it yourself ! */
void
nextstate(int s, int c)
{
       int j, *newpos;
       uchar *temp, *tz;
       int *pos, i, *f, num, curpos, number;
       /* state to goto from state s on char c */
       num = *state[s];
       temp = tmpstat;
       pos = state[s] + 1;
       for(i = 0; i<num; i++){
               curpos = *pos++;
               j = name[curpos];
               if(j < NCH && j == c
               || j == RSTR && c == right[curpos]
               || j == RCCL && member(c, ptr[curpos])){
                       f = foll[curpos];
                       number = *f;
                       newpos = f+1;
                       for(j=0;j<number;j++)
                               temp[*newpos++] = 2;
               }
       }
       j = 0;
       tz = temp + tptr;
       while(temp < tz){
               if(*temp == 2){
                       j++;
                       *temp++ = 1;
               }
               else *temp++ = 0;
       }
       count = j;
}

int
notin(int n)
{       /* see if tmpstat occurs previously */
       int *j,k;
       uchar *temp;
       int i;

       if(count == 0)
               return(-2);
       temp = tmpstat;
       for(i=n;i>=0;i--){      /* for each state */
               j = state[i];
               if(count == *j++){
                       for(k=0;k<count;k++)
                               if(!temp[*j++])break;
                       if(k >= count)
                               return(i);
               }
       }
       return(-1);
}

void
packtrans(int st, uchar *tch, int *tst, int cnt, int tryit)
{
       /* pack transitions into nchar, nexts */
       /* nchar is terminated by '\0', nexts uses cnt, followed by elements */
       /* gotof[st] = index into nchr, nexts for state st */

       /* sfall[st] =  t implies t is fall back state for st */
       /*              == -1 implies no fall back */

       int i,j,k;
       int cmin, cval, tcnt, diff, p, *ast;
       uchar *ach;
       int go[NCH], temp[NCH], c;
       int swork[NCH];
       uchar cwork[NCH];
       int upper;

       rcount += cnt;
       cmin = -1;
       cval = NCH;
       ast = tst;
       ach = tch;
       /* try to pack transitions using ccl's */
       if(tryit){      /* ccl's used */
               for(i=1;i<NCH;i++){
                       go[i] = temp[i] = -1;
                       symbol[i] = 1;
               }
               for(i=0;i<cnt;i++){
                       go[tch[i]] = tst[i];
                       symbol[tch[i]] = 0;
               }
               for(i=0; i<cnt;i++){
                       c = match[tch[i]];
                       if(go[c] != tst[i] || c == tch[i])
                               temp[tch[i]] = tst[i];
               }
               /* fill in error entries */
               for(i=1;i<NCH;i++)
                       if(symbol[i]) temp[i] = -2;     /* error trans */
               /* count them */
               k = 0;
               for(i=1;i<NCH;i++)
                       if(temp[i] != -1)k++;
               if(k <cnt){     /* compress by char */
# ifdef DEBUG
                       if(debug) print("use compression  %d,  %d vs %d\n",st,k,cnt);
# endif
                       k = 0;
                       for(i=1;i<NCH;i++)
                               if(temp[i] != -1){
                                       cwork[k] = i;
                                       swork[k++] = (temp[i] == -2 ? -1 : temp[i]);
                               }
                       cwork[k] = 0;
# ifdef PC
                       ach = cwork;
                       ast = swork;
                       cnt = k;
                       cpackflg[st] = TRUE;
# endif
               }
       }
       for(i=0; i<st; i++){    /* get most similar state */
                               /* reject state with more transitions, state already represented by a third state,
                                       and state which is compressed by char if ours is not to be */
               if(sfall[i] != -1) continue;
               if(cpackflg[st] == 1) if(!(cpackflg[i] == 1)) continue;
               p = gotof[i];
               if(p == -1) /* no transitions */ continue;
               tcnt = nexts[p];
               if(tcnt > cnt) continue;
               diff = 0;
               j = 0;
               upper = p + tcnt;
               while(ach[j] && p < upper){
                       while(ach[j] < nchar[p] && ach[j]){diff++; j++; }
                       if(ach[j] == 0)break;
                       if(ach[j] > nchar[p]){diff=NCH;break;}
                       /* ach[j] == nchar[p] */
                       if(ast[j] != nexts[++p] || ast[j] == -1 || (cpackflg[st] && ach[j] != match[ach[j]]))diff++;
                       j++;
               }
               while(ach[j]){
                       diff++;
                       j++;
               }
               if(p < upper)diff = NCH;
               if(diff < cval && diff < tcnt){
                       cval = diff;
                       cmin = i;
                       if(cval == 0)break;
               }
       }
       /* cmin = state "most like" state st */
# ifdef DEBUG
       if(debug)print("select st %d for st %d diff %d\n",cmin,st,cval);
# endif
# ifdef PS
       if(cmin != -1){ /* if we can use st cmin */
               gotof[st] = nptr;
               k = 0;
               sfall[st] = cmin;
               p = gotof[cmin]+1;
               j = 0;
               while(ach[j]){
                       /* if cmin has a transition on c, then so will st */
                       /* st may be "larger" than cmin, however */
                       while(ach[j] < nchar[p-1] && ach[j]){
                               k++;
                               nchar[nptr] = ach[j];
                               nexts[++nptr] = ast[j];
                               j++;
                       }
                       if(nchar[p-1] == 0)break;
                       if(ach[j] > nchar[p-1]){
                               warning("bad transition %d %d",st,cmin);
                               goto nopack;
                       }
                       /* ach[j] == nchar[p-1] */
                       if(ast[j] != nexts[p] || ast[j] == -1 || (cpackflg[st] && ach[j] != match[ach[j]])){
                               k++;
                               nchar[nptr] = ach[j];
                               nexts[++nptr] = ast[j];
                       }
                       p++;
                       j++;
               }
               while(ach[j]){
                       nchar[nptr] = ach[j];
                       nexts[++nptr] = ast[j++];
                       k++;
               }
               nexts[gotof[st]] = cnt = k;
               nchar[nptr++] = 0;
       } else {
# endif
nopack:
       /* stick it in */
               gotof[st] = nptr;
               nexts[nptr] = cnt;
               for(i=0;i<cnt;i++){
                       nchar[nptr] = ach[i];
                       nexts[++nptr] = ast[i];
               }
               nchar[nptr++] = 0;
# ifdef PS
       }
# endif
       if(cnt < 1){
               gotof[st] = -1;
               nptr--;
       } else
               if(nptr > ntrans)
                       error("Too many transitions %s",(ntrans==NTRANS?"\nTry using %a num":""));
}

# ifdef DEBUG
void
pstate(int s)
{
       int *p,i,j;
       print("State %d:\n",s);
       p = state[s];
       i = *p++;
       if(i == 0) return;
       print("%4d",*p++);
       for(j = 1; j<i; j++){
               print(", %4d",*p++);
               if(j%30 == 0)print("\n");
       }
       print("\n");
}
# endif

int
member(int d, uchar *t)
{
       int c;
       uchar *s;

       c = d;
       s = t;
       c = cindex[c];
       while(*s)
               if(*s++ == c) return(1);
       return(0);
}

# ifdef DEBUG
void
stprt(int i)
{
       int p, t;

       print("State %d:",i);
       /* print actions, if any */
       t = atable[i];
       if(t != -1)print(" final");
       print("\n");
       if(cpackflg[i] == TRUE)print("backup char in use\n");
       if(sfall[i] != -1)print("fall back state %d\n",sfall[i]);
       p = gotof[i];
       if(p == -1) return;
       print("(%d transitions)\n",nexts[p]);
       while(nchar[p]){
               charc = 0;
               if(nexts[p+1] >= 0)
                       print("%d\t",nexts[p+1]);
               else print("err\t");
               allprint(nchar[p++]);
               while(nexts[p] == nexts[p+1] && nchar[p]){
                       if(charc > LINESIZE){
                               charc = 0;
                               print("\n\t");
                       }
                       allprint(nchar[p++]);
               }
               print("\n");
       }
       print("\n");
}
# endif

void
acompute(int s) /* compute action list = set of poss. actions */
{
       int *p, i, j;
       int cnt, m;
       int temp[300], k, neg[300], n;

       k = 0;
       n = 0;
       p = state[s];
       cnt = *p++;
       if(cnt > 300)
               error("Too many positions for one state - acompute");
       for(i=0;i<cnt;i++){
               if(name[*p] == FINAL)temp[k++] = left[*p];
               else if(name[*p] == S1FINAL){temp[k++] = left[*p];
                       if (left[*p] >= NACTIONS) error("Too many right contexts");
                       extra[left[*p]] = 1;
               } else if(name[*p] == S2FINAL)neg[n++] = left[*p];
               p++;
       }
       atable[s] = -1;
       if(k < 1 && n < 1) return;
# ifdef DEBUG
       if(debug) print("final %d actions:",s);
# endif
       /* sort action list */
       for(i=0; i<k; i++)
               for(j=i+1;j<k;j++)
                       if(temp[j] < temp[i]){
                               m = temp[j];
                               temp[j] = temp[i];
                               temp[i] = m;
                       }
       /* remove dups */
       for(i=0;i<k-1;i++)
               if(temp[i] == temp[i+1]) temp[i] = 0;
       /* copy to permanent quarters */
       atable[s] = aptr;
# ifdef DEBUG
       Bprint(&fout,"/* actions for state %d */",s);
# endif
       Bputc(&fout, '\n');
       for(i=0;i<k;i++)
               if(temp[i] != 0){
                       Bprint(&fout,"%d,\n",temp[i]);
# ifdef DEBUG
                       if(debug)
                               print("%d ",temp[i]);
# endif
                       aptr++;
               }
       for(i=0;i<n;i++){               /* copy fall back actions - all neg */
               Bprint(&fout,"%d,\n",neg[i]);
               aptr++;
# ifdef DEBUG
               if(debug)print("%d ",neg[i]);
# endif
       }
# ifdef DEBUG
       if(debug)print("\n");
# endif
       Bprint(&fout,"0,\n");
       aptr++;
}

# ifdef DEBUG
void
pccl(void) {
       /* print character class sets */
       int i, j;

       print("char class intersection\n");
       for(i=0; i< ccount; i++){
               charc = 0;
               print("class %d:\n\t",i);
               for(j=1;j<NCH;j++)
                       if(cindex[j] == i){
                               allprint(j);
                               if(charc > LINESIZE){
                                       print("\n\t");
                                       charc = 0;
                               }
                       }
               print("\n");
       }
       charc = 0;
       print("match:\n");
       for(i=0;i<NCH;i++){
               allprint(match[i]);
               if(charc > LINESIZE){
                       print("\n");
                       charc = 0;
               }
       }
       print("\n");
}
# endif

void
mkmatch(void)
{
       int i;
       uchar tab[NCH];

       for(i=0; i<ccount; i++)
               tab[i] = 0;
       for(i=1;i<NCH;i++)
               if(tab[cindex[i]] == 0)
                       tab[cindex[i]] = i;
       /* tab[i] = principal char for new ccl i */
       for(i = 1; i<NCH; i++)
               match[i] = tab[cindex[i]];
}

void
layout(void)
{
       /* format and output final program's tables */
       int i, j, k;
       int  top, bot, startup, omin;

       for(i=0; i<outsize;i++)
               verify[i] = advance[i] = 0;
       omin = 0;
       yytop = 0;
       for(i=0; i<= stnum; i++){       /* for each state */
               j = gotof[i];
               if(j == -1){
                       stoff[i] = 0;
                       continue;
                       }
               bot = j;
               while(nchar[j])j++;
               top = j - 1;
# ifdef DEBUG
               if (debug) {
                       print("State %d: (layout)\n", i);
                       for(j=bot; j<=top;j++) {
                               print("  %o", nchar[j]);
                               if (j%10==0) print("\n");
                       }
                       print("\n");
               }
# endif
               while(verify[omin+NCH]) omin++;
               startup = omin;
# ifdef DEBUG
               if (debug) print("bot,top %d, %d startup begins %d\n",bot,top,startup);
# endif
               do {
                       startup += 1;
                       if(startup > outsize - NCH)
                               error("output table overflow");
                       for(j = bot; j<= top; j++){
                               k = startup + nchar[j];
                               if(verify[k])break;
                       }
               } while (j <= top);
               /* have found place */
# ifdef DEBUG
               if (debug) print(" startup going to be %d\n", startup);
# endif
               for(j = bot; j<= top; j++){
                       k = startup + nchar[j];
                       verify[k] = i+1;                        /* state number + 1*/
                       advance[k] = nexts[j+1]+1;              /* state number + 1*/
                       if(yytop < k) yytop = k;
               }
               stoff[i] = startup;
       }

       /* stoff[i] = offset into verify, advance for trans for state i */
       /* put out yywork */
       Bprint(&fout,"# define YYTYPE %s\n",stnum+1 >= NCH ? "int" : "Uchar");
       Bprint(&fout,"struct yywork { YYTYPE verify, advance; } yycrank[] = {\n");
       for(i=0;i<=yytop;i+=4){
               for(j=0;j<4;j++){
                       k = i+j;
                       if(verify[k])
                               Bprint(&fout,"%d,%d,\t",verify[k],advance[k]);
                       else
                               Bprint(&fout,"0,0,\t");
               }
               Bputc(&fout, '\n');
       }
       Bprint(&fout,"0,0};\n");

       /* put out yysvec */

       Bprint(&fout,"struct yysvf yysvec[] = {\n");
       Bprint(&fout,"0,\t0,\t0,\n");
       for(i=0;i<=stnum;i++){  /* for each state */
               if(cpackflg[i])stoff[i] = -stoff[i];
               Bprint(&fout,"yycrank+%d,\t",stoff[i]);
               if(sfall[i] != -1)
                       Bprint(&fout,"yysvec+%d,\t", sfall[i]+1);       /* state + 1 */
               else Bprint(&fout,"0,\t\t");
               if(atable[i] != -1)
                       Bprint(&fout,"yyvstop+%d,",atable[i]);
               else Bprint(&fout,"0,\t");
# ifdef DEBUG
               Bprint(&fout,"\t\t/* state %d */",i);
# endif
               Bputc(&fout, '\n');
       }
       Bprint(&fout,"0,\t0,\t0};\n");

       /* put out yymatch */

       Bprint(&fout,"struct yywork *yytop = yycrank+%d;\n",yytop);
       Bprint(&fout,"struct yysvf *yybgin = yysvec+1;\n");
       Bprint(&fout,"Uchar yymatch[] = {\n");
       for(i=0; i<NCH; i+=8){
               for(j=0; j<8; j++){
                       int fbch;
                       fbch = match[i+j];
                       if(isprint(fbch) && fbch != '\'' && fbch != '\\')
                               Bprint(&fout,"'%c' ,",fbch);
                       else Bprint(&fout,"0%-3o,",fbch);
               }
               Bputc(&fout, '\n');
       }
       Bprint(&fout,"0};\n");
       /* put out yyextra */
       Bprint(&fout,"Uchar yyextra[] = {\n");
       for(i=0;i<casecount;i+=8){
               for(j=0;j<8;j++)
                       Bprint(&fout, "%d,", i+j<NACTIONS ?
                               extra[i+j] : 0);
               Bputc(&fout, '\n');
       }
       Bprint(&fout,"0};\n");
}

# ifdef PP
void
padd(int **array, int n)
{
       int i, *j, k;

       array[n] = nxtpos;
       if(count == 0){
               *nxtpos++ = 0;
               return;
       }
       for(i=tptr-1;i>=0;i--){
               j = array[i];
               if(j && *j++ == count){
                       for(k=0;k<count;k++)
                               if(!tmpstat[*j++])break;
                       if(k >= count){
                               array[n] = array[i];
                               return;
                       }
               }
       }
       add(array,n);
}
# endif