Howto:Nasal Metaprogramming: Difference between revisions

From FlightGear wiki
Jump to navigation Jump to search
No edit summary
m (→‎bind(): huh... I always believed bind() returned its first argument and mutated the closures, but apparently it never has... not since it was introduced in 2005: https://github.com/andyross/nasal/commit/3bb78b7bdcd01ae9ff6afee51338253a0fea99f6.)
 
Line 72: Line 72:


== bind() ==
== bind() ==
Mutates the closures of a function, with the convenience return of first argument (the function).
Creates a new function based on the function that is passed and sets its first closure and closure function.


;1st argument
;1st argument
* The function whose closures will be modified.
* The function whose closures will be modified.
;2nd argument
;2nd argument (optional)
* The first closure (i.e., outer namespace) to bind to, as a hash.
* The first closure (i.e., outer namespace) to bind to, as a hash.
;3rd argument (optional)
;3rd argument (optional)
Line 88: Line 88:
var namespace = {};
var namespace = {};
# re-bind the function to that namespace, which becomes the first closure of the function, erasing any/all others
# re-bind the function to that namespace, which becomes the first closure of the function, erasing any/all others
bind(fn, namespace);
var fn = bind(fn, namespace);
# assertion
# assertion
if (closure(fn, 0) != namespace) die("bind() or closure() error!");
if (closure(fn, 0) != namespace) die("bind() or closure() error!");
Line 94: Line 94:
var other_fn = caller(0)[1];
var other_fn = caller(0)[1];
# re-bind the function to the namespace and all closures of *other_fn*
# re-bind the function to the namespace and all closures of *other_fn*
bind(fn, namespace, other_fn);
var fn = bind(fn, namespace, other_fn);
# same assertion holds
# same assertion holds
if (closure(fn, 0) != namespace) die("bind() or closure() error!");
if (closure(fn, 0) != namespace) die("bind() or closure() error!");
Line 104: Line 104:
# re-bind the function to the default closures, viz., namespace=caller(0)[0] and fn=caller(0)[1]
# 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())
# (this is the default when we have a *func {}* expression, or equivalently, any function we get from compile())
bind(fn, caller(0)[0], caller(0)[1]);
var fn = bind(fn, caller(0)[0], caller(0)[1]);
</syntaxhighlight>
</syntaxhighlight>



Latest revision as of 02:39, 29 June 2014

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 );