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