/* 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; } }