package chocopy.pa2; import chocopy.common.analysis.AbstractNodeAnalyzer; import chocopy.common.analysis.SymbolTable; import chocopy.common.analysis.types.*; import chocopy.common.astnodes.*; import java_cup.runtime.Symbol; import java.util.*; import com.fasterxml.jackson.annotation.JacksonInject.Value; /** Analyzes declarations to create a top-level symbol table. */ public class DeclarationAnalyzer extends AbstractNodeAnalyzer { /** Current symbol table. Changes with new declarative region. */ private SymbolTable sym = new SymbolTable<>(); /** Global symbol table. */ private final SymbolTable globals; /** Receiver for semantic error messages. */ private final TypeChecker typeChecker; private final Errors errors; private final boolean firstPass; // In the first pass declanalyzer will create the global symtable // In the second pass, typeAnalyzer will call declanalyzer to // analyze local vars/func/class defs and create sub-scope symtable. private ClassVType current_class=null; private boolean postCheck = false; private String classDefError = null; /** A new declaration analyzer sending errors to ERRORS0. */ public void initScope(SymbolTable s){ // Symbol table entry for object class ClassVType cvt = new ClassVType("object"), obj = cvt; s.put("object", cvt); //Symbol table entry for int class cvt = new ClassVType("int"); cvt.super_class = obj; s.put("int", cvt); //Symbol table entry for str class cvt = new ClassVType("str"); cvt.super_class = obj; s.put("str", cvt); //Symbol table entry for bool class cvt = new ClassVType("bool"); cvt.super_class = obj; s.put("bool", cvt); //Symbol table entry for None return type cvt = new ClassVType(""); cvt.super_class = obj; s.put("", cvt); //Symbol table entry for inbuilt print function ArrayList param = new ArrayList(); param.add(Type.OBJECT_TYPE); s.put("print", new FuncType(param, Type.NONE_TYPE)); //Symbol table entry for inbuilt len function param = new ArrayList(); param.add(Type.OBJECT_TYPE); s.put("len", new FuncType(param, Type.INT_TYPE)); //Symbol table entry for inbuilt input function s.put("input", new FuncType(new ArrayList<>(), Type.STR_TYPE)); } public SymbolTable createScope(SymbolTable s){ SymbolTable newScope = new SymbolTable<>(s); initScope(newScope); return newScope; } public DeclarationAnalyzer(Errors errors0) { firstPass = true; errors = errors0; globals = sym; initScope(sym); typeChecker = new TypeChecker(globals, errors); } public DeclarationAnalyzer(Errors errors0, TypeChecker typeChecker, SymbolTable globals) { firstPass = false; this.typeChecker = typeChecker; errors = errors0; this.globals = globals; } public SymbolTable getGlobals() { return globals; } private boolean putSymChecked(Node node, String name, Type ty) { if (ty == null) return false; if (globals.get(name)!= null && !(ty instanceof ClassVType) && globals.get(name) instanceof ClassVType) //class names are only in global scope errors.semError(node, "Cannot shadow class name: %s", name); else if (sym.declares(name)) errors.semError( node, "Duplicate declaration of identifier in same scope: %s", name); else { sym.put(name, ty); return true; } return false; } @Override public Type analyze(Program program) { for (Declaration decl : program.declarations) { Identifier id = decl.getIdentifier(); String name = id.name; Type type = decl.dispatch(this); } // Check for return statements at top for (Stmt stmt : program.statements) { if (stmt instanceof ReturnStmt) errors.semError( stmt, "Return statement cannot appear at the top level"); } return null; } @Override public Type analyze(FuncDef node) { if(!postCheck){ Type fTy = null; if(sym.declares(node.name.name)) fTy = sym.get(node.name.name); FuncType current_func=null; if(!(fTy instanceof FuncType)) { if(fTy == null) { current_func = new FuncType(new ArrayList(), ValueType.annotationToValueType(node.returnType)); for (TypedVar param : node.params) { ValueType p = ValueType.annotationToValueType(param.type); current_func.parameters.add(p); if(classDefError != null && p.className().equals(classDefError)) errors.semError(param.type, "Invalid type annotation; there is no class named: %s", classDefError); } sym.put(node.name.name, current_func); if(!firstPass) { SymbolTable parent = sym.getParent(); if(parent!=null && parent != globals){ parent.put(node.name.name, current_func); } } } else if(fTy instanceof ClassVType) errors.semError(node.name, "Cannot shadow class name: %s", node.name.name); else errors.semError( node.name, "Duplicate declaration of identifier in same scope: %s", node.name.name); } else if(firstPass || sym.declares(node.name.name)) errors.semError( node.name, "Duplicate declaration of identifier in same scope: %s", node.name.name); if(!firstPass){ } return current_func; } else { postCheck = false; ValueType returnType = ValueType.annotationToValueType(node.returnType); if(returnType!=null && !returnType.isSpecialType() && !returnType.isListType() && !(globals.get(returnType.className()) instanceof ClassVType)) errors.semError( node.returnType, "Invalid type annotation; there is no class named: %s", returnType.className()); for(TypedVar param : node.params) { ValueType pTy = ValueType.annotationToValueType(param.type); if(!(pTy.isListType() && !pTy.elementType().equals(Type.EMPTY_TYPE))&&!pTy.isSpecialType() && !(globals.get(pTy.className()) instanceof ClassVType)) errors.semError(param.type, "Invalid type annotation; there is no class named: %s", pTy.className()); putSymChecked(param.identifier, param.identifier.name, pTy); } ArrayList varDefs = new ArrayList<>(), otherDefs = new ArrayList<>(); for (Declaration decl : node.declarations) if(decl instanceof VarDef || decl instanceof GlobalDecl || decl instanceof NonLocalDecl) varDefs.add(decl); else otherDefs.add(decl); for (Declaration decl : varDefs) if(decl instanceof VarDef||decl instanceof NonLocalDecl) decl.dispatch(this); else decl.dispatch(typeChecker); for(Declaration decl : otherDefs) decl.dispatch(this); for(Declaration decl : otherDefs) if(decl instanceof FuncDef) decl.dispatch(typeChecker); return null; } } public boolean compare_functions(FuncType fun1, FuncType fun2) { if (fun1.returnType.equals(fun2.returnType)==false) return false; if (fun1.parameters.size() != fun2.parameters.size()) return false; for (int i=1; i current_scope=createScope(sym); sym=current_scope; current_class=cvt; Type super_class = sym.get(node.superClass.name); if(super_class instanceof ClassVType) cvt.super_class = (ClassVType)super_class;//new ClassVType(super_class.className()); SymbolTable super_scope=null; Set super_syms=null; if (super_class == null) { errors.semError( node.superClass, "Super-class not defined: %s", node.superClass.name); } else if ((super_class instanceof ClassVType)==false) { errors.semError( node.superClass, "Super-class must be a class: %s", node.superClass.name); } else if (node.superClass.name.equals("int") || node.superClass.name.equals("bool") || node.superClass.name.equals("str")) { errors.semError( node.superClass, "Cannot extend special class: %s", node.superClass.name); } else { super_scope = cvt.super_class.scope; if(cvt.super_class.scope != null) super_syms = super_scope.getDeclaredSymbols(); else super_syms = new HashSet(); HashSet curr_syms = new HashSet<>(); for (Declaration decl : node.declarations) { Identifier id = decl.getIdentifier(); String name = id.name; Type type = null;//decl.dispatch(this); type = decl.dispatch(this); if(type instanceof FuncType) {//For function declarations FuncType current_func = (FuncType) type; List params = current_func.parameters; if(name.equals("__init__") ) if( params.size() != 1 || !(params.get(0) instanceof ClassValueType)|| !((ClassValueType)params.get(0)).className().equals(current_class.className)) errors.semError(id, "Method overridden with different type signature: __init__"); else sym.put(name, current_func); if(params.size() < 1 || !(params.get(0) instanceof ClassValueType) || !((ClassValueType)params.get(0)).className().equals(current_class.className)) errors.semError( id, "First parameter of the following method must be of the enclosing class: %s", name); if(curr_syms.contains(name)){ errors.semError(id, "Duplicate declaration of identifier in same scope: %s", name); } else if (super_syms.contains(name)) { if ((super_scope.get(id.name) instanceof FuncType)==false) errors.semError(id, "Cannot re-define attribute: %s", name); else { FuncType super_func = (FuncType) super_scope.get(id.name); if (compare_functions(super_func, current_func)) sym.put(name, current_func); else errors.semError( id, "Method overridden with different type signature: %s", name); } } else sym.put(name, current_func); } else if (name.equals("__init__") && !(type instanceof FuncType)) errors.semError(id, "Cannot re-define attribute: %s", name); else if (super_syms.contains(name)) errors.semError(id, "Cannot re-define attribute: %s", name); else sym.put(name, type); curr_syms.add(name); } } if(super_syms != null) for (String super_sym : super_syms) { if (sym.getDeclaredSymbols().contains(super_sym)==false) sym.put(super_sym, super_scope.get(super_sym)); } sym = sym.getParent(); current_class.scope = current_scope; current_class=null; classDefError = null; return cvt; } boolean isVariableType(Type ty) { return ty.isSpecialType() || ty.equals(Type.OBJECT_TYPE)|| ty.isListType(); } @Override public Type analyze(NonLocalDecl node) { SymbolTable parent=sym.getParent(); if (parent.getParent()!=null && parent.declares(node.variable.name) && isVariableType(sym.get(node.variable.name))) { sym.put(node.variable.name, sym.get(node.variable.name)); return sym.get(node.variable.name); } errors.semError( node.variable, "Not a nonlocal variable: %s", node.variable.name); return null; } @Override public Type analyze(VarDef node) { Type var_type = sym.get(node.var.identifier.name); if(firstPass || (sym != globals && (current_class==null || !sym.equals(current_class.scope)))){ if (sym != globals && globals.get(node.var.identifier.name)!= null && globals.get(node.var.identifier.name) instanceof ClassVType) //class names are only in global scope errors.semError(node.var.identifier, "Cannot shadow class name: %s", node.var.identifier.name); else if(sym.getDeclaredSymbols().contains(node.var.identifier.name)) errors.semError( node.var.identifier, "Duplicate declaration of identifier in same scope: %s", node.var.identifier.name); var_type = ValueType.annotationToValueType(node.var.type); sym.put(node.var.identifier.name, var_type); } Type val_type = node.value.dispatch(typeChecker); if( !firstPass && var_type instanceof ClassValueType) { String className = ((ClassValueType)var_type).className(); Type varVType = sym.get(className); if(!(className != null && varVType instanceof ClassVType)) errors.semError(node.var.type, "Invalid type annotation; there is no class named: %s", (className!=null?className:"")); else if((!val_type.equals(Type.NONE_TYPE) && !StudentAnalysis.subClassOf(varVType,val_type, sym))|| val_type.equals(Type.NONE_TYPE) && var_type.isSpecialType()) errors.semError(node, "Expected type `%s`; got type `%s`", varVType, val_type); } return var_type; } public void setScope(SymbolTable currentScope) { sym = currentScope; } public void setCurrClass(ClassVType current_class) { this.current_class = current_class; } public void setPostCheck(){ postCheck = true; } }