X-Git-Url: https://git.mxchange.org/?a=blobdiff_plain;f=src%2FScripting%2FNasalSys.cxx;h=870d6def1455f0831e095243af62223d333bb799;hb=9c28ed02577e6d32e1365567107adbd048f3d743;hp=e481ac510a5f0384a73dadbe4baa4c7efa4b6136;hpb=d3181fb437818a9b333eac85c4afcef1335bf8b6;p=flightgear.git diff --git a/src/Scripting/NasalSys.cxx b/src/Scripting/NasalSys.cxx index e481ac510..870d6def1 100644 --- a/src/Scripting/NasalSys.cxx +++ b/src/Scripting/NasalSys.cxx @@ -7,10 +7,13 @@ #include #include +#include #include +#include #include #include
+#include
#include "NasalSys.hxx" @@ -34,7 +37,8 @@ static char* readfile(const char* file, int* lenOut) // etc...) SG_LOG(SG_NASAL, SG_ALERT, "ERROR in Nasal initialization: " << - "short count returned from fread(). Check your C library!"); + "short count returned from fread() of " << file << + ". Check your C library!"); delete[] buf; return 0; } @@ -45,8 +49,8 @@ FGNasalSys::FGNasalSys() { _context = 0; _globals = naNil(); - _timerHash = naNil(); - _nextTimerHashKey = 0; // Any value will do + _gcHash = naNil(); + _nextGCKey = 0; // Any value will do } FGNasalSys::~FGNasalSys() @@ -60,6 +64,42 @@ FGNasalSys::~FGNasalSys() _globals = naNil(); } +bool FGNasalSys::parseAndRun(const char* sourceCode) +{ + naRef code = parse("FGNasalSys::parseAndRun()", sourceCode, + strlen(sourceCode)); + if(naIsNil(code)) + return false; + + naCall(_context, code, 0, 0, naNil(), naNil()); + + if(!naGetError(_context)) return true; + logError(); + return false; +} + +FGNasalScript* FGNasalSys::parseScript(const char* src, const char* name) +{ + FGNasalScript* script = new FGNasalScript(); + script->_gcKey = -1; // important, if we delete it on a parse + script->_nas = this; // error, don't clobber a real handle! + + char buf[256]; + if(!name) { + sprintf(buf, "FGNasalScript@%p", script); + name = buf; + } + + script->_code = parse(name, src, strlen(src)); + if(naIsNil(script->_code)) { + delete script; + return 0; + } + + script->_gcKey = gcSave(script->_code); + return script; +} + // Utility. Sets a named key in a hash by C string, rather than nasal // string object. void FGNasalSys::hashset(naRef hash, const char* key, naRef val) @@ -76,11 +116,11 @@ void FGNasalSys::hashset(naRef hash, const char* key, naRef val) // is the utility function that walks the property tree. // Future enhancement: support integer arguments to specify array // elements. -static SGPropertyNode* findnode(naContext c, naRef vec, int len) +static SGPropertyNode* findnode(naContext c, naRef* vec, int len) { SGPropertyNode* p = globals->get_props(); for(int i=0; igetNode(naStr_data(a)); if(p == 0) return 0; @@ -91,9 +131,9 @@ static SGPropertyNode* findnode(naContext c, naRef vec, int len) // getprop() extension function. Concatenates its string arguments as // property names and returns the value of the specified property. Or // nil if it doesn't exist. -static naRef f_getprop(naContext c, naRef args) +static naRef f_getprop(naContext c, naRef me, int argc, naRef* args) { - const SGPropertyNode* p = findnode(c, args, naVec_size(args)); + const SGPropertyNode* p = findnode(c, args, argc); if(!p) return naNil(); switch(p->getType()) { @@ -103,12 +143,14 @@ static naRef f_getprop(naContext c, naRef args) return naNum(p->getDoubleValue()); case SGPropertyNode::STRING: + case SGPropertyNode::UNSPECIFIED: { naRef nastr = naNewString(c); const char* val = p->getStringValue(); naStr_fromdata(nastr, (char*)val, strlen(val)); return nastr; } + case SGPropertyNode::ALIAS: // <--- FIXME, recurse? default: return naNil(); } @@ -117,16 +159,15 @@ static naRef f_getprop(naContext c, naRef args) // setprop() extension function. Concatenates its string arguments as // property names and sets the value of the specified property to the // final argument. -static naRef f_setprop(naContext c, naRef args) +static naRef f_setprop(naContext c, naRef me, int argc, naRef* args) { #define BUFLEN 1024 - int argc = naVec_size(args); char buf[BUFLEN + 1]; buf[BUFLEN] = 0; char* p = buf; int buflen = BUFLEN; for(int i=0; iget_props(); - naRef val = naVec_get(args, argc-1); + naRef val = args[argc-1]; if(naIsString(val)) props->setStringValue(buf, naStr_data(val)); else props->setDoubleValue(buf, naNumValue(val).num); return naNil(); @@ -148,16 +189,17 @@ static naRef f_setprop(naContext c, naRef args) // print() extension function. Concatenates and prints its arguments // to the FlightGear log. Uses the highest log level (SG_ALERT), to // make sure it appears. Is there better way to do this? -static naRef f_print(naContext c, naRef args) +static naRef f_print(naContext c, naRef me, int argc, naRef* args) { #define BUFLEN 1024 char buf[BUFLEN + 1]; buf[BUFLEN] = 0; // extra nul to handle strncpy brain damage + buf[0] = 0; // Zero-length in case there are no arguments char* p = buf; int buflen = BUFLEN; - int n = naVec_size(args); + int n = argc; for(int i=0; iget_props()->getNode(naStr_data(props)); - if(pnode) - globals->get_commands()->execute(naStr_data(cmd), pnode); + if(argc < 2 || !naIsString(args[0]) || !naIsGhost(args[1])) + naRuntimeError(c, "bad arguments to fgcommand()"); + naRef cmd = args[0], props = args[1]; + SGPropertyNode_ptr* node = (SGPropertyNode_ptr*)naGhost_ptr(props); + globals->get_commands()->execute(naStr_data(cmd), *node); return naNil(); + } // settimer(func, dt, simtime) extension function. Falls through to // FGNasalSys::setTimer(). See there for docs. -static naRef f_settimer(naContext c, naRef args) +static naRef f_settimer(naContext c, naRef me, int argc, naRef* args) +{ + FGNasalSys* nasal = (FGNasalSys*)globals->get_subsystem("nasal"); + nasal->setTimer(argc, args); + return naNil(); +} + +// Returns a ghost handle to the argument to the currently executing +// command +static naRef f_cmdarg(naContext c, naRef me, int argc, naRef* args) { FGNasalSys* nasal = (FGNasalSys*)globals->get_subsystem("nasal"); - nasal->setTimer(args); + return nasal->cmdArgGhost(); +} + +// Sets up a property interpolation. The first argument is either a +// ghost (SGPropertyNode_ptr*) or a string (global property path) to +// interpolate. The second argument is a vector of pairs of +// value/delta numbers. +static naRef f_interpolate(naContext c, naRef me, int argc, naRef* args) +{ + SGPropertyNode* node; + naRef prop = argc > 0 ? args[0] : naNil(); + if(naIsString(prop)) node = fgGetNode(naStr_data(prop), true); + else if(naIsGhost(prop)) node = *(SGPropertyNode_ptr*)naGhost_ptr(prop); + else return naNil(); + + naRef curve = argc > 1 ? args[1] : naNil(); + if(!naIsVector(curve)) return naNil(); + int nPoints = naVec_size(curve) / 2; + double* values = new double[nPoints]; + double* deltas = new double[nPoints]; + for(int i=0; iget_subsystem("interpolator")) + ->interpolate(node, nPoints, values, deltas); + return naNil(); } +// This is a better RNG than the one in the default Nasal distribution +// (which is based on the C library rand() implementation). It will +// override. +static naRef f_rand(naContext c, naRef me, int argc, naRef* args) +{ + return naNum(sg_random()); +} + // Table of extension functions. Terminate with zeros. static struct { char* name; naCFunction func; } funcs[] = { { "getprop", f_getprop }, { "setprop", f_setprop }, { "print", f_print }, - { "fgcommand", f_fgcommand }, + { "_fgcommand", f_fgcommand }, { "settimer", f_settimer }, + { "_cmdarg", f_cmdarg }, + { "_interpolate", f_interpolate }, + { "rand", f_rand }, { 0, 0 } }; +naRef FGNasalSys::cmdArgGhost() +{ + return propNodeGhost(_cmdArg); +} + void FGNasalSys::init() { + int i; + _context = naNewContext(); // Start with globals. Add it to itself as a recursive @@ -220,14 +314,18 @@ void FGNasalSys::init() hashset(_globals, "math", naMathLib(_context)); // Add our custom extension functions: - for(int i=0; funcs[i].name; i++) + for(i=0; funcs[i].name; i++) hashset(_globals, funcs[i].name, naNewFunc(_context, naNewCCode(_context, funcs[i].func))); - // Make a "__timers" hash to hold the settimer() handlers (to - // protect them from begin garbage-collected). - _timerHash = naNewHash(_context); - hashset(_globals, "__timers", _timerHash); + // And our SGPropertyNode wrapper + hashset(_globals, "props", genPropsModule()); + + // Make a "__gcsave" hash to hold the naRef objects which get + // passed to handles outside the interpreter (to protect them from + // begin garbage-collected). + _gcHash = naNewHash(_context); + hashset(_globals, "__gcsave", _gcHash); // Now load the various source files in the Nasal directory SGPath p(globals->get_fg_root()); @@ -239,7 +337,60 @@ void FGNasalSys::init() fullpath.append(dent->d_name); SGPath file(dent->d_name); if(file.extension() != "nas") continue; - readScriptFile(fullpath, file.base().c_str()); + loadModule(fullpath, file.base().c_str()); + } + + // Pull scripts out of the property tree, too + loadPropertyScripts(); +} + +// Loads the scripts found under /nasal in the global tree +void FGNasalSys::loadPropertyScripts() +{ + SGPropertyNode* nasal = globals->get_props()->getNode("nasal"); + if(!nasal) return; + + for(int i=0; inChildren(); i++) { + SGPropertyNode* n = nasal->getChild(i); + + const char* module = n->getName(); + if(n->hasChild("module")) + module = n->getStringValue("module"); + + // allow multiple files to be specified within in a single + // Nasal module tag + int j = 0; + SGPropertyNode *fn; + bool file_specified = false; + while ( (fn = n->getChild("file", j)) != NULL ) { + file_specified = true; + const char* file = fn->getStringValue(); + SGPath p(globals->get_fg_root()); + p.append(file); + loadModule(p, module); + j++; + } + + // Old code which only allowed a single file to be specified per module + /* + const char* file = n->getStringValue("file"); + if(!n->hasChild("file")) file = 0; // Hrm... + if(file) { + SGPath p(globals->get_fg_root()); + p.append(file); + loadModule(p, module); + } + */ + + const char* src = n->getStringValue("script"); + if(!n->hasChild("script")) src = 0; // Hrm... + if(src) + createModule(module, n->getPath(), src, strlen(src)); + + if(!file_specified && !src) + SG_LOG(SG_NASAL, SG_ALERT, "Nasal error: " << + "no or