Merge pull request #4 from nyu-compiler-construction/bill/worklog_debugging_finished

Finished
master
Sanjar 4 years ago committed by GitHub
commit 7d3f526e96
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23

@ -5,14 +5,24 @@
<br>
## Passes:
- First Pass: Creates the symbol tables and adds all the elements of the program to the symbol table. The first step is creating the basic table symbol, which is the parent(root) of the symbol table structure(which forms a tree-like structure). Classes for Object, int, bool and str are added to this symbol table. This table contains the global declarations. At each level of scope, we create a symbol table which points to the parent symbol table. Variables, functions and classes are added to the appropriate symbol table according to the scope.
- Second Pass: At each node of the abstract syntax tree, the types are identified and populated.
### We generally used 2 passes to populate the symbol table and do the error checkings:
- Initialization: Every symbol table (incl. the global one and underlying class/function/temporary scope) is created with the built-in types `int str None list Empty object` and built-in functions `print len input`.
- The First Pass creates the global symbol tables by traversing the syntax tree's declaration part (non-recursively, will explain later.). It adds all the global symbols (incl. classes, global functions, global variables) to the global symbol table. Because we don't need type checking to populate the global symbol table, this pass is completely done in the `DeclarationAnalyzer` class. The driver for this pass is DeclarationAnalyzer.analyze(Program).
- The Second Pass does type checking for each statement and definition. It also recursively 'expends' every class and function definition and creates sub-scopes for them. When expending, we first need to process the underlying declarations and add them to the sub-SymbolTable of the corresponding scope. The statements inside these classes/functions or blocks are checked with their corresponding sub-SymbolTable. More specifically, each function, class, variable declaration are visited twice, the first time to create (sub-)SymbolTables and the second time to determine types. We reused the code in `DeclarationAnalyzer` declarations in such sub-scopes. To do this, in the `TypeChecker` class, we create an object of `DeclarationAnalyzer` and dispatch the nodes we need to analyze and update the current SymbolTable from to declaration analyzer. After analyzing a sub-scope, every function and class declaration is visited the second time to dispatch the underlying statements. This process is done recursively until we reached the deepest structure(function/class). Since the `dispatch` method is basically a function call and the traversing order tp the AST nodes follows the scoping hierarchy, we naturally make use of the stack frame to push and pop the sub-SymbolTables. Overall, each declaration is visited exactly twice, and each statement is visited once.
- The compilation will stop when there're errors found during the first pass. We didn't use more passes because this 2-pass architecture is sufficient to complete type checking for ChocoPy and more paths will only add to the complexity of the algorithm.
## Recovery:
-
- Whenever an error is encountered that causes ambiguity, we chose a default action and continue the compilation process. For example, when a Type mismatch happens, the default action is that the lhs keeps its original types.
- The compilation process stops when errors are found in constructing global symbol table. Because declaration errors adds too much ambiguities and it will make less sense to continue compiling.
## Challenges:
- Nested structures were a challenge. A function inside a function needs to handle variable references as well as ensure function calls have appropriate scope.
- Testing various scenarious with similarly defined variables were time consuming. Instead, we defined certain set of variables in the begging of the student contributed test programs, and then used the same variables troughout the programs to cover various bad and good scenarious.
- Another challenge was to come up with good test cases to have a broader cover. Our approach to this issue was investigating Type Checking rules and writing adverse code to those rules to see if our analyzer can make correct inferences.
- Nested structures were a challenge. A function inside a function/class needs us to build correct scoping as well as dealing with dependencies.
- This is dealt by the declaration-statement-definition recursion we described in the second pass above.
- Error reporting in PA2 is more complex than PA1, generally because there're more types of errors can happen in semantic analysis and there needs to be default actions when each type of error happens. And because of that, the correctness of error handling is very hard to check.
- In order for us to easily determine the correctness, we intentionally matches the error messages to the reference implementation.
- However, there're certainly discrepancies with the reference compiler because of implementation or architectural differences. We didn't matches those differences that doesn't seems to affect the overall correctness, we'll show these differences in diff.py.
- Assignment compatibilities.
- We dealt with this by finding the least common ancestor of the two classes. Special cases such as empty lists are dealt with seprately. This is implemented as a static helper method in class `StudentAnalysis`.
- Testing various scenarious with similarly defined variables were time consuming. Instead, we defined certain set of variables in the begging of the student contributed test programs, and then used the same variables troughout the programs to cover various bad and good scenarious.
- Another challenge was to come up with good test cases to have a broader cover. Our approach to this issue was investigating Type Checking rules and writing adverse code to those rules to see if our analyzer can make correct inferences.
## Improvements:
- Added more tests to rigorously check program flow and indentation.
- Added more tests to rigorously check program flow. And a test(diff.py) to show a case where our implementation showed better recoverability compared to the reference compiler.

@ -4,7 +4,9 @@ import chocopy.common.analysis.AbstractNodeAnalyzer;
import chocopy.common.analysis.SymbolTable;
import chocopy.common.analysis.types.*;
import chocopy.common.astnodes.*;
import java.util.*;
/** Analyzes declarations to create a top-level symbol table. */
public class DeclarationAnalyzer extends AbstractNodeAnalyzer<Type>
{
@ -22,64 +24,55 @@ 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 DeclarationAnalyzer(Errors errors0)
{
firstPass = true;
errors = errors0;
globals = sym;
public void initScope(SymbolTable<Type> s){
// 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);
s.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);
s.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);
s.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);
s.put("bool", cvt);
//Symbol table entry for None return type
cvt = new ClassVType("<None>");
cvt.super_class = obj;
sym.put("<None>", cvt);
s.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));
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);
sym.put("len", new FuncType(param, Type.INT_TYPE));
s.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));
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;
}
//Initializer for the first pass.
public DeclarationAnalyzer(Errors errors0)
{
firstPass = true;
errors = errors0;
globals = sym;
initScope(sym);
typeChecker = new TypeChecker(globals, errors);
}
//Initializer for the second pass.
public DeclarationAnalyzer(Errors errors0, TypeChecker typeChecker, SymbolTable<Type> globals)
{
firstPass = false;
@ -91,10 +84,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 +95,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 +123,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)
{
Type p = ValueType.annotationToValueType(param.type);
current_func.parameters.add((ValueType)p);
}
sym.put(node.name.name, current_func);
if(!firstPass)
if(fTy == null)
{
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 +195,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 +221,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 +274,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 +316,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 +356,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 +371,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);
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();
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 = origScope;
return null;
}
@ -162,8 +191,10 @@ public class TypeChecker extends AbstractNodeAnalyzer<Type> {
for(int i = 0; i < lArgs; ++i){
Type p = fty.parameters.get(i);
Type c = types.get(i);
if((p.isSpecialType()&&!p.equals(c)) ||
if(((p.isSpecialType()&&!p.equals(c)) ||
(!p.isSpecialType()&&!StudentAnalysis.subClassOf(p, c, currentScope)))
&&!(p.isListType()&&c.equals(Type.EMPTY_TYPE))
)
err(node,"Expected type `%s`; got type `%s` in parameter %d", p, c, i);
}
}
@ -209,7 +240,7 @@ public class TypeChecker extends AbstractNodeAnalyzer<Type> {
public Type analyze(IfExpr node) {
Type condTy = node.condition.dispatch(this);
if(!condTy.equals(Type.BOOL_TYPE)){
err(node, "If condition `%s` isn't a boolean expression.", node.condition);
err(node, "Condition expression cannot be of type `%s`", condTy.className());
}
Type ifTy = node.thenExpr.dispatch(this),
@ -223,7 +254,7 @@ public class TypeChecker extends AbstractNodeAnalyzer<Type> {
public Type analyze(IfStmt node) {
Type condTy = node.condition.dispatch(this);
if(!condTy.equals(Type.BOOL_TYPE)){
err(node, "If condition `%s` isn't a boolean expression.", node.condition);
err(node, "Condition expression cannot be of type `%s`", condTy.className());
}
boolean prevReturned = returned, thenReturned;
for(Stmt st : node.thenBody)
@ -274,7 +305,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
@ -306,7 +337,8 @@ public class TypeChecker extends AbstractNodeAnalyzer<Type> {
Type thisArgTy = thisArg.setInferredType(thisArg.dispatch(this)),
thisParamTy = funcTy.parameters.get(i + 1);
if(!thisParamTy.equals(thisArgTy) && !StudentAnalysis.subClassOf(thisParamTy, thisArgTy, currentScope))
if(!thisParamTy.equals(thisArgTy) && !StudentAnalysis.subClassOf(thisParamTy, thisArgTy, currentScope)
&&!(thisParamTy.isListType()&&thisArgTy.equals(Type.EMPTY_TYPE)))
err(node, "Expected type `%s`; got type `%s` in parameter %d",
thisParamTy, thisArgTy, i + 1);
}

@ -0,0 +1,117 @@
{
"kind" : "Program",
"location" : [ 1, 1, 13, 8 ],
"declarations" : [ {
"kind" : "VarDef",
"location" : [ 1, 1, 1, 9 ],
"var" : {
"kind" : "TypedVar",
"location" : [ 1, 1, 1, 5 ],
"identifier" : {
"kind" : "Identifier",
"location" : [ 1, 1, 1, 1 ],
"name" : "x"
},
"type" : {
"kind" : "ClassType",
"location" : [ 1, 3, 1, 5 ],
"className" : "int"
}
},
"value" : {
"kind" : "IntegerLiteral",
"location" : [ 1, 9, 1, 9 ],
"value" : 1
}
}, {
"kind" : "VarDef",
"location" : [ 2, 1, 2, 9 ],
"var" : {
"kind" : "TypedVar",
"location" : [ 2, 1, 2, 5 ],
"identifier" : {
"kind" : "Identifier",
"location" : [ 2, 1, 2, 1 ],
"name" : "x"
},
"type" : {
"kind" : "ClassType",
"location" : [ 2, 3, 2, 5 ],
"className" : "int"
}
},
"value" : {
"kind" : "IntegerLiteral",
"location" : [ 2, 9, 2, 9 ],
"value" : 2
}
}, {
"kind" : "FuncDef",
"location" : [ 6, 1, 13, 0 ],
"name" : {
"kind" : "Identifier",
"location" : [ 6, 5, 6, 9 ],
"name" : "fun_1"
},
"params" : [ ],
"returnType" : {
"kind" : "ClassType",
"location" : [ 6, 16, 6, 19 ],
"className" : "bool"
},
"declarations" : [ ],
"statements" : [ {
"kind" : "IfStmt",
"location" : [ 7, 5, 13, 0 ],
"condition" : {
"kind" : "BooleanLiteral",
"location" : [ 7, 8, 7, 11 ],
"value" : true
},
"thenBody" : [ {
"kind" : "IfStmt",
"location" : [ 8, 9, 13, 0 ],
"condition" : {
"kind" : "BooleanLiteral",
"location" : [ 8, 12, 8, 15 ],
"value" : true
},
"thenBody" : [ {
"kind" : "ReturnStmt",
"location" : [ 9, 13, 9, 23 ],
"value" : {
"kind" : "BooleanLiteral",
"location" : [ 9, 20, 9, 23 ],
"value" : true
}
} ],
"elseBody" : [ ]
} ],
"elseBody" : [ ]
} ]
} ],
"statements" : [ {
"kind" : "ExprStmt",
"location" : [ 13, 1, 13, 7 ],
"expr" : {
"kind" : "CallExpr",
"location" : [ 13, 1, 13, 7 ],
"function" : {
"kind" : "Identifier",
"location" : [ 13, 1, 13, 5 ],
"name" : "fun_1"
},
"args" : [ ]
}
} ],
"errors" : {
"errors" : [ {
"kind" : "CompilerError",
"location" : [ 6, 1, 6, 3 ],
"message" : "Parse error near token DEF: def",
"syntax" : true
} ],
"kind" : "Errors",
"location" : [ 0, 0, 0, 0 ]
}
}

@ -0,0 +1,94 @@
{
"kind" : "Program",
"location" : [ 1, 1, 5, 20 ],
"declarations" : [ {
"kind" : "VarDef",
"location" : [ 1, 1, 1, 12 ],
"var" : {
"kind" : "TypedVar",
"location" : [ 1, 1, 1, 5 ],
"identifier" : {
"kind" : "Identifier",
"location" : [ 1, 1, 1, 1 ],
"name" : "x"
},
"type" : {
"kind" : "ClassType",
"location" : [ 1, 3, 1, 5 ],
"className" : "int"
}
},
"value" : {
"kind" : "BooleanLiteral",
"location" : [ 1, 9, 1, 12 ],
"value" : true
}
} ],
"statements" : [ {
"kind" : "ExprStmt",
"location" : [ 2, 1, 2, 7 ],
"expr" : {
"kind" : "BinaryExpr",
"location" : [ 2, 1, 2, 7 ],
"left" : {
"kind" : "Identifier",
"location" : [ 2, 1, 2, 1 ],
"name" : "x"
},
"operator" : "+",
"right" : {
"kind" : "ListExpr",
"location" : [ 2, 5, 2, 7 ],
"elements" : [ {
"kind" : "IntegerLiteral",
"location" : [ 2, 6, 2, 6 ],
"value" : 1
} ]
}
}
}, {
"kind" : "AssignStmt",
"location" : [ 4, 3, 4, 14 ],
"targets" : [ {
"kind" : "Identifier",
"location" : [ 4, 3, 4, 6 ],
"name" : "bool"
} ],
"value" : {
"kind" : "BooleanLiteral",
"location" : [ 4, 10, 4, 14 ],
"value" : false
}
}, {
"kind" : "AssignStmt",
"location" : [ 5, 1, 5, 19 ],
"targets" : [ {
"kind" : "Identifier",
"location" : [ 5, 1, 5, 1 ],
"name" : "x"
}, {
"kind" : "Identifier",
"location" : [ 5, 5, 5, 5 ],
"name" : "y"
}, {
"kind" : "Identifier",
"location" : [ 5, 9, 5, 9 ],
"name" : "z"
} ],
"value" : {
"kind" : "StringLiteral",
"location" : [ 5, 13, 5, 19 ],
"value" : "Error"
}
} ],
"errors" : {
"errors" : [ {
"kind" : "CompilerError",
"location" : [ 4, 2, 4, 2 ],
"message" : "Parse error near token COLON: :",
"syntax" : true
} ],
"kind" : "Errors",
"location" : [ 0, 0, 0, 0 ]
}
}

@ -0,0 +1,17 @@
x : int = 0
y : int = 5
z : Foo = None
class Foo(object):
x : int = 3
def foo(x : int) -> int:
y : str = "test"
def foo2() -> str:
nonlocal y
return y
nonlocal x # The reference compiler prioritized for duplicate declaration
# error and stopped compilation.
# But our implementation prioritized this for an invalid nonlocal
# and recovered the complation process.
return x
x = 1 and 2
foo(y, 5)

@ -0,0 +1,260 @@
{
"kind" : "Program",
"location" : [ 1, 1, 17, 10 ],
"declarations" : [ {
"kind" : "VarDef",
"location" : [ 1, 1, 1, 11 ],
"var" : {
"kind" : "TypedVar",
"location" : [ 1, 1, 1, 7 ],
"identifier" : {
"kind" : "Identifier",
"location" : [ 1, 1, 1, 1 ],
"name" : "x"
},
"type" : {
"kind" : "ClassType",
"location" : [ 1, 5, 1, 7 ],
"className" : "int"
}
},
"value" : {
"kind" : "IntegerLiteral",
"location" : [ 1, 11, 1, 11 ],
"value" : 0
}
}, {
"kind" : "VarDef",
"location" : [ 2, 1, 2, 11 ],
"var" : {
"kind" : "TypedVar",
"location" : [ 2, 1, 2, 7 ],
"identifier" : {
"kind" : "Identifier",
"location" : [ 2, 1, 2, 1 ],
"name" : "y"
},
"type" : {
"kind" : "ClassType",
"location" : [ 2, 5, 2, 7 ],
"className" : "int"
}
},
"value" : {
"kind" : "IntegerLiteral",
"location" : [ 2, 11, 2, 11 ],
"value" : 5
}
}, {
"kind" : "VarDef",
"location" : [ 3, 1, 3, 14 ],
"var" : {
"kind" : "TypedVar",
"location" : [ 3, 1, 3, 7 ],
"identifier" : {
"kind" : "Identifier",
"location" : [ 3, 1, 3, 1 ],
"name" : "z"
},
"type" : {
"kind" : "ClassType",
"location" : [ 3, 5, 3, 7 ],
"className" : "Foo"
}
},
"value" : {
"kind" : "NoneLiteral",
"location" : [ 3, 11, 3, 14 ]
}
}, {
"kind" : "ClassDef",
"location" : [ 4, 1, 5, 16 ],
"name" : {
"kind" : "Identifier",
"location" : [ 4, 7, 4, 9 ],
"name" : "Foo"
},
"superClass" : {
"kind" : "Identifier",
"location" : [ 4, 11, 4, 16 ],
"name" : "object"
},
"declarations" : [ {
"kind" : "VarDef",
"location" : [ 5, 5, 5, 15 ],
"var" : {
"kind" : "TypedVar",
"location" : [ 5, 5, 5, 11 ],
"identifier" : {
"kind" : "Identifier",
"location" : [ 5, 5, 5, 5 ],
"name" : "x"
},
"type" : {
"kind" : "ClassType",
"location" : [ 5, 9, 5, 11 ],
"className" : "int"
}
},
"value" : {
"kind" : "IntegerLiteral",
"location" : [ 5, 15, 5, 15 ],
"value" : 3
}
} ]
}, {
"kind" : "FuncDef",
"location" : [ 6, 1, 15, 13 ],
"name" : {
"kind" : "Identifier",
"location" : [ 6, 5, 6, 7 ],
"name" : "foo"
},
"params" : [ {
"kind" : "TypedVar",
"location" : [ 6, 9, 6, 15 ],
"identifier" : {
"kind" : "Identifier",
"location" : [ 6, 9, 6, 9 ],
"name" : "x"
},
"type" : {
"kind" : "ClassType",
"location" : [ 6, 13, 6, 15 ],
"className" : "int"
}
} ],
"returnType" : {
"kind" : "ClassType",
"location" : [ 6, 21, 6, 23 ],
"className" : "int"
},
"declarations" : [ {
"kind" : "VarDef",
"location" : [ 7, 5, 7, 20 ],
"var" : {
"kind" : "TypedVar",
"location" : [ 7, 5, 7, 11 ],
"identifier" : {
"kind" : "Identifier",
"location" : [ 7, 5, 7, 5 ],
"name" : "y"
},
"type" : {
"kind" : "ClassType",
"location" : [ 7, 9, 7, 11 ],
"className" : "str"
}
},
"value" : {
"kind" : "StringLiteral",
"location" : [ 7, 15, 7, 20 ],
"value" : "test"
}
}, {
"kind" : "FuncDef",
"location" : [ 8, 5, 10, 17 ],
"name" : {
"kind" : "Identifier",
"location" : [ 8, 9, 8, 12 ],
"name" : "foo2"
},
"params" : [ ],
"returnType" : {
"kind" : "ClassType",
"location" : [ 8, 19, 8, 21 ],
"className" : "str"
},
"declarations" : [ {
"kind" : "NonLocalDecl",
"location" : [ 9, 9, 9, 18 ],
"variable" : {
"kind" : "Identifier",
"location" : [ 9, 18, 9, 18 ],
"name" : "y"
}
} ],
"statements" : [ {
"kind" : "ReturnStmt",
"location" : [ 10, 9, 10, 16 ],
"value" : {
"kind" : "Identifier",
"location" : [ 10, 16, 10, 16 ],
"name" : "y"
}
} ]
}, {
"kind" : "NonLocalDecl",
"location" : [ 11, 5, 11, 14 ],
"variable" : {
"kind" : "Identifier",
"location" : [ 11, 14, 11, 14 ],
"errorMsg" : "Duplicate declaration of identifier in same scope: x",
"name" : "x"
}
} ],
"statements" : [ {
"kind" : "ReturnStmt",
"location" : [ 15, 5, 15, 12 ],
"value" : {
"kind" : "Identifier",
"location" : [ 15, 12, 15, 12 ],
"name" : "x"
}
} ]
} ],
"statements" : [ {
"kind" : "AssignStmt",
"location" : [ 16, 1, 16, 11 ],
"targets" : [ {
"kind" : "Identifier",
"location" : [ 16, 1, 16, 1 ],
"name" : "x"
} ],
"value" : {
"kind" : "BinaryExpr",
"location" : [ 16, 5, 16, 11 ],
"left" : {
"kind" : "IntegerLiteral",
"location" : [ 16, 5, 16, 5 ],
"value" : 1
},
"operator" : "and",
"right" : {
"kind" : "IntegerLiteral",
"location" : [ 16, 11, 16, 11 ],
"value" : 2
}
}
}, {
"kind" : "ExprStmt",
"location" : [ 17, 1, 17, 9 ],
"expr" : {
"kind" : "CallExpr",
"location" : [ 17, 1, 17, 9 ],
"function" : {
"kind" : "Identifier",
"location" : [ 17, 1, 17, 3 ],
"name" : "foo"
},
"args" : [ {
"kind" : "Identifier",
"location" : [ 17, 5, 17, 5 ],
"name" : "y"
}, {
"kind" : "IntegerLiteral",
"location" : [ 17, 8, 17, 8 ],
"value" : 5
} ]
}
} ],
"errors" : {
"errors" : [ {
"kind" : "CompilerError",
"location" : [ 11, 14, 11, 14 ],
"message" : "Duplicate declaration of identifier in same scope: x"
} ],
"kind" : "Errors",
"location" : [ 0, 0, 0, 0 ]
}
}

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

@ -0,0 +1,8 @@
#!/bin/bash
echo "Running all test cases inside src/test/data/pa2/sample/ folder)"
java -cp "chocopy-ref.jar:target/assignment.jar" chocopy.ChocoPy --pass=rr --reset --dir src/test/data/pa2/
# good.py can't be parsed by reference compiler, because it contains our improvements in PA1. Therefore, we generate the ast with our parser and then use the reference semantic checker to generate the reference type information for testing.
java -cp "chocopy-ref.jar:target/assignment.jar" chocopy.ChocoPy --pass=s --reset --dir src/test/data/pa2/student_contributed/good.py
java -cp "chocopy-ref.jar:target/assignment.jar" chocopy.ChocoPy --pass=.r --reset --dir src/test/data/pa2/student_contributed/good.py.ast
Loading…
Cancel
Save