5 changed files with 246 additions and 12 deletions
@ -0,0 +1,158 @@ |
|||||
|
/* Takes an AST and converts all of the functions into "closures" |
||||
|
* A closure is a triple of: |
||||
|
* the bound variables in a function or let |
||||
|
* the free variables in a function or let |
||||
|
* a function body or let body and bound values (if it is an escaping closure only) |
||||
|
* The closure has the property that all of the free variables of the function or let |
||||
|
* are in the environment, or an exception is raised because the variable is not bound |
||||
|
* in the current environment. |
||||
|
* A free variable is simply those that are not in the list of formal parameters or bound variables if it is a let |
||||
|
* |
||||
|
* Therefore in order to call a closure one must first extract the actual function and then |
||||
|
* call the function with the environment associated with it. |
||||
|
* For the purposes of type checking it does not matter how the function gets called, the environment |
||||
|
* is only used for looking up the types of names. Formal parameters are given type variables. |
||||
|
* |
||||
|
* The first phase of closure conversion is not really closure conversion exactly. |
||||
|
* All it does is find out the free variables in scope and tie those up into a data structure with their types later. |
||||
|
* The second phase will be done to the CPS language and closures will actually lambda-lifted out potentially. |
||||
|
*/ |
||||
|
|
||||
|
var rep = require("./representation.js"); |
||||
|
var errors = require("./errors.js"); |
||||
|
var parser = require("./parse.js"); |
||||
|
var $ = require("./tools.js"); |
||||
|
var _ = require("underscore"); |
||||
|
|
||||
|
var notEmpty = _.compose($.not, _.partial(_.equal, [])); |
||||
|
|
||||
|
function fvs(stx) { |
||||
|
switch (stx.exprType) { |
||||
|
case "Integer": |
||||
|
return []; |
||||
|
case "Float": |
||||
|
return []; |
||||
|
case "String": |
||||
|
return []; |
||||
|
case "Function": |
||||
|
return []; |
||||
|
case "Nil": |
||||
|
return []; |
||||
|
case "Bool": |
||||
|
return []; |
||||
|
case "Let": |
||||
|
return []; |
||||
|
case "Unary": |
||||
|
return _.flatten([stx.op.ident, fvs(stx.val)]); |
||||
|
case "Definition": |
||||
|
return _.flatten(fvs(stx.val)); |
||||
|
case "Application": |
||||
|
var vs = _.flatten(fvs(stx.p)); |
||||
|
var f_fvs = _.flatten(fvs(stx.func)); |
||||
|
return _.flatten([vs, f_fvs]); |
||||
|
case "If": |
||||
|
if (stx.elseexp) { |
||||
|
var cond_fvs = fvs(stx.condition); |
||||
|
var then_fvs = fvs(stx.thenexp); |
||||
|
var else_fvs = fvs(stx.elseexp); |
||||
|
return _.flatten([cond_fvs, then_fvs, else_fvs]); |
||||
|
} |
||||
|
else { |
||||
|
return _.flatten([fvs(stx.condition), fvs(stx.thenexp)]); |
||||
|
} |
||||
|
break; |
||||
|
case "Name": |
||||
|
return [stx.ident]; |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
function annotate_fvs(stx) { |
||||
|
/* Takes a stx object that is either |
||||
|
* a lambda |
||||
|
* a let |
||||
|
* and returns a closure wrapped around that stx object |
||||
|
*/ |
||||
|
if (stx.exprType !== "Function" && |
||||
|
stx.exprType !== "Let") { |
||||
|
throw errors.JInternalError( |
||||
|
["Tried to calculate the free variables of", |
||||
|
"something that was not a function or let.\n", |
||||
|
"That something was a: " + stx.exprType +"\n"].reduce( |
||||
|
function (a,b) { |
||||
|
return a+" "+b; |
||||
|
}, "")); |
||||
|
} |
||||
|
var variables, free_variables, bound_vars, stx_type; |
||||
|
|
||||
|
switch (stx.exprType) { |
||||
|
case "Let": |
||||
|
bound_vars = stx.pairs.map( |
||||
|
function (stx) { |
||||
|
return stx.ident.ident; |
||||
|
}); |
||||
|
var let_fvs = stx.pairs.map(fvs); |
||||
|
var body_fvs = fvs(stx.body); |
||||
|
variables = _.flatten(let_fvs); |
||||
|
$.extend(variables, _.flatten(body_fvs)); |
||||
|
break; |
||||
|
case "Function": |
||||
|
bound_vars = [stx.p.ident,]; |
||||
|
variables = fvs(stx.body); |
||||
|
break; |
||||
|
} |
||||
|
free_variables = _.difference(_.uniq(variables), bound_vars); |
||||
|
return new rep.Closure(bound_vars, free_variables, stx, []); |
||||
|
} |
||||
|
|
||||
|
/* |
||||
|
* This traverse the tree and gathers up all of the free variables of various functions/let bindings |
||||
|
*/ |
||||
|
function annotate_fvs_all(stx) { |
||||
|
var closure; |
||||
|
switch (stx.exprType) { |
||||
|
case "Let": |
||||
|
closure = annotate_fvs(stx); |
||||
|
closure.body.pairs = closure.body.pairs.map(annotate_fvs_all); |
||||
|
closure.body = annotate_fvs_all(closure.body.body); |
||||
|
return closure; |
||||
|
case "Function": |
||||
|
closure = annotate_fvs(stx); |
||||
|
closure.body.body = annotate_fvs_all(closure.body.body); |
||||
|
return closure; |
||||
|
case "Unary": |
||||
|
stx.val = annotate_fvs_all(stx.val); |
||||
|
return stx; |
||||
|
case "Application": |
||||
|
stx.func = annotate_fvs_all(stx.func); |
||||
|
stx.p = annotate_fvs_all(stx.p); |
||||
|
return stx; |
||||
|
case "If": |
||||
|
if (stx.elseexp) { |
||||
|
stx.condition = annotate_fvs_all(stx.condition); |
||||
|
stx.thenexp = annotate_fvs_all(stx.thenexp); |
||||
|
stx.elseexp = annotate_fvs_all(stx.elseexp); |
||||
|
return stx; |
||||
|
} |
||||
|
else { |
||||
|
stx.condition = annotate_fvs_all(stx.condition); |
||||
|
stx.thenexp = annotate_fvs_all(stx.thenexp); |
||||
|
return stx; |
||||
|
} |
||||
|
break; |
||||
|
case "Definition": |
||||
|
stx.val = annotate_fvs_all(stx.val); |
||||
|
return stx; |
||||
|
default: |
||||
|
return stx; |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
|
||||
|
function test(src) { |
||||
|
var ast = parser.parse(src); |
||||
|
} |
||||
|
|
||||
|
//console.log(test("if something then if a then if b then c else d else rtrrt else some_other_thing"));
|
||||
|
module.exports = { |
||||
|
annotate_fvs : annotate_fvs_all |
||||
|
}; |
@ -0,0 +1,5 @@ |
|||||
|
#! /usr/bin/node
|
||||
|
|
||||
|
var typ = require("representation.js"); |
||||
|
|
||||
|
|
@ -0,0 +1,66 @@ |
|||||
|
#! /usr/bin/node
|
||||
|
|
||||
|
var typ = require("./representation.js"); |
||||
|
var parse = require("./parse.js"); |
||||
|
var tokenizer = require("./tokenize.js"); |
||||
|
var pprint = require("./pprint.js"); |
||||
|
var env = require("./environments.js"); |
||||
|
|
||||
|
|
||||
|
//var istr = fs.readFileSync('/dev/stdin').toString();
|
||||
|
//var istr = "if true then (+ 6 (a+a*b)) else 1";
|
||||
|
var istr = "def (f a) (a + b)" |
||||
|
var ast = parse.parseFull(tokenizer.tokenize(istr)); |
||||
|
|
||||
|
function apply(func, p) { |
||||
|
return func(p); |
||||
|
} |
||||
|
|
||||
|
function evaluateAll(ast, environment) { |
||||
|
var l = ast.length; |
||||
|
var evaled = []; |
||||
|
for (var i = 0; i < l; i++) { |
||||
|
// should look for closures?
|
||||
|
evaled.push(evaluate(ast[i], environment)); |
||||
|
} |
||||
|
return evaled; |
||||
|
} |
||||
|
|
||||
|
function evaluate(ast, environment) { |
||||
|
if (ast.exprType == "Application") { |
||||
|
return apply(evaluate(ast.func, environment), evaluate(ast.p, environment)); |
||||
|
} |
||||
|
else if (ast.exprType === "Unary") { /* Unary function application */ |
||||
|
return apply(evaluate(ast.op, environment), evaluate(ast.val, environment)); |
||||
|
} |
||||
|
else if (ast.exprType === "Name") { |
||||
|
//console.log(env.lookup(ast.ident, environment));
|
||||
|
return env.lookup(ast.ident, environment); |
||||
|
} |
||||
|
else if (ast.exprType === "If") { |
||||
|
if (evaluate(ast.condition, environment)) { |
||||
|
return evaluate(ast.thenexp, environment); |
||||
|
} |
||||
|
else { |
||||
|
return evaluate(ast.elseexp, environment); |
||||
|
} |
||||
|
} |
||||
|
else if (ast.exprType === "Definition") { |
||||
|
console.log(ast); |
||||
|
} |
||||
|
else { |
||||
|
return ast.val; |
||||
|
} |
||||
|
} |
||||
|
var testenv = env.makeEnv("toplevel", |
||||
|
[ |
||||
|
["+", function(a) { return function(b) { return a + b; } }], |
||||
|
["*", function(a) { return function(b) { return a * b; } }], |
||||
|
["a", 2], |
||||
|
["b", 3]]); |
||||
|
|
||||
|
var all = evaluate(ast[0][ast[0].length - 1], testenv); |
||||
|
console.log(all); |
||||
|
//console.log("%j", testenv);
|
||||
|
//console.log("%j", ast[0][ast[0].length - 1]);
|
||||
|
//console.log("%j", ast[0][ast[0].length - 1]);
|
Loading…
Reference in new issue