]> git.mxchange.org Git - flightgear.git/blob - docs-mini/Nasal.html
Update VS2008 projects : use Boost 1.44.0 available in last 3rd Party archive
[flightgear.git] / docs-mini / Nasal.html
1 <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//EN" "http://www.w3.org/TR/html4/strict.dtd">
2 <html>
3 <head>
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">
7 </head>
8 <body>
9
10 <h1>Using Nasal with FlightGear</h1>
11
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.
19
20 <h2>Basic Nasal/FlightGear Integration</h2>
21
22 <h3>Calling Nasal from Configuration File Bindings</h3>
23
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>&lt;binding&gt;</code> tag.
27 The relevant command type is "nasal", and you place your Nasal code
28 inside of the <code>&lt;script&gt;</code> tag:
29
30 <pre>
31 &lt;binding&gt;
32  &lt;command&gt;nasal&lt;/command&gt;
33  &lt;script&gt;
34   print("Binding Invoked!");
35  &lt;/script&gt;
36 &lt;/binding&gt;
37 </pre>
38
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.
43
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:
49
50 <pre>
51 &lt;binding&gt;
52  &lt;command&gt;nasal&lt;/command&gt;
53  &lt;script&gt;print(cmdarg().getNode("value").getValue());&lt;/script&gt;
54 &lt;/binding&gt;
55 </pre>
56
57 <p>Note that the current implementation parses the Nasal code inside
58 the <code>&lt;script&gt;</code> tag each time it is run.  This means
59 that you should avoid placing large code blocks inside a
60 <code>&lt;script&gt;</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.
64
65 <h3>Setting and Inspecting FlightGear Properties</h3>
66
67 <h4>getprop() and setprop()</h4>
68
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:
73
74 <pre>setprop("/sim/bar", getprop("/sim/foo"));</pre>
75
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.
84
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:
91
92 <pre>
93 ThisAircraft = "/sim/my/aircraft/properties";
94 setprop(ThisAircraft, "crashed", 0);
95 </pre>
96
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.
100
101 <h4>The props module</h4>
102
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.
107
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.
118
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>.
122
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.
129
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.
134
135 <h3>Invoking FlightGear Commands from Nasal</h3>
136
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>&lt;command&gt;</code> tag in a property binding.
142
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.
146 Example:
147
148 <pre>
149 # Use a temporary property in the global tree
150 ShowDialog = func {
151     setprop("/nasal/tmp/dialog-args/dialog-name", arg[0];
152     fgcommand("dialog-show", "/nasal/tmp/dialog-args");
153 }
154
155 # Does the same thing, but with a rootless Node object
156 ShowDialog = func {
157     fgcommand("dialog-show", props.Node.new({dialog-name : arg[0]});
158 }
159 </pre>
160
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.
165
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.
171
172 <h3>Writing extended Nasal code in source files</h3>
173
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.
180
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...
188
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:
194
195 <pre>
196 print("These are the symbols found in the global namespace:");
197 foreach(i; keys(globals)) { print("  ", i); }
198 </pre>
199
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).
211
212 <h3>Including Nasal Code from Configuration Files</h3>
213
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:
220
221 <pre>
222 &lt;nasal&gt;
223  &lt;c172&gt;
224   &lt;file&gt;Aircraft/c172/c172.nas&lt;/file&gt;
225  &lt;/c172&gt;
226 &lt;/nasal&gt;
227 </pre>
228
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>&lt;module&gt;</code> tag.  This trick can be useful if you need
233 to load extra script source into a previously-initialized module.
234
235 <p>You can also write literal Nasal scripts inside the property files
236 by including it in a <code>&lt;script&gt;</code> tag.  This sample
237 uses the <code>&lt;module&gt;</code> tag to add an extra function to
238 the math library.
239
240 <pre>
241 &lt;nasal&gt;
242  &lt;c172-tmp1&gt; &lt;!-- Use a unique, dummy name --&gt;
243   &lt;module&gt;math&lt;/module&gt;
244   &lt;script&gt;&lt;[CDATA[
245
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
248    # it.
249    atan = func { return atan2(arg[0], 1) }
250
251   ]]&gt;&lt;/script&gt;
252  &lt;/c172-tmp1&gt;
253 &lt;/nasal&gt;
254 </pre>
255
256 Note the use of a CDATA declaration.  This is required to properly
257 escape XML special characters like "<code>&lt;</code>".  As it
258 happens, this code doesn't use them.  But the CDATA is good practice
259 nonetheless.
260
261 <h3>Function Reference</h3>
262
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:
269
270 <h4>Global Functions</h4>
271
272 <dl>
273
274 <dt>rand()
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).
277
278 <dt>getprop()
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
281     not exist.
282
283 <dt>setprop()
284 <dd>The final argument specifies a value to set.  The remaining
285     arguments are concatenated to form a property path as in
286     getprop().
287
288 <dt>print()
289 <dd>The arguments are printed, in order, to the FlightGear console.
290     A newline is appended by the logging code, none is
291     required.
292
293 <dt>fgcommand()
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.
298
299 <dt>settimer()
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&nbsp;{&nbsp;...&nbsp;}</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.
307
308 <dt>interpolate()
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",&nbsp;0,&nbsp;0)</code> has the
319     effect of cancelling interpolation of "/sim/countdown" and setting
320     its value to zero.
321 </dl>
322
323 <h4>Property Module</h4>
324
325 <dl>
326 <dt>Node
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
329     following methods:
330 <dl>
331 <dt>getType()
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.
335 <dt>getName()
336 <dd>Returns the name of the property node.
337 <dt>getIndex()
338 <dd>Returns the child index of the property node.
339 <dt>getValue()
340 <dd>Returns the current value of the node, or nil if it has none.
341 <dt>setValue()
342 <dd>Sets the current value as either a string or a double, depending
343 on the internal type of the argument.
344 <dt>setIntValue()
345 <dd>Sets the current value, forcing the type to INT
346 <dt>setBoolValue()
347 <dd>Sets the current value, forcing the type to BOOL
348 <dt>setDoubleValue()
349 <dd>Sets the current value, forcing the type to DOUBLE
350 <dt>getParent()
351 <dd>Returns a Node object representing this node's parent, or nil if
352 it has none.
353 <dt>getChild()
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.
356 <dt>getChildren()
357 <dd>Returns a vector containing all the node's children.
358 <dt>removeChild()
359 <dd>Removes a child by name (first argument) and index (second argument).
360 <dt>getNode()
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.
364 <dt>setValues()
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
368 the property tree.
369 </dl>
370
371 <dt>props.Node.new()
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().
375
376 <dt>props.globals
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>
380
381 <dt>props.dump()
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.
385
386 </dl>
387
388 <h2>Integrating C++ code and Nasal</h2>
389
390 <h3>Calling Nasal from C++</h3>
391
392 <p>The FGNasalSys object has a <code>parseAndRun()</code> method to
393 which you can pass arbitrary Nasal source code for immediate
394 execution:
395
396 <pre>
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 :(");
400 </pre>
401
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
408 calls.
409
410 <pre>
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 :(");
414
415 ...
416
417 for(int i=0; i<1000; i++)
418     script->call();       // Spam the console
419 </pre>
420
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.
425
426 <h3>Calling C++ from Nasal</h3>
427
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.
431
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:
439
440 <pre>
441 // Define your handler function:
442 bool my_new_command(SGPropertyNode* arg) { ... }
443
444     ...
445     // And register it in your initialization code:
446     globals->get_commands()->addCommand("my-new-command", my_new_command);
447     ...
448 </pre>
449
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.
455
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.
462
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).
467
468 <h3>Extending Nasal</h3>
469
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.
480
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.
487 </body>
488 </html>