Nasal Best Practices

From FlightGear wiki
Jump to navigation Jump to search

FlightGear coding in Nasal is easy, but there are some best practices to consider to make your code easier to read and neater.

These are merely opinions; you may use any format you wish. However, it would be great if users could use this format, to make formatting more consistent across FlightGear.

Comments

Comments should use the following format:

#######################################
# Use this to split up blocks of code #
########################################
var xyz = 1
var abc = 2 # Note that there is a space after the hash

Comments should be used primarily to describe WHY code does what it does and WHAT it is doing. Usually there is no need to say HOW it is doing it, as that should be self-evident, unless your code is too complex! For instance, we could have the following:

# Checks whether we should create ice
# Icing conditions: freezing fog or low temp and below dp or in advanced wx cloud

var spread = temperature - dewpoint; 
if ((spread < maxSpread and temperature < 0) or (temperature < 0 and visibility < 1000) or (visibLclWx < 5000 and temperature < 0)) { 
# we use two visibility properties: one is for basic wx and the other is for advanced wx 		
   setprop("/systems/icing/icingcond", 1);
} else {
   setprop("/systems/icing/icingcond", 0);
}

Spaces vs Tabs

This has divided many developers! I feel the best method is to use tabs at the start of a line and to use spaces within a line. For example:

# we use spaces within the line to make it easier to read
if ((spread < maxSpread and temperature < 0) or (temperature < 0 and visibility < 1000) or (visibLclWx < 5000 and temperature < 0)) { 
# we tabs to move the "true" and "false" statement to the second column	
   setprop("/systems/icing/icingcond", 1);
} else {
   setprop("/systems/icing/icingcond", 0);
}

Another example:

var abc = 1;
var def = getprop("/abc/def/ghi");
# Note that there is a space around the '='!

Whichever method you choose, be consistent throughout your coding!

Lining up code

This applies to both Nasal and XML. I will use XML in this example. Use spaces to line up code within a code block; that is:

<filter>
	<name>VS-target-cmd</name>
	<type>gain</type>
	<gain>1</gain>
	<update-interval-secs type="double">0.05</update-interval-secs>
	<input>
		<expression>
			<table>
				<property>/it-autoflight/internal/vert-speed-fpm</property> 
				<entry><ind>-50000</ind><dep> -750</dep></entry>
				<entry><ind> -2000</ind><dep> -750</dep></entry>
				<entry><ind> -1000</ind><dep> -250</dep></entry>
				<entry><ind>     0</ind><dep>    0</dep></entry>
				<entry><ind>  1000</ind><dep>  810</dep></entry>
				<entry><ind>  2000</ind><dep> 1640</dep></entry>
				<entry><ind>  3000</ind><dep> 2100</dep></entry>
				<entry><ind>  4000</ind><dep> 2440</dep></entry>
				<entry><ind>  5000</ind><dep> 3000</dep></entry>
				<entry><ind> 10000</ind><dep> 5000</dep></entry>
				<entry><ind> 50000</ind><dep>50000</dep></entry>
			</table>
		</expression>
	</input>
	<output>/systems/pressurization/targetvs-cmd</output>
</filter>

Look particularly at the <table> part. Note how the last line lines up. This makes the code easy to read.

Documentation

Example:

# Calculates fib(n) % 16777216.
# Args:
#     n (number): The number to be calculated.
# Returns:
#     number: The Fibonacci number at position n.
# Throws: No
# Side effects: No
var fib = func(n) {
    if (n <= 1) return n;
    
    var a = 0;
    var b = 1;
    for (var i = 2; i <= n; i += 1) {
        (a, b) = (b, (a + b) & 16777215);
    }
    return b;
};
# Define a 3D position.
# static _className (string): For runtime lookup.
# static dimensions (number): Number of dimensions.
# x (number): The x-coordinate.
# y (number): The y-coordinate.
# z (number): The z-coordinate.
var Position3D = {
    _className: "Position3D", # for runtime lookup
    dimensions: 3,
    # Args:
    #     x, y, z (number): XYZ coordinates
    new: func(x, y, z) {
        var inst = { parents: [Position3D] }; # the parents vector is like prototypes in JS
        inst.x = x;
        inst.y = y;
        inst.z = z;
        return inst;
    },
    # Returns: string
    toString: func {
        return sprintf("%s(%g, %g, %g)", me._className, me.x, me.y, me.z);
    }
};

There is also an agent skill (AI prompt) that can document Nasal code and add type annotations to it. See here.