diff --git a/doc/Readme.md b/doc/Readme.md index 402477c8..d524b667 100644 --- a/doc/Readme.md +++ b/doc/Readme.md @@ -4,9 +4,8 @@ **Nombre** | **Grupo** | **Github** --|--|-- -Nombre1 Apellido1 Apellido2 | C4xx | [@github_user](https://github.com/) -Nombre2 Apellido1 Apellido2 | C4xx | [@github_user](https://github.com/) -Nombre3 Apellido1 Apellido2 | C4xx | [@github_user](https://github.com/) +Juan David Menendez del Cueto | C412 | [@juandamdc](https://github.com/juandamdc) +Karl Lewis Sosa Justiz | C412 | [@BlackBeard98](https://github.com/BlackBeard98) ## Readme diff --git a/doc/Reporte.pdf b/doc/Reporte.pdf new file mode 100755 index 00000000..850f12e6 Binary files /dev/null and b/doc/Reporte.pdf differ diff --git a/requirements.txt b/requirements.txt index 9eb0cad1..5a914fd8 100644 --- a/requirements.txt +++ b/requirements.txt @@ -1,2 +1,3 @@ pytest pytest-ordering +ply \ No newline at end of file diff --git a/src/AstNodes.py b/src/AstNodes.py new file mode 100644 index 00000000..2d84d1d0 --- /dev/null +++ b/src/AstNodes.py @@ -0,0 +1,169 @@ +# AST Classes +class Node: + pass + +class ProgramNode(Node): + def __init__(self, declarations): + self.declarations = declarations + self.line = declarations[0].line + self.column = declarations[0].column + +class DeclarationNode(Node): + pass + +class ClassDeclarationNode(DeclarationNode): + def __init__(self, idx, features, parent=None): + self.id = idx + self.parent = parent + self.features = features + self.line = 0 + self.column = 0 + +class AttrDeclarationNode(DeclarationNode): + def __init__(self, idx, typex, expression=None): + self.id = idx + self.type = typex + self.expression = expression + self.line = 0 + self.column = 0 + +class FuncDeclarationNode(DeclarationNode): + def __init__(self, idx, params, return_type, body): + self.id = idx + self.params = params + self.type = return_type + self.body = body + self.line = 0 + self.column = 0 + +class ExpressionNode(Node): + pass + +class IfThenElseNode(ExpressionNode): + def __init__(self, condition, if_body, else_body): + self.condition = condition + self.if_body = if_body + self.else_body = else_body + self.line = condition.line + self.column = condition.column + +class WhileLoopNode(ExpressionNode): + def __init__(self, condition, body): + self.condition = condition + self.body = body + self.line = condition.line + self.column = condition.column + + +class BlockNode(ExpressionNode): + def __init__(self, expressions): + self.expressions = expressions + self.line = expressions[-1].line + self.column = expressions[-1].column + +class LetInNode(ExpressionNode): + def __init__(self, let_body, in_body): + self.let_body = let_body + self.in_body = in_body + self.line = in_body.line + self.column = in_body.column + +class CaseOfNode(ExpressionNode): + def __init__(self, expression, branches): + self.expression = expression + self.branches = branches + self.line = expression.line + self.column = expression.column + +class AssignNode(ExpressionNode): + def __init__(self, idx, expression): + self.id = idx + self.expression = expression + self.line = 0 + self.column = 0 + +class UnaryNode(ExpressionNode): + def __init__(self, expression): + self.expression = expression + self.line = expression.line + self.column = expression.column + +class NotNode(UnaryNode): + pass + +class BinaryNode(ExpressionNode): + def __init__(self, left, right): + self.left = left + self.right = right + self.line = left.line + self.column = left.column + +class LessEqualNode(BinaryNode): + pass + +class LessNode(BinaryNode): + pass + +class EqualNode(BinaryNode): + pass + +class ArithmeticNode(BinaryNode): + pass + +class PlusNode(ArithmeticNode): + pass + +class MinusNode(ArithmeticNode): + pass + +class StarNode(ArithmeticNode): + pass + +class DivNode(ArithmeticNode): + pass + +class IsVoidNode(UnaryNode): + pass + +class ComplementNode(UnaryNode): + pass + +class FunctionCallNode(ExpressionNode): + def __init__(self, obj, line, idx, args, typex=None): + self.obj = obj + self.id = idx + self.args = args + self.typex = typex + self.line = line + self.column = 0 + +class MemberCallNode(ExpressionNode): + def __init__(self, idx, args): + self.id = idx + self.args = args + self.line = 0 + self.column = 0 + +class NewNode(ExpressionNode): + def __init__(self, typex): + self.type = typex + self.line = 0 + self.column = 0 + +class AtomicNode(ExpressionNode): + def __init__(self, token): + self.token = token + self.line = 0 + self.column = 0 + +class IntegerNode(AtomicNode): + pass + +class IdNode(AtomicNode): + pass + +class StringNode(AtomicNode): + pass + +class BoolNode(AtomicNode): + pass diff --git a/src/BaseToCILVisitor.py b/src/BaseToCILVisitor.py new file mode 100644 index 00000000..a5be1f26 --- /dev/null +++ b/src/BaseToCILVisitor.py @@ -0,0 +1,156 @@ +import cil +from semantic import VariableInfo, Scope +import queue + +class BaseCOOLToCILVisitor: + def __init__(self, context): + self.dottypes = [] + self.dotdata = [ cil.DataNode('_empty', '')] + self.dotcode = [] + self.current_type = None + self.current_method = None + self.current_function = None + self.context = context + self.basic_types() + + @property + def params(self): + return self.current_function.params + + @property + def localvars(self): + return self.current_function.localvars + + @property + def instructions(self): + return self.current_function.instructions + + @property + def labels(self): + return self.current_function.labels + + def basic_types(self): + for basicType in ['Int', 'String', 'Bool']: + cil_type = self.register_type(basicType) + cil_type.attributes.append(self.to_attribute_name('value', basicType)) + + for method , typeMethod in self.context.get_type(basicType).all_methods(): + cil_type.methods.append((method.name, self.to_function_name(method.name, typeMethod.name))) + + for basicType in ['Object', 'IO']: + cil_type = self.register_type(basicType) + for method , typeMethod in self.context.get_type(basicType).all_methods(): + cil_type.methods.append((method.name, self.to_function_name(method.name, typeMethod.name))) + + + def register_param(self, vinfo): + vinfo.cilName = vinfo.name # f'param_{self.current_function.name[9:]}_{vinfo.name}_{len(self.params)}' + param_node = cil.ParamNode(vinfo.cilName) + self.params.append(param_node) + return vinfo.cilName + + def register_local(self, vinfo): + vinfo.cilName = f'local_{self.current_function.name[9:]}_{vinfo.name}_{len(self.localvars)}' + local_node = cil.LocalNode(vinfo.cilName) + self.localvars.append(local_node) + return vinfo.cilName + + def register_label(self): + name = f'label_{self.current_function.name[9:]}_{len(self.labels)}' + self.labels.append(name) + return name + + def define_internal_local(self): + vinfo = VariableInfo('internal', None) + return self.register_local(vinfo) + + def register_instruction(self, instruction): + self.instructions.append(instruction) + return instruction + + def to_function_name(self, method_name, type_name): + return f'function_{method_name}_at_{type_name}' + + def to_attribute_name(self, attr_name, attr_type): + return f'attribute_{attr_name}' + + def register_function(self, function_name): + function_node = cil.FunctionNode(function_name, [], [], [], []) + self.dotcode.append(function_node) + return function_node + + def register_type(self, name): + type_node = cil.TypeNode(name) + self.dottypes.append(type_node) + return type_node + + def register_data(self, value): + for dataNode in self.dotdata: + if dataNode.value == value: + return dataNode + + vname = f'data_{len(self.dotdata)}' + data_node = cil.DataNode(vname, value) + self.dotdata.append(data_node) + return data_node + + + def get_attr(self, function_name, attribute): + for dottype in self.dottypes: + if dottype.name == function_name: + break + + + # for attrib in dottype.attributes: + # if self.to_attribute_name(attribute, None) == attrib: + # break + + return dottype.attributes.index(self.to_attribute_name(attribute,None)) + + def get_method(self, type_name, method_name): + for typeContext in self.context.types: + if typeContext == type_name: + break + + methods = list(self.context.types[typeContext].all_methods()) + + for m in methods: + if m[0].name == method_name: + break + + return methods.index(m) + + def sort_types(self, types): + q = queue.deque() + lst = [] + for tp1 in types: + if not any ([x for x in types if x!= tp1 and tp1.conforms_to(x) ]): + q.append(tp1) + + while len(q) != 0: + tp = q.popleft() + if tp in types: + lst.append(tp) + for s in tp.sons: + q.append(s) + lst = list(reversed(lst)) + return lst + + def get_preordenTypes(self, typex): + ret_lis = [] + + for son in typex.sons: + ret_lis.extend(self.get_preordenTypes(son)) + + ret_lis.append(typex) + return ret_lis + + + def box(self, typeName, value): + obj_internal = self.define_internal_local() + self.register_instruction(cil.AllocateNode(typeName, obj_internal)) + self.register_instruction(cil.SetAttribNode(obj_internal, 0, value)) + self.register_instruction(cil.LoadNode(obj_internal, f'{typeName}_name')) + self.register_instruction(cil.LoadIntNode(obj_internal, f'{typeName}_size', 4)) + self.register_instruction(cil.LoadNode(obj_internal, f'__virtual_table__{typeName}', 8)) + return obj_internal \ No newline at end of file diff --git a/src/CILtoMIPSVisitor.py b/src/CILtoMIPSVisitor.py new file mode 100644 index 00000000..7c3b5146 --- /dev/null +++ b/src/CILtoMIPSVisitor.py @@ -0,0 +1,462 @@ +from visitor import * +from cil import * +from mips import * + +class CILtoMIPSVisitor: + def __init__(self): + self.data = [] + self.text = [] + self.function = None + self.types = None + + @visitor.on('node') + def visit(self, node): + pass + + @visitor.when(ProgramNode) + def visit(self, node): + self.types = node.dottypes + + for typePN in node.dottypes: + self.visit(typePN) + + for dataNode in node.dotdata: + self.visit(dataNode) + + for instructionNode in node.dotcode: + self.visit(instructionNode) + + return MipsProgramNode(self.data, self.text) + + + @visitor.when(TypeNode) + def visit(self, node:TypeNode): + self.data.append(MipsStringNode(f'{node.name}_name', node.name)) + self.data.append(MipsWordNode(f'{node.name}_size', 4*(len(node.attributes) + 3))) + self.data.append(MipsTableNode(node.name , [ met[1] for met in node.methods])) + + + @visitor.when(DataNode) + def visit(self, node): + self.data.append(MipsStringNode(node.name, node.value)) + + @visitor.when(JumpNode) + def visit(self, node:DynamicCallNode): + self.register_instruction(MipsCommentNode('comienzo llamada al constructor')) + self.register_instruction(MipsJumpAtAddressNode(node.method)) + + num = int(node.dest.split('_')[-1]) + self.register_instruction(MipsSWNode('$a0', f'-{(num+1) * 4}($fp)')) + self.register_instruction(MipsCommentNode('fin llamada dinamica')) + + + @visitor.when(FunctionNode) + def visit(self, node:FunctionNode): + self.function = node + self.register_instruction(MipsLabelNode(node.name)) + + self.register_instruction(MipsCommentNode('muevo el fp al sp, pongo en sp ra y avanzo la pila')) + self.register_instruction(MipsMoveNode('$fp', '$sp')) + self.register_instruction(MipsSWNode('$ra', '0($sp)')) + self.register_instruction(MipsAddiuNode('$sp', '$sp', '-4')) + + self.register_instruction(MipsCommentNode('muevo la pila x las variables locales')) + self.register_instruction(MipsAddiuNode('$sp', '$sp', f'-{len(node.localvars) * 4}')) + + for ins in node.instructions: + self.visit(ins) + + self.register_instruction(MipsCommentNode('return sp, fp, ra')) + self.register_instruction(MipsLWNode('$ra', '0($fp)')) + self.register_instruction(MipsAddiuNode('$sp', '$sp', f'{len(node.localvars) * 4 + 8 + len(node.params) * 4}')) + self.register_instruction(MipsLWNode('$fp', '0($sp)')) + self.register_instruction(MipsJRNode('$ra')) + + + @visitor.when(ArgsNode) + def visit(self, node): + self.register_instruction(MipsCommentNode('guardando los parametros')) + self.register_instruction(MipsSWNode('$fp', '0($sp)')) + self.register_instruction(MipsAddiuNode('$sp', '$sp' ,'-4')) + + for name in node.names: + pos = self.request_pos(name) + if not pos is None: + self.register_instruction(MipsLWNode('$a0', pos)) + else: + self.register_instruction(MipsLINode('$a0', name)) + + self.register_instruction(MipsSWNode('$a0', '0($sp)')) + self.register_instruction(MipsAddiuNode('$sp', '$sp', '-4')) + self.register_instruction(MipsCommentNode(' fin guardando los parametros')) + + + + + @visitor.when(ReturnNode) + def visit(self, node:ReturnNode): + self.register_instruction(MipsCommentNode('retornando el valor')) + pos = self.request_pos(node.value) + + if not pos is None: + self.register_instruction(MipsLWNode('$a0', pos)) + else: + self.register_instruction(MipsLINode('$a0', node.value)) + + @visitor.when(AllocateNode) + def visit(self, node:AllocateNode): + self.register_instruction(MipsCommentNode('init allocate')) + self.register_instruction(MipsLINode('$v0', '9')) + self.register_instruction(MipsLWNode('$a0', f'{node.type}_size')) + self.register_instruction(MipsSyscallNode()) + + num = int(node.dest.split('_')[-1]) + self.register_instruction(MipsSWNode('$v0', f'-{(num+1) * 4}($fp)')) + self.register_instruction(MipsCommentNode('end allocate')) + + + + @visitor.when(StaticCallNode) + def visit(self, node:StaticCallNode): + self.register_instruction(MipsLWNode('$a0', '4($fp)')) + self.register_instruction(MipsLWNode('$a0', '8($a0)')) + self.register_instruction(MipsLWNode('$a0', f'{node.function * 4}($a0)')) + self.register_instruction(MipsJALRNode('$a0')) + + num = int(node.dest.split('_')[-1]) + self.register_instruction(MipsSWNode('$a0', f'-{(num+1) * 4}($fp)')) + + @visitor.when(DynamicCallNode) + def visit(self, node:DynamicCallNode): + self.register_instruction(MipsCommentNode('comienzo llamada dinamica')) + + if node.type is None: + pos = self.request_pos(node.ins) + self.register_instruction(MipsLWNode('$a0', pos)) + self.register_instruction(MipsLWNode('$a0', '8($a0)')) + else: + self.register_instruction(MipsLANode('$a0', f'__virtual_table__{node.type}')) + + self.register_instruction(MipsLWNode('$a0', f'{node.method * 4}($a0)')) + self.register_instruction(MipsJALRNode('$a0')) + + num = int(node.dest.split('_')[-1]) + self.register_instruction(MipsSWNode('$a0', f'-{(num+1) * 4}($fp)')) + self.register_instruction(MipsCommentNode('fin llamada dinamica')) + + + + @visitor.when(SetAttribNode) + def visit(self, node:SetAttribNode): + self.register_instruction(MipsCommentNode('init set attribute')) + + pos = self.request_pos(node.ins) + self.register_instruction(MipsLWNode('$a0', pos)) + + # nameType = node.att.split('_')[1] + # num = -1 + + # for typeAct in self.types: + # if typeAct.name == nameType: + # num = typeAct.attributes.index(node.att) + # break + + + pos = self.request_pos(node.value) + if not pos is None: + self.register_instruction(MipsLWNode('$t1', pos)) + else: + self.register_instruction(MipsLINode('$t1', node.value)) + + self.register_instruction(MipsSWNode('$t1', f'{node.att * 4 + 12}($a0)')) + self.register_instruction(MipsCommentNode('end set attribute')) + + @visitor.when(GetAttribNode) + def visit(self, node:GetAttribNode): + self.register_instruction(MipsCommentNode('init get attribute')) + + pos_result = self.request_pos(node.dest) + pos = self.request_pos(node.ins) + self.register_instruction(MipsLWNode('$a0', pos)) + + # nameType = node.att.split('_')[1] + # num = -1 + + # for typeAct in self.types: + # if typeAct.name == nameType: + # num = typeAct.attributes.index(node.att) + # break + + + self.register_instruction(MipsLWNode('$a0', f'{node.att * 4 + 12}($a0)')) + self.register_instruction(MipsSWNode('$a0', pos_result)) + + + + @visitor.when(LoadNode) + def visit(self, node): + self.register_instruction(MipsCommentNode('LOAD inicia')) + self.register_instruction(MipsLANode('$t1', node.msg)) + dest = self.request_pos(node.dest) + self.register_instruction(MipsLWNode("$t2",dest)) + self.register_instruction(MipsSWNode('$t1', f"{node.desp}($t2)")) + + @visitor.when(LoadAddressNode) + def visit(self, node): + pos = self.request_pos(node.dest) + self.register_instruction(MipsLANode('$t1', node.msg)) + self.register_instruction(MipsSWNode('$t1', pos)) + + @visitor.when(LoadIntNode) + def visit(self, node): + self.register_instruction(MipsCommentNode('LOAD inicia')) + self.register_instruction(MipsLWNode('$t1', node.msg)) + dest = self.request_pos(node.dest) + self.register_instruction(MipsLWNode("$t2",dest)) + self.register_instruction(MipsSWNode('$t1', f"{node.desp}($t2)")) + + + + @visitor.when(AssignNode) + def visit(self, node:AssignNode): + pos_dest = self.request_pos(node.dest) + pos_src = self.request_pos(node.source) + + if not pos_src is None: + self.register_instruction(MipsLWNode('$t1', pos_src)) + else: + self.register_instruction(MipsLINode('$t1', node.source)) + + self.register_instruction(MipsSWNode('$t1', pos_dest)) + + @visitor.when(GotoNode) + def visit(self, node): + self.register_instruction(MipsJumpNode(node.name)) + + @visitor.when(GotoIfNode) + def visit(self, node): + pos = self.request_pos(node.condition) + + if not pos is None: + self.register_instruction(MipsLWNode('$a0', pos)) + else: + self.register_instruction(MipsLINode('$a0', node.condition)) + + self.register_instruction(MipsBNENode('$a0', '$zero', node.name)) + + @visitor.when(LabelNode) + def visit(self, node): + self.register_instruction(MipsLabelNode(node.name)) + + + + @visitor.when(PlusNode) + def visit(self, node:PlusNode): + pos_dest = self.request_pos(node.dest) + pos_left = self.request_pos(node.left) + pos_right = self.request_pos(node.right) + + if not pos_left is None: + self.register_instruction(MipsLWNode('$t1', pos_left)) + else: + self.register_instruction(MipsLINode('$t1', node.left)) + + if not pos_right is None: + self.register_instruction(MipsLWNode('$a0', pos_right)) + else: + self.register_instruction(MipsLINode('$a0', node.right)) + + self.register_instruction(MipsAddNode('$a0', '$a0', '$t1')) + self.register_instruction(MipsSWNode('$a0', pos_dest)) + + @visitor.when(MinusNode) + def visit(self, node): + pos_dest = self.request_pos(node.dest) + pos_left = self.request_pos(node.left) + pos_right = self.request_pos(node.right) + + if not pos_left is None: + self.register_instruction(MipsLWNode('$t1', pos_left)) + else: + self.register_instruction(MipsLINode('$t1', node.left)) + + if not pos_right is None: + self.register_instruction(MipsLWNode('$a0', pos_right)) + else: + self.register_instruction(MipsLINode('$a0', node.right)) + + self.register_instruction(MipsMinusNode('$a0', '$t1', '$a0')) + self.register_instruction(MipsSWNode('$a0', pos_dest)) + + + @visitor.when(StarNode) + def visit(self, node): + pos_dest = self.request_pos(node.dest) + pos_left = self.request_pos(node.left) + pos_right = self.request_pos(node.right) + + if not pos_left is None: + self.register_instruction(MipsLWNode('$t1', pos_left)) + else: + self.register_instruction(MipsLINode('$t1', node.left)) + + if not pos_right is None: + self.register_instruction(MipsLWNode('$a0', pos_right)) + else: + self.register_instruction(MipsLINode('$a0', node.right)) + + self.register_instruction(MipsStarNode('$a0', '$t1', '$a0')) + self.register_instruction(MipsSWNode('$a0', pos_dest)) + + @visitor.when(DivNode) + def visit(self, node): + pos_dest = self.request_pos(node.dest) + pos_left = self.request_pos(node.left) + pos_right = self.request_pos(node.right) + + if not pos_left is None: + self.register_instruction(MipsLWNode('$t1', pos_left)) + else: + self.register_instruction(MipsLINode('$t1', node.left)) + + if not pos_right is None: + self.register_instruction(MipsLWNode('$a0', pos_right)) + else: + self.register_instruction(MipsLINode('$a0', node.right)) + + self.register_instruction(MipsDivNode('$a0', '$t1', '$a0')) + self.register_instruction(MipsSWNode('$a0', pos_dest)) + + @visitor.when(ComplementNode) + def visit(self, node): + pos_dest = self.request_pos(node.dest) + pos_expression = self.request_pos(node.expression) + + if not pos_expression is None: + self.register_instruction(MipsLWNode('$t1', pos_expression)) + else: + self.register_instruction(MipsLINode('$t1', node.expression)) + + self.register_instruction(MipsLINode('$t2', -1)) + self.register_instruction(MipsStarNode('$t1','$t1' ,'$t2')) + self.register_instruction(MipsSWNode('$t1', pos_dest)) + + @visitor.when(IsVoidNode) + def visit(self, node): + pos_obj = self.request_pos(node.obj) + pos_dest = self.request_pos(node.dest) + + if not pos_obj is None: + self.register_instruction(MipsLWNode('$t1', pos_obj)) + else: + self.register_instruction(MipsLINode('$a0', 1)) + self.register_instruction(MipsJumpNode(node.label)) + + self.register_instruction(MipsLINode('$a0', 1)) + self.register_instruction(MipsLANode('$t2', '_void')) + self.register_instruction(MipsBNENode('$t1', '$t2', node.label)) + self.register_instruction(MipsLINode('$a0', 0)) + self.register_instruction(MipsLabelNode(node.label)) + self.register_instruction(MipsSWNode('$a0', pos_dest)) + + @visitor.when(LessNode) + def visit(self, node): + pos_dest = self.request_pos(node.result) + pos_left = self.request_pos(node.left) + pos_right = self.request_pos(node.right) + + if not pos_left is None: + self.register_instruction(MipsLWNode('$t1', pos_left)) + else: + self.register_instruction(MipsLINode('$t1', node.left)) + + if not pos_right is None: + self.register_instruction(MipsLWNode('$a0', pos_right)) + else: + self.register_instruction(MipsLINode('$a0', node.right)) + + self.register_instruction(MipsBLTNode('$t1', '$a0', node.labelTrue)) + self.register_instruction(MipsLINode('$a0', 0)) + self.register_instruction(MipsJumpNode(node.labelEnd)) + self.register_instruction(MipsLabelNode(node.labelTrue)) + self.register_instruction(MipsLINode('$a0', 1)) + self.register_instruction(MipsLabelNode(node.labelEnd)) + self.register_instruction(MipsSWNode('$a0', pos_dest)) + + @visitor.when(LessEqualNode) + def visit(self, node): + pos_dest = self.request_pos(node.result) + pos_left = self.request_pos(node.left) + pos_right = self.request_pos(node.right) + + if not pos_left is None: + self.register_instruction(MipsLWNode('$t1', pos_left)) + else: + self.register_instruction(MipsLINode('$t1', node.left)) + + if not pos_right is None: + self.register_instruction(MipsLWNode('$a0', pos_right)) + else: + self.register_instruction(MipsLINode('$a0', node.right)) + + self.register_instruction(MipsBLENode('$t1', '$a0', node.labelTrue)) + self.register_instruction(MipsLINode('$a0', 0)) + self.register_instruction(MipsJumpNode(node.labelEnd)) + self.register_instruction(MipsLabelNode(node.labelTrue)) + self.register_instruction(MipsLINode('$a0', 1)) + self.register_instruction(MipsLabelNode(node.labelEnd)) + self.register_instruction(MipsSWNode('$a0', pos_dest)) + + @visitor.when(StringComparer) + def visit(self, node): + pos_dest = self.request_pos(node.result) + pos_left = self.request_pos(node.left) + pos_right = self.request_pos(node.right) + + self.register_instruction(MipsSWNode('$fp', '0($sp)')) + self.register_instruction(MipsAddiuNode('$sp', '$sp' ,'-4')) + + self.register_instruction(MipsLWNode('$a0', pos_left)) + self.register_instruction(MipsSWNode('$a0', '0($sp)')) + self.register_instruction(MipsAddiuNode('$sp', '$sp', '-4')) + + self.register_instruction(MipsLWNode('$a0', pos_right)) + self.register_instruction(MipsSWNode('$a0', '0($sp)')) + self.register_instruction(MipsAddiuNode('$sp', '$sp', '-4')) + + self.register_instruction(MipsJumpAtAddressNode('function_comparer_string')) + self.register_instruction(MipsSWNode('$a0', pos_dest)) + + + @visitor.when(CaseOption) + def visit(self, node): + pos_expression = self.request_pos(node.expression) + + if not pos_expression is None: + self.register_instruction(MipsLANode('$a0', f'{node.typex.name}_name')) + self.register_instruction(MipsLWNode('$t1', pos_expression)) + self.register_instruction(MipsLWNode('$t1', '0($t1)')) + self.register_instruction(MipsBEQNode('$t1', '$a0', node.label)) + else: + pass + + # registers + def register_data(self, instruction): + self.data.append(instruction) + + def register_instruction(self, instruction): + self.text.append(instruction) + + + + + # utils + def request_pos(self, name): + if name in [x.name for x in self.function.localvars]: + numb = int( name.split('_')[-1]) + return f'-{(numb+1) * 4}($fp)' + elif name in [x.name for x in self.function.params]: + numb = [param.name for param in self.function.params].index(name) + return f'{(numb + 1) * 4}($fp)' + else: + return None \ No newline at end of file diff --git a/src/COOLToCILVisitor.py b/src/COOLToCILVisitor.py new file mode 100644 index 00000000..fda098b6 --- /dev/null +++ b/src/COOLToCILVisitor.py @@ -0,0 +1,513 @@ +import cil +from BaseToCILVisitor import BaseCOOLToCILVisitor +from AstNodes import * +from semantic import * +import visitor + +class COOLToCILVisitor(BaseCOOLToCILVisitor): + @visitor.on('node') + def visit(self, node): + pass + + @visitor.when(ProgramNode) + def visit(self, node, scope): + self.current_function = self.register_function('main') + instance = self.define_internal_local() + result = self.define_internal_local() + result2 = self.define_internal_local() + + self.register_instruction(cil.AllocateNode('Main', instance)) + self.register_instruction(cil.ArgsNode([instance])) + + self.register_instruction(cil.JumpNode(self.to_function_name('Ctr', 'Main'), result)) + self.register_instruction(cil.ArgsNode([result])) + + realMethod = self.get_method(self.context.get_type('Main').name, 'main') + self.register_instruction(cil.DynamicCallNode('Main', realMethod , result2, result)) + self.register_instruction(cil.ReturnNode(0)) + self.current_function = None + + for declaration, child_scope in zip(node.declarations, scope.children): + self.visit(declaration, child_scope) + + return cil.ProgramNode(self.dottypes, self.dotdata, self.dotcode) + + @visitor.when(ClassDeclarationNode) + def visit(self, node, scope): + #################################################################### + # node.id -> str + # node.parent -> str + # node.features -> [ FuncDeclarationNode/AttrDeclarationNode ... ] + #################################################################### + + self.current_type = self.context.get_type(node.id) + + cil_type = self.register_type(node.id) + + for attr, type_attr in self.current_type.all_attributes(): + cil_type.attributes.append(self.to_attribute_name(attr.name, type_attr.name)) + + for func, type_func in self.current_type.all_methods(): + cil_type.methods.append((func.name, self.to_function_name(func.name, type_func.name))) + + nodeFunctions = [x for x in node.features if isinstance(x, FuncDeclarationNode)] + for feature, child_scope in zip(nodeFunctions, scope.children[0].children): + self.visit(feature, child_scope) + + #ctr + name = self.to_function_name("Ctr", self.current_type.name) + self.current_function = self.register_function(name) + self.register_param(VariableInfo('self', self.current_type)) + + self.register_instruction(cil.LoadNode('self', f'{self.current_type.name}_name')) + self.register_instruction(cil.LoadIntNode('self', f'{self.current_type.name}_size', 4)) + self.register_instruction(cil.LoadNode('self', f'__virtual_table__{self.current_type.name}', 8)) + + initResult = self.define_internal_local() + self.register_instruction(cil.ArgsNode(['self'])) + self.register_instruction(cil.JumpNode(self.to_function_name('Init', self.current_type.name), initResult)) + + # nodeatt = [x for x in node.features if isinstance(x, AttrDeclarationNode)] + # for feature, child_scope in zip(nodeatt, scope.children[1].children): + # self.visit(feature, child_scope) + + self.register_instruction(cil.ReturnNode('self')) + + + #init + name = self.to_function_name('Init', self.current_type.name) + self.current_function = self.register_function(name) + self.register_param(VariableInfo('self', self.current_type)) + + parentResult = self.define_internal_local() + self.register_instruction(cil.ArgsNode(['self'])) + self.register_instruction(cil.JumpNode(self.to_function_name('Init', self.current_type.parent.name), parentResult)) + + nodeatt = [x for x in node.features if isinstance(x, AttrDeclarationNode)] + for feature, child_scope in zip(nodeatt, scope.children[1].children): + self.visit(feature, child_scope) + + self.register_instruction(cil.ReturnNode('self')) + + self.current_type = None + + @visitor.when(FuncDeclarationNode) + def visit(self, node, scope): + ############################### + # node.id -> str + # node.params -> [ (str, str) ... ] + # node.type -> str + # node.body -> [ ExpressionNode ... ] + ############################### + + self.current_method = self.current_type.get_method(node.id) + + name = self.to_function_name(node.id, self.current_type.name) + self.current_function = self.register_function(name) + + self.register_param(VariableInfo('self', self.current_type)) + for param in scope.locals: + self.register_param(param) + + value = self.visit(node.body, scope.children[0]) + + self.register_instruction(cil.ReturnNode(value)) + self.current_method = None + + + @visitor.when(AttrDeclarationNode) + def visit(self, node, scope): + if not node.expression is None: + value = self.visit(node.expression, scope.children[0]) + + if node.type == 'Object' and node.expression.type.name in ['Int', 'Bool', 'String']: + value = self.box(node.expression.type.name, value) + + else: + if node.type == 'String': + internal = self.define_internal_local() + self.register_instruction(cil.LoadAddressNode(internal, "_empty")) + value = internal + elif node.type == 'Bool' or node.type == 'Int': + value = 0 + else: + internal = self.define_internal_local() + self.register_instruction(cil.LoadAddressNode(internal, "_void")) + value = internal + + attrib = self.get_attr(self.current_type.name, node.id) + self.register_instruction(cil.SetAttribNode('self', attrib, value)) + + @visitor.when(LetInNode) + def visit(self, node, scope): + scopeOpen = scope.children[0] + + value = None + for init in node.let_body: + if not init[2] is None: + value = self.visit(init[2], scopeOpen) + + if init[2].type.name in ['Int', 'Bool', 'String'] and init[1]== 'Object': + value = self.box(init[2].type.name, value) + + else: + if init[1] == 'String': + internal = self.define_internal_local() + self.register_instruction(cil.LoadAddressNode(internal, "_empty")) + value = internal + elif init[1] == 'Bool' or init[1] == 'Int': + value = 0 + else: + internal = self.define_internal_local() + self.register_instruction(cil.LoadAddressNode(internal, "_void")) + value = internal + + scopeOpen = scopeOpen.children[-1] + vinfo = scopeOpen.find_variable(init[0]) + vname = self.register_local(vinfo) + self.register_instruction(cil.AssignNode(vname, value)) + + return self.visit(node.in_body, scopeOpen.children[0]) + + @visitor.when(CaseOfNode) + def visit(self, node, scope): + result = self.define_internal_local() + + internal_expression = self.define_internal_local() + value_expression = self.visit(node.expression, scope.children[0]) + self.register_instruction(cil.AssignNode(internal_expression, value_expression)) + + types_ordered = self.sort_types([self.context.get_type(x[1]) for x in node.branches]) + list_label = [] + labels = dict() + + for typex in types_ordered: + labelTypex = self.register_label() + labels[typex.name] = labelTypex + pre = self.get_preordenTypes(typex) + for typex2 in pre: + if not typex2 in [x[0] for x in list_label]: + list_label.append((typex2, labelTypex)) + + for typex in list_label: + self.register_instruction(cil.CaseOption(value_expression, typex[1], typex[0])) + #error + + labelEnd = self.register_label() + for branch, scopeBranch in zip(node.branches, scope.children[1].children): + vinfo = scopeBranch.find_variable(branch[0]) + xxx = self.register_local(vinfo) + self.register_instruction(cil.LabelNode(labels[branch[1]])) + self.register_instruction(cil.AssignNode(xxx, value_expression)) + + valueBranch = self.visit(branch[2], scopeBranch) + self.register_instruction(cil.AssignNode(result, valueBranch)) + self.register_instruction(cil.GotoNode(labelEnd)) + + self.register_instruction(cil.LabelNode(labelEnd)) + return result + + + @visitor.when(BlockNode) + def visit(self, node, scope): + for expression, child in zip(node.expressions, scope.children): + value = self.visit(expression, child) + + return value + + @visitor.when(IfThenElseNode) + def visit(self, node, scope): + cond = self.visit(node.condition, scope.children[0]) + + labelTrue = self.register_label() + labelFalse = self.register_label() + result = self.define_internal_local() + + self.register_instruction(cil.GotoIfNode(labelTrue, cond)) + vfalse = self.visit(node.else_body, scope.children[2]) + self.register_instruction(cil.AssignNode(result, vfalse)) + self.register_instruction(cil.GotoNode(labelFalse)) + self.register_instruction(cil.LabelNode(labelTrue)) + vtrue = self.visit(node.if_body, scope.children[1]) + self.register_instruction(cil.AssignNode(result, vtrue)) + self.register_instruction(cil.LabelNode(labelFalse)) + + return result + + @visitor.when(WhileLoopNode) + def visit(self, node, scope): + labelWhileStart = self.register_label() + labelWhileContinue = self.register_label() + labelWhileBreak = self.register_label() + + self.register_instruction(cil.LabelNode(labelWhileStart)) + + cond = self.visit(node.condition, scope.children[0]) + + self.register_instruction(cil.GotoIfNode(labelWhileContinue, cond)) + self.register_instruction(cil.GotoNode(labelWhileBreak)) + self.register_instruction(cil.LabelNode(labelWhileContinue)) + self.visit(node.body, scope.children[1]) + self.register_instruction(cil.GotoNode(labelWhileStart)) + self.register_instruction(cil.LabelNode(labelWhileBreak)) + + result = self.define_internal_local() + self.register_instruction(cil.LoadAddressNode(result, "_void")) + return result + + @visitor.when(AssignNode) + def visit(self, node, scope): + ############################### + # node.id -> str + # node.expr -> ExpressionNode + ############################### + + vinfo = scope.find_variable(node.id) + value = self.visit(node.expression, scope.children[0]) + + if vinfo is None: + attrib = self.get_attr(self.current_type.name, node.id) + self.register_instruction(cil.SetAttribNode('self', attrib, value)) + else: + self.register_instruction(cil.AssignNode(vinfo.cilName, value)) + + return value + + @visitor.when(FunctionCallNode) + def visit(self, node, scope): + result = self.define_internal_local() + obj = self.visit(node.obj, scope.children[0]) + + if node.obj.type.name in ['Int', 'Bool', 'String']: + if node.id in ['abort', 'type_name', 'copy']: + obj = self.box(node.obj.type.name, obj) + + valuesArgs = [] + for arg, child in zip(node.args, scope.children[1:]): + value = self.visit(arg, child) + + if arg.type.name in ['Int', 'Bool', 'String']: + method = self.context.get_type(node.typex).get_method(node.id) + param_type = method.param_types[node.args.index(arg)] + + if param_type.name == 'Object': + valuesArgs.append(self.box(arg.type.name, value)) + continue + + valuesArgs.append(value) + + if node.typexa is None: + node.typex = node.obj.type.name + + self.register_instruction(cil.ArgsNode( list(reversed(valuesArgs)) + [obj])) + + if node.obj.type.name == 'String' and node.id in ['length', 'concat', 'substr']: + self.register_instruction(cil.JumpNode(self.to_function_name(node.id, 'String'), result)) + else: + realMethod = self.get_method(node.typex, node.id) + self.register_instruction(cil.DynamicCallNode(node.typexa, realMethod, result, obj)) + return result + + @visitor.when(MemberCallNode) + def visit(self, node, scope): + result = self.define_internal_local() + + valuesArgs = [] + for arg, child in zip(node.args, scope.children): + value = self.visit(arg, child) + + if arg.type.name in ['Int', 'Bool', 'String']: + method = self.current_type.get_method(node.id) + param_type = method.param_types[node.args.index(arg)] + + if param_type.name == 'Object': + valuesArgs.append(self.box(arg.type.name, value)) + continue + + valuesArgs.append(value) + + self.register_instruction(cil.ArgsNode( list(reversed(valuesArgs)) + ['self'])) + + realMethod = self.get_method(self.current_type.name, node.id) + self.register_instruction(cil.StaticCallNode(realMethod, result)) + return result + + @visitor.when(IntegerNode) + def visit(self, node, scope): + return int(node.token) + + + @visitor.when(BoolNode) + def visit(self, node, scope): + if node.token: + return 1 + return 0 + + + @visitor.when(StringNode) + def visit(self, node, scope): + msg = self.register_data(node.token).name + internal = self.define_internal_local() + self.register_instruction(cil.LoadAddressNode(internal, msg)) + return internal + + + @visitor.when(IdNode) + def visit(self, node:IdNode, scope): + if node.token == 'self': + return 'self' + + vinfo = scope.find_variable(node.token) + + if vinfo is None: + result = self.define_internal_local() + attrib = self.get_attr(self.current_type.name, node.token) + + self.register_instruction(cil.GetAttribNode('self', attrib, result)) + return result + + return vinfo.cilName + + + @visitor.when(NewNode) + def visit(self, node:NewNode, scope): + if not node.type.name == "Int": + instance = self.define_internal_local() + result = self.define_internal_local() + self.register_instruction(cil.AllocateNode(node.type.name, instance)) + self.register_instruction(cil.ArgsNode([instance])) + self.register_instruction(cil.JumpNode(self.to_function_name('Ctr', node.type.name), result)) + return instance + else: + return 0 + + + + @visitor.when(PlusNode) + def visit(self, node, scope): + result = self.define_internal_local() + left = self.visit(node.left, scope.children[0]) + right = self.visit(node.right, scope.children[1]) + self.register_instruction(cil.PlusNode(result, left, right)) + return result + + + @visitor.when(MinusNode) + def visit(self, node, scope): + result = self.define_internal_local() + left = self.visit(node.left, scope.children[0]) + right = self.visit(node.right, scope.children[1]) + self.register_instruction(cil.MinusNode(result, left, right)) + return result + + + @visitor.when(StarNode) + def visit(self, node, scope): + result = self.define_internal_local() + left = self.visit(node.left, scope.children[0]) + right = self.visit(node.right, scope.children[1]) + self.register_instruction(cil.StarNode(result, left, right)) + return result + + @visitor.when(DivNode) + def visit(self, node, scope): + result = self.define_internal_local() + left = self.visit(node.left, scope.children[0]) + right = self.visit(node.right, scope.children[1]) + self.register_instruction(cil.DivNode(result, left, right)) + return result + + @visitor.when(EqualNode) + def visit(self, node, scope): + result = self.define_internal_local() + left = self.visit(node.left, scope.children[0]) + right = self.visit(node.right, scope.children[1]) + + if node.left.type.name == 'String': + self.register_instruction(cil.StringComparer(result, left, right)) + else: + labelEquals = self.register_label() + labelsEnd = self.register_label() + + resultComparer = self.define_internal_local() + self.register_instruction(cil.MinusNode(resultComparer, left, right)) + + self.register_instruction(cil.GotoIfNode(labelEquals, resultComparer)) + self.register_instruction(cil.AssignNode(result, 1)) + self.register_instruction(cil.GotoNode(labelsEnd)) + self.register_instruction(cil.LabelNode(labelEquals)) + self.register_instruction(cil.AssignNode(result, 0)) + self.register_instruction(cil.LabelNode(labelsEnd)) + + return result + + @visitor.when(LessNode) + def visit(self, node, scope): + result = self.define_internal_local() + left = self.visit(node.left, scope.children[0]) + right = self.visit(node.right, scope.children[1]) + + labelTrue = self.register_label() + labelEnd = self.register_label() + + self.register_instruction(cil.LessNode(result, left, right, labelTrue, labelEnd)) + return result + + @visitor.when(LessEqualNode) + def visit(self, node, scope): + result = self.define_internal_local() + left = self.visit(node.left, scope.children[0]) + right = self.visit(node.right, scope.children[1]) + + labelTrue = self.register_label() + labelEnd = self.register_label() + + self.register_instruction(cil.LessEqualNode(result, left, right, labelTrue,labelEnd)) + return result + + @visitor.when(NotNode) + def visit(self, node, scope): + result = self.define_internal_local() + value = self.visit(node.expression, scope.children[0]) + + labelTrue = self.register_label() + labelEnd = self.register_label() + + self.register_instruction(cil.GotoIfNode(labelTrue, value)) + self.register_instruction(cil.AssignNode(result, 1)) + self.register_instruction(cil.GotoNode(labelEnd)) + self.register_instruction(cil.LabelNode(labelTrue)) + self.register_instruction(cil.AssignNode(result, 0)) + self.register_instruction(cil.LabelNode(labelEnd)) + + return result + + @visitor.when(ComplementNode) + def visit(self, node, scope): + result = self.define_internal_local() + expression = self.visit(node.expression, scope.children[0]) + + self.register_instruction(cil.ComplementNode(expression, result)) + return result + + @visitor.when(IsVoidNode) + def visit(self, node, scope): + result = self.define_internal_local() + expression = self.visit(node.expression, scope.children[0]) + + labelEnd = self.register_label() + + self.register_instruction(cil.IsVoidNode(expression, result, labelEnd)) + return result + + + # utils + def request_pos(self, name): + if name in [x.name for x in self.current_function.localvars]: + numb = int( name.split('_')[-1]) + return f'-{(numb+1) * 4}($fp)' + elif name in [x.name for x in self.current_function.params]: + numb = [param.name for param in self.function.params].index(name) + return f'{(numb + 1) * 4}($fp)' + else: + return None diff --git a/src/Parser.py b/src/Parser.py new file mode 100644 index 00000000..73fb37d1 --- /dev/null +++ b/src/Parser.py @@ -0,0 +1,328 @@ +import ply.yacc as yacc +from tokens import tokens +from AstNodes import * + # Get the token map from the lexer. This is required. +class CoolParser(): + + def __init__(self): + self.tokens = tokens + self.errors = [] + + def p_program(self, p): + 'program : class_list' + p[0]=ProgramNode(p[1]) + + def p_class_list(self, p): + 'class_list : def_class class_list' + p[0]=[p[1]] + p[2] + + def p_class_list_s(self, p): + 'class_list : def_class' + p[0]=[p[1]] + + def p_def_class(self, p): + 'def_class : CLASS TYPE OCUR feature_list CCUR SEMI' + p[0]=ClassDeclarationNode(p[2],p[4]) + p[0].line = p.lineno(1) + + def p_def_class_h(self, p): + 'def_class : CLASS TYPE INHERITS TYPE OCUR feature_list CCUR SEMI' + p[0]=ClassDeclarationNode(p[2],p[6],p[4]) + p[0].line = p.lineno(1) + + def p_feature_list(self, p): + 'feature_list : feature feature_list' + p[0]=[p[1]]+p[2] + + def p_feature_list_e(self, p): + 'feature_list : empty' + p[0]=[] + + def p_feature_1(self, p): + 'feature : OBJECT COLON TYPE SEMI' + p[0]=AttrDeclarationNode(p[1],p[3]) + p[0].line = p.lineno(1) + + def p_feature_2(self, p): + 'feature : OBJECT COLON TYPE LARROW expr SEMI' + p[0]=AttrDeclarationNode(p[1],p[3],p[5]) + p[0].line = p.lineno(1) + + def p_feature_3(self, p): + 'feature : OBJECT OPAR param_list CPAR COLON TYPE OCUR expr CCUR SEMI' + p[0]=FuncDeclarationNode(p[1], p[3], p[6], p[8]) + p[0].line = p.lineno(1) + + def p_feature_4(self, p): + 'feature : OBJECT OPAR CPAR COLON TYPE OCUR expr CCUR SEMI' + p[0]=FuncDeclarationNode(p[1], [], p[5], p[7]) + p[0].line = p.lineno(1) + + def p_param_list_1(self, p): + 'param_list : param' + p[0]=[p[1]] + + def p_param_list_2(self, p): + 'param_list : param COMMA param_list' + p[0]=[p[1]] + p[3] + + # ??? + def p_param(self, p): + r'param : OBJECT COLON TYPE' + p[0]=(p[1],p[3]) + + # ??? + def p_expr_list_1(self, p): + 'expr_list : expr SEMI' + p[0]=[p[1]] + + def p_expr_list_2(self, p): + 'expr_list : expr SEMI expr_list' + p[0]=[p[1]]+p[3] + + # ??? + def p_let_list1(self, p): + 'let_list : OBJECT COLON TYPE' + p[0]=[(p[1], p[3], None)] + + def p_let_list2(self, p): + 'let_list : OBJECT COLON TYPE LARROW expr' + p[0]=[(p[1], p[3], p[5])] + + def p_let_list3(self, p): + 'let_list : OBJECT COLON TYPE COMMA let_list' + p[0]=[(p[1], p[3], None)] + p[5] + + def p_let_list4(self, p): + 'let_list : OBJECT COLON TYPE LARROW expr COMMA let_list' + p[0]=[(p[1], p[3], p[5])] + p[7] + + # ??? + def p_case_list_1(self, p): + 'case_list : OBJECT COLON TYPE RARROW expr SEMI' + p[0] = [(p[1], p[3], p[5])] + + def p_case_list_2(self, p): + 'case_list : OBJECT COLON TYPE RARROW expr SEMI case_list' + p[0] = [(p[1], p[3], p[5])] + p[7] + + # ??? + + def p_expr_2(self, p): + 'expr : comp_expr' + p[0]=p[1] + + # ??? + + def p_comp_expr_1(self, p): + 'comp_expr : comp_expr LESSEQUAL arith' + p[0]= LessEqualNode(p[1], p[3]) + p[0].line = p.lineno(2) + + def p_comp_expr_2(self, p): + 'comp_expr : comp_expr LESS arith' + p[0]= LessNode(p[1], p[3]) + p[0].line = p.lineno(2) + + def p_comp_expr_3(self, p): + 'comp_expr : comp_expr EQUAL arith' + p[0]= EqualNode(p[1], p[3]) + p[0].line = p.lineno(2) + + def p_comp_expr_4(self, p): + 'comp_expr : arith' + p[0]= p[1] + + + + # ??? + + def p_arith_1(self, p): + 'arith2 : arith2 PLUS term' + p[0]= PlusNode(p[1], p[3]) + p[0].line = p.lineno(2) + + def p_arith_2(self, p): + 'arith2 : arith2 MINUS term' + p[0]= MinusNode(p[1], p[3]) + p[0].line = p.lineno(2) + + def p_arith_3(self, p): + 'arith2 : term' + p[0]=p[1] + + def p_arith_2_1(self, p): + r'arith : NOT comp_expr' + p[0]=NotNode(p[2]) + p[0].line = p.lineno(1) + + def p_arith_2_2(self, p): + r'arith : arith2' + p[0]=p[1] + + + # ??? + def p_term_1(self, p): + 'term : term MULT factor' + p[0]=StarNode(p[1], p[3]) + p[0].line = p.lineno(2) + + def p_term_2(self, p): + 'term : term DIV factor' + p[0]=DivNode(p[1], p[3]) + p[0].line = p.lineno(2) + + def p_term_3(self, p): + 'term : factor' + p[0]=p[1] + + # ??? + def p_factor_1(self, p): + 'factor : ISVOID factor2' + p[0]= IsVoidNode(p[2]) + p[0].line = p.lineno(1) + + def p_factor_2(self, p): + 'factor : factor2' + p[0]=p[1] + + # ??? + def p_factor_2_1(self, p): + 'factor2 : INT_COMPLEMENT atom' + p[0]=ComplementNode(p[2]) + p[0].line = p.lineno(1) + + def p_factor_2_2(self, p): + 'factor2 : atom' + p[0]=p[1] + # ??? + + def p_atom_1(self, p): + 'atom : IF expr THEN expr ELSE expr FI' + p[0]=IfThenElseNode(p[2], p[4], p[6]) + p[0].line = p.lineno(1) + + def p_atom_2(self, p): + 'atom : WHILE expr LOOP expr POOL' + p[0]=WhileLoopNode(p[2], p[4]) + p[0].line = p.lineno(1) + + def p_atom_3(self, p): + 'atom : LET let_list IN expr' + p[0]= LetInNode(p[2],p[4]) + p[0].line = p.lineno(1) + + def p_atom_4(self, p): + 'atom : CASE expr OF case_list ESAC ' + p[0]=CaseOfNode(p[2], p[4]) + p[0].line = p.lineno(1) + + def p_atom_5 (self, p): + 'atom : OBJECT LARROW expr' + p[0]= AssignNode(p[1],p[3]) + p[0].line = p.lineno(1) + + def p_atom_6(self, p): + 'atom : atom func_call' + p[0]= FunctionCallNode(p[1], *p[2]) + + def p_atom_7(self, p): + 'atom : member_call' + p[0]=p[1] + + def p_atom_8(self, p): + 'atom : NEW TYPE' + p[0]= NewNode(p[2]) + p[0].line = p.lineno(1) + + def p_atom_9(self, p): + 'atom : OPAR expr CPAR' + p[0]=p[2] + + def p_atom_10(self, p): + 'atom : OBJECT' + p[0]=IdNode(p[1]) + p[0].line = p.lineno(1) + + def p_atom_11(self, p): + 'atom : INTEGER' + p[0]= IntegerNode(p[1]) + p[0].line = p.lineno(1) + + def p_atom_12(self, p): + 'atom : STRING' + p[0]=StringNode(p[1]) + p[0].line = p.lineno(1) + + def p_atom_13(self, p): + 'atom : BOOL' + p[0]=BoolNode(p[1]) + p[0].line = p.lineno(1) + + def p_atom_14(self, p): + r'atom : OCUR expr_list CCUR' + p[0]=BlockNode(p[2]) + p[0].line = p.lineno(1) + + # ??? + def p_func_call_1(self, p): + 'func_call : DOT OBJECT OPAR arg_list CPAR' + p[0]=(p.lineno(1),p[2],p[4]) + + + def p_func_call_2(self, p): + 'func_call : DOT OBJECT OPAR CPAR' + p[0]=(p.lineno(1),p[2],[]) + + + + def p_func_call_3(self, p): + 'func_call : AT TYPE DOT OBJECT OPAR arg_list CPAR' + p[0]=(p.lineno(1),p[4],p[6],p[2]) + + + def p_func_call_4(self, p): + 'func_call : AT TYPE DOT OBJECT OPAR CPAR' + p[0]=(p.lineno(1),p[4],[],p[2]) + + + def p_arg_list_1(self, p): + 'arg_list : expr' + p[0]=[p[1]] + + def p_arg_list_2(self, p): + 'arg_list : expr COMMA arg_list' + p[0]=[p[1]]+p[3] + + def p_member_call_1(self, p): + 'member_call : OBJECT OPAR arg_list CPAR' + p[0]= MemberCallNode(p[1],p[3]) + p[0].line = p.lineno(1) + + def p_member_call_2(self, p): + 'member_call : OBJECT OPAR CPAR' + p[0]= MemberCallNode(p[1],[]) + p[0].line = p.lineno(1) + + def p_empty(self, p): + 'empty :' + p[0]=[] + + # Error rule for syntax errors + def p_error(self, p): + self.errors.append(f"({p.lineno},{self.lexer.find_column(self.data, p)}) - SyntacticError: Error near {p.value}") + + def build(self): + self.parser = yacc.yacc(module=self, write_tables=False) + + def parse(self, lexer): + self.data = lexer.data + self.lexer = lexer + self.build() + result = None + + if len(lexer.tokens_res) == 0: + self.errors.append(f"(0,0) - SyntacticError: Error near EOF") + else: + result = self.parser.parse(lexer=lexer) + return (result ,self.errors) \ No newline at end of file diff --git a/src/Readme.md b/src/Readme.md index 1200371b..cdca282e 100644 --- a/src/Readme.md +++ b/src/Readme.md @@ -1,78 +1,78 @@ -# COOL: Proyecto de Compilación - -La evaluación de la asignatura Complementos de Compilación, inscrita en el programa del 4to año de la Licenciatura en Ciencia de la Computación de la Facultad de Matemática y Computación de la -Universidad de La Habana, consiste este curso en la implementación de un compilador completamente -funcional para el lenguaje _COOL_. - -_COOL (Classroom Object-Oriented Language)_ es un pequeño lenguaje que puede ser implementado con un esfuerzo razonable en un semestre del curso. Aun así, _COOL_ mantiene muchas de las características de los lenguajes de programación modernos, incluyendo orientación a objetos, tipado estático y manejo automático de memoria. - -### Sobre el Lenguaje COOL - -Ud. podrá encontrar la especificación formal del lenguaje COOL en el documento _"COOL Language Reference Manual"_, que se distribuye junto con el presente texto. - -## Código Fuente - -### Compilando su proyecto - -Si es necesario compilar su proyecto, incluya todas las instrucciones necesarias en un archivo [`/src/makefile`](/src/makefile). -Durante la evaluación su proyecto se compilará ejecutando la siguiente secuencia: - -```bash -$ cd source -$ make clean -$ make -``` - -### Ejecutando su proyecto - -Incluya en un archivo [`/src/coolc.sh`](/src/coolc.sh) todas las instrucciones que hacen falta para lanzar su compilador. Recibirá como entrada un archivo con extensión `.cl` y debe generar como salida un archivo `.mips` cuyo nombre será el mismo que la entrada. - -Para lanzar el compilador, se ejecutará la siguiente instrucción: - -```bash -$ cd source -$ ./coolc.sh -``` - -### Sobre el Compilador de COOL - -El compilador de COOL se ejecutará como se ha definido anteriormente. -En caso de que no ocurran errores durante la operación del compilador, **coolc.sh** deberá terminar con código de salida 0, generar (o sobrescribir si ya existe) en la misma carpeta del archivo **.cl** procesado, y con el mismo nombre que éste, un archivo con extension **.mips** que pueda ser ejecutado con **spim**. Además, reportar a la salida estándar solamente lo siguiente: - - - - -En caso de que ocurran errores durante la operación del compilador, **coolc.sh** deberá terminar con código -de salida (exit code) 1 y reportar a la salida estándar (standard output stream) lo que sigue... - - - - _1 - ... - _n - -... donde `_i` tiene el siguiente formato: - - (,) - : - -Los campos `` y `` indican la ubicación del error en el fichero **.cl** procesado. En caso -de que la naturaleza del error sea tal que no pueda asociárselo a una línea y columna en el archivo de -código fuente, el valor de dichos campos debe ser 0. - -El campo `` será alguno entre: - -- `CompilerError`: se reporta al detectar alguna anomalía con la entrada del compilador. Por ejemplo, si el fichero a compilar no existe. -- `LexicographicError`: errores detectados por el lexer. -- `SyntacticError`: errores detectados por el parser. -- `NameError`: se reporta al referenciar un `identificador` en un ámbito en el que no es visible. -- `TypeError`: se reporta al detectar un problema de tipos. Incluye: - - incompatibilidad de tipos entre `rvalue` y `lvalue`, - - operación no definida entre objetos de ciertos tipos, y - - tipo referenciado pero no definido. -- `AttributeError`: se reporta cuando un atributo o método se referencia pero no está definido. -- `SemanticError`: cualquier otro error semántico. - -### Sobre la Implementación del Compilador de COOL - -El compilador debe estar implementado en `python`. Usted debe utilizar una herramienta generadora de analizadores -lexicográficos y sintácticos. Puede utilizar la que sea de su preferencia. +# COOL: Proyecto de Compilación + +La evaluación de la asignatura Complementos de Compilación, inscrita en el programa del 4to año de la Licenciatura en Ciencia de la Computación de la Facultad de Matemática y Computación de la +Universidad de La Habana, consiste este curso en la implementación de un compilador completamente +funcional para el lenguaje _COOL_. + +_COOL (Classroom Object-Oriented Language)_ es un pequeño lenguaje que puede ser implementado con un esfuerzo razonable en un semestre del curso. Aun así, _COOL_ mantiene muchas de las características de los lenguajes de programación modernos, incluyendo orientación a objetos, tipado estático y manejo automático de memoria. + +### Sobre el Lenguaje COOL + +Ud. podrá encontrar la especificación formal del lenguaje COOL en el documento _"COOL Language Reference Manual"_, que se distribuye junto con el presente texto. + +## Código Fuente + +### Compilando su proyecto + +Si es necesario compilar su proyecto, incluya todas las instrucciones necesarias en un archivo [`/src/makefile`](/src/makefile). +Durante la evaluación su proyecto se compilará ejecutando la siguiente secuencia: + +```bash +$ cd source +$ make clean +$ make +``` + +### Ejecutando su proyecto + +Incluya en un archivo [`/src/coolc.sh`](/src/coolc.sh) todas las instrucciones que hacen falta para lanzar su compilador. Recibirá como entrada un archivo con extensión `.cl` y debe generar como salida un archivo `.mips` cuyo nombre será el mismo que la entrada. + +Para lanzar el compilador, se ejecutará la siguiente instrucción: + +```bash +$ cd source +$ ./coolc.sh +``` + +### Sobre el Compilador de COOL + +El compilador de COOL se ejecutará como se ha definido anteriormente. +En caso de que no ocurran errores durante la operación del compilador, **coolc.sh** deberá terminar con código de salida 0, generar (o sobrescribir si ya existe) en la misma carpeta del archivo **.cl** procesado, y con el mismo nombre que éste, un archivo con extension **.mips** que pueda ser ejecutado con **spim**. Además, reportar a la salida estándar solamente lo siguiente: + + + + +En caso de que ocurran errores durante la operación del compilador, **coolc.sh** deberá terminar con código +de salida (exit code) 1 y reportar a la salida estándar (standard output stream) lo que sigue... + + + + _1 + ... + _n + +... donde `_i` tiene el siguiente formato: + + (,) - : + +Los campos `` y `` indican la ubicación del error en el fichero **.cl** procesado. En caso +de que la naturaleza del error sea tal que no pueda asociárselo a una línea y columna en el archivo de +código fuente, el valor de dichos campos debe ser 0. + +El campo `` será alguno entre: + +- `CompilerError`: se reporta al detectar alguna anomalía con la entrada del compilador. Por ejemplo, si el fichero a compilar no existe. +- `LexicographicError`: errores detectados por el lexer. +- `SyntacticError`: errores detectados por el parser. +- `NameError`: se reporta al referenciar un `identificador` en un ámbito en el que no es visible. +- `TypeError`: se reporta al detectar un problema de tipos. Incluye: + - incompatibilidad de tipos entre `rvalue` y `lvalue`, + - operación no definida entre objetos de ciertos tipos, y + - tipo referenciado pero no definido. +- `AttributeError`: se reporta cuando un atributo o método se referencia pero no está definido. +- `SemanticError`: cualquier otro error semántico. + +### Sobre la Implementación del Compilador de COOL + +El compilador debe estar implementado en `python`. Usted debe utilizar una herramienta generadora de analizadores +lexicográficos y sintácticos. Puede utilizar la que sea de su preferencia. diff --git a/src/cil.py b/src/cil.py new file mode 100644 index 00000000..6f04a023 --- /dev/null +++ b/src/cil.py @@ -0,0 +1,353 @@ +import visitor as visitor + + +class Node: + pass + +class ProgramNode(Node): + def __init__(self, dottypes, dotdata, dotcode): + self.dottypes = dottypes + self.dotdata = dotdata + self.dotcode = dotcode + +class TypeNode(Node): + def __init__(self, name): + self.name = name + self.attributes = [] + self.methods = [] + +class DataNode(Node): + def __init__(self, vname, value): + self.name = vname + self.value = value + +class FunctionNode(Node): + def __init__(self, fname, params, localvars, instructions, labels): + self.name = fname + self.params = params + self.localvars = localvars + self.instructions = instructions + self.labels = labels + +class ParamNode(Node): + def __init__(self, name): + self.name = name + +class LocalNode(Node): + def __init__(self, name): + self.name = name + +class InstructionNode(Node): + pass + +class AssignNode(InstructionNode): + def __init__(self, dest, source): + self.dest = dest + self.source = source + +class ArithmeticNode(InstructionNode): + def __init__(self, dest, left, right): + self.dest = dest + self.left = left + self.right = right + +class ComplementNode(InstructionNode): + def __init__(self, expression, dest): + self.expression = expression + self.dest = dest + +class PlusNode(ArithmeticNode): + pass + +class MinusNode(ArithmeticNode): + pass + +class StarNode(ArithmeticNode): + pass + +class DivNode(ArithmeticNode): + pass + +class LessNode(InstructionNode): + def __init__(self, result, left, right, labelTrue, labelEnd): + self.result = result + self.left = left + self.right =right + self.labelTrue = labelTrue + self.labelEnd = labelEnd + +class LessEqualNode(ArithmeticNode): + def __init__(self, result, left, right, labelTrue, labelEnd): + self.result = result + self.left = left + self.right =right + self.labelTrue = labelTrue + self.labelEnd = labelEnd + +class GetAttribNode(InstructionNode): + def __init__(self, ins,att,dest): + self.ins = ins + self.att = att + self.dest = dest + +class SetAttribNode(InstructionNode): + def __init__(self, ins,att, value): + self.ins = ins + self.att = att + self.value = value + +class GetIndexNode(InstructionNode): + pass + +class SetIndexNode(InstructionNode): + pass + +class AllocateNode(InstructionNode): + def __init__(self, itype, dest): + self.type = itype + self.dest = dest + +class JumpNode(InstructionNode): + def __init__(self, method, dest): + self.method = method + self.dest = dest + +class ArrayNode(InstructionNode): + pass + +class TypeOfNode(InstructionNode): + def __init__(self, obj, dest): + self.obj = obj + self.dest = dest + +class IsVoidNode(InstructionNode): + def __init__(self, obj, dest, label): + self.obj = obj + self.dest = dest + self.label = label + +class CaseOption(InstructionNode): + def __init__(self, expression, label, typex): + self.expression = expression + self.label = label + self.typex = typex + +class LabelNode(InstructionNode): + def __init__(self, name): + self.name = name + +class GotoNode(InstructionNode): + def __init__(self, name): + self.name = name + +class GotoIfNode(InstructionNode): + def __init__(self, name, condition): + self.name = name + self.condition = condition + +class StaticCallNode(InstructionNode): + def __init__(self, function, dest): + self.function = function + self.dest = dest + +class DynamicCallNode(InstructionNode): + def __init__(self, xtype, method, dest,ins): + self.type = xtype + self.method = method + self.dest = dest + self.ins = ins + +class ArgsNode(InstructionNode): + def __init__(self, names): + self.names = names + +class ReturnNode(InstructionNode): + def __init__(self, value=None): + self.value = value + +class LoadNode(InstructionNode): + def __init__(self, dest, msg, desp=0): + self.dest = dest + self.msg = msg + self.desp = desp + +class LoadAddressNode(LoadNode): + pass + +class LoadIntNode(InstructionNode): + def __init__(self, dest, msg, desp): + self.dest = dest + self.msg = msg + self.desp = desp + +class LengthNode(InstructionNode): + pass + +class ConcatNode(InstructionNode): + pass + +class PrefixNode(InstructionNode): + pass + +class SubstringNode(InstructionNode): + pass + +class StringComparer(InstructionNode): + def __init__(self, result, left, right): + self.result = result + self.left = left + self.right = right + +class ToStrNode(InstructionNode): + def __init__(self, dest, ivalue): + self.dest = dest + self.ivalue = ivalue + +class ReadNode(InstructionNode): + def __init__(self, dest): + self.dest = dest + +class PrintNode(InstructionNode): + def __init__(self, str_addr): + self.str_addr = str_addr + +def get_formatter(): + + class PrintVisitor(object): + @visitor.on('node') + def visit(self, node): + pass + + @visitor.when(ProgramNode) + def visit(self, node): + dottypes = '\n'.join(self.visit(t) for t in node.dottypes) + dotdata = '\n'.join(self.visit(t) for t in node.dotdata) + dotcode = '\n'.join(self.visit(t) for t in node.dotcode) + + return f'.TYPES\n{dottypes}\n\n.DATA\n{dotdata}\n\n.CODE\n{dotcode}' + + @visitor.when(DataNode) + def visit(self, node): + return f'{node.name}: "{node.value}"' + + @visitor.when(TypeNode) + def visit(self, node): + attributes = '\n\t'.join(f'attribute {x}' for x in node.attributes) + methods = '\n\t'.join(f'method {x}: {y}' for x,y in node.methods) + + return f'type {node.name} {{\n\t{attributes}\n\n\t{methods}\n}}' + + @visitor.when(FunctionNode) + def visit(self, node): + params = '\n\t'.join(self.visit(x) for x in node.params) + localvars = '\n\t'.join(self.visit(x) for x in node.localvars) + instructions = '\n\t'.join(self.visit(x) for x in node.instructions) + + return f'function {node.name} {{\n\t{params}\n\n\t{localvars}\n\n\t{instructions}\n}}' + + # @visitor.when(Node) + # def visit(self, node): + # return f'----------------------------------{node.__class__.__name__}' + + @visitor.when(ParamNode) + def visit(self, node): + return f'PARAM {node.name}' + + @visitor.when(LocalNode) + def visit(self, node): + return f'LOCAL {node.name}' + + @visitor.when(AssignNode) + def visit(self, node:AssignNode): + return f'{node.dest} = {node.source}' + + @visitor.when(GetAttribNode) + def visit(self, node:GetAttribNode): + return f'{node.dest} = GETATTR {node.ins} {node.att}' + + @visitor.when(SetAttribNode) + def visit(self, node:SetAttribNode): + return f'SETATTR {node.ins} {node.att} {node.value}' + + @visitor.when(PlusNode) + def visit(self, node): + return f'{node.dest} = {node.left} + {node.right}' + + @visitor.when(MinusNode) + def visit(self, node): + return f'{node.dest} = {node.left} - {node.right}' + + @visitor.when(StarNode) + def visit(self, node): + return f'{node.dest} = {node.left} * {node.right}' + + @visitor.when(DivNode) + def visit(self, node): + return f'{node.dest} = {node.left} / {node.right}' + + @visitor.when(LessEqualNode) + def visit(self, node): + return f'{node.dest} = {node.left} <= {node.right}' + + @visitor.when(LessNode) + def visit(self, node): + return f'{node.result} = {node.left} < {node.right}' + + @visitor.when(AllocateNode) + def visit(self, node): + return f'{node.dest} = ALLOCATE {node.type}' + + @visitor.when(LabelNode) + def visit(self, node): + return f'LABEL {node.name}' + + @visitor.when(JumpNode) + def visit(self, node): + return f'JUMP {node.method}' + + @visitor.when(GotoNode) + def visit(self, node): + return f'GOTO {node.name}' + + @visitor.when(GotoIfNode) + def visit(self, node): + return f'IF {node.condition} GOTO {node.name}' + + @visitor.when(TypeOfNode) + def visit(self, node): + return f'{node.dest} = TYPEOF {node.type}' + + @visitor.when(StaticCallNode) + def visit(self, node): + return f'{node.dest} = CALL {node.function}' + + @visitor.when(DynamicCallNode) + def visit(self, node): + return f'{node.dest} = VCALL {node.type} {node.method}' + + @visitor.when(ArgsNode) + def visit(self, node): + return '\n\t'.join(f'ARG {x}' for x in node.names) + + @visitor.when(ReturnNode) + def visit(self, node): + return f'RETURN {node.value if node.value is not None else ""}' + + @visitor.when(LoadNode) + def visit(self, node): + return f'LOAD {node.dest} {node.msg} {node.desp}' + + @visitor.when(LoadAddressNode) + def visit(self, node): + return f'LOAD_ADDRESS {node.dest} {node.msg} {node.desp}' + + @visitor.when(LoadIntNode) + def visit(self, node): + return f'LOAD_INT {node.dest} {node.msg} {node.desp}' + + @visitor.when(StringComparer) + def visit(self,node): + return f'STRCOMP {node.result}, {node.left}, {node.right}' + + printer = PrintVisitor() + return (lambda ast: printer.visit(ast)) \ No newline at end of file diff --git a/src/coolc.sh b/src/coolc.sh index 3088de4f..78547f55 100755 --- a/src/coolc.sh +++ b/src/coolc.sh @@ -3,9 +3,8 @@ INPUT_FILE=$1 OUTPUT_FILE=${INPUT_FILE:0: -2}mips -# Si su compilador no lo hace ya, aquí puede imprimir la información de contacto -echo "LINEA_CON_NOMBRE_Y_VERSION_DEL_COMPILADOR" # TODO: Recuerde cambiar estas -echo "Copyright (c) 2019: Nombre1, Nombre2, Nombre3" # TODO: líneas a los valores correctos +echo "Cool Compiler v0.1" +echo "Copyright (c) 2020: Juan David Menendez del Cueto, Karl Lewis Sosa" # Llamar al compilador -echo "Compiling $INPUT_FILE into $OUTPUT_FILE" +python3 exec_cool.py ${INPUT_FILE} diff --git a/src/exec_cool.py b/src/exec_cool.py new file mode 100644 index 00000000..5237f6a1 --- /dev/null +++ b/src/exec_cool.py @@ -0,0 +1,48 @@ +from lexer import CoolLexer +from Parser import CoolParser +from semantic import CoolSemantic, Scope +import cil +import sys +from COOLToCILVisitor import * +from CILtoMIPSVisitor import * +import mips + + +inputfile = sys.argv[1] +with open(inputfile, encoding="utf_8")as file: + coolprogram = file.read() +cool_lexer = CoolLexer() +errors_lexer = cool_lexer.tokenize(coolprogram) +# errors_lexer = cool_lexer.tokenize('''''') + +if len(errors_lexer) > 0: + for error in errors_lexer: + print(error) + exit(1) + +parser = CoolParser() +ast , errors_parser = parser.parse(cool_lexer) + +if len(errors_parser) > 0: + for error in errors_parser: + print(error) + exit(1) + +cool_sematic= CoolSemantic(ast) +semantics_error, context, scope = cool_sematic.check_semantics() + +if len(semantics_error) > 0: + for error in semantics_error: + print(error.text+"\n") + exit(1) + +# +cool2cil = COOLToCILVisitor(context) +f = cil.get_formatter() +c = cool2cil.visit(ast, scope) +# print(f(c)) + +cil2mips = CILtoMIPSVisitor() +d = cil2mips.visit(c) +e = mips.get_formatter() +open(sys.argv[1].split(".")[0]+".mips", 'w').write(e(d)) diff --git a/src/lexer.py b/src/lexer.py new file mode 100644 index 00000000..31f10858 --- /dev/null +++ b/src/lexer.py @@ -0,0 +1,233 @@ +import ply.lex as lex +import tokens + +class CoolLexer: + + def __init__(self): + self.tokens = tokens.tokens + self.keywords = tokens.keywords + self.errors = [] #list of errors + + #regular expression rule for comment + def t_comment(self, t): + r'--.*' + pass + + #regular expresion rule for operations + t_PLUS = r'\+' + t_MINUS = r'\-' + t_MULT = r'\*' + t_DIV = r'\/' + t_LESS = r'<' + t_LESSEQUAL = r'<=' + t_EQUAL = r'=' + t_INT_COMPLEMENT = r'~' + + #regular expresion rule for keywords + def check_keyword(self, t): + t_upper = t.value.upper() + + if t_upper in self.keywords: + t.type = t_upper + + #regular expresion rule for bool + def t_BOOL(self, t): + r't[Rr][Uu][Ee]|f[Aa][Ll][Ss][Ee]' + + t.value = True if t.value.upper() == "TRUE" else False + return t + + #regular expresion rule for integer + def t_INTEGER(self, t): + r'[0-9]+' + t.value = int(t.value) + return t + + #regular expresion rule for type + def t_TYPE(self, t): + r'[A-Z][0-9A-Za-z_]*' + self.check_keyword(t) + return t + + #regular expresion rule for object + def t_OBJECT(self, t): + r'[a-z][0-9A-Za-z_]*' + self.check_keyword(t) + return t + + #regular expresion rule for special + t_OCUR = r'{' + t_CCUR = r'}' + t_OPAR = r'\(' + t_CPAR = r'\)' + t_DOT = r'\.' + t_SEMI = r';' + t_COLON = r':' + t_COMMA = r',' + t_AT = '@' + t_LARROW = r'<-' + t_RARROW = r'=>' + + #others regular expresions + def t_newline(self, t): + r'\n+' + t.lexer.lineno += len(t.value) + pass + + def t_skip(self, t): + r'[ \t\r\f]+' + pass + + #regular expresion rules for string + states = ( + ("STRING", "exclusive"), + ) + + def t_start_string(self, t): + r"\"" + t.lexer.push_state("STRING") + t.lexer.string_backslashed = False + t.lexer.stringbuf = "" + + def t_STRING_newline(self, t): + r"\n" + t.lexer.lineno += 1 + if not t.lexer.string_backslashed: + self.errors.append("({0},{1}) - LexicographicError: 'Unterminated string constant'".format(t.lineno, self.find_column(self.data, t))) + t.lexer.pop_state() + else: + t.lexer.string_backslashed = False + + def t_STRING_null(self, t): + r"\0" + self.errors.append("({0},{1}) - LexicographicError: 'Null character in string'".format(t.lineno, self.find_column(self.data, t))) + t.lexer.skip(1) + + def t_STRING_end(self, t): + r"\"" + if not t.lexer.string_backslashed: + t.lexer.pop_state() + t.value = t.lexer.stringbuf + t.type = "STRING" + return t + else: + t.lexer.stringbuf += '"' + t.lexer.string_backslashed = False + + def t_STRING_anything(self, t): + r"[^\n]" + if t.lexer.string_backslashed: + if t.value == 'b': + t.lexer.stringbuf += '\b' + elif t.value == 't': + t.lexer.stringbuf += '\t' + elif t.value == 'n': + t.lexer.stringbuf += '\n' + elif t.value == 'f': + t.lexer.stringbuf += '\f' + elif t.value == '\\': + t.lexer.stringbuf += '\\' + else: + t.lexer.stringbuf += t.value + t.lexer.string_backslashed = False + else: + if t.value != '\\': + t.lexer.stringbuf += t.value + else: + t.lexer.string_backslashed = True + + # STRING ignored characters + t_STRING_ignore = '' + + # STRING error handler + def t_STRING_error(self, t): + self.errors.append("({0},{1}) - LexicographicError: 'ERROR at or near {2}'".format(t.lineno, self.find_column(self.data, t), t.value[:10])) + t.lexer.skip(1) + + def t_STRING_eof(self, t): + self.errors.append("({0},{1}) - LexicographicError: 'EOF in string'".format(t.lineno, self.find_column(self.data, t))) + t.lexer.pop_state() + + + #regular expresion rule for error + def t_error(self, t): + self.errors.append("({0},{1}) - LexicographicError: ' UNKNOW character {2}'".format(t.lineno, self.find_column(self.data, t), t.value[0])) + t.lexer.skip(1) + pass + + + #build the lexer + def build(self,**kwargs): + self.lexer = lex.lex(module=self, **kwargs) + + # Test it output + def input(self,data): + self.build() + + self.data = data + self.lexer.input(self.pre_proc(data)) + + def tokenize(self, data): + self.input(data) + + self.tokens_res = [] + while True: + tok = self.lexer.token() + if not tok: + break + self.tokens_res.append(tok) + # print(tok) + + self.idx = 0 + return self.errors + + def token(self): + if self.idx >= len(self.tokens_res): + return None + + self.idx += 1 + return self.tokens_res[self.idx-1] + + #### utils + def find_column(self, text, token): + line_start = text.rfind('\n', 0, token.lexpos) + 1 + return (token.lexpos - line_start) + 1 + + def pre_proc(self, text): + text_ret = '' + comments = 0 + idx = 0 + + lineno = 1 + last_nidx = 0 + + while idx < len(text): + if text[idx] == '\n': + lineno = lineno + 1 + last_nidx = idx + + if text[idx] == '(' and idx + 1 < len(text) and text[idx+1] == '*': + comments = comments+1 + idx = idx + 1 + text_ret = text_ret + ' ' + + elif text[idx] == '*' and idx+1 < len(text) and text[idx+1] == ')' and comments > 0: + comments = comments - 1 + idx = idx + 1 + text_ret = text_ret + ' ' + + elif comments == 0: + text_ret = text_ret + text[idx] + + else: + if text[idx] == '\n': + text_ret = text_ret + '\n' + else: + text_ret = text_ret + ' ' + + idx = idx +1 + + if comments > 0: + self.errors.append("({0},{1}) - LexicographicError: 'EOF in comment'".format(lineno, idx - last_nidx)) + + return text_ret \ No newline at end of file diff --git a/src/makefile b/src/makefile index 30df993f..04aa1cf4 100644 --- a/src/makefile +++ b/src/makefile @@ -10,3 +10,4 @@ clean: test: pytest ../tests -v --tb=short -m=${TAG} + diff --git a/src/mips.py b/src/mips.py new file mode 100644 index 00000000..601f61d8 --- /dev/null +++ b/src/mips.py @@ -0,0 +1,697 @@ +import visitor + +class MipsNode: + pass + +class MipsProgramNode(MipsNode): + def __init__(self, dotdata, dotcode): + self.dotdata = dotdata + self.dotcode = dotcode + +# string +class MipsStringNode(MipsNode): + def __init__(self, name, value): + self.name = name + self.value = value + +class MipsWordNode(MipsNode): + def __init__(self, name, value): + self.name = name + self.value = value + +class MipsTableNode(MipsNode): + def __init__(self , type_name,methods): + self.type_name = type_name + self.methods = methods + +# jumps +class MipsJumpNode(MipsNode): + def __init__(self, label): + self.label = label + +class MipsJumpAtAddressNode(MipsJumpNode): + pass + +class MipsJRNode(MipsJumpNode): + pass + +class MipsJALRNode(MipsJumpNode): + pass + + +# stack +class MipsLWNode(MipsNode): + def __init__(self, dest, src): + self.src = src + self.dest = dest + +class MipsLINode(MipsNode): + def __init__(self, dest, src): + self.src = src + self.dest = dest + +class MipsLANode(MipsNode): + def __init__(self, dest, src): + self.src = src + self.dest = dest + +class MipsSWNode(MipsNode): + def __init__(self, src, dest): + self.src = src + self.dest = dest + + +# syscall +class MipsSyscallNode(MipsNode): + pass + + +# move +class MipsMoveNode(MipsNode): + def __init__(self, dest, src): + self.src = src + self.dest = dest + + +# arithmetic +class MipsArithmeticNode: + def __init__(self, param1, param2, param3): + self.param1 = param1 + self.param2 = param2 + self.param3 = param3 + +class MipsAddNode(MipsArithmeticNode): + pass + +class MipsAddiuNode(MipsArithmeticNode): + pass + +class MipsMinusNode(MipsArithmeticNode): + pass + +class MipsStarNode(MipsArithmeticNode): + pass + +class MipsDivNode(MipsArithmeticNode): + pass + +class MipsNEGNode(MipsNode): + def __init__(self, dest, src): + self.dest = dest + self.src = src + +# comp +class MipsComparerNode(MipsNode): + def __init__(self, param1, param2, label): + self.param1 = param1 + self.param2 = param2 + self.label = label + +class MipsBEQNode(MipsComparerNode): + pass + +class MipsBNENode(MipsComparerNode): + pass + +class MipsBLTNode(MipsComparerNode): + pass + +class MipsBLENode(MipsComparerNode): + pass + + + +# label +class MipsLabelNode(MipsNode): + def __init__(self, name): + self.name = name + +class MipsCommentNode(MipsNode): + def __init__(self, comment): + self.comment = '\n #' + comment + '\n' + +def get_formatter(): + + class PrintVisitor(object): + @visitor.on('node') + def visit(self, node): + pass + + @visitor.when(MipsProgramNode) + def visit(self, node): + dotdata = '\n'.join(self.visit(t) for t in node.dotdata) + dotdata += ''' + _error1: .asciiz "Abort called from class " + _salto_para_abort: .asciiz "\n" + _buffer: .space 2048 + _void: .asciiz "" + ''' + + dotcode = '\n'.join(self.visit(t) for t in node.dotcode) + dotcode += '''\n + +function_Ctr_at_Object: + move $fp, $sp + sw $ra, 0($sp) + addiu $sp, $sp, -4 + + la $t1, Object_name + lw $t2, 4($fp) + sw $t1, 0($t2) + + lw $t1, Object_size + lw $t2, 4($fp) + sw $t1, 4($t2) + + la $t1, __virtual_table__Object + lw $t2, 4($fp) + sw $t1, 8($t2) + + + lw $a0, 4($fp) + + lw $ra, 0($fp) + addiu $sp, $sp, 12 + lw $fp, 0($sp) + jr $ra + +function_Init_at_Object: + move $fp, $sp + sw $ra, 0($sp) + addiu $sp, $sp, -4 + + lw $a0, 4($fp) + + lw $ra, 0($fp) + addiu $sp, $sp, 12 + lw $fp, 0($sp) + jr $ra + +function_Ctr_at_IO: + move $fp, $sp + sw $ra, 0($sp) + addiu $sp, $sp, -4 + + la $t1, IO_name + lw $t2, 4($fp) + sw $t1, 0($t2) + + lw $t1, IO_size + lw $t2, 4($fp) + sw $t1, 4($t2) + + la $t1, __virtual_table__IO + lw $t2, 4($fp) + sw $t1, 8($t2) + + + lw $a0, 4($fp) + + lw $ra, 0($fp) + addiu $sp, $sp, 12 + lw $fp, 0($sp) + jr $ra + +function_Init_at_IO: + move $fp, $sp + sw $ra, 0($sp) + addiu $sp, $sp, -4 + + lw $a0, 4($fp) + + lw $ra, 0($fp) + addiu $sp, $sp, 12 + lw $fp, 0($sp) + jr $ra + +function_type_name_at_Object: + move $fp, $sp + sw $ra, 0($sp) + addiu $sp, $sp, -4 + lw $a0, 4($fp) + lw $a0 0($a0) + lw $ra, 0($fp) + addiu $sp, $sp, 12 + lw $fp, 0($sp) + jr $ra + +function_abort_at_Object: + move $fp, $sp + sw $ra, 0($sp) + addiu $sp, $sp, -4 + la $a0, _error1 + li $v0, 4 + syscall + lw $a0 4($fp) + lw $a0 ($a0) + li $v0, 4 + syscall + la $a0, _salto_para_abort + li $v0, 4 + syscall + li $v0, 10 + syscall + +function_copy_at_Object: + move $fp, $sp + sw $ra, 0($sp) + addiu $sp, $sp, -4 + + lw $a0, 4($fp) + lw $a0, 0($a0) + la $t0, Int_name + + bne $a0, $t0, not_int + lw $a0, 4($fp) + lw $a0, 8($a0) + j end + + not_int: + lw $a0, 4($fp) + lw $a0, 0($a0) + la $t0, Bool_name + + bne $a0, $t0, not_bool + lw $a0, 4($fp) + lw $a0, 8($a0) + j end + + not_bool: + lw $a0, 4($fp) + lw $a0, 0($a0) + la $t0, String_name + + bne $a0, $t0, not_string + lw $a0, 4($fp) + lw $a0, 8($a0) + j end + + not_string: + lw $a0, 4($fp) + move $t2, $a0 + lw $a0, 4($a0) + move $t1, $a0 + + li $v0, 9 + syscall + + move $a0, $v0 + + copy: + lw $t0, 0($t2) + sw $t0, 0($a0) + addiu $a0, $a0, 4 + addiu $t2, $t2, 4 + addiu $t1, $t1, -4 + bne $t1, $zero, copy + + move $a0, $v0 + + end: + lw $ra, 0($fp) + addiu $sp, $sp, 12 + lw $fp, 0($sp) + jr $ra + +function_length_at_String: + move $fp, $sp + sw $ra, 0($sp) + addiu $sp, $sp, -4 + + lw $s0, 4($fp) + li $a0, -1 + + length: + lb $t0, ($s0) + addiu $a0, $a0, 1 + addiu $s0, $s0, 1 + bne $t0, $zero, length + + lw $ra, 0($fp) + addiu $sp, $sp, 12 + lw $fp, 0($sp) + jr $ra + +function_concat_at_String: + move $fp, $sp + sw $ra, 0($sp) + addiu $sp, $sp, -4 + + lw $s1, 4($fp) + sw $fp, 0($sp) + addiu $sp, $sp, -4 + sw $s1, 0($sp) + addiu $sp, $sp, -4 + jal function_length_at_String + + sw $a0, 0($sp) + addiu $sp, $sp, -4 + + lw $s1, 8($fp) + sw $fp, 0($sp) + addiu $sp, $sp, -4 + sw $s1, 0($sp) + addiu $sp, $sp, -4 + jal function_length_at_String + + lw $t7, 4($sp) + addiu $sp, $sp, 4 + + move $t6, $a0 + add $a0, $t7, $t6 + addiu $a0, $a0, 1 + + li $v0, 9 + syscall + + move $t0, $v0 + + lw $s1, 4($fp) + copy_self: + lb $t5, ($s1) + beq $t5, $zero, end_copy_self + sb $t5, ($t0) + addiu $s1, $s1, 1 + addiu $t0, $t0, 1 + j copy_self + + end_copy_self: + lw $s1, 8($fp) + + copy_given: + lb $t5, ($s1) + sb $t5, ($t0) + addiu $s1, $s1, 1 + addiu $t0, $t0, 1 + bne $t5, $zero, copy_given + + + move $a0, $v0 + + lw $ra, 0($fp) + addiu $sp, $sp, 16 + lw $fp, 0($sp) + jr $ra + + +function_substr_at_String: + move $fp, $sp + sw $ra, 0($sp) + addiu $sp, $sp, -4 + + lw $a0, 12($fp) + addiu $a0, $a0, 1 + li $v0, 9 + syscall + + move $t0, $v0 + + lw $s1, 4($fp) + lw $t1, 8($fp) + add $s1, $s1, $t1 + + lw $t1, 12($fp) + + substr: + lb $t5, ($s1) + sb $t5, ($t0) + addiu $s1, $s1, 1 + addiu $t0, $t0, 1 + addiu $t1, $t1, -1 + bne $t1, $zero, substr + + sb $zero, ($t0) + move $a0, $v0 + + lw $ra, 0($fp) + addiu $sp, $sp, 20 + lw $fp, 0($sp) + jr $ra + + +function_comparer_string: + move $fp, $sp + sw $ra, 0($sp) + addiu $sp, $sp, -4 + + lw $s1, 4($fp) + sw $fp, 0($sp) + addiu $sp, $sp, -4 + sw $s1, 0($sp) + addiu $sp, $sp, -4 + jal function_length_at_String + + sw $a0, 0($sp) + addiu $sp, $sp, -4 + + lw $s1, 8($fp) + sw $fp, 0($sp) + addiu $sp, $sp, -4 + sw $s1, 0($sp) + addiu $sp, $sp, -4 + jal function_length_at_String + + lw $t7, 4($sp) + addiu $sp, $sp, 4 + + bne $t7, $a0, not_equals_strings + + lw $t7, 4($fp) + lw $a0, 8($fp) + + equal_chart: + lb $t1, ($t7) + lb $t2, ($a0) + addiu $t7, $t7, 1 + addiu $a0, $a0, 1 + bne $t1, $t2, not_equals_strings + beq $t1, $zero, equals_strings + j equal_chart + + not_equals_strings: + li $a0, 0 + j end_equal_string + + equals_strings: + li $a0, 1 + + end_equal_string: + lw $ra, 0($fp) + addiu $sp, $sp, 16 + lw $fp, 0($sp) + jr $ra + + + +function_out_string_at_IO: + move $fp, $sp + sw $ra, 0($sp) + addiu $sp, $sp, -4 + + lw $a0, 8($fp) + li $v0, 4 + syscall + + lw $a0, 4($fp) + + lw $ra, 0($fp) + addiu $sp, $sp, 16 + lw $fp, 0($sp) + jr $ra + +function_out_int_at_IO: + move $fp, $sp + sw $ra, 0($sp) + addiu $sp, $sp, -4 + + lw $a0, 8($fp) + li $v0, 1 + syscall + + lw $a0, 4($fp) + + lw $ra, 0($fp) + addiu $sp, $sp, 16 + lw $fp, 0($sp) + jr $ra + +function_in_int_at_IO: + move $fp, $sp + sw $ra, 0($sp) + addiu $sp, $sp, -4 + + lw $a0, 4($fp) + li $v0, 5 + syscall + + move $a0, $v0 + + lw $ra, 0($fp) + addiu $sp, $sp, 12 + lw $fp, 0($sp) + jr $ra + +function_in_string_at_IO: + move $fp, $sp + sw $ra, 0($sp) + addiu $sp, $sp, -4 + + la $a0, _buffer + li $a1, 1024 + + li $v0, 8 + syscall + + sw $fp, 0($sp) + addiu $sp, $sp, -4 + sw $a0, 0($sp) + addiu $sp, $sp, -4 + jal function_length_at_String + + addiu $a0, $a0, 1 + li $v0, 9 + syscall + + move $t0, $v0 + la $a0, _buffer + + IO_copy: + lb $t1, ($a0) + sb $t1, ($t0) + addiu $a0, $a0, 1 + addiu $t0, $t0, 1 + bne $t1, $zero, IO_copy + + addiu $t0, $t0, -2 + li $t1 10 + lb $t2, ($t0) + bne $t1 , $t2 not_slash + sb $zero, ($t0) + + not_slash: + move $a0, $v0 + + lw $ra, 0($fp) + addiu $sp, $sp, 12 + lw $fp, 0($sp) + jr $ra + + ''' + + return f'.data\n{dotdata}\n\n.text\n{dotcode}' + + @visitor.when(MipsStringNode) + def visit(self, node): + return f'\t\t\t{node.name}: .asciiz "{node.value}"' + + @visitor.when(MipsWordNode) + def visit(self, node): + return f'\t\t\t{node.name}: .word {node.value}' + + @visitor.when(MipsTableNode) + def visit(self , node:MipsTableNode): + return f'__virtual_table__{node.type_name}:\n' + '\n'.join( f"\t\t\t .word {m}" for m in node.methods) + + # jumps + @visitor.when(MipsJumpNode) + def visit(self, node): + return f'\t\t\tj {node.label}' + + @visitor.when(MipsJumpAtAddressNode) + def visit(self, node): + return f'\t\t\tjal {node.label}' + + @visitor.when(MipsJRNode) + def visit(self, node): + return f'\t\t\tjr {node.label}' + + @visitor.when(MipsJALRNode) + def visit(self, node): + return f'\t\t\tjalr {node.label}' + + # stack + @visitor.when(MipsLWNode) + def visit(self, node): + return f'\t\t\tlw {node.dest}, {node.src}' + + @visitor.when(MipsLINode) + def visit(self, node): + return f'\t\t\tli {node.dest}, {node.src}' + + @visitor.when(MipsLANode) + def visit(self, node): + return f'\t\t\tla {node.dest}, {node.src}' + + @visitor.when(MipsSWNode) + def visit(self, node): + return f'\t\t\tsw {node.src}, {node.dest}' + + + # syscall + @visitor.when(MipsSyscallNode) + def visit(self, node): + return '\t\t\tsyscall' + + + # move + @visitor.when(MipsMoveNode) + def visit(self, node): + return f'\t\t\tmove {node.dest}, {node.src}' + + + + # arithmetic + @visitor.when(MipsAddNode) + def visit(self, node): + return f'\t\t\tadd {node.param1}, {node.param2}, {node.param3}' + + @visitor.when(MipsAddiuNode) + def visit(self, node): + return f'\t\t\taddiu {node.param1}, {node.param2}, {node.param3}' + + @visitor.when(MipsMinusNode) + def visit(self, node): + return f'\t\t\tsub {node.param1}, {node.param2}, {node.param3}' + + @visitor.when(MipsStarNode) + def visit(self, node): + return f'\t\t\tmul {node.param1} {node.param2} {node.param3}' + + @visitor.when(MipsNEGNode) + def visit(self, node): + return f'\t\t\tneg {node.dest}, {node.src}' + + @visitor.when(MipsDivNode) + def visit(self, node): + return f'\t\t\tdiv {node.param1}, {node.param2}, {node.param3}' + + # comp + @visitor.when(MipsBNENode) + def visit(self, node): + return f'\t\t\tbne {node.param1}, {node.param2}, {node.label}' + + @visitor.when(MipsBEQNode) + def visit(self, node): + return f'\t\t\tbeq {node.param1}, {node.param2}, {node.label}' + + @visitor.when(MipsBLTNode) + def visit(self, node): + return f'\t\t\tblt {node.param1}, {node.param2}, {node.label}' + + @visitor.when(MipsBLENode) + def visit(self, node): + return f'\t\t\tble {node.param1}, {node.param2}, {node.label}' + + + + # label + @visitor.when(MipsLabelNode) + def visit(self, node): + return f'{node.name}:' + + @visitor.when(MipsCommentNode) + def visit(self, node): + return node.comment + + printer = PrintVisitor() + return (lambda ast: printer.visit(ast)) \ No newline at end of file diff --git a/src/parsetab.py b/src/parsetab.py new file mode 100644 index 00000000..4c66eb53 --- /dev/null +++ b/src/parsetab.py @@ -0,0 +1,92 @@ + +# parsetab.py +# This file is automatically generated. Do not edit. +# pylint: disable=W,C,R +_tabversion = '3.10' + +_lr_method = 'LALR' + +_lr_signature = 'AT BOOL CASE CCUR CLASS COLON COMMA CPAR DIV DOT ELSE EQUAL ESAC FI IF IN INHERITS INTEGER INT_COMPLEMENT ISVOID LARROW LESS LESSEQUAL LET LOOP MINUS MULT NEW NOT OBJECT OCUR OF OPAR PLUS POOL RARROW SEMI STRING THEN TYPE WHILEprogram : class_listclass_list : def_class class_listclass_list : def_classdef_class : CLASS TYPE OCUR feature_list CCUR SEMIdef_class : CLASS TYPE INHERITS TYPE OCUR feature_list CCUR SEMIfeature_list : feature feature_listfeature_list : emptyfeature : OBJECT COLON TYPE SEMIfeature : OBJECT COLON LARROW expr SEMIfeature : OBJECT OPAR param_list CPAR COLON TYPE OCUR expr CCUR SEMIfeature : OBJECT CPAR COLON TYPE OCUR expr CCUR SEMIparam_list : paramparam_list : param COMMA param_listparam : OBJECT COLON TYPEexpr_list : expr SEMIexpr_list : expr SEMI expr_listlet_list : OBJECT COLON TYPElet_list : OBJECT COLON TYPE LARROW exprlet_list : OBJECT COLON TYPE COMMA let_listlet_list : OBJECT COLON TYPE LARROW expr COMMA let_listcase_list : OBJECT COLON TYPE RARROW expr SEMIcase_list : OBJECT COLON TYPE RARROW expr SEMI case_listexpr : comp_exprcomp_expr : comp_expr LESSEQUAL arithcomp_expr : comp_expr LESS arithcomp_expr : comp_expr EQUAL arithcomp_expr : aritharith2 : arith2 PLUS termarith2 : arith2 MINUS termarith2 : termarith : NOT arith2arith : arith2term : term MULT factorterm : term DIV factorterm : factorfactor : ISVOID factor2factor : factor2factor2 : INT_COMPLEMENT atomfactor2 : atomatom : IF expr THEN expr ELSE expr FIatom : WHILE expr LOOP expr POOLatom : LET let_list OBJECT IN expratom : CASE expr OF case_list ESAC atom : OBJECT LARROW expratom : atom func_callatom : member_callatom : NEW TYPEatom : OPAR expr CPARatom : OBJECTatom : INTEGERatom : STRINGatom : BOOLatom : OCUR expr_list CCURfunc_call : DOT OBJECT OPAR arg_list CPARfunc_call : DOT OBJECT OPAR CPARfunc_call : AT TYPE DOT OBJECT OPAR arg_list CPARfunc_call : AT TYPE DOT OBJECT OPAR CPARarg_list : exprarg_list : expr COMMA arg_listmember_call : OBJECT OPAR arg_list CPARmember_call : OBJECT OPAR CPARempty :' + +_lr_action_items = {'CLASS':([0,3,20,86,],[4,4,-4,-5,]),'$end':([1,2,3,5,20,86,],[0,-1,-3,-2,-4,-5,]),'TYPE':([4,8,16,26,46,52,72,83,103,133,],[6,13,21,55,78,82,99,108,117,141,]),'OCUR':([6,13,22,33,37,39,41,42,44,47,51,55,57,58,60,61,62,64,65,66,67,85,100,101,107,108,111,112,116,121,127,130,137,147,],[7,19,51,51,51,51,51,51,51,51,51,85,51,51,51,51,51,51,51,51,51,51,51,51,51,121,51,51,51,51,51,51,51,51,]),'INHERITS':([6,],[8,]),'OBJECT':([7,10,17,19,22,28,29,31,32,33,34,35,36,37,38,39,40,41,42,43,44,45,47,48,49,50,51,54,57,58,59,60,61,62,63,64,65,66,67,68,69,70,71,75,78,85,87,89,91,92,93,94,95,96,97,100,101,104,105,106,107,110,111,112,113,116,117,121,125,127,128,129,130,131,132,135,136,137,139,140,144,145,146,147,148,149,150,152,],[12,12,23,12,29,-8,-49,-23,-27,29,-32,-30,-35,29,-37,29,-39,29,29,76,29,-46,29,-50,-51,-52,29,23,29,29,-9,29,29,29,-31,29,29,29,29,-36,-38,-45,98,102,-47,29,-44,-61,-24,-25,-26,-28,-29,-33,-34,29,29,119,-48,-53,29,-60,29,29,126,29,-17,29,-55,29,-41,-42,29,76,-43,-11,-54,29,-18,-19,-57,-40,76,29,-10,-56,-20,119,]),'CCUR':([7,9,10,11,15,19,27,28,29,31,32,34,35,36,38,40,45,48,49,50,59,63,68,69,70,78,80,87,89,91,92,93,94,95,96,97,105,106,107,109,110,120,125,128,129,132,134,135,136,144,145,148,149,],[-62,14,-62,-7,-6,-62,56,-8,-49,-23,-27,-32,-30,-35,-37,-39,-46,-50,-51,-52,-9,-31,-36,-38,-45,-47,106,-44,-61,-24,-25,-26,-28,-29,-33,-34,-48,-53,-15,122,-60,-16,-55,-41,-42,-43,142,-11,-54,-57,-40,-10,-56,]),'COLON':([12,18,23,53,76,119,],[16,26,52,83,103,133,]),'OPAR':([12,22,29,33,37,39,41,42,44,47,51,57,58,60,61,62,64,65,66,67,85,98,100,101,107,111,112,116,121,126,127,130,137,147,],[17,47,58,47,47,47,47,47,47,47,47,47,47,47,47,47,47,47,47,47,47,112,47,47,47,47,47,47,47,137,47,47,47,47,]),'CPAR':([12,24,25,29,31,32,34,35,36,38,40,45,48,49,50,58,63,68,69,70,78,79,82,84,87,88,89,90,91,92,93,94,95,96,97,105,106,110,112,123,124,125,128,129,132,136,137,143,144,145,149,],[18,53,-12,-49,-23,-27,-32,-30,-35,-37,-39,-46,-50,-51,-52,89,-31,-36,-38,-45,-47,105,-14,-13,-44,110,-61,-58,-24,-25,-26,-28,-29,-33,-34,-48,-53,-60,125,-59,136,-55,-41,-42,-43,-54,144,149,-57,-40,-56,]),'SEMI':([14,21,29,30,31,32,34,35,36,38,40,45,48,49,50,56,63,68,69,70,78,81,87,89,91,92,93,94,95,96,97,105,106,110,122,125,128,129,132,136,142,144,145,149,151,],[20,28,-49,59,-23,-27,-32,-30,-35,-37,-39,-46,-50,-51,-52,86,-31,-36,-38,-45,-47,107,-44,-61,-24,-25,-26,-28,-29,-33,-34,-48,-53,-60,135,-55,-41,-42,-43,-54,148,-57,-40,-56,152,]),'LARROW':([16,29,117,],[22,57,130,]),'NOT':([22,41,42,44,47,51,57,58,60,61,62,85,100,101,107,111,112,116,121,127,130,137,147,],[33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,]),'ISVOID':([22,33,41,42,44,47,51,57,58,60,61,62,64,65,66,67,85,100,101,107,111,112,116,121,127,130,137,147,],[37,37,37,37,37,37,37,37,37,37,37,37,37,37,37,37,37,37,37,37,37,37,37,37,37,37,37,37,]),'INT_COMPLEMENT':([22,33,37,41,42,44,47,51,57,58,60,61,62,64,65,66,67,85,100,101,107,111,112,116,121,127,130,137,147,],[39,39,39,39,39,39,39,39,39,39,39,39,39,39,39,39,39,39,39,39,39,39,39,39,39,39,39,39,39,]),'IF':([22,33,37,39,41,42,44,47,51,57,58,60,61,62,64,65,66,67,85,100,101,107,111,112,116,121,127,130,137,147,],[41,41,41,41,41,41,41,41,41,41,41,41,41,41,41,41,41,41,41,41,41,41,41,41,41,41,41,41,41,41,]),'WHILE':([22,33,37,39,41,42,44,47,51,57,58,60,61,62,64,65,66,67,85,100,101,107,111,112,116,121,127,130,137,147,],[42,42,42,42,42,42,42,42,42,42,42,42,42,42,42,42,42,42,42,42,42,42,42,42,42,42,42,42,42,42,]),'LET':([22,33,37,39,41,42,44,47,51,57,58,60,61,62,64,65,66,67,85,100,101,107,111,112,116,121,127,130,137,147,],[43,43,43,43,43,43,43,43,43,43,43,43,43,43,43,43,43,43,43,43,43,43,43,43,43,43,43,43,43,43,]),'CASE':([22,33,37,39,41,42,44,47,51,57,58,60,61,62,64,65,66,67,85,100,101,107,111,112,116,121,127,130,137,147,],[44,44,44,44,44,44,44,44,44,44,44,44,44,44,44,44,44,44,44,44,44,44,44,44,44,44,44,44,44,44,]),'NEW':([22,33,37,39,41,42,44,47,51,57,58,60,61,62,64,65,66,67,85,100,101,107,111,112,116,121,127,130,137,147,],[46,46,46,46,46,46,46,46,46,46,46,46,46,46,46,46,46,46,46,46,46,46,46,46,46,46,46,46,46,46,]),'INTEGER':([22,33,37,39,41,42,44,47,51,57,58,60,61,62,64,65,66,67,85,100,101,107,111,112,116,121,127,130,137,147,],[48,48,48,48,48,48,48,48,48,48,48,48,48,48,48,48,48,48,48,48,48,48,48,48,48,48,48,48,48,48,]),'STRING':([22,33,37,39,41,42,44,47,51,57,58,60,61,62,64,65,66,67,85,100,101,107,111,112,116,121,127,130,137,147,],[49,49,49,49,49,49,49,49,49,49,49,49,49,49,49,49,49,49,49,49,49,49,49,49,49,49,49,49,49,49,]),'BOOL':([22,33,37,39,41,42,44,47,51,57,58,60,61,62,64,65,66,67,85,100,101,107,111,112,116,121,127,130,137,147,],[50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,]),'COMMA':([25,29,31,32,34,35,36,38,40,45,48,49,50,63,68,69,70,78,82,87,89,90,91,92,93,94,95,96,97,105,106,110,117,125,128,129,132,136,139,144,145,149,],[54,-49,-23,-27,-32,-30,-35,-37,-39,-46,-50,-51,-52,-31,-36,-38,-45,-47,-14,-44,-61,111,-24,-25,-26,-28,-29,-33,-34,-48,-53,-60,131,-55,-41,-42,-43,-54,146,-57,-40,-56,]),'DOT':([29,31,32,34,35,36,38,40,45,48,49,50,63,68,69,70,78,87,89,91,92,93,94,95,96,97,99,105,106,110,125,128,129,132,136,144,145,149,],[-49,-23,-27,-32,-30,-35,-37,71,-46,-50,-51,-52,-31,-36,71,-45,-47,-44,-61,-24,-25,-26,-28,-29,-33,-34,113,-48,-53,-60,-55,-41,-42,-43,-54,-57,-40,-56,]),'AT':([29,31,32,34,35,36,38,40,45,48,49,50,63,68,69,70,78,87,89,91,92,93,94,95,96,97,105,106,110,125,128,129,132,136,144,145,149,],[-49,-23,-27,-32,-30,-35,-37,72,-46,-50,-51,-52,-31,-36,72,-45,-47,-44,-61,-24,-25,-26,-28,-29,-33,-34,-48,-53,-60,-55,-41,-42,-43,-54,-57,-40,-56,]),'MULT':([29,31,32,34,35,36,38,40,45,48,49,50,63,68,69,70,78,87,89,91,92,93,94,95,96,97,105,106,110,125,128,129,132,136,144,145,149,],[-49,-23,-27,-32,66,-35,-37,-39,-46,-50,-51,-52,-31,-36,-38,-45,-47,-44,-61,-24,-25,-26,66,66,-33,-34,-48,-53,-60,-55,-41,-42,-43,-54,-57,-40,-56,]),'DIV':([29,31,32,34,35,36,38,40,45,48,49,50,63,68,69,70,78,87,89,91,92,93,94,95,96,97,105,106,110,125,128,129,132,136,144,145,149,],[-49,-23,-27,-32,67,-35,-37,-39,-46,-50,-51,-52,-31,-36,-38,-45,-47,-44,-61,-24,-25,-26,67,67,-33,-34,-48,-53,-60,-55,-41,-42,-43,-54,-57,-40,-56,]),'PLUS':([29,31,32,34,35,36,38,40,45,48,49,50,63,68,69,70,78,87,89,91,92,93,94,95,96,97,105,106,110,125,128,129,132,136,144,145,149,],[-49,-23,-27,64,-30,-35,-37,-39,-46,-50,-51,-52,64,-36,-38,-45,-47,-44,-61,-24,-25,-26,-28,-29,-33,-34,-48,-53,-60,-55,-41,-42,-43,-54,-57,-40,-56,]),'MINUS':([29,31,32,34,35,36,38,40,45,48,49,50,63,68,69,70,78,87,89,91,92,93,94,95,96,97,105,106,110,125,128,129,132,136,144,145,149,],[-49,-23,-27,65,-30,-35,-37,-39,-46,-50,-51,-52,65,-36,-38,-45,-47,-44,-61,-24,-25,-26,-28,-29,-33,-34,-48,-53,-60,-55,-41,-42,-43,-54,-57,-40,-56,]),'LESSEQUAL':([29,31,32,34,35,36,38,40,45,48,49,50,63,68,69,70,78,87,89,91,92,93,94,95,96,97,105,106,110,125,128,129,132,136,144,145,149,],[-49,60,-27,-32,-30,-35,-37,-39,-46,-50,-51,-52,-31,-36,-38,-45,-47,-44,-61,-24,-25,-26,-28,-29,-33,-34,-48,-53,-60,-55,-41,-42,-43,-54,-57,-40,-56,]),'LESS':([29,31,32,34,35,36,38,40,45,48,49,50,63,68,69,70,78,87,89,91,92,93,94,95,96,97,105,106,110,125,128,129,132,136,144,145,149,],[-49,61,-27,-32,-30,-35,-37,-39,-46,-50,-51,-52,-31,-36,-38,-45,-47,-44,-61,-24,-25,-26,-28,-29,-33,-34,-48,-53,-60,-55,-41,-42,-43,-54,-57,-40,-56,]),'EQUAL':([29,31,32,34,35,36,38,40,45,48,49,50,63,68,69,70,78,87,89,91,92,93,94,95,96,97,105,106,110,125,128,129,132,136,144,145,149,],[-49,62,-27,-32,-30,-35,-37,-39,-46,-50,-51,-52,-31,-36,-38,-45,-47,-44,-61,-24,-25,-26,-28,-29,-33,-34,-48,-53,-60,-55,-41,-42,-43,-54,-57,-40,-56,]),'THEN':([29,31,32,34,35,36,38,40,45,48,49,50,63,68,69,70,73,78,87,89,91,92,93,94,95,96,97,105,106,110,125,128,129,132,136,144,145,149,],[-49,-23,-27,-32,-30,-35,-37,-39,-46,-50,-51,-52,-31,-36,-38,-45,100,-47,-44,-61,-24,-25,-26,-28,-29,-33,-34,-48,-53,-60,-55,-41,-42,-43,-54,-57,-40,-56,]),'LOOP':([29,31,32,34,35,36,38,40,45,48,49,50,63,68,69,70,74,78,87,89,91,92,93,94,95,96,97,105,106,110,125,128,129,132,136,144,145,149,],[-49,-23,-27,-32,-30,-35,-37,-39,-46,-50,-51,-52,-31,-36,-38,-45,101,-47,-44,-61,-24,-25,-26,-28,-29,-33,-34,-48,-53,-60,-55,-41,-42,-43,-54,-57,-40,-56,]),'OF':([29,31,32,34,35,36,38,40,45,48,49,50,63,68,69,70,77,78,87,89,91,92,93,94,95,96,97,105,106,110,125,128,129,132,136,144,145,149,],[-49,-23,-27,-32,-30,-35,-37,-39,-46,-50,-51,-52,-31,-36,-38,-45,104,-47,-44,-61,-24,-25,-26,-28,-29,-33,-34,-48,-53,-60,-55,-41,-42,-43,-54,-57,-40,-56,]),'ELSE':([29,31,32,34,35,36,38,40,45,48,49,50,63,68,69,70,78,87,89,91,92,93,94,95,96,97,105,106,110,114,125,128,129,132,136,144,145,149,],[-49,-23,-27,-32,-30,-35,-37,-39,-46,-50,-51,-52,-31,-36,-38,-45,-47,-44,-61,-24,-25,-26,-28,-29,-33,-34,-48,-53,-60,127,-55,-41,-42,-43,-54,-57,-40,-56,]),'POOL':([29,31,32,34,35,36,38,40,45,48,49,50,63,68,69,70,78,87,89,91,92,93,94,95,96,97,105,106,110,115,125,128,129,132,136,144,145,149,],[-49,-23,-27,-32,-30,-35,-37,-39,-46,-50,-51,-52,-31,-36,-38,-45,-47,-44,-61,-24,-25,-26,-28,-29,-33,-34,-48,-53,-60,128,-55,-41,-42,-43,-54,-57,-40,-56,]),'FI':([29,31,32,34,35,36,38,40,45,48,49,50,63,68,69,70,78,87,89,91,92,93,94,95,96,97,105,106,110,125,128,129,132,136,138,144,145,149,],[-49,-23,-27,-32,-30,-35,-37,-39,-46,-50,-51,-52,-31,-36,-38,-45,-47,-44,-61,-24,-25,-26,-28,-29,-33,-34,-48,-53,-60,-55,-41,-42,-43,-54,145,-57,-40,-56,]),'IN':([102,],[116,]),'ESAC':([118,152,153,],[132,-21,-22,]),'RARROW':([141,],[147,]),} + +_lr_action = {} +for _k, _v in _lr_action_items.items(): + for _x,_y in zip(_v[0],_v[1]): + if not _x in _lr_action: _lr_action[_x] = {} + _lr_action[_x][_k] = _y +del _lr_action_items + +_lr_goto_items = {'program':([0,],[1,]),'class_list':([0,3,],[2,5,]),'def_class':([0,3,],[3,3,]),'feature_list':([7,10,19,],[9,15,27,]),'feature':([7,10,19,],[10,10,10,]),'empty':([7,10,19,],[11,11,11,]),'param_list':([17,54,],[24,84,]),'param':([17,54,],[25,25,]),'expr':([22,41,42,44,47,51,57,58,85,100,101,107,111,112,116,121,127,130,137,147,],[30,73,74,77,79,81,87,90,109,114,115,81,90,90,129,134,138,139,90,151,]),'comp_expr':([22,41,42,44,47,51,57,58,85,100,101,107,111,112,116,121,127,130,137,147,],[31,31,31,31,31,31,31,31,31,31,31,31,31,31,31,31,31,31,31,31,]),'arith':([22,41,42,44,47,51,57,58,60,61,62,85,100,101,107,111,112,116,121,127,130,137,147,],[32,32,32,32,32,32,32,32,91,92,93,32,32,32,32,32,32,32,32,32,32,32,32,]),'arith2':([22,33,41,42,44,47,51,57,58,60,61,62,85,100,101,107,111,112,116,121,127,130,137,147,],[34,63,34,34,34,34,34,34,34,34,34,34,34,34,34,34,34,34,34,34,34,34,34,34,]),'term':([22,33,41,42,44,47,51,57,58,60,61,62,64,65,85,100,101,107,111,112,116,121,127,130,137,147,],[35,35,35,35,35,35,35,35,35,35,35,35,94,95,35,35,35,35,35,35,35,35,35,35,35,35,]),'factor':([22,33,41,42,44,47,51,57,58,60,61,62,64,65,66,67,85,100,101,107,111,112,116,121,127,130,137,147,],[36,36,36,36,36,36,36,36,36,36,36,36,36,36,96,97,36,36,36,36,36,36,36,36,36,36,36,36,]),'factor2':([22,33,37,41,42,44,47,51,57,58,60,61,62,64,65,66,67,85,100,101,107,111,112,116,121,127,130,137,147,],[38,38,68,38,38,38,38,38,38,38,38,38,38,38,38,38,38,38,38,38,38,38,38,38,38,38,38,38,38,]),'atom':([22,33,37,39,41,42,44,47,51,57,58,60,61,62,64,65,66,67,85,100,101,107,111,112,116,121,127,130,137,147,],[40,40,40,69,40,40,40,40,40,40,40,40,40,40,40,40,40,40,40,40,40,40,40,40,40,40,40,40,40,40,]),'member_call':([22,33,37,39,41,42,44,47,51,57,58,60,61,62,64,65,66,67,85,100,101,107,111,112,116,121,127,130,137,147,],[45,45,45,45,45,45,45,45,45,45,45,45,45,45,45,45,45,45,45,45,45,45,45,45,45,45,45,45,45,45,]),'func_call':([40,69,],[70,70,]),'let_list':([43,131,146,],[75,140,150,]),'expr_list':([51,107,],[80,120,]),'arg_list':([58,111,112,137,],[88,123,124,143,]),'case_list':([104,152,],[118,153,]),} + +_lr_goto = {} +for _k, _v in _lr_goto_items.items(): + for _x, _y in zip(_v[0], _v[1]): + if not _x in _lr_goto: _lr_goto[_x] = {} + _lr_goto[_x][_k] = _y +del _lr_goto_items +_lr_productions = [ + ("S' -> program","S'",1,None,None,None), + ('program -> class_list','program',1,'p_program','Parser.py',178), + ('class_list -> def_class class_list','class_list',2,'p_class_list','Parser.py',182), + ('class_list -> def_class','class_list',1,'p_class_list_s','Parser.py',186), + ('def_class -> CLASS TYPE OCUR feature_list CCUR SEMI','def_class',6,'p_def_class','Parser.py',190), + ('def_class -> CLASS TYPE INHERITS TYPE OCUR feature_list CCUR SEMI','def_class',8,'p_def_class_h','Parser.py',194), + ('feature_list -> feature feature_list','feature_list',2,'p_feature_list','Parser.py',199), + ('feature_list -> empty','feature_list',1,'p_feature_list_e','Parser.py',203), + ('feature -> OBJECT COLON TYPE SEMI','feature',4,'p_feature_1','Parser.py',206), + ('feature -> OBJECT COLON LARROW expr SEMI','feature',5,'p_feature_2','Parser.py',210), + ('feature -> OBJECT OPAR param_list CPAR COLON TYPE OCUR expr CCUR SEMI','feature',10,'p_feature_3','Parser.py',214), + ('feature -> OBJECT CPAR COLON TYPE OCUR expr CCUR SEMI','feature',8,'p_feature_4','Parser.py',218), + ('param_list -> param','param_list',1,'p_param_list_1','Parser.py',223), + ('param_list -> param COMMA param_list','param_list',3,'p_param_list_2','Parser.py',227), + ('param -> OBJECT COLON TYPE','param',3,'p_param','Parser.py',232), + ('expr_list -> expr SEMI','expr_list',2,'p_expr_list_1','Parser.py',237), + ('expr_list -> expr SEMI expr_list','expr_list',3,'p_expr_list_2','Parser.py',241), + ('let_list -> OBJECT COLON TYPE','let_list',3,'p_let_list1','Parser.py',246), + ('let_list -> OBJECT COLON TYPE LARROW expr','let_list',5,'p_let_list2','Parser.py',250), + ('let_list -> OBJECT COLON TYPE COMMA let_list','let_list',5,'p_let_list3','Parser.py',254), + ('let_list -> OBJECT COLON TYPE LARROW expr COMMA let_list','let_list',7,'p_let_list4','Parser.py',258), + ('case_list -> OBJECT COLON TYPE RARROW expr SEMI','case_list',6,'p_case_list_1','Parser.py',263), + ('case_list -> OBJECT COLON TYPE RARROW expr SEMI case_list','case_list',7,'p_case_list_2','Parser.py',267), + ('expr -> comp_expr','expr',1,'p_expr_2','Parser.py',273), + ('comp_expr -> comp_expr LESSEQUAL arith','comp_expr',3,'p_comp_expr_1','Parser.py',279), + ('comp_expr -> comp_expr LESS arith','comp_expr',3,'p_comp_expr_2','Parser.py',283), + ('comp_expr -> comp_expr EQUAL arith','comp_expr',3,'p_comp_expr_3','Parser.py',287), + ('comp_expr -> arith','comp_expr',1,'p_comp_expr_4','Parser.py',291), + ('arith2 -> arith2 PLUS term','arith2',3,'p_arith_1','Parser.py',297), + ('arith2 -> arith2 MINUS term','arith2',3,'p_arith_2','Parser.py',301), + ('arith2 -> term','arith2',1,'p_arith_3','Parser.py',305), + ('arith -> NOT arith2','arith',2,'p_arith_2_1','Parser.py',309), + ('arith -> arith2','arith',1,'p_arith_2_2','Parser.py',313), + ('term -> term MULT factor','term',3,'p_term_1','Parser.py',319), + ('term -> term DIV factor','term',3,'p_term_2','Parser.py',323), + ('term -> factor','term',1,'p_term_3','Parser.py',327), + ('factor -> ISVOID factor2','factor',2,'p_factor_1','Parser.py',331), + ('factor -> factor2','factor',1,'p_factor_2','Parser.py',334), + ('factor2 -> INT_COMPLEMENT atom','factor2',2,'p_factor_2_1','Parser.py',338), + ('factor2 -> atom','factor2',1,'p_factor_2_2','Parser.py',341), + ('atom -> IF expr THEN expr ELSE expr FI','atom',7,'p_atom_1','Parser.py',346), + ('atom -> WHILE expr LOOP expr POOL','atom',5,'p_atom_2','Parser.py',350), + ('atom -> LET let_list OBJECT IN expr','atom',5,'p_atom_3','Parser.py',354), + ('atom -> CASE expr OF case_list ESAC','atom',5,'p_atom_4','Parser.py',358), + ('atom -> OBJECT LARROW expr','atom',3,'p_atom_5','Parser.py',362), + ('atom -> atom func_call','atom',2,'p_atom_6','Parser.py',366), + ('atom -> member_call','atom',1,'p_atom_7','Parser.py',370), + ('atom -> NEW TYPE','atom',2,'p_atom_8','Parser.py',374), + ('atom -> OPAR expr CPAR','atom',3,'p_atom_9','Parser.py',378), + ('atom -> OBJECT','atom',1,'p_atom_10','Parser.py',382), + ('atom -> INTEGER','atom',1,'p_atom_11','Parser.py',386), + ('atom -> STRING','atom',1,'p_atom_12','Parser.py',390), + ('atom -> BOOL','atom',1,'p_atom_13','Parser.py',394), + ('atom -> OCUR expr_list CCUR','atom',3,'p_atom_14','Parser.py',398), + ('func_call -> DOT OBJECT OPAR arg_list CPAR','func_call',5,'p_func_call_1','Parser.py',402), + ('func_call -> DOT OBJECT OPAR CPAR','func_call',4,'p_func_call_2','Parser.py',406), + ('func_call -> AT TYPE DOT OBJECT OPAR arg_list CPAR','func_call',7,'p_func_call_3','Parser.py',410), + ('func_call -> AT TYPE DOT OBJECT OPAR CPAR','func_call',6,'p_func_call_4','Parser.py',414), + ('arg_list -> expr','arg_list',1,'p_arg_list_1','Parser.py',418), + ('arg_list -> expr COMMA arg_list','arg_list',3,'p_arg_list_2','Parser.py',422), + ('member_call -> OBJECT OPAR arg_list CPAR','member_call',4,'p_member_call_1','Parser.py',426), + ('member_call -> OBJECT OPAR CPAR','member_call',3,'p_member_call_2','Parser.py',430), + ('empty -> ','empty',0,'p_empty','Parser.py',434), +] diff --git a/src/semantic.py b/src/semantic.py new file mode 100644 index 00000000..61fb82eb --- /dev/null +++ b/src/semantic.py @@ -0,0 +1,817 @@ +from AstNodes import * +import visitor as visitor +import queue +from collections import OrderedDict +import itertools as itt + + + +class SemanticError(Exception): + @property + def text(self): + return f'({self.args[1]}, 11) - {self.__class__.__name__}: {self.args[0]}.' +class TypeError(SemanticError): + pass +class NameError(SemanticError): + pass +class AttributeError(SemanticError): + pass + +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,line=-1): + self.name = name + self.attributes = [] + self.methods = [] + self.parent = None + self.sons = [] + self.line = line + + def set_parent(self, parent, pos=0): + if self.parent is not None: + raise SemanticError(f'Parent type is already set for {self.name}', pos) + self.parent = parent + parent.sons.append(self) + + def get_attribute(self, name:str,pos=0): + try: + return next(attr for attr in self.attributes if attr.name == name) + except StopIteration: + if self.parent is None: + raise SemanticError(f'Attribute "{name}" is not defined in {self.name}',pos) + try: + return self.parent.get_attribute(name) + except SemanticError: + raise SemanticError(f'Attribute "{name}" is not defined in {self.name}',pos) + + def define_attribute(self, name:str, typex, pos): + try: + self.get_attribute(name) + except SemanticError: + attribute = Attribute(name, typex) + self.attributes.append(attribute) + return attribute + else: + raise SemanticError(f'Attribute "{name}" is already defined in {self.name}',pos) + + def get_method(self, name:str,pos=0): + try: + return next(method for method in self.methods if method.name == name) + except StopIteration: + if self.parent is None: + raise AttributeError(f'Method "{name}" is not defined in {self.name}',pos) + try: + return self.parent.get_method(name) + except SemanticError: + raise AttributeError(f'Method "{name}" is not defined in {self.name}',pos) + + def define_method(self, name:str, param_names:list, param_types:list, return_type, pos): + try: + method = self.get_method(name, pos) + except SemanticError: + pass + else: + if method.return_type != return_type or method.param_types != param_types: + raise SemanticError(f'Method "{name}" already defined in {self.name} with a different signature.', pos) + for method in self.methods : + if method.name == name: + raise SemanticError(f'Method "{name}" already defined in {self.name} ', pos) + + method = Method(name, param_names, param_types, return_type) + self.methods.append(method) + + def all_attributes(self, clean=True): + plain = OrderedDict() if self.parent is None else self.parent.all_attributes(False) + for attr in self.attributes: + plain[attr.name] = (attr, self) + return plain.values() if clean else plain + + def all_methods(self, clean=True): + plain = OrderedDict() if self.parent is None else self.parent.all_methods(False) + for method in self.methods: + plain[method.name] = (method, self) + return plain.values() if clean else plain + + def conforms_to(self, other): + return other.bypass() or self == other or self.parent is not None and self.parent.conforms_to(other) + + def bypass(self): + return False + + 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 ErrorType(Type): + def __init__(self): + Type.__init__(self, '') + + def conforms_to(self, other): + return True + + def bypass(self): + return True + + def __eq__(self, other): + return isinstance(other, Type) + +class VoidType(Type): + def __init__(self): + Type.__init__(self, '') + + def conforms_to(self, other): + raise Exception('Invalid type: void type.') + + def bypass(self): + return True + + def __eq__(self, other): + return isinstance(other, VoidType) + +class SELF_TYPE(Type): + def __init__(self): + Type.__init__(self, "SELF_TYPE") + + def __eq__(self, other): + return isinstance(other, SELF_TYPE) + +class Context: + def __init__(self): + self.types = {} + self.bassic_classes() + + def bassic_classes(self): + Bool = Type("Bool") + self.types["Bool"] = Bool + + Int = Type("Int") + self.types["Int"] = Int + + Object = Type("Object") + String = Type("String") + IO = Type("IO") + + Object.define_method("abort",[],[],Object,0) + Object.define_method("type_name",[],[],String,0) + Object.define_method("copy",[],[],SELF_TYPE(),0) + self.types["Object"] =Object + + String.define_method("length",[],[],Int,0) + String.define_method("concat",['s'],[String],String,0) + String.define_method("substr",['i','l'],[Int,Int],SELF_TYPE(),0) + self.types["String"] = String + + IO.define_method("out_string",["x"],[String],SELF_TYPE(),0) + IO.define_method("out_int",['x'],[Int],SELF_TYPE(),0) + IO.define_method("in_int",[],[],Int,0) + IO.define_method("in_string",[], [], String, 0) + self.types['IO']= IO + + + def check_type(self,x:Type,y:Type,pos): + if not x.conforms_to(y) : + raise(TypeError(f"Expr type {x.name} is no subclass of {y.name} ",pos)) + + def create_type(self, name:str,pos=0): + if name in self.types: + raise SemanticError(f'Type with the same name ({name}) already in context.' , pos) + typex = self.types[name] = Type(name,pos) + return typex + + def get_type(self, name:str,pos=0): + try: + return self.types[name] + except KeyError: + raise TypeError(f'Type "{name}" is not defined.' , pos) + + def closest_common_antecesor(self, typexa:Type, typexb:Type): + antecesor = [] + while not typexa is None or not typexb is None : + if not typexb is None : + if typexb in antecesor: + return typexb + antecesor.append(typexb) + + if not typexa is None: + if typexa in antecesor: + return typexa + antecesor.append(typexa) + + if not typexa is None: + typexa = typexa.parent + if not typexb is None: + typexb = typexb.parent + + return self.get_type("Object") + + 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, cilName=''): + self.name = name + self.type = vtype + self.cilName = cilName + +class Scope: + def __init__(self, parent=None): + self.locals = [] + self.parent = parent + self.children = [] + self.id = 0 + self.index = 0 if parent is None else len(parent) + + def __len__(self): + return len(self.locals) + + def create_child(self): + child = Scope(self) + child.id = self.id*10 +len(self.children) + self.children.append(child) + return child + + def define_variable(self, vname, vtype,pos=0): + if self.is_local(vname): + raise SemanticError(f"Variable {vname} already define in scope " , pos) + 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: + if not self.parent is None: + return self.parent.find_variable(vname, self.index) + else: + 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) +class CoolSemantic: + + def __init__(self , ast): + self.ast = ast + self.mask = set() + + def check_semantics(self): + errors = [] + collector = TypeCollector(errors) + collector.visit(self.ast) + context = collector.context + #builder = TypeBuilder(context, errors) + #builder.visit(self.ast) + cycles = self.check_for_cycles(context) + for cycle in cycles : + errors.append(SemanticError(f"Class {cycle[0][0]}, is involved in an inheritance cycle.",cycle[0][1])) + return errors , None, None + builder = TypeBuilder(context,errors) + builder.visit(self.ast) + typechecking = TypeChecking (context,errors) + scope = Scope() + typechecking.visit(self.ast, scope) + #print('Context:') + #print(context) + return errors, context , scope + + def check_for_cycles(self , context): + visited = set() + on_cycle = {} + count = 0 + for tp in context.types: + temp = context.types[tp] + ancestor = set() + while 1: + if temp.name in visited: + break + ancestor.add(temp.name) + visited.add(temp.name) + if temp.parent is None: + break + if temp.parent.name in ancestor: + on_cycle[count] = [] + on_cycle[count].append((temp.name,temp.line)) + temp2 = temp.parent + while temp != temp2: + on_cycle[count].append((temp2.name,temp2.line)) + temp2 = temp2.parent + on_cycle[count].sort(key= lambda x:x[1],reverse=True) + count = count + 1 + break + temp = temp.parent + return on_cycle.values() + + + +class TypeCollector(object): + def __init__(self, errors=[]): + self.context = None + self.errors = errors + + @visitor.on('node') + def visit(self, node): + pass + + @visitor.when(ProgramNode) + def visit(self, node): + self.context = Context() + # Your code here!!! + for decl in node.declarations: + self.visit(decl) + for dec_node in node.declarations: + try: + if dec_node.parent is not None: + if dec_node.parent in ["String","Int","Bool"]: + self.errors.append(SemanticError("Basic type as parent", dec_node.line)) + self.context.get_type(dec_node.id, dec_node.line).set_parent(self.context.get_type(dec_node.parent,dec_node.line),node.line) + except SemanticError as e: + self.errors.append(e) + # Your code here!!! + # ???? + @visitor.when(ClassDeclarationNode) + def visit(self, node): + try: + self.context.create_type(node.id,node.line) + except SemanticError as e: + self.errors.append(e) + +def sort_types(types): + q = queue.deque() + lst = [] + for tp in types: + if types[tp].parent is None: + if tp != "Object": + types[tp].set_parent( types["Object"]) + + q.append("Object") + while len(q) != 0: + tp = q.popleft() + lst.append(tp) + for son in types[tp].sons: + q.append(son.name) + return lst + +class TypeBuilder: + def __init__(self, context:Context, errors=[]): + self.context = context + self.errors = errors + + @visitor.on('node') + def visit(self, node): + pass + + # Your code here!!! + # ???? + @visitor.when(ProgramNode) + def visit(self, node): + nodec={ def_class.id:def_class for def_class in node.declarations} + sorted_types = sort_types(self.context.types) + for stypes in sorted_types: + if stypes in nodec: + self.visit(nodec[stypes]) + + + + @visitor.when(ClassDeclarationNode) + def visit(self, node): + + self.current_type = self.context.get_type(node.id,node.line) + for feature in node.features: + self.visit(feature) + + # dont forget the parent class + # and check errors + + + @visitor.when(AttrDeclarationNode) + def visit(self, node): + try: + attr_type = SELF_TYPE() if node.type == "SELF_TYPE" else self.context.get_type(node.type,node.line) + if node.id == "self": + raise SemanticError('Trying to assign value to self' ,node.line) + self.current_type.define_attribute(node.id, attr_type, node.line) + except SemanticError as e: + self.errors.append(e) + + @visitor.when(FuncDeclarationNode) + def visit(self, node): + arg_names = [param[0] for param in node.params] + arg_types = [] + for param in node.params: + try: + arg_types.append(self.context.get_type(param[1],node.line) ) + except SemanticError as e: + self.errors.append(e) + arg_types.append(ErrorType()) + try: + ret_type = SELF_TYPE() if node.type =="SELF_TYPE" else self.context.get_type(node.type,node.line) + except SemanticError as e: + self.errors.append(e) + ret_type = ErrorType() + try: + self.current_type.define_method(node.id, arg_names, arg_types, ret_type, node.line) + except SemanticError as e: + self.errors.append(e) + +class TypeChecking: + def __init__(self, context:Context, errors=[]): + self.context = context + self.current_type = None + self.errors = errors + + @visitor.on('node') + def visit(self, node): + pass + + @visitor.when(ProgramNode) + def visit(self, node:ProgramNode, scope=None): + for dec in node.declarations: + try: + self.visit(dec,scope.create_child()) + except SemanticError as e: + self.errors.append(e) + + + @visitor.when(ClassDeclarationNode) + def visit(self, node:ClassDeclarationNode, scope:Scope): + try : + typex = self.context.get_type(node.id, node.line) + except SemanticError as e: + self.errors.append(e) + + self.current_type = typex + #for at in typex.all_attributes(): + # scope.define_variable(at[0].name, at[0].type,node.line) + scope.define_variable("self",typex,node.line) + mscope = scope.create_child() + ascope = scope.create_child() + for feat in node.features: + if isinstance(feat, FuncDeclarationNode): + self.visit(feat,mscope.create_child()) + else: + self.visit(feat, ascope.create_child()) + + @visitor.when(AttrDeclarationNode) + def visit(self, node:AttrDeclarationNode,scope:Scope): + self.visit(node.expression, scope.create_child()) + if not node.expression is None: + try: + typex =self.current_type if node.type == "SELF_TYPE" else self.context.get_type(node.type,node.line) + self.context.check_type(node.expression.type,typex,node.line) + except SemanticError as e: + self.errors.append(e) + + @visitor.when(FuncDeclarationNode) + def visit(self, node:FuncDeclarationNode,scope:Scope): + method = self.current_type.get_method(node.id) + for i in range(len(method.param_names)): + try: + if method.param_names[i] == "self": + raise SemanticError('Trying to assign value to self' ,node.line) + scope.define_variable(method.param_names[i],method.param_types[i],node.line) + except SemanticError as e: + self.errors.append(e) + + self.visit(node.body,scope.create_child()) + try: + typex = method.return_type if not isinstance(method.return_type,SELF_TYPE) else self.current_type + self.context.check_type(node.body.type,typex,node.line) + except SemanticError as e: + self.errors.append(e) + + + @visitor.when(CaseOfNode) + def visit(self, node:CaseOfNode, scope:Scope): + node.type = ErrorType() + sce = scope.create_child() + self.visit(node.expression, sce) + scb = scope.create_child() + common_type = None + typesbr = set() + for branches in node.branches: + tmpscope = scb.create_child() + if branches[1 ]in typesbr: + self.errors.append(SemanticError("Type in more than one branch",branches[2].line)) + typesbr.add(branches[1]) + try : + typex = self.context.get_type(branches[1],branches[2].line) + except SemanticError as e: + self.errors.append(e) + tmpscope.define_variable(branches[0],typex,node.line) + self.visit(branches[2],tmpscope) + if common_type is None: + common_type = branches[2].type + else: + common_type = self.context.closest_common_antecesor(common_type,branches[2].type) + + node.type = common_type + + + + + + @visitor.when(FunctionCallNode) + def visit(self, node:FunctionCallNode, scope:Scope): + self.visit(node.obj,scope.create_child()) + node.type = ErrorType() + node.typexa = node.typex + for i in range(len(node.args)): + self.visit(node.args[i],scope.create_child()) + + if not node.typex is None: + try: + temp = self.context.get_type(node.typex,node.line) + self.context.check_type(node.obj.type,temp,node.line) + except SemanticError as e: + self.errors.append(e) + return + else: + if isinstance( node.obj.type , ErrorType): + return + node.typex = node.obj.type.name + try: + typex = self.context.get_type(node.typex,node.line) + except SemanticError as e: + self.errors.append(e) + return + try : + if isinstance( typex , ErrorType): + return + method = typex.get_method(node.id,node.line) + ret_type = method.return_type if not isinstance(method.return_type,SELF_TYPE) else typex + node.type = ret_type + if len(method.param_types) != len(node.args): + raise SemanticError (f'Method takes {len(method.param_types)} params but {len(node.args)} were given', node.line) + for i in range(len(node.args)): + try: + self.context.check_type(node.args[i].type,method.param_types[i],node.line) + except SemanticError as e: + self.errors.append(e) + except SemanticError as e: + self.errors.append(e) + + @visitor.when(MemberCallNode) + def visit(self, node:MemberCallNode, scope:Scope): + node.type = ErrorType() + + for i in range(len(node.args)): + self.visit(node.args[i],scope.create_child()) + + typex = self.current_type + try : + if isinstance( typex , ErrorType): + return + method = typex.get_method(node.id, node.line) + ret_type = method.return_type if not isinstance(method.return_type,SELF_TYPE) else typex + node.type = ret_type + if len(method.param_types) != len(node.args): + raise SemanticError (f'Method takes {len(method.param_types)} params but {len(node.args)} were given') + for i in range(len(node.args)): + try: + self.context.check_type(node.args[i].type,method.param_types[i],node.line) + except SemanticError as e: + self.errors.append(e) + except SemanticError as e: + self.errors.append(e) + + + + + @visitor.when(IfThenElseNode) + def visit(self,node:IfThenElseNode,scope:Scope): + self.visit(node.condition,scope.create_child()) + try: + self.context.check_type(node.condition.type,self.context.get_type("Bool"),node.line) + except SemanticError as e: + self.errors.append(e) + + self.visit(node.if_body,scope.create_child()) + + self.visit(node.else_body, scope.create_child()) + + try: + node.type = self.context.closest_common_antecesor(node.if_body.type, node.else_body.type) + except SemanticError as e: + self.errors.append(e) + node.type = ErrorType() + + + @visitor.when(AssignNode) + def visit(self, node:AssignNode,scope:Scope): + self.visit(node.expression, scope.create_child()) + try: + if node.id == "self": + raise SemanticError('Trying to assign value to self' ,node.line) + + var = scope.find_variable(node.id) + + if var is None: + try: + at = [ at[0] for at in self.current_type.all_attributes() if at[0].name == node.id] + var = at[0] + except: + raise NameError(f"Variable {node.id} not defined",node.line) + + typex = self.current_type if isinstance(var.type , SELF_TYPE) else var.type + self.context.check_type(node.expression.type, typex, node.line) + node.type = node.expression.type + except SemanticError as e: + self.errors.append(e) + node.type = node.expression.type + + + @visitor.when(WhileLoopNode) + def visit(self , node:WhileLoopNode, scope:Scope): + self.visit(node.condition, scope.create_child()) + if self.context.get_type("Bool",node.line) != node.condition.type: + self.errors.append(TypeError("Expr should be boolean", node.line)) + self.visit(node.body, scope.create_child()) + node.type = self.context.get_type("Object",node.line) + + + @visitor.when(BlockNode) + def visit (self, node:BlockNode, scope:Scope): + for expr in node.expressions: + self.visit(expr,scope.create_child()) + node.type = node.expressions[-1].type + + + @visitor.when(LetInNode) + def visit(self, node:LetInNode,scope:Scope): + sc = scope.create_child() + for init in node.let_body: + if not init[2] is None: + self.visit(init[2],sc) + try: + typex = self.context.get_type(init[1],node.line) if init[1] != "SELF_TYPE" else self.current_type + self.context.check_type(init[2].type,typex,node.line) + except SemanticError as e: + self.errors.append(e) + + sc = sc.create_child() + typex= None + try: + typex = self.context.get_type(init[1],node.line) if init[1] != "SELF_TYPE" else self.current_type + except SemanticError as e: + self.errors.append(e) + typex = ErrorType() + try: + if init[0] == "self": + raise SemanticError('Trying to assign value to self' ,node.line) + sc.define_variable(init[0],typex,node.line) + except SemanticError as e: + self.errors.append(e) + + sc = sc.create_child() + node.body_scope=sc + self.visit(node.in_body,sc) + node.type = node.in_body.type + + + @visitor.when(NewNode) + def visit(self, node:NewNode,scope:Scope): + try: + if node.type == "SELF_TYPE": + node.type= self.current_type + else: + node.type = self.context.get_type(node.type,node.line) + except SemanticError as e: + self.errors.append(e) + node.type = ErrorType() + + + + @visitor.when(IsVoidNode) + def visit(self, node:IsVoidNode, scope:Scope): + + self.visit(node.expression,scope.create_child()) + node.type = self.context.get_type("Bool", node.line) + + + @visitor.when(ArithmeticNode) + def visit(self, node:ArithmeticNode,scope:Scope): + self.visit(node.left,scope.create_child()) + if node.left.type != self.context.get_type("Int", node.line): + self.errors.append(TypeError ("Expr must be an integer" ,node.line)) + self.visit(node.right,scope.create_child()) + if node.right.type != self.context.get_type("Int", node.line): + self.errors.append(TypeError ("Expr must be an integer" ,node.line)) + node.type = self.context.get_type("Int", node.line) + + + @visitor.when(LessNode) + def visit(self, node:LessNode,scope:Scope): + self.visit(node.left,scope.create_child()) + if node.left.type != self.context.get_type("Int", node.line): + self.errors.append(TypeError ("Expr must be an integer" ,node.line)) + self.visit(node.right,scope.create_child()) + if node.right.type != self.context.get_type("Int", node.line): + self.errors.append(TypeError ("Expr must be an integer" ,node.line)) + node.type = self.context.get_type("Bool", node.line) + + @visitor.when(LessEqualNode) + def visit(self, node:LessEqualNode, scope:Scope): + self.visit(node.left,scope.create_child()) + if node.left.type != self.context.get_type("Int", node.line): + self.errors.append(TypeError ("Expr must be an integer" ,node.line)) + self.visit(node.right,scope.create_child()) + if node.right.type != self.context.get_type("Int", node.line): + self.errors.append(TypeError ("Expr must be an integer" ,node.line)) + node.type = self.context.get_type("Bool", node.line) + + @visitor.when(EqualNode) + def visit(self, node:EqualNode, scope:Scope): + self.visit(node.left,scope.create_child()) + self.visit(node.right,scope.create_child()) + if node.left.type != node.right.type: + basic = ['Int', 'String', 'Bool'] + if node.left.type.name in basic or node.right.type.name in basic: + self.errors.append(TypeError("Exprs must have same type", node.line)) + node.type = self.context.get_type("Bool", node.line) + + @visitor.when(ComplementNode) + def visit(self, node:ComplementNode, scope:Scope): + self.visit(node.expression, scope.create_child()) + if node.expression.type != self.context.get_type("Int", node.line): + self.errors.append(TypeError ("Expr must be an integer" ,node.line)) + node.type = self.context.get_type("Int", node.line) + + @visitor.when(NotNode) + def visit(self, node:NotNode, scope:Scope): + self.visit(node.expression, scope.create_child()) + if node.expression.type != self.context.get_type("Bool", node.line): + self.errors.append(TypeError ("Expr must be an integer" ,node.line)) + node.type = self.context.get_type("Bool", node.line) + + + @visitor.when(IntegerNode) + def visit (self, node:IntegerNode,scope:Scope): + node.type = self.context.get_type("Int", node.line) + + @visitor.when(StringNode) + def visit (self, node:StringNode, scope:Scope): + node.type = self.context.get_type("String", node.line) + + @visitor.when(BoolNode) + def visit (self, node:BoolNode, scope:Scope): + node.type = self.context.get_type("Bool",node.line) + + @visitor.when(IdNode) + def visit (self, node:IntegerNode,scope:Scope): + + x = scope.find_variable(node.token) + if x is None: + try: + at = [ at[0] for at in self.current_type.all_attributes() if at[0].name == node.token] + x = at[0] + except: + node.type = ErrorType() + self.errors.append(NameError(f"Variable {node.token} not defined",node.line)) + return + node.type = x.type if not isinstance(x.type , SELF_TYPE) else self.current_type + diff --git a/src/tokens.py b/src/tokens.py new file mode 100644 index 00000000..5e753afc --- /dev/null +++ b/src/tokens.py @@ -0,0 +1,53 @@ +keywords = [ + 'CLASS', + 'ELSE', + 'FI', + 'IF', + 'IN', + 'INHERITS', + 'ISVOID', + 'LET', + 'LOOP', + 'POOL', + 'THEN', + 'WHILE', + 'CASE', + 'ESAC', + 'NEW', + 'OF', + 'NOT' + ] + +operators = [ + 'PLUS', + 'MINUS', + 'MULT', + 'DIV', + 'LESS', + 'LESSEQUAL', + 'EQUAL', + 'INT_COMPLEMENT' + ] + +specials= [ + 'OCUR', + 'CCUR', + 'OPAR', + 'CPAR', + 'DOT', + 'SEMI', + 'COLON', + 'COMMA', + 'AT', + 'LARROW', + 'RARROW' + ] + + #list of token names +tokens = [ + 'INTEGER', + 'STRING', + 'BOOL', + 'TYPE', + 'OBJECT' + ] + operators + keywords + specials \ No newline at end of file diff --git a/src/visitor.py b/src/visitor.py new file mode 100644 index 00000000..96484283 --- /dev/null +++ b/src/visitor.py @@ -0,0 +1,80 @@ +# The MIT License (MIT) +# +# Copyright (c) 2013 Curtis Schlak +# +# Permission is hereby granted, free of charge, to any person obtaining a copy +# of this software and associated documentation files (the "Software"), to deal +# in the Software without restriction, including without limitation the rights +# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +# copies of the Software, and to permit persons to whom the Software is +# furnished to do so, subject to the following conditions: +# +# The above copyright notice and this permission notice shall be included in +# all copies or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +# THE SOFTWARE. + +import inspect + +__all__ = ['on', 'when'] + +def on(param_name): + def f(fn): + dispatcher = Dispatcher(param_name, fn) + return dispatcher + return f + + +def when(param_type): + def f(fn): + frame = inspect.currentframe().f_back + func_name = fn.func_name if 'func_name' in dir(fn) else fn.__name__ + dispatcher = frame.f_locals[func_name] + if not isinstance(dispatcher, Dispatcher): + dispatcher = dispatcher.dispatcher + dispatcher.add_target(param_type, fn) + def ff(*args, **kw): + return dispatcher(*args, **kw) + ff.dispatcher = dispatcher + return ff + return f + + +class Dispatcher(object): + def __init__(self, param_name, fn): + frame = inspect.currentframe().f_back.f_back + top_level = frame.f_locals == frame.f_globals + self.param_index = self.__argspec(fn).args.index(param_name) + self.param_name = param_name + self.targets = {} + + def __call__(self, *args, **kw): + typ = args[self.param_index].__class__ + d = self.targets.get(typ) + if d is not None: + return d(*args, **kw) + else: + issub = issubclass + t = self.targets + ks = t.keys() + ans = [t[k](*args, **kw) for k in ks if issub(typ, k)] + if len(ans) == 1: + return ans.pop() + return ans + + def add_target(self, typ, target): + self.targets[typ] = target + + @staticmethod + def __argspec(fn): + # Support for Python 3 type hints requires inspect.getfullargspec + if hasattr(inspect, 'getfullargspec'): + return inspect.getfullargspec(fn) + else: + return inspect.getargspec(fn)