Browse Source

readability fixes, a few bug fixes, can calculate free variables of let blocks, environments not being passed yet or closures created

pull/1/head
nisstyre56 11 years ago
parent
commit
b890053d20
  1. 2
      cexps.js
  2. 94
      closure_conversion.js
  3. 2
      desugar.js
  4. 13
      environments.js
  5. 10
      errors.js
  6. 18
      evaluate.js
  7. 49
      parse.js
  8. 33
      representation.js
  9. 68
      tokenize.js
  10. 40
      tools.js
  11. 19
      typecheck.js

2
cexps.js

@ -40,7 +40,7 @@ function offset(i, v, w, next_cexp) {
offset.prototype = cexp;
function app(k, vs) {
this.k =k;
this.k = k;
this.vs = vs;
return this;
}

94
closure_conversion.js

@ -8,8 +8,8 @@
* in the current environment.
* A free variable is simply those that are not in the list of formal parameters.
* We start with the global environment and traverse the AST. Every time a new function is entered
* the current environment gets extended with the formal parameters of the function.
* When a let is encountered the current environment also gets extended.
* the current environment gets $.extended with the formal parameters of the function.
* When a let is encountered the current environment also gets $.extended.
* The algorithm continues for any further function definitions in that branch
* otherwise it just stops for that particular branch and continues with the rest of the AST
*
@ -24,33 +24,11 @@ var env = require("./environments.js");
var errors = require("./errors.js");
var parser = require("./parse.js");
var pprint = require("./pprint.js");
var tool = require("./tools.js");
var $ = require("./tools.js");
/*function convert(stx, cur_types, cur_exprs) {
switch (stx.exprType) {
case "If":
case "Definition":
case "Name":
case "Application":
case "Function":
case "Let":
default:
return stx;
}
}*/
function fvs(stx) {
/*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 notEmpty = $.compose($.not, $.eq([]));
function fvs_helper(stx) {
switch (stx.exprType) {
case "Integer":
return [];
@ -62,38 +40,62 @@ function fvs(stx) {
return [];
case "Nil":
return [];
case "List":
return [];
case "Bool":
return [];
case "FunctionDefinition":
return [];
case "Let":
return stx.pairs.map(fvs);
return [];
case "Unary":
return fvs(stx.val);
return $.flatten([stx.op.ident, fvs_helper(stx.val)]);
case "Definition":
return [fvs(stx.val)];
return $.flatten(fvs_helper(stx.val));
case "Application":
var vs = fvs(stx.p);
var f_fvs = fvs(stx.func);
return [].concat.apply([], [vs, f_fvs]);
var vs = $.flatten(fvs_helper(stx.p));
var f_fvs = $.flatten(fvs_helper(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 [cond_fvs, then_fvs, else_fvs];
var cond_fvs = fvs_helper(stx.condition);
var then_fvs = fvs_helper(stx.thenexp);
var else_fvs = fvs_helper(stx.elseexp);
return $.flatten([cond_fvs, then_fvs, else_fvs]);
}
else {
return [fvs(stx.condition)] + [fvs(stx.thenexp)];
return $.flatten([fvs_helper(stx.condition), fvs_helper(stx.thenexp)]);
}
case "Name":
return stx.ident;
}
}
var ast = parser.parse("(^ wat (a+(ar*b*c^twerp+\"sdfdsfsdfsdfsdf\")*rt))")[0];
console.log(pprint.pprint(ast));
console.log(tool.unique(fvs(ast)));
//console.log(ast);
function fvs(stx) {
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;
switch (stx.exprType) {
case "Let":
var bound_vars = stx.pairs.map(
function (stx) {
return stx.ident.ident;
});
var let_fvs = stx.pairs.map(fvs_helper);
var body_fvs = fvs_helper(stx.body);
variables = $.flatten(let_fvs);
$.extend(variables, $.flatten(body_fvs));
}
return $.difference($.unique($.flatten(variables)), bound_vars);
}
//var ast = parser.parse("let { c = trtr a = let {tttt = (rtertret^yyyy) } let { dfsdf = let { asdsd = 3434 } gdfgdfg } (45+(asdddy*uyuy)) q = ((lambda x y -> (x+y)) 4 ui) } (^ wat (a+(ar*b*c^twerp+\"sdfdsfsdfsdfsdf\")*rt))")[0];
//var ast = parser.parse("let { a = let { b = let {dsdfgf = sddd } fdgfg } gggggg } t")[0];
//console.log(pprint.pprint(ast));
var ast = parser.parse("let { a = 12 b = (a + t) } (a + b * d)")[0];
console.log(fvs(ast));

2
desugar.js

@ -32,7 +32,7 @@ function desugarLet(stx) {
}
function desugar(stx) {
switch (stx.exprType) {
switch (stx.exprType) {
case "If":
if (stx.elseexp)
return new typ.If(desugar(stx.condition), desugar(stx.thenexp), desugar(stx.elseexp));

13
environments.js

@ -11,10 +11,15 @@ var rep = require("./representation.js");
function extend(env, values) {
for (var i = 0; i < values.length; i++) {
env[values[i][0].val] = values[i][1];
var new_env = {};
var env_keys = Object.keys(env);
for (var i = 0; i < env_keys.length; i++) {
new_env[env_keys[i]] = env[env_keys[i]];
}
return env;
for (i = 0; i < values.length; i++) {
new_env[values[i][0].val] = values[i][1];
}
return new_env;
}
// creates a new environment initialized with the pairs in values
@ -22,7 +27,7 @@ function makeEnv(name, values) {
var env = {};
env.name = name;
for (var i = 0; i < values.length; i++) {
var name = values[i][0].val;
name = values[i][0].val;
var val = values[i][1];
env[name] = val;
}

10
errors.js

@ -26,6 +26,14 @@ function JTypeError(linenum, charnum, token, message) {
return this;
}
function JInternalError(message) {
this.errormessage = message;
console.log(message);
return this;
}
module.exports =
{JSyntaxError : JSyntaxError,
JTypeError : JTypeError};
JTypeError : JTypeError,
JInternalError : JInternalError
};

18
evaluate.js

@ -0,0 +1,18 @@
var parse = require("./parse.js").parse;
function isAtom(x) {
return stx.exprType != "List";
}
function evaluate(exp, env) {
if (isAtom(exp)) {
switch (exp.exprType) {
case "Function":
return evaluate(invoke(exp, env), env);
case "Name":
return lookup(expr.val);
default:
return expr.val;
}
}
}

49
parse.js

@ -21,7 +21,7 @@ function snd(ts) {
/*Checks if the next token is not followed by any of ``checks'' */
function notFollowedBy(tokens, checks, linenum, charnum) {
if (!fst(tokens)) {
throw error.JSyntaxError(0,0,"unexpected end of source")
throw error.JSyntaxError(0,0,"unexpected end of source");
}
var nextT = fst(tokens)[0];
if (checks.some(function (x) {
@ -71,7 +71,7 @@ function parseMany(parse, exprType, valid, tokens, charnum, linenum) {
if (!exprType(fst(results).exprType))
break;
if (fst(tokens))
current = fst(tokens)[0]
current = fst(tokens)[0];
else
throw error.JSyntaxError(charnum, linenum, "Unexpected end of source");
if (tokens.length <= 1)
@ -115,15 +115,16 @@ function parseBetween(exprType, between, tokens, charnum, linenum) {
}
function parseList(tokens) {
var xs;
if (fst(tokens)[0] === "right_square") {
var xs = [];
xs = [];
}
else if (fst(tokens)[0] === "comma") {
tokens.pop();
var xs = [];
xs = [];
}
else {
var xs = parseBetween(function (x) { return true; }, "comma", tokens, fst(tokens)[3], fst(tokens)[2]);
xs = parseBetween(function (x) { return true; }, "comma", tokens, fst(tokens)[3], fst(tokens)[2]);
}
if (!fst(tokens) || fst(tokens)[0] !== "right_square") {
throw error.JSyntaxError(fst(tokens)[3],
@ -137,16 +138,17 @@ function parseList(tokens) {
function parseDefFunction(tokens) {
var fname = parse(tokens);
var parameters;
if (fname.exprType != "Name") {
throw error.JSyntaxError(fst(tokens)[3],
fst(tokens)[2],
"Expected an identifier in function definition");
}
if (fst(tokens)[0] === "right_paren") {
var parameters = [];
parameters = [];
}
else {
var parameters = parseMany(parse,
parameters = parseMany(parse,
validName,
validFormPar,
tokens,
@ -209,6 +211,7 @@ function parseLetForm(tokens, linenum, charnum) {
function parseLetFunction(tokens, linenum, charnum) {
var fname = parse(tokens);
var parameters;
if (fname.exprType != "Name") {
throw error.JSyntaxError(fst(tokens)[3],
@ -216,10 +219,10 @@ function parseLetFunction(tokens, linenum, charnum) {
"Expected an identifier in function definition");
}
if (fst(tokens)[0] === "right_paren") {
var parameters = [];
parameters = [];
}
else {
var parameters = parseMany(parse,
parameters = parseMany(parse,
validName,
validFormPar,
tokens,
@ -235,7 +238,7 @@ function parseLetFunction(tokens, linenum, charnum) {
if (fst(tokens)[0] !== "arrow") {
throw error.JSyntaxError(fst(tokens)[3],
fst(tokens)[2],
"Function parameters in let/def form must be followed by ->")
"Function parameters in let/def form must be followed by ->");
}
tokens.pop();
var body = parse(tokens);
@ -435,7 +438,7 @@ function parseLambda(tokens) {
fst(tokens)[2],
"arrow must follow parameters in lambda, not "+fst(tokens)[0]);
}
tokens.pop()
tokens.pop();
var body = parse(tokens);
return new typ.FuncT(parameters, body);
}
@ -448,8 +451,10 @@ var validOperator = makeChecker(["identifier"]);
/* Parses function application (either infix or prefix) */
function computeApp(tokens, charnum, linenum) {
var lhs = parse(tokens);
var next;
var result;
if (fst(tokens))
var next = fst(tokens);
next = fst(tokens);
else {
throw error.JSyntaxError(linenum,
charnum,
@ -457,7 +462,7 @@ function computeApp(tokens, charnum, linenum) {
}
if (typ.OPInfo[next[1]]) {
/* it's an infix expression */
var result = parseInfix(tokens, 1, lhs, linenum, charnum);
result = parseInfix(tokens, 1, lhs, linenum, charnum);
if (!fst(tokens) || fst(tokens)[0] !== "right_paren") {
throw error.JSyntaxError(linenum,
charnum,
@ -495,7 +500,7 @@ function computeApp(tokens, charnum, linenum) {
*/
function parseInfix(tokens, minPrec, lhs, linenum, charnum) {
if (!lhs) {
var lhs = parse(tokens);
lhs = parse(tokens);
}
while (true) {
var cur = fst(tokens);
@ -524,8 +529,9 @@ function parseInfix(tokens, minPrec, lhs, linenum, charnum) {
function parse(tokens) {
var charnum = fst(tokens)[2];
var linenum = fst(tokens)[3];
var toktype;
if (fst(tokens)) {
var toktype = fst(tokens)[0];
toktype = fst(tokens)[0];
}
else {
process.exit(code=1);
@ -570,9 +576,9 @@ function parse(tokens) {
}
}
var istr = fs.readFileSync('/dev/stdin').toString();
//var istr = fs.readFileSync('/dev/stdin').toString();
function parseFull(tokenized) {
var ast = new Array();
var ast = [];
try {
while (tokenized.length > 0) {
var parsed = desugarer.desugar(parse(tokenized));
@ -584,7 +590,14 @@ function parseFull(tokenized) {
process.exit(1);
}
}
console.log(parseFull(tokenizer.tokenize(istr)).map(pprint.pprint).join("\n"));
module.exports = { parse : function(str) {
return parseFull(tokenizer.tokenize(str));
}
};
//console.log(parseFull(tokenizer.tokenize(istr)).map(pprint.pprint).join("\n"));
//console.log(tokenizer.tokenize(istr));
//console.log(parseFull(tokenizer.tokenize(istr)));

33
representation.js

@ -168,13 +168,6 @@ function TypeOp(name, params, body) {
return this;
}
function TypeBinding(name, type) {
this.name = name;
this.type = type;
return this;
}
//convenience function to construct binary operators
//assumes that the identifier refers to the name of a primitive
//operation
@ -195,11 +188,22 @@ function makeApp(name, parameters) {
}
function makeGensym() {
var n = 0;
return function() {
var x = "G"+n;
n = n + 1;
return x;
};
}
var gensym = makeGensym();
OPInfo = {"+" : [3, "Left"],
"-" : [3, "Left"],
"*" : [4, "Left"],
"/" : [4, "Left"],
"^" : [5, "Right"],
"-" : [3, "Left"],
"*" : [4, "Left"],
"/" : [4, "Left"],
"^" : [5, "Right"],
"++" : [3, "Left"],
"==" : [2, "Left"],
">" : [2, "Left"],
@ -234,5 +238,8 @@ module.exports =
DefFunc : DefFunc,
UnaryOp : UnaryOp,
Nil : Nil,
LetExp : LetExp
}
LetExp : LetExp,
gensym : gensym,
TypeVar : TypeVar,
TypeOp : TypeOp
};

68
tokenize.js

@ -136,70 +136,83 @@ function tokenize(tokstream, matchop) {
var tokens = [];
var charnum = 1;
var linenum = 1;
var i, result, lambda, num;
while (tokstream) {
switch (tokstream[0].charCodeAt()) {
/* falls through */
case 9: // '\t'
charnum++;
tokens.push(["whitespace", '\t', charnum, linenum]);
tokstream = tokstream.substr(1);
break;
/* falls through */
case 32: // ' '
charnum++;
tokens.push(["whitespace", ' ', charnum, linenum]);
tokstream = tokstream.substr(1);
break;
/* falls through */
case 10: // '\n'
linenum++;
charnum = 1;
tokens.push(["whitespace", '\n', charnum, linenum]);
tokstream = tokstream.substr(1);
break;
/* falls through */
case 44: // ','
charnum++;
tokens.push(["comma", ",", charnum, linenum]);
tokstream = tokstream.substr(1);
break;
/* falls through */
case 40: // '('
charnum++;
tokens.push(["left_paren", '(', charnum, linenum]);
tokstream = tokstream.substr(1);
break;
/* falls through */
case 41: // ')'
charnum++;
tokens.push(["right_paren", ')', charnum, linenum]);
tokstream = tokstream.substr(1);
break;
/* falls through */
case 123: // '{'
charnum++;
tokens.push(["left_brace", '{', charnum, linenum]);
tokstream = tokstream.substr(1);
break;
/* falls through */
case 125: // '}'
charnum++;
tokens.push(["right_brace", '}', charnum, linenum]);
tokstream = tokstream.substr(1);
break;
/* falls through */
case 91: // '['
charnum++;
tokens.push(["left_square", '[', charnum, linenum]);
tokstream = tokstream.substr(1);
break;
/* falls through */
case 93: // ']'
charnum++;
tokens.push(["right_square", ']', charnum, linenum]);
tokstream = tokstream.substr(1);
break;
/* falls through */
case 34: // '"'
var result = tokenizeStr(tokstream, charnum, linenum);
result = tokenizeStr(tokstream, charnum, linenum);
var str = result[1];
var i = result[0];
i = result[0];
tokens.push(str);
tokstream = tokstream.substr(i);
break;
/* falls through */
case 45: // '-'
var lambda = peek(tokstream, "arrow", "->");
lambda = peek(tokstream, "arrow", "->");
if (lambda) {
tokens.push(lambda);
tokstream = tokstream.substr(2);
@ -212,24 +225,27 @@ function tokenize(tokstream, matchop) {
break;
}
/* falls through */
case 46: // '.'
if (isDigit(tokstream[1])) {
var result = tokenizeNum(tokstream, charnum, linenum);
var num = result[1];
var i = result[0];
if (num[1] !== NaN)
result = tokenizeNum(tokstream, charnum, linenum);
num = result[1];
i = result[0];
if (!isNaN(num[1])) {
tokens.push(num);
}
tokstream = tokstream.substr(i);
break;
}
/* falls through */
case 116: // 't'
var result = tokenizeT(tokstream);
result = tokenizeT(tokstream);
if (result) {
tokens.push(result);
tokstream = tokstream.substr(4); // 4 = length of either token
break;
}
/* falls through */
case 105: // 'i'
var ifexp = peek(tokstream, "ifexp", "if");
if (ifexp) {
@ -244,6 +260,7 @@ function tokenize(tokstream, matchop) {
break;
}
/* falls through */
case 100: // 'd'
var defop = peek(tokstream, "defop", "defop");
if (defop) {
@ -257,22 +274,25 @@ function tokenize(tokstream, matchop) {
tokstream = tokstream.substr(3);
break;
}
/* falls through */
case 101: // e
var result = peek(tokstream, "elsexp", "else");
result = peek(tokstream, "elsexp", "else");
if (result) {
tokens.push(result);
tokstream = tokstream.substr(4);
break;
}
/* falls through */
case 102: // f
var result = peek(tokstream, "falselit", "false");
result = peek(tokstream, "falselit", "false");
if (result) {
tokens.push(result);
tokstream = tokstream.substr(5);
break;
}
/* falls through */
case 108: // l
var lambda = peek(tokstream, "lambda", "lambda");
lambda = peek(tokstream, "lambda", "lambda");
if (lambda) {
tokens.push(lambda);
tokstream = tokstream.substr(6);
@ -285,13 +305,15 @@ function tokenize(tokstream, matchop) {
break;
}
/* falls through */
default:
if (isDigit(tokstream[0])) {
var result = tokenizeNum(tokstream, charnum, linenum);
var num = result[1];
var i = result[0];
if (num[1] !== NaN)
result = tokenizeNum(tokstream, charnum, linenum);
num = result[1];
i = result[0];
if (!isNaN(num[1])) {
tokens.push(num);
}
tokstream = tokstream.substr(i);
break;
}
@ -303,12 +325,12 @@ function tokenize(tokstream, matchop) {
tokens.push(["identifier", op, charnum, linenum]);
}
else {
var result = tokenizeIdent(tokstream, matchop, charnum, linenum);
result.map(function(x) {
result = tokenizeIdent(tokstream, matchop, charnum, linenum);
for(var index = 0; index < result.length; index++) {
charnum++;
tokens.push(x[1]);
tokstream = tokstream.substr(x[0]);
});
tokens.push(result[index][1]);
tokstream = tokstream.substr(result[index][0]);
}
}
}
}
@ -328,9 +350,9 @@ function tokenizeHelp(input, matchop) {
function tokenizeFull(input) {
var matchop = tools.opMatch(operators);
var initialPass = tokenizeHelp(input, matchop).reverse();;
var initialPass = tokenizeHelp(input, matchop).reverse();
for (var i = 0; i < initialPass.length; i++) {
if (initialPass.slice(i, i+7).map(function(x) { return x[0]; }) ===
if (initialPass.slice(i, i+7).map(tools.fst) ===
["defop", "integer", "identifier", "Left", "Right",
"left_paren", "identifier", "identifier", "identifier",
"right_paren"])

40
tools.js

@ -85,6 +85,10 @@ function eq(a) {
};
}
function fst(xs) {
return xs[0];
}
function equal(a) {
return function(b) {
return a === b;
@ -103,11 +107,11 @@ function groupBy(eq, xs) {
}
function groupOps(ops) {
return groupBy(eq, ops.sort());
return groupBy(function (x) { return function(y) { return x === y; };}, ops.sort());
}
function unique(ops) {
return groupOps(ops).map(function(x) { return x[0]; });
return groupOps(ops).map(fst);
}
function find(f, haystack) {
@ -126,13 +130,34 @@ function dict(pairs) {
return o;
}
function flatten(xs) {
if (!(xs instanceof Array))
return xs;
if (xs.every(function (x) { return !(x instanceof Array); }))
return xs;
return [].concat.apply([], xs);
}
function extend(xs, ys) {
xs.push.apply(xs, ys);
return xs;
}
function difference(xs, ys) {
var difflist = groupOps(extend(xs, ys));
return difflist.filter(function (group) {
if (group.length > 1) {
return false;
}
return true;
}).map(fst);
}
/*
* Problem:
* >> > >>^ <- longest one must be matched
* regex?
*/
RegExp.escape= function(s) {
return s.replace(/[-\/\\^$*+?.()|[\]{}]/g, '\\$&');
};
@ -151,6 +176,8 @@ function operatorMatch(ops) {
return false;
};
}
/*
var print = console.log;
@ -168,4 +195,9 @@ module.exports = {compose : compose,
groupOps : groupOps,
opMatch : operatorMatch,
dict: dict,
unique : unique};
unique : unique,
fst : fst,
eq: eq,
extend : extend,
flatten : flatten,
difference : difference}

19
typecheck.js

@ -0,0 +1,19 @@
/*
* Typecheck an AST with a given environment
* the environment maps variables to types
* a variable can either be bound or free
* when we say a variable is free that means that it is either
* unbound (which causes an exception to be raised immediately)
* or it is bound in the outer scope
*
* So the AST must first be converted to a form where each function body is tied
* to an environment mapping identifiers to types
*/
var rep = require("./representation.js");
var env = require("./environments.js");
var TypeOp = rep.TypeOp;
var TypeVar = rep.TypeVar;
Loading…
Cancel
Save