/*
 * tclNRE.h --
 *
 *	This file contains declarations for the infrastructure for
 *	non-recursive commands. Contents may or may not migrate to tcl.h,
 *	tcl.decls, tclInt.h and/or tclInt.decls
 *
 * Copyright (c) 2008 by Miguel Sofer
 *
 * See the file "license.terms" for information on usage and redistribution of
 * this file, and for a DISCLAIMER OF ALL WARRANTIES.
 *
 * // FIXME: RCS numbering?
 * RCS: @(#) $Id: tclCompCmds.c,v 1.140 2008/01/23 19:41:27 dgp Exp $
 */


#ifndef _TCLNONREC
#define _TCLNONREC

/*****************************************************************************
 * Stuff during devel
 *****************************************************************************/

#define USE_SMALL_ALLOC     1 /* perf is important for some of these things! */
#define USE_STACK_ALLOC     1 /* good mainly for debugging, crashes at
			       * smallest timing error */
#define ENABLE_ASSERTS      1

/*
 *   IMPLEMENTED IN THIS VERSION - flags for partial enabling of the different
 *   parts, useful for debugging. May not work - meant to be used at "all ones"
 */

#define USE_NR_PROC         1 /* are procs defined as NR functions or not?
			       * Used for testing that the old interfaces
			       * still work, as they are used by TclOO and
			       * iTcl */
#define USE_NR_TEBC         1 /* does TEBC know about his special powers?
			       * with 1 TEBC remains on stack, TEOV gets
			       * evicted. */
#define USE_NR_ALIAS        1 /* First examples: my job */

#define USE_NR_IMPORTS      1 /* First examples: my job */

#define USE_NR_TAILCALLS    1 /* Incomplete implementation as
			       * tcl::unsupported::tailcall; best semantics
			       * are yet not 100% clear to me. */

#define USE_NR_NS_ENSEMBLE  1 /* snit!! */

/* Here to remind me of what's still missing: none of these do anything today */

#define USE_NR_EVAL         0 /* Tcl_EvalObj should be easy; the others may
			       * require some adapting of the parser. dgp? */
#define USE_NR_UPLEVEL      0 /* piece of cake, I think */
#define USE_NR_VAR_TRACES   0 /* require major redesign, I fear. About time
			       * for it too! */

#define USE_NR_CONTINUATIONS 0

#define MAKE_3X_FASTER       0
#define RULE_THE_WORLD       0

#define USE_NR_CMD_TRACES  /* NEVER?? Maybe ... enter traces on the way in,
			    * leave traces done in the callback? So a trace
			    * just needs to replace the procPtr and
			    * clientData, and TEOV needn't know about the
			    * whole s**t! Mmhhh */

/*****************************************************************************
 * Stuff for the public api: gone to the stubs table!
 *
 * Question: should we allow more callback requests during the callback
 * itself? Easy enough to either handle or block, nothing done yet. We could
 * also "lock" the Tcl stack during postProc, but it doesn't sound
 * reasonable. I think.
 *****************************************************************************/

/*****************************************************************************
 * Private api fo NRE
 *****************************************************************************/

/*
 * Main data struct for representing NR commands (generated at runtime). 
 */

struct ByteCode;

/* Fill up a SmallAlloc: 4 free ptrs for the user */
typedef struct TEOV_callback {
    TclNR_PostProc *procPtr;
    ClientData data0;
    ClientData data1;
    ClientData data2;
    ClientData data3;
    struct TEOV_callback *nextPtr;
} TEOV_callback;
    

/* Try to keep within SmallAlloc sizes! */
typedef struct TEOV_record {
    int type;
    Command *cmdPtr;
    TEOV_callback *callbackPtr;
    struct TEOV_record *nextPtr;
    union {
	struct ByteCode *codePtr;
	struct {
	    Tcl_Obj *objPtr;
	    int flags;
	} obj;
	struct {
	    Tcl_ObjCmdProc *objProc;
	    ClientData clientData;
	} objProc;
	struct {
	    int objc;
	    Tcl_Obj *const *objv;
	} objv;
    } data;
#if !USE_SMALL_ALLOC
    /* Extra checks: can disappear later */
    Tcl_Obj **tosPtr;
#endif
} TEOV_record;

/*
 * The types for records; we save the first bit to indicate that it stores an
 * obj, to indicate the necessary refCount management. That is, odd numbers
 * only for obj-carrying types
 */

#define TCL_NR_NO_TYPE             0  /* for internal (cleanup) use only */
#define TCL_NR_BC_TYPE             2  /* procs, lambdas, TclOO+Itcl sometime ... */ 
#define TCL_NR_OBJPROC_TYPE        4  /* ns-imports (cmdd redirect) */
#define TCL_NR_TAILCALL_TYPE       6  
#define TCL_NR_TEBC_SWAPENV_TYPE   8  /* continuations, micro-threads !? */

#define TCL_NR_CMD_TYPE           1  /* i-alias, ns-ens use this */
#define TCL_NR_SCRIPT_TYPE         3  /* ns-eval, uplevel use this */

#define TCL_NR_HAS_OBJ(TYPE) ((TYPE) & 1)

#define TOP_RECORD(iPtr) (((Interp *)(iPtr))->execEnvPtr->recordPtr)

#define GET_TOSPTR(iPtr)			\
    (((Interp *)iPtr)->execEnvPtr->execStackPtr->tosPtr)

#if !USE_SMALL_ALLOC
#define STORE_EXTRA(iPtr, recordPtr)		\
    recordPtr->tosPtr = GET_TOSPTR(iPtr)
#else
#define STORE_EXTRA(iPtr, recordPtr)
#endif

/* A SINGLE record being pushed is what is detected as an NRE request by TEOV */ 

#define PUSH_RECORD(iPtr, recordPtr)				\
    TCLNR_ALLOC(interp, recordPtr);	\
    recordPtr->nextPtr = TOP_RECORD(iPtr);			\
    STORE_EXTRA(iPtr, recordPtr);				\
    TOP_RECORD(iPtr) = recordPtr;				\
    recordPtr->type = TCL_NR_NO_TYPE;                             \
    recordPtr->cmdPtr = NULL;            			\
    recordPtr->callbackPtr = NULL

#define TEBC_CALL(iPtr)				\
    (((Interp *)iPtr)->execEnvPtr->tebcCall)

#define TEBC_DATA(iPtr)				\
    (((Interp *)iPtr)->execEnvPtr->tebcData)

#define TEBC_DO_EXEC             1  /* MUST NOT be 0 */
#define TEBC_DO_TAILCALL         2

/*
 * These are only used by TEOV; here for ease of ref. They should move to
 * tclBasic.c later on.
 */

#define COMPLETE_RECORD(recordPtr)			\
    /* accesses variables by name, careful */		\
    recordPtr->cmdPtr = cmdPtr;				\

#if !USE_SMALL_ALLOC
#define CHECK_EXTRA(iPtr, recordPtr)		\
    (recordPtr->tosPtr == GET_TOSPTR(iPtr))
#else
#define CHECK_EXTRA(iPtr, recordPtr) 1
#endif

#define POP_RECORD(iPtr, recordPtr)		\
    {						\
	recordPtr = TOP_RECORD(iPtr);		\
	TOP_RECORD(iPtr) = recordPtr->nextPtr;	\
    }
				 

#define FREE_RECORD(iPtr, recordPtr)					\
    {									\
	TEOV_callback *callbackPtr = recordPtr->callbackPtr;		\
	if (TCL_NR_HAS_OBJ(recordPtr->type)) {		\
	    Tcl_DecrRefCount(recordPtr->data.obj.objPtr);		\
	}								\
	while (callbackPtr) {						\
	    callbackPtr = callbackPtr->nextPtr;				\
	    TclSmallFree(recordPtr->callbackPtr);			\
	}								\
	TCLNR_FREE(((Tcl_Interp *)iPtr), recordPtr);			\
    }

#define VALID_NEW_REQUEST(recordPtr)				\
    ( (recordPtr)->callbackPtr || ((recordPtr)->type != TCL_NR_NO_TYPE))

#define CHECK_VALID_RETURN(iPtr, recordPtr)			\
    ((TOP_RECORD(iPtr) == recordPtr)  &&			\
	    CHECK_EXTRA(iPtr, recordPtr))

#define READ_OBJV_RECORD(recordPtr)   /* TBD? Or read by hand (braille?) */


/*
 * functions
 */

#if 0
/* built as static inline in tclProc.c. Do TclOO/Itcl need this? */
MODULE_SCOPE int TclNR_BC  (Tcl_Interp * interp, ByteCode *codePtr,
                           TclNR_PostProc *postProcPtr, ClientData clientData);
#endif

/* The following starts purges the stack popping TclStackAllocs down to where
 * tosPtr has the requested value. Panics on failure.*/
MODULE_SCOPE void TclStackPurge(Tcl_Interp *interp, Tcl_Obj **tosPtr);

/*
 * Tailcalls!
 */

MODULE_SCOPE Tcl_ObjCmdProc TclNRApplyObjCmd;
MODULE_SCOPE Tcl_ObjCmdProc TclNRUplevelObjCmd;
MODULE_SCOPE Tcl_ObjCmdProc TclTailcallObjCmd;


/*****************************************************************************
 * Stuff that goes away: temp during devel
 *****************************************************************************/

#if USE_SMALL_ALLOC
#define TCLNR_ALLOC(interp, ptr) TclSmallAlloc(sizeof(TEOV_record), ptr)
#define TCLNR_FREE(interp, ptr)  TclSmallFree((ptr))
#elif USE_STACK_ALLOC
#define TCLNR_ALLOC(interp, ptr) (ptr = TclStackAlloc(interp, sizeof(TEOV_record)))
#define TCLNR_FREE(interp, ptr)  TclStackFree(interp, (ptr))
#else
#define TCLNR_ALLOC(interp, size, ptr) (ptr = ((ClientData) ckalloc(sizeof(TEOV_record))))
#define TCLNR_FREE(interp, ptr)  ckfree((char *) (ptr))
#endif

#if ENABLE_ASSERTS
#include <assert.h>
#else
#define assert(expr)
#endif

#endif /* _TCLNONREC */
