#include "icode2ast.h"
#include "icode2ast_data.h"
#include "showicode.h"

static char *rcs_version = "icode2ast.c V$Revision: 1.8 $";

// Take an icode file and initially convert it to a 1:1 mapped AST.
// Then refine the AST until it can be output as C.

// During the refinement process, things like the individual icodes
// comprising a procedure can be built into a single higher-level AST
// procedure object.  This will facilitate all sorts of things such
// as exbedding or procedure flattening.  Basic blocks can be built
// up between jumps, etc etc.

// At some future point this can be called directly from pass1 to avoid
// the need for an intermediate icode altogether.

static int get_raw_byte(void) {
  int i = fgetc(stdin);
  if (i == EOF) return EOF;
  return (int)((unsigned char)i);
}

static void unget_byte(int c) {
  ungetc(c, stdin);
}

static int get_byte(void) {
  int i = fgetc(stdin);
  if (i == EOF) {
    fprintf(stderr, "* Unexpected EOF\n");
    exit(1);
  }
  return i&255;
}

static int get_short(void) {
  int i1 = get_byte();
  int i0 = get_byte();
  return (i1<<8) | i0;
}

static int get_word32(void) {
  int i3, i2, i1, i0;
  i3 = get_byte();
  i2 = get_byte();
  i1 = get_byte();
  i0 = get_byte();
  return (i3 << 24) | (i2 << 16) | (i1 << 8) | (i0);
}

static char *get_impstring(void) {
  // fails if there is a \0 *inside* the string.
  static char local[257];
  int len;
  char *s = local;
  len = get_byte();
  for (int i = 0; i < len; i++) {
    int c = get_byte();
    *s++ = c;
  }
  *s++ = '\0';
  return strdup(local);
}

#ifdef NEVER
static char *get_impstring_in_C_syntax(void) {
  static char local[256*2+3];
  int i, c, len;
  char *s = local;
  len = get_byte();
  *s++ = '"';
  for (i = 0; i < len; i++) {
    c = get_byte();
    if (c == '"') {
      *s++ = '\\';
      *s++ = c;
    } else if (c == '\n') {
      *s++ = '\\';
      *s++ = 'n';
    } else *s++ = c;
  }
  *s++ = '"';
  *s++ = '\0';
  return strdup(local);
}

static char *get_terminated_string(int termin) {
  char str[256];
  int i = 0;
  str[0] = '\0';
  for (;;) {
    int c = get_byte();
    if (c == termin) break;
    if (i == 255) break;
    str[i++] = c; str[i] = '\0';
  }
  return strdup(str);
}
#endif

static char *get_alphanum(void) {
  // no terminator.
  char str[256];
  int c, i = 0;
  for (;;) {
    c = get_byte();
    if ((isalpha(c) && isupper(c)) || isdigit(c)) {
      if (i == 255) break;
      str[i++] = c; str[i] = '\0';
    } else break;
  }
  unget_byte(c);
  return strdup(str);
}

// The malloc calls in the code below could quite happily be taken sequentially
// from a single block of memory.

IMPAST *read_one_icode(void) {
  IMPAST *this = NULL;
  int opcode = get_raw_byte();

  if (opcode < 0 || opcode >= 127) {

    if (opcode != EOF) fprintf(stderr, "? icode2ast.c: OPCODE %d\n", opcode);
    return NULL;

  } else {

    // if (opcode != '\n') fprintf(stderr, "Icode: %c %s\n", opcode, icode_name[opcode]);

    switch(opcode) {

    case '\n':
      {
        return NULL; // EOF
      }

    case OP_OR: // '!'
      {
        this = malloc(sizeof(IMPAST *) + sizeof(ICODE_OR));
        this->opcode = opcode;
        return this;
      }
    case OP_JUMPIFD: // '"' cond label
      {
        this = malloc(sizeof(IMPAST *) + sizeof(ICODE_JUMPIFD));
        this->opcode = opcode;
        this->jumpifd.cond = get_byte();
        this->jumpifd.label = get_short();
        return this;
      }
    case OP_BNE: // '#'
      {
        this = malloc(sizeof(IMPAST *) + sizeof(ICODE_BNE));
        this->opcode = opcode;
        return this;
      }
    case OP_DEF: // '$' <tag> [id] <a> <b> <c>
      {
        this = malloc(sizeof(IMPAST *) + sizeof(ICODE_DEF));
        this->opcode = opcode;
        this->def.tag = get_short();
        this->def.id = get_alphanum();
        (void)get_byte();
        this->def.a = get_short();
        (void)get_byte();
        this->def.b = get_short();
        (void)get_byte();
        this->def.c = get_short();
        return this;
      }
    case OP_XOR: // '%'
      {
        this = malloc(sizeof(IMPAST *) + sizeof(ICODE_XOR));
        this->opcode = opcode;
        return this;
      }
    case OP_AND: // '&'
      {
        this = malloc(sizeof(IMPAST *) + sizeof(ICODE_AND));
        this->opcode = opcode;
        return this;
      }
    case OP_PUSHS: // '\'' "sconst"
      {
        this = malloc(sizeof(IMPAST *) + sizeof(ICODE_PUSHS));
        this->opcode = opcode;
        this->pushs.sconst = get_impstring();
        return this;
      }
    case OP_BLE:
      {
        this = malloc(sizeof(IMPAST *) + sizeof(ICODE_BLE));
        this->opcode = opcode;
        return this;
      }
    case OP_BGE:
      {
        this = malloc(sizeof(IMPAST *) + sizeof(ICODE_BGE));
        this->opcode = opcode;
        return this;
      }
    case OP_MUL: // '*'
      {
        this = malloc(sizeof(IMPAST *) + sizeof(ICODE_MUL));
        this->opcode = opcode;
        return this;
      }
    case OP_ADD: // '+'
      {
        this = malloc(sizeof(IMPAST *) + sizeof(ICODE_ADD));
        this->opcode = opcode;
        return this;
      }
    case OP_PLUS:
      {
        this = malloc(sizeof(IMPAST *) + sizeof(ICODE_PLUS));
        this->opcode = opcode;
        return this;
      }
    case OP_SUB: // '-'
      {
        this = malloc(sizeof(IMPAST *) + sizeof(ICODE_SUB));
        this->opcode = opcode;
        return this;
      }
    case OP_CONCAT: // '.'
      {
        this = malloc(sizeof(IMPAST *) + sizeof(ICODE_CONCAT));
        this->opcode = opcode;
        return this;
      }
    case OP_QUOT: // '/'
      {
        this = malloc(sizeof(IMPAST *) + sizeof(ICODE_QUOT));
        this->opcode = opcode;
        return this;
      }
    case OP_LOCATE: // ':' label
      {
        this = malloc(sizeof(IMPAST *) + sizeof(ICODE_LOCATE));
        this->opcode = opcode;
        this->locate.label = get_short();
        return this;
      }
    case OP_END: // ';'
      {
        this = malloc(sizeof(IMPAST *) + sizeof(ICODE_END));
        this->opcode = opcode;
        return this;
      }
    case OP_BLT:
      {
        this = malloc(sizeof(IMPAST *) + sizeof(ICODE_BLT));
        this->opcode = opcode;
        return this;
      }
    case OP_BEQ:
      {
        this = malloc(sizeof(IMPAST *) + sizeof(ICODE_BEQ));
        this->opcode = opcode;
        return this;
      }
    case OP_BGT:
      {
        this = malloc(sizeof(IMPAST *) + sizeof(ICODE_BGT));
        this->opcode = opcode;
        return this;
      }
    case OP_JUMPIF: // '?' cond label
      {
        this = malloc(sizeof(IMPAST *) + sizeof(ICODE_JUMPIF));
        this->opcode = opcode;
        this->jumpif.cond = get_byte();
        this->jumpif.label = get_short();
        return this;
      }
    case OP_PUSH: // '@' tag
      {
        this = malloc(sizeof(IMPAST *) + sizeof(ICODE_PUSH));
        this->opcode = opcode;
        this->push.tag = get_short();
        return this;
      }
    case OP_INIT: // 'A' short
      {
        this = malloc(sizeof(IMPAST *) + sizeof(ICODE_INIT));
        this->opcode = opcode;
        this->init.data = get_short();
        return this;
      }
    case OP_REPEAT: // 'B' label
      {
        this = malloc(sizeof(IMPAST *) + sizeof(ICODE_REPEAT));
        this->opcode = opcode;
        this->repeat.label = get_short();
        return this;
      }
    case OP_JUMPIFA: // 'C' cond label
      {
        this = malloc(sizeof(IMPAST *) + sizeof(ICODE_JUMPIFA));
        this->opcode = opcode;
        this->jumpifa.cond = get_byte();
        this->jumpifa.label = get_short();
        return this;
      }
    case OP_PUSHR: // 'D' code "num" '@' exp
      {
        int len, c, idx;
        char str[256];

        this = malloc(sizeof(IMPAST *) + sizeof(ICODE_PUSHR));
        this->opcode = opcode;
        this->pushr.code = get_byte();

        idx = 0;
        len = get_byte();
        (void)get_byte();
        for (int i = 1; i <= len; i++) {
          c = get_byte();
          if (c == '@') break;
          str[idx++] = c; str[idx] = '\0';
        }
        this->pushr.str = strdup(str);
        this->pushr.exponent = (c == '@' ? get_short() : 0);
        return this;
      }
    case OP_CALL: // 'E'
      {
        this = malloc(sizeof(IMPAST *) + sizeof(ICODE_CALL));
        this->opcode = opcode;
        return this;
      }
    case OP_GOTO: // 'F' label
      {
        this = malloc(sizeof(IMPAST *) + sizeof(ICODE_GOTO));
        this->opcode = opcode;
        this->igoto.label = get_short();
        return this;
      }
    case OP_ALIAS: // 'G' "alias"
      {
        this = malloc(sizeof(IMPAST *) + sizeof(ICODE_ALIAS));
        this->opcode = opcode;
        this->alias.alias = get_impstring();
        return this;
      }
    case OP_BEGIN: // 'H'
      {
        this = malloc(sizeof(IMPAST *) + sizeof(ICODE_BEGIN));
        this->opcode = opcode;
        return this;
      }
    case OP_SELECTINPUT2: // 'I'
      {
        this = malloc(sizeof(IMPAST *) + sizeof(ICODE_SELECTINPUT2));
        this->opcode = opcode;
        return this;
      }
    case OP_JUMP: // 'J' label
      {
        this = malloc(sizeof(IMPAST *) + sizeof(ICODE_JUMP));
        this->opcode = opcode;
        this->jump.label = get_short();
        return this;
      }
    case OP_FALSE: // 'K'
      {
        this = malloc(sizeof(IMPAST *) + sizeof(ICODE_FALSE));
        this->opcode = opcode;
        return this;
      }
    case OP_LABEL: // 'L' label
      {
        this = malloc(sizeof(IMPAST *) + sizeof(ICODE_LABEL));
        this->opcode = opcode;
        this->label.label = get_short();
        return this;
      }
    case OP_MAP: // 'M'
      {
        this = malloc(sizeof(IMPAST *) + sizeof(ICODE_MAP));
        this->opcode = opcode;
        return this;
      }
    case OP_PUSHI: // 'N' iconst
      {
        this = malloc(sizeof(IMPAST *) + sizeof(ICODE_PUSHI));
        this->opcode = opcode;
        this->pushi.word32 = get_word32();
        return this;
      }
    case OP_LINE: // 'O' lineno "filename"
      {
        this = malloc(sizeof(IMPAST *) + sizeof(ICODE_LINE));
        this->opcode = opcode;
        this->line.lineno = get_short();
        this->line.filename = get_impstring(); // if using i77 extension
        return this;
      }
    case OP_PLANT: // 'P'
      {
        this = malloc(sizeof(IMPAST *) + sizeof(ICODE_PLANT));
        this->opcode = opcode;
        return this;
      }
    case OP_DIVIDE: // 'Q'
      {
        this = malloc(sizeof(IMPAST *) + sizeof(ICODE_DIVIDE));
        this->opcode = opcode;
        return this;
      }
    case OP_RETURN: // 'R'
      {
        this = malloc(sizeof(IMPAST *) + sizeof(ICODE_RETURN));
        this->opcode = opcode;
        return this;
      }
    case OP_ASSVAL: // 'S'
      {
        this = malloc(sizeof(IMPAST *) + sizeof(ICODE_ASSVAL));
        this->opcode = opcode;
        return this;
      }
    case OP_TRUE: // 'T'
      {
        this = malloc(sizeof(IMPAST *) + sizeof(ICODE_TRUE));
        this->opcode = opcode;
        return this;
      }
    case OP_NEGATE: // 'U'
      {
        this = malloc(sizeof(IMPAST *) + sizeof(ICODE_NEGATE));
        this->opcode = opcode;
        return this;
      }
    case OP_RESULT: // 'V'
      {
        this = malloc(sizeof(IMPAST *) + sizeof(ICODE_RESULT));
        this->opcode = opcode;
        return this;
      }
    case OP_SJUMP: // 'W' label
      {
        this = malloc(sizeof(IMPAST *) + sizeof(ICODE_SJUMP));
        this->opcode = opcode;
        this->sjump.label = get_short();
        return this;
      }
    case OP_IEXP: // 'X'
      {
        this = malloc(sizeof(IMPAST *) + sizeof(ICODE_IEXP));
        this->opcode = opcode;
        return this;
      }
    case OP_DEFAULT: // 'Y' data
      {
        this = malloc(sizeof(IMPAST *) + sizeof(ICODE_DEFAULT));
        this->opcode = opcode;
        this->idefault.data = get_short();
        return this;
      }
    case OP_ASSREF: // 'Z'
      {
        this = malloc(sizeof(IMPAST *) + sizeof(ICODE_ASSREF));
        this->opcode = opcode;
        return this;
      }
    case OP_LSH: // '['
      {
        this = malloc(sizeof(IMPAST *) + sizeof(ICODE_LSH));
        this->opcode = opcode;
        return this;
      }
    case OP_NOT: // '\\'
      {
        this = malloc(sizeof(IMPAST *) + sizeof(ICODE_NOT));
        this->opcode = opcode;
        return this;
      }
    case OP_RSH: // ']'
      {
        this = malloc(sizeof(IMPAST *) + sizeof(ICODE_RSH));
        this->opcode = opcode;
        return this;
      }
    case OP_PROC: // '^' tag
      {
        this = malloc(sizeof(IMPAST *) + sizeof(ICODE_PROC));
        this->opcode = opcode;
        this->proc.tag = get_short();
        return this;
      }
    case OP_SLABEL: // '_' label
      {
        this = malloc(sizeof(IMPAST *) + sizeof(ICODE_SLABEL));
        this->opcode = opcode;
        this->slabel.label = get_short();
        return this;
      }
    case OP_DEFSW: // '`' label
      {
        this = malloc(sizeof(IMPAST *) + sizeof(ICODE_DEFSW));
        this->opcode = opcode;
        this->defsw.label = get_short();
        return this;
      }
    case OP_ACCESS: // 'a'
      {
        this = malloc(sizeof(IMPAST *) + sizeof(ICODE_ACCESS));
        this->opcode = opcode;
        return this;
      }
    case OP_BOUNDS: // 'b'
      {
        this = malloc(sizeof(IMPAST *) + sizeof(ICODE_BOUNDS));
        this->opcode = opcode;
        return this;
      }
    case OP_MCODE1: // 'c'
      {
        this = malloc(sizeof(IMPAST *) + sizeof(ICODE_MCODE1));
        this->opcode = opcode;
        // *** TO DO ***
#ifdef NEVER
        for (;;) {
          p1 = getmcstring(&i1);
          if (display) fprintf(stdout, " %s", p1);
          if (i1 == ';') break;
          p2 = getshort();
          if (display) fprintf(stdout, " tag_%s", p2);
        }
#endif
        return this;
      }
    case OP_DIM: // 'd'
      {
        /*
         Instruction:  Dimension <n><d>

         Effect:       <d> pairs of integer values on the stack are used  to
                       define the bounds of the last <n> arrays to have been
                       defined.    Code   is  generated,  if  necessary,  to
                       allocate the arrays and the definitions are  adjusted
                       to reference the appropriate storage.

         Notes:        The  pairs  of  values  are  stacked  in order of the
                       declaration, that is, first dimension first.
                       In each pair of values the  lower  bound  is  stacked
                       before the upper bound.
                       The  last  <n>  tags  must have had consecutive index
                       values.

         Errors:       1. The stack contains less than 2*<d> items.
                       2. The last <n> definitions were not all arrays.

         Example:      integerarray A, B, C(1:2, Low:4)
                       Define A......
                       Define B......
                       Define C......
                       Byte 1;     Byte 2
                       Stack Low;  Byte 4
                       Dimension 3 2
        */
        
        /*
          $ DEF    DP tag=V_0214 a/tf=1b b/format=1 c/ostate=100  type=1 (integer) form=11 (array) special=0 (none) linkage=0 (auto) spec=0 indirect=0 unass=0
                  N PUSHI  1
                  N PUSHI  2
                  d DIM    dim? 3 size? 1
                  O LINE   2138 regression-compile-tmp/allimpc1-77.imp
          $ DEF    OP tag=V_0215 a/tf=1b b/format=1 c/ostate=100  type=1 (integer) form=11 (array) special=0 (none) linkage=0 (auto) spec=0 indirect=0 unass=0
          $ DEF    OPPREC tag=V_0216 a/tf=1b b/format=1 c/ostate=100  type=1 (integer) form=11 (array) special=0 (none) linkage=0 (auto) spec=0 indirect=0 unass=0
                  N PUSHI  0
                  N PUSHI  5
                  d DIM    dim? 2 size? 1
        */
        
        this = malloc(sizeof(IMPAST *) + sizeof(ICODE_DIM));
        this->opcode = opcode;
        this->dim.dimens = get_short();
        (void)get_byte();
        this->dim.arrays = get_short();
        return this;
      }
    case OP_EVENT: // 'e' short
      {
        this = malloc(sizeof(IMPAST *) + sizeof(ICODE_EVENT));
        this->opcode = opcode;
        this->event.data = get_short();
        return this;
      }
    case OP_FOR: // 'f' label
      {
        this = malloc(sizeof(IMPAST *) + sizeof(ICODE_FOR));
        this->opcode = opcode;
        this->ifor.label = get_short();
        return this;
      }
#ifdef NEVER
      /* *** TO DO *** reusing this opcode in i77 for a different purpose */
    case OP_REDEF: // 'g' <tag> [id] <a> <b> <c> // extension. won't be in .icd file
      {
        this = malloc(sizeof(IMPAST *) + sizeof(ICODE_REDEF));
        this->opcode = opcode;
        return this;
      }
#endif
    case OP_ALTBEG: // 'h'
      {
        this = malloc(sizeof(IMPAST *) + sizeof(ICODE_ALTBEG));
        this->opcode = opcode;
        return this;
      }
    case OP_INDEX: // 'i'
      {
        this = malloc(sizeof(IMPAST *) + sizeof(ICODE_INDEX));
        this->opcode = opcode;
        return this;
      }
    case OP_JAM: // 'j'
      {
        this = malloc(sizeof(IMPAST *) + sizeof(ICODE_JAM));
        this->opcode = opcode;
        return this;
      }
    case OP_BF: // 'k' label
      {
        this = malloc(sizeof(IMPAST *) + sizeof(ICODE_BF));
        this->opcode = opcode;
        this->bf.label = get_short();
        return this;
      }
    case OP_LANG: // 'l' short
      {
        this = malloc(sizeof(IMPAST *) + sizeof(ICODE_LANG));
        this->opcode = opcode;
        this->lang.language = get_short();
        return this;
      }
    case OP_MONITOR: // 'm'
      {
        this = malloc(sizeof(IMPAST *) + sizeof(ICODE_MONITOR));
        this->opcode = opcode;
        return this;
      }
    case OP_SELECT: // 'n' tag
      {
        this = malloc(sizeof(IMPAST *) + sizeof(ICODE_SELECT));
        this->opcode = opcode;
        this->select.tag = get_short();
        return this;
      }
    case OP_ON: // 'o' mask label
      {
        this = malloc(sizeof(IMPAST *) + sizeof(ICODE_ON));
        this->opcode = opcode;
        this->on.mask = get_short();
        (void)get_byte(); // extra comma
        this->on.label = get_short();
        return this;
      }
    case OP_ASSPAR: // 'p'
      {
        this = malloc(sizeof(IMPAST *) + sizeof(ICODE_ASSPAR));
        this->opcode = opcode;
        return this;
      }
    case OP_SUBA: // 'q'
      {
        this = malloc(sizeof(IMPAST *) + sizeof(ICODE_SUBA));
        this->opcode = opcode;
        return this;
      }
    case OP_RESOLVE: // 'r' flags
      {
        this = malloc(sizeof(IMPAST *) + sizeof(ICODE_RESOLVE));
        this->opcode = opcode;
        this->resolve.flags = get_short();
        return this;
      }
    case OP_STOP: // 's'
      {
        this = malloc(sizeof(IMPAST *) + sizeof(ICODE_STOP));
        this->opcode = opcode;
        return this;
      }
    case OP_BT: // 't' label
      {
        this = malloc(sizeof(IMPAST *) + sizeof(ICODE_BT));
        this->opcode = opcode;
        this->bt.label = get_short();
        return this;
      }
    case OP_ADDA: // 'u'
      {
        this = malloc(sizeof(IMPAST *) + sizeof(ICODE_ADDA));
        this->opcode = opcode;
        return this;
      }
    case OP_MOD: // 'v'
      {
        this = malloc(sizeof(IMPAST *) + sizeof(ICODE_MOD));
        this->opcode = opcode;
        return this;
      }
    case OP_MCODE2: // 'w'
      {
        this = malloc(sizeof(IMPAST *) + sizeof(ICODE_MCODE2));
        this->opcode = opcode;
        return this;
      }
    case OP_REXP: // 'x'
      {
        this = malloc(sizeof(IMPAST *) + sizeof(ICODE_REXP));
        this->opcode = opcode;
        return this;
      }
    case OP_DIAG: // 'y' short
      {
        this = malloc(sizeof(IMPAST *) + sizeof(ICODE_DIAG));
        this->opcode = opcode;
        this->diag.data = get_short();
        return this;
      }
    case OP_CONTROL: // 'z' short
      {
        this = malloc(sizeof(IMPAST *) + sizeof(ICODE_CONTROL));
        this->opcode = opcode;
        this->control.data = get_short();
        return this;
      }
    case OP_START: // '{'
      {
        this = malloc(sizeof(IMPAST *) + sizeof(ICODE_START));
        this->opcode = opcode;
        return this;
      }
    case OP_ALT: // '|'
      {
        this = malloc(sizeof(IMPAST *) + sizeof(ICODE_ALT));
        this->opcode = opcode;
        return this;
      }
    case OP_FINISH: // '}'
      {
        this = malloc(sizeof(IMPAST *) + sizeof(ICODE_FINISH));
        this->opcode = opcode;
        return this;
      }
    case OP_ALTALT: // '~'
      {
        this = malloc(sizeof(IMPAST *) + sizeof(ICODE_ALTALT));
        this->opcode = opcode;
        this->altalt.alttype = get_byte();
        return this;
      }

    default:
      {
        fprintf(stderr, "? %c %s", opcode, icode_name[opcode]);
        return NULL;
      }
    }
  }
}

//------------------------------------------------------------------------------------------------------------

IMPAST *load_icode_file(void) {
  IMPAST *ICode, *Program = NULL, *last = NULL;
  // Read the icode file into a very basic AST
  for (;;) {
    ICode = read_one_icode();
    if (ICode == NULL) break;
    if (Program == NULL) {
      Program = ICode;
      last = Program;
    } else {
      last->next_icode = ICode;
      last = ICode;
    }
  }
  return Program;
}

//------------------------------------------------------------------------------------------------------------

void dump_ast(IMPAST *icode, FILE *out) {
  while (icode != NULL) {
    //fprintf(stderr, "Icode: %c %s\n", icode->opcode, icode_name[icode->opcode]);
    show_icode(icode, TRUE, out); fprintf(out, "\n");
    icode = icode->next_icode;
  }
}

//------------------------------------------------------------------------------------------------------------

#ifdef NEVER
static char *icode_needs_replacing[65536]; // a DEF FN without the details.  Look up the fn with get_fn and output full version with params!
int flagged_for_replacement(char *s) {
  int i;
  for (i = 0; i < 65536; i++) { // ***VERY VERY*** inefficient.  Placeholder code.
    if ((icode_needs_replacing[i] != NULL) && (strcmp(icode_needs_replacing[i], s) == 0)) return TRUE;
  }
  return FALSE;
}
#endif

#define MAX_SPEC_STACK 128
static int next_free_spec = 0;
static int specs[MAX_SPEC_STACK];

void push_spec(int spec) { specs[next_free_spec++] = spec; }
int pop_spec(void) { if (next_free_spec > 0) return specs[--next_free_spec]; else return specs[0]; }

#define MAX_FORM_STACK 128
static int next_free_form = 0;
static int forms[MAX_FORM_STACK];

void push_form(int form) { forms[next_free_form++] = form; }
int pop_form(void) { if (next_free_form > 0) return forms[--next_free_form]; else return forms[0]; }


#ifdef NEVER
#define MAX_FNS 1024
static char *saved_fn[MAX_FNS];
static int saved_icode_idx[MAX_FNS]; // MUST be initialised to all zeroes
static int next_free_saved = 0;

void save_fn(int icode_idx, char *fname) {
  if (next_free_saved == MAX_FNS) {
    fprintf(stderr, "* Must increase MAX_FNS\n");
    exit(EXIT_FAILURE);
  }
  saved_fn[next_free_saved] = strdup(fname);
  saved_icode_idx[next_free_saved] = icode_idx;
  //fprintf(stdout, "/* SAVED ICODE INDEX %d FOR %s */", icode_idx, fname);
  next_free_saved += 1;
}

int get_fn(char *fname) {
  int idx;
  for (idx = 0; idx < next_free_saved; idx++) {
    if (strcmp(saved_fn[idx], fname)==0) return saved_icode_idx[idx];
  }
  return -1;
}
#endif
