Skip to content

Commit

Permalink
Fixed Bugs.
Browse files Browse the repository at this point in the history
  • Loading branch information
rodrigo-pino committed Feb 26, 2021
1 parent 114afa4 commit 669cab4
Show file tree
Hide file tree
Showing 17 changed files with 1,388 additions and 168 deletions.
53 changes: 51 additions & 2 deletions src/devdeb.py
Original file line number Diff line number Diff line change
@@ -1,2 +1,51 @@
def run_pipeline():
pass
import os
from parsing import parser
from semantics import type_collector, type_builder, autotype_collector

def format_errors(errors, s = ""):
count = 1
for error in errors:
s += str(count) + ". " + error + "\n"
count += 1
return s

def run_pipeline(program):
ast = parser.parse(program)

collector = type_collector.TypeCollector()
collector.visit(ast)
context = collector.context
print('Context\n', context)

builder = type_builder.TypeBuilder(context)
builder.visit(ast)
print('Context\n', context)

auto_collector = autotype_collector.AutotypeCollector(context)
auto_collector.visit(ast)

s = "Type Collector Errors:\n"
s = format_errors(collector.errors, s)
s += "Type Builder Errors:\n"
s = format_errors(builder.errors, s)
s += "Inference Gatherer Errors:\n"
s = format_errors(auto_collector.errors, s)

print(s)

folder_path = r'./zTests/Misc'
filenames = os.listdir(folder_path)
filenames.sort()

for filename in filenames:
path = os.path.join(folder_path, filename)
file = open(path, "r")
program = file.read()
file.close()

print(f"Running {filename}")
run_pipeline(program)
input()

print("EndOfFiles")

36 changes: 29 additions & 7 deletions src/semantics/tools.py
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import itertools as itt
from collections import OrderedDict
from typing import FrozenSet
from semantics.utils import from_dict_to_set
#from semantics.utils import from_dict_to_set

class InternalError(Exception):
@property
Expand Down Expand Up @@ -155,6 +155,22 @@ def least_common_ancestor(self, other):
if this == None:
return None
return this

def __str__(self):
output = f'type {self.name}'
parent = '' if self.parent is None else f' : {self.parent.name}'
output += parent
output += ' {'
output += '\n\t' if self.attributes or self.methods else ''
output += '\n\t'.join(str(x) for x in self.attributes)
output += '\n\t' if self.attributes else ''
output += '\n\t'.join(str(x) for x in self.methods)
output += '\n' if self.methods else ''
output += '}\n'
return output

def __repr__(self):
return str(self)

class TypeBag:
def __init__(self, type_set, heads = []) -> None:
Expand Down Expand Up @@ -215,6 +231,7 @@ def swap_types(self, update_type, remove_type):
pass
return self


def clone(self):
clone = TypeBag(self.type_set, self.heads)
clone.condition_list = self.condition_list
Expand All @@ -231,9 +248,6 @@ def conforms_to(self, other):
def bypass(self):
raise InternalError("SELF_TYPE is yet to be assigned, cannot bypass.")

class AutoType(Type):
pass

class ErrorType(Type):
def __init__(self):
self.name = "<error>"
Expand All @@ -248,18 +262,20 @@ def __init__(self) -> None:
def create_type(self, name:str) -> Type:
if name in self.types:
raise SemanticError(f'Type with the same name ({name}) already exists.')
if name[0] != name[0].upper:
if name[0] != name[0].upper():
raise SemanticError(f'Type name ({name}) must start with upper case')
typex = self.types[name] = Type(name)
return typex

def get_type(self, name:str, selftype=True, autotype=True) -> Type:
def get_type(self, name:str, selftype=True, autotype=True, unpacked=False) -> Type:
if selftype and name == "SELF_TYPE":
return TypeBag({SelfType()}) #SelfType()
if autotype and name == "AUTO_TYPE":
self.num_autotypes += 1
return TypeBag(self.types, [self.types['Object']]) #AutoType(f"T{self.num_autotypes}", [self.types["Object"]], self.types)
try:
if unpacked:
return self.types[name]
return TypeBag({self.types[name]})
except KeyError:
raise TypeError(f'Type "{name}" is not defined.')
Expand Down Expand Up @@ -339,4 +355,10 @@ def next_child(self):
def reset(self):
self.current_child = -1
for child in self.children:
child.reset()
child.reset()

def from_dict_to_set(types:dict):
type_set = set()
for typex in types:
type_set.add(types[typex])
return type_set
152 changes: 81 additions & 71 deletions src/semantics/type_builder.py
Original file line number Diff line number Diff line change
@@ -1,97 +1,107 @@
import semantics.visitor as visitor
from parsing.ast import Node, ProgramNode, ClassDeclarationNode, MethodDeclarationNode, AttrDeclarationNode
from semantics.utils import SemanticError
from semantics.utils import Context
from parsing.ast import ProgramNode, ClassDeclarationNode, MethodDeclarationNode, AttrDeclarationNode
from semantics.tools import SemanticError
from semantics.tools import ErrorType, SelfType
from semantics.tools import Context

class TypeCollector(object):
def __init__(self) -> None:
self.context = Context()
class TypeBuilder:
def __init__(self, context: Context):
self.context = context
self.current_type = None
self.errors = []

self.type_graph = {"Object":["IO", "String", "Int", "Bool"], "IO":[], "String":[], "Int":[], "Bool":[]}

@visitor.on('node')
def visit(self, node):
pass

@visitor.when(ProgramNode)
def visit(self, node):
self.context = Context()
self.init_default_classes()
self.build_default_classes()

for class_def in node.declarations:
self.visit(class_def)

new_declarations = self.get_type_hierarchy()
node.declarations = new_declarations
self.context.type_graph = self.type_graph

try:
self.context.get_type('Main', unpacked=True).get_method('main', local=True)
except SemanticError as err:
self.add_error(node, err.text)

@visitor.when(ClassDeclarationNode)
def visit(self, node):
self.current_type = self.context.get_type(node.id, unpacked=True)

if node.parent:
try:
parent_type = self.context.get_type(node.parent, unpacked=True)
self.current_type.set_parent(parent_type)
for idx, _ in list(parent_type.all_attributes(True)):
self.current_type.attributes.append(idx)
except SemanticError as err:
self.add_error(node, err.text)

for feature in node.features:
self.visit(feature)

@visitor.when(AttrDeclarationNode)
def visit(self, node):
try:
self.context.create_type(node.id)
self.type_graph[node.id] = []
if node.parent:
if node.parent in {'String', 'Int, Bool'}:
raise SemanticError(f"Type \'{node.id}\' cannot inherit from \'{node.parent}\' beacuse is forbidden.")
try:
self.type_graph[node.parent].append(node.id)
except KeyError:
self.type_graph[node.parent] = [node.id]
else:
node.parent = "Object"
self.type_graph["Object"] = [node.id]
except SemanticError as error:
self.add_error(node, error.text)
attr_type = self.context.get_type(node.type)
except SemanticError as err:
self.add_error(node, err.text)
attr_type = ErrorType()

try:
self.current_type.define_attribute(node.id, attr_type)
except SemanticError as err:
self.add_error(err.text)

def get_type_hierarchy(self):
visited = set(["Object"])
new_order = []
self.dfs_type_graph("Object", self.type_graph, visited, new_order, 1)
@visitor.when(MethodDeclarationNode)
def visit(self, node):
try:
ret_type = self.context.get_type(node.type)
except SemanticError as err:
self.add_error(err.text)
ret_type = ErrorType()

params_type = []
params_name = []
for p_name, p_type in node.params:
try:
params_type.append(self.context.get_type(p_type))
except SemanticError as err:
params_type.append(ErrorType())
self.add_error(node, err.text)
params_name.append(p_name)

try:
self.current_type.define_method(node.id, params_name, params_type, ret_type)
except SemanticError as err:
self.add_error(node, err.text)

circular_heritage_errors = []
for node in self.type_graph:
if not node in visited:
visited.add(node)
path = [node]
circular_heritage_errors.append(self.check_circular_heritage(node, self.type_graph, path, visited))
new_order = new_order + [self.context.get_type(node) for node in path]

if circular_heritage_errors:
error = "Semantic Error: Circular Heritage:\n"
error += "\n".join(err for err in circular_heritage_errors)
self.add_error(None, error)
def build_default_classes(self):
Object = self.context.get_type("Object", unpacked=True)
String = self.context.get_type("String", unpacked=True)
Int = self.context.get_type("Int", unpacked=True)
Io = self.context.get_type("IO", unpacked=True)
Bool = self.context.get_type("Bool", unpacked=True)

return new_order
String.set_parent(Object)
Int.set_parent(Object)
Io.set_parent(Object)
Bool.set_parent(Object)

def dfs_type_graph(self, root, graph, visited:set, new_order, index):
if not root in graph:
return

for node in graph[root]:
if node in visited:
continue
visited.add(node)
if node not in {"Int", "String", "IO", "Bool", "Object"}:
new_order.append(self.context.get_type(node))
self.context.get_type(node).index = index
self.dfs_type_graph(node, graph, visited, new_order, index + 1)

def check_circular_heritage(self, root, graph, path, visited):
for node in graph[root]:
if node in path:
return ' -> '.join(child for child in visited + [visited[0]])
Object.define_method("abort", [], [], Object)
Object.define_method("type_name", [], [], String)
Object.define_method("copy", [], [], SelfType())

visited.add(node)
path.append(node)
return self.check_circular_heritage(node, graph, path, visited)
String.define_method("length", [], [], Int)
String.define_method("concat", ["s"], [String], String)
String.define_method("substr", ["i", "l"], [Int, Int], String)

def init_default_classes(self):
self.context.create_type('Object').index = 0
self.context.create_type('String')
self.context.create_type('Int')
self.context.create_type('IO')
self.context.create_type('Bool')
Io.define_method("out_string", ["x"],[String], SelfType())
Io.define_method("out_int", ["x"],[Int], SelfType())
Io.define_method("in_string", [],[], String)
Io.define_method("in_int", [], [], Int)

def add_error(self, node, text:str):
line, col = node.get_position() if node else 0, 0
Expand Down
Loading

0 comments on commit 669cab4

Please sign in to comment.