Howto:Nasal Metaprogramming

From FlightGear wiki
Jump to navigation Jump to search
WIP.png Work in progress
This article or section will be worked on in the upcoming hours or days.
See history for the latest developments.

call()

var foo = func {
 print( me.x );
};

call( foo, [], {x:100} );

closure()

Find the closure of a function, or nil if it does not exist. "Closure" is the name given to an outer namespace of a function; when considering a variable name (like "i"), that variable name is looked for in the current execution frame (the "locals" namespace) then any closures of the function. Closures are stored via two items in the internal structure of a function: a namespace (hash), which is the first to recurse to, and a link to another function (or nil if there isn't one), which stores an additional namespace and potentially another outer function. This creates a chain of namespaces stored in the functions.

The default closures for a new function (either a func {} expression or the return from compile()) are the immediate caller's namespace and function, so any local variables declared before/after the function (not inside!) become the first closure, and any closures of the current function are found by the new function. The closures, once set, become semi-permanent, and remain with the function until changed via bind(), even though they may be referenced from completely different places and namespaces. For more on closures see #bind().

1st argument
  • The function to find the closure of.
2nd argument (optional; default: 0)
  • The number of closures to look through.

caller()

compile ()

var x = 100;
var source_code = "print(x);";
var code = compile(source_code);
code();


var location = func {
  var called_by = caller();
  var file = called_by [2];
  var line = called_by [3];
  return "file:"~ file ~'@ line:'~ line;
}

var x = 100;
var source_code = "print(x);";
print( location() );
var code = compile(source_code, location() );
code();


var namespace = {
 x:1, y:2, z:3,
 print: print
};

var source_code = "print(x);";

var code = compile(source_code );
code = bind(code, namespace,nil);
code();

bind()

Creates a new function based on the function that is passed and sets its first closure and closure function.

1st argument
  • The function whose closures will be modified.
2nd argument (optional)
  • The first closure (i.e., outer namespace) to bind to, as a hash.
3rd argument (optional)
  • Another function, whose closure(s) are recursed into.

Example

# make a function
var fn = func ;
# make a "namespace"
var namespace = {};
# re-bind the function to that namespace, which becomes the first closure of the function, erasing any/all others
var fn = bind(fn, namespace);
# assertion
if (closure(fn, 0) != namespace) die("bind() or closure() error!");
# another function: the currently executing one
var other_fn = caller(0)[1];
# re-bind the function to the namespace and all closures of *other_fn*
var fn = bind(fn, namespace, other_fn);
# same assertion holds
if (closure(fn, 0) != namespace) die("bind() or closure() error!");
# assertion for all other closures: closure(fn, i) must equal closure(other_fn, i-1)
var i = 1;
while ((var cl = closure(other_fn, i-1)) != nil) {
    if (closure(fn, i) != nil) die("bind() or closure() error!");
}
# re-bind the function to the default closures, viz., namespace=caller(0)[0] and fn=caller(0)[1]
# (this is the default when we have a *func {}* expression, or equivalently, any function we get from compile())
var fn = bind(fn, caller(0)[0], caller(0)[1]);

Calling methods in Bindings

var function = namespace.class.method;
var arguments = ["Hello World"];
var me = caller(0)[0];
call(function, arguments, me );