Newer
Older
zweic / sources / zweic / Parser.scala
/*  zweic -- a compiler for zwei
 *
 *  Stephane Micheloud & LAMP
 *
 */

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 = 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;
		start = 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 = start;
      nextToken;
      val rval = token;
      pushedBack = Some(Triple(start, chars, token));
      token = savedToken;
      chars = savedChars;
      start = 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 = {
	val pos = start;
    var classes:List[ClassDef] = Nil;
    while (token == CLASS) {
      classes = classDecl() :: classes;
    }
    val main = expression();
    return Program(classes.reverse, main).setPos(pos);
  }

  /**
   * ClassDecl = "class" ident ["extends" ident] "{" {Member} "}"
   */
  private def classDecl(): ClassDef = {
	val pos = start;
    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).setPos(pos);
  }

  /**
   * Member = Formal
   *         (";" |
   *          "(" [Formal {"," Formal}] ")" Block)
   */
  private def member(): Member = {
	val pos = start;
    val f:Formal = formal();
    if (check(SEMICOLON)) {
      return FieldDecl(f).setPos(pos);
    } else {
      accept(LPAREN);
      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).setPos(pos);
    }
  }

  /**
   * Formal = Type ident
   */
  private def formal(): Formal = {
    val typ = type1();
	val pos = start;
    val name = Name(chars);
    accept(IDENT);
    return Formal(name, typ).setPos(pos);
  }

  /**
   * Block = "{" {Statements} ["return" Expression] "}"
   */
  private def block(): Block = {
	val pos = start;
    accept (LACCOLADE);
    var s:List[Stat] = Nil;
    var e:Expr = NullLit().setPos(pos);
    while (token != RETURN && token != RACCOLADE) {
      s = statement() :: s;
    }
    if (check (RETURN)) {
      e = expression();
    }
    accept(RACCOLADE);
    return Block(s.reverse, e).setPos(pos);
  }

  /**
   * Type1 = "Int" | "Null" | ident
   */
  private def type1(): TypeTree = {
	val pos = start;
	token match {
      case INT => nextToken; return IntType().setPos(pos);
      case NULLTYPE => nextToken; return NullType().setPos(pos);
      case IDENT => val name=Name(chars); nextToken;
		return ClassType(name).setPos(pos);
      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 = {
	val pos = start;
    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).setPos(pos);

		case EQUALS =>
		  //affection
		  val name = Name(chars);
		  accept(IDENT);
		  accept(EQUALS);
		  val e = expression();
		  accept(SEMICOLON);
		  return Set(name, e).setPos(pos);

		case _ =>
		  //expression
		  val e = expression();
		  accept(SEMICOLON);
		  return Do(e).setPos(pos);
      }

    } 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).setPos(pos);

	  } 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).setPos(pos);

	  } else if (check(PRINTINT)) {
		//printInt
		accept(LPAREN);
		val e = expression();
		accept(RPAREN);
		accept(SEMICOLON);
		return PrintInt(e).setPos(pos);

      } else if (check(PRINTCHAR)) {
		//printChar
		accept(LPAREN);
		val e = expression();
		accept(RPAREN);
		accept(SEMICOLON);
		return PrintChar(e).setPos(pos);

      } else {
		//expression
		val e = expression();
		accept(SEMICOLON);
		return Do(e).setPos(pos);
      }
    }
  }

  /**
  * Expression = "if" "(" Expression ")" Expression [ "else" Expression ] | CmpExpression
  */
  private def expression(): Expr = {
	val pos = start;
  	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).setPos(pos);
	} else {
		return cmpExpression();
	}
  }

  /**
  * MyCmpExpression = SumExpression { CompOp SumExpression }
  */
  private def cmpExpression(): Expr = {
	val pos = start;
	var left = sumExpression();
	while ( token == EQ || token == NE || token == LT
	|| token == GT || token == LE || token == GE ) {
		var op = compOp();
		var right = sumExpression();
		left = Binop(op, left, right).setPos(pos);
	}
	return left;
  }

  /**
  * SumExpression = Term | SumExpression SumOp Term
  * MySumExpression = Term { SumOp Term }
  */
  private def sumExpression(): Expr = {
	val pos = start;
	var left = term();

	while ( token == ADD || token == SUB || token == OR ) {
		if ( check(OR) ) {
			var right = term();
			left.brackets = true;
			right.brackets = true;
			var bo = Binop(Operators.AND, Unop(Operators.NOT, left).setPos(pos), Unop(Operators.NOT, right).setPos(pos)).setPos(pos);
			bo.brackets = true;
			left = Unop(Operators.NOT, bo).setPos(pos);
		} else {
			left = Binop(sumOp(), left, term()).setPos(pos);
		}
	}

	return left;
  }

  /**
  * Term = [ Term ProdOp ] [ NegateOp ] Factor
  * MyTerm = [ NegateOp ] Factor { ProdOp [ NegateOp ] Factor }
  */
  private def term(): Expr = {
	val pos = start;
	var left:Expr = _negFactor();

	while ( token == MUL || token == DIV || token == MOD || token == AND ) {
		var op = prodOp();
		left = Binop(op, left, _negFactor()).setPos(pos);
	}

	return left;
  }

  private def _negFactor(): Expr = {
	val pos = start;
	if ( token == SUB || token == NOT ) {
		val op = negateOp();
		val e = factor();
		e.brackets = true;
		return Unop(op, e).setPos(pos);
	} else {
		return factor();
	}
  }

  private def _string(str: String): New = {
	val pos = start;
  	/*if ( str.length() > 1 ) {
		return New(Name("Cons"), IntLit(str.charAt(0).asInstanceOf[Int]).setPos(pos)::_string(str.substring(1))::Nil).setPos(pos);
	} else {
		return New(Name("Cons"), New(Name("Nil"), Nil).setPos(pos)::Nil).setPos(pos);
	}*/
  	if ( str.length() > 0 ) {
		val il = IntLit(str.charAt(0).asInstanceOf[Int]);
		il.setPos(pos);
		val n = New(Name("Cons"), il::_string(str.substring(1))::Nil);
		n.setPos(pos);
		return n;
	} else {
		val nl = New(Name("Nil"), Nil);
		nl.setPos(pos);
		return nl;
	}
  }

  /**
  * Factor = look it up yourself!
  */
  private def factor(): Expr = {
	val pos = start;
  	var name = chars;
	var rval:Expr = null;
	rval = token match {
		case IDENT => nextToken; Ident(Name(name)).setPos(pos);
		case NUMBER => nextToken; IntLit(Integer.parseInt(name)).setPos(pos);
		case STRING => nextToken; _string(name);
		case TRUE => nextToken; IntLit(1).setPos(pos);
		case FALSE => nextToken; IntLit(0).setPos(pos);
		case THIS => nextToken; Ident(Name("this")).setPos(pos);
		case NULLFACTOR => nextToken; NullLit().setPos(pos);
		case READINT => nextToken; ReadInt().setPos(pos);
		case READCHAR => nextToken; ReadChar().setPos(pos);
		case LPAREN =>
		    nextToken; var t = expression(); t.brackets = true;
		    accept(RPAREN); t;
		case LACCOLADE => block();
		case NEW => nextToken; accept(IDENT);
		    New(Name(chars), params()).setPos(pos);
		case _ => error("Identifier, Number, String, 'true', 'false', 'this', 'null', 'readInt', 'readChar', '(Expression)', '{Block}' or 'new'"); null;
	}

	while ( check(DOT) ) {
		accept(IDENT);
		if ( token == LPAREN ) {
			rval = Call(rval, Name(chars), params()).setPos(pos);
		} else {
			rval = Select(rval, Name(chars)).setPos(pos);
		}
		name = chars;
	}

	return rval;
  }

  /**
  * Expressions = [ Expression { "," Expression } ]
  */
  private def expressions(): List[Expr] =
    return (
      if ( IF::SUB::NOT::IDENT::NUMBER::STRING::TRUE::FALSE::THIS::NULLFACTOR
	::READINT::READCHAR::LPAREN::NEW::LACCOLADE::Nil contains token) {
	  var rval = expression() :: Nil;
	  while ( check(PERIOD) ) {
	    rval = expression() :: rval;
	  }
	rval;
      } else Nil).reverse;

  /**
  * Params = "(" Expressions ")"
  */
  private def params(): List[Expr] = {
	accept(LPAREN);
	val 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;
  }
}