Browse Source

Merge pull request #23 from oftn/master

closures!
master
Wesley Kerfoot 10 years ago
parent
commit
8d61b9d640
  1. 158
      closures.js
  2. 5
      cps.js
  3. 12
      environments.js
  4. 17
      parse.js
  5. 66
      vm.js

158
closures.js

@ -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
};

5
cps.js

@ -0,0 +1,5 @@
#! /usr/bin/node
var typ = require("representation.js");

12
environments.js

@ -27,23 +27,25 @@ function extend(env, values) {
function makeEnv(name, values) {
var env = {};
env.name = name;
env.bindings = {};
for (var i = 0; i < values.length; i++) {
name = values[i][0].val;
name = values[i][0];
var val = values[i][1];
env[name] = val;
env.bindings[name] = val;
}
return env;
}
function lookup(name, env) {
var value = env[name];
var value = env.bindings[name];
if (!value) {
throw errors.UnboundError(name, env.name);
throw errors.JUnboundError(name, env.name);
}
return value;
}
module.exports = {
lookup : lookup,
extend : extend
extend : extend,
makeEnv : makeEnv
};

17
parse.js

@ -7,7 +7,7 @@ var tokenizer = require("./tokenize.js");
var desugarer = require("./desugar.js");
var pprint = require("./pprint.js");
var error = require("./errors.js");
var closure = require("./closures.js");
var print = console.log;
function sourcePos(tokens, linenum, charnum) {
@ -808,9 +808,11 @@ function parse(tokens) {
function parseFull(tokenized) {
var ast = [];
var typeBindings = {};
var current;
try {
while (tokenized.length > 0) {
ast.push(desugarer.desugar(parse(tokenized), typeBindings));
current = closure.annotate_fvs(desugarer.desugar(parse(tokenized), typeBindings));
ast.push(current);
}
return [ast, typeBindings];
} catch (e) {
@ -828,9 +830,10 @@ function parseFull(tokenized) {
module.exports = { parse : function(str) {
return parseFull(tokenizer.tokenize(str));
},
tokenize : tokenizer.tokenize
tokenize : tokenizer.tokenize,
parseFull : parseFull,
};
var istr = fs.readFileSync('/dev/stdin').toString();
var testParse = parseFull(tokenizer.tokenize(istr));
console.log(testParse[1]);
console.log(testParse[0].map(pprint.pprint));
//var istr = fs.readFileSync('/dev/stdin').toString();
//var testParse = parseFull(tokenizer.tokenize(istr));
//console.log(testParse[1]);
//console.log(testParse[0].map(pprint.pprint));

66
vm.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…
Cancel
Save