Exception handling with Nasal

From FlightGear wiki
Jump to navigation Jump to search

die() aborts a function with an error message (this can be compared to the throw() mechanism in C++).

var divide = func(a, b) {
    if (b == 0)
        die("division by zero");
    return a / b;     # this line won't be reached if b == 0

die() is also used internally by built-in extension functions or Nasal core functions. getprop("/4me"), for example, dies with an error message "name must begin with alpha or '_'". Now assume we want to write a dialog where the user can type a property path into an input field, and we display the property's value in a popup dialog. What if the user typed an invalid path and we hand that over to getprop()? We don't want Nasal to abort our code because of that. We want to display a nice error message instead. The call() function can catch die() exceptions:

var value = getprop(property);                                    # dies if 'property' is invalid
var value = call(func getprop(property), nil, var err = []);      # catches invalid-property-exception and continues

The second line calls getprop(property) just like the first, and returns its value. But if 'property' was invalid then the call() function catches the exception and sets the 'err' vector instead. That vector remains empty on success.

if (size(err))
    print("ERROR: bad property ", property, " (", err[0], ")");   # err[0] contains the die() message
    print("value of ", property, " is ", value);

The first argument of call() is a function object, the second a vector of function arguments (or nil), and the third a vector where the function will return a possible error. For more information on the call() function see the Nasal library documentation.

die() doesn't really care about what its argument is. It doesn't have to be a string, and can be any variable, for example a class. This can be used to pass values through a chain of functions.

var Error = {                                                             # exception class
    new: func(msg, number) {
        return { parents: [Error], message: msg, number: number };

var A = func(a) {
    if (a < 0)
        die(Error.new("negative argument to A", a));                      # throw Error
    return "A received " ~ a;

var B = func(val) {
    var result = A(val);
    print("B finished");      # this line is not reached if A threw an exception
    return result;

var value = call(B, [-4], var err = []);                                  # try B(-4)

if (size(err)) {                                                          # catch (...)
    print("ERROR: ", err[0].message, "; bad value was ", err[0].number);
    die(err[0]);                                                          # re-throw
} else {
    print("SUCCESS: ", value);