/*
 * lispdep.c --
 *	LISP-dependent C code.
 *
 *	This is where we really get our hands dirty with LISP guts.
 *
 *	Used in:
 *		parser/*.c
 *		fmgr/{fmgr,ppreserve}.c
 *		util/{plancat,rlockutils,syscache}.c
 */

#include <strings.h>
#include <stdio.h>

#include "tmp/postgres.h"

RcsId("$Header: /private/postgres/src/lib/C/RCS/lispdep.c,v 1.38 1991/11/14 15:03:50 jolly Exp $");

#include "nodes/pg_lisp.h"
#include "parser/atoms.h"
#include "utils/palloc.h"
#include "utils/log.h"
#include "tmp/stringinfo.h"

#include "lib/equalfuncs.h"

/* ----------------
 *	_copy function extern declarations
 * ----------------
 */
#include "lib/copyfuncs.h"

/*
 *	Allocation and manipulation routines.
 *
 *	XXX It may be assumed that garbage collection has been turned off
 *	    before these routines are called.
 *
 *	lispAtom - new atom with print name set to the argument
 *	lispDottedPair - new uninitialized cons cell
 *	lispFloat - new floating point number (>= 32 bit)
 *	lispInteger - new integer (>= 32-bit)
 *	lispString - new string (null-delimited)
 *	lispVectori - new byte vector (size in bytes >= argument)
 *	evalList - eval the argument
 *	quote - quote the argument symbol (only really necessary for Franz?)
 */

/* ===================== PGLISP ==================== */


char *
CString(lstr)
     LispValue lstr;
{
    if (stringp(lstr))
	return(LISPVALUE_STRING(lstr));
    else {
	elog(WARN,"CString called on non-string\n");
	return(NULL);
    }
}

int 
CAtom(lv)
     LispValue lv;
{
    if (atom(lv))
	return ((int)(LISPVALUE_SYMBOL(lv)));
    else {
	elog(WARN,"CAtom called on non-atom\n");
	return (NULL);
    }
}

double
CDouble(lval)
     LispValue lval;
{
    if (floatp(lval))
	return(LISPVALUE_DOUBLE(lval));
    
    elog(WARN,"error : bougs float");
    return((double)0);
}
int
CInteger(lval)
     LispValue lval;
{
    if (integerp(lval) || atom(lval))
	return(LISPVALUE_INTEGER(lval));
    
    elog(WARN,"error : bogus integer");
    return(0);
}

/* ----------------
 *	lispAtom actually returns a node with type T_LispSymbol
 * ----------------
 */
LispValue
lispAtom(atomName)
    char *atomName; 
{
    LispValue	newobj = lispAlloc();
    int 	keyword;
    ScanKeyword	*search_result;	

    search_result = ScanKeywordLookup(atomName);
    Assert(search_result);

    keyword = search_result->value;

    LISP_TYPE(newobj) = 		PGLISP_ATOM;
    newobj->equalFunc = 	_equalLispValue;
    newobj->outFunc = 		_outLispValue;
    newobj->copyFunc = 		_copyLispSymbol;
    LISPVALUE_SYMBOL(newobj) = 	(char *) keyword;
    CDR(newobj) = 		LispNil;
    
    return(newobj);
}

/* ----------------
 *	lispDottedPair actually returns a node of type T_LispList
 * ----------------
 */
LispValue
lispDottedPair()
{
    LispValue	newobj = lispAlloc();
    
    LISP_TYPE(newobj) = 		PGLISP_DTPR;
    newobj->equalFunc = 	_equalLispValue;
    newobj->outFunc = 		_outLispValue;
    newobj->copyFunc = 		_copyLispList;
    CAR(newobj) = 		LispNil;
    CDR(newobj) = 		LispNil;

    return(newobj);
}

/* ----------------
 *	lispFloat returns a node of type T_LispFloat
 * ----------------
 */
LispValue
lispFloat(floatValue)
    double	floatValue;
{
    LispValue	newobj = lispAlloc();

    LISP_TYPE(newobj) = 		PGLISP_FLOAT;
    newobj->equalFunc = 	_equalLispValue;
    newobj->outFunc = 		_outLispValue;
    newobj->copyFunc =		_copyLispFloat;
    LISPVALUE_DOUBLE(newobj) = 	floatValue;
    CDR(newobj) = 		LispNil;

    return(newobj);
}

/* ----------------
 *	lispInteger returns a node of type T_LispInt
 * ----------------
 */
LispValue
lispInteger(integerValue)
    int	integerValue;
{
    LispValue	newobj = lispAlloc();

    LISP_TYPE(newobj) = 	PGLISP_INT;
    newobj->equalFunc = 	_equalLispValue;
    newobj->outFunc = 		_outLispValue;
    newobj->copyFunc = 		_copyLispInt;
    LISPVALUE_INTEGER(newobj) = integerValue;
    CDR(newobj) = 		LispNil;

    return(newobj);
}

/* ----------------
 *	lispName returns a node of type T_LispStr
 *	but gurantees that the string is char16 aligned and filled
 * ----------------
 */
LispValue
lispName(string)
	char	*string;
{
    LispValue	newobj = lispAlloc();
    char		*newstr;

    LISP_TYPE(newobj) = PGLISP_STR;
    newobj->equalFunc = _equalLispValue;
    newobj->outFunc = _outLispValue;
    newobj->copyFunc = _copyLispStr;
    CDR(newobj) = LispNil;

    if (string) {
	if (strlen(string) > sizeof(NameData)) {
	    elog(WARN,"Name %s was longer than %d",string,sizeof(NameData));
	    /* NOTREACHED */
	}
	newstr = (char *) palloc(sizeof(NameData)+1);
	newstr = strcpy(newstr,string);
    } else
      newstr = (char *)NULL;

    LISPVALUE_STRING(newobj) = newstr;
    
    return(newobj);
}


/* ----------------
 *	lispString returns a node of type T_LispStr
 * ----------------
 */
LispValue
lispString(string)
	char	*string;
{
    LispValue	newobj = lispAlloc();
    char		*newstr;

    LISP_TYPE(newobj) = PGLISP_STR;
    newobj->equalFunc = _equalLispValue;
    newobj->outFunc = 	_outLispValue;
    newobj->copyFunc = 	_copyLispStr;
    CDR(newobj) =	 LispNil;

    if (string) {
	newstr = (char *) palloc(strlen(string)+1);
	newstr = strcpy(newstr,string);
    } else
      newstr = (char *)NULL;

    LISPVALUE_STRING(newobj) = newstr;
    return(newobj);
}

/* ----------------
 *	lispVectori returns a node of type T_LispVector
 * ----------------
 */
LispValue
lispVectori(nBytes)
    int	nBytes;
{
    LispValue	newobj = lispAlloc();
	
    LISP_TYPE(newobj) = PGLISP_VECI;
    newobj->equalFunc = _equalLispValue;
    newobj->outFunc = 	_outLispValue;
    newobj->copyFunc = 	_copyLispVector;
    
    LISPVALUE_VECI(newobj) = (struct vectori *)
	palloc((unsigned) (sizeof(struct vectori) + nBytes));
    LISPVALUE_VECTORSIZE(newobj) = nBytes;
    CDR(newobj) = LispNil;

    return(newobj);
}

LispValue
evalList(list)
	LispValue	list;
{
    elog(WARN,"trying to evaluate a list, unsupported function");
    return(list);
}

LispValue
quote(lispObject)
	LispValue	lispObject;
{
    elog(WARN,"calling quote which is being phased out");
    return(lispObject);
}

/* ===================== LISP INDEPENDENT ==================== */

/*
 *	Manipulation routines.
 *
 *	lispList - allocate a new cons cell, initialized to (nil . nil)
 *	lispCons - allocate a new cons cell, initialized to the arguments
 *	nappend1 - destructive append of "object" to the end of "list"
 *	car, cdr - same as LISP
 *	rplaca, rplacd - same as LISP
 *	init_list - set the car of a list
 */

LispValue
lispList()
{
	LispValue p;

	p = lispDottedPair();
	CAR(p) = LispNil;
	CDR(p) = LispNil;
	return(p);
}

LispValue
lispCons(lispObject1, lispObject2)
	LispValue lispObject1, lispObject2;
{
	LispValue p = lispDottedPair();

	CAR(p) = lispObject1;
	CDR(p) = lispObject2;
	return(p);
}


LispValue
nappend1(list, lispObject)
	LispValue	list, lispObject;
{
	LispValue	p;
	
	if (null(list)) {
	    list = lispList();
	    CAR(list) = lispObject;
	    CDR(list) = LispNil;
	    return(list);
	}
	
	for (p = list; !null(CDR(p)); p = CDR(p))
		;
	CDR(p) = lispList();
	CAR(CDR(p)) = lispObject;
	return(list);
}


/* XXX - this will only work for single level lists */

LispValue
append1(list, lispObject)
	LispValue	list, lispObject;
{
    LispValue	p = LispNil;
    LispValue 	retval = LispNil;

    if (null(list)) {
	retval = lispList();
	CAR(retval) = lispObject;
	CDR(retval) = LispNil;
	return(retval);
    }

    for (p = list; !null(p); p = CDR(p)) {
	retval = nappend1(retval,CAR(p));
    }

    for (p = retval; !null(CDR(p)); p = CDR(p))
      ;

    CDR(p) = lispList();
    CAR(CDR(p)) = lispObject;
    
    return(retval);
}

LispValue
car(dottedPair)
	LispValue	dottedPair;
{
	if (null(dottedPair))
	    return (LispNil);
	
	AssertArg(listp(dottedPair));
	return (CAR(dottedPair));
}

LispValue
cdr(dottedPair)
	LispValue	dottedPair;
{
	if (null(dottedPair))
	    return (LispNil);
	
	AssertArg(listp(dottedPair));
	return (CDR(dottedPair));
}

LispValue
rplaca(dottedPair, newValue)
	LispValue	dottedPair, newValue;
{
	CAR(dottedPair) = newValue;
	return(dottedPair);
}

LispValue
rplacd(dottedPair, newValue)
	LispValue	dottedPair, newValue;
{
	CDR(dottedPair) = newValue;
	return(dottedPair);
}

/* XXX fix lispList and get rid of this ... */
init_list(list, newValue)
	LispValue	list, newValue;
{
	CAR(list) = newValue;
	CDR(list) = LispNil;
}


/*
 *      More Manipulation routines
 *
 *      append   - appends lisp obj to the end of the lisp.
 *                 non-destructive. XXX needs to be extended to
 *                 manipulate lists
 *      length   - returns the length of a list, as an int.
 *      nthCdr   - returns a list where the car of the list
 *                 is the indexed element.  Used to implement the
 *                 nth function.
 *      nconc    - returns the concatenation of *2* lists.
 *                 destructive modification.
 */


LispValue
append(list,lispObject)
     LispValue list, lispObject;
{
     LispValue  p;
     LispValue newlist, newlispObject;

     if (null(list))
        return lispCopy(lispObject);
     
     Assert(listp(list));
     
     newlist = lispCopy(list);
     newlispObject = lispCopy(lispObject);

     for (p = newlist; !null(CDR(p)); p = CDR(p))
       ;
     CDR(p) = newlispObject;
     return(newlist);

}

/* ----------------
 *	lispCopy has been moved to copyfuncs.c with the other
 *	copy functions.
 * ----------------
 */

int
length(list)
     LispValue list;
{
     LispValue temp;
     int count = 0;
     for (temp = list; !null(temp); temp = CDR(temp))
       count += 1;

     return(count);
}


LispValue
nthCdr(index, list)
     LispValue list;
     int index;
{
    int i;
    LispValue temp = list;
    for (i= 1; i <= index; i++) {
	if (! null(temp))
	    temp = CDR(temp);
	else 
	    return(LispNil);
    }
    return(temp);
}

LispValue
nconc(list1, list2)
     LispValue list1,list2;
{
     LispValue temp;

     if (list1 == LispNil)
       return(list2);
     if (list2 == LispNil)
       return(list1);
     if (list1 == list2)
	elog(WARN,"trying to nconc a list to itself");

     for (temp = list1; !null(CDR(temp)); temp = CDR(temp))
       ;

     CDR(temp) = list2;
     return(list1);      /* list1 is now list1[]list2  */
}



LispValue
nreverse(list)
     LispValue list;
{
     LispValue temp =LispNil;
     LispValue rlist = LispNil;
     LispValue p = LispNil;
     bool last = true;

     if(null(list))
       return(LispNil);

     Assert(IsA(list,LispList));

     if (length(list) == 1)
       return(list);

     for (p = list; !null(p); p = CDR(p)) {
            rlist = lispCons(CAR(p),rlist);
     }

     CAR(list) = CAR(rlist);
     CDR(list) = CDR(rlist);
     return(list);
}

int
position(foo,bar)
     LispValue foo;
     List bar;
{
    return(0);
}

/*
 * member()
 * - nondestructive, returns t iff foo is a member of the list
 *   bar
 */
bool
member(foo,bar)
     LispValue foo;
     List bar;
{
    LispValue i;
    foreach (i,bar)
      if (equal((Node)(CAR(i)),(Node)foo))
	return(true);
    return(false);
}

LispValue
remove_duplicates(foo,test)
     List foo;
     bool (* test)();
{
    LispValue i;
    LispValue j;
    LispValue result = LispNil;
    bool there_exists_duplicate = false;
    int times = 0;

    if (length(foo) == 1)
	return(foo);
    
    foreach (i,foo) {
	if (listp(i) && !null(i)) {
	    foreach (j,CDR(i)) {
		if ( (* test)(CAR(i),CAR(j)) )
		  there_exists_duplicate = true;
	    }
	    if (! there_exists_duplicate) 
	      result = nappend1(result, CAR(i) );

	    there_exists_duplicate = false;
	}
    }
    /* XXX assumes that the last element in the list is never
     *  deleted.
     */
/*    result = nappend1(result,last_element(foo));  */
    return(result);
}

/* Returns the leftmost element in the list which 
 * does not satisfy the predicate.  Returns LispNil 
 * if no elements satisfiy the pred.
 */

LispValue
find_if_not(pred,bar)
     LispValue bar;
     bool (*pred)();
{
    LispValue temp;
    
    foreach(temp,bar)
      if (!(*pred)(CAR(temp)))
	return(CAR(temp));
    return(LispNil);
}

LispValue
LispDelete(foo,bar)
     LispValue foo;
     List bar;
{
    LispValue i = LispNil;
    LispValue j = LispNil;

    foreach (i,bar) {
	if (equal((Node)(CAR(i)),(Node)(foo)))
	  if (i == bar ) {
	      /* first element */
	      CAR(bar) = CAR(CDR(bar));
	      CDR(bar) = CDR(CDR(bar));
	  } else {
	      CDR(j) = CDR(i);
	  }
	j = i;
    }
}

LispValue
setf(foo,bar)
     LispValue foo,bar;
{
    elog(WARN,"unsupported function, 'setf' being called");
    return(bar);
}

LispValue
LispRemove(foo,bar)
     LispValue foo;
     List bar;
{
    LispValue temp = LispNil;
    LispValue result = LispNil;

    for (temp = bar; !null(temp); temp = CDR(temp))
      if (! equal((Node)(foo),(Node)(CAR(temp))) ) {
	  result = append1(result,CAR(temp));
      }
	  
    return(result);
}

List
nLispRemove(foo, bar)
List foo;
LispValue bar;
{
    LispValue x;
    List result = LispNil;

    foreach (x, foo)
	if (bar != CAR(x))
	    result = nappend1(result, CAR(x));
    return result;
}


LispValue
set_difference(foo,bar)
     LispValue foo,bar;
{
    LispValue temp1 = LispNil;
    LispValue result = LispNil;

    if(null(bar))
	return(foo); 
    
    foreach (temp1,foo) {
	if (! member(CAR(temp1),bar))
	  result = nappend1(result,CAR(temp1));
    }
    return(result);
}

List
nset_difference(foo, bar)
List foo, bar;
{
    LispValue x;
    List result;

    result = foo;
    foreach (x, bar) {
	result = nLispRemove(result, CAR(x));
      }
    return result;
}

LispValue
push(foo,bar)
     LispValue foo;
     List bar;
{
	LispValue tmp = lispList();

	if (null(bar)) {
		CAR(tmp) = foo;
		CDR(tmp) = LispNil;
		return(tmp);
	}

	CAR(tmp) = CAR(bar);
	CDR(tmp) = CDR(bar);

	CAR(bar) = foo;
	CDR(bar) = tmp;

	return(bar);
}

LispValue 
last(foo)
     LispValue foo;
{
    LispValue bar;
    for (bar = foo; !null(CDR(bar)); bar = CDR(bar))
      ;
    return(bar);
}

LispValue
LispUnion(foo,bar)
     LispValue foo,bar;
{
    LispValue retval = LispNil;
    LispValue i = LispNil;
    LispValue j = LispNil;
    
    if (null(foo))
      return(bar); /* XXX - should be copy of bar */
    
    if (null(bar))
      return(foo); /* XXX - should be copy of foo */
    
    Assert(IsA(foo,LispList));
    Assert(IsA(bar,LispList));

    foreach (i,foo) {
	foreach (j,bar) {
	    if (! equal((Node)(CAR(i)),(Node)(CAR(j)))) {
	      retval = nappend1(retval,CAR(i));
	      break;
	    }
	}
    }
    foreach(i,bar) {
      retval = nappend1(retval,CAR(i));
    }

    return(retval);
}

/* temporary functions */

LispValue 
mapcar(foo,bar)
     void (*foo)();
     LispValue bar;
{
    elog(WARN, "unsupported function 'mapcar'");
    return(bar);
}

bool
zerop(foo)
     LispValue foo;
{
    if (integerp(foo))
	return((bool)(CInteger(foo) == 0));
    else {
	elog(WARN,"zerop called on noninteger");
	return ((bool) 1); /* non-integer is always zero */
    }
}

LispValue
lispArray(foo)
     int foo;
{
    elog(WARN,"bogus function : lispArray");
    return(lispInteger(foo));
}

/*    
 *    	number-list
 *    
 *    	Returns a list with 'n' fixnums from 'start' in order.
 *    
 */

List
number_list(start,n)
     int start,n;
{
    LispValue return_list = LispNil;
    int i,j;

    for(i = start, j = 0; j < n ; j++,i++ )
      return_list = nappend1(return_list,lispInteger(i));

    return(return_list);
}


LispValue
apply(foo,bar)
     LispValue (*foo)();
     LispValue bar;
{
    elog(WARN,"unsupported function 'apply' being called");
    return(bar);
}

LispValue
find_if(pred,bar)
     bool (*pred )();
     LispValue bar;
{
    LispValue temp;

    foreach (temp,bar) 
      if ((*pred)(CAR(temp)))
	return(CAR(temp));
    return(LispNil);
}

LispValue
find(foo,bar,test, key)
     LispValue foo;
     LispValue bar;
     bool (*test)();
     Node (*key)();

{
    LispValue temp;

    elog(WARN,"unsupported function");

    foreach(temp,bar) {
	if ((*test)(foo,(*key)(CAR(temp))))
	  return(CAR(temp));
    }
    return(LispNil);
}

LispValue
some(foo,bar)
     bool(*foo)();
     LispValue bar;
{
    LispValue i = LispNil;
    LispValue temp = LispNil;

    foreach(i,bar) {
	temp =CAR(i);
	if ((*foo)(temp))
	  return(temp);
    }
    return(LispNil);
    
}

LispValue
sort(foo)
     LispValue foo;
{
    if (length(foo) == 1)
      return(foo);
    else {
     elog(WARN,"unsupported function");
	 return(foo);
	}
}

double
expt(foo)
     double foo;
{
    elog(WARN,"unsupported function");
    return foo;
}

bool
same(foo,bar)
     LispValue foo;
     LispValue bar;
{
  LispValue temp = LispNil;

  if (null(foo))
    return(null(bar));
  if (null(bar))
    return(null(foo));
  if (length(foo) == length(bar)) {
    foreach (temp,foo) {
      if (!member(CAR(temp),bar))
	return(false);
    }
    return(true);
  }
  return(false);
	
}	  

/*---------------------------------------------------------------------
 *	lispDisplayFp
 *
 *	Print a PGLISP tree depth-first.
 *---------------------------------------------------------------------
 */

void 
lispDisplayFp(fp, lispObject)
FILE 		*fp;
LispValue	lispObject;
{
    char *s;

    s = lispOut(lispObject);
    fprintf(fp, "%s", s);
    pfree(s);
}

/*------------------------------------------------------------------
 *	lispDisplay
 *
 * 	Print a PGLISP tree depth-first in stdout
 *------------------------------------------------------------------
 */
void
lispDisplay(lispObject)
LispValue	lispObject;
{
    lispDisplayFp(stdout, lispObject);
    fflush(stdout);
}

/*--------------------------------------------------------------------------
 * lispOut
 *
 * Given a lisp structure, create a string with its visula representation.
 *
 * Keep Postgres memory clean! Don't forget to 'pfree' the string when
 * you are done with it!
 *--------------------------------------------------------------------------
 */
char *
lispOut(lispObject)
    LispValue	lispObject;
{
    StringInfo str;
    char *s;

    str = makeStringInfo();
    _outLispValue(str, lispObject);
    s = str->data;

    /*
     * free the StringInfoData, but not the string itself...
     */
    pfree((Pointer)str);

    return(s);
}

/*--------------------------------------------------------------------------
 * _outLispValue
 *
 * Given a lisp structure, create a string with its visual representation.
 * In fact, we do not create a string, but a 'StringInfo'.
 * If you want to get back just the string, use 'lispOut'.
 *
 *--------------------------------------------------------------------------
 */

void
_outLispValue(str, lispObject)
StringInfo str;
LispValue	lispObject;
{

    register int i;
    register LispValue t;
    register int done;
    char buf[500];
    char *buf2;


    if (null(lispObject)) {
	appendStringInfo(str, "nil ");
	return;
    }

    switch(lispObject->type) {
	case PGLISP_ATOM:
	    buf2 = AtomValueGetString((int)(LISPVALUE_SYMBOL(lispObject)));
	    sprintf(buf, "%s ", buf2);
	    appendStringInfo(str, buf);
	    break;
	case PGLISP_DTPR:
	    appendStringInfo(str, "(");
	    /*
	     * This object can be the beginning of a list of objects,
	     * or a single dotted pair (e.g. "(1 . 2)").
	     * In the first case its CDR will be either NIL or
	     * another dotted pair.
	     * In the second case it won't!
	     */
	    done = 0;
	    t = lispObject;
	    while (!done) {
		/*
		 * first, print the CAR
		 */
		_outLispValue(str, CAR(t));
		/*
		 * Now check the CDR & decide what to do...
		 */
		if (null(CDR(t))) {
		    /*
		     * End of list
		     */
		    done = 1;
		} else if (consp(CDR(t))) {
		    /*
		     * there is at least another element in the list
		     */
		    t = CDR(t);
		} else {
		    /*
		     * this is a dotted pair
		     * print its CDR too and get out of here!
		     */
		    appendStringInfo(str, " . ");
		    _outLispValue(str, CDR(t));
		    done = 1;
		}
	    } /*while*/
	    appendStringInfo(str, ")");
	    break;
	case PGLISP_FLOAT:
	    sprintf(buf, "%g ", LISPVALUE_DOUBLE(lispObject));
	    appendStringInfo(str, buf);
	    break;
	case PGLISP_INT:
	    sprintf(buf, "%d ", LISPVALUE_INTEGER(lispObject));
	    appendStringInfo(str, buf);
	    break;
	case PGLISP_STR:
	    sprintf(buf, "\"%s\" ", LISPVALUE_STRING(lispObject));
	    appendStringInfo(str, buf);
	    break;
	case PGLISP_VECI:
	    sprintf(buf, "#<%d:", LISPVALUE_VECTORSIZE(lispObject));
	    appendStringInfo(str, buf);
	    for (i = 0; i < LISPVALUE_VECTORSIZE(lispObject); ++i) {
		sprintf(buf, " %d", LISPVALUE_BYTEVECTOR(lispObject)[i]);
		appendStringInfo(str, buf);
	    }
	    sprintf(buf, " >");
	    appendStringInfo(str, buf);
	    break;
	default:
	    (* ((Node)lispObject)->outFunc)(str, lispObject);
	    break;
    }

}
