/* zweic -- a compiler for Zwei * * Stephane Micheloud & LAMP * * $Id$ */ package zweic; /** * This class implements the semantic analyzer of zweic. */ class Analyzer { import Operators._; import scala.collection.immutable.{Map, ListMap}; // Global scope of classes type ClassScope = Map[String, ClassSymbol]; var classScope: ClassScope = ListMap.Empty; // For local scopes of variables type VarScope = Map[String, VarSymbol]; val emptyVarScope: VarScope = ListMap.Empty; // Analyze a program def analyzeProgram(tree: Program): Unit = tree match { case Program(classes, main) => classes.foreach(analyzeClass); analyzeExpr(emptyVarScope, main); () } // Analyze a class private def analyzeClass(tree: ClassDef): Unit = tree match { case ClassDef(name, None, members) => classScope.get(name.name) match { case Some(c) => Report.error(tree.pos, "Class already defined: " + name) case None => val cs = ClassSymbol(tree.pos, name.name, None); classScope(name.name) = cs; members.foreach(x => analyzeMember(cs,x)) } case ClassDef(name, extend, members) => classScope.get(name.name) match { case Some(c) => Report.error(tree.pos, "Class already defined: " + name); case None => val cs = ClassSymbol(tree.pos, name.name, classScope.get(extend.get.name)); classScope(name.name) = cs; members.foreach(x => analyzeMember(cs,x)) } } // Analyze a member private def analyzeMember(ownerClass: ClassSymbol, tree: Member): Unit = tree.match { case FieldDecl(Formal(name, typ)) => ownerClass.lookupField(name.name) match { case Some(v) => Report.error(tree.pos, "Class variable already defined: " + name); case None => name.sym= FieldSymbol(tree.pos, name.name, analyzeType(typ)); ownerClass.enterField(name.sym.asInstanceOf[FieldSymbol]); } case MethodDef(name, args, rtype, expr) => var myVarScope = emptyVarScope; for ( val x <- ownerClass.allFields ) myVarScope = myVarScope + x.name -> x.asInstanceOf[VarSymbol]; val paramtypes = for ( val f <- args ) yield { //TODO: check that there isn't already a class field with the same name f.name.sym = VarSymbol(f.pos, f.name.name, analyzeType(f.typ)); myVarScope = myVarScope + f.name.name -> f.name.sym.asInstanceOf[VarSymbol]; analyzeType(f.typ); } val rt = analyzeType(rtype); checkSubtype(tree.pos, "Return type mismatch", analyzeExpr(myVarScope, expr), rt); name.sym = MethodSymbol(tree.pos, name.name, paramtypes, rt); ownerClass.lookupMethod(name.name) match { case Some(m) => ownerClass.enterMethod(name.sym.asInstanceOf[MethodSymbol]); case None => ownerClass.enterNewMethod(name.sym.asInstanceOf[MethodSymbol]); } } // Analyze a statement private def analyzeStat(varScope: VarScope, tree: Stat): VarScope = tree match { case While(cond, stats) => checkIntType(cond.pos, analyzeExpr(varScope, cond)); stats.foldLeft(varScope)(analyzeStat); varScope case Var(name, typ, expr) => varScope.get(name.name) match { case Some(v) => Report.error(tree.pos, "Variable already defined " + name.name); varScope case None => val mt = analyzeType(typ); checkSubtype(tree.pos, "Incompatible types", analyzeExpr(varScope, expr), mt); name.sym = VarSymbol(tree.pos, name.name, mt); varScope + name.name -> name.sym.asInstanceOf[VarSymbol]; } case Set(ident, expr) => varScope.get(ident.name) match { case Some(v) => ident.sym = v; checkSubtype(tree.pos, "Incompatible types", analyzeExpr(varScope, expr), v.vartype); case None => Report.error(tree.pos, "Unknown variable " + ident.name); } varScope case Do(expr) => analyzeExpr(varScope, expr); varScope case PrintInt(expr) => checkIntType(tree.pos, analyzeExpr(varScope, expr)); varScope case PrintChar(expr) => checkIntType(tree.pos, analyzeExpr(varScope, expr)); varScope } // Analyze a type private def analyzeType(tree: TypeTree): Type = tree match { case IntType() => IIntType case NullType() => INullType case ClassType(name) => classScope.get(name.name) match { case Some(c) => name.sym = c; IClassType(c) case None => Report.error(tree.pos, "Class " + name + " is unknown"); IBadType } } // Analyze an expression private def analyzeExpr(varScope: VarScope, tree: Expr): Type = tree match { // ... à compléter ... case Ident(name) => varScope.get(name.name) match { case Some(v) => name.sym = v; v.vartype case None => Report.error(tree.pos, "Unknown variable " + name); IBadType } case Select(expr, name) => val ct = analyzeExpr(varScope, expr); ct match { case IClassType(c) => c.lookupField(name.name) match { case Some(v) => name.sym = v; v.fieldtype case None => Report.error(tree.pos, "Unknown class variable " + name + " in " + ct); IBadType; } case _ => Report.error(tree.pos, "Field '" + name.name + "' does not exist"); IBadType; } case Call(expr, name, args) => val ct = analyzeExpr(varScope, expr); //XXX: this implies checking if class exists ct match { case IClassType(c) => c.lookupMethod(name.name) match { case Some(v) => if (args.length != v.paramtypes.length) { Report.error(tree.pos, "Wrong number of arguments for class method " + name + " in " + ct); IBadType } if ( !args.map(x => analyzeExpr(varScope, x)).zip(v.paramtypes) .forall( x:Pair[Type,Type] => x._1.isSubtype(x._2)) ) { Report.error(tree.pos, "Incompatible Types: \nrequired:" + v.paramtypes.foldLeft("")((a,b)=>a + b) + "\nfound:" + args.foldLeft("")((a,b)=>a + b) ); IBadType; } name.sym = v; v.restype case None => Report.error(tree.pos, "Unknown class method " + name + " in " + ct); IBadType } case _ => Report.error(tree.pos, "Function '" + name.name + "' does not exist"); IBadType; } case New(name, args) => val ct = classScope.get(name.name); ct match { case Some(v) => if (args.length != v.allFields.length) { Report.error(tree.pos, "Wrong number of arguments for class constructor in " + ct); IBadType } if ( args.map(x => analyzeExpr(varScope, x)).zip(v.allFields.map(y => y.fieldtype)) .forall( x:Pair[Type, Type] => x._1.isSubtype(x._2)) ) { Report.error(tree.pos, "Incompatible Types: \nrequired:" + v.allFields + "\nfound:" + args); IBadType; } name.sym = v; IClassType(v) case None => Report.error(tree.pos, "Class " + name + " is unknown"); IBadType } case NullLit() => INullType case IntLit(_) => IIntType case Unop(op, expr) => checkIntType(tree.pos, analyzeExpr(varScope, expr)); IIntType case Binop(op, left, right) => val t1 = analyzeExpr(varScope, left); val t2 = analyzeExpr(varScope, right); if ( op == EQ || op == NE ) { if ( !(t1.isSubtype(t2) || t2.isSubtype(t1)) ) Report.error(tree.pos, "Incompatible types: " + t1 + " <=> " + t2); } else { checkIntType(left.pos, t1); checkIntType(right.pos, t2); } IIntType case ReadInt() => IIntType case ReadChar() => IIntType case If(cond, stata, statb) => checkIntType(cond.pos, analyzeExpr(varScope, cond)); analyzeExpr(varScope, stata).lub(analyzeExpr(varScope, statb)).get; case Block(stats, expr) => analyzeExpr(stats.foldLeft(varScope)(analyzeStat), expr) } /** * Generates an error if the expected type is not equal * to the found type. */ private def checkSametype(pos: Int, found: Type, expected: Type): Unit = if (!found.isSametype(expected)) error(pos, "Type mismatch", found, expected); /** * Generates an error if the found type is not equal to IIntType. */ private def checkIntType(pos: Int, found: Type): Unit = checkSametype(pos, found, IIntType); /** * Generates an error if the expected type is not a super type * of the found type. */ private def checkSubtype(pos: Int, header: String, found: Type, expected: Type) = if (!found.isSubtype(expected)) error(pos, header, found, expected); /** * Generates an error message for unexpected types. */ private def error(pos: Int, header: String, found: Type, expected: Type) = Report.error(pos, header + "\nexpected type: " + expected + "\n" + "actual type : " + found); }