Using Nasal with FlightGear

This document is a tutorial on how to interface Nasal scripts with FlightGear. It is not an introduction to the Nasal language itself. For that, see Andy's Nasal website at http://www.plausible.org/nasal. The information there is sparse, but you should have it ready for reference while reading this document.

Basic Nasal/FlightGear Integration

Calling Nasal from Configuration File Bindings

Nasal scripts can be used as FGBinding objects, and can therefore appear anywhere in a configuration file (keyboard, mouse and joystick bindings, etc...) that accepts a <binding> tag. The relevant command type is "nasal", and you place your Nasal code inside of the <script> tag:
<binding>
 <command>nasal</command>
 <script>
  print("Binding Invoked!");
 </script>
</binding>

The code above invokes the print() function. This is a simple extension function that simply prints out its arguments, in order, to the FlightGear console as a single-line log entry. It is useful for debugging, but little else.

Some command have SGPropertyNode arguments. This argument is available as a props.Node object and is returned from the built-in cmdarg() function. See below for full documentation, but as an example the following "joystick axis" binding will print the current axis value to the console:

<binding>
 <command>nasal</command>
 <script>print(cmdarg().getNode("value").getValue());</script>
</binding>

Note that the current implementation parses the Nasal code inside the <script> tag each time it is run. This means that you should avoid placing large code blocks inside a <script> tag for performance reasons. It also means that any local variables you set inside the script will be lost the next time it is run. See the discussion about namespaces and Nasal source files below for more information.

Setting and Inspecting FlightGear Properties

getprop() and setprop()

You can use getprop() and setprop() functions to interact with the global property tree. They work as you would expect. For example, the following Nasal code copies the value of the "/sim/foo" property to the "/sim/bar" property:

setprop("/sim/bar", getprop("/sim/foo"));

Note that Nasal's notion of "type" is coarser than that of the SGPropertyNode class. Numers and strings come out of getprop() as Nasal scalars, of course, and boolean properties are converted to a numeric 1 or 0 by getprop(). But all non-string values passed to setprop() become doubles in the property tree. The props.Node interface (see below) provides the ability to set explicit types in the property tree.

A useful feature of getprop and setprop that you should be aware of is that they both accept variable numbers of arguments. These are concatenated together to form a property tree path internally within the function. This avoids the need for extensive string concatenation in the script for the common case where you are acting on a variable property root. That is, you can do:

ThisAircraft = "/sim/my/aircraft/properties";
setprop(ThisAircraft, "crashed", 0);
to set the property "/sim/my/aircraft/properties/crashed" to false. This feature is useful for writing Nasal functions that will work on parameterized property trees.

The props module

Think of the getprop() and setprop() functions as equivalents of the fgGet*() and fgSet*() C++ functions. They provide simple and easy access to the global property tree.

For some situations, however, you need finer control over the property nodes. For these situations you can use the props module, which provides a Node class similar to the SimGear SGPropertyNode class. The global tree is available as a Node object named props.globals. You can use the getNode() method on it to retrieve an arbitrary sub-node. The children of any given node can be inspected with getChild() and getChildren() methods. And of course its name, index, and value can be accessed with appropriate methods. See the reference below for complete documentation.

As seen above in the discussion on fgcommand(), you can also create new, "rootless" Node objects using props.Node.new().

The most powerful method on a Node object is setValues(). This takes a Nasal hash table as its only argument, and initializes properties under the node using the key/value pairs in the hash. This works with vectors (i.e. indexed properties) and recursively, essentially making a deep copy of a Nasal object in the property tree.

For debugging (and amusement) purposes, the props modules also defines a dump() function which recursively prints the state of a property node to the console. Try props.dump(props.globals) to see it walk the entire tree.

Invoking FlightGear Commands from Nasal

Just as Nasal code can be run as a command binding from the property tree, existing FlightGear commands can be invoked by Nasal code using the fgcommand() function. The first argument to this function is a string, equivalent to what you would place inside the <command> tag in a property binding. The second argument specifies the property tree that will be passed as the "arguments" to the command. It can be a either a string specifying a path in the global property tree or a props.Node object. Example:

# Use a temporary property in the global tree
ShowDialog = func {
    setprop("/nasal/tmp/dialog-args/dialog-name", arg[0];
    fgcommand("dialog-show", "/nasal/tmp/dialog-args");
}

# Does the same thing, but with a rootless Node object
ShowDialog = func {
    fgcommand("dialog-show", props.Node.new({dialog-name : arg[0]});
}
These both define a ShowDialog() function, which pops up a named dialog from the /gui/dialogs directory. Calling ShowDialog("autopilot") will therefore pop up the autopilot dialog just as if it had been bound to a key.

The first variant uses a "temporary" property tree in the global property space to hold the arguments. The second creates a new props.Node object instead. Note that props.Node.new() accepts a hash table as its argument and uses that to initialize the returned property tree.

Writing extended Nasal code in source files

Nasal is a "real" language, with real namespaces and modules. What "really" happens when you run a script binding is that the script is treated as a function body and bound (lexically, in the functional programming sense) to a single global namespace. It is as if it were enclosed in a func { ... } expression and executed inside a "file" containing all the global symbols.

Some symbols in the global namespace are built-in extension functions, like the print/getprop/setprop we have already seen. Others are objects (or hash tables -- they are the same thing in Nasal). These objects act as namespaces and can contain code of their own. One such example is the math library. The built-in math functions live in their own namespace and are accessible as math.sin(), math.cos(), etc...

The global namespace itself is available as a module named "globals". This allows you to create new symbols in the global namespace if you desire (be careful!) and to otherwise inspect its contents. It's just a hash table, after all. The following code will print all the symbols found in the global namespace:

print("These are the symbols found in the global namespace:");
foreach(i; keys(globals)) { print("  ", i); }

You can write your own modules, too. The mechanism is very simple: merely create a file with a ".nas" extension in the Nasal directory of your FlightGear base package. FlightGear will read, parse and execute these files during initialization, and create a module of the same name for use by your scripts. So you can write, say, a "mouse.nas" script. Functions defined therein are available to your script bindings (and any other nasal code on the system) as members of the global "mouse" object. So you can define bindings that do things like mouse.handleXAxis(offset) to call functions defined in the mouse.nas file (remember that "offset" is an automatically initialized variable containing the binding's offset argument).

Including Nasal Code from Configuration Files

Nasal modules can also be imported from the property tree at initialization. This is useful for applications like aircraft-specific scripts that need to be loaded only when that aircraft is active. The usage is simple: the Nasal interpreter creates a module for every property node child of "/nasal" that it finds at initialization time. Example:

<nasal>
 <c172>
  <file>Aircraft/c172/c172.nas</file>
 </c172>
</nasal>
This creates a module named "c172" and loads the contents of the Aircraft/c172/c172.nas file into it. The module name is, by default, the same as the property node. But this can be overridden with the <module> tag. This trick can be useful if you need to load extra script source into a previously-initialized module.

You can also write literal Nasal scripts inside the property files by including it in a <script> tag. This sample uses the <module> tag to add an extra function to the math library.

<nasal>
 <c172-tmp1> <!-- Use a unique, dummy name -->
  <module>math</module>
  <script><[CDATA[

   # The math library doesn't include this, because Andy is a pedant
   # and thinks it's dangerous.  But the c172 code just *has* to have
   # it.
   atan = func { return atan2(arg[0], 1) }

  ]]></script>
 </c172-tmp1>
</nasal>
Note the use of a CDATA declaration. This is required to properly escape XML special characters like "<". As it happens, this code doesn't use them. But the CDATA is good practice nonetheless.

Function Reference

These are the built-in extension functions available to all Nasal code in FlightGear. Be sure to examine the core Nasal documentation at the Nasal site as well. Only FlightGear-specific library code is documented here:

Global Functions

rand()
Returns a random number in the range [0:1) (that is, 0.0 is a possible return value, but 1.0 is not).
getprop()
The arguments are concatenated to form a path to a global property node. Returns the value of that node, or nil if it does not exist.
setprop()
The final argument specifies a value to set. The remaining arguments are concatenated to form a property path as in getprop().
print()
The arguments are printed, in order, to the FlightGear console. A newline is appended by the logging code, none is required.
fgcommand()
The first argument is a string specifying a FlightGear command to execute (e.g. "show-dialog"). The second is a property sub-tree (either a global path string or a props.Node object) which will be passed to the command as arguments.
settimer()
The first argument is a Nasal expression which evaluates to a Nasal function object (it can be either a symbol name for a function or a literal func { ... } expression. The second argument is a (floating point) number specifying a delta time in seconds. Some time after that delta time has elapsed, the specified function will be invoked. Exact timing will depend on the frame rate of the simulator.
interpolate()
The first argument specifies a property. It can be either a string representing a global property name or a props.Node object. The remaining arguments specify pairs of value/delta-time numbers. The property is interpolated smoothly from its current value to the new value over the specified time delta, in seconds. Multiple value pairs can be used to indicate successive values or to acheive a piecewise linear approximation to a non-linear function. This function cancels any preexisting interpolation for that property, so interpolate("/sim/countdown", 0, 0) has the effect of cancelling interpolation of "/sim/countdown" and setting its value to zero.

Property Module

Node
The props.Node class wraps a SGPropertyNode object, either in or outside of the global property tree. It supports the following methods:
getType()
Returns the "type" of the SGPropertyNode object. The return value is a string; one of: NONE, ALIAS, BOOL, INT, LONG, FLOAT, DOUBLE, STRING or UNSPECIFIED.
getName()
Returns the name of the property node.
getIndex()
Returns the child index of the property node.
getValue()
Returns the current value of the node, or nil if it has none.
setValue()
Sets the current value as either a string or a double, depending on the internal type of the argument.
setIntValue()
Sets the current value, forcing the type to INT
setBoolValue()
Sets the current value, forcing the type to BOOL
setDoubleValue()
Sets the current value, forcing the type to DOUBLE
getParent()
Returns a Node object representing this node's parent, or nil if it has none.
getChild()
Returns a named child, or nil if it does not exist. If multiple children with that name exist, returns the one with an index of zero.
getChildren()
Returns a vector containing all the node's children.
removeChild()
Removes a child by name (first argument) and index (second argument).
getNode()
Returns a Node specified by its "relative path" to this node, or nil if none exists. The optional second argument, if true, causes the node to be created if it does not exist.
setValues()
Takes a hash as argument, and sets all the key/value pairs in the hash as property subnodes of the object. This works recursively, with sub-hashes and vectors; thus making a deep copy of the Nasal hash in the property tree.
props.Node.new()
Static "constructor" function returning a new, rootless Node object. Takes a hash argument to initialize the new node via setValues().
props.globals
This is a Node object representing the root of the global property tree; the Nasal equivalent of globals->get_props()
props.dump()
This method prints out a "dump" of the state of a single Node object and all of its children to the console. Very useful for debugging and exploration.

Integrating C++ code and Nasal

Calling Nasal from C++

The FGNasalSys object has a parseAndRun() method to which you can pass arbitrary Nasal source code for immediate execution:

FGNasalSys n = (FGNasalSys*)globals->get_subsystem("nasal");
if(! n->parseAndRun("print('This script was called from C++!')")) 
  SG_LOG(SG_GENERAL, SG_ALERT, "My Nasal code failed :(");

You can also use parseScript() to get a pointer to a FGNasalScript object. This object supports a call() method which you can use to invoke the script later on, at a time of your choosing. If you will be invoking the script multiple times, this mechanism can be more efficient because it avoids the parsing and code generation overhead for the successive calls.

FGNasalSys n = (FGNasalSys*)globals->get_subsystem("nasal");
FGNasalScript* script = n->parseScript("print('Spam!')")) 
if(!script) SG_LOG(SG_GENERAL, SG_ALERT, "My Nasal code failed :(");

...

for(int i=0; i<1000; i++)
    script->call();       // Spam the console

Note that there is no way to inspect the return value of the function that you called. It simply returns a boolean indicating successful execution. Handling of "native" Nasal data structures has to be done via the Nasal extension API. See below.

Calling C++ from Nasal

You have three options for invoking C++ code from Nasal. The first two take advantage of pre-existing FlightGear mechanisms for registering "callback" handlers for specific events.

If your task is sufficiently general, you should consider defining it as a new FGCommand using the existing interface. This can be invoked efficiently from both Nasal code (using the fgcommand() function) and existing property bindings, and is very easy to do. Simply define a handler function which takes a property tree as an argument and returns a bool (to indicate successful execution), and register it during initialization with the global command manager:

// Define your handler function:
bool my_new_command(SGPropertyNode* arg) { ... }

    ...
    // And register it in your initialization code:
    globals->get_commands()->addCommand("my-new-command", my_new_command);
    ...

This mechanism works well when your C++ code is a "global" function that you will want to call from many locations with potentially differing data. For some applications, however, you want to register a handler that will be called only by code involved with computations on a single data set.

For this, there is the property listener interface. You can create a subclass of SGPropertyChangeListener which implements the valueChange, childAdded and/or childRemoved methods and associate it with a specific property node in the global tree. You can then "invoke" this handler from Nasal code (or from anywhere else) by simply setting the property value.

I haven't tested the property listener interface, and it requires somewhat more typing to implement; so I will include no example here. It is also rather rarely used by existing FlightGear code (the property picker GUI is the only significant application I could find).

Extending Nasal

This is the third mechanism for invoking C++ (strictly C, in this case) code from Nasal. This is the API you must use if you want to inspect and/or modify Nasal data structures, or create a function object that will be visible to Nasal scripts as a callable function. Unfortunately, there really isn't space here to document this API fully. For now, examin the nasal.h header which defines it, and the lib.c and mathlib.c source files which implement the existing built-in library functions. The FlightGear-specific extension functions in NasalSys.cxx and nasal-props.cxx are also good examples.

But for most purposes, consider the first two mechanisms instead. FlightGear's general inter-module communication mechanism is the property tree, which is exposed from both Nasal and C++ code already. A Nasal extension function, by definition, is useful only to Nasal code. Even worse, data structures definied by a Nasal interface are completely invisible to the C++ world.