diff --git a/closures.js b/closures.js new file mode 100644 index 0000000..caf3198 --- /dev/null +++ b/closures.js @@ -0,0 +1,159 @@ +/* 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.export = { + test : test, + annotate_fvs: annotate_fvs_all +}; diff --git a/cps.js b/cps.js new file mode 100644 index 0000000..afb69f5 --- /dev/null +++ b/cps.js @@ -0,0 +1,5 @@ +#! /usr/bin/node + +var typ = require("representation.js"); + + diff --git a/parse.js b/parse.js index 08b6052..2ee2504 100755 --- a/parse.js +++ b/parse.js @@ -828,9 +828,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)); diff --git a/vm.js b/vm.js new file mode 100644 index 0000000..e5410be --- /dev/null +++ b/vm.js @@ -0,0 +1,24 @@ +#! /usr/bin/node + +var typ = require("./representation.js"); +var parse = require("./parse.js"); +var tokenizer = require("./tokenize.js"); +var pprint = require("./pprint.js"); + + +//var istr = fs.readFileSync('/dev/stdin').toString(); +var istr = "lambda a b -> (a + b)"; +var ast = parse.parseFull(tokenizer.tokenize(istr)); + + +function evaluate(ast, environment) { + var l = ast.length; + for (var i = 0; i < l; i++) { + if (ast[i].exprType == "Function") { + console.log(ast[i]); + } + } +} + +evaluate(ast[0], false); +