/* zweic -- a compiler for zwei
*
* Stephane Micheloud & LAMP
*
* $Id$
*/
package zweic;
/**
* This class implements the parser of the zwei compiler.
*/
class Parser(in: java.io.InputStream) extends Scanner(in) {
import Tokens._;
var pushedBack: Option[Triple[Int,String,Token]] = None;
var pos: Int = 0;
/**
* Reports an error that a specific token was expected but
* a different token found.
*/
private def error(expected: Token): Unit =
error("token of class " + expected);
/**
* Reports an error that a specific token was expected but
* a different token found.
*/
private def error(expected: String): Unit = {
val message = "syntax error\n" +
"expected: " + expected + "\n" +
"found : " + representation;
Report.fail(start, message);
}
/**
* If the current token corresponds to the token class 'expected'
* then skip it, otherwise signal an error.
*/
private def accept(expected: Token): Unit =
if (token == expected)
nextToken;
else
error(expected);
/**
* If the current token corresponds to the token class 'expected'
* then skip it and return success.
*/
private def check(expected: Token): Boolean = {
if (token == expected) {
nextToken;
true;
} else {
false;
}
}
override def nextToken: Unit = {
if (debug == true) {
Console.print (" " + token);
}
pushedBack match {
case Some(Triple(p, c, t)) =>
token = t;
chars = c;
pos = p;
pushedBack = None;
case _ => return super.nextToken;
}
}
def peekAhead: Token = pushedBack match {
case Some(Triple(p, c, t)) =>
return t;
case _ =>
val savedToken = token;
val savedChars = chars;
val savedPos = pos;
nextToken;
val rval = token;
pushedBack = Some(Triple(pos, chars, token));
token = savedToken;
chars = savedChars;
pos = savedPos;
return rval;
}
/**
* Parse the input and return an abstract syntax tree.
*/
def parse: Program = {
val result = program();
accept(EOF);
return result;
}
/* --------------------------------------------------*/
/**
* Program = { ClassDecl } Expression
*/
private def program(): Program = {
var classes:List[ClassDef] = Nil;
while (token == CLASS) {
classes = classDecl() :: classes;
}
var main = expression();
return Program(classes.reverse, main);
}
/**
* ClassDecl = "class" ident ["extends" ident] "{" {Member} "}"
*/
private def classDecl(): ClassDef = {
accept(CLASS);
val name = Name(chars);
accept(IDENT);
var extend:Option[Name] = None;
if (check (EXTENDS)) {
extend = Some(Name(chars));
accept(IDENT);
}
accept (LACCOLADE);
var members:List[Member] = Nil;
while ( !check(RACCOLADE) ) {
members = member() :: members;
}
return ClassDef (name, extend, members.reverse);
}
/**
* Member = Formal
* (";" |
* "(" [Formal {"," Formal}] ")" Block)
*/
private def member(): Member = {
val f:Formal = formal();
if (check(SEMICOLON)) {
return FieldDecl(f);
} else {
accept(LPAREN);
//TODO: is this neccessary?
var par:List[Formal] = Nil;
if (token == IDENT || token == INT || token == NULLTYPE) {
par = formal() :: par;
while (token != RPAREN) {
accept (PERIOD);
par = formal() :: par;
}
}
accept (RPAREN);
val b:Block = block();
return MethodDef (f.name, par.reverse, f.typ, b);
}
}
/**
* Formal = Type ident
*/
private def formal(): Formal = {
var typ = type1();
var name = Name(chars);
accept(IDENT);
return Formal(name, typ);
}
/**
* Block = "{" {Statements} ["return" Expression] "}"
*/
private def block(): Block = {
accept (LACCOLADE);
var s:List[Stat] = Nil;
var e:Expr = NullLit(); //CHAOS
while (token != RETURN && token != RACCOLADE) {
s = statement() :: s;
}
if (check (RETURN)) {
e = expression();
}
accept(RACCOLADE);
return Block(s.reverse, e);
}
/**
* Type1 = "Int" | "Null" | ident
*/
private def type1(): TypeTree = token match {
case INT => nextToken; return IntType();
case NULLTYPE => nextToken; return NullType();
case IDENT => var name=Name(chars); nextToken; return ClassType(name);
case _ => error("'Int', 'Null' or identifier token"); return null;
}
/**
* Statement = "while" "(" Expression ")" "{" {Statement} "}"
* | Formal "=" Expression ";"
* | ident "=" Expression ";"
* | Expression ";"
* | "printInt" "(" Expression ")" ";"
* | "printChar" "(" Expression ")" ";"
*/
private def statement(): Stat = {
if (token == IDENT) {
peekAhead match {
case IDENT =>
//type definition and affection
val v = formal();
accept(EQUALS);
val e = expression();
accept(SEMICOLON);
return Var(v.name, v.typ, e);
case EQUALS =>
//affection
val name = Name(chars);
accept(IDENT);
accept(EQUALS);
val e = expression();
accept(SEMICOLON);
return Set(name, e);
case _ =>
//expression
val e = expression();
accept(SEMICOLON);
return Do(e);
}
} else {
if (check(WHILE)) {
//while
accept(LPAREN);
val cond = expression();
accept(RPAREN);
accept(LACCOLADE);
var stats:List[Stat] = Nil;
while ( !check(RACCOLADE) ) {
stats = statement() :: stats;
}
return While(cond, stats.reverse);
} else if (token == NULLTYPE || token == INT) {
//type definition and affection
val v = formal();
accept(EQUALS);
val e = expression();
accept(SEMICOLON);
return Var(v.name, v.typ, e);
} else if (check(PRINTINT)) {
//printInt
accept(LPAREN);
val e = expression();
accept(RPAREN);
accept(SEMICOLON);
return PrintInt(e);
} else if (check(PRINTCHAR)) {
//printChar
accept(LPAREN);
val e = expression();
accept(RPAREN);
accept(SEMICOLON);
return PrintChar(e);
} else {
//expression
val e = expression();
accept(SEMICOLON);
return Do(e);
}
}
}
/**
* Expression = "if" "(" Expression ")" Expression [ "else" Expression ] | CmpExpression
*/
private def expression(): Expr = {
if ( check(IF) ) {
accept(LPAREN);
val cond = expression();
accept(RPAREN);
val stata = expression();
var statb:Expr = NullLit();
if ( check(ELSE) ) {
statb = expression();
}
return If(cond, stata, statb);
} else {
return cmpExpression();
}
}
/**
* CmpExpression = [ SumExpression CompOp ] SumExpression
* MyCmpExpression = SumExpression [ CompOp SumExpression ]
*/
private def cmpExpression(): Expr = {
var left = sumExpression();
if ( token == EQ || token == NE || token == LT
|| token == GT || token == LE || token == GE ) {
val op = compOp();
val right = sumExpression();
return Binop(op, left, right);
}
return left;
}
/**
* SumExpression = Term | SumExpression SumOp Term
* MySumExpression = Term [ SumOp SumExpression ]
*/
private def sumExpression(): Expr = {
var left = term();
if ( token == ADD || token == SUB ) {
return Binop(sumOp(), left, sumExpression());
} else {
if ( token == OR ) {
return Unop(Operators.NOT,
Binop(Operators.AND,
Unop(Operators.NOT, left),
Unop(Operators.NOT, sumExpression())
)
);
} else {
return left;
}
}
}
/**
* Term = [ Term ProdOp ] [ NegateOp ] Factor
* MyTerm = [ NegateOp ] Factor [ ProdOp Term ]
*/
private def term(): Expr = {
var left:Expr = null;
if ( token == SUB || token == NOT ) {
val op = Operators.token2op(token);
nextToken;
val e = factor();
left = Unop(op, e);
} else {
left = factor();
}
if ( token == MUL || token == DIV || token == MOD || token == AND ) {
val op = prodOp();
val right = term();
return Binop(op, left, right);
}
return left;
}
private def _string(str: String): New = {
// TODO: make this less dirty
if ( str.length() > 1 ) {
return New(Name("Cons"), IntLit(str.charAt(0).asInstanceOf[Int])::_string(str.substring(1))::Nil);
} else {
return New(Name("Cons"), IntLit(str.charAt(0).asInstanceOf[Int])::New(Name("Nil"), Nil)::Nil);
}
}
/**
* Factor = look it up yourself!
*/
private def factor(): Expr = {
var name = chars;
var rval:Expr = null;
token match {
case IDENT => nextToken; rval = Ident(Name(name));
case NUMBER => nextToken; rval = IntLit(Integer.parseInt(name));
case STRING => nextToken; rval = _string(name);
case TRUE => nextToken; rval = IntLit(1);
case FALSE => nextToken; rval = IntLit(0);
case THIS => nextToken; rval = Ident(Name("this"));
case NULLFACTOR => nextToken; rval = NullLit();
case READINT => nextToken; rval = ReadInt();
case READCHAR => nextToken; rval = ReadChar();
case LPAREN => nextToken; rval = expression(); accept(RPAREN);
case LACCOLADE => rval = block();
case NEW => nextToken; accept(IDENT); rval = New(Name(chars), params());
case _ => error("Identifier, Number, String, 'true', 'false', 'this', 'null', 'readInt', 'readChar', '(Expression)', '{Block}' or 'new'");
}
while ( check(DOT) ) {
accept(IDENT);
if ( token == LPAREN ) {
rval = Call(rval, Name(chars), params());
} else {
rval = Select(rval, Name(chars));
}
name = chars;
}
return rval;
}
/**
* Expressions = [ Expression { "," Expression } ]
*/
private def expressions(): List[Expr] = {
var rval:List[Expr] = Nil;
if ( token == IF || token == SUB || token == NOT
|| token == IDENT || token == NUMBER
|| token == STRING || token == TRUE
|| token == FALSE || token == THIS
|| token == NULLFACTOR || token == READINT
|| token == READCHAR || token == LPAREN
|| token == NEW || token == LACCOLADE ) {
rval = expression()::rval;
while ( check(PERIOD) ) {
rval = expression()::rval;
}
}
return rval.reverse;
}
/**
* Params = "(" Expressions ")"
*/
private def params(): List[Expr] = {
accept(LPAREN);
var exprs = expressions();
accept(RPAREN);
return exprs;
}
/**
* ProdOp = "*" | "/" | "%" | "&&"
*/
private def prodOp(): Operators.Operator = token match {
case MUL => nextToken; return Operators.MUL;
case DIV => nextToken; return Operators.DIV;
case MOD => nextToken; return Operators.MOD;
case AND => nextToken; return Operators.AND;
case _ => error("'*', '/', '%' or '&&' token"); return null;
}
/**
* NegateOp = "-" | "!"
*/
private def negateOp(): Operators.Operator = token match {
case SUB => nextToken; return Operators.NEG;
case NOT => nextToken; return Operators.NOT;
case _ => error("'-' or '!' token"); return null;
}
/**
* SumOp = "+" | "-" | "||"
*/
private def sumOp(): Operators.Operator = token match {
case ADD => nextToken; return Operators.ADD;
case SUB => nextToken; return Operators.SUB;
// case OR => nextToken; return null; // no longer needed
case _ => error("'+', '-' or '||' token"); return null;
}
/**
* CompOp = "==" | "!" | "<" | ">" | "<=" | ">="
*/
private def compOp(): Operators.Operator = token match {
case EQ => nextToken; return Operators.EQ;
case NE => nextToken; return Operators.NE;
case LT => nextToken; return Operators.LT;
case GT => nextToken; return Operators.GT;
case LE => nextToken; return Operators.LE;
case GE => nextToken; return Operators.GE;
case _ => error("comparison operator ('==', '!', '<', '>', '<=', '>=')"); return null;
}
}