//#define TYPE_CHECKING 1
#include "bus_type.hpp"

#include <stdio.h>
#include <ctype.h>
#include <stdlib.h>
#include <string.h>
#include <stdarg.h>

#include "lisp.hpp"
#include "lisp_gc.hpp"
#ifdef NO_LIBS
#include "fakelib.hpp"
#else
#include "status.hpp"
#include "jmalloc.hpp"
#include "macs.hpp"
#include "specs.hpp"
#include "dprint.hpp"
#include "cache.hpp"
#include "dev.hpp"
#endif

/* To bypass the whole garbage collection issue of lisp I am going to have seperate spaces
   where lisp objects can reside.  Compiled code and gloabal varibles will reside in permanant
   space.  Eveything else will reside in tmp space which gets thrown away after completion of eval. 
     system functions reside in permant space.
*/

bFILE *current_print_file=NULL;
lisp_symbol *lsym_root=NULL;
long ltotal_syms=0;



char *space[4],*free_space[4];
int space_size[4],print_level=0,trace_level=0,trace_print_level=1000;
int total_user_functions;

void lprint(void *i);

int current_space;  // normally set to TMP_SPACE, unless compiling or other needs 

inline int streq(char *s1, char *s2)   // when you don't need as much as strcmp, this is faster...
{
  while (*s1)
  {
    if (*(s1++)!=*(s2++)) 
      return 0;
  }
  return (*s2==0);
}

int break_level=0;

void l1print(void *block)
{
  if (!block)
    lprint(block);
  else
  {
    if (item_type(block)==L_CONS_CELL)
    {
      dprintf("(");
      for (;block && item_type(block)==L_CONS_CELL;block=CDR(block))
      {
	void *a=CAR(block);
	if (item_type(a)==L_CONS_CELL)
	  dprintf("[...]");
	else lprint(a);
      }
      if (block)
      {
        dprintf(" . ");
	lprint(block);
      }
      dprintf(")");
    } else lprint(block);
  }
}

void where_print(int max_lev=-1)
{
  dprintf("Main program\n");   
  if (max_lev==-1) max_lev=l_ptr_stack.son;
  else if (max_lev>=l_ptr_stack.son) max_lev=l_ptr_stack.son-1;

  for (int i=0;i<max_lev;i++)
  {
    dprintf("%d> ",i);
    lprint(*l_ptr_stack.sdata[i]);
  }
}

void print_trace_stack(int max_levels)
{
  where_print(max_levels);
}

void lbreak(const char *format, ...)
{
  break_level++;
  bFILE *old_file=current_print_file;
  current_print_file=NULL;
  char st[300];
  va_list ap;
  va_start(ap, format);
  vsprintf(st,format,ap);
  va_end(ap);
  dprintf("%s\n",st);
  int cont=0;
  do
  {
    dprintf("type q to quit\n");
    dprintf("%d. Break> ",break_level);
    dgets(st,300);
    if (!strcmp(st,"c") || !strcmp(st,"cont") || !strcmp(st,"continue"))    
      cont=1;
    else if (!strcmp(st,"w") || !strcmp(st,"where"))    
      where_print();
    else if (!strcmp(st,"q") || !strcmp(st,"quit"))    
      exit(1);
    else if (!strcmp(st,"e") || !strcmp(st,"env") || !strcmp(st,"environment"))    
    {
      dprintf("Enviorment : \nnot supported right now\n");

    } else if (!strcmp(st,"h") || !strcmp(st,"help") || !strcmp(st,"?"))    
    {
      dprintf("CLIVE Debugger\n");
      dprintf(" w, where : show calling parents\n"
	      " e, env   : show enviroment\n"
	      " c, cont  : continue if possible\n"
	      " q, quit  : quits the program\n"
	      " h, help  : this\n");
    }
    else
    {
      char *s=st;
      do
      {
				void *prog=compile(s);
				p_ref r1(prog);
				while (*s==' ' || *s=='\t' || *s=='\r' || *s=='\n') s++;
				lprint(eval(prog));
      } while (*s);
    }

  } while (!cont);
  current_print_file=old_file;
  break_level--;
}

void need_perm_space(char *why)
{
  if (current_space!=PERM_SPACE && current_space!=GC_SPACE)
  {  
    lbreak("%s : action requires permanant space\n",why);
    exit(0);
  }
}

void *mark_heap(int heap)
{
  return free_space[heap];  
}

void restore_heap(void *val, int heap)
{
  free_space[heap]=(char *)val; 
}

void *lmalloc(int size, int which_space)
{      
#ifdef WORD_ALLIGN
  size=(size+3)&(~3);
#endif

  if ((char *)free_space[which_space]-(char *)space[which_space]+size>space_size[which_space])
  {
    int fart=1;
    if (which_space==PERM_SPACE)
    {
      collect_space(PERM_SPACE);
      if ((char *)free_space[which_space]-(char *)space[which_space]+size<=space_size[which_space])
        fart=0;
    } else if (which_space==TMP_SPACE)
    {
      collect_space(TMP_SPACE);
      if ((char *)free_space[which_space]-(char *)space[which_space]+size<=space_size[which_space])
        fart=0;
    }
    if (fart)
    {
      lbreak("lisp : cannot malloc %d bytes in space #%d\n",size,which_space);
      exit(0);
    }
  }
  void *ret=(void *)free_space[which_space];
  free_space[which_space]+=size;
  return ret;
}

void *eval_block(void *list)
{
  p_ref r1(list);
  void *ret=NULL;
  while (list) 
  { 
    ret=eval(CAR(list));
    list=CDR(list);
  }
  return ret;
}

lisp_1d_array *new_lisp_1d_array(ushort size, void *rest)
{
  p_ref r11(rest);
  long s=sizeof(lisp_1d_array)+size*sizeof(void *);
  if (s<8) s=8;
  void *p=(lisp_1d_array *)lmalloc(s,current_space);
  ((lisp_1d_array *)p)->type=L_1D_ARRAY;
  ((lisp_1d_array *)p)->size=size;
  void **data=(void **)(((lisp_1d_array *)p)+1);
  memset(data,0,size*sizeof(void *));
  p_ref r1(p);

  if (rest)
  {
    void *x=eval(CAR(rest));
    if (x==colon_initial_contents)
    {
      x=eval(CAR(CDR(rest)));
      data=(void **)(((lisp_1d_array *)p)+1);
      for (int i=0;i<size;i++,x=CDR(x))
      {
	if (!x) 
	{ 
	  lprint(rest); 
	  lbreak("(make-array) incorrect list length\n"); 
	  exit(0); 
	}
	data[i]=CAR(x);
      }
      if (x) { lprint(rest); lbreak("(make-array) incorrect list length\n"); exit(0); }
    }
    else if (x==colon_initial_element)
    {
      x=eval(CAR(CDR(rest)));
      data=(void **)(((lisp_1d_array *)p)+1);
      for (int i=0;i<size;i++)
        data[i]=x;
    }
    else
    {
      lprint(x);
      lbreak("Bad option argument to make-array\n");
      exit(0);
    }
  }
  
  return ((lisp_1d_array *)p);
}

lisp_fixed_point *new_lisp_fixed_point(long x)
{
  lisp_fixed_point *p=(lisp_fixed_point *)lmalloc(sizeof(lisp_fixed_point),current_space);
  p->type=L_FIXED_POINT;
  p->x=x;
  return p;
}


lisp_object_var *new_lisp_object_var(short number)
{
  lisp_object_var *p=(lisp_object_var *)lmalloc(sizeof(lisp_object_var),current_space);
  p->type=L_OBJECT_VAR;
  p->number=number;
  return p;
}


struct lisp_pointer *new_lisp_pointer(void *addr)
{
  if (addr==NULL) return NULL;
  lisp_pointer *p=(lisp_pointer *)lmalloc(sizeof(lisp_pointer),current_space);
  p->type=L_POINTER;
  p->addr=addr;
  return p;
}

struct lisp_character *new_lisp_character(unsigned short ch)
{
  lisp_character *c=(lisp_character *)lmalloc(sizeof(lisp_character),current_space);
  c->type=L_CHARACTER;
  c->ch=ch;
  return c;
}

struct lisp_string *new_lisp_string(char *string)
{
  long size=sizeof(lisp_string)+strlen(string)+1;
  if (size<8) size=8;

  lisp_string *s=(lisp_string *)lmalloc(size,current_space);
  s->type=L_STRING;
  char *sloc=((char *)s)+sizeof(lisp_string);
  strcpy(sloc,string);
  return s;
}

struct lisp_string *new_lisp_string(char *string, int length)
{
  long size=sizeof(lisp_string)+length+1;
  if (size<8) size=8;
  lisp_string *s=(lisp_string *)lmalloc(size,current_space);
  s->type=L_STRING;
  char *sloc=((char *)s)+sizeof(lisp_string);
  memcpy(sloc,string,length);
  sloc[length]=0;
  return s;
}

struct lisp_string *new_lisp_string(long length)
{
  long size=sizeof(lisp_string)+length;
  if (size<8) size=8;
  lisp_string *s=(lisp_string *)lmalloc(size,current_space);
  s->type=L_STRING;
  char *sloc=((char *)s)+sizeof(lisp_string);
  strcpy(sloc,"");
  return s;
}

#ifdef NO_LIBS
lisp_user_function *new_lisp_user_function(void *arg_list, void *block_list)
{
  p_ref r1(arg_list),r2(block_list);
  lisp_user_function *lu=(lisp_user_function *)lmalloc(sizeof(lisp_user_function),current_space);
  lu->type=L_USER_FUNCTION;
  lu->arg_list=arg_list;
  lu->block_list=block_list;
  return lu;
}
#else
lisp_user_function *new_lisp_user_function(long arg_list, long block_list)
{
  int sp=current_space;
  if (current_space!=GC_SPACE)
    current_space=PERM_SPACE;       // make sure all functions get defined in permanant space

  lisp_user_function *lu=(lisp_user_function *)lmalloc(sizeof(lisp_user_function),current_space);
  lu->type=L_USER_FUNCTION;
  lu->alist=arg_list;
  lu->blist=block_list;

  current_space=sp;

  return lu;
}
#endif


lisp_sys_function *new_lisp_sys_function(int min_args, int max_args, int fun_number)
{
  // sys functions should reside in permanant space
  lisp_sys_function *ls=(lisp_sys_function *)lmalloc(sizeof(lisp_sys_function),
						     current_space==GC_SPACE ? GC_SPACE : PERM_SPACE);
  ls->type=L_SYS_FUNCTION;
  ls->min_args=min_args;
  ls->max_args=max_args;
  ls->fun_number=fun_number;
  return ls;
}

lisp_sys_function *new_lisp_c_function(int min_args, int max_args,  long (*fun)(void *))
{
  // sys functions should reside in permanant space
  lisp_sys_function *ls=(lisp_sys_function *)lmalloc(sizeof(lisp_sys_function),
						     current_space==GC_SPACE ? GC_SPACE : PERM_SPACE);
  ls->type=L_C_FUNCTION;
  ls->min_args=min_args;
  ls->max_args=max_args;
  ls->fun=fun;
  return ls;
}

lisp_sys_function *new_lisp_c_bool(int min_args, int max_args,  long (*fun)(void *))
{
  // sys functions should reside in permanant space
  lisp_sys_function *ls=(lisp_sys_function *)lmalloc(sizeof(lisp_sys_function),
						     current_space==GC_SPACE ? GC_SPACE : PERM_SPACE);
  ls->type=L_C_BOOL;
  ls->min_args=min_args;
  ls->max_args=max_args;
  ls->fun=fun;
  return ls;
}

lisp_sys_function *new_user_lisp_function(int min_args, int max_args, int fun_number)
{
  // sys functions should reside in permanant space
  lisp_sys_function *ls=(lisp_sys_function *)lmalloc(sizeof(lisp_sys_function),
						     current_space==GC_SPACE ? GC_SPACE : PERM_SPACE);
  ls->type=L_L_FUNCTION;
  ls->min_args=min_args;
  ls->max_args=max_args;
  ls->fun_number=fun_number;
  return ls;
}

lisp_number *new_lisp_node(long num)
{
  lisp_number *n=(lisp_number *)lmalloc(sizeof(lisp_number),current_space);
  n->type=L_NUMBER;
  n->num=num;
  return n;
}

lisp_symbol *new_lisp_symbol(char *name)
{
  lisp_symbol *s=(lisp_symbol *)lmalloc(sizeof(lisp_symbol),current_space);  
  s->type=L_SYMBOL;
  s->name=new_lisp_string(name);
  s->value=l_undefined;
  s->function=l_undefined;
#ifdef L_PROFILE
  s->time_taken=0;
#endif
  return s;
}

lisp_number *new_lisp_number(long num)
{
  lisp_number *s=(lisp_number *)lmalloc(sizeof(lisp_number),current_space);
  s->type=L_NUMBER;
  s->num=num;
  return s;
}


cons_cell *new_cons_cell()
{
  cons_cell *c=(cons_cell *)lmalloc(sizeof(cons_cell),current_space);
  c->type=L_CONS_CELL;
  c->car=NULL;
  c->cdr=NULL;
  return c;
}


char *lerror(char *loc, char *cause)
{
  int lines;
  if (loc)
  {
    for (lines=0;*loc && lines<10;loc++)
    {
      if (*loc=='\n') lines++;
      dprintf("%c",*loc);
    }
    dprintf("\nPROGRAM LOCATION : \n");
  }
  if (cause)
    dprintf("ERROR MESSAGE : %s\n",cause);
  lbreak("");
  exit(0);
  return NULL;
}

void *nth(int num, void *list)
{
  if (num<0) 
  { 
    lbreak("NTH: %d is not a nonnegative fixnum and therefore not a valid index\n",num);
    exit(1);
  }

  while (list && num)
  {
    list=CDR(list);
    num--;
  }
  if (!list) return NULL;
  else return CAR(list);
}

void *lpointer_value(void *lpointer)
{
  if (!lpointer) return NULL;
#ifdef TYPE_CHECKING
  else if (item_type(lpointer)!=L_POINTER)
  {
    lprint(lpointer);
    lbreak(" is not a pointer\n");
    exit(0);
  }
#endif
  return ((lisp_pointer *)lpointer)->addr;  
}

long lnumber_value(void *lnumber)
{
  switch (item_type(lnumber))
  {
    case L_NUMBER :
      return ((lisp_number *)lnumber)->num;
    case L_FIXED_POINT :
      return (((lisp_fixed_point *)lnumber)->x)>>16;
    case L_STRING :
      return (uchar)*lstring_value(lnumber);
    case L_CHARACTER :
      return lcharacter_value(lnumber);
    default :
    {
      lprint(lnumber);
      lbreak(" is not a number\n");
      exit(0);
    }
  }
  return 0;
}

char *lstring_value(void *lstring)
{
#ifdef TYPE_CHECKING
  if (item_type(lstring)!=(ltype)L_STRING)
  {
    lprint(lstring);
    lbreak(" is not a string\n");
    exit(0);
  }
#endif
  return ((char *)lstring)+sizeof(lisp_string);
}



void *lisp_atom(void *i)
{
  if (item_type(i)==(ltype)L_CONS_CELL)
    return NULL;
  else return true_symbol;
}

void *lcdr(void *c)
{
  if (!c) return NULL;
  else if (item_type(c)==(ltype)L_CONS_CELL)
    return ((cons_cell *)c)->cdr;
  else
    return NULL;
}

void *lcar(void *c)
{
  if (!c) return NULL;
  else if (item_type(c)==(ltype)L_CONS_CELL)
    return ((cons_cell *)c)->car;
  else return NULL;
}

unsigned short lcharacter_value(void *c)
{
#ifdef TYPE_CHECKING
  if (item_type(c)!=L_CHARACTER)
  {
    lprint(c);
    lbreak("is not a character\n");
    exit(0);
  }
#endif
  return ((lisp_character *)c)->ch;
}

long lfixed_point_value(void *c)
{
  switch (item_type(c))
  {
    case L_NUMBER :
      return ((lisp_number *)c)->num<<16; break;
    case L_FIXED_POINT :
      return (((lisp_fixed_point *)c)->x); break;
    default :
    {
      lprint(c);
      lbreak(" is not a number\n");
      exit(0);
    }
  }
  return 0;
}

void *lisp_eq(void *n1, void *n2)
{
  if (!n1 && !n2) return true_symbol;	
  else if ((n1 && !n2) || (n2 && !n1)) return NULL;
  {
    int t1=*((ltype *)n1),t2=*((ltype *)n2);
    if (t1!=t2) return NULL;
    else if (t1==L_NUMBER)
    { if (((lisp_number *)n1)->num==((lisp_number *)n2)->num)
        return true_symbol;
      else return NULL;
    } else if (t1==L_CHARACTER)
    {
      if (((lisp_character *)n1)->ch==((lisp_character *)n2)->ch)
        return true_symbol;
      else return NULL;
    }
    else if (n1==n2)
      return true_symbol;
    else if (t1==L_POINTER)
      if (n1==n2) return true_symbol;
  }
  return NULL;
}

void *lget_array_element(void *a, long x)
{
#ifdef TYPE_CHECKING
  if (item_type(a)!=L_1D_ARRAY)
  {
    lprint(a);
    lbreak("is not an array\n");
    exit(0);
  }
#endif
  if (x>=((lisp_1d_array *)a)->size || x<0)
  {
    lbreak("array refrence out of bounds (%d)\n",x);
    exit(0);
  }
  return ((void **)(((lisp_1d_array *)a)+1))[x];
}

void *lisp_equal(void *n1, void *n2)
{

  if (!n1 && !n2)           // if both nil, then equal
    return true_symbol;	
  else if ((n1 && !n2) || (n2 && !n1))   // one nil, nope
    return NULL;
  else 
  {
    int t1=item_type(n1),t2=item_type(n2);
    if (t1!=t2) return NULL;
    else 
    {
      switch (t1)
      {
	case L_STRING : 
	{ if (streq(lstring_value(n1),lstring_value(n2))) return true_symbol; else return NULL; }
	break;
	case L_CONS_CELL :
	{
	  while (n1 && n2) // loop through the list and compare each element
	  {
	    if (!lisp_equal(CAR(n1),CAR(n2)))
	      return NULL;
	    n1=CDR(n1);
	    n2=CDR(n2);
	    if (n1 && *((ltype *)n1)!=L_CONS_CELL)
	      return lisp_equal(n1,n2);
	  }
	  if (n1 || n2) return NULL;   // if one is longer than the other
	  else return true_symbol;
	} break;
	default :
          return lisp_eq(n1,n2);
	break;
      }
    }
  }
}

long lisp_cos(long x)
{
  x=(x+FIXED_TRIG_SIZE/4)%FIXED_TRIG_SIZE;
  if (x<0) return sin_table[FIXED_TRIG_SIZE+x];
  else return sin_table[x];
}

long lisp_sin(long x)
{
  x=x%FIXED_TRIG_SIZE;
  if (x<0) return sin_table[FIXED_TRIG_SIZE+x];
  else return sin_table[x];
}

long lisp_atan2(long dy, long dx)
{
  if (dy==0)
  {
    if (dx>0) return 0;
    else return 180;
  } else if (dx==0)
  {
    if (dy>0) return 90;
    else return 270;
  } else
  {
    if (dx>0)
    {      
      if (dy>0)
      {
	if (abs(dx)>abs(dy))
	{
	  long a=dx*29/dy;
	  if (a>=TBS) return 0;
	  else return 45-atan_table[a];
	}
	else 
	{
	  long a=dy*29/dx;
	  if (a>=TBS) return 90;
	  else return 45+atan_table[a];
	}
      } else
      {
	if (abs(dx)>abs(dy))
	{
	  long a=dx*29/abs(dy);
	  if (a>=TBS)
	    return 0;
	  else
	    return 315+atan_table[a];
	}
	else
	{
	  long a=abs(dy)*29/dx;
	  if (a>=TBS)
	    return 260;
	  else
	    return 315-atan_table[a];
	}
      } 
    } else
    {
      if (dy>0)
      {
	if (abs(dx)>abs(dy))
	{
	  long a=-dx*29/dy;
	  if (a>=TBS)
	    return 135+45;
	  else
	    return 135+atan_table[a];
	}
	else 
	{
	  long a=dy*29/-dx;
	  if (a>=TBS)
	    return 135-45;
	  else
	    return 135-atan_table[a];
	}
      } else
      {
	if (abs(dx)>abs(dy))
	{
	  long a=-dx*29/abs(dy);
	  if (a>=TBS)
	    return 225-45;
	  else return 225-atan_table[a];
	}
	else 
	{
	  long a=abs(dy)*29/abs(dx);
	  if (a>=TBS)
	    return 225+45;	  
	  else return 225+atan_table[a];
	}
      } 
    }
  }  
}


/*
lisp_symbol *find_symbol(char *name)
{
  cons_cell *cs;
  for (cs=(cons_cell *)symbol_list;cs;cs=(cons_cell *)CDR(cs))
  {
    if (streq( ((char *)((lisp_symbol *)cs->car)->name)+sizeof(lisp_string),name))
      return (lisp_symbol *)(cs->car);   
  }
  return NULL;
}


lisp_symbol *make_find_symbol(char *name)    // find a symbol, if it doesn't exsist it is created
{
  lisp_symbol *s=find_symbol(name);
  if (s) return s;
  else 
  {
    int sp=current_space;
    if (current_space!=GC_SPACE)
      current_space=PERM_SPACE;       // make sure all symbols get defined in permanant space
    cons_cell *cs;
    cs=new_cons_cell();
    s=new_lisp_symbol(name);
    cs->car=s;
    cs->cdr=symbol_list;
    symbol_list=cs;
    current_space=sp;
  }
  return s;
}

*/

lisp_symbol *find_symbol(char *name)
{
  lisp_symbol *p=lsym_root;
  while (p)
  {
    int cmp=strcmp(name,((char *)p->name)+sizeof(lisp_string));
    if (cmp==0) return p;
    else if (cmp<0) p=p->left;
    else p=p->right;
  }
  return NULL;
}



lisp_symbol *make_find_symbol(char *name)
{
  lisp_symbol *p=lsym_root;
  lisp_symbol **parent=&lsym_root;
  while (p)
  {
    int cmp=strcmp(name,((char *)p->name)+sizeof(lisp_string));
    if (cmp==0) return p;
    else if (cmp<0) 
    { 
      parent=&p->left;
      p=p->left;
    }
    else 
    {
      parent=&p->right;
      p=p->right;
    }
  }
  int sp=current_space;
  if (current_space!=GC_SPACE)
     current_space=PERM_SPACE;       // make sure all symbols get defined in permanant space

  p=(lisp_symbol *)jmalloc(sizeof(lisp_symbol),"lsymbol");
  p->type=L_SYMBOL;
  p->name=new_lisp_string(name);

  if (name[0]==':')     // constant, set the value to ourself
    p->value=p;
  else
    p->value=l_undefined;
  p->function=l_undefined;
#ifdef L_PROFILE
  p->time_taken=0;
#endif
  p->left=p->right=NULL;
  *parent=p;
  ltotal_syms++;

  current_space=sp;
  return p;
}


void ldelete_syms(lisp_symbol *root)
{
  if (root)
  {
    ldelete_syms(root->left);
    ldelete_syms(root->right);
    jfree(root);
  }
}

void *assoc(void *item, void *list)
{
  if (item_type(list)!=(ltype)L_CONS_CELL)
    return NULL;
  else
  {
    while (list)
    {
      if (lisp_eq(CAR(CAR(list)),item))
        return lcar(list);	     
      list=(cons_cell *)(CDR(list));
    }
  }
  return NULL;
}

long list_length(void *i)
{
  long x;

#ifdef TYPE_CHECKING
  if (i && item_type(i)!=(ltype)L_CONS_CELL)
  {
    lprint(i);
    lbreak(" is not a sequence\n");
    exit(0);
  } 
#endif

  for (x=0;i;x++,i=CDR(i));
  return x;
}

	 

void *pairlis(void *list1, void *list2, void *list3)
{	  
  if (item_type(list1)!=(ltype)L_CONS_CELL || item_type(list1)!=item_type(list2))
    return NULL;

  void *ret=NULL;  
  long l1=list_length(list1),l2=list_length(list2);
  if (l1!=l2)
  {	   
    lprint(list1);
    lprint(list2);
    lbreak("... are not the same length (pairlis)\n");
    exit(0);
  }
  if (l1!=0)
  {
    void *first=NULL,*last=NULL,*cur=NULL;
    p_ref r1(first),r2(last),r3(cur);
    while (list1)
    {
      cur=new_cons_cell();
      if (!first) first=cur;
      if (last)
        ((cons_cell *)last)->cdr=cur;
      last=cur;
	      
      cons_cell *cell=new_cons_cell();	      
      ((cons_cell *)cell)->car=lcar(list1);
      ((cons_cell *)cell)->cdr=lcar(list2);
      ((cons_cell *)cur)->car=cell;

      list1=((cons_cell *)list1)->cdr;
      list2=((cons_cell *)list2)->cdr;
    }
    ((cons_cell *)cur)->cdr=list3;
    ret=first;
  } else ret=NULL;
  return ret;
}

void *lookup_symbol_function(void *symbol)
{
  return ((lisp_symbol *)symbol)->function;
}

void set_symbol_function(void *symbol, void *function)
{
  ((lisp_symbol *)symbol)->function=function;
}

void *lookup_symbol_value(void *symbol)
{
#ifdef TYPE_CHECKING
  if (((lisp_symbol *)symbol)->value!=l_undefined)
#endif
    return ((lisp_symbol *)symbol)->value;
#ifdef TYPE_CHECKING
  else 
  {
    lprint(symbol);
    lbreak(" has no value\n");
    exit(0);
  }
#endif
  return NULL;
}

void set_variable_value(void *symbol, void *value)
{
  ((lisp_symbol *) symbol)->value=value;
}

lisp_symbol *add_sys_function(char *name, short min_args, short max_args, short number)
{
  need_perm_space("add_sys_function");
  lisp_symbol *s=make_find_symbol(name);
  if (s->function!=l_undefined)
  {
    lbreak("add_sys_fucntion -> symbol %s already has a function\n",name);
    exit(0);
  }
  else s->function=new_lisp_sys_function(min_args,max_args,number);
  return s;
}

lisp_symbol *add_c_object(void *symbol, short number)
{
  need_perm_space("add_c_object");
  lisp_symbol *s=(lisp_symbol *)symbol;
  if (s->value!=l_undefined)
  {
    lbreak("add_c_object -> symbol %s already has a value\n",lstring_value(symbol_name(s)));
    exit(0);
  }
  else s->value=new_lisp_object_var(number); 
  return NULL;
}

lisp_symbol *add_c_function(char *name, short min_args, short max_args,  long (*fun)(void *))
{
  total_user_functions++;
  need_perm_space("add_c_function");
  lisp_symbol *s=make_find_symbol(name);
  if (s->function!=l_undefined)
  {
    lbreak("add_sys_fucntion -> symbol %s already has a function\n",name);
    exit(0);
  }
  else s->function=new_lisp_c_function(min_args,max_args,fun);
  return s;
}

lisp_symbol *add_c_bool_fun(char *name, short min_args, short max_args,  long (*fun)(void *))
{
  total_user_functions++;
  need_perm_space("add_c_bool_fun");
  lisp_symbol *s=make_find_symbol(name);
  if (s->function!=l_undefined)
  {
    lbreak("add_sys_fucntion -> symbol %s already has a function\n",name);
    exit(0);
  }
  else s->function=new_lisp_c_bool(min_args,max_args,fun);
  return s;
}


lisp_symbol *add_lisp_function(char *name, short min_args, short max_args, short number)
{
  total_user_functions++;
  need_perm_space("add_c_bool_fun");
  lisp_symbol *s=make_find_symbol(name);
  if (s->function!=l_undefined)
  {
    lbreak("add_sys_fucntion -> symbol %s already has a function\n",name);
    exit(0);
  }
  else s->function=new_user_lisp_function(min_args,max_args,number);
  return s;
}

void skip_c_comment(char *&s)
{
  s+=2;
  while (*s && (*s!='*' || *(s+1)!='/'))
  {
    if (*s=='/' && *(s+1)=='*')
      skip_c_comment(s);
    else s++;
  }
  if (*s) s+=2;
}

long str_token_len(char *st)
{
  long x=1;
  while (*st && (*st!='"' || st[1]=='"'))
  {
    if (*st=='\\' || *st=='"') st++;    
    st++; x++;
  }
  return x;
}

int read_ltoken(char *&s, char *buffer)
{
  // skip space
  while (*s==' ' || *s=='\t' || *s=='\n' || *s=='\r' || *s==26) s++;
  if (*s==';')  // comment
  {
    while (*s && *s!='\n' && *s!='\r' && *s!=26) s++;
    return read_ltoken(s,buffer);
  } else if  (*s=='/' && *(s+1)=='*')   // c style comment
  {
    skip_c_comment(s);
    return read_ltoken(s,buffer);    
  }
  else if (*s==0)
    return 0;
  else if (*s==')' || *s=='(' || *s=='\'' || *s=='`' || *s==',' || *s==26)
  {
    *(buffer++)=*(s++);
    *buffer=0;
  } else if (*s=='"')    // string
  {
    *(buffer++)=*(s++);          // don't read off the string because it
                                 // may be to long to fit in the token buffer
                                 // so just read the '"' so the compiler knows to scan the rest.
    *buffer=0;
  } else if (*s=='#')
  {
    *(buffer++)=*(s++);      
    if (*s!='\'')
      *(buffer++)=*(s++);      
    *buffer=0;
  } else
  {
    while (*s && *s!=')' && *s!='(' && *s!=' ' && *s!='\n' && *s!='\r' && *s!='\t' && *s!=';' && *s!=26)
      *(buffer++)=*(s++);      
    *buffer=0;
  }
  return 1;    
}


char n[MAX_LISP_TOKEN_LEN];  // assume all tokens will be < 200 characters

int end_of_program(char *s)
{
  return !read_ltoken(s,n);
}


void push_onto_list(void *object, void *&list)
{
  p_ref r1(object),r2(list);
  cons_cell *c=new_cons_cell();
  c->car=object;
  c->cdr=list;
  list=c;
}

void *comp_optimize(void *list);

void *compile(char *&s)
{
  void *ret=NULL;
  if (!read_ltoken(s,n))
    lerror(NULL,"unexpected end of program");
  if (streq(n,"nil"))
    return NULL;
  else if (toupper(n[0])=='T' && !n[1])
    return true_symbol;
  else if (n[0]=='\'')                    // short hand for quote function
  {
    void *cs=new_cons_cell(),*c2=NULL;
    p_ref r1(cs),r2(c2);

    ((cons_cell *)cs)->car=quote_symbol;
    c2=new_cons_cell();
    ((cons_cell *)c2)->car=compile(s);
    ((cons_cell *)c2)->cdr=NULL;
    ((cons_cell *)cs)->cdr=c2;
    ret=cs;
  }
  else if (n[0]=='`')                    // short hand for backquote function
  {
    void *cs=new_cons_cell(),*c2=NULL;
    p_ref r1(cs),r2(c2);

    ((cons_cell *)cs)->car=backquote_symbol;
    c2=new_cons_cell();
    ((cons_cell *)c2)->car=compile(s);
    ((cons_cell *)c2)->cdr=NULL;
    ((cons_cell *)cs)->cdr=c2;
    ret=cs;
  }  else if (n[0]==',')              // short hand for comma function
  {
    void *cs=new_cons_cell(),*c2=NULL;
    p_ref r1(cs),r2(c2);

    ((cons_cell *)cs)->car=comma_symbol;
    c2=new_cons_cell();
    ((cons_cell *)c2)->car=compile(s);
    ((cons_cell *)c2)->cdr=NULL;
    ((cons_cell *)cs)->cdr=c2;
    ret=cs;
  }
  else if (n[0]=='(')                     // make a list of everything in ()
  {
    void *first=NULL,*cur=NULL,*last=NULL;   
    p_ref r1(first),r2(cur),r3(last);
    int done=0;
    do
    {
      char *tmp=s;
      if (!read_ltoken(tmp,n))           // check for the end of the list
        lerror(NULL,"unexpected end of program");
      if (n[0]==')') 
      {
				done=1;
				read_ltoken(s,n);                // read off the ')'
      }
      else
      {     
				if (n[0]=='.' && !n[1])
				{
				  if (!first)
				    lerror(s,"token '.' not allowed here\n");	      
				  else 
				  {
				    read_ltoken(s,n);              // skip the '.'
				    ((cons_cell *)last)->cdr=compile(s);          // link the last cdr to 
				    last=NULL;
				  }
				} else if (!last && first)
				  lerror(s,"illegal end of dotted list\n");
				else
				{		 
				  cur=new_cons_cell();
				  p_ref r1(cur);
				  if (!first) first=cur;
				  ((cons_cell *)cur)->car=compile(s);	
				  if (last)
				    ((cons_cell *)last)->cdr=cur;
				  last=cur;
				}
      } 
    } while (!done);
    ret=comp_optimize(first);

  } else if (n[0]==')')
    lerror(s,"mismatched )");
  else if (isdigit(n[0]) || (n[0]=='-' && isdigit(n[1])))
  {
    lisp_number *num=new_lisp_number(0);
    sscanf(n,"%d",&num->num);
    ret=num;
  } else if (n[0]=='"')
  {
    ret=new_lisp_string(str_token_len(s));
    char *start=lstring_value(ret);
    for (;*s && (*s!='"' || s[1]=='"');s++,start++)
    {
      if (*s=='\\')
      {
				s++;
				if (*s=='n') *start='\n';
				if (*s=='r') *start='\r';
				if (*s=='t') *start='\t';
				if (*s=='\\') *start='\\';
      } else *start=*s;
      if (*s=='"') s++;
    }
    *start=0;
    s++;
  } else if (n[0]=='#')
  {
    if (n[1]=='\\')
    {
      read_ltoken(s,n);                   // read character name
      if (streq(n,"newline"))
        ret=new_lisp_character('\n');
      else if (streq(n,"space"))
        ret=new_lisp_character(' ');       
      else 
        ret=new_lisp_character(n[0]);       
    }
    else if (n[1]==0)                           // short hand for function
    {
      void *cs=new_cons_cell(),*c2=NULL;
      p_ref r4(cs),r5(c2);
      ((cons_cell *)cs)->car=make_find_symbol("function");
      c2=new_cons_cell();
      ((cons_cell *)c2)->car=compile(s);
      ((cons_cell *)cs)->cdr=c2;
      ret=cs;
    }
    else
    {
      lbreak("Unknown #\\ notation : %s\n",n);
      exit(0);
    }
  } else return make_find_symbol(n);
  return ret;
}


static void lprint_string(char *st)
{
  if (current_print_file)
  {
    for (char *s=st;*s;s++) 
    {
/*      if (*s=='\\') 
      {
	s++;
	if (*s=='n')
	  current_print_file->write_byte('\n');
	else if (*s=='r')
	  current_print_file->write_byte('\r');
	else if (*s=='t')
	  current_print_file->write_byte('\t');
	else if (*s=='\\')
	  current_print_file->write_byte('\\');
      }
      else*/
        current_print_file->write_byte(*s);
    }
  }
  else
    dprintf(st);
}

void lprint(void *i)
{
  print_level++;
  if (!i)
    lprint_string("nil");
  else
  {
    switch ((short)item_type(i))
    {      
      case L_CONS_CELL :
      {
				cons_cell *cs=(cons_cell *)i;
        lprint_string("(");
        for (;cs;cs=(cons_cell *)lcdr(cs))	
				{
				  if (item_type(cs)==(ltype)L_CONS_CELL)
				  {
			            lprint(cs->car);
				    if (cs->cdr)
				      lprint_string(" ");
				  }
				  else
				  {
				    lprint_string(". ");
				    lprint(cs);
				    cs=NULL;
				  }
				}
        lprint_string(")");
      }
      break;
      case L_NUMBER :
      {
				char num[10];
				sprintf(num,"%d",((lisp_number *)i)->num);
        lprint_string(num);
      }
      break;
      case L_SYMBOL :        
        lprint_string((char *)(((lisp_symbol *)i)->name)+sizeof(lisp_string));
      break;
      case L_USER_FUNCTION :
      case L_SYS_FUNCTION :      
        lprint_string("err... function?");
      break;
      case L_C_FUNCTION :
        lprint_string("C function, returns number\n");
      break;
      case L_C_BOOL :
        lprint_string("C boolean function\n");
      break;
      case L_L_FUNCTION :
        lprint_string("External lisp function\n");
			break;
      case L_STRING :
      {
				if (current_print_file)
				 	lprint_string(lstring_value(i));
				else
         	dprintf("\"%s\"",lstring_value(i));
      }
      break;

      case L_POINTER :
      {
				char ptr[10];
			        sprintf(ptr,"%p",lpointer_value(i));
				lprint_string(ptr);
      }
      break;
      case L_FIXED_POINT :
      { 
				char num[20];
				sprintf(num,"%g",(lfixed_point_value(i)>>16)+
					      ((lfixed_point_value(i)&0xffff))/(double)0x10000); 
				lprint_string(num);
      } break;
      case L_CHARACTER :
      {
				if (current_print_file)
				{
				  uchar ch=((lisp_character *)i)->ch;
				  current_print_file->write(&ch,1);
				} else
				{
				  unsigned short ch=((lisp_character *)i)->ch;
				  dprintf("#\\");
				  switch (ch)
				  {
				    case '\n' : 
				    { dprintf("newline"); break; }
				    case ' ' : 
				    { dprintf("space"); break; }
				    default :
				      dprintf("%c",ch);
				  }
				}       
      } break;
      case L_OBJECT_VAR :
      {
				l_obj_print(((lisp_object_var *)i)->number);
      } break;
      case L_1D_ARRAY :
      {
				lisp_1d_array *a=(lisp_1d_array *)i;
				void **data=(void **)(a+1);
				dprintf("#(");
				for (int j=0;j<a->size;j++)
				{
				  lprint(data[j]);
				  if (j!=a->size-1)
				    dprintf(" ");
				}
				dprintf(")");
      } break;
      case L_COLLECTED_OBJECT :
      {
				lprint_string("GC_refrence->");
				lprint(((lisp_collected_object *)i)->new_reference);
      } break;
      default :
        dprintf("Shouldn't happen\n");
    }
  }
  print_level--;
  if (!print_level && !current_print_file)
    dprintf("\n");
}

void *eval(void *prog);

void *eval_sys_function(lisp_sys_function *fun, void *arg_list);

#ifdef L_PROFILE
static int prof_level=0;
#endif

void *eval_function(lisp_symbol *sym, void *arg_list)
{


#ifdef TYPE_CHECKING  
  int args,req_min,req_max;

  if (item_type(sym)!=L_SYMBOL)
  {
    lprint(sym);
    lbreak("EVAL : is not a function name (not symbol either)");
    exit(0);
  } 
#endif

#ifdef L_PROFILE
  time_marker start;
  prof_level++;
#endif  

  void *fun=(lisp_sys_function *)(((lisp_symbol *)sym)->function);
  p_ref ref2( fun  );

  // make sure the arguments given to the function are the correct number
  ltype t=item_type(fun);

#ifdef TYPE_CHECKING
  switch (t)
  {
    case L_SYS_FUNCTION :
    case L_C_FUNCTION :
    case L_C_BOOL :
    case L_L_FUNCTION :    
    {
      req_min=((lisp_sys_function *)fun)->min_args;
      req_max=((lisp_sys_function *)fun)->max_args;
    } break;
    case L_USER_FUNCTION :
    {

#ifdef L_PROFILE
      time_marker end;
      if (prof_level<3)
        ((lisp_symbol *)sym)->time_taken+=end.diff_time(&start);
      prof_level--;
#endif  
      return eval_user_fun(sym,arg_list);
    } break;
    default :
    {
      lprint(sym);
      lbreak(" is not a function name");
      exit(0);	
    } break;
  }

  if (req_min!=-1)
  {
    void *a=arg_list;
    for (args=0;a;a=CDR(a)) args++;    // count number of paramaters

    if (args<req_min)
    {
      lprint(arg_list);
      lprint(sym->name);
      lbreak("\nToo few parameters to function\n");
      exit(0);
    } else if (req_max!=-1 && args>req_max)
    {
      lprint(arg_list);
      lprint(sym->name);
      lbreak("\nToo many parameters to function\n");
      exit(0);
    }
  }
#endif


  p_ref ref1(arg_list);
  void *ret=NULL;

  switch (t)
  {
    case L_SYS_FUNCTION :
    { ret=eval_sys_function( ((lisp_sys_function *)fun),arg_list); } break;    
    case L_L_FUNCTION :
    { ret=l_caller( ((lisp_sys_function *)fun)->fun_number,arg_list); } break;
    case L_USER_FUNCTION :
    {
#ifdef L_PROFILE
      time_marker end;
      if (prof_level<3)
        ((lisp_symbol *)sym)->time_taken+=end.diff_time(&start);
      prof_level--;
#endif  
      return eval_user_fun(sym,arg_list);
    } break;
    case L_C_FUNCTION :
    {
      void *first=NULL,*cur=NULL;
      p_ref r1(first),r2(cur);
      while (arg_list)
      {
				if (first)
				  cur=((cons_cell *)cur)->cdr=new_cons_cell();
				else
				  cur=first=new_cons_cell();
			
				void *val=eval(CAR(arg_list));
				((cons_cell *)cur)->car=val;
				arg_list=lcdr(arg_list);
      }        
      ret=new_lisp_number(((lisp_sys_function *)fun)->fun(first));
    } break;
    case L_C_BOOL :
    {
      void *first=NULL,*cur=NULL;
      p_ref r1(first),r2(cur);
      while (arg_list)
      {
				if (first)
				  cur=((cons_cell *)cur)->cdr=new_cons_cell();
				else
				  cur=first=new_cons_cell();
			
				void *val=eval(CAR(arg_list));
				((cons_cell *)cur)->car=val;
				arg_list=lcdr(arg_list);
      }        

      if (((lisp_sys_function *)fun)->fun(first))
        ret=true_symbol;
      else ret=NULL;
    } break;
    default :
      dprintf("not a fun, sholdn't happed\n");
  }

#ifdef L_PROFILE
  time_marker end;
  if (prof_level<3)
    ((lisp_symbol *)sym)->time_taken+=end.diff_time(&start);
  prof_level--;
#endif  


  return ret;
}	  

#ifdef L_PROFILE
void pro_print(bFILE *out, lisp_symbol *p)
{
  if (p)
  {
    pro_print(out,p->right);
    {
      char st[100];
      sprintf(st,"%20s %f\n",lstring_value(symbol_name(p)),((lisp_symbol *)p)->time_taken);
      out->write(st,strlen(st));
    }
    pro_print(out,p->left);
  }
}

void preset(lisp_symbol *root)
{
  if (root)
  {
    preset(root->right);
    preset(root->left);
    root->time_taken=0;
  }
}

void preport(char *fn)
{
  bFILE *fp=open_file("preport.out","wb");
  pro_print(fp,lsym_root);
  delete fp;
}
#endif

void *mapcar(void *arg_list)
{
  p_ref ref1(arg_list);
  void *sym=eval(CAR(arg_list));
  switch ((short)item_type(sym))
  {
    case L_SYS_FUNCTION :
    case L_USER_FUNCTION :
    case L_SYMBOL :
    break;
    default :
    {
      lprint(sym);
      lbreak(" is not a function\n");
      exit(0);
    }
  }
  int num_args=list_length(CDR(arg_list)),i,stop=0;
  if (!num_args) return 0;

  void **arg_on=(void **)jmalloc(sizeof(void *)*num_args,"mapcar tmp array");
  cons_cell *list_on=(cons_cell *)CDR(arg_list);
  long old_ptr_son=l_ptr_stack.son;

  for (i=0;i<num_args;i++)
  {
    arg_on[i]=(cons_cell *)eval(CAR(list_on));
    l_ptr_stack.push(&arg_on[i]);

    list_on=(cons_cell *)CDR(list_on);
    if (!arg_on[i]) stop=1;
  }
  
  if (stop)
  {
    jfree(arg_on);
    return NULL;
  }

  cons_cell *na_list=NULL,*return_list=NULL,*last_return;

  do
  {
    na_list=NULL;          // create a cons list with all of the parameters for the function

    cons_cell *first;                       // save the start of the list
    for (i=0;!stop &&i<num_args;i++)
    {
      if (!na_list)
        first=na_list=new_cons_cell();
      else
      {
        na_list->cdr=new_cons_cell();
				na_list=(cons_cell *)CDR(na_list);
      }

      
      if (arg_on[i])
      {
				na_list->car=CAR(arg_on[i]);
				arg_on[i]=(cons_cell *)CDR(arg_on[i]);
      }
      else stop=1;        
    }
    if (!stop)
    {
      cons_cell *c=new_cons_cell();
      c->car=eval_function((lisp_symbol *)sym,first);
      if (return_list)
        last_return->cdr=c;
      else
        return_list=c;
      last_return=c;
    }
  }
  while (!stop);
  l_ptr_stack.son=old_ptr_son;

  jfree(arg_on);
  return return_list;
}

void *concatenate(void *prog_list)
{
  void *el_list=CDR(prog_list);
  p_ref ref1(prog_list),ref2(el_list);
  void *ret=NULL;
  void *rtype=eval(CAR(prog_list));

  long len=0;                                // determin the length of the resulting string
  if (rtype==string_symbol)
  {
    int elements=list_length(el_list);       // see how many things we need to concat
    if (!elements) ret=new_lisp_string("");
    else
    {
      void **str_eval=(void **)jmalloc(elements*sizeof(void *),"tmp eval array");
      int i,old_ptr_stack_start=l_ptr_stack.son;

      // evalaute all the strings and count their lengths
      for (i=0;i<elements;i++,el_list=CDR(el_list))
      {
        str_eval[i]=eval(CAR(el_list));
	l_ptr_stack.push(&str_eval[i]);

	switch ((short)item_type(str_eval[i]))
	{
	  case L_CONS_CELL :
	  {
	    cons_cell *char_list=(cons_cell *)str_eval[i];
	    while (char_list)
	    {
	      if (item_type(CAR(char_list))==(ltype)L_CHARACTER)
	        len++;
	      else
	      {
		lprint(str_eval[i]);
		lbreak(" is not a character\n");		
		exit(0);
	      }
	      char_list=(cons_cell *)CDR(char_list);
	    }
	  } break;
	  case L_STRING : len+=strlen(lstring_value(str_eval[i])); break;
	  default :
	    lprint(prog_list);
	    lbreak("type not supported\n");
	    exit(0);
	  break;

	}
      }
      lisp_string *st=new_lisp_string(len+1);
      char *s=lstring_value(st);

      // now add the string up into the new string
      for (i=0;i<elements;i++)
      {
	switch ((short)item_type(str_eval[i]))
	{
	  case L_CONS_CELL :
	  {
	    cons_cell *char_list=(cons_cell *)str_eval[i];
	    while (char_list)
	    {
	      if (item_type(CAR(char_list))==L_CHARACTER)
	        *(s++)=((lisp_character *)CAR(char_list))->ch;
	      char_list=(cons_cell *)CDR(char_list);
	    }
	  } break;
	  case L_STRING : 
	  {
	    memcpy(s,lstring_value(str_eval[i]),strlen(lstring_value(str_eval[i])));
	    s+=strlen(lstring_value(str_eval[i]));
	  } break;
	  default : ;     // already checked for, but make compiler happy
	}
      }
      jfree(str_eval);
      l_ptr_stack.son=old_ptr_stack_start;   // restore pointer GC stack
      *s=0;      
      ret=st;
    }
  }
  else 
  {
    lprint(prog_list);
    lbreak("concat operation not supported, try 'string\n");
    exit(0);
  }
  return ret;
}


void *backquote_eval(void *args)
{
  if (item_type(args)!=L_CONS_CELL)
    return args;
  else if (args==NULL)
    return NULL;
  else if ((lisp_symbol *) (((cons_cell *)args)->car)==comma_symbol)
    return eval(CAR(CDR(args)));
  else
  {
    void *first=NULL,*last=NULL,*cur=NULL;
    p_ref ref1(first),ref2(last),ref3(cur),ref4(args);
    while (args)
    {
      if (item_type(args)==L_CONS_CELL)
      {
	if (CAR(args)==comma_symbol)               // dot list with a comma?
	{
	  ((cons_cell *)last)->cdr=eval(CAR(CDR(args)));
	  args=NULL;
	}
	else
	{
	  cur=new_cons_cell();
	  if (first)
	    ((cons_cell *)last)->cdr=cur;
	  else 
            first=cur;
	  last=cur;
          ((cons_cell *)cur)->car=backquote_eval(CAR(args));
 	  args=CDR(args);
	}
      } else
      {
	((cons_cell *)last)->cdr=backquote_eval(args);
	args=NULL;
      }

    }
    return (void *)first;
  }
  return NULL;       // for stupid compiler messages
}


void *eval_sys_function(lisp_sys_function *fun, void *arg_list)
{
  p_ref ref1(arg_list);
  void *ret=NULL;
  switch (fun->fun_number)
  {
    case 0 :                                                    // print
    { 
      ret=NULL;
      while (arg_list)
      {
        ret=eval(CAR(arg_list));  arg_list=CDR(arg_list);
	lprint(ret); 
      }
      return ret; 
    } break;
    case 1 :                                                    // car
    { ret=lcar(eval(CAR(arg_list))); } break;
    case 2 :                                                    // cdr
    { ret=lcdr(eval(CAR(arg_list))); } break;
    case 3 :                                                    // length
    { 
      void *v=eval(CAR(arg_list));
      switch (item_type(v))
      { 
	case L_STRING : ret=new_lisp_number(strlen(lstring_value(v))); break;
	case L_CONS_CELL : ret=new_lisp_number(list_length(v)); break;
	default :
	{ lprint(v);
	  lbreak("length : type not supported\n");
	}
      }
    } break;						
    case 4 :                                                    // list
    { 
      void *cur=NULL,*last=NULL,*first=NULL;
      p_ref r1(cur),r2(first),r3(last);
      while (arg_list)
      {
	cur=new_cons_cell();
	void *val=eval(CAR(arg_list));
	((cons_cell *) cur)->car=val;
	if (last)
	  ((cons_cell *)last)->cdr=cur;
	else first=cur;
	last=cur;
	arg_list=(cons_cell *)CDR(arg_list);
      }	  
      ret=first; 
    } break;
    case 5 :                                             // cons
    { void *c=new_cons_cell(); 
      p_ref r1(c);
      void *val=eval(CAR(arg_list)); 
      ((cons_cell *)c)->car=val;
      val=eval(CAR(CDR(arg_list))); 
      ((cons_cell *)c)->cdr=val;
      ret=c;
    } break;
    case 6 :                                             // quote
    ret=CAR(arg_list);
    break;
    case 7 :                                             // eq
    {
      l_user_stack.push(eval(CAR(arg_list)));
      l_user_stack.push(eval(CAR(CDR(arg_list))));
      ret=lisp_eq(l_user_stack.pop(1),l_user_stack.pop(1));
    } break;
    case 24 :                                             // equal
    {
      l_user_stack.push(eval(CAR(arg_list)));
      l_user_stack.push(eval(CAR(CDR(arg_list))));
      ret=lisp_equal(l_user_stack.pop(1),l_user_stack.pop(1));
    } break;
    case 8 :                                           // +
    {
      long sum=0;
      while (arg_list)
      {
	sum+=lnumber_value(eval(CAR(arg_list)));
	arg_list=CDR(arg_list);
      }
      ret=new_lisp_number(sum);
    }
    break;
    case 28 :                                          // *
    {
      long sum;
      void *first=eval(CAR(arg_list));
      p_ref r1(first);
      if (arg_list && item_type(first)==L_FIXED_POINT)
      {
	sum=1<<16;
	do
	{
	  sum=(sum>>8)*(lfixed_point_value(first)>>8);
	  arg_list=CDR(arg_list);
	  if (arg_list) first=eval(CAR(arg_list));
	} while (arg_list);

	ret=new_lisp_fixed_point(sum);
      } else
      { sum=1;
	do
	{
	  sum*=lnumber_value(eval(CAR(arg_list)));
	  arg_list=CDR(arg_list);
	  if (arg_list) first=eval(CAR(arg_list));
	} while (arg_list);
	ret=new_lisp_number(sum);
      }
    }
    break;
    case 29 :                                           // /
    {
      long sum=0,first=1;
      while (arg_list)
      {
	void *i=eval(CAR(arg_list));
	p_ref r1(i);
	if (item_type(i)!=L_NUMBER)
	{
	  lprint(i);
	  lbreak("/ only defined for numbers, cannot divide ");
	  exit(0);
	} else if (first) 
	{
	  sum=((lisp_number *)i)->num;
	  first=0;
	}
	else sum/=((lisp_number *)i)->num;
	arg_list=CDR(arg_list);
      }
      ret=new_lisp_number(sum);
    }
    break;
    case 9 :                                           // -
    {
      long x=lnumber_value(eval(CAR(arg_list)));         arg_list=CDR(arg_list);
      while (arg_list)
      {
	x-=lnumber_value(eval(CAR(arg_list)));
	arg_list=CDR(arg_list);
      }
      ret=new_lisp_number(x);
    }
    break;
    case 10 :                                         // if
    {
      if (eval(CAR(arg_list)))
      ret=eval(CAR(CDR(arg_list)));
      else 
      { arg_list=CDR(CDR(arg_list));                 // check for a else part
	if (arg_list)	
	  ret=eval(CAR(arg_list));
	else ret=NULL;
      }
    } break;
    case 63 :
    case 11 :                                         // setf
    {     
      void *set_to=eval(CAR(CDR(arg_list))),*i=NULL;
      p_ref r1(set_to),r2(i);
      i=CAR(arg_list);

      ltype x=item_type(set_to);
      switch (item_type(i))
      {
	case L_SYMBOL :
	{
	  switch (item_type (((lisp_symbol *)i)->value))
	  {
	    case L_NUMBER :
	    { 
	      if (x==L_NUMBER && ((lisp_symbol *)i)->value!=l_undefined)
	      ((lisp_number *)(((lisp_symbol *)i)->value))->num=lnumber_value(set_to);
	      else 
	      ((lisp_symbol *)i)->value=set_to;
	    } break;
	    case L_OBJECT_VAR :
	    {
	      l_obj_set(((lisp_object_var *)(((lisp_symbol *)i)->value))->number,set_to);  
	    } break;
	    default :
	    ((lisp_symbol *)i)->value=set_to;
	  }
	  ret=((lisp_symbol *)i)->value;
	} break;
	case L_CONS_CELL :   // this better be an 'aref'
	{
#ifdef TYPE_CHECKING
	  void *car=((cons_cell *)i)->car;
	  if (car==car_symbol)
	  {
	    car=eval(CAR(CDR(i)));
	    if (!car || item_type(car)!=L_CONS_CELL)
	    { lprint(car); lbreak("setq car : evaled object is not a cons cell\n"); exit(0); }
	    ((cons_cell *)car)->car=set_to;
	  } else if (car==cdr_symbol)
	  {
	    car=eval(CAR(CDR(i)));
	    if (!car || item_type(car)!=L_CONS_CELL)
	    { lprint(car); lbreak("setq cdr : evaled object is not a cons cell\n"); exit(0); }
	    ((cons_cell *)car)->cdr=set_to;
	  } else if (car==aref_symbol)
	  {
#endif
	    void *a=(lisp_1d_array *)eval(CAR(CDR(i)));
	    p_ref r1(a);
#ifdef TYPE_CHECKING
	    if (item_type(a)!=L_1D_ARRAY)
	    {
	      lprint(a);
	      lbreak("is not an array (aref)\n");
	      exit(0);
	    }
#endif
	    long num=lnumber_value(eval(CAR(CDR(CDR(i)))));
#ifdef TYPE_CHECKING
	    if (num>=((lisp_1d_array *)a)->size || num<0)
	    {
	      lbreak("aref : value of bounds (%d)\n",num);
	      exit(0);
	    }
#endif
	    void **data=(void **)(((lisp_1d_array *)a)+1);
	    data[num]=set_to;
#ifdef TYPE_CHECKING
	  } else
	  {
	    lbreak("expected (aref, car, cdr, or symbol) in setq\n");
	    exit(0);
	  } 
#endif
	  ret=set_to;
	} break;

	default :
	{
	  lprint(i);
	  lbreak("setq/setf only defined for symbols and arrays now..\n");
	  exit(0);
	} 
      }
    } break;
    case 12 :                                      // symbol-list
      ret=NULL;
    break;
    case 13 :                                      // assoc
    {
      void *item=eval(CAR(arg_list));
      p_ref r1(item);
      void *list=(cons_cell *)eval(CAR(CDR(arg_list)));
      p_ref r2(list);
      ret=assoc(item,(cons_cell *)list);
    } break;
    case 20 :                                       // not is the same as null
    case 14 :                                       // null
    if (eval(CAR(arg_list))==NULL) ret=true_symbol; else ret=NULL;
    break;
    case 15 :                                       // acons
    {
      void *i1=eval(CAR(arg_list)),*i2=eval(CAR(CDR(arg_list)));
      p_ref r1(i1);
      cons_cell *cs=new_cons_cell();
      cs->car=i1;
      cs->cdr=i2;
      ret=cs;
    } break;

    case 16 :                                       // pairlis
    {	  
      l_user_stack.push(eval(CAR(arg_list))); arg_list=CDR(arg_list);
      l_user_stack.push(eval(CAR(arg_list))); arg_list=CDR(arg_list);
      void *n3=eval(CAR(arg_list));
      void *n2=l_user_stack.pop(1);
      void *n1=l_user_stack.pop(1);      
      ret=pairlis(n1,n2,n3);
    } break;
    case 17 :                                      // let
    {
      // make an a-list of new variable names and new values
      void *var_list=CAR(arg_list),
           *block_list=CDR(arg_list);
      p_ref r1(block_list),r2(var_list);
      long stack_start=l_user_stack.son;

      while (var_list)
      {
	void *var_name=CAR(CAR(var_list));
#ifdef TYPE_CHECKING
	if (item_type(var_name)!=L_SYMBOL)
	{
	  lprint(var_name);
	  lbreak("should be a symbol (let)\n");
	  exit(0);
	}
#endif

	l_user_stack.push(((lisp_symbol *)var_name)->value);
	((lisp_symbol *)var_name)->value=eval(CAR(CDR(CAR(var_list))));	
	var_list=CDR(var_list);
      }

      // now evaluate each of the blocks with the new enviroment and return value
      // from the last block
      while (block_list)
      {	   
	ret=eval(CAR(block_list));
	block_list=CDR(block_list);	    
      }

      long cur_stack=stack_start;
      var_list=CAR(arg_list);      // now restore the old symbol values
      while (var_list)
      {
	void *var_name=CAR(CAR(var_list));
	((lisp_symbol *)var_name)->value=l_user_stack.sdata[cur_stack++];
	var_list=CDR(var_list);
      }
      l_user_stack.son=stack_start;     // restore the stack
    }
    break;       
    case 18 :                                   // defun
    {
      void *symbol=CAR(arg_list);
#ifdef TYPE_CHECKING
      if (item_type(symbol)!=L_SYMBOL)
      {
	lprint(symbol);
	lbreak(" is not a symbol! (DEFUN)\n");
	exit(0);
      }

      if (item_type(arg_list)!=L_CONS_CELL)
      {
	lprint(arg_list);
	lbreak("is not a lambda list (DEFUN)\n");
	exit(0);
      }
#endif
      void *block_list=CDR(CDR(arg_list));

#ifndef NO_LIBS
      long a=cash.reg_lisp_block(lcar(lcdr(arg_list)));
      long b=cash.reg_lisp_block(block_list);
      lisp_user_function *ufun=new_lisp_user_function(a,b);
#else
      lisp_user_function *ufun=new_lisp_user_function(lcar(lcdr(arg_list)),block_list);
#endif
      set_symbol_function(symbol,ufun);
      ret=symbol;
    } break;
    case 19 :                                       // atom
    { ret=lisp_atom(eval(CAR(arg_list))); }
    case 21 :                                           // and
    {
      void *l=arg_list;
      p_ref r1(l);
      ret=true_symbol;
      while (l)
      {
	if (!eval(CAR(l)))
	{
	  ret=NULL;
	  l=NULL;             // short-circuit
	} else l=CDR(l);
      }
    } break;
    case 22 :                                           // or
    {
      void *l=arg_list;
      p_ref r1(l);
      ret=NULL;
      while (l)
      {
	if (eval(CAR(l)))
	{
	  ret=true_symbol;
	  l=NULL;            // short circuit
	} else l=CDR(l);
      }
    } break;
    case 23 :                                          // progn
    { ret=eval_block(arg_list); } break;
    case 25 :                                        // concatenate
      ret=concatenate(arg_list);
    break;
    case 26 :                                        // char-code
    {
      void *i=eval(CAR(arg_list));    
      p_ref r1(i);
      ret=NULL;
      switch (item_type(i))
      {
	case L_CHARACTER : 
	{ ret=new_lisp_number(((lisp_character *)i)->ch); } break;
	case L_STRING :
	{  ret=new_lisp_number(*lstring_value(i)); } break;
	default :
	{
	  lprint(i);
	  lbreak(" is not character type\n");
	  exit(0);
	}
      }		    
    } break;
    case 27 :                                        // code-char
    {
      void *i=eval(CAR(arg_list));
      p_ref r1(i);
      if (item_type(i)!=L_NUMBER)
      {
	lprint(i);
	lbreak(" is not number type\n");
	exit(0);
      }
      ret=new_lisp_character(((lisp_number *)i)->num);
    } break;
    case 30 :                                       // cond
    {
      void *block_list=CAR(arg_list);
      p_ref r1(block_list);
      if (!block_list) ret=NULL;
      else
      {
	ret=NULL;
        while (block_list)
	{
	  if (eval(lcar(CAR(block_list))))
	    ret=eval(CAR(CDR(CAR(block_list))));
	  block_list=CDR(block_list);
	}
      }
    } break;
    case 31 :                                       // select
    {
      void *selector=eval(CAR(arg_list));
      void *sel=CDR(arg_list);
      p_ref r1(selector),r2(sel);
      while (sel)
      {
	if (lisp_equal(selector,eval(CAR(CAR(sel)))))
	{
	  sel=CDR(CAR(sel));
	  while (sel)
	  {
	    ret=eval(CAR(sel));
	    sel=CDR(sel);
	  }
	  sel=NULL;
	} else sel=CDR(sel);
      }
    } break;
    case 32 :                                      // function    
      ret=lookup_symbol_function(eval(CAR(arg_list)));
    break;
    case 33 :                                      // mapcar
      ret=mapcar(arg_list);    
    case 34 :                                      // funcall
    {
      void *n1=eval(CAR(arg_list));
      ret=eval_function((lisp_symbol *)n1,CDR(arg_list));      
    } break;
    case 35 :                                                   // >
    {
      long n1=lnumber_value(eval(CAR(arg_list)));
      long n2=lnumber_value(eval(CAR(CDR(arg_list))));
      if (n1>n2) ret=true_symbol; else ret=NULL;
    }
    break;      
    case 36 :                                                   // <
    {
      long n1=lnumber_value(eval(CAR(arg_list)));
      long n2=lnumber_value(eval(CAR(CDR(arg_list))));
      if (n1<n2) ret=true_symbol; else ret=NULL;
    }    
    break;
    case 47 :                                                   // >=
    {
      long n1=lnumber_value(eval(CAR(arg_list)));
      long n2=lnumber_value(eval(CAR(CDR(arg_list))));
      if (n1>=n2) ret=true_symbol; else ret=NULL;
    }
    break;      
    case 48 :                                                   // <=
    {
      long n1=lnumber_value(eval(CAR(arg_list)));
      long n2=lnumber_value(eval(CAR(CDR(arg_list))));
      if (n1<=n2) ret=true_symbol; else ret=NULL;
    }    
    break;

    case 37 :                                                  // tmp-space
      tmp_space();
      ret=true_symbol;
    break;
    case 38 :                                                  // perm-space
      perm_space();
      ret=true_symbol;
    break;
    case 39 :
      void *symb;
      symb=eval(CAR(arg_list));
#ifdef TYPE_CHECKING
      if (item_type(symb)!=L_SYMBOL)
      {
	lprint(symb);
	lbreak(" is not a symbol (symbol-name)\n");
	exit(0);
      }
#endif
      ret=((lisp_symbol *)symb)->name;    
    break;
    case 40 :                                                  // trace
      trace_level++;
      if (arg_list)
        trace_print_level=lnumber_value(eval(CAR(arg_list)));
      ret=true_symbol;
    break;
    case 41 :                                                  // untrace
      if (trace_level>0)
      {
				trace_level--;
				ret=true_symbol;
      } else ret=NULL;
    break;
    case 42 :                                                 // digitstr
    {
      char tmp[50],*tp;
      long num=lnumber_value(eval(CAR(arg_list)));
      long dig=lnumber_value(eval(CAR(CDR(arg_list))));
      tp=tmp+49;
      *(tp--)=0;
      for (;num;)
      {
				int d;
				d=num%10;
				*(tp--)=d+'0';
				num/=10;
				dig--;
      }
      while (dig--)
        *(tp--)='0';	
      ret=new_lisp_string(tp+1);      
    } break;
    case 98 :  
    case 66 :
    case 43 :                                                // compile-file
    {
      void *fn=eval(CAR(arg_list));
      char *st=lstring_value(fn);
      p_ref r1(fn);
      bFILE *fp;
      if (fun->fun_number==98)                              // local load
        fp=new jFILE(st,"rb");
      else
        fp=open_file(st,"rb");

      if (fp->open_failure())
      {
				delete fp;
				if (DEFINEDP(symbol_value(load_warning)) && symbol_value(load_warning))
			          dprintf("Warning : file %s does not exists\n",st);
				ret=NULL;
      }
      else
      {
				long l=fp->file_size();
				char *s=(char *)jmalloc(l+1,"loaded script");
				if (!s)
				{
				  dprintf("Malloc error in load_script\n");  
				  exit(0);
				}
			
				fp->read(s,l);  
				s[l]=0;
				delete fp;
				char *cs=s;
			#ifndef NO_LIBS      
				char msg[100];
				sprintf(msg,"(load \"%s\")",st);
				if (stat_man) stat_man->push(msg,NULL);
				crc_man.get_filenumber(st);               // make sure this file gets crc'ed
			#endif
				void *compiled_form=NULL;
				p_ref r11(compiled_form);
				while (!end_of_program(cs))  // see if there is anything left to compile and run
				{
			#ifndef NO_LIBS      
				  if (stat_man) stat_man->update((cs-s)*100/l);
			#endif
				  void *m=mark_heap(TMP_SPACE);
				  compiled_form=compile(cs);
				  eval(compiled_form);
				  compiled_form=NULL;
				  restore_heap(m,TMP_SPACE);
				}	

			#ifndef NO_LIBS
                                stat_man->update(100);
				if (stat_man) stat_man->pop();
			#endif      
				jfree(s);
				ret=fn;
      }
    } break;
    case 44 :                                                 // abs
      ret=new_lisp_number(abs(lnumber_value(eval(CAR(arg_list))))); break;
    case 45 :                                                 // min
    {
      int x=lnumber_value(eval(CAR(arg_list))),y=lnumber_value(eval(CAR(CDR(arg_list))));
      if (x<y) ret=new_lisp_number(x); else ret=new_lisp_number(y);
    } break;
    case 46 :                                                 // max
    {
      int x=lnumber_value(eval(CAR(arg_list))),y=lnumber_value(eval(CAR(CDR(arg_list))));
      if (x>y) ret=new_lisp_number(x); else ret=new_lisp_number(y);
    } break;
    case 49 :                        // backquote
    {
      ret=backquote_eval(CAR(arg_list));
    } break;
    case 50 : 
    {
      lprint(arg_list);
      lbreak("comma is illegal outside of backquote\n");
      exit(0);
      ret=NULL;
    } break;
    case 51 : 
    {
      long x=lnumber_value(eval(CAR(arg_list)));
      ret=nth(x,eval(CAR(CDR(arg_list)))); 
    } break;
    case 52 : resize_tmp(lnumber_value(eval(CAR(arg_list)))); break;
    case 53 : resize_perm(lnumber_value(eval(CAR(arg_list)))); break;    
    case 54 : ret=new_lisp_fixed_point(lisp_cos(lnumber_value(eval(CAR(arg_list))))); break;
    case 55 : ret=new_lisp_fixed_point(lisp_sin(lnumber_value(eval(CAR(arg_list))))); break;
    case 56 :
    {
      long y=(lnumber_value(eval(CAR(arg_list))));   arg_list=CDR(arg_list);
      long x=(lnumber_value(eval(CAR(arg_list))));
      ret=new_lisp_number(lisp_atan2(y,x));      
    } break;
    case 57 :
    {
      int sp=current_space;
      current_space=PERM_SPACE;
      long x=0;
      while (arg_list)
      {
	void *sym=eval(CAR(arg_list));
	p_ref r1(sym);
	switch (item_type(sym))
	{
	  case L_SYMBOL : 
	  { ((lisp_symbol *)sym)->value=new_lisp_number(x); } break;
	  case L_CONS_CELL :
	  {
	    void *s=eval(CAR(sym));
	    p_ref r1(s);
#ifdef TYPE_CHECKING
	    if (item_type(s)!=L_SYMBOL)
	    { lprint(arg_list);
	      lbreak("expecting (sybmol value) for enum\n");
	      exit(0);
	    }
#endif
	    x=lnumber_value(eval(CAR(CDR(sym))));
	    ((lisp_symbol *)sym)->value=new_lisp_number(x);
	  } break;
	  default :
	  {
	    lprint(arg_list);
	    lbreak("expecting symbol or (symbol value) in enum\n");
	    exit(0);
	  }
	}
	arg_list=CDR(arg_list);
	x++;
      }      
      current_space=sp;
    } break;
    case 58 :
    {
      exit(0);
    } break;
    case 59 :
    {
      ret=eval(eval(CAR(arg_list)));
    } break;
    case 60 : lbreak("User break"); break;
    case 61 :
    {
      long x=lnumber_value(eval(CAR(arg_list))); arg_list=CDR(arg_list);
      long y=lnumber_value(eval(CAR(arg_list)));
      if (y==0) { lbreak("mod : division by zero\n"); y=1; }      
      ret=new_lisp_number(x%y);
    } break;
/*    case 62 :
    {
      char *fn=lstring_value(eval(CAR(arg_list)));
      FILE *fp=fopen(fn,"wb");
      if (!fp)
        lbreak("could not open %s for writing",fn);
      else
      {	
	for (void *s=symbol_list;s;s=CDR(s))		  
	  fprintf(fp,"%8d  %s\n",((lisp_symbol *)(CAR(s)))->call_counter,
		  lstring_value(((lisp_symbol *)(CAR(s)))->name));
	fclose(fp);
      }
    } break;*/
    case 64 :
    {
      void *bind_var=CAR(arg_list); arg_list=CDR(arg_list);
      p_ref r1(bind_var);
      if (item_type(bind_var)!=L_SYMBOL)
      { lbreak("expecting for iterator to be a symbol\n"); exit(1); }

      if (CAR(arg_list)!=in_symbol)
      { lbreak("expecting in after 'for iterator'\n"); exit(1); }
      arg_list=CDR(arg_list);

      void *ilist=eval(CAR(arg_list)); arg_list=CDR(arg_list);
      p_ref r2(ilist);
      
      if (CAR(arg_list)!=do_symbol)
      { lbreak("expecting do after 'for iterator in list'\n"); exit(1); }
      arg_list=CDR(arg_list);

      void *block=NULL,*ret=NULL;
      p_ref r3(block);
      l_user_stack.push(symbol_value(bind_var));  // save old symbol value
      while (ilist)
      {
				set_symbol_value(bind_var,CAR(ilist));
				for (block=arg_list;block;block=CDR(block))
				  ret=eval(CAR(block));
				ilist=CDR(ilist);
      }
      set_symbol_value(bind_var,l_user_stack.pop(1));
      ret=ret;
    } break;
    case 65 :
    {
      bFILE *old_file=current_print_file;
      void *str1=eval(CAR(arg_list));
      p_ref r1(str1);
      void *str2=eval(CAR(CDR(arg_list)));
      
      
      current_print_file=open_file(lstring_value(str1),
				   lstring_value(str2));

      if (!current_print_file->open_failure())
      {
				while (arg_list)
				{
				  ret=eval(CAR(arg_list));	  
				  arg_list=CDR(arg_list);
				}
      }     
      delete current_print_file;
      current_print_file=old_file;      

    } break;
    case 67 :
    {
      long first=lnumber_value(eval(CAR(arg_list))); arg_list=CDR(arg_list);
      while (arg_list)
      {
        first&=lnumber_value(eval(CAR(arg_list)));
				arg_list=CDR(arg_list);
      } 
      ret=new_lisp_number(first);
    } break;
    case 68 :
    {
      long first=lnumber_value(eval(CAR(arg_list))); arg_list=CDR(arg_list);
      while (arg_list)
      {
        first|=lnumber_value(eval(CAR(arg_list)));
				arg_list=CDR(arg_list);
      } 
      ret=new_lisp_number(first);
    } break;
    case 69 :
    {
      long first=lnumber_value(eval(CAR(arg_list))); arg_list=CDR(arg_list);
      while (arg_list)
      {
        first^=lnumber_value(eval(CAR(arg_list)));
				arg_list=CDR(arg_list);
      } 
      ret=new_lisp_number(first);
    } break;
    case 70 :  // make-array
    {
      long l=lnumber_value(eval(CAR(arg_list)));
      if (l>=2<<16 || l<=0)
      {
				lbreak("bad array size %d\n",l);
				exit(0);
      }
      ret=new_lisp_1d_array(l,CDR(arg_list));
    } break;
    case 71 : // aref
    {
      long x=lnumber_value(eval(CAR(CDR(arg_list))));
      ret=lget_array_element(eval(CAR(arg_list)),x);
    } break;
    case 72 : // if-1progn
    {
      if (eval(CAR(arg_list)))
        ret=eval_block(CAR(CDR(arg_list)));
      else ret=eval(CAR(CDR(CDR(arg_list))));

    } break;
    case 73 : // if-2progn
    {
      if (eval(CAR(arg_list)))
        ret=eval(CAR(CDR(arg_list)));
      else ret=eval_block(CAR(CDR(CDR(arg_list))));

    } break;
    case 74 : // if-12progn
    {
      if (eval(CAR(arg_list)))
        ret=eval_block(CAR(CDR(arg_list)));
      else ret=eval_block(CAR(CDR(CDR(arg_list))));

    } break;
    case 75 : // eq0
    {
      void *v=eval(CAR(arg_list));
      if (item_type(v)!=L_NUMBER || (((lisp_number *)v)->num!=0))
        ret=NULL;
      else ret=true_symbol;
    } break;
    case 76 : // preport
    {
#ifdef L_PROFILE
      char *s=lstring_value(eval(CAR(arg_list)));     
      preport(s);
#endif
    } break;
    case 77 : // search
    {
      void *arg1=eval(CAR(arg_list)); arg_list=CDR(arg_list);
      p_ref r1(arg1);       // protect this refrence
      char *haystack=lstring_value(eval(CAR(arg_list)));     
      char *needle=lstring_value(arg1);

      char *find=strstr(haystack,needle);
      if (find)
        ret=new_lisp_number(find-haystack);
      else ret=NULL;
    } break;
    case 78 : // elt
    {
      void *arg1=eval(CAR(arg_list)); arg_list=CDR(arg_list);
      p_ref r1(arg1);       // protect this refrence
      long x=lnumber_value(eval(CAR(arg_list)));           
      char *st=lstring_value(arg1);
      if (x<0 || x>=strlen(st))
      { lbreak("elt : out of range of string\n"); ret=NULL; }
      else
        ret=new_lisp_character(st[x]);      
    } break;
    case 79 : // listp
    {
      return item_type(eval(CAR(arg_list)))==L_CONS_CELL ? true_symbol : NULL;
    } break;
    case 80 : // numberp
    {
      int t=item_type(eval(CAR(arg_list)));
      if (t==L_NUMBER || t==L_FIXED_POINT) return true_symbol; else return NULL;
    } break;
    case 81 : // do
    {
      void *init_var=CAR(arg_list);
      p_ref r1(init_var);
      int i,ustack_start=l_user_stack.son;      // restore stack at end
      void *sym=NULL;
      p_ref r2(sym);

      // check to make sure iter vars are symbol and push old values
      for (init_var=CAR(arg_list);init_var;init_var=CDR(init_var))
      {
				sym=CAR(CAR(init_var));
				if (item_type(sym)!=L_SYMBOL)
				{ lbreak("expecting symbol name for iteration var\n"); exit(0); }
				l_user_stack.push(symbol_value(sym));
      }
      
      void **do_evaled=l_user_stack.sdata+l_user_stack.son;
      // push all of the init forms, so we can set the symbol
      for (init_var=CAR(arg_list);init_var;init_var=CDR(init_var))    
				l_user_stack.push(eval(CAR(CDR(CAR((init_var))))));

      // now set all the symbols
      for (init_var=CAR(arg_list);init_var;init_var=CDR(init_var),do_evaled++)
      {
				sym=CAR(CAR(init_var));
				set_symbol_value(sym,*do_evaled);
      }

      i=0;       // set i to 1 when terminate conditions are meet
      do
      {
				i=(eval(CAR(CAR(CDR(arg_list))))!=NULL);
				if (!i)
				{
				  eval_block(CDR(CDR(arg_list)));
				  for (init_var=CAR(arg_list);init_var;init_var=CDR(init_var))
				    eval(CAR(CDR(CDR(CAR(init_var)))));
				}
      } while (!i);
      
      ret=eval(CAR(CDR(CAR(CDR(arg_list)))));

      // restore old values for symbols
      do_evaled=l_user_stack.sdata+ustack_start;
      for (init_var=CAR(arg_list);init_var;init_var=CDR(init_var),do_evaled++)      
      {
				sym=CAR(CAR(init_var));
				set_symbol_value(sym,*do_evaled);
      }

      l_user_stack.son=ustack_start;
      
    } break;
    case 82 : // gc
    { 
      collect_space(current_space);
    } break;
    case 83 : // schar
    {
      char *s=lstring_value(eval(CAR(arg_list)));      arg_list=CDR(arg_list);
      long x=lnumber_value(eval(CAR(arg_list)));

      if (x>=strlen(s))
      { lbreak("SCHAR: index %d should be less than the length of the string\n",x); exit(0); }
      else if (x<0)
      { lbreak("SCHAR: index should not be negative\n"); exit(0); }
      return new_lisp_character(s[x]);
    } break;
    case 84 :// symbolp
    { if (item_type(eval(CAR(arg_list)))==L_SYMBOL) return true_symbol;
      else return NULL; } break;
    case 85 :  // num2str
    {
      char str[10];
      sprintf(str,"%d",lnumber_value(eval(CAR(arg_list))));
      ret=new_lisp_string(str);
    } break;
    case 86 : // nconc
    {
      void *l1=eval(CAR(arg_list)); arg_list=CDR(arg_list);            
      p_ref r1(l1);      
      void *first=l1,*next;
      p_ref r2(first);

      if (!l1)
      {
				l1=first=eval(CAR(arg_list));
				arg_list=CDR(arg_list);
      }
     
      if (item_type(l1)!=L_CONS_CELL)
      { lprint(l1); lbreak("first arg should be a list\n"); }
      do
      {
				next=l1;
				while (next) { l1=next; next=lcdr(next); }
				((cons_cell *)l1)->cdr=eval(CAR(arg_list));	
				arg_list=CDR(arg_list);
      } while (arg_list);      
      ret=first;
    } break;
    case 87 : // first
    { ret=CAR(eval(CAR(arg_list))); } break;
    case 88 : // second
    { ret=CAR(CDR(eval(CAR(arg_list)))); } break;
    case 89 : // third
    { ret=CAR(CDR(CDR(eval(CAR(arg_list))))); } break;
    case 90 : // fourth
    { ret=CAR(CDR(CDR(CDR(eval(CAR(arg_list)))))); } break;
    case 91 : // fifth
    { ret=CAR(CDR(CDR(CDR(CDR(eval(CAR(arg_list))))))); } break;
    case 92 : // sixth
    { ret=CAR(CDR(CDR(CDR(CDR(CDR(eval(CAR(arg_list)))))))); } break;
    case 93 : // seventh
    { ret=CAR(CDR(CDR(CDR(CDR(CDR(CDR(eval(CAR(arg_list))))))))); } break;
    case 94 : // eight
    { ret=CAR(CDR(CDR(CDR(CDR(CDR(CDR(CDR(eval(CAR(arg_list)))))))))); } break;
    case 95 : // ninth
    { ret=CAR(CDR(CDR(CDR(CDR(CDR(CDR(CDR(CDR(eval(CAR(arg_list))))))))))); } break;
    case 96 : // tenth
    { ret=CAR(CDR(CDR(CDR(CDR(CDR(CDR(CDR(CDR(CDR(eval(CAR(arg_list)))))))))))); } break;
    case 97 :
    {
      long x1=lnumber_value(eval(CAR(arg_list))); arg_list=CDR(arg_list);
      long x2=lnumber_value(eval(CAR(arg_list))); arg_list=CDR(arg_list);
      void *st=eval(CAR(arg_list));
      p_ref r1(st);

      if (x1<0 || x1>x2 || x2>=strlen(lstring_value(st)))
        lbreak("substr : bad x1 or x2 value");
      
      lisp_string *s=new_lisp_string(x2-x1+2);
      if (x2-x1)
        memcpy(lstring_value(s),lstring_value(st)+x1,x2-x1+1);

      *(lstring_value(s)+(x2-x1+1))=0;
      ret=s;
    } break;

    case 99 : // preset
    {
#ifdef L_PROFILE
      preset(lsym_root);
#endif
    } break;

    default : 
    { dprintf("Undefined system function number %d\n",((lisp_sys_function *)fun)->fun_number); }
  }
  return ret;
}

void tmp_space()
{
  current_space=TMP_SPACE;
}

void perm_space()
{
  current_space=PERM_SPACE;
}

void use_user_space(void *addr, long size)
{
  current_space=2;
  free_space[USER_SPACE]=space[USER_SPACE]=(char *)addr;
  space_size[USER_SPACE]=size;
}


void *eval_user_fun(lisp_symbol *sym, void *arg_list)
{
  int args,req_min,req_max;
  void *ret=NULL;
  p_ref ref1(ret);

#ifdef TYPE_CHECKING
  if (item_type(sym)!=L_SYMBOL)
  {
    lprint(sym);
    lbreak("EVAL : is not a function name (not symbol either)");
    exit(0);
  } 
#endif


  lisp_user_function *fun=(lisp_user_function *)(((lisp_symbol *)sym)->function);

#ifdef TYPE_CHECKING
  if (item_type(fun)!=L_USER_FUNCTION)
  {
    lprint(sym);
    lbreak("is not a user defined function\n");
  }
#endif

#ifndef NO_LIBS
  void *fun_arg_list=cash.lblock(fun->alist);
  void *block_list=cash.lblock(fun->blist); 
  p_ref r9(block_list),r10(fun_arg_list);
#else
  void *fun_arg_list=fun->arg_list;
  void *block_list=fun->block_list;
  p_ref r9(block_list),r10(fun_arg_list);
#endif



  // mark the start start, so we can restore when done
  long stack_start=l_user_stack.son;  

  // first push all of the old symbol values
  void *f_arg=fun_arg_list;
  p_ref r18(f_arg);
  p_ref r19(arg_list);
  for (;f_arg;f_arg=CDR(f_arg))
  {
    l_user_stack.push(((lisp_symbol *)CAR(f_arg))->value);
  }

  // open block so that local vars aren't saved on the stack
  {
    int new_start=l_user_stack.son;
    int i=new_start;
    // now push all the values we wish to gather
    for (f_arg=fun_arg_list;f_arg;)
    {
      if (!arg_list)
      { lprint(sym);  lbreak("too few parameter to function\n"); exit(0); }
      l_user_stack.push(eval(CAR(arg_list)));
      f_arg=CDR(f_arg);
      arg_list=CDR(arg_list);
    }


    // now store all the values and put them into the symbols
    for (f_arg=fun_arg_list;f_arg;f_arg=CDR(f_arg))
      ((lisp_symbol *)CAR(f_arg))->value=l_user_stack.sdata[i++];

    l_user_stack.son=new_start;
  }



  if (f_arg)
  { lprint(sym);  lbreak("too many parameter to function\n"); exit(0); }


  // now evaluate the function block
  while (block_list)
  {
    ret=eval(CAR(block_list));
    block_list=CDR(block_list);    
  }

  long cur_stack=stack_start;
  for (f_arg=fun_arg_list;f_arg;f_arg=CDR(f_arg))
    ((lisp_symbol *)CAR(f_arg))->value=l_user_stack.sdata[cur_stack++];

  l_user_stack.son=stack_start;


  return ret;
} 





void *eval(void *prog)
{
 

  void *ret=NULL;  
  p_ref ref1(prog);


  int tstart=trace_level;
  
  if (trace_level)
  {
    if (trace_level<=trace_print_level)
    {
      dprintf("%d (%d,%d,%d) TRACE : ",trace_level, 
	      space_size[PERM_SPACE]-((char *)free_space[PERM_SPACE]-(char *)space[PERM_SPACE]),
	      space_size[TMP_SPACE]-((char *)free_space[TMP_SPACE]-(char *)space[TMP_SPACE]),
	      l_ptr_stack.son);
      lprint(prog);

      dprintf("\n");
    }
    trace_level++;
  }
  if (prog)
  {
    switch (item_type(prog))
    {   
      case L_BAD_CELL :
      { lbreak("error : eval on a bad cell\n"); exit(0); } break;
      case L_CHARACTER :
      case L_STRING :
      case L_NUMBER : 
      case L_POINTER :
      case L_FIXED_POINT :
      { ret=prog; } break;
      case L_SYMBOL : 
      { if (prog==true_symbol)
	  			ret=prog;
        else
				{
				  ret=lookup_symbol_value(prog);
				  if (item_type(ret)==L_OBJECT_VAR)
				    ret=l_obj_get(((lisp_object_var *)ret)->number);
				}
      } break;
      case L_CONS_CELL :
      {
        ret=eval_function((lisp_symbol *)CAR(prog),CDR(prog));
      }
      break;
      default :
        dprintf("shouldn't happen\n");
    }
  }
  if (tstart)
  {
    trace_level--;
    if (trace_level<=trace_print_level)
      dprintf("%d (%d,%d,%d) TRACE ==> ",trace_level, 
	      space_size[PERM_SPACE]-((char *)free_space[PERM_SPACE]-(char *)space[PERM_SPACE]),
	      space_size[TMP_SPACE]-((char *)free_space[TMP_SPACE]-(char *)space[TMP_SPACE]),
	      l_ptr_stack.son);
    lprint(ret);
    dprintf("\n");
  }
  
/*  l_user_stack.push(ret);
  collect_space(PERM_SPACE);
  ret=l_user_stack.pop(1);  */


  return ret;
}

#define TOTAL_SYS_FUNCS 100
                                 //  0      1    2       3       4      5      6      7
char *sys_funcs[TOTAL_SYS_FUNCS]={"print","car","cdr","length","list","cons","quote","eq",
				// 8   9   10    11       12          13     14      15      16
				  "+","-","if","setf","symbol-list","assoc","null","acons","pairlis",
				// 17     18     19     20     21     22    23      24
				  "let","defun","atom","not", "and", "or","progn","equal",
				// 25               26          27       28  29   30     31
				  "concatenate","char-code","code-char","*","/","cond","select",
				// 32            33         34     35    36    37        
				  "function", "mapcar", "funcall", ">", "<", "tmp-space",
				//   38              39        40       41         42
				  "perm-space","symbol-name","trace","untrace","digstr",
				//   43            44   45    46    47  48       49
				  "compile-file","abs","min","max",">=","<=","backquote",
				//  50      51      52         53           54    55     56
				  "comma","nth","resize-tmp","resize-perm","cos","sin","atan2",
				  // 57       58     59     60     61   62              63
                                  "enum", "quit","eval","break","mod","write_profile","setq",
				  // 64    65          66      67       68        69        70
				  "for", "open_file","load","bit-and","bit-or","bit-xor","make-array",
				  // 71      72          73          74        75      76
				  "aref","if-1progn","if-2progn","if-12progn","eq0","preport",
				  // 77     78         79        80       81     82     83
				  "search","elt",    "listp", "numberp", "do",  "gc", "schar",
				  // 84       85        86      87      88        89    90
				  "symbolp","num2str","nconc","first","second","third","fourth",
				  // 91       92       93       94       95      96
				  "fifth", "sixth", "seventh","eighth","ninth","tenth",
				  "substr",       // 97
				  "local_load",    // 98, filename
                                  "preset"        // 99
				};

/* select, digistr, load-file are not a common lisp functions! */

short sys_args[TOTAL_SYS_FUNCS*2]={

// 0      1       2        3       4         5       6      7        8
 1, -1,   1, 1,   1, 1,   0, -1,   0, -1,   2, 2,   1, 1,   2, 2,  0, -1, 
// 9      10      11      12       13       14      15      16      17
 1, -1,   2, 3,   2, 2,   0, 0,    2, 2,    1, 1,   2, 2,   2, 2,   1, -1, 
// 18     19      20      21       22       23      24      25      26
 2, -1,  1, 1,   1, 1,  -1, -1,  -1, -1,  -1, -1,  2, 2,   1,-1,   1, 1,
// 27      28      29     30       31      32        33,     34      35
 1, 1,   -1,-1,  1,-1,  -1, -1,   1,-1,    1, 1,   2, -1,  1,-1,   2,2,
// 36     37     38       39       40       41      42      43      44
 2,2,    0,0,   0,0,      1,1,    0,-1,    0,-1,   2,2,    1,1,    1,1,
// 45     46     47       48       49       50      51      52      53
 2,2,    2,2,   2,2,     2,2,     1,1,     1,1,    2,2,    1,1,    1,1,
// 54     55     56       57       58       59      60      61      62
 1,1,    1,1,   2,2,     1,-1,    0,0,     1,1,    0,0,    2,2,    1,1,
// 63     64     65      66        67       68      69      70      71
 2,2,    4,-1,  2,-1,    1,1,     1,-1,    1,-1,   1,-1,   1,-1,    2,2,
// 72     73     74      75        76       77      78      79       80
 2,3,     2,3,  2,3,     1,1,     1,1,     2,2,    2,2,    1,1,     1,1,
// 81     82     83      84        85       86      87       88      89
 2,3,     0,0,  2,2,     1,1,     1,1,     2,-1,   1,1,     1,1,    1,1,
// 90      91    92      93        94       95      96       97     98
 1,1,     1,1,   1,1,    1,1,     1,1,      1,1,     1,1,   3,3,    1,1
  
};  

int total_symbols()
{
  return ltotal_syms;
}

void resize_perm(int new_size)
{
  if (new_size<((char *)free_space[PERM_SPACE]-(char *)space[PERM_SPACE]))
  {
    lbreak("resize perm : %d is to small to hold current heap\n",new_size);
    exit(0);
  } else if (new_size>space_size[PERM_SPACE])
  {
    lbreak("Only smaller resizes allowed for now.\n");
    exit(0);
  } else 
    dprintf("doesn't work yet!\n");
}

void resize_tmp(int new_size)
{
  if (new_size<((char *)free_space[TMP_SPACE]-(char *)space[TMP_SPACE]))
  {
    lbreak("resize perm : %d is to small to hold current heap\n",new_size);
    exit(0);
  } else if (new_size>space_size[TMP_SPACE])
  {
    dprintf("Only smaller resizes allowed for now.\n");
    exit(0);
  } else if (free_space[TMP_SPACE]==space[TMP_SPACE])
  {
    free_space[TMP_SPACE]=space[TMP_SPACE]=(char *)jrealloc(space[TMP_SPACE],new_size,"lisp tmp space");
    space_size[TMP_SPACE]=new_size;
    dprintf("Lisp : tmp space resized to %d\n",new_size);
  } else dprintf("Lisp :tmp not empty, cannot resize\n");
}

void l_comp_init();
void lisp_init(long perm_size, long tmp_size)
{
  int i;
  lsym_root=NULL;
  total_user_functions=0;
  free_space[0]=space[0]=(char *)jmalloc(perm_size,"lisp perm space");  
  space_size[0]=perm_size;
  

  free_space[1]=space[1]=(char *)jmalloc(tmp_size,"lisp tmp space");
  space_size[1]=tmp_size;


  current_space=PERM_SPACE;  
  
  
  l_comp_init();
  for (i=0;i<TOTAL_SYS_FUNCS;i++)
    add_sys_function(sys_funcs[i],sys_args[i*2],sys_args[i*2+1],i);
  clisp_init();
  current_space=TMP_SPACE;
  dprintf("Lisp : %d symbols defined, %d system functions, %d pre-compiled functions\n",
	  total_symbols(),TOTAL_SYS_FUNCS,total_user_functions);
}

void lisp_uninit()
{
#ifdef L_PROFILE
  preport("preport.out");
#endif

  jfree(space[0]);
  jfree(space[1]);
  ldelete_syms(lsym_root);
  lsym_root=NULL;
  ltotal_syms=0;
}

void clear_tmp()
{
  free_space[TMP_SPACE]=space[TMP_SPACE];
}

void *symbol_name(void *symbol)
{
  return ((lisp_symbol *)symbol)->name;
}


void *set_symbol_number(void *symbol, long num)
{
#ifdef TYPE_CHECKING
  if (item_type(symbol)!=L_SYMBOL)
  {
    lprint(symbol);
    lbreak("is not a symbol\n");
    exit(0);
  }
#endif
  if (((lisp_symbol *)symbol)->value!=l_undefined &&
      item_type(((lisp_symbol *)symbol)->value)==L_NUMBER)
    ((lisp_number *)((lisp_symbol *)symbol)->value)->num=num;
  else 
    ((lisp_symbol *)(symbol))->value=new_lisp_number(num);

  return ((lisp_symbol *)(symbol))->value;
}

void *set_symbol_value(void *symbol, void *value)
{
#ifdef TYPE_CHECKING
  if (item_type(symbol)!=L_SYMBOL)
  {
    lprint(symbol);
    lbreak("is not a symbol\n");
    exit(0);
  }
#endif
  ((lisp_symbol *)(symbol))->value=value;
  return value;
}

void *symbol_function(void *symbol)
{
#ifdef TYPE_CHECKING
  if (item_type(symbol)!=L_SYMBOL)
  {
    lprint(symbol);
    lbreak("is not a symbol\n");
    exit(0);
  }
#endif
  return ((lisp_symbol *)symbol)->function;
}

void *symbol_value(void *symbol)
{
#ifdef TYPE_CHECKING
  if (item_type(symbol)!=L_SYMBOL)
  {
    lprint(symbol);
    lbreak("is not a symbol\n");
    exit(0);
  }
#endif
  return ((lisp_symbol *)symbol)->value;
}






