Nasal library

From FlightGear wiki
Jump to: navigation, search


This page documents the core library functions of FlightGear's built-in scripting language, Nasal. The relevant folders in Git are:

Note  Feel free to copy & paste the examples into your Nasal Console and execute them to see what they do.

Core library

This is the list of the basic core library functions that were in Nasal before its integration in to FlightGear. See also:

append()

append(vector, element[, element[, ...]]);
Source Source code

This function appends, or adds, the given element(s) to the end of the vector given in the first argument. Returns the vector operated on.

vector
The vector to which the arguments will be appended.
element
An element to be added to the vector.

Examples

var vector = [1, 2, 3]; # Initialize the vector
append(vector, 4); # Append the number 4 to the end of the vector
debug.dump(vector); # Print the contents of the vector
var vector = [1, 2, 3]; # Initialize the vector
append(vector, 4, 5, 6); # Append the numbers 4, 5, and 6 to the end of the vector
debug.dump(vector); # Print the contents of the vector

bind()

bind(function, locals[, outer_scope]);
Source Source code

This creates a new function object. A function in Nasal is three things: the actual code, a hash/namespace of local variables available to the function namespace, and the closure object of that namespace. These correspond to the three arguments respectively.

function
Function to evaluate.
locals
Hash containing values that will become the namespace (first closure) for the function.
outer_scope
Optional function which is bound to the next closure. This can be bound to yet another, making a linked list.

Example

# This is a namespace/hash with a single member, named "key", which is initialized to 12 
var Namespace = {
    key: 12
};
 
# This is different namespace/hash containing a function
# dividing a variable "key" (which is unavailable/nil in this namespace) by 2
var AnotherNamespace = {
    ret: func {
        key /= 2;
    }
};
 
# To see that key is not available, try to call AnotherNamespace.ret() first
call(AnotherNamespace.ret, [], nil, nil, var errors = []);
if(size(errors)){
    print("Key could not be divided/resolved!");
    debug.printerror(errors);
}
 
# Associate the AnotherNamespace.ret() function with the first namespace
# so that "key" is now available
var function = bind(AnotherNamespace.ret, Namespace);
 
# Invoke the new function
function();
 
# Print out the value of Namespace.key
# It was changed to 6 from 12 by AnotherNamespace.ret()
print(Namespace.key);

call()

call(func[, args[, me[, locals[, error]]]);
Source Source code

Calls the given function with the given arguments and returns the result. This function is very useful as it allows much more control over function calls and catches any errors or die() calls that would normally trigger run-time errors cancelling execution of the script otherwise.

func
Function to execute.
args
Vector containing arguments to give to the called function.
me
me reference for the function call (i.e., for method calls). If given, this will override any me value existing in the namespace (locals argument).
locals
A hash with key/value pairs that will be available to the called function, typically used as the namespace for the function to be called.
error
A vector to append errors to. If the called function generates an error, the error, place, and line will be written to this. These errors can be printed using debug.printerror().

Examples

# prints "Called from call()"
call(func {
    print("Called from call()");
});
# prints "a = 1 : b = 2
call(func(a, b){
        print("a = ", a, " : b = ", b);
    },
    [1, 2]
);
var Hash = {
    new: func {
        var m = { parents: [Hash] };
 
        m.el1 = "string1";
        m.el2 = "string2";
 
        return m;
    }
};
 
# prints "me.el1 = string1", then "me.el2 = string2" on the next line
call(func(a, b){        
        print("me.el", a, " = ", me["el" ~ a]);      
        print("me.el", b, " = ", me["el" ~ b]);
    },
    [1, 2],
    Hash.new()
);
# prints the value of math.pi
call(func {
        print(pi);
    }, nil, nil, 
    math
);
call(func {
        print(math.ip); # math.ip doesn't exist
    }, nil, nil, nil,
    var errs = []
);
debug.printerror(errs); # The error is caught and printed using debug.printerror()

caller()

caller([level]);
Source Source code

Returns a vector containing a record from the current call stack. The level numbering starts from the currently executing function (level 0). Level 1 (the default) is the caller of the current function, and so on. The result is a four-element vector containing a hash of local variables, the function object, the source, and the line number.

level
Optional integer specifying the stack level to return a result from. Defaults to 1 (the caller of the currently executing function.

Examples

var myFunction = func(a, b){
    debug.dump(caller(0)[0]); # prints a hash of local variables, including arguments a and b
    return 2 * 2;
};
 
print("2 x 2 = ", myFunction(2, 2));
var get_arg_value = func(){
    print("Argument to myFunc = ", caller(1)[0]['a']); # print the value of myFunc's single argument, using caller()
};
 
var myFunc = func(a){
    get_arg_value();
};
 
myFunc(3);
This is a real example taken from $FG_ROOT/Nasal/canvas/MapStructure.nas. r(); (highlighted) returns a hash with the key/value pairs as per its arguments. For example, something like this is returned: { name: "<name>", vis: 1, zindex: nil }.
  1. var MapStructure_selfTest = func() {
  2.         var temp = {};
  3.         temp.dlg = canvas.Window.new([600,400],"dialog");
  4.         temp.canvas = temp.dlg.createCanvas().setColorBackground(1,1,1,0.5);
  5.         temp.root = temp.canvas.createGroup();
  6.         var TestMap = temp.root.createChild("map");
  7.         TestMap.setController("Aircraft position");
  8.         TestMap.setRange(25); # TODO: implement zooming/panning via mouse/wheel here, for lack of buttons :-/
  9.         TestMap.setTranslation(
  10.                 temp.canvas.get("view[0]")/2,
  11.                 temp.canvas.get("view[1]")/2
  12.         );
  13.         var r = func(name,vis=1,zindex=nil) return caller(0)[0];
  14.         # TODO: we'll need some z-indexing here, right now it's just random
  15.         # TODO: use foreach/keys to show all layers in this case by traversing SymbolLayer.registry direclty ?
  16.         # maybe encode implicit z-indexing for each lcontroller ctor call ? - i.e. preferred above/below order ?
  17.         foreach(var type; [r('TFC',0),r('APT'),r('DME'),r('VOR'),r('NDB'),r('FIX',0),r('RTE'),r('WPT'),r('FLT'),r('WXR'),r('APS'), ] )
  18.                 TestMap.addLayer(factory: canvas.SymbolLayer, type_arg: type.name,
  19.                                         visible: type.vis, priority: type.zindex,
  20.                 );
  21. }; # MapStructure_selfTest

chr()

chr(code);
Source Source code

Returns a character as per the single argument. Extended ASCII is supported (see http://www.asciitable.com/ for a list of supported characters), although this may vary between different systems. For a list of the most commonly used characters, see http://en.wikipedia.org/wiki/ASCII#ASCII_printable_code_chart (Dec column). The following table lists supported control characters, along with their equivalent control characters in Nasal strings.

Note  In Nasal, only strings enclosed with double-quotes ("string") supports control chracters. Strings in single quotes ('string') do not.
Code Name Equivalent to
10 Newline This is a link to a Wikipedia article \n
9 Horizontal tab This is a link to a Wikipedia article \t
13 Carriage return This is a link to a Wikipedia article \r
code
Integer character code for the desired glyph.

Examples

print("Code 65 = ", chr(65)); # prints "Code 65 = A"
This example displays all of the characters in a list, in the format Code n = >char<, n being the index, and char being the character.
for(var i = 0; i <= 255; i += 1){
    print("Code ", i, " = >", chr(i), "<");
}

closure()

closure(func[, level]);
Source Source code

Returns the hash table containing the lexical namespace of the given function. The level numbering start with level 0 being the namespace of func.

func
Function to evaluate.
level
Optional integer specifying the scope level. Defaults to 0 (the namespace of func).

Example

var get_math_e = func {
    return e; # return the value of math.e
}

var myFunction = bind(get_math_e, math); # bind get_math_e to the math namespace, so that math.e is immediately available to get_math_e
debug.dump(closure(myFunction)); # print the namespace of get_math_e

print(myFunction());

cmp()

cmp(a, b);
Source Source code

Compares two strings, returning -1 if a is less than b, 0 if they are identical and 1 if a is greater than b.

a
First string argument for comparison.
b
Second string argument for comparison.

Examples

print(cmp("1", "two")); # prints -1
print(cmp("string", "string")); # prints 0
print(cmp("one", "2")); # prints 1
print(cmp("string1", "string2")); # prints -1

compile()

compile(code[, filename]);
Source Source code

Compiles the specified code string and returns a function object bound to the current lexical context. If there is an error, the function dies, with the argument to die() being filename.

code
String containing Nasal code to be compiled.
filename
Optional string used for error messages/logging. Defaults to <compile>

Examples

var myCode = 'print("hello");';
var helloFunc = compile(myCode, "myCode");
helloFunc();

compile is very convenient to support Nasal loaded from other files. For instance, PropertyList XML files (such as GUI dialogs) may contain embedded Nasal sections that need to be parsed, processed and compiled. For an example of how to do this, save the below XML code as $FG_ROOT/gui/dialogs/test.xml.

<?xml version="1.0"?>
 
<PropertyList>
 
<nasal><![CDATA[
print("You have FlightGear v", getprop("/sim/version/flightgear"));
]]></nasal>
 
</PropertyList>
Now, start FlightGear and execute this code in the Nasal Console.
# Build the path
var FGRoot = getprop("/sim/fg-root");
var filename = "/gui/dialogs/test.xml";
var path = FGRoot ~ filename;
 
var blob = io.read_properties(path);
var script = blob.getValues().nasal; # Get the nasal string
 
# Compile the script.  We're passing the filename here for better runtime diagnostics 
var code = call(func {
    compile(script, filename);
}, nil, nil, var compilation_errors = []);
 
if(size(compilation_errors)){
    die("Error compiling code in: " ~ filename);
}
 
# Invoke the compiled script, equivalent to code(); 
# We're using call() here to detect errors:
call(code, [], nil, nil, var runtime_errors = []);
 
if(size(runtime_errors)){
    die("Error calling code compiled loaded from: " ~ filename);
}

contains()

contains(hash, key);
Source Source code

Returns 1 (True) if the hash contains the specified key, or 0 (False) if not.

hash
The hash to search in.
key
The scalar to be searched for, contained as a key in the hash.

Examples

# Initialize a hash
var hash = {
    element: "value"
};
print(contains(hash, "element") ? "Yes" : "No"); # This will print "Yes"
# Initialize a hash
var hash = {
    element: "value"
};
print(contains(hash, "element2") ? "Yes" : "No"); # This will print "No"

delete()

delete(hash, key);
Source Source code

Deletes the key from the hash if it exists. Operationally, this is identical to setting the hash value specified by the key to nil, but this variant potentially frees storage by deleting the reference to the key and by shrinking the hash. Returns the hash that has been operated on.

hash
The hash from which to delete the key.
key
The scalar to be deleted, contained as a key in the hash.

Example

# Initialize the hash
var hash = {
    element1: "value1",
    element2: "value2"
};
delete(hash, "element1"); # Delete element1
debug.dump(hash); # prints the hash, which is now minus element1

die()

die(error);
Source Source code

Terminates execution and unwinds the stack. The place and the line will be added to the error. This invokes the same internal exception handler used for internal runtime errors. Use this to signal fatal errors, or to implement exception handling. The error thrown (including internal runtime errors) can be caught with call().

error
String describing the error.
Note This parameter is technically optional, but it is highly recommended to use it.

Example

print("Will print");
die("Don't go any further!"); 
print("Won't print"); # Will not be printed because die() stops the process

find()

find(needle, haystack);
Source Source code

Finds and returns the index of the first occurrence of the string needle in the string haystack, or -1 if no such occurrence was found.

needle
String to search for.
haystack
String to search in.

Examples

print(find("c", "abcdef")); # prints 2
print(find("x", "abcdef")); # prints -1
print(find("cd", "abcdef")); # prints 2

ghosttype()

ghosttype(ghost);
Source Source code

Returns a string containing either a descriptive name of a ghost (a raw C/C++ object), or a unique id (the pointer to the C/C++ naGhostType instance) if no name has been set. Ghost is an acronym that stands for Garbage-collected Handle to OutSide Thingy.

ghost
Ghost to return a description for.

id()

id(object);
Source Source code

Returns a string containing information on the type and ID of the object provided in the single argument. The information is returned in the form of type:id, where type is the type of object, and id is the ID.

object
Can be either of a string, a vector, a hash, a code, a function, or a ghost.

Example

print(id("A")); # prints "str:000000001624A590"

int()

int(number);
Source Source code

Returns the integer part of the numeric value of the single argument, or nil if none exists.

number
Number or string with just a number in it to return an integer from.

Examples

print(int(23)); # prints "23"
print(int(23.123)); # prints "23"
debug.dump(int("string")); # prints "nil"

keys()

keys(hash);
Source Source code

Returns a vector containing the list of keys found in the single hash argument.

hash
The hash to return the keys from.

Example

# Initialize a hash
var hash = {
    element1: "value",
    element2: "value"
};
debug.dump(keys(hash)); # print the vector

left()

left(string, length);
Source Source code
Version 2.12.0+

Returns a substring of string, starting from the left.

string
String to return part of.
length
Integer specifying the length of the substring to return.

Example

print(left("string", 2)); # prints "st"

num()

num(number);
Source Source code

Returns the numerical value of the single striing argument, or nil if none exists.

number
String with just a number in it to return a number from.

Examples

print(num("23")); # prints "23"
print(num("23.123")); # prints "23.123"
debug.dump(num("string")); # prints "nil"

pop()

pop(vector);
Source Source code

Removes and returns the last element of the single vector argument, or nil if the vector is empty.

vector
Vector to remove an element from.

Examples

var vector = [1, 2, 3];
pop(vector);
debug.dump(vector); # prints "[1, 2]"
var vector = [1, 2, 3];
debug.dump(pop(vector)); # prints "3"
var vector = [];
debug.dump(pop(vector)); # prints "nil"

right()

right(string, length);
Source Source code
Version 2.12.0+

Returns a substring of string, starting from the right.

string
String to return part of.
length
Integer specifying the length of the substring to return.

Example

print(right("string", 2)); # prints "ng"

setsize()

setsize(vector, size);
Source Source code

Sets the size of a vector. The first argument specifies a vector, the second a number representing the desired size of that vector. If the vector is currently larger than the specified size, it is truncated. If it is smaller, it is padded with nil entries. Returns the vector operated upon.

vector
The vector to be operated on.
size
The desired size of the vector in number of entries.

Examples

var vector = [1, 2, 3]; # Initialize a vector
setsize(vector, 4);
debug.dump(vector); # print the vector
var vector = [1, 2, 3]; # Initialize a vector
setsize(vector, 2);
debug.dump(vector); # print the vector

size()

size(object);
Source Source code

Returns the size of the single argument. For strings, this is the length in bytes. For vectors, this is the number of elements. For hashes, it is the number of key/value pairs. If the arguent is nil or a number, this error will be thrown: object has no size().

object
Object to find the size of. Must be a string, a vector or a hash.

Examples

var string = "string";
print(size(string)); # prints "6"
var vector = [1, 2, 3];
print(size(vector)); # prints "3"
var hash = {
    element1: "value1",
    element2: "value2",
    element3: "value3"
};
print(size(hash)); # prints "3"

sort()

sort(vector, function);
Source Source code

Returns a vector containing the elements in the input vector sorted in according to the rule given by function. Implemented with the ANSI C qsort(), sort() is stable. This means that if the rules in the first example are used, equal elements in the output vector will appear in the same relative order as they do in the input. It is run in a loop, so function is run several times.

vector
Input vector to sort.
function
Function according to which the elements will be sorted by. It should take two arguments and should return one of 1, 0, or -1.
Return value Meaning
less than 0 first argument should go before second argument
0 first argument equals second argument
greater than 0 first argument should go after second argument

Examples

This example sorts elements from greatest to smallest.
var sort_rules = func(a, b){
    if(a < b){
        return -1; # A should before b in the returned vector
    }elsif(a == b){
        return 0; # A is equivalent to b 
    }else{
        return 1; # A should after b in the returned vector
    }
}
debug.dump(sort([3, 2, 5, 6, 4, 1], sort_rules)); # prints "[1, 2, 3, 4, 5, 6]"
# Outputs the elements in reverse order (greatest to smallest)
var sort_rules = func(a, b){
    if(a < b){
        return 1; # -1 in the above example
    }elsif(a == b){
        return 0;
    }else{
        return -1; # 1 in the above example
    }
}
debug.dump(sort([3, 2, 5, 6, 4, 1], sort_rules)); # prints "[6, 5, 4, 3, 2, 1]"

split()

split(delimiter, string);
Source Source code

Splits the input string into a vector of substrings bounded by occurrences of the delimiter substring.

delimiter
String that will split the substrings in the returned vector.
string
String to split up.

Examples

debug.dump(split("cd", "abcdef")); # prints "['ab', 'ef']"
debug.dump(split(".", "3.2.0")); # prints "[3, 2, 0]"
debug.dump(split("/", "path/to/file")); # prints "['path', 'to', 'file']"

sprintf()

sprintf(format[, arg[, arg, [...]]]);
Source Source code

Creates and returns a string formatted using ANSI C vsnprintf() [1]. Below is a table of supported format specifiers.

 %[flags][width][.precision]specifier
Flags
Flag Output
+ Forces to precede the result with a plus or minus sign (+ or -) even for positive numbers. By default, only negative numbers are preceded with a - sign.
space Prefixes non-signed numbers with a space.
- Left-align the output of this placeholder (the default is to right-align the output) when the width option is specified.
0 Use 0 instead of spaces to pad a field when the width option is specified.
# Used with o, x or X specifiers the value is preceded with 0, 0x or 0X respectively for values different than zero. Used with e, E and f, it forces the written output to contain a decimal point even if no digits would follow. By default, if no digits follow, no decimal point is written. Used with g or G the result is the same as with e or E but trailing zeros are not removed.
Width
Integer specifying the minimum number of characters to be returned.
Precision
Integer preceded by a dot specifying the number of decimal places to be written.
Specifiers
Specifier Output
d, i Signed decimal number.
% Percent (%) character.
c A single character assigned to a character code, the code given in an integer argument. See http://www.asciitable.com/ for a list of supported characters and their codes.
o Unsigned integer as an octal number.
u Unsigned decimal integer.
x, X Unsigned integer as a hexadecimal number. If x is used, any letters in the number are lowercase, while X gives uppercase.
e, E Double value in scientific notation (i.e., [-]ddd.ddde[+/-]ddd), with an exponent being denoted by e or E depending on whether an upper or lowercase is used respectively.
f Floating-point number, in fixed decimal notation, by default with 6 decimal places.
F Appears to be supported [2], but doesn't work.
g, G Double in either normal or exponential notation, whichever is more appropriate for its magnitude. g uses lower-case letters, G uses upper-case letters. This type differs slightly from fixed-point notation in that insignificant zeroes to the right of the decimal point are not included. Also, the decimal point is not included on whole numbers.
format
String specifying the format. Can be used with or without a format specifiers. See below for examples.
arg
Argument specifying a value to replace a format placeholder (such as %d) in the format string. Not required if there are no format specifiers.

Examples

print(sprintf("%i", 54)); # prints "54"
print(sprintf("Pi = %+.10f", math.pi)); # prints "Pi = +3.1415926536"
print(sprintf("%6d", 23)); # prints "    23"
print(sprintf("%06d", 23)); # prints "000023"
var FGVer = getprop("/sim/version/flightgear");
print(sprintf("You have FlightGear v%s", FGVer)); # prints "You have FlightGear v<your version>"
print(sprintf("Hexadecimal 100000 = %X", 100000)); # prints "Hexadecimal 100000 = 186A0"
print(sprintf("Hexadecimal 100000 = %x", 100000)); # prints "Hexadecimal 100000 = 186a0"
print(sprintf("Code 65 is %c", 65)); # prints "Code 65 is A"
print(sprintf("%e", 54)); # prints "5.400000e+001"
print(sprintf("%E", 54)); # prints "5.400000E+001"
print(sprintf("%o", 54)); # prints "66"
print(sprintf("50%% of 100 is %i", 100 / 2)); # prints "50% of 100 is 50"

streq()

streq(a, b);
Source Source code

Tests the string values of the two arguments for equality. This function is needed because the == operator (see Nasal Operators) tests for numeric equality first. If either or both the arguments are not strings, 0 (False) will be returned. Returns either 0 (False) or 1 (True).

Note  This function is rarely required in typical code.
a
First argument for testing equality.
b
Second argument for testing equality.

Examples

print(streq("0", "0")); # prints "1" (True)
print(0 == 0.0); # prints "1" (True)
print(streq("0", "0.0")); # prints "0" (False)

substr()

substr(string, start [, length]);
Source Source code

Similar the subvec(), but operates on strings. Computes and returns a substring. The first argument specifies a string, the second is the index of the start of a substring, the optional third argument specifies a length (the default is to return the rest of the string from the start).

string
String to return a substring from.
start
Integer specifying the start of a substring.
length
Optional argument specifying the length of the substring. Defaults to the end of the string.

Examples

print(substr("abcde", 1, 3)); # prints "bcd"
print(substr("abcde", 1)); # prints "bcde"
print(substr("abcde", 2, 1)); # prints "c"

subvec()

subvec(vector, start[, length]);
Source Source code

Returns a sub-range of a vector. The first argument specifies a vector, the second a starting index, and the optional third argument indicates a length (the default is to the end of the vector).

vector
The vector to take the sub-vector from.
start
The starting point of the sub-vetor within the given vector.
length
Optional argument specifying the length of the sub-vector, from the starting point.

Examples

var vector = [1, 2, 3];
debug.dump(subvec(vector, 0)); # prints "[1, 2, 3]"
var vector = [1, 2, 3];
debug.dump(subvec(vector, 1)); # prints "[2, 3]"
var vector = [1, 2, 3];
debug.dump(subvec(vector, 1, 1)); # prints "[2]"

typeof()

typeof(object);
Source Source code

Returns a string indicating the whether the object is nil, a scalar (number or string), a vector, a hash, a function, or a ghost.

object
Object to return the type of.

Examples

var object = nil;
print(typeof(object)); # prints "nil"
var object = "Hello world!";
print(typeof(object)); # prints "scalar"
var object = math.pi;
print(typeof(object)); # prints "scalar"
var object = [1, 2, 3];
print(typeof(object)); # prints "vector"
var object = {};
print(typeof(object)); # prints "hash"
var object = func {};
print(typeof(object)); # prints "func"

Extension functions

cmdarg()

cmdarg() is a mechanism to pass arguments to a nasal script (wrapped in properties) instead of "normal" function parameters. Note that cmdarg() should be primarily used in Nasal code embedded in XML files and should be considered depreciated otherwise (see [1]).

cmdarg() will keep working in (joystick) XML-bindings and on the top-level of embedded Nasal scripts (i.e. dialog and animation XML files).

As such, the cmdarg() function is primarily used for listener callbacks declared in XML markup, cmdarg() returns the listened-to property as props.Node object, so you can use it with all its methods (see $FG_ROOT/Nasal/props.nas) for example:

print(cmdarg().getPath(), " has been changed to ", cmdarg().getValue())

The cmdarg() function avoids that you have to type the exact same path twice (once here and once in the setlistener() command) and it makes clear that this is the listened to property. Also, you can use all the nice props.Node methods on cmdarg() directly:

setlistener("/gear/launchbar/state", func {
    if (cmdarg().getValue() == "Engaged")
        setprop("/sim/messages/copilot", "Engaged!");
}, 1, 0);

Use of cmdarg() outside of XML-bindings won't cause an error, but (still) return the last cmdarg() property. This just won't be the listened-to property anymore, but whatever the last legitimate cmdarg() user set. Most of the time it will be the property root of a joystick binding.

Don't make any assumptions and use cmdarg() only in one of these cases:

  • binding: returns root of this binding's property branch. Needed for accessing an axis' value: cmdarg().getNode("setting").getValue()
  • dialog xml files: returns root of that file's property branch in memory. This can be used to let embedded Nasal change the dialog (e.g. clear and build lists) before the final layout is decided
  • animation xml files: returns root of this model's place in /ai/models/ when used as AI/MP model. Examples: /ai/models/multiplayer[3], /ai/models/tanker[1], etc.[3]
  • AI aircraft XML files
  • remotely invoking Nasal code by setting properties using the built-in telnet daemon (RPC)[4][5].

In all cases, the cmdarg() call must not be delayed until later using settimer() or setlistener(). Because later it'll again return some unrelated property!

fgcommand()

Runs an internal "fgcommand", see Bindings for a full list of available items (since they are most often used in bindings for input devices). The first argument is the name of the command (e.g. "property-interpolate") and the second is a props.Node object or its ghost which is the "argument" to the fgcommand. (This ensures that fgcommands are universal since props.Node/SGPropertyNode objects can really be accessed from anywhere in FlightGear.) Each fgcommand returns 1 if it succeeded or 0 if it failed.

The profiling related fgcommands profiler-start and profiler-stop are documented at Built-in Profiler.

addcommand()

Add a fgcommand which can be used like a regular fgcommand (global scope, etc.). First argument is the name, second is the function to run. Note that this fgcommand will always return true! Inside the callback/function one can use either cmdarg() to retrieve the arguments or use the first argument passed to the function (since they will be the same).

removecommand()

As you can guess, there's also a removecommand() function which will remove any command – even those implemented in C++! As such it can be very dangerous and remove core functionality, so use with caution.

print()

Concatenates an arbitrary number of arguments to one string, appends a new-line, and prints it to the terminal. Returns the number of printed characters.

print("Just", " a ", "test");

logprint()

Similar, but the first argument is reserved for number specifying the priority (i.e. matching a sgDebugPriority object: 1 for bulk, 2 for debug, 3 for info, and 4 for warn). Also see printlog() in globals.nas: it does essentially the same but with named levels ("bulk", etc.). (The latter relies on print(), however, and does not make use of the sophistication of sglog in dealing with source file and line number.)

getprop()

Returns the node value for a given path, or nil if the node doesn't exist or hasn't been initialized to a value yet.

getprop(<path> [, <path>, [...]]);

Several arguments will be added together to produce a path, with numeric arguments specifying indexes (as of FlightGear 3.1), so

getprop("canvas/by-index", "texture", 1, "name");

is the same as:

getprop("canvas/by-index/texture[1]/name");

Example:

print("The frame rate is ", getprop("/sim/frame-rate"), " FPS");
for (var i=0; i < 10; i += 1) {
    print("View ", i, "'s name is: ", getprop("/sim/view", i, "name"));
}

setprop()

Sets a property value for a given node path string. Returns 1 on success or 0 if the property could not be set (i.e. was read-only).

setprop(<path> [, <path>, [...]], <value>);

All arguments but the last are concatenated to a path string, like getprop() above. The last value is written to the respective node. If the node isn't writable, then an error message is printed to the console.

Examples:

setprop("/sim/current-view/view-number", 2);
setprop("/controls", "engines/engine[0]", "reverser", 1);

Erasing a property from the property tree: a property that has been created, for example through setprop() has to be erased using the props namespace helper, like this:

props.globals.getNode("foo/bar").remove(); 		# take out the complete node
props.globals.getNode("foo").removeChild("bar"); 	# take out a certain child node

interpolate()

Give the value from a value or a source node to a destination node in given time.

interpolate(<path>, <value>, <time> [, <value2>, <time2> [, ...]]);

Examples:

interpolate("controls/switches/nav-lights-pos", 1, 0.25); # After 25ms, nav-lights-pos = 1
interpolate("controls/gear/brake-left-pos", getprop("controls/gear/brake-left"), 1); # After 1s, brake-left-pos = brake-left

To interpolate with a constant speed, use the formula time = distance/speed.

# Apply the brakes at 20%/second
var p = "controls/gear/brake-left";
var current = getprop(p);
var dist = 1-current;
if (dist) interpolate(p, 1, dist/.2);

setlistener()

settimer()

CautionImproper use of the settimer() API may cause resource leaks.

This typically is caused by the low-level nature of such code, requiring manual tracking of listeners and timers and manual reset and re-init handling.

It is instead recommended that you use the maketimer() API. Alternatively a wrapping helper class can be used to handle low-level APIs, which is the recommended way to support multiple FlightGear versions.

Runs a function after a given simulation time (default) or real time in seconds.

settimer(<function>, <time> [, <realtime=0>]);

The first argument is a function object (ie, "func { ... }"). Note that this is different from a function call (ie, "func ( ... )"). If you don't understand what this means, just remember to always enclose the first argument in any call to settimer with the word "func" and braces "{ }", and it will always work.

The second argument is a delay time. After this amount of time the function will be executed. For instance, if you want to print the words "My result" in five seconds, use this code:

settimer ( func { print ( "My result"); }, 5);

Inside the braces of the func object you can put any valid Nasal code, including a function call. In fact, if you want to call a function with certain values as arguments, the way to do it is to turn it into a function object by enclosing it with a func{}, for example:

myarg1="My favorite string";
myarg2=432;
settimer ( func { myfunction ( myarg1, myarg2); }, 25);

The third argument is optional and defaults to 0, which lets the time argument be interpreted as "seconds simulation time". In this case the timer doesn't run when FlightGear is paused. For user interaction purposes (measuring key press time, displaying popups, etc.) one usually prefers real time.

# simulation time example
var copilot_annoyed = func { setprop("/sim/messages/copilot", "Stop it! Immediately!") }
settimer(copilot_annoyed, 10);
# real time example
var popdown = func ( tipArg ) { 
    fgcommand("dialog-close", tipArg); 
}
 
var selfStatusPopupTip = func (label, delay = 10, override = nil) {	
    var tmpl = props.Node.new({
            name : "PopTipSelf", modal : 0, layout : "hbox",
            y: 140,
            text : { label : label, padding : 6 }
    });
    if (override != nil) tmpl.setValues(override);
 
    popdown(tipArgSelf);
    fgcommand("dialog-new", tmpl);
    fgcommand("dialog-show", tipArgSelf);
 
    currTimerSelf += 1;
    var thisTimerSelf = currTimerSelf;
 
    # Final argument 1 is a flag to use "real" time, not simulated time
    settimer(func { if(currTimerSelf == thisTimerSelf) { popdown(tipArgSelf) } }, delay, 1);
}

More information about using the settimer function to create loops.

maketimer() (2.11+)

As of 2.11, there is a new API for making a timer that allows more control over what happens in a timer – as opposed to setting one and forgetting about it.

var timer = maketimer(<interval>, <function>)
var timer = maketimer(<interval>, <self>, <function>)
timer.start()
timer.stop()
timer.restart(<interval>)
timer.singleShot [read/write]
timer.isRunning [read-only]
# create timer with 1 second interval
var timer = maketimer(1.0, func { 
 print('timer called'); 
 }
);
# start the timer (with 1 second inverval)
timer.start();
# restart timer with 4 second interval
timer.restart(4);
 
# fire one single time in 6 seconds
timer.singleShot = 1;
timer.restart(6);
var Tooltip = {
  new: func
  {
    var m = {
      parents: [Tooltip]
    }
    m._hideTimer = maketimer(1.0, m, Tooltip._hideTimeout);
    m._hideTimer.singleShot = 1;
 
   return m;
  },
  run: func
  {
    if( !me._hideTimer.isRunning )
      me._hideTimer.start();
  }
  _hideTimeout: func
  {
    print('_hideTimeout');
  }
};

geodinfo()

Returns information about geodetic coordinates. Takes two arguments: lat, lon (in degree) and returns a vector with two entries, or nil if no information could be obtained because the terrain tile wasn't loaded. The first entry is the elevation (in meters) for the given point, and the second is a hash with information about the assigned material, or nil if there was no material information available, because there is, for instance, an untextured building at that spot or the scenery tile is not loaded.

var lat = getprop("/position/latitude-deg");
var lon = getprop("/position/longitude-deg");
var info = geodinfo(lat, lon);
 
if (info != nil) {
    print("the terrain under the aircraft is at elevation ", info[0], " m");
    if (info[1] != nil)
        print("and it is ", info[1].solid ? "solid ground" : "covered by water");
}

A full data set looks like this:

debug.dump(geodinfo(lat, lon));
 
# outputs
[ 106.9892101062052, { light_coverage : 0, bumpiness : 0.5999999999999999, load_resistance : 1e+30,
solid : 0,  names : [ "Lake", "Pond", "Reservoir", "Stream", "Canal" ], friction_factor : 1, 
rolling_friction : 1.5 } ]

Note that geodinfo is a *very* CPU intensive operation, particularly in FG 2.4.0 and earlier, so use sparingly (forum discussion here).

airportinfo()

Function for retrieval of airport/runway information. Usage:

var apt = airportinfo("KHAF");   # get info about KHAF
var apt = airportinfo(lat, lon); # get info about apt closest to lat/lon
var apt = airportinfo();         # get info about apt closest to aircraft

The command debug.dump(airportinfo("KHAF")) outputs this:

{ lon : -122.4962626410256, lat : 37.51343502564102, has_metar : 0,
  runways : { 12 : { stopway2 : 0, threshold1 : 232.5624,
  lon : -122.5010889999999, lat : 37.513831, stopway1 : 0, width : 45.72,
  threshold2 : 232.5624, heading : 138.1199999999999, length : 1523.0856 } },
  elevation : 20.42159999999999, id : "KHAF", name : "Half Moon Bay" }

That is: a hash with elements lat/lon/elev/id/name/has_metar for the airport, and a hash with runways, each of which consists of lat/lon/length/width/heading/threshold[12]/stopway[12]. Only one side of each runway is listed -- the other can easily be deduced.

To list all Runways :

var rwy = airportinfo("KHAF").runways;
foreach(var key;keys(rwy)) {
 print (key);    # returns "30", "12"
}

Positioned Object Queries

Several functions exist to query the navigation database of 'positioned' objects, i.e items with a defined location in the simulation world. (These objects all inherit from FGPositioned internally). The functions return either one, or a vector, of wrapped Nasal objects. These are efficient, but unlike a hash they cannot simply be debug.dump() to view all their members and methods, many of which are computed in a lazy fashion (i.e. on demand).

When the query functions take a position, the default value is the current aircraft location. An alternative location can be supplied by passing two number (lat, lon), a Geo.Coord object, or any positioned object or waypoint retrieved from another query.

findAirportsWithinRange()
Find all airports within a specified range (in NM) of the current position or explicit position
findAirportsByICAO()
Find airports matching a complete or partial ICAO code. In particular this can search for all airports starting with a two or three letter prefix.
navinfo()
Return a list of navaids near a location by type and ident. Type should be 'fix', 'vor', 'ndb', 'dme'
findNavaidsWithinRange()
Search for navaids within a particular range (in NM), optionally limited by type. This provides a 'find the closest ILS' function
findNavaidByFrequency()
find the closest navaid (of a particular type) matching an exact frequency
findNavaidsByFrequency()
find all the navaids matching a particular frequency and optional type, sorted by distance from the search location.

All positioned objects returned by the above methods have the following members:

id
Identifier - ICAO code for airports, published ident for navaids and fixes
lon
degrees longitude
lat
degrees latitude

Depending on type, the following members are also available:

name
the full name of the airport or navaid if one is defined
elevation
the ASL elevation of the object in feet

For navaids, the following members are available:

frequency
the navaid frequency in kHz
type
the navaid type as a string: vor, dme, loc, ils, ndb
course
the degrees course associated with the navaid, for localiser and ILS stations

history() (3.1+)

Function to expose flight history as aircraft.history()

var hist = aircraft.history();
 
# get history of aircraft position/orientation collapsing
# nodes with a distance smaller than the given minimum
# edge legth
debug.dump( hist.pathForHistory(<minimum-edge-length-meter>) );


flightplan()

Function to retrieve the active flight-plan object, or load a flight plan from a file path.

Usage:

var fp = flightplan();
var fp = flightplan('/some/path/to/a/flightplan.xml');

In advance of converting the Map and NavDisplay to use the Canvas, James has improved the "flightplan()" extension function of the Nasal scripting interpreter to expose the full route-path vector for each flight plan leg as a vector on the leg.


var fp = flightplan();
for (var i=0; i<fp.getPlanSize(); i += 1)
{
  var leg = fp.getWP(i);
  debug.dump(leg.path());
}

systime()

Returns epoch time (time since 1970/01/01 00:00) in seconds as a floating point number with high resolution. This is useful for benchmarking purposes.

#benchmarking example:
var start = systime();
how_fast_am_I(123);
var end = systime();
print("took ", end - start, " seconds");

carttogeod()

Converts cartesian coordinates x/y/z to geodetic coordinates lat/lon/alt, which are returned as a vector. Units are degree and meter.

var geod = carttogeod(-2737504, -4264101, 3862172);
print("lat=", geod[0], " lon=", geod[1], " alt=", geod[2]);
 
# outputs
lat=37.49999782141546 lon=-122.6999914632327 alt=998.6042055172776

geodtocart()

Converts geodetic coordinates lat/lon/alt to cartesian coordinates x/y/z. Units are degree and meter.

var cart = geodtocart(37.5, -122.7, 1000); # lat/lon/alt(m)
print("x=", cart[0], " y=", cart[1], " z=", cart[2]);
 
# outputs
x=-2737504.667684828 y=-4264101.900993474 z=3862172.834656495

parsexml()

This function is an interface to the built-in Expat XML parser. It takes up to five arguments. The first is a mandatory absolute path to an XML file, the remaining four are optional callback functions, each of which can be nil (which is also the default value).

var ret = parsexml(<path> [, <start-elem> [, <end-elem> [, <data> [, <pi> ]]]]);
 
<start-elem>  ... called for every starting tag with two arguments: the tag name, and an attribute hash
<end-elem>    ... called for every ending tag with one argument: the tag name
<data>        ... called for every piece of data with one argument: the data string
<pi>          ... called for every "processing information" with two args: target and data string
 
<ret>         ... the return value is nil on error, and the <path> otherwise

Example:

var start = func(name, attr) {
    print("starting tag ", name);
    foreach (var a; keys(attr))
        print("\twith attribute ", a, "=", attr[a]);
}
var end = func(name) { print("ending tag ", name) }
var data = func(data) { print("data=", data) }
var pi = func(target, data) { print("processing instruction: target=", target, " data=", data) }
parsexml("/tmp/foo.xml", start, end, data, pi);

resolvepath()

SimGear features its own path resolving framework that takes a relative path and returns an absolute path, checking from base directories such as $FG_ROOT, $FG_HOME, $FG_AIRCRAFT, and the current aircraft directory (/sim/aircraft-dir). This function in Nasal takes a path string and returns the absolute path or an empty string if the path couldn't be resolved.

Example:

var guess_path = func(path...) {
    var path_concat = string.join(path, "/");
    var file_path = resolvepath(path_concat);
    if (file_path == "") die("Path not found: "~path_concat);
    return file_path;
}

HTTP module (2.99+)

Get remote data using the HTTP.

http.load()

Load resource identified by its URL into memory.

var request = http.load(<url>);
http.load("http://example.com/test.txt")
    .done(func(r) print("Got response: " ~ r.response));

http.save()

Save resource to a local file.

var request = http.save(<url>, <file_path>);
http.save("http://example.com/test.png", getprop('/sim/fg-home') ~ '/cache/test.png')
    .fail(func print("Download failed!"))
    .done(func(r) print("Finished request with status: " ~ r.status ~ " " ~ r.reason))
    .always(func print("Request complete (fail or success)"));

rand()

Return a random number as generated by sg_random.

srand()

Seed the random number generator based upon the current time. Returns 0.

md5() (3.1+)

Get the md5 hash of a string.

var hash = md5(<str>);

abort()

Wrapper for the C++ library abort() function – i.e. it just aborts the process without regard to what's happening. For exiting (gracefully) from FlightGear use the fgcommand "exit" instead.

References
  1. fg/simgear/source/next:simgear/nasal/lib.c, line 308. Retrieved February 2015.
  2. fg/simgear/source/next:simgear/nasal/lib.c, line 389. Retrieved February 2015.
  3. Melchior Franz (8 December 2007). multiplayer generic properties.
  4. Melchior Franz (2 January 2006). Calling FG functions via network interface.
  5. Melchior Franz (7 January 2006). Calling FG functions via network interface.