diff --git a/LICENSE.md b/LICENSE.md index c73294e..c1a31c4 100644 --- a/LICENSE.md +++ b/LICENSE.md @@ -1,4 +1,4 @@ -Copyright © 2013 Wesley Kerfoot +Copyright 2014 by ΩF:∅ Working Group members Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the “Software”), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: diff --git a/closure_conversion.js b/closure_conversion.js index 34a26d4..7293ce9 100644 --- a/closure_conversion.js +++ b/closure_conversion.js @@ -2,7 +2,7 @@ * 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 + * 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. @@ -12,6 +12,10 @@ * 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"); @@ -102,7 +106,7 @@ function closure_convert(stx) { return new rep.Closure(bound_vars, free_variables, stx, []); } -function closure_convert_all(stx) { +function closure_convert_all(stx, env) { var closure; switch (stx.exprType) { case "Let": @@ -147,6 +151,8 @@ function test(src) { console.log(JSON.stringify(closure_convert_all(ast), null, 4)); } +//console.log(test(pprint.pprint(parser.parse(pprint.pprint(parser.parse("if something then if a then if b then c else d else rtrrt else some_other_thing")[0]))[0]))); +//console.log(pprint.pprint(parser.parse("def main (print let { a = def {f = (lambda a b -> (a+b))} f} (a 2 3))")[0])); module.export = { test : test, closureConvert : closure_convert_all diff --git a/parse.js b/parse.js index 795ec7b..245918f 100755 --- a/parse.js +++ b/parse.js @@ -553,27 +553,40 @@ function parse(tokens) { } var token = fst(tokens)[1]; tokens.pop(); - if (toktype === "stringlit") + if (toktype === "stringlit") { return new typ.StrT(token); - else if (toktype === "left_square") + } + else if (toktype === "left_square") { return parseList(tokens); - else if (toktype === "lambda") + } + else if (toktype === "lambda") { return parseLambda(tokens); - else if (toktype === "integer") + } + else if (toktype === "integer") { return new typ.IntT(token); - else if (toktype === "float") + } + else if (toktype === "float") { return new typ.FloatT(token); - else if (toktype === "identifier") + } + else if (toktype === "identifier") { return new typ.Name(token); - else if (toktype === "truelit" || toktype === "falselit") + } + else if (toktype === "constructor") { + return new typ.TypeOp(token); + } + else if (toktype === "truelit" || toktype === "falselit") { return new typ.BoolT(token); + } else if (toktype === "def" || - toktype === "let") + toktype === "let") { return parseDef(tokens, fst(tokens)[3], fst(tokens)[2]); - else if (toktype === "defop") + } + else if (toktype === "defop") { return parseDefOp(tokens, fst(tokens)[3], fst(tokens)[2]); - else if (toktype === "ifexp") + } + else if (toktype === "ifexp") { return parseIf(tokens); + } else if (toktype === "left_paren") { if (fst(tokens)[0] === "lambda") { tokens.pop(); @@ -610,4 +623,5 @@ module.exports = { parse : function(str) { return parseFull(tokenizer.tokenize(str)); } }; -//var istr = fs.readFileSync('/dev/stdin').toString(); +/*var istr = fs.readFileSync('/dev/stdin').toString(); +console.log(parseFull(tokenizer.tokenize(istr)).map(pprint.pprint));*/ diff --git a/pprint.js b/pprint.js index 023b1f8..8656512 100644 --- a/pprint.js +++ b/pprint.js @@ -4,9 +4,9 @@ function pprintName(ident) { function pprintFunc(func) { if (func.p.exprType === "Name") - return "(\\ " + pprint(func.p) + " -> " + pprint(func.body) + ")"; + return "(lambda " + pprint(func.p) + " -> " + pprint(func.body) + ")"; else - return "(\\ " + func.p.map(pprint).join(" ") + " -> " + pprint(func.body) + ")"; + return "(lambda " + func.p.map(pprint).join(" ") + " -> " + pprint(func.body) + ")"; } @@ -21,45 +21,62 @@ function pprintDef(def) { } function pprintIf(ifexp) { - if (ifexp.elseexp) - return "(if " + pprint(ifexp.condition) + " then " + pprint(ifexp.thenexp) + " else " + pprint(ifexp.elseexp) + ")"; - else - return "(if " + pprint(ifexp.condition) + " then " + pprint(ifexp.thenexp) + ")"; + return ("(if " + pprint(ifexp.condition) + + " then " + pprint(ifexp.thenexp) + + " else " + pprint(ifexp.elseexp) + ")"); } function pprint(expr) { - if (expr.exprType === "Name") + if (expr.exprType === "Name") { + return expr.val; + } + else if (expr.exprType === "TypeOperator") { return expr.val; - else if (expr.exprType === "Bool") - if (expr.val) + } + else if (expr.exprType === "Bool") { + if (expr.val) { return "True"; - else + } + else { return "False"; - else if (expr.exprType === "Integer") + } + } + else if (expr.exprType === "Integer") { return "("+expr.val+")"; - else if (expr.exprType === "Float") + } + else if (expr.exprType === "Float") { return "("+expr.val+")"; - else if (expr.exprType === "String") + } + else if (expr.exprType === "String") { return '"'+expr.val+'"'; - else if (expr.exprType === "Name") + } + else if (expr.exprType === "Name") { return expr.val; - else if (expr.exprType === "Application") + } + else if (expr.exprType === "Application") { return pprintApp(expr); - else if (expr.exprType === "Definition") + } + else if (expr.exprType === "Definition") { return pprintDef(expr); - else if (expr.exprType === "If") + } + else if (expr.exprType === "If") { return pprintIf(expr); - else if (expr.exprType === "Function") + } + else if (expr.exprType === "Function") { return pprintFunc(expr); - else if (expr.exprType === "Nil") + } + else if (expr.exprType === "Nil") { return "[]"; - else if (expr.exprType === "Unary") + } + else if (expr.exprType === "Unary") { return "("+expr.op.ident+" "+pprint(expr.val)+")"; - else if (expr.exprType === "Let") + } + else if (expr.exprType === "Let") { return "let {" + expr.pairs.map( function (v) { return pprint(v); }).join(" ; ") + "} in " + pprint(expr.body); + } } module.exports = {pprint : pprint}; diff --git a/representation.js b/representation.js index 646429b..ebe8dd7 100644 --- a/representation.js +++ b/representation.js @@ -164,13 +164,14 @@ function If(condition, thenexp, elseexp) { function TypeVar(name) { this.name = name; + this.exprType = "TypeVariable"; return this; } -function TypeOp(name, params, body) { +function TypeOp(name) { this.name = name; - this.params = params; - this.body = body; + this.val = name; + this.exprType = "TypeOperator" return this; } @@ -205,27 +206,28 @@ function makeGensym() { var gensym = makeGensym(); -OPInfo = {"+" : [3, "Left"], - "-" : [3, "Left"], - "*" : [4, "Left"], - "/" : [4, "Left"], - "^" : [5, "Right"], - "++" : [3, "Left"], - "==" : [2, "Left"], - ">" : [2, "Left"], - ">=" : [2, "Left"], - "<" : [2, "Left"], - "<=" : [2, "Left"], - "&&" : [2, "Left"], - "||" : [2, "Left"], - "::" : [2, "Left"], - ":" : [1, "Left"], - "$" : [1, "Left"], - ">>" : [1, "Left"], - ">>=" : [1, "Left"], - "<$>" : [1, "Left"], - "." : [1, "Left"], - "," : [1, "Left"]}; +OPInfo = {"+" : [4, "Left"], + "-" : [4, "Left"], + "*" : [5, "Left"], + "/" : [5, "Left"], + "^" : [6, "Right"], + "++" : [4, "Left"], + "==" : [3, "Left"], + ">" : [3, "Left"], + ">=" : [3, "Left"], + "<" : [3, "Left"], + "<=" : [3, "Left"], + "&&" : [3, "Left"], + "||" : [3, "Left"], + "::" : [1, "Left"], + ":" : [2, "Left"], + "$" : [2, "Left"], + ">>" : [2, "Left"], + ">>=" : [2, "Left"], + "<$>" : [2, "Left"], + "." : [2, "Left"], + "," : [2, "Left"], + "->" : [2, "Right"]} module.exports = { IntT : IntT, diff --git a/tokenize.js b/tokenize.js index 99b2be3..23e226f 100755 --- a/tokenize.js +++ b/tokenize.js @@ -6,24 +6,51 @@ var error = require("./errors.js"); var operators = Object.keys(rep.OPInfo); var _ = require("underscore"); -function isDigit(a) { - if (!a) +function isDigit(c) { + if (!c) return false; - var code = a.charCodeAt(); - return (46 < code && code < 58 || code < 58 && code > 46); + var code = c.charCodeAt(); + if (isNaN(code)) { + return false; + } + return (46 < code && + code < 58 || + code < 58 && + code > 46); } -function isWhitespace(a) { - if (!a) +function isWhitespace(c) { + if (!c) return true; - var code = a.charCodeAt(); - return (code === 9 || code === 32 || code === 10 || code === 13 || code === 11); + var code = c.charCodeAt(); + if (isNaN(code)) { + return false; + } + return (code === 9 || + code === 32 || + code === 10 || + code === 13 || + code === 11); +} + +function isIdentifier(c) { + var code = c.charCodeAt(); + return (!isNaN(code) && + code !== 41 && + code !== 40 && + code !== 125 && + code !== 123 && + code !== 93 && + code !== 91 && + code !== 44); } -function isIdentifier(a) { - var code = a.charCodeAt(); - return code !== 41 && code !== 40 && code && 125 && code && 123 && code !== 93 && code !== 91 && code !== 44; +function isUpper(c) { + var code = c.charCodeAt(); + return (!isNaN(code) && + (code >= 65) && + (code <= 90)); } function tokenizeNum(tokstream, charnum, linenum) { @@ -79,7 +106,10 @@ function tokenizeNum(tokstream, charnum, linenum) { * Everything after the operator goes back on to the token stream */ -function tokenizeIdent(tokstream, matchop, charnum, linenum) { +function tokenizeIdent(tokstream, + matchop, + charnum, + linenum) { var identifier = []; var n = 0; while ((!isWhitespace(tokstream[0])) && isIdentifier(tokstream[0]) && !matchop(tokstream)) { @@ -93,6 +123,18 @@ function tokenizeIdent(tokstream, matchop, charnum, linenum) { return [[n, ["identifier", identifier, charnum, linenum]]]; } +function tokenizeCtor(tokstream, + matchop, + charnum, + linenum) { + var ident = tokenizeIdent(tokstream, + matchop, + charnum, + linenum); + ident[0][1][0] = "constructor"; + return ident; +} + function tokenizeStr(tokstream, charnum, linenum) { var stringlit = []; var n = 1; @@ -212,9 +254,9 @@ function tokenize(tokstream, matchop) { break; /* falls through */ - case 45: // '-' + /*case 45: // '-' lambda = peek(tokstream, "arrow", "->"); - if (lambda) { + if (false) { tokens.push($.extend(lambda, [charnum, linenum])); tokstream = tokstream.substr(2); break; @@ -326,7 +368,12 @@ function tokenize(tokstream, matchop) { tokens.push(["identifier", op, charnum, linenum]); } else { - result = tokenizeIdent(tokstream, matchop, charnum, linenum); + if (isUpper(tokstream[0])) { + result = tokenizeCtor(tokstream, matchop, charnum, linenum); + } + else { + result = tokenizeIdent(tokstream, matchop, charnum, linenum); + } for(var index = 0; index < result.length; index++) { charnum++; tokens.push(result[index][1]); @@ -376,4 +423,6 @@ function tokenizeFull(input) { return tokenizeHelp(input, matchop, true); } -module.exports = {tokenize : tokenizeFull}; + +module.exports = {tokenize : tokenizeFull, + isIdentifier : isIdentifier}; diff --git a/tools.js b/tools.js index 5e65e34..b4d387f 100644 --- a/tools.js +++ b/tools.js @@ -20,6 +20,18 @@ function min(a, b) { } } +function max(a, b) { + if (a > b) { + return 1; + } + else if (a < b) { + return -1; + } + else { + return 0; + } +} + function groupOps(ops) { return _.groupBy(ops.sort(), _.isEqual); } @@ -56,7 +68,8 @@ function operatorMatch(ops) { function (op) { return op.length > 0; }); - var rstring = ops.sort(min).reduce( + ops.sort(min); + var rstring = ops.reduce( function(acc, x) { if (!x || x.length < 1) { return ""; diff --git a/treegen.js b/treegen.js new file mode 100644 index 0000000..f85a2aa --- /dev/null +++ b/treegen.js @@ -0,0 +1,20 @@ +#! /usr/bin/node + +var parser = require("./parse.js"); +var pprint = require("./pprint.js"); +var repr = require("./representation.js"); +var lex = require("./tokenize.js"); + +var qc = require("quickcheck"); + +function arbIdentifier() { + var st = qc.arbString() + if (lex.isIdentifier(st)) { + return new repr.Name(st); + } + else { + return arbIdentifier(); + } +} + +console.log(arbIdentifier());