Skip to content

Commit

Permalink
Added Type Builder and Type Collector
Browse files Browse the repository at this point in the history
  • Loading branch information
rodrigo-pino committed Feb 26, 2021
1 parent 5a9ac89 commit d516ef8
Show file tree
Hide file tree
Showing 6 changed files with 558 additions and 0 deletions.
48 changes: 48 additions & 0 deletions src/semantics/autotype_collector.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
import semantics.visitor as visitor
from semantics.tools import Context, Scope
from parsing.ast import AttrDeclarationNode, ClassDeclarationNode, ProgramNode

class AutotypeCollector:
def __init__(self, context:Context):
self.context = context
self.current_type = None
self.current_method = None
self.current_attrb = None
self.inference_graph = dict()
self.errors = []

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

@visitor.when(ProgramNode)
def visit(self, node:ProgramNode) -> Scope:
scope = Scope()
for declaration in node.declarations:
self.visit(declaration, scope.create_child())

return scope

@visitor.when(ClassDeclarationNode)
def visit(self, node, scope):
self.current_type = self.context.get_type(node.id)
scope.define_variable("self", self.current_type)
for attr in self.current_type.attributes:
scope.define_variable(attr.name, attr.type)

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

@visitor.when(AttrDeclarationNode)
def visit(self, node, scope):
self.current_attrb = self.current_type.get_attribute(node.id)
node_type = self.update_type(self.current_attrb.type)

if not node.expr:
self.current_attrb = None
node.inferenced_type = node_type
return


# todo: Revisar los auto types que me hace falta y que no
# todo: completar de manera acorde el autotype collector
177 changes: 177 additions & 0 deletions src/semantics/tools.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,177 @@
import itertools as itt
from collections import OrderedDict

class InternalError(Exception):
@property
def text(self):
return "Internal Error: " + self.args[0]

class SemanticError(Exception):
@property
def text(self):
return "Semantic Error: " + self.args[0]

class TypeError(SemanticError):
@property
def text(self):
return "Type Error: " + self.args[0]

class AttributeError(SemanticError):
@property
def text(self):
return "Attribute Error: " + self.args[0]

class Attribute:
def __init__(self, name, typex):
self.name = name
self.type = typex

def __str__(self):
return f'[attrib] {self.name} : {self.type.name};'

def __repr__(self):
return str(self)

class Method:
def __init__(self, name, param_names, params_types, return_type):
self.name = name
self.param_names = param_names
self.param_types = params_types
self.return_type = return_type

def __str__(self):
params = ', '.join(f'{n}:{t.name}' for n,t in zip(self.param_names, self.param_types))
return f'[method] {self.name}({params}): {self.return_type.name};'

def __eq__(self, other):
return other.name == self.name and \
other.return_type == self.return_type and \
other.param_types == self.param_types

class Type:
def __init__(self, name:str):
self.name = name
self.attributes = []
self.methods = []
self.parent = None
self.index = -1

def set_parent(self, parent):
if self.parent is not None:
raise SemanticError(f'Type \'{self.name}\' already has parent type \'{self.parent.name}\'. Type \'{parent.name}\' cannot be set as parent.')
if parent.name in {"String", "Int", "Bool"}:
raise SemanticError(f'Cannot set \'{self.name}\' parent, \'{parent.name}\' type cannot be inherited.')
self.parent = parent

def get_attribute(self, name:str):
try:
return next(attr for attr in self.attributes if attr.name == name)
except StopIteration:
if self.parent is None:
raise AttributeError(f'Attribute "{name}" is not defined in {self.name}.')
try:
return self.parent.get_attribute(name)
except SemanticError:
raise AttributeError(f'Attribute "{name}" is not defined in {self.name}.')

class SelfType(Type):
def __init__(self):
self.name = "SELF_TYPE"
def conforms_to(self, other):
#if isinstance(other, SelfType):
# return True
raise InternalError("SELF_TYPE yet to be assigned, cannot conform.")
def bypass(self):
raise InternalError("SELF_TYPE yet to be assigned, cannot bypass.")

class AutoType(Type):
pass

class ErrorType(Type):
pass

class Context:
def __init__(self) -> None:
self.types = {}
self.num_autotypes = 0
self.type_graph = 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:
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:
if selftype and name == "SELF_TYPE":
return SelfType()
if autotype and name == "AUTO_TYPE":
self.num_autotypes += 1
return AutoType(f"T{self.num_autotypes}", [self.types["Object"]], self.types)
try:
return self.types[name]
except KeyError:
raise TypeError(f'Type "{name}" is not defined.')

def __str__(self):
return '{\n\t' + '\n\t'.join(y for x in self.types.values() for y in str(x).split('\n')) + '\n}'

def __repr__(self):
return str(self)

class VariableInfo:
def __init__(self, name, vtype):
self.name = name
self.type = vtype

def __str__(self):
return self.name + ":" + self.type


class Scope:
def __init__(self, parent=None):
self.locals = []
self.parent = parent
self.children = []
self.index = 0 if parent is None else len(parent)
self.current_child = -1

def __len__(self):
return len(self.locals)

def create_child(self):
child = Scope(self)
self.children.append(child)
return child

def define_variable(self, vname, vtype):
info = VariableInfo(vname, vtype)
self.locals.append(info)
return info

def find_variable(self, vname, index=None):
locals = self.locals if index is None else itt.islice(self.locals, index)
try:
return next(x for x in locals if x.name == vname)
except StopIteration:
try:
return self.parent.find_variable(vname, self.index)# if self.parent else None
except AttributeError:
return None

def is_defined(self, vname):
return self.find_variable(vname) is not None

def is_local(self, vname):
return any(True for x in self.locals if x.name == vname)

def next_child(self):
self.current_child += 1
return self.children[self.current_child]

def reset(self):
self.current_child = -1
for child in self.children:
child.reset()
98 changes: 98 additions & 0 deletions src/semantics/type_builder.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,98 @@
import semantics.visitor as visitor
from parsing.ast import Node, ProgramNode, ClassDeclarationNode, MethodDeclarationNode, AttrDeclarationNode
from semantics.utils import SemanticError
from semantics.utils import Context

class TypeCollector(object):
def __init__(self) -> None:
self.context = Context()
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()

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

@visitor.when(ClassDeclarationNode)
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)

def get_type_hierarchy(self):
visited = set(["Object"])
new_order = []
self.get_type_hierarchy("Object", self.type_graph, visited, new_order, 1)

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)

return new_order

def get_type_hierarchy(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.get_type_hierarchy(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]])

visited.add(node)
path.append(node)
return self.check_circular_heritage(node, graph, path, visited)

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')

def add_error(self, node, text:str):
line, col = node.get_position() if node else 0, 0
self.errors.append(f"Line: {line} Col: {col} " + text)
Loading

0 comments on commit d516ef8

Please sign in to comment.