|
|
@ -37,7 +37,7 @@ function makeChecker(props) { |
|
|
|
|
|
|
|
/*Tries to parse until the prediction ``valid'' fails or the wrong type is parsed |
|
|
|
Collects the results into an array and returns it*/ |
|
|
|
function parseMany(exprType, valid, tokens) { |
|
|
|
function parseMany(exprType, valid, tokens, charnum, linenum) { |
|
|
|
var current = fst(tokens)[0]; |
|
|
|
var results = []; |
|
|
|
var parsed; |
|
|
@ -46,7 +46,9 @@ function parseMany(exprType, valid, tokens) { |
|
|
|
parsed = parse(tokens); |
|
|
|
} |
|
|
|
else { |
|
|
|
throw error.JSyntaxError(fst(tokens)[2], fst(tokens)[3], "unexpected token "+fst(tokens)[0]+" in parseMany"); |
|
|
|
throw error.JSyntaxError(linenum, |
|
|
|
charnum, |
|
|
|
"unexpected token "+fst(tokens)[0]+" in parseMany"); |
|
|
|
} |
|
|
|
results.push(parsed); |
|
|
|
|
|
|
@ -66,7 +68,9 @@ function parseMany(exprType, valid, tokens) { |
|
|
|
} |
|
|
|
//do the same validity check as before and in the loop
|
|
|
|
if (!fst(tokens)) |
|
|
|
throw "unexpected end of source"; |
|
|
|
throw error.JSyntaxError(linenum, |
|
|
|
charnum, |
|
|
|
"unexpected end of source"); |
|
|
|
if (valid(fst(tokens)[0])) |
|
|
|
results.push(parse(tokens)); |
|
|
|
return results; |
|
|
@ -76,7 +80,7 @@ function parseMany(exprType, valid, tokens) { |
|
|
|
/* Tries to parse exprType separated by the token between |
|
|
|
* e.g. <identifier>,<identifier>,... |
|
|
|
*/ |
|
|
|
function parseBetween(exprType, between, tokens) { |
|
|
|
function parseBetween(exprType, between, tokens, charnum, linenum) { |
|
|
|
var first = parse(tokens); |
|
|
|
if (!exprType(first)) { |
|
|
|
throw "unexpected token:"+fst(tokens)[0]; |
|
|
@ -87,6 +91,10 @@ function parseBetween(exprType, between, tokens) { |
|
|
|
while (fst(tokens)[0] === between) { |
|
|
|
tokens.pop(); |
|
|
|
parsed = parse(tokens); |
|
|
|
if (!fst(tokens)) |
|
|
|
throw error.JSyntaxError(linenum, |
|
|
|
charnum, |
|
|
|
"Missing terminator: "+between); |
|
|
|
items.push(parsed); |
|
|
|
} |
|
|
|
return items; |
|
|
@ -103,10 +111,12 @@ function parseList(tokens) { |
|
|
|
var xs = []; |
|
|
|
} |
|
|
|
else { |
|
|
|
var xs = parseBetween(function (x) { return true; }, "comma", tokens); |
|
|
|
var xs = parseBetween(function (x) { return true; }, "comma", tokens, fst(tokens)[3], fst(tokens)[2]); |
|
|
|
} |
|
|
|
if (fst(tokens)[0] !== "right_square") { |
|
|
|
throw error.JSyntaxError(fst(tokens)[3], fst(tokens)[2], "list must be terminated by ]"); |
|
|
|
throw error.JSyntaxError(fst(tokens)[3], |
|
|
|
fst(tokens)[2], |
|
|
|
"list must be terminated by ]"); |
|
|
|
} |
|
|
|
tokens.pop(); |
|
|
|
return new typ.ListT(xs); |
|
|
@ -116,16 +126,20 @@ function parseList(tokens) { |
|
|
|
function parseDefFunction(tokens) { |
|
|
|
var fname = parse(tokens); |
|
|
|
if (fname.exprType != "Name") { |
|
|
|
throw error.JSyntaxError(fst(tokens)[3], fst(tokens)[2], "Expected an identifier in function definition"); |
|
|
|
throw error.JSyntaxError(fst(tokens)[3], |
|
|
|
fst(tokens)[2], |
|
|
|
"Expected an identifier in function definition"); |
|
|
|
} |
|
|
|
if (fst(tokens)[0] === "right_paren") { |
|
|
|
var parameters = []; |
|
|
|
} |
|
|
|
else { |
|
|
|
var parameters = parseMany(validName, validFormPar, tokens); |
|
|
|
var parameters = parseMany(validName, validFormPar, tokens, fst(tokens)[2], fst(tokens)[3]); |
|
|
|
} |
|
|
|
if ((fst(tokens)[0]) !== "right_paren") { |
|
|
|
throw error.JSyntaxError(fst(tokens)[3], fst(tokens)[2],"Formal parameters must be followed by )"); |
|
|
|
throw error.JSyntaxError(fst(tokens)[3], |
|
|
|
fst(tokens)[2], |
|
|
|
"Formal parameters must be followed by )"); |
|
|
|
} |
|
|
|
tokens.pop(); |
|
|
|
var body = parse(tokens); |
|
|
@ -155,12 +169,16 @@ function parseDef(tokens) { |
|
|
|
|
|
|
|
function parseIf(tokens) { |
|
|
|
if (!notFollowedBy(tokens, ["def","comma","lambda"])) { |
|
|
|
throw "``if'' cannot be followed by "+fst(tokens)[0]; |
|
|
|
throw error.JSyntaxError(fst(tokens)[3], |
|
|
|
fst(tokens)[2], |
|
|
|
"``if'' cannot be followed by "+fst(tokens)[0]) ; |
|
|
|
} |
|
|
|
else { |
|
|
|
var ifC = parse(tokens); |
|
|
|
if (!fst(tokens) || fst(tokens)[0] !== "thenexp") |
|
|
|
throw "if <exp> must be folowed by <then> exp, not "+snd(tokens)[0]; |
|
|
|
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); |
|
|
@ -183,10 +201,12 @@ var validFormPar = makeChecker(["identifier"]); |
|
|
|
var validName = makeChecker(["Name"]); |
|
|
|
|
|
|
|
function parseLambda(tokens) { |
|
|
|
var parameters = parseMany(validName,validFormPar, tokens); |
|
|
|
var parameters = parseMany(validName,validFormPar, tokens, fst(tokens)[2], fst(tokens)[3]); |
|
|
|
|
|
|
|
if (fst(tokens)[0] !== "arrow") { |
|
|
|
throw "arrow must follow parameters in lambda, not "+fst(tokens)[0]; |
|
|
|
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); |
|
|
@ -202,12 +222,14 @@ var validArgTypes = tool.compose(tool.not, makeChecker(["Definition"])); |
|
|
|
var validOperator = makeChecker(["identifier"]); |
|
|
|
|
|
|
|
//Parses function application (either infix or prefix)
|
|
|
|
function computeApp(tokens) { |
|
|
|
function computeApp(tokens, charnum, linenum) { |
|
|
|
var lhs = parse(tokens); |
|
|
|
if (fst(tokens)) |
|
|
|
var next = fst(tokens); |
|
|
|
else { |
|
|
|
throw "Unexpected end of source"; |
|
|
|
throw error.JSyntaxError(linenum, |
|
|
|
charnum, |
|
|
|
"Unexpected end of source"); |
|
|
|
} |
|
|
|
if (typ.OPInfo[next[1]]) { |
|
|
|
//it's an infix expression
|
|
|
@ -224,9 +246,11 @@ function computeApp(tokens) { |
|
|
|
else { |
|
|
|
//it's a prefix application
|
|
|
|
|
|
|
|
var parameters = parseMany(validArgTypes, validArgument, tokens); |
|
|
|
if (fst(tokens)[0] !== "right_paren") { |
|
|
|
throw "mismatched parentheses"; |
|
|
|
var parameters = parseMany(validArgTypes, validArgument, tokens, fst(tokens)[2], fst(tokens)[3]); |
|
|
|
if (!fst(tokens) || fst(tokens)[0] !== "right_paren") { |
|
|
|
throw error.JSyntaxError(linenum, |
|
|
|
charnum, |
|
|
|
"Mismatched parentheses or missing parenthesis on right-hand side"); |
|
|
|
} |
|
|
|
else { |
|
|
|
//return the result
|
|
|
@ -267,8 +291,11 @@ function parseInfix(tokens, minPrec, lhs) { |
|
|
|
} |
|
|
|
|
|
|
|
function parse(tokens) { |
|
|
|
if (fst(tokens)) |
|
|
|
var charnum = fst(tokens)[2]; |
|
|
|
var linenum = fst(tokens)[3]; |
|
|
|
if (fst(tokens)) { |
|
|
|
var toktype = fst(tokens)[0]; |
|
|
|
} |
|
|
|
else { |
|
|
|
process.exit(code=1); |
|
|
|
} |
|
|
@ -300,7 +327,7 @@ function parse(tokens) { |
|
|
|
return parsed; |
|
|
|
} |
|
|
|
else |
|
|
|
return computeApp(tokens); |
|
|
|
return computeApp(tokens, charnum, linenum); |
|
|
|
} |
|
|
|
// else if (toktype === "let") {
|
|
|
|
// return parseLet(tokens);
|
|
|
|