
/* libq.h: C interface library */

/* ALWAYS include gmp.h prior to this header when your module links to a
   static version of the GMP library. This is required to properly initialize
   GMP memory management in your module. */

/*  Q eQuational Programming System
    Copyright (c) 1991-2006 by Albert Graef
    <ag@muwiinfa.geschichte.uni-mainz.de, Dr.Graef@t-online.de>

    This program 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 1, or (at your option)
    any later version.

    This program 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 this program; if not, write to the Free Software
    Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.

*/

#ifndef LIBQ_H
#define LIBQ_H 1

#include <stdlib.h>
#include <stdio.h>

/* __declspec magic required to make Windows DLLs work */
#ifndef __DLL_BUILD
#if defined _WIN32
#define __DLLIMPORT __declspec(dllimport)
#define __DLLEXPORT __declspec(dllexport)
#else
#define __DLLIMPORT extern
#define __DLLEXPORT
#endif
#endif

/* WIN32 KLUDGE ALERT: Mingw and MSVC malloc() are not compatible. This may
   lead to memory corruption when using mingw- and msvc-compiled modules
   together. We redefine the memory management routines here to use routines
   provided by libq instead. */

#ifndef __DLL_BUILD
#if defined _WIN32
#include <malloc.h>
#define malloc libq_malloc
#define realloc libq_realloc
#define calloc libq_calloc
#define free libq_free
#endif
#endif

/* support C++ */
#ifdef __cplusplus
extern "C" {
#endif

__DLLIMPORT void *libq_malloc(size_t size);
__DLLIMPORT void *libq_realloc(void *p, size_t size);
__DLLIMPORT void *libq_calloc(size_t num, size_t size);
__DLLIMPORT void libq_free(void *p);

/* Q expression type (opaque). */

typedef void *expr;

/* Declaration macros. */

/* The module header. */

#ifndef __GNU_MP_VERSION

#define MODULE(id) static int __modno = -1; \
__DLLEXPORT void __ ## id ## __initmod(int modno) { __modno = modno; }

#else

#define MODULE(id) static int __modno = -1; \
__DLLEXPORT void __ ## id ## __initmod(int modno, \
void *(*gmp_allocate) (), void *(*gmp_reallocate) (), void (*gmp_free) ()) \
{ \
  __modno = modno; \
  mp_set_memory_functions((void *(*) (size_t))gmp_allocate, \
			  (void *(*) (void *, size_t, size_t))gmp_reallocate, \
			  (void (*) (void *, size_t))gmp_free); \
}

#endif

/* User-defined module initialization/finalization code. */

#define INIT(id) \
__DLLEXPORT void __ ## id ## __init(void)

#define FINI(id) \
__DLLEXPORT void __ ## id ## __fini(void)

/* Declare functions and destructors. */

#define FUNCTION(id,name,argc,argv) __DLLEXPORT expr __F__ ## id ## _ ## name (int argc, expr *argv)

#define DESTRUCTOR(id,name,ptr) __DLLEXPORT void __D__ ## id ## _ ## name (void *ptr)

/* Call an external function in C. */

#define FUNCALL(id,name,argc,argv) __F__ ## id ## _ ## name(argc,argv)

/* Special expr return values in case of error conditions. */

#define __FAIL NULL		/* failure to apply dl function (no error) */
#define __ERROR __mkerror()	/* error in dl function (aborts evaluation) */

/* Macros to access the symbol table. sym(name) returns the integer code for
   symbol name (without double quotes). type(name) does the same for type
   symbols. You can use these macros to pass symbol values to mksym() and
   mkobj(). */

#define sym(name) __getsym( #name , __modno)
#define type(name) __gettype( #name , __modno)

/* Predefined function and type symbols. */

#if defined(_WIN32) && defined(__cplusplus)
/* Apparently __DLLIMPORT+const gives troubles with Mingw, so we just drop the
   const here. Thanks to Brian Wilfley <drclearly@gmail.com>. */
__DLLIMPORT int truesym, falsesym, nilsym, voidsym;
__DLLIMPORT int inttype, floattype, booltype, strtype, filetype,
  listtype, tupletype;
#else
__DLLIMPORT const int truesym, falsesym, nilsym, voidsym;
__DLLIMPORT const int inttype, floattype, booltype, strtype, filetype,
  listtype, tupletype;
#endif

/* Expression construction. */

/* Expressions are allocated dynamically on the interpreter's expression
   heap. If this fails, NULL is returned, and any dynamic argument values are
   collected using dispose(). You can use the constructed expressions as
   return values in interface functions, or as temporary values during the
   execution of an interface function. In the former case the return value
   will be evaluated by the interpreter automatically. In the latter case the
   values should be destroyed using dispose() (see below) before the interface
   function finishes. Note that it is generally *not* save to assume that a
   temporary expression persists after an external function invokation, since
   the garbage collector will collect all unreachable expression nodes,
   including temporaries created by hosted modules. */

/* Atomic objects: integers, real values, strings, files, and pipes created
   with popen(3). */

/* Note that mkint() and mkuint() can only be used to construct machine
   integers. For constructing arbitrary-sized integers from GMP mpz_t values,
   the mkmpz() routine is used. It takes a mpz_t value (cast to void*) as its
   argument. To avoid the overhead of taking an extra copy, this value is
   taken over by the interpreter, hence the calling program must _not_
   manipulate the passed value afterwards. The mkmpz_float() function is a
   convenience function which creates an integer expression from a floating
   point number. */

__DLLIMPORT expr mkint(long i);
__DLLIMPORT expr mkuint(unsigned long i);
__DLLIMPORT expr mkmpz(void *z /* mpz_t z */);
__DLLIMPORT expr mkmpz_float(double f);
__DLLIMPORT expr mkfloat(double f);
__DLLIMPORT expr mkstr(char *s);
__DLLIMPORT expr mkfile(FILE *fp);
__DLLIMPORT expr mkpipe(FILE *fp);

/* Function (and variable) symbols and external objects. The mkbool() routine
   is a convenience function which creates a Bool value from an int (0 for
   false, nonzero for true). */

__DLLIMPORT expr mksym(int sym);
__DLLIMPORT expr mkbool(int flag);
__DLLIMPORT expr mkobj(int type, void *ptr);

/* Applications, list and tuple construction ([|] resp. (|)). */

__DLLIMPORT expr mkapp(expr fun, expr arg);
__DLLIMPORT expr mkcons(expr hd, expr tl);
__DLLIMPORT expr mkcont(expr hd, expr tl);

/* Create a list or tuple from a given element list. The mklistvl()/mktuplel()
   functions take the elements directly as parameters, while the mklistv()/
   mktuplev() functions take an expression vector. The interpreter takes
   control of these vectors and will deallocate them when it is appropriate. */

__DLLIMPORT expr mklistl(int nelems, ...);
__DLLIMPORT expr mklistv(int nelems, expr *elems);

__DLLIMPORT expr mktuplel(int nelems, ...);
__DLLIMPORT expr mktuplev(int nelems, expr *elems);

/* Common types of constants defined as parameterless macros. */

#define mktrue mksym(truesym)
#define mkfalse mksym(falsesym)
#define mknil mksym(nilsym)
#define mkvoid mksym(voidsym)

/* Type checking and unboxing. exprsym() and exprtype() return the function
   and type symbol of an expression. The remaining functions are used to check
   for a specific kind of object, and return the corresponding component
   values. */

__DLLIMPORT int exprsym(const expr x);
__DLLIMPORT int exprtype(const expr x);

/* In analogy to the construction functions, there are several functions to
   check for integer objects. The isint() and isuint() functions will only
   return an integer value which fits into a machine integer. Arbitrary-sized
   integers can be obtained using the ismpz() function which returns an mpz_t
   value. This function overwrites the value of the given mpz_t variable,
   hence the variable must not be initialized. Also note that the value
   returned by ismpz() is owned by the interpreter and hence must not be
   modified by the calling program in any way. If necessary, you can make your
   own copy of the value using the GMP mpz_set() function. Finally, the
   ismpz_float() function returns an integer value as a floating point number,
   and isbool() returns a Bool value as an integer (0 for false, 1 for
   true). */

__DLLIMPORT int isint(const expr x, long *i);
__DLLIMPORT int isuint(const expr x, unsigned long *i);
__DLLIMPORT int ismpz(const expr x, void *z /*mpz_t z*/);
__DLLIMPORT int ismpz_float(const expr x, double *f);
__DLLIMPORT int isfloat(const expr x, double *f);
__DLLIMPORT int isstr(const expr x, char **s);
__DLLIMPORT int isfile(const expr x, FILE **fp);

__DLLIMPORT int issym(const expr x, int sym);
__DLLIMPORT int isbool(const expr x, int *flag);
__DLLIMPORT int isobj(const expr x, int type, void **ptr);

__DLLIMPORT int isapp(const expr x, expr *fun, expr *arg);
__DLLIMPORT int iscons(const expr x, expr *hd, expr *tl);
__DLLIMPORT int iscont(const expr x, expr *hd, expr *tl);

/* The elems vector returned by istuple() is owned by the interpreter, and
   must *not* be manipulated in any way. This is again to save the overhead of
   creating an extra copy. */

__DLLIMPORT int istuple(const expr x, int *nelems, expr **elems);

/* Check for the predefined constants. */

#define istrue(x) issym(x, truesym)
#define isfalse(x) issym(x, falsesym)
#define isnil(x) issym(x, nilsym)
#define isvoid(x) issym(x, voidsym)

/* Unicode support. Note that as of Q 7.0, the interpreter internally
   represents all string data using the UTF-8 encoding. This is the encoding
   to be used when a string is constructed using mkstr, and the encoding of
   the string data returned by isstr. It is the responsibility of the
   application to convert its string data from/to this encoding as
   necessary. To help with this, the following convenience functions are
   provided. They allow you to specify the desired source/target encoding in
   the second parameter (if NULL then the default system encoding is
   used). The converted string is allocated dynamically and must be freed by
   the caller. If no suitable conversion is found then a copy of the input
   string is returned as is. */

__DLLIMPORT char *from_utf8(const char *s, const char *encoding);
__DLLIMPORT char *to_utf8(const char *s, const char *encoding);

/* The following routines work like the functions above, but use a file object
   to keep track of the conversion state across different invokations. Input
   and output conversions are handled using separate conversion handles. The
   file encoding used by these functions can be changed with the file_encoding
   routine (default is the system encoding), which returns nonzero iff the
   request to change the encoding was successful. */

__DLLIMPORT char *file_from_utf8(const char *s, expr file);
__DLLIMPORT char *file_to_utf8(const char *s, expr file);
__DLLIMPORT int file_encoding(expr file, const char *encoding);

/* Expression evaluation. eval() recursively invokes the interpreter on the
   given expression and returns the computed result (NULL if an error occurs
   or the evaluation is aborted). In any case, the argument (if it is
   different from the result) is collected using dispose(). Thus, after
   applying eval() to a temporary value, never use dispose() on the argument,
   but only on the evaluated result. */

__DLLIMPORT expr eval(const expr x);

/* Garbage collection of temporary toplevel values (created with the
   construction functions or with eval()). You can safely apply this to any
   expression, since only top level temporaries will actually be
   destroyed. Component values of a toplevel expression will be collected
   automatically when they are not referred to any more. */

__DLLIMPORT void dispose(expr x);

/* Multithreading support. As of Q 3.0, you can safely invoke eval() and the
   other libq operations in child threads created by a hosted module. For this
   to work, some amount of synchronization with the interpreter is required.

   First, you must invoke init_thread() from the new thread before invoking
   other libq operations. This function initializes some thread-local
   resources in the interpreter and obtains an internal mutex which allows the
   other libq operations to run in a safe context. It also returns the id
   under which the thread is known by the interpreter. (If the result is -1
   then the operation was unsuccessful, possibly because the interpreter does
   not support multithreading. In this case it is *not* safe to call the other
   operations afterwards.)

   Second, you call eval() and/or whatever other calculations with the
   interpreter are required.

   Third, once you're finished with the interpreter, you must invoke the
   exit_thread() function with the id returned by init_thread() to release the
   internal mutex obtained with the init_thread() function, and to update some
   other internal bookkeeping information. After calling exit_thread(), it is
   *not* safe to call other libq operations any more. But you can still access
   the results of your calculations as the thread-local resources are still
   available.

   Fourth, you call fini_thread() with the id returned by init_thread() to
   release the thread-local resources associated with the new thread. This can
   be done from the new thread, but also from any other thread. Once this
   operation is completed, any trace of the new thread will have vanished from
   the interpreter.

   Note that the new thread, while performing an evaluation, is subject to
   preemption, since the interpreter will switch over to other waiting tasks
   in its internal execution loop. Moreoever, the thread may also be canceled
   during evaluation when the interpreter is about to exit or run a new
   script. Therefore you should provide an appropriate cleanup handler. At the
   very least, your cleanup handler must call exit_thread() in case the thread
   is canceled after init_thread() but before the thread reaches the
   exit_thread() call. Finally, if your module uses any internal
   synchronization objects, these should be handled at fork time with
   appropriate "atfork" handlers, see the POSIX threads manual for
   details. (Never call pthread_atfork yourself to install the handlers, but
   use the provided atfork() interface routine. This is necessary to prevent
   dangling function pointers if the module is unloaded by the interpreter.)

   Another issue arises with external operations which may block for extended
   or even indefinite periods of time. By default, the interpreter will only
   allow one external function to be entered at any time, hence such an
   operation may block other threads and the interpreter itself. To prevent
   this, the release_lock() and acquire_lock() operation can be used to
   temporarily release the lock on the interpreter's mutex, which lets the
   interpreter continue with another thread while your thread performs a
   lengthy or potentially blocking operation. These operations also establish
   cancellation points. Note that after releasing the mutex your thread
   continues to run in parallel with the interpreter, so the executed code
   must be thread-safe. In particular, you must not invoke any other libq
   operations after releasing the mutex, and you must do your own mutex
   locking on private globals if necessary. Before returning from the
   operation, you must *always* reacquire the mutex. The have_lock() function
   can be used to test whether the current thread currently holds the mutex.

   Synchronization with the interpreter is also required if you want to do
   terminal input, to prevent strange interactions with the readline
   library. A "tty" mutex is provided for that purpose. When you're about to
   read something from stdin you should acquire this mutex and release it when
   finished.

   When passing an (argument or temporary) expression to a newly created
   thread, you must ensure that the expression is not garbage collected by the
   main thread or some other thread while your thread is still executing. For
   this purpose, you can count a new reference to the expression using the
   newref() function, which will cause garbage collection to be deferred until
   you call freeref(). Note that each call to newref() counts a new reference,
   so the same number of freeref() invokations is required to actually free
   the expression. There also is an unref() operation which merely decreases
   the reference count without actually deallocating the expression. Note that
   you must make sure that the expression is actually freed *before* the
   thread terminates.

   As of Q 7.1, it is also possible to register so-called "sentinel"
   expressions which are to be evaluated as soon as it is safe to do so. This
   is to be used in contexts where it may _not_ be safe to enter a recursive
   evaluation immediately (destructors, in particular) or if a primitive wants
   to have additional cleanup operations performed after delivering the return
   value. The registered sentinel expressions will be evaluated by the
   interpreter in order, in the thread that called the destructor or
   primitive, as soon as it is possible to do so in a safe context. Note that
   the resulting values are just thrown away, so this is only useful if some
   operation involving side-effects is to be executed as a sentinel. */

/* thread initialization and termination */
__DLLIMPORT int init_thread(void);
__DLLIMPORT void exit_thread(int id);
__DLLIMPORT void fini_thread(int id);

/* get the id of the current thread */
__DLLIMPORT int this_thread(void);

/* enable background processing */
__DLLIMPORT int have_lock(void);
__DLLIMPORT void release_lock(void);
__DLLIMPORT void acquire_lock(void);

/* stdin access */
__DLLIMPORT void acquire_tty(void);
__DLLIMPORT void release_tty(void);

/* defer garbage collection */
__DLLIMPORT expr newref(expr x);
__DLLIMPORT expr unref(expr x);
__DLLIMPORT void freeref(expr x);

/* register a sentinel */
__DLLIMPORT void sentinel(expr x);

/* register pthread_atfork handlers (only one set of handlers can be
   installed for each module) */
#define thread_atfork(prepare,parent,child) \
__thread_atfork(prepare,parent,child,__modno)

/* Internals (*never* access these directly!). */

__DLLIMPORT int __libq_init();
__DLLIMPORT expr __mkerror(void);
__DLLIMPORT int __getsym(const char *name, int modno);
__DLLIMPORT int __gettype(const char *name, int modno);
__DLLIMPORT const char *__getpname(int sym, int modno);
__DLLIMPORT void __thread_atfork(void (*prepare)(void), void (*parent)(void),
				 void (*child)(void), int modno);

#ifdef __cplusplus
}
#endif

#endif
