/* Declare expression classses.  This is -*- C++ -*-.
   Copyright (C) 1992 Per Bothner.

This file is part of Q.

Q is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2, or (at your option)
any later version.

Q is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
GNU General Public License for more details.

You should have received a copy of the GNU General Public License
along with GNU CC; see the file COPYING.  If not, write to
the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.  */

#ifndef EXPRESSION_H
#define EXPRESSION_H
#pragma interface
#include <types.h>
#ifndef NEWTYPE_H
#include <newtype.h>
#endif

class VarStateLink; class Function;


/* macros for squeezing one bit into a pointer */
#define _Pointer_Mark_ 0x80000000
#define MarkPointer(p) ((void*)((long)(p)+_Pointer_Mark_))
#define UnmarkPointer(p) ((void*)((long)(p)-_Pointer_Mark_))
#define NomarkPointer(p) ((void*)((long)(p) & ~Pointer_Mark_))
#define PointerIsMarked(p) ((long)(p) < 0)

#define NullExpr &NullExprQ
extern struct ExprQuote NullExprQ;

enum ExprCode {
    UnknownExpr_code,
 /* the next two should be the first two (after UnknownExpr_code
  * for the sake of the macro IdentifierLike */
    Identifier_code,
    LoopCons_code, /* struct Identifier -- see lists.c:ParseLoopCons */
    ExprCall_code,
    ExtractExpr_code, ProcExpr_code, Block_code, ElseExpr_code,
    UnifyExpr_code, InverseExpr_code, ExprQuote_code, GotoExpr_code,
    ReturnExpr_code, LabelExpr_code,	/* in expression.Q */
    C_Code_code,
    MakeTuple_code,
    ListChoose_code, /* the operator @| --- a struct ExprStdOp */
    QuotedWordList_code,
    ListCons_code,
    Dummy_code, /* the expression ``_'' */
    ExprList_code, ExprPostfix_code,
    Collect_code,
    ExprNode_code,
    External_code, /* struct ExprCall */
    IndexExpr_code, /* the expression ``?'' --- a struct ExprStdOp */
    SetSortExpr_code,
/*  Many of the following are obsolete */    
    CmpLss_code, /* must be the first one, unless IsStdOpCode is fixed */
    CmpNeq_code, CmpGrt_code,
    CmpGeq_code, CmpEqu_code /*use UnifyExpr instead */, CmpLeq_code,
    PlusOp_code, MinusOp_code, FromOp_code,
    TimesOp_code, DivOp_code, IDivOp_code,
    TakeOp_code, DropOp_code, ConsOp_code,
    WhenOp_code, SetSort_code, Length_code, Concat_code,
    ListIndex_code, SetDiff_code,
    GiveNameToType_code, DefException_code,
    MakeSymbol_code, Word_code, MapPair_code,
    MakeString_code,
    SelectExpr_code, TypeDefExpr_code,	/* obsolete */
    RegExpr_code, MoveExpr_code,	/* in lowlevel.Q */
    Coerce_code, /* arg[0] is coercee, arg[1] is coercion function */
    Reduce_code,
    RedirectOut_code,
    ReadFileFrom_code,
    FileName_code,
    Union_code,
    CoerceSeq_code,
    RunCommand_code, Unquote_code, LispExpr_code, LispIf_code,
    CoerceStringList_code,
    Load_code, ExprQuoteOp_code, QuoteOnlyExpr_code, DynamicBind_code,
    ExtractField_code
};

struct ElseExpr; struct Block; struct Identifier; struct UnifyExpr;
struct ProcExpr;
struct ExprNode; struct InverseExpr; struct ExprQuote; struct ExprStdOp;

struct Expr_Ptr {
    Expr *E;
    Expr_Ptr() { }
    Expr_Ptr(Expr *expr) { E = expr; }
//    Expr_Ptr& operator=(Expr* expr) { E = expr; return *this; }
//    Expr_Ptr& operator=(Expr_Ptr expr) { E = expr.E; return *this; }
    operator Expr *() { return E; }
//    operator ExprPtr () { ExprPtr ep; ep.E = E; return ep; } /*Temporary*/
    void eval(void *dst, Type* dstType, struct DisplayEnv *env);
    Root *eval(DisplayEnv *env) {
	Root *dst;
	eval(&dst, &RefRoot, env);
	return dst;
    }
#define BAD_GPLUSPLUS
#ifdef BAD_GPLUSPLUS
    inline Expr* traverse(struct TraverseData *data);
#else
    Expr_Ptr traverse(struct TraverseData *data);
#endif
    inline enum ExprCode code() const;
    inline void printon(ostream&) const;
    inline void compile(CFile*);
    inline Type* val_type();
    inline void set_val_type(Type*);
    Expr * operator->() { return E; }
    ElseExpr *or() const { return (ElseExpr*)E; }
    Block * block() const { return (Block*)E; }
    Identifier * ident() const { return (Identifier*)E; }
    ExprNode * node() const { return (ExprNode*)E; }
    InverseExpr *inverse() const { return (InverseExpr*)E; }
    ExprQuote *quote() const { return (ExprQuote*)E; }
    ExprStdOp *bin() const { return (ExprStdOp*)E; }
    ProcExpr *proc() const { return (ProcExpr*)E; }
    UnifyExpr *unify() const { return (UnifyExpr*)E; }
};

#define IsStdOpCode(code) ((int)(code) >= (int)CmpLss_code)
#define ExprCodeOf(expr) ((enum ExprCode)(expr)->sourcePos.code)
#define IdentifierLike(ex) ((ex) != NullExpr && (ex)->code() <= LoopCons_code)

typedef struct Type * ExType;
#define ExType_Kind(ex) ExBogusType
#define ExType_Val(ex) ((int)(ex))
#define ExType_Ptr(ex) ((void *)(ex))
#define ExType_Type(ex) (ex)
#define ExType_BitSize(ex) ((int)(ex) & 0x1FFFFFF)
#define ExType_BitOffset(ex) ((int)(ex) >> 25) /* bitoffset in byte */
#define MakeExType(kind, ptr) ((struct Type*)(ptr))

#define ExPointerType 0
#define ExDirectType 1
#define ExConstantPtr 2
#define ExMethodPtr 3
/* the bit field types must be consecutive and last - see IsBitField */
#define ExSignedType 4
#define ExUnsignedType 5
#define ExBogusType 15
#define MakeSignedType(len) MakeExType(ExSignedType, (void*)len)
#define MakeUnsignedType(len) MakeExType(ExUnsignedType, (void*)len)

extern struct ReferenceType RefRoot;
#define ExTypePtr MakeExType(ExPointerType, &RefRoot)
#define ExTypeAny MakeExType(ExDirectType, AnyT)

#define SetStdExprFields(ex, ff) \
  ((ex)->type=0, (ex)->sourcePos = SourceLocation(ff), (ex)->flags = 0, (ex)->sourcePos.code = 0)

struct PipeResult {
    int wait_pid; /* ID of Process generating result, if any. */
    int file_id; /* File number to read result from. */
};

struct Expression : Root {
    DECLARE_MEMBERS(Expression)
	inline enum ExprCode code() const
	    { return ((enum ExprCode)sourcePos.code); }
    inline void set_code(enum ExprCode cod) { sourcePos.code = (int)cod; }
#define EString_Quote 1
    inline void set_location(Location& pos) {
	sourcePos.fileNo = pos.fileNo;
	sourcePos.byteNo = pos.byteNo;
	sourcePos.lineNo = pos.lineNo; }
    inline void clear_std_fields(enum ExprCode cod) {
	type=0; sourcePos.set_unknown(); flags= 0; set_code(cod); }
    virtual void printon(ostream&) const;
    virtual void eval(void* dst, Type* dstType, struct DisplayEnv *env);
    Root* eval(DisplayEnv *env) {
	Root* dst;
	eval(&dst, &RefRoot, env);
	return dst;
    }
    virtual Expression * traverse(struct TraverseData *data);
//    virtual Expression * traverseEvaluated(struct TraverseData *data);
    struct Type* val_type();
    inline void set_val_type(Type *t) { type = t; }
    virtual void compile(CFile*);
    inline Expression() { }
    inline Expression(enum ExprCode code) { clear_std_fields(code); }
    // Convert to an expression that evaluates to a StringList
    // that is the "quoted" value of this.
    // quote_words(data) == quote_words()->traverse(data).
    virtual Expression * quote_words(struct TraverseData *data = NULL);

    ExType type;
    struct Location sourcePos;
    unsigned short flags;
};

/* general expression flags */
#define LastfixProtect 0x800 /* only for Identifier or ExprNode */
#define ExprAnyPair 0x80
#define ExprAtMostOneResult 0x40
#define ExprCannotFail 0x20
#define ExprHasSingleResult (ExprAtMostOneResult|ExprCannotFail)

struct Identifier : public Expression {
    Symbol * name;	/* normal case, when code==Identifier_code */
    union {
	struct Declaration *decl;
	int nesting; /* only used during parsing */
    } v;
    Identifier *next;
    char *quoted_chars; /* symbol()->string()[i] was originally
	prefixed by '\' iff (quoted_chars != NULL && quoted_chars[i]). */
    Identifier() { clear_std_fields(Identifier_code); }
    virtual Expression * traverse(struct TraverseData *data);
    Expression * traverse1(struct TraverseData *data);
    Expression * traverse2(struct TraverseData *data);
//    virtual Expression * traverseEvaluated(struct TraverseData *data);
    virtual void eval(void* dst, Type* dstType, DisplayEnv *env);
    inline Declaration * decl() const { return v.decl; }
    inline Declaration *& decl() { return v.decl; }
    inline Symbol * symbol() const { return name; }
    virtual void printon(ostream&) const;
    virtual void compile(CFile*);
    virtual Expression * quote_words(struct TraverseData *data = NULL);
};
#define IdentHasMark 0x1000 /* '|' indicates export/public */
#define IdentFree 0x2000 /* only one use - do FreeToken */
#define IdentExplicit 0x8000 /* appears in explicit-declaration context */
#define IdentFirstRef 0x4000
//#define IdentNesting 0x400 /* probably not needed */
#define IdentExported 0x200
#define IdentFuncOnly 0x2 /* Lisp: Only look for function bindings. */
#define IdentValueOnly 0x1 /* Lisp: Only look for value bindings. */

class DummyExpr : public Identifier {
  public:
    DummyExpr();
    virtual void printon(ostream& outs) const;
    virtual Expression * traverse(struct TraverseData *data) { return this;}
    virtual void eval(void* dst, Type* dstType, DisplayEnv *env);
};


/* flags field of a struct Declaration */
#define PrivateDeclaration 1 /* may apply to GlobalDeclaration?? */
#define KnownDeclaration 2 /* (set but never used) decl->block is the value of decl; level==-1 */
#define NonLocalDeclaration 4 /* needed for a closure */
#define AnyPairDecl 8 /* ALMOST OBSOLETE */
/* #define GlobalDeclaration 8 * uses token; allocated statically */
#define LookupDeclaration 32

/* the following are used when Traversing an expression, to see
 * if a unification can be avoided.
 */
#define ImplicitDeclMask (64+128)
#define UnusedDeclaration 0
#define SetDeclaration 64
#define TentativeDeclaration 128 /* not used currently - requires resetting */
#define GeneralDeclaration (128+64)

/* #define UnboundDeclaration 0 */
/* OneRefDeclaration should be an attribute of an Identifier */
/* #define OneRefDeclaration 64 * initial reference of variable seen */
/* #define SetDeclaration 128 */
/* #define OtherDeclaration (64+128) */

#define Label_SizeCode (-3)	/* not a field but a label */
#define Unknown_SizeCode (-1)	/* don't know */
#define Include_SizeCode (-5)
#define DeclIsInclude(decl) ((decl)->size == Include_SizeCode)
	/* used to be: decl->name == sAsterisk */
#define IsBitField(decl) ((decl)->type.kind >= ExSignedType)
#define BitSize(decl) ((int)(decl)->type.ptr)
#define BitOffset(decl) ((decl)->offset)
#define BitSigned(decl) ... /* interpret as signed */
#define BitUnsigned(decl)
/* if IsBitField but neither BitSigned or BitUnsigned (keyword "bits",
 assume BitUnsigned, but default printout is in hex */

struct Declaration : public Field {
    struct Field dummyNextField;
/*    short offset;	* in bytes unless IsBitField */
    short size;	/* length in bytes, or one of of *_SizeCode above */
/*    Name name;*/
    unsigned char flags;
    unsigned char blockLevel;
    unsigned char _loop_nesting;
    char useCount; /* only 0, 1 or > 1 of interest */
    Expr_Ptr defining;
    Declaration *shadowed;
    struct VarStateLink *tr_link; /* see traverse.h and MarVarSet */
    inline unsigned char& loop_nesting() { return _loop_nesting; }
    inline int isPrivate() { return flags & PrivateDeclaration; }
    inline void setPrivate(int mode) {
	if (mode) flags |= PrivateDeclaration;
	else flags &= ~PrivateDeclaration; }
    inline VarStateLink ** linkAddr() { return &tr_link; }
    inline void set_const(void *addr) { Field::set_const(addr);
		type = MakeExType(ExConstantPtr, addr); }
    inline void set_proc(Function *func) { Field::set_proc(func);
	   type = MakeExType(ExMethodPtr, func); }
//   nline Declaration **next_ptr()
//         { return (Declaration**)dummyNextField.fchain_ptr(); }
    inline Declaration *&next()
	{ return *(Declaration**)dummyNextField.fchain_ptr(); }
    int must_allocate() const
	{ return (flags & ImplicitDeclMask) == GeneralDeclaration; }
#define index_in_decl_list(decl) (decl)->dummyNextField.flags
};
/* static decl: token is label with name: module_name$name[$nn] */
/* kinds of Declarations (and useage of 'next' field):
   explicit local variable: linked in search list
   implicit variable: linked within proc (by implicitVariables)
   copied values for closure:
	linked within ProcExpr as varList (sameName points to original)
   global variable

   explicit local uses offset field (level > 0)
   block:
	actual value if KnownDeclaration
	ProcExpr of defining proc if ImplicitDeclaration
   (OLD: sameName: points to UnifyExpr definition if ImplicitVariable)
possbility: if NonLocalDeclaration: points to DataToken ???
	points to other declaration for same name otherwise
 */

struct ExtractFieldExpr : public Expression {
    Expr *value;
    Field *field;
    ExtractFieldExpr(Expr*v, Field *f) { value = v; field = f; clear_std_fields(ExtractField_code); }
    virtual Expression * traverse(struct TraverseData *data);
    virtual void eval(void* dst, Type* dstType, DisplayEnv *env);
    virtual void printon(ostream&) const;
};

typedef Declaration** DeclListMark;
struct DeclList {
    struct Declaration *first, **last;
    inline DeclList() { first = NULL; last = &first; }
    void add(Declaration* decl) { *last = decl; last = &decl->next(); }
    void add_front(Declaration* decl) {
	decl->next() = first;
	if (!first) last = &decl->next();
	first = decl; }
    // Steal all decls from LIST that are more recent than MARK.
    void grab_from(DeclList& list, DeclListMark mark) {
	*last = *mark;
	if (*mark) last = list.last;
	list.last = mark;
	*mark = NULL;
    }
    inline DeclListMark mark() { return last; }
};

#if 0
struct Reference
  {
    struct Reference *next;	/* next refernce of (logically) same ident */
    struct Declaration *decl;
  };
#endif

struct ExprStdOp : public Expression {
    Expr_Ptr arg[2];
};

struct MapPairExpr : public ExprStdOp {
    MapPairExpr() { clear_std_fields(MapPair_code); }
    virtual void eval(void* dst, Type* dstType, DisplayEnv *env);
    virtual void printon(ostream&) const;
    Symbol* label() const;
    Expr_Ptr& value() const { return arg[1]; }
};
struct MakeTupleExpr : public Expression {
    Expr_Ptr seq;
    Expr *right;
    virtual Expression * traverse(struct TraverseData *data);
    MakeTupleExpr(Expr* exp, Expr* r=NULL) : seq(exp)
	{ clear_std_fields(MakeTuple_code); right = r; }
    virtual void printon(ostream&) const;
    virtual void eval(void* dst, Type* dstType, DisplayEnv *env);
    virtual void compile(CFile*);
    virtual Expression * quote_words(struct TraverseData *data = NULL);
};
struct CoerceSeqExpr : public Expression {
    Expr_Ptr coercee;
    CoerceSeqExpr() { clear_std_fields(CoerceSeq_code); }
    CoerceSeqExpr(Expr_Ptr c) {
	clear_std_fields(CoerceSeq_code); coercee = c; }
    virtual Expression * traverse(struct TraverseData *data);
    virtual void eval(void* dst, Type* dstType, DisplayEnv *env);
    virtual void printon(ostream&) const;
};
struct CoerceStringListExpr : public Expression {
    Expr_Ptr coercee;
    CoerceStringListExpr() { clear_std_fields(CoerceStringList_code); }
//    CoerceStringListExpr(Expr_Ptr c) {
//	clear_std_fields(CoerceStringList_code); coercee = c; }
    CoerceStringListExpr(Expr *c) {
	clear_std_fields(CoerceStringList_code); coercee.E = c; }
    virtual Expression * traverse(struct TraverseData *data);
    virtual void eval(void*, Type*, DisplayEnv *env);
    virtual void printon(ostream&) const;
};

typedef struct Any EnvType;
#define NullEnv MAKE_ANY(0,0)

#if 0
enum ExLKind { ExLExit, ExLEnd, ExLNormal, ExLNamed, ExLTupled};
/*struct ExList is defined in types.h */
/*typedef struct ExList ThunkList;*/
#define EndExL (Expr*)MarkPointer(1)
#define ExitExL (Expr*)MarkPointer(3)
#define TupleExL (Expr*)MarkPointer(5)
#endif

#define ExprOneWord 0x1000
#define ExprHasParens 0x2000
#define ExprDontOpParse 0x800 /* Don't check for operators */

struct ExprList : public Expression {
    Expr_Ptr *arg;
    int length;
/* each arg is "expr", or "name:", or end-marker */
    ExprList(int len = -1, Expr_Ptr* exprs = NULL);
    virtual Expression * traverse(struct TraverseData *data);
    inline int one_word() {return flags & ExprOneWord;}
    inline int has_parens() { return flags & ExprHasParens; }
    virtual void eval(void* dst, Type* dstType, DisplayEnv *env);
    virtual void printon(ostream&) const;
    virtual void compile(CFile*);
    virtual Expression * quote_words(struct TraverseData *data = NULL);
  protected:
    void printon(int postfix, ostream&) const;
};

struct ExprPostfix : public ExprList {
    ExprPostfix(int len = -1, Expr_Ptr* exprs = NULL);
    virtual void eval(void* dst, Type* dstType, DisplayEnv *env);
    virtual void printon(ostream&) const;
};

struct QuotedWordListExpr : public ExprList {
    virtual void eval(void* dst, Type* dstType, DisplayEnv *env);
    virtual Expression * traverse(struct TraverseData *data);
    QuotedWordListExpr(int len = -1, Expr_Ptr* exprs = NULL);
};

#define RUN_REPLACE_SELF 1   // Evaluate right_args, instead of exec'ing.
#define RUN_EVAL_ARGS 2
#define RUN_BACKGROUND 4

class RunCommandExpr : public Expression {
  public:
    Expr_Ptr left; // Left arg, or NULL
    Expr_Ptr right_args;
    int run_flags;
    unsigned int eval_args : 1; // Evaluate args, don't exec.
    int replaces_self() const { return run_flags & RUN_REPLACE_SELF; }
    RunCommandExpr(int rflags) : run_flags(rflags)
	{ clear_std_fields(RunCommand_code);}
    virtual Expression * traverse(struct TraverseData *data);
    virtual void eval(void* dst, Type* dstType, DisplayEnv *env);
    virtual void printon(ostream&) const;
//    inline char *name() const { return action->name; }
};

struct ExprNode : public Expression {
    Expr_Ptr arg, func;
    char postfix;	/* 1 if func only selects the application; 2=lastfix */
/*    Name name;		* arg name: func --- if postfix == 3 */
/*    Object up; */
    virtual void printon(ostream&) const;
    virtual Expression * quote_words(struct TraverseData *data = NULL);
};

struct ExprCall : public Expression {   /* call an external C or Q procedure; no arg testing */
    short kind;	      /*
	CALL	0: proc is (Name) Cproc;
	CALL	1: proc is expression returning a CProc;
		2: proc is struct Declaration (Q or C proc) !OBSOLETE!
	Call	3: proc is (Name) Qproc
	Call	4: proc is expression returning a Qproc
	CallR	5: proc is (Name) Qproc, using regs
	CallR	6: proc is expr Qproc, using regs
	CallC	7: proc is (Name) Cproc that returns struct Any
	CallC	8: proc is expression yielding Cproc returning Any
		9: proc is struct FunctionCall *
		10:proc is struct FunctionCall *; offset=clause#
		*/
    short args;		/* # of params */
    Expr_Ptr proc;	/* if kind=0: name of procedure; else expr */
    long offset;	/* add this to proc */
    Expr_Ptr arg[1];	/* 1st parameter expression */
    /* ... remaining parameters ... */
    virtual Expression * traverse(struct TraverseData *data);
    virtual void printon(ostream&) const;
};

struct ExprQuote : public Expression {
    struct Any quotee;
    char *text;
    int force_postfix; /* if 2nd op, force expr to be postfix */
    ExprQuote(Root*);
    ExprQuote(struct Any);
    Root *value() { return (Root*)quotee.addr; }
    virtual void printon(ostream&) const;
    virtual void eval(void*, Type*, struct DisplayEnv *env);
    void compile(CFile*);
    virtual Expression * traverse(struct TraverseData *data);
    virtual Expression * quote_words(struct TraverseData *data = NULL);
    virtual void dumpPtr(CFile *cf) const;
};
extern ExprQuote NULL_expr; // Evaluates to (Root*)NULL.  Handle with care!

// A quoted operator - i.e. a Functional value with precedence.

struct ExprQuoteOp : public ExprQuote {
    // value() is Functional
    short left_priority, right_priority;
    char *name;  // Name of GFunction implementing op, e.g., "__add".
    ExprQuoteOp(char* op, Root* f, int l, int r) : ExprQuote(f)
	{ text = op;
	  left_priority = l; right_priority = r; set_code(ExprQuoteOp_code);}
    ExprQuoteOp(char *op, char* fn, int l, int r) : ExprQuote(NULL) {
	name=fn; text = op; left_priority=l; right_priority=r;
	set_code(ExprQuoteOp_code);}
    virtual Expression * traverse(struct TraverseData *data);
    virtual Expression * quote_words(struct TraverseData *data = NULL);
    Functional* func_value();
    virtual void printon(ostream&) const;
};

extern ExprQuoteOp QEquals;

#if 0
struct ExtractExpr : public Expression {
    Expr_Ptr source;
    Expr_Ptr offset; /* offset is given in units of stepSize */
    int stepSize;
    ExtractExpr() { clear_std_fields(ExtractExpr_code); }
/* if stepSize==-1 or -2 then offset is a Declaration which names a field */
/* if -1: return lvalue (ApplyRecordField); if -2: rvalue (ExtractField) */
/*    virtual Expression * traverse(struct TraverseData *data);*/
};
#endif

/* Kinds of statement:
  expr
  forget id+
  x$F$y=>expr
  *: expr
  a: expr (== a=expr, except for Explicit declarations)
  a:: expr
 */
#define StatementNotRecursive 0x4000
enum StatementKind {
    ExprStatement,
    DeclStatement,
    MethodStatement,
    IncludeStatement,
    ForgetStatement,
    ClosureStatement /* a compiler-added unification to create a closure */
};
struct Statement
  {
    enum StatementKind kind;
    struct Location sourcePos;
    unsigned short flags;
    struct Statement *next;
    struct Identifier *idList; // Now only used for name of method.
    Expr_Ptr src;
  };

#define BlockReturnSelf 0x100
#define BlockIsGlobal 0x2000 /* same as block->level == 0 ??? */
#define BlockIsReturnable 0x4000 /* RETURN can exit loop or function blocks */
#define BlockHasReturn 0x8000
#define BlockGivenResult 0x1000	/* if result pre-allocated in AuxReg */
#define BlockIsLoopMagic 0xF0F /* size can have this value during parsing */

struct Block : public Expression { /* NOTE: must "match" TypeDefExpr */
    struct Statement *first, **last;
    short size;
    unsigned char level; /* twice level of nesting (to allow insertion of closures).  Calculated at traverse time. 0 for globals. */
    unsigned char combiner;
    struct DeclList decls;
    Block *enclosing; /* NULL if global; other lexically enclosing block. */
    char * globalName;
    struct RecordType *rtype;
    Expr* coercion;  // If non-NULL, coerce the result to this type.
    Block(Block *outer);
    virtual Expression * traverse(struct TraverseData *data);
    Block * bl_traverse(struct TraverseData *data);
    virtual void eval(void* dst, Type* dstType, DisplayEnv *env);
    virtual void printon(ostream&) const;
    void compile(CFile*);
};

#if 0
struct TypeDefExpr : public Expression { /* NOTE: must "match" Block */
    struct Statement *first, **last;
    char filler;
    char defs;
    short level;
    Object superType;	/* either Identifier or (struct Type *) */
    char *name;
    struct TypeKindInfo *kind;
    short size;
    struct RecordField *fieldList;
    struct Type *result; /* if non-NULL, result of evaluating self */
    struct ProcExpr *gen;
    struct TypeDefExpr *next; /* to link pendingTypeDefs */
    struct Block *block;
  };
#endif

struct ClassDefExpr : public Expression {
  };

struct RecordExpr : public Expression {
    Name name;
    Object param_list;
  };

struct TestBranch
  {
   struct Statement *matchers;
   Object body;	/* NULL if multiple lists of matchers per body */
   struct TestBranch *next;
  };

struct SelectExpr : public Expression {
    struct Statement *selectors;
    struct TestBranch *branches;
    short n_selectors;
    short kind;	/* 0: do first match; 1: do all matches */
  };

struct InverseExpr : public Expression {
    Expr_Ptr arg, func;
    InverseExpr() { clear_std_fields(InverseExpr_code); }
    virtual Expression * traverse(struct TraverseData *data);
    virtual void eval(void* dst, Type* dstType, DisplayEnv *env);
    virtual void printon(ostream&) const;
    virtual Expression * quote_words(struct TraverseData *data = NULL);
};

#define UnifyDontSwap 0x8000
struct UnifyExpr : public Expression {
    Expr_Ptr left, right;
    char set; /* 0: standard unification;
		 1: left is a temporary which is set from right;
		 2: Becomes (i.e. := )
		 3: identity */
    UnifyExpr(Expr *e1, Expr *e2, int kind = 0);
    virtual Expression * traverse(struct TraverseData *data);
    virtual void eval(void* dst, Type* dstType, DisplayEnv *env);
    virtual void printon(ostream&) const;
    void compile(CFile*);
};

struct ReturnExpr : public Expression {
    Expr_Ptr result;
    struct Block *block;
  };

struct GotoExpr : public Expression {
    struct LabelExpr *label;
    Expr_Ptr result;
  };

#if 0
struct LabelExpr : public Expression {
    Expr_Ptr leftExpr;
    Name name;
    struct Block *block;
    struct DataToken label[1];
  };
#endif

struct C_Code_Insert { Expr_Ptr expr; char *code; };

struct C_Code : public Expression {
    short inserts;
    char *code0;
    struct C_Code_Insert insert[1];
    virtual Expression * traverse(struct TraverseData *data);
};

struct ElseExpr : public Expression {
    Expr_Ptr e1, then, e2;
    short kind; /* 0 if "if => ||", 1 if "|" */
    short handlerCount;
/*    struct ExceptionClass *(exception[1]);*/
    virtual Expression * traverse(struct TraverseData *data);
    virtual void eval(void* dst, Type* dstType, DisplayEnv *env);
    void compile(CFile*);
    virtual void printon(ostream&) const;
};

class OrExpr : public ElseExpr {
  public:
    virtual void eval(void* dst, Type* dstType, DisplayEnv *env);
};

struct ParamExpr {
    struct ParamExpr *next;
    Expr_Ptr arg_expr;
    Expr *default_expr;
    char flags; /* same flags as those for struct Formal */
    Symbol * name; /* Label of keyword parameter. */
    ExType arg_type; /* only valid if arg_expr.code()==Identifier_code */
//  char param_kind; /* NOT USED! 0 = normal; 1 = named, private; 2 = named */
};

#define BitsIsShort(bs) ((bs).i < 0)
#define SizeofLongInBits 5
#define MAX_SHORT_BITS ((1<<SizeofLongInBits)-1) /* usually 31 */
#define GetBit(bs, el) ((bs).i < 0 ? (bs).i & 1<<(el) : GetBitLong(bs,el))

typedef union {
    long i; /* if negative */
    long *p; /* if positive */
} Bits;

/* for each formal parameter: */
struct Formal {
    union {
	void * v;
	Expr *ex; /* general expression to evaluate*/
	struct Field *id; /* pointer formal identifier struct */
    } u;
    Expr *default_expr;
/*  expression for default value (function passed (struct Partial*) ) */
    short flags;
    short index; /* if keyword: index into func's keyword list */
};
#define FormalMustUnify 1
#define FormalKeyword 2 /* Can be given by name (FormalMustUnify is invalid) */
#define FormalPositional 4 /* Can be given by position */
#define FormalMultiple (8+16) /* Mask for any FormalMultiple* variant */
#define FormalMultipleVector (8+16) /* Bundle many args into Vector */
#define FormalMultipleList 16 /* Bundle many args into list (Lisp: &REST) */
#define FormalMultipleMany 8 /* NOT IMPLEMENTED: Bundle into (int, Root**) */
#define FormalDontEvaluate 32
#if 0
-- only two bits are needed:
0 - Positional, Unify
1 - Positional, Copy
2 - Positional or Keyword, Copy
3 - Keyword only, Copy
Unify: 0
Positional: < 3
Keyword: >= 2
#endif

#define ClauseDirectCallOk 1
#define ClauseCompiled 2
#define ClauseHasNotBeenTraversed 4
#define ClauseAtMostOneResult 8
#define ClauseDontEvaluateArgs 16

struct ParamCount {
    unsigned short required; /* # of positional parameters without default */
    unsigned short optional :15; /* # of positional parameters with default */
    unsigned tuple :1; /* "rest" (tuple) parameters */
    int total() { return required+optional+tuple; }
    // Assuming a call with 'given' actual parameters:
    // If too many args, return count of excess args.
    // If too few args, return negative number.
    // Else return 0.
    int excess(int given) {
	if (required > given) return given - required; // Negative.
	if (tuple) return 0;
	given -= required+optional; // Calculate excess number.
	return given <= 0 ? 0 : given;
    }
};

/* The keywords table of a Clause is sorted by label. */
struct KeywordEntry {
    Symbol *label;
    short formal_number; /* index of Formal for this keyword */
    short inverse_formal_number; /* inverse permutation of formal_number */
};

struct Clause {
    Func code;
    struct ClassDesc *paramDesc;
    struct Formal *formals;
    struct Field *result, *self; /* point to paramDesc fields */
    short nParams;
    short minParams; /* nParams - # of params with default values */
    short paramSize; /* should probably be part of paramDesc somehow */
    short flags;
    struct Type *resultType;
    struct ProcExpr *expr;
    struct ParamCount pn[3]; /* 0: left params, 1: right params; 2: keywords */
    struct KeywordEntry* keywords;
    // These are only valid when flags&ClauseHasNotBeenTraversed.
    void procexpr(ProcExpr *p) { formals = (Formal*)p; }
    ProcExpr* procexpr() const { return (ProcExpr*)formals; }
};

#define FunctionNeedsEnvType 1 /* needs code to actually set this flag! */
#define FuncDontEvaluateArgs ClauseDontEvaluateArgs /*16*/
struct Function {
#if 0
    struct Type t;
#endif
    Symbol *fname;
    short flags;
    short nClauses;
    struct Clause *clauses;
    short nParams; short nKeywords;
    struct Field *keywords; /* union of keyword params for all clauses */
    short *keywordHash; /* used as if keywords+keywordHash were a ClassDesc */
    unsigned short min_required[2]; /* min([left/right].required) over all clauses */
    Symbol* sym_name() const { return fname; }
    Symbol*& sym_name() { return fname; }
    const char *str_name() const { return sym_name()->string(); }
};

struct FunctionCall {
    /* stuff that is usually known at compile-time about a call */
    struct Function *func;
    short unnamedParams;
 /* Should also have a list of possible clauses */
#if 0
    short *keywordMap;
    short namedParams;
#endif
};

struct Partial {
    struct FunctionCall fc;
    struct Type *envtype;
    void * env;
    struct Any func;
    short unnamedParams;
    short namedParams;
    Bits bitmap;    
    RootPtr arg[0];
};

#define PROC_EXTRA_LEVELS 3 /* desired value for 
	proc->block->level - proc->block->enclosing->level */
/* The 3 extra levels are for closure;params block; result block */
#define PARAM_LEVEL(proc) ((proc)->expr->level-(PROC_EXTRA_LEVELS-1))
#define ENVIR_LEVEL(proc) ((proc)->expr->level-PROC_EXTRA_LEVELS)

/*#define ProcCompatibleWithC 0x4000*/
#define ProcIsMacro 0x4000
#define ProcIsExternal 0x2000
#define ProcPrefix 1	/* if no left argument (postfix) */
#define ProcHasNamedParams 2
#define ProcHasNotBeenTraversed ClauseHasNotBeenTraversed
#define ProcIsPending 8
#define ProcDontEvaluateArgs ClauseDontEvaluateArgs /* 16 */

struct ProcExpr : public Expression {
    struct Block *expr;
    struct ParamExpr *argList; /* parameter number is "offset" */
    union {
	// chain can only be used iff flags&ProcHasNotBeenTraversed.
	struct Block *closure;
	// chain is used to link trdata->pendingProcs.
	ProcExpr* chain;
    };
    struct Clause *clause;
    struct Identifier *context; /* "self" - only if actually used */
    char procKind;
    char inherit_closure;
    unsigned char nesting;
    unsigned char displayMax;
    short nParams;
    struct Declaration *paramDecls;
    Expr_Ptr resultType;
    Symbol * fname;
    struct ParamCount pn[3]; /* 0: left params, 1: right params; 2: keywords */
    struct Function * _function;
    Root *lisp_body;
    Root *code_label; // Name of C function (if StringC: assembler name).
    ProcExpr(Block*);
    virtual void eval(void* dst, Type* dstType, DisplayEnv *env);
    virtual Expression * traverse(struct TraverseData *data);
    void traverse1(struct TraverseData *data);
    Expression* traverse2(struct TraverseData *data);
    virtual void printon(ostream&) const;
    inline Function* function() const { return _function; }
    inline Function*& function() { return _function; }
};

struct DisplayEnv {
    EnvType tryNext;
    unsigned char minLevel, maxLevel;
    void *(env[0]);
};

#define ConsMapping 0x4000
#define ConsTupled 0x2000
class ListConsExpr : public ExprList {
  public:
    virtual Expression * traverse(struct TraverseData *data);
    virtual void eval(void* dst, Type* dstType, DisplayEnv *env);
    virtual void printon(ostream&) const;
    virtual Expression * quote_words(struct TraverseData *data = NULL);
};

class MakeStringExpr : public ListConsExpr {
    int add_quotes;
  public:
    MakeStringExpr() { clear_std_fields(MakeString_code); add_quotes=0;}
    virtual Expression * traverse(struct TraverseData *data);
    virtual void printon(ostream&) const;
    virtual void eval(void*, Type*, struct DisplayEnv *env);
    virtual StringList *eval_quote(struct DisplayEnv *env);
    virtual Expression * quote_words(struct TraverseData *data = NULL);
};

class MakeSymbolExpr : public Expression {
  public:
    Expr* name; // evaluates to a StringC*
    MakeSymbolExpr(Expr* n) { clear_std_fields(MakeSymbol_code); name=n;}
    virtual Expression * traverse(struct TraverseData *data);
    virtual void printon(ostream&) const;
    virtual void eval(void* dst, Type* dstType, DisplayEnv *env);
};

#if 0
class WordExpr : public ExprList { // Should be merged with ExprList!
  public:
    WordExpr() { clear_std_fields(Word_code); flags |= ExprOneWord; }
    virtual Expression * traverse(struct TraverseData *data);
    virtual void eval(void* dst, Type* dstType, DisplayEnv *env);
};
#endif

class UnquoteExpr : public Expression {
  public:
    Expr_Ptr arg;
    UnquoteExpr(Expr*e) : arg(e) { clear_std_fields(Unquote_code);}
    virtual void printon(ostream&) const;
    virtual Expression * traverse(struct TraverseData *data);
    virtual void eval(void* dst, Type* dstType, DisplayEnv *env);
    virtual Expression * quote_words(struct TraverseData *data = NULL);
};

struct UnionExpr : public Expression {
    Expr *left, *right;
    virtual Expression * traverse(struct TraverseData *data);
    virtual void eval(void* dst, Type* dstType, DisplayEnv *env);
    virtual void printon(ostream&) const;
};

struct RedirectOut : public Expression {
    Expr *filename;
    Expr *action;
    unsigned int append: 1;
    unsigned int stderr_too : 1;
    short fd;
    RedirectOut(Expr *fname, Expr *action);
    virtual Expression * traverse(struct TraverseData *data);
    virtual void eval(void* dst, Type* dstType, DisplayEnv *env);
};

struct FileName : public Expression {
    Expr *filename;
    FileName(Expr *fname);
    virtual void eval(void* dst, Type* dstType, DisplayEnv *env);
    virtual Expression * traverse(struct TraverseData *data);
    virtual Expression * quote_words(struct TraverseData *data = NULL);
    virtual void printon(ostream&) const;
};

struct Module;
struct LoadExpr : public Expression {
    Expr *filename;
    Module *module;
    LoadExpr(Expr *fn, Module *m=NULL)
	{ filename = fn; module = m; clear_std_fields(Load_code);}
    virtual void eval(void* dst, Type* dstType, DisplayEnv *env);
    virtual Expression * traverse(struct TraverseData *data);
};

struct QuoteOnlyExpr : public Expression {
    // A dummy expression that is only valid when quoted.
    const StringC* name; // Quoted name.
    QuoteOnlyExpr(const StringC* n) : name(n)
	{ clear_std_fields(QuoteOnlyExpr_code);}
    virtual Expression * traverse(struct TraverseData *data);
    virtual Expression * quote_words(struct TraverseData *data = NULL);
    virtual void printon(ostream&) const;
};

struct DynamicBindExpr : public Expression {
    int count;
    Expr **var_exprs;
    Expr **init_exprs;
    Expr* body;
    DynamicBindExpr() { clear_std_fields(DynamicBind_code); }
    virtual void printon(ostream&) const;
    virtual Expression * traverse(struct TraverseData *data);
    virtual void eval(void* dst, Type* dstType, DisplayEnv *env);
};

struct ParseFile;
extern struct Declaration *Ident2Decl(Identifier*);
extern struct ExprStdOp * AllocStdOp(enum ExprCode, int);

extern struct Identifier * NewIdentifier(Symbol *name, struct ParseFile *ff);
extern struct ExprNode * NewExprNode(Expr *arg, Expr *func);
extern struct Declaration * Symbol2Declaration(Symbol *symbol);
extern Expr * NewBindExpr(Expr *name, Expr *val);
extern struct ExprQuote *DoQuote(void * object);
extern struct OrExpr *MakeOrNode(Expr *e1, Expr *e2);
extern struct ElseExpr *MakeElseNode(Expr *e1, Expr *e2);
extern struct ExprStdOp * AllocStdOp(enum ExprCode code, int n);
extern struct ExprCall * AllocExprCall(int i, Expr_Ptr *args);
extern void InitClause(struct Clause *clause, struct ProcExpr *proc);
extern struct ExprList * ConvertNode(struct ExprNode *expr);
//extern Expr * MakeLastfix(Expr *expr);
extern void CompileExprC(Expr *ex, CFile *cf);
#ifdef __cplusplus
extern struct Identifier * Decl2Ident(struct Declaration *decl);
extern void PrintExpr(Expr *ex, ostream&);
extern void ChainDeclaration(Declaration**&end, struct Declaration *decl);
inline void AddDeclaration(struct Block *block, struct Declaration *decl)
  {ChainDeclaration(block->decls.last, decl); decl->blockLevel = block->level;}
extern UnifyExpr *AppendDeclaration(Block*, Declaration*, Expr*);
extern void AllocContext(struct ProcExpr *proc);
extern struct Block* GetClosure(struct TraverseData *data);
extern struct Function *AllocFunction(Symbol*);
extern Root * AllocGenVar(struct Declaration *decl);
extern void BindRecordType(Block *closure, const RecordType *super);
extern StringList *String2Strings(char* arg, int free_arg = 1);
extern void EvalApply(void* dst, Type* dstType, Root *self, Root *left_arg,
		      Expr_Ptr *exList, DisplayEnv *env);
extern void EvalString(char *str, int len);
EXTERN RootPtr EvalInteractive(Expr *exp, struct TraverseData *, struct Module *);

inline void Expr_Ptr::eval(void *dst, Type* dstType, struct DisplayEnv *env)
{ E->eval(dst, dstType, env); }
#ifdef BAD_GPLUSPLUS
inline Expr* Expr_Ptr::traverse(struct TraverseData *data)
  { return E->traverse(data); }
#else
inline Expr_Ptr Expr_Ptr::traverse(struct TraverseData *data)
  { Expr_Ptr ptr; ptr.E = E->traverse(data); return ptr; }
#endif
inline enum ExprCode Expr_Ptr::code() const
  {return ((enum ExprCode)E->sourcePos.code);}
inline Type* Expression::val_type() {return ExType_Type(type);}
inline Type* Expr_Ptr::val_type() {return ExType_Type(E->type);}
inline void Expr_Ptr::set_val_type(Type* t) { E->type = t; }
inline void Expr_Ptr::printon(ostream& outs) const { E->printon(outs); }
inline void Expr_Ptr::compile(CFile *cf) { E->compile(cf); }
inline ostream& operator<<(ostream& outs,Expr_Ptr p)
  { p.printon(outs); return outs; }
extern Expr FailedParse[1];
extern ExprQuote NilExpr;
#endif
#endif EXPRESSION_H
