lpc4/lib/
lpc4/lib/doc/efun/
lpc4/lib/doc/lfun/
lpc4/lib/doc/operators/
lpc4/lib/doc/simul_efuns/
lpc4/lib/doc/types/
lpc4/lib/etc/
lpc4/lib/include/
lpc4/lib/include/arpa/
lpc4/lib/obj/d/
lpc4/lib/save/
lpc4/lib/secure/
lpc4/lib/std/
lpc4/lib/std/living/
#include <setjmp.h>
#include "global.h"
#include "lang.h"
#include "interpret.h"
#include "las.h"
#include "instrs.h"
#include "array.h"
#include "exec.h"
#include "object.h"
#include "simul_efun.h"
#include "incralloc.h"
#include "stralloc.h"
#include "main.h"
#include "operators.h"
#include "dynamic_buffer.h"
#include "lex.h"
#include "mapping.h"
#include "simulate.h"
#include "save_objectII.h"

#define LASDEBUG
int lasdebug=0;

static node *eval(node *);
static int eval_low(node *n);
static node *optimize(node *n,int needlval,node *parent);
static node *call_optimizer(node *n);

static struct vector *current_switch_mapping=0;
int *break_stack=0;
static int current_break,break_stack_size;
int *continue_stack=0;
static int current_continue,continue_stack_size;

struct program fake_prog;
struct object fake_object;

struct mem_block mem_block[NUMAREAS];

extern int exact_types;
extern int num_parse_error;
extern char **local_names;
extern unsigned short *type_of_locals;
extern int current_number_of_locals;
extern int current_line;
extern char *get_type_name(int);
void nice_print_tree(node *);

#define PC mem_block[A_PROGRAM].current_size

INLINE void add_to_mem_block(int n,char *data,int size)
{
  struct mem_block *mbp = &mem_block[n];
#ifdef MALLOC_DEBUG
  check_sfltable();
#endif
  while (mbp->current_size + size > mbp->max_size) {
    mbp->max_size <<= 1;
    mbp->block = realloc((char *)mbp->block, mbp->max_size);
  }
  MEMCPY(mbp->block + mbp->current_size, data, size);
  mbp->current_size += size;
}

void ins_byte(unsigned char b,int area)
{
  add_to_mem_block(area, (char *)&b, 1);
}

/*
 * Store a 2 byte number. It is stored in such a way as to be sure
 * that correct byte order is used, regardless of machine architecture.
 * Also beware that some machines can't write a word to odd addresses.
 */
void ins_short(short l,int area)
{
  add_to_mem_block(area, (char *)&l, sizeof(short));
}

#if YYDEBUG
extern int yydebug;
#endif
void upd_short(int offset, short l)
{
#if YYDEBUG
  if(yydebug>1)
    fprintf(stderr,"Upd short %d %d\n",offset,l);
#endif
#ifdef HANDLES_UNALIGNED_MEMORY_ACCESS
  *((short *)(mem_block[A_PROGRAM].block+offset))=l;
#else
  mem_block[A_PROGRAM].block[offset + 0] = ((char *)&l)[0];
  mem_block[A_PROGRAM].block[offset + 1] = ((char *)&l)[1];
#endif
}

void upd_branch(int offset, int tmp)
{
#if YYDEBUG
  if(yydebug>1)
    fprintf(stderr,"Upd branch %d %d\n",offset,tmp);
#endif
  tmp-=offset;
#ifdef HANDLES_UNALIGNED_MEMORY_ACCESS
  *((int *)(mem_block[A_PROGRAM].block+offset))=tmp;
#else
  mem_block[A_PROGRAM].block[offset + 0] = ((char *)&tmp)[0];
  mem_block[A_PROGRAM].block[offset + 1] = ((char *)&tmp)[1];
  mem_block[A_PROGRAM].block[offset + 2] = ((char *)&tmp)[2];
  mem_block[A_PROGRAM].block[offset + 3] = ((char *)&tmp)[3];
#endif
}

short read_short(int offset)
{
#ifdef HANDLES_UNALIGNED_MEMORY_ACCESS
  return *((short *)(mem_block[A_PROGRAM].block+offset));
#else
  short l;
  ((char *)&l)[0] = mem_block[A_PROGRAM].block[offset + 0];
  ((char *)&l)[1] = mem_block[A_PROGRAM].block[offset + 1];
  return l;
#endif
}

/*
 * Store a 4 byte number. It is stored in such a way as to be sure
 * that correct byte order is used, regardless of machine architecture.
 */
void ins_long(long l,int area)
{
  add_to_mem_block(area, (char *)&l+0, sizeof(long));
}
#if YYDEBUG
extern int yydebug;
#endif

static int store_linenumbers=1;
static int last_line,last_pc;

void start_line_numbering() { last_pc=last_line=0; }

static void insert_small_number(int a,int area)
{
  if(a>-127 && a<=127)
  {
    ins_byte(a,area);
  }else if(a>=-32768 && a<32768){
    ins_byte(-127,area);
    ins_short(a,area);
  }else{
    ins_byte(-128,area);
    ins_long(a,area);
  }	
}

static void low_ins_f_byte(unsigned int b)
{
  if(store_linenumbers && last_line!=current_line)
  {
    insert_small_number(PC-last_pc,A_LINENUMBERS);
    insert_small_number(current_line-last_line,A_LINENUMBERS);
    last_line=current_line;
    last_pc=PC;
  }
#if YYDEBUG
  if(yydebug)
    fprintf(stderr,"Inserting f_byte %s (%d) at %d\n",get_instruction_name(b),b,
	    PC);
#endif
#if defined(LASDEBUG)
  if(lasdebug>1)
    if(store_linenumbers)
      fprintf(stderr,"Inserting f_byte %s (%d) at %d\n",get_instruction_name(b),b,
	      PC);
#endif
  b-=F_OFFSET;
#ifdef OPCPROF
  if(store_linenumbers) add_compiled(b);
#endif
  if(b>255)
  {
    switch(b/256)
    {
    case 1: low_ins_f_byte(F_ADD_256); break;
    case 2: low_ins_f_byte(F_ADD_512); break;
    case 3: low_ins_f_byte(F_ADD_768); break;
    case 4: low_ins_f_byte(F_ADD_1024); break;
    default:
      low_ins_f_byte(F_ADD_256X);
      ins_byte(b/256,A_PROGRAM);
    }
    b&=255;
  }
  ins_byte((unsigned char)b,A_PROGRAM);
}

static void ins_f_byte(unsigned int b)
{
  extern int T_flag;
  if(T_flag>2) low_ins_f_byte(F_WRITE_OPCODE);
  low_ins_f_byte(b);
}

static void ins_int(int i)
{
  if ( i == 0 ) {
    ins_f_byte(F_CONST0);
  }else if ( i == 1 ) {
    ins_f_byte(F_CONST1);
  } else if ( i == -1) {
    ins_f_byte(F_CONST_1);
  } else if ( i>1 && i<258) {
    ins_f_byte(F_BYTE); ins_byte(i-2,A_PROGRAM);
  } else if ( i<-1 && i>-258) {
    ins_f_byte(F_NEG_BYTE); ins_byte((1024-i-2)&0xff,A_PROGRAM);
  } else if( i>=-37768 && i<32768){
    ins_f_byte(F_SHORT); ins_short(i,A_PROGRAM);
  }else{
    ins_f_byte(F_NUMBER); ins_long(i,A_PROGRAM);
  }
}

void ins_float(float f)
{
  ins_f_byte(F_FLOAT);
  add_to_mem_block(A_PROGRAM,(char *)&f,sizeof(float));
}


/*
 * A mechanism to remember addresses on a stack. The size of the stack is
 * defined in config.h.
 */
int comp_stackp;
int comp_stack[COMPILER_STACK_SIZE];

void push_address()
{
 if (comp_stackp >= COMPILER_STACK_SIZE)
 {
   yyerror("Compiler stack overflow");
   comp_stackp++;
   return;
 }
 comp_stack[comp_stackp++] = PC;
}

void push_explicit(int address)
{
  if (comp_stackp >= COMPILER_STACK_SIZE)
  {
    yyerror("Compiler stack overflow");
    comp_stackp++;
    return;
  }
  comp_stack[comp_stackp++] = address;
}

int pop_address()
{
  if (comp_stackp == 0)
     fatal("Compiler stack underflow.\n");
  if (comp_stackp > COMPILER_STACK_SIZE)
  {
    --comp_stackp;
    return 0;
  }
  return comp_stack[--comp_stackp];
}

short store_prog_string(char *str)
{
  short i;
  char **p;

  p = (char **) mem_block[A_STRINGS].block;

  for (i=mem_block[A_STRINGS].current_size / sizeof str -1; i>=0; --i)
    if (p[i] == str)
      return i;

  str=copy_shared_string(str);
  add_to_mem_block(A_STRINGS, (char *)&str, sizeof str);
  return mem_block[A_STRINGS].current_size / sizeof str - 1;
}

int store_constant(struct svalue *foo)
{
  struct svalue *s,tmp;
  int size,e;
  s=(struct svalue *)mem_block[A_CONSTANTS].block;
  size=mem_block[A_CONSTANTS].current_size / sizeof(struct svalue);
  for(e=0;e<size;e++)
    if(is_equal(s+e,foo))
      return e;

  assign_svalue_no_free(&tmp,foo);
  add_to_mem_block(A_CONSTANTS,(char *)&tmp,sizeof(struct svalue));
  return size;
}

#ifdef LINMALLOC_DEBUG
extern void assert_malloced_memory(void *);

void assert_node(node *n)
{
  if(!n) return;
  assert_malloced_memory((char *)n);
  switch(n->token)
  {
  case F_CONSTANT:
    /* assert_svalue_malloced(&(n->u.sval)); */
    break;

  case F_STRING:
    /* assert_malloced_memory(n->u.s.a.str); */
    break;

  default:
    if(n->opt_info & OPT_A_IS_NODE)
      assert_node(n->u.s.a.node);
    if(n->opt_info & OPT_B_IS_NODE)
      assert_node(n->u.s.b.node);
  }
}
#endif



#define MAX_GLOBAL 256
#define MAX(X,Y) ((X)<(Y)?(Y):(X))
static int max_local_vital=-1;
static int max_global_vital=-1;
struct nodepair
{
  int used;
  node *assignment;
  node *index;
  struct nodepair *active;
};

static struct nodepair *active;
static struct nodepair local_vital[MAX_LOCAL];
static struct nodepair global_vital[MAX_GLOBAL];

static void reset_remember(struct nodepair *n)
{
  while(n)
  {
    struct nodepair *p;
    n->assignment=NULL;
    p=n->active;
    n->active=NULL;
    n=p;
  }
}

static void clear_remember_local()
{
  while(max_local_vital>=0)
    reset_remember(local_vital+max_local_vital--);
}

static void clear_remember_global()
{
  while(max_global_vital>=0)
    reset_remember(global_vital+max_global_vital--);
}

static void clear_remember()
{
  clear_remember_local();
  clear_remember_global();
}

struct nodepair *find_remember(node *a)
{

  switch(a->token)
  {
  case F_ASSIGN_AND_POP:
  case F_ASSIGN: return find_remember(a->u.s.b.node);

  case F_INC:
  case F_POST_INC:
  case F_INC_AND_POP:
  case F_DEC:
  case F_POST_DEC:
  case F_DEC_AND_POP:
  case F_INDEX: return find_remember(a->u.s.a.node);
  case F_LOCAL: return local_vital+a->u.s.a.number;
  case F_GLOBAL: return global_vital+a->u.s.a.number;
  default: return NULL;
  }
}

static void remember_remember(node *b,int used)
{
  node *a;
  
  a=b;
  while(1)
  {
    switch(a->token)
    {
    case F_ASSIGN_AND_POP:
    case F_ASSIGN:
      a=a->u.s.b.node;
      break;

    case F_INC:
    case F_POST_INC:
    case F_INC_AND_POP:
    case F_DEC:
    case F_POST_DEC:
    case F_DEC_AND_POP:
    case F_INDEX:
      a=a->u.s.a.node;
      break;

    case F_LOCAL:
      local_vital[a->u.s.a.number].assignment=b;
      local_vital[a->u.s.a.number].index=a;
      local_vital[a->u.s.a.number].used=used;
      local_vital[a->u.s.a.number].active=active;
      max_local_vital=MAX(max_local_vital,a->u.s.a.number);
      return;
    
    case F_GLOBAL:
      global_vital[a->u.s.a.number].assignment=b;
      global_vital[a->u.s.a.number].index=a;
      global_vital[a->u.s.a.number].used=used;
      global_vital[a->u.s.a.number].active=active;
      max_global_vital=MAX(max_global_vital,a->u.s.a.number);
      return;

    default:
      return;
    }
  }
}

void free_node(node *n)
{
  if(!n) return;
#ifdef MALLOC_DEBUG
  check_sfltable();
#endif
  switch(n->token)
  {
  case F_LOCAL:
    if(local_vital[n->u.s.a.number].index==n)
      reset_remember(local_vital+n->u.s.a.number);
    break;

  case F_GLOBAL:
    if(global_vital[n->u.s.a.number].index==n)
      reset_remember(global_vital+n->u.s.a.number);
    break;

  case F_CONSTANT:
    free_svalue(&(n->u.sval));
    break;

  case F_STRING:
    free_string(n->u.s.a.str);
    break;

  default:
    if(n->opt_info & OPT_A_IS_NODE)
      free_node(n->u.s.a.node);
    if(n->opt_info & OPT_B_IS_NODE)
      free_node(n->u.s.b.node);
  }
  free((char *)n);
}

node *mknode(short token,node *a,node *b)
{
  node *res;
  res=(node *)xalloc(sizeof(node));
  res->u.s.a.node=a;
  res->u.s.b.node=b;
  res->token=token;
  res->type=0;
  res->opt_info=OPT_A_IS_NODE | OPT_B_IS_NODE;
  res->lnum=current_line;
  return res;
}

node *mkstrnode(char *str)
{
  node *res;
  res=(node *)xalloc(sizeof(node));
  res->token=F_STRING;
  res->type=TYPE_STRING;
  res->opt_info=OPT_CONST | OPT_OPT_COMPUTED | OPT_OPTIMIZED;
#ifdef DEBUG
  if(str!=debug_findstring(str))
    fatal("Mkstrnode on nonshared string.\n");
#endif
  res->u.s.a.str=str;
  res->lnum=current_line;
  return res;
}

node *mkintnode(int nr)
{
  node *res;
  res=(node *)xalloc(sizeof(node));
  res->token=F_NUMBER;
  if(nr)
    res->type=TYPE_NUMBER;
  else
    res->type=TYPE_ANY;
  res->opt_info=OPT_CONST | OPT_OPT_COMPUTED | OPT_OPTIMIZED;
  res->u.s.a.number=nr;
  res->lnum=current_line;
  return res;
}

node *mkfloatnode(float foo)
{
  node *res;
  res=(node *)xalloc(sizeof(node));
  res->token=F_FLOAT;
  res->type=TYPE_FLOAT;
  res->opt_info=OPT_CONST | OPT_OPT_COMPUTED | OPT_OPTIMIZED;
  res->u.s.a.fnum=foo;
  res->lnum=current_line;
  return res;
}

node *mkconstfunnode(int fun)
{
  node *res;
  res=(node *)xalloc(sizeof(node));
  res->token=F_CONSTANT_FUNCTION;
  res->type=TYPE_FUNCTION;
  res->opt_info=OPT_CONST;
  res->u.s.a.number=fun;
  res->lnum=current_line;
  return res;
}

node *mkglobalnode(int var)
{
  node *res;
  res=(node *)xalloc(sizeof(node));
  res->token=F_GLOBAL;
  res->type=VARIABLE(var)->type & TYPE_MOD_MASK;
  res->opt_info=OPT_COULD_BE_CONST;
  res->u.s.a.number=var;
  res->lnum=current_line;
  return res;
}

node *mklocalnode(int var)
{
  node *res;
  res=(node *)xalloc(sizeof(node));
  res->token=F_LOCAL;
  res->type=type_of_locals[var] & TYPE_MOD_MASK;
  res->opt_info=OPT_COULD_BE_CONST;
  res->u.s.a.number=var;
  res->lnum=current_line;
  return res;
}

node *mksimulefunnode(int fun)
{
  node *res;
  res=(node *)xalloc(sizeof(node));
  res->token=F_PUSH_SIMUL_EFUN;
  res->type=TYPE_FUNCTION;
  res->opt_info=OPT_CONST;
  res->u.s.a.number=fun;
  res->lnum=current_line;
  return res;
}

node *mkcastnode(int type,node *n)
{
  node *res;
  res=(node *)xalloc(sizeof(node));
  res->token=F_CAST;
  res->type=type;
  res->opt_info=OPT_A_IS_NODE;
  res->u.s.a.node=n;
  res->u.s.b.node=NULL;
  res->lnum=current_line;
  return res;
}

node *mkefunnode(int efun,node *args)
{
  node *res;
  res=(node *)xalloc(sizeof(node));
  res->token=F_EFUN_CALL;
  res->type=instrs[efun-F_OFFSET].ret_type;
  res->opt_info=OPT_B_IS_NODE;
  if(instrs[efun-F_OFFSET].ret_type & TYPE_MOD_CONSTANT)
  {
    res->opt_info|=OPT_CONST;
  }else{
    res->opt_info|=OPT_SIDE_EFFECT;
  }
  res->u.s.a.number=efun;
  res->u.s.b.node=args;
  res->lnum=current_line;
  return res;
}

static int can_use_constants(struct svalue *s,int svalues)
{
  int e;
  for(e=0;e<svalues;e++)
  {
    switch(s[e].type)
    {
    case T_FUNCTION:
    case T_OBJECT:
      return 0;

    case T_LIST:
    case T_MAPPING:
    case T_POINTER:
    case T_ALIST_PART:
      if(!can_use_constants(s[e].u.vec->item,s[e].u.vec->size))
	return 0;
    }
  }
  return 1;
}

int node_is_eq(node *a,node *b)
{
  if(a==b) return 1;
  if(!a || !b) return 0;
  if(a->token!=b->token) return 0;
  switch(a->token)
  {
  case F_STRING:
    return a->u.s.a.str==b->u.s.a.str;

  case F_CONSTANT_FUNCTION:
  case F_PUSH_SIMUL_EFUN:
  case F_GLOBAL:
  case F_LOCAL:
  case F_NUMBER:
    return a->u.s.a.number==b->u.s.a.number;

  case F_FLOAT:
    return a->u.s.a.fnum==b->u.s.a.fnum;

  case F_CAST:
    return a->type==b->type &&
      a->opt_info==b->opt_info &&
	node_is_eq(a->u.s.a.node,b->u.s.a.node);

  case F_EFUN_CALL:
    return a->u.s.a.number==b->u.s.a.number &&
      a->type==b->type &&
      a->opt_info==b->opt_info &&
	node_is_eq(a->u.s.b.node,b->u.s.b.node);

  case F_CONSTANT:
    return is_equal(&(a->u.sval),&(b->u.sval));

  default:
    return a->type==b->type &&
      a->opt_info==b->opt_info &&
	((a->opt_info & OPT_A_IS_NODE) && node_is_eq(a->u.s.a.node,b->u.s.a.node)) &&
	  ((a->opt_info & OPT_B_IS_NODE) && node_is_eq(a->u.s.b.node,b->u.s.b.node));
  }

}

static node *can_use_allocate(struct vector *v)
{
  int e;
  node *n,*n2;
  if(!v->size) return mkintnode(0);

  if(IS_ZERO(v->item))
  {
    for(e=1;e<v->size;e++)
      if(!IS_ZERO(v->item+e))
	return NULL;
    return mkintnode(v->size);
  }else if(v->item->type==T_POINTER){
    n=can_use_allocate(v->item->u.vec);
    if(!n) return NULL;
    for(e=1;e<v->size;e++)
    {
      n2=NULL;
      if(v->item[e].type!=T_POINTER ||
	 !node_is_eq(n,n2=can_use_allocate(v->item[e].u.vec)))
      {
	free_node(n);
	free_node(n2);
	return NULL;
      }
      free_node(n2);
    }
    return mknode(F_ARG_LIST,mkintnode(v->size),n);
  }
  return NULL;
}

node *mksvaluenode(struct svalue *s)
{
  node *res;
  int e;
  switch(s->type)
  {
  default:
    fatal("Unknown type?\n");
    break;

  case T_STRING:
    return mkstrnode(copy_shared_string(strptr(s)));

  case T_INT:
    if(s->subtype)
    {
      res=(node *)xalloc(sizeof(node));
      res->token=F_CONSTANT;
      res->type=TYPE_NUMBER;
      res->opt_info=OPT_CONST;
      res->lnum=current_line;
      assign_svalue_no_free(&(res->u.sval),s);
      return res;
    }
    return mkintnode(s->u.number);

  case T_FLOAT: return mkfloatnode(s->u.fnum);

  case T_REGEXP:
    res=(node *)xalloc(sizeof(node));
    res->token=F_CONSTANT;
    res->opt_info=OPT_CONST;
    res->lnum=current_line;
    assign_svalue_no_free(&(res->u.sval),s);
    res->type=TYPE_REGULAR_EXPRESSION;
    return res;

  case T_POINTER:
    if((res=can_use_allocate(s->u.vec)))
    {
      res=mkefunnode(F_ALLOCATE,res);
      res->type=TYPE_ANY | TYPE_MOD_POINTER;
      return res;
    }
    /* FALL THROUGH */

  case T_ALIST_PART:
  case T_LIST:
  case T_MAPPING:
    if(can_use_constants(s,1))
    {
      res=(node *)xalloc(sizeof(node));
      res->token=F_CONSTANT;
      res->lnum=current_line;
      assign_svalue_no_free(&(res->u.sval),s);
      if(res->u.sval.type==T_ALIST_PART)
	res->u.sval.type=T_POINTER;
      res->opt_info=OPT_CONST;
      if(s->type==T_POINTER || s->type==T_ALIST_PART)
	res->type=TYPE_ANY | TYPE_MOD_POINTER;
      else if(s->type==T_LIST)
	res->type=TYPE_LIST;
      else
	res->type=TYPE_MAPPING;
      return res;
    }else{
      switch(s->type)
      {
      case T_POINTER:
      case T_ALIST_PART:
	if(!s->u.vec->size)
	{
	  res=mkefunnode(F_AGGREGATE,NULL);
	}else{
	  res=mksvaluenode(s->u.vec->item);
	  for(e=1;e<s->u.vec->size;e++)
	    res=mknode(F_ARG_LIST,res,mksvaluenode(s->u.vec->item+e));
	  res=mkefunnode(F_AGGREGATE,res);
	  res->type=TYPE_ANY | TYPE_MOD_POINTER;
	  return res;
	}

      case T_MAPPING:
	if(!s->u.vec->item[0].u.vec->size)
	{
	  res=mkefunnode(F_M_AGGREGATE,NULL);
	}else{
	  res=mkefunnode(F_MKMAPPING,
			 mknode(F_ARG_LIST,mksvaluenode(s->u.vec->item),
				mksvaluenode(s->u.vec->item+1)));
	  res->type=TYPE_MAPPING;
	}
	return res;

      case T_LIST:
	if(!s->u.vec->item[0].u.vec->size)
	{
	  res=mkefunnode(F_L_AGGREGATE,NULL);
	}else{
	  res=mksvaluenode(s->u.vec->item[0].u.vec->item);
	  for(e=1;e<s->u.vec->size;e++)
	    res=mknode(F_ARG_LIST,res,
		       mksvaluenode(s->u.vec->item[0].u.vec->item+e));
	  res=mkefunnode(F_L_AGGREGATE,res);
	  res->type=TYPE_LIST;
	}
	return res;
      }
    }
    return NULL;

  case T_OBJECT:
    if(s->u.ob==&fake_object)
    {
      return mkefunnode(F_THIS_OBJECT,0);
    }else{
      res=(node *)xalloc(sizeof(node));      
      res->lnum=current_line;
      res->token=F_CONSTANT;
      assign_svalue_no_free(&(res->u.sval),s);
      res->opt_info=OPT_CONST;
      res->type=TYPE_OBJECT;
      return res;
    }

  case T_FUNCTION:
    if(s->u.ob==&fake_object)
    {
      return mkconstfunnode(s->subtype);
    }else{
      int e;
      for(e=0;e<num_simul_efuns;e++)
	if(!MEMCMP((char *)s,(char *)&(simul_efuns[e].fun),sizeof(struct svalue)))
	  break;
      if(e<num_simul_efuns){
	return mksimulefunnode(e);
      }else{
	res=(node *)xalloc(sizeof(node));      
	res->lnum=current_line;
	res->token=F_CONSTANT;
	assign_svalue_no_free(&(res->u.sval),s);
	res->opt_info=OPT_CONST;
	res->type=TYPE_FUNCTION;
	return res;
      }
    }
  }
  return 0;
}


/* here comes the typechecking */
/* note that the typechecking does certain casts that won't be made */
/* if typechecking is turned off. */

static int cant_check_types;
static int typep;
static int types[256];

static void get_types(node *n)
{
  if(typep>255 || !n)
    return;

#ifdef MALLOC_DEBUG
      check_sfltable();
#endif
  switch(n->token)
  {
    case F_ARG_LIST:
      get_types(n->u.s.a.node);
      get_types(n->u.s.b.node);
      break;
      
    case F_PUSH_ARRAY:
      cant_check_types=1;
      break;
      
    default:
      if(n->type!=TYPE_VOID)
	types[typep++]=n->type;
  }
#ifdef MALLOC_DEBUG
      check_sfltable();
#endif
}

static int match_arg(int *argp,int type)
{
  for(;*argp;argp++)
    if(TYPE(type,*argp)) return 1;
  return 0;
}

static int check_efun_types(int **argp,int offset,int left,node **n,char *name,int *changed)
{
  if(!*n) return offset;
  if(offset==left) return offset;
  if((*n)->token==F_ARG_LIST)
  {
    offset=check_efun_types(argp,offset,left,&((*n)->u.s.a.node),name,changed);
    offset=check_efun_types(argp,offset,left,&((*n)->u.s.b.node),name,changed);
    return offset;
  }
  if(!match_arg(*argp,(*n)->type))
  {
    if((*n)->type==TYPE_STRING && match_arg(*argp,TYPE_OBJECT))
    {
      *n=mkcastnode(TYPE_OBJECT,*n);
      *changed=1;
    }else if((*n)->type==TYPE_STRING && match_arg(*argp,TYPE_FUNCTION))
    {
      *changed=1;
      *n=mkcastnode(TYPE_FUNCTION,*n);
      *n=optimize(*n,0,0);
    }else if((*n)->type==TYPE_STRING &&
	     match_arg(*argp,TYPE_REGULAR_EXPRESSION))
    {
      *n=mkcastnode(TYPE_REGULAR_EXPRESSION,*n);
      *n=optimize(*n,0,0);
      *changed=1;
    }else{
      char buf[200];
      sprintf(buf, "Bad argument %d type to efun %s()",offset+1,name);
      yyerror(buf);
    }
  }
  while(**argp) (*argp)++;
  (*argp)++;
  return offset+1;
}

static void check_args_for_summation(node *n)
{
  int e,type;
  char buf[200];
  type=types[0];

  if(typep<1)
  {
    sprintf(buf,"Too few arguments to sumation.");
    yyerror(buf);
  }else{
    for(e=0;e<typep;e++)
    {
      types[e]&=TYPE_MOD_MASK;
      if(types[e]==TYPE_FUNCTION || types[0]==TYPE_OBJECT)
      {
	sprintf(buf,"Bad argument %d to + (%s).",e+1,get_type_name(types[e]));
	yyerror(buf);
      }
      if(type==TYPE_ANY) continue;
      if(types[e]==TYPE_ANY) { type=TYPE_ANY; continue; }
      if(types[e]==type) continue;
      if(types[e] & TYPE_MOD_POINTER) type=TYPE_MOD_POINTER;
      if(types[e]&type&TYPE_MOD_POINTER) continue;
      switch(type)
      {
      case TYPE_STRING:
	if(types[e]==TYPE_NUMBER || types[e]==TYPE_FLOAT) continue;
	break;

      case TYPE_NUMBER:
      case TYPE_FLOAT:
	if(types[e]==TYPE_STRING)
	{
	  type=TYPE_STRING;
	  continue;
	}
	break;

      }
    }
    if(type!=TYPE_ANY)
    {
      for(e=0;e<typep;e++)
      {
	switch(type)
	{
	case TYPE_STRING:
	  if(types[e]!=TYPE_STRING &&
	     types[e]!=TYPE_NUMBER &&
	     types[e]!=TYPE_FLOAT)
	  {
	    sprintf(buf,"Bad argument %d to + (%s).",e+1,
		    get_type_name(types[e]));
	    yyerror(buf);
	  }
	  break;

	case TYPE_MOD_POINTER:
	  if(!(types[e]&TYPE_MOD_POINTER))
	  {
	    sprintf(buf,"Bad argument %d to + (%s).",e+1,
		    get_type_name(types[e]));
	    yyerror(buf);
	  }
	  break;

	case TYPE_LIST:
	case TYPE_MAPPING:
	case TYPE_NUMBER:
	case TYPE_FLOAT:
	  if(types[e]!=type)
	  {
	    sprintf(buf,"Bad argument %d to + (%s).",e+1,
		    get_type_name(types[e]));
	    yyerror(buf);
	  }
	  break;
	}
      }
    }
  }
  n->type=type;
}

static int check_types(node *n)
{
  char buf[200];
  if(!n) return 0;
  typep=0;
  cant_check_types=0;

#ifdef LINMALLOC_DEBUG
  assert_node(n);
#endif
#ifdef MALLOC_DEBUG
      check_sfltable();
#endif

  switch(n->token)
  {
  case F_INDEX:
    get_types(n->u.s.a.node);
    get_types(n->u.s.b.node);
    if(typep!=2)
    {
      sprintf(buf,"Wrong number of arguments to %s.",get_f_name(n->token));
      yyerror(buf);
    }
    if(types[0]==TYPE_STRING || (types[0]&TYPE_MOD_POINTER))
    {
      if(!TYPE(types[1],TYPE_NUMBER))
      {
	sprintf(buf,"Index not integer.");
	yyerror(buf);
      }
      if(types[0]==TYPE_STRING)
      {
	n->type=TYPE_NUMBER;
      }else{
	n->type=types[0] & ~ TYPE_MOD_POINTER;
      }
    }else if(types[0]==TYPE_OBJECT || types[0]==TYPE_FUNCTION ||
	   types[0]==TYPE_NUMBER || types[0]==TYPE_FLOAT)
    {
      sprintf(buf,"Indexing on illegal type.");
      yyerror(buf);
    }else{
      n->type=TYPE_ANY;
    }
    break;

  case F_ADD:
    get_types(n->u.s.a.node);
    get_types(n->u.s.b.node);
    check_args_for_summation(n);
    break;

  case F_SUBTRACT:
  case F_AND:
  case F_XOR:
  case F_OR:
    get_types(n->u.s.a.node);
    get_types(n->u.s.b.node);
    if(typep!=2)
    {
      sprintf(buf,"Wrong number of arguments to %s.",get_f_name(n->token));
      yyerror(buf);
    }
    if(types[0]==TYPE_FUNCTION || types[0]==TYPE_FUNCTION ||
       (n->token!=F_SUBTRACT && types[0]==TYPE_FLOAT))
    {
      sprintf(buf,"Bad argument 1 to %s.",get_f_name(n->token));
      yyerror(buf);
    }
    if(!TYPE(types[0],types[1]))
    {
      sprintf(buf,"Bad arg 2 to %s.",get_f_name(n->token));
      yyerror(buf);
    }
    n->type=types[0];
    break;

  case F_MULTIPLY:
    get_types(n->u.s.a.node);
    get_types(n->u.s.b.node);
    if(typep!=2)
    {
      sprintf(buf,"Wrong number of arguments to %s.",get_f_name(n->token));
      yyerror(buf);
    }
    if(types[0]==TYPE_FUNCTION || types[0]==TYPE_OBJECT ||
       types[0]==TYPE_LIST || types[0]==TYPE_MAPPING)
    {
      sprintf(buf,"Bad arg 1 to *.");
      yyerror(buf);
    }
    if(types[1]==TYPE_FUNCTION || types[1]==TYPE_OBJECT ||
       types[1]==TYPE_LIST || types[1]==TYPE_MAPPING)
    {
      sprintf(buf,"Bad arg 2 to *.");
      yyerror(buf);
    }
    if((types[0]&TYPE_MOD_POINTER) && types[1]==TYPE_STRING)
    {
      n->type=TYPE_STRING;
    }else if(types[0]==TYPE_NUMBER && types[1]==TYPE_NUMBER){
      n->type=TYPE_NUMBER;
    }else if(types[0]==TYPE_FLOAT && types[1]==TYPE_FLOAT){
      n->type=TYPE_FLOAT;
    }else{
      n->type=TYPE_ANY;
    }
    break;

  case F_DIVIDE:
    get_types(n->u.s.a.node);
    get_types(n->u.s.b.node);
    if(typep!=2)
    {
      sprintf(buf,"Wrong number of arguments to %s.",get_f_name(n->token));
      yyerror(buf);
    }
    if(types[0]==TYPE_FUNCTION || types[0]==TYPE_OBJECT ||
       types[0]==TYPE_LIST || types[0]==TYPE_MAPPING)
    {
      sprintf(buf,"Bad arg 1 to /.");
      yyerror(buf);
    }
    if(types[1]==TYPE_FUNCTION || types[1]==TYPE_OBJECT ||
       types[1]==TYPE_LIST || types[1]==TYPE_MAPPING)
    {
      sprintf(buf,"Bad arg 2 to /.");
      yyerror(buf);
    }
    if(types[0]==TYPE_STRING && types[1]==TYPE_STRING)
    {
      n->type=TYPE_STRING | TYPE_MOD_POINTER;
    }else if(types[0]==TYPE_NUMBER && types[1]==TYPE_NUMBER){
      n->type=TYPE_NUMBER;
    }else if(types[0]==TYPE_FLOAT && types[1]==TYPE_FLOAT){
      n->type=TYPE_FLOAT;
    }else{
      n->type=TYPE_ANY;
    }
    break;

  case F_NEGATE:
    get_types(n->u.s.a.node);
    if(typep!=1)
    {
      sprintf(buf,"Wrong number of arguments to %s.",get_f_name(n->token));
      yyerror(buf);
    }
    if(!BASIC_TYPE(types[0],TYPE_NUMBER) && !BASIC_TYPE(types[0],TYPE_FLOAT))
    {
      sprintf(buf,"Bad 1 arg to subtract.");
      yyerror(buf);
    }
    n->type=types[0];
    break;

  case F_COMPL:
    get_types(n->u.s.a.node);
    if(typep!=1)
    {
      sprintf(buf,"Wrong number of arguments to %s.",get_f_name(n->token));
      yyerror(buf);
    }
    if(!BASIC_TYPE(types[0],TYPE_NUMBER))
    {
      sprintf(buf,"Bad 1 arg to ~.");
      yyerror(buf);
    }
    n->type=types[0];
    break;

  case F_EQ:
  case F_NE:
    get_types(n->u.s.a.node);
    get_types(n->u.s.b.node);
    if(typep!=2)
    {
      sprintf(buf,"Wrong number of arguments to %s.",get_f_name(n->token));
      yyerror(buf);
    }
    n->type=TYPE_NUMBER;
    break;
      
  case F_GT:
  case F_GE:
  case F_LT:
  case F_LE:
    get_types(n->u.s.a.node);
    get_types(n->u.s.b.node);
    if(typep!=2)
    {
      sprintf(buf,"Wrong number of arguments to %s.",get_f_name(n->token));
      yyerror(buf);
    }
    if(!BASIC_TYPE(types[0],TYPE_NUMBER) && !BASIC_TYPE(types[0],TYPE_FLOAT) &&
       !BASIC_TYPE(types[1],TYPE_STRING))
    {
      sprintf(buf,"Bad arg 1 to %s.",get_f_name(n->token));
      yyerror(buf);
	
    }
    if(!BASIC_TYPE(types[0],types[1]))
    {
      sprintf(buf,"Bad arg 2 to %s.",get_f_name(n->token));
      yyerror(buf);
    }
    n->type=TYPE_NUMBER;
    break;

  case F_NOT:
    n->type=TYPE_NUMBER;
    break;

  case F_MOD:
    get_types(n->u.s.a.node);
    get_types(n->u.s.b.node);
    if(typep!=2)
    {
      sprintf(buf,"Wrong number of arguments to %s.",get_f_name(n->token));
      yyerror(buf);
    }

    if(!BASIC_TYPE(types[0],TYPE_NUMBER) && !
       BASIC_TYPE(types[0],TYPE_FLOAT))
    {
      sprintf(buf,"Bad arg 1 to %s.",get_f_name(n->token));
      yyerror(buf);
	
    }

    if(!TYPE(types[0],types[1]))
    {
      sprintf(buf,"Bad argument 2 to %s.",get_f_name(n->token));
      yyerror(buf);
    }

    n->type=types[0];
    break;

  case F_RSH:
  case F_LSH:
    get_types(n->u.s.a.node);
    get_types(n->u.s.b.node);
    if(typep!=2)
    {
      sprintf(buf,"Wrong number of arguments to %s.",get_f_name(n->token));
      yyerror(buf);
    }
    if(!BASIC_TYPE(types[0],TYPE_NUMBER))
    {
      sprintf(buf,"Bad arg 1 to %s.",get_f_name(n->token));
      yyerror(buf);
	
    }
    if(!BASIC_TYPE(types[0],TYPE_NUMBER))
    {
      sprintf(buf,"Bad arg 2 to %s.",get_f_name(n->token));
      yyerror(buf);
    }
    n->type=TYPE_NUMBER;
    break;

  case F_SSCANF:
    get_types(n->u.s.a.node);
    if(typep!=2)
    {
      sprintf(buf,"Wrong number of arguments to sscanf.");
      yyerror(buf);
    }
    if(!BASIC_TYPE(types[0],TYPE_STRING))
    {
      sprintf(buf,"Bad arg 1 to sscanf.");
      yyerror(buf);
    }
    if(!BASIC_TYPE(types[1],TYPE_STRING))
    {
      sprintf(buf,"Bad arg 2 to sscanf.");
      yyerror(buf);
    }
    n->type=TYPE_NUMBER;
    break;

  case F_SWAP:
    n->type=TYPE_VOID;
    break;
      
  case F_LAND:
  case F_LOR:
    get_types(n->u.s.a.node);
    get_types(n->u.s.b.node);
    if(typep!=2)
    {
      sprintf(buf,"Wrong number of arguments to %s.",get_f_name(n->token));
      yyerror(buf);
    }
    if(types[0]==types[1])
      n->type=types[0];
    else
      n->type=TYPE_ANY;
    break;

  case F_ASSIGN:
  case F_ASSIGN_AND_POP:
    get_types(n->u.s.b.node);
    get_types(n->u.s.a.node);
    if(!TYPE(types[0],types[1]))
      yyerror("Different types to assign.");
    if(n->token==F_ASSIGN)
    {
      n->type=types[1];
    }else{
      n->type=TYPE_VOID;
    }
    break;

  case F_INC:
  case F_DEC:
  case F_POST_INC:
  case F_POST_DEC:
    get_types(n->u.s.a.node);
    if(typep!=1)
    {
      sprintf(buf,"Wrong number of arguments to %s.",get_f_name(n->token));
      yyerror(buf);
    }
    n->type=TYPE_NUMBER;
    break;

  case F_EFUN_CALL:
  {
    int min,max,def,ret,*argp;
    extern int efun_arg_types[];

    get_types(n->u.s.b.node);
#ifdef MALLOC_DEBUG
  check_sfltable();
#endif

    if(n->u.s.a.number==F_SUM)
    {
      if(cant_check_types) break;
      check_args_for_summation(n);
      break;
    }

    min=instrs[n->u.s.a.number-F_OFFSET].min_arg;
    max=instrs[n->u.s.a.number-F_OFFSET].max_arg;
    def=instrs[n->u.s.a.number-F_OFFSET].Default;
    ret=instrs[n->u.s.a.number-F_OFFSET].ret_type;

    if(cant_check_types)
    {
      if(min==max)
	yyerror("Cannot use '@' on efun with fixed number of arguments.");
      break;
    }
      
    argp = &efun_arg_types[instrs[n->u.s.a.number-F_OFFSET].arg_index];
    ret&=TYPE_MOD_MASK;
    n->type=ret;
    if(def && typep==min-1)
    {
      node *tmp;
      if(instrs[def-F_OFFSET].efunc)
      {
	tmp=mkefunnode(def,0);
      }else{
	switch(def)
	{
	case F_CONST0:
	  tmp=mkintnode(0);
	  break;

	case F_CONST1:
	  tmp=mkintnode(1);
	  break;

	case F_CONST_1:
	  tmp=mkintnode(-1);
	  break;
	  
	default:
	  fatal("Illegal 'default' (%d) for opcode %d\n",def,n->u.s.a.number);
	  tmp=mkintnode(0); /* oh well.. */
	}
      }
      n->u.s.b.node=mknode(F_ARG_LIST,n->u.s.b.node,tmp);
      n->opt_info&=~(OPT_OPT_COMPUTED | OPT_OPTIMIZED);
      n->u.s.b.node=optimize(n->u.s.b.node,0,0);
      typep=0;
      get_types(n->u.s.b.node);
    }
    if(typep<min)
    {
      sprintf(buf, "Too few arguments to %s", get_f_name(n->u.s.a.number));
      yyerror(buf);
    }
    if(typep>max && max!=-1)
    {
      sprintf(buf, "Too many arguments to %s", get_f_name(n->u.s.a.number));
      yyerror(buf);
    }
#ifdef MALLOC_DEBUG
    check_sfltable();
#endif
    if(exact_types)
    {
      int foo=0;
      check_efun_types(&argp,0,max==-1?min:typep,&(n->u.s.b.node),get_f_name(n->u.s.a.number),&foo);
      return foo;
    }
    break;
  }

  case '?':
    get_types(n->u.s.a.node);
    get_types(n->u.s.b.node->u.s.a.node);
    get_types(n->u.s.b.node->u.s.b.node);
    if(types[0]==TYPE_VOID)
      yyerror("Unexpected void argument to if or ?>\n");
    if(typep!=3)
      n->type=TYPE_VOID;
    else if(types[1]==types[2])
      n->type=types[1];
    else
      n->type=TYPE_ANY;
    break;

  default:
    if(!n->type) n->type=TYPE_ANY;
  }
#ifdef MALLOC_DEBUG
  check_sfltable();
#endif
  return 0;
}

/* these routines operates on parsetrees and are mostly used by the
 * optimizer
 */

node *copy_node(node *n)
{
  node *b;
#ifdef MALLOC_DEBUG
  check_sfltable();
#endif
  if(!n) return n;
  switch(n->token)
  {
    case F_NUMBER:
    case F_FLOAT:
    case F_LOCAL:
    case F_GLOBAL:
    case F_CONSTANT_FUNCTION:
    case F_PUSH_SIMUL_EFUN:
      b=mkintnode(0);
      *b=*n;
      return b;

    case F_STRING:
      b=mkstrnode(copy_shared_string(n->u.s.a.str));
      break;
    case F_EFUN_CALL:
      b=mkefunnode(n->u.s.a.number,copy_node(n->u.s.b.node));
      break;
    case F_CAST:
      b=mkcastnode(n->type,copy_node(n->u.s.a.node));
      break;
    case F_CONSTANT:
      b=mksvaluenode(&(n->u.sval));
      break;

    default:
      if((n->opt_info & OPT_A_IS_NODE) && (n->opt_info & OPT_B_IS_NODE))
	b=mknode(n->token,copy_node(n->u.s.a.node),copy_node(n->u.s.b.node));
      else if(n->opt_info & OPT_A_IS_NODE)
	b=mknode(n->token,copy_node(n->u.s.a.node),n->u.s.b.node);
      else if(n->opt_info & OPT_B_IS_NODE)
	b=mknode(n->token,n->u.s.a.node,copy_node(n->u.s.b.node));
      else
	b=mknode(n->token,n->u.s.a.node,n->u.s.b.node);
  }
  b->type=n->type;
  b->lnum=n->lnum;
  b->opt_info=n->opt_info;
  return b;
}


int is_const(node *n)
{
  return (n->opt_info & OPT_CONST) &&
	 !(n->opt_info & (OPT_SIDE_EFFECT |
			OPT_CASE |
			OPT_CONT |
			OPT_BREAK |
			OPT_LOCAL_ASSIGNMENT |
			OPT_GLOBAL_ASSIGNMENT |
			OPT_RESTORE_OBJECT |
			OPT_RETURN
	   ));
         
}

/* this one supposes that the value is optimized */
int node_is_true(node *n)
{
  if(!n) return 0;
  switch(n->token)
  {
    case F_NUMBER: return n->u.s.a.number!=0;
    case F_FLOAT: /* return n->u.s.a.fnum!=0.0; /wrong */
    case F_STRING: return 1;
    case F_CONSTANT:
    {
      switch(n->u.sval.type)
      {
      case F_NUMBER: return n->u.sval.u.number!=0;
      case F_FLOAT: return n->u.sval.u.fnum!=0.0;
      default: return 1;
      }
    }
    default: return 0;
  }
}

/* this one supposes that the value is optimized */
int node_is_false(node *n)
{
  if(!n) return 0;
  switch(n->token)
  {
    case F_NUMBER: return !n->u.s.a.number;
    case F_FLOAT: return n->u.s.a.fnum==0.0;
    case F_CONSTANT:
    {
      switch(n->u.sval.type)
      {
      case F_NUMBER: return n->u.sval.u.number==0;
      case F_FLOAT: return n->u.sval.u.fnum==0.0;
      default: return 1;
      }
    }
    default: return 0;
  }
}

static node **last_cmd(node **a)
{
  node **n;
  if(!a || !*a) return (node **)NULL;
  if((*a)->token==F_CAST) return last_cmd(&(*a)->u.s.a.node);
  if((*a)->token!='{' && (*a)->token!=F_ARG_LIST) return a;
  if((*a)->u.s.b.node)
  {
    if((*a)->u.s.b.node->token!='{' && (*a)->u.s.b.node->token!=F_CAST &&
       (*a)->u.s.a.node->token!=F_ARG_LIST)
      return &((*a)->u.s.b.node);
    if((n=last_cmd(&(*a)->u.s.b.node)))
      return n;
  }
  if((*a)->u.s.a.node)
  {
    if((*a)->u.s.a.node->token!='{' && (*a)->u.s.a.node->token!=F_CAST &&
       (*a)->u.s.a.node->token!=F_ARG_LIST)
      return &((*a)->u.s.a.node);
    if((n=last_cmd(&(*a)->u.s.a.node)))
      return n;
  }
  return 0;
}

static node **low_get_arg(node **a,int *nr)
{
  node **n;
  if(a[0]->token!=F_ARG_LIST)
  {
    if(!(*nr)--)
      return a;
    else
      return NULL;
  }
  if(a[0]->u.s.a.node)
    if((n=low_get_arg(&(a[0]->u.s.a.node),nr)))
      return n;

  if(a[0]->u.s.b.node)
    if((n=low_get_arg(&(a[0]->u.s.b.node),nr)))
      return n;

  return 0;
}

static node **my_get_arg(node **a,int n) { return low_get_arg(a,&n); }
static node **first_arg(node **a) { return my_get_arg(a,0); }

void print_tree(node *foo,int needlval)
{
  if(!foo) return;
#ifdef LINMALLOC_DEBUG
  assert_node(foo);
#endif
  switch(foo->token)
  {
  case F_CONSTANT_FUNCTION:
    printf("%s",FUNC(&fake_prog,foo->u.s.a.number)->name);
    break;

  case F_LOCAL:
    if(needlval) putchar('&');
    printf("$%d",foo->u.s.a.number);
    break;

  case '?':
    printf("(");
    print_tree(foo->u.s.a.node,0);
    printf(")?(");
    print_tree(foo->u.s.b.node->u.s.a.node,0);
    printf("):(");
    print_tree(foo->u.s.b.node->u.s.b.node,0);
    printf(")");
    break;

  case F_GLOBAL:
    if(needlval) putchar('&');
    printf("%s",VARIABLE(foo->u.s.a.number)->name);
    break;

  case F_NUMBER:
    printf("%d",foo->u.s.a.number);
    break;

  case F_FLOAT:
    printf("%f",foo->u.s.a.fnum);
    break;

  case F_STRING:
    printf("\"%s\"",foo->u.s.a.str);
    break;

  case F_ASSIGN:
  case F_ASSIGN_AND_POP:
    print_tree(foo->u.s.b.node,1);
    printf("=");
    print_tree(foo->u.s.a.node,0);
    break;

  case F_CAST:
    printf("(%s){",get_type_name(foo->type));
    print_tree(foo->u.s.a.node,0);
    printf("}");
    break;

  case F_ARG_LIST:
    print_tree(foo->u.s.a.node,0);
    if(foo->u.s.a.node && foo->u.s.b.node) putchar(',');
    print_tree(foo->u.s.b.node,needlval);
    return;

  case F_LVALUE_LIST:
    print_tree(foo->u.s.a.node,1);
    if(foo->u.s.a.node && foo->u.s.b.node) putchar(',');
    print_tree(foo->u.s.b.node,1);
    return;

  case '{':
    print_tree(foo->u.s.a.node,0);
    if(foo->u.s.a.node) printf(";\n ");
    print_tree(foo->u.s.b.node,needlval);
    return;

  case F_EFUN_CALL:
    printf("efun::%s(",get_f_name(foo->u.s.a.number));
    print_tree(foo->u.s.b.node,0);
    printf(")");
    return;

  default:
    if(!(foo->opt_info & OPT_A_IS_NODE) && !(foo->opt_info & OPT_B_IS_NODE))
    {
      printf("%s",get_f_name(foo->token));
      return;
    }
    if(foo->token<256)
    {
      printf("%c(",foo->token);
    }else{
      printf("%s(",get_f_name(foo->token));
    }
    if(foo->opt_info & OPT_A_IS_NODE) print_tree(foo->u.s.a.node,0);
    if((foo->opt_info & OPT_A_IS_NODE) && (foo->opt_info & OPT_B_IS_NODE)
       && foo->u.s.a.node && foo->u.s.b.node)
      putchar(',');
    if(foo->opt_info & OPT_B_IS_NODE) print_tree(foo->u.s.b.node,0);
    printf(")");
    return;
  }
}

void nice_print_tree(node *n)
{
  print_tree(n,0);
  printf("\n");
  fflush(stdout);
}

static int depend_p3(node *a,node *b)
{
  if(!a || is_const(a)) return 0;
  if(!(a->opt_info & OPT_COULD_BE_CONST)) return 2;
  if(a->token==F_GLOBAL)
    if(b->opt_info & OPT_RESTORE_OBJECT) return 2;

  if((a->opt_info & OPT_A_IS_NODE) && depend_p3(a->u.s.a.node,b))
    return 1;
  if((a->opt_info & OPT_B_IS_NODE) && depend_p3(a->u.s.b.node,b))
    return 1;
  return 0;
}


#if 1
struct used_vars
{
  char locals[MAX_LOCAL];
  char globals[MAX_GLOBAL];
};

#define VAR_BLOCKED 0
#define VAR_UNUSED 1
#define VAR_USED 3
#define VAR_SET 4
static void do_and_vars(struct used_vars *a,struct used_vars *b)
{
  int e;
  for(e=0;e<MAX_LOCAL;e++) a->locals[e]|=b->locals[e];
  for(e=0;e<MAX_GLOBAL;e++) a->globals[e]|=b->globals[e];
  free((char *)b);
}

static struct used_vars *copy_vars(struct used_vars *a)
{
  struct used_vars *ret;
  ret=(struct used_vars *)xalloc(sizeof(struct used_vars));
  MEMCPY((char *)ret,(char *)a,sizeof(struct used_vars));
  return ret;
}

static int find_used_variables(node *n,struct used_vars *p,int noblock)
{
  struct used_vars *a;

  if(!n) return 0;
  switch(n->token)
  {
  case F_LOCAL:
    if(n->opt_info & OPT_LVALUE_OVERWRITE)
    {
      if(p->locals[n->u.s.a.number]==VAR_UNUSED && !noblock)
	p->locals[n->u.s.a.number]=VAR_BLOCKED;
    }else{
      if(p->locals[n->u.s.a.number]==VAR_UNUSED)
	p->locals[n->u.s.a.number]=VAR_USED;
    }
    break;

  case F_GLOBAL:
    if(n->opt_info & OPT_LVALUE_OVERWRITE)
    {
      if(p->globals[n->u.s.a.number]==VAR_UNUSED && !noblock)
	p->globals[n->u.s.a.number]=VAR_BLOCKED;
    }else{
      if(p->globals[n->u.s.a.number]==VAR_UNUSED)
	p->globals[n->u.s.a.number]=VAR_USED;
    }
    break;

  case '?':
    find_used_variables(n->u.s.a.node,p,noblock);
    a=copy_vars(p);
    find_used_variables(n->u.s.b.node->u.s.a.node,a,noblock);
    find_used_variables(n->u.s.b.node->u.s.b.node,p,noblock);
    do_and_vars(p,a);
    break;

  case F_INC_NEQ_LOOP:
  case F_DEC_NEQ_LOOP:
  case F_INC_LOOP:
  case F_DEC_LOOP:
  case F_FOREACH:
  case F_FOR:
    find_used_variables(n->u.s.a.node,p,noblock);
    a=copy_vars(p);
    find_used_variables(n->u.s.b.node,a,noblock);
    do_and_vars(p,a);
    break;

  case F_SWITCH:
    find_used_variables(n->u.s.a.node,p,noblock);
    a=copy_vars(p);
    find_used_variables(n->u.s.b.node,a,1);
    do_and_vars(p,a);
    break;

  case F_DO:
    a=copy_vars(p);
    find_used_variables(n->u.s.a.node,a,noblock);
    do_and_vars(p,a);
    find_used_variables(n->u.s.b.node,p,noblock);
    break;

  case F_BRANCH:
    find_used_variables(n->u.s.a.node,p,noblock);
    find_used_variables(n->u.s.b.node,p,noblock);
    break;

  default:
    if(n->opt_info & OPT_A_IS_NODE)
      find_used_variables(n->u.s.a.node,p,noblock);
    if(n->opt_info & OPT_B_IS_NODE)
      find_used_variables(n->u.s.b.node,p,noblock);
  }
  return 0;
}

/* no subtility needed */
static void find_written_vars(node *n,struct used_vars *p)
{
  if(!n) return;
  switch(n->token)
  {
  case F_LOCAL:
    if(n->opt_info & (OPT_LVALUE_OVERWRITE | OPT_LVALUE_CHANGE))
      p->locals[n->u.s.a.number]=VAR_USED;
    break;

  case F_GLOBAL:
    if(n->opt_info & (OPT_LVALUE_OVERWRITE | OPT_LVALUE_CHANGE))
      p->globals[n->u.s.a.number]=VAR_USED;
    break;

  default:
    if(n->opt_info & OPT_A_IS_NODE) find_written_vars(n->u.s.a.node,p);
    if(n->opt_info & OPT_B_IS_NODE) find_written_vars(n->u.s.b.node,p);
  }
}

/* return 1 if A depends on B */
static int depend_p2(node *a,node *b)
{
  struct used_vars aa,bb;
  int e;

  if(!a || !b || is_const(a)) return 0;
  for(e=0;e<MAX_LOCAL;e++) aa.locals[e]=bb.locals[e]=VAR_UNUSED;
  for(e=0;e<MAX_GLOBAL;e++) aa.globals[e]=bb.globals[e]=VAR_UNUSED;

  find_used_variables(a,&aa,0);
  find_written_vars(b,&bb);

  for(e=0;e<MAX_LOCAL;e++)
    if(aa.locals[e]==VAR_USED && bb.locals[e]!=VAR_UNUSED)
      return 1;

  for(e=0;e<MAX_GLOBAL;e++)
    if(aa.globals[e]==VAR_USED && bb.globals[e]!=VAR_UNUSED)
      return 1;
  return 0;
}
#else
/* return 1 if a depends on b */
static int depend_p2(node *a,node *b)
{
  int foo;
  if(!a || !b || is_const(a)) return 0;

  switch(b->token)
  {
  case F_INDEX:
    if(b->opt_info & ( OPT_LVALUE_CHANGE | OPT_LVALUE_OVERWRITE ))
    {
      if(a->token==F_INDEX) return 1;
      switch(a->token)
      {
      default:
	if((a->opt_info & OPT_A_IS_NODE) && (foo=depend_p2(a->u.s.a.node,b)))
	  return foo;
	if((a->opt_info & OPT_B_IS_NODE) && (foo=depend_p2(a->u.s.b.node,b)))
	  return foo;
      }
    }
    break;
    
  case F_GLOBAL:
  case F_LOCAL:
    if(b->opt_info & ( OPT_LVALUE_CHANGE | OPT_LVALUE_OVERWRITE ))
    {
      if(a->token==b->token && a->u.s.a.number==b->u.s.a.number)
      {
/*	if(a->opt_info & OPT_LVALUE_OVERWRITE)
	  return -1;
	else
*/	  return 1;
      }
      switch(a->token)
      {
      default:
	if((a->opt_info & OPT_A_IS_NODE) && (foo=depend_p2(a->u.s.a.node,b)))
	  return foo;
	if((a->opt_info & OPT_B_IS_NODE) && (foo=depend_p2(a->u.s.b.node,b)))
	  return foo;
      }
    }
    break;
  }
  if((b->opt_info & OPT_A_IS_NODE) && (0<depend_p2(a,b->u.s.a.node)))
    return 1;
  if((b->opt_info & OPT_B_IS_NODE) && (0<depend_p2(a,b->u.s.b.node)))
    return 1;
  return 0;
}

#endif

static int depend_p(node *a,node *b)
{
  if(!b) return 0;
  if(!(b->opt_info & OPT_SIDE_EFFECT))
  {
    return depend_p2(a,b);
  }else{
    return depend_p3(a,b) || depend_p2(a,b);
  }
}

static int find_and_clear_optimize_flag(node *n,node *a)
{
  if(!a || !n) return 0;
  if(a==n) return 1;
  if(((n->opt_info & OPT_A_IS_NODE) &&
      find_and_clear_optimize_flag(n->u.s.a.node,a)) ||
     ((n->opt_info & OPT_B_IS_NODE) &&
      find_and_clear_optimize_flag(n->u.s.b.node,a)))
  {
    n->opt_info&=~(OPT_OPT_COMPUTED|OPT_OPTIMIZED);
    return 1;
  }
  return 0;
}

static int cntargs(node *n)
{
  if(!n) return 0;
  switch(n->token)
  {
    case F_ASSIGN_AND_POP:
    case F_INC_AND_POP:
    case F_DEC_AND_POP:
    case '{':         return 0;

    case F_CAST:
    case F_EFUN_CALL: return n->type!=TYPE_VOID;

    case F_FOREACH:   return 3;
    case F_INC_NEQ_LOOP:
    case F_DEC_NEQ_LOOP:
    case F_INC_LOOP:
    case F_DEC_LOOP:  return 2;

    case ' ':
    case F_LVALUE_LIST:
    case F_ARG_LIST:
      return cntargs(n->u.s.a.node)+cntargs(n->u.s.b.node);

    /* this might not be true, but it doesn't matter very much */
    default: return 1;
  }
}

static node *do_optimize(node *n,int needlval,node *parent);
static node *optimize(node *n,int needlval,node *parent)
{
  int save_current_line=current_line;
  if(!n) return n;

#ifdef LINMALLOC_DEBUG
  assert_node(n);
#endif

#ifdef MALLOC_DEBUG
  check_sfltable();
#endif
  if(n->opt_info & OPT_OPTIMIZED) return n;
  current_line=n->lnum;
  n=do_optimize(n,needlval,parent);
  if(n) n->opt_info|=OPT_OPTIMIZED;
  current_line=save_current_line;
  return n;
}

static node *call_optimizer(node *n)
{
  clear_remember();
  return optimize(n,0,NULL);
}

static node *do_optimize(node *n,int needlval,node *parent)
{
  node *tmp1,*tmp2,*tmp3;
  int opt_info1,opt_info2,try_optimize;

  if(!n) return NULL;

#ifdef DEBUG
  if(n->token>=F_MAX_INSTR)
  {
    fatal("Internal compiler breakdown.\n");
  }
#endif

#ifdef MALLOC_DEBUG
    check_sfltable();
#endif

  try_optimize=OPT_TRY_OPTIMIZE | OPT_COULD_BE_CONST;
  opt_info1=opt_info2=OPT_CONST;
  switch(n->token)
  {
  case F_NUMBER:
  case F_STRING:
  case F_FLOAT:
  case F_CONSTANT:
    try_optimize=OPT_COULD_BE_CONST;
    break;

  case F_CAST:
    n->u.s.a.node=optimize(n->u.s.a.node,needlval,n);
    opt_info1=n->u.s.a.node?n->u.s.a.node->opt_info:OPT_CONST;
    if(n->type==TYPE_OBJECT)
    {
      clear_remember();
      if((n->u.s.a.node->type & TYPE_MOD_MASK) == TYPE_STRING ||
	 (n->u.s.a.node->type & TYPE_MOD_MASK) == TYPE_ANY)
      {
	opt_info2=OPT_SIDE_EFFECT;
      }else if((n->u.s.a.node->type & TYPE_MOD_MASK) == TYPE_NUMBER){
	opt_info2=0;
      }
    }else{
    }
    break;

  case F_CONSTANT_FUNCTION:
    try_optimize=OPT_COULD_BE_CONST;
    if(needlval!=-1)
    {
      opt_info1=OPT_CONST;
    }else{
      if((FUNCTIONP(n->u.s.a.number)->flags & NAME_PROTOTYPE) ||
	 !(FUNCTIONP(n->u.s.a.number)->type & TYPE_MOD_INLINE))
      {
	opt_info1=OPT_SIDE_EFFECT | OPT_RESTORE_OBJECT; /* assume the worst */
      }else{
	opt_info1=FUNCTIONP(n->u.s.a.number)->type;
	opt_info1=(opt_info1&TYPE_MOD_CONSTANT?OPT_CONST:0) |
	  (opt_info1&TYPE_MOD_SIDE_EFFECT?OPT_SIDE_EFFECT:0) ;
	if(!(opt_info1&OPT_CONST)) try_optimize=OPT_TRY_OPTIMIZE;
      }
    }
    break;

  case F_PUSH_SIMUL_EFUN:
    try_optimize=OPT_COULD_BE_CONST;
    if(needlval!=-1)
    {
      opt_info1=OPT_CONST;
    }else{
      struct svalue *s;
      s=&(simul_efuns[n->u.s.a.number].fun);
      if(s->type==T_FUNCTION)
      {
	opt_info1=s->u.ob->prog->function_ptrs[s->subtype].type;
	opt_info1=(opt_info1&TYPE_MOD_CONSTANT?OPT_CONST:0) |
	  (opt_info1&TYPE_MOD_SIDE_EFFECT?OPT_SIDE_EFFECT:0) ;
	if(!(opt_info1&OPT_CONST)) try_optimize=OPT_TRY_OPTIMIZE;
      }else{
	fatal("Simul efun destructed unexpectedly.\n");
      }
    }
    break;

  case F_EFUN_CALL:
    switch(n->u.s.a.number)
    {
    case F_RESTORE_OBJECT:
      opt_info1=instrs[n->u.s.a.number-F_OFFSET].ret_type;
      opt_info1=(opt_info1&TYPE_MOD_CONSTANT?OPT_CONST:0) |
	(opt_info1&TYPE_MOD_SIDE_EFFECT?OPT_SIDE_EFFECT:0)|
	  OPT_RESTORE_OBJECT;
      n->u.s.b.node=optimize(n->u.s.b.node,0,n);
      opt_info2=n->u.s.b.node?n->u.s.b.node->opt_info:OPT_CONST;
      clear_remember();
      try_optimize=0;
      break;
      
    case F_CALL_FUNCTION:
      n->u.s.b.node=optimize(n->u.s.b.node,-1,n);
      opt_info1=n->u.s.b.node?n->u.s.b.node->opt_info:OPT_CONST;
      clear_remember();
      if(!(opt_info1&OPT_CONST)) opt_info1|=OPT_SIDE_EFFECT;
      break;

    case F_ALLOCATE:
      opt_info1=instrs[n->u.s.a.number-F_OFFSET].ret_type;
      opt_info1=(opt_info1&TYPE_MOD_CONSTANT?OPT_CONST:0) |
	(opt_info1&TYPE_MOD_SIDE_EFFECT?OPT_SIDE_EFFECT:0) ;
      n->u.s.b.node=optimize(n->u.s.b.node,0,n);
      opt_info2=n->u.s.b.node?n->u.s.b.node->opt_info:OPT_CONST;
      if(opt_info1&OPT_SIDE_EFFECT)
	clear_remember();

    default:
      opt_info1=instrs[n->u.s.a.number-F_OFFSET].ret_type;
      opt_info1=(opt_info1&TYPE_MOD_CONSTANT?OPT_CONST:0) |
	(opt_info1&TYPE_MOD_SIDE_EFFECT?OPT_SIDE_EFFECT:0) ;
      n->u.s.b.node=optimize(n->u.s.b.node,0,n);
      opt_info2=n->u.s.b.node?n->u.s.b.node->opt_info:OPT_CONST;
      if(opt_info1&OPT_SIDE_EFFECT)
	clear_remember();
      if(!(opt_info1&OPT_CONST) &&
	 n->u.s.a.number!=F_ALLOCATE)
	try_optimize=OPT_TRY_OPTIMIZE;
    }
    break;

  case F_CONTINUE:
    opt_info1=OPT_CONT;
    break;

  case F_BREAK:
    opt_info1=OPT_BREAK;
    break;

  case F_BRANCH:
  case F_DO:
    clear_remember();
    n->u.s.a.node=optimize(n->u.s.a.node,0,n);
    opt_info1=n->u.s.a.node?n->u.s.a.node->opt_info:OPT_CONST;
    opt_info1&=~OPT_CONT & ~OPT_BREAK;
    clear_remember();
    n->u.s.b.node=optimize(n->u.s.b.node,0,n);
    opt_info2=n->u.s.b.node?n->u.s.b.node->opt_info:OPT_CONST;
    clear_remember();
    break;

  case F_INC:
  case F_INC_AND_POP:
  case F_POST_INC:
  case F_DEC:
  case F_DEC_AND_POP:
  case F_POST_DEC:
  {
    struct nodepair *p;
    p=active;
    active=find_remember(n->u.s.a.node);
    n->u.s.a.node=optimize(n->u.s.a.node,OPT_LVALUE_CHANGE,n);
    active=p;
    opt_info1=n->u.s.a.node?n->u.s.a.node->opt_info:OPT_CONST;
    break;
  }

  case F_FOREACH:
  case F_INC_NEQ_LOOP:
  case F_DEC_NEQ_LOOP:
  case F_INC_LOOP:
  case F_DEC_LOOP:
  case F_FOR:
    clear_remember();
    n->u.s.a.node=optimize(n->u.s.a.node,0,n);
    opt_info2=n->u.s.a.node?n->u.s.a.node->opt_info:OPT_CONST;
    clear_remember();
    n->u.s.b.node=optimize(n->u.s.b.node,0,n);
    opt_info1=n->u.s.b.node?n->u.s.b.node->opt_info:OPT_CONST;
    opt_info1&= ~OPT_CONT & ~OPT_BREAK;
    clear_remember();
    break;

  case F_ASSIGN_AND_POP:
  case F_ASSIGN:
  {
    struct nodepair *p;
    p=active;
    active=find_remember(n->u.s.b.node);
    n->u.s.a.node=optimize(n->u.s.a.node,0,n);
    active=p;
    opt_info2=n->u.s.a.node?n->u.s.a.node->opt_info:OPT_CONST;
    n->u.s.b.node=optimize(n->u.s.b.node,OPT_LVALUE_OVERWRITE,n);
    opt_info1=n->u.s.b.node?n->u.s.b.node->opt_info:OPT_CONST;
    break;
  }

  case F_AND_EQ:
  case F_OR_EQ:
  case F_XOR_EQ:
  case F_LSH_EQ:
  case F_RSH_EQ:
  case F_ADD_EQ:
  case F_SUB_EQ:
  case F_MULT_EQ:
  case F_MOD_EQ:
  case F_DIV_EQ:
    n->u.s.b.node=optimize(n->u.s.b.node,0,n);
    opt_info2=n->u.s.b.node?n->u.s.b.node->opt_info:OPT_CONST;
    n->u.s.a.node=optimize(n->u.s.a.node,OPT_LVALUE_CHANGE,n);
    opt_info1=n->u.s.a.node?n->u.s.a.node->opt_info:OPT_CONST;
    break;

  case ' ':
    clear_remember();
    n->u.s.a.node=optimize(n->u.s.a.node,0,n);
    opt_info1=n->u.s.a.node?n->u.s.a.node->opt_info:OPT_CONST;
    clear_remember();
    n->u.s.b.node=optimize(n->u.s.b.node,OPT_LVALUE_CHANGE,n);
    opt_info2=n->u.s.b.node?n->u.s.b.node->opt_info:OPT_CONST;
    clear_remember();
    break;

  case F_LOCAL:
    if(needlval)
    {
      opt_info1=OPT_LOCAL_ASSIGNMENT;
    }else{
      opt_info1=0;
    }
    break;

  case F_GLOBAL:
    if(needlval)
    {
      opt_info1=OPT_GLOBAL_ASSIGNMENT;
    }else{
      opt_info1=OPT_GLOBAL_DEPEND;
    }
    break;

  case F_INDEX:
    /* hm */
    n->u.s.a.node=optimize(n->u.s.a.node,needlval?OPT_LVALUE_CHANGE:0,n);
    opt_info1=n->u.s.a.node?n->u.s.a.node->opt_info:OPT_CONST;
    n->u.s.b.node=optimize(n->u.s.b.node,0,n);
    opt_info2=n->u.s.b.node?n->u.s.b.node->opt_info:OPT_CONST;
    needlval=0;
    break;

  case '{':
  case F_ARG_LIST:
    try_optimize=OPT_COULD_BE_CONST;
    if(needlval==-1)
    {
      n->u.s.a.node=optimize(n->u.s.a.node,needlval,n);
      opt_info1=n->u.s.a.node?n->u.s.a.node->opt_info:OPT_CONST;
      n->u.s.b.node=optimize(n->u.s.b.node,0,n);
      opt_info2=n->u.s.b.node?n->u.s.b.node->opt_info:OPT_CONST;
    }else{
      n->u.s.a.node=optimize(n->u.s.a.node,0,n);
      opt_info1=n->u.s.a.node?n->u.s.a.node->opt_info:OPT_CONST;
      n->u.s.b.node=optimize(n->u.s.b.node,needlval,n);
      opt_info2=n->u.s.b.node?n->u.s.b.node->opt_info:OPT_CONST;
    }
    break;

  case F_LVALUE_LIST:
    try_optimize=OPT_COULD_BE_CONST;
    n->u.s.a.node=optimize(n->u.s.a.node,OPT_LVALUE_CHANGE,n);
    opt_info1=n->u.s.a.node?n->u.s.a.node->opt_info:OPT_CONST;
    n->u.s.b.node=optimize(n->u.s.b.node,OPT_LVALUE_CHANGE,n);
    opt_info2=n->u.s.b.node?n->u.s.b.node->opt_info:OPT_CONST;
    break;

  case F_CASE:
  case F_DEFAULT:
    clear_remember();
    n->u.s.a.node=optimize(n->u.s.a.node,0,n);
    opt_info1=n->u.s.a.node?n->u.s.a.node->opt_info:OPT_CONST;
    opt_info1|=OPT_CASE;
    clear_remember();
    n->u.s.b.node=optimize(n->u.s.b.node,0,n);
    opt_info2=n->u.s.b.node?n->u.s.b.node->opt_info:OPT_CONST;
    opt_info2|=OPT_CASE;
    clear_remember();
    break;

  case F_RETURN:
    opt_info1=OPT_RETURN;
    n->u.s.a.node=optimize(n->u.s.a.node,0,n);
    opt_info2=n->u.s.a.node?n->u.s.a.node->opt_info:OPT_CONST;
    clear_remember();
    break;

  case F_SWITCH:
    n->u.s.a.node=optimize(n->u.s.a.node,0,n);
    opt_info1=n->u.s.a.node?n->u.s.a.node->opt_info:OPT_CONST;
    clear_remember();
    n->u.s.b.node=optimize(n->u.s.b.node,0,n);
    opt_info2=n->u.s.b.node?n->u.s.b.node->opt_info:OPT_CONST;
    opt_info2&= ~OPT_CASE & ~OPT_BREAK;
    clear_remember();
    break;

  case F_ADD: case F_ADD_INT: case F_SUBTRACT: case F_MULTIPLY:
  case F_DIVIDE: case F_MOD: case F_NOT: case F_AND: case F_OR:
    if(n->opt_info & OPT_A_IS_NODE)
    {
      n->u.s.a.node=optimize(n->u.s.a.node,needlval,n);
      opt_info1=n->u.s.a.node?n->u.s.a.node->opt_info:OPT_CONST;
    }else{
      opt_info1=OPT_CONST;
    }
    if(n->opt_info & OPT_B_IS_NODE)
    {
      n->u.s.b.node=optimize(n->u.s.b.node,needlval,n);
      opt_info2=n->u.s.b.node?n->u.s.b.node->opt_info:OPT_CONST;
    }else{
      opt_info2=OPT_CONST;
    }
    break;
    
  default:
    clear_remember();
    if(n->opt_info & OPT_A_IS_NODE)
    {
      n->u.s.a.node=optimize(n->u.s.a.node,needlval,n);
      opt_info1=n->u.s.a.node?n->u.s.a.node->opt_info:OPT_CONST;
    }else{
      opt_info1=OPT_CONST;
    }
    clear_remember();
    if(n->opt_info & OPT_B_IS_NODE)
    {
      n->u.s.b.node=optimize(n->u.s.b.node,needlval,n);
      opt_info2=n->u.s.b.node?n->u.s.b.node->opt_info:OPT_CONST;
    }else{
      opt_info2=OPT_CONST;
    }
    clear_remember();
  }

#ifdef MALLOC_DEBUG
    check_sfltable();
#endif

  n->opt_info=
    (
     (opt_info1 & opt_info2 & OPT_CONST) |
     ((opt_info1 | opt_info2) &
      (OPT_SIDE_EFFECT | OPT_GLOBAL_ASSIGNMENT | OPT_LOCAL_ASSIGNMENT | OPT_RESTORE_OBJECT |
       OPT_TRY_OPTIMIZE | OPT_CONT | OPT_BREAK | OPT_RETURN | OPT_GLOBAL_DEPEND | OPT_CASE)) |
     (n->opt_info & (OPT_A_IS_NODE | OPT_B_IS_NODE)) |
     OPT_OPT_COMPUTED |
     try_optimize |
     (needlval & (OPT_LVALUE_CHANGE | OPT_LVALUE_OVERWRITE))
     );

#ifdef LASDEBUG
  if(lasdebug>3)
  {
    fprintf(stderr,"Computed [%c%c%c%c%c%c%c%c%c%c%c%c%c%c] opt info for:\n",
	    (n->opt_info & OPT_A_IS_NODE)?'A':'-',
	    (n->opt_info & OPT_B_IS_NODE)?'B':'-',
	    (n->opt_info & OPT_CONST)?'C':'-',
	    (n->opt_info & OPT_SIDE_EFFECT)?'X':'-',
	    (n->opt_info & OPT_LOCAL_ASSIGNMENT)?'l':'-',
	    (n->opt_info & OPT_GLOBAL_ASSIGNMENT)?'g':'-',
	    (n->opt_info & OPT_RESTORE_OBJECT)?'r':'-',
	    (n->opt_info & OPT_OPT_COMPUTED)?'O':'-',
	    (n->opt_info & OPT_TRY_OPTIMIZE)?'T':'-',
	    (n->opt_info & OPT_CASE)?'c':'-',
	    (n->opt_info & OPT_CONT)?'c':'-',
	    (n->opt_info & OPT_CONT)?'b':'-',
	    (n->opt_info & OPT_RETURN)?'r':'-',
	    (n->opt_info & OPT_GLOBAL_DEPEND)?'G':'-'
	    );
  }
#endif

#ifdef MALLOC_DEBUG
      check_sfltable();
#endif

 if(check_types(n)) 
    return do_optimize(n,needlval,parent);

#ifdef MALLOC_DEBUG
      check_sfltable();
#endif

  if(lasdebug>2)
  {
    fprintf(stderr,"do_optimizing:");
    nice_print_tree(n);
  }

#ifdef MALLOC_DEBUG
      check_sfltable();
#endif
  switch(n->token)
  {
  case ' ':
  case ':': return n;		/* hands of optimizer ! */

  case '?':
    /* (!X) ? (Y) : (Z) ->  (X) ? (Z) : (Y)*/
    while(n->u.s.a.node->token==F_NOT)
    {
      tmp1=n->u.s.b.node->u.s.a.node;
      n->u.s.b.node->u.s.a.node=n->u.s.b.node->u.s.b.node;
      n->u.s.b.node->u.s.b.node=tmp1;
      tmp1=n->u.s.a.node;
      n->u.s.a.node=tmp1->u.s.a.node;
      tmp1->u.s.a.node=0;
      free_node(tmp1);
    }
    /* 1 ? X : Y => X 
     * if(1) X; else Y; => X; 
     */
    if(node_is_true(n->u.s.a.node))
    {
      tmp1=n->u.s.b.node->u.s.a.node;
      n->u.s.b.node->u.s.a.node=NULL;
      free_node(n);
      return optimize(tmp1,needlval,parent);
    }

    /* 0 ? X : Y => Y 
     * if(0) X; else Y; => Y;
     */
    if(node_is_false(n->u.s.a.node))
    {
      tmp1=n->u.s.b.node->u.s.b.node;
      n->u.s.b.node->u.s.b.node=NULL;
      free_node(n);
      return optimize(tmp1,needlval,parent);
    }

    /* if(X) Y; else Y; => { X; Y; } */
    if(node_is_eq(n->u.s.b.node->u.s.a.node,n->u.s.b.node->u.s.b.node))
    {
      n->token=F_ARG_LIST;
      n->u.s.a.node=mkcastnode(TYPE_VOID,n->u.s.a.node);
      tmp1=n->u.s.b.node;
      n->u.s.b.node=tmp1->u.s.a.node;
      tmp1->u.s.a.node=NULL;
      free_node(tmp1);
      n->opt_info&=~OPT_OPT_COMPUTED;
      return optimize(n,needlval,parent);
    }
    break;
       
  case F_LOCAL:
  case F_GLOBAL:
  case F_INDEX:
  {
    struct nodepair *p;
    if((p=find_remember(n)) && p->assignment)
    {
      if(n->opt_info & (OPT_LVALUE_CHANGE | OPT_LVALUE_OVERWRITE))
      {
	if(n->opt_info & OPT_LVALUE_OVERWRITE)
	{
	  if(p->assignment && !p->used)
	  {
	    tmp1=p->assignment;
	    free_node(tmp1->u.s.b.node);
	    free_node(tmp1->u.s.a.node);
	    tmp1->u.s.a.node=NULL;
	    tmp1->u.s.b.node=NULL;
	    tmp1->token='{';
	  }else{
	    reset_remember(p);
	  }
	}else{
	  reset_remember(p);
	}
      }else{
	if(node_is_eq(n,p->assignment->u.s.a.node))
	{
	  if((p->assignment->token==F_ASSIGN ||
	      p->assignment->token==F_ASSIGN_AND_POP) &&
	     is_const(p->assignment->u.s.a.node))
	  {
	    free_node(n);
	    return optimize(copy_node(p->assignment->u.s.b.node),needlval,parent);
	  }else{
	    if(!p->used)
	    {
	      int tmp=0;
	      switch(p->assignment->token)
	      {
	      case F_INC_AND_POP:
	      case F_POST_INC:
	      case F_INC:
		tmp=F_INC;

	      case F_DEC_AND_POP:
	      case F_POST_DEC:
	      case F_DEC:
		tmp=F_DEC;
		break;

	      case F_ASSIGN_AND_POP:
	      case F_ASSIGN:
		tmp=F_ASSIGN;
		break;
	      }
	      if(!tmp)
		fatal("Optimizer fatal error!!\n");
	      tmp1=mknode(tmp,p->assignment->u.s.a.node,p->assignment->u.s.b.node);
	      p->assignment->u.s.a.node=NULL;
	      p->assignment->u.s.b.node=NULL;
	      p->assignment->token='{';
	      p->assignment->opt_info&=~OPT_OPT_COMPUTED;
	      reset_remember(p);
	      free_node(n);
	      return optimize(tmp1,needlval,parent);
	    }else{
	      reset_remember(p);
	    }
	  }
	}else{
	  reset_remember(p);
	}
      }
    }
    break;
  }
       
  case F_ARG_LIST:
  case '{':
    /* free useless nodes */
    if(!n->u.s.a.node && !n->u.s.b.node)
    {
      free_node(n);
      return NULL;
    }

    /* free useless nodes */
    if(!n->u.s.a.node)
    {
      tmp1=n->u.s.b.node;
      n->u.s.b.node=0;
      free_node(n);
      return optimize(tmp1,needlval,parent);
    }

    /* free useless nodes */
    if(!n->u.s.b.node)
    {
      tmp1=n->u.s.a.node;
      n->u.s.a.node=0;
      free_node(n);
      return optimize(tmp1,needlval,parent);
    }

    /* { X; return; Y; } -> { X; return; }
     * { X; break; Y; } -> { X; return; }
     * { X; continue; Y; } -> { X; return; }
     */

    if((n->u.s.a.node->token==F_RETURN ||
	n->u.s.a.node->token==F_BREAK ||
	n->u.s.a.node->token==F_CONTINUE ||
	((n->u.s.a.node->token=='{' || n->u.s.a.node->token==F_ARG_LIST) &&
	 n->u.s.a.node->u.s.b.node &&
	 (n->u.s.a.node->u.s.b.node->token==F_RETURN ||
	  n->u.s.a.node->u.s.b.node->token==F_BREAK ||
	  n->u.s.a.node->u.s.b.node->token==F_CONTINUE))) &&
       !(n->u.s.b.node->opt_info & OPT_CASE))
    {
      tmp1=n->u.s.a.node;
      n->u.s.a.node=0;
      free_node(n);
      return optimize(tmp1,needlval,parent);
    }

    if(n->token=='{')
    {
      node **prev,**curr;
      /* { (void)a; } => { a; } */
      while(n->u.s.a.node->token==F_CAST && n->u.s.a.node->type==TYPE_VOID)
      {
	tmp1=n->u.s.a.node;
	n->u.s.a.node=tmp1->u.s.a.node;
	tmp1->u.s.a.node=0;
	free_node(tmp1);
      }
      /* { (void)b; } => { b; } */
      while(n->u.s.b.node->token==F_CAST && n->u.s.b.node->type==TYPE_VOID)
      {
	tmp1=n->u.s.b.node;
	n->u.s.b.node=tmp1->u.s.a.node;
	tmp1->u.s.a.node=0;
	free_node(tmp1);
      }

      curr=&(n->u.s.b.node);
      prev=last_cmd(&(n->u.s.a.node));
      if(prev && (*prev) && (*curr))
      {
	if(((*curr)->token==F_INC ||
	    (*curr)->token==F_POST_INC ||
	    (*curr)->token==F_INC_AND_POP) &&
	   ((*prev)->token==F_ASSIGN ||
	    (*prev)->token==F_ASSIGN_AND_POP) &&
	   node_is_eq((*prev)->u.s.b.node,(*curr)->u.s.a.node))
	{
	  free_node(*curr);
	  *curr=0;
	  (*prev)->u.s.a.node=mknode(F_ADD,(*prev)->u.s.a.node,mkintnode(1));
	  find_and_clear_optimize_flag(n,*prev);
	  return optimize(n,needlval,parent);
	}

	if(((*curr)->token==F_DEC ||
	    (*curr)->token==F_POST_DEC ||
	    (*curr)->token==F_DEC_AND_POP) &&
	   ((*prev)->token==F_ASSIGN ||
	    (*prev)->token==F_ASSIGN_AND_POP) &&
	   node_is_eq((*prev)->u.s.b.node,(*curr)->u.s.a.node))
	{
	  free_node(*curr);
	  *curr=0;
	  (*prev)->u.s.a.node=mknode(F_ADD,(*prev)->u.s.a.node,mkintnode(-1));
	  find_and_clear_optimize_flag(n,*prev);
	  return optimize(n,needlval,parent);
	}
      }
      if(!(n->opt_info & (OPT_CONT | OPT_BREAK | OPT_CASE)) &&
	 cntargs(n->u.s.a.node)+cntargs(n->u.s.b.node)<10)
      {
	n->token=F_ARG_LIST;
	return optimize(mkcastnode(TYPE_VOID,n),needlval,parent);
      }
    }

    break;

  case F_MULTIPLY:
    if(SAME_TYPE(n->u.s.a.node->type,n->u.s.b.node->type & TYPE_MOD_MASK))
    {
      if(n->u.s.a.node->token==F_NUMBER || n->u.s.b.node->token==F_NUMBER)
      {
	int tmp,e;

	/* x*(X) -> (X)*x */
	if(n->u.s.a.node->token==F_NUMBER && n->u.s.b.node->token!=F_NUMBER)
	{
	  tmp1=n->u.s.a.node;
	  n->u.s.a.node=n->u.s.b.node;
	  n->u.s.b.node=tmp1;
	}
	tmp=n->u.s.b.node->u.s.a.number;
	/* X*1 -> X */
	if(tmp==1)
	{
	  tmp1=n->u.s.a.node;
	  n->u.s.a.node=0;
	  free_node(n);
	  return tmp1;
	}
	if(tmp==0)
	{
	  if(!(n->u.s.a.node->opt_info & OPT_NOT_THROWABLE))
	  {
	    free_node(n);
	    return mkintnode(0);
	  }
	}
	/* X*(1<<x) -> X<<x */
	for(e=1;e;e*=2)
	{
	  if(tmp==e)
	  {
	    n->u.s.b.node->u.s.a.number=0;
	    while(e>1)
	    {
	      n->u.s.b.node->u.s.a.number++;
	      e/=2;
	    }
	    n->token=F_LSH;
	    n->opt_info &=~ (OPT_OPT_COMPUTED | OPT_OPTIMIZED);
	    return optimize(n,needlval,parent);
	  }
	}
      }

      /* 1.0*X => X */
      /* X*0.0 => 0.0 */
      if((n->u.s.a.node->token==F_FLOAT && n->u.s.a.node->u.s.a.fnum==1.0) ||
	 (n->u.s.b.node->token==F_FLOAT && n->u.s.b.node->u.s.a.fnum==0.0 &&
	  !(n->u.s.a.node->opt_info & OPT_NOT_THROWABLE)))
      {
	tmp1=n->u.s.b.node;
	n->u.s.b.node=0;
	free_node(n);
	return optimize(tmp1,needlval,parent);
      }

      /* 1*X => X */
      if((n->u.s.b.node->token==F_FLOAT && n->u.s.b.node->u.s.a.fnum==1.0) ||
	 (n->u.s.a.node->token==F_FLOAT && n->u.s.a.node->u.s.a.fnum==0.0 &&
	   !(n->u.s.b.node->opt_info & OPT_NOT_THROWABLE)))
      {
	tmp1=n->u.s.a.node;
	n->u.s.a.node=0;
	free_node(n);
	return optimize(tmp1,needlval,parent);
      }
    }
    break;

  case F_DIVIDE:
    if(SAME_TYPE(n->u.s.a.node->type,n->u.s.b.node->type))
    {
      if(n->u.s.b.node->token==F_NUMBER)
      {
	int tmp,e;

	tmp=n->u.s.b.node->u.s.a.number;
	/* X*1 -> X */
	if(tmp==1)
	{
	  tmp1=n->u.s.a.node;
	  n->u.s.a.node=0;
	  free_node(n);
	  return tmp1;
	}

	/* X/(1<<x) -> X>>x */
	for(e=1;e;e*=2)
	{
	  if(tmp==e)
	  {
	    n->u.s.b.node->u.s.a.number=0;
	    while(e>1)
	    {
	      n->u.s.b.node->u.s.a.number++;
	      e/=2;
	    }
	    n->token=F_RSH;
	    n->opt_info &=~ (OPT_OPT_COMPUTED | OPT_OPTIMIZED);
	    return optimize(n,needlval,parent);
	  }
	}
      }

      /* X/1 => X */
      /* 0/X => 0 */
      if((n->u.s.a.node->token==F_NUMBER && n->u.s.a.node->u.s.a.number==0 &&
	  !(n->u.s.b.node->opt_info & OPT_NOT_THROWABLE)) ||
	 (n->u.s.a.node->token==F_FLOAT && n->u.s.a.node->u.s.a.fnum==0.0 &&
	  !(n->u.s.b.node->opt_info & OPT_NOT_THROWABLE)) ||
	 (n->u.s.b.node->token==F_NUMBER && n->u.s.b.node->u.s.a.number==1) ||
	 (n->u.s.b.node->token==F_FLOAT && n->u.s.b.node->u.s.a.fnum==1.0))
      {
	tmp1=n->u.s.a.node;
	n->u.s.a.node=0;
	free_node(n);
	return optimize(tmp1,needlval,parent);
      }
    }
    break;

  case F_ADD:
    /* X+0 => X */
    if(((n->u.s.a.node->type & TYPE_MOD_MASK) == TYPE_NUMBER &&
	n->u.s.b.node->token==F_NUMBER && n->u.s.b.node->u.s.a.number==0) ||
       ((n->u.s.a.node->type & TYPE_MOD_MASK) == TYPE_FLOAT &&
	n->u.s.b.node->token==F_FLOAT && n->u.s.b.node->u.s.a.fnum==0.0) ||
       ((n->u.s.a.node->type & TYPE_MOD_MASK) == TYPE_STRING &&
	n->u.s.b.node->token==F_STRING && !strlen(n->u.s.b.node->u.s.a.str)))
    {
      tmp1=n->u.s.a.node;
      n->u.s.a.node=0;
      free_node(n);
      return optimize(tmp1,needlval,parent);
    }
    /* 0+X => X */
    if(((n->u.s.b.node->type & TYPE_MOD_MASK) == TYPE_NUMBER &&
	n->u.s.a.node->token==F_NUMBER && n->u.s.a.node->u.s.a.number==0) ||
       ((n->u.s.b.node->type & TYPE_MOD_MASK) == TYPE_FLOAT &&
	n->u.s.a.node->token==F_FLOAT && n->u.s.a.node->u.s.a.fnum==0.0) ||
       ((n->u.s.b.node->type & TYPE_MOD_MASK) == TYPE_STRING &&
	n->u.s.a.node->token==F_STRING && !strlen(n->u.s.a.node->u.s.a.str)))
    {
      tmp1=n->u.s.b.node;
      n->u.s.b.node=0;
      free_node(n);
      return optimize(tmp1,needlval,parent);
    }
    /* (a+b)+c => sum(a,b,c) */
    if(n->u.s.a.node->token==F_ADD &&
       SAME_TYPE(n->u.s.a.node->type,n->u.s.b.node->type))
    {
      n->u.s.a.node->token=F_ARG_LIST;
      n->u.s.a.node->opt_info&=~(OPT_OPT_COMPUTED|OPT_OPTIMIZED);
      n->token=F_ARG_LIST;
      n->opt_info&=~OPT_OPT_COMPUTED;
      return optimize(mkefunnode(F_SUM,n),needlval,parent);
    }

    /* a+(b+c) => sum(a,b,c) */
    if(n->u.s.b.node->token==F_ADD &&
       SAME_TYPE(n->u.s.a.node->type,n->u.s.b.node->type))
    {
      n->u.s.b.node->token=F_ARG_LIST;
      n->u.s.b.node->opt_info&=~(OPT_OPT_COMPUTED|OPT_OPTIMIZED);
      n->token=F_ARG_LIST;
      n->opt_info&=~OPT_OPT_COMPUTED;
      return optimize(mkefunnode(F_SUM,n),needlval,parent);
    }

    /* sum(X)+c => sum(X,c) */
    if(n->u.s.a.node->token==F_EFUN_CALL &&
       n->u.s.a.node->u.s.a.number==F_SUM &&
       SAME_TYPE(n->u.s.a.node->type,n->u.s.b.node->type))
    {
      n->opt_info&=~(OPT_OPT_COMPUTED|OPT_OPTIMIZED);
      n->u.s.a.node->opt_info&=~(OPT_OPT_COMPUTED|OPT_OPTIMIZED);
      tmp1=n->u.s.a.node;
      n->token=F_ARG_LIST;
      n->u.s.a.node=tmp1->u.s.b.node;
      tmp1->u.s.b.node=n;
      return optimize(tmp1,needlval,parent);
    }

    /* c+sum(X) => sum(c,X) */
    if(n->u.s.b.node->token==F_EFUN_CALL &&
       n->u.s.b.node->u.s.a.number==F_SUM &&
       SAME_TYPE(n->u.s.a.node->type,n->u.s.b.node->type))
    {
      n->opt_info&=~(OPT_OPT_COMPUTED|OPT_OPTIMIZED);
      n->u.s.b.node->opt_info&=~(OPT_OPT_COMPUTED|OPT_OPTIMIZED);
      tmp1=n->u.s.b.node;
      n->token=F_ARG_LIST;
      n->u.s.b.node=tmp1->u.s.b.node;
      tmp1->u.s.b.node=n;
      return optimize(tmp1,needlval,parent);
    }
    break;

  case F_ASSIGN:
  case F_ASSIGN_AND_POP:
    /* X=X => {} */
    if(node_is_eq(n->u.s.a.node,n->u.s.b.node))
    {
      free_node(n);
      return NULL;
    }

    if(!n->u.s.b.node) break;

    /* X=X+1 => ++X */
    if(n->u.s.a.node->token==F_ADD &&
       n->u.s.b.node->type==TYPE_NUMBER &&
       node_is_eq(n->u.s.b.node,n->u.s.a.node->u.s.a.node) &&
       n->u.s.a.node->u.s.b.node->token==F_NUMBER &&
       n->u.s.a.node->u.s.b.node->u.s.a.number==1)
    {
      tmp1=mknode(F_INC,n->u.s.b.node,0);
      n->u.s.b.node=NULL;
      free_node(n);
      return optimize(tmp1,needlval,parent);
    }

    /* X=X-1 => --X */
    if(n->u.s.a.node->token==F_SUBTRACT &&
       n->u.s.b.node->type==TYPE_NUMBER &&
       node_is_eq(n->u.s.b.node,n->u.s.a.node->u.s.a.node) &&
       n->u.s.a.node->u.s.b.node->token==F_NUMBER &&
       n->u.s.a.node->u.s.b.node->u.s.a.number==1)
    {
      tmp1=mknode(F_DEC,n->u.s.b.node,0);
      n->u.s.b.node=NULL;
      free_node(n);
      return optimize(tmp1,needlval,parent);
    }

    if(n->token!=F_ASSIGN || !parent || parent->token=='{' ||
       (parent->token==F_CAST && parent->type==TYPE_VOID))
    {
      /* return value probably not used */
      remember_remember(n,0);
    }else{
      /* return value probably used */
      remember_remember(n,1);
    }
    break;

  case F_EFUN_CALL:
    switch(n->u.s.a.number)
    {
    case F_SIZEOF:
      /* sizeof(m_indices(X)) => size(X)
       * sizeof(m_values(X)) => size(X)
       * sizeof(indices(X)) => size(X) */
      if(n->u.s.b.node &&
	 n->u.s.b.node->token==F_EFUN_CALL)
      {
	switch(n->u.s.b.node->u.s.a.number)
	{
	case F_M_INDICES:
	case F_M_VALUES:
	case F_INDICES:
	  n->u.s.a.number=F_SIZE;
	  tmp1=n->u.s.b.node;
	  n->u.s.b.node=tmp1->u.s.b.node;
	  tmp1->u.s.b.node=NULL;
	  free_node(tmp1);
	  n->opt_info&=~(OPT_OPT_COMPUTED|OPT_OPTIMIZED);
	  return optimize(n,needlval,parent);
	}
      }

      /* this_object()->X => constant function */
    case F_GET_FUNCTION:
      if(n->u.s.b.node &&
	 n->u.s.b.node->token==F_ARG_LIST &&
	 n->u.s.b.node->u.s.a.node &&
	 n->u.s.b.node->u.s.b.node &&
	 n->u.s.b.node->u.s.a.node->token==F_EFUN_CALL &&
	 n->u.s.b.node->u.s.a.node->u.s.a.number==F_THIS_OBJECT &&
	 n->u.s.b.node->u.s.b.node->token==F_STRING)
      {
	int i;
	if((i=defined_function(n->u.s.b.node->u.s.b.node->u.s.a.str))!=-1)
	{
	  free_node(n);
	  return optimize(mkconstfunnode(i),needlval,parent);
	}
      }else{
	node **a;
	int e;
	a=my_get_arg(&(n->u.s.b.node),1);
	if(a && *a && (*a)->token==F_STRING)
	{
	  for(e=0;e<num_lfuns;e++)
	  {
	    if(lfuns[e]==(*a)->u.s.a.str) /* shared strings */
	    {
	      free_node(*a);
	      *a=mkintnode(e);
	      n->u.s.a.number=F_GET_LFUN;
	      break;
	    }
	  }
	}
      }
      break;
      
    case F_MAP_ARRAY:
    case F_SEARCH_ARRAY:
    case F_FILTER_ARRAY:
    case F_MAP_MAPPING:
    case F_FILTER_MAPPING:
    {
      node **a;
      int e;
      a=my_get_arg(&(n->u.s.b.node),1);
      if(a && *a && (*a)->token==F_STRING)
      {
	for(e=0;e<num_lfuns;e++)
	{
	  if(lfuns[e]==(*a)->u.s.a.str) /* shared strings */
	  {
	    free_node(*a);
	    *a=mkintnode(e);
	    break;
	  }
	}
      }
      break;
    }
	
      /* sum(a+b,X) => sum(a,b,X) */
    case F_SUM:
    {
      node **t,**tt;
#ifdef MALLOC_DEBUG
      check_sfltable();
#endif
      t=first_arg(&n->u.s.b.node);
      tt=first_arg(&n->u.s.b.node);
      if(t && tt &&
	 (*t)->token==F_ADD &&
	 SAME_TYPE((*t)->type,(*tt)->type))
      {
	(*t)->token=F_ARG_LIST;
	(*t)->opt_info&=~(OPT_OPT_COMPUTED|OPT_OPTIMIZED);
	return optimize(n,needlval,parent);
      }
#ifdef MALLOC_DEBUG
      check_sfltable();
#endif
      break;
    }
    }
    break;

  case F_XOR:
    /* X^0 => X */
    if(((n->u.s.a.node->type & TYPE_MOD_MASK) == TYPE_NUMBER &&
	n->u.s.b.node->token==F_NUMBER && n->u.s.b.node->u.s.a.number==0))
    {
      tmp1=n->u.s.a.node;
      n->u.s.a.node=0;
      free_node(n);
      return optimize(tmp1,needlval,parent);
    }
    /* 0^X => X */
    if(((n->u.s.a.node->type & TYPE_MOD_MASK) == TYPE_NUMBER &&
	n->u.s.b.node->token==F_NUMBER && n->u.s.b.node->u.s.a.number==-1))
    {
      tmp1=n->u.s.a.node;
      n->u.s.a.node=0;
      free_node(n);
      return optimize(mknode(F_COMPL,tmp1,0),needlval,parent);
    }
    break;

  case F_AND:
    /* X&-1 => X; */
    if((n->u.s.a.node->type== TYPE_NUMBER &&
	n->u.s.b.node->token==F_NUMBER && n->u.s.b.node->u.s.a.number==-1) ||
       (n->u.s.b.node->type== TYPE_NUMBER &&
	n->u.s.a.node->token==F_NUMBER && n->u.s.a.node->u.s.a.number==0))
    {
      tmp1=n->u.s.a.node;
      n->u.s.a.node=0;
      free_node(n);
      return optimize(tmp1,needlval,parent);
    }
    /* -1&X => X; */
    if((n->u.s.b.node->type == TYPE_NUMBER &&
	n->u.s.a.node->token==F_NUMBER && n->u.s.a.node->u.s.a.number==-1) ||
       (n->u.s.a.node->type == TYPE_NUMBER &&
	n->u.s.b.node->token==F_NUMBER && n->u.s.b.node->u.s.a.number==0))
    {
      tmp1=n->u.s.b.node;
      n->u.s.b.node=0;
      free_node(n);
      return optimize(tmp1,needlval,parent);
    }
    break;

  case F_LAND:
    /* !a and !b -> !(a or b) */
    if(n->u.s.a.node->token==F_NOT && n->u.s.b.node->token==F_NOT)
    {
      tmp1=mknode(F_NOT,mknode(F_LOR,n->u.s.a.node->u.s.a.node,
			       n->u.s.b.node->u.s.a.node),0);
      n->u.s.a.node->u.s.a.node=NULL;
      n->u.s.b.node->u.s.a.node=NULL;
      free_node(n);
      return optimize(tmp1,needlval,parent);
    }
    /* (a && b) && c -> a && (b && c) */
    while(n->u.s.a.node->token==F_LAND)
    {
      tmp1=n->u.s.a.node;
      n->u.s.a.node=tmp1->u.s.a.node;
      tmp1->u.s.a.node=tmp1->u.s.b.node;
      tmp1->u.s.b.node=n->u.s.b.node;
      n->u.s.b.node=tmp1;
    }

    /* 1 && X => X */
    if(node_is_true(n->u.s.a.node))
    {
      tmp1=n->u.s.b.node;
      n->u.s.b.node=NULL;
      free_node(n);
      return optimize(tmp1,needlval,parent);
    }

    /* X && 1 -> X */
    if(node_is_true(n->u.s.b.node))
    {
      tmp1=n->u.s.a.node;
      n->u.s.a.node=NULL;
      free_node(n);
      return optimize(tmp1,needlval,parent);
    }

    /* 0 && X => 0 */
    if(node_is_false(n->u.s.a.node))
    {
      free_node(n);
      return mkintnode(0);
    }
    break;

  case F_LOR:
    /* !a || !b -> !(a || b) */
    if(n->u.s.a.node->token==F_NOT && n->u.s.b.node->token==F_NOT)
    {
      tmp1=mknode(F_NOT,mknode(F_LAND,n->u.s.a.node->u.s.a.node,
			       n->u.s.b.node->u.s.a.node),0);
      n->u.s.a.node->u.s.a.node=NULL;
      n->u.s.b.node->u.s.a.node=NULL;
      free_node(n);
      return optimize(tmp1,needlval,parent);
    }
    /* (a && b) && c -> a && (b && c) */
    while(n->u.s.a.node->token==F_LOR)
    {
      tmp1=n->u.s.a.node;
      n->u.s.a.node=tmp1->u.s.a.node;
      tmp1->u.s.a.node=tmp1->u.s.b.node;
      tmp1->u.s.b.node=n->u.s.b.node;
      n->u.s.b.node=tmp1;
    }

    /* 0 || X => X */
    if(node_is_false(n->u.s.a.node))
    {
      tmp1=n->u.s.b.node;
      n->u.s.b.node=NULL;
      free_node(n);
      return optimize(tmp1,needlval,parent);
    }

    /* X || 0 => X */
    if(node_is_false(n->u.s.b.node))
    {
      tmp1=n->u.s.a.node;
      n->u.s.a.node=NULL;
      free_node(n);
      return optimize(tmp1,needlval,parent);
    }

    /* true || X => true */
    if(node_is_true(n->u.s.a.node))
    {
      tmp1=n->u.s.a.node;
      n->u.s.a.node=NULL;
      free_node(n);
      return optimize(tmp1,needlval,parent);
    }
    break;

  case F_OR:
    /* 0 | X => X */
    if((n->u.s.a.node->type == TYPE_NUMBER &&
	n->u.s.b.node->token==F_NUMBER && n->u.s.b.node->u.s.a.number==-1) ||
       (n->u.s.b.node->type == TYPE_NUMBER &&
	n->u.s.a.node->token==F_NUMBER && n->u.s.a.node->u.s.a.number==0))
    {
      tmp1=n->u.s.b.node;
      n->u.s.b.node=0;
      free_node(n);
      return optimize(tmp1,needlval,parent);
    }
    /* X | 0 => X */
    if((n->u.s.b.node->type == TYPE_NUMBER &&
	n->u.s.a.node->token==F_NUMBER && n->u.s.a.node->u.s.a.number==-1) ||
       (n->u.s.a.node->type== TYPE_NUMBER &&
	n->u.s.b.node->token==F_NUMBER && n->u.s.b.node->u.s.a.number==0))
    {
      tmp1=n->u.s.a.node;
      n->u.s.a.node=0;
      free_node(n);
      return optimize(tmp1,needlval,parent);
    }
    break;

  case F_CAST:
    if(n->type==TYPE_VOID)
    {
      if(!(n->u.s.a.node) || !(n->opt_info & (OPT_SIDE_EFFECT |
					      OPT_LOCAL_ASSIGNMENT |
					      OPT_GLOBAL_ASSIGNMENT |
					      OPT_RESTORE_OBJECT |
					      OPT_CASE |
					      OPT_RETURN |
					      OPT_CONT |
					      OPT_BREAK
					      )))
      {
	free_node(n);
	return NULL;
      }
      tmp1=n->u.s.a.node;
      switch(tmp1->token)
      {
      case F_ASSIGN:
	tmp1->token=F_ASSIGN_AND_POP;
	tmp1->type=TYPE_VOID;
	n->u.s.a.node=0;
	free_node(n);
	return optimize(tmp1,needlval,parent);

      case F_INC:
      case F_POST_INC:
	tmp1->token=F_INC_AND_POP;
	tmp1->type=TYPE_VOID;
	n->u.s.a.node=0;
	free_node(n);
	return optimize(tmp1,needlval,parent);

      case F_DEC:
      case F_POST_DEC:
	tmp1->token=F_DEC_AND_POP;
	tmp1->type=TYPE_VOID;
	n->u.s.a.node=0;
	free_node(n);
	return optimize(tmp1,needlval,parent);
      }
      /*
	 if(opt_to_assignments)
	 {
	 remember_all_variables();
	 eval(n->u.s.b.node);
	 compare_all_variables_and_make_assignments();
	 
	 }
	 */
     
    }

    if(!n->u.s.a.node)
    {
      switch(n->type)
      {
      case TYPE_NUMBER:
	free_node(n);
	return mkintnode(0);

      case TYPE_STRING:
	free_node(n);
	return mkstrnode(make_shared_string(""));

      }
    }
    if(n->type==(n->u.s.a.node->type & TYPE_MOD_MASK))
    {
      tmp1=n->u.s.a.node;
      n->u.s.a.node=0;
      free_node(n);
      return optimize(tmp1,needlval,parent);
    }
    if(n->type==TYPE_FUNCTION && n->u.s.a.node->token==F_STRING)
    {
      int i;
      if(-1!=(i=defined_function(n->u.s.a.node->u.s.a.str)))
      {
	free_node(n);
	return mkconstfunnode(i);
      }
    }
    break;

  case F_FOR:
  {
    node **last;
    int inc,less;
    if(node_is_false(n->u.s.a.node))
    {
      free_node(n);
      return NULL;
    }

    if(n->u.s.a.node &&
       (n->u.s.a.node->token==F_INC ||
	n->u.s.a.node->token==F_DEC ||
	n->u.s.a.node->token==F_POST_INC ||
	n->u.s.a.node->token==F_POST_DEC) &&
       (!n->u.s.b.node->u.s.a.node &&
	!(n->u.s.b.node->u.s.b.node->opt_info & OPT_CONT)))
    {
      if(n->u.s.a.node->token==F_DEC || n->u.s.a.node->token==F_POST_DEC)
	n->token=F_DEC_LOOP;
      else
	n->token=F_INC_LOOP;
      n->u.s.a.node->token=' ';
      n->u.s.a.node->u.s.b.node=n->u.s.a.node->u.s.a.node;
      n->u.s.a.node->u.s.a.node=mkintnode(0);
      n->u.s.a.node->opt_info&=~(OPT_OPT_COMPUTED|OPT_OPTIMIZED);
      n->u.s.b.node->token='{';
      n->u.s.b.node->opt_info&=~(OPT_OPT_COMPUTED|OPT_OPTIMIZED);
      n->opt_info&=~(OPT_OPT_COMPUTED|OPT_OPTIMIZED);
      return optimize(n,needlval,parent);
    }

    last=&(n->u.s.b.node->u.s.b.node);
    tmp1=last?*last:(node *)NULL;
    if(tmp1 && (tmp1->token==F_INC ||
		tmp1->token==F_POST_INC ||
		tmp1->token==F_INC_AND_POP ||
		tmp1->token==F_DEC_AND_POP ||
		tmp1->token==F_DEC ||
		tmp1->token==F_POST_DEC))
    {
      if(tmp1->token==F_INC || tmp1->token==F_POST_INC ||
	 tmp1->token==F_INC_AND_POP)
	inc=1;
      else
	inc=0;

      less=0;
      if(n->u.s.a.node->token!=F_GT &&
	 n->u.s.a.node->token!=F_GE &&
	 n->u.s.a.node->token!=F_LE &&
	 n->u.s.a.node->token!=F_NE &&
	 n->u.s.a.node->token!=F_LT)
	break;

      if(!node_is_eq(n->u.s.a.node->u.s.a.node,tmp1->u.s.a.node) ||
	 depend_p(n->u.s.a.node->u.s.b.node,n->u.s.b.node->u.s.a.node) ||
	 depend_p(n->u.s.a.node->u.s.b.node,n->u.s.a.node->u.s.a.node))
      {
	if(node_is_eq(n->u.s.a.node->u.s.b.node,tmp1->u.s.a.node) &&
	   !depend_p(n->u.s.a.node->u.s.a.node,n->u.s.b.node->u.s.a.node) &&
	   !depend_p(n->u.s.a.node->u.s.a.node,n->u.s.a.node->u.s.b.node))
	{
	  switch(n->u.s.a.node->token)
	  {
	  case F_LT: n->u.s.a.node->token=F_GT; break;
	  case F_LE: n->u.s.a.node->token=F_GT; break;
	  case F_GT: n->u.s.a.node->token=F_LE; break;
	  case F_GE: n->u.s.a.node->token=F_LE; break;
	  }
	  tmp2=n->u.s.a.node->u.s.a.node;
	  n->u.s.a.node->u.s.a.node=n->u.s.a.node->u.s.b.node;
	  n->u.s.a.node->u.s.b.node=tmp2;
	}else{
	  break;
	}
      }
      if(inc && n->u.s.a.node->token==F_LE)
	tmp3=mknode(F_ADD,n->u.s.a.node->u.s.b.node,mkintnode(1));
      else if(inc && n->u.s.a.node->token==F_LT)
	tmp3=n->u.s.a.node->u.s.b.node;
      else if(!inc && n->u.s.a.node->token==F_GE)
	tmp3=mknode(F_SUBTRACT,n->u.s.a.node->u.s.b.node,mkintnode(1));
      else if(!inc && n->u.s.a.node->token==F_GT)
	tmp3=n->u.s.a.node->u.s.b.node;
      else
	break;

      *last=0;
      if(n->u.s.a.node->token==F_NE)
      {
	if(inc)
	{
	  tmp2=mknode(F_INC_NEQ_LOOP,mknode(' ',tmp3,n->u.s.a.node->u.s.a.node),n->u.s.b.node->u.s.a.node);
	}else{
	  tmp2=mknode(F_DEC_NEQ_LOOP,mknode(' ',tmp3,n->u.s.a.node->u.s.a.node),n->u.s.b.node->u.s.a.node);
	}
      }else{
	if(inc)
	{
	  tmp2=mknode(F_INC_LOOP,mknode(' ',tmp3,n->u.s.a.node->u.s.a.node),n->u.s.b.node->u.s.a.node);
	}else{
	  tmp2=mknode(F_DEC_LOOP,mknode(' ',tmp3,n->u.s.a.node->u.s.a.node),n->u.s.b.node->u.s.a.node);
	}
      }
      n->u.s.a.node->u.s.a.node=0;
      n->u.s.a.node->u.s.b.node=0;
      n->u.s.b.node->u.s.a.node=0;
      n->u.s.b.node->u.s.b.node=0;
      free_node(n);
      if(inc)
      {
	tmp1->token=F_DEC;
      }else{
	tmp1->token=F_INC;
      }
      tmp1=mknode('{',mkcastnode(TYPE_VOID,tmp1),tmp2);
      return optimize(tmp1,needlval,parent);
    }
  }
    break;

  case F_ADD_EQ:
    if(n->u.s.a.node->type==TYPE_NUMBER &&
       n->u.s.b.node->token==F_NUMBER && n->u.s.b.node->u.s.a.number==1)
    {
      tmp1=mknode(F_INC,n->u.s.a.node,0);
      n->u.s.a.node=0;
      free_node(n);
      return optimize(tmp1,needlval,parent);
    }

    if(n->u.s.a.node->type==TYPE_NUMBER &&
       n->u.s.b.node->token==F_NUMBER && n->u.s.b.node->u.s.a.number==-1)
    {
      tmp1=mknode(F_DEC,n->u.s.a.node,0);
      n->u.s.a.node=0;
      free_node(n);
      return optimize(tmp1,needlval,parent);
    }
    break;

  case F_SUB_EQ:
    if(n->u.s.a.node->type==TYPE_NUMBER &&
       n->u.s.b.node->token==F_NUMBER && n->u.s.b.node->u.s.a.number==1)
    {
      tmp1=mknode(F_DEC,n->u.s.a.node,0);
      n->u.s.a.node=0;
      free_node(n);
      return optimize(tmp1,needlval,parent);
    }

    if(n->u.s.a.node->type==TYPE_NUMBER &&
       n->u.s.b.node->token==F_NUMBER && n->u.s.b.node->u.s.a.number==-1)
    {
      tmp1=mknode(F_ADD,n->u.s.a.node,0);
      n->u.s.a.node=0;
      free_node(n);
      return optimize(tmp1,needlval,parent);
    }
    break;

  case F_INC_NEQ_LOOP:
  case F_DEC_NEQ_LOOP:
  case F_INC_LOOP:
  case F_DEC_LOOP:
    if(!n->u.s.b.node)
    {
      n->u.s.a.node->token=F_ASSIGN;
      n->token=F_CAST;
      n->type=TYPE_VOID;
      n->opt_info&=~(OPT_OPT_COMPUTED|OPT_OPTIMIZED);
      n->u.s.a.node->opt_info&=~(OPT_OPT_COMPUTED|OPT_OPTIMIZED);
      n->u.s.a.node->type=n->u.s.a.node->u.s.b.node->type;
      return optimize(n,needlval,parent);
    }
    if(!((n->u.s.a.node->opt_info | n->u.s.b.node->opt_info)&
	 (OPT_SIDE_EFFECT | OPT_RETURN | OPT_CONT | OPT_BREAK)) &&
       !depend_p(n->u.s.a.node,n->u.s.b.node) &&
       !depend_p(n->u.s.b.node,n->u.s.b.node) &&
       !depend_p(n->u.s.b.node,n->u.s.a.node))
    {
      n->u.s.a.node->token=F_ASSIGN;
      n->token='{';
      n->type=TYPE_VOID;
      n->opt_info&=~(OPT_OPT_COMPUTED|OPT_OPTIMIZED);
      n->u.s.a.node->opt_info&=~(OPT_OPT_COMPUTED|OPT_OPTIMIZED);
      n->u.s.a.node->type=n->u.s.a.node->u.s.b.node->type;
      return optimize(n,needlval,parent);
    }
    break;

  case F_SWITCH:
  {
    node **tmp;
    while((tmp=last_cmd(&(n->u.s.b.node))) && (*tmp)->token==F_BREAK)
    {
      free_node(*tmp);
      *tmp=0;
    }
    if(!n->u.s.b.node)
    {
      n->token=F_CAST;
      n->type=TYPE_VOID;
      n->opt_info&=~(OPT_OPT_COMPUTED|OPT_OPTIMIZED);
      return optimize(n,needlval,parent);
    }
  }
  case F_DO:
  {
    if(node_is_true(n->u.s.b.node))
    {
      n->token=F_BRANCH;
      free_node(n->u.s.b.node);
      n->u.s.b.node=NULL;
    }
    if(node_is_false(n->u.s.b.node))
    {
      tmp1=n->u.s.a.node;
      n->u.s.a.node=NULL;
      free_node(n);
      return optimize(tmp1,needlval,parent);
    }
  }
  }

#ifdef MALLOC_DEBUG
    check_sfltable();
#endif
  if(is_const(n) &&
     (n->opt_info & OPT_TRY_OPTIMIZE) &&
     parent!=n && /* this is used to avoid doing things a zillion times */
/*     (!parent || !is_const(parent)) && I would like to do this
 * but it is not yet known if parent is const
 */
     !num_parse_error)
  {
    tmp1=eval(n);
    free_node(n);
    return tmp1;
  }
#ifdef LINMALLOC_DEBUG
  assert_node(n);
#endif
  return n;
}


/* depend_optim: nullify a node at a time and see anything depends on it
 * beeing there
 */
static int depend_optim(node **a,node *b,int chronologie,int voided,node *tree2)
{
  node *n,*n2;
  if(!(n=*a)) return 0;
#ifdef LINMALLOC_DEBUG
  assert_node(*a);
#endif
#ifdef MALLOC_DEBUG
    check_sfltable();
#endif
  if(!(n->opt_info & (OPT_SIDE_EFFECT | OPT_CASE | OPT_CONT | OPT_BREAK |
	 OPT_GLOBAL_ASSIGNMENT | OPT_RETURN)))
  {
    *a=0;
    if(!depend_p(b,n) && !depend_p(tree2,n))
    {
      free_node(n);
      return 1;
    }
    *a=n;
  }
  switch(n->token)
  {
  case F_DO:
  {
    int i;
    tree2=mknode('{',n->u.s.b.node,tree2);
    i=depend_optim(&(n->u.s.a.node),b,0,0,tree2);
    n2=tree2->u.s.a.node;
    tree2->u.s.b.node=tree2->u.s.a.node=NULL;
    free_node(tree2);
    tree2=n2;
    if(i)
    {
      n->opt_info&=~OPT_OPTIMIZED;
      return 1;
    }
    return 0;
  }

  case F_INC_NEQ_LOOP:
  case F_DEC_NEQ_LOOP:
  case F_INC_LOOP:
  case F_DEC_LOOP:
  case F_FOREACH:
  case F_FOR:
  {
    int i;
    tree2=mknode('{',n->u.s.a.node,tree2);
    i=depend_optim(&(n->u.s.b.node),b,0,0,tree2);
    n2=tree2->u.s.a.node;
    tree2->u.s.b.node=tree2->u.s.a.node=NULL;
    free_node(tree2);
    tree2=n2;
    if(i)
    {
      n->opt_info&=~OPT_OPTIMIZED;
      return 1;
    }
    return 0;
  }

  case F_CAST:
    if(n->type==TYPE_VOID)
    {
      if(depend_optim(&(n->u.s.a.node),b,chronologie,1,tree2))
      {
	n->opt_info&=~OPT_OPTIMIZED;
	return 1;
      }else{
	return 0;
      }
    }
    return 0;

  case F_ARG_LIST:
  case '{':
    if(n->token == F_ARG_LIST && !voided) return 0;
    if(chronologie)
    {
      int i;
      /* if chronological dependency is wanted, nullify and ignore
       * things that happened before this node while testing
       */
      while(depend_optim(&(n->u.s.a.node),b,chronologie,1,tree2))
	n->opt_info&=~OPT_OPTIMIZED;

      n2=n->u.s.a.node;
      n->u.s.a.node=0;
      i=depend_optim(&(n->u.s.b.node),b,chronologie,1,tree2);
      if(i) n->opt_info&=~OPT_OPTIMIZED;
      n->u.s.a.node=n2;
      return i;
    }else{
      int i;

      tree2=mknode('{',n->u.s.b.node,tree2);
      n->u.s.b.node=NULL;
      i=depend_optim(&(n->u.s.a.node),b,chronologie,1,tree2);
      n->u.s.b.node=tree2->u.s.a.node;
      n2=tree2;
      tree2=tree2->u.s.b.node;
      n2->u.s.a.node=n2->u.s.b.node=NULL;
      free_node(n2);

      tree2=mknode('{',tree2,n->u.s.a.node);
      n->u.s.a.node=NULL;
      i|=depend_optim(&(n->u.s.b.node),b,chronologie,1,tree2);
      n->u.s.a.node=tree2->u.s.b.node;
      n2=tree2;
      tree2=tree2->u.s.a.node;
      n2->u.s.b.node=n2->u.s.a.node=NULL;
      free_node(n2);
      if(i) n->opt_info&=~OPT_OPTIMIZED;
      return i;
    }
  }
  return 0;
}

/* we need to dome computations to see if the arguments are pointer arguments
 * if so, let the function depend on those, so we won't remove code that
 * changes shared arrays.
 */
static void do_depend_optim(node **n,int args)
{
  node *tree2;
  int e;
  tree2=NULL;
  for(e=0;e<args;e++)
  {
    if(type_of_locals[e]==TYPE_MAPPING ||
       type_of_locals[e]&TYPE_MOD_POINTER)
    {
      tree2=mknode(F_ARG_LIST,mklocalnode(e),tree2);
    }
  }
  tree2=call_optimizer(mknode(F_RETURN,mkefunnode(F_AGGREGATE,tree2),0));
  while(depend_optim(n,*n,1,0,tree2));
  free_node(tree2);
}

static void do_pop(int nr)
{
  if(!nr) return;
  if(nr==1)
  {
    ins_f_byte(F_POP_VALUE);
  }else if(nr<256){
    ins_f_byte(F_POP_N_ELEMS);
    ins_byte(nr,A_PROGRAM);
  }else{
    ins_f_byte(F_POP_N_ELEMS);
    ins_byte(255,A_PROGRAM);
    do_pop(nr-255);
  }
}

#define JUMP_CONDITIONAL 1
#define JUMP_UNSET 2

struct jump
{
  unsigned char flags;
  unsigned char size;
  short token;
  int whereto;
  int wherefrom;
  int address;
};

static int jump_ptr, max_jumps;
static struct jump jumps[256];

static void set_branch(int address,int whereto)
{
  int j,e;

  if(address<0) return;
  j=jump_ptr;
  e=max_jumps;
  while(e>=0)
  {
    if(jumps[j].address==address && (jumps[j].flags & JUMP_UNSET))
      break;
    if(j) j--; else j=max_jumps;
    e--;
  }
  if(e<0) j=-1; /* not found */

  for(e=0;e<=max_jumps;e++)
  {
    if(jumps[e].flags & JUMP_UNSET) continue;
    if(jumps[e].flags & JUMP_CONDITIONAL) continue;
    if(jumps[e].wherefrom!=whereto) continue;
#if defined(LASDEBUG)
    if(lasdebug>1) printf("Optimized Jump to a jump\n");
#endif
    whereto=jumps[e].whereto;
    break;
  }
  
  if(e>=0)
  {
    if(!(jumps[j].flags & JUMP_CONDITIONAL))
    {
      for(e=0;e<=max_jumps;e++)
      {
	if(jumps[e].flags & JUMP_UNSET) continue;
	if(jumps[e].size!=4) continue;
	if(jumps[e].whereto==jumps[j].wherefrom)
	{
	  upd_branch(jumps[e].address,whereto);
	  jumps[e].whereto=whereto;
#if defined(LASDEBUG)
	  if(lasdebug>1) printf("Optimized Jump to a jump\n");
#endif
	}
      }
    }
    jumps[j].whereto=whereto;
    jumps[j].flags&=~JUMP_UNSET;
  }
  upd_branch(address,whereto);
}

static int do_jump(int token,int whereto)
{
  jump_ptr=(jump_ptr+1)%NELEM(jumps);
  if(jump_ptr>max_jumps) max_jumps=jump_ptr;

  jumps[jump_ptr].flags=JUMP_UNSET;
  jumps[jump_ptr].whereto=-1;
  jumps[jump_ptr].token=token;
  jumps[jump_ptr].size=sizeof(int);

  if(token!=F_BRANCH && token!=F_SHORT_BRANCH)
    jumps[jump_ptr].flags|=JUMP_CONDITIONAL;

  if(token==F_SHORT_BRANCH ||
     token==F_SHORT_BRANCH_WHEN_ZERO ||
     token==F_SHORT_BRANCH_WHEN_NON_ZERO) /* not used (yet) */
    jumps[jump_ptr].size=1;

  jumps[jump_ptr].wherefrom=PC;
  ins_f_byte(token);
  jumps[jump_ptr].address=PC;
  ins_long(0,A_PROGRAM);
  if(whereto!=-1) set_branch(jumps[jump_ptr].address,whereto);
  return jumps[jump_ptr].address;
}

static void clean_jumptable() { max_jumps=jump_ptr=-1; }

static void push_break_stack()
{
  push_explicit((int)break_stack);
  push_explicit(current_break);
  push_explicit(break_stack_size);
  break_stack_size=10;
  break_stack=(int *)xalloc(sizeof(int)*break_stack_size);
  current_break=0;
}

static void pop_break_stack(int jump)
{
  for(current_break--;current_break>=0;current_break--)
    set_branch(break_stack[current_break],jump);

  free((char *)break_stack);
  break_stack_size=pop_address();
  current_break=pop_address();
  break_stack=(int *)pop_address();
}

static void push_continue_stack()
{
  push_explicit((int)continue_stack);
  push_explicit(current_continue);
  push_explicit(continue_stack_size);
  continue_stack_size=10;
  continue_stack=(int *)xalloc(sizeof(int)*continue_stack_size);
  current_continue=0;
}

static void pop_continue_stack(int jump)
{
  for(current_continue--;current_continue>=0;current_continue--)
    set_branch(continue_stack[current_continue],jump);

  free((char *)continue_stack);
  continue_stack_size=pop_address();
  current_continue=pop_address();
  continue_stack=(int *)pop_address();
}

static int do_docode2(node *n,int needlval);

static int do_docode(node *n,int needlval)
{
  int i;
  int save_current_line=current_line;
  if(!n) return 0;
  current_line=n->lnum;
  i=do_docode2(n,needlval);
#ifdef MALLOC_DEBUG
  check_sfltable();
#endif
  current_line=save_current_line;
  return i;
}

static int docode(node *n)
{
  clean_jumptable();
  return do_docode(n,0);
}

static int do_jump_when_non_zero(node *n,int j)
{
  if(!(n->opt_info & OPT_SIDE_EFFECT))
  {
    if(node_is_true(n))
      return do_jump(F_BRANCH,j);

    if(node_is_false(n))
      return -1;
  }
  if(do_docode(n,0)!=1)
    fatal("Infernal compiler skiterror.\n");
  return do_jump(F_BRANCH_WHEN_NON_ZERO,j);
}

static void ins_add(node *a,node *b)
{
  if(a->type==b->type)
  {
    if(a->type==TYPE_NUMBER)
      ins_f_byte(F_ADD_INT);
    else
      ins_f_byte(F_ADD);
  }else{
    ins_f_byte(F_ADD);
  }
}

static int do_jump_when_zero(node *n,int j)
{
  if(!(n->opt_info & OPT_SIDE_EFFECT))
  {
    if(node_is_true(n))
      return -1;

    if(node_is_false(n))
      return do_jump(F_BRANCH,j);
  }
  if(do_docode(n,0)!=1)
    fatal("Infernal compiler skiterror.\n");
  return do_jump(F_BRANCH_WHEN_ZERO,j);
}

static int do_docode2(node *n,int needlval)
{
  int tmp1,tmp2,tmp3;

#ifdef MALLOC_DEBUG
    check_sfltable();
#endif

  if(!n) return 0;

  if(needlval & 1)
  {
    switch(n->token)
    {
    case F_ASSIGN:
      if(needlval & 2) break;

    default:
      yyerror("Illegal lvalue.");
      ins_int(0);
      return 1;

    case F_LVALUE_LIST:
    case F_LOCAL:
    case F_GLOBAL:
    case F_INDEX:
    case F_ARG_LIST:
      break;
    }
  }
  switch(n->token)
  {
  case F_PUSH_ARRAY:
    tmp1=do_docode(n->u.s.a.node,2);
    if(tmp1!=1)
    {
      fatal("Internal compiler error, Yikes!\n");
    }
    ins_f_byte(F_PUSH_ARRAY);
    return -0x7ffffff;

  case F_STRING:
    tmp1=store_prog_string(n->u.s.a.str);
    if(tmp1<256)
    {
      ins_f_byte(F_SHORT_STRING);
      ins_byte(tmp1,A_PROGRAM);
    }else{
      ins_f_byte(F_STRING);
      ins_short(tmp1,A_PROGRAM);
    }
    return 1;

  case F_NUMBER:
    ins_int(n->u.s.a.number);
    return 1;

  case F_FLOAT:
    ins_float(n->u.s.a.fnum);
    return 1;

  case '?':
  {
    int tmp5=0;			/* make gcc happy */
    tmp1=do_jump_when_zero(n->u.s.a.node,-1);
    tmp3=do_docode(n->u.s.b.node->u.s.a.node,0);
    if(tmp3)
    {
      ins_f_byte(F_POP_N_ELEMS);
      tmp5=PC;
      ins_byte(0,A_PROGRAM);
    }
    if(n->u.s.b.node->u.s.b.node)
    {
      int tmp4;
      tmp2=do_jump(F_BRANCH,-1);
      set_branch(tmp1,PC);
      tmp4=do_docode(n->u.s.b.node->u.s.b.node,0);
      if(tmp4>tmp3)
      {
	do_pop(tmp4-tmp3);
      }else if(tmp4<tmp3){
	mem_block[A_PROGRAM].block[tmp5]=tmp3-tmp4;
	tmp3=tmp4;
      }
      tmp1=tmp2;
    }else{
      if(tmp3)
      {
	mem_block[A_PROGRAM].block[tmp5]=tmp3;
	tmp3=0;
      }
    }
    set_branch(tmp1,PC);
    return tmp3;
  }
      
  case F_AND_EQ:
  case F_OR_EQ:
  case F_XOR_EQ:
  case F_LSH_EQ:
  case F_RSH_EQ:
  case F_ADD_EQ:
  case F_SUB_EQ:
  case F_MULT_EQ:
  case F_MOD_EQ:
  case F_DIV_EQ:
    tmp1=do_docode(n->u.s.a.node,1);

    if(n->u.s.a.node->type & TYPE_MOD_POINTER)
    {
      if(do_docode(n->u.s.b.node,2)!=1)
	fatal("Internal compiler error, shit happens\n");
      ins_f_byte(F_PUSH_LTOSVAL2);
    }else{
      ins_f_byte(F_PUSH_LTOSVAL);
      if(do_docode(n->u.s.b.node,2)!=1)
	fatal("Internal compiler error, shit happens (again)\n");
    }


    switch(n->token)
    {
    case F_ADD_EQ:
      ins_add(n->u.s.a.node,n->u.s.b.node);
      break;
    case F_AND_EQ: ins_f_byte(F_AND); break;
    case F_OR_EQ:  ins_f_byte(F_OR);  break;
    case F_XOR_EQ: ins_f_byte(F_XOR); break;
    case F_LSH_EQ: ins_f_byte(F_LSH); break;
    case F_RSH_EQ: ins_f_byte(F_RSH); break;
    case F_SUB_EQ: ins_f_byte(F_SUBTRACT); break;
    case F_MULT_EQ:ins_f_byte(F_MULTIPLY);break;
    case F_MOD_EQ: ins_f_byte(F_MOD); break;
    case F_DIV_EQ: ins_f_byte(F_DIVIDE); break;
    }
    ins_f_byte(F_ASSIGN);
    return tmp1;

  case F_ASSIGN:
  case F_ASSIGN_AND_POP:
    switch(n->u.s.a.node->token)
    {
    case F_AND:
    case F_OR:
    case F_XOR:
    case F_LSH:
    case F_RSH:
    case F_ADD:
    case F_MOD:
    case F_SUBTRACT:
    case F_DIVIDE:
    case F_MULTIPLY:
      if(node_is_eq(n->u.s.b.node,n->u.s.a.node->u.s.a.node))
      {
	tmp1=do_docode(n->u.s.b.node,1);
	if((n->u.s.b.node->type & TYPE_MOD_POINTER) ||
	   (n->u.s.b.node->type & TYPE_MOD_MASK)==TYPE_ANY)
	{
	  if(do_docode(n->u.s.a.node->u.s.b.node,2)!=1)
	    fatal("Infernal compiler error (dumpar core |ver hela mattan).\n");
	  ins_f_byte(F_PUSH_LTOSVAL2);
	}else{
	  ins_f_byte(F_PUSH_LTOSVAL);
	  if(do_docode(n->u.s.a.node->u.s.b.node,2)!=1)
	    fatal("Infernal compiler error (dumpar core).\n");
	}

	if(n->u.s.a.node->token==F_ADD)
	  ins_add(n->u.s.a.node->u.s.a.node,n->u.s.a.node->u.s.b.node);
	else
	  ins_f_byte(n->u.s.a.node->token);

	ins_f_byte(n->token);
	return n->token==F_ASSIGN;
      }

    default:
      if(n->u.s.b.node->token!=F_LOCAL && n->u.s.b.node->token!=F_GLOBAL)
	tmp1=do_docode(n->u.s.b.node,1);

      if(do_docode(n->u.s.a.node,0)!=1)
	fatal("Infernal compiler error *sigh*.\n");
      switch(n->u.s.b.node->token)
      {
      case F_LOCAL:
	ins_f_byte(n->token==F_ASSIGN?F_ASSIGN_LOCAL:F_ASSIGN_LOCAL_AND_POP);
	ins_byte(n->u.s.b.node->u.s.a.number,A_PROGRAM);
	break;

      case F_GLOBAL:
	ins_f_byte(n->token==F_ASSIGN?F_ASSIGN_GLOBAL:F_ASSIGN_GLOBAL_AND_POP);
	ins_byte(n->u.s.b.node->u.s.a.number,A_PROGRAM);
	break;

      default:
	ins_f_byte(n->token);
	break;
      }
      return n->token==F_ASSIGN;
    }

  case F_LAND:
  case F_LOR:
    if(do_docode(n->u.s.a.node,0)!=1)
      fatal("Compiler internal error.\n");
    tmp1=do_jump(n->token,-1);
    if(do_docode(n->u.s.b.node,0)!=1)
      fatal("Compiler internal error.\n");
    set_branch(tmp1,PC);
    return 1;

  case F_ADD:
    tmp1=do_docode(n->u.s.a.node,2);
    if(do_docode(n->u.s.b.node,2)!=1)
      fatal("Compiler internal error.\n");
    ins_add(n->u.s.a.node,n->u.s.b.node);
    return tmp1;

  case F_EQ:
  case F_NE:
    tmp1=do_docode(n->u.s.a.node,0);
    if(do_docode(n->u.s.b.node,0)!=1)
      fatal("Compiler internal error (gnng!).\n");
    ins_f_byte(n->token);
    return tmp1;

  case F_LT:
  case F_LE:
  case F_GT:
  case F_GE:
  case F_SUBTRACT:
  case F_MULTIPLY:
  case F_DIVIDE:
  case F_MOD:
  case F_LSH:
  case F_RSH:
  case F_XOR:
  case F_OR:
  case F_AND:
    tmp1=do_docode(n->u.s.a.node,2);
    if(do_docode(n->u.s.b.node,2)!=1)
      fatal("Compiler internal error.\n");
    ins_f_byte(n->token);
    return tmp1;

  case F_RANGE:
    tmp1=do_docode(n->u.s.a.node,2);
    if(do_docode(n->u.s.b.node,2)!=2)
      fatal("Compiler internal error.\n");
    ins_f_byte(n->token);
    return tmp1;

  case F_INC:
  case F_DEC:
  case F_POST_INC:
  case F_POST_DEC:
    tmp1=do_docode(n->u.s.a.node,1);
    ins_f_byte(n->token);
    return tmp1;

  case F_INC_AND_POP:
  case F_DEC_AND_POP:
    tmp1=do_docode(n->u.s.a.node,1);
    ins_f_byte(n->token);
    return tmp1-1;

  case F_NOT:
  case F_COMPL:
  case F_NEGATE:
    tmp1=do_docode(n->u.s.a.node,2);
    ins_f_byte(n->token);
    return tmp1;

  case F_FOR:
  {
    struct vector *tmp_store;
    tmp_store=current_switch_mapping;
    current_switch_mapping=(struct vector *)NULL;
    push_break_stack();
    push_continue_stack();
    tmp1=do_jump(F_BRANCH,-1);
    tmp2=PC;
    if(n->u.s.b.node)
      do_pop(do_docode(n->u.s.b.node->u.s.a.node,0));
    pop_continue_stack(PC);
    if(n->u.s.b.node)
      do_pop(do_docode(n->u.s.b.node->u.s.b.node,0));
    set_branch(tmp1,PC);
    do_jump_when_non_zero(n->u.s.a.node,tmp2);
    pop_break_stack(PC);
    current_switch_mapping=tmp_store;
    return 0;
  }

  case ' ':
    return do_docode(n->u.s.a.node,0)+do_docode(n->u.s.b.node,1);

  case F_FOREACH:
  {
    struct vector *tmp_store;
    tmp_store=current_switch_mapping;
    current_switch_mapping=(struct vector *)NULL;
    tmp2=do_docode(n->u.s.a.node,2);
    ins_f_byte(F_CONST0);
    tmp3=do_jump(F_BRANCH,-1);
    tmp1=PC;
    push_break_stack();
    push_continue_stack();
    do_pop(do_docode(n->u.s.b.node,0));
    pop_continue_stack(PC);
    set_branch(tmp3,PC);
    do_jump(n->token,tmp1);
    pop_break_stack(PC);
    current_switch_mapping=tmp_store;
    return tmp2+1;
  }

  case F_INC_NEQ_LOOP:
  case F_DEC_NEQ_LOOP:
  case F_INC_LOOP:
  case F_DEC_LOOP:
  {
    struct vector *tmp_store;
    tmp_store=current_switch_mapping;
    current_switch_mapping=(struct vector *)NULL;
    tmp2=do_docode(n->u.s.a.node,0);
    tmp3=do_jump(F_BRANCH,-1);
    tmp1=PC;
    push_break_stack();
    push_continue_stack();
    do_pop(do_docode(n->u.s.b.node,0));
    pop_continue_stack(PC);
    set_branch(tmp3,PC);
    do_jump(n->token,tmp1);
    pop_break_stack(PC);
    current_switch_mapping=tmp_store;
    return tmp2;
  }

  case F_DO:
  {
    struct vector *tmp_store;
    tmp_store=current_switch_mapping;
    current_switch_mapping=(struct vector *)NULL;
    tmp2=PC;
    push_break_stack();
    push_continue_stack();
    do_pop(do_docode(n->u.s.a.node,0));
    pop_continue_stack(PC);
    do_jump_when_non_zero(n->u.s.b.node,tmp2);
    pop_break_stack(PC);
    current_switch_mapping=tmp_store;
    return 0;
  }

  case F_BRANCH:
  {
    struct vector *tmp_store;
    tmp_store=current_switch_mapping;
    current_switch_mapping=(struct vector *)NULL;
    tmp2=PC;
    push_break_stack();
    push_continue_stack();
    do_pop(do_docode(n->u.s.a.node,0));
    pop_continue_stack(PC);
    do_jump(F_BRANCH,tmp2);
    pop_break_stack(PC);
    current_switch_mapping=tmp_store;
    return 0;
  }

  case F_CAST:
    tmp1=do_docode(n->u.s.a.node,0);
    if(n->type==TYPE_VOID)
    {
      do_pop(tmp1);
      return 0;
    }
    if(!tmp1) { ins_f_byte(F_CONST0); tmp1=1; }
    switch(n->type)
    {
    case TYPE_STRING:
      ins_f_byte(F_CAST_TO_STRING);
      break;

    case TYPE_NUMBER:
      ins_f_byte(F_CAST_TO_INT);
      break;

    case TYPE_FLOAT:
      ins_f_byte(F_CAST_TO_FLOAT);
      break;

    case TYPE_OBJECT:
      ins_f_byte(F_CAST_TO_OBJECT);
      break;

    case TYPE_FUNCTION:
      ins_f_byte(F_CAST_TO_FUNCTION);
      break;

    case TYPE_REGULAR_EXPRESSION:
      ins_f_byte(F_CAST_TO_REGEXP);
      break;
    }
    return tmp1;

  case F_EFUN_CALL:
    if(n->u.s.a.number==F_CALL_FUNCTION)
    {
      node **a,*b;
      a=first_arg(&(n->u.s.b.node));
      if(a && a[0]->token==F_CONSTANT_FUNCTION)
      {
	ins_f_byte(F_MARK);
	b=a[0];
	a[0]=NULL;
	do_docode(n->u.s.b.node,0);
	a[0]=b;
	ins_f_byte(a[0]->u.s.a.number+F_MAX_OPCODE);
	return 1;
      }
    }

    if(instrs[n->u.s.a.number-F_OFFSET].min_arg!=instrs[n->u.s.a.number-F_OFFSET].max_arg)
    {
      ins_f_byte(F_MARK);
      do_docode(n->u.s.b.node,0);
      ins_f_byte(n->u.s.a.number);
    }else{
      if(n->u.s.b.node && BASIC_TYPE(n->u.s.b.node->type,TYPE_MAPPING))
      {
	tmp1=do_docode(n->u.s.b.node,0);
      }else{
	tmp1=do_docode(n->u.s.b.node,2);
      }

      if(tmp1>256)
        yyerror("To many arguments to function call.");

      if(tmp1<0)
        yyerror("Can't use '@' with fixed argument efun.\n");

      if(tmp1!=instrs[n->u.s.a.number-F_OFFSET].min_arg)
        fatal("GAAH, INTERNAL COMPILER ERROR.\n");
      ins_f_byte(n->u.s.a.number);
    }
    if((n->type & TYPE_MOD_MASK)==TYPE_VOID)
      return 0;
    else
      return 1;

  case '{':
    do_pop(do_docode(n->u.s.a.node,2));
    do_pop(do_docode(n->u.s.b.node,2));
    return 0;

  case F_ARG_LIST:
    return do_docode(n->u.s.a.node,needlval & ~1)+
      do_docode(n->u.s.b.node,needlval);

  case F_SWITCH:
  {
    int e,range;
    struct vector *v;
    struct vector *tmp_store=current_switch_mapping;
    if(do_docode(n->u.s.a.node,0)!=1)
      fatal("Internal compiler error, time to panic\n");        
    current_switch_mapping=allocate_mapping(allocate_array(0),
					    allocate_array(0));
    ins_f_byte(F_SWITCH);
    tmp1=PC;
    ins_short(0,A_PROGRAM);
    push_break_stack();
    do_pop(do_docode(n->u.s.b.node,2));

    v=current_switch_mapping->item[1].u.vec;
    for(range=e=0;e<v->size;e++)
    {
      char *fel,*tmp;
      switch(v->item[e].subtype)
      {
      case 0: if(range) break; continue;
      case 1: if(range++) break; continue;
      case 2: range--; continue;
      default:
	fatal("Internal error in switch mapping.\n");
      }
      switch(v->item[e].subtype==0)
      {
      default: fel="Case inside range"; break;
      case 1: fel="Ranges overlap"; break;
      }
      init_buf();
      my_strcat(fel);
      my_strcat(" at ");
      save_svalue(current_switch_mapping->item[0].u.vec->item+e);
      tmp=simple_free_buf();
      yyerror(tmp);
      free(tmp);
    }
        
    if(!current_switch_mapping->item[2].u.number)
    {
      current_switch_mapping->item[2].u.number=PC;
    }
    pop_break_stack(PC);
    upd_short(tmp1,mem_block[A_SWITCH_MAPPINGS].current_size /
	      sizeof(struct vector *));
    add_to_mem_block(A_SWITCH_MAPPINGS, (char *)&current_switch_mapping,
		     sizeof(struct vector *));
    current_switch_mapping=tmp_store;
    return 0;
  }

  case F_CASE:
  {
    extern struct svalue *sp;
    struct svalue bar;
    if(!current_switch_mapping)
    {
      yyerror("Case outside switch.");
    }else{
      if(!is_const(n->u.s.a.node))
	yyerror("Case label isn't constant.");

      tmp1=eval_low(n->u.s.a.node);
      if(tmp1<1)
      {
	yyerror("Error in case label.");
	return 0;
      }

      if(!n->u.s.b.node)
      {
	if(assoc(sp,current_switch_mapping->item[0].u.vec)>=0)
	{
	  yyerror("Duplicate case");
	}else{
	  bar.type=T_NUMBER;
	  bar.subtype=NUMBER_NUMBER;
	  bar.u.number=PC;
	  insert_alist(sp,&bar,current_switch_mapping);
	}
      }else{
	if(assoc(sp,current_switch_mapping->item[0].u.vec)>=0)
	{
	  yyerror("Duplicate case");
	}else{
	  bar.type=T_NUMBER;
	  bar.subtype=1;
	  bar.u.number=PC;
	  insert_alist(sp,&bar,current_switch_mapping);
	}
	if(!is_const(n->u.s.b.node))
	  yyerror("Case label isn't constant.");

	tmp1+=tmp2=eval_low(n->u.s.b.node);
	if(tmp2<1)
	{
	  pop_n_elems(tmp1);
	  yyerror("Error in case label.");
	  return 0;
	}

	if(sp->type!=sp[-1].type)
	  yyerror("Mixed types in case range");

	if(assoc(sp,current_switch_mapping->item[0].u.vec)>=0)
	{
	  yyerror("Duplicate case");
	}else{
	  bar.subtype=2;
	  insert_alist(sp,&bar,current_switch_mapping);
	}
      }
      pop_n_elems(tmp1);
    }
  }
    return 0;

  case F_DEFAULT:
    if(!current_switch_mapping)
    {
      yyerror("Default outside switch.");
    }else{
      if(current_switch_mapping->item[2].u.number)
      {
	yyerror("Dual Default in switch.");
      }
      current_switch_mapping->item[2].u.number=PC;
    }
    return 0;

  case F_BREAK:
    if(!break_stack)
    {
      yyerror("Break outside loop or switch.");
    }else{
      if(current_break>=break_stack_size)
      {
	break_stack_size*=2;
	break_stack=(int *)realloc((char *)break_stack,
				   sizeof(int)*break_stack_size);
      }
      break_stack[current_break++]=do_jump(F_BRANCH,-1);
    }
    return 0;

  case F_CONTINUE:
    if(!continue_stack)
    {
      yyerror("continue outside loop or switch.");
    }else{
      if(current_continue>=continue_stack_size)
      {
	continue_stack_size*=2;
	continue_stack=(int *)realloc((char *)continue_stack,
				      sizeof(int)*continue_stack_size);
      }
      continue_stack[current_continue++]=do_jump(F_BRANCH,-1);
    }
    return 0;

  case F_RETURN:
    if(n->u.s.a.node->token==F_EFUN_CALL &&
       n->u.s.a.node->u.s.a.number==F_CALL_FUNCTION)
    {
      ins_f_byte(F_MARK);
      do_docode(n->u.s.a.node->u.s.b.node,0);
      ins_f_byte(F_TAILRECURSE);
    }else{
      if(do_docode(n->u.s.a.node,0)!=1)
	ins_f_byte(F_CONST0);
      ins_f_byte(F_RETURN);
    }
    return 0;

  case F_SSCANF:
    tmp1=do_docode(n->u.s.a.node,2);
    tmp2=do_docode(n->u.s.b.node,2);
    ins_f_byte(F_SSCANF);
    ins_byte(tmp2+2,A_PROGRAM);
    return tmp1-1;

  case F_SWAP_VARIABLES:
    tmp1=do_docode(n->u.s.a.node,0);
    if(tmp1!=2)
      fatal("Infernal compiler error (skit oxo)\n");        
    ins_f_byte(F_SWAP_VARIABLES);
    return 0;

  case F_CATCH:
  {
    struct vector *tmp_store;
    tmp_store=current_switch_mapping;
    current_switch_mapping=(struct vector *)NULL;
    tmp1=do_jump(F_CATCH,-1);
    push_break_stack();
    push_continue_stack();
    do_pop(do_docode(n->u.s.a.node,0));
    pop_continue_stack(PC);
    pop_break_stack(PC);
    ins_f_byte(F_DUMB_RETURN);
    set_branch(tmp1,PC);
    current_switch_mapping=tmp_store;
    return 1;
  }

  case F_GAUGE:
  {
    struct vector *tmp_store;
    tmp_store=current_switch_mapping;
    current_switch_mapping=(struct vector *)NULL;
    ins_f_byte(F_PUSH_COST);
    do_pop(do_docode(n->u.s.a.node,2));
    ins_f_byte(F_PUSH_COST);
    ins_f_byte(F_SUBTRACT);
    ins_f_byte(F_NEGATE);
    current_switch_mapping=tmp_store;
    return 1;
  }

  case F_LVALUE_LIST:
    return do_docode(n->u.s.a.node,1)+do_docode(n->u.s.b.node,1);

  case F_INDEX:
    tmp1=do_docode(n->u.s.a.node,needlval | 2);
    if(do_docode(n->u.s.b.node,2)!=1)
      fatal("Internal compiler error, please report this.");

    if(needlval & 1)
    {
      ins_f_byte(F_PUSH_INDEXED_LVALUE);
    }else{
      ins_f_byte(F_INDEX);
      if(!(needlval & 2))
      {
	while(n && n->token==F_INDEX) n=n->u.s.a.node;
	if(n->token==F_CONSTANT)
	  ins_f_byte(F_COPY_VALUE);
      }
    }
    return tmp1;

  case F_CONSTANT:
    tmp1=store_constant(&(n->u.sval));
    if(tmp1>255)
    {
      ins_f_byte(F_CONSTANT);
      ins_short(tmp1,A_PROGRAM);
    }else{
      ins_f_byte(F_SHORT_CONSTANT);
      ins_byte(tmp1,A_PROGRAM);
    }
    if(!(needlval & 2)) /* copy later */
      if(IS_TYPE(n->u.sval,BT_VECTOR))
	ins_f_byte(F_COPY_VALUE);
    return 1;

  case F_LOCAL:
    if(needlval & 1)
    {
      ins_f_byte(F_PUSH_LOCAL_LVALUE);
      ins_byte(n->u.s.a.number,A_PROGRAM);
    }else{
      ins_f_byte(F_LOCAL);
      ins_byte(n->u.s.a.number,A_PROGRAM);
    }
    return 1;

  case F_GLOBAL:
    if(needlval & 1)
    {
      ins_f_byte(F_PUSH_GLOBAL_LVALUE);
      ins_byte(n->u.s.a.number,A_PROGRAM);
    }else{
      ins_f_byte(F_GLOBAL);
      ins_byte(n->u.s.a.number,A_PROGRAM);
    }
    return 1;

  case F_CONSTANT_FUNCTION:
    if(needlval & 1)
    {
      yyerror("Function name is not a lvalue.");
    }
    if(n->u.s.a.number<256)
    {
      ins_f_byte(F_SHORT_CONSTANT_FUNCTION);
      ins_byte(n->u.s.a.number,A_PROGRAM);
    }else{
      ins_f_byte(F_CONSTANT_FUNCTION);
      ins_short(n->u.s.a.number,A_PROGRAM);
    }
    return 1;

  case F_PUSH_SIMUL_EFUN:
    ins_f_byte(F_PUSH_SIMUL_EFUN);
    ins_short(n->u.s.a.number,A_PROGRAM);
    return 1;
    
  default:
    fatal("Infernal compiler error (unknown parse-tree-token).\n");
    return 0;			/* make gcc happy */
  }
}

void setup_fake_program()
{
  fake_prog.next_hash=0;
  fake_prog.program=mem_block[A_PROGRAM].block;
  fake_prog.program_size=mem_block[A_PROGRAM].current_size;
  fake_prog.line_numbers=(char *)mem_block[A_LINENUMBERS].block;
  fake_prog.num_line_numbers=mem_block[A_LINENUMBERS].current_size;
  fake_prog.strings=(char **)mem_block[A_STRINGS].block;
  fake_prog.num_strings=
    mem_block[A_STRINGS].current_size/sizeof(char **);
  fake_prog.constants=(struct svalue *)mem_block[A_CONSTANTS].block;
  fake_prog.num_constants=
    mem_block[A_CONSTANTS].current_size/sizeof(struct svalue);
  fake_prog.functions=(struct function *)mem_block[A_FUNCTIONS].block;
  fake_prog.num_functions=
    mem_block[A_FUNCTIONS].current_size/sizeof(struct function);
  fake_prog.function_ptrs=
    (struct function_p *)mem_block[A_FUNCTION_PTRS].block;
  fake_prog.num_function_ptrs=
    mem_block[A_FUNCTION_PTRS].current_size/sizeof(struct function_p);
  fake_prog.switch_mappings=(struct vector **)
    mem_block[A_SWITCH_MAPPINGS].block;
  fake_prog.num_switch_mappings=
    mem_block[A_SWITCH_MAPPINGS].current_size/sizeof(struct vector **);
  fake_prog.inherit=(struct inherit *)mem_block[A_INHERITS].block;
  fake_prog.num_inherited = mem_block[A_INHERITS].current_size /
    sizeof (struct inherit);
  fake_prog.inherit[0].prog=&fake_prog;
  fake_prog.lfuns=NULL;
  fake_prog.num_lfuns=0;

  fake_object.prog=&fake_prog;
  fake_object.ref=1;
}

static int eval_low(node *n)
{
  extern struct svalue *sp;
  extern int error_recovery_context_exists;

  extern jmp_buf error_recovery_context;

  extern struct object *command_giver,*current_object;
  struct object *save_command_giver = command_giver;
  struct object *save_current_object = current_object;
  extern struct svalue catch_value;
  extern struct control_stack *csp;
  extern struct program *current_prog;
  extern struct svalue *fp;

  int returnval;
  VOLATILE int foo,fo2;
  int e;
  char **bar;
  struct svalue *save_sp,*gazonk;
  int jump=PC;
  save_sp=sp;

  if(num_parse_error)
    return -1;

#ifdef MALLOC_DEBUG
    check_sfltable();
#endif

  store_linenumbers=0;
  do_docode(n,0);
  ins_f_byte(F_DUMB_RETURN);
  store_linenumbers=1;

  if(num_parse_error)
    return -1;

  foo=mem_block[A_STRINGS].current_size/sizeof(char *);
  fo2=mem_block[A_CONSTANTS].current_size/sizeof(struct svalue);
  
  push_control_stack(0);
  push_pop_error_context (1);

  csp->num_local_variables = 0; /* No extra variables */
  fp = sp - csp->num_local_variables + 1;

  setup_fake_program();
  current_prog=&fake_prog;
  current_object=&fake_object;
  /* signal catch OK - print no err msg */
  error_recovery_context_exists = 2;

  if (setjmp(error_recovery_context))
  {
    push_pop_error_context(-1);
    pop_control_stack();
    if(catch_value.type==T_STRING)
    {
      if(strcmp(strptr(&catch_value),"**"))
      {
	yyerror(strptr(&catch_value));
      }
    }else{
      yyerror("Eval error");
    }
    while(sp>save_sp) pop_stack();
    returnval=-1;
  }else{
    eval_instruction(mem_block[A_PROGRAM].block+jump);
    pop_control_stack();
    push_pop_error_context(0);
    returnval=sp-save_sp;
  }
  PC=jump;

  /* free any strings we no longer need */
  bar=(char **)mem_block[A_STRINGS].block;
  e=mem_block[A_STRINGS].current_size/sizeof(char *)-1;
  for(;e>=foo;e--) free_string(bar[e]);
  mem_block[A_STRINGS].current_size=foo*sizeof(char *);

  /* remove constants not needed */
  gazonk=(struct svalue *)mem_block[A_CONSTANTS].block;
  e=mem_block[A_CONSTANTS].current_size/sizeof(struct svalue)-1;
  for(;e>=fo2;e--) free_svalue(gazonk+e);
  mem_block[A_CONSTANTS].current_size=fo2*sizeof(struct svalue);

  command_giver=save_command_giver;
  current_object=save_current_object;
  return returnval;
}

static node *eval(node *n)
{
  node *new;
  int args;
  extern struct svalue *sp;
  if(!is_const(n)) return copy_node(n);
  args=eval_low(n);

#ifdef MALLOC_DEBUG
    check_sfltable();
#endif

  switch(args)
  {
  case -1:
    n=copy_node(n);
    break;

  case 0:
    n=NULL;
    break;

  case 1:
    new=mksvaluenode(sp);
    pop_stack();
    n=optimize(new,0,new);
    break;

  default:
    new=NULL;
    while(args--)
    {
      new=mknode(F_ARG_LIST,mksvaluenode(sp),new);
      pop_stack();
    }
    n=optimize(new,0,new);
  }
#ifdef MALLOC_DEBUG
  check_sfltable();
#endif
  return n;
}

int last_function_opt_info;

void dooptcode(node *n,int args)
{
  last_function_opt_info=OPT_SIDE_EFFECT;
#ifdef LASDEBUG
  if(lasdebug)
  {
    fprintf(stderr,"Optimizing:\n");
    nice_print_tree(n);
  }
#endif
  if(!num_parse_error)
  {
    n=call_optimizer(n);
#ifdef LASDEBUG
    if(lasdebug>1)
    {
      fprintf(stderr,"Result after optimize:\n");
      nice_print_tree(n);
    }
#endif
    do_depend_optim(&n,args);
    clear_remember();
    n=call_optimizer(n);
#ifdef LASDEBUG
    if(lasdebug)
    {
      fprintf(stderr,"Result after depend_optim:\n");
      nice_print_tree(n);
    }
#endif
  }
  if(!num_parse_error)
  {
    do_pop(docode(n));
    last_function_opt_info=n->opt_info;
  }
  free_node(n);
}

int get_opt_info()
{
  if (last_function_opt_info&(OPT_SIDE_EFFECT | OPT_GLOBAL_ASSIGNMENT))
    return TYPE_MOD_SIDE_EFFECT;
  if(last_function_opt_info & OPT_GLOBAL_DEPEND)
    return 0;
  else
    return TYPE_MOD_CONSTANT;
}