diff --git a/src/main/java/chocopy/pa2/TypeChecker.java b/src/main/java/chocopy/pa2/TypeChecker.java index 8f97c7f25cb505f0ee899d62c6e16c678cd0b02d..9c86167a368688998470eb4bef809eaeb231483e 100644 --- a/src/main/java/chocopy/pa2/TypeChecker.java +++ b/src/main/java/chocopy/pa2/TypeChecker.java @@ -42,9 +42,9 @@ public class TypeChecker extends AbstractNodeAnalyzer<SymbolType> { public SymbolType analyze(VarDef varDef) { SymbolType lType = varDef.value.dispatch(this); SymbolType vType = varDef.var.dispatch(this); - if(!vType.equals(lType)) + if(!(vType.equals(lType) || lType.equals(NONE_TYPE))) err(varDef, "incorrect initialization type"); - return lType; + return vType; } @Override @@ -177,31 +177,25 @@ public class TypeChecker extends AbstractNodeAnalyzer<SymbolType> { @Override public SymbolType analyze(AssignStmt node) { SymbolType right_type = node.value.dispatch(this); + SymbolType left_type = null; if(right_type == null) right_type = NONE_TYPE; - if(node.targets.size() == 1) { - String target = node.targets.get(0).toString(); - System.out.println("debug: assign to target " + target); - SymbolType left_type = sym.get(target); + for(Expr target : node.targets) { + Identifier id = (Identifier) target; + if(id == null) { + err(node, "assign to non-identifier expr."); + return NONE_TYPE; + } + left_type = id.dispatch(this); if(left_type == null) { - err(node, "Syntax error: assign to non-declared variable."); - return OBJECT_TYPE; + err(node, "Syntax error: assign to non-declared variable: " + id.name); + return NONE_TYPE; } - if(!left_type.equals(right_type)) { - err(node, "Syntax error: implicit type convertion not allowed."); + if(!typeConvertible(right_type, left_type)) { + err(node, "Syntax error: implicit type convertion failed."); } - return right_type; - } - else { - if(!right_type.isListType()) { - err(node, "Syntax error: assign non-list type to list targets"); - return OBJECT_TYPE; - } - ListValueType right_ls_type = (ListValueType) right_type; - // TODO: assign list to list - err(node, "Not implemented error: assign list to list"); - return OBJECT_TYPE; } + return left_type; } @Override @@ -363,11 +357,15 @@ public class TypeChecker extends AbstractNodeAnalyzer<SymbolType> { SymbolType this_type = expr.dispatch(this); if(elementType == null) elementType = this_type; - if(!this_type.equals(elementType)) - err(node, "list elements must have the same type."); + if(!typeConvertible(this_type, elementType)) { + //err(node, "list elements must have the same type."); + elementType = calcCommonType(this_type, elementType); + } } - ListValueType resultType = new ListValueType(elementType); - return node.setInferredType(resultType); + if(elementType == null) + return node.setInferredType(EMPTY_TYPE); + else + return node.setInferredType(new ListValueType(elementType)); } @Override @@ -415,4 +413,35 @@ public class TypeChecker extends AbstractNodeAnalyzer<SymbolType> { err(id, "Not a variable: %s", varName); return id.setInferredType(ValueType.OBJECT_TYPE); } + + private boolean typeConvertible(SymbolType from, SymbolType to) { + if(from == null || to == null) + throw new RuntimeException("debug: SymbolType can not be null"); + if(from.equals(NONE_TYPE) || to.equals(OBJECT_TYPE)) + return true; + if(from.equals(EMPTY_TYPE)) from = new ListValueType(NONE_TYPE); + if(to.equals(EMPTY_TYPE)) to = new ListValueType(NONE_TYPE); + if(!from.isListType() || !to.isListType()) { + return from.equals(to); + } + + ListValueType lsFrom = (ListValueType) from, lsTo = (ListValueType) to; + if(lsFrom.elementType == null || lsFrom.elementType.equals(NONE_TYPE)) + return true; + return typeConvertible(lsFrom.elementType, lsTo.elementType); + } + private SymbolType calcCommonType(SymbolType another, SymbolType preferred) { + SymbolType l = another, r = preferred; + if(l == null || r == null) + throw new RuntimeException("debug: SymbolType can not be null"); + if(l.equals(NONE_TYPE)) + return r; + if(r.equals(NONE_TYPE)) + return l; + if(typeConvertible(l, r)) + return r; + if(typeConvertible(r, l)) + return l; + return OBJECT_TYPE; + } }