844 lines · 21.1 KB · C
RawDownload
/* -------------------------------------------------------------------------- * Implementation of the Hugs server API. * * The Hugs server allows you to programmatically load scripts and * build/evaluate terms. Used by 'runhugs' to provide a batch-mode * UI to the interpreter. * * The Hugs 98 system is Copyright (c) Mark P Jones, Alastair Reid, the * Yale Haskell Group, and the OGI School of Science & Engineering at OHSU, * 1994-2003, All rights reserved.  It is distributed as free software under * the license in the file "License", which is included in the distribution. * * $RCSfile: server.c,v $ * $Revision: 1.45 $ * $Date: 2005/09/10 09:42:26 $ * ------------------------------------------------------------------------*/#include "prelude.h"#include "storage.h"#include "connect.h"#include "script.h"#include "machdep.h"#include "evaluator.h"#include "opts.h"#include "strutil.h"#include "errors.h"#include "server.h" #include <setjmp.h> static Void   setHugsAPI   Args((Void));static Bool   SetModule    Args((String));#ifndef NO_DYNAMIC_TYPESstatic Bool   linkDynamic     Args((Void));#endif /* -------------------------------------------------------------------------- * Dynamic linking * * The simplest way to do dynamic linking is this: * 1) load the dll/shared object file * 2) get the address of an initialisation function *    from the dll symbol table * 3) call the initialisation function - which returns a "virtual *    function table" - a struct containing the addresses of all other *    functions and variables that we need to access. * ------------------------------------------------------------------------*/ static Int    GetNumScripts   Args((Void));static Void   Reset           Args((Int));static Void   SetOutputEnable Args((Bool));static Void   ChangeDir       Args((String));static Void   LoadProject     Args((String));static Void   LoadFile        Args((String));static Void   LoadStringF     Args((String));static Void   SetOptions      Args((String));static String GetOptions      Args((Void));static HVal   CompileExpr     Args((String, String));static Void   GarbageCollect  Args((void));static Void   LookupName      Args((String, String));static Void   MkInt           Args((Int));static Void   MkAddr          Args((void*));static Void   MkString        Args((String));static Void   Apply           Args((Void));static Int    EvalInt         Args((Void));static void*  EvalAddr        Args((void));static String EvalString      Args((Void));static Int    DoIO            Args((Void));static Int    DoIO_Int        Args((int*));static Int    DoIO_Addr       Args((void**));static HVal   PopHVal         Args((Void));static Void   PushHVal        Args((HVal));static Void   FreeHVal        Args((HVal)); static HugsServerAPI hugs;             /* virtual function table            */ static Void setHugsAPI() {       /* initialise virtual function table */    static Bool api_inited = FALSE;    if (!api_inited) {      api_inited = TRUE;       hugs.clearError      = ClearError;      hugs.setHugsArgs     = setHugsArgs;      hugs.getNumScripts   = GetNumScripts;      hugs.reset           = Reset;      hugs.setOutputEnable = SetOutputEnable;      hugs.changeDir       = ChangeDir;      hugs.loadProject     = LoadProject;      hugs.loadFile        = LoadFile;      hugs.loadFromBuffer  = LoadStringF;      hugs.setOptions      = SetOptions;      hugs.getOptions      = GetOptions;      hugs.compileExpr     = CompileExpr;      hugs.garbageCollect  = GarbageCollect;      hugs.lookupName      = LookupName;      hugs.mkInt           = MkInt;      hugs.mkAddr          = MkAddr;      hugs.mkString        = MkString;      hugs.apply           = Apply;      hugs.evalInt         = EvalInt;      hugs.evalAddr        = EvalAddr;      hugs.evalString      = EvalString;      hugs.doIO            = DoIO;      hugs.doIO_Int        = DoIO_Int;      hugs.doIO_Addr       = DoIO_Addr;      hugs.popHVal         = PopHVal;      hugs.pushHVal        = PushHVal;      hugs.freeHVal        = FreeHVal;    }} /* -------------------------------------------------------------------------- * Error handling * * We buffer error messages and refuse to execute commands until * the error is cleared. * ------------------------------------------------------------------------*/ #define ErrorBufferSize 10000 static char  serverErrMsg[ErrorBufferSize];   /* Buffer for error messages */char* lastError = NULL; String ClearError(){    String err = lastError;    lastError  = NULL;    ClearOutputBuffer();     if (err && (numLoadedScripts() > 0))     {        everybody(RESET);                dropScriptsFrom(numLoadedScripts()-1);  /* remove partially loaded scripts */    }    return err;} Void setError(s)            /* Format an error message        */String s; {    Int    n = 0;    String err = ClearOutputBuffer();     if (NULL == err) {	n = snprintf(serverErrMsg, ErrorBufferSize, "%s\n", s);    } else {	n = snprintf(serverErrMsg, ErrorBufferSize, "%s\n%s\n", s, err);    }    if (0 <= n && n <= ErrorBufferSize) {	lastError = serverErrMsg;    } else {	lastError = "error buffer overflowed\n";    }} /* All server entry points set CStackBase for the benefit of the (conservative) * GC and do error catching.  Any calls to Hugs functions should be "protected" * by being placed inside this macro. * *   void entryPoint(arg1, arg2, result) *   T1 arg1; *   T2 arg2; *   T3 *result; *   { *       protect(doNothing(), *           ... *       ); *   } * * Macro decomposed into BEGIN_PROTECT and END_PROTECT pieces so that i * can be used on some compilers (Mac?) that have limits on the size of * macro arguments. */#define BEGIN_PROTECT \  if (NULL == lastError) { \      Cell dummy; \      CStackBase = &dummy;              /* Save stack base for use in gc  */ \      consGC = TRUE;                    /* conservative GC is the default */ \      if (!setjmp(catch_error)) {#define END_PROTECT \      } else { \	setError("Error occurred"); \	normalTerminal(); \      }	\  }#define protect(s)	BEGIN_PROTECT s; END_PROTECT /* -------------------------------------------------------------------------- * Initialisation * ------------------------------------------------------------------------*/ /* I've added a special case for the server.  Probably should just add   another entry point but what the heck.  If argc = -1 then the hugs   server should NOT read registry or default hugs path stuff.  Instead,   all options are in the first argument in argv.   -- jcp */ DLLEXPORT(HugsServerAPI*) initHugsServer(argc, argv) /*server initialisation*/Int    argc;String argv[]; {     static Bool is_initialized = FALSE;     if (!is_initialized) {      is_initialized = TRUE;      setHugsAPI();            BEGIN_PROTECT			/* Too much text for protect()	   */      Int i;       startEvaluator();       if (argc == -1) {	readOptions(argv[0],FALSE);      } else {	readOptionSettings();	for (i=1; i<argc && (argv[i][0]=='+' || argv[i][0]=='-'); ++i) {	  if (!processOption(argv[i])) {	    setError("Unrecognised option");	    return NULL;	  }	}      }      EnableOutput(FALSE);      loadPrelude(); #ifndef NO_DYNAMIC_TYPES      addScriptName("Hugs.Dynamic",TRUE);#endif       readScripts(0);      everybody(RESET);#ifndef NO_DYNAMIC_TYPES      if (!linkDynamic()) {	setError("module Hugs.Dynamic doesn't define the expected functions");	return NULL;      }#endif      END_PROTECT   }   return &hugs;  /* error must have occurred */} /* Just give me the method table; initialisation is assumed to already have taken * place. Used when external code needs to callback into Haskell. */HugsServerAPI* getHugsAPI() {  setHugsAPI();  return &hugs;} /* -------------------------------------------------------------------------- * Shutting down: * ------------------------------------------------------------------------*/DLLEXPORT(Void) shutdownHugsServer(hserv) /* server shutdown */HugsServerAPI* hserv; {  /* The 'hserv' argument isn't actually used */  clearStack();  stopAnyPrinting();  everybody(EXIT);  stopEvaluator();  return;} /* -------------------------------------------------------------------------- * Dynamic typing * * We use dynamic typing to make the system more robust - all values pushed * on the stack have type "Dynamic" and we check the real type on function * application and evaluation.  This requires on functions and a class * defined in the HugsDynamic library. * * sof 2001 - interposing a Dynamic-typed layer sometimes gets in the way *            (missing Typeable instances, extra loading of modules), as *            has proven the case when using the HugsServerAPI by HaskellScript, *            Lambada, and mod_haskell. By defining NO_DYNAMIC_TYPES, *            you may turn this feature off. * ------------------------------------------------------------------------*/#ifndef NO_DYNAMIC_TYPESstatic Name  nameRunDyn;static Name  nameDynApp;static Name  nameToDyn;static Name  nameCoerceDynamic;static Class classTypeable; static Bool linkDynamic(){    nameRunDyn        = findName(findText("runDyn"));    nameDynApp        = findName(findText("dynApp"));    nameToDyn         = findName(findText("toDyn"));    nameCoerceDynamic = findName(findText("coerceDynamic"));    classTypeable     = findClass(findText("Typeable"));     return (   nonNull(nameRunDyn        )	    && nonNull(nameDynApp        )	    && nonNull(nameToDyn         )	    && nonNull(nameCoerceDynamic )	    && nonNull(classTypeable     ));}#endif /* -------------------------------------------------------------------------- * Miscellaneous: * ------------------------------------------------------------------------*/ static Int GetNumScripts()      /* Get number of scripts in system  */{    protect(return numLoadedScripts());    return 0;} static Void Reset(scripts) /* Restore number of scripts to old level */Int scripts; {    protect(	ClearOutputBuffer();	forgetScriptsFrom(scripts);	everybody(RESET);	);} static Void SetOutputEnable(f)    /* enable/disable compiler output  */Bool f;{    protect(EnableOutput(f));} static Void ChangeDir(s)          /* change current directory        */String s;{    protect(	if (chdir(s)) {	    setError("changeDir: invalid directory");	    return;	}	);} static Void LoadProject(fn)       /* load a project into the system  */String fn;{    protect(setError("loadProject: not implemented"));} static Void LoadFile(fn)          /* load a module (from a file) into the system   */String fn;{    protect(	addScriptName(fn,TRUE);	readScripts(numLoadedScripts());	everybody(RESET);	);} static Void LoadStringF(mod)      /* load a module (from a string) into the system   */String mod;{    protect(	addScriptName(mod,TRUE);	readScripts(numLoadedScripts());	everybody(RESET);	);} static Void SetOptions(opt)String opt; {  readOptions2(opt);  return;} static String GetOptions() {  return strCopy(optionsToStr());} static Bool SetModule(m)String m; {    Module mod   = findModule(findText(m));    if (isNull(mod)) {	return FALSE;    }    setCurrModule(mod);    return TRUE;} static HVal CompileExpr(m,e)    /* compile expression e wrt module m */String m;String e; {    BEGIN_PROTECT	Type type = NIL;	Cell d    = NIL;	HVal r    = 0; 	if (!SetModule(m)) {	    char errmsg[256];	    snprintf(errmsg, 255, "can't find \"%s\" module", m);	    setError(errmsg);	    return 0;	}	scriptFile = 0;	stringInput(e);                /* Put expression into input buffer */					startNewScript(0);             /* Enables recovery of storage      */				       /* allocated during evaluation      */	parseExp();	checkExp();	defaultDefns = evalDefaults;	type         = typeCheckExp(TRUE);#ifndef NO_DYNAMIC_TYPES	d = getTypeableDict(type);	if (isNull(d)) {	    setError("compileExpr: can't create Typeable instance");	    return 0;	}	inputExpr = ap(ap(nameToDyn,d),inputExpr);#endif	compileExp();	run(inputCode,sp);  /* Build graph for redex */ 	r = mkStablePtr(pop());	if (0 == r) {	    setError("compileExpr: no free stable pointers");	    return 0;	}	return r;    END_PROTECT    return 0;} static Void GarbageCollect(){    garbageCollect();} /* -------------------------------------------------------------------------- * Building expressions on the stack * ------------------------------------------------------------------------*/ static Void LookupName(m,v) /*Push value of qualified name onto stack*/String m,v;{    BEGIN_PROTECT	Name   n;	Cell   d;        char   errbuf[256]; 	if (!SetModule(m)) {	    snprintf(errbuf, 255, "lookupName: invalid module '%s'", m);	    setError(errbuf);	    return;	}	if (isNull(n = findName(findText(v)))) {            snprintf( errbuf, 255, "lookupName: invalid name '%s.%s'", m, v );	    setError(errbuf);	    return;	}#ifndef NO_DYNAMIC_TYPES	d = getTypeableDict(name(n).type);	if (isNull(d)) {	    setError("lookupName: can't create Typeable instance");	    return;	}	push(ap(ap(nameToDyn,d),n));#else	push(n);#endif    END_PROTECT} static Void MkInt(i)              /* Push an Int onto the stack      */Int i;{#ifndef NO_DYNAMIC_TYPES    Cell d = getTypeableDict(typeInt);    protect(push(ap(ap(nameToDyn,d),mkInt(i))));#else    protect(push(mkInt(i)));#endif} static Void MkAddr(a)              /* Push an Addr onto the stack      */void* a;{#ifndef NO_DYNAMIC_TYPES    Cell d = getTypeableDict(typeAddr);    protect(push(ap(ap(nameToDyn,d),mkPtr(a))));#else    protect(push(mkPtr(a)));#endif} static Void MkString(s)           /* Push a String onto the stack    */String s;{    BEGIN_PROTECT	pushString(s);#ifndef NO_DYNAMIC_TYPES	topfun(ap(nameToDyn,getTypeableDict(typeString)));#endif    END_PROTECT} static Void Apply()              /* Apply stack[sp-1] to stack[sp]   */

Lines 1–500 of 844.