/* xlsym - symbol handling routines */
/*        Copyright (c) 1985, by David Michael Betz
          All Rights Reserved
          Permission is granted for unrestricted non-commercial use   */

#include "flp1_xlisp_h"

/* external variables */
extern NODE *obarray,*s_unbound;
extern NODE *xlenv;

/* forward declarations */
FORWARD NODE *findprop();

/* xlenter - enter a symbol into the obarray */
NODE *xlenter(name,type)
  char *name; int type;
{
    NODE ***oldstk,*sym,*array;
    int i;

    /* check for nil */
    if (strcmp(name,"NIL") == 0)
          return (NIL);

    /* check for symbol already in table */
    array = getvalue(obarray);
    i = hash(name,HSIZE);
    for (sym = getelement(array,i); sym; sym = cdr(sym))
          if (strcmp(name,getstring(getpname(car(sym)))) == 0)
              return (car(sym));

    /* make a new symbol node and link it into the list */
    oldstk = xlstack;
    xlsave1(sym);
    sym = consd(getelement(array,i));
    rplaca(sym,xlmakesym(name,type));
    setelement(array,i,sym);
    xlstack = oldstk;

    /* return the new symbol */
    return (car(sym));
}

/* xlsenter - enter a symbol with a static print name */
NODE *xlsenter(name)
  char *name;
{
    return (xlenter(name,STATIC));
}

/* xlmakesym - make a new symbol node */
NODE *xlmakesym(name,type)
  char *name;
{
    NODE *sym;
    sym = (type == DYNAMIC ? cvsymbol(name) : cvcsymbol(name));
    setvalue(sym,*name == ':' ? sym : s_unbound);
    return (sym);
}

/* xlframe - create a new environment frame */
NODE *xlframe(env)
  NODE *env;
{
    return (consd(env));
}

/* xlbind - bind a value to a symbol */
xlbind(sym,val,env)
  NODE *sym,*val,*env;
{
    NODE *ptr;

    /* create a new environment list entry */
    ptr = consd(car(env));
    rplaca(env,ptr);

    /* create a new variable binding */
    rplaca(ptr,cons(sym,val));
}

/* xlgetvalue - get the value of a symbol */
NODE *xlgetvalue(sym)
  NODE *sym;
{
    register NODE *fp,*ep;
    NODE *val;

    /* look for the value of the symbol */
    for (;;) {

          /* get the global value */
          val = getvalue(sym);

          /* check the environment list */
          for (fp = xlenv; fp; fp = cdr(fp))

              /* check for an instance variable */
              if ((ep = car(fp)) && objectp(car(ep))) {
                    if (xlobgetvalue(ep,sym,&val))
                        goto check_unbound;
              }

              /* check an environment stack frame */
              else {
                    for (; ep; ep = cdr(ep))
                        if (sym == car(car(ep))) {
                              val = cdr(car(ep));
                              goto check_unbound;
                        }
              }

check_unbound:
          /* check for a good value */
          if (val != s_unbound)
              break;

          /* handle the unbound variable */
          xlunbound(sym);
    }

    /* return the value */
    return (val);
}

/* xlsetvalue - set the value of a symbol */
xlsetvalue(sym,val)
  NODE *sym,*val;
{
    register NODE *fp,*ep;

    /* look for the symbol in the environment list */
    for (fp = xlenv; fp; fp = cdr(fp))

          /* check for an instance variable */
          if ((ep = car(fp)) && objectp(car(ep))) {
              if (xlobsetvalue(ep,sym,val))
                    return;
          }

          /* check an environment stack frame */
          else {
              for (; ep; ep = cdr(ep))
                    if (sym == car(car(ep))) {
                        rplacd(car(ep),val);
                        return;
                    }
          }

    /* store the global value */
    setvalue(sym,val);
}

/* xlgetprop - get the value of a property */
NODE *xlgetprop(sym,prp)
  NODE *sym,*prp;
{
    NODE *p;
    return ((p = findprop(sym,prp)) ? car(p) : NIL);
}

/* xlputprop - put a property value onto the property list */
xlputprop(sym,val,prp)
  NODE *sym,*val,*prp;
{
    NODE *pair;
    if (pair = findprop(sym,prp))
          rplaca(pair,val);
    else
          setplist(sym,cons(prp,cons(val,getplist(sym))));
}

/* xlremprop - remove a property from a property list */
xlremprop(sym,prp)
  NODE *sym,*prp;
{
    NODE *last,*p;
    last = NIL;
    for (p = getplist(sym); consp(p) && consp(cdr(p)); p = cdr(last)) {
          if (car(p) == prp)
              if (last)
                    rplacd(last,cdr(cdr(p)));
              else
                    setplist(sym,cdr(cdr(p)));
          last = cdr(p);
    }
}

/* findprop - find a property pair */
LOCAL NODE *findprop(sym,prp)
  NODE *sym,*prp;
{
    NODE *p;
    for (p = getplist(sym); consp(p) && consp(cdr(p)); p = cdr(cdr(p)))
          if (car(p) == prp)
              return (cdr(p));
    return (NIL);
}

/* hash - hash a symbol name string */
int hash(str,len)
  char *str;
{
    int i;
    for (i = 0; *str; )
          i = (i << 2) ^ *str++;
    i %= len;
    return (i < 0 ? -i : i);
}

/* xlsinit - symbol initialization routine */
xlsinit()
{
    NODE *array,*p;

    /* initialize the obarray */
    obarray = xlmakesym("*OBARRAY*",STATIC);
    array = newvector(HSIZE);
    setvalue(obarray,array);

    /* add the symbol *OBARRAY* to the obarray */
    p = consa(obarray);
    setelement(array,hash("*OBARRAY*",HSIZE),p);

    /* enter the unbound symbol indicator */
    s_unbound = xlsenter("*UNBOUND*");
    setvalue(s_unbound,s_unbound);
}
