diff --git a/src/main/java/chocopy/common/analysis/SymbolTable.java b/src/main/java/chocopy/common/analysis/SymbolTable.java index 930ea9da597d163b60dccee3317d89c7144c773b..41b9ab018d131504960957576c1efc3519770394 100644 --- a/src/main/java/chocopy/common/analysis/SymbolTable.java +++ b/src/main/java/chocopy/common/analysis/SymbolTable.java @@ -1,5 +1,7 @@ package chocopy.common.analysis; +import org.apache.tools.ant.taskdefs.PathConvert; + import java.util.HashMap; import java.util.Map; import java.util.Set; @@ -66,4 +68,13 @@ public class SymbolTable<T> { return tab.isEmpty(); } + public String toString() { + String res = "{"; + for(String k : tab.keySet()) { + res += k + "->" + tab.get(k).toString() + ";"; + } + res += "}"; + return res; + } + } diff --git a/src/main/java/chocopy/pa2/StudentAnalysis.java b/src/main/java/chocopy/pa2/StudentAnalysis.java index aa5410dca2739ecdc9572922f60b4920e27809d5..b8e6f266ddf97cbdecf3ed4d857543b47da088f9 100644 --- a/src/main/java/chocopy/pa2/StudentAnalysis.java +++ b/src/main/java/chocopy/pa2/StudentAnalysis.java @@ -23,12 +23,13 @@ public class StudentAnalysis { SymbolTable<SymbolType> globalSym = new SymbolTable<>(); //if (!program.hasErrors()) { - TypeChecker typeChecker = new TypeChecker(globalSym, program.errors, true); - program.dispatch(typeChecker); + // TODO: I should run this part again and again, until symbolTable convergences. + TypeChecker symBuilder = new TypeChecker(globalSym, program.errors, true); + program.dispatch(symBuilder); //} if(!program.hasErrors()) { - TypeChecker next_time = new TypeChecker(typeChecker.getGlobals(), program.errors, false); + TypeChecker next_time = new TypeChecker(symBuilder.getGlobals(), program.errors, false); program.dispatch(next_time); } diff --git a/src/main/java/chocopy/pa2/TypeChecker.java b/src/main/java/chocopy/pa2/TypeChecker.java index aade9b8862b126f5740a1af9e1781f0258675f36..2b4ea8f58bb6f397168222799375f9f8614d9e70 100644 --- a/src/main/java/chocopy/pa2/TypeChecker.java +++ b/src/main/java/chocopy/pa2/TypeChecker.java @@ -67,6 +67,10 @@ public class TypeChecker extends AbstractNodeAnalyzer<SymbolType> { if(isFirstRun) sym.put(name, type); } + + if(isFirstRun) + // firstRun: symbolTable and memberMap is not useable now. STOP dispatching to avoid crash. + return null; for (Stmt stmt : program.statements) { stmt.dispatch(this); } @@ -76,6 +80,10 @@ public class TypeChecker extends AbstractNodeAnalyzer<SymbolType> { ///////////////////////////// Def&decl s ////////////////////////////////////// @Override public SymbolType analyze(VarDef varDef) { + if(isFirstRun) { + // firstRun: symbolTable and memberMap is not useable now. STOP dispatching to avoid crash. + return resolveTypeAnnotation(varDef.var.type); + } SymbolType lType = varDef.value.dispatch(this); SymbolType vType = varDef.var.dispatch(this); if(!(vType.equals(lType) || lType.equals(NONE_TYPE))) @@ -87,7 +95,7 @@ public class TypeChecker extends AbstractNodeAnalyzer<SymbolType> { public SymbolType analyze(ClassDef classDef) { if(isFirstRun) { assert classDef.memberMap == null; - classDef.memberMap = new HashMap<>(); + classDef.memberMap = new HashMap<>(); // clearing may prevent symTable from convergence forever. } assert classDef.memberMap != null; @@ -95,9 +103,24 @@ public class TypeChecker extends AbstractNodeAnalyzer<SymbolType> { // TA doesn't like this: // classDef.name.dispatch(this); // TA doesn't like this: // classDef.superClass.dispatch(this); + // Append parent to child if not exist. + if(!classDef.superClass.name.equals("object")) { + // This logic doesn't allow superClass use-before-decl, unless loop again and again until convergence. + ClassValueType superClassType = (ClassValueType) sym.get(classDef.superClass.name); + if(superClassType.memberMap != null) { + for(String k : superClassType.memberMap.keySet()) { + if(!classDef.memberMap.containsKey(k)) + classDef.memberMap.put(k, superClassType.memberMap.get(k)); + } + } + } + ClassValueType result_type = new ClassValueType(classDef.name.name); + result_type.memberMap = classDef.memberMap; SymbolTable<SymbolType> symLayer = new SymbolTable<>(sym); + // TODO: Maybe this is not necessary. I'm a cxx programmer. symLayer.put("self", result_type); + System.out.println("debug: self_type=" + result_type.toString() + ", where symbolMap=" + result_type.memberMap); sym = symLayer; for(Declaration decl : classDef.declarations) { @@ -106,9 +129,10 @@ public class TypeChecker extends AbstractNodeAnalyzer<SymbolType> { if(isFirstRun) classDef.memberMap.put(name, type); } + System.out.println("debug: self_type=" + result_type.toString() + ", where symbolMap=" + result_type.memberMap); sym = sym.getParent(); - return new ClassValueType(classDef.name.name); + return result_type; } @Override @@ -125,7 +149,9 @@ public class TypeChecker extends AbstractNodeAnalyzer<SymbolType> { List<ValueType> args = new ArrayList<>(); for(TypedVar param : funcDef.params) { String name = param.identifier.name; - ValueType type = ValueType.annotationToValueType(param.type); + ValueType type = resolveTypeAnnotation(param.type); + //if(typeIsUserDefinedClass(type)) + // type = (ValueType) sym.get(((ClassValueType) type).className()); if(isFirstRun) sym.put(name, type); args.add(type); @@ -138,11 +164,14 @@ public class TypeChecker extends AbstractNodeAnalyzer<SymbolType> { sym.put(name, type); } - for(Stmt stmt : funcDef.statements) { - stmt.dispatch(this); + if(!isFirstRun) { + // firstRun: symbolTable and memberMap is not useable now. STOP dispatching to avoid crash. + for(Stmt stmt : funcDef.statements) { + stmt.dispatch(this); + } } - FuncType funcT = new FuncType(args, ValueType.annotationToValueType(funcDef.returnType)); + FuncType funcT = new FuncType(args, resolveTypeAnnotation(funcDef.returnType)); if(isFirstRun) sym.put(funcDef.name.name, funcT); // this funcdef should be add to both parentSym and localSym to support recursive func. @@ -217,7 +246,7 @@ public class TypeChecker extends AbstractNodeAnalyzer<SymbolType> { left_type = id.dispatch(this); } else { - err(node, "Not implemented assignStmt"); + err(node, "Not implemented assignStmt, target=" + target.getClass().getName()); } if(left_type == null) { err(node, "Syntax error: assign to non-declared variable"); @@ -475,7 +504,10 @@ public class TypeChecker extends AbstractNodeAnalyzer<SymbolType> { err(node, ". operator left operand is not object type."); return NONE_TYPE; } - SymbolType resultType = objType.memberMap.get(node.member.name); // currently won't work. classDef not analyzed. + //System.out.println("manual lookup: memberMap=" + ((ClassValueType)sym.get(objType.toString())).memberMap ); + System.out.println("debug: isFirstRun=" + isFirstRun + ", memberMap=" + objType.memberMap + ", objtype=" + objType.toString()); + // Won't work. + SymbolType resultType = objType.memberMap.get(node.member.name); if(resultType == null) { err(node, "undefined identifier " + node.member.name + " as object member."); return NONE_TYPE; @@ -485,11 +517,13 @@ public class TypeChecker extends AbstractNodeAnalyzer<SymbolType> { @Override public SymbolType analyze(MethodCallExpr node) { - FuncType funcType = (FuncType) node.method.dispatch(this); - if(funcType == null) { - err(node, "method is not callable."); + SymbolType methodType = node.method.dispatch(this); + if(!( methodType instanceof FuncType)) { + err(node, "method is not callable or not found."); return NONE_TYPE; } + + FuncType funcType = (FuncType) methodType; List<SymbolType> args_type = new ArrayList<>(); for(Expr expr : node.args) { args_type.add(expr.dispatch(this)); @@ -511,6 +545,12 @@ public class TypeChecker extends AbstractNodeAnalyzer<SymbolType> { } System.out.println("var " + varName + "->" + varType.toString()); + if(varType instanceof ClassValueType) { + // TODO: This is just a workaround. I don't know why this line is required. + // Should debug it sometime. + varType = fixClassTypeProblem((ValueType) varType); + } + if (varType != null) { return id.setInferredType(varType); } @@ -521,11 +561,17 @@ public class TypeChecker extends AbstractNodeAnalyzer<SymbolType> { @Override public SymbolType analyze(TypedVar node) { - return ValueType.annotationToValueType(node.type); + SymbolType result_type = resolveTypeAnnotation(node.type); + //if(typeIsUserDefinedClass(result_type)) + // result_type = sym.get(((ClassValueType)result_type).className()); + return result_type; } + @Override public SymbolType analyze(ClassType node) { - return new ClassValueType(node.className); + // TODO: verify it. + throw new RuntimeException("TODO: not verified."); + //return new ClassValueType(node.className); } @Override public SymbolType analyze(ListType node) { @@ -563,5 +609,33 @@ public class TypeChecker extends AbstractNodeAnalyzer<SymbolType> { return l; return OBJECT_TYPE; } + private boolean typeIsUserDefinedClass(SymbolType type) { + if(type instanceof ClassValueType) { + if(type.equals(INT_TYPE) || type.equals(BOOL_TYPE) || type.equals(STR_TYPE) || type.equals(NONE_TYPE) || type.equals(EMPTY_TYPE) || type.equals(OBJECT_TYPE)) + return false; + if(type.isFuncType() || type.isListType()) + return false; + return true; + } + else + return false; + } + private ValueType fixClassTypeProblem(ValueType valType) { + if(typeIsUserDefinedClass(valType)) { + System.out.println("debug: annotation class name resolved." + valType.className()); + ClassValueType realType = (ClassValueType)sym.get(valType.className()); + if(realType == null) { + System.out.println("FIXME: annotation uses an unknown class type. Unable to assign member map."); + return valType; + } + System.out.println("debug: -m- " + realType.memberMap); + return realType; + } + else + return valType; + } + private ValueType resolveTypeAnnotation(TypeAnnotation annotation) { + return fixClassTypeProblem(ValueType.annotationToValueType(annotation)); + } }