diff --git a/WORKLOG.md b/WORKLOG.md index 93043f3..f4b8582 100644 --- a/WORKLOG.md +++ b/WORKLOG.md @@ -5,12 +5,14 @@
## 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. + - 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. ## Recovery: - - + - ## 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. + - 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. ## Improvements: - - Added more tests to rigorously check program flow and indentation. + - Added more tests to rigorously check program flow and indentation. diff --git a/src/test/data/pa2/student_contributed/bad_semantic.py b/src/test/data/pa2/student_contributed/bad_semantic.py index 490a475..c18142b 100644 --- a/src/test/data/pa2/student_contributed/bad_semantic.py +++ b/src/test/data/pa2/student_contributed/bad_semantic.py @@ -1,14 +1,93 @@ -x:int = 1 -x:int = 2 +# class defs +class A_CLASS(object): + a_class_i:int = 0 + def __init__(self:"A_CLASS", x:int): + self.x = x -x + # Bad, self param is missing + def add(y:int) -> int: + y = y+self.x + return y -def fun_1() -> bool: - if True: - if True: - return True - # All path should return, not just one +class B_CLASS(object): + b_class_i:int = 0 +class C_CLASS(B_CLASS): + pass -fun_1() +# Bad, duplicate class def +class A_CLASS(object): + pass + +# Bad, E_CLASS is not declared +class D_CLASS(E_CLASS): + pass + + +# var defs +a_s:str = "a_s" +b_s:str = "b_s" +c_s:str = "c_s" + +a_i:int = 0 +b_i:int = 0 +c_i:int = 0 + +a_b:bool = False +b_b:bool = False +c_b:bool = False + +a_list:[int] = None +b_list:[int] = None +c_list:[int] = None + +a_class:A_CLASS = None +b_class:B_CLASS = None +c_class:C_CLASS = None + + +# fun defs +def f_1() -> object: + def f_f_1() -> object: + # a_s:int = 0 Fails if we uncomment this line + global a_s # Bad, duplicate declarion + print(a_s) + pass + pass + +def f_2() -> object: + f_a_s:str = "s" + def f_f_2() -> object: + nonlocal f_a_s + print(f_a_s) + pass + pass + +def f_3(x:int) -> bool: + f_b_s:int = 3 + if (x + f_b_s > 3): + return True + elif (x + f_b_s == 3): + print("Equal") # Bad, this path should return + return False + +def f_4() -> object: + f_a_i:int = 2 + a_i = f_a_i + 1 # Bad, cant assign to a_i without declaring it as global or nonlocal + return f_a_i + +# NEGATIVE TEST CASES - SEMANTIC +# Bad, f_2 cannot be redefined in the same scope +def f_2() -> object: + pass + +# Bad, print cannot be redefined in the same scope +def print(val:object) -> object: + pass + +# Bad, a_i cannot be redefined in the same scope +a_i:int = 2 + +# Bad return +return a_i diff --git a/src/test/data/pa2/student_contributed/bad_types.py b/src/test/data/pa2/student_contributed/bad_types.py index 91ba352..262c0ea 100644 --- a/src/test/data/pa2/student_contributed/bad_types.py +++ b/src/test/data/pa2/student_contributed/bad_types.py @@ -1,5 +1,111 @@ -x:int = True -x + [1] +# class defs +class A_CLASS(object): + a_class_i:int = 0 + def __init__(self:"A_CLASS", x:int): + self.x = x + + def add(self:"A_CLASS", y:int) -> int: + y = y+self.x + return y + +class B_CLASS(object): + b_class_i:int = 0 + +class C_CLASS(B_CLASS): + pass + +# var defs +a_s:str = "a_s" +b_s:str = "b_s" +c_s:str = "c_s" + +a_i:int = 0 +b_i:int = 0 +c_i:int = 0 + +a_b:bool = False +b_b:bool = False +c_b:bool = False + +a_list:[int] = None +b_list:[int] = None +c_list:[int] = None + +a_class:A_CLASS = None +b_class:B_CLASS = None +c_class:C_CLASS = None + + +# fun defs +def f_1() -> object: + def f_f_1() -> object: + global a_s # Fails if we change it to z, which doesnt exist in global scope + pass + pass + +def f_2() -> object: + f_a_s:str = "s" + def f_f_2() -> object: + nonlocal f_a_s # Fails if we change this to a_s which is in global scope but not in upper scope + pass + pass + +def f_3(x:int) -> str: + f_b_s:int = 3 + return x*f_b_s + + +# Declarations +a_list = [1, 2, 3] +b_list = [0, 0, 0] +c_list = [-1, -2, -3] + +a_class = A_CLASS(5) +b_class = B_CLASS() +c_class = C_CLASS() + + +# NEGATIVE TEST CASES - TYPES +c_i = True + +c_i + [1] # Bad, addint list to an int + +a_i = a_b = z = "Error" # Bad, z is not defined and a_b is boolean + +a_s = a_s + 1 # Bad, adding integer to a string + +c_s = a_s[a_s] # Bad, indexing with a string variable + +b_class.b_class_i = 2 # Bad, object attribute is not assignable + +f_1 = 5 # Bad, function is not assignable + +f_2 = f_1 # Bad, function is not storable + +a_i = b_class.b_class_i = z = 5 # Bad, b_class.b_class_i is not assignable and z is not declared + +x_i = "ss" # Bad assignment + +a_s = a_i + b_i # Bad, assigning integer to a variable with string type + +a_s = a_i == b_i and True # Bad, assigning boolean to a variable with string type + +a_list = a_list + a_s # Bad, adding string and list + +a_s = a_list[a_s] # Bad, indexing with a string variable and assigning int to str + +a_list[1] = "a" # Bad, assigning str to int + +a_i = f_3(3) + 5 # Bad, f_3 has string return type but it actually returns an int + +f_1() + +f_2() + +a_i = a_class.add(a_s) # Bad, passing string where method expects int + + + + + -y:bool = False -x = y = z = "Error" diff --git a/src/test/data/pa2/student_contributed/good.py b/src/test/data/pa2/student_contributed/good.py index c16c124..825c9fb 100644 --- a/src/test/data/pa2/student_contributed/good.py +++ b/src/test/data/pa2/student_contributed/good.py @@ -1,83 +1,109 @@ -# Below this point we have all the same test cases from PA1 for validation purposes. -class Foo(object): - x:int = 0 - - def __init__(self:"Foo", x:int): +# class defs +class A_CLASS(object): + a_class_i:int = 0 + def __init__(self:"A_CLASS", x:int): self.x = x - def bar(y:int): - print("Hello World!",self.x+y) - y = 10 - -def get_stones(name:str)->str: - def map_name(nm:str)->str: - return stones[color.index(nm)] - color=["Red","Blue"] - stones=["Mind","Soul"] - return map_name(name) - -def funa(): - def funb(): - print("Hello") - funb() - -def fund(): - def fune(): - print("Hello") - c = 4 + 5 - -def funf(): - def fung(): - print("Hello") - c = 6 - c = 4 + 5 - - -if True: - if True: - if True: - print("Hello") -print("World") - -if True: - if True: - if True: - print("Hello") - print("World") - -if True: - if True: - if True: - print("Hello") - print("World") - -if True: - if True: - if True: - print("Hello") - else: - print("World") - -if True: - if True: - if True: - print("Hello") -else: - print("World") - - - -f = Foo(1) -print(f.x) -f.bar(4) - -a=[[[1],[2]],[[3],[4]]] -print(a[0][0][1]*a[1][1][0]) - -multiline_string="Hi World,\ -Here I am" - -expr_precedence = -a + b * (c + d) - -stone="Blue" -print(get_stones(stone)) + def add(self:"A_CLASS", y:int) -> int: + y = y+self.x + return y + +class B_CLASS(object): + b_class_i:int = 0 + +class C_CLASS(B_CLASS): + pass + +# var defs +a_s:str = "a_s" +b_s:str = "b_s" +c_s:str = "c_s" + +a_i:int = 0 +b_i:int = 0 +c_i:int = 0 + +a_b:bool = False +b_b:bool = False +c_b:bool = False + +a_list:[int] = None +b_list:[int] = None +c_list:[int] = None + +a_class:A_CLASS = None +b_class:B_CLASS = None +c_class:C_CLASS = None + + +# fun defs +def f_1() -> object: + def f_f_1() -> object: + global a_s + print(a_s) + pass + pass + +def f_2() -> object: + f_a_s:str = "s" + def f_f_2() -> object: + nonlocal f_a_s + print(f_a_s) + pass + pass + +def f_3(x:int) -> int: + f_b_s:int = 3 + return x*f_b_s + +# Declarations +a_list = [1, 2, 3] +b_list = [0, 0, 0] +c_list = [-1, -2, -3] + +a_class = A_CLASS(5) +b_class = B_CLASS() +c_class = C_CLASS() + + +# POSITIVE TEST CASES + +#------------------- +# String operations +# String addition and assignment operations +a_s = a_s + b_s +print(a_s) + +# Assigning to a string with string indexing operation +c_s = a_s[0] +print(c_s) + + +# -------------------- +# Boolean operations +a_b = a_i == b_i and not b_b +print(a_b) + + +# -------------------- +# List operations +a_list = a_list + b_list +c_i = a_list[0] +a_list[1] = 2 + + +# -------------------- +# function operations +a_i = f_3(3) + 5 +f_1() +f_2() + + +# -------------------- +# class operations +a_i = a_class.add(2) +print(a_i) + + +a_i = a_class.add(c_class.b_class_i) +print(a_i) diff --git a/test_py_file.sh b/test_py_file.sh index 4e47b37..8334e48 100755 --- a/test_py_file.sh +++ b/test_py_file.sh @@ -9,8 +9,10 @@ fi echo "Testing file ${FILENAME}" +echo "Generating .ast.typed file using student parser and reference analyzer" java -cp "chocopy-ref.jar:target/assignment.jar" chocopy.ChocoPy --pass=sr \ ${FILENAME} --out=${FILENAME}.ast.typed +echo "Comparing the pervious output with student parser and student analyzer" java -cp "chocopy-ref.jar:target/assignment.jar" chocopy.ChocoPy \ --pass=ss --test ${FILENAME}