| |
---|
| | val emptyVarScope: VarScope = ListMap.Empty; |
---|
| | |
---|
| | // Analyze a program |
---|
| | def analyzeProgram(tree: Program): Unit = tree match { |
---|
| | case Program(classes, main) => |
---|
| | case Program(classes, main) => |
---|
| | classes.foreach(analyzeClass); |
---|
| | analyzeExpr(emptyVarScope, main); |
---|
| | () |
---|
| | } |
---|
| |
---|
| | 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 => |
---|
| | Report.error(tree.pos, "class '" + name + "' already defined") |
---|
| | case None => |
---|
| | val cs = ClassSymbol(tree.pos, name.name, None); |
---|
| | classScope = classScope + name.name -> cs; |
---|
| | //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); |
---|
| | Report.error(tree.pos, "class '" + name + "' already defined"); |
---|
| | case None => |
---|
| | classScope.get(extend.get.name) match { |
---|
| | case None => Report.error(tree.pos, "Superclass not defined: " + extend.get.name) |
---|
| | case None => Report.error(tree.pos, "superclass '" + extend.get.name + "' not defined") |
---|
| | case _ => () |
---|
| | } |
---|
| | val cs = ClassSymbol(tree.pos, name.name, classScope.get(extend.get.name)); |
---|
| | classScope = classScope + name.name -> cs; |
---|
| | //classScope(name.name) = cs; |
---|
| | members.foreach(x => analyzeMember(cs,x)) |
---|
| | } |
---|
| | } |
---|
| | |
---|
| | |
---|
| | |
---|
| | // Analyze a member |
---|
| | private def analyzeMember(ownerClass: ClassSymbol, tree: Member): Unit = |
---|
| | 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); |
---|
| | Report.error(tree.pos, "class variable '" + name + "' already defined"); |
---|
| | case None => |
---|
| | name.sym = FieldSymbol(tree.pos, name.name, analyzeType(typ)); |
---|
| | ownerClass.enterField(name.sym.asInstanceOf[FieldSymbol]); |
---|
| | } |
---|
| |
---|
| | var myVarScope = emptyVarScope; |
---|
| | |
---|
| | myVarScope = myVarScope + "this" -> VarSymbol(ownerClass.pos, "this", IClassType(ownerClass)); |
---|
| | |
---|
| | for ( val x <- ownerClass.allFields ) { |
---|
| | Console.println(x); |
---|
| | // TODO: FieldSymbols can't be VarSymbols !!! |
---|
| | /*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)); |
---|
| |
---|
| | case None => |
---|
| | ownerClass.enterNewMethod(name.sym.asInstanceOf[MethodSymbol]); |
---|
| | } |
---|
| | |
---|
| | checkSubtype(tree.pos, "Return type mismatch", analyzeExpr(myVarScope, expr), rt); |
---|
| | checkSubtype(tree.pos, "returntype mismatch", analyzeExpr(myVarScope, expr), rt); |
---|
| | } |
---|
| | |
---|
| | // Analyze a statement |
---|
| | private def analyzeStat(varScope: VarScope, tree: Stat): VarScope = |
---|
| |
---|
| | |
---|
| | case Var(name, typ, expr) => |
---|
| | varScope.get(name.name) match { |
---|
| | case Some(v) => |
---|
| | Report.error(tree.pos, "Variable already defined " + name.name); |
---|
| | Report.error(tree.pos, "variable '" + name + "' already defined"); |
---|
| | varScope |
---|
| | case None => |
---|
| | val mt = analyzeType(typ); |
---|
| | checkSubtype(tree.pos, "Incompatible types", analyzeExpr(varScope, expr), mt); |
---|
| | 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); |
---|
| | checkSubtype(tree.pos, "incompatible types", analyzeExpr(varScope, expr), v.vartype); |
---|
| | case None => |
---|
| | Report.error(tree.pos, "Unknown variable " + ident.name); |
---|
| | Report.error(tree.pos, "unknown variable '" + ident.name + "'"); |
---|
| | } |
---|
| | varScope |
---|
| | |
---|
| | case Do(expr) => |
---|
| |
---|
| | case Some(c) => |
---|
| | name.sym = c; |
---|
| | IClassType(c) |
---|
| | case None => |
---|
| | Report.error(tree.pos, "type " + name + " is unknown"); |
---|
| | Report.error(tree.pos, "type '" + name + "' is unknown"); |
---|
| | IBadType |
---|
| | } |
---|
| | } |
---|
| | |
---|
| |
---|
| | case Some(v) => |
---|
| | name.sym = v; |
---|
| | v.vartype |
---|
| | case None => |
---|
| | Report.error(tree.pos, "Unknown variable " + name); |
---|
| | Report.error(tree.pos, "unknown variable '" + name + "'"); |
---|
| | IBadType |
---|
| | } |
---|
| | |
---|
| | case Select(expr, name) => |
---|
| |
---|
| | case Some(v) => |
---|
| | name.sym = v; |
---|
| | v.fieldtype |
---|
| | case None => |
---|
| | Report.error(tree.pos, "Unknown class variable " + name + " in " + ct); |
---|
| | Report.error(tree.pos, "unknown class variable '" + name + "' in " + ct); |
---|
| | IBadType; |
---|
| | } |
---|
| | case _ => |
---|
| | Report.error(tree.pos, "Field '" + name.name + "' does not exist"); |
---|
| | Report.error(tree.pos, "class variable '" + name.name + "' does not exist"); |
---|
| | IBadType; |
---|
| | } |
---|
| | |
---|
| | case Call(expr, name, args) => |
---|
| | val ct = analyzeExpr(varScope, expr); //XXX: this implies checking if class exists |
---|
| | val ct = analyzeExpr(varScope, expr); //XXX: this implies checking if class exists |
---|
| | |
---|
| | ct match { |
---|
| | case IClassType(c) => |
---|
| | 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); |
---|
| | 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) ); |
---|
| | val argtypes = args.map(x => analyzeExpr(varScope, x)); |
---|
| | if ( !argtypes.zip(v.paramtypes).forall( x:Pair[Type,Type] => x._1.isSubtype(x._2)) ) { |
---|
| | Report.error(tree.pos, "incompatible argument types for class method: \nrequired:" + v.paramtypes.foldLeft(" ")((a,b)=>a + b) + |
---|
| | "\nfound:" + argtypes.foldLeft(" ")((a,b)=>a + b) ); |
---|
| | IBadType; |
---|
| | } |
---|
| | name.sym = v; |
---|
| | v.restype |
---|
| | |
---|
| | case None => |
---|
| | Report.error(tree.pos, "Unknown class method " + name + " in " + ct); |
---|
| | Report.error(tree.pos, "unknown class method '" + name + "' in " + ct); |
---|
| | IBadType |
---|
| | } |
---|
| | case _ => |
---|
| | Report.error(tree.pos, "Function '" + name.name + "' does not exist"); |
---|
| | IBadType; |
---|
| | Report.error(tree.pos, "method '" + 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); |
---|
| | 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); |
---|
| | val argtypes = args.map(x => analyzeExpr(varScope, x)); |
---|
| | val fieldtypes = v.allFields.map(y => y.fieldtype); |
---|
| | if ( !argtypes.zip(fieldtypes).forall( x:Pair[Type, Type] => x._1.isSubtype(x._2)) ) { |
---|
| | Report.error(tree.pos, "incompatible argument types for class constructor: \nrequired:" + fieldtypes.foldLeft(" ")((a,b) => a + b) + |
---|
| | "\nfound:" + argtypes.foldLeft(" ")((a,b) => a + b)); |
---|
| | IBadType; |
---|
| | } |
---|
| | |
---|
| | |
---|
| | name.sym = v; |
---|
| | IClassType(v) |
---|
| | case None => |
---|
| | Report.error(tree.pos, "Class " + name + " is unknown"); |
---|
| | Report.error(tree.pos, "class '" + name + "' is unknown"); |
---|
| | IBadType |
---|
| | } |
---|
| | |
---|
| | case NullLit() => |
---|
| |
---|
| | 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); |
---|
| | Report.error(tree.pos, "incompatible types: '" + t1 + "' <=> '" + t2 + "'"); |
---|
| | } else { |
---|
| | checkIntType(left.pos, t1); |
---|
| | checkIntType(right.pos, t2); |
---|
| | } |
---|
| |
---|
| | IIntType |
---|
| | |
---|
| | case If(cond, stata, statb) => |
---|
| | checkIntType(cond.pos, analyzeExpr(varScope, cond)); |
---|
| | analyzeExpr(varScope, stata).lub(analyzeExpr(varScope, statb)).get; |
---|
| | analyzeExpr(varScope, stata).lub(analyzeExpr(varScope, statb)) match { |
---|
| | case Some(v) => v |
---|
| | case None => IBadType |
---|
| | } |
---|
| | |
---|
| | 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); |
---|
| | if (!found.isSametype(expected)) |
---|
| | error(pos, "type mismatch", found, expected); |
---|
| | |
---|
| | /** |
---|
| | * Generates an error if the found type is not equal to IIntType. |
---|
| | */ |
---|
| |
---|
| | * 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" + |
---|
| | "found type : " + found); |
---|
| | "actual type : " + found); |
---|
| | |
---|
| | } |
---|
| | |
---|
| | |