Add Nasal Vs. 1.5
This commit is contained in:
parent
3b2d97289c
commit
79e48829da
4 changed files with 357 additions and 33 deletions
|
@ -1,6 +1,6 @@
|
|||
noinst_LIBRARIES = libScripting.a
|
||||
|
||||
libScripting_a_SOURCES = NasalSys.cxx NasalSys.hxx
|
||||
libScripting_a_SOURCES = NasalSys.cxx NasalSys.hxx nasal-props.cxx
|
||||
# libScripting_a_SOURCES = scriptmgr.cxx scriptmgr.hxx NasalSys.cxx NasalSys.hxx
|
||||
|
||||
INCLUDES = -I$(top_srcdir) -I$(top_srcdir)/src
|
||||
|
|
|
@ -34,7 +34,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;
|
||||
}
|
||||
|
@ -60,6 +61,20 @@ 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, naNil(), naNil(), naNil());
|
||||
|
||||
if(!naGetError(_context)) return true;
|
||||
logError();
|
||||
return false;
|
||||
}
|
||||
|
||||
// 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)
|
||||
|
@ -176,13 +191,11 @@ static naRef f_fgcommand(naContext c, naRef args)
|
|||
{
|
||||
naRef cmd = naVec_get(args, 0);
|
||||
naRef props = naVec_get(args, 1);
|
||||
if(!naIsString(cmd) || !naIsString(props)) return naNil();
|
||||
|
||||
SGPropertyNode* pnode =
|
||||
globals->get_props()->getNode(naStr_data(props));
|
||||
if(pnode)
|
||||
globals->get_commands()->execute(naStr_data(cmd), pnode);
|
||||
if(!naIsString(cmd) || !naIsGhost(props)) return naNil();
|
||||
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
|
||||
|
@ -194,18 +207,34 @@ static naRef f_settimer(naContext c, naRef args)
|
|||
return naNil();
|
||||
}
|
||||
|
||||
// Returns a ghost handle to the argument to the currently executing
|
||||
// command
|
||||
static naRef f_cmdarg(naContext c, naRef args)
|
||||
{
|
||||
FGNasalSys* nasal = (FGNasalSys*)globals->get_subsystem("nasal");
|
||||
return nasal->cmdArgGhost();
|
||||
}
|
||||
|
||||
// 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 },
|
||||
{ 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,10 +249,13 @@ 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)));
|
||||
|
||||
// And our SGPropertyNode wrapper
|
||||
hashset(_globals, "props", genPropsModule());
|
||||
|
||||
// Make a "__timers" hash to hold the settimer() handlers (to
|
||||
// protect them from begin garbage-collected).
|
||||
_timerHash = naNewHash(_context);
|
||||
|
@ -241,6 +273,42 @@ void FGNasalSys::init()
|
|||
if(file.extension() != "nas") continue;
|
||||
readScriptFile(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; i<nasal->nChildren(); i++) {
|
||||
SGPropertyNode* n = nasal->getChild(i);
|
||||
|
||||
const char* module = n->getName();
|
||||
if(n->hasChild("module"))
|
||||
module = n->getStringValue("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);
|
||||
readScriptFile(p, module);
|
||||
}
|
||||
|
||||
const char* src = n->getStringValue("script");
|
||||
if(!n->hasChild("script")) src = 0; // Hrm...
|
||||
if(src)
|
||||
initModule(module, n->getPath(), src, strlen(src));
|
||||
|
||||
if(!file && !src)
|
||||
SG_LOG(SG_NASAL, SG_ALERT, "Nasal error: " <<
|
||||
"no <file> or <script> defined in " <<
|
||||
"/nasal/" << module);
|
||||
}
|
||||
}
|
||||
|
||||
// Logs a runtime error, with stack trace, to the FlightGear log stream
|
||||
|
@ -260,27 +328,47 @@ void FGNasalSys::logError()
|
|||
// 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* lib)
|
||||
void FGNasalSys::readScriptFile(SGPath file, const char* module)
|
||||
{
|
||||
int len = 0;
|
||||
char* buf = readfile(file.c_str(), &len);
|
||||
if(!buf) return;
|
||||
if(!buf) {
|
||||
SG_LOG(SG_NASAL, SG_ALERT,
|
||||
"Nasal error: could not read script file " << file.c_str()
|
||||
<< " into module " << module);
|
||||
return;
|
||||
}
|
||||
|
||||
// Parse and run. Save the local variables namespace, as it will
|
||||
// become a sub-object of globals.
|
||||
naRef code = parse(file.c_str(), buf, len);
|
||||
initModule(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)
|
||||
{
|
||||
if(len == 0) len = strlen(src);
|
||||
|
||||
naRef code = parse(fileName, src, len);
|
||||
if(naIsNil(code))
|
||||
return;
|
||||
|
||||
naRef locals = naNewHash(_context);
|
||||
// See if we already have a module hash to use. This allows the
|
||||
// user to, for example, add functions to the built-in math
|
||||
// module. Make a new one if necessary.
|
||||
naRef locals;
|
||||
naRef modname = naNewString(_context);
|
||||
naStr_fromdata(modname, (char*)moduleName, strlen(moduleName));
|
||||
if(!naHash_get(_globals, modname, &locals))
|
||||
locals = naNewHash(_context);
|
||||
|
||||
naCall(_context, code, naNil(), naNil(), locals);
|
||||
if(naGetError(_context)) {
|
||||
logError();
|
||||
return;
|
||||
}
|
||||
|
||||
hashset(_globals, lib, locals);
|
||||
hashset(_globals, moduleName, locals);
|
||||
}
|
||||
|
||||
naRef FGNasalSys::parse(const char* filename, const char* buf, int len)
|
||||
|
@ -310,23 +398,13 @@ bool FGNasalSys::handleCommand(const SGPropertyNode* arg)
|
|||
naRef code = parse("<command>", nasal, strlen(nasal));
|
||||
if(naIsNil(code)) return false;
|
||||
|
||||
// FIXME: Cache the just-created code object somewhere, but watch
|
||||
// for changes to the source in the property tree. Maybe store an
|
||||
// integer index into a Nasal vector in the original property
|
||||
// location?
|
||||
|
||||
// Extract the "value" or "offset" arguments if present
|
||||
naRef locals = naNil();
|
||||
if(arg->hasValue("value")) {
|
||||
locals = naNewHash(_context);
|
||||
hashset(locals, "value", naNum(arg->getDoubleValue("value")));
|
||||
} else if(arg->hasValue("offset")) {
|
||||
locals = naNewHash(_context);
|
||||
hashset(locals, "offset", naNum(arg->getDoubleValue("offset")));
|
||||
}
|
||||
// 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(), locals);
|
||||
naRef result = naCall(_context, code, naNil(), naNil(), naNil());
|
||||
if(!naGetError(_context)) return true;
|
||||
logError();
|
||||
return false;
|
||||
|
|
|
@ -15,9 +15,18 @@ public:
|
|||
|
||||
virtual bool handleCommand(const SGPropertyNode* arg);
|
||||
|
||||
// Simple hook to run arbitrary source code. Returns a bool to
|
||||
// indicate successful execution. Does *not* return any Nasal
|
||||
// values, because handling garbage-collected objects from C space
|
||||
// is deep voodoo and violates the "simple hook" idea.
|
||||
bool parseAndRun(const char* sourceCode);
|
||||
|
||||
// Implementation of the settimer extension function
|
||||
void setTimer(naRef args);
|
||||
|
||||
// Returns a ghost wrapper for the current _cmdArg
|
||||
naRef cmdArgGhost();
|
||||
|
||||
private:
|
||||
//
|
||||
// FGTimer subclass for handling Nasal timer callbacks.
|
||||
|
@ -31,15 +40,22 @@ private:
|
|||
FGNasalSys* nasal;
|
||||
};
|
||||
|
||||
void loadPropertyScripts();
|
||||
void initModule(const char* moduleName, const char* fileName,
|
||||
const char* src, int len);
|
||||
void readScriptFile(SGPath file, const char* lib);
|
||||
void hashset(naRef hash, const char* key, naRef val);
|
||||
void logError();
|
||||
naRef parse(const char* filename, const char* buf, int len);
|
||||
naRef genPropsModule();
|
||||
naRef propNodeGhost(SGPropertyNode* handle);
|
||||
|
||||
naContext _context;
|
||||
naRef _globals;
|
||||
naRef _timerHash;
|
||||
|
||||
SGPropertyNode* _cmdArg;
|
||||
|
||||
int _nextTimerHashKey;
|
||||
|
||||
public:
|
||||
|
|
230
src/Scripting/nasal-props.cxx
Normal file
230
src/Scripting/nasal-props.cxx
Normal file
|
@ -0,0 +1,230 @@
|
|||
#include <simgear/nasal/nasal.h>
|
||||
#include <simgear/props/props.hxx>
|
||||
|
||||
#include <Main/globals.hxx>
|
||||
|
||||
#include "NasalSys.hxx"
|
||||
|
||||
// Implementation of a Nasal wrapper for the SGPropertyNode class,
|
||||
// using the Nasal "ghost" (er... Garbage collection Handle for
|
||||
// OutSide Thingy) facility.
|
||||
//
|
||||
// Note that these functions appear in Nasal with prepended
|
||||
// underscores. They work on the low-level "ghost" objects and aren't
|
||||
// intended for use from user code, but from Nasal code you will find
|
||||
// in props.nas. That is where the Nasal props.Node class is defined,
|
||||
// which provides a saner interface along the lines of SGPropertyNode.
|
||||
|
||||
static void propNodeGhostDestroy(void* ghost)
|
||||
{
|
||||
SGPropertyNode_ptr* prop = (SGPropertyNode_ptr*)ghost;
|
||||
delete prop;
|
||||
}
|
||||
|
||||
naGhostType PropNodeGhostType = { propNodeGhostDestroy };
|
||||
|
||||
static naRef propNodeGhostCreate(naContext c, SGPropertyNode* n)
|
||||
{
|
||||
SGPropertyNode_ptr* ghost = new SGPropertyNode_ptr(n);
|
||||
return naNewGhost(c, &PropNodeGhostType, ghost);
|
||||
}
|
||||
|
||||
naRef FGNasalSys::propNodeGhost(SGPropertyNode* handle)
|
||||
{
|
||||
return propNodeGhostCreate(_context, handle);
|
||||
}
|
||||
|
||||
#define NASTR(s) s ? naStr_fromdata(naNewString(c),(char*)(s),strlen(s)) : naNil()
|
||||
|
||||
//
|
||||
// Standard header for the extension functions. It turns the "ghost"
|
||||
// found in arg[0] into a SGPropertyNode_ptr*, and then "unwraps" the
|
||||
// vector found in the second argument into a normal-looking args
|
||||
// array. This allows the Nasal handlers to do things like:
|
||||
// Node.getChild = func { _getChild(me.ghost, arg) }
|
||||
//
|
||||
#define NODEARG() \
|
||||
naRef ghost = naVec_get(args, 0); \
|
||||
SGPropertyNode_ptr* node = (SGPropertyNode_ptr*)naGhost_ptr(ghost); \
|
||||
if(!node || naGhost_type(ghost) != &PropNodeGhostType) \
|
||||
return naNil(); \
|
||||
if(naVec_size(args) > 1) { \
|
||||
args = naVec_get(args, 1); \
|
||||
if(!naIsVector(args)) return naNil(); \
|
||||
} else { args = naNil(); }
|
||||
|
||||
static naRef f_getType(naContext c, naRef args)
|
||||
{
|
||||
NODEARG();
|
||||
char* t = "unknown";
|
||||
switch((*node)->getType()) {
|
||||
case SGPropertyNode::NONE: t = "NONE"; break;
|
||||
case SGPropertyNode::ALIAS: t = "ALIAS"; break;
|
||||
case SGPropertyNode::BOOL: t = "BOOL"; break;
|
||||
case SGPropertyNode::INT: t = "INT"; break;
|
||||
case SGPropertyNode::LONG: t = "LONG"; break;
|
||||
case SGPropertyNode::FLOAT: t = "FLOAT"; break;
|
||||
case SGPropertyNode::DOUBLE: t = "DOUBLE"; break;
|
||||
case SGPropertyNode::STRING: t = "STRING"; break;
|
||||
case SGPropertyNode::UNSPECIFIED: t = "UNSPECIFIED"; break;
|
||||
}
|
||||
return NASTR(t);
|
||||
}
|
||||
|
||||
static naRef f_getName(naContext c, naRef args)
|
||||
{
|
||||
NODEARG();
|
||||
return NASTR((*node)->getName());
|
||||
}
|
||||
|
||||
static naRef f_getIndex(naContext c, naRef args)
|
||||
{
|
||||
NODEARG();
|
||||
return naNum((*node)->getIndex());
|
||||
}
|
||||
|
||||
static naRef f_getValue(naContext c, naRef args)
|
||||
{
|
||||
NODEARG();
|
||||
switch((*node)->getType()) {
|
||||
case SGPropertyNode::BOOL: case SGPropertyNode::INT:
|
||||
case SGPropertyNode::LONG: case SGPropertyNode::FLOAT:
|
||||
case SGPropertyNode::DOUBLE:
|
||||
return naNum((*node)->getDoubleValue());
|
||||
case SGPropertyNode::STRING:
|
||||
case SGPropertyNode::UNSPECIFIED:
|
||||
return NASTR((*node)->getStringValue());
|
||||
}
|
||||
return naNil();
|
||||
}
|
||||
|
||||
static naRef f_setValue(naContext c, naRef args)
|
||||
{
|
||||
NODEARG();
|
||||
naRef val = naVec_get(args, 0);
|
||||
if(naIsString(val)) (*node)->setStringValue(naStr_data(val));
|
||||
else (*node)->setDoubleValue(naNumValue(val).num);
|
||||
return naNil();
|
||||
}
|
||||
|
||||
static naRef f_setIntValue(naContext c, naRef args)
|
||||
{
|
||||
NODEARG();
|
||||
int iv = (int)naNumValue(naVec_get(args, 0)).num;
|
||||
(*node)->setIntValue(iv);
|
||||
return naNil();
|
||||
}
|
||||
|
||||
static naRef f_setBoolValue(naContext c, naRef args)
|
||||
{
|
||||
NODEARG();
|
||||
naRef val = naVec_get(args, 0);
|
||||
(*node)->setBoolValue(naTrue(val) ? true : false);
|
||||
return naNil();
|
||||
}
|
||||
|
||||
static naRef f_setDoubleValue(naContext c, naRef args)
|
||||
{
|
||||
NODEARG();
|
||||
(*node)->setDoubleValue(naNumValue(naVec_get(args, 0)).num);
|
||||
return naNil();
|
||||
}
|
||||
|
||||
static naRef f_getParent(naContext c, naRef args)
|
||||
{
|
||||
NODEARG();
|
||||
SGPropertyNode* n = (*node)->getParent();
|
||||
if(!n) return naNil();
|
||||
return propNodeGhostCreate(c, n);
|
||||
}
|
||||
|
||||
static naRef f_getChild(naContext c, naRef args)
|
||||
{
|
||||
NODEARG();
|
||||
naRef child = naVec_get(args, 0);
|
||||
if(!naIsString(child)) return naNil();
|
||||
SGPropertyNode* n = (*node)->getChild(naStr_data(child));
|
||||
if(!n) return naNil();
|
||||
return propNodeGhostCreate(c, n);
|
||||
}
|
||||
|
||||
static naRef f_getChildren(naContext c, naRef args)
|
||||
{
|
||||
NODEARG();
|
||||
naRef result = naNewVector(c);
|
||||
if(naIsNil(args) || naVec_size(args) == 0) {
|
||||
// Get all children
|
||||
for(int i=0; i<(*node)->nChildren(); i++)
|
||||
naVec_append(result, propNodeGhostCreate(c, (*node)->getChild(i)));
|
||||
} else {
|
||||
// Get all children of a specified name
|
||||
naRef name = naVec_get(args, 0);
|
||||
if(!naIsString(name)) return naNil();
|
||||
vector<SGPropertyNode_ptr> children
|
||||
= (*node)->getChildren(naStr_data(name));
|
||||
for(int i=0; i<children.size(); i++)
|
||||
naVec_append(result, propNodeGhostCreate(c, children[i]));
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
static naRef f_removeChild(naContext c, naRef args)
|
||||
{
|
||||
NODEARG();
|
||||
naRef child = naVec_get(args, 0);
|
||||
naRef index = naVec_get(args, 1);
|
||||
if(!naIsString(child) || !naIsNum(index)) return naNil();
|
||||
(*node)->removeChild(naStr_data(child), (int)index.num);
|
||||
return naNil();
|
||||
}
|
||||
|
||||
static naRef f_getNode(naContext c, naRef args)
|
||||
{
|
||||
NODEARG();
|
||||
naRef path = naVec_get(args, 0);
|
||||
bool create = naTrue(naVec_get(args, 1));
|
||||
if(!naIsString(path)) return naNil();
|
||||
SGPropertyNode* n = (*node)->getNode(naStr_data(path), create);
|
||||
return propNodeGhostCreate(c, n);
|
||||
}
|
||||
|
||||
static naRef f_new(naContext c, naRef args)
|
||||
{
|
||||
return propNodeGhostCreate(c, new SGPropertyNode());
|
||||
}
|
||||
|
||||
static naRef f_globals(naContext c, naRef args)
|
||||
{
|
||||
return propNodeGhostCreate(c, globals->get_props());
|
||||
}
|
||||
|
||||
struct {
|
||||
naCFunction func;
|
||||
char* name;
|
||||
} propfuncs[] = {
|
||||
{ f_getType, "_getType" },
|
||||
{ f_getName, "_getName" },
|
||||
{ f_getIndex, "_getIndex" },
|
||||
{ f_getValue, "_getValue" },
|
||||
{ f_setValue, "_setValue" },
|
||||
{ f_setIntValue, "_setIntValue" },
|
||||
{ f_setBoolValue, "_setBoolValue" },
|
||||
{ f_setDoubleValue, "_setDoubleValue" },
|
||||
{ f_getParent, "_getParent" },
|
||||
{ f_getChild, "_getChild" },
|
||||
{ f_getChildren, "_getChildren" },
|
||||
{ f_removeChild, "_removeChild" },
|
||||
{ f_getNode, "_getNode" },
|
||||
{ f_new, "_new" },
|
||||
{ f_globals, "_globals" },
|
||||
{ 0, 0 }
|
||||
};
|
||||
|
||||
naRef FGNasalSys::genPropsModule()
|
||||
{
|
||||
naRef namespc = naNewHash(_context);
|
||||
for(int i=0; propfuncs[i].name; i++)
|
||||
hashset(namespc, propfuncs[i].name,
|
||||
naNewFunc(_context, naNewCCode(_context, propfuncs[i].func)));
|
||||
return namespc;
|
||||
}
|
Loading…
Add table
Reference in a new issue