You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
379 lines
16 KiB
379 lines
16 KiB
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<Type>
|
|
{
|
|
|
|
/** Current symbol table. Changes with new declarative region. */
|
|
private SymbolTable<Type> sym = new SymbolTable<>();
|
|
/** Global symbol table. */
|
|
private final SymbolTable<Type> 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<Type> 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("<None>");
|
|
cvt.super_class = obj;
|
|
s.put("<None>", cvt);
|
|
//Symbol table entry for inbuilt print function
|
|
ArrayList<ValueType> param = new ArrayList<ValueType>();
|
|
param.add(Type.OBJECT_TYPE);
|
|
s.put("print", new FuncType(param, Type.NONE_TYPE));
|
|
//Symbol table entry for inbuilt len function
|
|
param = new ArrayList<ValueType>();
|
|
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<Type> createScope(SymbolTable<Type> s){
|
|
SymbolTable<Type> 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<Type> globals)
|
|
{
|
|
firstPass = false;
|
|
this.typeChecker = typeChecker;
|
|
errors = errors0;
|
|
this.globals = globals;
|
|
}
|
|
public SymbolTable<Type> 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>(),
|
|
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<Type> 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<Declaration> 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<fun1.parameters.size(); i++)
|
|
if (fun1.parameters.get(i).equals(fun2.parameters.get(i))==false)
|
|
return false;
|
|
return true;
|
|
}
|
|
|
|
@Override
|
|
public Type analyze(ClassDef node)
|
|
{
|
|
ClassVType cvt=new ClassVType(node.name.name);
|
|
if(!putSymChecked(node.name, node.name.name, cvt))
|
|
classDefError = node.name.name;
|
|
|
|
SymbolTable<Type> 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<Type> super_scope=null;
|
|
Set<String> 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<String>();
|
|
HashSet<String> 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<ValueType> 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<Type> 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<Type> currentScope)
|
|
{
|
|
sym = currentScope;
|
|
}
|
|
public void setCurrClass(ClassVType current_class)
|
|
{
|
|
this.current_class = current_class;
|
|
}
|
|
public void setPostCheck(){
|
|
postCheck = true;
|
|
}
|
|
}
|