diff --git a/parse.js b/parse.js index 4f5e520..b197d6d 100755 --- a/parse.js +++ b/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(); diff --git a/representation.js b/representation.js index 6b2732a..3df95fa 100644 --- a/representation.js +++ b/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 }; @@ -62,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() { @@ -113,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) { @@ -166,11 +166,11 @@ 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) { @@ -221,14 +221,14 @@ 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() {