/* libq.c: C interface library */

/*  Q eQuational Programming System
    Copyright (c) 1991-2001 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.

*/

#include <stdarg.h>

/* We do _not_ include gmp.h here to make this module self-contained. This
   allows to interface to C modules compiled with non-gnu compilers, for which
   the gmp library may not be available. However, expr.h needs the definition
   of mpz_t, hence we provide a dummy declaration here. */

typedef struct { int a, s; void* d; } mpz_t[1];

#if defined (HAVE_CONFIG_H)
#  include "config.h"
#else
#  ifdef __MINGW32__
#    include "mingw.h"
#  endif
#endif

#include "opcodes.h"
#include "expr.h"

#define __DLL_BUILD 1
#if defined _WIN32
#define __DLLIMPORT __declspec(dllexport)
#define __DLLEXPORT __declspec(dllimport)
#else
#define __DLLIMPORT
#define __DLLEXPORT extern
#endif

#include "libq.h"

#ifdef DMALLOC
#include <dmalloc.h>
#endif

/* Memory allocation routines provided for Mingw/MSVC compatibility under
   Windows (see libq.h). */

__DLLIMPORT void *libq_malloc(size_t size)
{
  return malloc(size);
}

__DLLIMPORT void *libq_realloc(void *p, size_t size)
{
  return realloc(p, size);
}

__DLLIMPORT void *libq_calloc(size_t num, size_t size)
{
  return calloc(num, size);
}

__DLLIMPORT void libq_free(void *p)
{
  free(p);
}

typedef expr (*intp_expr_fun)();
typedef int (*intp_int_fun)();
typedef void (*intp_void_fun)();

static intp_expr_fun __qintp_intexpr;
static intp_expr_fun __qintp_uintexpr;
static intp_expr_fun __qintp_mpzexpr;
static intp_expr_fun __qintp_mpz_floatexpr;
static intp_expr_fun __qintp_floatexpr;
static intp_expr_fun __qintp_strexpr;
static intp_expr_fun __qintp_fileexpr;
static intp_expr_fun __qintp_pipeexpr;
static intp_expr_fun __qintp_funexpr;
static intp_expr_fun __qintp_usrexpr;
static intp_expr_fun __qintp_consexpr;
static intp_expr_fun __qintp_tupleexpr;

static intp_int_fun __qintp_getint;
static intp_int_fun __qintp_getuint;
static intp_int_fun __qintp_getmpz;
static intp_int_fun __qintp_getmpz_float;

static intp_expr_fun __qintp_eval;

static intp_void_fun __qintp_free;

static intp_int_fun __qintp_issym;
static intp_int_fun __qintp_istype;
static intp_int_fun __qintp_isusrtype;
static intp_int_fun __qintp_sym_lookup;
static intp_int_fun __qintp_type_lookup;

static intp_int_fun __qintp_init_thread;
static intp_void_fun __qintp_exit_thread;
static intp_void_fun __qintp_fini_thread;
static intp_int_fun __qintp_this_thread;
static intp_int_fun __qintp_have_lock;
static intp_void_fun __qintp_release_lock;
static intp_void_fun __qintp_acquire_lock;
static intp_void_fun __qintp_acquire_tty;
static intp_void_fun __qintp_release_tty;
static intp_void_fun __qintp_thread_atfork;

static intp_void_fun __qintp_error;

/* Predefined function and type symbols. */

__DLLIMPORT const int truesym = TRUEOP, falsesym = FALSEOP, nilsym = NILOP,
  voidsym = VOIDOP;
__DLLIMPORT const int inttype = INTTYPE, floattype = FLOATTYPE,
  booltype =BOOLTYPE, strtype = STRTYPE, filetype = FILETYPE,
  listtype = LISTTYPE, tupletype = TUPLETYPE;

/* Expression construction. */

__DLLIMPORT expr mkint(long i)
{
  return __qintp_intexpr(i);
}

__DLLIMPORT expr mkuint(unsigned long i)
{
  return __qintp_uintexpr(i);
}

__DLLIMPORT expr mkmpz(void *z)
{
  return __qintp_mpzexpr(z);
}

__DLLIMPORT expr mkmpz_float(double f)
{
  return __qintp_mpz_floatexpr(f);
}

__DLLIMPORT expr mkfloat(double f)
{
  return __qintp_floatexpr(f);
}

__DLLIMPORT expr mkstr(char *s)
{
  return s?__qintp_strexpr(s):NULL;
}

__DLLIMPORT expr mkfile(FILE *fp)
{
  return fp?__qintp_fileexpr(fp):NULL;
}

__DLLIMPORT expr mkpipe(FILE *fp)
{
  return fp?__qintp_pipeexpr(fp):NULL;
}


__DLLIMPORT expr mksym(int sym)
{
  if (__qintp_issym(sym))
    return __qintp_funexpr(sym);
  else
    return NULL;
}

__DLLIMPORT expr mkbool(int flag)
{
  if (flag)
    return mktrue;
  else
    return mkfalse;
}

__DLLIMPORT expr mkobj(int type, void *ptr)
{
  if (__qintp_isusrtype(type))
    return __qintp_usrexpr(type, ptr);
  else
    return NULL;
}


__DLLIMPORT expr mkapp(expr fun, expr arg)
{
  expr x = (fun&&arg)?__qintp_consexpr(APPOP, fun, arg):NULL;
  if (!x) { dispose(fun); dispose(arg); }
  return x;
}

__DLLIMPORT expr mkcons(expr hd, expr tl)
{
  expr x = (hd&&tl)?__qintp_consexpr(CONSOP, hd, tl):NULL;
  if (!x) { dispose(hd); dispose(tl); }
  return x;
}

__DLLIMPORT expr mkcont(expr hd, expr tl)
{
  expr x = (hd&&tl)?__qintp_consexpr(PAIROP, hd, tl):NULL;
  if (!x) { dispose(hd); dispose(tl); }
  return x;
}


__DLLIMPORT expr mklistl(int nelems, ...)
{
  if (nelems <= 0)
    return mknil;
  else {
    expr *elems = malloc(nelems*sizeof(expr));
    if (elems) {
      int i;
      va_list ap;
      va_start(ap, nelems);
      for (i = 0; i < nelems; i++)
	elems[i] = va_arg(ap, expr);
      va_end(ap);
      return mklistv(nelems, elems);
    } else
      return NULL;
  }
}

__DLLIMPORT expr mklistv(int nelems, expr *elems)
{
  if (nelems < 0) nelems = 0;
  if (nelems > 0 && !elems)
    return NULL;
  else {
    int i;
    expr x = mknil;
    for (i = nelems-1; x && i >= 0; i--) {
      expr y = mkcons(elems[i], x);
      x = y;
      if (!x) {
	int j;
	for (j = 0; j < i; j++) dispose(elems[j]);
	break;
      }
    }
    if (elems) free(elems);
    return x;
  }
}


__DLLIMPORT expr mktuplel(int nelems, ...)
{
  if (nelems <= 0)
    return mkvoid;
  else {
    expr *elems = malloc(nelems*sizeof(expr));
    if (elems) {
      int i;
      va_list ap;
      va_start(ap, nelems);
      for (i = 0; i < nelems; i++)
	elems[i] = va_arg(ap, expr);
      va_end(ap);
      return mktuplev(nelems, elems);
    } else
      return NULL;
  }
}

__DLLIMPORT expr mktuplev(int nelems, expr *elems)
{
  if (nelems < 0) nelems = 0;
  if (nelems > 0 && !elems)
    return NULL;
  else if (nelems > 0) {
    int i, chk = 1;
    for (i = 0; i < nelems; i++)
      if (!elems[i]) {
	chk = 0;
	break;
      }
    if (!chk) {
      for (i = 0; i < nelems; i++)
	dispose(elems[i]);
      free(elems);
      return NULL;
    } else {
      for (i = 0; i < nelems; i++)
	((EXPR*)elems[i])->refc++;
      return __qintp_tupleexpr(nelems, elems);
    }
  } else
    return mkvoid;
}


/* Type checking and unboxing. */

__DLLIMPORT int exprsym(const expr x)
{
  if (((EXPR*)x)->fno >= BINARY)
    return ((EXPR*)x)->fno;
  else
    return 0;
}

__DLLIMPORT int exprtype(const expr x)
{
  return !((EXPR*)x)->argc?((EXPR*)x)->type:0;
}


__DLLIMPORT int isint(const expr x, long *i)
{
  return __qintp_getint(x, i);
}

__DLLIMPORT int isuint(const expr x, unsigned long *i)
{
  return __qintp_getuint(x, i);
}

__DLLIMPORT int ismpz(const expr x, void *z)
{
  return __qintp_getmpz(x, z);
}

__DLLIMPORT int ismpz_float(const expr x, double *f)
{
  return __qintp_getmpz_float(x, f);
}

__DLLIMPORT int isfloat(const expr x, double *f)
{
  if (((EXPR*)x)->fno == FLOATVALOP) {
    *f = ((EXPR*)x)->data.f;
    return 1;
  } else
    return 0;
}

__DLLIMPORT int isstr(const expr x, char **s)
{
  if (((EXPR*)x)->fno == STRVALOP) {
    *s = ((EXPR*)x)->data.s;
    return 1;
  } else
    return 0;
}

__DLLIMPORT int isfile(const expr x, FILE **fp)
{
  if (((EXPR*)x)->fno == FILEVALOP) {
    *fp = ((EXPR*)x)->data.fp;
    return 1;
  } else
    return 0;
}


__DLLIMPORT int issym(const expr x, int sym)
{
   return sym >= BINARY && ((EXPR*)x)->fno == sym;
}

__DLLIMPORT int isbool(const expr x, int *flag)
{
  if (istrue(x)) {
    *flag = 1;
    return 1;
  } else if (isfalse(x)) {
    *flag = 0;
    return 1;
  } else
    return 0;
}


__DLLIMPORT int isobj(const expr x, int type, void **ptr)
{
  if (((EXPR*)x)->fno == USRVALOP && ((EXPR*)x)->type == type) {
    *ptr = ((EXPR*)x)->data.vp;
    return 1;
  } else
    return 0;
}

__DLLIMPORT int isapp(const expr x, expr *fun, expr *arg)
{
  if (((EXPR*)x)->fno == APPOP) {
    *fun = ((EXPR*)x)->data.args.x1;
    *arg = ((EXPR*)x)->data.args.x2;
    return 1;
  } else
    return 0;
}

__DLLIMPORT int iscons(const expr x, expr *hd, expr *tl)
{
  if (((EXPR*)x)->fno == CONSOP) {
    *hd = ((EXPR*)x)->data.args.x1;
    *tl = ((EXPR*)x)->data.args.x2;
    return 1;
  } else
    return 0;
}

__DLLIMPORT int iscont(const expr x, expr *hd, expr *tl)
{
  if (((EXPR*)x)->fno == PAIROP) {
    *hd = ((EXPR*)x)->data.args.x1;
    *tl = ((EXPR*)x)->data.args.x2;
    return 1;
  } else
    return 0;
}


__DLLIMPORT int istuple(const expr x, int *nelems, expr **elems)
{
  if (((EXPR*)x)->fno == VECTOP) {
    *nelems = ((EXPR*)x)->data.vect.n;
    *elems = (expr*)((EXPR*)x)->data.vect.xv;
    return 1;
  } else
    return 0;
}


/* Expression evaluation. */

__DLLIMPORT expr eval(const expr x)
{
  if (x) {
    expr ret = __qintp_eval(x);
    if (x != ret) dispose(x);
    return ret;
  } else
    return NULL;
}

/* Garbage collection. */

__DLLIMPORT void dispose(expr x)
{
  if (x && ((EXPR*)x)->refc == 0) {
    ((EXPR*)x)->refc = 1;
    __qintp_free(x);
  }
}

/* Multithreading. */

__DLLIMPORT int init_thread(void)
{
  return __qintp_init_thread();
}

__DLLIMPORT void exit_thread(int id)
{
  if (id >= 0)
    __qintp_exit_thread(id);
}

__DLLIMPORT void fini_thread(int id)
{
  if (id >= 0)
    __qintp_fini_thread(id);
}

__DLLIMPORT int this_thread(void)
{
  return __qintp_this_thread();
}

__DLLIMPORT int have_lock(void)
{
  return __qintp_have_lock();
}

__DLLIMPORT void release_lock(void)
{
  __qintp_release_lock();
}

__DLLIMPORT void acquire_lock(void)
{
  __qintp_acquire_lock();
}

__DLLIMPORT void acquire_tty(void)
{
  __qintp_acquire_tty();
}

__DLLIMPORT void release_tty(void)
{
  __qintp_release_tty();
}

__DLLIMPORT expr newref(expr x)
{
  if (x) ((EXPR*)x)->refc++;
  return x;
}

__DLLIMPORT expr unref(expr x)
{
  if (x) ((EXPR*)x)->refc--;
  return x;
}

__DLLIMPORT void freeref(expr x)
{
  if (x && --(((EXPR*)x)->refc) == 0)
    dispose(x);
}

/* Internals. */

/* initialization (executed by the interpreter); set up interface to the
   interpreter */

__DLLIMPORT int __libq_init
(

 /* expression construction */
 intp_expr_fun qintp_intexpr,
 intp_expr_fun qintp_uintexpr,
 intp_expr_fun qintp_mpzexpr,
 intp_expr_fun qintp_mpz_floatexpr,
 intp_expr_fun qintp_floatexpr,
 intp_expr_fun qintp_strexpr,
 intp_expr_fun qintp_fileexpr,
 intp_expr_fun qintp_pipeexpr,
 intp_expr_fun qintp_funexpr,
 intp_expr_fun qintp_usrexpr,
 intp_expr_fun qintp_consexpr,
 intp_expr_fun qintp_tupleexpr,

 /* expression lookup */
 intp_int_fun qintp_getint,
 intp_int_fun qintp_getuint,
 intp_int_fun qintp_getmpz,
 intp_int_fun qintp_getmpz_float,

 /* expression evaluation */
 intp_expr_fun qintp_eval,

 /* garbage collection */
 intp_void_fun qintp_free,

 /* symbol table lookup */
 intp_int_fun qintp_issym,
 intp_int_fun qintp_istype,
 intp_int_fun qintp_isusrtype,
 intp_int_fun qintp_sym_lookup,
 intp_int_fun qintp_type_lookup,

 /* multithreading */
 intp_int_fun qintp_init_thread,
 intp_void_fun qintp_exit_thread,
 intp_void_fun qintp_fini_thread,
 intp_int_fun qintp_this_thread,
 intp_int_fun qintp_have_lock,
 intp_void_fun qintp_release_lock,
 intp_void_fun qintp_acquire_lock,
 intp_void_fun qintp_acquire_tty,
 intp_void_fun qintp_release_tty,
 intp_void_fun qintp_thread_atfork,

 /* error status */
 intp_void_fun qintp_error
)
{
  __qintp_intexpr = qintp_intexpr;
  __qintp_uintexpr = qintp_uintexpr;
  __qintp_mpzexpr = qintp_mpzexpr;
  __qintp_mpz_floatexpr = qintp_mpz_floatexpr;
  __qintp_floatexpr = qintp_floatexpr;
  __qintp_strexpr = qintp_strexpr;
  __qintp_fileexpr = qintp_fileexpr;
  __qintp_pipeexpr = qintp_pipeexpr;
  __qintp_funexpr = qintp_funexpr;
  __qintp_usrexpr = qintp_usrexpr;
  __qintp_consexpr = qintp_consexpr;
  __qintp_tupleexpr = qintp_tupleexpr;

  __qintp_getint = qintp_getint;
  __qintp_getuint = qintp_getuint;
  __qintp_getmpz = qintp_getmpz;
  __qintp_getmpz_float = qintp_getmpz_float;

  __qintp_eval = qintp_eval;

  __qintp_free = qintp_free;

  __qintp_issym = qintp_issym;
  __qintp_istype = qintp_istype;
  __qintp_isusrtype = qintp_isusrtype;
  __qintp_sym_lookup = qintp_sym_lookup;
  __qintp_type_lookup = qintp_type_lookup;

  __qintp_init_thread = qintp_init_thread;
  __qintp_exit_thread = qintp_exit_thread;
  __qintp_fini_thread = qintp_fini_thread;
  __qintp_this_thread = qintp_this_thread;
  __qintp_have_lock = qintp_have_lock;
  __qintp_release_lock = qintp_release_lock;
  __qintp_acquire_lock = qintp_acquire_lock;
  __qintp_acquire_tty = qintp_acquire_tty;
  __qintp_release_tty = qintp_release_tty;
  __qintp_thread_atfork = qintp_thread_atfork;

  __qintp_error = qintp_error;

  return 1;
}

/* helper functions */

__DLLIMPORT expr __mkerror(void)
{
  __qintp_error();
  return NULL;
}

__DLLIMPORT int __getsym(const char *name, int modno)
{
  return __qintp_sym_lookup(name, modno);
}

__DLLIMPORT int __gettype(const char *name, int modno)
{
  return __qintp_type_lookup(name, modno);
}

__DLLIMPORT void __thread_atfork(void (*prepare)(void), void (*parent)(void),
				 void (*child)(void), int modno)
{
  __qintp_thread_atfork(prepare, parent, child, modno);
}


syntax highlighted by Code2HTML, v. 0.9.1