|
@ -18,20 +18,22 @@ 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''
|
|
|
/*Checks if the next token is not followed by any of ``checks'' */ |
|
|
function notFollowedBy(tokens, checks) { |
|
|
function notFollowedBy(tokens, checks, linenum, charnum) { |
|
|
if (!fst(tokens)) { |
|
|
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]; |
|
|
var nextT = fst(tokens)[0]; |
|
|
if (checks.some(function (x) {return x === nextT;})) |
|
|
if (checks.some(function (x) { |
|
|
|
|
|
return x === nextT; |
|
|
|
|
|
})) |
|
|
return false; |
|
|
return false; |
|
|
else |
|
|
else |
|
|
return true; |
|
|
return true; |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
//returns a function that takes a parameter and
|
|
|
/* returns a function that takes a parameter and |
|
|
//checks if it is in the array ``props''
|
|
|
checks if it is in the array ``props''*/ |
|
|
function makeChecker(props) { |
|
|
function makeChecker(props) { |
|
|
return function(x) { |
|
|
return function(x) { |
|
|
return x && props.some(function (y) {return y === x;}); |
|
|
return x && props.some(function (y) {return y === x;}); |
|
@ -65,7 +67,6 @@ function parseMany(exprType, valid, tokens, charnum, linenum) { |
|
|
while (valid(snd(tokens)[0])) { |
|
|
while (valid(snd(tokens)[0])) { |
|
|
if (!(valid(fst(tokens)[0]))) |
|
|
if (!(valid(fst(tokens)[0]))) |
|
|
break; |
|
|
break; |
|
|
//print(valid(fst(tokens)[0]), tokens);
|
|
|
|
|
|
results.push(parse(tokens)); |
|
|
results.push(parse(tokens)); |
|
|
if (!exprType(fst(results).exprType)) |
|
|
if (!exprType(fst(results).exprType)) |
|
|
break; |
|
|
break; |
|
@ -157,24 +158,33 @@ function parseDefFunction(tokens) { |
|
|
return new typ.DefFunc(fname, parameters, body); |
|
|
return new typ.DefFunc(fname, parameters, body); |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
function parseDef(tokens) { |
|
|
function parseDef(tokens, linenum, charnum) { |
|
|
if (tokens.length < 2) |
|
|
if (tokens.length < 2) |
|
|
throw error.JSyntaxError(0,0,"Unexpected end of source"); |
|
|
throw error.JSyntaxError(linenum, |
|
|
var charnum = fst(tokens)[2]; |
|
|
charnum, |
|
|
var linenum = fst(tokens)[3]; |
|
|
"Unexpected end of source"); |
|
|
if (fst(tokens)[0] === "left_paren") { |
|
|
if (fst(tokens)[0] === "left_paren") { |
|
|
// It's a function definition
|
|
|
/* It's a function definition */ |
|
|
tokens.pop(); |
|
|
tokens.pop(); |
|
|
return parseDefFunction(tokens); |
|
|
return parseDefFunction(tokens); |
|
|
} |
|
|
} |
|
|
if (notFollowedBy(tokens, ["identifier"])) { |
|
|
if (notFollowedBy(tokens, ["identifier"], linenum, charnum)) { |
|
|
throw error.JSyntaxError(linenum, |
|
|
throw error.JSyntaxError(linenum, |
|
|
charnum, |
|
|
charnum, |
|
|
"def must be followed by identifier, not "+fst(tokens)[0]); |
|
|
"def must be followed by identifier, not "+fst(tokens)[0]); |
|
|
} |
|
|
} |
|
|
else { |
|
|
else { |
|
|
var identifier = parse(tokens); |
|
|
var identifier = parse(tokens); |
|
|
if (!notFollowedBy(tokens, ["def", "comma", "arrow", "right_brace", "right_square"])) { |
|
|
if (!fst(tokens)) |
|
|
|
|
|
throw error.JSyntaxError(linenum, |
|
|
|
|
|
charnum, |
|
|
|
|
|
"Unexpected end of source"); |
|
|
|
|
|
linenum = fst(tokens)[3]; |
|
|
|
|
|
charnum = fst(tokens)[2]; |
|
|
|
|
|
if (!notFollowedBy(tokens, |
|
|
|
|
|
["def", "comma", "arrow", "right_brace", "right_square"], |
|
|
|
|
|
linenum, |
|
|
|
|
|
charnum)) { |
|
|
throw error.JSyntaxError(linenum, |
|
|
throw error.JSyntaxError(linenum, |
|
|
charnum, |
|
|
charnum, |
|
|
"def " + identifier.val + " must not be followed by " + fst(tokens)[0]); |
|
|
"def " + identifier.val + " must not be followed by " + fst(tokens)[0]); |
|
@ -185,9 +195,14 @@ function parseDef(tokens) { |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
function parseIf(tokens) { |
|
|
function parseIf(tokens) { |
|
|
if (!notFollowedBy(tokens, ["def","comma","lambda"])) { |
|
|
var linenum = fst(tokens)[3]; |
|
|
throw error.JSyntaxError(fst(tokens)[3], |
|
|
var charnum = fst(tokens)[2]; |
|
|
fst(tokens)[2], |
|
|
if (!notFollowedBy(tokens, |
|
|
|
|
|
["def","comma","lambda"], |
|
|
|
|
|
linenum, |
|
|
|
|
|
charnum)) { |
|
|
|
|
|
throw error.JSyntaxError(linenum, |
|
|
|
|
|
charnum, |
|
|
"``if'' cannot be followed by "+fst(tokens)[0]) ; |
|
|
"``if'' cannot be followed by "+fst(tokens)[0]) ; |
|
|
} |
|
|
} |
|
|
else { |
|
|
else { |
|
@ -238,7 +253,7 @@ var validArgument = tool.compose(tool.not, makeChecker(invalidArguments)); |
|
|
var validArgTypes = tool.compose(tool.not, makeChecker(["Definition"])); |
|
|
var validArgTypes = tool.compose(tool.not, makeChecker(["Definition"])); |
|
|
var validOperator = makeChecker(["identifier"]); |
|
|
var validOperator = makeChecker(["identifier"]); |
|
|
|
|
|
|
|
|
//Parses function application (either infix or prefix)
|
|
|
/* Parses function application (either infix or prefix) */ |
|
|
function computeApp(tokens, charnum, linenum) { |
|
|
function computeApp(tokens, charnum, linenum) { |
|
|
var lhs = parse(tokens); |
|
|
var lhs = parse(tokens); |
|
|
if (fst(tokens)) |
|
|
if (fst(tokens)) |
|
@ -249,7 +264,7 @@ function computeApp(tokens, charnum, linenum) { |
|
|
"Unexpected end of source"); |
|
|
"Unexpected end of source"); |
|
|
} |
|
|
} |
|
|
if (typ.OPInfo[next[1]]) { |
|
|
if (typ.OPInfo[next[1]]) { |
|
|
//it's an infix expression
|
|
|
/* it's an infix expression */ |
|
|
var result = parseInfix(tokens, 1, lhs, linenum, charnum); |
|
|
var result = parseInfix(tokens, 1, lhs, linenum, charnum); |
|
|
if (!fst(tokens) || fst(tokens)[0] !== "right_paren") { |
|
|
if (!fst(tokens) || fst(tokens)[0] !== "right_paren") { |
|
|
throw error.JSyntaxError(linenum, |
|
|
throw error.JSyntaxError(linenum, |
|
@ -257,13 +272,12 @@ function computeApp(tokens, charnum, linenum) { |
|
|
"Mismatched parentheses or missing parenthesis on right-hand side"); |
|
|
"Mismatched parentheses or missing parenthesis on right-hand side"); |
|
|
} |
|
|
} |
|
|
else { |
|
|
else { |
|
|
//return the result
|
|
|
|
|
|
tokens.pop(); |
|
|
tokens.pop(); |
|
|
return result; |
|
|
return result; |
|
|
} |
|
|
} |
|
|
} |
|
|
} |
|
|
else { |
|
|
else { |
|
|
//it's a prefix application
|
|
|
/* it's a prefix application */ |
|
|
|
|
|
|
|
|
var parameters = parseMany(validArgTypes, validArgument, tokens, charnum, linenum); |
|
|
var parameters = parseMany(validArgTypes, validArgument, tokens, charnum, linenum); |
|
|
if ((!fst(tokens)) || fst(tokens)[0] !== "right_paren") { |
|
|
if ((!fst(tokens)) || fst(tokens)[0] !== "right_paren") { |
|
@ -272,7 +286,6 @@ function computeApp(tokens, charnum, linenum) { |
|
|
"Mismatched parentheses or missing parenthesis on right-hand side"); |
|
|
"Mismatched parentheses or missing parenthesis on right-hand side"); |
|
|
} |
|
|
} |
|
|
else { |
|
|
else { |
|
|
//return the result
|
|
|
|
|
|
tokens.pop(); |
|
|
tokens.pop(); |
|
|
return typ.makeApp(lhs, parameters); |
|
|
return typ.makeApp(lhs, parameters); |
|
|
} |
|
|
} |
|
@ -304,7 +317,7 @@ function parseInfix(tokens, minPrec, lhs, linenum, charnum) { |
|
|
var assoc = opinfo[1]; |
|
|
var assoc = opinfo[1]; |
|
|
var nextMinPrec = assoc === "Left" ? prec + 1 : prec; |
|
|
var nextMinPrec = assoc === "Left" ? prec + 1 : prec; |
|
|
tokens.pop(); |
|
|
tokens.pop(); |
|
|
//remove the operator token
|
|
|
/*remove the operator token*/ |
|
|
var rhs = parseInfix(tokens, nextMinPrec); |
|
|
var rhs = parseInfix(tokens, nextMinPrec); |
|
|
lhs = typ.makeApp(op, [lhs, rhs]); |
|
|
lhs = typ.makeApp(op, [lhs, rhs]); |
|
|
} |
|
|
} |
|
@ -337,7 +350,7 @@ function parse(tokens) { |
|
|
else if (toktype === "truelit" || toktype === "falselit") |
|
|
else if (toktype === "truelit" || toktype === "falselit") |
|
|
return new typ.BoolT(token); |
|
|
return new typ.BoolT(token); |
|
|
else if (toktype === "def") |
|
|
else if (toktype === "def") |
|
|
return parseDef(tokens); |
|
|
return parseDef(tokens, fst(tokens)[3], fst(tokens)[2]); |
|
|
else if (toktype === "ifexp") |
|
|
else if (toktype === "ifexp") |
|
|
return parseIf(tokens); |
|
|
return parseIf(tokens); |
|
|
else if (toktype === "left_paren") { |
|
|
else if (toktype === "left_paren") { |
|
|