#include <Main/fg_props.hxx>
#include "NasalSys.hxx"
+#include "NasalDisplay.hxx"
// Read and return file contents in a single buffer. Note use of
// stat() to get the file size. This is a win32 function, believe it
if(naIsNil(code))
return false;
- naCall(_context, code, naNil(), naNil(), naNil());
+ naCall(_context, code, 0, 0, naNil(), naNil());
if(!naGetError(_context)) return true;
logError();
char buf[256];
if(!name) {
- sprintf(buf, "FGNasalScript@%8.8x", (int)script);
+ sprintf(buf, "FGNasalScript@%p", script);
name = buf;
}
- script->_code = parse(name, src);
+ script->_code = parse(name, src, strlen(src));
if(naIsNil(script->_code)) {
delete script;
return 0;
return naNil();
}
+// setlistener(func, property) extension function. Falls through to
+// FGNasalSys::setListener(). See there for docs.
+static naRef f_setlistener(naContext c, naRef me, int argc, naRef* args)
+{
+ FGNasalSys* nasal = (FGNasalSys*)globals->get_subsystem("nasal");
+ nasal->setListener(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)
return naNum(sg_random());
}
+static naRef f_srand(naContext c, naRef me, int argc, naRef* args)
+{
+ sg_srandom_time();
+ return naNum(0);
+}
+
+// Wrapper function for screenPrint
+static naRef f_screenPrint(naContext c, naRef me, int argc, naRef* args)
+{
+ if(argc != 1 || !naIsString(args[0]))
+ naRuntimeError(c, "bad arguments to screenPrint()");
+ naRef lmsg = args[0];
+ FGNasalSys* nasal = (FGNasalSys*)globals->get_subsystem("nasal");
+ nasal->screenPrint(naStr_data(lmsg));
+ return naNil();
+}
+
+// Return an array listing of all files in a directory
+static naRef f_directory(naContext c, naRef me, int argc, naRef* args)
+{
+ if(argc != 1 || !naIsString(args[0]))
+ naRuntimeError(c, "bad arguments to directory()");
+ naRef ldir = args[0];
+ ulDir* dir = ulOpenDir(naStr_data(args[0]));
+ if(!dir) return naNil();
+ naRef result = naNewVector(c);
+ ulDirEnt* dent;
+ while((dent = ulReadDir(dir)))
+ naVec_append(result, naStr_fromdata(naNewString(c), dent->d_name,
+ strlen(dent->d_name)));
+ ulCloseDir(dir);
+ return result;
+}
+
// Table of extension functions. Terminate with zeros.
static struct { char* name; naCFunction func; } funcs[] = {
{ "getprop", f_getprop },
{ "print", f_print },
{ "_fgcommand", f_fgcommand },
{ "settimer", f_settimer },
+ { "_setlistener", f_setlistener },
{ "_cmdarg", f_cmdarg },
{ "_interpolate", f_interpolate },
{ "rand", f_rand },
+ { "srand", f_srand },
+ { "screenPrint", f_screenPrint },
+ { "directory", f_directory },
{ 0, 0 }
};
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
const char* file = fn->getStringValue();
SGPath p(globals->get_fg_root());
p.append(file);
- readScriptFile(p, module);
+ loadModule(p, module);
j++;
}
if(file) {
SGPath p(globals->get_fg_root());
p.append(file);
- readScriptFile(p, module);
+ loadModule(p, module);
}
*/
const char* src = n->getStringValue("script");
if(!n->hasChild("script")) src = 0; // Hrm...
if(src)
- initModule(module, n->getPath(), src, strlen(src));
+ createModule(module, n->getPath(), src, strlen(src));
if(!file_specified && !src)
SG_LOG(SG_NASAL, SG_ALERT, "Nasal error: " <<
// Reads a script file, executes it, and places the resulting
// namespace into the global namespace under the specified module
// name.
-void FGNasalSys::readScriptFile(SGPath file, const char* module)
+void FGNasalSys::loadModule(SGPath file, const char* module)
{
int len = 0;
char* buf = readfile(file.c_str(), &len);
return;
}
- initModule(module, file.c_str(), buf, len);
+ createModule(module, file.c_str(), buf, len);
delete[] buf;
}
// Parse and run. Save the local variables namespace, as it will
// become a sub-object of globals.
-void FGNasalSys::initModule(const char* moduleName, const char* fileName,
- const char* src, int len)
+void FGNasalSys::createModule(const char* moduleName, const char* fileName,
+ const char* src, int len)
{
naRef code = parse(fileName, src, len);
if(naIsNil(code))
if(!naHash_get(_globals, modname, &locals))
locals = naNewHash(_context);
- naCall(_context, code, naNil(), naNil(), locals);
+ naCall(_context, code, 0, 0, naNil(), locals);
if(naGetError(_context)) {
logError();
return;
naRef FGNasalSys::parse(const char* filename, const char* buf, int len)
{
- if(len == 0) len = strlen(buf);
int errLine = -1;
naRef srcfile = naNewString(_context);
naStr_fromdata(srcfile, (char*)filename, strlen(filename));
bool FGNasalSys::handleCommand(const SGPropertyNode* arg)
{
- // Parse the Nasal source. I'd love to use the property name of
- // the argument, but it's actually a *clone* of the original
- // location in the property tree. arg->getPath() returns an empty
- // string.
const char* nasal = arg->getStringValue("script");
- naRef code = parse("<command>", nasal);
+ const char* moduleName = arg->getStringValue("module");
+ naRef code = parse(arg->getPath(true), nasal, strlen(nasal));
if(naIsNil(code)) return false;
-
+
+ naRef locals = naNil();
+ if (moduleName[0]) {
+ naRef modname = naNewString(_context);
+ naStr_fromdata(modname, (char*)moduleName, strlen(moduleName));
+ if(!naHash_get(_globals, modname, &locals))
+ locals = naNewHash(_context);
+ }
// Cache the command argument for inspection via cmdarg(). For
// performance reasons, we won't bother with it if the invoked
// code doesn't need it.
_cmdArg = (SGPropertyNode*)arg;
// Call it!
- naRef result = naCall(_context, code, naNil(), naNil(), naNil());
+ naRef result = naCall(_context, code, 0, 0, naNil(), locals);
if(!naGetError(_context)) return true;
logError();
return false;
// settimer(func, dt, simtime) extension function. The first argument
// is a Nasal function to call, the second is a delta time (from now),
// in seconds. The third, if present, is a boolean value indicating
-// that "simulator" time (rather than real time) is to be used.
+// that "real world" time (rather than simulator time) is to be used.
//
// Implementation note: the FGTimer objects don't live inside the
// garbage collector, so the Nasal handler functions have to be
naRef delta = argc > 1 ? args[1] : naNil();
if(naIsNil(delta)) return;
- bool simtime = (argc > 2 && naTrue(args[2])) ? true : false;
+ bool simtime = (argc > 2 && naTrue(args[2])) ? false : true;
// Generate and register a C++ timer handler
NasalTimer* t = new NasalTimer;
void FGNasalSys::handleTimer(NasalTimer* t)
{
- naCall(_context, t->handler, naNil(), naNil(), naNil());
+ naCall(_context, t->handler, 0, 0, naNil(), naNil());
if(naGetError(_context))
logError();
gcRelease(t->gcKey);
nasal->handleTimer(this);
delete this;
}
+
+// setlistener(property, func) extension function. The first argument
+// is either a ghost (SGPropertyNode_ptr*) or a string (global property
+// path), the second is a Nasal function.
+void FGNasalSys::setListener(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;
+
+ naRef handler = argc > 1 ? args[1] : naNil();
+ if(!(naIsCode(handler) || naIsCCode(handler) || naIsFunc(handler)))
+ return;
+
+ node->addChangeListener(new FGNasalListener(handler, this, gcSave(handler)));
+}
+
+// functions providing access to the NasalDisplay - used to display text directly on the screen
+void FGNasalSys::screenPrint(const char* src)
+{
+ globals->get_Nasal_display()->RegisterSingleMessage(src, 0);
+}