diff --git a/doc/informe.pdf b/doc/informe.pdf new file mode 100644 index 000000000..e1ac3466c Binary files /dev/null and b/doc/informe.pdf differ diff --git a/requirements.txt b/requirements.txt index 9eb0cad1a..cba16ee2f 100644 --- a/requirements.txt +++ b/requirements.txt @@ -1,2 +1,3 @@ pytest pytest-ordering +ply diff --git a/src/AST.py b/src/AST.py new file mode 100644 index 000000000..55acff320 --- /dev/null +++ b/src/AST.py @@ -0,0 +1,193 @@ +class Node: + line = 0 + index = 0 + +class Program(Node): + def __init__(self, classes = None): + self.classes = classes + if classes is None: + self.classes = [] + +class Class(Node): + def __init__(self, _type, inherit, features=None): + self.name = _type + self.inherit = inherit#parent + self.methods = [] + self.attributes = [] + + if features is not None: + for feature in features: + if isinstance(feature, Method): + self.methods.append(feature) + else: + self.attributes.append(feature) + +class Type: + def __init__(self, type): + self.name = type + +class Branch: + def __init__(self, _var, _expr): + self.var = _var + self.expr = _expr + +class Feature(Node): + pass + +class Method(Feature): + def __init__(self, id, parameters, return_type, expr=None): + self.id = id + self.parameters = parameters + self.return_type = return_type + self.expression = expr + +class Attribute(Feature): + def __init__(self, id, _type, _expr = None): + self.type = _type + self.expr = _expr + self.id = id + +class Expression(Node): + computed_type = None + +class Atom(Expression): + pass + +class Assign(Expression): + def __init__(self, _id, expr): + self.id = _id + self.expression = expr + +class Dispatch(Atom): + def __init__(self, func_id, params = None, left_expr = None): + self.left_expression = left_expr + self.func_id = func_id + self.parameters = params + self.className = 'dispatch' + + if params is None: + self.parameters = [] + +class StaticDispatch(Atom): + def __init__(self, func_id, params, left_expr, parent_type): + self.left_expression = left_expr + self.func_id = func_id + self.parameters = params + self.parent_type = parent_type + self.className = 'dispatch' + +class Conditional(Atom): + def __init__(self, if_expr, then_expr, else_expr): + self.if_expression = if_expr + self.then_expression = then_expr + self.else_expression = else_expr + self.className = 'conditional' + +class Loop(Atom): + def __init__(self, while_expr, loop_exprs): + self.while_expression = while_expr + self.loop_expression = loop_exprs + self.className = 'loop' + +class LetVar(Atom): + def __init__(self, declarations, in_expr): + self.in_expression = in_expr + self.declarations = declarations + +class Var(Atom): + def __init__(self, _name, _type): + self.id = _name + self.type = _type + +class Case(Atom): + def __init__(self, case_expr, implications): + self.case_expression = case_expr + self.implications = implications + self.className = 'case' + +class NewType(Atom): + def __init__(self, _type_name): + self.type_name = _type_name + +class UnaryOperator(Expression): + def __init__(self,expr): + self.expression = expr + +class BinaryOperator(Expression): + def __init__(self, left_expr, right_expr): + self.left_expression = left_expr + self.right_expression = right_expr + +class BAritmeticOperation(BinaryOperator): + pass + +class Plus(BAritmeticOperation): + def __init__(self, _first, _second): + self.symbol = "+" + self.first = _first + self.second = _second + +class Minus(BAritmeticOperation): + def __init__(self, _first, _second): + self.symbol = "-" + self.first = _first + self.second = _second + +class Star(BAritmeticOperation): + def __init__(self, _first, _second): + self.symbol = "*" + self.first = _first + self.second = _second + +class Div(BAritmeticOperation): + def __init__(self, _first, _second): + self.symbol = "/" + self.first = _first + self.second = _second + +class Not(UnaryOperator): + def __init__(self, _expr): + self.expr = _expr + +class IntegerComplement(Atom): + def __init__(self, _expr): + self.expression = _expr + +class IsVoid(UnaryOperator): + def __init__(self, _expr): + self.expression = _expr + +class LowerThan(BinaryOperator): + def __init__(self, _first, _second): + self.symbol = "<" + self.first = _first + self.second = _second + +class LowerEqualThan(BinaryOperator): + def __init__(self, _first, _second): + self.symbol = "<=" + self.first = _first + self.second = _second + +class EqualThan(BinaryOperator): + def __init__(self, _first, _second): + self.symbol = "=" + self.first = _first + self.second = _second + +class Block(Atom): + def __init__(self, exprs): + self.expressions = exprs + self.className = 'block' + +class Interger(Atom): + def __init__(self,value): + self.value = value + +class String(Atom): + def __init__(self, value): + self.value = value + +class Boolean(Atom): + def __init__(self,value): + self.value = value \ No newline at end of file diff --git a/src/AST_CIL.py b/src/AST_CIL.py new file mode 100644 index 000000000..a602c1f9b --- /dev/null +++ b/src/AST_CIL.py @@ -0,0 +1,309 @@ +class Node: + pass + +class Program(Node): + def __init__(self): + self.type_section = [] + self.data_section = {} #data[string] = tag + self.code_section = [] + +class Type(Node): + def __init__(self, name): + self.name = name + self.attributes = [] + self.methods = {} + def to_string(self): + return "type {} \n attributes {}\n methods {}\n".format(self.name, self.attributes, self.methods) + +class Data(Node): + def __init__(self, vname, value): + self.vname = vname + self.value = value + + + + +class Function(Node): + def __init__(self, fname): + self.fname = fname + self.params = [] + self.localvars = [] + self.instructions = [] + +class Instruction(Node): + pass + +class Assign(Instruction): + def __init__(self, dest, _type, source): + self.dest = dest + self.type = _type + self.source = source + def to_string(self): + return "{} <- {}".format(self.dest, self.source) + + +class Copy(Instruction): + def __init__(self, dest, source): + self.dest = dest + self.source = source + def to_string(self): + return "{} Copy {}".format(self.dest, self.source) + + +class Arithmetic(Instruction): + pass + +class Plus(Arithmetic): + def __init__(self, dest, left, right): + self.dest = dest + self.left = left + self.right = right + def to_string(self): + return "{} = {} + {}".format(self.dest, self.left, self.right) + +class Minus(Arithmetic): + def __init__(self, dest, left, right): + self.dest = dest + self.left = left + self.right = right + def to_string(self): + return "{} = {} - {}".format(self.dest, self.left, self.right) + +class Star(Arithmetic): + def __init__(self, dest, left, right): + self.dest = dest + self.left = left + self.right = right + def to_string(self): + return "{} = {} * {}".format(self.dest, self.left, self.right) + +class Div(Arithmetic): + def __init__(self, dest, left, right): + self.dest = dest + self.left = left + self.right = right + def to_string(self): + return "{} = {} / {}".format(self.dest, self.left, self.right) + + +class GetParent(Instruction): + def __init__(self, dest, instance, array, length): + self.dest = dest + self.instance = instance #local con la direccion en memoria + self.array_name = array #indice del atributo(contando los heredados) + self.length = length + def to_string(self): + return "{} = GETPARENT {} {}".format(self.dest, self.instance, self.array_name) + + +class GetAttrib(Instruction): + def __init__(self, dest, instance, attribute): + self.dest = dest + self.instance = instance #local con la direccion en memoria + self.attribute = attribute #indice del atributo(contando los heredados) + def to_string(self): + return "{} = GETATTR {} {}".format(self.dest, self.instance, self.attribute) + +class SetAttrib(Instruction): + def __init__(self, instance, attribute, src): + self.instance = instance #local con la direccion en memoria + self.attribute = attribute #indice del atributo + self.value = src + def to_string(self): + return "SETATTR {} {} {}".format(self.instance, self.attribute, self.value) + +class Allocate(Instruction): + def __init__(self, dest, ttype): + self.dest = dest + self.ttype = ttype + + def to_string(self): + return "{} = ALLOCATE {}".format(self.dest, self.ttype) + +# class Array(Instruction): +# def __init__(self, dest, src): +# self.dest = dest +# self.src = src + +class Array(Instruction): + def __init__(self, name, data): + self.name = name + self.data_list = data + def to_string(self): + return "ARRAY {}".format(self.name) + +class TypeOf(Instruction): + def __init__(self, dest, var): + self.dest = dest + self.var = var + def to_string(self): + return "{} = TYPEOF {}".format(self.dest, self.var) + +class Label(Instruction): + def __init__(self, name): + self.name = name + + def to_string(self): + return "LABEL {}".format(self.name) + +class Goto(Instruction): + def __init__(self, name): + self.name = name + + def to_string(self): + return "GOTO {}".format(self.name) + +class GotoIf(Instruction): + def __init__(self, condition, label): + self.condition = condition + self.label = label + def to_string(self): + return "IF {} GOTO {}".format(self.condition, self.label) + +class Call(Instruction): + def __init__(self, dest, func): + self.dest = dest + self.func = func + + def to_string(self): + return "{} = CALL {}".format(self.dest, self.func) + +class Dynamic_Call(Instruction): + def __init__(self, dest, ttype, func, left): + self.dest = dest + self.ttype = ttype + self.func = func + self.left = left + def to_string(self): + return "{} = VCALL {} {}".format(self.dest, self.ttype, self.func) + +class Arg(Instruction): + def __init__(self, vinfo): + self.vinfo = vinfo + def to_string(self): + return "ARG {}".format(self.vinfo) + +class Return(Instruction): + def __init__(self, value=None): + self.value = value + + def to_string(self): + return "RETURN {}".format(self.value) + +class Load(Instruction): + def __init__(self, dest, msg): + self.dest = dest + self.msg = msg + def to_string(self): + return "{} = LOAD {}".format(self.dest, self.msg) + +class Length(Instruction): + def __init__(self, dest, str_addr): + self.dest = dest + self.str_addr = str_addr + def to_string(self): + return "{} = LENGTH {}".format(self.dest, self.str_addr) + +class Concat(Instruction): + def __init__(self, dest, head, tail): + self.dest = dest + self.head = head + self.tail = tail + + def to_string(self): + return "{} = CONCAT {} {}".format(self.dest, self.head, self.tail) + +class Prefix(Instruction): + def __init__(self, dest, str_addr, pos): + self.dest = dest + self.str_addr = str_addr + self.pos = pos + +class Substring(Instruction): + def __init__(self, dest, str_addr, pos, length): + self.dest = dest + self.str_addr = str_addr + self.pos = pos + self.length = length + def to_string(self): + return "{} = Substring {} {} {}".format(self.dest, self.str_addr, self.pos, self.length) + +class ToStr(Instruction): + def __init__(self, dest, ivalue): + self.dest = dest + self.ivalue = ivalue + +class ReadStr(Instruction): + def __init__(self, dest): + self.dest = dest + def to_string(self): + return "{} = READ".format(self.dest) + +class ReadInt(Instruction): + def __init__(self, dest): + self.dest = dest + def to_string(self): + return "{} = READ".format(self.dest) + +class PrintStr(Instruction): + def __init__(self, str_addr): + self.str_addr = str_addr + def to_string(self): + return "PRINT {}".format(self.str_addr) + +class PrintInt(Instruction): + def __init__(self, value): + self.value = value + def to_string(self): + return "PRINT {}".format(self.value) + +class EndProgram(Instruction): + def __init__(self): + pass + + def to_string(self): + return "PROGRAM END WITH ERROR" + + +class IsVoid(Instruction): + def __init__(self, dest, obj): + self.dest = dest + self.obj = obj + +class LowerThan(Instruction): + def __init__(self, dest, left_expr, right_expr): + self.dest = dest + self.left = left_expr + self.right = right_expr + def to_string(self): + return "{} <- {} < {}".format(self.dest, self.left, self.right) + +class LowerEqualThan(Instruction): + def __init__(self, dest, left_expr, right_expr): + self.dest = dest + self.left = left_expr + self.right = right_expr + def to_string(self): + return "{} <- {} <= {}".format(self.dest, self.left, self.right) + +class EqualThan(Instruction): + def __init__(self, dest, left_expr, right_expr): + self.dest = dest + self.left = left_expr + self.right = right_expr + def to_string(self): + return "{} <- {} == {}".format(self.dest, self.left, self.right) + +class Exit(Instruction): + def __init__(self): + pass + def to_string(self): + return 'Exit' + +class EqualStrThanStr(Instruction): + def __init__(self, dest, left_expr, right_expr): + self.dest = dest + self.left = left_expr + self.right = right_expr + def to_string(self): + return "{} <- {} == {}".format(self.dest, self.left, self.right) \ No newline at end of file diff --git a/src/TOKEN.py b/src/TOKEN.py new file mode 100644 index 000000000..acc67d23c --- /dev/null +++ b/src/TOKEN.py @@ -0,0 +1,9 @@ +class LexToken: + def __init__(self, name, symbol, Ln, Col): + self.name = name + self.symbol = symbol + self.Ln = Ln + self.Col = Col + + def to_string(self): + return "LexToken({},'{}',{},{})".format(self.name, self.symbol, self.Ln, self.Col) \ No newline at end of file diff --git a/src/basic_classes.py b/src/basic_classes.py new file mode 100644 index 000000000..6af8165fa --- /dev/null +++ b/src/basic_classes.py @@ -0,0 +1,165 @@ +import AST_CIL + +class Build: + def __init__(self, code, types): + self.idCount = 0 + self.build_IO(code, types) + self.build_Object(code, types) + self.build_String(code, types) + self.build_Int(code, types) + self.build_Bool(code, types) + + def get_local(self): + dest = 'local__' + str(self.idCount) + self.idCount += 1 + return dest + + def build_Object(self, code, types): + # abort() : Object + _type_Object = AST_CIL.Type('Object') + func = 'function' + '_' + 'Object' + '_' + 'abort' + _type_Object.methods['abort'] = func + f = AST_CIL.Function(func) + f.instructions.append(AST_CIL.EndProgram()) + code.append(f) + + # type_name() : String + func = 'function' + '_' + 'Object' + '_' + 'type_name' + _type_Object.methods['type_name'] = func + f = AST_CIL.Function(func) + f.params = ['self'] + d = self.get_local() + f.localvars.append(d) + f.instructions.append(AST_CIL.TypeOf(d, 'self')) + instance = self.get_local() + intr1 = AST_CIL.Allocate(instance, 'String') + intr2 = AST_CIL.SetAttrib(instance, 0, d) + f.localvars.append(instance) + f.instructions += [intr1, intr2] + f.instructions.append(AST_CIL.Return(instance)) + code.append(f) + + + # copy() : SELF_TYPE + _type_Object = AST_CIL.Type('Object') + func = 'function' + '_' + 'Object' + '_' + 'copy' + _type_Object.methods['copy'] = func + f = AST_CIL.Function(func) + f.instructions.append(AST_CIL.EndProgram()) #IMPLEMENTAR + code.append(f) + types.append(_type_Object) + + def build_String(self, code, types): + # length() : Int + _type_String = AST_CIL.Type('String') + func = 'function' + '_' + 'String' + '_' + 'length' + _type_String.methods['length'] = func + f = AST_CIL.Function(func) + f.params = ['self'] + d = self.get_local() + f.localvars.append(d) + f.instructions.append(AST_CIL.Length(d,'self')) + instance = self.get_local() + intr1 = AST_CIL.Allocate(instance, 'Int') + intr2 = AST_CIL.SetAttrib(instance, 0, d) + f.localvars.append(instance) + f.instructions += [intr1, intr2] + f.instructions.append(AST_CIL.Return(instance)) + code.append(f) + + ################################################ + + # concat(s : String) : String + func = 'function' + '_' + 'String' + '_' + 'concat' + _type_String.methods['concat'] = func + f = AST_CIL.Function(func) + f.params = ['self', 's'] + d = self.get_local() + f.localvars.append(d) + f.instructions.append(AST_CIL.Concat(d,'self','s')) + instance = self.get_local() + intr1 = AST_CIL.Allocate(instance, 'String') + intr2 = AST_CIL.SetAttrib(instance, 0, d) + f.localvars.append(instance) + f.instructions += [intr1, intr2] + f.instructions.append(AST_CIL.Return(instance)) + code.append(f) + + # substr(i : Int, l : Int) : String + func = 'function' + '_' + 'String' + '_' + 'substr' + _type_String.methods['substr'] = func + f = AST_CIL.Function(func) + f.params = ['self', 'i', 'l'] + d = self.get_local() + f.localvars.append(d) + f.instructions.append(AST_CIL.Substring(d,'self','i', 'l')) + instance = self.get_local() + intr1 = AST_CIL.Allocate(instance, 'String') + intr2 = AST_CIL.SetAttrib(instance, 0, d) + f.localvars.append(instance) + f.instructions += [intr1, intr2] + f.instructions.append(AST_CIL.Return(instance)) + code.append(f) + types.append(_type_String) + + def build_Int(self, code, types): + pass + + def build_Bool(self, code, types): + pass + + def build_IO(self, code, types): + + _type_IO = AST_CIL.Type('IO') + func = 'function' + '_' + 'IO' + '_' + 'out_string' + _type_IO.methods['out_string'] = func + f = AST_CIL.Function(func) + f.params = ['self', 'x'] + f.instructions.append(AST_CIL.PrintStr('x')) + f.instructions.append(AST_CIL.Return('self')) + code.append(f) + + ################################################ + + func = 'function' + '_' + 'IO' + '_' + 'out_int' + _type_IO.methods['out_int'] = func + f = AST_CIL.Function(func) + f.params = ['self', 'x'] + f.instructions.append(AST_CIL.PrintInt('x')) + f.instructions.append(AST_CIL.Return('self')) + code.append(f) + + ################################################ + + func = 'function' + '_' + 'IO' + '_' + 'in_string' + _type_IO.methods['in_string'] = func + f = AST_CIL.Function(func) + f.params = ['self'] + d = self.get_local() + f.localvars.append(d) + f.instructions.append(AST_CIL.ReadStr(d)) + instance = self.get_local() + intr1 = AST_CIL.Allocate(instance, 'String') + intr2 = AST_CIL.SetAttrib(instance, 0, d) + f.localvars.append(instance) + f.instructions += [intr1, intr2] + f.instructions.append(AST_CIL.Return(instance)) + code.append(f) + + ################################################ + + func = 'function' + '_' + 'IO' + '_' + 'in_int' + _type_IO.methods['in_int'] = func + f = AST_CIL.Function(func) + f.params = ['self'] + d = self.get_local() + f.localvars.append(d) + f.instructions.append(AST_CIL.ReadInt(d)) + instance = self.get_local() + intr1 = AST_CIL.Allocate(instance, 'Int') + intr2 = AST_CIL.SetAttrib(instance, 0, d) + f.localvars.append(instance) + f.instructions += [intr1, intr2] + f.instructions.append(AST_CIL.Return(instance)) + code.append(f) + types.append(_type_IO) diff --git a/src/cil_to_mips.py b/src/cil_to_mips.py new file mode 100644 index 000000000..a9fe48d81 --- /dev/null +++ b/src/cil_to_mips.py @@ -0,0 +1,837 @@ +from AST_CIL import * +import visitor + +class Build_Mips: + def __init__(self, ast, sem, class_functions_list): + self.lines = [] + self.idCount = 0 + self.current_function = None + self.attributes = {} + self.class_functions_list = class_functions_list + for c, a in sem.class_attrs.items(): + self.attributes[c] = len(a) + self.attributes['Int'] = 1 + self.attributes['String'] = 1 + self.attributes['Bool'] = 1 + self.attributes['Object'] = 0 + self.conform = {} + self.compute_parents(sem.class_parent) + self.visit(ast) + + def compute_parents(self, inherit): + self.conform['Object'] = ['Object'] + class_list = [] + for c, _ in inherit.items(): + self.conform[c] = [c] + class_list.append(c) + for c in class_list: + current = c + while not current == 'Object': + self.conform[c].append(inherit[current]) + current = inherit[current] + + def add(self, line): + self.lines.append(line) + + def stack_pos(self, name): + temp = self.current_function.params + self.current_function.localvars + index = 4*temp.index(name) + return -index + + @visitor.on('node') + def visit(self, node): + pass + + @visitor.when(Program) + def visit(self, program): + + self.add('.data') + self.add('p_error' + ':' + ' .asciiz ' + '"Abort called from class String\\n"')#"') + self.add('runtime_error: .asciiz "RuntimeError: Index out of range Error\\n"') + for c, _ in self.attributes.items(): + self.add(c + '_class_name' + ': .asciiz "' + c + '"') + + for c, conform_list in self.conform.items(): + line = c + '_conforms_to: .word '+ conform_list[0] + '_class_name' + n = len(conform_list) + for i in range(1, n): line += ', ' + conform_list[i] + '_class_name' + self.add(line) + + for _str, tag in program.data_section.items(): + self.add(tag + ':' + ' .asciiz ' + _str) + + self.add('.text') + self.add('.globl main') + + # self.add('main:') + # self.add('jal function_Main_main') + # self.add('li $v0, 10') + # self.add('syscall') + + for f in program.code_section: + self.visit(f) + + self.add(''' + str_len: + li $v0,0 + move $v1, $a0 + __lenLoop: + lbu $t1, 0($v1) + beq $t1,$0,__lenExit + addu $v0,$v0,1 + addu $v1,$v1,1 + b __lenLoop + __lenExit: + jr $ra + + str_copy: + lw $a0, -4($fp) + lw $a1, -8($fp) + lw $a2, -12($fp) + + move $v0, $a0 + + str__while_copy: + beqz $a2, str__end_copy + + xor $t0, $t0, $t0 + lb $t0, 0($a1) + sb $t0, 0($a0) + + subu $a2, $a2,1 + addu $a0, $a0,1 + addu $a1, $a1,1 + j str__while_copy + + str__end_copy: + jr $ra + + str_index_error: + li $v0, 4 + la $a0, runtime_error + syscall + li $v0, 10 + syscall + jr $ra + + str_substring: + # load arguments + move $t5, $a0 + move $t3, $a1 + li $t4, 0 + move $t2, $a2 + + # check for index out of range + move $a3, $ra + jal str_len + move $ra, $a3 + + addu $t6, $t3, $t2 + bgt $t6, $v0, str_index_error + + # create substring + move $a0, $t2 #length + addu $a0, $a0, 1 + li $v0, 9 #make space + syscall + # tenemos en $v0 la direccion del nuevo string + + addu $t5, $t5, $t3 + + subu $sp, $sp, 4 + sw $ra, 0($sp) + subu $sp, $sp, 4 + sw $fp, 0($sp) + move $fp,$sp + subu $sp, $sp, 4 + sw $v0, 0($sp) + subu $sp, $sp, 4 + sw $t5, 0($sp) + subu $sp, $sp, 4 + sw $t2, 0($sp) + + jal str_copy + move $sp,$fp + + lw $fp, 0($sp) + addi $sp,$sp, 4 + + lw $ra, 0($sp) + addi $sp,$sp, 4 + + addu $t9, $v0, $t2 #null terminated + sb $0, 0($t9) + jr $ra + + + #$a0 el prefijo, y en $a1, el str. + + str1_prefix_of_str2: + lb $t0, 0($a0) + lb $t1, 0($a1) + beqz $t0, prefixTrue + bne $t0, $t1, prefixFalse + addu $a0,$a0,1 + addu $a1,$a1,1 + b str1_prefix_of_str2 + prefixFalse: + li $v0, 0 + jr $ra + prefixTrue: + li $v0, 1 + jr $ra + + str_comparer: + move $a0, $a2 + move $a1, $ra + jal str_len #$v0=len(message1) + move $ra, $a1 + + move $s1, $v0 + + move $a0, $a3 + + move $a1, $ra + jal str_len #$v0=len(message2) + move $ra, $a1 + + beq $v0, $s1, string_length_comparer_end + li $v0, 0 + j string_comparer_end + + string_length_comparer_end: + move $a0, $a2 + move $a1, $a3 + move $s1, $ra + jal str1_prefix_of_str2 + move $ra, $s1 + string_comparer_end: + jr $ra + + case_conform: + move $s0, $a0 + move $s1, $a1 + START_CASE_LOOP: + + lw $a1, 0($s0) + + addi $s0, $s0, 4 + + move $t0, $s1 # Address of 1st element in array. + li $v0, 4 # System call code 4 (print_string). + li $t1, 0 # Initialize array offset. + + loop_INTERNAL: + + # Use the address mode label(register). + + lw $a0, 0($t0) # Load value at address str_array + $t1 (offset). + + beq $a0, $a1, END_CASE_LOOP + + addi $t0, $t0, 4 # Next element, i.e., increment offset by 4. + addi $t1, $t1, 4 # Next element, i.e., increment offset by 4. + + # Done or loop once more? + + ble $t1, $a2, loop_INTERNAL + b START_CASE_LOOP + END_CASE_LOOP: + move $v0, $a0 + jr $ra + + str_concat: + move $a3, $ra + jal str_len + move $ra, $a3 + + # guardamos en $t4, la longitud de str1 + move $t4, $v0 + # el str1 + move $t5, $a0 + move $a0, $a1 + move $t8, $a1 + + move $a3, $ra + jal str_len + move $ra, $a3 + + # reservamos espacio para el nuevo string + # guardamos en $t7 la longitud de str2 + move $t7, $v0 + addu $v0, $t4, $v0 + addu $v0, $v0, 1 + move $a0, $v0 + li $v0, 9 + syscall + + # en $t5 esta str1, y en $t8, str2------------------------- + + # save str1 part------------------------------------------ + # push $ra + subu $sp, $sp, 4 + sw $ra, 0($sp) + # push $fp + subu $sp, $sp, 4 + sw $fp, 0($sp) + + move $fp, $sp + + # push dest to copy pointer + subu $sp, $sp, 4 + sw $v0, 0($sp) + + # push copy from + subu $sp, $sp, 4 + sw $t5, 0($sp) + + # push how much to copy + subu $sp, $sp, 4 + sw $t4, 0($sp) + + jal str_copy + + move $sp, $fp + + lw $fp, 0($sp) + addu $sp, $sp, 4 + + lw $ra, 0($sp) + addu $sp, $sp, 4 + + # save str2 part------------- + # push $ra + subu $sp, $sp, 4 + sw $ra, 0($sp) + + # push $fp + subu $sp, $sp, 4 + sw $fp, 0($sp) + + move $fp, $sp + + # push where to copy + move $t9, $v0 + addu $t0, $v0, $t4 + subu $sp, $sp, 4 + sw $t0, 0($sp) + + # push copy from + subu $sp, $sp, 4 + sw $t8, 0($sp) + + subu $sp, $sp, 4 + sw $t7, 0($sp) + + jal str_copy + + move $sp, $fp + + lw $fp, 0($sp) + addu $sp, $sp, 4 + + lw $ra, 0($sp) + addu $sp, $sp, 4 + + addu $v0, $t7, $v0 + sb $0, 0($v0) + + move $v0, $t9 + jr $ra + ''') + + @visitor.when(Function) + def visit(self, function): + #ya se pusieron los argumentos en la pila + self.current_function = function + self.add(function.fname + ':') + + #ya se guardaron los argumentos en la pila + #tenemos que guardar espacio para las variables locales + + line = 'addi $sp, $sp, -' + str(4*len(function.localvars)) #espacio para las variables locales + self.add(line) + + self.add('addi $sp, $sp, -8') # adjust stack for 2 item + self.add('sw $ra, 4($sp)') # save return address + self.add('sw $fp, 0($sp)') # save old frame pointer + + n = 4*(len(function.params) + len(function.localvars) + 1) + self.add('addi $fp, $sp, {}'.format(n)) # fp apunta al primer argumento + + for intr in function.instructions: + self.visit(intr) + + #restaurar los valores de los registros + #poner el frame pointer donde estaba + + self.add('lw $ra, 4($sp)')#restauro direccion de retorno + self.add('lw $t1, 0($sp)') + self.add('addi $sp, $fp, 4') + self.add('move $fp, $t1') + + self.add('jr $ra') # and return + self.current_function = None + + @visitor.when(Arg) + def visit(self, arg): + self.add('#Arg') + self.add('addi $sp, $sp, -4') # adjust stack for 1 item + #localizar el valor de arg en las variables locales + index = self.stack_pos(arg.vinfo) + #pasarlo a un registro + self.add('lw $t1, {}($fp)'.format(index)) + self.add('sw $t1, 0($sp)') # save argument for next function + + @visitor.when(Call) + def visit(self, call): + #ya se pusieron los argumentos en la pila + self.add('#Call') + self.add('jal ' + call.func) + index = self.stack_pos(call.dest) + self.add('sw $v0, {}($fp)'.format(index)) + + @visitor.when(Dynamic_Call) + def visit(self, dynamic_Call): + #ya se pusieron los argumentos en la pila + self.add('#Dynamic_Call') + + index_left = self.stack_pos(dynamic_Call.left) + index_function = self.class_functions_list[dynamic_Call.ttype].index(dynamic_Call.func) + + self.add('lw $t0, {}($fp)'.format(index_left)) # Dir en el heap + self.add('lw $a0, 4($t0)') # Dispatch pointer + self.add('lw $a1, {}($a0)'.format(4*index_function)) # Load function + + self.add('jalr $a1') + + index = self.stack_pos(dynamic_Call.dest) + self.add('sw $v0, {}($fp)'.format(index)) + + @visitor.when(Load) + def visit(self, load): + index = self.stack_pos(load.dest) + self.add('#Load') + self.add('la $t1, {}'.format(load.msg)) + self.add('lw $t2, {}($fp)'.format(index)) #direccion en el heap + self.add('sw $t1, 8($t2)') + + @visitor.when(PrintStr) + def visit(self, _print): + self.add('#PrintStr') + self.add('li $v0, 4') # system call code for print_str + index = self.stack_pos(_print.str_addr) # pos en la pila + self.add('lw $t0, {}($fp)'.format(index)) # dir en el heap + self.add('lw $a0, 8($t0)') # str to print + self.add('syscall') # print it + + @visitor.when(PrintInt) + def visit(self, _print): + self.add('#PrintInt') + self.add('li $v0, 1') # system call code for print_int + index = self.stack_pos(_print.value) # pos en la pila de la instancia + self.add('lw $t0, {}($fp)'.format(index)) # dir en el heap + self.add('lw $a0, 8($t0)') # int to print + self.add('syscall') # print it + + @visitor.when(Return) + def visit(self, ret): + if not ret.value is None: + index = self.stack_pos(ret.value) + self.add('#Return') + self.add('lw $t1, {}($fp)'.format(index)) + self.add('move $v0, $t1') + + @visitor.when(ReadInt) + def visit(self, r): + #leer un int de la consola + self.add('li $v0, 5') + self.add('syscall') + #el valor esta en $v0 + index = self.stack_pos(r.dest) + self.add('move $t1, $v0') + self.add('sw $t1, {}($fp)'.format(index)) + + @visitor.when(TypeOf) + def visit(self, typeOf): + self.add('#typeOf') + index1 = self.stack_pos(typeOf.var) # pos en la pila + self.add('lw $s0, {}($fp)'.format(index1)) # dir en el heap + self.add('lw $t1, 0($s0)') # dir del array + self.add('lw $t2, 0($t1)') # primer elemento del array + ## $t1 = typeOf + + #el valor esta en $t1 + index = self.stack_pos(typeOf.dest) + self.add('sw $t2, {}($fp)'.format(index)) + + @visitor.when(Length) + def visit(self, length): + self.add('#str_Length') + index1 = self.stack_pos(length.str_addr) # pos en la pila + self.add('lw $s0, {}($fp)'.format(index1)) # dir en el heap + self.add('lw $a0, 8($s0)') + + self.add('jal str_len') + + #el valor esta en $v0 + index = self.stack_pos(length.dest) + self.add('sw $v0, {}($fp)'.format(index)) + + @visitor.when(Concat) + def visit(self, concat): + self.add('#concat')# Recibe en $a0 str1 y en $a1 str2 + index_str1 = self.stack_pos(concat.head) + index_str2 = self.stack_pos(concat.tail) + index_dest = self.stack_pos(concat.dest) + + self.add('lw $s0, {}($fp)'.format(index_str1)) # dir en el heap + self.add('lw $a0, 8($s0)') + + self.add('lw $s0, {}($fp)'.format(index_str2)) # dir en el heap + self.add('lw $a1, 8($s0)') + + self.add('jal str_concat') + + #el str esta en $v0 + self.add('sw $v0, {}($fp)'.format(index_dest)) + + @visitor.when(Substring) + def visit(self, substring): + self.add('#substring') + index1 = self.stack_pos(substring.str_addr) # pos en la pila + self.add('lw $s0, {}($fp)'.format(index1)) # dir en el heap + self.add('lw $a0, 8($s0)') + index2 = self.stack_pos(substring.pos) + self.add('lw $s0, {}($fp)'.format(index2)) # dir en el heap + self.add('lw $a1, 8($s0)') + index3 = self.stack_pos(substring.length) + self.add('lw $s0, {}($fp)'.format(index3)) # dir en el heap + self.add('lw $a2, 8($s0)') + + self.add('jal str_substring') + + #el str esta en $v0 + index = self.stack_pos(substring.dest) + self.add('sw $v0, {}($fp)'.format(index)) + + @visitor.when(EqualStrThanStr) + def visit(self, equalStrThanStr): + self.add('#string_comparer') + index1 = self.stack_pos(equalStrThanStr.left) # pos en la pila + index2 = self.stack_pos(equalStrThanStr.right) # pos en la pila + index = self.stack_pos(equalStrThanStr.dest) # pos en la pila + self.add('lw $s0, {}($fp)'.format(index1)) # dir en el heap + self.add('lw $a2, 8($s0)') + self.add('lw $s0, {}($fp)'.format(index2)) # dir en el heap + self.add('lw $a3, 8($s0)') + + self.add('jal str_comparer') + + #el resultado esta en $v0 + self.add('lw $s0, {}($fp)'.format(index)) # dir en el heap + self.add('sw $v0, 8($s0)') # store bool result + + @visitor.when(ReadStr) + def visit(self, r): + #leer string de la consola + self.add('#read_string') + self.add('li $v0, 9') #make space + self.add('li $a0, 100') #space=100 + self.add('syscall') + + self.add('move $a0, $v0') #buffer + self.add('li $v0, 8') #input str syscall + self.add('la $a1, 100') + self.add('syscall') + self.add('move $t5, $a0') #$t5=buffer(str start) + + self.add('move $a3, $ra') + self.add('jal str_len') #$v0=len(message2) + self.add('move $ra, $a3') + + self.add('subu $v0, $v0, 1') + self.add('addu $v1, $v0, $t5') + self.add('sb $0, 0($v1)') #null terminated + self.add('move $v0, $t5') #$v0=buffer + + #el buffer esta en $v0 + index = self.stack_pos(r.dest) + self.add('sw $v0, {}($fp)'.format(index)) + + @visitor.when(GetAttrib) + def visit(self, get): + + index = self.stack_pos(get.instance) + self.add('#GetAttrib') + self.add('lw $s1, {}($fp)'.format(index)) + self.add('lw $s0, {}($s1)'.format(4*get.attribute + 8)) #????????????????????????????? + index = self.stack_pos(get.dest) + self.add('sw $s0, {}($fp)'.format(index)) + + @visitor.when(SetAttrib) + def visit(self, _set): + + index = self.stack_pos(_set.instance) + self.add('#SetAttrib') + self.add('lw $s1, {}($fp)'.format(index)) #s1 = this + + if isinstance(_set.value, int): + self.add('li $s0, {}'.format(_set.value)) + + else: + index = self.stack_pos(_set.value) + self.add('lw $s0, {}($fp)'.format(index)) + + self.add('sw $s0, {}($s1)'.format(4*_set.attribute + 8)) #this.data = data + + @visitor.when(Allocate) + def visit(self, allocate): + #malloc(4) + #cuantos atributos tiene el objeto(1) + #devolver direccion de inicio del objeto + + sizeof = self.attributes[allocate.ttype]*4 + 8 # + tag_name + Dispatch pointer + self.add('#---Allocate-----') + self.add('addiu $a0, $zero, {}'.format(sizeof)) #call sbrk(sizeof(Object)) + self.add('li $v0, 9') #set syscall code for sbrk + self.add('syscall') + + #en $v0 se encuentra la direccion de inicio del objeto + self.add('addu $s1, $zero, $v0') #s1=this + + ############################################################################################# + + count = len(self.class_functions_list[allocate.ttype]) + sizeof_dispatch = count*4 #Dispatch size + self.add('addiu $a0, $zero, {}'.format(sizeof_dispatch)) #call sbrk(sizeof(Object)) + self.add('li $v0, 9') #set syscall code for sbrk + self.add('syscall') + + #en $v0 se encuentra la direccion de inicio del objeto + self.add('addu $s0, $zero, $v0') + for i in range(count): + self.add('la $a0, {}'.format(self.class_functions_list[allocate.ttype][i])) + self.add('sw $a0, {}($s0)'.format(4*i)) + self.add('sw $s0, 4($s1)') #$s1[1]=Dispatch pointer + + ############################################################################################# + + #class tag + self.add('la $a0, {}'.format(allocate.ttype + '_conforms_to')) + self.add('sw $a0, 0($s1)') #$s1[0]=tag_name + + index = self.stack_pos(allocate.dest) + self.add('sw $s1, {}($fp)'.format(index)) + + @visitor.when(Copy) + def visit(self, copy): + index_dest = self.stack_pos(copy.dest) + index_source = self.stack_pos(copy.source) + self.add('lw $t0, {}($fp)'.format(index_source)) + self.add('sw $t0, {}($fp)'.format(index_dest)) + + @visitor.when(Assign) + def visit(self, assign): + + index1 = self.stack_pos(assign.source) + self.add('#Assign:' + assign.type) + self.add('lw $t0, {}($fp)'.format(index1)) #copia cada argumento + + index2 = self.stack_pos(assign.dest) + self.add('lw $t1, {}($fp)'.format(index2)) + + n = self.attributes[assign.type] + + for i in range(n): + self.add('lw $s0, {}($t0)'.format(4*(i+2))) #---error + self.add('sw $s0, {}($t1)'.format(4*(i+2))) #---error + + @visitor.when(Plus) + def visit(self, plus): + index = self.stack_pos(plus.left) + self.add('#Plus') + self.add('lw $t0, {}($fp)'.format(index)) #direccion en el heap del int + self.add('lw $t1, 8($t0)') #valor del int + index = self.stack_pos(plus.right) + self.add('lw $t0, {}($fp)'.format(index)) + self.add('lw $t2, 8($t0)') + self.add('add $t1, $t1, $t2') #$t1 = a + b + index = self.stack_pos(plus.dest) + self.add('lw $t0, {}($fp)'.format(index)) + self.add('sw $t1, 8($t0)') + + @visitor.when(Minus) + def visit(self, minus): + index = self.stack_pos(minus.left) + self.add('#Minus') + self.add('lw $t0, {}($fp)'.format(index)) #direccion en el heap del int + self.add('lw $t1, 8($t0)') #valor del int + index = self.stack_pos(minus.right) + self.add('lw $t0, {}($fp)'.format(index)) + self.add('lw $t2, 8($t0)') + self.add('sub $t1, $t1, $t2') #$t1 = a - b + index = self.stack_pos(minus.dest) + self.add('lw $t0, {}($fp)'.format(index)) + self.add('sw $t1, 8($t0)') + + @visitor.when(Star) + def visit(self, star): + index = self.stack_pos(star.left) + self.add('#Star') + self.add('lw $t0, {}($fp)'.format(index)) #direccion en el heap del int + self.add('lw $t1, 8($t0)') #valor del int + index = self.stack_pos(star.right) + self.add('lw $t0, {}($fp)'.format(index)) + self.add('lw $t2, 8($t0)') + self.add('mul $t1, $t1, $t2') #$t1 = a * b + index = self.stack_pos(star.dest) + self.add('lw $t0, {}($fp)'.format(index)) + self.add('sw $t1, 8($t0)') + + @visitor.when(Div) + def visit(self, div): + index = self.stack_pos(div.left) + self.add('#Div') + self.add('lw $t0, {}($fp)'.format(index)) #direccion en el heap del int + self.add('lw $t1, 8($t0)') #valor del int + index = self.stack_pos(div.right) + self.add('lw $t0, {}($fp)'.format(index)) + self.add('lw $t2, 8($t0)') + + self.add('div $t1, $t1, $t2') #$t1 = a / b + + index = self.stack_pos(div.dest) + self.add('lw $t0, {}($fp)'.format(index)) + self.add('sw $t1, 8($t0)') + + @visitor.when(Label) + def visit(self, label): + self.add('#Label') + self.add(label.name + ':') + + @visitor.when(Goto) + def visit(self, goto): + self.add('#Goto') + self.add('j ' + goto.name) + + @visitor.when(GotoIf) + def visit(self, goto_if): + index = self.stack_pos(goto_if.condition) + self.add('#GotoIf') + self.add('lw $t0, {}($fp)'.format(index)) #direccion en el heap + self.add('lw $t1, 8($t0)') #value + self.add('bnez $t1, {}'.format(goto_if.label)) #Branch on Not Equal Zero + + @visitor.when(EqualThan) + def visit(self, equal): + index = self.stack_pos(equal.dest) + index_left = self.stack_pos(equal.left) + index_right = self.stack_pos(equal.right) + + self.add('#EqualThan') + self.add('lw $t2, {}($fp)'.format(index_left)) + self.add('lw $t3, {}($fp)'.format(index_right)) + + self.add('lw $a0, 8($t2)') + self.add('lw $a1, 8($t3)') + + label = 'eq_false_' + str(self.idCount) + self.idCount += 1 + + self.add('li $t1, 0') #false + self.add('bne $a0, $a1 {}'.format(label)) + self.add('li $t1, 1') #true + self.add(label + ':') + self.add('lw $t0, {}($fp)'.format(index)) + self.add('sw $t1, 8($t0)') + + @visitor.when(LowerThan) + def visit(self, equal): + index = self.stack_pos(equal.dest) + index_left = self.stack_pos(equal.left) + index_right = self.stack_pos(equal.right) + + self.add('#LowerThan') + self.add('lw $t2, {}($fp)'.format(index_left)) + self.add('lw $t3, {}($fp)'.format(index_right)) + + self.add('lw $a0, 8($t2)') + self.add('lw $a1, 8($t3)') + + label = 'eq_false_' + str(self.idCount) + self.idCount += 1 + + self.add('li $t1, 0') #false + self.add('bge $a0, $a1 {}'.format(label)) + self.add('li $t1, 1') #true + self.add(label + ':') + self.add('lw $t0, {}($fp)'.format(index)) + self.add('sw $t1, 8($t0)') + + @visitor.when(LowerEqualThan) + def visit(self, equal): + index = self.stack_pos(equal.dest) + index_left = self.stack_pos(equal.left) + index_right = self.stack_pos(equal.right) + + self.add('#LowerEqualThan') + self.add('lw $t2, {}($fp)'.format(index_left)) + self.add('lw $t3, {}($fp)'.format(index_right)) + + self.add('lw $a0, 8($t2)') #load atributo1 + self.add('lw $a1, 8($t3)') #load atributo2 + + label = 'eq_false_' + str(self.idCount) + self.idCount += 1 + + self.add('li $t1, 0') #false + self.add('bgt $a0, $a1 {}'.format(label)) + self.add('li $t1, 1') #true + self.add(label + ':') + self.add('lw $t0, {}($fp)'.format(index)) + self.add('sw $t1, 8($t0)') + + @visitor.when(Array) + def visit(self, array): + line = array.name + ': .word ' + array.data_list[0] #str_array: .word one, two, three + n = len(array.data_list) + for i in range(1, n): + line += ', ' + array.data_list[i] + self.lines.insert(1, line) + + @visitor.when(GetParent) + def visit(self, get_parent): + self.add('#GetParent') + index = self.stack_pos(get_parent.instance) + index_dest = self.stack_pos(get_parent.dest) + self.add('lw $s1, {}($fp)'.format(index)) + self.add('lw $a0, 0($s1)') + self.add('la $a1, {}'.format(get_parent.array_name)) + self.add('li $a2, {}'.format((get_parent.length - 1) * 4)) + + self.add('jal case_conform') + + #el valor esta en $v0 + self.add('sw $v0, {}($fp)'.format(index_dest)) + + @visitor.when(EndProgram) + def visit(self, end): + self.add('#EndProgram') + self.add('li $v0, 4') # system call code for print_str + self.add('la $a0, p_error') # buscar el tag + self.add('syscall') # print it + + #self.add('li $v0, 4') # system call code for print_str + #self.add('la $a0, ' + end.type) # buscar el tag + #self.add('syscall') # print it + + self.add('li $v0, 10') + self.add('syscall') + + @visitor.when(Exit) + def visit(self, exit): + self.add('#Exit') + self.add('li $v0, 10') + self.add('syscall') + diff --git a/src/codegenTest.py b/src/codegenTest.py new file mode 100644 index 000000000..1d7c023fc --- /dev/null +++ b/src/codegenTest.py @@ -0,0 +1,36 @@ +import lexer_rules +import parser_rules +import sys + +from ply.lex import lex +from ply.yacc import yacc +from semantic_rules import Semantic +import cool_to_cil, cil_to_mips + +def run(addr): + lexer = lex(module=lexer_rules) + parser = yacc(module=parser_rules) + sem = Semantic() + + with open(addr, encoding = "utf-8") as f: + text = f.read() + + ast = parser.parse(text, lexer) + sem.visit(ast) + + cil = cool_to_cil.Build_CIL(ast, sem) + + mips = cil_to_mips.Build_Mips(cil.astCIL, sem, cil.class_functions_list) + + out_file = addr.split(".") + out_file[-1] = "mips" + out_file = ".".join(out_file) + + mips_code = '' + for line in mips.lines: + mips_code += line + '\n' + + with open(out_file, 'w') as f: + f.write(mips_code) + f.close() + exit(0) diff --git a/src/compiling.py b/src/compiling.py new file mode 100644 index 000000000..2dc8d4590 --- /dev/null +++ b/src/compiling.py @@ -0,0 +1,9 @@ +import sys, lexerTest, parserTest, semanticTest, codegenTest + +addr = None +addr = sys.argv[1] + +lexerTest.run(addr) +parserTest.run(addr) +semanticTest.run(addr) +codegenTest.run(addr) diff --git a/src/cool_to_cil.py b/src/cool_to_cil.py new file mode 100644 index 000000000..3d735a0e6 --- /dev/null +++ b/src/cool_to_cil.py @@ -0,0 +1,744 @@ +import visitor as visitor +from AST import * +import AST_CIL, basic_classes + +class Build_CIL: + def __init__(self, ast, sem): + self.end_line = {} + self.info = sem + self.current_method = None + self.const_1 = 'const_1' + self.null_const = 'const_0' + self.idCount = 0 + self.astCIL = AST_CIL.Program() + self._self = Var('self', 'SELF_TYPE') + self._self.line = 0 + self._self.index = 0 + self.local_variables = [self._self] + self.constructor = {} + self.classmethods = {} + self.class_functions_list = {} + self.class_attrs = {} + for item in sem.class_attrs.items(): + self.class_attrs[item[0]] = [x.id for x in item[1]] + + self.BFS(sem.graph, sem.classmethods_original, sem.classmethods) + self.compute_function_list(sem.classmethods_original, sem.graph) + + self.visit(ast, self.astCIL) + + def BFS(self, graph, class_methods_original, inherits_methods): + self.classmethods[('Object', 'abort')] = 'function_Object_abort' + self.classmethods[('Object', 'type_name')] = 'function_Object_type_name' + self.classmethods[('Object', 'copy')] = 'function_Object_copy' + self.classmethods[('IO', 'out_string')] = 'function_IO_out_string' + self.classmethods[('IO', 'out_int')] = 'function_IO_out_int' + self.classmethods[('IO', 'in_string')] = 'function_IO_in_string' + self.classmethods[('IO', 'in_int')] = 'function_IO_in_int' + self.classmethods[('String', 'length')] = 'function_String_length' + self.classmethods[('String', 'concat')] = 'function_String_concat' + self.classmethods[('String', 'substr')] = 'function_String_substr' + for c, methods in class_methods_original.items(): + for m in methods: + self.classmethods[(c, m)] = 'function_' + c + '_' + m + + l = ['Object'] + while len(l) > 0: + temp = l.pop(0) + if not graph.__contains__(temp): continue + for _class in graph[temp]: + l.append(_class) + for function in inherits_methods[temp]: + if self.classmethods.__contains__((_class, function)): continue + self.classmethods[(_class, function)] = self.classmethods[(temp, function)] + + def compute_function_list(self, class_methods_original, graph): + for c, methods in class_methods_original.items(): + for m in methods: + fname = 'function_' + c + '_' + m + if not self.class_functions_list.__contains__(c): + self.class_functions_list[c] = [] + self.class_functions_list[c].append(fname) + + l = ['Object'] + while len(l) > 0: + + temp = l.pop(0) + + if not graph.__contains__(temp): continue + + for _class in graph[temp]: + + l.append(_class) + index = 0 + if not self.class_functions_list.__contains__(_class): + self.class_functions_list[_class] = [] + + for function in self.class_functions_list[temp]: #lista de funciones de parent + + k = len(temp) + end = len(function) + fname = 'function_' + _class + '_' + function[10 + k : end] + + if self.class_functions_list[_class].__contains__(fname): + self.class_functions_list[_class].remove(fname) + self.class_functions_list[_class].insert(index, fname) #is a original function + else: + self.class_functions_list[_class].insert(index, function) #is a inherit function + + index += 1 + + def get_local(self): + dest = 'local_' + str(self.idCount) + self.idCount += 1 + return dest + + def get_label(self): + label = 'label' + str(self.idCount) + self.idCount+=1 + return label + + @visitor.on('node') + def visit(self, node, nodeCIL): + pass + + @visitor.when(Program) + def visit(self, program, programCIL): + + basic_classes.Build(self.astCIL.code_section, self.astCIL.type_section) + func = 'main' + f = AST_CIL.Function(func) + + self_instance = 'self' #crear instancia de un valor entero + false_local = 'false' + + intr = AST_CIL.Call(self_instance, 'function_Main___init__') + + intr2 = AST_CIL.Arg(self_instance) + intr3 = AST_CIL.Call(false_local, 'function_Main_main') + intr4 = AST_CIL.Exit() + + f.instructions += [intr, intr2, intr3, intr4] + f.localvars += [false_local, self_instance] + self.astCIL.code_section.insert(0, f) + + for c in program.classes: + self.visit(c, programCIL) + + @visitor.when(Class) + def visit(self, _class, programCIL): + + #clase que estoy visitando + self.current_class = _class.name + + #crear el tipo correspondiente + _type = AST_CIL.Type(_class.name) + self.current_method = None + func = 'function' + '_' + self.current_class + '_' + '__init__' # ---> devuelve una instancia de la clase + _type.methods ['__init__'] = func # ---> añado el metodo init a la clase + f = AST_CIL.Function(func) + + self_instance = 'self' # ---> reservo espacio en el heap para self + intr = AST_CIL.Allocate(self_instance, _class.name) # ---> reservo para la cant de atributos del Main + + #f.instructions.append(intr) + f.localvars += [self_instance] + + ################################# + #visito los atributos + index = 0 + for att in _class.attributes: + + attribute_instance = self.visit(att, f) + + _intr = AST_CIL.SetAttrib(self_instance, index, attribute_instance) + + #f.instructions.insert(1, _intr) #<------cambio + f.instructions.append(_intr) #<------comment + + index += 1 + ################################# + + f.instructions.insert(0, intr) + + f.instructions.append(AST_CIL.Return(self_instance)) + + ########################################## + local = self.const_1 + intr1 = AST_CIL.Allocate(local, 'Int') + intr2 = AST_CIL.SetAttrib(local, 0, 1) + f.instructions.insert(0, intr2) + f.instructions.insert(0, intr1) + f.localvars.append(local) + + local2 = self.null_const + intr3 = AST_CIL.Allocate(local2, 'Int') + intr4 = AST_CIL.SetAttrib(local2, 0, 0) + f.instructions.insert(0, intr4) + f.instructions.insert(0, intr3) + f.localvars.append(local2) + ######################################### + + self.astCIL.code_section.append(f) + + #visito los metodos + for m in _class.methods: self.visit(m, _type) + programCIL.type_section.append(_type) + self.current_class = None + + @visitor.when(Method) + def visit(self, method, typeCIL): + self.current_method = method.id + func = 'function' + '_' + self.current_class + '_' + method.id + typeCIL.methods[method.id] = func + f = AST_CIL.Function(func) + f.params.insert(0, 'self') + + for arg in method.parameters: + f.params.append(arg.id) + + result = self.visit(method.expression, f) + + f.instructions.append(AST_CIL.Return(result)) + + ########################################## + local = self.const_1 + intr = AST_CIL.Allocate(local, 'Int') + intr2 = AST_CIL.SetAttrib(local, 0, 1) + f.instructions.insert(0, intr2) + f.instructions.insert(0, intr) + f.localvars.append(local) + + local2 = self.null_const + intr3 = AST_CIL.Allocate(local2, 'Int') + intr4 = AST_CIL.SetAttrib(local2, 0, 0) + f.instructions.insert(0, intr4) + f.instructions.insert(0, intr3) + f.localvars.append(local2) + ######################################### + + self.astCIL.code_section.append(f) + self.local_variables.clear() + self.local_variables.append(self._self) + self.current_method = None + + @visitor.when(Boolean) + def visit(self, _bool, functionCIL): + instance = self.get_local() + intr1 = AST_CIL.Allocate(instance, 'Bool') + value = 0 + if _bool.value == 'true': value = 1 + intr2 = AST_CIL.SetAttrib(instance, 0, value) + functionCIL.localvars.append(instance) + functionCIL.instructions.insert(0, intr2) + functionCIL.instructions.insert(0, intr1) + return instance + + @visitor.when(Interger) + def visit(self, _int, functionCIL): + instance = self.get_local() + intr1 = AST_CIL.Allocate(instance, 'Int') + intr2 = AST_CIL.SetAttrib(instance, 0, _int.value) + functionCIL.localvars.append(instance) + functionCIL.instructions.insert(0, intr2) + functionCIL.instructions.insert(0, intr1) + return instance + + @visitor.when(NewType) + def visit(self, _newType, functionCIL): + instance = self.get_local() + intr1 = AST_CIL.Allocate(instance, _newType.type_name) + + if _newType.type_name == 'Int' or _newType.type_name == 'Bool': + intr2 = AST_CIL.SetAttrib(instance, 0, 0) + functionCIL.instructions.insert(0, intr2) + + elif not _newType.type_name == 'IO' and not _newType.type_name == 'Object' and not _newType.type_name == 'String': + intr3 = AST_CIL.Call(instance, 'function_' + _newType.type_name + '_' + '__init__') + functionCIL.instructions.append(intr3) + + functionCIL.localvars.append(instance) + functionCIL.instructions.insert(0, intr1) + return instance + + @visitor.when(String) + def visit(self, string, functionCIL): + tag = 's' + str(len(self.astCIL.data_section)) + n = len(string.value) + if n > 0 and string.value.__contains__('\n'): #[n-1] == '\n': + s = string.value.replace("\n", '\\n') + s = '\"' + s + '\"' + else: s = '"' + string.value + '"' + + # self.astCIL.data_section[s] = tag + if self.astCIL.data_section.__contains__(s): + tag = self.astCIL.data_section[s] + else: self.astCIL.data_section[s] = tag + + instance = self.get_local() + intr1 = AST_CIL.Allocate(instance, 'String') + intr2 = AST_CIL.Load(instance, tag) + + functionCIL.localvars.append(instance) + functionCIL.instructions.insert(0, intr2) + functionCIL.instructions.insert(0, intr1) + return instance + + @visitor.when(StaticDispatch) + def visit(self, static_dispatch, functionCIL): + dest = 'local_' + str(self.idCount) + self.idCount += 1 + + args_list = [] + for item in static_dispatch.parameters: args_list.append(self.visit(item, functionCIL)) + + result = self.visit(static_dispatch.left_expression, functionCIL) + + functionCIL.instructions.append(AST_CIL.Arg(result)) #Bind self + + for item in args_list: functionCIL.instructions.append(AST_CIL.Arg(item)) + + intr = AST_CIL.Call(dest, self.classmethods[(static_dispatch.parent_type, static_dispatch.func_id)]) + #intr = AST_CIL.Dynamic_Call(dest, static_dispatch.left_expression.static_type, self.classmethods[(static_dispatch.left_expression.static_type, static_dispatch.func_id)], result) + + functionCIL.localvars.append(dest) + + functionCIL.instructions.append(intr) + + return dest + + @visitor.when(Dispatch) + def visit(self, dispatch, functionCIL): + dest = 'local_' + str(self.idCount) + self.idCount += 1 + args_list = [] + for item in dispatch.parameters: args_list.append(self.visit(item, functionCIL)) + if dispatch.left_expression is None: + functionCIL.instructions.append(AST_CIL.Arg('self')) + for item in args_list: functionCIL.instructions.append(AST_CIL.Arg(item)) + + #intr = AST_CIL.Call(dest, self.classmethods[(self.current_class, dispatch.func_id)]) + intr = AST_CIL.Dynamic_Call(dest, self.current_class, self.classmethods[(self.current_class, dispatch.func_id)], 'self') + + functionCIL.localvars.append(dest) + functionCIL.instructions.append(intr) + + else: + result = self.visit(dispatch.left_expression, functionCIL) + + functionCIL.instructions.append(AST_CIL.Arg(result)) #Bind self + + for item in args_list: functionCIL.instructions.append(AST_CIL.Arg(item)) + + #intr = AST_CIL.Call(dest, self.classmethods[(dispatch.left_expression.static_type, dispatch.func_id)]) + intr = AST_CIL.Dynamic_Call(dest, dispatch.left_expression.static_type, self.classmethods[(dispatch.left_expression.static_type, dispatch.func_id)], result) + + functionCIL.localvars.append(dest) + functionCIL.instructions.append(intr) + return dest + + @visitor.when(Block) + def visit(self, block, functionCIL): + n = len(block.expressions) - 1 + for i in range(n): self.visit(block.expressions[i], functionCIL) + result = self.visit(block.expressions[n], functionCIL) + return result + + @visitor.when(LetVar) + def visit(self, let, functionCIL): + for item in let.declarations: + self.visit(item, functionCIL) + self.local_variables.append(item) + result = self.visit(let.in_expression, functionCIL) + + n = len(let.declarations) + m = len(self.local_variables) + for i in range(n): self.local_variables.pop(m - i - 1) + return result + + @visitor.when(Attribute)#ok + def visit(self, attr, functionCIL): + + #declara un nuevo objeto y le asigna un valor + + result = self.visit(attr.expr, functionCIL) + + instance = attr.id + '_' + str(attr.line) + '_' + str(attr.index) #creo una instancia con el nombre del atributo + + #intr1 = AST_CIL.Allocate(instance, attr.type) # <--------- change + #functionCIL.instructions.insert(0, intr1) # <--------- change + #intr2 = AST_CIL.Assign(instance, attr.type, result) # <--------- change + + #change------------------------------------------------------------------- + + intr2 = AST_CIL.Copy(instance, result) + + # ---> poner los atributos en su indice + functionCIL.localvars.append(instance) + + functionCIL.instructions.append(intr2) + + return instance + + @visitor.when(Var) + def visit(self, var, functionCIL): #expr --> ID : TYPE + #declara un nuevo objeto y le asigna un valor inicial + instance = var.id + '_' + str(var.line) + '_' + str(var.index) + intr1 = AST_CIL.Allocate(instance, var.type) + + if var.type == 'Int' or var.type == 'Bool': + intr2 = AST_CIL.SetAttrib(instance, 0, 0) + functionCIL.instructions.insert(0, intr2) + + elif var.type == 'String': + tag = 's' + str(len(self.astCIL.data_section)) + s = '""' + # self.astCIL.data_section[s] = tag + if self.astCIL.data_section.__contains__(s): + tag = self.astCIL.data_section[s] + else: self.astCIL.data_section[s] = tag + + intr1 = AST_CIL.Allocate(instance, 'String') + intr2 = AST_CIL.Load(instance, tag) + functionCIL.instructions.insert(0, intr2) + + functionCIL.localvars.append(instance) + functionCIL.instructions.insert(0, intr1) + return instance + + @visitor.when(Type) #expr --> ID + def visit(self, _type, functionCIL): + if _type.name == 'self': return 'self' + n = len(self.local_variables) - 1 + + #variable local(let, case) ok + for i in range(n, -1, -1): + local_id = self.local_variables[i].id + if local_id == _type.name: + return local_id + '_' + str(self.local_variables[i].line) + '_' + str(self.local_variables[i].index) + + #parametro del metodo + if not self.current_method is None: + for arg_id in functionCIL.params: + if arg_id == _type.name: + return arg_id + + #cuando es un atributo global + d = self.get_local() + intr = AST_CIL.GetAttrib(d , 'self', self.class_attrs[self.current_class].index(_type.name)) + functionCIL.localvars.append(d) + functionCIL.instructions.append(intr) + return d + + @visitor.when(Plus) + def visit(self, plus, functionCIL): + #d = 'temp' + + d = self.get_local() + + #if not d in functionCIL.localvars: + + functionCIL.localvars.append(d) + intr1 = AST_CIL.Allocate(d, 'Int') + functionCIL.instructions.insert(0, intr1) + + a = self.visit(plus.first, functionCIL) + b = self.visit(plus.second, functionCIL) + intr = AST_CIL.Plus(d, a, b) + functionCIL.instructions.append(intr) + return d + + @visitor.when(Minus) + def visit(self, minus, functionCIL): + #d = 'temp' + + d = self.get_local() + + #if not d in functionCIL.localvars: + + functionCIL.localvars.append(d) + intr1 = AST_CIL.Allocate(d, 'Int') + functionCIL.instructions.insert(0, intr1) + + a = self.visit(minus.first, functionCIL) + b = self.visit(minus.second, functionCIL) + intr = AST_CIL.Minus(d, a, b) + functionCIL.instructions.append(intr) + return d + + @visitor.when(Div) + def visit(self, div, functionCIL): + #d = 'temp' + + d = self.get_local() + + # if not d in functionCIL.localvars: + + functionCIL.localvars.append(d) + intr1 = AST_CIL.Allocate(d, 'Int') + functionCIL.instructions.insert(0, intr1) + + a = self.visit(div.first, functionCIL) + b = self.visit(div.second, functionCIL) + intr = AST_CIL.Div(d, a, b) + functionCIL.instructions.append(intr) + return d + + @visitor.when(Star) + def visit(self, star, functionCIL): + #d = 'temp' + + d = self.get_local() + + # if not d in functionCIL.localvars: + + functionCIL.localvars.append(d) + intr1 = AST_CIL.Allocate(d, 'Int') + functionCIL.instructions.insert(0, intr1) + + a = self.visit(star.first, functionCIL) + b = self.visit(star.second, functionCIL) + + intr = AST_CIL.Star(d, a, b) + functionCIL.instructions.append(intr) + return d + + @visitor.when(Assign) + def visit(self, assign, functionCIL): + result = self.visit(assign.expression, functionCIL) + n = len(self.local_variables) - 1 + + for i in range(n, -1, -1): + local_id = self.local_variables[i].id + local_type = self.local_variables[i].type + if local_id == assign.id: + local = local_id + '_' + str(self.local_variables[i].line) + '_' + str(self.local_variables[i].index) + intr = AST_CIL.Assign(local, local_type, result) + functionCIL.instructions.append(intr) + return local + + if not self.current_method is None: + for arg_id, arg_type in self.info.classmethods[self.current_class][self.current_method][0]: + if arg_id == assign.id: + intr = AST_CIL.Assign(arg_id, arg_type, result) + functionCIL.instructions.append(intr) + return arg_id + + if assign.expression.static_type == 'Int': + d = self.get_local() + functionCIL.localvars.append(d) + intr_1 = AST_CIL.Allocate(d, 'Int') + intr_2 = AST_CIL.Assign(d, 'Int', result) + intr = AST_CIL.SetAttrib('self' , self.class_attrs[self.current_class].index(assign.id), d) + functionCIL.instructions.insert(0, intr_1) + functionCIL.instructions.append(intr_2) + functionCIL.instructions.append(intr) + else: + intr = AST_CIL.SetAttrib('self' , self.class_attrs[self.current_class].index(assign.id), result) + functionCIL.instructions.append(intr) + + return result + + @visitor.when(EqualThan) + def visit(self, equalThan, functionCIL): + d = self.get_local() + functionCIL.localvars.append(d) + intr1 = AST_CIL.Allocate(d, 'Bool') + functionCIL.instructions.insert(0, intr1) + a = self.visit(equalThan.first, functionCIL) + b = self.visit(equalThan.second, functionCIL) + + if equalThan.first.static_type == 'String': + intr=AST_CIL.EqualStrThanStr(d, a, b) + functionCIL.instructions += [intr] + else: + intr = AST_CIL.EqualThan(d, a, b) + functionCIL.instructions += [intr] + return d + + @visitor.when(LowerThan) + def visit(self, lower, functionCIL): + d = self.get_local() + functionCIL.localvars.append(d) + intr1 = AST_CIL.Allocate(d, 'Bool') + functionCIL.instructions.insert(0, intr1) + a = self.visit(lower.first, functionCIL) + b = self.visit(lower.second, functionCIL) + intr = AST_CIL.LowerThan(d, a, b) + functionCIL.instructions += [intr] + return d + + @visitor.when(LowerEqualThan) + def visit(self, lowerEq, functionCIL): + d = self.get_local() + functionCIL.localvars.append(d) + intr1 = AST_CIL.Allocate(d, 'Bool') + functionCIL.instructions.insert(0, intr1) + a = self.visit(lowerEq.first, functionCIL) + b = self.visit(lowerEq.second, functionCIL) + intr = AST_CIL.LowerEqualThan(d, a, b) + functionCIL.instructions += [intr] + return d + + @visitor.when(Not) + def visit(self, neg, functionCIL): + d = self.get_local() + functionCIL.localvars.append(d) + intr1 = AST_CIL.Allocate(d, 'Bool') + functionCIL.instructions.insert(0, intr1) + a = self.visit(neg.expr, functionCIL) + intr = AST_CIL.Minus(d, self.const_1, a) + functionCIL.instructions.append(intr) + return d + + @visitor.when(IntegerComplement) + def visit(self, integerComplement, functionCIL): + d = self.get_local() + functionCIL.localvars.append(d) + intr1 = AST_CIL.Allocate(d, 'Int') + functionCIL.instructions.insert(0, intr1) + a = self.visit(integerComplement.expression, functionCIL) + intr = AST_CIL.Minus(d, self.null_const, a) + functionCIL.instructions.append(intr) + return d + + @visitor.when(IsVoid) + def visit(self, isVoid, functionCIL): + d = self.get_local() + functionCIL.localvars.append(d) + intr1 = AST_CIL.Allocate(d, 'Bool') + intr2 = AST_CIL.SetAttrib(d, 0, 0) + functionCIL.instructions.insert(0, intr2) + functionCIL.instructions.insert(0, intr1) + a = self.visit(isVoid.expression, functionCIL) + return d + + @visitor.when(Loop) + def visit(self, loop, functionCIL): + + start = 'START_' + self.get_label() + do = 'DO_' + self.get_label() + end = 'END_' + self.get_label() + + intr1 = AST_CIL.Label(start) + functionCIL.instructions.append(intr1) + w = self.visit(loop.while_expression, functionCIL) + intr2 = AST_CIL.GotoIf(w, do) + functionCIL.instructions.append(intr2) + intr3 = AST_CIL.Goto(end) + functionCIL.instructions.append(intr3) + intr4 = AST_CIL.Label(do) + functionCIL.instructions.append(intr4) + l = self.visit(loop.loop_expression, functionCIL) + intr5 = AST_CIL.Goto(start) + functionCIL.instructions.append(intr5) + intr6 = AST_CIL.Label(end) + functionCIL.instructions.append(intr6) + return l + + @visitor.when(Conditional) + def visit(self, cond, functionCIL): + then = self.get_label() + fi = self.get_label() + dest = self.get_local() + functionCIL.localvars.append(dest) + + # intr = AST_CIL.Allocate(dest, cond.static_type) + # functionCIL.instructions.insert(0, intr) + + if_expression = self.visit(cond.if_expression, functionCIL) + functionCIL.instructions.append(AST_CIL.GotoIf(if_expression, then)) + result1 = self.visit(cond.else_expression, functionCIL) + + #change + #functionCIL.instructions.append(AST_CIL.Assign(dest, cond.else_expression.static_type, result1)) + #functionCIL.instructions.append(AST_CIL.Assign(dest, cond.static_type, result1)) + functionCIL.instructions.append(AST_CIL.Copy(dest, result1)) + functionCIL.instructions.append(AST_CIL.Goto(fi)) + functionCIL.instructions.append(AST_CIL.Label(then)) + result2 = self.visit(cond.then_expression, functionCIL) + + #change + #functionCIL.instructions.append(AST_CIL.Assign(dest, cond.then_expression.static_type, result2)) + #functionCIL.instructions.append(AST_CIL.Assign(dest, cond.static_type, result2)) + functionCIL.instructions.append(AST_CIL.Copy(dest, result2)) + functionCIL.instructions.append(AST_CIL.Label(fi)) + return dest + + # @visitor.when(LetVar) + # def visit(self, let, functionCIL): + # for item in let.declarations: + # self.visit(item, functionCIL) + # self.local_variables.append(item) + + # result = self.visit(let.in_expression, functionCIL) + + # n = len(let.declarations) + # m = len(self.local_variables) + # for i in range(n): self.local_variables.pop(m - i - 1) + # return result + + @visitor.when(Case) + def visit(self, case, functionCIL): + current_type = self.get_local() + parent = self.get_local() + comparer = self.get_local() + d = self.get_local() + dest = self.get_local() + functionCIL.localvars += [current_type, parent, comparer, d, dest] + + intr1 = AST_CIL.Allocate(comparer, 'Bool') + intr2 = AST_CIL.Allocate(current_type, 'String') + intr3 = AST_CIL.Allocate(parent, 'String') + functionCIL.instructions.insert(0, intr1) + functionCIL.instructions.insert(0, intr2) + functionCIL.instructions.insert(0, intr3) + + expr_0 = self.visit(case.case_expression, functionCIL) + + name = 'Case_' + str(self.idCount) + case_types = [] + for item in case.implications: + case_types.append(item.var.type + '_class_name') + intr4 = AST_CIL.Array(name, case_types) + functionCIL.instructions.insert(0, intr4) + + intr5 = AST_CIL.GetParent(d, expr_0, name, len(case_types)) + intr6 = AST_CIL.SetAttrib(parent, 0, d) + functionCIL.instructions.append(intr5) + functionCIL.instructions.append(intr6) + + n = len(case.implications) + labels = [] + for i in range(n): labels.append(self.get_label()) + label_end = self.get_label() + + for i in range(n): + branch = case.implications[i] + intr7 = AST_CIL.Load(current_type, branch.var.type + '_class_name') + functionCIL.instructions.append(intr7) + intr8 = AST_CIL.EqualStrThanStr(comparer, current_type, parent) + functionCIL.instructions.append(intr8) + intr9 = AST_CIL.GotoIf(comparer, labels[i]) + functionCIL.instructions.append(intr9) + + for i in range(n): + branch = case.implications[i] + functionCIL.instructions.append(AST_CIL.Label(labels[i])) + + self.local_variables.append(branch.var) + instance = branch.var.id + '_' + str(branch.var.line) + '_' + str(branch.var.index) + functionCIL.localvars.append(instance) + intr = AST_CIL.Copy(instance, expr_0) + functionCIL.instructions.append(intr) + + result = self.visit(branch.expr, functionCIL) + + m = len(self.local_variables) + self.local_variables.pop(m - 1) + + functionCIL.instructions.append(AST_CIL.Copy(dest, result)) + functionCIL.instructions.append(AST_CIL.Goto(label_end)) + functionCIL.instructions.append(AST_CIL.Label(label_end)) + return dest + diff --git a/src/coolc.sh b/src/coolc.sh index 3088de4f9..9e9c30b48 100755 --- a/src/coolc.sh +++ b/src/coolc.sh @@ -4,8 +4,9 @@ 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 "Lexer and Parser" # TODO: Recuerde cambiar estas +echo "Carlos Martinez Molina y Eziel Ramos Pinon" # TODO: líneas a los valores correctos # Llamar al compilador -echo "Compiling $INPUT_FILE into $OUTPUT_FILE" +#echo "Compiling $INPUT_FILE into $OUTPUT_FILE" +python compiling.py ${INPUT_FILE} ${OUTPUT_FILE} \ No newline at end of file diff --git a/src/lexerTest.py b/src/lexerTest.py new file mode 100644 index 000000000..66567d28b --- /dev/null +++ b/src/lexerTest.py @@ -0,0 +1,82 @@ +import lexer_rules2 + +def run(addr): + + with open(addr, encoding = "utf-8") as f: + text = f.read() + + _lex = lexer_rules2.Lexer(text) + + if _lex.my_bool: + print(_lex.result + '\n') + _lex.result = '' + _lex.my_bool = False + exit(1) + + + + + + + + + + + + + + + + + + + + + + +# import lexer_rules +# from ply.lex import lex +# import sys + +# def run(addr): +# lexer = lex(module=lexer_rules) +# with open(addr, encoding = "utf-8") as f: +# text = f.read() + +# lexer.input(text) +# token = lexer.token() + +# while token is not None: +# try: +# token = lexer.token() +# except(): +# lexer.skip(1) + +# # temp = lexer_rules.result.split(':') +# # s = temp[0] +# # lexer_rules.result = '' +# # return s + +# if lexer_rules.my_bool: +# print(lexer_rules.result + '\n') +# lexer_rules.result = '' +# lexer_rules.my_bool = False +# exit(1) + +# # run('C:/Users/acast/Documents/GitHub/cool-compiler-2020/tests/lexer/comment1.cl') + +# # text = "" +# # lexer = lex(module=lexer_rules) +# # fpath = "C:/Users/Eziel/Downloads/Telegram Desktop/cool-compiler-2020/tests/" +# # fpath += "parser/" +# # fpath += "assignment1" +# # fpath += ".cl" +# # with open(fpath, encoding = "utf-8") as file: +# # text = file.read() +# # lexer.input(text) + +# # token = lexer.token() + +# # while token is not None: +# # print(token) +# # token = lexer.token() \ No newline at end of file diff --git a/src/lexer_rules.py b/src/lexer_rules.py new file mode 100644 index 000000000..ed830fe0b --- /dev/null +++ b/src/lexer_rules.py @@ -0,0 +1,270 @@ +from ply.lex import TOKEN +my_bool = False +result = '' + +tokens = [ +# Identifiers +'ID', 'TYPE', + +# Primitive Types +'INTEGER', 'STRING', 'TRUE', 'FALSE', + +# Literals +'LPAREN', 'RPAREN', 'LBRACE', 'RBRACE', 'TDOTS', 'COMMA', 'DOT', 'SEMICOLON', 'AT', + +# Operators +'PLUS', 'MINUS', 'MULTIPLY', 'DIVIDE', 'EQ', 'LT', 'LTEQ', 'ASSIGN', 'INT_COMP', + +# Special Operators +'ARROW', + +# reserved_keywords +'CASE', 'CLASS', 'ELSE', 'ESAC', 'FI', 'IF', 'IN', 'INHERITS', 'ISVOID', "LET", +"LOOP", "NEW", "OF", "POOL", "THEN", "WHILE", "NOT" + +] + +reserved_keywords = { + "case": "CASE", + + "class": "CLASS", + #"Class": "CLASS", + #"CLaSS": "CLASS", + + #"eLSe": "ELSE", + "else": "ELSE", + #"elsE": "ELSE", + #"ElsE": "ELSE", + + "esac": "ESAC", + + "fi": "FI", + #"Fi": "FI", + #"fI": "FI", + + "if": "IF", + #"If": "IF", + #"iF": "IF", + + "in": "IN", + + "inherits": "INHERITS", + #"iNHeRiTS": "INHERITS", + + "isvoid": "ISVOID", + "let": "LET", + "loop": "LOOP", + "new": "NEW", + "of": "OF", + "pool": "POOL", + + "then": "THEN", + #"THeN": "THEN", + #"tHen": "THEN", + + "while": "WHILE", + "not": "NOT", + "true":"TRUE", + "false":"FALSE" + } + +reserved = reserved_keywords.keys() # ply reserved keywords map + + +# Simple tokens +t_LPAREN = r'\(' # ( +t_RPAREN = r'\)' # ) +t_LBRACE = r'\{' # { +t_RBRACE = r'\}' # } +t_TDOTS = r'\:' # : +t_COMMA = r'\,' # , +t_DOT = r'\.' # . +t_SEMICOLON = r'\;' # ; +t_AT = r'\@' # @ +t_MULTIPLY = r'\*' # * +t_DIVIDE = r'\/' # / +t_PLUS = r'\+' # + +t_MINUS = r'\-' # - +t_INT_COMP = r'~' # ~ +t_LT = r'\<' # < +t_EQ = r'\=' # = +t_LTEQ = r'\<\=' # <= +t_ASSIGN = r'\<\-' # <- +t_ARROW = r'\=\>' # => + +t_ignore_WHITESPACES = r"[ \t]+" + +def find_column(t): + line_start = t.lexer.lexdata.rfind('\n', 0, t.lexpos) + 1 + return t.lexpos - line_start + 1 + +@TOKEN(r"\d+") +def t_INTEGER(token): + token.value = int(token.value) + return token + +@TOKEN(r"[A-Z][a-zA-Z_0-9]*") +def t_TYPE(token): + tempL = str.lower(token.value) + if reserved_keywords.keys().__contains__(tempL): + token.value = tempL + token.type = reserved_keywords.get(token.value, 'TYPE') + return token + +@TOKEN(r"[a-z][a-zA-Z_0-9]*") +def t_ID(token): + tempL = str.lower(token.value) + if reserved_keywords.keys().__contains__(tempL): + token.value = tempL + token.type = reserved_keywords.get(token.value, 'ID') + return token + +def t_NEWLINE(token): + r"\n+" + token.lexer.lineno += len(token.value) + +# LEXER STATES +def states(): + return ( + ("STRING", "exclusive"), + ("COMMENT", "exclusive") + ) +states = states() + + +# THE STRING STATE +@TOKEN(r"\"") +def t_start_string(token): + token.lexer.push_state("STRING") + token.lexer.string_backslashed = False + token.lexer.stringbuf = "" + + +@TOKEN(r"\n") +def t_STRING_newline(token): + global my_bool + global result + token.lexer.lineno += 1 + if not token.lexer.string_backslashed: + token.lexer.skip(1) + token.lexer.pop_state() + #print(f'({token.lineno}, {find_column(token)}) - LexicographicError: Unterminated string constant') + if result == '': + result = f'({token.lineno}, {find_column(token)}) - LexicographicError: Unterminated string constant' + my_bool = True + else: + token.lexer.string_backslashed = False + +@TOKEN(r"\"") +def t_STRING_end(token): + if not token.lexer.string_backslashed: + token.lexer.pop_state() + token.value = token.lexer.stringbuf + token.type = "STRING" + return token + else: + token.lexer.stringbuf += '"' + token.lexer.string_backslashed = False + +@TOKEN('\0') +def t_STRING_null(t): + global my_bool + global result + #print(f'({t.lexer.lineno}, {find_column(t)}) - LexicographicError: String contains null character') + if result=='': + result = f'({t.lexer.lineno}, {find_column(t)}) - LexicographicError: String contains null character' + my_bool = True + +@TOKEN(r"[^\n]") +def t_STRING_anything(token): + if token.lexer.string_backslashed: + if token.value == 'b': + token.lexer.stringbuf += '\b' + elif token.value == 't': + token.lexer.stringbuf += '\t' + elif token.value == 'n': + token.lexer.stringbuf += '\n' + elif token.value == 'f': + token.lexer.stringbuf += '\f' + elif token.value == '\\': + token.lexer.stringbuf += '\\' + else: + token.lexer.stringbuf += token.value + token.lexer.string_backslashed = False + else: + if token.value != '\\': + token.lexer.stringbuf += token.value + else: + token.lexer.string_backslashed = True + +# STRING ignored characters +t_STRING_ignore = '' + +def t_STRING_eof(t): + global my_bool + global result + #print(f'({t.lineno}, {find_column(t)}) - LexicographicError: EOF in string constant') + if result=='': + result = f'({t.lineno}, {find_column(t)}) - LexicographicError: EOF in string constant' + my_bool = True + +# STRING error handler +def t_STRING_error(token): + global my_bool + global result + if result == '': + result = "Illegal character! Line: {0}, character: {1}".format(token.lineno, token.value[0]) + #print("Illegal character! Line: {0}, character: {1}".format(token.lineno, token.value[0])) + token.lexer.skip(1) + my_bool = True + + +# THE COMMENT STATE +@TOKEN(r"\(\*") +def t_start_comment(token): + token.lexer.push_state("COMMENT") + token.lexer.comment_count = 0 + +@TOKEN(r"\(\*") +def t_COMMENT_startanother(t): + t.lexer.comment_count += 1 + +@TOKEN(r"\n") +def t_COMMENT_NEWLINE(t): + t.lexer.lineno+=1 + +def t_COMMENT_eof(t): + global my_bool + global result + #print(f"({t.lineno}, {find_column(t)}) - LexicographicError: EOF in comment") + if result=='': + result = f"({t.lineno}, {find_column(t)}) - LexicographicError: EOF in comment" + my_bool = True + +@TOKEN(r"\*\)") +def t_COMMENT_end(token): + if token.lexer.comment_count == 0: + token.lexer.pop_state() + else: + token.lexer.comment_count -= 1 + +# COMMENT ignored characters +t_COMMENT_ignore = '' +t_ignore_COMMENT_LINE = r'\-\-[^\n]*' +t_ignore = ' \t\r\f' + +# COMMENT error handler +def t_COMMENT_error(t): + t.lexer.skip(1) + +def t_error(t): + global my_bool + global result + message = f'({t.lineno}, {find_column(t)}) - LexicographicError: ERROR "' + message += t.value[0] + message +='"' + #print(message) + if result =='': + result = message + t.lexer.skip(1) + my_bool = True \ No newline at end of file diff --git a/src/lexer_rules2.py b/src/lexer_rules2.py new file mode 100644 index 000000000..f65532be9 --- /dev/null +++ b/src/lexer_rules2.py @@ -0,0 +1,243 @@ +from TOKEN import LexToken + +class Lexer: + def __init__(self,text): + self.my_bool = False + self.result = '' + self.names = { + "case" : "CASE", + "class" : "CLASS", + "else" : "ELSE", + "esac" : "ESAC", + "fi" : "FI", + "if" : "IF", + "in" : "IN", + "inherits" : "INHERITS", + "isvoid" : "ISVOID", + "let" : "LET", + "loop" : "LOOP", + "new" : "NEW", + "of" : "OF", + "pool" : "POOL", + "then" : "THEN", + "while" : "WHILE", + "not" : "NOT", + "true" : "TRUE", + "false" : "FALSE", + "(" : "LPAREN", + ")" : "RPAREN", + "{" : "LBRACE", + "}" : "RBRACE", + ":" : "TDOTS", + "," : "COMMA", + "." : "DOT", + ";" : "SEMICOLON", + "@" : "AT", + "*" : "MULTIPLY", + "/" : "DIVIDE", + "+" : "PLUS", + "-" : "MINUS", + "~" : "INT_COMP", + "<" : "LT", + "=" : "EQ", + "<=" : "LTEQ", + "<-" : "ASSIGN", + "=>" : "ARROW", } + self.token_list = [] + self.Simple_tokens = ['(', ')', '{', '}', ':', ',','.',';','@','*','/','+','-','~','<','=','<=','<-','=>'] + self.error_tokens = ['!','$','%','^','?','[',']','#','&'] + self.ABC = ['A','B','C','D','E','F','G','H','I','J','K','L','M','N','O','P','Q','R','S','T','U','V','W','X','Y','Z'] + self.abc = [str.lower(item) for item in self.ABC] + self._int = ['0','1','2','3','4','5','6','7','8','9'] + self.get_tokens(text) + + def error(self,line,column,value): + message = f'({line}, {column}) - LexicographicError: ERROR "' + message += value + message +='"' + if self.result =='': + self.result = message + self.my_bool = True + + def error_String_null(self,line,column): + if self.result=='': + self.result = f'({line}, {column}) - LexicographicError: String contains null character' + self.my_bool = True + + def error_Comment_EOF(self,line,column): + if self.result=='': + self.result = f"({line}, {column}) - LexicographicError: EOF in comment" + self.my_bool = True + + def error_String_EOF(self,line,column): + if self.result=='': + self.result = f'({line}, {column}) - LexicographicError: EOF in string constant' + self.my_bool = True + + def error_String_New_Line(self,line,column): + if self.result == '': + self.result = f'({line}, {column}) - LexicographicError: Unterminated string constant' + self.my_bool = True + + def get_tokens(self,text): + i=-1 + n = len(text) + Ln = 1 + Col = 1 + current1 = '' + current2 = '' + open_comments = 0 + while i < n - 1: + + i += 1 + + if text[i] in self.error_tokens: + Col+=len(current1) + self.error(Ln, Col, text[i]) + break + + if text[i] == '\t': + Col+=1 + continue + + if text[i] == ' ': + Col+=1 + continue + + if text[i] == '\n': #end line + Col=1 + Ln+=1 + continue + + if text[i] == '-' and text[i + 1] == '-': #ignore comment + while not text[i] == '\n': i+=1 + Col=1 + Ln+=1 + continue + + if text[i] == '(' and text[i + 1] == '*': #ignore comment + open_comments += 1 + while open_comments > 0: + i+=1 + Col+=1 + if i == len(text): + self.error_Comment_EOF(Ln,Col) + i=len(text) #end + break + if text[i] == '\n': + Ln+=1 + Col=0 + if text[i] == '(' and text[i + 1] == '*': + open_comments += 1 + if text[i] == '*' and text[i + 1] == ')': + i+=1 + open_comments -= 1 + continue + + if text[i] == '"': + + i+=1 + length = 1 + + if i==len(text): + Col+=length + self.error_String_EOF(Ln,Col) + break + + while not text[i] == '"': + + if text[i] == '\n': + Col+=length + self.error_String_New_Line(Ln,Col) + i=len(text) + break + + if text[i]=='\0': + Col+=length + self.error_String_null(Ln,Col) + i=len(text) + break + + if text[i]=='\\': + if not text[i+1]=='b' and not text[i+1]=='t' and not text[i+1]=='n' and not text[i+1]=='f': + + current1+=text[i+1] + length+=2 + + if text[i+1]=='\n': + Ln+=1 + Col=0 + length=1 + + i+=2 + continue + + current1 += text[i] + length+=1 + i+=1 + if i==len(text): + Col+=length + self.error_String_EOF(Ln,Col) + break + + self.token_list.append(LexToken('STRING',current1,Ln,Col)) + Col+=length + 1 + current1 = '' + continue + + current1 += text[i] + + if i + 1 < len(text): current2 = current1 + text[i + 1] + else: current2 = current1 + + _next = current2[-1] #text[i + 1] + + if current1[0] == '_': + self.error(Ln,Col,current1[0]) + break + + if current1[0] in self._int: + i+=1 + while text[i] in self._int: + current1 += text[i] + i+=1 + i-=1 + self.token_list.append(LexToken('INTEGER',int(current1), Ln,Col)) + Col+=len(current1) + current1 = '' + continue + + if current2 in self.Simple_tokens: + self.token_list.append(LexToken(self.names[current2],current2,Ln,Col)) + Col+=len(current2) + i+=1 + current1 = '' + continue + + if current1 in self.Simple_tokens: + self.token_list.append(LexToken(self.names[current1],current1,Ln,Col)) + Col+=len(current1) + current1 = '' + continue + + if _next in self.Simple_tokens or _next == ' ' or _next == '\n' or _next == '\t' or i+1==len(text): + + lower = str.lower(current1) + + if self.names.__contains__(lower): + self.token_list.append(LexToken(self.names[lower],lower,Ln,Col)) + Col+=len(current1) + current1 = '' + continue + + if current1[0] in self.ABC: + self.token_list.append(LexToken('TYPE',current1,Ln,Col)) + Col+=len(current1) + current1 = '' + continue + + if current1[0] in self.abc: + self.token_list.append(LexToken('ID',current1,Ln,Col)) + Col+=len(current1) + current1 = '' + diff --git a/src/makefile b/src/makefile index 30df993f5..021189d6f 100644 --- a/src/makefile +++ b/src/makefile @@ -5,7 +5,6 @@ main: clean: rm -rf build/* - rm -rf ../tests/*/*.mips test: pytest ../tests -v --tb=short -m=${TAG} diff --git a/src/parserTest.py b/src/parserTest.py new file mode 100644 index 000000000..df823ee4d --- /dev/null +++ b/src/parserTest.py @@ -0,0 +1,40 @@ +import lexer_rules +import parser_rules +import sys + +from ply.lex import lex +from ply.yacc import yacc + +def run(addr): + lexer = lex(module=lexer_rules) + parser = yacc(module=parser_rules) + with open(addr, encoding = "utf-8") as f: + text = f.read() + + parser.parse(text, lexer) + + # temp = parser_rules.result.split(':') + # s = temp[0] + # parser_rules.result = '' + # return s + + if parser_rules.my_bool: + print(parser_rules.result + '\n') + parser_rules.result = '' + parser_rules.my_bool = False + exit(1) + + +# lexer = lex(module=lexer_rules) +# parser = yacc(module=parser_rules) + +# text = "" +# lexer = lex(module=lexer_rules) + +# fpath = "C:/Users/Eziel/Desktop/ejemplos/cool-compiler-jj-christian-alberto-hector-c411/src/test/TestCases/Semantics/success/" +# fpath = fpath + 'binary_tree.cl' + +# with open(fpath, encoding = "utf-8") as file: +# text = file.read() + +# parser.parse(text, lexer) \ No newline at end of file diff --git a/src/parser_rules.py b/src/parser_rules.py new file mode 100644 index 000000000..5a73af312 --- /dev/null +++ b/src/parser_rules.py @@ -0,0 +1,364 @@ +from lexer_rules import tokens +import AST +#from expressions import * + +from operator import add, mul + +my_bool = False +result = '' + +#Parsing rules +precedence = ( + ('right', 'ASSIGN'), + ('right', 'NOT'), + ('nonassoc', 'LTEQ', 'LT', 'EQ'), + ('left', 'PLUS', 'MINUS'), + ('left', 'MULTIPLY', 'DIVIDE'), + ('right', 'ISVOID'), + ('right', 'INT_COMP'), + ('left', 'AT'), + ('left', 'DOT') +) + + + + +# precedence = ( +# ('left', 'PLUS','MINUS'), +# ('left', 'MULTIPLY','DIVIDE'), +# ('right', 'ASSIGN'), +# ('left', 'NOT'), +# ('nonassoc', 'LT','LTEQ','EQ'), +# ('right', 'ISVOID'), +# ('right', 'INT_COMP'), +# ('left', 'AT'), +# ('left', 'DOT') +# ) + + + + +def p_program(production): + '''program : classSet''' + production[0] = AST.Program(production[1]) + production[0].line = 1 + +def p_classSet(production): + ''' + classSet : class SEMICOLON classSet + | class SEMICOLON + ''' + if len(production) == 3: + production[0] = [production[1]] + else: production[0] = [production[1]] + production[3] + +def p_class(production): + 'class : CLASS TYPE _inherits LBRACE ffeature RBRACE' + production[0] = AST.Class(production[2], production[3], production[5]) + production[0].line = production.lineno(1) + line_start = production.lexer.lexdata.rfind('\n', 0, production.lexpos(1)) + 1 + production[0].index = production.lexpos(1) - line_start + 1 + +def p_inherits(production): + '''_inherits : INHERITS TYPE + | empty''' + if len(production) == 3: + production[0] = AST.Type(production[2]) + else: production[0] = None + +def p_ffeature(production): + '''ffeature : feature SEMICOLON ffeature + | empty''' + if len(production) == 4: + production[0] = [production[1]] + production[3] #creo la lista de metodos y atributos + else: production[0] = [] + +def p_feature(production): #metodo || atributo + #definition: name (args):returnType{expr} + '''feature : ID LPAREN formal RPAREN TDOTS TYPE LBRACE expr RBRACE + | ID LPAREN RPAREN TDOTS TYPE LBRACE expr RBRACE + | temp + ''' + if len(production) == 10: + production[0] = AST.Method(production[1], production[3], production[6], production[8]) + production[0].line = production.lineno(1) + line_start = production.lexer.lexdata.rfind('\n', 0, production.lexpos(1)) + 1 + production[0].index = production.lexpos(1) - line_start + 1 + elif len(production) == 9: + production[0] = AST.Method(production[1], [], production[5], production[7]) + production[0].line = production.lineno(1) + line_start = production.lexer.lexdata.rfind('\n', 0, production.lexpos(1)) + 1 + production[0].index = production.lexpos(1) - line_start + 1 + else:#atr + production[0] = production[1] + production[0].line = production[1].line + production[0].index = production[1].index + +#cambiar temp por atributos o variables +def p_temp(production): + ''' + temp : idDots + | idDots ASSIGN expr + ''' + if len(production) == 2: + production[0] = production[1] + else: #en este caso creo un atributo para el let + production[0] = AST.Attribute(production[1].id, production[1].type, production[3]) + production[0].line = production.lineno(2) + line_start = production.lexer.lexdata.rfind('\n', 0, production.lexpos(2)) + 1 + production[0].index = production.lexpos(2) - line_start + 1 + +def p_idDots(production): + 'idDots : ID TDOTS TYPE' + production[0] = AST.Var(production[1], production[3]) + production[0].line = production.lineno(1) + line_start = production.lexer.lexdata.rfind('\n', 0, production.lexpos(1)) + 1 + production[0].index = production.lexpos(1) - line_start + 1 + +def p_formal(production): + '''formal : idDots COMMA formal + | idDots''' + if len(production) == 2: + production[0] = [production[1]] + else: production[0] = [production[1]] + production[3] + +def p_expression_list(production): + '''expression_list : expression_list expr SEMICOLON + | expr SEMICOLON + ''' + if len(production) == 3: + production[0] = [production[1]] + else: production[0] = production[1] + [production[2]] + +def p_expression_not(production): #boolean complement of + '''expr : NOT expr''' + production[0] = AST.Not(production[2]) + production[0].line = production.lineno(1) + line_start = production.lexer.lexdata.rfind('\n', 0, production.lexpos(1)) + 1 + production[0].index = production.lexpos(1) - line_start + 1 + +def p_expression_binop(production): + '''expr : expr PLUS expr + | expr MINUS expr + | expr MULTIPLY expr + | expr DIVIDE expr''' + if production[2] == '+': + production[0] = AST.Plus(production[1], production[3]) + elif production[2] == '-': + production[0] = AST.Minus(production[1], production[3]) + elif production[2] == '*': + production[0] = AST.Star(production[1], production[3]) + elif production[2] == '/': + production[0] = AST.Div(production[1], production[3]) + production[0].line = production.lineno(2) + line_start = production.lexer.lexdata.rfind('\n', 0, production.lexpos(2)) + 1 + production[0].index = production.lexpos(2) - line_start + 1 + +def p_expression_g(production): + ''' + expr : LPAREN expr RPAREN + | ISVOID expr + | block + | conditional + | loop + | case + | dispatch + | INT_COMP expr + ''' + if len(production) == 4: + production[0] = production[2] + elif len(production) == 3: + if production[1] == 'isvoid': + production[0] = AST.IsVoid(production[2]) + production[0].line = production.lineno(1) + line_start = production.lexer.lexdata.rfind('\n', 0, production.lexpos(1)) + 1 + production[0].index = production.lexpos(1) - line_start + 1 + else: + production[0] = AST.IntegerComplement(production[2]) + production[0].line = production.lineno(1) + production[0].index = production[2].index + # line_start = production.lexer.lexdata.rfind('\n', 0, production.lexpos(1)) + 1 + # production[0].index = production.lexpos(1) - line_start + 1 + else: production[0] = production[1] + +def p_block(production): + 'block : LBRACE expression_list RBRACE' + production[0] = AST.Block(production[2]) + production[0].line = production.lineno(1) + line_start = production.lexer.lexdata.rfind('\n', 0, production.lexpos(1)) + 1 + production[0].index = production.lexpos(1) - line_start + 1 + +def p_expression_id(production): + '''expr : ID''' + production[0] = AST.Type(production[1]) + production[0].line = production.lineno(1) + line_start = production.lexer.lexdata.rfind('\n', 0, production.lexpos(1)) + 1 + production[0].index = production.lexpos(1) - line_start + 1 + +def p_expression_int(production): + '''expr : INTEGER ''' + production[0] = AST.Interger(production[1]) + production[0].line = production.lineno(1) + line_start = production.lexer.lexdata.rfind('\n', 0, production.lexpos(1)) + 1 + production[0].index = production.lexpos(1) - line_start + 1 + +def p_expression_str(production): + '''expr : STRING''' + production[0] = AST.String(production[1]) + production[0].line = production.lineno(1) + line_start = production.lexer.lexdata.rfind('\n', 0, production.lexpos(1)) + 1 + production[0].index = production.lexpos(1) - line_start + 1 + +def p_expression_bool(production): + '''expr : TRUE + | FALSE''' + production[0] = AST.Boolean(production[1]) + production[0].line = production.lineno(1) + line_start = production.lexer.lexdata.rfind('\n', 0, production.lexpos(1)) + 1 + production[0].index = production.lexpos(1) - line_start + 1 + +def p_expression_newtype(production): + '''expr : NEW TYPE''' + production[0] = AST.NewType(production[2]) + production[0].line = production.lineno(1) + line_start = production.lexer.lexdata.rfind('\n', 0, production.lexpos(1)) + 1 + production[0].index = production.lexpos(1) - line_start + 1 + +def p_expression_l(production): + '''expr : let''' + production[0] = production[1] + +#e_0.f(e_1,...,e_n) +def p_dispatch(production): + ''' + dispatch : expr DOT ID LPAREN arguments_list_opt RPAREN + | expr AT TYPE DOT ID LPAREN arguments_list_opt RPAREN + | ID LPAREN arguments_list_opt RPAREN + ''' + if len(production) == 7: + production[0] = AST.Dispatch(production[3], production[5], production[1]) + production[0].line = production.lineno(2) + # line_start = production.lexer.lexdata.rfind('\n', 0, production.lexpos(2)) + 1 + # production[0].index = production.lexpos(2) - line_start + 1 + production[0].index = production[1].index + elif len(production) == 5: + production[0] = AST.Dispatch(production[1], production[3], None) + production[0].line = production.lineno(1) + line_start = production.lexer.lexdata.rfind('\n', 0, production.lexpos(1)) + 1 + production[0].index = production.lexpos(1) - line_start + 1 + else: + production[0] = AST.StaticDispatch(production[5], production[7], production[1], production[3]) + production[0].line = production.lineno(2) + line_start = production.lexer.lexdata.rfind('\n', 0, production.lexpos(2)) + 1 + production[0].index = production.lexpos(2) - line_start + 1 + +def p_arguments_list(production): + """ + arguments_list : arguments_list COMMA expr + | expr + """ + if len(production) == 4: + production[0] = production[1] + [production[3]] + else: production[0] = [production[1]] + +def p_arguments_list_opt(production): + """ + arguments_list_opt : arguments_list + | empty + """ + production[0] = [] if production.slice[1].type == "empty" else production[1] + +def p_empty(production): + 'empty :' + production[0] = None + +def p_let_expression(production): + 'let : LET declaration_list IN expr' + production[0] = AST.LetVar(production[2], production[4]) + production[0].line = production.lineno(1) + line_start = production.lexer.lexdata.rfind('\n', 0, production.lexpos(1)) + 1 + production[0].index = production.lexpos(1) - line_start + 1 + +def p_declaration_list(production): + ''' + declaration_list : temp COMMA declaration_list + | temp + ''' + if len(production) == 2: + production[0] = [production[1]] + else: production[0] = [production[1]] + production[3] + +def p_conditional(production): + 'conditional : IF expr THEN expr ELSE expr FI' + production[0] = AST.Conditional(production[2], production[4], production[6]) + production[0].line = production.lineno(1) + line_start = production.lexer.lexdata.rfind('\n', 0, production.lexpos(1)) + 1 + production[0].index = production.lexpos(1) - line_start + 1 + +def p_loop(production): + 'loop : WHILE expr LOOP expr POOL' + production[0] = AST.Loop(production[2], production[4]) + production[0].line = production.lineno(1) + line_start = production.lexer.lexdata.rfind('\n', 0, production.lexpos(1)) + 1 + production[0].index = production.lexpos(1) - line_start + 1 + +def p_case(production): + 'case : CASE expr OF add ESAC' + production[0] = AST.Case(production[2], production[4]) + production[0].line = production.lineno(1) + line_start = production.lexer.lexdata.rfind('\n', 0, production.lexpos(1)) + 1 + production[0].index = production.lexpos(1) - line_start + 1 + +def p_add(production): + '''add : derivate SEMICOLON add + | derivate SEMICOLON + ''' + if len(production) == 4: + production[0] = [production[1]] + production[3] + else: production[0] = [production[1]] + +def p_derivate(production): + '''derivate : idDots ARROW expr''' + production[0] = AST.Branch(production[1], production[3]) + production[0].line = production.lineno(2) + line_start = production.lexer.lexdata.rfind('\n', 0, production.lexpos(2)) + 1 + production[0].index = production.lexpos(2) - line_start + 1 + +def p_expression_cmp(production): + '''expr : expr LT expr + | expr LTEQ expr + | expr EQ expr''' + if production[2] == '<': + production[0] = AST.LowerThan(production[1], production[3]) + elif production[2] == '<=': + production[0] = AST.LowerEqualThan(production[1], production[3]) + elif production[2] == '=': + production[0] = AST.EqualThan(production[1], production[3]) + production[0].line = production.lineno(2) + line_start = production.lexer.lexdata.rfind('\n', 0, production.lexpos(2)) + 1 + production[0].index = production.lexpos(2) - line_start + 1 + +def p_expression_assign(production): + 'expr : ID ASSIGN expr' + production[0] = AST.Assign(production[1], production[3]) + production[0].line = production.lineno(1) + line_start = production.lexer.lexdata.rfind('\n', 0, production.lexpos(1)) + 1 + production[0].index = production.lexpos(1) - line_start + 1 + +def find_column(production): + line_start = production.lexer.lexdata.rfind('\n', 0, production.lexpos) + 1 + return production.lexpos - line_start + 1 + +def p_error(production): + global my_bool + global result + """ + Error rule for Syntax Errors handling and reporting. + """ + if production is None: + result = '(0, 0) - SyntacticError: ERROR at or near EOF' + #print('(0, 0) - SyntacticError: ERROR at or near EOF') + else: + result = '({}, {}) - SyntacticError: ERROR at or near "{}"'.format(production.lineno, find_column(production), production.value) + #print('({}, {}) - SyntacticError: ERROR at or near "{}"'.format( production.lineno, find_column(production), production.value)) + my_bool = True \ No newline at end of file diff --git a/src/parsetab.py b/src/parsetab.py new file mode 100644 index 000000000..145656208 --- /dev/null +++ b/src/parsetab.py @@ -0,0 +1,90 @@ + +# parsetab.py +# This file is automatically generated. Do not edit. +# pylint: disable=W,C,R +_tabversion = '3.10' + +_lr_method = 'LALR' + +_lr_signature = 'rightASSIGNrightNOTnonassocLTEQLTEQleftPLUSMINUSleftMULTIPLYDIVIDErightISVOIDrightINT_COMPleftATleftDOTARROW ASSIGN AT CASE CLASS COMMA DIVIDE DOT ELSE EQ ESAC FALSE FI ID IF IN INHERITS INTEGER INT_COMP ISVOID LBRACE LET LOOP LPAREN LT LTEQ MINUS MULTIPLY NEW NOT OF PLUS POOL RBRACE RPAREN SEMICOLON STRING TDOTS THEN TRUE TYPE WHILEprogram : classSet\n classSet : class SEMICOLON classSet\n | class SEMICOLON\n class : CLASS TYPE _inherits LBRACE ffeature RBRACE_inherits : INHERITS TYPE\n | emptyffeature : feature SEMICOLON ffeature\n | emptyfeature : ID LPAREN formal RPAREN TDOTS TYPE LBRACE expr RBRACE\n | ID LPAREN RPAREN TDOTS TYPE LBRACE expr RBRACE\n | temp\n \n temp : idDots\n | idDots ASSIGN expr\n idDots : ID TDOTS TYPEformal : idDots COMMA formal\n | idDotsexpression_list : expression_list expr SEMICOLON \n | expr SEMICOLON\n expr : NOT exprexpr : expr PLUS expr \n | expr MINUS expr\n | expr MULTIPLY expr\n | expr DIVIDE expr\n expr : LPAREN expr RPAREN\n | ISVOID expr\n | block\n | conditional\n | loop\n | case\n | dispatch\n | INT_COMP expr\n block : LBRACE expression_list RBRACEexpr : IDexpr : INTEGER expr : STRINGexpr : TRUE\n | FALSEexpr : NEW TYPEexpr : let\n dispatch : expr DOT ID LPAREN arguments_list_opt RPAREN\n | expr AT TYPE DOT ID LPAREN arguments_list_opt RPAREN\n | ID LPAREN arguments_list_opt RPAREN\n \n arguments_list : arguments_list COMMA expr\n | expr\n \n arguments_list_opt : arguments_list\n | empty\n empty :let : LET declaration_list IN expr\n declaration_list : temp COMMA declaration_list\n | temp\n conditional : IF expr THEN expr ELSE expr FIloop : WHILE expr LOOP expr POOLcase : CASE expr OF add ESACadd : derivate SEMICOLON add\n | derivate SEMICOLON\n derivate : idDots ARROW exprexpr : expr LT expr\n | expr LTEQ expr\n | expr EQ exprexpr : ID ASSIGN expr' + +_lr_action_items = {'CLASS':([0,5,],[4,4,]),'$end':([1,2,5,7,],[0,-1,-3,-2,]),'SEMICOLON':([3,14,17,18,19,29,30,34,35,36,37,38,40,41,42,43,44,46,64,66,67,70,72,81,82,83,84,85,86,87,90,91,96,97,108,114,116,124,125,129,130,134,135,137,138,],[5,20,-11,-12,-4,-14,-13,-26,-27,-28,-29,-30,-33,-34,-35,-36,-37,-39,-19,-25,-31,-38,98,-20,-21,-22,-23,-57,-58,-59,-24,-60,-32,110,-42,126,-48,-52,-53,-10,-40,-56,-9,-51,-41,]),'TYPE':([4,9,22,45,53,63,78,],[6,12,29,70,79,89,104,]),'INHERITS':([6,],[9,]),'LBRACE':([6,8,10,12,23,31,32,33,39,47,48,49,50,55,56,57,58,59,60,61,68,69,71,79,98,99,100,102,104,105,106,109,110,118,123,127,131,],[-47,11,-6,-5,47,47,47,47,47,47,47,47,47,47,47,47,47,47,47,47,47,47,47,105,-18,47,47,47,118,47,47,47,-17,47,47,47,47,]),'ID':([11,20,21,23,31,32,33,39,47,48,49,50,51,54,55,56,57,58,59,60,61,62,68,69,71,98,99,100,101,102,103,105,106,107,109,110,118,123,126,127,131,],[16,16,25,40,40,40,40,40,40,40,40,40,25,25,40,40,40,40,40,40,40,88,40,40,40,-18,40,40,25,40,25,40,40,121,40,-17,40,40,25,40,40,]),'RBRACE':([11,13,15,20,24,34,35,36,37,38,40,41,42,43,44,46,64,66,67,70,71,81,82,83,84,85,86,87,90,91,96,98,108,110,116,119,124,125,128,130,137,138,],[-47,19,-8,-47,-7,-26,-27,-28,-29,-30,-33,-34,-35,-36,-37,-39,-19,-25,-31,-38,96,-20,-21,-22,-23,-57,-58,-59,-24,-60,-32,-18,-42,-17,-48,129,-52,-53,135,-40,-51,-41,]),'LPAREN':([16,23,31,32,33,39,40,47,48,49,50,55,56,57,58,59,60,61,68,69,71,88,98,99,100,102,105,106,109,110,118,121,123,127,131,],[21,32,32,32,32,32,69,32,32,32,32,32,32,32,32,32,32,32,32,32,32,106,-18,32,32,32,32,32,32,-17,32,131,32,32,32,]),'TDOTS':([16,25,27,52,],[22,22,53,78,]),'COMMA':([18,28,29,30,34,35,36,37,38,40,41,42,43,44,46,64,66,67,70,77,81,82,83,84,85,86,87,90,91,93,95,96,108,116,122,124,125,130,137,138,],[-12,54,-14,-13,-26,-27,-28,-29,-30,-33,-34,-35,-36,-37,-39,-19,-25,-31,-38,103,-20,-21,-22,-23,-57,-58,-59,-24,-60,109,-44,-32,-42,-48,-43,-52,-53,-40,-51,-41,]),'IN':([18,29,30,34,35,36,37,38,40,41,42,43,44,46,64,66,67,70,76,77,81,82,83,84,85,86,87,90,91,96,108,116,117,124,125,130,137,138,],[-12,-14,-13,-26,-27,-28,-29,-30,-33,-34,-35,-36,-37,-39,-19,-25,-31,-38,102,-50,-20,-21,-22,-23,-57,-58,-59,-24,-60,-32,-42,-48,-49,-52,-53,-40,-51,-41,]),'ASSIGN':([18,29,40,],[23,-14,68,]),'RPAREN':([21,26,28,29,34,35,36,37,38,40,41,42,43,44,46,64,65,66,67,69,70,80,81,82,83,84,85,86,87,90,91,92,93,94,95,96,106,108,116,120,122,124,125,130,131,136,137,138,],[27,52,-16,-14,-26,-27,-28,-29,-30,-33,-34,-35,-36,-37,-39,-19,90,-25,-31,-47,-38,-15,-20,-21,-22,-23,-57,-58,-59,-24,-60,108,-45,-46,-44,-32,-47,-42,-48,130,-43,-52,-53,-40,-47,138,-51,-41,]),'NOT':([23,31,32,33,39,47,48,49,50,55,56,57,58,59,60,61,68,69,71,98,99,100,102,105,106,109,110,118,123,127,131,],[31,31,31,31,31,31,31,31,31,31,31,31,31,31,31,31,31,31,31,-18,31,31,31,31,31,31,-17,31,31,31,31,]),'ISVOID':([23,31,32,33,39,47,48,49,50,55,56,57,58,59,60,61,68,69,71,98,99,100,102,105,106,109,110,118,123,127,131,],[33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,-18,33,33,33,33,33,33,-17,33,33,33,33,]),'INT_COMP':([23,31,32,33,39,47,48,49,50,55,56,57,58,59,60,61,68,69,71,98,99,100,102,105,106,109,110,118,123,127,131,],[39,39,39,39,39,39,39,39,39,39,39,39,39,39,39,39,39,39,39,-18,39,39,39,39,39,39,-17,39,39,39,39,]),'INTEGER':([23,31,32,33,39,47,48,49,50,55,56,57,58,59,60,61,68,69,71,98,99,100,102,105,106,109,110,118,123,127,131,],[41,41,41,41,41,41,41,41,41,41,41,41,41,41,41,41,41,41,41,-18,41,41,41,41,41,41,-17,41,41,41,41,]),'STRING':([23,31,32,33,39,47,48,49,50,55,56,57,58,59,60,61,68,69,71,98,99,100,102,105,106,109,110,118,123,127,131,],[42,42,42,42,42,42,42,42,42,42,42,42,42,42,42,42,42,42,42,-18,42,42,42,42,42,42,-17,42,42,42,42,]),'TRUE':([23,31,32,33,39,47,48,49,50,55,56,57,58,59,60,61,68,69,71,98,99,100,102,105,106,109,110,118,123,127,131,],[43,43,43,43,43,43,43,43,43,43,43,43,43,43,43,43,43,43,43,-18,43,43,43,43,43,43,-17,43,43,43,43,]),'FALSE':([23,31,32,33,39,47,48,49,50,55,56,57,58,59,60,61,68,69,71,98,99,100,102,105,106,109,110,118,123,127,131,],[44,44,44,44,44,44,44,44,44,44,44,44,44,44,44,44,44,44,44,-18,44,44,44,44,44,44,-17,44,44,44,44,]),'NEW':([23,31,32,33,39,47,48,49,50,55,56,57,58,59,60,61,68,69,71,98,99,100,102,105,106,109,110,118,123,127,131,],[45,45,45,45,45,45,45,45,45,45,45,45,45,45,45,45,45,45,45,-18,45,45,45,45,45,45,-17,45,45,45,45,]),'IF':([23,31,32,33,39,47,48,49,50,55,56,57,58,59,60,61,68,69,71,98,99,100,102,105,106,109,110,118,123,127,131,],[48,48,48,48,48,48,48,48,48,48,48,48,48,48,48,48,48,48,48,-18,48,48,48,48,48,48,-17,48,48,48,48,]),'WHILE':([23,31,32,33,39,47,48,49,50,55,56,57,58,59,60,61,68,69,71,98,99,100,102,105,106,109,110,118,123,127,131,],[49,49,49,49,49,49,49,49,49,49,49,49,49,49,49,49,49,49,49,-18,49,49,49,49,49,49,-17,49,49,49,49,]),'CASE':([23,31,32,33,39,47,48,49,50,55,56,57,58,59,60,61,68,69,71,98,99,100,102,105,106,109,110,118,123,127,131,],[50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,-18,50,50,50,50,50,50,-17,50,50,50,50,]),'LET':([23,31,32,33,39,47,48,49,50,55,56,57,58,59,60,61,68,69,71,98,99,100,102,105,106,109,110,118,123,127,131,],[51,51,51,51,51,51,51,51,51,51,51,51,51,51,51,51,51,51,51,-18,51,51,51,51,51,51,-17,51,51,51,51,]),'ARROW':([29,115,],[-14,127,]),'PLUS':([30,34,35,36,37,38,40,41,42,43,44,46,64,65,66,67,70,72,73,74,75,81,82,83,84,85,86,87,90,91,95,96,97,108,111,112,116,119,122,124,125,128,130,132,134,137,138,],[55,-26,-27,-28,-29,-30,-33,-34,-35,-36,-37,-39,55,55,-25,-31,-38,55,55,55,55,-20,-21,-22,-23,55,55,55,-24,55,55,-32,55,-42,55,55,55,55,55,-52,-53,55,-40,55,55,-51,-41,]),'MINUS':([30,34,35,36,37,38,40,41,42,43,44,46,64,65,66,67,70,72,73,74,75,81,82,83,84,85,86,87,90,91,95,96,97,108,111,112,116,119,122,124,125,128,130,132,134,137,138,],[56,-26,-27,-28,-29,-30,-33,-34,-35,-36,-37,-39,56,56,-25,-31,-38,56,56,56,56,-20,-21,-22,-23,56,56,56,-24,56,56,-32,56,-42,56,56,56,56,56,-52,-53,56,-40,56,56,-51,-41,]),'MULTIPLY':([30,34,35,36,37,38,40,41,42,43,44,46,64,65,66,67,70,72,73,74,75,81,82,83,84,85,86,87,90,91,95,96,97,108,111,112,116,119,122,124,125,128,130,132,134,137,138,],[57,-26,-27,-28,-29,-30,-33,-34,-35,-36,-37,-39,57,57,-25,-31,-38,57,57,57,57,57,57,-22,-23,57,57,57,-24,57,57,-32,57,-42,57,57,57,57,57,-52,-53,57,-40,57,57,-51,-41,]),'DIVIDE':([30,34,35,36,37,38,40,41,42,43,44,46,64,65,66,67,70,72,73,74,75,81,82,83,84,85,86,87,90,91,95,96,97,108,111,112,116,119,122,124,125,128,130,132,134,137,138,],[58,-26,-27,-28,-29,-30,-33,-34,-35,-36,-37,-39,58,58,-25,-31,-38,58,58,58,58,58,58,-22,-23,58,58,58,-24,58,58,-32,58,-42,58,58,58,58,58,-52,-53,58,-40,58,58,-51,-41,]),'LT':([30,34,35,36,37,38,40,41,42,43,44,46,64,65,66,67,70,72,73,74,75,81,82,83,84,85,86,87,90,91,95,96,97,108,111,112,116,119,122,124,125,128,130,132,134,137,138,],[59,-26,-27,-28,-29,-30,-33,-34,-35,-36,-37,-39,59,59,-25,-31,-38,59,59,59,59,-20,-21,-22,-23,None,None,None,-24,59,59,-32,59,-42,59,59,59,59,59,-52,-53,59,-40,59,59,-51,-41,]),'LTEQ':([30,34,35,36,37,38,40,41,42,43,44,46,64,65,66,67,70,72,73,74,75,81,82,83,84,85,86,87,90,91,95,96,97,108,111,112,116,119,122,124,125,128,130,132,134,137,138,],[60,-26,-27,-28,-29,-30,-33,-34,-35,-36,-37,-39,60,60,-25,-31,-38,60,60,60,60,-20,-21,-22,-23,None,None,None,-24,60,60,-32,60,-42,60,60,60,60,60,-52,-53,60,-40,60,60,-51,-41,]),'EQ':([30,34,35,36,37,38,40,41,42,43,44,46,64,65,66,67,70,72,73,74,75,81,82,83,84,85,86,87,90,91,95,96,97,108,111,112,116,119,122,124,125,128,130,132,134,137,138,],[61,-26,-27,-28,-29,-30,-33,-34,-35,-36,-37,-39,61,61,-25,-31,-38,61,61,61,61,-20,-21,-22,-23,None,None,None,-24,61,61,-32,61,-42,61,61,61,61,61,-52,-53,61,-40,61,61,-51,-41,]),'DOT':([30,34,35,36,37,38,40,41,42,43,44,46,64,65,66,67,70,72,73,74,75,81,82,83,84,85,86,87,89,90,91,95,96,97,108,111,112,116,119,122,124,125,128,130,132,134,137,138,],[62,-26,-27,-28,-29,-30,-33,-34,-35,-36,-37,-39,62,62,62,62,-38,62,62,62,62,62,62,62,62,62,62,62,107,-24,62,62,-32,62,-42,62,62,62,62,62,-52,-53,62,-40,62,62,-51,-41,]),'AT':([30,34,35,36,37,38,40,41,42,43,44,46,64,65,66,67,70,72,73,74,75,81,82,83,84,85,86,87,90,91,95,96,97,108,111,112,116,119,122,124,125,128,130,132,134,137,138,],[63,-26,-27,-28,-29,-30,-33,-34,-35,-36,-37,-39,63,63,63,63,-38,63,63,63,63,63,63,63,63,63,63,63,-24,63,63,-32,63,-42,63,63,63,63,63,-52,-53,63,-40,63,63,-51,-41,]),'THEN':([34,35,36,37,38,40,41,42,43,44,46,64,66,67,70,73,81,82,83,84,85,86,87,90,91,96,108,116,124,125,130,137,138,],[-26,-27,-28,-29,-30,-33,-34,-35,-36,-37,-39,-19,-25,-31,-38,99,-20,-21,-22,-23,-57,-58,-59,-24,-60,-32,-42,-48,-52,-53,-40,-51,-41,]),'LOOP':([34,35,36,37,38,40,41,42,43,44,46,64,66,67,70,74,81,82,83,84,85,86,87,90,91,96,108,116,124,125,130,137,138,],[-26,-27,-28,-29,-30,-33,-34,-35,-36,-37,-39,-19,-25,-31,-38,100,-20,-21,-22,-23,-57,-58,-59,-24,-60,-32,-42,-48,-52,-53,-40,-51,-41,]),'OF':([34,35,36,37,38,40,41,42,43,44,46,64,66,67,70,75,81,82,83,84,85,86,87,90,91,96,108,116,124,125,130,137,138,],[-26,-27,-28,-29,-30,-33,-34,-35,-36,-37,-39,-19,-25,-31,-38,101,-20,-21,-22,-23,-57,-58,-59,-24,-60,-32,-42,-48,-52,-53,-40,-51,-41,]),'ELSE':([34,35,36,37,38,40,41,42,43,44,46,64,66,67,70,81,82,83,84,85,86,87,90,91,96,108,111,116,124,125,130,137,138,],[-26,-27,-28,-29,-30,-33,-34,-35,-36,-37,-39,-19,-25,-31,-38,-20,-21,-22,-23,-57,-58,-59,-24,-60,-32,-42,123,-48,-52,-53,-40,-51,-41,]),'POOL':([34,35,36,37,38,40,41,42,43,44,46,64,66,67,70,81,82,83,84,85,86,87,90,91,96,108,112,116,124,125,130,137,138,],[-26,-27,-28,-29,-30,-33,-34,-35,-36,-37,-39,-19,-25,-31,-38,-20,-21,-22,-23,-57,-58,-59,-24,-60,-32,-42,124,-48,-52,-53,-40,-51,-41,]),'FI':([34,35,36,37,38,40,41,42,43,44,46,64,66,67,70,81,82,83,84,85,86,87,90,91,96,108,116,124,125,130,132,137,138,],[-26,-27,-28,-29,-30,-33,-34,-35,-36,-37,-39,-19,-25,-31,-38,-20,-21,-22,-23,-57,-58,-59,-24,-60,-32,-42,-48,-52,-53,-40,137,-51,-41,]),'ESAC':([113,126,133,],[125,-55,-54,]),} + +_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,]),'classSet':([0,5,],[2,7,]),'class':([0,5,],[3,3,]),'_inherits':([6,],[8,]),'empty':([6,11,20,69,106,131,],[10,15,15,94,94,94,]),'ffeature':([11,20,],[13,24,]),'feature':([11,20,],[14,14,]),'temp':([11,20,51,103,],[17,17,77,77,]),'idDots':([11,20,21,51,54,101,103,126,],[18,18,28,18,28,115,18,115,]),'formal':([21,54,],[26,80,]),'expr':([23,31,32,33,39,47,48,49,50,55,56,57,58,59,60,61,68,69,71,99,100,102,105,106,109,118,123,127,131,],[30,64,65,66,67,72,73,74,75,81,82,83,84,85,86,87,91,95,97,111,112,116,119,95,122,128,132,134,95,]),'block':([23,31,32,33,39,47,48,49,50,55,56,57,58,59,60,61,68,69,71,99,100,102,105,106,109,118,123,127,131,],[34,34,34,34,34,34,34,34,34,34,34,34,34,34,34,34,34,34,34,34,34,34,34,34,34,34,34,34,34,]),'conditional':([23,31,32,33,39,47,48,49,50,55,56,57,58,59,60,61,68,69,71,99,100,102,105,106,109,118,123,127,131,],[35,35,35,35,35,35,35,35,35,35,35,35,35,35,35,35,35,35,35,35,35,35,35,35,35,35,35,35,35,]),'loop':([23,31,32,33,39,47,48,49,50,55,56,57,58,59,60,61,68,69,71,99,100,102,105,106,109,118,123,127,131,],[36,36,36,36,36,36,36,36,36,36,36,36,36,36,36,36,36,36,36,36,36,36,36,36,36,36,36,36,36,]),'case':([23,31,32,33,39,47,48,49,50,55,56,57,58,59,60,61,68,69,71,99,100,102,105,106,109,118,123,127,131,],[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,37,]),'dispatch':([23,31,32,33,39,47,48,49,50,55,56,57,58,59,60,61,68,69,71,99,100,102,105,106,109,118,123,127,131,],[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,38,38,38,]),'let':([23,31,32,33,39,47,48,49,50,55,56,57,58,59,60,61,68,69,71,99,100,102,105,106,109,118,123,127,131,],[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,]),'expression_list':([47,],[71,]),'declaration_list':([51,103,],[76,117,]),'arguments_list_opt':([69,106,131,],[92,120,136,]),'arguments_list':([69,106,131,],[93,93,93,]),'add':([101,126,],[113,133,]),'derivate':([101,126,],[114,114,]),} + +_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 -> classSet','program',1,'p_program','parser_rules.py',36), + ('classSet -> class SEMICOLON classSet','classSet',3,'p_classSet','parser_rules.py',42), + ('classSet -> class SEMICOLON','classSet',2,'p_classSet','parser_rules.py',43), + ('class -> CLASS TYPE _inherits LBRACE ffeature RBRACE','class',6,'p_class','parser_rules.py',50), + ('_inherits -> INHERITS TYPE','_inherits',2,'p_inherits','parser_rules.py',57), + ('_inherits -> empty','_inherits',1,'p_inherits','parser_rules.py',58), + ('ffeature -> feature SEMICOLON ffeature','ffeature',3,'p_ffeature','parser_rules.py',64), + ('ffeature -> empty','ffeature',1,'p_ffeature','parser_rules.py',65), + ('feature -> ID LPAREN formal RPAREN TDOTS TYPE LBRACE expr RBRACE','feature',9,'p_feature','parser_rules.py',71), + ('feature -> ID LPAREN RPAREN TDOTS TYPE LBRACE expr RBRACE','feature',8,'p_feature','parser_rules.py',72), + ('feature -> temp','feature',1,'p_feature','parser_rules.py',73), + ('temp -> idDots','temp',1,'p_temp','parser_rules.py',94), + ('temp -> idDots ASSIGN expr','temp',3,'p_temp','parser_rules.py',95), + ('idDots -> ID TDOTS TYPE','idDots',3,'p_idDots','parser_rules.py',106), + ('formal -> idDots COMMA formal','formal',3,'p_formal','parser_rules.py',113), + ('formal -> idDots','formal',1,'p_formal','parser_rules.py',114), + ('expression_list -> expression_list expr SEMICOLON','expression_list',3,'p_expression_list','parser_rules.py',120), + ('expression_list -> expr SEMICOLON','expression_list',2,'p_expression_list','parser_rules.py',121), + ('expr -> NOT expr','expr',2,'p_expression_not','parser_rules.py',128), + ('expr -> expr PLUS expr','expr',3,'p_expression_binop','parser_rules.py',135), + ('expr -> expr MINUS expr','expr',3,'p_expression_binop','parser_rules.py',136), + ('expr -> expr MULTIPLY expr','expr',3,'p_expression_binop','parser_rules.py',137), + ('expr -> expr DIVIDE expr','expr',3,'p_expression_binop','parser_rules.py',138), + ('expr -> LPAREN expr RPAREN','expr',3,'p_expression_g','parser_rules.py',153), + ('expr -> ISVOID expr','expr',2,'p_expression_g','parser_rules.py',154), + ('expr -> block','expr',1,'p_expression_g','parser_rules.py',155), + ('expr -> conditional','expr',1,'p_expression_g','parser_rules.py',156), + ('expr -> loop','expr',1,'p_expression_g','parser_rules.py',157), + ('expr -> case','expr',1,'p_expression_g','parser_rules.py',158), + ('expr -> dispatch','expr',1,'p_expression_g','parser_rules.py',159), + ('expr -> INT_COMP expr','expr',2,'p_expression_g','parser_rules.py',160), + ('block -> LBRACE expression_list RBRACE','block',3,'p_block','parser_rules.py',179), + ('expr -> ID','expr',1,'p_expression_id','parser_rules.py',186), + ('expr -> INTEGER','expr',1,'p_expression_int','parser_rules.py',193), + ('expr -> STRING','expr',1,'p_expression_str','parser_rules.py',200), + ('expr -> TRUE','expr',1,'p_expression_bool','parser_rules.py',207), + ('expr -> FALSE','expr',1,'p_expression_bool','parser_rules.py',208), + ('expr -> NEW TYPE','expr',2,'p_expression_newtype','parser_rules.py',215), + ('expr -> let','expr',1,'p_expression_l','parser_rules.py',222), + ('dispatch -> expr DOT ID LPAREN arguments_list_opt RPAREN','dispatch',6,'p_dispatch','parser_rules.py',228), + ('dispatch -> expr AT TYPE DOT ID LPAREN arguments_list_opt RPAREN','dispatch',8,'p_dispatch','parser_rules.py',229), + ('dispatch -> ID LPAREN arguments_list_opt RPAREN','dispatch',4,'p_dispatch','parser_rules.py',230), + ('arguments_list -> arguments_list COMMA expr','arguments_list',3,'p_arguments_list','parser_rules.py',251), + ('arguments_list -> expr','arguments_list',1,'p_arguments_list','parser_rules.py',252), + ('arguments_list_opt -> arguments_list','arguments_list_opt',1,'p_arguments_list_opt','parser_rules.py',260), + ('arguments_list_opt -> empty','arguments_list_opt',1,'p_arguments_list_opt','parser_rules.py',261), + ('empty -> ','empty',0,'p_empty','parser_rules.py',267), + ('let -> LET declaration_list IN expr','let',4,'p_let_expression','parser_rules.py',271), + ('declaration_list -> temp COMMA declaration_list','declaration_list',3,'p_declaration_list','parser_rules.py',279), + ('declaration_list -> temp','declaration_list',1,'p_declaration_list','parser_rules.py',280), + ('conditional -> IF expr THEN expr ELSE expr FI','conditional',7,'p_conditional','parser_rules.py',287), + ('loop -> WHILE expr LOOP expr POOL','loop',5,'p_loop','parser_rules.py',294), + ('case -> CASE expr OF add ESAC','case',5,'p_case','parser_rules.py',301), + ('add -> derivate SEMICOLON add','add',3,'p_add','parser_rules.py',308), + ('add -> derivate SEMICOLON','add',2,'p_add','parser_rules.py',309), + ('derivate -> idDots ARROW expr','derivate',3,'p_derivate','parser_rules.py',316), + ('expr -> expr LT expr','expr',3,'p_expression_cmp','parser_rules.py',324), + ('expr -> expr LTEQ expr','expr',3,'p_expression_cmp','parser_rules.py',325), + ('expr -> expr EQ expr','expr',3,'p_expression_cmp','parser_rules.py',326), + ('expr -> ID ASSIGN expr','expr',3,'p_expression_assign','parser_rules.py',339), +] diff --git a/src/semanticTest.py b/src/semanticTest.py new file mode 100644 index 000000000..5010f7936 --- /dev/null +++ b/src/semanticTest.py @@ -0,0 +1,22 @@ +import lexer_rules +import parser_rules +import sys + +from ply.lex import lex +from ply.yacc import yacc +from semantic_rules import Semantic + +def run(addr): + lexer = lex(module=lexer_rules) + parser = yacc(module=parser_rules) + sem = Semantic() + + with open(addr, encoding = "utf-8") as f: + text = f.read() + + ast = parser.parse(text, lexer) + sem.visit(ast) + + if len(sem.error) > 0: + print(sem.error[0] + '\n') + exit(1) diff --git a/src/semantic_rules.py b/src/semantic_rules.py new file mode 100644 index 000000000..740c5d30e --- /dev/null +++ b/src/semantic_rules.py @@ -0,0 +1,676 @@ +import visitor as visitor +from AST import * +import tools + +class Semantic: + def __init__(self): + self.error = [] + #classes + self.classmethods = {} + self.classmethods['Object'] = { + 'abort': ([],'Object'), + 'type_name':([],'String'), + 'copy': ([],'SELF_TYPE')} + self.classmethods['IO'] = { + 'out_string': ([('x','String')] , 'SELF_TYPE'), + 'out_int': ([('x','Int')] , 'SELF_TYPE'), + 'in_string': ([], 'String'), + 'in_int': ([], 'Int')} + self.classmethods['Int'] = {} + self.classmethods['String'] = { + 'length': ([], 'Int'), + 'concat': ([('s','String')], 'String'), + 'substr': ([('i','Int'), ('l','Int')], 'String')} + self.classmethods['Bool'] = {} + self.classmethods_original = {} + self.class_attrs = {'Object':[], 'IO':[], 'Int':[], 'String':[], 'Bool':[]} + self.graph = {} + self.local_variables = [('self', 'SELF_TYPE')] + self.current_class = None + self.current_method = None + self.class_parent = {'Int':'Object', 'String':'Object', 'Bool':'Object', 'IO':'Object'} + self.conforms = { + 'Object':set(['Object']), + 'Int':set(['Object','Int']), + 'String':set(['Object','String']), + 'IO':set(['Object','IO']), + 'Bool':set(['Object','Bool'])} + #conforms to + + def lca(self, type1, type2): + temp = type1 + parents = set([temp]) + while temp != 'Object': + temp = self.class_parent[temp] + parents.add(temp) + + temp = type2 + while not parents.__contains__(temp): + temp = self.class_parent[temp] + + return temp + + def check_eq(self, method1, method2): + args1 = method1[0] + return_type1 = method1[1] + + args2 = method2[0] + return_type2 = method2[1] + + if return_type1 != return_type2 or len(args1) != len(args2): + return False + + n = len(args1) + for i in range(n): + if args1[i][1] != args2[i][1]: + return False + return True + + def ComputeInheritsfeature(self, program): + l = ['Object'] + while len(l) > 0: + temp = l[0] + l.pop(0) + if not self.graph.__contains__(temp): + continue + for c in self.graph[temp]: + last = len(self.class_attrs[temp]) - 1 + for i in range(last, -1, -1): + self.class_attrs[c].insert(0, self.class_attrs[temp][i]) + + self.conforms[c].update(self.conforms[temp]) + l.append(c) + for m in self.classmethods[temp].items(): + if not self.classmethods[c].__contains__(m[0]): + # SI NO CONTIENE EL METODO, LO AGREGO A LA CLASE + self.classmethods[c][m[0]] = m[1] + elif not self.check_eq(m[1], self.classmethods[c][m[0]]): + for _class in program.classes: + if _class.name == c: + for _method in _class.methods: + if _method.id == m[0]: + self.error.append('({}, {}) - SemanticError: the types of the formal parameters, and the return type are not exactly the same in both definitions.'.format(_method.line, _method.index)) + return False + return True + + @visitor.on('node') + def visit(self, node): + pass + + @visitor.when(Program) + def visit(self, program): + '''Classes defined only once''' + for c in program.classes: + if not self.classmethods.__contains__(c.name): + self.classmethods[c.name] = {} + self.conforms[c.name] = set([c.name]) + else: + self.error.append('({}, {}) - SemanticError: Classes may not be redefined.'.format(c.line, c.index)) + return False + self.class_attrs[c.name] = c.attributes + for m in c.methods: + if self.classmethods[c.name].__contains__(m.id): + self.error.append('({}, {}) - SemanticError: Method {} is multiply defined.'.format(m.line, m.index, m.id)) + return False + params = [(v.id, v.type) for v in m.parameters] + self.classmethods[c.name][m.id] = (params, m.return_type) + + if not self.classmethods.__contains__('Main'): + #print('Every program must have a class Main') + return False + + '''Inheritance relationships''' + classes = self.classmethods.keys() + self.graph['Object'] = ['Int', 'String', 'Bool', 'IO'] + for c in program.classes: + if c.inherit is None: + self.graph['Object'].append(c.name) + self.conforms[c.name].add('Object') + self.class_parent[c.name] = 'Object' + elif not self.classmethods.__contains__(c.inherit.name): + self.error.append('({}, {}) - TypeError: Class {} inherits from an undefined class {}.'.format(c.line, c.index, c.name, c.inherit.name)) + return False + elif c.inherit.name == 'Int' or c.inherit.name =='String' or c.inherit.name =='Bool' or c.inherit.name =='Object': + self.error.append('({}, {}) - SemanticError: Class {} cannot inherit class {}.'.format(c.line, c.index, c.name, c.inherit.name)) + return False + else: + if self.graph.__contains__(c.inherit.name): + self.graph[c.inherit.name].append(c.name) #c.name is a child class of c.inherit + else: self.graph[c.inherit.name] = [c.name] + self.class_parent[c.name] = c.inherit.name + self.conforms[c.name].add(c.inherit.name) + if tools.dfs(self.graph, classes):#si hay ciclo + self.error.append('({}, {}) - SemanticError: Class {}, or an ancestor of {}, is involved in an inheritance cycle.'.format(c.line, c.index, c.name, c.name)) + return False + + # self.classmethods_original.update(self.classmethods) + for c, d in self.classmethods.items(): + self.classmethods_original[c] = d.copy() + + if not self.ComputeInheritsfeature(program): return False + + for _cl in classes: + for _item in self.classmethods[_cl].items(): + if _item[1][1] == 'SELF_TYPE': + _new = (_item[1][0],_cl) + self.classmethods[_cl][_item[0]] = _new + + for c in program.classes: + if not self.graph.__contains__(c.name): + atr_id = set([x.id for x in self.class_attrs[c.name]]) + if len(atr_id) < len(self.class_attrs[c.name]): + _redefinedID = set([]) + for item in self.class_attrs[c.name]: + if _redefinedID.__contains__(item.id): + self.error.append('({}, {}) - SemanticError: Attribute redef.'.format(item.line, item.index)) + return False + else: _redefinedID.add(item.id) + + for _item in self.class_attrs.items(): + n = len(_item[1]) + for i in range(n): + if _item[1][i].type == 'SELF_TYPE': + _item[1][i].type = _item[0] + + '''visito las clases''' + for c in program.classes: + if not self.visit(c): + return False + return True + + @visitor.when(Class) + def visit(self, _class): + + if _class.name == 'Main': + if not self.classmethods[_class.name].__contains__('main'): + #print('error: Main class must have a method main') + return False + elif len(self.classmethods['Main']['main'][0]) > 0: + #print('error: main takes no formal parameters') + return False + + self.current_class = _class.name + + for m in _class.methods: + if not self.visit(m): + return False + + #importante + for atr in _class.attributes: + if not self.visit(atr): + return False + + self.current_class = None + return True + + @visitor.when(Method) + def visit(self, method): + + for formal in method.parameters: + if not self.visit(formal): + return False + + args = method.parameters + if not method.parameters is None: + args_id = set([arg.id for arg in args]) + if len(args_id) < len(args): + self.error.append('({}, {}) - SemanticError: The identifiers used in the formal parameter list must be distinct.'.format(method.line, method.index)) + return False + + self.current_method = method.id + if not self.visit(method.expression): + return False + + self.local_variables.clear() + self.local_variables.append(('self','SELF_TYPE')) + self.current_method = None + _returnType = method.return_type if method.return_type != 'SELF_TYPE' else self.current_class + if not self.conforms[method.expression.static_type].__contains__(_returnType): + self.error.append('({}, {}) - TypeError: Inferred return type {} of method {} does not conform to declared return type {}.'.format(method.line, method.expression.index, method.expression.static_type, method.id, _returnType)) + return False + return True + + @visitor.when(Block) + def visit(self, block): + + for e in block.expressions: + if not self.visit(e): + return False + + n = len(block.expressions) - 1 + block.static_type = block.expressions[n].static_type + return True + + @visitor.when(Star) + def visit(self, star): + if not self.visit(star.first): + return False + if not self.visit(star.second): + return False + if star.first.static_type != "Int" or star.second.static_type != "Int": + self.error.append('({}, {}) - TypeError: non-Int arguments: {} * {}'.format(star.line, star.index,star.first.static_type,star.second.static_type)) + return False + star.static_type = 'Int' + return True + + @visitor.when(Plus) + def visit(self, plus): + if not self.visit(plus.first): + return False + if not self.visit(plus.second): + return False + if plus.first.static_type != "Int" or plus.second.static_type != "Int": + self.error.append('({}, {}) - TypeError: non-Int arguments: {} + {}'.format(plus.line, plus.index, plus.first.static_type, plus.second.static_type)) + return False + plus.static_type = 'Int' + return True + + @visitor.when(Minus) + def visit(self, minus): + if not self.visit(minus.first): + return False + if not self.visit(minus.second): + return False + if minus.first.static_type != "Int" or minus.second.static_type != "Int": + self.error.append('({}, {}) - TypeError: non-Int arguments: {} - {}'.format(minus.line, minus.index,minus.first.static_type, minus.second.static_type)) + return False + minus.static_type = 'Int' + return True + + @visitor.when(Div) + def visit(self, div): + if not self.visit(div.first): + return False + if not self.visit(div.second): + return False + if div.first.static_type != "Int" or div.second.static_type != "Int": + self.error.append('({}, {}) - TypeError: non-Int arguments: {} / {}'.format(div.line, div.index, div.first.static_type, div.second.static_type)) + return False + div.static_type = 'Int' + return True + + @visitor.when(LowerEqualThan) + def visit(self, lowerEqualThan): + if not self.visit(lowerEqualThan.first): + return False + + if not self.visit(lowerEqualThan.second): + return False + + if lowerEqualThan.first.static_type != "Int" or lowerEqualThan.second.static_type != "Int": + self.error.append('({}, {}) - TypeError: non-Int arguments: {} <= {}'.format(lowerEqualThan.line, lowerEqualThan.index,lowerEqualThan.first.static_type,lowerEqualThan.second.static_type)) + return False + + lowerEqualThan.static_type = 'Bool' + return True + + @visitor.when(EqualThan) + def visit(self, equalThan): + if not self.visit(equalThan.first): + return False + + if not self.visit(equalThan.second): + return False + + if equalThan.first.static_type == 'Int' or equalThan.first.static_type == 'String' or equalThan.first.static_type == 'Bool': + if equalThan.first.static_type != equalThan.second.static_type: + self.error.append('({}, {}) - TypeError: Illegal comparison with a basic type.'.format(equalThan.line, equalThan.index)) + return False + + equalThan.static_type = 'Bool' + return True + + @visitor.when(LowerThan) + def visit(self, lowerThan): + if not self.visit(lowerThan.first): + return False + if not self.visit(lowerThan.second): + return False + if lowerThan.first.static_type != 'Int' or lowerThan.second.static_type != 'Int': + self.error.append('({}, {}) - TypeError: non-Int arguments: {} < {}'.format(lowerThan.line, lowerThan.index,lowerThan.first.static_type,lowerThan.second.static_type)) + return False + + lowerThan.static_type = 'Bool' + return True + + @visitor.when(Not) + def visit(self, negation): + if not self.visit(negation.expr): + return False + + if negation.expr.static_type != 'Bool': + self.error.append("({}, {}) - TypeError: Argument of 'not' has type {} instead of Bool.".format(negation.line, negation.index, negation.expr.static_type)) + return False + + negation.static_type = 'Bool' + return True + + @visitor.when(IntegerComplement) + def visit(self, I_complement): + + if not self.visit(I_complement.expression): + return False + + if I_complement.expression.static_type != "Int": + self.error.append("({}, {}) - TypeError: Argument of '~' has type {} instead of Int.".format(I_complement.line, I_complement.index, I_complement.expression.static_type)) + return False + + I_complement.static_type = 'Int' + return True + + @visitor.when(IsVoid) + def visit(self, is_void): + + if not self.visit(is_void.expression): + return False + + is_void.static_type = 'Bool' + return True + + @visitor.when(Type) + def visit(self, _type): + #expr --> ID + + #verificar si esta declarado + if _type.name == 'self': + _type.static_type = self.current_class + return True + else: + n = len(self.local_variables) - 1 + for i in range(n, -1, -1): + local_id, local_type = self.local_variables[i] + if local_id == _type.name: + _type.static_type = local_type + return True + + if not self.current_method is None: + for args_id, args_type in self.classmethods[self.current_class][self.current_method][0]: + if args_id == _type.name: + _type.static_type = args_type + return True + + for _var in self.class_attrs[self.current_class]: + attr_id = _var.id + attr_type = _var.type + if attr_id == _type.name: + _type.static_type = attr_type + return True + + #print("error: tipo no declarado") + self.error.append('({}, {}) - NameError: Undeclared identifier {}.'.format(_type.line, _type.index, _type.name)) + return False + + @visitor.when(NewType) + def visit(self, new_type): + if new_type.type_name == 'SELF_TYPE': + new_type.static_type = self.current_class + else: + if not self.classmethods.__contains__(new_type.type_name): + self.error.append("({}, {}) - TypeError: 'new' used with undefined class {}.".format(new_type.line, new_type.index, new_type.type_name)) + return False + new_type.static_type = new_type.type_name + return True + + @visitor.when(Boolean) + def visit(self, boolean): + boolean.static_type = 'Bool' + return True + + @visitor.when(Interger) + def visit(self, interger): + interger.static_type = 'Int' + return True + + @visitor.when(String) + def visit(self, string): + string.static_type = 'String' + return True + + @visitor.when(Conditional) + def visit(self, cond): + if not self.visit(cond.if_expression): + return False + if not self.visit(cond.then_expression): + return False + if not self.visit(cond.else_expression): + return False + + if cond.if_expression.static_type != 'Bool': + self.error.append("({}, {}) - TypeError: Predicate of 'if' does not have type Bool.".format(cond.line, cond.index)) + return False + + thenType = cond.then_expression.static_type + elseType = cond.else_expression.static_type + + cond.static_type = self.lca(thenType, elseType) + return True + + @visitor.when(Loop) + def visit(self, loop): + + if not self.visit(loop.while_expression): + return False + + if not self.visit(loop.loop_expression): + return False + + if loop.while_expression.static_type != 'Bool': + self.error.append('({}, {}) - TypeError: Loop condition does not have type Bool.'.format(loop.line, loop.index)) + return False + + loop.static_type = 'Object' + + return True + + @visitor.when(LetVar) + def visit(self, let): + + for item in let.declarations: + if not self.visit(item): + return False + self.local_variables.append((item.id, item.static_type)) + + # if item.expr is None: + # local_variables.append((item.id,item.type)) + # else: + # if not visit(item.expr): + # return False + #local_variables.append((item.id,item.type)) + + if not self.visit(let.in_expression): + return False + + n = len(let.declarations) + m = len(self.local_variables) + + for i in range(n): + self.local_variables.pop(m - i - 1) + + let.static_type = let.in_expression.static_type + + return True + + @visitor.when(Assign) + def visit(self, assign): + + if not self.visit(assign.expression): + return False + + id_declared = False + id_type = None + + #verificar si esta declarado + if assign.id == 'self': + self.error.append("({}, {}) - SemanticError: Cannot assign to 'self'.".format(assign.line, assign.index)) + return False + else: + n = len(self.local_variables) - 1 + for i in range(n, -1, -1): + local_id, local_type = self.local_variables[i] + if local_id == assign.id: + id_declared = True + id_type = local_type + break + if not id_declared: + if not self.current_method is None: + for args_id, args_type in self.classmethods[self.current_class][self.current_method][0]: + if args_id == assign.id: + id_declared = True + id_type = args_type + break + if not id_declared: + for _var in self.class_attrs[self.current_class]: + attr_id = _var.id + attr_type = _var.type + if attr_id == assign.id: + id_declared = True + id_type = attr_type + break + + if not id_declared: + #print('error: variable no declarada') + return False + + if not self.conforms[assign.expression.static_type].__contains__(id_type): + #print('error: el tipo de la variable no se corresponde con el de la expression') + return False + + assign.static_type = assign.expression.static_type + return True + + @visitor.when(Attribute) + def visit(self, attr): + + if attr.id == 'self': + self.error.append('({}, {}) - SemanticError: is an error to assign to self or to bind self in a let, a case, or as a formal parameter. It is also illegal to have attributes named self.'.format(attr.line, attr.index)) + return False + + if not self.classmethods.__contains__(attr.type): + self.error.append('({}, {}) - TypeError: Class {} of declaration of {} is undefined.'.format(attr.line, attr.index, attr.type, attr.id)) + return False + if not self.visit(attr.expr): + return False + if not self.conforms[attr.expr.static_type].__contains__(attr.type): + self.error.append('({}, {}) - TypeError: Inferred type {} of initialization of {} does not conform to declared type {}.'.format(attr.line, attr.index, attr.expr.static_type, attr.id, attr.type)) + return False + + attr.static_type = attr.type + return True + + @visitor.when(Var) + def visit(self, var): + + if var.id == 'self': + self.error.append('({}, {}) - SemanticError: is an error to assign to self or to bind self in a let, a case, or as a formal parameter. It is also illegal to have attributes named self.'.format(var.line, var.index)) + return False + + if not self.classmethods.__contains__(var.type): + self.error.append('({}, {}) - TypeError: Class {} of declaration of {} is undefined.'.format(var.line, var.index, var.type, var.id)) + return False + + var.static_type = var.type + return True + + @visitor.when(Dispatch) + def visit(self, dispatch): + paramsTypes = [] + if not dispatch.parameters is None: + for e in dispatch.parameters: + if not self.visit(e): + return False + paramsTypes.append(e.static_type) + if dispatch.left_expression is None: + if not self.classmethods[self.current_class].__contains__(dispatch.func_id): + self.error.append('({}, {}) - AttributeError: Dispatch to undefined method {}.'.format(dispatch.line, dispatch.index, dispatch.func_id)) + return False + officialArgsType = [x[1] for x in self.classmethods[self.current_class][dispatch.func_id][0]] + _static_type = self.classmethods[self.current_class][dispatch.func_id][1] + else: + if not self.visit(dispatch.left_expression): + return False + if not self.classmethods[dispatch.left_expression.static_type].__contains__(dispatch.func_id): + self.error.append('({}, {}) - AttributeError: Dispatch to undefined method {}.'.format(dispatch.line, dispatch.index, dispatch.func_id)) + return False + officialArgsType = [x[1] for x in self.classmethods[dispatch.left_expression.static_type][dispatch.func_id][0]] + _static_type = self.classmethods[dispatch.left_expression.static_type][dispatch.func_id][1] + + if len(officialArgsType) != len(paramsTypes): + self.error.append('({}, {}) - SemanticError: Method {} called with wrong number of arguments.'.format(dispatch.line, dispatch.index, dispatch.func_id)) + return False + + _len = len(paramsTypes) + for i in range(_len): + if not self.conforms[paramsTypes[i]].__contains__(officialArgsType[i]): + self.error.append('({}, {}) - TypeError: In call of method {}, of parameters does not conform to declared type.'.format(dispatch.line, dispatch.index, dispatch.func_id)) + return False + dispatch.static_type = _static_type + return True + + @visitor.when(StaticDispatch) + def visit(self, static_dispatch): + paramsTypes = [] + if not static_dispatch.parameters is None: + for e in static_dispatch.parameters: + if not self.visit(e): + return False + paramsTypes.append(e.static_type) + + if not self.visit(static_dispatch.left_expression): + return False + + if not self.conforms[static_dispatch.left_expression.static_type].__contains__(static_dispatch.parent_type): + self.error.append('({}, {}) - TypeError: Expression type {} does not conform to declared static dispatch type {}.'.format(static_dispatch.line, static_dispatch.index, static_dispatch.left_expression.static_type, static_dispatch.parent_type)) + return False + + if not self.classmethods_original[static_dispatch.parent_type].__contains__(static_dispatch.func_id): + self.error.append('({}, {}) - AttributeError: Dispatch to undefined method {}.'.format(static_dispatch.line, static_dispatch.index, static_dispatch.func_id)) + return False + + officialArgsType = [x[1] for x in self.classmethods[static_dispatch.left_expression.static_type][static_dispatch.func_id][0]] + if len(officialArgsType) != len(paramsTypes): + self.error.append('({}, {}) - SemanticError: Method {} called with wrong number of arguments.'.format(static_dispatch.line, static_dispatch.index, static_dispatch.func_id)) + return False + _len = len(paramsTypes) + for i in range(_len): + if not self.conforms[paramsTypes[i]].__contains__(officialArgsType[i]): + self.error.append('({}, {}) - TypeError: In call of method {}, of parameters does not conform to declared type.'.format(static_dispatch.line, static_dispatch.index, static_dispatch.func_id)) + return False + + static_dispatch.static_type = self.classmethods[static_dispatch.left_expression.static_type][static_dispatch.func_id][1] + return True + + @visitor.when(Branch) + def visit(self, branch): + if not self.visit(branch.expr): + return False + branch.static_type = branch.expr.static_type + return True + + @visitor.when(Case) + def visit(self, case): + + if not self.visit(case.case_expression): + return False + + branchTypeSet = set([]) + for branch in case.implications: + if branchTypeSet.__contains__(branch.var.type): + self.error.append('({}, {}) - SemanticError: Duplicate branch {} in case statement.'.format(branch.line, branch.index, branch.var.type)) + return False + branchTypeSet.add(branch.var.type) + if not self.classmethods.__contains__(branch.var.type): + self.error.append('({}, {}) - TypeError: Class {} of case branch is undefined.'.format(branch.line, branch.index, branch.var.type)) + return False + + self.local_variables.append((branch.var.id, branch.var.type)) + if not self.visit(branch): + return False + n = len(self.local_variables) - 1 + self.local_variables.pop(n) + + static_type = case.implications[0].static_type + for branch in case.implications[1:]: + static_type = self.lca(static_type, branch.static_type) + + case.static_type = static_type + return True diff --git a/src/tools.py b/src/tools.py new file mode 100644 index 000000000..ec7260e82 --- /dev/null +++ b/src/tools.py @@ -0,0 +1,29 @@ +def dfs_visit(G, v, t, c, d): + t += 1 + d[v] = t + c[v] = 1 + for u in G[v]: + if not G.__contains__(u): + continue + if c[u] == 0: + if dfs_visit(G, u, t, c, d): + return True + elif c[u] == 1: + return True + c[v] = 2 + return False + +def dfs(G, classes): + '''retorna true si hay ciclo''' + color = {} + d = {} + for c in classes: + color[c] = 0 + d[c] = -1 + time = 0 + for v in G.keys(): + if color[v] == 0: + if dfs_visit(G, v, time, color, d): + return True + return False + diff --git a/src/visitor.py b/src/visitor.py new file mode 100644 index 000000000..765159f69 --- /dev/null +++ b/src/visitor.py @@ -0,0 +1,76 @@ +# The MIT License (MIT) +# +# Copyright (c) 2019 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 = iter(t) + return [t[k](*args, **kw) for k in ks if issub(typ, k)] + + 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) \ No newline at end of file