/*
 * ici.h - ICI exports for C and C++ interfacing to ICI.
 *
 * This file is automatically generated from internal ICI include files.
 * It is platform specific. This file is based on "conf-bsd.h".
 */

#ifndef ICI_ICI_H
#define ICI_ICI_H

#ifdef __cplusplus
extern "C" {
#endif


/* From fwd.h */

#include <stddef.h>
#include <stdlib.h>
#include <stdarg.h>
#include <stdio.h>
#include <string.h>
#ifndef NOSIGNALS
# ifdef SUNOS5
#  include <signal.h>
# endif
#endif

/*
 * ICI version number. Note that this occurs in a string in conf.c too.
 */
#define ICI_VER_MAJOR   4
#define ICI_VER_MINOR   1
#define ICI_VER_RELEASE 0

/*
 * The ICI version number composed into an 8.8.16 unsigned long for simple
 * comparisons. The components of this are also available as 'ICI_VER_MAJOR',
 * 'ICI_VER_MINOR', and 'ICI_VER_RELEASE'.
 *
 * This --macro-- forms part of the --ici-api--.
 */
#define ICI_VER \
    (((unsigned long)ICI_VER_MAJOR << 24) \
    | ((unsigned long)ICI_VER_MINOR << 16) \
    | ICI_VER_RELEASE)

/*
 * The oldet version number for which the binary interface for seperately
 * compiled modules is backwards compatible. This is updated whenever
 * the exernal interface changes in a way that could break already compiled
 * modules. We aim to never to do that again. See 'ici_interface_check()'.
 *
 * This --macro-- forms part of the --ici-api--.
 */
#define ICI_BACK_COMPAT_VER ((4UL << 24) | (0UL << 16) | 3)

/*
 * DLI is defined in some configurations (Windows, in the conf include file)
 * to be a declaration modifier which must be applied to data objects being
 * referenced from a dynamically loaded DLL.
 *
 * If it hasn't been defined yet, define it to be null. Most system don't
 * need it.
 */
#ifndef DLI
#define DLI
#endif

#ifndef ICI_PATH_SEP
/*
 * The character which seperates directories in a path list on this
 * architecture.
 *
 * This --macro-- forms part of the --ici-api--.
 */
#define ICI_PATH_SEP    ':' /* Default, may have been set in config file */
#endif

/*
 * The character which seperates segments in a path on this
 * architecture. This is the default value, it may have been set in
 * the config file.
 */
#ifndef ICI_DIR_SEP
/*
 * The character which seperates segments in a path on this
 * architecture.
 *
 * This --macro-- forms part of the --ici-api--.
 */
#define ICI_DIR_SEP    '/' /* Default, may have been set in config file */
#endif

#ifndef ICI_DLL_EXT
/*
 * The string which is the extension of a dynamicly loaded library on this
 * architecture.
 *
 * This --macro-- forms part of the --ici-api--.
 */
#define ICI_DLL_EXT     ".so" /* Default, may have been set in config file */
#endif

/*
 * A hash function for pointers.  This is used in a few places.  Notably in
 * the hash of object addresses for struct lookup.  It is a balance between
 * effectiveness, speed, and machine knowledge.  It may or may not be right
 * for a given machine, so we allow it to be defined in the config file.  But
 * if it wasn't, this is what we use.
 */
#ifndef ICI_PTR_HASH
#define ICI_PTR_HASH(p) \
     (ici_crc_table[((size_t)(p) >>  4) & 0xFF] \
    ^ ici_crc_table[((size_t)(p) >> 12) & 0xFF])
/*
 * This is an alternative that avoids looking up the crc table.
#define ICI_PTR_HASH(p) (((unsigned long)(p) >> 12) * 31 ^ ((unsigned long)(p) >> 4) * 17)
*/
#endif

/*
 * A 'random' value for Windows event handling functions to return
 * to give a better indication that an ICI style error has occured which
 * should be propagated back. See events.c
 */
#define ICI_EVENT_ERROR 0x7A41B291

#ifndef nels
#define nels(a)         (sizeof a / sizeof a[0])
#endif

#define ICI_OBJNAMEZ    30

typedef struct ici_array    ici_array_t;
typedef struct ici_catch    ici_catch_t;
typedef struct ici_sslot    ici_sslot_t;
typedef struct ici_set      ici_set_t;
typedef struct ici_struct   ici_struct_t;
typedef struct ici_exec     ici_exec_t;
typedef struct ici_float    ici_float_t;
typedef struct ici_file     ici_file_t;
typedef struct ici_func     ici_func_t;
typedef struct ici_cfunc    ici_cfunc_t;
typedef struct ici_method   ici_method_t;
typedef struct ici_int      ici_int_t;
typedef struct ici_mark     ici_mark_t;
typedef struct ici_null_t   ici_null_t;
typedef struct ici_obj      ici_obj_t;
typedef struct ici_objwsup  ici_objwsup_t;
typedef struct ici_op       ici_op_t;
typedef struct ici_pc       ici_pc_t;
typedef struct ici_ptr      ici_ptr_t;
typedef struct ici_regexp   ici_regexp_t;
typedef struct ici_src      ici_src_t;
typedef struct ici_str      ici_str_t;
typedef struct ici_type     ici_type_t;
typedef struct ici_wrap     ici_wrap_t;
typedef struct ici_ftype    ici_ftype_t;
typedef struct ici_forall   ici_forall_t;
typedef struct ici_parse    ici_parse_t;
typedef struct ici_mem      ici_mem_t;
typedef struct ici_handle   ici_handle_t;
typedef struct ici_debug    ici_debug_t;
typedef struct ici_code     ici_code_t;
typedef struct ici_name_id  ici_name_id_t;

/*
 * This define may be made before an include of 'ici.h' to suppress a group
 * of old (backward compatible) names. These names have been upgraded to
 * have 'ici_' prefixes since version 4.0.4. These names don't effect the
 * binary interface of the API; they are all type or macro names. But you
 * might want to suppress them if you get a clash with some other include
 * file (for example, 'file_t' has been known to clash with defines in
 * '<file.h>' on some systems).
 *
 * If you just was to get rid of one or two defines, you can '#undef' them
 * after the include of 'ici.h'.
 *
 * The names this define supresses are:
 *
 *  array_t     float_t     object_t    catch_t
 *  slot_t      set_t       struct_t    exec_t
 *  file_t      func_t      cfunc_t     method_t
 *  int_t       mark_t      null_t      objwsup_t
 *  op_t        pc_t        ptr_t       regexp_t
 *  src_t       string_t    type_t      wrap_t
 *  ftype_t     forall_t    parse_t     mem_t
 *  debug_t
 *
 * This --macro-- forms part of the --ici-api--.
 */
#ifndef ICI_NO_OLD_NAMES

#   define array_t          ici_array_t
#   define float_t          ici_float_t
#   define object_t         ici_obj_t
#   define catch_t          ici_catch_t
#   define slot_t           ici_sslot_t
#   define set_t            ici_set_t
#   define struct_t         ici_struct_t
#   define exec_t           ici_exec_t
#   define file_t           ici_file_t
#   define func_t           ici_func_t
#   define cfunc_t          ici_cfunc_t
#   define method_t         ici_method_t
#   define int_t            ici_int_t
#   define mark_t           ici_mark_t
#   define null_t           ici_null_t
#   define objwsup_t        ici_objwsup_t
#   define op_t             ici_op_t
#   define pc_t             ici_pc_t
#   define ptr_t            ici_ptr_t
#   define regexp_t         ici_regexp_t
#   define src_t            ici_src_t
#   define string_t         ici_str_t
#   define type_t           ici_type_t
#   define wrap_t           ici_wrap_t
#   define ftype_t          ici_ftype_t
#   define forall_t         ici_forall_t
#   define parse_t          ici_parse_t
#   define mem_t            ici_mem_t
#   define debug_t          ici_debug_t

#endif /* ICI_NO_OLD_NAMES */


extern DLI ici_int_t    *ici_zero;
extern DLI ici_int_t    *ici_one;
extern DLI char         *ici_error;
extern DLI ici_exec_t   *ici_execs;
extern DLI ici_exec_t   *ici_exec;
extern DLI ici_array_t  ici_xs;
extern DLI ici_array_t  ici_os;
extern DLI ici_array_t  ici_vs;

extern DLI long         ici_vsver;

#define NSUBEXP         (10)
extern DLI int  re_bra[(NSUBEXP + 1) * 3];
extern DLI int  re_nbra;

extern DLI volatile int ici_aborted;            /* See exec.c */

extern DLI int  ici_dont_record_line_nums;      /* See lex.c */
extern DLI char *ici_buf;                       /* See buf.h */
extern DLI int  ici_bufz;                       /* See buf.h */

extern DLI ici_ftype_t  ici_stdio_ftype;
extern DLI ici_ftype_t  ici_popen_ftype;

extern DLI ici_null_t   o_null;

/*
 * This ICI NULL object. It is of type '(ici_obj_t *)'.
 *
 * This --macro-- forms part of the --ici-api--.
 */
#define ici_null        (objof(&o_null))

extern DLI ici_debug_t *ici_debug;

extern char             ici_version_string[];

extern unsigned long const ici_crc_table[256];
extern int              ici_exec_count;

/*
 * Use 'return ici_null_ret();' to return a ICI NULL from an intrinsic
 * fuction.
 *
 * This --macro-- forms part of the --ici-api--.
 */
#define ici_null_ret()  ici_ret_no_decref(objof(&o_null))

extern ici_obj_t        *ici_atom_probe(ici_obj_t *o);
extern ici_obj_t        *ici_copy_simple(ici_obj_t *);
extern ici_obj_t        *ici_fetch_fail(ici_obj_t *, ici_obj_t *);
extern ici_obj_t        *ici_atom(ici_obj_t *, int);
extern int              ici_parse_file(char *, char *, ici_ftype_t *);
extern ici_array_t      *ici_array_new(ptrdiff_t);
extern ici_mem_t        *ici_mem_new(void *, size_t, int, void (*)());
extern ici_str_t        *ici_str_alloc(int);
extern ici_str_t        *ici_str_new_nul_term(char *);
extern ici_str_t        *ici_str_get_nul_term(char *);
extern ici_set_t        *ici_set_new(void);
extern ici_struct_t     *ici_struct_new(void);
extern ici_float_t      *ici_float_new(double);
extern ici_file_t       *ici_file_new(void *, ici_ftype_t *, ici_str_t *, ici_obj_t *);
extern ici_int_t        *ici_int_new(long);
extern int              ici_interface_check(unsigned long, unsigned long, char const *);
extern ici_str_t        *ici_str_new(char *, int);
extern ici_ptr_t        *ici_ptr_new(ici_obj_t *, ici_obj_t *);
extern ici_regexp_t     *ici_regexp_new(ici_str_t *, int);
extern int              ici_assign_fail(ici_obj_t *, ici_obj_t *, ici_obj_t *);
extern ici_file_t       *ici_sopen(char *, int, ici_obj_t *);
extern unsigned long    ici_hash_unique(ici_obj_t *);
extern int              ici_cmp_unique(ici_obj_t *, ici_obj_t *);
extern int              ici_get_last_errno(const char *, const char *);
extern int              ici_argcount(int);
extern int              ici_argerror(int);
extern void             ici_struct_unassign(ici_struct_t *, ici_obj_t *);
extern int              ici_set_unassign(ici_set_t *, ici_obj_t *);
extern char             *ici_objname(char [ICI_OBJNAMEZ], ici_obj_t *);
extern int              ici_file_close(ici_file_t *f);
extern int              ici_ret_with_decref(ici_obj_t *);
extern int              ici_int_ret(long);
extern int              ici_ret_no_decref(ici_obj_t *);
extern int              ici_typecheck(char *, ...);
extern int              ici_retcheck(char *, ...);
extern int              ici_init(void);
extern void             ici_uninit(void);
extern ici_file_t       *ici_need_stdin(void);
extern ici_file_t       *ici_need_stdout(void);
extern ici_array_t      *ici_need_path(void);
extern void             ici_reclaim(void);
extern int              ici_str_ret(char *);
extern int              ici_float_ret(double);
extern int              ici_func(ici_obj_t *, char *, ...);
extern int              ici_method(ici_obj_t *, ici_str_t *, char *, ...);
extern int              ici_funcv(ici_obj_t *, ici_obj_t *, char *, va_list);
extern int              ici_call(ici_str_t *, char *, ...);
extern int              ici_callv(ici_str_t *, char *, va_list);
extern int              ici_cmkvar(ici_objwsup_t *, char *, int, void *);
extern int              ici_set_val(ici_objwsup_t *, ici_str_t *, int, void *);
extern int              ici_fetch_num(ici_obj_t *, ici_obj_t *, double *);
extern int              ici_fetch_int(ici_obj_t *, ici_obj_t *, long *);
extern int              ici_assign_cfuncs(ici_objwsup_t *, ici_cfunc_t *);
extern int              ici_def_cfuncs(ici_cfunc_t *);
extern int              ici_main(int, char **);
extern ici_method_t     *ici_method_new(ici_obj_t *, ici_obj_t *);
extern ici_handle_t     *ici_handle_new(void *, ici_str_t *, ici_objwsup_t *);
extern ici_handle_t     *ici_handle_probe(void *, ici_str_t *);
extern int              ici_register_type(ici_type_t *t);
extern void             ici_rego_work(ici_obj_t *o);
extern ptrdiff_t        ici_array_nels(ici_array_t *);
extern int              ici_grow_stack(ici_array_t *, ptrdiff_t);
extern int              ici_fault_stack(ici_array_t *, ptrdiff_t);
extern void             ici_array_gather(ici_obj_t **, ici_array_t *, ptrdiff_t, ptrdiff_t);
extern int              ici_array_push(ici_array_t *, ici_obj_t *);
extern int              ici_array_rpush(ici_array_t *, ici_obj_t *);
extern ici_obj_t        *ici_array_pop(ici_array_t *);
extern ici_obj_t        *ici_array_rpop(ici_array_t *);
extern ici_obj_t        *ici_array_get(ici_array_t *, ptrdiff_t);
extern void             ici_invalidate_struct_lookaside(ici_struct_t *);
extern int              ici_engine_stack_check(void);
extern void             ici_atexit(void (*)(void), ici_wrap_t *);
extern ici_objwsup_t    *ici_class_new(ici_cfunc_t *cf, ici_objwsup_t *super);
extern ici_objwsup_t    *ici_module_new(ici_cfunc_t *cf);
extern int              ici_handle_method_check(ici_obj_t *, ici_str_t *, ici_handle_t **, void **);
extern int              ici_method_check(ici_obj_t *o, int tcode);
extern unsigned long    ici_crc(unsigned long, unsigned char const *, ptrdiff_t);
extern int              ici_str_need_size(ici_str_t *, int);
extern ici_str_t        *ici_str_buf_new(int);
extern int              ici_parse(ici_file_t *, ici_objwsup_t *);
extern ici_obj_t        *ici_eval(ici_str_t *);
extern ici_obj_t        *ici_make_handle_member_map(ici_name_id_t *);
extern int              ici_parse_fname(char *);
extern ici_obj_t        **ici_array_find_slot(ici_array_t *, ptrdiff_t);

extern ici_exec_t       *ici_leave(void);
extern void             ici_enter(ici_exec_t *);
extern void             ici_yield(void);
extern int              ici_waitfor(ici_obj_t *);
extern int              ici_wakeup(ici_obj_t *);
extern int              ici_init_thread_stuff(void);

extern DLI int          ici_debug_enabled;
extern int              ici_debug_ign_err;
extern DLI void         ici_debug_ignore_errors(void);
extern DLI void         ici_debug_respect_errors(void);

#ifdef NODEBUGGING
    /*
     * If debug is not compiled in, we let the compiler use it's sense to
     * remove a lot of the debug code in performance critical areas.
     * Just to save on lots of ifdefs.
     */
#   define ici_debug_active 0
#else
    /*
     * Debugging is compiled-in. It is active if it is enabled at
     * run-time.
     */
#   define ici_debug_active     ici_debug_enabled
#endif

#ifndef NOSIGNALS
#ifdef SUNOS5
extern volatile sigset_t ici_signals_pending;
#else
extern volatile long    ici_signals_pending;
#endif
extern volatile long    ici_signals_count[];
extern void             ici_signals_init(void);
extern int              ici_signals_invoke_handlers(void);
extern int              ici_signals_blocking_syscall(int);
#else
/*
 * Let compiler remove code without resorting to ifdefs as for debug.
 */
#define ici_signals_pending 0
#define ici_signals_blocking_syscall(x)
#define ici_signals_invoke_handlers()
#endif

#ifdef BSD
extern int      select();
#endif

#ifdef  sun
double strtod(const char *ptr, char **endptr);
#endif

#ifndef NOTRACE
extern void     trace_pcall(ici_obj_t *);
#endif


/* From object.h */


/*
 * ici_types[] is the map from the small integer type codes found in the
 * o_tcode field of every object header, to a type structure (below) that
 * characterises that type of object.  Standard core data types have standard
 * positions (see TC_* defines below).  Other types are registered at run-time
 * by calls to ici_register_type() and are given the next available slot.
 */
#define ICI_MAX_TYPES   64
extern DLI ici_type_t   *ici_types[ICI_MAX_TYPES];

/*
 * Every object has a header. In the header the o_tcode (type code) field
 * can be used to index the ici_types[] array to discover the obejct's
 * type structure. This is the type structure.
 *
 * Implemantations of new types typically declare one of these strutures
 * statically and initialise its members with the functions that determine the
 * nature of the new type.  (Actually, most of the time it is only initialised
 * as far as the 't_name' field.  The remainder is mostly for intenal ICI use
 * and should be left zero.)
 *
 * This --struct-- forms part of the --ici-api--.
 */
struct ici_type
{
    unsigned long (*t_mark)(ici_obj_t *);
    void        (*t_free)(ici_obj_t *);
    unsigned long (*t_hash)(ici_obj_t *);
    int         (*t_cmp)(ici_obj_t *, ici_obj_t *);
    ici_obj_t   *(*t_copy)(ici_obj_t *);
    int         (*t_assign)(ici_obj_t *, ici_obj_t *, ici_obj_t *);
    ici_obj_t   *(*t_fetch)(ici_obj_t *, ici_obj_t *);
    char        *t_name;
    void        (*t_objname)(ici_obj_t *, char [ICI_OBJNAMEZ]);
    int         (*t_call)(ici_obj_t *, ici_obj_t *);
    ici_str_t   *t_ici_name;
    int         (*t_assign_super)(ici_obj_t *, ici_obj_t *, ici_obj_t *, ici_struct_t *);
    int         (*t_fetch_super)(ici_obj_t *, ici_obj_t *, ici_obj_t **, ici_struct_t *);
    int         (*t_assign_base)(ici_obj_t *, ici_obj_t *, ici_obj_t *);
    ici_obj_t   *(*t_fetch_base)(ici_obj_t *, ici_obj_t *);
    ici_obj_t   *(*t_fetch_method)(ici_obj_t *, ici_obj_t *);
    void        *t_reserved2;   /* Must be zero. */
    void        *t_reserved3;   /* Must be zero. */
    void        *t_reserved4;   /* Must be zero. */
};
/*
 * t_mark(o)            Must sets the O_MARK flag in o->o_flags of this object
 *                      and all objects referenced by this one which don't
 *                      already have O_MARK set.  Returns the approximate
 *                      memory cost of this and all other objects it sets the
 *                      O_MARK of.  Typically recurses on all referenced
 *                      objects which don't already have O_MARK set (this
 *                      recursion is a potential problem due to the
 *                      uncontrolled stack depth it can create).  This is only
 *                      used in the marking phase of garbage collection.
 *
 *                      The macro ici_mark() calls the t_mark function of the
 *                      object (based on object type) if the O_MARK flag of
 *                      the object is clear, else it returns 0.  This is the
 *                      usual interface to an object's mark function.
 *
 *                      The mark function implemantation of objects can assume
 *                      the O_MARK flag of the object they are being invoked
 *                      on is clear.
 *
 * t_free(o)            Must free the object o and all associated data, but not
 *                      other objects which are referenced from it.  This is
 *                      only called from garbage collection.  Care should be
 *                      taken to remember that errors can occur during object
 *                      creation and that the free function might be asked to
 *                      free a partially allocated object.
 *
 * t_cmp(o1, o2)        Must compare o1 and o2 and return 0 if they are the
 *                      same, else non zero.  This similarity is the basis for
 *                      merging objects into single atomic objects and the
 *                      implementation of the == operator.
 *
 *                      Currently only zero versus non-zero results are
 *                      significant.  However in future versions the t_cmp()
 *                      function may be generalised to return less than, equal
 *                      to, or greater than zero depending if 'o1' is less
 *                      than, equal to, or greater than 'o2'.  New
 *                      implementations would be wise to adopt this usage now.
 *
 *                      Some objects are by nature both unique and
 *                      intrinsically atomic (for example, objects which are
 *                      one-to-one with some other allocated data which they
 *                      alloc when the are created and free when they die).
 *                      For these objects the existing function
 *                      ici_cmp_unique() can be used as their implementation
 *                      of this function.
 *
 *                      It is very important in implementing this function not
 *                      to miss any fields which may otherwise distinguish two
 *                      obejcts.  The cmp, hash and copy operations of an
 *                      object are all related.  It is useful to check that
 *                      they all regard the same data fields as significant in
 *                      performing their operation.
 *
 * t_copy(o)            Must returns a copy of the given object.  This is the
 *                      basis for the implementation of the copy() function.
 *                      On failure, NULL is returned and error is set.  The
 *                      returned object has been ici_incref'ed.  The returned
 *                      object should cmp() as being equal, but be a distinct
 *                      object for objects that are not intrinsically atomic.
 *
 *                      Intrinsically atomic objects may use the existing
 *                      function ici_copy_simple() as their implemenation of
 *                      this function.
 *
 *                      Return NULL on failure, usual conventions.
 *
 * t_hash(o)            Must return an unsigned long hash which is sensitive
 *                      to the value of the object.  Two objects which cmp()
 *                      equal should return the same hash.
 *
 *                      The returned hash is used in a hash table shared by
 *                      objects of all types.  So, somewhat problematically,
 *                      it is desireable to generate hashes which have good
 *                      spread and seperation across all objects.
 *
 *                      Some objects are by nature both unique and
 *                      intrinsically atomic (for example, objects which are
 *                      one-to-one with some other allocated data which they
 *                      alloc when the are created and free when they die).
 *                      For these objects the existing function
 *                      'ici_hash_unique()' can be used as their
 *                      implementation of this function.
 *
 * t_assign(o, k, v)    Must assign to key 'k' of the object 'o' the value
 *                      'v'.  Return 1 on error, else 0.
 *
 *                      The existing function 'ici_assign_fail()' may be used
 *                      both as the implementation of this function for object
 *                      types which do not support any assignment, and as a
 *                      simple method of generating an error for particular
 *                      assignments which break some rule of the object.
 *
 *                      Not that it is not necessarilly wrong for an
 *                      intrinsically atomic object to support some form of
 *                      assignment.  Only for the modified field to be
 *                      significant in a 't_cmp()' operation.  Objects which
 *                      are intrinsically unique and atomic often support
 *                      assignments.
 *
 *                      Return non-zero on failure, usual conventions.
 *
 * t_fetch(o, k)        Fetch the value of key 'k' of the object 'o'.  Return
 *                      NULL on error.
 *              
 *                      Note that the returned object does not have any extra
 *                      reference count; however, in some circumstances it may
 *                      not have any garbage collector visible references to
 *                      it.  That is, it may be vunerable to a garbage
 *                      collection if it is not either incref()ed or hooked
 *                      into a referenced object immediately.  Callers are
 *                      responsible for taking care.
 *
 *                      The existing function 'ici_fetch_fail()' may be used
 *                      both as the implementation of this function for object
 *                      types which do not support any assignment, and as a
 *                      simple method of generating an error for particular
 *                      fetches which break some rule of the object.
 *
 *                      Return NULL on failure, usual conventions.
 *
 * t_name               The name of this type. Use for the implementation of
 *                      'typeof()' and in error messages.  But apart from that,
 *                      type names have no fundamental importance in the
 *                      langauge and need not even be unique.
 *
 * t_objname(o, p)      Must place a short (less than 30 chars) human readable
 *                      representation of the object in the given buffer.
 *                      This is not intended as a basis for re-parsing or
 *                      serialisation.  It is just for diagnostics and debug.
 *                      An implementation of 't_objname()' must not allocate
 *                      memory or otherwise allow the garbage collector to
 *                      run.  It is often used to generate formatted failure
 *                      messages after an error has occured, but before
 *                      cleanup has completed.
 *
 * t_call(o, s)         Must call the object 'o'.  If the object does not
 *                      support being called, this should be NULL.  If 's' is
 *                      non-NULL this is a method call and s is the subject
 *                      object of the call.  Return 1 on error, else 0.
 *                      The environment upon calling this function is
 *                      the same as that for intrinsic functions. Functions
 *                      and techniques that can be used in intrinsic function
 *                      implementations can be used in the implementation of
 *                      this function. The object being called can be assumed
 *                      to be on top of the operand stack
 *                      (i.e. ici_os.a_top[-1])
 *
 * t_ici_name           A 'ici_str_t' copy of 't_name'. This is just a cached
 *                      version so that typeof() doesn't keep re-computing the
 *                      string.
 *
 * t_fetch_method       An optional alternative to the basic 't_fetch()' that
 *                      will be called (if supplied) when doing a fetch for
 *                      the purpose of forming a method.  This is really only
 *                      a hack to support COM under Windows.  COM allows
 *                      remote objects to have properties, like
 *                      object.property, and methods, like object:method().
 *                      But without this special hack, we can't tell if a
 *                      fetch operation is supposed to perform the COM get/set
 *                      property operation, or return a callable object for a
 *                      future method call.  Most objects will leave this
 *                      NULL.
 *
 *                      Return NULL on failure, usual conventions.
 * --ici-api--
 */

/*
 * Macros to perform the operation on the object.
 */

/*
 * The recursive traversal of all objects performed by marking is particularly
 * expensive. So we take pains to cut short function calls wherever possible.
 * Thus the size of this macro. The o_leafz field of an object tells us it
 * doesn't reference any other objects and is of small (ie o_leafz) size.
 *
 * Note that the argument 'o' is subject to multiple expansions.
 */
#define ici_mark(o)         ((objof(o)->o_flags & O_MARK) == 0 \
                            ? (objof(o)->o_leafz != 0 \
                                ? (objof(o)->o_flags |= O_MARK, objof(o)->o_leafz) \
                                : (*ici_typeof(o)->t_mark)(objof(o))) \
                            : 0L)

/*
 * Fetch the value of the key 'k' from the object 'o'.  This macro just calls
 * the particular object's 't_fetch()' function.
 *
 * Note that the returned object does not have any extra reference count;
 * however, in some circumstances it may not have any garbage collector
 * visible references to it.  That is, it may be vunerable to a garbage
 * collection if it is not either incref()ed or hooked into a referenced
 * object immediately.  Callers are responsible for taking care.
 *
 * Note that the argument 'o' is subject to multiple expansions.
 *
 * Returns NULL on failure, usual conventions.
 *
 * This --macro-- forms part of the --ici-api--.
 */
#define ici_fetch(o,k)      ((*ici_typeof(o)->t_fetch)(objof(o), objof(k)))

/*
 * Assign the value 'v' to key 'k' of the object 'o'. This macro just calls
 * the particular object's 't_assign()' function.
 *
 * Note that the argument 'o' is subject to multiple expansions.
 *
 * Returns non-zero on error, usual conventions.
 *
 * This --macro-- forms part of the --ici-api--.
 */
#define ici_assign(o,k,v)   ((*ici_typeof(o)->t_assign)(objof(o), objof(k), objof(v)))

/*
 * Assign the value 'v' to key 'k' of the object 'o', but only assign into
 * the base object, even if there is a super chain. This may only be called
 * on objects that support supers.
 *
 * Note that the argument 'o' is subject to multiple expansions.
 *
 * Returns non-zero on error, usual conventions.
 *
 * This --macro-- forms part of the --ici-api--.
 */
#define ici_assign_base(o,k,v) ((*ici_typeof(o)->t_assign_base)(objof(o), objof(k), objof(v)))
/*
 * This version retained for backwards compatibility.
 */
#define assign_base(o,k,v) ((*ici_typeof(o)->t_assign_base)(objof(o), objof(k), objof(v)))

/*
 * Fetch the value of the key 'k' from the object 'o', but only consider
 * the base object, even if there is a super chain. See the notes on
 * 'ici_fetch()', which also apply here. The object 'o' *must* be one that
 * supports super types (such as a 'struct' or a 'handle').
 *
 * This --macro-- forms part of the --ici-api--.
 */
#define ici_fetch_base(o,k) ((*ici_typeof(o)->t_fetch_base)(objof(o), objof(k)))
/*
 * This version retained for backwards compatibility.
 */
#define fetch_base(o,k) ((*ici_typeof(o)->t_fetch_base)(objof(o), objof(k)))

/*
 * Fetch the value of the key 'k' from 'o' and store it through 'v', but only
 * if the item 'k' is already an element of 'o' or one of its supers.  See the
 * notes on 'ici_fetch()', which also apply here.  The object 'o' *must* be
 * one that supports supers (such as a 'struct' or a 'handle').
 *
 * This function is used internally in fetches up the super chain (thus the
 * name).  In this context the argument 'b' indicates the base struct of the
 * fetch and is used to maintain the internal lookup look-aside mechanism.  If
 * not used in this manner, 'b' should be supplied as NULL.
 *
 * Return -1 on error, 0 if it was not found, and 1 if it was found.  If
 * found, the value is stored in *v.
 *
 * This --macro-- forms part of the --ici-api--.
 */
#define ici_fetch_super(o,k,v,b) ((*ici_typeof(o)->t_fetch_super)(objof(o), objof(k), v, b))

/*
 * Assign the value 'v' at the key 'k' of the object 'o', but only if the key
 * 'k' is already an element of 'o' or one of its supers.  The object 'o'
 * *must* be one that supports supers (such as a 'struct' or a 'handle').
 *
 * This function is used internally in assignments up the super chain (thus
 * the name).  In this context the argument 'b' indicates the base struct of
 * the assign and is used to maintain the internal lookup look-aside
 * mechanism.  If not used in this manner, 'b' should be supplied as NULL.
 *
 * Return -1 on error, 0 if it was not found, and 1 if the assignment was
 * completed.
 *
 * This --macro-- forms part of the --ici-api--.
 */
#define ici_assign_super(o,k,v,b) ((*ici_typeof(o)->t_assign_super)(objof(o), objof(k), objof(v), b))

/*
 * Increment the object 'o's reference count.  References from ordinary
 * machine data objects (ie.  variables and stuff, not other objects) are
 * invisible to the garbage collector.  These refs must be accounted for if
 * there is a possibility of garbage collection.  Note that most routines that
 * make objects (new_*(), copy() etc...) return objects with 1 ref.  The
 * caller is expected to ici_decref() it when they attach it into wherever it
 * is going.
 *
 * This --macro-- forms part of the --ici-api--.
 */
#define ici_incref(o)       (++objof(o)->o_nrefs)

/*
 * Decrement the object 'o's reference count.  References from ordinary
 * machine data objects (ie.  variables and stuff, not other objects) are
 * invisible to the garbage collector.  These refs must be accounted for if
 * there is a possibility of garbage collection.  Note that most routines that
 * make objects (new_*(), copy() etc...) return objects with 1 ref.  The
 * caller is expected to ici_decref() it when they attach it into wherever it
 * is going.
 *
 * This --macro-- forms part of the --ici-api--.
 */
#define ici_decref(o)       (--objof(o)->o_nrefs)

/*
 * This is the universal header of all objects.  Each object includes this as
 * its first element.  In the real structures associated with each object type the type
 * specific stuff follows
 *
 * This --struct-- forms part of the --ici-api--.
 */
struct ici_obj
{
    char        o_tcode;
    char        o_flags;
    char        o_nrefs;
    char        o_leafz;
};
/*
 * o_tcode              The small integer type code that characterises
 *                      this object. Standard core types have well known
 *                      codes identified by the TC_* defines. Other
 *                      types are registered at run-time and are given
 *                      the next available code.
 *
 *                      This code can be used to index ici_types[] to discover
 *                      a pointer to the type structure.
 *
 * o_flags              Some boolean flags. Well known flags that apply to
 *                      all objects occupy the lower 4 bits of this byte.
 *                      The upper four bits are available for object specific
 *                      use. See O_* below.
 *
 * o_nrefs              A small integer count of the number of references
 *                      to this object that are *not* otherwise visible
 *                      to the garbage collector.
 *
 * o_leafz              If (and only if) this object does not reference any
 *                      other objects (i.e. its t_mark() function just sets
 *                      the O_MARK flag), and its memory cost fits in this
 *                      signed byte (< 127), then its size can be set here
 *                      to accelerate the marking phase of the garbage
 *                      collector. Else it must be zero.
 *
 * --ici-api-- continued.
 */

/*
 * The generic flags that may appear in the lower 4 bits of o_flags are:
 *
 * O_MARK               The garbage collection mark flag.
 *
 * O_ATOM               Indicates that this object is the read-only
 *                      atomic form of all objects of the same type with
 *                      the same value. Any attempt to change an object
 *                      in a way that would change its value with respect
 *                      to the 't_cmp()' function (see 'ici_type_t') must
 *                      check for this flag and fail the attempt if it is
 *                      set.
 *
 * O_SUPER              This object can support a super.
 *
 * --ici-api-- continued.
 */
#define O_MARK          0x01    /* Garbage collection mark. */
#define O_ATOM          0x02    /* Is a member of the atom pool. */
#define O_TEMP          0x04    /* Is a re-usable temp (flag for asserts). */
#define O_SUPER         0x08    /* Has super (is ici_objwsup_t derived). */


#define objof(x)        ((ici_obj_t *)(x))

/*
 * "Object with super." This is a specialised header for all objects that
 * support a super pointer.  All such objects must have the O_SUPER flag set
 * in o_flags and provide the 't_fetch_super()' and 't_assign_super()'
 * functions in their type structure.  The actual 'o_super' pointer will be
 * NULL if there is no actual super, which is different from O_SUPER being
 * clear (which would mean there could not be a super, ever).
 *
 * This --struct-- forms part of the --ici-api--.
 */
struct ici_objwsup
{
    ici_obj_t       o_head;
    ici_objwsup_t   *o_super;
};
#define objwsupof(o)    ((ici_objwsup_t *)(o))
/*
 * Test if this object supports a super type.  (It may or may not have a super
 * at any particular time).
 *
 * This --macro-- forms part of the --ici-api--.
 */
#define hassuper(o)     (objof(o)->o_flags & O_SUPER)

/*
 * Return a pointer to the 'ici_type_t' struct of the given object.
 *
 * This --macro-- forms part of the --ici-api--.
 */
#define ici_typeof(o)   (ici_types[(int)objof(o)->o_tcode])

/*
 * For static object initialisations...
 */
#define OBJ(tc)    {(tc), 0, 1, 0}

/*
 * Set the basic fields of the object header of 'o'.  'o' can be any struct
 * declared with an object header (this macro casts it).  This macro is
 * prefered to doing it by hand in case there is any future change in the
 * structure.  See comments on each field of 'ici_obj_t'.  This is normally
 * the first thing done after allocating a new bit of memory to hold an ICI
 * object.
 *
 * This --macro-- forms part of the --ici-api--.
 */
#define ICI_OBJ_SET_TFNZ(o, tcode, flags, nrefs, leafz) \
    (objof(o)->o_tcode = (tcode), \
     objof(o)->o_flags = (flags), \
     objof(o)->o_nrefs = (nrefs), \
     objof(o)->o_leafz = (leafz))

/*
 * I was really hoping that most compilers would reduce the above to a
 * single word write. Especially as they are all constants most of the
 * time. Maybe in future we can have some endian specific code to to
 * it manually.
 (*(unsigned long *)(o) = (tcode) | ((flags) << 8) | ((nrefs) << 16) | ((leafz) << 24))
 */

/*
 * Register the object 'o' with the garbage collector.  Object that are
 * registered with the garbage collector can get collected.  This is typically
 * done after allocaton and initialisation of basic fields when making a new
 * object.  Once an object has been registered with the garbage collector, it
 * can *only* be freed by the garbage collector.
 *
 * (Not all objects are registered with the garabage collector. The main
 * exception is staticly defined objects. For example, the 'ici_cfunt_t'
 * objects that are the ICI objects representing functions coded in C
 * are typically staticly defined and never registered. However there
 * are problems with unregistered objects that reference other objects,
 * so this should be used with caution.)
 *
 * This --macro-- forms part of the --ici-api--.
 */
#define ici_rego(o)     ici_rego_work(objof(o))

/*
 * The o_tcode field is a small int. These are the "well known" core
 * language types. See comments on o_tcode above and ici_types above.
 */
#define TC_OTHER        0
#define TC_PC           1
#define TC_SRC          2
#define TC_PARSE        3
#define TC_OP           4
#define TC_STRING       5
#define TC_CATCH        6
#define TC_FORALL       7
#define TC_INT          8
#define TC_FLOAT        9
#define TC_REGEXP       10
#define TC_PTR          11
#define TC_ARRAY        12
#define TC_STRUCT       13
#define TC_SET          14

#define TC_MAX_BINOP    14 /* Max of 15 for binary op args. */

#define TC_EXEC         15
#define TC_FILE         16
#define TC_FUNC         17
#define TC_CFUNC        18
#define TC_METHOD       19
#define TC_MARK         20
#define TC_NULL         21
#define TC_HANDLE       22
#define TC_MEM          23
#define TC_PROFILECALL  24
#define TC_CODE         25

#define TC_MAX_CORE     25

#define TRI(a,b,t)      (((((a) << 4) + b) << 6) + t_subtype(t))

#define isfalse(o)      ((o) == objof(ici_zero) || (o) == objof(&o_null))


/* From alloc.h */


/*
 * Allocate an object of the given type 't'. Return NULL on failure, usual
 * conventions. The resulting object *must* be freed with 'ici_tfree()'.
 * Note that 'ici_tfree()' also requires to know the type of the object
 * being freed.
 *
 * This --macro-- forms part of the --ici-api--.
 */
#define ici_talloc(t)   ici_nalloc(sizeof(t))

/*
 * Free the object 'o' which was allocated by a call to 'ici_talloc()' with
 * the type 't'.  The object *must* have been allocated with 'ici_talloc()'.
 *
 * This --macro-- forms part of the --ici-api--.
 */

#define ici_tfree(p, t) ici_nfree((p), sizeof(t))

extern void             *ici_nalloc(size_t);
extern void             ici_nfree(void *, size_t);
extern void             *ici_alloc(size_t);
extern void             ici_free(void *);


/* From buf.h */

/*
 * Ensure that 'ici_buf' points to enough memory to hold index 'n' (plus
 * room for a nul char at the end). Returns 0 on success, else 1 and
 * sets 'ici_error'.
 *
 * See also: 'The error return convention'.
 *
 * This --macro-- forms part of the --ici-api--.
 */
#define ici_chkbuf(n)       (ici_bufz > (int)(n) ? 0 : ici_growbuf(n))

extern int      ici_growbuf(int);


/* From catch.h */



/* From cfunc.h */


/*
 * The C struct which is the ICI intrinsic function type. That is,
 * a function that is coded in C. (There are actually two types, this
 * one, and a second for functions that are coded in ICI, that are both
 * called 'func'.)
 *
 * This --struct-- forms part of the --ici-api--.
 */
struct ici_cfunc
{
    ici_obj_t   o_head;
    char        *cf_name;
    int         (*cf_cfunc)();
    void        *cf_arg1;
    void        *cf_arg2;
};
/*
 * 'ici_cfunc_t' objects are often declared staticly (in an array) when
 * setting up a group of C functions to be called from ICI. When doing
 * this, the macro 'CF_OBJ' can be used as the initialiser of the
 * 'o_head' field (the standard ICI object heade).
 *
 * The type has a well-known built-in type code of 'TC_CFUNC'.
 *
 * o_head               The standard ICI object header.
 *
 * cf_name              A name for the function. Calls to functions
 *                      such as 'ici_assign_cfuncs' will use this as
 *                      the name to use when assigning it into an ICI
 *                      struct. Apart from that, it is only used in
 *                      error messages.
 *
 * cf_func()            The implementation of the function.  The formals are
 *                      not mentioned here deliberately as implementaions will
 *                      vary in their use of them.
 *
 * cf_arg1, cf_arg2     Optional additional data items.  Sometimes it is
 *                      useful to write a single C function that masquerades
 *                      as severl ICI functions - driven by distinguishing
 *                      data from these two fields. See 'CF_ARG1()'.
 *
 * This comment is also part of the --ici-api--.
 */
#define cfuncof(o)      ((ici_cfunc_t *)(o))
#define iscfunc(o)      (objof(o)->o_tcode == TC_CFUNC)

/*
 * Convienience macro for the object header for use in static
 * initialisations of ici_cfunc_t objects.
 */
#define CF_OBJ          {TC_CFUNC, 0, 1, 0}

/*
 * The operand stack on entry to an intrinsic function:
 *
 * arg(n-1) ... arg(1) arg(0) NARGS FUNC
 *                                        ^-ici_os.a_top
 *
 * NARGS is an ICI int and FUNC is the function object (us).
 */

/*
 * In a call from ICI to a function coded in C, this macro returns the object
 * passed as the 'i'th actual parameter (the first parameter is ARG(0)).  The
 * type of the result is an '(ici_obj_t *)'.  There is no actual or implied
 * incref associated with this.  Parameters are known to be on the ICI operand
 * stack, and so can be assumed to be referenced and not garbage collected.
 *
 * (This macro has no ICI_ prefix for historical reasons.)
 *
 * This --macro-- forms part of the --ici-api--.
 */
#define ARG(n)          (ici_os.a_top[-3 - (n)])

/*
 * In a call from ICI to a function coded in C, this macro returns the
 * count of actual arguments to this C function.
 *
 * (This macro has no ICI_ prefix for historical reasons.)
 *
 * This --macro-- forms part of the --ici-api--.
 */
#define NARGS()         ((int)intof(ici_os.a_top[-2])->i_value)

/*
 * In a call from ICI to a function coded in C, this macro returns
 * a pointer to the first argument to this function, with subsequent
 * arguments being available by *decrementing* the pointer.
 *
 * (This macro has no ICI_ prefix for historical reasons.)
 *
 * This --macro-- forms part of the --ici-api--.
 */
#define ARGS()          (&ici_os.a_top[-3])

/*
 * In a call from ICI to a function coded in C, this macro returns the
 * 'cf_arg1' field of the current C function.  The macro 'CF_ARG2()' can also
 * be used to obtain the 'cf_arg2' field. See the 'ici_cfunc_t' type.
 *
 * They are both (void *) (Prior to ICI 4.0, 'CF_ARG1()' was a function
 * pointer.)
 *
 * This --macro-- forms part of the --ici-api--.
 */
#define CF_ARG1()       (cfuncof(ici_os.a_top[-1])->cf_arg1)
#define CF_ARG2()       (cfuncof(ici_os.a_top[-1])->cf_arg2)


/* From array.h */

/*
 * The ICI array object. Array objects are implemented in a manner to
 * make them efficient to use as stacks. And they are used that way in the
 * execution engine. A single contiguous array of object pointers is
 * associated with the array. a_base points to its start, a_limit one
 * past its end and a_bot and a_top somewhere in the middle.
 *
 * In the general case, arrays are growable circular buffers (queues). Thus
 * allowing efficient addition and removal from both ends. However, their
 * use in the internals of the execution engine is only as stacks. For
 * efficiency, we desire to be able to ensure that there is some amount of
 * contiguous space available on a stack by just making a single efficient
 * check.
 *
 * This is easy if they really are stacks, and the bottom is always anchored
 * to the start of allocation. (See Case 1 below.) This condition will
 * be true if *no* rpop() or rpush() operations have been done on the array.
 * Thus we conceptually consider arrays to have two forms. Ones that are
 * stacks (never had rpop() or rpush() done) and ones that are queues
 * (may, possibly, have had rpop() or rpush() done).
 *
 * Now, if an array is still a stack, you can use the functions (macros):
 *
 *     ici_stk_push_chk(a, n)
 *     ici_stk_pop_chk(a, n)
 *
 * to ensure that there are n spaces or objects available, then just
 * increment/decrement a_top as you push and pop things on the stack.
 * Basically you can assume that object pointers and empty slots are
 * contiguous around a_top.
 *
 * But, if you can not guarantee (by context) that the array is a stack,
 * you can only push, pop, rpush or rpop single object pointers at a time.
 * Basically, the end you are dealing with may be near the wrap point of
 * the circular buffer.
 *
 * Case 1: Pure stack. Only ever been push()ed and pop()ed.
 *   ooooooooooooooooooooo.....................
 *   ^a_base              ^a_top               ^a_limit
 *   ^a_bot
 *
 * Case 2: Queue. rpush() and/or rpop()s have been done.
 *   ..........ooooooooooooooooooo.............
 *   ^a_base   ^a_bot             ^a_top       ^a_limit
 *
 * Case 3: Queue that has wrapped.
 *   oooooooooooooo.................ooooooooooo
 *   ^a_base       ^a_top           ^a_bot     ^a_limit
 *
 * A data structure such as this should really use an integer count
 * to indicate how many used elements there are. By using pure pointers
 * we have to keep one empty slot so we don't find ourselves unable
 * to distinguish full from empty (a_top == a_bot). But by using simple
 * pointers, only a_top needs to change as we push and pop elements.
 * If a_top == a_bot, the array is empty.
 *
 * Note that one must never take the atomic form of a stack, and
 * assume the result is still a stack.
 */
struct ici_array
{
    ici_obj_t   o_head;
    ici_obj_t   **a_top;    /* The next free slot. */
    ici_obj_t   **a_bot;    /* The first used slot. */
    ici_obj_t   **a_base;   /* The base of allocation. */
    ici_obj_t   **a_limit;  /* Allocation limit, first one you can't use. */
};
#define arrayof(o)  ((ici_array_t *)(o))
#define isarray(o)  ((o)->o_tcode == TC_ARRAY)

/*
 * Check that there is room for 'n' new elements on the end of 'a'.  May
 * reallocate array memory to get more room. Return non-zero on failure,
 * usual conventions.
 *
 * This macro can only be used where the array has never had elements
 * rpush()ed or rpop()ed. See the discussion on
 * 'Accessing ICI array object from C' before using.
 *
 * This --macro-- forms part of the --ici-ap--.
 */
#define ici_stk_push_chk(a, n) \
                ((a)->a_limit - (a)->a_top < (n) ? ici_grow_stack((a), (n)) : 0)

/*
 * Ensure that the stack a has i as a valid index.  Will grow and NULL fill
 * as necessary. Return non-zero on failure, usual conventions.
 */
#define ici_stk_probe(a, i) ((a)->a_top - (a)->a_bot <= (i) \
                ? ici_fault_stack((a), (i)) \
                : 0)

/*
 * A macro to assist in doing for loops over the elements of an array.
 * Use as:
 *
 *  ici_array_t  *a;
 *  ici_obj_t    **e;
 *  for (e = ici_astart(a); e != ici_alimit(a); e = ici_anext(a, e))
 *      ...
 *
 * This --macro-- forms part of the --ici-api--.
 */
#define ici_astart(a)   ((a)->a_bot == (a)->a_limit && (a)->a_bot != (a)->a_top \
                            ? (a)->a_base : (a)->a_bot)

/*
 * A macro to assist in doing for loops over the elements of an array.
 * Use as:
 *
 *  ici_array_t  *a;
 *  ici_obj_t    **e;
 *  for (e = ici_astart(a); e != ici_alimit(a); e = ici_anext(a, e))
 *      ...
 *
 * This --macro-- forms part of the --ici-api--.
 */
#define ici_alimit(a)   ((a)->a_top)

/*
 * A macro to assist in doing for loops over the elements of an array.
 * Use as:
 *
 *  ici_array_t  *a;
 *  ici_obj_t    **e;
 *  for (e = ici_astart(a); e != ici_alimit(a); e = ici_anext(a, e))
 *      ...
 *
 * This --macro-- forms part of the --ici-api--.
 */
#define ici_anext(a, e) ((e) + 1 == (a)->a_limit && (a)->a_limit != (a)->a_top \
                            ? (a)->a_base : (e) + 1)

 
/* From int.h */

/*
 * The C struct that is the ICI int object.
 *
 * This --struct-- forms part of the --ici-api--.
 */
struct ici_int
{
    ici_obj_t   o_head;
    long        i_value;
};
#define intof(o)        ((ici_int_t *)(o))
#define isint(o)        ((o)->o_tcode == TC_INT)

/* From float.h */


/*
 * The C struct that is the ICI float object.
 *
 * This --struct-- forms part of the --ici-api--.
 */
struct ici_float
{
    ici_obj_t   o_head;
    double      f_value;
};
#define floatof(o)      ((ici_float_t *)o)
#define isfloat(o)      ((o)->o_tcode == TC_FLOAT)

/* From exec.h */


/*
 * ICI debug interface.  The interpreter has a global debug interface enable
 * flag, 'ici_debug_enabled', and a global pointer, 'ici_debug', to one of
 * these structs.  If the flag is set, the interpreter calls these functions.
 * See 'ici_debug' and 'ici_debug_enabled'.
 *
 * idbg_error()         Called with the current value of ici_error (redundant,
 *                      for historical reasons) and a source line marker
 *                      object (see 'ici_src_t') on an uncaught error.
 *                      Actually, this is not so useful, because it is
 *                      currently called after the stack has been unwound.  So
 *                      a user would not be able to see their stack traceback
 *                      and local context.  This behaviour may change in
 *                      future.
 *
 * idbg_fncall()        Called with the object being called, the pointer to
 *                      the first actual argument (see 'ARGS()' and the number
 *                      of actual arguments just before control is transfered
 *                      to a callable object (function, method or anything
 *                      else).
 *
 * idbg_fnresult()      Called with the object being returned from any call.
 *
 * idbg_src()           Called each time execution passes into the region of a
 *                      new source line marker.  These typically occur before
 *                      any of the code generated by a particular line of
 *                      source.
 *
 * idbg_watch()         In theory, called when assignments are made.  However
 *                      optimisations in the interpreter have made this
 *                      difficult to support without performance penalties
 *                      even when debugging is not enabled.  So it is
 *                      currently disabled.  The function remains here pending
 *                      discovery of a method of achieving it efficiently.
 *
 * This --struct-- forms part of the --ici-api--.
 */
struct ici_debug
{
    void    (*idbg_error)(char *, ici_src_t *);
    void    (*idbg_fncall)(ici_obj_t *, ici_obj_t **, int);
    void    (*idbg_fnresult)(ici_obj_t *);
    void    (*idbg_src)(ici_src_t *);
    void    (*idbg_watch)(ici_obj_t *, ici_obj_t *, ici_obj_t *);
};


/* From file.h */


/*
 * A set of function pointers for simple file abstraction.  ICI file objects
 * are implemented on top of this simple file abstraction in order to allow
 * several different types of file-like entities.  Each different type of file
 * uses one of these structures with specific functions.  Each function is
 * assumed to be compatible with the stdio function of the same name.  In the
 * case were the file is a stdio stream, these *are* the stdio functions.
 *
 * See also: 'ici_stdio_ftype'.
 *
 * This --struct-- forms part of the --ici-api--.
 */
struct ici_ftype
{
    int         (*ft_getch)();
    int         (*ft_ungetch)();
    int         (*ft_putch)();
    int         (*ft_flush)();
    int         (*ft_close)();
    long        (*ft_seek)();
    int         (*ft_eof)();
    int         (*ft_write)();
};

struct ici_file
{
    ici_obj_t   o_head;
    void        *f_file;
    ici_ftype_t *f_type;
    ici_str_t   *f_name;    /* Reasonable name to call it by. */
    ici_obj_t   *f_ref;
};
/*
 * f_ref                An object for this file object to reference.
 *                      This is used to reference the string when we
 *                      are treating a string as a file, and other cases,
 *                      to keep the object referenced. Basically if f_file
 *                      is an implicit reference to some object. May be NULL.
 */
#define fileof(o)   ((ici_file_t *)(o))
#define isfile(o)   (objof(o)->o_tcode == TC_FILE)

#define F_CLOSED    0x10    /* File is closed. */
#define F_NOCLOSE   0x20    /* Don't close on object free. */

/* From forall.h */

struct ici_forall
{
    ici_obj_t   o_head;
    int         fa_index;
    ici_obj_t   *fa_objs[6];
};
#define fa_aggr         fa_objs[0]
#define fa_code         fa_objs[1]
#define fa_vaggr        fa_objs[2]
#define fa_vkey         fa_objs[3]
#define fa_kaggr        fa_objs[4]
#define fa_kkey         fa_objs[5]

#define forallof(o)     ((ici_forall_t *)o)
#define isforall(o)     ((o)->o_tcode == TC_FORALL)

/* From func.h */

struct ici_func
{
    ici_obj_t       o_head;
    ici_array_t     *f_code;    /* The code of this function, atom. */
    ici_array_t     *f_args;    /* Array of argument names. */
    ici_struct_t    *f_autos;   /* Prototype struct of autos (incl. args). */
    ici_str_t       *f_name;    /* Some name for the function (diagnostics). */
    int             f_nautos;   /* If !=0, a hint for auto struct alloc. */
};

#define funcof(o)       ((ici_func_t *)(o))
#define isfunc(o)       (objof(o)->o_tcode == TC_FUNC)


/* From handle.h */


/*
 * The C struct which is the ICI handle object.  A handle is a generic object
 * that can be used to refer to some C data object.  Handles support an
 * (optional) super pointer.  Handles are named with an ICI string to give
 * type checking, reporting, and diagnostic support.  The handle object
 * provides most of the generic machinery of ICI objects.  An optional
 * pre-free function pointer can be supplied to handle cleanup on final
 * collection of the handle.
 *
 * See also 'ici_handle_new()'.
 *
 * o_head               The object header for objects that (can) support super
 *                      pointers.
 *
 * h_ptr                The pointer to the primitive data object that his
 *                      handle is associated with.
 *
 * h_name               The type name this handle will appear to have from ICI
 *                      script code, and for type checking in interfacing with
 *                      C.
 *
 * h_pre_free           An optional function that will be called just before
 *                      this handle object is freed by the garbage collector.
 *                      NULL if not needed.
 *
 * h_member_map         An optional map (NULL if not needed) as made by
 *                      'ici_make_handle_member_map()' and used internally
 *                      when the 'h_member_intf' function is used.
 *
 * h_member_intf        An optional function (NULL if not needed) to implement
 *                      property access and method invocation on the object.
 *                      'ptr' is the 'h_ptr' field of the handle.  The
 *                      implementation must know which 'id' values apply to
 *                      methods, and which to properties.  When the 'id'
 *                      refers to a method, the usual environment for
 *                      intrinsic function invocations can be assumed (e.g.
 *                      'ici_typecheck()' is available) except the return
 *                      value should be stored through '*retv' without any
 *                      extra reference count.
 *
 *                      When the 'id' refers to a property, if 'setv' is
 *                      non-NULL, this is an assignment of 'setv' to the
 *                      property.  If the assignment is possible and proceeds
 *                      without error, 'setv' should be assigned to '*retv'
 *                      prior to return (else '*retv' should be unmodified).
 *
 *                      When the 'id' refers to a property and 'setv' is NULL,
 *                      this is a fetch, and '*retv' should be set to the
 *                      value, without any extra reference count.
 *
 *                      In all cases, 0 indicates a successful return
 *                      (although if '*retv' has not been updated, it will be
 *                      assumed that the 'id' was not actually a member of
 *                      this object and an error may be raised by the calling
 *                      code).  Non-zero on error, usual conventions.
 *
 * h_general_intf       An optional function (NULL if not needed) to implement
 *                      general fetch and assign processing on the handle,
 *                      even when the keys are not known in advance (as might
 *                      happen, for example, if the object could be indexed by
 *                      integers).  If 'h_member_intf' is non-NULL, and
 *                      satisfied a fetch or assign first, this function is
 *                      not called.
 *
 *                      If 'setv' is non-NULL, this is an assignment.  If the
 *                      assignment is to a key ('k') that is valid and the
 *                      assignment is successful, '*retv' should be updated
 *                      with 'setv'.
 *
 *                      If 'setv' is NULL, this is a fetch, and '*retv' should
 *                      be set to the fetched value.
 *
 *                      In both cases, no extra reference should be given to
 *                      the returned object.
 *
 *                      In both cases, 0 indicates a successful return
 *                      (although if '*retv' has not been updated, it will be
 *                      assumed that the key was not actually a member of
 *                      this object and an error may be raised by the calling
 *                      code).  Non-zero on error, usual conventions.
 *
 * This --struct-- forms part of the --ici-api--.
 */
struct ici_handle
{
    ici_objwsup_t   o_head;
    void            *h_ptr;
    ici_str_t       *h_name;
    void            (*h_pre_free)(ici_handle_t *h);
    ici_obj_t       *h_member_map;
    int             (*h_member_intf)(void *ptr, int id, ici_obj_t *setv, ici_obj_t **retv);
    int             (*h_general_intf)(ici_handle_t *h, ici_obj_t *k, ici_obj_t *setv, ici_obj_t **retv);
};

#define handleof(o)        ((ici_handle_t *)(o))
#define ishandle(o)        (objof(o)->o_tcode == TC_HANDLE)
#define ishandleof(o, n)   (ishandle(o) && handleof(o)->h_name == (n))
/*
 * Flags set in the upper nibble of o_head.o_flags, which is
 * allowed for type specific use.
 */
#define H_CLOSED                0x10
#define H_HAS_PRIV_STRUCT       0x20
/*
 * H_CLOSED             If set, the thing h_ptr points to is no longer
 *                      valid (it has probably been freed). This flag
 *                      exists for the convenience of users, as the
 *                      core handle code doesn't touch this much.
 *                      Use of this latent feature depends on needs.
 *
 * H_HAS_PRIV_STRUCT    This handle has had a private struct allocated
 *                      to hold ICI values that have been assigned to
 *                      it. This does not happen until required, as
 *                      not all handles will ever need one. The super
 *                      is the private struct (and it's super is the
 *                      super the creator originally supplied).
 */

struct ici_name_id
{
    char    *ni_name;
    long    ni_id;
};
#define H_METHOD    0x8000000


/* From mark.h */



/* From mem.h */

struct ici_mem
{
    ici_obj_t           o_head;
    void                *m_base;
    size_t              m_length;       /* In m_accessz units. */
    int                 m_accessz;      /* Read/write size. */
    void                (*m_free)();
};

#define memof(o)        ((ici_mem_t *)o)
#define ismem(o)        (objof(o)->o_tcode == TC_MEM)

/* From method.h */

struct ici_method
{
    ici_obj_t   o_head;
    ici_obj_t   *m_subject;
    ici_obj_t   *m_callable;
};
#define methodof(o)     ((ici_method_t *)(o))
#define ismethod(o)     (objof(o)->o_tcode == TC_METHOD)


/* From null.h */

struct ici_null_t
{
    ici_obj_t   o_head;
};
#define nullof(o)       ((ici_null_t *)o)
#define isnull(o)       ((o) == objof(&o_null))


/* From op.h */

struct ici_op
{
    ici_obj_t   o_head;
    int         (*op_func)();
    int         op_ecode;       /* See OP_* below. */
    int         op_code;
};
#define opof(o) ((ici_op_t *)o)
#define isop(o) ((o)->o_tcode == TC_OP)


/* From parse.h */

#define isparse(o)      (objof(o)->o_tcode == TC_PARSE)
#define parseof(o)      ((ici_parse_t *)(o))

/* From pc.h */

struct ici_pc
{
    ici_obj_t   o_head;
    ici_array_t *pc_code;
    ici_obj_t   **pc_next;
};
#define pcof(o)         ((ici_pc_t *)(o))
#define ispc(o)         ((o)->o_tcode == TC_PC)

/* From profile.h */


typedef struct profilecall profilecall_t;
extern int ici_profile_active;
void ici_profile_call(ici_func_t *f);
void ici_profile_return();
void ici_profile_set_done_callback(void (*)(profilecall_t *));
profilecall_t *ici_profilecall_new(profilecall_t *called_by);


/*
 * Type used to store profiling call graph.
 *
 * One of these objects is created for each function called from within another
 * of these.  If a function is called more than once from the same function
 * then the object is reused.
 */
struct profilecall
{
    ici_obj_t       o_head;
    profilecall_t   *pc_calledby;
    ici_struct_t    *pc_calls;
    long            pc_total;
    long            pc_laststart;
    long            pc_call_count;
};
/*
 * o_head           What makes it a good little polymorphic ICI object.
 * pc_caller        The records for the function that called this function.
 * pc_calls         Records for functions called by this function.
 * pc_total         The total number of milliseconds spent in this call
 *                  so far.
 * pc_laststart     If this is currently being called then this is the
 *                  time (in milliseconds since some fixed, but irrelevent,
 *                  point) it started.  We use this at the time the call
 *                  returns and add the difference to pc_total.
 * pc_call_count    The number of times this function was called.
 */
#define profilecallof(o)    ((profilecall_t *)o)
#define isprofilecall(o)    ((o)->o_tcode == TC_PROFILECALL)

/* From ptr.h */

struct ici_ptr
{
    ici_obj_t   o_head;
    ici_obj_t   *p_aggr;        /* The aggregate which contains the object. */
    ici_obj_t   *p_key;         /* The key which references it. */
};
#define ptrof(o)        ((ici_ptr_t *)o)
#define isptr(o)        ((o)->o_tcode == TC_PTR)

/* From re.h */

#define regexpof(o)     ((ici_regexp_t *)(o))
#define isregexp(o)     ((o)->o_tcode == TC_REGEXP)

extern int
ici_pcre(ici_regexp_t *r,
    const char *subject, int length, int start_offset,
    int options, int *offsets, int offsetcount);



/* From set.h */

struct ici_set
{
    ici_obj_t   o_head;
    int         s_nels;         /* How many slots used. */
    int         s_nslots;       /* How many slots allocated. */
    ici_obj_t   **s_slots;
};
#define setof(o)        ((ici_set_t *)(o))
#define isset(o)        ((o)->o_tcode == TC_SET)

/* From src.h */


/*
 * The C struct which is the ICI src object. These are never seen by
 * ICI script code. They are source line markers that are passed to
 * debugger functions to indicate source location.
 *
 * This --struct-- forms part of the --ici-api--.
 */
struct ici_src
{
    ici_obj_t   s_head;
    int         s_lineno;
    ici_str_t   *s_filename;
};
/*
 * s_filename           The name of the source file this source
 *                      marker is associated with.
 *
 * s_lineno             The linenumber.
 *
 * --ici-api-- continued.
 */
#define srcof(o)        ((ici_src_t *)o)
#define issrc(o)        ((o)->o_tcode == TC_SRC)

/* From str.h */


/*
 * This define enables keeping of the string hash in the string structure to
 * save re-computation. It is potentially recomputed when the atom pool is
 * rebuilt or atoms are being removed from it. Changes to hashing and atom
 * pool usage have reduced the number of times hashes are recomputer and it
 * now seems that *not* keeping the hash value makes things faster overall
 * than keeping it (which must be due to memory bandwidth and cache misses).
 * So it is now ifdefed out. However it's a close thing and it could go back
 * in one day.
 */
#define KEEP_STRING_HASH 0

struct ici_str
{
    ici_obj_t       o_head;
    ici_struct_t    *s_struct;      /* Where we were last found on the vs. */
    ici_sslot_t     *s_slot;        /* And our slot. */
    long            s_vsver;        /* The vs version at that time. */
#   if KEEP_STRING_HASH
        unsigned long s_hash;  /* String hash code or 0 if not yet computed */
#   endif
    int             s_nchars;
    char            *s_chars;
    union
    {
        int         su_nalloc;
        char        su_inline_chars[1]; /* And following bytes. */
    }
                    s_u;
};
/*
 * s_nchars             The actual number of characters in the string. Note
 *                      that room is always allocated for a guard '\0' beyond
 *                      this amount.
 *
 * s_chars              This points to the characters of the string, which
 *                      *may* be in a seperate allocation, or may be following
 *                      directly on in the same allocation. The flag
 *                      S_SEP_ALLOC reveals which (see below).
 *
 * s_u.su_nalloc        The number of bytes allocaed at s_chars iff it
 *                      is a seperate allocation (ICI_S_SEP_ALLOC set in
 *                      o_head.o_flags).
 *
 * su.su_inline_chars   If ICI_S_SEP_ALLOC is *not* set, this is where s_chars will
 *                      be pointing. The actual string chars follow on from this.
 */
#define stringof(o)     ((ici_str_t *)o)
#define isstring(o)     ((o)->o_tcode == TC_STRING)

/*
 * This flag (in o_head.o_flags) indicates that the lookup-lookaside mechanism
 * is referencing an atomic struct.  It is stored in the allowed area of
 * o_flags in o_head.
 */
#define S_LOOKASIDE_IS_ATOM 0x10

/*
 * This flag (in o_head.o_flags) indicates that s_chars points to seperately
 * allocated memory.  If this is the case, s_u.su_nalloc is significant and
 * the memory was allocated with ici_nalloc(s_u.su_nalloc).
 */
#define ICI_S_SEP_ALLOC     0x20

/*
 * Macros to assist external modules in getting ICI strings. To use, make
 * an include file called "icistr.h" with your strings, and what you want to
 * call them by, formatted like this:
 *
 *  ICI_STR(fred, "fred")
 *  ICI_STR(amp, "&")
 *
 * etc. Include that file in any files that access ICI strings.
 * Access them with either ICIS(fred) or ICISO(fred) which return
 * ici_str_t* and ici_obj_t* pointers respectively. For example:
 *
 *  o = ici_fetch(s, ICIS(fred));
 *
 * Next, in one of your source file, include the special include file
 * "icistr-setup.h". This will (a) declare pointers to the string objects,
 * and (b) define a function (init_ici_str()) that initialises those pointers.
 *
 * Finally, call init_ici_str() at startup. It returns 1 on error, usual
 * conventions.
 */
#ifdef ICI_MODULE_NAME
#define ICIS_SYM_EXP(module, name) ici_##module##_str_##name
#define ICIS_SYM(module, name)  ICIS_SYM_EXP(module, name)
#define ICIS(name)              (ICIS_SYM(ICI_MODULE_NAME, name))
#define ICI_STR_NORM(name, str) extern ici_str_t *ICIS_SYM(ICI_MODULE_NAME, name);
#define ICI_STR_DECL(name, str) ici_str_t *ICIS_SYM(ICI_MODULE_NAME, name);
#else
#define ICIS(name)              (ici_str_##name)
#define ICI_STR_NORM(name, str) extern ici_str_t *ici_str_##name;
#define ICI_STR_DECL(name, str) ici_str_t *ici_str_##name;
#endif
#define ICISO(name)             (objof(ICIS(name)))
#define ICI_STR_MAKE(name, str) (ICIS(name) = ici_str_new_nul_term(str)) == NULL ||
#define ICI_STR_REL(name, str)  ici_decref(ICIS(name));
#define ICI_STR                 ICI_STR_NORM

/* From struct.h */


struct ici_sslot
{
    ici_obj_t   *sl_key;
    ici_obj_t   *sl_value;
};

struct ici_struct
{
    ici_objwsup_t   o_head;
    int         s_nels;         /* How many slots used. */
    int         s_nslots;       /* How many slots allocated. */
    ici_sslot_t *s_slots;
};
#define structof(o)     ((ici_struct_t *)(o))
#define isstruct(o)     (objof(o)->o_tcode == TC_STRUCT)


/* From trace.h */


extern int trace_yes;
extern int trace_flags;

#define TRACE_LEXER         1
#define TRACE_EXPR          2
#define TRACE_INTRINSICS    4
#define TRACE_FUNCS         8
#define TRACE_MEM           16
#define TRACE_SRC           32
#define TRACE_GC            64

#define TRACE_ALL       (TRACE_EXPR|TRACE_FUNCS|TRACE_GC)

/* From wrap.h */


struct ici_wrap
{
    ici_wrap_t  *w_next;
    void        (*w_func)(void);
};


#ifdef __cplusplus
}
#endif

#endif /* ICI_ICI_H */
