I am writing a compiler and I need to convert Mini Pascal (a simple version of Pascal) to Jasmin byte code.
But how do I declare the nested function in the Jasmin language?
Because
function tt(I): I
can only pass in one Integer (which isrr
) how do I transfer the variabled
from functionss
to functiontt
?
C++ (translated from Mini Pascal by myself):
#include <iostream>
using namespace std;
int a, b;
int ss(int rr)
{
int d;
int tt(int rr)
{
int e;
e = rr * 3;
return rr + d - e + b;
}
d = rr - 4;
return tt(rr);
}
int main()
{
b = -5;
a = ss(3);
cout << a;
return 0;
}
Mini Pascal:
PROGRAM test_nested_function(input, output, error);
VAR a, b : INTEGER;
FUNCTION ss(rr :INTEGER) : INTEGER;
VAR d : INTEGER;
FUNCTION tt(rr : INTEGER) : INTEGER;
VAR e : INTEGER;
BEGIN
e := rr * 3;
tt := rr + d - e + b;
END;
BEGIN
d := rr - 4;
ss := tt(rr)
END;
BEGIN
b := -5;
a := ss(3);
writelnI(a)
END.
When you're translating from a language that has closures to one that doesn't, you have to retain a reference to the outer function somehow. There are, broadly speaking, two ways to do that: nested closures and flat closures. The important thing to note is that, in both of these cases, your closure is not an ordinary top level function. It's some structure that happens to be callable.
Nested Closures
In a nested closure, your nested function simply maintains a reference to the enclosing scope as a pointer to some abstract data structure. So,
tt
maintains a reference toss
(in the abstract) and then can access thed
variable viass.d
. Java pseudo-code for the same:In this model, your
ss
function gets a closure frame in your program. All of its local variables (or at least, those needed by the closures) get put into that structure rather than being declared as actual local variables. Then that closure structure gets passed to any nested function that needs it.If you have functions nested multiple layers deep, then a closure maintains a reference to the immediately enclosing scope. If it needs to access variables that exist multiple layers up the closure stack, it will do so through indirect references, since each closure frame retains a reference to its own enclosing scope.
Flat Closures
With flat closures, your nested function receives copies of the actual concrete variables from the enclosing scope. Your
tt
is still a structure, but rather than storing a reference to some otherss
structure, it gets the actualint
it needs.This is a more space-efficient approach. Rather than storing a bunch of pointers everywhere, you store only the actual data, and the data you need. Further, if you have nested closures, you automatically flatten them when you create these specialized structures, so there's less indirection.
However, care must be taken if you have mutable closures. If you have a
final
variable, then there's no problem. In fact, this is exactly why the Java language forbids non-final
variables from being closed over.If you want to allow mutable variables in closures, you'll need to explicitly add in a layer of indirection. This can be a simple class that stores one instance variable, and then you can use that variable in both the enclosing scope and the closure.
You need to wrap any local variable in a
Cell<T>
if it is (a) used in a closure, and (b) reassigned to at any point. Variables that arefinal
(hence never reassigned) need not be wrapped, and those that are only used locally (never in a closure) need not be wrapped.