1 <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//EN" "http://www.w3.org/TR/html4/strict.dtd">
4 <title>Using Nasal with FlightGear</title>
5 <meta http-equiv="Content-Type" content="text/html; charset=ISO-8859-1">
6 <link rel="stylesheet" href="nasal.css">
10 <h1>Using Nasal with FlightGear</h1>
12 <p>This document is a tutorial on how to interface Nasal scripts
13 with FlightGear. It is not an introduction to the Nasal language
14 itself. For that, see Andy's <a
15 href="http://www.plausible.org/nasal/">Nasal website</a> at <a
16 href="http://www.plausible.org/nasal/">http://www.plausible.org/nasal</a>.
17 The information there is sparse, but you should have it ready for
18 reference while reading this document.
20 <h2>Basic Nasal/FlightGear Integration</h2>
22 <h3>Calling Nasal from Configuration File Bindings</h3>
24 Nasal scripts can be used as FGBinding objects, and can therefore
25 appear anywhere in a configuration file (keyboard, mouse and joystick
26 bindings, etc...) that accepts a <code><binding></code> tag.
27 The relevant command type is "nasal", and you place your Nasal code
28 inside of the <code><script></code> tag:
32 <command>nasal</command>
34 print("Binding Invoked!");
39 <p>The code above invokes the <code>print()</code> function. This is
40 a simple extension function that simply prints out its arguments, in
41 order, to the FlightGear console as a single-line log entry. It is
42 useful for debugging, but little else.
44 <p>Some command have SGPropertyNode arguments. This argument is
45 available as a <code>props.Node</code> object and is returned from the
46 built-in <code>cmdarg()</code> function. See below for full
47 documentation, but as an example the following "joystick axis" binding
48 will print the current axis value to the console:
52 <command>nasal</command>
53 <script>print(cmdarg().getNode("value").getValue());</script>
57 <p>Note that the current implementation parses the Nasal code inside
58 the <code><script></code> tag each time it is run. This means
59 that you should avoid placing large code blocks inside a
60 <code><script></code> tag for performance reasons. It also
61 means that any local variables you set inside the script will be lost
62 the next time it is run. See the discussion about namespaces and
63 Nasal source files below for more information.
65 <h3>Setting and Inspecting FlightGear Properties</h3>
67 <h4>getprop() and setprop()</h4>
69 <p>You can use <code>getprop()</code> and <code>setprop()</code>
70 functions to interact with the global property tree. They work as you
71 would expect. For example, the following Nasal code copies the value
72 of the "/sim/foo" property to the "/sim/bar" property:
74 <pre>setprop("/sim/bar", getprop("/sim/foo"));</pre>
76 <p>Note that Nasal's notion of "type" is coarser than that of the
77 SGPropertyNode class. Numers and strings come out of
78 <code>getprop()</code> as Nasal scalars, of course, and boolean
79 properties are converted to a numeric 1 or 0 by
80 <code>getprop()</code>. But <b>all</b> non-string values passed to
81 <code>setprop()</code> become doubles in the property tree. The
82 props.Node interface (see below) provides the ability to set explicit
83 types in the property tree.
85 <p>A useful feature of getprop and setprop that you should be aware of
86 is that they both accept variable numbers of arguments. These are
87 concatenated together to form a property tree path internally within
88 the function. This avoids the need for extensive string concatenation
89 in the script for the common case where you are acting on a variable
90 property root. That is, you can do:
93 ThisAircraft = "/sim/my/aircraft/properties";
94 setprop(ThisAircraft, "crashed", 0);
97 to set the property "/sim/my/aircraft/properties/crashed" to false.
98 This feature is useful for writing Nasal functions that will work on
99 parameterized property trees.
101 <h4>The props module</h4>
103 <p>Think of the <code>getprop()</code> and <code>setprop()</code>
104 functions as equivalents of the <code>fgGet*()</code> and
105 <code>fgSet*()</code> C++ functions. They provide simple and easy
106 access to the global property tree.
108 <p>For some situations, however, you need finer control over the
109 property nodes. For these situations you can use the props module,
110 which provides a <code>Node</code> class similar to the SimGear
111 <code>SGPropertyNode</code> class. The global tree is available as a
112 <code>Node</code> object named <code>props.globals</code>. You can
113 use the <code>getNode()</code> method on it to retrieve an arbitrary
114 sub-node. The children of any given node can be inspected with
115 <code>getChild()</code> and <code>getChildren()</code> methods. And
116 of course its name, index, and value can be accessed with appropriate
117 methods. See the reference below for complete documentation.
119 <p>As seen above in the discussion on <code>fgcommand()</code>, you
120 can also create new, "rootless" <code>Node</code> objects using
121 <code>props.Node.new()</code>.
123 <p>The most powerful method on a Node object is
124 <code>setValues()</code>. This takes a Nasal hash table as its only
125 argument, and initializes properties under the node using the
126 key/value pairs in the hash. This works with vectors (i.e. indexed
127 properties) and recursively, essentially making a deep copy of a Nasal
128 object in the property tree.
130 <p>For debugging (and amusement) purposes, the <code>props</code>
131 modules also defines a <code>dump()</code> function which recursively
132 prints the state of a property node to the console. Try
133 <code>props.dump(props.globals)</code> to see it walk the entire tree.
135 <h3>Invoking FlightGear Commands from Nasal</h3>
137 <p>Just as Nasal code can be run as a command binding from the
138 property tree, existing FlightGear commands can be invoked by Nasal
139 code using the <code>fgcommand()</code> function. The first argument
140 to this function is a string, equivalent to what you would place
141 inside the <code><command></code> tag in a property binding.
143 The second argument specifies the property tree that will be passed as
144 the "arguments" to the command. It can be a either a string
145 specifying a path in the global property tree or a props.Node object.
149 # Use a temporary property in the global tree
151 setprop("/nasal/tmp/dialog-args/dialog-name", arg[0];
152 fgcommand("dialog-show", "/nasal/tmp/dialog-args");
155 # Does the same thing, but with a rootless Node object
157 fgcommand("dialog-show", props.Node.new({dialog-name : arg[0]});
161 These both define a <code>ShowDialog()</code> function, which pops up
162 a named dialog from the <fgroot>/gui/dialogs directory. Calling
163 <code>ShowDialog("autopilot")</code> will therefore pop up the
164 autopilot dialog just as if it had been bound to a key.
166 <p>The first variant uses a "temporary" property tree in the global
167 property space to hold the arguments. The second creates a new
168 <code>props.Node</code> object instead. Note that
169 <code>props.Node.new()</code> accepts a hash table as its argument and
170 uses that to initialize the returned property tree.
172 <h3>Writing extended Nasal code in source files</h3>
174 <p>Nasal is a "real" language, with real namespaces and modules. What
175 "really" happens when you run a script binding is that the script is
176 treated as a function body and bound (lexically, in the functional
177 programming sense) to a single global namespace. It is as if it were
178 enclosed in a <code>func { ... }</code> expression and executed inside
179 a "file" containing all the global symbols.
181 <p>Some symbols in the global namespace are built-in extension
182 functions, like the print/getprop/setprop we have already seen.
183 Others are objects (or hash tables -- they are the same thing in
184 Nasal). These objects act as namespaces and can contain code of their
185 own. One such example is the math library. The built-in math
186 functions live in their own namespace and are accessible as
187 <code>math.sin()</code>, <code>math.cos()</code>, etc...
189 <p>The global namespace itself is available as a module named
190 "globals". This allows you to create new symbols in the global
191 namespace if you desire (be careful!) and to otherwise inspect its
192 contents. It's just a hash table, after all. The following code will
193 print all the symbols found in the global namespace:
196 print("These are the symbols found in the global namespace:");
197 foreach(i; keys(globals)) { print(" ", i); }
200 <p>You can write your own modules, too. The mechanism is very simple:
201 merely create a file with a ".nas" extension in the Nasal directory of
202 your FlightGear base package. FlightGear will read, parse and execute
203 these files during initialization, and create a module of the same
204 name for use by your scripts. So you can write, say, a "mouse.nas"
205 script. Functions defined therein are available to your script
206 bindings (and any other nasal code on the system) as members of the
207 global "mouse" object. So you can define bindings that do things like
208 <code>mouse.handleXAxis(offset)</code> to call functions defined in
209 the mouse.nas file (remember that "offset" is an automatically
210 initialized variable containing the binding's offset argument).
212 <h3>Including Nasal Code from Configuration Files</h3>
214 <p>Nasal modules can also be imported from the property tree at
215 initialization. This is useful for applications like
216 aircraft-specific scripts that need to be loaded only when that
217 aircraft is active. The usage is simple: the Nasal interpreter
218 creates a module for every property node child of "/nasal" that it
219 finds at initialization time. Example:
224 <file>Aircraft/c172/c172.nas</file>
229 This creates a module named "c172" and loads the contents of the
230 Aircraft/c172/c172.nas file into it. The module name is, by default,
231 the same as the property node. But this can be overridden with the
232 <code><module></code> tag. This trick can be useful if you need
233 to load extra script source into a previously-initialized module.
235 <p>You can also write literal Nasal scripts inside the property files
236 by including it in a <code><script></code> tag. This sample
237 uses the <code><module></code> tag to add an extra function to
242 <c172-tmp1> <!-- Use a unique, dummy name -->
243 <module>math</module>
244 <script><[CDATA[
246 # The math library doesn't include this, because Andy is a pedant
247 # and thinks it's dangerous. But the c172 code just *has* to have
249 atan = func { return atan2(arg[0], 1) }
251 ]]></script>
256 Note the use of a CDATA declaration. This is required to properly
257 escape XML special characters like "<code><</code>". As it
258 happens, this code doesn't use them. But the CDATA is good practice
261 <h3>Function Reference</h3>
263 <p>These are the built-in extension functions available to all Nasal
264 code in FlightGear. Be sure to examine the <a
265 href="http://www.plausible.org/nasal/doc.html">core Nasal
266 documentation</a> at the <a
267 href="http://www.plausible.org/nasal">Nasal site</a> as well. Only
268 FlightGear-specific library code is documented here:
270 <h4>Global Functions</h4>
275 <dd>Returns a random number in the range [0:1) (that is, 0.0 is a
276 possible return value, but 1.0 is not).
279 <dd>The arguments are concatenated to form a path to a global
280 property node. Returns the value of that node, or nil if it does
284 <dd>The final argument specifies a value to set. The remaining
285 arguments are concatenated to form a property path as in
289 <dd>The arguments are printed, in order, to the FlightGear console.
290 A newline is appended by the logging code, none is
294 <dd>The first argument is a string specifying a FlightGear command to
295 execute (e.g. "show-dialog"). The second is a property sub-tree
296 (either a global path string or a props.Node object) which will be
297 passed to the command as arguments.
300 <dd>The first argument is a Nasal expression which evaluates to a
301 Nasal function object (it can be either a symbol name for a
302 function or a literal <code>func { ... }</code>
303 expression. The second argument is a (floating point) number
304 specifying a delta time in seconds. Some time after that delta
305 time has elapsed, the specified function will be invoked. Exact
306 timing will depend on the frame rate of the simulator.
309 <dd>The first argument specifies a property. It can be either a
310 string representing a global property name or a
311 <code>props.Node</code> object. The remaining arguments specify
312 pairs of value/delta-time numbers. The property is interpolated
313 smoothly from its current value to the new value over the
314 specified time delta, in seconds. Multiple value pairs can be
315 used to indicate successive values or to acheive a piecewise
316 linear approximation to a non-linear function. This function
317 cancels any preexisting interpolation for that property, so
318 <code>interpolate("/sim/countdown", 0, 0)</code> has the
319 effect of cancelling interpolation of "/sim/countdown" and setting
323 <h4>Property Module</h4>
327 <dd>The <code>props.Node</code> class wraps a SGPropertyNode object,
328 either in or outside of the global property tree. It supports the
332 <dd>Returns the "type" of the SGPropertyNode object. The return value
333 is a string; one of: NONE, ALIAS, BOOL, INT, LONG, FLOAT, DOUBLE,
334 STRING or UNSPECIFIED.
336 <dd>Returns the name of the property node.
338 <dd>Returns the child index of the property node.
340 <dd>Returns the current value of the node, or nil if it has none.
342 <dd>Sets the current value as either a string or a double, depending
343 on the internal type of the argument.
345 <dd>Sets the current value, forcing the type to INT
347 <dd>Sets the current value, forcing the type to BOOL
349 <dd>Sets the current value, forcing the type to DOUBLE
351 <dd>Returns a Node object representing this node's parent, or nil if
354 <dd>Returns a named child, or nil if it does not exist. If multiple
355 children with that name exist, returns the one with an index of zero.
357 <dd>Returns a vector containing all the node's children.
359 <dd>Removes a child by name (first argument) and index (second argument).
361 <dd>Returns a Node specified by its "relative path" to this node, or
362 nil if none exists. The optional second argument, if true, causes the
363 node to be created if it does not exist.
365 <dd>Takes a hash as argument, and sets all the key/value pairs in the
366 hash as property subnodes of the object. This works recursively, with
367 sub-hashes and vectors; thus making a deep copy of the Nasal hash in
372 <dd>Static "constructor" function returning a new, rootless
373 <code>Node</code> object. Takes a hash argument to initialize the
374 new node via setValues().
377 <dd>This is a <code>Node</code> object representing the root of the
378 global property tree; the Nasal equivalent of
379 <code>globals->get_props()</code>
382 <dd>This method prints out a "dump" of the state of a single
383 <code>Node</code> object and all of its children to the console.
384 Very useful for debugging and exploration.
388 <h2>Integrating C++ code and Nasal</h2>
390 <h3>Calling Nasal from C++</h3>
392 <p>The FGNasalSys object has a <code>parseAndRun()</code> method to
393 which you can pass arbitrary Nasal source code for immediate
397 FGNasalSys n = (FGNasalSys*)globals->get_subsystem("nasal");
398 if(! n->parseAndRun("print('This script was called from C++!')"))
399 SG_LOG(SG_GENERAL, SG_ALERT, "My Nasal code failed :(");
402 <p>You can also use <code>parseScript()</code> to get a pointer to a
403 <code>FGNasalScript</code> object. This object supports a
404 <code>call()</code> method which you can use to invoke the script
405 later on, at a time of your choosing. If you will be invoking the
406 script multiple times, this mechanism can be more efficient because it
407 avoids the parsing and code generation overhead for the successive
411 FGNasalSys n = (FGNasalSys*)globals->get_subsystem("nasal");
412 FGNasalScript* script = n->parseScript("print('Spam!')"))
413 if(!script) SG_LOG(SG_GENERAL, SG_ALERT, "My Nasal code failed :(");
417 for(int i=0; i<1000; i++)
418 script->call(); // Spam the console
421 <p>Note that there is no way to inspect the return value of the
422 function that you called. It simply returns a boolean indicating
423 successful execution. Handling of "native" Nasal data structures has
424 to be done via the Nasal extension API. See below.
426 <h3>Calling C++ from Nasal</h3>
428 <p>You have three options for invoking C++ code from Nasal. The first
429 two take advantage of pre-existing FlightGear mechanisms for
430 registering "callback" handlers for specific events.
432 <p>If your task is sufficiently general, you should consider defining
433 it as a new FGCommand using the existing interface. This can be
434 invoked efficiently from both Nasal code (using the fgcommand()
435 function) and existing property bindings, and is very easy to do.
436 Simply define a handler function which takes a property tree as an
437 argument and returns a bool (to indicate successful execution), and
438 register it during initialization with the global command manager:
441 // Define your handler function:
442 bool my_new_command(SGPropertyNode* arg) { ... }
445 // And register it in your initialization code:
446 globals->get_commands()->addCommand("my-new-command", my_new_command);
450 <p>This mechanism works well when your C++ code is a "global" function
451 that you will want to call from many locations with potentially
452 differing data. For some applications, however, you want to register
453 a handler that will be called only by code involved with computations
454 on a single data set.
456 <p>For this, there is the property listener interface. You can create
457 a subclass of SGPropertyChangeListener which implements the
458 valueChange, childAdded and/or childRemoved methods and associate it
459 with a specific property node in the global tree. You can then
460 "invoke" this handler from Nasal code (or from anywhere else) by
461 simply setting the property value.
463 <p>I haven't tested the property listener interface, and it requires
464 somewhat more typing to implement; so I will include no example here.
465 It is also rather rarely used by existing FlightGear code (the
466 property picker GUI is the only significant application I could find).
468 <h3>Extending Nasal</h3>
470 <p>This is the third mechanism for invoking C++ (strictly C, in this
471 case) code from Nasal. This is the API you must use if you want to
472 inspect and/or modify Nasal data structures, or create a function
473 object that will be visible to Nasal scripts as a callable function.
474 Unfortunately, there really isn't space here to document this API
475 fully. For now, examin the <code>nasal.h</code> header which defines
476 it, and the <code>lib.c</code> and <code>mathlib.c</code> source files
477 which implement the existing built-in library functions. The
478 FlightGear-specific extension functions in <code>NasalSys.cxx</code>
479 and <code>nasal-props.cxx</code> are also good examples.
481 <p>But for most purposes, consider the first two mechanisms instead.
482 FlightGear's general inter-module communication mechanism is the
483 property tree, which is exposed from both Nasal and C++ code already.
484 A Nasal extension function, by definition, is useful only to Nasal
485 code. Even worse, data structures definied by a Nasal interface are
486 completely invisible to the C++ world.