#define SHOW_TABLES 1 // try to decode tables

#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <ctype.h>
#include "tables.h"

const char *quoted(char *P) {
  if (!strcmp(P, "T")) return "\"";
  if (!strcmp(P, "COLON")) return "\"";
  if (!strcmp(P, "COMMENT")) return "\"";
  if (!strcmp(P, "LB")) return "\"";
  if (!strcmp(P, "ALIAS")) return "\"";
  if (!strcmp(P, "RB")) return "\"";
  if (!strcmp(P, "SUB")) return "\"";
  if (!strcmp(P, "JUMP")) return "\"";
  if (!strcmp(P, "FOR")) return "\"";
  if (!strcmp(P, "THEN")) return "\"";
  return"";
}


char *actions(char *P) {
  if (!strcmp(P, "%DUMMY")) return "set dummy var for init";
  if (!strcmp(P, "%DECL")) return "escape to declarator";
  if (!strcmp(P, "%TYPE")) return "2 - check operand type";
  if (!strcmp(P, "%ZERO")) return "check record=0";
  if (!strcmp(P, "%APPLY")) return "apply parameters";
  if (!strcmp(P, "%PROT")) return "check prot=0";
  if (!strcmp(P, "%SETPROT")) return "set protection";
  if (!strcmp(P, "%PTYPE")) return "6 - set phrase type";
  if (!strcmp(P, "%GAPP")) return "generate and set app";
  if (!strcmp(P, "%LOCAL")) return "reset local limit";
  if (!strcmp(P, "%GUARD")) return "protect record subnames";
  if (!strcmp(P, "%MCODE")) return "handle machine code";
  if (!strcmp(P, "%CDUMMY")) return "check = + DUMMY";
  if (!strcmp(P, "%SETTYPE")) return "1 - fixing type";
  if (!strcmp(P, "%OPER")) return "8 - check operator type";
  if (!strcmp(P, "%PARAM")) return "10- set parameter type & check";
  if (!strcmp(P, "%BLOCK")) return "7 - check block type";
  if (!strcmp(P, "%OTHER")) return "3,4,9 - sundry others";
  if (!strcmp(P, "%COMPILE")) return "compile phrase";
}

char *manifest(char *P) {
  if (!strcmp(P, "%MSTART")) return "infiltrate %start";
  if (!strcmp(P, "%CLEAR")) return "record=0";
  if (!strcmp(P, "%PRED")) return "set cc from predicate";
  if (!strcmp(P, "%DUBIOUS")) return "check dubious declaration";
  if (!strcmp(P, "%DUP")) return "duplicate proc param id";
  if (!strcmp(P, "%PIN")) return "start of parameters";
  if (!strcmp(P, "%POUT")) return "end   of parameters";
  if (!strcmp(P, "%EDUP")) return "end of DUP";
  return "???";
}

char *builtin(char *P) {
  if (!strcmp(P, "IDENT")) return "identifier";
  if (!strcmp(P, "V")) return "simple variable";
  if (!strcmp(P, "N")) return "pointer variable";
  if (!strcmp(P, "CONST")) return "constant";
  if (!strcmp(P, "FM")) return "recordformat";
  if (!strcmp(P, "R")) return "routine call";
  if (!strcmp(P, "F")) return "function call";
  if (!strcmp(P, "M")) return "map call";
  if (!strcmp(P, "P")) return "predicate call";
  if (!strcmp(P, "RP")) return "routine parameter";
  if (!strcmp(P, "FP")) return "function parameter";
  if (!strcmp(P, "MP")) return "map parameter";
  if (!strcmp(P, "PP")) return "predicate parameter";
  if (!strcmp(P, "L")) return "label";
  if (!strcmp(P, "S")) return "switch";
  if (!strcmp(P, "A")) return "array";
  if (!strcmp(P, "AN")) return "arrayname";
  if (!strcmp(P, "NA")) return "name array";
  if (!strcmp(P, "NAN")) return "namearrayname";
  return "???";
}

int DEBUG = 0;
int ZERO = 0; // Z
int GENERATED = 1; // VDEC
int NORMAL = 20;  // T
int BUILTIN = 90; // IDENT
int MANIFEST = 120; // %MSTART
int FIGURATIVE = 130; // PIDENT
int ACTIONS = 180; //
int PHRASAL = 200; // APP
int i,j;
char left[128];
char mid[128];
char *name[256];
#define MAX_STACK 64
int norecurse[MAX_STACK];
int nextfree = 0;
/*

INITDEC -> %CDUMMY (       V   (EQ  %SETTYPE  EXP<[1],
           166           167   171  176       187
                               JAM  %SETTYPE  SIGN" EXP<[1]),
                               172  177       188   187
                           N   EQEQ %SETTYPE (V<    %TYPE,
                         168   173  178       179   189
                                              N<    %TYPE)          %PROT (ADDOP< SEXP<<[4])
                                              180   189             191    193    195
                                                                           ?,
                                                                           194
                          AN   EQEQ %SETTYPE (A<  %TYPE,
                         169   174  181       182
                                              AN< %TYPE) %PROT,
                                              183 190    192
                         NAN   EQEQ %SETTYPE (NA< %TYPE,
                         170   175  184       185 190
                                              NAN< %TYPE) %PROT
                                              186  190    192
                   )

gram 166 [0a6]:           0 193 %CDUMMY       {167},
  gram 167 [0a7]:  MORE     0  91 V             {171},
  gram 168 [0a8]:  MORE     0  92 N             {173},
  gram 169 [0a9]:  MORE     0 107 AN            {174},
  gram 170 [0aa]:           0 109 NAN           {175},

gram 171 [0ab]:  MORE     0  51 EQ            {176},
gram 172 [0ac]:           0  53 JAM           {177},

gram 173 [0ad]:           0  52 EQEQ          {178},

gram 174 [0ae]:           0  52 EQEQ          {181},

gram 175 [0af]:           0  52 EQEQ          {184},
gram 176 [0b0]:           0 194 %SETTYPE      {187},
gram 177 [0b1]:           0 194 %SETTYPE      {188},
gram 178 [0b2]:           0 194 %SETTYPE      {179},
gram 179 [0b3]:  MORE     0  91 V        <    {189},
gram 180 [0b4]:           0  92 N        <    {189},
gram 181 [0b5]:           0 194 %SETTYPE      {182},
gram 182 [0b6]:  MORE     0 106 A        <    {190},
gram 183 [0b7]:           0 107 AN       <    {190},
gram 184 [0b8]:           0 194 %SETTYPE      {185},
gram 185 [0b9]:  MORE     0 108 NA       <    {190},
gram 186 [0ba]:           0 109 NAN      <    {190},
gram 187 [0bb]:           1 228 EXP      <    {  0},
gram 188 [0bc]:       O   0  45 SIGN          {187},
gram 189 [0bd]:           0 183 %TYPE         {191},
gram 190 [0be]:           0 183 %TYPE         {192},
gram 191 [0bf]:           0 186 %PROT         {193},
gram 192 [0c0]:           0 186 %PROT         {  0},
gram 193 [0c1]:  MORE     0  89 ADDOP    <    {195},
gram 194 [0c2]:           0   0 Z             {  0},
gram 195 [0c3]:           4 221 SEXP     <<   {  0},

 */


/*

IDENTS  -> IDENT (COMMA IDENT)*
           196    197   196
                  Z
                  198

IDENTS  -> IDENT (COMMA IDENTS,
           196    197   196
                  Z)
                  198



gram 196 [0c4]:           0  90 IDENT         {197},
gram 197 [0c5]:  MORE O   0  19 COMMA         {196},
gram 198 [0c6]:           0   0 Z             {  0},

 */



/*
    EXP1    -> SEXP< (OP1 %OPER SEXP<)*
               432    433 435   432


    EXP1    -> SEXP< (OP1 %OPER SEXP<,
               432    433 435   432
                      Z)

gram 432 [1b0]:           0 221 SEXP     <    {433},
gram 433 [1b1]:  MORE     0  42 OP1           {435},
gram 434 [1b2]:           0   0 Z             {  0},
gram 435 [1b3]:           0 195 %OPER         {432},


 */




/*
      (TERM TERM TERM, Z) is equivalent to (TERM TERM TERM)?

the recursive case may be an exception...

      PHRASE -> TERM (COMMA TERM)* is encoded as PHRASE -> TERM (COMMA PHRASE, Z)

simpler case:

      EXP1    -> SEXP< (OP1 %OPER SEXP<)*

we may need to match up tails, to simplify these:

      (A, B) C  is encoded as  A C, B C

 */

int dontrecurse(int gp) {
  int i;
  for (i = 0; i < nextfree; i++) {
    if (norecurse[i] == gp) return 1;
  }
  return 0;
}

void expand(int depth, int gp) {
  if (DEBUG) printf(" {Grammar entry point = %d} ", gp);
  int MORE  = (gram(gp) & 0x8000)>>12;
  int O     = (gram(gp) & 0x4000)>>12;
  int ORDER = (gram(gp) & 0x3000)>>12;
  int TYPE  = (gram(gp) & 0x0F00)>>8;
  int CLASS =  gram(gp) & 0x00FF;
  int close;
  if (dontrecurse(gp)) {
    printf("%s%s",
           name[CLASS],
           ORDER == 0 ? "" : (ORDER == 1 ? "<" : (ORDER == 2 ? "<<" : "<<<")));
    return;
  }
  norecurse[nextfree++] = gp;
  if (close=MORE) printf("(");
  for (;;) {
    printf("%s%s",
           name[CLASS],
           ORDER == 0 ? "" : (ORDER == 1 ? "<" : (ORDER == 2 ? "<<" : "<<<")));
    if (TYPE) printf("[%d] ", TYPE); else printf(" ");
    if (O) printf(" O "); // don't know significance of this yet.
    if (glink(gp)) expand(depth+1, glink(gp)); // next in sequence
    if (!MORE) break;
    // next alternative
    printf(", "); // \n
    gp += 1;
    MORE  = (gram(gp) & 0x8000)>>12;
    O     = (gram(gp) & 0x4000)>>12;
    ORDER = (gram(gp) & 0x3000)>>12;
    TYPE  = (gram(gp) & 0x0F00)>>8;
    CLASS =  gram(gp) & 0x00FF;
  }
  if (close) printf(")");
  nextfree -= 1;
}

void decode(char *PhraseName) {
  int PN;
  printf("%-12s -> ", PhraseName);
  for (PN = PHRASAL; PN < 256; PN++) {
    if (!strcmp(name[PN], PhraseName)) {
      expand(0, phrase(PN)&0xFFFF);
      printf("\n");
      return;
    }
  }  
  printf(" *** ERROR ***\n");
}

void trieprint(int p, char *word, int cp, int tag1, int tag2) {
  int oldp = p;
  int MORE = kdict(cp)&0x8000;
  int ENDSWORD = kdict(cp)&0x4000;
  int LEFTCH = kdict(cp)&127;
  int RIGHTCH = (kdict(cp)>>7)&127;

  // kdict 206 [0ce]:  {206} #   'L' ( 76)
  //                             ' ' ( 15) [1996], LIAS
  if (ENDSWORD) { // $
    printf("  \"%s\",\n", word);
    return;
  }

  if (MORE) { // #
    if (LEFTCH == '(') {
      strcat(word, "$40"); p += 3;
    } else if (LEFTCH == 10) {
      strcat(word, "$10"); p += 3;
    } else {
      word[p++] = LEFTCH;
      word[p] = '\0';
    }
    trieprint(p, word, cp+RIGHTCH, tag1, tag2);
    word[p=oldp] = '\0';
    trieprint(p, word, cp+1, tag1, tag2);
    return;
  }
  
  word[p++] = LEFTCH; word[p] = '\0';
  if (RIGHTCH) {
    if (RIGHTCH == '(') {
      strcat(word, "$40"); p += 3;
    } else if (RIGHTCH == 10) {
      strcat(word, "$10"); p += 3;
    } else {
      word[p++] = RIGHTCH;
      word[p] = '\0';
    }
  }

  trieprint(p, word, cp+1, tag1, tag2);  
  word[oldp] = '\0';
}

int main(int argc, char **argv) {
  printf("%s\n", "* 16-OCT-1980        IMP-77 GRAMMAR");
  printf("%s\n", "");
  printf("%s\n", "*     actions                             pactions");
  printf("%s\n", "*");
  printf("%s\n", "* 1 - set type                            check for record exp");
  printf("%s\n", "* 2 - check type                          set numerical ptype");
  printf("%s\n", "* 3 - check string                        set string type");
  printf("%s\n", "* 4 - recover atom                        set integer type");
  printf("%s\n", "* 5 - apply parameters,subscripts         set const type");
  printf("%s\n", "* 6 - indirect next                       set next");
  printf("%s\n", "* 7 - check&set block type                generalise cond exps");
  printf("%s\n", "* 8 - check operator");
  printf("%s\n", "* 9 - check non-local %for");
  printf("%s\n", "*10 - direct next");
  printf("%s\n", "");
  printf("%s\n", "/CLASSES");
  printf("%s\n", "");
  // determine offsets of various things...

  sprintf(left, "%3d %s\"", 0, name[0]=text(0));
  printf("%-20s,(%d)\n", left, 19);

  printf("\n[generated classes=%d]\n\n", GENERATED);
  for (i = GENERATED; i < NORMAL; i++) {
    if (*text(i) != '\0') {
      sprintf(left, "%3d %s\"", i, name[i]=text(i));
      if (initial(i) == 0) {
        printf("%-15s%%\n", left);
      } else {
        // I've displayed info relating to the 'initial' array in places where it
        // *might* be used, but those are wild-assed guesses.
        printf("%-15s%% {initial=%04x (%d)}\n", left, initial(i)&0xFFFF, initial(i));
      }
    } else name[i]="?";
  }

  printf("\n[normal classes=%d]\n\n", NORMAL);
  for (i = NORMAL; i < BUILTIN; i++) {
    if (*text(i) != '\0') {
      sprintf(left, "%3d %s%s", i, name[i]=text(i), quoted(text(i)));
      if ((initial(i)&0xFFFF) == 0) {
        printf("%-15s\n", left);
      } else {
        printf("%-15s%3d%c\n", left, initial(i)&0x7FFF, initial(i)&0x8000 ? '!' : ' ');
      }
    } else name[i]="?";
  }
   
  printf("\n[built-in=%d]\n\n", BUILTIN);
  for (i = BUILTIN; i < MANIFEST; i++) {
    if (*text(i) != '\0') {
      sprintf(left, "%3d %s", i, name[i]=text(i));
      sprintf(mid, "%-15s[%s]", left, builtin(text(i)));
      if ((initial(i)&0xFFFF) == 0) {
        printf("%-37s\n", mid);
      } else {
        printf("%-37s {initial=%04x %c (%3d)}\n", mid, initial(i)&0xFFFF, initial(i)&0x8000 ? '!' : ' ', initial(i)&0x7FFF);
      }
      //printf("%-37s {initial=%04x %c (%3d)}\n", mid, initial(i)&0xFFFF,  initial(i)&0x8000 ? '!' : ' ', initial(i)&0x7FFF);
    } else name[i]="?";
  }

  printf("\n[manifest=%d]\n\n", MANIFEST);
  for (i = MANIFEST; i < FIGURATIVE; i++) {
    if (*text(i) != '\0') {
      sprintf(left, "%3d %s", i, name[i]=text(i));
      printf("%-15s[%s]\n", left, manifest(text(i)));
    } else name[i]="?";
  }

  printf("\n[figurative=%d]\n\n", FIGURATIVE);
  for (i = FIGURATIVE; i < ACTIONS; i++) {
    if (*text(i) != '\0') {
      sprintf(left, "%3d %s", i, name[i]=text(i));
      printf("%-15s=%s\n", left, name[_atomic[i-FIGURATIVE]]);
    } else name[i]="?";
  }

  printf("\n[actions=%d]\n\n", ACTIONS);
  for (i = ACTIONS; i < PHRASAL; i++) {
    if (*text(i) != '\0') {
      sprintf(left, "%3d %s", i, name[i]=text(i));
      printf("%-15s[%s]\n", left, actions(text(i)));
    } else name[i]="?";
  }

  printf("\n[phrasal=%d]\n\n", PHRASAL);
  for (i = PHRASAL; i < 256; i++) {
    if (*text(i) != '\0') {
      if (i == PHRASAL) {
        sprintf(left, "%3d %s", i, name[i]=text(i));
        printf("%-15s*** used by compile ***\n", left);
      } else {
        sprintf(left, "%3d %s", i, name[i]=text(i));
#ifdef SHOW_TABLES
        printf("%-15s[phrase=%03d [%03x]]\n", left, phrase(i)&0xFFFF, phrase(i)&0xFFFF);
#else
        printf("%-15s\n", left);
#endif
      }
    }
  }

  printf("\n\n/RULES\n\n# SS is missing\n# APP is a dummy (null match), which is used for (...)? and (...)* \n\n");
  decode("APP");  // MISSING "SS"

  // (...)* still needs work.
  // Could get these from PHRASAL at 200 but this is while debugging and keeps them in the right order.
  decode("INITDEC");
  decode("IDENTS");
  decode("SWIDS");
  decode("SWDEF");
  decode("IMP");
  decode("EIMP");
  decode("XIMP");
  decode("COND");
  decode("SCOND");
  decode("RECEXP");
  decode("EXP");
  decode("EXP2");
  decode("EXP1");
  decode("SEXP");
  decode("IEXP");
  decode("IEXP2");
  decode("ISEXP");
  decode("SEQ");
  decode("RCONST");
  decode("SCONST");
  decode("FDEF");
  decode("FITEM");
  decode("MOREA");
  decode("CONSTB");
  decode("RCONSTB");
  decode("BPL");
  decode("FPP");
  decode("FPP0");
  decode("FPP1");
  decode("FPP2");
  decode("ADEFN");
  decode("BASEAPP");
  decode("APP2");
  decode("APP3");
  decode("APP4");
  decode("APP5");
  decode("APP6");
  decode("CLIST");
  decode("INITVAR");
  decode("CIEXP");
  decode("RESEXP");
  decode("NPARM");
  decode("VARP");
  decode("ARRAYP");
  decode("NARRAYP");
  decode("STRUCT");
  decode("RANGE");
  //decode("ESCDEC");  // these are currently broken.
  //decode("ESCPROC");
  //decode("ESCARRAY");
  //decode("ESCREC");
  printf("\n\n/END\n");


  printf("\n# LITERALS:  # no idea where these are referenced from (presumably somewhere in gram/glink),\n             # or how they are indexed. \n\n");
  for (i = 32; i < 127; i++) {
    int cp = kdict(i)>>2;
    if ((cp == 0) || (cp == 32)) {
      if (1==0 && kdict(i)&3) {
        printf("%c: %c%c\n",
               i,
               kdict(i)&1 ? '@':' ',
               kdict(i)&2 ? '%':' ');
      }
    } else {
      static char word[64];
      word[0] = i;
      trieprint(1, word, cp, kdict(i)&1, kdict(i)&2);
    }
  }

#ifdef SHOW_TABLES // Turn this on while developing.  Very handy.
  printf("\n"); // FLAG<1> 0<1> SS<2> 0<3> T<1> LINK<8>
  for (i = 0; i < 120; i++) {
    if (initial(i)) {
      int FLAG = (initial(i)>>15)&1;
      int   O1 = (initial(i)>>14)&1;
      int   SS = (initial(i)>>13)&3;
      int   O2 = (initial(i)>>11)&7;
      int    T = (initial(i)>>8)&1;
      int LINK =  initial(i)&255;
      printf("initial %3d: FLAG %d  O1 %d  SS %d  O2 %d  T %d  LINK %3d\n",
             i, FLAG, O1, SS, O2, T, LINK);
    }
  }
  
  printf("\n"); // MORE<1> 0<1> ORDER<2> TYPE<4> CLASS<8>
  for (j = 0; j <= max_grammar-1; j+=1) {
    if (gram(j+0) || gram(j+1) || gram(j+2) || gram(j+3) || gram(j+4) || gram(j+5) || gram(j+6) || gram(j+7)) {
      printf("gram %3d [%03x]:  ", j, j);
      for (i = 0; i < 1; i++) {
        if (i == max_grammar) break;
        int MORE  = (gram(i+j)>>15)&1;
        int O     = (gram(i+j)>>14)&1;
        int ORDER = (gram(i+j)>>12)&3;
        int TYPE  = (gram(i+j)>>8)&15;
        int CLASS =  gram(i+j) & 0x00FF;
        printf("%s%s",
               MORE ? "MORE ":"     ",
               O ? "O ":"  ");

        printf("%9s", // "%4d %9s",
               // CLASS,
               name[CLASS]);

        printf("%s",
               ORDER == 0 ? "" : (ORDER == 1 ? "<" : (ORDER == 2 ? "<<" : "<<<")));

        if (TYPE) printf("[%d]", TYPE); else printf("   ");

        printf("%s",
               ORDER == 0 ? "    " : (ORDER == 1 ? "   " : (ORDER == 2 ? "  " : " ")));


        printf("{%3d},  ", glink(i+j));
      }
      printf("\n");
    }
  }
  
  printf("\n");
  for (j = 0; j <= max_grammar-8; j+=8) {
    if (glink(j+0) || glink(j+1) || glink(j+2) || glink(j+3) || glink(j+4) || glink(j+5) || glink(j+6) || glink(j+7)) {
      printf("glink %3d [%03x]:  ", j, j);
      for (i = 0; i < 8; i++) {
        if (i == max_grammar) break;
        if (glink(i+j) == -1) {
          printf("         -1, ");
        } else {
          printf("%3d [%3x %c], ", glink(i+j)&0xFFFF, glink(i+j)&0xFFFF, isprint(glink(i+j)&255) ? glink(i+j)&255 : ' ');
        }
      }
      printf("\n");
    }
  }
  
  printf("\n");

  for (j = 32; j < 128; j+=1) {
    if (kdict(j+0) || kdict(j+1) || kdict(j+2) || kdict(j+3) || kdict(j+4) || kdict(j+5) || kdict(j+6) || kdict(j+7)) {
      printf("kdict %3d [%03x]:  ", j, j);
      for (i = 0; i < 1; i++) {
        if (i+j == 127) {
          printf("DEL   ");
        } else {
          printf("{%c}   ", i+j);
        }
        printf("%c%c      (-> %3d), ",
               (kdict(i+j)&0x8000) ? '#' : ' ',
               (kdict(i+j)&0x4000) ? '$' : ' ',
               (kdict(i+j)&0x3FFF)>>2
               );
      }
      printf("\n");
    }
  }

  for (j = 128; j < 596; j+=1) {
    if (kdict(j+0) || kdict(j+1) || kdict(j+2) || kdict(j+3) || kdict(j+4) || kdict(j+5) || kdict(j+6) || kdict(j+7)) {
      printf("kdict %3d [%03x]:  ", j, j);
      for (i = 0; i < 1; i++) {
        if (i+j > 596) break;
        if (i+j < 127) {
          printf("{'%c'} ", i+j);
        } else {
          printf("{%3d} ", i+j);
        }
        printf("%c %c '%c' (%3d)\n                            '%c' (%3d)",
               (kdict(i+j)&0x8000) ? '#' : ' ',
               (kdict(i+j)&0x4000) ? '$' : ' ',
               isprint(kdict(i+j)&127) ? kdict(i+j)&127 : ' ',
               kdict(i+j)&127,
               isprint((kdict(i+j)>>7)&127) ? (kdict(i+j)>>7)&127 : ' ',
               (kdict(i+j)>>7)&127
               );
        if (((kdict(i+j)&0x8000) != 0) ||
            ((kdict(i+j)&0x4000) != 0) ||
            (!isprint((kdict(i+j)>>7)&127)) ||
            (!isprint(kdict(i+j)&127)))
          printf(" [%4d], ", (kdict(i+j)&0x3FFF));
        else
          printf(", ");
        if ((kdict(i+j)&0x8000) && ((kdict(i+j)&0x4000) == 0)) { // #
          int cp = i+j;
          printf("%c", isprint(kdict(cp)&127) ? kdict(cp)&127 : ' ');
          cp += (kdict(cp)>>7)&127;
          while (((kdict(cp)&0x8000) == 0) && ((kdict(cp)&0x4000) == 0)) {
            if ((kdict(i+j)>>7)&127 == 0) {
              printf("%c {DONE:0}",
                     isprint(kdict(cp)&127) ? kdict(cp)&127 : ' ');
              break;
            } else {
              printf("%c%c",
                     isprint(kdict(cp)&127) ? kdict(cp)&127 : ' ',
                     isprint((kdict(cp)>>7)&127) ? (kdict(cp)>>7)&127 : ' ');
              cp += 1;
            }
          }
        }
      }
      printf("\n");
    }
  }
#endif // SHOW_TABLES
  

  exit(0);
  return(0);
}
