/* 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.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.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)) => // TODO check that field does not yet exist! ownerClass.enterField(FieldSymbol(tree.pos, name.name, analyzeType(typ))) case MethodDef(name, args, rtype, expr) => ownerClass.lookupMethod(name.name) match { case Some(m) => var paramtypes = for ( val Formal(x,y) <- args ) yield analyzeType(y); ownerClass.enterMethod(MethodSymbol(tree.pos, name.name, paramtypes, analyzeType(rtype))) case None => var paramtypes = for ( val Formal(x,y) <- args ) yield analyzeType(y); ownerClass.enterNewMethod(MethodSymbol(tree.pos, name.name, paramtypes, analyzeType(rtype))) } } // 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); val ms = VarSymbol(tree.pos, name.name, mt); ident.sym = ms; varScope + name.name -> ms; } 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.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 Call(expr, name, args) => val ct = analyzeExpr(varScope, expr); //XXX: this implies checking if class exists ct.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(Tuple(a,b) => checkSubtype(tree.pos, "Incompatible types", a, b)) ) IBadType name.sym = v; v.restype case None => Report.error(tree.pos, "Unknown class method " + name + " in " + ct); IBadType } case New(name, args) => classScope.get(name.name) 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).forall(Tuple(a,b) => checkSubtype(tree.pos, "Incompatible types", a, b)) ) 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(analyzeExpr(varScope, cond)) analyzeExpr(varScope, stata).lub(analyzeExpr(varScope, statb)) 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); }