diff --git a/src/main/java/chocopy/common/analysis/SymbolTable.java b/src/main/java/chocopy/common/analysis/SymbolTable.java index 41b9ab018d131504960957576c1efc3519770394..e9bc757e21e26546b2ab3371f6472ad73a536652 100644 --- a/src/main/java/chocopy/common/analysis/SymbolTable.java +++ b/src/main/java/chocopy/common/analysis/SymbolTable.java @@ -47,6 +47,10 @@ public class SymbolTable<T> { return this; } + public void overwrite_put(String k, T v) { + tab.put(k, v); + } + /** Returns whether NAME has a mapping in this region (ignoring * enclosing regions. */ public boolean declares(String name) { diff --git a/src/main/java/chocopy/pa2/StudentAnalysis.java b/src/main/java/chocopy/pa2/StudentAnalysis.java index b8e6f266ddf97cbdecf3ed4d857543b47da088f9..26d710cfa277e8d0a396d4d0cf0d4f212dc9610f 100644 --- a/src/main/java/chocopy/pa2/StudentAnalysis.java +++ b/src/main/java/chocopy/pa2/StudentAnalysis.java @@ -22,11 +22,25 @@ public class StudentAnalysis { // declarationAnalyzer.getGlobals(); SymbolTable<SymbolType> globalSym = new SymbolTable<>(); - //if (!program.hasErrors()) { // TODO: I should run this part again and again, until symbolTable convergences. TypeChecker symBuilder = new TypeChecker(globalSym, program.errors, true); program.dispatch(symBuilder); - //} + // program.dispatch(symBuilder); // Twice run to update these use-before-decl globalDecl/nonlocalDecl symTable entry. + // note: thanks to the silly grammar, it doesn't allow type-deduction. + // so we can support use-before-decl by only one extra run, rather than many many times. + // now globalDecl and nonLocalDecl relies to the second run. + // but we can just allow globalDecl and nonLocalDecl to update symTable on the typeCheckerRun below! + // what a silly design! + // def fuck(): + // def shit(): + // def func(): + // nonlocal c + // # ... + // nonlocal b + // c:decltype(b) = None + // nonlocal a + // b:decltype(a) = None + if(!program.hasErrors()) { TypeChecker next_time = new TypeChecker(symBuilder.getGlobals(), program.errors, false); diff --git a/src/main/java/chocopy/pa2/TypeChecker.java b/src/main/java/chocopy/pa2/TypeChecker.java index 19a64f5ab1733f7a9ff8be5dcf99890827fc61a6..1a24a45aae5406149b459822307167cf434a89b8 100644 --- a/src/main/java/chocopy/pa2/TypeChecker.java +++ b/src/main/java/chocopy/pa2/TypeChecker.java @@ -24,7 +24,8 @@ public class TypeChecker extends AbstractNodeAnalyzer<SymbolType> { /** Collector for errors. */ private Errors errors; - private boolean isFirstRun; + private boolean isBuildingSym; + public boolean buildingSymDone; /** Creates a type checker using GLOBALSYMBOLS for the initial global * symbol table and ERRORS0 to receive semantic errors. */ @@ -32,7 +33,8 @@ public class TypeChecker extends AbstractNodeAnalyzer<SymbolType> { sym = globalSymbols; globals = sym; errors = errors0; - isFirstRun = firstRun; + isBuildingSym = firstRun; + buildingSymDone = true; } public SymbolTable<SymbolType> getGlobals() { return globals; @@ -42,15 +44,17 @@ public class TypeChecker extends AbstractNodeAnalyzer<SymbolType> { * The message is constructed with MESSAGE and ARGS as for * String.format. */ private void err(Node node, String message, Object... args) { - if(!isFirstRun) + if(!isBuildingSym) errors.semError(node, message, args); } ///////////////////////////// Program ////////////////////////////////////// @Override public SymbolType analyze(Program program) { - if(isFirstRun) + if(isBuildingSym) { sym.put("object", OBJECT_TYPE); + sym.put("print", new FuncType(new ArrayList<ValueType>(Arrays.asList(OBJECT_TYPE)), NONE_TYPE)); + } for (Declaration decl : program.declarations) { String name = decl.getIdentifier().name; @@ -64,11 +68,11 @@ public class TypeChecker extends AbstractNodeAnalyzer<SymbolType> { } // TODO: DO NOT throw on duplicate id. generate a compiler error. - if(isFirstRun) + if(isBuildingSym) sym.put(name, type); } - if(isFirstRun) + if(isBuildingSym) // firstRun: symbolTable and memberMap is not useable now. STOP dispatching to avoid crash. return null; for (Stmt stmt : program.statements) { @@ -80,7 +84,7 @@ public class TypeChecker extends AbstractNodeAnalyzer<SymbolType> { ///////////////////////////// Def&decl s ////////////////////////////////////// @Override public SymbolType analyze(VarDef varDef) { - if(isFirstRun) { + if(isBuildingSym) { // firstRun: symbolTable and memberMap is not useable now. STOP dispatching to avoid crash. return resolveTypeAnnotation(varDef.var.type); } @@ -93,9 +97,9 @@ public class TypeChecker extends AbstractNodeAnalyzer<SymbolType> { @Override public SymbolType analyze(ClassDef classDef) { - if(isFirstRun) { - assert classDef.memberMap == null; - classDef.memberMap = new HashMap<>(); // clearing may prevent symTable from convergence forever. + if(isBuildingSym) { + if(classDef.memberMap == null) + classDef.memberMap = new HashMap<>(); // clearing may prevent symTable from convergence forever. } assert classDef.memberMap != null; @@ -128,7 +132,7 @@ public class TypeChecker extends AbstractNodeAnalyzer<SymbolType> { for(Declaration decl : classDef.declarations) { String name = decl.getIdentifier().name; SymbolType type = decl.dispatch(this); - if(isFirstRun) + if(isBuildingSym) classDef.memberMap.put(name, type); } System.out.println("debug: self_type=" + result_type.toString() + ", where symbolMap=" + result_type.memberMap); @@ -139,9 +143,10 @@ public class TypeChecker extends AbstractNodeAnalyzer<SymbolType> { @Override public SymbolType analyze(FuncDef funcDef) { - if(isFirstRun) { - assert funcDef.symTable == null; - sym = funcDef.symTable = new SymbolTable<SymbolType>(sym); + if(isBuildingSym) { + if(funcDef.symTable == null) + funcDef.symTable = new SymbolTable<SymbolType>(sym); + sym = funcDef.symTable; } else sym = funcDef.symTable; @@ -154,7 +159,7 @@ public class TypeChecker extends AbstractNodeAnalyzer<SymbolType> { ValueType type = resolveTypeAnnotation(param.type); //if(typeIsUserDefinedClass(type)) // type = (ValueType) sym.get(((ClassValueType) type).className()); - if(isFirstRun) + if(isBuildingSym) sym.put(name, type); args.add(type); } @@ -162,11 +167,11 @@ public class TypeChecker extends AbstractNodeAnalyzer<SymbolType> { for(Declaration decl : funcDef.declarations) { String name = decl.getIdentifier().name; SymbolType type = decl.dispatch(this); - if(isFirstRun) + if(isBuildingSym) sym.put(name, type); } - if(!isFirstRun) { + if(!isBuildingSym) { // firstRun: symbolTable and memberMap is not useable now. STOP dispatching to avoid crash. for(Stmt stmt : funcDef.statements) { stmt.dispatch(this); @@ -174,7 +179,7 @@ public class TypeChecker extends AbstractNodeAnalyzer<SymbolType> { } FuncType funcT = new FuncType(args, resolveTypeAnnotation(funcDef.returnType)); - if(isFirstRun) + if(isBuildingSym) sym.put(funcDef.name.name, funcT); // this funcdef should be add to both parentSym and localSym to support recursive func. // TA don't like it. OK I won't dispatch this id... @@ -191,11 +196,17 @@ public class TypeChecker extends AbstractNodeAnalyzer<SymbolType> { public SymbolType analyze(GlobalDecl globalDecl) { String name = globalDecl.variable.name; SymbolType T = globals.get(name); - if(T == null) + System.out.println("DEBUG< GOL< " + isBuildingSym + " buildingSym, name=" + name + ", Type=" + T); + if(T == null) { + if(isBuildingSym) + return OBJECT_TYPE; // workaround for fucking use-before-decl errors.semError(globalDecl, "global id '" + name + "' not found in global scope.."); + } if(sym == globals) // ref equal errors.semError(globalDecl, "global declaration '" + name + "' not allowed in global scope."); - //sym.put(name, T); + if(!isBuildingSym) + // dirty! see comment in StudentAnalysis.java + sym.overwrite_put(name, T); return T; } @@ -203,10 +214,16 @@ public class TypeChecker extends AbstractNodeAnalyzer<SymbolType> { public SymbolType analyze(NonLocalDecl nonLocalDecl) { String name = nonLocalDecl.variable.name; SymbolType T = sym.get(name); // auto-iterate through the tree. - if(T == null) + if(T == null) { + if(isBuildingSym) + return OBJECT_TYPE; errors.semError(nonLocalDecl, "nonlocal id '" + name + "' not found in parent scope.."); + } if(sym == globals) errors.semError(nonLocalDecl, "nonlocal declaration '" + name + "' not allowed in global scope."); + if(!isBuildingSym) + // dirty! see comment in StudentAnalysis.java + sym.overwrite_put(name, T); return T; } @@ -414,7 +431,7 @@ public class TypeChecker extends AbstractNodeAnalyzer<SymbolType> { for(Expr arg : node.args) { args_type.add(arg.dispatch(this)); } - if(!fType.parameters.equals(args_type)) { + if(!matchArgList(args_type, fType.parameters)) { err(node, "function parameter type list mismatch: " + node.function.name + ". Expected " + fType.parameters.toString() + ", got " + args_type.toString()); } return node.setInferredType(fType.returnType); @@ -511,7 +528,7 @@ public class TypeChecker extends AbstractNodeAnalyzer<SymbolType> { return NONE_TYPE; } //System.out.println("manual lookup: memberMap=" + ((ClassValueType)sym.get(objType.toString())).memberMap ); - System.out.println("debug: isFirstRun=" + isFirstRun + ", memberMap=" + objType.memberMap + ", objtype=" + objType.toString()); + System.out.println("debug: isBuildingSym=" + isBuildingSym + ", memberMap=" + objType.memberMap + ", objtype=" + objType.toString()); // Won't work. SymbolType resultType = objType.memberMap.get(node.member.name); if(resultType == null) { @@ -603,6 +620,7 @@ public class TypeChecker extends AbstractNodeAnalyzer<SymbolType> { return new ListValueType(elementType); } + ///////////////////////////// helpers /////////////////////////////// private boolean typeConvertible(SymbolType from, SymbolType to) { if(from == null || to == null) throw new RuntimeException("debug: SymbolType can not be null");