Final bug fixes.
Worklog. Added test diff.py demonstrating the differences between the reference compiler.
This commit is contained in:
@@ -4,7 +4,11 @@ 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>
|
||||
{
|
||||
@@ -22,61 +26,51 @@ public class DeclarationAnalyzer extends AbstractNodeAnalyzer<Type>
|
||||
// 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;
|
||||
// Symbol table entry for object class
|
||||
ClassVType cvt = new ClassVType("object"), obj = cvt;
|
||||
FuncType init = new FuncType(Type.OBJECT_TYPE);
|
||||
init.parameters.add(Type.OBJECT_TYPE);
|
||||
SymbolTable<Type> cvt_scope=new SymbolTable<>(sym);
|
||||
cvt_scope.put("init",init);
|
||||
cvt.scope=cvt_scope;
|
||||
sym.put("object", cvt);
|
||||
//Symbol table entry for int class
|
||||
cvt = new ClassVType("int");
|
||||
cvt.super_class = obj;
|
||||
init = new FuncType(Type.INT_TYPE);
|
||||
init.parameters.add(Type.INT_TYPE);
|
||||
cvt_scope=new SymbolTable<>(sym);
|
||||
cvt_scope.put("init",init);
|
||||
cvt.scope=cvt_scope;
|
||||
sym.put("int", cvt);
|
||||
//Symbol table entry for str class
|
||||
cvt = new ClassVType("str");
|
||||
cvt.super_class = obj;
|
||||
init = new FuncType(Type.STR_TYPE);
|
||||
init.parameters.add(Type.STR_TYPE);
|
||||
cvt_scope=new SymbolTable<>(sym);
|
||||
cvt_scope.put("init",init);
|
||||
cvt.scope=cvt_scope;
|
||||
sym.put("str", cvt);
|
||||
//Symbol table entry for bool class
|
||||
cvt = new ClassVType("bool");
|
||||
cvt.super_class = obj;
|
||||
init = new FuncType(Type.BOOL_TYPE);
|
||||
init.parameters.add(Type.BOOL_TYPE);
|
||||
cvt_scope=new SymbolTable<>(sym);
|
||||
cvt_scope.put("init",init);
|
||||
cvt.scope=cvt_scope;
|
||||
sym.put("bool", cvt);
|
||||
//Symbol table entry for None return type
|
||||
cvt = new ClassVType("<None>");
|
||||
cvt.super_class = obj;
|
||||
sym.put("<None>", cvt);
|
||||
//Symbol table entry for inbuilt print function
|
||||
ArrayList<ValueType> param = new ArrayList<ValueType>();
|
||||
param.add(Type.OBJECT_TYPE);
|
||||
sym.put("print", new FuncType(param, Type.NONE_TYPE));
|
||||
//Symbol table entry for inbuilt len function
|
||||
param = new ArrayList<ValueType>();
|
||||
param.add(Type.OBJECT_TYPE);
|
||||
sym.put("len", new FuncType(param, Type.INT_TYPE));
|
||||
//Symbol table entry for inbuilt input function
|
||||
sym.put("input", new FuncType(new ArrayList<>(), Type.STR_TYPE));
|
||||
initScope(sym);
|
||||
typeChecker = new TypeChecker(globals, errors);
|
||||
}
|
||||
|
||||
@@ -91,10 +85,10 @@ public class DeclarationAnalyzer extends AbstractNodeAnalyzer<Type>
|
||||
{
|
||||
return globals;
|
||||
}
|
||||
private void putSymChecked(Node node, String name, Type ty)
|
||||
private boolean putSymChecked(Node node, String name, Type ty)
|
||||
{
|
||||
if (ty == null)
|
||||
return;
|
||||
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);
|
||||
@@ -102,7 +96,11 @@ public class DeclarationAnalyzer extends AbstractNodeAnalyzer<Type>
|
||||
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)
|
||||
@@ -126,42 +124,53 @@ public class DeclarationAnalyzer extends AbstractNodeAnalyzer<Type>
|
||||
@Override
|
||||
public Type analyze(FuncDef node)
|
||||
{
|
||||
Type fTy = globals.get(node.name.name);
|
||||
FuncType current_func=null;
|
||||
if(!postCheck){
|
||||
Type fTy = null;
|
||||
if(sym.declares(node.name.name))
|
||||
fTy = sym.get(node.name.name);
|
||||
|
||||
if(!(fTy instanceof FuncType))
|
||||
{
|
||||
if(fTy == null)
|
||||
FuncType current_func=null;
|
||||
|
||||
if(!(fTy instanceof FuncType))
|
||||
{
|
||||
current_func = new FuncType(new ArrayList<ValueType>(),
|
||||
ValueType.annotationToValueType(node.returnType));
|
||||
|
||||
for (TypedVar param : node.params)
|
||||
if(fTy == null)
|
||||
{
|
||||
Type p = ValueType.annotationToValueType(param.type);
|
||||
current_func.parameters.add((ValueType)p);
|
||||
}
|
||||
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);
|
||||
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(fTy instanceof ClassVType)
|
||||
errors.semError(node.name, "Cannot shadow class name: %s", node.name.name);
|
||||
else
|
||||
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;
|
||||
|
||||
}
|
||||
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){
|
||||
ValueType returnType = ValueType.annotationToValueType(node.returnType);
|
||||
if(returnType!=null && !returnType.isSpecialType() && !returnType.isListType() && !(globals.get(returnType.className()) instanceof ClassVType))
|
||||
errors.semError(
|
||||
@@ -187,13 +196,14 @@ public class DeclarationAnalyzer extends AbstractNodeAnalyzer<Type>
|
||||
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);
|
||||
else
|
||||
decl.dispatch(this);
|
||||
return null;
|
||||
|
||||
}
|
||||
return current_func;
|
||||
}
|
||||
|
||||
public boolean compare_functions(FuncType fun1, FuncType fun2)
|
||||
@@ -212,7 +222,10 @@ public class DeclarationAnalyzer extends AbstractNodeAnalyzer<Type>
|
||||
public Type analyze(ClassDef node)
|
||||
{
|
||||
ClassVType cvt=new ClassVType(node.name.name);
|
||||
SymbolTable<Type> current_scope=new SymbolTable<>(sym);
|
||||
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);
|
||||
@@ -262,9 +275,10 @@ public class DeclarationAnalyzer extends AbstractNodeAnalyzer<Type>
|
||||
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==false) || ((ClassValueType)params.get(0)).className().equals(current_class.className)==false)
|
||||
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);
|
||||
}
|
||||
@@ -303,7 +317,7 @@ public class DeclarationAnalyzer extends AbstractNodeAnalyzer<Type>
|
||||
sym = sym.getParent();
|
||||
current_class.scope = current_scope;
|
||||
current_class=null;
|
||||
putSymChecked(node.name, node.name.name, cvt);
|
||||
classDefError = null;
|
||||
return cvt;
|
||||
}
|
||||
boolean isVariableType(Type ty)
|
||||
@@ -343,7 +357,8 @@ public class DeclarationAnalyzer extends AbstractNodeAnalyzer<Type>
|
||||
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!=Type.NONE_TYPE && !StudentAnalysis.subClassOf(varVType,val_type, sym))
|
||||
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;
|
||||
@@ -357,4 +372,7 @@ public class DeclarationAnalyzer extends AbstractNodeAnalyzer<Type>
|
||||
{
|
||||
this.current_class = current_class;
|
||||
}
|
||||
public void setPostCheck(){
|
||||
postCheck = true;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -90,7 +90,7 @@ public class StudentAnalysis {
|
||||
TypeChecker typeChecker = new TypeChecker(globalSym, program.errors);
|
||||
program.dispatch(typeChecker);
|
||||
}
|
||||
//System.out.println(program);
|
||||
// System.out.println(program);
|
||||
return program;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -15,6 +15,7 @@ import static chocopy.common.analysis.types.Type.INT_TYPE;
|
||||
import static chocopy.common.analysis.types.Type.OBJECT_TYPE;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashMap;
|
||||
|
||||
import javax.swing.text.StyledEditorKit.BoldAction;
|
||||
|
||||
@@ -33,7 +34,9 @@ public class TypeChecker extends AbstractNodeAnalyzer<Type> {
|
||||
private boolean returned = false, member = false;
|
||||
/** Collector for errors. */
|
||||
private final Errors errors;
|
||||
private Boolean assign = false;
|
||||
private boolean assign = false;
|
||||
private boolean declAnalyzed = false;
|
||||
private final HashMap<FuncDef, SymbolTable<Type>> funcScopes;
|
||||
/**
|
||||
* Creates a type checker using GLOBALSYMBOLS for the initial global symbol table and ERRORS0 to
|
||||
* receive semantic errors.
|
||||
@@ -44,14 +47,23 @@ public class TypeChecker extends AbstractNodeAnalyzer<Type> {
|
||||
errors = errors0;
|
||||
currReturnType = null;
|
||||
declAnalyzer = new DeclarationAnalyzer(errors0, this, globalSymbols);
|
||||
funcScopes = new HashMap<>();
|
||||
}
|
||||
/**
|
||||
* Inserts an error message in NODE if there isn't one already. The message is constructed with
|
||||
* MESSAGE and ARGS as for String.format.
|
||||
*/
|
||||
boolean isVariableType(Type ty){
|
||||
private boolean isVariableType(Type ty){
|
||||
return ty.isSpecialType() || ty.equals(Type.OBJECT_TYPE);
|
||||
}
|
||||
public boolean pushDeclAnalyzed() {
|
||||
boolean orig = declAnalyzed;
|
||||
declAnalyzed = true;
|
||||
return orig;
|
||||
}
|
||||
public void popDeclAnalyzed(boolean orig) {
|
||||
declAnalyzed = orig;
|
||||
}
|
||||
private Type declAnalyze(Node node){
|
||||
//if(currentScope != sym)
|
||||
declAnalyzer.setScope(currentScope);
|
||||
@@ -129,20 +141,37 @@ public class TypeChecker extends AbstractNodeAnalyzer<Type> {
|
||||
public Type analyze(BooleanLiteral node) {
|
||||
return node.setInferredType(Type.BOOL_TYPE);
|
||||
}
|
||||
public void dispatchFuncDef(FuncDef node, SymbolTable<Type> scope, boolean declAnalyzed){
|
||||
boolean prevDeclAnalyzed = this.declAnalyzed;
|
||||
this.declAnalyzed = declAnalyzed;
|
||||
SymbolTable<Type> origScope = currentScope;
|
||||
currentScope = scope;
|
||||
node.dispatch(this);
|
||||
currentScope = origScope;
|
||||
this.declAnalyzed = prevDeclAnalyzed;
|
||||
}
|
||||
@Override
|
||||
public Type analyze(FuncDef node) {
|
||||
returned = false;
|
||||
Type prevReturnType = this.currReturnType;
|
||||
currentScope = new SymbolTable<>(currentScope);
|
||||
this.currReturnType = ValueType.annotationToValueType(node.returnType);
|
||||
declAnalyze(node);
|
||||
for(Stmt st : node.statements)
|
||||
st.dispatch(this);
|
||||
SymbolTable<Type> origScope = currentScope;
|
||||
if(funcScopes.get(node) != null)
|
||||
System.out.println("error");
|
||||
{
|
||||
currentScope = declAnalyzer.createScope(currentScope);
|
||||
funcScopes.put(node, currentScope);
|
||||
declAnalyzer.setPostCheck();
|
||||
declAnalyze(node);
|
||||
//currentScope = funcScopes.get(node);
|
||||
returned = false;
|
||||
Type prevReturnType = this.currReturnType;
|
||||
this.currReturnType = ValueType.annotationToValueType(node.returnType);
|
||||
for(Stmt st : node.statements)
|
||||
st.dispatch(this);
|
||||
|
||||
if(currReturnType != null && currReturnType.isSpecialType() && !returned)
|
||||
err(node.name, "All paths in this function/method must have a return statement: %s", node.name.name);
|
||||
this.currReturnType = prevReturnType;
|
||||
currentScope = currentScope.getParent();
|
||||
if(currReturnType != null && currReturnType.isSpecialType() && !returned)
|
||||
err(node.name, "All paths in this function/method must have a return statement: %s", node.name.name);
|
||||
this.currReturnType = prevReturnType;
|
||||
}
|
||||
currentScope = origScope;
|
||||
return null;
|
||||
}
|
||||
|
||||
@@ -274,7 +303,7 @@ public class TypeChecker extends AbstractNodeAnalyzer<Type> {
|
||||
ty = currentScope.get(((ClassValueType) ty).className());
|
||||
if(ty instanceof ClassVType){
|
||||
ClassVType classTy = (ClassVType) ty;
|
||||
Type type = classTy.scope.get(node.member.name);
|
||||
Type type = classTy.scope == null? null:classTy.scope.get(node.member.name);
|
||||
if(type != null)
|
||||
return node.setInferredType(type);
|
||||
else
|
||||
|
||||
Reference in New Issue
Block a user