#ifndef __ICODE2AST_C__
#define __ICODE2AST_C__ 1

#include "math.h" // for pow() (or maybe pow10?)
#include "icode2ast.h"
#include "icode2ast_data.h"
#include "showicode.h"
#include "stack.h"
#include "errorf.h"

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

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

// (The first implementation of an output procedure will be to generate
//  an Imp77 source, because that will be much more easily checked against
//  the Imp77 input.  Also it will be usable as the initial stage of a
//  flattener utility which may be wanted later.)

// 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 file altogether.


// Currently most declarations are generated OK, code and labels are not.
// Code generation of pass2.icd stops at the first call to a perm procedure
// in the ICode file, without any message as to why.  The code is very much
// a work-in-progress and nowhere near ready for testing by anyone else.


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

// These should be the only (and canonical) calls to read from a .icd file:

// (Unlike the draft implementation of i2c, I am trying to avoid multiple
// versions of similar procedures.)

// NO STACK MANIPULATION IS DONE DURING THIS PASS, IT IS *ONLY* FOR LOADING
// FROM FILE TO RAM.  SOME MINOR FILLING IN OF DATA THAT CAN BE STATICALLY
// DERIVED FROM THE LOADED ICODES WITH NO CONTEXT IS DONE FOR SIMPLICITY.

// astid[] and DEFTAG[] may be synonymous, as may next_free_astid and seqno!
// check, and conflate if so.

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) {
    fatalf("Unexpected EOF\n");
  }
  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);
}

static char *get_alphanum(void) {
  // no terminator.
  char str[257];
  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);
}

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

// Code to implement ICode's label handling:

/*
: LOCATE
  if (label_id[tag] == 0) {
    label_id[tag]   = get_next_label_id();
    label_id[tag+1] = get_next_label_id();
    label_id[tag+2] = get_next_label_id();
    output_label(label_id[tag]);
  } else {
    output_label(label_id[tag]);
    label_id[tag] = 0;
  }

? JUMPIF # tag   {and JUMPIFA, JUMPIFD. Maybe BF and BT.  Not sure about ON.}
  label_id[tag] = get_next_label_id();
  output_label(label_id[tag]);

F GOTO     tag
  output_label(label_id[tag]);

B REPEAT   tag
  output_label(label_id[tag]);
  label_id[tag] = 0;

f FOR      tag
  label_id[tag]   = get_next_label_id();
  label_id[tag+1] = get_next_label_id();
  label_id[tag+2] = get_next_label_id();
  output_label(label_id[tag]);


 */

#define MAX_LABEL_IDS (64*1024)
#define MAX_NESTED_BLOCKS 1024 // More than Imp ever allowed...
static int label_id[MAX_LABEL_IDS];        /* init to 0 means not used */
static int user_label_id[MAX_LABEL_IDS];   /* init to 0 means not used */
static int next_label_id = 0;           // start at L_0001
static int next_user_label_id = 0x0FFF; // start at U_1000

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

static int blocklevel = 0;

// DEFTAG is the major lookup facility that maps from a tag (basically any variable) to
// its definitive definition.
IMPAST *DEFTAG[MAX_TAGS]; // raw tag to def
IMPAST *DEFUNIQUETAG[MAX_TAGS]; // raw tag to def
static int raw_tag_to_unique_tag[MAX_TAGS];

//IMPAST *DEFSEQ[MAX_ASTS]; // FOR DEBUGGING ONLY!


static IMPAST *unique_tag_to_def[MAX_TAGS] = { NULL };
static int next_free_unique_tag = 1;

static int highest_raw_tag_seen = -1;
static int highest_raw_tag_stack[MAX_NESTED_BLOCKS];

// TO DO: perhaps the stack of tags needs to mark and release next_free_unique_tag
// rather than the raw tags as supplied in icode?

int DEFINE_UNIQUE_TAG(int raw_tag, IMPAST *def) {
  //if (raw_tag == 0) return 0;
  if (raw_tag > highest_raw_tag_seen) highest_raw_tag_seen = raw_tag; 

  unique_tag_to_def[next_free_unique_tag] = def;
  raw_tag_to_unique_tag[raw_tag] = next_free_unique_tag;
  
  next_free_unique_tag += 1; if (next_free_unique_tag >=MAX_TAGS) fatalf("corruption in icode2ast:%d\n", __LINE__);
  return next_free_unique_tag-1;
}

int LOOKUP_UNIQUE_TAG(int raw_tag) {
  if (raw_tag == 0) return 0;
  if (raw_tag < 0 || raw_tag >= MAX_TAGS) fatalf("raw_tag 0x%08x out of valid range\n", raw_tag);
  return raw_tag_to_unique_tag[raw_tag];
  //fatalf("Cannot find raw tag 0x%04x\n", raw_tag);
  //return 0;
}

void WIPE_TAG(int raw_tag) {
  //debugf("WIPE TAG DEFTAG[0x%04x]\n", raw_tag);
  DEFTAG[raw_tag] = NULL;
  raw_tag_to_unique_tag[raw_tag] = 0;
}

static void ENTER_BLOCKLEVEL(void) {
  blocklevel += 1;
  //debugf("ENTER_BLOCKLEVEL %d\n", blocklevel);
  highest_raw_tag_stack[blocklevel] = highest_raw_tag_seen;
}

static void EXIT_BLOCKLEVEL(void) {
  blocklevel -= 1;
  //debugf("STARTING EXIT_BLOCKLEVEL %d - max was 0x%04x\n", blocklevel, highest_raw_tag_seen);
  if (blocklevel < 0) {
    highest_raw_tag_seen = -1;
    return;
  }
  highest_raw_tag_seen = highest_raw_tag_stack[blocklevel]+1;  // * EXPERIMENTAL *
  for (int i = highest_raw_tag_seen /* BUG? + 1 */; i <= highest_raw_tag_stack[blocklevel+1]; i++) {
    //WIPE_TAG(i);
  }
  //debugf("FINISHED EXIT_BLOCKLEVEL %d - new max is 0x%04x\n", blocklevel, highest_raw_tag_seen);
}

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

// A nest of blocks isn't strictly required for translation to C but it's useful
// to know at the end of a block what kind of block it was.  Actually, having
// written this, I realise the best place to store this information is not here
// in the output generator but in icode2ast.c when the ICodes are read, so that
// the END Icode can contain an IMPAST * field pointing to the start of that block...

// (This code below has now been moved here out of genc.c)

// Note that I would have merged this with ENTER_BLOCKLEVEL() but ENTER_BLOCKLEVEL()
// also handles START/FINISH blocks (for parameter lists and record formats) so keeping
// a separate stack is easier to manage.

static IMPAST *BlockStack[MAX_NESTED_BLOCKS];
static int next_free_block = 0;

void PushBlock(IMPAST *block) {
  //debugf("> blocklevel %d  next_free_block %d\n", blocklevel, next_free_block);
  BlockStack[next_free_block++] = block;
  if (next_free_block >= MAX_NESTED_BLOCKS) fatalf("Too many nested %%begin/%%end blocks or %%routine/%%fn/%%map/%%predicates\n");
}

IMPAST *PopBlock(void) {
  //debugf("< blocklevel %d  next_free_block %d\n", blocklevel, next_free_block);
  next_free_block -= 1;
  if (next_free_block < 0) {
    //debugf("Excessive blocks popped!\n");
    next_free_block = 0;
    return NULL;
  }
  return BlockStack[next_free_block];
}

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

static int get_next_label_id(int tag) {

  // It took me a long time to that a JUMPIF after a GOTO to
  // the same label should have preserved that label.  This
  // was a nasty bug to fix with a one-line addition!:
  if (label_id[tag] != 0) return label_id[tag];
  
  next_label_id += 1;
  if (next_label_id >= MAX_LABEL_IDS) {
    fatalf("Ran out of label IDs!!!\n");
  }
  //debugf("get_next_label_id(tag=0x%04X) returns L_%04X\n", tag, next_label_id);
  label_id[tag] = next_label_id;
  return next_label_id;
}

static int get_next_user_label(void) {
  next_user_label_id += 1;
  if (next_user_label_id >= MAX_LABEL_IDS) {
    fatalf("Ran out of label IDs!!!\n");
  }
  //fprintf(stderr, "next user label: %08x\n", next_user_label_id);
  return next_user_label_id;
}

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

#define MAX_LEVELS 1024

char *blockname[MAX_LEVELS] = { 0 };
IMPAST *blockdef[MAX_LEVELS] = { 0 };
int unique_block_no = 0;
char current_blockname[1024] = { '\0' };
IMPAST *current_blockdef = NULL;
int IN_START_FINISH_BLOCK = 0;
//int POP_BLOCKNAME_ON_FINISH = FALSE;

char *next_blockname(int lev) {
  static char tmp[128];
  if (lev <= 0) { // really '== 0' ...
    sprintf(tmp, "main");
  } else {
    sprintf(tmp, "lev%did%d", lev, unique_block_no);
  }
  unique_block_no += 1;
  return tmp;
}

void set_blockname(IMPAST *this, char *s) {
  char *p;
  p = strrchr(s, '_');
  if (p) s = p+1;
  if (blocklevel < 0) {
    warnf("Cannot push a blockname at level %d\n", blocklevel);
    return;
  }
  blockname[blocklevel] = strdup(s);
  current_blockdef = blockdef[blocklevel] = this;  
}

void get_blockname(void) {
  static char tmp[4096];
  if (blocklevel < 0 || blocklevel > MAX_LEVELS || blockname[blocklevel] == NULL) {
    //fprintf(stderr, "Cannot pop a blockname at level %d\n", blocklevel);  // should only happen on end of file so should check for that and if not, output an error...
    *current_blockname = '\0'; current_blockdef = NULL;
    return;
  }
  *tmp = '\0';

  strcat(tmp, "{");
  for (int lev = 0; lev < blocklevel; lev++) {
    if (blockname[lev] == NULL) {
      strcat(tmp, "Null");
    } else {
      strcat(tmp, blockname[lev]);
    }
    strcat(tmp, " ");
  }
  strcat(tmp, "}");
  int len = strlen(tmp);
  if ((len >= 2) && (tmp[len-2] == ' ') && (tmp[len-1] == '}')) {
    tmp[len-2] = '}'; tmp[len-1] = ' ';
  }
  strcat(tmp, blockname[blocklevel]);
  current_blockdef = blockdef[blocklevel];

  if (strncmp(tmp, "{}", 2) == 0) {
    strncpy(current_blockname, tmp+2, 1023);
  } else {
    strncpy(current_blockname, tmp, 1023);
  }
  current_blockname[1023] = '\0';
  // fprintf(stderr, "CB:[%s]\n", current_blockname);
}

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

// This is the code to load an ICode file into the initial linear AST:

// (The malloc calls in the code below could quite happily be taken sequentially
// from a single block of memory, for efficiency.  Nothing is ever freed.)

static IMPAST *LASTDEF; // used with this->def.previous_def for assigning bounds to multiple array declarations.
                        // Frankly, a rather hacky icode definition.

IMPAST *read_one_icode(void) {   // This now also assigns blocklevels...
  IMPAST *this = NULL;
  int opcode = get_raw_byte();

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

    if (opcode != EOF) warnf("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 = new_icode(OR);
        this->or.opcode = opcode;
        this->generic.blocklevel = blocklevel;
        return this;
      }
    case OP_JUMPIFD: // '"' cond label
      {
        char tmp[32];
        this = new_icode(JUMPIFD);
        this->jumpifd.opcode = opcode;
        this->jumpifd.cond = get_byte();
        this->jumpifd.label = get_short();

        // JUMPIF # tag   {and JUMPIFA, JUMPIFD. Maybe BF and BT.  Not sure about ON.}
        get_next_label_id(this->jumpifd.label);
        sprintf(tmp, "L_%04X", label_id[this->jumpifd.label]);
        this->jumpifd.printable_label = strdup(tmp);

        this->generic.blocklevel = blocklevel;
        return this;
      }

      
    case OP_DEF: // '$' <tag> [id] <a> <b> <c>
      {
        this = new_icode(DEF);

//      DEFSEQ[this->def.seqno] = this; //       define_tag(this);
//      debugf("DEFSEQ[%05d] = %s\n", this->def.seqno, this->def.imp_id);

        this->def.opcode = opcode;
        this->def.tag = get_short(); this->def.unique_tag = DEFINE_UNIQUE_TAG(this->def.tag, this);

        this->def.imp_id = get_alphanum();
        this->def.c_id = strdup(this->def.imp_id);
        
        (void)get_byte(); // ,
        
        this->def.tf = get_short();   // a:
        this->def.type = ((this->def.tf) >> 4) & 0xF;
        this->def.form = this->def.tf & 0xF;
        
        (void)get_byte(); // ,
        
        this->def.format = get_short();   // b:
        /* handle bizarre type encoding scheme!: */
        this->def.special = SPECIAL_DEFAULT;
        if (this->def.type == T_INTEGER) {
          /*
 If T is INTEGER <b> takes the following meaning:
1, full range (depends on machine i.e. 16-bit, 32-bit or 64-bit integers)
2, range 0 .. 255 (=0..2^8-1)
3, range -32768 .. 32767 (=-2^15 .. 2^15-1) (16-bit)
4, range -2^31 .. 2^31-1 (32-bit)
5, range -2^63 .. 2^63-1 (64-bit)
           */
          if (this->def.format == 1) {
            /* default 32 bit int */;
          } else if (this->def.format == 2) {
            this->def.special = SPECIAL_BYTE_INT;
          } else if (this->def.format == 3) {
            this->def.special = SPECIAL_SHORT_INT;
          } else if (this->def.format == 4) {
            this->def.special = SPECIAL_LONG_INT;
          } else {
            fatalf("Unexpected SPECIAL_* accompanying T_INTEGER: b = 0x%04x\n", this->def.format);
          }
        } else if (this->def.type == T_REAL && this->def.format == 4) {
          this->def.special = SPECIAL_LONG_REAL;
        } else if (this->def.type == T_STRING) {
          /*
            If T is STRING <b> gives the maximum length  of
            the string.
          */
          this->def.maxstring = this->def.format;
          if (this->def.maxstring > 255) fatalf("icode2ast.c%d this->def.maxstring = %d", __LINE__, this->def.maxstring);
        } else if (this->def.type == T_RECORD) {
          // TO DO:
          /*
            If  T  is  RECORD  <b>  gives  the  tag  of the
            corresponding recordformat 
           */
          this->def.recordformatdef = DEFTAG[this->def.format];
        }

        
        (void)get_byte();
        
        this->def.ostate = get_short();  // c:
        
        // S = 1 if this is a spec, S = 0 otherwise:
        if (((this->def.ostate) &  8) != 0) this->def.is_spec = 1;
        /* An indirect object (I=1) differs from F=2 in that F=2 implies that the actual object
           created will be a pointer and will be dereferenced whenever used unless explicit
           action is taken (e.g. use of Assign-Reference).
           If I=1 a pointer will be created (usually as an integer) and will be treated as an
           integer (or address) with no automatic dereferencing taking place:
         */
        // I = 1 if the object is an indirect object, I = 0 otherwise
        if (((this->def.ostate) & 16) != 0) this->def.is_indirect = 1;
        
        // U = 1 check the object for unassigned, U = 0 otherwise
        if (((this->def.ostate) & 32) != 0) this->def.no_unass_check = 1;
        /*
          X = 0 :: automatic (stack) allocation
          X = 1 :: own
          X = 2 :: constant
          X = 3 :: external
          X = 4 :: system
          X = 5 :: dynamic
          X = 6 :: primitive
          X = 7 :: permanent
         */        
        this->def.linkage = this->def.ostate&7;

        // At some point I was under the impression that the number of dimensions of
        // an array was passed in with the OP_DEF opcode.  However I have not found anywhere
        // in the ICode documentation that confirms this, and indeed it looks like the
        // correct place that dimension information comes from is OP_DIM.  So I'll leave
        // the variable in place but not assign it here, and have OP_DIM plug in the
        // missing value later.
        this->def.dims = 0; // this->def.ostate&7; // *WRONG*

        DEFUNIQUETAG[this->def.unique_tag] = this; // not using this yet, may never do.
                                                   // - it was useful during debugging
        if (this->def.imp_id != NULL) {
          //debugf("DEFUNIQUETAG[0x%04x] = %s\n", this->def.unique_tag, this->def.imp_id);
        } else {
          fatalf("DEFUNIQUETAG[0x%04x] = NULL\n", this->def.unique_tag);
        }
          
        if (LASTDEF != NULL) {
          this->def.previous_def = LASTDEF;
        }
        LASTDEF = this;
        
        // Assign blocklevels:
        if (IS_PROCEDURE(this)) {
          set_blockname(this, this->def.imp_id); 
          get_blockname();

          if (this->def.linkage == L_PERM || this->def.linkage == L_PRIM) {
            char name[1024];
            sprintf(name, "_imp_%s", this->def.c_id);
            this->def.c_id = strdup(name);
          }
          
          if (this->def.linkage >= L_EXTERNAL) strcpy(current_blockname, this->def.imp_id); // relies on system etc being all that follows L_EXTERNAL
          this->def.imp_id = strdup(current_blockname);
          if (!IS_SPEC(this) && !IN_START_FINISH_BLOCK) {
            ENTER_BLOCKLEVEL(); // blocklevel += 1;
            PushBlock(this);
         }
        } else {
        }
        this->def.blocklevel = blocklevel;
        

        if (FORM(this) == F_LABEL) {
          char tmp[256];
          // %label labname
          user_label_id[this->def.tag] = get_next_user_label();
          sprintf(tmp, "U_%04X", user_label_id[this->def.tag]);
          this->def.printable_label =  strdup(tmp);  // getuserlab_from_tag(this->def.tag);

          // Unlike random user labels (e.g. "fred: i = 1"), where the name of
          // the label is not passed on explicitly to the icode stream, labels
          // declared with the %label command do pass through their original name.
          // (Note something similar may be needed for %switch.  Or maybe not.)
        }

        if (this->def.tag != 0x0000) {
          if (this->def.tag > MAX_TAGS) fatalf("icode2ast.c:%d DEFTAG bounds exceeded: %d\n", __LINE__, this->def.tag);
          DEFTAG[this->def.tag] = this; //       define_tag(this);
          if (this->def.imp_id != NULL) {
            //debugf("DEFTAG[0x%04x] = %s\n", this->def.tag, this->def.imp_id);
          } else {
            fatalf("DEFTAG[0x%04x] = NULL\n", this->def.tag);
          }
        } else {
          // This is a record field definition!
          //warnf("OP_DEF tag=0x0000:   this is a record field definition?\n");
          //show_icode(this, stdout); fprintf(stdout, "\n");
          //show_icode(this, stderr); fprintf(stderr, "\n");
        }
    
        return this;
      }

      
    case OP_XOR: // '%'
      {
        this = new_icode(XOR);
        this->xor.opcode = opcode;
        this->generic.blocklevel = blocklevel;
        return this;
      }
    case OP_AND: // '&'
      {
        this = new_icode(AND);
        this->and.opcode = opcode;
        this->generic.blocklevel = blocklevel;
        return this;
      }
    case OP_PUSHS: // '\'' "sconst"
      {
        this = new_icode(PUSHS);
        this->pushs.opcode = opcode;
        this->pushs.string_value = get_impstring();
        this->pushs.string_length = strlen(this->pushs.string_value);;
        this->generic.blocklevel = blocklevel;
        return this;
      }
    case OP_BLE:
      {
        this = new_icode(BLE);
        this->ble.opcode = opcode;
        this->generic.blocklevel = blocklevel;
        return this;
      }
    case OP_BGE:
      {
        this = new_icode(BGE);
        this->bge.opcode = opcode;
        this->generic.blocklevel = blocklevel;
        return this;
      }
    case OP_MUL: // '*'
      {
        this = new_icode(MUL);
        this->mul.opcode = opcode;
        this->generic.blocklevel = blocklevel;
        return this;
      }
    case OP_ADD: // '+'
      {
        this = new_icode(ADD);
        this->add.opcode = opcode;
        this->generic.blocklevel = blocklevel;
        return this;
      }
    case OP_PLUS:
      {
        this = new_icode(PLUS);
        this->plus.opcode = opcode;
        this->generic.blocklevel = blocklevel;
        return this;
      }
    case OP_SUB: // '-'
      {
        this = new_icode(SUB);
        this->sub.opcode = opcode;
        this->generic.blocklevel = blocklevel;
        return this;
      }
    case OP_CONCAT: // '.'
      {
        this = new_icode(CONCAT);
        this->concat.opcode = opcode;
        this->concat.maxstring = 255;
        this->generic.blocklevel = blocklevel;
        return this;
      }
    case OP_QUOT: // '/'
      {
        this = new_icode(QUOT);
        this->quot.opcode = opcode;
        this->generic.blocklevel = blocklevel;
        return this;
      }
    case OP_LOCATE: // ':' label
      {
        char tmp[32];
        this = new_icode(LOCATE);
        this->locate.opcode = opcode;
        this->locate.label = get_short();

        if (label_id[this->locate.label] == 0) {
          get_next_label_id(this->locate.label);
          sprintf(tmp, "L_%04X", label_id[this->locate.label]);
        } else {
          sprintf(tmp, "L_%04X", label_id[this->locate.label]);
          label_id[this->locate.label] = 0;
        }
        this->locate.printable_label = strdup(tmp);

        this->generic.blocklevel = blocklevel;
        return this;
      }
    case OP_END: // ';'
      {
        this = new_icode(END);
        this->end.opcode = opcode;

        // Assign blocklevels:
        EXIT_BLOCKLEVEL(); // blocklevel -= 1;  // Any stored values seen during processing are applied to
                          // all following icodes so the level refers to what follows
                          // the icode, hence decremented before assignment:
        get_blockname();
        this->end.imp_id = strdup(current_blockname);
        this->end.c_id = strdup(current_blockname);
        this->end.blocklevel = blocklevel;

        this->end.startblock = PopBlock();
        return this;
      }
    case OP_BLT:
      {
        this = new_icode(BLT);
        this->blt.opcode = opcode;
        this->generic.blocklevel = blocklevel;
        return this;
      }
    case OP_BEQ:
      {
        this = new_icode(BEQ);
        this->beq.opcode = opcode;
        this->generic.blocklevel = blocklevel;
        return this;
      }
    case OP_BGT:
      {
        this = new_icode(BGT);
        this->bgt.opcode = opcode;
        this->generic.blocklevel = blocklevel;
        return this;
      }
    case OP_JUMPIF: // '?' cond label
      {
        char tmp[32];
        this = new_icode(JUMPIF);
        this->jumpif.opcode = opcode;
        this->jumpif.cond = get_byte();
        this->jumpif.label = get_short();
        
        // JUMPIF # tag   {and JUMPIFA, JUMPIFD. Maybe BF and BT.  Not sure about ON.}
        get_next_label_id(this->jumpif.label);
        sprintf(tmp, "L_%04X", label_id[this->jumpif.label]);
        this->jumpif.printable_label = strdup(tmp);

        this->generic.blocklevel = blocklevel;
        return this;
      }
    case OP_PUSH: // '@' tag
      {
        this = new_icode(PUSH);
        this->push.opcode = opcode;
        this->push.tag = get_short();
        
        if (DEFTAG[this->push.tag] == NULL) {

          int check = LOOKUP_UNIQUE_TAG(this->push.tag);
          
          fatalf("Tag 0x%04x was PUSHed by ICode[%05d] but there is no DEFTAG[0x%04x] (LOOKUP_UNIQUE_TAG(0x%04x) returned 0x%04x)\n",
                 this->push.tag, this->push.seqno, this->push.tag, this->push.tag, check);
        }
        this->push.unique_tag = DEFTAG[this->push.tag]->def.unique_tag;  // * EXPERIMENTAL *
        //debugf("Tag 0x%04x was PUSHed.  This corresponds to DEFUNIQUETAG[0x%04x]\n", this->push.tag, this->push.unique_tag);
        
        this->generic.blocklevel = blocklevel;
        return this;
      }
    case OP_INIT: // 'A' short
      {
        this = new_icode(INIT);
        this->init.opcode = opcode;
        this->init.repeats = get_short();
        this->generic.blocklevel = blocklevel;
        return this;
      }
    case OP_REPEAT: // 'B' label     Jump Backward
      {
        char tmp[32];
        this = new_icode(REPEAT);
        this->repeat.opcode = opcode;
        this->repeat.label = get_short();

        sprintf(tmp, "L_%04X", label_id[this->repeat.label]);
        this->ifor.printable_forlab = strdup(tmp);
        label_id[this->repeat.label] = 0;

        this->generic.blocklevel = blocklevel;

        return this;
      }
    case OP_JUMPIFA: // 'C' cond label
      {
        char tmp[32];
        this = new_icode(JUMPIFA);
        this->jumpifa.opcode = opcode;
        this->jumpifa.cond = get_byte();
        this->jumpifa.label = get_short();

        // JUMPIF # tag   {and JUMPIFA, JUMPIFD. Maybe BF and BT.  Not sure about ON.}
        get_next_label_id(this->jumpifa.label);
        sprintf(tmp, "L_%04X", label_id[this->jumpifa.label]);
        this->jumpifa.printable_label = strdup(tmp);

        this->generic.blocklevel = blocklevel;
        return this;
      }
    case OP_PUSHR: // 'D' code "num" '@' exp
      {
        int len, c, idx;
        char *endptr;
        char str[256];

        this = new_icode(PUSHR);
        this->pushr.opcode = opcode;
        this->pushr.code = get_byte();  // I have completely forgotten what 'code' was for :-(

        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);
        this->generic.blocklevel = blocklevel;
        // get value here
        this->pushr.value = strtod(this->pushr.str, &endptr);
        if (*endptr != '\0') warnf("Bad conversion of decimal string \"%s\"\n", this->pushr.str);
        if (this->pushr.exponent != 0) {
          this->pushr.value *= pow(10.0, this->pushr.exponent);
//fprintf(stderr, "PUSHR: \"%s\" %0d -> %.16le\n", this->pushr.str, this->pushr.exponent, this->pushr.value);
        } else {
//fprintf(stderr, "PUSHR: \"%s\" 0 -> %.16lf\n", this->pushr.str, this->pushr.value);
        }
        return this;
      }
    case OP_CALL: // 'E'
      {
        this = new_icode(CALL);
        this->call.opcode = opcode;
        this->generic.blocklevel = blocklevel;
        return this;
      }
    case OP_GOTO: // 'F' label
      {
        char tmp[32];
        this = new_icode(GOTO);
        this->igoto.opcode = opcode;
        this->igoto.label = get_short();

        // GOTO  tag   {and JUMPIFA, JUMPIFD. Maybe BF and BT.  Not sure about ON.}
        get_next_label_id(this->igoto.label);
        sprintf(tmp, "L_%04X", label_id[this->igoto.label]);
        this->igoto.printable_label = strdup(tmp);

        this->generic.blocklevel = blocklevel;
        return this;
      }
    case OP_ALIAS: // 'G' "alias"
      {
        this = new_icode(ALIAS);
        this->alias.opcode = opcode;
        this->alias.alias = get_impstring();
        this->generic.blocklevel = blocklevel;
        return this;
      }
    case OP_BEGIN: // 'H'
      {
        this = new_icode(BEGIN);
        this->begin.opcode = opcode;

        // Assign blocklevels:
        set_blockname(this, next_blockname(blocklevel));
        get_blockname();
        ENTER_BLOCKLEVEL(); // blocklevel += 1;
        this->begin.imp_id = strdup(current_blockname);
        this->begin.c_id = strdup(current_blockname);
        this->begin.blocklevel = blocklevel;

        PushBlock(this);

        return this;
      }
    case OP_SELECTINPUT2: // 'I'
      {
        this = new_icode(SELECTINPUT2);
        this->selectinput2.opcode = opcode;
        this->generic.blocklevel = blocklevel;
        return this;
      }
    case OP_JUMP: // 'J' label (User Jump)
      {
        char tmp[256];
        this = new_icode(JUMP);
        this->jump.opcode = opcode;
        this->jump.label = get_short();

        if ( user_label_id[this->jump.label] == 0) {
          user_label_id[this->jump.label] = get_next_user_label();
        }
        sprintf(tmp, "U_%04X", user_label_id[this->jump.label]);
        this->jump.printable_label = strdup(tmp);   // getuserlab_from_tag(this->jump.label);
        
        this->generic.blocklevel = blocklevel;
        return this;
      }
    case OP_FALSE: // 'K'
      {
        this = new_icode(FALSE);
        this->ifalse.opcode = opcode;
        this->generic.blocklevel = blocklevel;
        return this;
      }
    case OP_LABEL: // 'L' label  (Define User Label))
      {
        char tmp[256];
        this = new_icode(LABEL);
        this->label.opcode = opcode;
        this->label.label = get_short();

        if ( user_label_id[this->label.label] == 0) {
          user_label_id[this->label.label] = get_next_user_label();
        }
        sprintf(tmp, "U_%04X", user_label_id[this->label.label]);
        this->label.printable_label = strdup(tmp);   // getuserlab_from_tag(this->jump.label);
        
        this->generic.blocklevel = blocklevel;
        return this;
      }
    case OP_MAP: // 'M'
      {
        this = new_icode(MAP);
        // TO DO: this->map.mapdef = ...  needs to know type of fn so it can pass string results properly. (which means a stack of current fn defs that will handle nested functions)
        this->map.opcode = opcode;
        this->generic.blocklevel = blocklevel;
        // Looks like I have two places where the start block was pushed :-)
        // (Could also have used get_blocknam which assigns current_blockdef)
        this->map.mapdef = PopBlock(); PushBlock(this->map.mapdef);
        return this;
      }
    case OP_PUSHI: // 'N' iconst
      {
        // NOTE: the icode format limits the max size of an integer so a %long literal
        //       constant on a 64-bit system will not work.  Long integers may require
        //       a new ICode that passes them as a string, for future portability, in
        //       the same way that reals are passed. *TO DO*
        this = new_icode(PUSHI);
        this->pushi.opcode = opcode;
        this->pushi.value = get_word32();
        this->generic.blocklevel = blocklevel;
        return this;
      }
    case OP_LINE: // 'O' lineno "filename"
      {
        this = new_icode(LINE);
        this->line.opcode = opcode;
        this->line.lineno = get_short();
        this->line.filename = get_impstring(); // if using i77 extension
        this->generic.blocklevel = blocklevel;
        return this;
      }
    case OP_PLANT: // 'P' raw-byte
      {
        this = new_icode(PLANT);
        this->plant.opcode = opcode;
        this->plant.byte = get_byte();
        this->generic.blocklevel = blocklevel;
        return this;
      }
    case OP_DIVIDE: // 'Q'
      {
        this = new_icode(DIVIDE);
        this->divide.opcode = opcode;
        this->generic.blocklevel = blocklevel;
        return this;
      }
    case OP_RETURN: // 'R'
      {
        this = new_icode(RETURN);
        this->ireturn.opcode = opcode;
        this->ireturn.startblock = PopBlock(); PushBlock(this->ireturn.startblock);
        this->generic.blocklevel = blocklevel;
        return this;
      }
    case OP_ASSVAL: // 'S'
      {
        this = new_icode(ASSVAL);
        this->assval.opcode = opcode;
        this->generic.blocklevel = blocklevel;
        return this;
      }
    case OP_TRUE: // 'T'
      {
        this = new_icode(TRUE);
        this->itrue.opcode = opcode;
        this->generic.blocklevel = blocklevel;
        return this;
      }
    case OP_NEGATE: // 'U'
      {
        this = new_icode(NEGATE);
        this->negate.opcode = opcode;
        this->generic.blocklevel = blocklevel;
        return this;
      }
    case OP_RESULT: // 'V'
      {
        this = new_icode(RESULT);
        // Looks like I have two places where the start block was pushed :-)
        // (Could also have used get_blocknam which assigns current_blockdef)
        this->result.fndef = PopBlock(); PushBlock(this->result.fndef);
        this->result.opcode = opcode;
        this->generic.blocklevel = blocklevel;
        return this;
      }
    case OP_SJUMP: // 'W' label
      {
        char tmp[32];
        this = new_icode(SJUMP);
        this->sjump.opcode = opcode;
        this->sjump.label = get_short();

        get_next_label_id(this->sjump.label);
        sprintf(tmp, "L_%04X", label_id[this->sjump.label]);
        this->sjump.printable_label = strdup(tmp);

        // not sure if switch jumps need the 'printable_label' code too?
        // (since the switch is more like an array variable than an internal or user label)
        // i.e. it uses variable descriptors rather than label tags.
        this->generic.blocklevel = blocklevel;
        return this;
      }
    case OP_IEXP: // 'X'
      {
        this = new_icode(IEXP);
        this->iexp.opcode = opcode;
        this->generic.blocklevel = blocklevel;
        return this;
      }
    case OP_DEFAULT: // 'Y' data
      {
        this = new_icode(DEFAULT);
        this->idefault.opcode = opcode;
        this->idefault.data = get_short();
        this->generic.blocklevel = blocklevel;
        return this;
      }
    case OP_ASSREF: // 'Z'
      {
        this = new_icode(ASSREF);
        this->assref.opcode = opcode;
        this->generic.blocklevel = blocklevel;
        return this;
      }
    case OP_LSH: // '['
      {
        this = new_icode(LSH);
        this->lsh.opcode = opcode;
        this->generic.blocklevel = blocklevel;
        return this;
      }
    case OP_NOT: // '\\'
      {
        this = new_icode(NOT);
        this->not.opcode = opcode;
        this->generic.blocklevel = blocklevel;
        return this;
      }
    case OP_RSH: // ']'
      {
        this = new_icode(RSH);
        this->rsh.opcode = opcode;
        this->generic.blocklevel = blocklevel;
        return this;
      }
    case OP_PROC: // '^' tag   (what does this do?)
      {
        this = new_icode(PROC);
        this->proc.opcode = opcode;
        this->proc.tag = get_short();
        this->generic.blocklevel = blocklevel;
        return this;
      }
    case OP_SLABEL: // '_' label
      {
        char tmp[32];
        this = new_icode(SLABEL);
        this->slabel.opcode = opcode;
        this->slabel.label = get_short();
        
        get_next_label_id(this->slabel.label);
        sprintf(tmp, "L_%04X", label_id[this->slabel.label]);
        this->slabel.printable_label = strdup(tmp);

        this->generic.blocklevel = blocklevel;
        return this;
      }
    case OP_DEFSW: // '`' label   // Switchname(*): in IMP, "default:" statement for a case in C.
      {
        char tmp[32];
        this = new_icode(DEFSW);
        this->defsw.opcode = opcode;
        this->defsw.label = get_short();
        
        get_next_label_id(this->defsw.label);
        sprintf(tmp, "L_%04X", label_id[this->defsw.label]);
        this->defsw.printable_label = strdup(tmp);

        // TO DO: locate the DEF and attach it to this, and attach this to the DEF's .init field
        
        this->generic.blocklevel = blocklevel;
        return this;
      }
    case OP_ACCESS: // 'a'
      {
        this = new_icode(ACCESS);
        this->access.opcode = opcode;
        this->generic.blocklevel = blocklevel;
        return this;
      }
    case OP_BOUNDSPAIR: // 'b'
      {
        this = new_icode(BOUNDSPAIR);
        this->boundspair.opcode = opcode;
        this->generic.blocklevel = blocklevel;
        return this;
      }
    case OP_MCODE1: // 'c'
      {
        this = new_icode(MCODE1);
        this->mcode1.opcode = opcode;
        // *** TO DO ***
        
#ifdef NEVER
        for (;;) {
          p1 = getmcstring(&i1);
          fprintf(stdout, " %s", p1);
          if (i1 == ';') break;
          p2 = getshort();
          fprintf(stdout, " tag_%s", p2);
        }
#endif
        
        this->generic.blocklevel = blocklevel;
        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

         NB in params: d =0 -> simple array, # 0 -> array-in-record
        */
        
        /*
          $ 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 = new_icode(DIM);
        this->dim.opcode = opcode;
        this->dim.dimens = get_short();
        (void)get_byte();
        this->dim.arrays = get_short();
        this->generic.blocklevel = blocklevel;
        return this;
      }
    case OP_EVENT: // 'e' short
      {
        this = new_icode(EVENT);
        this->event.opcode = opcode;
        this->event.event = get_short();
        this->generic.blocklevel = blocklevel;
        return this;
      }
    case OP_FOR: // 'f' label
      {
        char tmp[32];
        this = new_icode(FOR);
        this->ifor.opcode = opcode;
        this->ifor.label = get_short();

        get_next_label_id(this->ifor.label);
        get_next_label_id(this->ifor.label+1);
        get_next_label_id(this->ifor.label+2);

        sprintf(tmp, "L_%04X", label_id[this->ifor.label]);
        this->ifor.printable_forlab = strdup(tmp);

        sprintf(tmp, "L_%04X", label_id[this->ifor.label+1]);
        this->ifor.printable_exitlab = strdup(tmp);

        sprintf(tmp, "L_%04X", label_id[this->ifor.label+2]);
        this->ifor.printable_continuelab = strdup(tmp);

        this->generic.blocklevel = blocklevel;
        return this;
      }
    case OP_ALTBEG: // 'h'
      {
        this = new_icode(ALTBEG);
        this->altbeg.opcode = opcode;
        this->generic.blocklevel = blocklevel;
        return this;
      }
    case OP_INDEX: // 'i'
      {
        this = new_icode(INDEX);
        this->index.opcode = opcode;
        this->generic.blocklevel = blocklevel;
        return this;
      }
    case OP_JAM: // 'j'
      {
        this = new_icode(JAM);
        this->jam.opcode = opcode;
        this->generic.blocklevel = blocklevel;
        return this;
      }
    case OP_BF: // 'k' label
      {
        char tmp[32];
        this = new_icode(BF);
        this->bf.opcode = opcode;
        this->bf.label = get_short();

        // JUMPIF # tag   {and JUMPIFA, JUMPIFD. Maybe BF and BT.  Not sure about ON.}
        get_next_label_id(this->bf.label);
        sprintf(tmp, "L_%04X", label_id[this->bf.label]);
        this->bf.printable_label = strdup(tmp);

        this->generic.blocklevel = blocklevel;
        return this;
      }
    case OP_LANG: // 'l' short
      {
        this = new_icode(LANG);
        this->lang.opcode = opcode;
        this->lang.language = get_short();
        this->generic.blocklevel = blocklevel;
        return this;
      }
    case OP_MONITOR: // 'm'
      {
        this = new_icode(MONITOR);
        this->monitor.opcode = opcode;
        this->generic.blocklevel = blocklevel;
        return this;
      }
    case OP_SELECT: // 'n' tag
      {
        this = new_icode(SELECT);
        this->select.opcode = opcode;
        this->select.fieldtag = get_short();
        this->generic.blocklevel = blocklevel;

        //fprintf(stdout, "\n"); fprintf(stderr, "\n"); 
        //debugf("The tag in the SELECT that we are loading from ICODE is: %d, i.e. the %dth field in the recordformat of the parent.\n", this->select.tag, this->select.tag);

        return this;
      }
    case OP_ON: // 'o' mask label
      {
        char tmp[32];
        this = new_icode(ON);
        this->on.opcode = opcode;
        this->on.mask = get_short();
        (void)get_byte(); // extra comma
        this->on.label = get_short();

        get_next_label_id(this->on.label);
        sprintf(tmp, "L_%04X", label_id[this->on.label]);
        this->on.printable_label = strdup(tmp);

        this->generic.blocklevel = blocklevel;
        return this;
      }
    case OP_ASSPAR: // 'p'
      {
        this = new_icode(ASSPAR);
        this->asspar.opcode = opcode;
        this->generic.blocklevel = blocklevel;
        return this;
      }
    case OP_SUBA: // 'q'
      {
        this = new_icode(SUBA);
        this->suba.opcode = opcode;
        this->generic.blocklevel = blocklevel;
        return this;
      }
    case OP_RESOLVE: // 'r' flags
      {
        this = new_icode(RESOLVE);
        this->resolve.opcode = opcode;
        this->resolve.flags = get_short();
        if ((this->resolve.flags&4) != 0) this->resolve.conditional = 1; 
        this->generic.blocklevel = blocklevel;
        return this;
      }
    case OP_STOP: // 's'
      {
        this = new_icode(STOP);
        this->stop.opcode = opcode;
        this->generic.blocklevel = blocklevel;
        return this;
      }
    case OP_BT: // 't' label
      {
        char tmp[32];
        this = new_icode(BT);
        this->bt.opcode = opcode;
        this->bt.label = get_short();

        // JUMPIF # tag   {and JUMPIFA, JUMPIFD. Maybe BF and BT.  Not sure about ON.}
        get_next_label_id(this->bt.label);
        sprintf(tmp, "L_%04X", label_id[this->bt.label]);
        this->bt.printable_label = strdup(tmp);

        this->generic.blocklevel = blocklevel;
        return this;
      }
    case OP_ADDA: // 'u'
      {
        this = new_icode(ADDA);
        this->adda.opcode = opcode;
        this->generic.blocklevel = blocklevel;
        return this;
      }
    case OP_MOD: // 'v'
      {
        this = new_icode(MOD);
        this->mod.opcode = opcode;
        this->generic.blocklevel = blocklevel;
        return this;
      }
    case OP_MCODE2: // 'w' assembly-text ';'
      {
        this = new_icode(MCODE2);
        this->mcode2.opcode = opcode;
        // *** TO DO ***
        
#ifdef NEVER
        for (;;) {
          p1 = getmcstring(&i1);
          fprintf(stdout, " %s", p1);
          if (i1 == ';') break;
          p2 = getshort();
          fprintf(stdout, " tag_%s", p2);
        }
#endif
        
        this->generic.blocklevel = blocklevel;
        return this;
      }
    case OP_REXP: // 'x'
      {
        this = new_icode(REXP);
        this->rexp.opcode = opcode;
        this->generic.blocklevel = blocklevel;
        return this;
      }
    case OP_DIAG: // 'y' short
      {
        this = new_icode(DIAG);
        this->diag.opcode = opcode;
        this->diag.data = get_short();
        this->generic.blocklevel = blocklevel;

        DIAGNOSE = this->diag.data;
        
        return this;
      }
    case OP_CONTROL: // 'z' short
      {
        /*
              (lsb)   Bit 0  ::  Unassigned variable           (%CONTROL X'00000001')
                          1  ::  String capacity               (%CONTROL X'00000002')
                          2  ::  %for loop                     (%CONTROL X'00000004')
                          3  ::  Array bound                   (%CONTROL X'00000008')
                          4  ::  Integer overflow              (%CONTROL X'00000010')
                         28  ::  Decode wanted in listing file (%CONTROL X'10000000')
         */
        this = new_icode(CONTROL);
        this->control.opcode = opcode;
        this->control.data = get_short();
        this->generic.blocklevel = blocklevel;
        
        CONTROL = this->control.data;
        
        return this;
      }
    case OP_START: // '{'
      {
        this = new_icode(START);
        this->start.opcode = opcode;

        // Assign blocklevels:
        set_blockname(this, next_blockname(blocklevel));
        get_blockname();
        ENTER_BLOCKLEVEL(); // blocklevel += 1;  // will need to look closely at procedures/specs and order of filtering...
        this->start.imp_id = strdup(current_blockname);
        this->start.c_id = strdup(current_blockname);
        this->start.blocklevel = blocklevel;
        if (IN_START_FINISH_BLOCK) {
          fatalf("Bad assumption: It looks like we may have some nested START/FINISH blocks?\n");
        }
        IN_START_FINISH_BLOCK = 1; // These can't be nested so a simple flag ought to suffice.
        return this;
      }
    case OP_ALT: // '|'
      {
        this = new_icode(ALT);
        this->alt.opcode = opcode;
        this->generic.blocklevel = blocklevel;
        return this;
      }
    case OP_FINISH: // '}'
      {
        this = new_icode(FINISH);
        this->finish.opcode = opcode;

        // Assign blocklevels: // *** TO DO ***
        IN_START_FINISH_BLOCK = 0;
        EXIT_BLOCKLEVEL(); // blocklevel -= 1;
        get_blockname();
        this->finish.imp_id = strdup(current_blockname);
        this->finish.c_id = strdup(current_blockname);
        this->finish.blocklevel = blocklevel;
        return this;
      }
    case OP_ALTALT: // '~'
      {
        this = new_icode(ALTALT);
        this->altalt.opcode = opcode;
        this->altalt.alttype = get_byte();
        if (this->altalt.alttype == 'A') {
          this->altalt.alttype = '(';
        } else if (this->altalt.alttype == 'C') {
          this->altalt.alttype = '|';
        } else if (this->altalt.alttype == 'B') {
          this->altalt.alttype = ')';
        } else {
          // error
        }
        this->generic.blocklevel = blocklevel;
        return this;
      }

    default:
      {
        warnf("%c %s", opcode, icode_name[opcode]);
        return NULL;
      }
    }
  }
  /* NOT REACHED */
  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->generic.next_icode = ICode;
      last = ICode;
    }
  }
  return Program;
}

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

#ifdef NEVER
// Some utility code I thought I needed when I started this rewrite.  It may not
// be needed after all?

#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[0] = form;
  return;
  
  forms[next_free_form++] = form;
}

int pop_form(void) {
  return forms[0];
    
  if (next_free_form > 0)
    return forms[--next_free_form];
  else
    return forms[0];
}
#endif

#endif // !__ICODE2AST_C__
