forked from CoolCows/cool-compiler-2021
-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Added Type Builder and Type Collector
- Loading branch information
1 parent
e3d473e
commit be90765
Showing
6 changed files
with
558 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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 |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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() |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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) |
Oops, something went wrong.