Nasal scripting language: Difference between revisions

Jump to navigation Jump to search
Line 76: Line 76:


Many short "howto"-style tutorials on Nasal programming can be found in the [[:Category:Nasal|Nasal category]].
Many short "howto"-style tutorials on Nasal programming can be found in the [[:Category:Nasal|Nasal category]].
== Creating new Scripts ==
Nasal scripts need to be plain text files, saved with a *.nas extension.
They can be created and written using a conventional plain text editor like notepad, gedit, kate etc.
But you can also use a programming text editor with syntax lighting support, see [[Howto:Syntax highlighting for Nasal]].
We are also currently planning to add some very basic syntax highlighting support to the built-in [[Nasal Console]] using the new [[Canvas]] system.
=== Aircraft specific Nasal code ===
Generally, aircraft specific Nasal scripts reside in the corresponding aircraft's folder (or a corresponding /Nasal subfolder) where they are usually included by adding a corresponding <nasal> tag to the [[aircraft-set.xml]] file (see [[Writing simple scripts in %22nasal%22|Writing simple scripts in "nasal"]]). Also see the section on [[Nasal scripting language#Namespaces|namespaces]] which contains more specific examples.
=== Instrument specific Nasal code ===
While instrument specific scripts are saved within the instrument's folder (as previously mentioned, Nasal scripts can also be embedded in various other XML files), Nasal scripts driving shared instruments are generally stored in <tt>[[$FG ROOT]]/Aircraft/Generic/</tt>.
=== Nasal code as bindings in XML files ===
Nasal scripts can also be used as "binding" 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:
<syntaxhighlight lang="xml">
<binding>
  <command>nasal</command>
  <script>
  print("Binding Invoked!");
  </script>
</binding>
</syntaxhighlight>
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.
=== System-wide Nasal code ===
Nasal scripts that are not specific to certain aircraft, instruments or other uses, generally reside in the system-wide <tt>[[$FG ROOT]]/Nasal</tt> directory. Scripts that are placed inside this directory (with a *.nas extension) are automatically loaded and run during FlightGear startup.
=== Nasal sub modules ===
As of 06/2011, FlightGear also supports so called Nasal "sub modules" which may reside in their own sub folder under $FG_ROOT/Nasal/ and which provide support for on-demand loading at runtime by toggling properties.
Some advantages are:
* Nasal files can be grouped neatly instead of all scripts being mixed up in a single fgdata/Nasal directory. Grouping makes a lot of sense for modules consisting of several scripts - local weather is the best example.
* Guaranteed loading sequence. Submodules are loaded _after_ the main fgdata/Nasal scripts, so they can rely on all fgdata/Nasal content to be already present. No more need for awkward listener callbacks, just to make sure that basic "props" or "gui" modules are available.
* Finally, users have the option to disable loading modules. Unfortunately, just loading scripts (code/data) into memory already causes certain _run-time_ performance effects - even if the Nasal code was never executed (so even when all listeners/timers were disabled).
Please note that there is  a difference between the _individual_ Nasal files in fgdata/Nasal and files belonging to a common Nasal _module in general (no matter whether loaded at run-time or loaded at start-up using a "<nasal>" tag).
The individual Nasal files in fgdata/Nasal have an own namespace _each_. The namespace get's the name of the Nasal file itself. So if you have a "gui.nas" in the directory, then you can reference a symbol "foo" using "gui.foo".
Nasal modules also have a single namespace. But all files belonging to the module share this _single_ namespace. The name of their namespace is made from its directory (for the run-time loadable modules), or from the specific tag given below the <nasal> XML element, which are often used for a/c specific modules (e.g. <nasal><ufo>...</ufo></nasal> creates the ufo Nasal namespace in ufo-set.xml).
'''So each Nasal file in a new Nasal "module" folder now shares the same namespace.'''
Another important thing to keep in mind is that Nasal sub modules should preferably register a listener to handle initialization, because they are not necessarily loaded by default but rather "on demand". The signal property to listen to will be in /nasal/MODULE_NAME/loaded - for example:
<syntaxhighlight lang="php">
var init = func {
# this is where you put all your startup code
# to ensure that it will only be called
# AFTER all sub modules have been processed
}
_setlistener("/nasal/my_module/loaded", init);
</syntaxhighlight>
This is why you need to wrap everything in a function and register a listener, so that the code is only invoked AFTER ALL sub module files have been LOADED - otherwise, you'll have unresolved references.
As you can see in <tt>[[$FG_ROOT]]/Nasal/local_weather/compat_layer.nas</tt> there's a sub module listener registered that will be fired once ALL source files in the sub module folder have been processed - this is basically the signal to fire off the callback, so that the code can actually start running. The local weather system doesn't use a separate function, but rather an anonymous func: http://gitorious.org/fg/fgdata/blobs/master/Nasal/local_weather/compat_layer.nas#line76
The whole point of this technique is being able to tell when all files have been parsed by Nasal - so that you know that you can actually start running the code, and rely on all symbols being available in the module's namespace.
All sub modules are automatically loaded AFTER $FG_ROOT/Nasal has been processed - so your sub modules only need to register their own "loaded" listener to make sure that the sub module code is only actually run once all files have been processed. Nasal core modules (such as gui.nas io.nas etc) are automatically available to every sub module.
For more information on Nasal sub modules, please see:
* http://www.mail-archive.com/flightgear-devel@lists.sourceforge.net/msg31761.html
* http://www.mail-archive.com/flightgear-devel@lists.sourceforge.net/msg32657.html
* http://www.mail-archive.com/flightgear-devel@lists.sourceforge.net/msg33458.html
=== User specific Nasal scripts ===
It's also possible to put Nasal files into $FG_HOME/Nasal/, that is: ~/.fgfs/Nasal/ on Unix, and %APPDATA%\flightgear.org\Nasal\ on MS Windows. This has the following advantages:
* one doesn't have to mix local extensions with standard files
* one is less likely to lose such local additions when upgrading
* one doesn't need write permission to $FG_ROOT/Nasal/ or
* one doesn't have to become "root" to edit such files
The files are guaranteed to be read after all the files in $FG_ROOT/Nasal/, so one can safely use elements of files like props.nas (props.Node), or globals.nas (setlistener() without leading underscore).
The files are guaranteed to be read in alphabetic order. So, if there are two files where one depends on the other, just name them appropriately.
The contents of each file are added to a namespace derived from the filename. So, all functions and variables of a file ~/.fgfs/nasal/local.nas will be added to nasal namespace "local", and a function test() is globally accessible as local.test().
It's possible to extend a standard module like "math" with definitions in ~/.fgfs/Nasal/math.nas, though this should, of course, not be exploited by code that is to be submitted to cvs.


== Hello world ==
== Hello world ==

Navigation menu