merge OFTN version #8

Merged
weskerfoot merged 5 commits from master into master 11 years ago
  1. 2
      closure_conversion.js
  2. 23
      desugar.js
  3. 11
      example.jl
  4. 270
      parse.js
  5. 12
      pprint.js
  6. 193
      representation.js

2
closure_conversion.js

@ -151,7 +151,7 @@ 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(test("if something then if a then if b then c else d else rtrrt else some_other_thing"));
module.export = {
test : test,
closureConvert : closure_convert_all

23
desugar.js

@ -26,7 +26,6 @@ function desugarDefFunc(def) {
def.body));
}
function curryFunc(ps, body) {
if (_.isEmpty(ps)) {
return desugar(body);
@ -43,6 +42,15 @@ function desugarLet(stx) {
return new typ.LetExp(values, desugar(stx.body));
}
function sugarTypeApp(stx) {
var type;
var expression;
type = stx.p;
expression = desugar(stx.func.p);
return new typ.TypeApp(expression, type);
}
function desugar(stx) {
switch (stx.exprType) {
case "If":
@ -56,10 +64,15 @@ function desugar(stx) {
case "Name":
return stx;
case "Application":
if (stx.func.ident === "::") {
//It's a type binding
return desugarTypeBinding(stx);
}
if ((stx.func.func != undefined ) &&
(stx.func.func.ident === "::")) {
/* It's a type application probably (will be verified later)
* In this case we actually *add* syntax here to differentiate type applications
* from normal applications
*/
return sugarTypeApp(stx);
}
if ((stx.func.ident === "-" ||
stx.func.ident === "+") &&
stx.p) {

11
example.jl

@ -1,8 +1,9 @@
defop 2 Left (a ## b)
defop 2 Left (a ++#$ b)
(a - b)
def (f a b)
(a ++ b)
(qat :: A)
def qat (lambda a b c -> (a + b))
def (add a b)
(a + b)
@ -63,6 +64,6 @@ def main
if False
then undefined
else
(unary >>
fileLines >>
(unary +
fileLines +
(print splitted))

270
parse.js

@ -11,33 +11,33 @@ var error = require("./errors.js");
var print = console.log;
function fst(ts) {
return ts[ts.length-1];
return ts[ts.length-1];
}
function snd(ts) {
return ts[ts.length-2];
return ts[ts.length-2];
}
/*Checks if the next token is not followed by any of ``checks'' */
function notFollowedBy(tokens, checks, linenum, charnum) {
if (!fst(tokens)) {
if (!fst(tokens)) {
throw error.JSyntaxError(0,0,"unexpected end of source");
}
var nextT = fst(tokens)[0];
if (checks.some(function (x) {
if (checks.some(function (x) {
return x === nextT;
}))
return false;
else
return true;
return false;
else
return true;
}
/* returns a function that takes a parameter and
checks if it is in the array ``props''*/
function makeChecker(props) {
return function(x) {
return x && props.some(function (y) {return y(x);});
};
return function(x) {
return x && props.some(function (y) {return y(x);});
};
}
function tokTypeCheck(name) {
@ -61,43 +61,43 @@ function parseMany(parse, exprType, valid, tokens, charnum, linenum) {
"Unexpected end of source");
}
var current = fst(tokens)[0];
var results = [];
var parsed;
if (valid(fst(tokens))) {
parsed = parse(tokens);
}
else {
throw error.JSyntaxError(linenum,
var results = [];
var parsed;
if (valid(fst(tokens))) {
parsed = parse(tokens);
}
else {
throw error.JSyntaxError(linenum,
charnum,
"Unexpected token: ``"+fst(tokens)[0]+"''");
}
}
results.push(parsed);
//make sure there are at least 2 tokens to parse
if (tokens.length > 1 && fst(tokens) && valid(fst(tokens))) {
while (valid(snd(tokens))) {
if (!(valid(fst(tokens))))
//make sure there are at least 2 tokens to parse
if (tokens.length > 1 && fst(tokens) && valid(fst(tokens))) {
while (valid(snd(tokens))) {
if (!(valid(fst(tokens))))
break;
results.push(parse(tokens));
if (!exprType(fst(results).exprType))
break;
if (fst(tokens))
if (!exprType(fst(results).exprType))
break;
if (fst(tokens))
current = fst(tokens)[0];
else
throw error.JSyntaxError(charnum, linenum, "Unexpected end of source");
if (tokens.length <= 1)
break;
}
}
//do the same validity check as before and in the loop
if (tokens.length <= 1)
break;
}
}
//do the same validity check as before and in the loop
if (!fst(tokens))
throw error.JSyntaxError(linenum,
charnum,
"unexpected end of source");
if (valid(fst(tokens)))
results.push(parse(tokens));
return results;
if (valid(fst(tokens)))
results.push(parse(tokens));
return results;
}
@ -149,7 +149,7 @@ function parseList(tokens) {
function parseDefFunction(tokens) {
var fname = parse(tokens);
var fname = parse(tokens);
var parameters;
if (fname.exprType != "Name") {
throw error.JSyntaxError(fst(tokens)[3],
@ -285,7 +285,7 @@ function parseLetBinding(tokens, linenum, charnum) {
charnum,
"A definition cannot be the value of a binding");
}
return new typ.Def(name, bound);
return new typ.Def(name, bound);
}
function parseLetItem(tokens) {
@ -321,12 +321,12 @@ function parseDef(tokens, linenum, charnum) {
fst(tokens)[2]);
}
if (notFollowedBy(tokens, ["identifier"], linenum, charnum)) {
throw error.JSyntaxError(linenum,
if (notFollowedBy(tokens, ["identifier"], linenum, charnum)) {
throw error.JSyntaxError(linenum,
charnum,
"def must be followed by identifier, not "+fst(tokens)[0]);
}
else {
}
else {
var identifier = parse(tokens);
if (!fst(tokens))
throw error.JSyntaxError(linenum,
@ -349,8 +349,8 @@ function parseDef(tokens, linenum, charnum) {
charnum,
"A definition cannot be the value of a binding");
}
return new typ.Def(identifier, bound);
}
return new typ.Def(identifier, bound);
}
}
function parseDefOp(tokens, linenum, charnum) {
@ -402,35 +402,35 @@ function parseDefOp(tokens, linenum, charnum) {
function parseIf(tokens) {
var linenum = fst(tokens)[3];
var charnum = fst(tokens)[2];
if (!notFollowedBy(tokens,
if (!notFollowedBy(tokens,
["def","comma","lambda"],
linenum,
charnum)) {
throw error.JSyntaxError(linenum,
throw error.JSyntaxError(linenum,
charnum,
"``if'' cannot be followed by "+fst(tokens)[0]) ;
}
else {
var ifC = parse(tokens);
if (!fst(tokens) || fst(tokens)[0] !== "thenexp") {
throw error.JSyntaxError(fst(tokens)[3],
}
else {
var ifC = parse(tokens);
if (!fst(tokens) || fst(tokens)[0] !== "thenexp") {
throw error.JSyntaxError(fst(tokens)[3],
fst(tokens)[2],
"if ``exp'' must be folowed by ``then'' exp, not "+snd(tokens)[0]);
}
else {
tokens.pop();
var thenC = parse(tokens);
else {
tokens.pop();
var thenC = parse(tokens);
if (fst(tokens) && fst(tokens)[0] === "elsexp") {
tokens.pop();
if (fst(tokens) && fst(tokens)[0] === "elsexp") {
tokens.pop();
if (_.size(tokens) < 1) {
throw error.JSyntaxError(linenum,
charnum,
"Unexpected end of source");
}
else {
var elseC = parse(tokens);
return new typ.If(ifC, thenC, elseC);
var elseC = parse(tokens);
return new typ.If(ifC, thenC, elseC);
}
}
else {
@ -452,20 +452,20 @@ function validFormPar(tok) {
function parseLambda(tokens) {
var linenum = fst(tokens)[2];
var charnum = fst(tokens)[3];
var parameters = parseMany(parse,
var parameters = parseMany(parse,
validName,
validFormPar,
tokens,
charnum,
linenum);
if (fst(tokens)[1] !== "->") {
throw error.JSyntaxError(fst(tokens)[3],
if (fst(tokens)[1] !== "->") {
throw error.JSyntaxError(fst(tokens)[3],
fst(tokens)[2],
"arrow must follow parameters in lambda, not "+fst(tokens)[0]);
}
tokens.pop();
var body = parse(tokens);
return new typ.FuncT(parameters, body);
}
tokens.pop();
var body = parse(tokens);
return new typ.FuncT(parameters, body);
}
var invalidArguments = ["def", "comma", "right_paren", "right_square", "right_brace", "left_brace", "right_brace"].map(tokTypeCheck);
@ -477,31 +477,31 @@ function computeApp(tokens, charnum, linenum) {
var lhs = parse(tokens);
var next;
var result;
if (fst(tokens)) {
next = fst(tokens);
if (fst(tokens)) {
next = fst(tokens);
}
else {
throw error.JSyntaxError(linenum,
else {
throw error.JSyntaxError(linenum,
charnum,
"Unexpected end of source");
}
if (typ.OPInfo[next[1]]) {
/* it's an infix expression */
result = parseInfix(tokens, 1, lhs, linenum, charnum);
if (!fst(tokens) || fst(tokens)[0] !== "right_paren") {
throw error.JSyntaxError(linenum,
}
if (typ.OPInfo[next[1]]) {
/* it's an infix expression */
result = parseInfix(tokens, 1, lhs, linenum, charnum);
if (!fst(tokens) || fst(tokens)[0] !== "right_paren") {
throw error.JSyntaxError(linenum,
charnum,
"Mismatched parentheses or missing parenthesis on right-hand side");
}
else {
}
else {
tokens.pop();
return result;
}
}
else {
/* it's a prefix application */
return result;
}
}
else {
/* it's a prefix application */
var parameters;
if (fst(tokens)[0] !== "right_paren") {
if (fst(tokens)[0] !== "right_paren") {
parameters = parseMany(parse,
validArgTypes,
validArgument,
@ -512,16 +512,16 @@ function computeApp(tokens, charnum, linenum) {
else {
parameters = [];
}
if ((!fst(tokens)) || fst(tokens)[0] !== "right_paren") {
throw error.JSyntaxError(linenum,
if ((!fst(tokens)) || fst(tokens)[0] !== "right_paren") {
throw error.JSyntaxError(linenum,
charnum,
"Mismatched parentheses or missing parenthesis on right-hand side");
}
else {
}
else {
tokens.pop();
return typ.makeApp(lhs, parameters);
}
}
return typ.makeApp(lhs, parameters);
}
}
}
/*Parses infix expressions by precedence climbing
@ -529,81 +529,81 @@ function computeApp(tokens, charnum, linenum) {
http://eli.thegreenplace.net/2012/08/02/parsing-expressions-by-precedence-climbing/
*/
function parseInfix(tokens, minPrec, lhs, linenum, charnum) {
if (!lhs) {
lhs = parse(tokens);
}
while (true) {
var cur = fst(tokens);
if (!cur) {
throw error.JSyntaxError(linenum,
if (!lhs) {
lhs = parse(tokens);
}
while (true) {
var cur = fst(tokens);
if (!cur) {
throw error.JSyntaxError(linenum,
charnum,
"Unexpected end of source");
}
var opinfo = typ.OPInfo[cur[1]];
if (!opinfo || opinfo[0] < minPrec)
break;
var op = new typ.Name(cur[1]);
var prec = opinfo[0];
var assoc = opinfo[1];
var nextMinPrec = assoc === "Left" ? prec + 1 : prec;
tokens.pop();
/*remove the operator token*/
var rhs = parseInfix(tokens, nextMinPrec);
lhs = typ.makeApp(op, [lhs, rhs]);
}
return lhs;
}
var opinfo = typ.OPInfo[cur[1]];
if (!opinfo || opinfo[0] < minPrec)
break;
var op = new typ.Name(cur[1]);
var prec = opinfo[0];
var assoc = opinfo[1];
var nextMinPrec = assoc === "Left" ? prec + 1 : prec;
tokens.pop();
/*remove the operator token*/
var rhs = parseInfix(tokens, nextMinPrec);
lhs = typ.makeApp(op, [lhs, rhs]);
}
return lhs;
}
function parse(tokens) {
var charnum = fst(tokens)[2];
var linenum = fst(tokens)[3];
var toktype;
if (fst(tokens)) {
toktype = fst(tokens)[0];
if (fst(tokens)) {
toktype = fst(tokens)[0];
}
else {
process.exit(code=1);
}
var token = fst(tokens)[1];
tokens.pop();
if (toktype === "stringlit") {
return new typ.StrT(token);
else {
process.exit(code=1);
}
var token = fst(tokens)[1];
tokens.pop();
if (toktype === "stringlit") {
return new typ.StrT(token);
}
else if (toktype === "left_square") {
return parseList(tokens);
}
else if (toktype === "lambda") {
return parseLambda(tokens);
else if (toktype === "lambda") {
return parseLambda(tokens);
}
else if (toktype === "integer") {
return new typ.IntT(token);
else if (toktype === "integer") {
return new typ.IntT(token);
}
else if (toktype === "float") {
return new typ.FloatT(token);
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 === "constructor") {
return new typ.TypeOp(token);
}
else if (toktype === "truelit" || toktype === "falselit") {
return new typ.BoolT(token);
else if (toktype === "truelit" || toktype === "falselit") {
return new typ.BoolT(token);
}
else if (toktype === "def" ||
else if (toktype === "def" ||
toktype === "let") {
return parseDef(tokens, fst(tokens)[3], fst(tokens)[2]);
return parseDef(tokens, fst(tokens)[3], fst(tokens)[2]);
}
else if (toktype === "defop") {
return parseDefOp(tokens, fst(tokens)[3], fst(tokens)[2]);
}
else if (toktype === "ifexp") {
return parseIf(tokens);
else if (toktype === "ifexp") {
return parseIf(tokens);
}
else if (toktype === "left_paren") {
if (fst(tokens)[0] === "lambda") {
else if (toktype === "left_paren") {
if (fst(tokens)[0] === "lambda") {
tokens.pop();
var parsed = parseLambda(tokens);
tokens.pop();

12
pprint.js

@ -30,9 +30,6 @@ function pprint(expr) {
if (expr.exprType === "Name") {
return expr.val;
}
else if (expr.exprType === "TypeOperator") {
return expr.val;
}
else if (expr.exprType === "Bool") {
if (expr.val) {
return "True";
@ -77,6 +74,15 @@ function pprint(expr) {
return pprint(v);
}).join(" ; ") + "} in " + pprint(expr.body);
}
else if (expr.exprType === "TypeOperator") {
return "("+expr.val+")";
}
else if (expr.exprType === "TypeVar") {
return "("+expr.name+")";
}
else if (expr.exprType === "TypeApplication") {
return "( " + pprint(expr.expression) + " :: " + pprint(expr.type) + " )";
}
}
module.exports = {pprint : pprint};

193
representation.js

@ -2,26 +2,26 @@ var errors = require("./errors.js");
var _ = require("underscore");
var Expression = {
display :
function() {
return this.exprType + " " + this.val;
},
type :
function () {
return this.exprType;
}
display :
function() {
return this.exprType + " " + this.val;
},
type :
function () {
return this.exprType;
}
};
var TypeExpression = {
unify :
function (t) {
if (this.expr === t.expr) {
return t.expr;
}
else {
console.log("Could not unify " + this.expr + " with " + t.expr);
}
},
function (t) {
if (this.expr === t.expr) {
return t.expr;
}
else {
console.log("Could not unify " + this.expr + " with " + t.expr);
}
},
isTypeExpr : true
};
@ -41,7 +41,9 @@ function LetExp(pairs, body) {
return (x.exprType === "Definition" ||
x.exprType === "FunctionDefinition");
})) {
throw "let can only be used to bind things to names or functions";
throw Errors.JInternalError(
"let can only be used to bind things to names or functions"
);
}
this.exprType = "Let";
this.val = [pairs, body];
@ -60,46 +62,46 @@ function UnaryOp(op, v) {
UnaryOp.prototype = Expression;
function IntT(v) {
this.exprType = "Integer";
this.val = parseInt(v, 10);
return this;
this.exprType = "Integer";
this.val = parseInt(v, 10);
return this;
}
IntT.prototype = Expression;
function FloatT(v) {
this.exprType = "Float";
this.val = parseFloat(v, 10);
return this;
this.exprType = "Float";
this.val = parseFloat(v, 10);
return this;
}
FloatT.prototype = Expression;
function StrT(v) {
this.exprType = "String";
this.val = v;
return this;
this.exprType = "String";
this.val = v;
return this;
}
StrT.prototype = Expression;
function BoolT(b) {
if (b === "true") {
this.val = true;
}
else {
this.val = false;
}
this.exprType = "Bool";
return this;
if (b === "true") {
this.val = true;
}
else {
this.val = false;
}
this.exprType = "Bool";
return this;
}
BoolT.prototype = Expression;
function ListT(xs) {
this.xs = xs;
this.val = xs;
this.exprType = "List";
return this;
this.xs = xs;
this.val = xs;
this.exprType = "List";
return this;
}
function Nil() {
@ -111,47 +113,47 @@ Nil.prototype = Expression;
ListT.prototype = Expression;
function FuncT(p, body) {
this.p = p;
this.body = body;
this.val = [p, body];
this.exprType = "Function";
return this;
this.p = p;
this.body = body;
this.val = [p, body];
this.exprType = "Function";
return this;
}
FuncT.prototype = Expression;
//Wrapper for function objects
function OpT(operator) {
this.op = operator;
this.val = this.op;
this.exprType = "Function";
return this;
this.op = operator;
this.val = this.op;
this.exprType = "Function";
return this;
}
OpT.prototype = Expression;
// Applications separate from other types
function App(func, p) {
this.func = func;
this.exprType = "Application";
if (p)
this.p = p;
return this;
this.func = func;
this.exprType = "Application";
if (p)
this.p = p;
return this;
}
// Names are not types
function Name(identifier) {
this.ident = identifier;
this.val = this.ident;
this.exprType = "Name";
return this;
this.ident = identifier;
this.val = this.ident;
this.exprType = "Name";
return this;
}
function Def(ident, exp) {
this.ident = ident;
this.val = exp;
this.exprType = "Definition";
return this;
this.ident = ident;
this.val = exp;
this.exprType = "Definition";
return this;
}
function DefFunc(ident, params, body) {
@ -164,55 +166,69 @@ function DefFunc(ident, params, body) {
}
function If(condition, thenexp, elseexp) {
this.condition = condition;
this.thenexp = thenexp;
this.elseexp = elseexp;
this.exprType = "If";
return this;
this.condition = condition;
this.thenexp = thenexp;
this.elseexp = elseexp;
this.exprType = "If";
return this;
}
function TypeVar(name) {
this.exprtype = "TypeVar";
this.name = name;
this.exprType = "TypeVar";
return this;
}
TypeVar.prototype = TypeExpression;
function TypeOp(name, params, body) {
if (!_.every(params, _.compose(
_.partial(_.isEqual, "TypeVar"),
_.property("exprtype")))) {
throw errors.JInternalError(
"Parameters to a type operator must be type variables"
);
}
function TypeOp(name) {
this.name = name;
this.params = params;
this.body = body;
this.val = name;
this.exprType = "TypeOperator";
return this;
}
function isTypeExpr(expr) {
if (!expr.exprType) {
throw errors.JInternalError(expr);
}
return ((expr.exprType === "TypeOperator") ||
(expr.exprType === "TypeVar") ||
(expr.exprType === "TypeApplication"));
}
TypeOp.prototype = TypeExpression;
function TypeBinding(expression, type) {
this.expr = expression;
function TypeApp(expression, type) {
if (isTypeExpr(expression)) {
throw errors.JInternalError(
"Left-hand-side of type application must not be in the type language"
);
}
if (!isTypeExpr(type)) {
throw errors.JInternalError(
"Right-hand-side of type application must be a type expression"
);
}
this.expression = expression;
this.type = type;
this.exprType = "TypeApplication";
return this;
}
TypeBinding.prototype = TypeExpression;
TypeApp.prototype = TypeExpression;
//Applies the function ``name'' to the list of parameters
function makeApp(name, parameters) {
if (parameters) {
return parameters.slice(1).reduce(function(f, ident) {
return new App(f, ident);
}, new App(name, parameters[0]));
}
else {
return new App(name);
}
if (parameters) {
return parameters.slice(1).reduce(function(f, ident) {
return new App(f, ident);
}, new App(name, parameters[0]));
}
else {
return new App(name);
}
}
function makeGensym() {
@ -246,7 +262,8 @@ OPInfo = {"+" : [3, "Left"],
">>=" : [1, "Left"],
"<$>" : [1, "Left"],
"." : [1, "Left"],
"," : [1, "Left"]};
"," : [1, "Left"],
"->" : [1, "Right"]};
module.exports =
{
@ -270,6 +287,6 @@ module.exports =
gensym : gensym,
TypeVar : TypeVar,
TypeOp : TypeOp,
TypeBinding : TypeBinding,
TypeApp: TypeApp,
Closure : Closure
};

Loading…
Cancel
Save