		/* Interface for Typechecker */

check(TTY) => t where
		write(TTY, 'Type an expression ending with semicolon: ');
		[p|rest] <- expr(readlist(TTY));
		[t|env] <- infer(p, []). 

check(FILE) => checkall(parseall(readall())).

readall() => if null(x) then [] else [x|readall()]
 			where x = readlist(RULE).

readlist(RULE) => if eq(x,';') or eq(x, 'eof') then [] 
		  else [x|readlist(RULE)]
 			where x = read('typefile').

readlist(TTY) => if eq(x,';') then [] else [x|readlist(TTY)]
 			where x = read(TTY).

parseall([]) => [].
parseall([p|t]) => [parse(p) | parseall(t)].

parse(in) => def
		where  lhs(in,body) = [def|out1];
		       ['='|out2]   = out1;			
		       [body|out3]  <- expr(out2). 

lhs(in,body) => [[Def, name, lambda] |out]
		where
			[name| ['('| out1]]  = in;  
				atom(name) = true; 
			[lambda|out]  <- flist(out1, body). 

flist([')' | out], body) => [body|out]. 
			/* assumes one or more pars */
flist([x | in], body) => [[Lambda, x,  args] | out]
			where 
				atom(x) = true; eq(')', x) = false;
			      [args|out] <- flist(in, body). 
			     
expr(['if' | in]) => [[Cond, e1, e2, e3] | out]
		where   
			[e1| out2] <- expr(in); 
		        out2       = ['then'|out3]; 
			[e2| out4] <- expr(out3);
		   	out4       = ['else'|out5]; 
			[e3|out]   <- expr(out5).

expr(in) => in where    [num|out] = in; numberp(num) = true.

expr([name|in]) => optexprlist(in, name) where atom(name) = true.

optexprlist(in, name) => [name|in]
		where in = [next|out]; 
		      followid(next) = true.
				/* the above is the follow set for id */
		      
optexprlist(['('| in], name) => exprlist(in, name).

exprlist(in, name) => exprlist2(out1, [name, body1]) 
		where   /* assumes one argument at least */
			[body1|out1] <- expr(in).

exprlist2([','|in], name) => exprlist2(out1, [name, body1]) 
		where   /* assumes one argument at least */
			[body1|out1] <- expr(in).
			
exprlist2([')'|in], name) => [name|in].

followid('then') => true.
followid('else') => true.
followid(',') => true.
followid('.') => true.
followid(')') => true.
