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 ;
}
}