//
// This file is part of the Jlisp implementation of Standard Lisp
// Copyright \u00a9 (C) Codemist Ltd, 1998-2000.
//


// Lisp has a single inclusive data-type, which I call
// LispObject here. It has sub-types that are symbols,
// numbers, strings and lists. Here I give just a few
// methods (eg print and eval) that may be used on anything.

import java.io.*;
import java.math.*;

public abstract class LispObject extends Object
{
    public boolean atom;   // true if it is atomic
    public LispObject car; // car and cdr fields to reduce number of expensive casts!
    public LispObject cdr;

    LispObject()
    {
        car = cdr = null;
        atom = true;
    }

// The following constructor is ONLY intended for use via a call
//       super(car, cdr);
// in the constructor for the "Cons" sub-class.

    LispObject(LispObject car, LispObject cdr)
    {
        atom = false;
        this.car = car; 
        this.cdr = cdr;
    }

    static final int printEscape      = 1; // flags to pass to print(n)
    static final int printBinary      = 2;
    // (decimal is the default)       = 4
    static final int printOctal       = 8;
    static final int printHex         = 16;
    static final int printLower       = 32;
    static final int printUpper       = 64;
    static final int noLineBreak      = 128;

    void print()
    {
        currentOutput = (LispStream)Jlisp.lit[Lit.std_output].car/*value*/;
        currentFlags = 0;
        iprint();
    }

    void print(int flags)
    {
        currentOutput = (LispStream)Jlisp.lit[Lit.std_output].car/*value*/;
        currentFlags = flags;
        iprint();
    }

// real printing will usually be done by iprint where the current output
// stream and format flags can be accessed via static variables.

    static LispStream currentOutput;
    static int currentFlags;

    abstract void iprint();
    abstract void blankprint(); // print but with whitespace before it

    void errPrint() // print to error output stream
    {
        currentOutput = (LispStream)Jlisp.lit[Lit.err_output].car/*value*/;
        currentFlags = printEscape;
        iprint();
    }

    void tracePrint() // print to trace output stream
    {
        currentOutput = (LispStream)Jlisp.lit[Lit.tr_output].car/*value*/;
        currentFlags = printEscape;
        iprint();
    }

// Codes for use in my (custom) serialisation format.

// I make special provision for references to 64 things. I will use
// this for the first 48 things used at all and the 16 most recent ones.

    static final int X_REFn     = 0x00;

    static final int X_BREAK1   = 0x40;

// The next bunch are optimisations for common cases when the
// length code is short. The length code is folded into the main byte. Thus
// (eg) symbols whose name is from 0 to 15 characters long are dealt with
// especially neatly.

    static final int X_SYMn     = 0x40; // symbol with 0 to 15 chars
    static final int X_UNDEFn   = 0x50; // symbol (0-15), not a function
    static final int X_GENSYMn  = 0x60; // gensym with 0 to 15 bytes
    static final int X_LIST     = 0x70; // list with 0 to 15 items: (LIST) = NIL
    static final int X_LISTX    = 0x80; // like (LIST* ..) with 1-16 items then tail
    static final int X_INTn     = 0x90; // integer with 0 to 15 bytes
    static final int X_STRn     = 0xa0; // string, 0 to 15 chars

    static final int X_BREAK2   = 0xb0;

    static final int X_REF      = 0xb0; // refer to a previously mentioned item
    static final int X_REFBACK  = 0xb4; // (only 1 and 2 byte versions used)
    static final int X_INT      = 0xb8; // LispBigInteger represented by an array
    static final int X_STR      = 0xbc; // Strings
    static final int X_SYM      = 0xc0; // Symbol with given name
    static final int X_UNDEF    = 0xc4; // Symbol (not a function)
    static final int X_UNDEF1   = 0xc8; // disembodied undefined function
    static final int X_GENSYM   = 0xcc; // a gensym or other uninterned name
    static final int X_BPS      = 0xd0; // "binary code" ha ha ha.
    static final int X_VEC      = 0xd4; // a Lisp vector
// perhaps X_INT with a short-enough operand could be used for X_FIXNUM
// as a rationalisation here.
    static final int X_FIXNUM   = 0xd8; // 1, 2, 3 or 4-byte small integer

//  0xdc spare at present

    static final int X_BREAK3   = 0xe0;

// The final collection of codes are all one-byte incidental ones and
// the amount of any associated data is implicit in them. Eg X_DOUBLE will
// be followed by 8 bytes that represent a double-precision floating point
// value. X_FNAME is followed by a single length byte (n) then n characters.

    static final int X_NULL     = 0xe0; // empty cell (ie Java null)
    static final int X_DOUBLE   = 0xe1; // double-precision number
    static final int X_STREAM   = 0xe2; // an open file (not dumpable)
    static final int X_FNAME    = 0xe3; // built-in function
    static final int X_SPECFN   = 0xe4; // built-in special form
    static final int X_STORE    = 0xe5; // the next item will be re-used
    static final int X_HASH     = 0xe6; // EQ hash
    static final int X_HASH1    = 0xe7; // EQL hash (not used)
    static final int X_HASH2    = 0xe8; // EQUAL hash
    static final int X_HASH3    = 0xe9; // EQUALS hash (not used)
    static final int X_HASH4    = 0xea; // EQUALP hash (not used)
    static final int X_ENDHASH  = 0xeb; // end of data for hash table
    static final int X_AUTOLOAD = 0xec; // autoloading fn def
    static final int X_SPID     = 0xed; // internal marker
    static final int X_DEFINMOD = 0xee; // "define-in-module" in fasl files
    static final int X_INTERP   = 0xef; // interpreted code
    static final int X_MACRO    = 0xf0; // interpreted macro
    static final int X_CALLAS   = 0xf1; // simple tail-call object
    static final int X_RECENT   = 0xf2; // used in FASL but not checkpoints
    static final int X_RECENT1  = 0xf3; // used in FASL but not checkpoints
    static final int X_OBLIST   = 0xf4; // oblist vector

// 0xf2 to 0xff spare at present...

    abstract void scan();
    abstract void dump() throws IOException;

// dealing with references to shared structure has the most complicated
// treatment here because it appears to be an especially heavily used
// case and one where special cases may make some real difference.

    void putSharedRef(Object w) throws IOException
    {
        int n = ((Integer)w).intValue();
        if (n < 48)
        {   Jlisp.odump.write(X_REFn + n);
            return;
        }
	int n1 = Jlisp.sharedIndex - n;
        if (n1 < 17)  // range 1 to 16 is possible here (0 can not arise)
        {   Jlisp.odump.write(X_REFn + n1 - 1 + 48);
            return;
        }
	if (n >= 0x100 && n1 < 0x100 ||
	    n >= 0x10000 && n1 < 0x10000) putPrefix(n1, X_REFBACK);
        else putPrefix(n, X_REF);
    }

    void putPrefix2(int n, int code1, int code2) throws IOException
    {
        if (n < 16)
	{   Jlisp.odump.write(code1+n);
	}
	else putPrefix(n, code2);
    }

    void putPrefix(int n, int code) throws IOException
    {
	if ((n & 0xffffff00) == 0)
	{   Jlisp.odump.write(code);
            Jlisp.odump.write(n);
        }
        else if ((n & 0xffff0000) == 0)
        {   Jlisp.odump.write(code+1);
            Jlisp.odump.write(n >> 8);
            Jlisp.odump.write(n);
        }
	else if ((n & 0xff000000) == 0)
	{   Jlisp.odump.write(code+2);
	    Jlisp.odump.write(n >> 16);
	    Jlisp.odump.write(n >> 8);
	    Jlisp.odump.write(n);
	}
        else
        {   Jlisp.odump.write(code+3);
            Jlisp.odump.write(n >> 24);
            Jlisp.odump.write(n >> 16);
            Jlisp.odump.write(n >> 8);
            Jlisp.odump.write(n);
        }
    }

    public boolean lispequals(Object a)
    {
        return this.equals(a);
    }

    LispObject eval() throws Exception
    {
        return this;
    }

    LispObject copy()
    {
        return this;
    }

    int lisphashCode()
    {
        return this.hashCode();
    }

    double doubleValue() throws Exception
    {
        Jlisp.error("Number needed", this);
        return 0.0;  // never reached!
    }

    int intValue() throws Exception
    {
        Jlisp.error("Number needed", this);
        return 0;    // never reached!
    }

    BigInteger bigIntValue() throws Exception
    {
        Jlisp.error("Number needed", this);
        return null;    // never reached!
    }

    public LispObject negate() throws Exception
    {
        return Jlisp.error("Number needed", this);
    }

    public LispObject ash(int n) throws Exception
    {
        return Jlisp.error("Number needed", this);
    }

    public LispObject ash1(int n) throws Exception
    {
        return Jlisp.error("Number needed", this);
    }

    public LispObject rightshift(int n) throws Exception
    {
        return Jlisp.error("Number needed", this);
    }

    public LispObject add1() throws Exception
    {
        return Jlisp.error("Number needed", this);
    }

    public LispObject sub1() throws Exception
    {
        return Jlisp.error("Number needed", this);
    }

    public LispObject floor() throws Exception
    {
        return Jlisp.error("Number needed", this);
    }

    public LispObject ceiling() throws Exception
    {
        return Jlisp.error("Number needed", this);
    }

    public LispObject round() throws Exception
    {
        return Jlisp.error("Number needed", this);
    }

    LispObject truncate() throws Exception
    {
        return Jlisp.error("Number needed", this);
    }

    public LispObject evenp() throws Exception
    {
        return Jlisp.nil;
    }

    public LispObject oddp() throws Exception
    {
        return Jlisp.nil;
    }

    public LispObject fix() throws Exception
    {
        return Jlisp.error("Number needed", this);
    }

    public LispObject fixp() throws Exception
    {
        return Jlisp.nil;
    }

    public LispObject integerp() throws Exception
    {
        return Jlisp.nil;
    }

    public LispObject jfloat() throws Exception
    {
        return Jlisp.error("Number needed", this);
    }

    public LispObject floatp() throws Exception
    {
        return Jlisp.nil;
    }

    public LispObject minusp() throws Exception
    {
        return Jlisp.nil;
    }

    public LispObject plusp() throws Exception
    {
        return Jlisp.nil;
    }

    public LispObject zerop() throws Exception
    {
        return Jlisp.nil;
    }

    public LispObject onep() throws Exception
    {
        return Jlisp.nil;
    }

    public LispObject abs() throws Exception
    {
        return Jlisp.error("Number needed", this);
    }

    public LispObject msd() throws Exception
    {
        return Jlisp.error("Number needed", this);
    }

    public LispObject lsd() throws Exception
    {
        return Jlisp.error("Number needed", this);
    }

    LispObject not() throws Exception
    {
        return Jlisp.error("Number needed", this);
    }

    LispObject modMinus() throws Exception
    {
        return Jlisp.error("Number needed", this);
    }

    LispObject modRecip() throws Exception
    {
        return Jlisp.error("Number needed", this);
    }

    LispObject reduceMod() throws Exception
    {
        return Jlisp.error("Number needed", this);
    }

    public LispObject add(LispObject a) throws Exception
    {
        return Jlisp.error("Number needed", this);
    }

    public LispObject subtract(LispObject a) throws Exception
    {
        return Jlisp.error("Number needed", this);
    }

    public LispObject multiply(LispObject a) throws Exception
    {
        return Jlisp.error("Number needed", this);
    }

    public LispObject divide(LispObject a) throws Exception
    {
        return Jlisp.error("Number needed", this);
    }

    public LispObject remainder(LispObject a) throws Exception
    {
        return Jlisp.error("Number needed", this);
    }

    public LispObject quotientAndRemainder(LispObject a) throws Exception
    {
        return Jlisp.error("Number needed", this);
    }

    public LispObject mod(LispObject a) throws Exception
    {
        return Jlisp.error("Number needed", this);
    }

    public LispObject expt(LispObject a) throws Exception
    {
        return Jlisp.error("Number needed", this);
    }

    public LispObject max(LispObject a) throws Exception
    {
        return Jlisp.error("Number needed", this);
    }

    public LispObject min(LispObject a) throws Exception
    {
        return Jlisp.error("Number needed", this);
    }

    public LispObject and(LispObject a) throws Exception
    {
        return Jlisp.error("Number needed", this);
    }

    public LispObject or(LispObject a) throws Exception
    {
        return Jlisp.error("Number needed", this);
    }

    LispObject xor(LispObject a) throws Exception
    {
        return Jlisp.error("Number needed", this);
    }

    LispObject gcd(LispObject a) throws Exception
    {
        return Jlisp.error("Number needed", this);
    }

    LispObject modAdd(LispObject a) throws Exception
    {
        return Jlisp.error("Number needed", this);
    }

    LispObject modSubtract(LispObject a) throws Exception
    {
        return Jlisp.error("Number needed", this);
    }

    LispObject modMultiply(LispObject a) throws Exception
    {
        return Jlisp.error("Number needed", this);
    }

    LispObject modDivide(LispObject a) throws Exception
    {
        return Jlisp.error("Number needed", this);
    }

    LispObject modExpt(int n) throws Exception
    {
        return Jlisp.error("Number needed", this);
    }

    boolean eqn(LispObject a) throws Exception
    {
        Jlisp.error("Number needed", this);
        return false;
    }

    boolean neqn(LispObject a) throws Exception
    {
        Jlisp.error("Number needed", this);
        return false;
    }

    boolean ge(LispObject a) throws Exception
    {
        Jlisp.error("Number needed", this);
        return false;
    }

    boolean geq(LispObject a) throws Exception
    {
        Jlisp.error("Number needed", this);
        return false;
    }

    boolean le(LispObject a) throws Exception
    {
        Jlisp.error("Number needed", this);
        return false;
    }

    boolean leq(LispObject a) throws Exception
    {
        Jlisp.error("Number needed", this);
        return false;
    }


    LispObject addInteger(LispBigInteger a) throws Exception
    {
        return Jlisp.error("Number needed", this);
    }

    LispObject subtractInteger(LispBigInteger a) throws Exception
    {
        return Jlisp.error("Number needed", this);
    }

    LispObject multiplyInteger(LispBigInteger a) throws Exception
    {
        return Jlisp.error("Number needed", this);
    }

    LispObject divideInteger(LispBigInteger a) throws Exception
    {
        return Jlisp.error("Number needed", this);
    }

    LispObject remainderInteger(LispBigInteger a) throws Exception
    {
        return Jlisp.error("Number needed", this);
    }

    LispObject quotientAndRemainderInteger(LispBigInteger a) throws Exception
    {
        return Jlisp.error("Number needed", this);
    }

    LispObject modInteger(LispBigInteger a) throws Exception
    {
        return Jlisp.error("Number needed", this);
    }

    LispObject exptInteger(LispBigInteger a) throws Exception
    {
        return Jlisp.error("Number needed", this);
    }

    LispObject maxInteger(LispBigInteger a) throws Exception
    {
        return Jlisp.error("Number needed", this);
    }

    LispObject minInteger(LispBigInteger a) throws Exception
    {
        return Jlisp.error("Number needed", this);
    }

    LispObject andInteger(LispBigInteger a) throws Exception
    {
        return Jlisp.error("Number needed", this);
    }

    LispObject orInteger(LispBigInteger a) throws Exception
    {
        return Jlisp.error("Number needed", this);
    }

    LispObject xorInteger(LispBigInteger a) throws Exception
    {
        return Jlisp.error("Number needed", this);
    }

    LispObject gcdInteger(LispBigInteger a) throws Exception
    {
        return Jlisp.error("Number needed", this);
    }

    LispObject modAddInteger(LispBigInteger a) throws Exception
    {
        return Jlisp.error("Number needed", this);
    }

    LispObject modSubtractInteger(LispBigInteger a) throws Exception
    {
        return Jlisp.error("Number needed", this);
    }

    LispObject modMultiplyInteger(LispBigInteger a) throws Exception
    {
        return Jlisp.error("Number needed", this);
    }

    LispObject modDivideInteger(LispBigInteger a) throws Exception
    {
        return Jlisp.error("Number needed", this);
    }

    boolean eqnInteger(LispBigInteger a) throws Exception
    {
        Jlisp.error("Number needed", this);
        return false;
    }

    boolean neqnInteger(LispBigInteger a) throws Exception
    {
        Jlisp.error("Number needed", this);
        return false;
    }

    boolean geInteger(LispBigInteger a) throws Exception
    {
        Jlisp.error("Number needed", this);
        return false;
    }

    boolean geqInteger(LispBigInteger a) throws Exception
    {
        Jlisp.error("Number needed", this);
        return false;
    }

    boolean leInteger(LispBigInteger a) throws Exception
    {
        Jlisp.error("Number needed", this);
        return false;
    }

    boolean leqInteger(LispBigInteger a) throws Exception
    {
        Jlisp.error("Number needed", this);
        return false;
    }

    LispObject addSmallInteger(LispSmallInteger a) throws Exception
    {
        return Jlisp.error("Number needed", this);
    }

    LispObject subtractSmallInteger(LispSmallInteger a) throws Exception
    {
        return Jlisp.error("Number needed", this);
    }

    LispObject multiplySmallInteger(LispSmallInteger a) throws Exception
    {
        return Jlisp.error("Number needed", this);
    }

    LispObject divideSmallInteger(LispSmallInteger a) throws Exception
    {
        return Jlisp.error("Number needed", this);
    }

    LispObject remainderSmallInteger(LispSmallInteger a) throws Exception
    {
        return Jlisp.error("Number needed", this);
    }

    LispObject quotientAndRemainderSmallInteger(LispSmallInteger a) throws Exception
    {
        return Jlisp.error("Number needed", this);
    }

    LispObject modSmallInteger(LispSmallInteger a) throws Exception
    {
        return Jlisp.error("Number needed", this);
    }

    LispObject exptSmallInteger(LispSmallInteger a) throws Exception
    {
        return Jlisp.error("Number needed", this);
    }

    LispObject maxSmallInteger(LispSmallInteger a) throws Exception
    {
        return Jlisp.error("Number needed", this);
    }

    LispObject minSmallInteger(LispSmallInteger a) throws Exception
    {
        return Jlisp.error("Number needed", this);
    }

    LispObject andSmallInteger(LispSmallInteger a) throws Exception
    {
        return Jlisp.error("Number needed", this);
    }

    LispObject orSmallInteger(LispSmallInteger a) throws Exception
    {
        return Jlisp.error("Number needed", this);
    }

    LispObject xorSmallInteger(LispSmallInteger a) throws Exception
    {
        return Jlisp.error("Number needed", this);
    }

    LispObject gcdSmallInteger(LispSmallInteger a) throws Exception
    {
        return Jlisp.error("Number needed", this);
    }

    LispObject modAddSmallInteger(LispSmallInteger a) throws Exception
    {
        return Jlisp.error("Number needed", this);
    }

    LispObject modSubtractSmallInteger(LispSmallInteger a) throws Exception
    {
        return Jlisp.error("Number needed", this);
    }

    LispObject modMultiplySmallInteger(LispSmallInteger a) throws Exception
    {
        return Jlisp.error("Number needed", this);
    }

    LispObject modDivideSmallInteger(LispSmallInteger a) throws Exception
    {
        return Jlisp.error("Number needed", this);
    }

    boolean eqnSmallInteger(LispSmallInteger a) throws Exception
    {
        Jlisp.error("Number needed", this);
        return false;
    }

    boolean neqnSmallInteger(LispSmallInteger a) throws Exception
    {
        Jlisp.error("Number needed", this);
        return false;
    }

    boolean geSmallInteger(LispSmallInteger a) throws Exception
    {
        Jlisp.error("Number needed", this);
        return false;
    }

    boolean geqSmallInteger(LispSmallInteger a) throws Exception
    {
        Jlisp.error("Number needed", this);
        return false;
    }

    boolean leSmallInteger(LispSmallInteger a) throws Exception
    {
        Jlisp.error("Number needed", this);
        return false;
    }

    boolean leqSmallInteger(LispSmallInteger a) throws Exception
    {
        Jlisp.error("Number needed", this);
        return false;
    }

}

// End of LispObject.java

