OK, I had some time over the weekend to carry out my threat of hacking
the Nasal interpreter into FlightGear. So now we have another
language to flame about. :)
http://www.plausible.org/andy/fg-nasal-1.0.tar.gz
[This touches SimGear, FlightGear and the base package, so you'll
see source, data and SimGear directories inside the tarball.]
Obviously people are going to have differing opinions on whether this
language is worth pursuing. I've chosen to fight the other
alternatives via relentless integration. This code actually does
useful things. (I said "integrated", by the way, not "debugged". :)
+ Like the existing PSL engine, it can be used to define command
bindings and get and set properties in the global tree.
And there are a few new features:
+ You can call globally defined FlightGear commands, allowing
scriptable control over the GUI, for example. You can even call a
PSL command from a Nasal script if you want. (Amusingly, you
*can't* call a "nasal" command from a Nasal script -- there's only
one context object in the current implementation and you'll corrupt
your call stack. Obviously no one would want to do this, though.)
+ You can write whole files of script code by dropping ".nas" files
into the Nasal directory of the base backage. These are parsed and
interpreted at startup, and can hold any code or data you like.
And becasue Nasal pretends to be a Real Language, it has a few and
advantages (IMHO) over the PSL stuff:
+ It is much richer, semantically. You have vectors and hashes, OOP
syntax, inheritance, functional programming, etc...
+ You can write your code in whole source files, store data in
persistent variables and organize it into modules with real
namespaces. Emacs users even have access to a mostly-working
nasal-mode.el that I hacked together (don't laugh; I basically
blind-copied awk-mode.el and tried to avoid changing the things I
didn't understand).
+ The flip side is that because of the ability to abstract complicated
stuff out into modules, the XML-side configuration can be much
cleaner. The existing keyboard "C" binding looks like this when
written in PSL:
<binding>
<command>script</command>
<script>
int main ()
{
print("Longitude: ", get_property("/position/longitude-deg"), "
deg\n");
print("Latitude: ", get_property("/position/latitude-deg"), "
deg\n");
print("Altitude: ", get_property("/position/altitude-ft"), " ft\n");
}
</script>
</binding>
But shortens to this when you put your code into a test.nas module
(and you don't need the silly main() declaration because Nasal
expressions and statments are grammatically identical; but that's
only a mild PSL misfeature):
<binding>
<command>nasal</command>
<script> test.handleTestKeyEvent() </script>
</binding>
The rest of the world looks very similar. The way that Nasal is
integrated mirrors the PSL engine almost exactly. Neither touches the
core of FlightGear much at all; they just hook a new subsystem into
the global list. SimGear is affected only in that it gets a new
directory and a new library to build.
There is some (admittedly terse) documentation on the language
available at:
http://www.plausible.org/nasal/
http://www.plausible.org/nasal/doc.html
http://www.plausible.org/nasal/sample.nas
Nasal scripts have access to a small standard library of useful data
structure and math functions plus the following FlightGear-specific
ones:
+ print() - Concatenates and writes out its arguments via SG_LOG()
+ getprop() - Fetches a property from the global tree. Will accept a
variable-length list of arguments and append them together to form
a property name. This is so that you can do stuff like:
getprop("/sim/mode/space-wars");
getprop(CurrentGalaxy, "size-parsecs");
+ setprop() - Ditto.
+ fgcommand() - First argument specifies a FlightGear "command" name,
second is the path of a property sub-node where you have placed
the arguments to the command.
Over the next few days, I'm going to try to write some non-trivial
example code (the existing test.nas module is attached to this
message) and maybe code up a few enhancements. Useful future stuff
might be:
+ A timer API, so you can register callbacks for a specified time
and/or interval. Almost the whole ATC subsystem could be based on
such a mechanism, for example.
+ A "threading" API, where a script could run in a loop forever making
blocking calls (waitfor(), or somesuch). This kind of programming
is wasteful and fraught with race conditions, but still a good fit
for some problems (AI, for example).
+ Hooks into more FlightGear subsystems. The GUI module doesn't work
in the main property tree, so it will require just a little bit of
work to integrate. But raw PUI bindings might not be a bad idea
either.
+ A "wrapped object" library for SGPropertyNode, so you can use the
property system more like you do in C++.
+ Lots of new library functions. What's there now is pretty basic:
just core data structures and math functions that can't be otherwise
done in user code.
So anyway, take a look and see what you think.
Andy
#
# Spam the console, just to wake up the user. :)
#
for(i=0; i<1000; i=i+1) {
print("Nasal module 'test' initializing... [", i, "]");
}
#
# Inspect the globals, just to be sure they're all there
#
print("These are the symbols found in the global namespace:");
foreach(i; keys(globals)) { print(" ", i); }
#
# Define a handler for the "C" key (bound in keyboard.xml)
#
handleTestKeyEvent = func {
handler = TestHandlers[TestHandlerIdx];
TestHandlerIdx = TestHandlerIdx + 1;
if(TestHandlerIdx >= size(TestHandlers)) {
TestHandlerIdx = 0;
}
handler();
}
#
# ... which calls each of these functions in rotation
#
WhereAmI = func {
print("Where I am:");
print(" Longitude: ", getprop("/position/longitude-deg"), " deg");
print(" Latitude: ", getprop("/position/latitude-deg"), " deg");
print(" Altitude: ", getprop("/position/altitude-ft"), " ft");
}
PopUp = func {
print("Popping up 'autopilot' dialog");
setprop("/nasal/dialog-args/dialog-name", "autopilot");
fgcommand("dialog-show", "/nasal/dialog-args");
}
## This one doesn't work. I *think* it's a bug with dialog-close...
PopDown = func {
print("Popping down 'autopilot' dialog");
setprop("/nasal/dialog-args/dialog-name", "autopilot");
fgcommand("dialog-close", "/nasal/dialog-args");
}
Pause = func {
print("Pausing FlightGear via Nasal code...");
setprop("/sim/freeze/master", 0);
setprop("/sim/freeze/clock", 0);
setprop("/sim/freeze/replay", 0);
}
Unpause = func {
print("Un-pausing FlightGear via Nasal code...");
setprop("/sim/freeze/master", 1);
setprop("/sim/freeze/clock", 1);
setprop("/sim/freeze/replay", 1);
}
TestHandlerIdx = 0;
TestHandlers = [WhereAmI, Pause, PopUp, PopDown, Unpause];
_______________________________________________
Flightgear-devel mailing list
[EMAIL PROTECTED]
http://mail.flightgear.org/mailman/listinfo/flightgear-devel