// http://www.ece.cmu.edu/~koopman/stack_compiler/stack_co.html


// Generate assembly code for a virtual stack machine.

// This assembly code assumes a macro-assembler; it is in fact fairly easy to
// write a macro-assembler which generates x86 code, treating the x86 as a stack
// machine.  And that in turn can be peephole optimised into fairly acceptable
// register-based code.  However we do not recommend that path for a real
// compiler, so we supply that as a separate program rather than integrating
// it here.  Note that this pseudo-code could alternatively be compiled to a
// byte-code and interpreted, in the style of the JVM and others.

// dump code to output file.
void stack_emit(char *label, char *opcode, char *addressing_mode, char *operand, char *comment)
{
  static char *SPACES = "                                                                                        ";
  char line[512];

  sprintf(line, "%s%s", label, SPACES); line[9] = ' '; line[10] = '\0';
  sprintf(line+10, "%s%s", opcode, SPACES);
  if (line[19] == ' ') {line[19] = '\0';sprintf(line+19, "%s%s", addressing_mode, operand);}
  strcat(line, " ");
  if (strlen(line) < 55) {strcat(line, SPACES);line[54] = ' '; line[55] = '\0';}
  strcat(line, "; "); strcat(line, comment);
  fprintf(stdout, "%s\n", line);
}

// recursive code-generation procedure.
void stack_code(int ap, int thenlab, int elselab, int donelab)
{
  char temp[128];
  char temp2[128];
  int oper;
  int count;
  int disp;
  char *opname;
  char *code;
  int this;

  if ((ap == -1) || AST[ap] == -1) return;
  while (AST[ap] == AST_REDIRECT) {
    fprintf(stderr, "! redirect from %d to %d in genstack - should have been handled earlier.\n", ap, leftchild(ap));
    ap = leftchild(ap); // hack
  }
  if ((ap == -1) || AST[ap] == -1) return;

  oper = AST[ap]; count = AST[ap+1];
  if ((oper < AST_BASE) || (oper > AST_TOP)) {
    fprintf(stderr, "! Bad oper %d at switch in genstack.c\n", oper); exit(1);
  }

  disp = op[oper-AST_BASE].Display_Children; opname = op[oper-AST_BASE].Diag_Name; code = op[oper-AST_BASE].Stack_Name;

  if (count != op[oper-AST_BASE].Children) {
    fprintf(stderr, "! Oper = %d (%s)\n", oper, opname);
    assert(count == op[oper-AST_BASE].Children);
  }

  switch (oper) {

  case AST_TYPE_Struct:
    return;

  case AST_Scope:
    push_scope(ap); // next block, block type
    stack_code(leftchild(ap), -1,-1,-1); // now compile the block under the new scope
    pop_scope(); // and return to where we were before entering the block
    return;

  case AST_SEQ:
    stack_code(leftchild(ap), -1,-1,-1);
    stack_code(rightchild(ap), -1,-1,-1);
    break;

  case AST_Declare: // decln that needs runtime action to set it up, eg dynamic array, or initialised var.
    // stack_code(leftchild(ap), -1,-1,-1);
    // rightchild is the type information    
    break;

  case AST_TAG:
    {
      if (astop(TYPEINFO(ap)) == AST_TYPE_ArrayOf) {
        stack_emit("", "PUSH", "&", c[leftchild(ap)].s, "push address of zeroth element of array");
      } else if (astop(TYPEINFO(ap)) == AST_TYPE_Struct) {
        stack_emit("", "PUSH", "&", c[leftchild(ap)].s, "push address of base of struct");
      } else if (astop(TYPEINFO(ap)) == -1) {
        stack_emit("", "PUSH", "", c[leftchild(ap)].s, "(implied external?)");
      } else {
        sprintf(temp, "[[typeinfo=%d]]", astop(TYPEINFO(ap)));
        stack_emit("", "PUSH", "", c[leftchild(ap)].s, temp);
      }
    }
    break;

  case AST_Var:
    // no longer used now that we have the tag lookup type system
    break;

  case AST_Idx: ;

    // BREAKTHOUGH!  [] and . operators should only push the address of the base object -
    // everything else must be evaluated as an *offset* to be added to that base.
    // So look at the left operand first to see what it is.  Classic example is:

    /*
          struct wow {
            int i;
            int a[4];
          };

          struct wow test[9];

          {
            test[7].a[3] = test[7].i;
          }

    */
//              166: =                        ..
//                 /   \                      ..
//                /     \                     ..
//               /       \                    ..
//        151: [ ]        \                   ..
//             / \         \                  ..
//       156: .   3 :146    . :184            ..
//           / \           / \                ..
//          /   a :172    /   \               ..
//   161: [ ]      189: [ ]    i :180         ..
//        / \           / \                   ..
// 133: test 7 :137    /   \                  ..
//               171: test  7 :175            ..

    // In order to work properly however, the [] and . operators must have LHS operands
    // whose TYPEINFO() is filled in correctly.

    // This also necessitates any AddressOf operators being propogated down the left
    // branch until it hits the base object (which it might be doing already?)

// 121: AST_Declare [99] [113] [-1]  [[typeinfo=-1]]
//  99: AST_TAG "test"  [[typeinfo=-1]]
// 113: AST_TYPE_ArrayOf [93] [108] 0 103 20  [[typeinfo=-1]]
// 108: AST_TYPE_Atom
// 199: AST_SEQ [127] [-1]  [[typeinfo=-1]]
// 127: AST_Scope [194] 219 2  [[typeinfo=-1]]
// 194: AST_SEQ [-1] [166]  [[typeinfo=-1]]
// 166: AST_AssignTo [151] [184]  [[typeinfo=36]]
// 151: AST_Idx [156] [146]  [[typeinfo=36]]
// 156: AST_Member [161] [142]  [[typeinfo=55]]
// 161: AST_Idx [133] [137]  [[typeinfo=93]]
// 133: AST_TAG "test"  [[typeinfo=113]]
// 113: AST_TYPE_ArrayOf [93] [108] 0 103 20  [[typeinfo=-1]]
// 108: AST_TYPE_Atom
// 137: AST_Const 7
// 142: AST_TAG "a"  [[typeinfo=55]]
//  55: AST_TYPE_ArrayOf [36] [50] 0 45 4  [[typeinfo=-1]]
//  36: AST_TYPE_Atom
//  50: AST_TYPE_Atom
// 146: AST_Const 3
// 184: AST_Member [189] [180]  [[typeinfo=10]]
// 189: AST_Idx [171] [175]  [[typeinfo=93]]
// 171: AST_TAG "test"  [[typeinfo=113]]
// 113: AST_TYPE_ArrayOf [93] [108] 0 103 20  [[typeinfo=-1]]
// 108: AST_TYPE_Atom
// 175: AST_Const 7
// 180: AST_TAG "i"  [[typeinfo=10]]
//  10: AST_TYPE_Atom

#ifdef OLD_AND_BROKEN
    {int objecttype = leftchild(TYPEINFO(leftchild(ap))); // array of objecttype
    stack_code(leftchild(ap), -1,-1,-1);
    // we can do index range testing here if wanted, using TYPEINFO(leftchild(ap))
    //fprintf(stdout, "Next thing before 'INDEX' is array offset - %d\n", rightchild(ap));
    stack_code(rightchild(ap), -1,-1,-1); // if it is a const, we want to push & add,
                                          // and convert earlier so that constants can be folded
    if (astop(objecttype) == AST_TYPE_Struct) {
      sprintf(temp, "(by size of struct %s)", c[leftchild(leftchild(objecttype))].s);
      sprintf(temp2, "%d", get_size(objecttype)); // replaced object_sizeof with newer more accurate get_size
    } else {
      sprintf(temp, "(by size of object type %d)", astop(objecttype));
      sprintf(temp2, "4"); // to do: array of arrays
    }
    stack_emit("", "INDEX", temp2, "", temp);
    }
#else
    {
      int objecttype = leftchild(TYPEINFO(leftchild(ap))); // array of objecttype
      stack_code(leftchild(ap), -1,-1,-1);
      // Now work out the offset and add
      stack_code(rightchild(ap), -1,-1,-1);
      sprintf(temp, "(by size of array element)");
      sprintf(temp2, "%d", 4/*TO DO*//*get_size(objecttype)*/); // ? ast_type_arrayof: child index lower upper elsize 
      stack_emit("", "INDEX", temp2, "", temp);
    }
#endif
    break;

  case AST_Member:
    // need to use offset_of(type information)
#ifdef BROKEN
    stack_code(leftchild(ap), -1,-1,-1);
    {
      int offset = -1;
      if (astop(leftchild(ap)) == AST_TAG) {
        if (TYPEINFO(leftchild(ap)) == -1) {
          fprintf(stderr, "Bad struct\n");
          exit(1);
	}
        offset = get_member_offset(TYPEINFO(leftchild(ap)), rightchild(ap));
        stack_code(mkconst(offset), -1,-1,-1);
      } else {
        stack_code(rightchild(ap), -1,-1,-1); // wrong...
      }
    }
#else
    {int offset;
      stack_code(leftchild(ap), -1,-1,-1);
      // this code fails if rightchild is an array element, eg rec.a[4].i = i
      // probably also if another struct, eg rec.subrec.i = i
      fprintf(stderr, "ap = ");;show_one_ast(ap,-1,-1);fprintf(stderr, "\n");
      fprintf(stderr, "leftchild(ap) = ");show_one_ast(leftchild(ap),-1,-1);fprintf(stderr, "\n");
      fprintf(stderr, "TYPEINFO(leftchild(ap)) = ");show_one_ast(TYPEINFO(leftchild(ap)),-1,-1);;fprintf(stderr, "\n");
      fprintf(stderr, "rightchild(ap) = ");show_one_ast(rightchild(ap),-1,-1);;fprintf(stderr, "\n");
      offset = get_member_offset(leftchild(ap), rightchild(ap));
      stack_code(mkconst(offset), -1,-1,-1);
    }
#endif
    stack_emit("", "ADD", "", "", "add offset to this member to object base address");
    break;

  case AST_Ptr: ;
    stack_code(leftchild(ap), -1,-1,-1);
    stack_emit("", "PUSHI", "", "", "dereference pointer to base of struct");
    //fprintf(stdout, "Next thing before 'ADD' is ->field offset - %d\n", rightchild(ap));
    //stack_code(rightchild(ap), -1,-1,-1); // this is a field offset too.
    {
      int offset;
      if (astop(leftchild(ap)) == AST_TAG) {
        if (TYPEINFO(leftchild(ap)) == -1) {
          fprintf(stderr, "Bad struct\n");
          exit(1);
	}
        offset = get_member_offset(TYPEINFO(leftchild(ap)), rightchild(ap));
      } else if (astop(leftchild(ap)) == AST_TYPE_PointerTo) {
        offset = get_member_offset(leftchild(leftchild(ap)), rightchild(ap));
      } else if (astop(leftchild(ap)) == AST_AddressOf) {
        offset = get_member_offset(leftchild(TYPEINFO(leftchild(leftchild(ap)))), rightchild(ap));
      } else {
	//fprintf(stderr, "astop(leftchild(%d)) = %d  [[typeinfo=%d]]\n", ap, astop(leftchild(ap)), TYPEINFO(leftchild(ap)));
        offset = get_member_offset(leftchild(ap), rightchild(ap));
      }
      // ORIG: stack_code(rightchild(ap), -1,-1,-1); // this is a field offset.  May need special handling.
      // REPLACED WITH:
      stack_code(mkconst(offset), -1,-1,-1);
      // Note: would be even better to move this into the ast and apply constant folding/prop optimizations
      // Also... we need a new construct to allow us to dump &fred+offset instead of doing an add.
    }
    stack_emit("", "ADD", "", "", "add offset to this field in the struct");
    break;

  case AST_AddressOf: // if not simple, push '&' down left branches until you hit an actual object?
    // TO DO: IndirectThrough(AddressOf(x)) == x
    ap = leftchild(ap);
    if (astop(ap)==AST_TAG) {
      stack_emit("", "PUSH", "&", c[leftchild(ap)].s, "AddressOf()");
    } else if (astop(ap)==AST_TYPE_Struct) { // TO DO: check arrays as well - and ++ is also a real problem
      stack_emit("", "PUSH", "&", c[leftchild(ap)].s, "AddressOf()");
    } else if (astop(ap)==AST_IndirectThrough) { // eg an int * parameter to a procedure
      stack_code(leftchild(ap), -1,-1,-1);
    } else if (children(ap) == 2) {
      leftchild(ap) = mkmonop(AST_AddressOf, leftchild(ap));
      stack_code(ap, -1,-1,-1);
    } else {
      fprintf(stderr, "Internal error, not sure how to handle AddressOf(@ %d:)\n",ap); exit(1);
    }
    break;

  case AST_AssignTo:
    if (astop(leftchild(ap))==AST_TAG) {
      stack_code(rightchild(ap), -1,-1,-1);
      ap = leftchild(ap); // TO DO: Not debugged yet
      if (astop(TYPEINFO(ap)) == AST_TYPE_ArrayOf) {
        stack_emit("", "POPI", "", c[leftchild(ap)].s, "store indirect");
      } else if (astop(TYPEINFO(ap)) == -1) {
        stack_emit("", "POP", "", c[leftchild(ap)].s, "(implied external?)");
      } else {
        sprintf(temp, "[[typeinfo=%d]]", astop(TYPEINFO(ap)));
        stack_emit("", "POP", "", c[leftchild(ap)].s, temp);
      }
    } else {
  case AST_AddAss:
  case AST_SubAss:
  case AST_MulAss:
  case AST_DivAss:
  case AST_ModAss:
  case AST_BitLshAss:
  case AST_BitRshAss:
  case AST_BitAndAss:
  case AST_BitOrAss:
  case AST_BitXorAss:
      stack_emit("", "", "", "", "next is addressof(LHS)");
      stack_code(mkmonop(AST_AddressOf, leftchild(ap)), -1,-1,-1);
      stack_code(rightchild(ap), -1,-1,-1);
      stack_emit("", code, "", "", "*(TOS-1) (op?)= TOS");
    }
    break;

  case AST_Const:
    // TO DO: this will change later, when we create a constant table.  (Esp for strings)
    stack_emit("", "PUSH", "#", escape(c[leftchild(ap)].s, c[leftchild(ap)].t), "AST_Const");
    break;

  case AST_CommaSEQ:
    stack_code(leftchild(ap), -1,-1,-1);
    if (rightchild(ap) != -1) {
      stack_emit("", "POP", "", "", "void"); // TO DO: this is a quick & buggy hack
      stack_code(rightchild(ap), -1,-1,-1); // keep the last value in a comma sequence
    }                              // to assign to a variable if needed
    break;

  case AST_DefProc: ;
    //   {AST_DefProc, 4, "AST_DefProc", "DefProc", "", 7}, // DefProc: scope/qual/type/ptr-seq, id, params, body
    // to do: add ,parms,rsult to PROC ? (Like at point of call)
    stack_emit("", "PROC", c[leftchild(rightchild(ap))].s, "", "AST_DefProc");
    stack_code(nthchild(ap, 2), -1,-1,-1); // COMMA-LIST OF DEFPARAMS
    stack_code(nthchild(ap, 2+1), -1,-1,-1); // maybe reset labs to all -1's?
    // TO DO: if it is a procedure, plant a 'return' after the end of the body
    /* if (fn result type == void) */ stack_emit("", "RET", "", "0", "Return by dropping through end of proc");
    // TO DO: if it was a function, plant code to generate a 'missing result from function' error message
    break;

  case AST_DefParam:
    // no code generated for DefParam; AST entry is only used by caller for type information
    break;

  case AST_ReceiveParam:
    { // param name (ident), param type, link to next ReceiveParam
      // leftchild of an identifier is the tag.
      sprintf(temp, "[[typeinfo=%d]]", astop(rightchild(ap)));
      stack_emit("", "PARAM", "", c[leftchild(leftchild(ap))].s, temp);
      if (nthchild(ap, 2) != -1) stack_code(nthchild(ap, 2), -1,-1,-1);
    }
    break;

  case AST_Call: ; // left: VAR  right: UseParam chain
    {
      int UseParam;
      int Var;
      int DefProc;
      int count;
      int results;
      char callee[128];

      assert(astop(rightchild(ap)) == AST_UseParam); // (I think a (void) param list is a UseParam -1 -1)

      Var = leftchild(ap); UseParam = rightchild(ap); count = 0;
      while (astop(UseParam) == AST_UseParam) { count += 1; UseParam = rightchild(UseParam); }

      DefProc = nthchild(ap, 2);
      assert(DefProc != -1);
      if (rightchild(leftchild(DefProc)) == 0 /*void*/) results = 0; else results = 1 /* Assume all results in a single register for now*/;
      sprintf(callee, "%s,%d,%d", c[leftchild(Var)].s, count-1, results);
      stack_emit("", "PRECALL", "", callee, "in case we need to reserve space etc");
      stack_code(rightchild(ap), -1,-1,-1); // push parameters */
      sprintf(callee, "%s,%d,%d", c[leftchild(Var)].s, count-1, results);
      stack_emit("", "CALL", "", callee, "AST_Call");
    }
    break;

  case AST_ReturnResult:
    stack_code(leftchild(ap), -1,-1,-1);
    stack_emit("", "RET", "", "1", "Return a result");
    break;

  case AST_Return:
    // TO DO: make sure a check is made once the AST is built to confirm that void procs
    // don't return results, and non-void procs do - and that they're of the right type.
    // (and appropriatelt typecast if need be)
    stack_emit("", "RET", "", "0", "Return");
    break;

  // TO DO: labels should be renamed using the context of a block to avoid clashes
  // EVEN BETTER! ... replace *all* user labels with __Lnnnnn internal labels!
  // and if we lay out the record so that the numeric label is in the same slot
  // as the destination of a continue or break, we can merge this in with code
  // to save a redundant branch from an if statement
  case AST_Label:
    stack_emit(c[leftchild(leftchild(ap))].s, "", "", "", "AST_Label");
    break;

  case AST_Goto:
    stack_emit("", "B", "", c[leftchild(leftchild(ap))].s, "AST_Goto");
    break;

  case AST_C_While:
    sprintf(temp, "__L%0d", nthchild(ap, 2+1)); // continue lab
    stack_emit(temp, "", "", "", "CONTINUE: AST_C_While");
    stack_code(leftchild(ap), nthchild(ap, 2+2),nthchild(ap, 2+0),nthchild(ap, 2+2)); // condition
    sprintf(temp, "__L%0d", nthchild(ap, 2+2)); // drop through if cond true
    stack_emit(temp, "", "", "", "LOOP: body");// start of body lab (ie code following the condition)
    stack_code(rightchild(ap), -1,-1,-1); // body
    sprintf(temp, "__L%0d", nthchild(ap, 2+1)); // continue lab
    stack_emit("", "B", "", temp, "LOOP: AST_C_While");
    sprintf(temp, "__L%0d", nthchild(ap, 2+0)); // exit lab
    stack_emit(temp, "", "", "", "EXIT: AST_C_While");
    break;

  case AST_C_DoWhile: ;
    sprintf(temp, "__L%0d", nthchild(ap, 2+2)); // branch back lab
    stack_emit(temp, "", "", "", "START: AST_C_DoWhile");
    stack_code(leftchild(ap), -1,-1,-1); // body
    sprintf(temp, "__L%0d", nthchild(ap, 2+1)); // continue lab
    stack_emit(temp, "", "", "", "CONTINUE: AST_C_DoWhile");
    stack_code(rightchild(ap), nthchild(ap, 2+2),nthchild(ap, 2+0),nthchild(ap, 2+0)); // condition
    //sprintf(temp, "__L%0d", nthchild(ap, 2+2)); // branch back lab
    //stack_emit("", "BT", "", temp, "LOOP: AST_C_While");
    sprintf(temp, "__L%0d", nthchild(ap, 2+0)); // exit lab
    stack_emit(temp, "", "", "", "EXIT: AST_C_DoWhile");
    break;

  case AST_C_ForLoop: // init-expr test-expr cont-expr body  break continue branch_back body

    stack_code(leftchild(ap), -1,-1,-1); // init expr

    sprintf(temp, "__L%0d", nthchild(ap, 2+4)); // loopback lab
    stack_emit(temp, "", "", "", "START: AST_C_ForLoop");

    stack_code(rightchild(ap), nthchild(ap, 2+5),nthchild(ap, 2+2),nthchild(ap, 2+5)); // test expr

    sprintf(temp, "__L%0d", nthchild(ap, 2+5)); // body lab
    stack_emit(temp, "", "", "", "BODY: AST_C_ForLoop");
    stack_code(nthchild(ap, 2+1), -1,-1,-1); // body

    sprintf(temp, "__L%0d", nthchild(ap, 2+3)); // continue lab
    stack_emit(temp, "", "", "", "CONTINUE: AST_C_ForLoop");

    stack_code(nthchild(ap, 2), -1,-1,-1); // cont expr
    sprintf(temp, "__L%0d", nthchild(ap, 2+4)); // loopback lab
    stack_emit("", "B", "", temp, "LOOP: AST_C_ForLoop");

    sprintf(temp, "__L%0d", nthchild(ap, 2+2)); // break lab
    stack_emit(temp, "", "", "", "EXIT: AST_C_ForLoop");
    break;

  case AST_C_Break: // also handled in AST_IFTHEN for efficiency
    sprintf(temp, "__L%0d", leftchild(ap)); stack_emit("", "B", "", temp, "AST_C_Break");
    break;

  case AST_C_Continue:
    sprintf(temp, "__L%0d", leftchild(ap)); stack_emit("", "B", "", temp, "AST_C_Continue");
    break;

  case AST_Cond: // near-identical to AST_IFTHENELSE!
    {
        int cond = leftchild(ap);
        int thenpart = rightchild(ap);
        int elsepart = nthchild(ap, 2);
	{
	int thenlab = temp_label();
	int donelab = temp_label();
	int elselab = temp_label();

	stack_code(cond, thenlab, elselab, thenlab);
        sprintf(temp, "__L%0d", thenlab); stack_emit(temp, "", "", "", "?-lab");        //put_label(thenlab);
	stack_code(thenpart, -1, -1, -1); // push the then value
        sprintf(temp, "__L%0d", donelab); stack_emit("", "B", "", temp, "");            //put_goto(donelab);
        sprintf(temp, "__L%0d", elselab); stack_emit(temp, "", "", "", ":-lab");        //put_label(elselab);
	stack_code(elsepart, -1, -1, -1);
        sprintf(temp, "__L%0d", donelab); stack_emit(temp, "", "", "", "donelab");      //put_label(donelab); 
	}
    }
    break;

  case AST_LE: ;
  case AST_GT: ;
  case AST_LT: ;
  case AST_GE: ;
  case AST_EQ: ;
  case AST_NE: ;
    {
      if ((oper == AST_NE) && ((astop(leftchild(ap)) == AST_Const) && (astop(rightchild(ap)) == AST_Const))
          && (rightchild(leftchild(ap)) != rightchild(rightchild(ap)))) {
          sprintf(temp, "__L%0d", thenlab);
          if (thenlab != donelab) {
            stack_emit("", "B", "", temp, "");        //put_goto(thenlab);
	  }
      } else if ((oper == AST_EQ) && ((astop(leftchild(ap)) == AST_Const) && (astop(rightchild(ap)) == AST_Const))
          && (rightchild(leftchild(ap)) == rightchild(rightchild(ap)))) {
          sprintf(temp, "__L%0d", thenlab);
          if (thenlab != donelab) {
            stack_emit("", "B", "", temp, "");        //put_goto(thenlab);
	  }
      } else if ((oper == AST_NE) && ((astop(leftchild(ap)) == AST_Const) && (astop(rightchild(ap)) == AST_Const))
          && (rightchild(leftchild(ap)) == rightchild(rightchild(ap)))) {
          sprintf(temp, "__L%0d", elselab);
          if (elselab != donelab) {
            stack_emit("", "B", "", temp, "");        //put_goto(elselab);
	  }
      } else if ((oper == AST_EQ) && ((astop(leftchild(ap)) == AST_Const) && (astop(rightchild(ap)) == AST_Const))
          && (rightchild(leftchild(ap)) != rightchild(rightchild(ap)))) {
          sprintf(temp, "__L%0d", elselab);
          if (elselab != donelab) {
            stack_emit("", "B", "", temp, "");        //put_goto(elselab);
	  }
      } else {
        stack_code(leftchild(ap), -1,-1,-1);
        stack_code(rightchild(ap), -1,-1,-1);
        stack_emit("", code, "", "", "");            // dump CMP<op>
        if (thenlab != donelab) {
          sprintf(temp, "__L%0d", thenlab);
          stack_emit("", "BT", "", temp, "");        //put_ifgoto(root, thenlab, TRUE);
        }
        if (elselab != donelab) {
          sprintf(temp, "__L%0d", elselab);
          stack_emit("", "BF", "", temp, "");        //put_ifgoto(root, elselab, FALSE);
        }
      }
    }
    break;

  // TO DO: dump BNOT for this, and move this to AST_UShortcutBoolNot
  case AST_UBoolNot:
    stack_code(leftchild(ap), elselab,thenlab,donelab); // reversed destinations for true/false
    return;

  case AST_BoolAnd:  // some shortcut nodes are turned into boolean nodes by optimisations
  case AST_BoolOr:   // when the sequence is short and is assigned to a variable and has no side-effects
    stack_code(leftchild(ap), -1,-1,-1);
    stack_code(rightchild(ap), -1,-1,-1);
    stack_emit("", code, "", "", opname);
    return;

  case AST_ShortcutBoolAnd:
  case AST_ShortcutBoolOr:
    {
      int truelab = thenlab;
      int falselab = elselab;
      int dropthrough = donelab;
      {
      int thenlab; // WOO HOO! big scoping rules test when we bootstrap this!
      int elselab;
      int donelab;

      int nexttestlab = temp_label();
      if ((astop(ap) == AST_BoolAnd) || (astop(ap) == AST_ShortcutBoolAnd)) {
	thenlab = nexttestlab; elselab = falselab;
      } else {
	thenlab = truelab; elselab = nexttestlab;
      }
      donelab = nexttestlab;

      stack_code(leftchild(ap), thenlab, elselab, donelab);
      /* if the first one was true, drop through to next part of && and check it too */
      sprintf(temp, "__L%0d", nexttestlab); stack_emit(temp, "", "", "", "nexttestlab");      //put_label(nexttestlab);
      stack_code(rightchild(ap), truelab, falselab, dropthrough);
      /* drop through may be to truelab; if not (because we're nested), jump to truelab */
      }
    }
    break;

  case AST_IFTHEN: ;
    {
      int cond = leftchild(ap);
      int thenpart = rightchild(ap);
      {
	int thenlab = temp_label();
	int donelab = temp_label();

        if ((astop(thenpart) == AST_C_Break) || (astop(thenpart) == AST_C_Continue)) { // TO DO: AST_Goto
          int breaklab = leftchild(rightchild(ap));
	  stack_code(cond, breaklab, donelab, donelab);
	} else {
	  stack_code(cond, thenlab, donelab, thenlab);
	  //put_label(thenlab);
          sprintf(temp, "__L%0d", thenlab); stack_emit(temp, "", "", "", "thenlab");
	  stack_code(thenpart, -1,-1,-1);
          sprintf(temp, "__L%0d", donelab); stack_emit(temp, "", "", "", "donelab");
	}
     }
    }
    break;

  case AST_IFTHENELSE: ;
    {
      int cond = leftchild(ap);
      int thenpart = rightchild(ap);
      int elsepart = nthchild(ap, 2);
      {
	int thenlab = temp_label();
	int donelab = temp_label();
	int elselab = temp_label();

	stack_code(cond, thenlab, elselab, thenlab);
        sprintf(temp, "__L%0d", thenlab); stack_emit(temp, "", "", "", "thenlab");      //put_label(thenlab);
	stack_code(thenpart, -1,-1,-1);
        sprintf(temp, "__L%0d", donelab); stack_emit("", "B", "", temp, "");            //put_goto(donelab);
        sprintf(temp, "__L%0d", elselab); stack_emit(temp, "", "", "", "elselab");      //put_label(elselab);
	stack_code(elsepart, -1,-1,-1);
        sprintf(temp, "__L%0d", donelab); stack_emit(temp, "", "", "", "donelab");      //put_label(donelab); 

     }
    }
    break;

  case AST_Switch: ;
    {  // currently implemented by a skip chain.  Decomposing into groups
       // of semi-compact jump-table entries to improve efficiency 
       // will be a good student exercise.
    int next = nthchild(ap, 2+1);
    int switch_table = temp_label();
    // leftchild = switch value, rightchild = compound statement
    // 1st extra field is for target of "break" statement,
    // 2nd extra field is link to chain of case statements,
    // 3rd extra field is default case

    // output the entry sequence/jump table
    stack_code(leftchild(ap), -1,-1,-1);
    sprintf(temp, "__L%0d", switch_table);
    stack_emit("", "SWITCH", "", temp, "special opcodes handle DUP of TOS");
    stack_emit(temp, "", "", "", "jump table start");
    // dump the block contents
    while (next != -1) {
      // 'next' is in case statement format
      stack_code(leftchild(next), -1,-1,-1); // TO DO: need a function to return the const,
                                             // then make it into a proper table.
      sprintf(temp, "__L%0d", nthchild(next,2));
      stack_emit("", "CASE", "", temp, "");
      next = rightchild(next);
    }
    next = nthchild(ap, 2+2); 

    // TO DO: Run-time error: if no "default:" was given, generate a jump to
    // an error procedure for 'missing case label'
    if ((next != -1) && (rightchild(next) != -1)) { // this check is untested
      sprintf(temp, "__L%0d", rightchild(next));
      stack_emit("", "DEFAULT", "", temp, "");
    } else {
      stack_emit("", "DEFAULT", "", "__missing_case_label__", "");
    }
    stack_code(rightchild(ap), -1,-1,-1);

    // dump a label for the 'break' statements to jump to
    sprintf(temp, "__L%0d", nthchild(ap, 2));
    stack_emit(temp, "", "", "", "breaking from the switch comes here");
    }
    break;

  case AST_Case:
    // leftchild = <n>,
    // rightchild = link to next case
    // last = internal number for label

    // dump an internal label for the jump table
    sprintf(temp, "__L%0d", nthchild(ap,2));
    stack_emit(temp, "", "", "", "Case element");
    break;

  case AST_DefaultCase:
    // leftchild = link to next case
    // rightchild = internal number for label

    // dump an internal label for the jump table
    sprintf(temp, "__L%0d", rightchild(ap));
    stack_emit(temp, "", "", "", "Default case");
    break;

  case AST_SHOWTREE:
    break;

  default:

    if ((oper < AST_BASE) || (oper > AST_TOP)) {
      fprintf(stderr, "* WARNING: genstack oper=%d branched via 'default:' - bad oper or coding error?\n",
                      oper);
    } else {
      fprintf(stderr, "* WARNING: genstack oper=%s branched via 'default:' - add a case for it.\n",
                       op[oper-AST_BASE].Diag_Name);
    }
    exit(0);

  case AST_Add: ;
  case AST_Sub: ;
  case AST_Mul: ;
  case AST_Div: ;
  case AST_Mod: ;
  case AST_Post_Inc: ;
  case AST_Pre_Inc: ;
  case AST_Post_Dec: ;
  case AST_Pre_Dec: ;
  case AST_Type: ;
  case AST_UNeg: ;
  case AST_UPos: ;
  case AST_UBitNot: ;
  case AST_BitAnd: ;
  case AST_BitOr: ;
  case AST_BitXor: ;
  case AST_BitLsh: ;
  case AST_BitRsh: ;
  case AST_UseParam: ;
  case AST_IndirectThrough: ;
  case AST_SizeOf: ;
  case AST_Cast: ;
  case AST_Sourceline: ;
  case AST_LINENO: ;
  case AST_REDIRECT: ;
  case AST_LINEAR_BLOB: ;

  // Not sure if we need the AST_TYPE_* entries here?

    {
      int child = 0;

      if ((count <= 2) && (*code != '\0')) {
        if (count >= 1) stack_code(leftchild(ap), -1,-1,-1); // monop
        if (count >= 2) stack_code(rightchild(ap), -1,-1,-1); // binop
        stack_emit("", code, "", "", opname); return;
      }

      this = 1<<(count-1);
      for (;;) {
        if (child == count) break;
        if (disp&this) stack_code(nthchild(ap, child), -1,-1,-1);
        this = this >> 1; child += 1;
      }
      if (oper != AST_UseParam) stack_emit("", "", "", "", opname);
    }
    break;
  }
  fflush(stdout);
  return;
}

void stack_data(void)
{
  // Output the string and constant table.
  // possibly also an initialised static/own table?
}

#ifdef DEBUG_DRAW_TREES
void draw_selected_trees(int p) {
  int count = 0;
  int i;
  int disp;
  int this;

  if ((p == -1) || (astop(p) == -1)) return;

  // recursively process all children, doing the folds on the way back up
  // once grandchildren constant expressions have already been folded:
  if ((astop(p) < AST_BASE) || (astop(p) > AST_TOP)) {
    fprintf(stderr, "* Internal Error: p=%d  astop(p) - %d\n", p, astop(p));
    exit(1);
  }

  disp = op[astop(p)-AST_BASE].Display_Children;
  this = 1<<(children(p)-1);
  for (i = 0; i < children(p); i++) {
    if (astop(nthchild(p, i)) == AST_SHOWTREE) {
      // "? statement" causes the tree for statement to be printed before code generation
      draw_tree(leftchild(nthchild(p, i))); nthchild(p, i) = leftchild(nthchild(p, i));
    } else if ((nthchild(p, i) != -1) && (disp&this)) {
      draw_selected_trees(nthchild(p, i));
      while (astop(nthchild(p, i)) == AST_REDIRECT) nthchild(p, i) = leftchild(nthchild(p,i));
    }
    this = this>>1;
  }
}
#endif
