Scope of Nasal scripts

From FlightGear wiki
Jump to navigation Jump to search

Nasal is a very powerful scripting language for manipulating all kinds of things in FG. However, it can be put to even better use if code is written to be reusable. As Nasal scripts can be a very powerful tool to make aircraft models better, this page tries to explain how the scope (or namespace) of Nasal works.

An Initial Setup

This section describes how to get a little Nasal code into ones aircraft.

Including Nasal into an Aircraft

There are two major steps to make this happen. First the script needs to be written (check here for a quick intro into the language), then it needs to be integrated into the model.

Let's assume we made a new instrument for the B777-200 and that instrument would be called CDU. We would then have a file cdu_stuff.nas that contains our code. Let's say the file is physically stored in $FGDATA$/Aircraft/777-200/Nasal/. To include the script into the B777-200 we would open $FGDATA$/Aircraft/777-200/777-200ER-set.xml and find the section tagged <nasal>...</nasal>. The script can be included as follows:

 ...
 <nasal>
   ...
   <cdu>
     <!-- This section deals with the CDU of the B777 -->
     <file>Aircraft/777-200/Nasal/cdu_stuff.nas</file>
   </cdu>
 </nasal>
 ...

Note how the $FGDATA$ has been dropped, the XML automatically assumes that that is the root folder.

Scope of Nasal scripts

Globally the code in this script is now put into a namespace matching its module name, in our case cdu. As an immediate effect of that, all the variables and functions in cdu_stuff.nas are now accessible via

 cdu.some_function_in_cdu_stuff(); # call the function some_function_in_cdu_stuff() in the file cdu_stuff.nas, loaded into the module "cdu"

This is why it is so tremendously important to use the var keyword in nasal scripts. Anything that doesn't have a var keyword is globally accessible, i.e. one could open a Nasal console in FlightGear (Menu "Debug"->"Nasal Console") and simply type

 some_function_in_cdu_stuff();

to call that function. That might seem practical, but soon we would run out of good (read unique) names, and stuff gets overwritten... So, please use var!


A More Advanced Setup

For quick and dirty (or simply simple things) the setup above might be ok, but once the code grows, one might feel the urge to structure ones dozens of lines of Nasal script a little more.

Sticking with the CDU example, it becomes fairly obvious fairly quickly that the CDU is not unique to the B777-200. As such it might make sense to separate parts that are generic and parts that are specific.

In order to accomplish this, again two things need to happen: splitting the files and then loading them again. Let's assume want to split cdu_stuff.nas into boeing_cdu_generic.nas and b777_cdu.nas. But how? Well, the scope can give some guidance.

Everything that is loaded inside the same module (those sub blocks itn the <nasal>...</nasal> block in the aircraft's -set.xml) is still in the same scope. This means that effectively all files which are loaded in one module can be treated as if they were one file, stacked in the order in which they are mentioned in the block. That means that stuff declared with var is still accessible in the other nasal files in the same module. (So this is no excuse not to use it!). Furthermore, where to store the files can also help. Generic stuff really shouldn't be stored with the aircraft, but more with the instrument itself.

So the long and the short of this could be a setup as follows (again, the $FGDATA$/Aircraft/777-200/777-200ER-set.xml file) :

 ...
 <nasal>
   ...
   <cdu>
     <!-- This section deals with the generics of the CDU-->
       <file>Aircraft/Instruments-3d/boeing_cdu/boeing_cdu_generic.nas</file>
     <!-- This section deals with the B777 specifics of the CDU-->
       <file>Aircraft/777-200/Nasal/b777_cdu.nas</file>
   </cdu>
 </nasal>
 ...

Further Reading

If you got hooked on the CDU example, go have a read at Coding a Boeing CDU.