From a6c721da56a3615a5dab91a0f2091e0d7289147a Mon Sep 17 00:00:00 2001 From: Dan Kaplun Date: Tue, 3 Jun 2014 00:14:32 -0500 Subject: [PATCH 1/7] Adds simple NodeJS .gitignore --- .gitignore | 25 +++++++++++++++++++++++++ 1 file changed, 25 insertions(+) create mode 100644 .gitignore diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..da23d0d --- /dev/null +++ b/.gitignore @@ -0,0 +1,25 @@ +# Logs +logs +*.log + +# Runtime data +pids +*.pid +*.seed + +# Directory for instrumented libs generated by jscoverage/JSCover +lib-cov + +# Coverage directory used by tools like istanbul +coverage + +# Grunt intermediate storage (http://gruntjs.com/creating-plugins#storing-task-files) +.grunt + +# Compiled binary addons (http://nodejs.org/api/addons.html) +build/Release + +# Dependency directory +# Deployed apps should consider commenting this line out: +# see https://npmjs.org/doc/faq.html#Should-I-check-my-node_modules-folder-into-git +node_modules -- 2.30.2 From bd801f8e18a95cd4fd55aa7e97b48049234489b2 Mon Sep 17 00:00:00 2001 From: Dan Kaplun Date: Tue, 3 Jun 2014 00:17:14 -0500 Subject: [PATCH 2/7] Adds package.json --- package.json | 26 ++++++++++++++++++++++++++ 1 file changed, 26 insertions(+) create mode 100644 package.json diff --git a/package.json b/package.json new file mode 100644 index 0000000..f3044c2 --- /dev/null +++ b/package.json @@ -0,0 +1,26 @@ +{ + "name": "JLambda", + "version": "0.0.0", + "description": "yet another static functional language implemented in JS", + "scripts": { + "test": "./test.js" + }, + "repository": { + "type": "git", + "url": "https://github.com/nisstyre56/JLambda.git" + }, + "keywords": [ + "static", + "functional", + "language" + ], + "author": "nisstyre56", + "license": "ΩF:∅", + "bugs": { + "url": "https://github.com/nisstyre56/JLambda/issues" + }, + "homepage": "https://github.com/nisstyre56/JLambda", + "dependencies": { + "underscore": "^1.6.0" + } +} -- 2.30.2 From 5b847fe09070f4db6c7ec075b44cc6f4692fcfca Mon Sep 17 00:00:00 2001 From: Dan Kaplun Date: Tue, 3 Jun 2014 00:21:27 -0500 Subject: [PATCH 3/7] Adds installation and usage instructions to README --- README.md | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) diff --git a/README.md b/README.md index 69e5928..57ad6bb 100644 --- a/README.md +++ b/README.md @@ -1,3 +1,6 @@ +JLambda +======= + JLambda is a functional language in the spirit of languages such as Scheme, SML, or Clean. It aims to have a very flexible syntax and a clean and easy to understand type system. Another goal is to generate very efficient JavaScript @@ -11,3 +14,18 @@ JLambda also aims to support concurrency which will be built on a continuation-passing style intermediate language. I have not figured out how scheduling threads will work, or whether I will provide any programmer directed way of scheduling (i.e. yield). + +Installation +------------ + + git clone git@github.com:nisstyre56/JLambda.git + cd JLambda + npm install + +Usage +----- + +Since the language is currently under heavy construction, the parser is the +entry point for now: + + cat example.jl | ./parse.js -- 2.30.2 From 826b19a4dee029e24d75595c05d1aeee457e4d93 Mon Sep 17 00:00:00 2001 From: nisstyre56 Date: Sun, 31 Aug 2014 04:44:03 -0400 Subject: [PATCH 4/7] restructure for evaluator --- closures.js | 159 ++++++++++++++++++++++++++++++++++++++++++++++++++++ cps.js | 5 ++ parse.js | 11 ++-- vm.js | 24 ++++++++ 4 files changed, 194 insertions(+), 5 deletions(-) create mode 100644 closures.js create mode 100644 cps.js create mode 100644 vm.js 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); + -- 2.30.2 From cc035dc383c1c4c5a7db01478f880cce73d3eb9d Mon Sep 17 00:00:00 2001 From: nisstyre56 Date: Mon, 8 Sep 2014 10:07:55 -0400 Subject: [PATCH 5/7] progress on test evaluator --- vm.js | 30 ++++++++++++++++++++++++------ 1 file changed, 24 insertions(+), 6 deletions(-) diff --git a/vm.js b/vm.js index e5410be..d7e6078 100644 --- a/vm.js +++ b/vm.js @@ -7,18 +7,36 @@ var pprint = require("./pprint.js"); //var istr = fs.readFileSync('/dev/stdin').toString(); -var istr = "lambda a b -> (a + b)"; +var istr = "(a + b)"; var ast = parse.parseFull(tokenizer.tokenize(istr)); +function apply(func, p, environment) { + var full_func = evaluate(func, environment); + return [full_func, evaluate(p, environment)]; +} -function evaluate(ast, environment) { +function evaluateAll(ast, environment) { var l = ast.length; + var evaled = []; for (var i = 0; i < l; i++) { - if (ast[i].exprType == "Function") { - console.log(ast[i]); - } + evaled.push(evaluate(ast[i], environment)); } + return evaled; } -evaluate(ast[0], false); +function evaluate(ast, environment) { + if (ast.exprType == "Application") { + return apply(ast.func, ast.p, environment); + } + else if (ast.exprType === "Unary") { /* Unary function application */ + return apply(ast.op, ast.val, environment); + } + else if (ast.exprType === "Name") { + return ast.ident; + } + else { + return ast.val; + } +} +console.log("%j", evaluateAll(ast[0], false)); -- 2.30.2 From b66501f7a7cc4b96608f59316e5f298c629575ea Mon Sep 17 00:00:00 2001 From: nisstyre56 Date: Sun, 7 Dec 2014 16:16:55 -0500 Subject: [PATCH 6/7] prevent clashes in the environment from the 'name' attribute --- environments.js | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/environments.js b/environments.js index d88426e..f0dc277 100644 --- a/environments.js +++ b/environments.js @@ -27,16 +27,17 @@ 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); } @@ -45,5 +46,6 @@ function lookup(name, env) { module.exports = { lookup : lookup, - extend : extend + extend : extend, + makeEnv : makeEnv }; -- 2.30.2 From cd1a4785602852a3a48a0780742c0fd38862d83c Mon Sep 17 00:00:00 2001 From: nisstyre56 Date: Mon, 5 Jan 2015 14:56:57 -0500 Subject: [PATCH 7/7] closures! --- closures.js | 5 ++--- environments.js | 2 +- parse.js | 6 ++++-- vm.js | 40 ++++++++++++++++++++++++++++++++-------- 4 files changed, 39 insertions(+), 14 deletions(-) mode change 100644 => 100755 vm.js diff --git a/closures.js b/closures.js index caf3198..c261113 100644 --- a/closures.js +++ b/closures.js @@ -153,7 +153,6 @@ function test(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 +module.exports = { + annotate_fvs : annotate_fvs_all }; diff --git a/environments.js b/environments.js index f0dc277..dedd90c 100644 --- a/environments.js +++ b/environments.js @@ -39,7 +39,7 @@ function makeEnv(name, values) { function lookup(name, env) { var value = env.bindings[name]; if (!value) { - throw errors.UnboundError(name, env.name); + throw errors.JUnboundError(name, env.name); } return value; } diff --git a/parse.js b/parse.js index 2ee2504..9d9bbc8 100755 --- a/parse.js +++ b/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) { diff --git a/vm.js b/vm.js old mode 100644 new mode 100755 index d7e6078..e4ace80 --- a/vm.js +++ b/vm.js @@ -4,21 +4,23 @@ 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 = "(a + b)"; +//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, environment) { - var full_func = evaluate(func, environment); - return [full_func, evaluate(p, environment)]; +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; @@ -26,17 +28,39 @@ function evaluateAll(ast, environment) { function evaluate(ast, environment) { if (ast.exprType == "Application") { - return apply(ast.func, ast.p, environment); + return apply(evaluate(ast.func, environment), evaluate(ast.p, environment)); } else if (ast.exprType === "Unary") { /* Unary function application */ - return apply(ast.op, ast.val, environment); + return apply(evaluate(ast.op, environment), evaluate(ast.val, environment)); } else if (ast.exprType === "Name") { - return ast.ident; + //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; } } -console.log("%j", evaluateAll(ast[0], false)); +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]); -- 2.30.2