From 2b80744ac066eb6972c62851174d2da4f7e7151b Mon Sep 17 00:00:00 2001 From: mavaldivie Date: Wed, 24 Feb 2021 02:20:06 -0500 Subject: [PATCH 01/75] "Lexer and Parser tests passed" --- .gitignore | 1 + src/Main.py | 41 ++ src/Tools/Lexer.py | 271 +++++++++++++ src/Tools/Parser.py | 340 ++++++++++++++++ src/Tools/Tools/Errors.py | 34 ++ src/Tools/Tools/Firsts_and_Follows.py | 128 ++++++ src/Tools/Tools/Parser_LR1.py | 185 +++++++++ src/Tools/Tools/Semantic.py | 333 +++++++++++++++ src/Tools/Tools/automata.py | 210 ++++++++++ src/Tools/Tools/evaluation.py | 44 ++ src/Tools/Tools/parsing.py | 95 +++++ src/Tools/Tools/pycompiler.py | 560 ++++++++++++++++++++++++++ src/Tools/Tools/utils.py | 221 ++++++++++ src/Tools/Tools/visitor.py | 80 ++++ src/Tools/format_visitor.py | 116 ++++++ src/coolc.sh | 6 +- tests/lexer/iis1.cl | 2 +- tests/lexer/iis1_error.txt | 2 +- tests/lexer_test.py | 2 +- 19 files changed, 2666 insertions(+), 5 deletions(-) create mode 100644 src/Main.py create mode 100644 src/Tools/Lexer.py create mode 100644 src/Tools/Parser.py create mode 100644 src/Tools/Tools/Errors.py create mode 100644 src/Tools/Tools/Firsts_and_Follows.py create mode 100644 src/Tools/Tools/Parser_LR1.py create mode 100644 src/Tools/Tools/Semantic.py create mode 100644 src/Tools/Tools/automata.py create mode 100644 src/Tools/Tools/evaluation.py create mode 100644 src/Tools/Tools/parsing.py create mode 100644 src/Tools/Tools/pycompiler.py create mode 100644 src/Tools/Tools/utils.py create mode 100644 src/Tools/Tools/visitor.py create mode 100644 src/Tools/format_visitor.py diff --git a/.gitignore b/.gitignore index 4acafde18..ea399309b 100644 --- a/.gitignore +++ b/.gitignore @@ -408,3 +408,4 @@ dmypy.json # Custom rules (everything added below won't be overriden by 'Generate .gitignore File' if you use 'Update' option) +/src/code.cl diff --git a/src/Main.py b/src/Main.py new file mode 100644 index 000000000..a9b779ac0 --- /dev/null +++ b/src/Main.py @@ -0,0 +1,41 @@ +from Tools.Lexer import Lexer +from Tools.Parser import CoolParser + +def main(args): + try: + with open(args.file, 'r') as fd: + code = fd.read() + except Exception as e: + print(f"(0,0) - CompilerError: {e.args}") + exit(1) + + lexer = Lexer() + tokens = lexer.tokenize(code) + + for t in lexer.errors: + print(t) + + if any(lexer.errors): exit(1) + + #print(CoolParser.HasConflict) + + productions, operations = CoolParser(tokens) + + for e in CoolParser.errors: + print(e) + + if any(CoolParser.errors): exit(1) + + + + + + +if __name__ == "__main__": + import argparse + + parser = argparse.ArgumentParser(description='CoolCompiler pipeline') + parser.add_argument('-f', '--file', type=str, default='code.cl', help='file to read') + + args = parser.parse_args() + main(args) diff --git a/src/Tools/Lexer.py b/src/Tools/Lexer.py new file mode 100644 index 000000000..43440f7ed --- /dev/null +++ b/src/Tools/Lexer.py @@ -0,0 +1,271 @@ +import ply.lex as lex +from .Tools.utils import Token +from .Parser import CoolGrammar +from .Tools.Errors import LexicographicError + +class Lexer: + states = ( + ('comment', 'exclusive'), + ('string', 'exclusive') + ) + + # Palabras reservadas del lenguaje COOL + reserved = { + 'class': 'CLASS', + 'inherits': 'INHERITS', + 'function': 'FUNCTION', + 'if': 'IF', + 'then': 'THEN', + 'else': 'ELSE', + 'fi': 'FI', + 'while': 'WHILE', + 'loop': 'LOOP', + 'pool': 'POOL', + 'let': 'LET', + 'in': 'IN', + 'case': 'CASE', + 'of': 'OF', + 'esac': 'ESAC', + 'new': 'NEW', + 'isvoid': 'ISVOID' + } + + t_ignore = ' \f\r\t\v' + t_comment_ignore = '' + t_string_ignore = '' + + tokens = [ + # Identifiers + 'TYPE', 'ID', + # Primitive data types + 'INTEGER', 'STRING', 'BOOL', + # Special keywords + 'ACTION', + # Operators + 'ASSIGN', 'LESS', 'LESSEQUAL', 'EQUAL', 'INT_COMPLEMENT', 'NOT', + # Literals + 'PLUS', 'MINUS', 'STAR', 'DIVIDE', 'COLON', 'SEMICOLON', + 'OPAR', 'CPAR', 'OCUR', 'CCUR', 'AT', 'DOT', 'COMMA', + ] + list(reserved.values()) + + tokens_dict = {} + for tok in tokens: + try: + tokens_dict[tok] = CoolGrammar[tok.lower()].Name + except: + pass + + tokens_dict['ACTION'] = CoolGrammar['=>'].Name + tokens_dict['ASSIGN'] = CoolGrammar['<-'].Name + tokens_dict['LESS'] = CoolGrammar['<'].Name + tokens_dict['LESSEQUAL'] = CoolGrammar['<='].Name + tokens_dict['EQUAL'] = CoolGrammar['='].Name + tokens_dict['INT_COMPLEMENT'] = CoolGrammar['~'].Name + + tokens_dict['PLUS'] = CoolGrammar['+'].Name + tokens_dict['MINUS'] = CoolGrammar['-'].Name + tokens_dict['STAR'] = CoolGrammar['*'].Name + tokens_dict['DIVIDE'] = CoolGrammar['/'].Name + tokens_dict['COLON'] = CoolGrammar[':'].Name + tokens_dict['SEMICOLON'] = CoolGrammar[';'].Name + tokens_dict['OPAR'] = CoolGrammar['('].Name + tokens_dict['CPAR'] = CoolGrammar[')'].Name + tokens_dict['OCUR'] = CoolGrammar['{'].Name + tokens_dict['CCUR'] = CoolGrammar['}'].Name + tokens_dict['AT'] = CoolGrammar['@'].Name + tokens_dict['DOT'] = CoolGrammar['.'].Name + tokens_dict['COMMA'] = CoolGrammar[','].Name + + + def __init__(self): + self.lexer = lex.lex(module=self) + self.comment_level = 0 + self.code = '' + self.current_string = '' + self.errors = [] + + + # Expresiones regulares + + def t_INTEGER(self, t): + r'[0-9]+' + t.value = int(t.value) + return t + + def t_BOOL(self, t): + r't[rR][uU][eE]|f[aA][lL][sS][eE]' + t.value = True if t.value == 'true' else False + return t + + + # Other tokens with precedence before TYPE and ID + + def t_NOT(self, t): + r'[nN][oO][tT]' + return t + + # Identifiers + + def t_TYPE(self, t): + r'[A-Z][A-Za-z0-9_]*' + + try: + t.type = self.reserved[t.value.lower()] + except KeyError: + pass + + return t + + def t_ID(self, t): + r'[a-z][A-Za-z0-9_]*' + + try: + t.type = self.reserved[t.value.lower()] + except KeyError: + pass + + return t + + + t_ASSIGN = r'<-' + t_LESS = r'<' + t_LESSEQUAL = r'<=' + t_EQUAL = r'=' + t_INT_COMPLEMENT = r'~' + t_ACTION = r'=>' + + t_PLUS = r'\+' + t_MINUS = r'-' + t_STAR = r'\*' + t_DIVIDE = r'/' + t_COLON = r':' + t_SEMICOLON = r';' + t_OPAR = r'\(' + t_CPAR = r'\)' + t_OCUR = r'{' + t_CCUR = r'}' + t_AT = r'@' + t_DOT = r'\.' + t_COMMA = r',' + + #################### + ##### COMMENTS ##### + #################### + def t_LINECOMMENT(self, t): + r'--.*' + pass + + def t_COMMENTBEGIN(self, t): + r'\(\*' + self.comment_level += 1 + t.lexer.begin('comment') + + def t_comment_COMMENTBEGIN(self, t): + r'\(\*' + self.comment_level += 1 + + def t_comment_COMMENTEND(self, t): + r'\*\)' + self.comment_level -= 1 + if self.comment_level == 0: + t.lexer.begin('INITIAL') + + def t_comment_eof(self, t): + self.errors.append(LexicographicError(t.lineno, + self.find_column(t), 'EOF in comment')) + self.lexer.begin('INITIAL') + + def t_comment_error(self, t): + t.lexer.skip(1) + + ############################ + ##### STRING CONSTANTS ##### + ############################ + + def t_STRINGBEGIN(self, t): + r'"' + self.current_string = '' + self.lexer.begin('string') + + def t_string_NULL(self, t): + r'\0' + self.errors.append(LexicographicError(t.lineno, + self.find_column(t), 'Null caracter in string')) + self.lexer.begin('INITIAL') + + def t_string_NEWLINE(self, t): + r'\\\n' + self.current_string += '\n' + t.lexer.lineno += 1 + + def t_string_INVALID_NEWLINE(self, t): + r'\n' + t.lexer.lineno += 1 + self.errors.append(LexicographicError(t.lineno, + self.find_column(t), 'Unterminated string constant')) + self.lexer.begin('INITIAL') + + def t_string_SCAPED_SPECIAL_CHARACTER(self, t): + r'\\(b|t|f)' + self.current_string += t.value + + def t_string_SCAPED_CHARACTER(self, t): + r'\\.' + self.current_string += t.value[1] + + def t_string_eof(self, t): + self.errors.append(LexicographicError(t.lineno, + self.find_column(t), 'EOF in string constant')) + self.lexer.begin('INITIAL') + + def t_string_STRINGEND(self, t): + r'"' + t.value = self.current_string + t.type = 'STRING' + self.lexer.begin('INITIAL') + return t + + def t_string_CHARACTER(self, t): + r'.' + self.current_string += t.value + + def t_string_error(self, t): + return t + + ########################### + ###### SPECIAL RULES ###### + ########################### + + def t_ANY_newline(self, t): + r'\n+' + t.lexer.lineno += len(t.value) + + def find_column(self, token): + line_start = self.code.rfind('\n', 0, token.lexpos) + 1 + return (token.lexpos - line_start) + 1 + + def t_error(self, t): + self.errors.append(LexicographicError(t.lineno, + self.find_column(t), f'ERROR "{t.value[0]}"')) + t.lexer.skip(1) + + + ''' + Dado un string retorna el arreglo de tokens resultante de analizar dicho string + ''' + def tokenize(self, code): + tokens = [] + self.code = code + + self.lexer.input(code) + while True: + token = self.lexer.token() + if token is None: + break + + tokens.append(Token(token.value, self.tokens_dict[token.type], + token.lineno, self.find_column(token))) + + tokens.append(Token('$', CoolGrammar.EOF.Name)) + + return tokens diff --git a/src/Tools/Parser.py b/src/Tools/Parser.py new file mode 100644 index 000000000..038c7b00a --- /dev/null +++ b/src/Tools/Parser.py @@ -0,0 +1,340 @@ +from .Tools.pycompiler import Grammar +from .Tools.Parser_LR1 import LR1Parser + + +# Clases necesarias para representar el AST del programa COOL +class Node: + pass + +# Raiz del AST +class ProgramNode(Node): + def __init__(self, declarations): + self.declarations = declarations + self.line = declarations[0].line + self.column = declarations[0].column + + +class DeclarationNode(Node): + pass + + +class ClassDeclarationNode(DeclarationNode): + def __init__(self, idx, features, parent=None): + self.id = idx + self.parent = parent + self.features = features + self.line = idx.line + self.column = idx.column + + +class AttrDeclarationNode(DeclarationNode): + def __init__(self, idx, typex, expression=None): + self.id = idx + self.type = typex + self.expression = expression + self.line = idx.line + self.column = idx.column + + +class FuncDeclarationNode(DeclarationNode): + def __init__(self, idx, params, return_type, body): + self.id = idx + self.params = params + self.type = return_type + self.body = body + self.line = idx.line + self.column = idx.column + + +class ExpressionNode(Node): + pass + + +class IfThenElseNode(ExpressionNode): + def __init__(self, condition, if_body, else_body): + self.condition = condition + self.if_body = if_body + self.else_body = else_body + self.line = condition.line + self.column = condition.column + + +class WhileLoopNode(ExpressionNode): + def __init__(self, condition, body): + self.condition = condition + self.body = body + self.line = condition.line + self.column = condition.column + + +class BlockNode(ExpressionNode): + def __init__(self, expressions): + self.expressions = expressions + self.line = expressions[-1].line + self.column = expressions[-1].column + + +class LetInNode(ExpressionNode): + def __init__(self, let_body, in_body): + self.let_body = let_body + self.in_body = in_body + self.line = in_body.line + self.column = in_body.column + + +class CaseOfNode(ExpressionNode): + def __init__(self, expression, branches): + self.expression = expression + self.branches = branches + self.line = expression.line + self.column = expression.column + + +class AssignNode(ExpressionNode): + def __init__(self, idx, expression): + self.id = idx + self.expression = expression + self.line = idx.line + self.column = idx.column + + +class UnaryNode(ExpressionNode): + def __init__(self, expression): + self.expression = expression + self.line = expression.line + self.column = expression.column + + +class NotNode(UnaryNode): + pass + + +class BinaryNode(ExpressionNode): + def __init__(self, left, right): + self.left = left + self.right = right + self.line = left.line + self.column = left.column + + +class LessEqualNode(BinaryNode): + pass + + +class LessNode(BinaryNode): + pass + + +class EqualNode(BinaryNode): + pass + + +class ArithmeticNode(BinaryNode): + pass + + +class PlusNode(ArithmeticNode): + pass + + +class MinusNode(ArithmeticNode): + pass + + +class StarNode(ArithmeticNode): + pass + + +class DivNode(ArithmeticNode): + pass + + +class IsVoidNode(UnaryNode): + pass + + +class ComplementNode(UnaryNode): + pass + + +class FunctionCallNode(ExpressionNode): + def __init__(self, obj, idx, args, typex=None): + self.obj = obj + self.id = idx + self.args = args + self.type = typex + self.line = idx.line + self.column = idx.column + + +class MemberCallNode(ExpressionNode): + def __init__(self, idx, args): + self.id = idx + self.args = args + self.line = idx.line + self.column = idx.column + + +class NewNode(ExpressionNode): + def __init__(self, typex): + self.type = typex + self.line = typex.line + self.column = typex.column + + +class AtomicNode(ExpressionNode): + def __init__(self, token): + self.token = token + self.line = token.line + self.column = token.column + + +class IntegerNode(AtomicNode): + pass + + +class IdNode(AtomicNode): + pass + + +class StringNode(AtomicNode): + pass + + +class BoolNode(AtomicNode): + pass + + +# Representacion de la gramatica de COOL utilizando la clase grammar +CoolGrammar = Grammar() + +# noterminales +program = CoolGrammar.NonTerminal('', startSymbol=True) +class_list, def_class = CoolGrammar.NonTerminals(' ') +feature_list, feature = CoolGrammar.NonTerminals(' ') +param_list, param = CoolGrammar.NonTerminals(' ') +expr_1, expr_2, member_call, expr_list, let_list, case_list = CoolGrammar.NonTerminals( + ' ') +comp_expr, arith, arith_2, term, factor, factor_2 = CoolGrammar.NonTerminals( + ' ') +atom, func_call, arg_list = CoolGrammar.NonTerminals(' ') + +# terminales +classx, inherits, function = CoolGrammar.Terminals('class inherits function') +ifx, then, elsex, fi = CoolGrammar.Terminals('if then else fi') +whilex, loop, pool = CoolGrammar.Terminals('while loop pool') +let, inx = CoolGrammar.Terminals('let in') +case, of, esac = CoolGrammar.Terminals('case of esac') +semi, colon, comma, dot, at, opar, cpar, ocur, ccur, larrow, rarrow = CoolGrammar.Terminals( + '; : , . @ ( ) { } <- =>') +plus, minus, star, div, isvoid, compl = CoolGrammar.Terminals('+ - * / isvoid ~') +notx, less, leq, equal = CoolGrammar.Terminals('not < <= =') +new, idx, typex, integer, string, boolx = CoolGrammar.Terminals('new id type integer string bool') + +# Producciones +program %= class_list, lambda h, s: ProgramNode(s[1]) + +# Lista de clases +class_list %= def_class + class_list, lambda h, s: [s[1]] + s[2] +class_list %= def_class, lambda h, s: [s[1]] + +# Defincicion de la clase +def_class %= classx + typex + ocur + feature_list + ccur + semi, lambda h, s: ClassDeclarationNode(s[2], s[4]) +def_class %= classx + typex + inherits + typex + ocur + feature_list + ccur + semi, lambda h, s: ClassDeclarationNode( + s[2], s[6], s[4]) + +# Lista de propiedades de la clase +feature_list %= feature + feature_list, lambda h, s: [s[1]] + s[2] +feature_list %= CoolGrammar.Epsilon, lambda h, s: [] + +# Atributos de la clase +feature %= idx + colon + typex + semi, lambda h, s: AttrDeclarationNode(s[1], s[3]) +feature %= idx + colon + typex + larrow + expr_1 + semi, lambda h, s: AttrDeclarationNode(s[1], s[3], s[5]) + +# Metodos constructores de la clase +feature %= idx + opar + param_list + cpar + colon + typex + ocur + expr_1 + ccur + semi, lambda h, s: FuncDeclarationNode( + s[1], s[3], s[6], s[8]) +feature %= idx + opar + cpar + colon + typex + ocur + expr_1 + ccur + semi, lambda h, s: FuncDeclarationNode(s[1], [], + s[5], s[7]) +# Metodos de la clase +feature %= function + idx + opar + param_list + cpar + colon + typex + ocur + expr_1 + ccur + semi, lambda h, s: FuncDeclarationNode( + s[2], s[4], s[7], s[9]) +feature %= function + idx + opar + cpar + colon + typex + ocur + expr_1 + ccur + semi, lambda h, s: FuncDeclarationNode(s[2], [], + s[6], s[8]) +# Lista de parametros de funcion +param_list %= param, lambda h, s: [s[1]] +param_list %= param + comma + param_list, lambda h, s: [s[1]] + s[3] + +# parametro de funcion +param %= idx + colon + typex, lambda h, s: (s[1], s[3]) + +### Expresiones ### +# Expresion Let-in +expr_1 %= let + let_list + inx + expr_1, lambda h, s: LetInNode(s[2], s[4]) +let_list %= idx + colon + typex, lambda h, s: [(s[1], s[3], None)] +let_list %= idx + colon + typex + larrow + expr_1, lambda h, s: [(s[1], s[3], s[5])] +let_list %= idx + colon + typex + comma + let_list, lambda h, s: [(s[1], s[3], None)] + s[5] +let_list %= idx + colon + typex + larrow + expr_1 + comma + let_list, lambda h, s: [(s[1], s[3], s[5])] + s[7] + +expr_1 %= idx + larrow + expr_1, lambda h, s: AssignNode(s[1], s[3]) +expr_1 %= notx + expr_1, lambda h, s: NotNode(s[2]) +expr_1 %= expr_2 + equal + expr_1, lambda h, s: EqualNode(s[1], s[3]) +expr_1 %= expr_2, lambda h, s: s[1] + +expr_2 %= arith + less + arith, lambda h, s: LessNode(s[1], s[3]) +expr_2 %= arith + leq + arith, lambda h, s: LessEqualNode(s[1], s[3]) +expr_2 %= arith, lambda h, s: s[1] + +#Expresiones aritmeticas +arith %= arith + plus + factor, lambda h, s: PlusNode(s[1], s[3]) +arith %= arith + minus + factor, lambda h, s: MinusNode(s[1], s[3]) +arith %= factor, lambda h, s: s[1] + +factor %= factor + star + atom, lambda h, s: StarNode(s[1], s[3]) +factor %= factor + div + atom, lambda h, s: DivNode(s[1], s[3]) +factor %= term, lambda h, s: s[1] + +term %= compl + term, lambda h, s: ComplementNode(s[2]) +term %= isvoid + term, lambda h, s: IsVoidNode(s[2]) +term %= atom, lambda h, s: s[1] + +# Encapsulaciones atomicas +atom %= opar + expr_1 + cpar, lambda h, s: s[2] +atom %= integer, lambda h, s: IntegerNode(s[1]) +atom %= string, lambda h, s: StringNode(s[1]) +atom %= boolx, lambda h, s: BoolNode(s[1]) +atom %= idx, lambda h, s: IdNode(s[1]) +atom %= ifx + expr_1 + then + expr_1 + elsex + expr_1 + fi, lambda h, s: IfThenElseNode(s[2], s[4], s[6]) +atom %= whilex + expr_1 + loop + expr_1 + pool, lambda h, s: WhileLoopNode(s[2], s[4]) + +# Expresion new +atom %= new + typex, lambda h, s: NewNode(s[2]) + +# Encapsulamiento entre corchetes +atom %= ocur + expr_list + ccur, lambda h, s: BlockNode(s[2]) +expr_list %= expr_1 + semi, lambda h, s: [s[1]] +expr_list %= expr_1 + semi + expr_list, lambda h, s: [s[1]] + s[3] + +# Expresion Case of +atom %= case + expr_1 + of + case_list + esac, lambda h, s: CaseOfNode(s[2], s[4]) +case_list %= idx + colon + typex + rarrow + expr_1 + semi, lambda h, s: [(s[1], s[3], s[5])] +case_list %= idx + colon + typex + rarrow + expr_1 + semi + case_list, lambda h, s: [(s[1], s[3], s[5])] + s[7] + +atom %= func_call, lambda h, s: s[1] + +# Llamado a funcion +func_call %= atom + at + typex + dot + idx + opar + arg_list + cpar, lambda h, s: FunctionCallNode(s[1], s[5], s[7], s[3]) +func_call %= atom + at + typex + dot + idx + opar + cpar, lambda h, s: FunctionCallNode(s[1], s[5], [], s[3]) +func_call %= atom + dot + idx + opar + arg_list + cpar, lambda h, s: FunctionCallNode(s[1], s[3], s[5]) +func_call %= atom + dot + idx + opar + cpar, lambda h, s: FunctionCallNode(s[1], s[3], []) + +# Llamado a miembro de clase +func_call %= idx + opar + arg_list + cpar, lambda h, s: MemberCallNode(s[1], s[3]) +func_call %= idx + opar + cpar, lambda h, s: MemberCallNode(s[1], []) + +# Lista de argumentos +arg_list %= expr_1, lambda h, s: [s[1]] +arg_list %= expr_1 + comma + arg_list, lambda h, s: [s[1]] + s[3] + +# parser +CoolParser = LR1Parser(CoolGrammar) diff --git a/src/Tools/Tools/Errors.py b/src/Tools/Tools/Errors.py new file mode 100644 index 000000000..e40b9da9d --- /dev/null +++ b/src/Tools/Tools/Errors.py @@ -0,0 +1,34 @@ + +class Error: + def __init__(self, line = None, column = None, text = ''): + self.line = line + self.column = column + self.text = text + def __str__(self): + raise NotImplementedError() + def __repr__(self): + raise NotImplementedError() + +class CompilerError(Error): + def __str__(self): + return f'{self.line, self.column} - ' \ + f'CompilerError: {self.text}' + def __repr__(self): + return f'{self.line, self.column} - ' \ + f'CompilerError: {self.text}' + +class LexicographicError(Error): + def __str__(self): + return f'{self.line, self.column} - ' \ + f'LexicographicError: {self.text}' + def __repr__(self): + return f'{self.line, self.column} - ' \ + f'LexicographicError: {self.text}' + +class SyntacticError(Error): + def __str__(self): + return f'{self.line, self.column} - ' \ + f'SyntacticError: ERROR at or near {self.text}' + def __repr__(self): + return f'{self.line, self.column} - ' \ + f'SyntacticError: ERROR at or near {self.text}' \ No newline at end of file diff --git a/src/Tools/Tools/Firsts_and_Follows.py b/src/Tools/Tools/Firsts_and_Follows.py new file mode 100644 index 000000000..029b33aa0 --- /dev/null +++ b/src/Tools/Tools/Firsts_and_Follows.py @@ -0,0 +1,128 @@ +from .utils import ContainerSet + +''' +Dada una forma oracional alpha computa sus firsts +''' +def compute_local_first(firsts, alpha): + first_alpha = ContainerSet() + + try: + alpha_is_epsilon = alpha.IsEpsilon + except: + alpha_is_epsilon = False + + # alpha == epsilon ? First(alpha) = { epsilon } + if alpha_is_epsilon: + first_alpha.set_epsilon() + return first_alpha + + # alpha = X1 ... XN + # First(Xi) subconjunto First(alpha) + # epsilon pertenece a First(X1)...First(Xi) ? First(Xi+1) subconjunto de First(X) y First(alpha) + # epsilon pertenece a First(X1)...First(XN) ? epsilon pertence a First(X) y al First(alpha) + for symbol in alpha: + first_alpha.update(firsts[symbol]) + if not firsts[symbol].contains_epsilon: + break + else: + first_alpha.set_epsilon() + + return first_alpha + + +''' +Computa los firsts de todos los simbolos de la gramatica +''' +def compute_firsts(G): + firsts = {} + change = True + + # Los firsts de los terminales son ellos mismos + for terminal in G.terminals: + firsts[terminal] = ContainerSet(terminal) + + # Inicializa los firsts de los noterminales como un conjunto vacio + for nonterminal in G.nonTerminals: + firsts[nonterminal] = ContainerSet() + + # Metodo de punto fijo, mientras halla algun cambio en los firsts + # de algun simbolo, itera por todas las producciones recalculando los firsts + # del noterminal correspondiente + while change: + change = False + + # P: X -> alpha + for production in G.Productions: + X = production.Left + alpha = production.Right + + # get current First(X) + first_X = firsts[X] + + # init First(alpha) + try: + first_alpha = firsts[alpha] + except KeyError: + first_alpha = firsts[alpha] = ContainerSet() + + # CurrentFirst(alpha) + local_first = compute_local_first(firsts, alpha) + + # update First(X) and First(alpha) from CurrentFirst(alpha) + change |= first_alpha.hard_update(local_first) + change |= first_X.hard_update(local_first) + + return firsts + +''' +Computa los follows de cada noterminal de la gramatica +''' +from itertools import islice +def compute_follows(G, firsts): + follows = {} + change = True + + local_firsts = {} + + # Inicializa los follows de los noterminales como un conjunto vacio + for nonterminal in G.nonTerminals: + follows[nonterminal] = ContainerSet() + # EOF pertenece a los follows del simbolo inicial + follows[G.startSymbol] = ContainerSet(G.EOF) + + # Metodo de punto fijo, mientras haya cambios en los follows + # de algun noterminal, itera por todas las producciones y recalcula + # los follows de cada noterminal + while change: + change = False + + # P: X -> alpha + for production in G.Productions: + X = production.Left + alpha = production.Right + + follow_X = follows[X] + + # Si la produccion actual es de la forma X -> v Y w + # donde v y w son formas oracionales entonces cada elemento + # que pertenece al first de w (excepto epsilon) pertenece tambien al follow + # de Y. Si ademas w ->* epsilon entonces los follows de X pertenecen + # al follows de Y. + # X -> zeta Y beta + # First(beta) - { epsilon } subset of Follow(Y) + # beta ->* epsilon or X -> zeta Y ? Follow(X) subset of Follow(Y) + for i, symbol in enumerate(alpha): + if symbol.IsNonTerminal: + try: + first_beta = local_firsts[alpha, i] + except KeyError: + first_beta = local_firsts[alpha, i] = compute_local_first(firsts, islice(alpha, i + 1, None)) + + change |= follows[symbol].update(first_beta) + + if first_beta.contains_epsilon: + change |= follows[symbol].update(follow_X) + + return follows + + diff --git a/src/Tools/Tools/Parser_LR1.py b/src/Tools/Tools/Parser_LR1.py new file mode 100644 index 000000000..f12ba3162 --- /dev/null +++ b/src/Tools/Tools/Parser_LR1.py @@ -0,0 +1,185 @@ +from .pycompiler import Item +from .utils import ContainerSet +from .Firsts_and_Follows import compute_firsts, compute_local_first +from .automata import State +from .parsing import ShiftReduceParser + +''' +Recibe un item LR(1) y devuelve el conjunto de items que +sugiere incluir (directamente) debido a la presencia de +un . delante de un no terminal. +expand("𝑌→𝛼.𝑋𝛿,𝑐") = { "𝑋→.𝛽,𝑏" | 𝑏∈𝐹𝑖𝑟𝑠𝑡(𝛿𝑐) } +''' +def expand(item, firsts): + next_symbol = item.NextSymbol + if next_symbol is None or not next_symbol.IsNonTerminal: + return [] + + lookaheads = ContainerSet() + # (Compute lookahead for child items) + # Preview retorna los elementos que se encuantran detras del punto + # en el item mas sus lookaheads + for prev in item.Preview(): + lookaheads.update(compute_local_first(firsts, prev)) + + assert not lookaheads.contains_epsilon + + # (Build and return child items) + return [Item(x, 0, lookaheads) for x in next_symbol.productions] + +''' +Recibe un conjunto de items LR(1) y devuelve el mismo conjunto pero +en el que los items con mismo centro están unidos (se combinan los lookahead). +''' +def compress(items): + centers = {} + + for item in items: + center = item.Center() + try: + lookaheads = centers[center] + except KeyError: + centers[center] = lookaheads = set() + lookaheads.update(item.lookaheads) + + return {Item(x.production, x.pos, set(lookahead)) for x, lookahead in centers.items()} + +''' +Computa la clausura de un conjunto de items +''' +# Metodo de punto fijo, mientras haya cambios en el conjunto de items, +# itero por todos los items X -> v.Yw,p, donde X y Y son noterminales y +# v y w son formas oracionales, e incluyo los items de la forma +# Y -> Z,b donde b ∈ First(wp), para todas las producciones de Y. +# Dischos items se calculan mediantes el metodo expand. +# 𝐶𝐿(𝐼) = 𝐼 ∪ { 𝑋→.𝛽,𝑏 } +# tales que 𝑌→𝛼.𝑋𝛿,𝑐 ∈ 𝐶𝐿(𝐼) y 𝑏 ∈ 𝐹𝑖𝑟𝑠𝑡(𝛿𝑐) + +def closure_lr1(items, firsts): + closure = ContainerSet(*items) + + changed = True + while changed: + changed = False + + new_items = ContainerSet() + for item in closure: + new_items.extend(expand(item, firsts)) + + changed = closure.update(new_items) + + return compress(closure) + +''' +Recibe como parámetro un conjunto de items y un símbolo, y devuelve el +conjunto goto(items, symbol). El método permite setear el parámentro +just_kernel=True para calcular solamente el conjunto de items kernels +en lugar de todo el conjunto de items. En caso contrario, se debe proveer +el conjunto con los firsts de la gramática, puesto que serán usados al +calcular la clausura. +''' +# 𝐺𝑜𝑡𝑜(𝐼,𝑋) = 𝐶𝐿({ 𝑌→𝛼𝑋.𝛽,𝑐 | 𝑌→𝛼.𝑋𝛽,𝑐 ∈ 𝐼}) +def goto_lr1(items, symbol, firsts=None, just_kernel=False): + assert just_kernel or firsts is not None, '`firsts` must be provided if `just_kernel=False`' + items = frozenset(item.NextItem() for item in items if item.NextSymbol == symbol) + return items if just_kernel else closure_lr1(items, firsts) + +''' +Computa el automata LR1 correspondiente a la gramatica +''' +# El estado inicial es la clausura del item S' -> .S, $. +# Todos los estados son finales. +# Las transiciones ocurren con terminales y no terminales. +# La función de transición está dada por la función goto. +# f(Ii, c) = Goto(Ii, c) +def build_LR1_automaton(G): + assert len(G.startSymbol.productions) == 1, 'Grammar must be augmented' + + firsts = compute_firsts(G) + firsts[G.EOF] = ContainerSet(G.EOF) + + start_production = G.startSymbol.productions[0] + start_item = Item(start_production, 0, lookaheads=(G.EOF,)) + start = frozenset([start_item]) + + # El estado inicial es la clausura del item S' -> .S, $ + closure = closure_lr1(start, firsts) + automaton = State(frozenset(closure), True) + + pending = [start] + visited = {start: automaton} + + # BFS para construir el automata + # Mientras hallan estados pendientes + while pending: + # Tomo el siguiente estado a analizar + current = pending.pop() + current_state = visited[current] + + # Itero por cada simbolo de la gramatica + for symbol in G.terminals + G.nonTerminals: + # Chequeo si el estado actual posee transicion con ese simbolo a algun estado x + next_state_key = goto_lr1(current_state.state, symbol, just_kernel=True) + + # Si no la posee, continuo al siguiente simbolo + if not next_state_key: + continue + try: + next_state = visited[next_state_key] + except KeyError: + # Si el estado x no ha sido visto por el bfs, lo incluyo + # en la lista de pending + next_state_items = goto_lr1(current_state.state, symbol, firsts) + next_state = State(frozenset(next_state_items), True) + pending.append(next_state_key) + visited[next_state_key] = next_state + + # incluto la transicion del estado actual a x con el simbolo actual + current_state.add_transition(symbol.Name, next_state) + + #automaton.set_formatter(multiline_formatter) + return automaton + + +# Recordar que la diferencia entre los parsers Shift-Reduce es solo la forma +# en que se llenan las tablas ACTION y GOTO +class LR1Parser(ShiftReduceParser): + def _build_parsing_table(self): + G = self.G.AugmentedGrammar(True) + + automaton = build_LR1_automaton(G) + for i, node in enumerate(automaton): + node.idx = i + + for node in automaton: + idx = node.idx + for item in node.state: + # - Fill `self.Action` and `self.Goto` according to `item`) + # - Feel free to use `self._register(...)`) + if item.IsReduceItem: + if item.production.Left == G.startSymbol: + # Sea 𝐼𝑖 el estado que contiene el item "𝑆′→𝑆.,$" (𝑆′ distinguido). + # Entonces 𝐴𝐶𝑇𝐼𝑂𝑁[𝐼𝑖,$]=‘𝑂𝐾‘ + self._register(self.action, (idx, G.EOF.Name), (self.OK, None)) + else: + # Sea "𝑋→𝛼.,𝑠" un item del estado 𝐼𝑖. + # Entonces 𝐴𝐶𝑇𝐼𝑂𝑁[𝐼𝑖,𝑠]=‘𝑅𝑘‘ (producción k es 𝑋→𝛼) + for c in item.lookaheads: + self._register(self.action, (idx, c.Name), (self.REDUCE, item.production)) + else: + next_symbol = item.NextSymbol + try: + next_state = node[next_symbol.Name][0] + if next_symbol.IsNonTerminal: + # Sea "𝑋→𝛼.𝑌𝜔,𝑠" item del estado 𝐼𝑖 y 𝐺𝑜𝑡𝑜(𝐼𝑖,𝑌)=𝐼𝑗. + # Entonces 𝐺𝑂𝑇𝑂[𝐼𝑖,𝑌]=𝑗 + self._register(self.goto, (idx, next_symbol.Name), next_state.idx) + else: + # Sea "𝑋→𝛼.𝑐𝜔,𝑠" un item del estado 𝐼𝑖 y 𝐺𝑜𝑡𝑜(𝐼𝑖,𝑐)=𝐼𝑗. + # Entonces 𝐴𝐶𝑇𝐼𝑂𝑁[𝐼𝑖,𝑐]=‘𝑆𝑗‘ + self._register(self.action, (idx, next_symbol.Name), (self.SHIFT, next_state.idx)) + except KeyError: + print(f'Node: {node} without transition with symbol {next_symbol}') + return + + diff --git a/src/Tools/Tools/Semantic.py b/src/Tools/Tools/Semantic.py new file mode 100644 index 000000000..44d94fd3a --- /dev/null +++ b/src/Tools/Tools/Semantic.py @@ -0,0 +1,333 @@ +# Aqui estan las clases necesarias para representar los tipos del programa + +# Necesario para enviar errores semanticos +class SemanticException(Exception): + @property + def text(self): + return self.args[0] + +# Representa un atributo en un tipo del programa +class Attribute: + def __init__(self, name : str, type, line : int, column : int, expression = None): + self.name = name + self.type = type + self.line = line + self.column = column + + def __str__(self): + return f'[attrib] {self.name}: {self.type.name};' + + def __repr__(self): + return str(self) + +# Representa un metodo en un tipo del programa +class Method: + def __init__(self, name : str, param_names : list, param_types : list, return_type): + self.name = name + self.param_names = param_names + self.param_types = param_types + self.return_type = return_type + self.return_info = None + + def __str__(self): + params = ', '.join(f'{n}: {t.name}' for n, t in zip(self.param_names, self.param_types)) + return f'[method] {self.name}({params}): {self.return_type.name};' + + def __eq__(self, other): + return other.name == self.name and \ + other.return_type == self.return_type and \ + other.param_types == self.param_types + +#Clase base para representar los tipos del programa +class Type: + def __init__(self, name:str, sealed=False): + self.name = name + self.attributes = [] + self.methods = {} + self.parent = None + # Profundidad en el arbol de herencia + self.depth = 0 + # True si la clase esta sellada (no se puede heredar de ella) + self.sealed = sealed + + def set_parent(self, parent): + # Si el padre esta definido o es un tipo sellado entonces hay un error semantico + if self.parent is not None: + raise SemanticException(f'Parent type is already set for {self.name}.') + if parent.sealed: + raise SemanticException(f'Parent type "{parent.name}" is sealed. Can\'t inherit from it.') + self.parent = parent + self.depth = parent.depth + 1 + + # Retorna el tipo en el arbol de herencia de mayor profundidad + # que es padre comun de ambos tipos + def type_union(self, other): + if self is AutoType: return other + elif other is AutoType: return self + + x, y = self, other + while x.depth > y.depth: + x = x.parent + while y.depth > x.depth: + y = y.parent + + while x and x != y: + x = x.parent + y = y.parent + + return x + + # Retorna el atributo del tipo actual con el nombre correspondiente + def get_attribute(self, name:str): + try: + return next(attr for attr in self.attributes if attr.name == name) + except StopIteration: + if self.parent is None: + raise SemanticException(f'Attribute "{name}" is not defined in {self.name}.') + try: + return self.parent.get_attribute(name) + except SemanticException: + raise SemanticException(f'Attribute "{name}" is not defined in {self.name}.') + + # Define un atributo para el tipo actual con el nombre y el tipo correspondiente + def define_attribute(self, name:str, typex, line, column): + try: + self.get_attribute(name) + except SemanticException: + attribute = Attribute(name, typex, line, column) + self.attributes.append(attribute) + return attribute + else: + raise SemanticException(f'Attribute "{name}" is already defined in {self.name}.') + + # Retorna el metodo del tipo actual con el nombre correspondiente + def get_method(self, name:str): + try: + return self.methods[name] + except KeyError: + if self.parent is None: + raise SemanticException(f'Method "{name}" is not defined in {self.name}.') + try: + return self.parent.get_method(name) + except SemanticException: + raise SemanticException(f'Method "{name}" is not defined in {self.name}.') + + # Define un metodo para el tipo actual con el nombre, los parametros y el tipo de + # retorno correspondientes + def define_method(self, name:str, param_names:list, param_types:list, return_type, line, column): + if name in self.methods: + raise SemanticException(f'Method "{name}" already defined in {self.name}') + + method = self.methods[name] = Method(name, param_names, param_types, return_type) + method.return_info = VariableInfo(f'{self.name}_{name}_returnType', return_type, + line, column, f'{self.name}_{name}_returnType') + return method + + # Returns true if other is a parent of current type + def inherits_from(self, other): + return other.is_special_type() \ + or self.name == other.name \ + or (self.parent is not None and self.parent.inherits_from(other)) + + def is_special_type(self): + return False + + def __str__(self): + output = f'type {self.name}' + parent = '' if self.parent is None else f' : {self.parent.name}' + output += parent + output += ' {' + output += '\n\t' if self.attributes or self.methods else '' + output += '\n\t'.join(str(x) for x in self.attributes) + output += '\n\t' if self.attributes else '' + output += '\n\t'.join(str(x) for x in self.methods.values()) + output += '\n' if self.methods else '' + output += '}\n' + return output + + def __repr__(self): + return str(self) + +# Special_Types +class SelfType(Type): + def __init__(self): + Type.__init__(self, 'SELF_TYPE') + self.sealed = True + + def inherits_from(self, other): + return False + + def is_special_type(self): + return True + + def __eq__(self, other): + return isinstance(other, SelfType) + +class AutoType(Type): + def __init__(self): + Type.__init__(self, 'AUTO_TYPE') + self.sealed = True + + def type_union(self, other): + return self + + def inherits_from(self, other): + return True + + def is_special_type(self): + return True + + def __eq__(self, other): + return isinstance(other, Type) + +class ErrorType(Type): + def __init__(self): + Type.__init__(self, '') + self.sealed = True + + def type_union(self, other): + return self + + def inherits_from(self, other): + return True + + def is_special_type(self): + return True + + def __eq__(self, other): + return isinstance(other, Type) + +# Clase para representar una variable dentro del programa +class VariableInfo: + def __init__(self, name, vtype, line, column, info): + self.name = name + self.type = vtype + self.info = info + self.line = line + self.column = column + self.infered = not isinstance(vtype, AutoType) + self.upper = [] + self.lower = [] + self.dependencies = [] + self.weakDependencies = [] + + # Incluye un tipo entre los tipos posibles de los que hereda la variable + def set_upper_type(self, typex): + if not self.infered and not isinstance(typex, AutoType): + self.upper.append(typex) + def set_lower_type(self, typex): + if not self.infered and not isinstance(typex, AutoType): + self.lower.append(typex) + + # Infiere el tipo de la variable, obtiene entre todos los tipos posibles + # uno de los que hereden todos los tipos definidos anteriormente, basicamente + # obtiene el lca de todos los tipos definidos anteriormente + def infer_type(self): + if not self.infered: + for d in self.dependencies: + if not isinstance(d, VariableInfo) or d.name == self.name: + continue + if isinstance(d.type, AutoType): + return False + self.lower.append(d.type) + + upper_type = None + for x in self.upper: + if not upper_type or x.inherits_from(upper_type): + upper_type = x + elif upper_type and not upper_type.inherits_from(x): + upper_type = ErrorType() + return False + + lower_type = None + for typex in self.lower: + lower_type = typex if not lower_type else lower_type.type_union(typex) + + if lower_type: + self.type = lower_type if not upper_type or lower_type.inherits_from(upper_type) else ErrorType() + else: + self.type = upper_type + + if not self.type or isinstance(self.type, ErrorType): + self.type = AutoType() + return False + + self.infered = not isinstance(self.type, AutoType) + self.upper, self.lower = [], [] + + return self.infered + return False + +# Clase para representar el contexto en el que se guardan las variables y los +# tipos durante el chequeo semantico del programa +class Context: + def __init__(self): + self.types = {} + + # Incluye un tipo dentro del contexto + def add_type(self, type : Type): + if type.name in self.types: + raise SemanticException(f'Type with the same name ({type.name}) already in context.') + self.types[type.name] = type + return type + + # Crea un tipo dentro del contexto + def create_type(self, name : str): + if name in self.types: + raise SemanticException(f'Type with the same name ({name}) already in context.') + type = self.types[name] = Type(name) + return type + + # Obtiene un tipo del contexto con el nombre correspondiente + def get_type(self, name : str): + try: + return self.types[name] + except KeyError: + raise SemanticException(f'Type ({name}) not defined.') + + def __str__(self): + return '{\n\t' + '\n\t'.join(y for x in self.types.values() for y in str(x).split('\n')) + '\n}' + + def __repr__(self): + return str(self) + + +# Clase para representar el scope durante el chequeo semantico +class Scope: + def __init__(self, parent = None): + self.parent = parent + self.locals = [] + self.childs = [] + + # Crea un scope hijo + def create_child(self): + self.childs.append(Scope(self)) + return self.childs[-1] + + # Define una variable en el scope + def define_variable(self, name : str, type : Type, line : int, column : int, info : str = ''): + self.locals.append(VariableInfo(name, type, line, column, info)) + return self.locals[-1] + + # Retorna una variable definida en el scope, None si no esta definida + def find_variable(self, name : str): + try: + return next(i for i in self.locals if i.name == name) + except StopIteration: + return self.parent.find_variable(name) if self.parent is not None else None + + # True si la variable esta definida en el scope + def is_defined(self, name : str): + return self.find_variable(name) is not None + + # True si la variable fue definida en el scope actual y no en alguno + # de sus scope padres + def is_local(self, name : str): + return any(i for i in self.locals if i.name == name) + + def __iter__(self): + for var in self.locals: + yield var + for ch in self.childs: + for var in ch: + yield var diff --git a/src/Tools/Tools/automata.py b/src/Tools/Tools/automata.py new file mode 100644 index 000000000..2bdadd2d1 --- /dev/null +++ b/src/Tools/Tools/automata.py @@ -0,0 +1,210 @@ +try: + import pydot +except: + pass + +# Esta es la clase dada en clases practicas +# para representar un DFA, +# o cualkier grafo en general +class State: + def __init__(self, state, final=False, formatter=lambda x: str(x), shape='circle'): + self.state = state + self.final = final + self.transitions = {} + self.epsilon_transitions = set() + self.tag = None + self.formatter = formatter + self.shape = shape + + # The method name is set this way from compatibility issues. + def set_formatter(self, value, attr='formatter', visited=None): + if visited is None: + visited = set() + elif self in visited: + return + + visited.add(self) + self.__setattr__(attr, value) + for destinations in self.transitions.values(): + for node in destinations: + node.set_formatter(value, attr, visited) + for node in self.epsilon_transitions: + node.set_formatter(value, attr, visited) + return self + + def has_transition(self, symbol): + return symbol in self.transitions + + def add_transition(self, symbol, state): + try: + self.transitions[symbol].append(state) + except: + self.transitions[symbol] = [state] + return self + + def add_epsilon_transition(self, state): + self.epsilon_transitions.add(state) + return self + + def recognize(self, string): + states = self.epsilon_closure + for symbol in string: + states = self.move_by_state(symbol, *states) + states = self.epsilon_closure_by_state(*states) + return any(s.final for s in states) + + def to_deterministic(self, formatter=lambda x: str(x)): + closure = self.epsilon_closure + start = State(tuple(closure), any(s.final for s in closure), formatter) + + closures = [ closure ] + states = [ start ] + pending = [ start ] + + while pending: + state = pending.pop() + symbols = { symbol for s in state.state for symbol in s.transitions } + + for symbol in symbols: + move = self.move_by_state(symbol, *state.state) + closure = self.epsilon_closure_by_state(*move) + + if closure not in closures: + new_state = State(tuple(closure), any(s.final for s in closure), formatter) + closures.append(closure) + states.append(new_state) + pending.append(new_state) + else: + index = closures.index(closure) + new_state = states[index] + + state.add_transition(symbol, new_state) + + return start + + @staticmethod + def from_nfa(nfa, get_states=False): + states = [] + for n in range(nfa.states): + state = State(n, n in nfa.finals) + states.append(state) + + for (origin, symbol), destinations in nfa.map.items(): + origin = states[origin] + origin[symbol] = [ states[d] for d in destinations ] + + if get_states: + return states[nfa.start], states + return states[nfa.start] + + @staticmethod + def move_by_state(symbol, *states): + return { s for state in states if state.has_transition(symbol) for s in state[symbol]} + + @staticmethod + def epsilon_closure_by_state(*states): + closure = { state for state in states } + + l = 0 + while l != len(closure): + l = len(closure) + tmp = [s for s in closure] + for s in tmp: + for epsilon_state in s.epsilon_transitions: + closure.add(epsilon_state) + return closure + + @property + def epsilon_closure(self): + return self.epsilon_closure_by_state(self) + + @property + def name(self): + return self.formatter(self.state) + + def get(self, symbol): + target = self.transitions[symbol] + assert len(target) == 1 + return target[0] + + def __getitem__(self, symbol): + if symbol == '': + return self.epsilon_transitions + try: + return self.transitions[symbol] + except KeyError: + return None + + def __setitem__(self, symbol, value): + if symbol == '': + self.epsilon_transitions = value + else: + self.transitions[symbol] = value + + def __repr__(self): + return str(self) + + def __str__(self): + return str(self.state) + + def __hash__(self): + return hash(self.state) + + def __iter__(self): + yield from self._visit() + + def _visit(self, visited=None): + if visited is None: + visited = set() + elif self in visited: + return + + visited.add(self) + yield self + + for destinations in self.transitions.values(): + for node in destinations: + yield from node._visit(visited) + for node in self.epsilon_transitions: + yield from node._visit(visited) + + def graph(self, dir = 'LR'): + G = pydot.Dot(rankdir=dir, margin=0.1) + G.add_node(pydot.Node('start', shape='plaintext', label='', width=0, height=0)) + + visited = set() + def visit(start): + ids = id(start) + if ids not in visited: + visited.add(ids) + G.add_node(pydot.Node(ids, label = f'\'{start.name}\'', shape=self.shape, style='bold' if start.final else '')) + for tran, destinations in start.transitions.items(): + for end in destinations: + visit(end) + G.add_edge(pydot.Edge(ids, id(end), label=tran, labeldistance=2)) + for end in start.epsilon_transitions: + visit(end) + G.add_edge(pydot.Edge(ids, id(end), label='ε', labeldistance=2)) + + visit(self) + G.add_edge(pydot.Edge('start', id(self), label='', style='dashed')) + + return G + + def _repr_svg_(self): + try: + return self.graph().create_svg().decode('utf8') + except: + pass + + def write_to(self, fname): + return self.graph().write_svg(fname) + +def multiline_formatter(state): + return '\n'.join(str(item) for item in state) + +def lr0_formatter(state): + try: + return '\n'.join(str(item)[:-4] for item in state) + except TypeError: + return str(state)[:-4] \ No newline at end of file diff --git a/src/Tools/Tools/evaluation.py b/src/Tools/Tools/evaluation.py new file mode 100644 index 000000000..941113216 --- /dev/null +++ b/src/Tools/Tools/evaluation.py @@ -0,0 +1,44 @@ +from .parsing import ShiftReduceParser + +''' +Este metodo retorna el ast dado el conjunto de producciones, +las operaciones y los tokens +''' +def evaluate_reverse_parse(right_parse, operations, tokens): + if not right_parse or not operations or not tokens: + return + + right_parse = iter(right_parse) + tokens = iter(tokens) + stack = [] + for operation in operations: + # Si hay que hacer un shift, pasemos a analizar + # el proximo token e incluyamoslo en la pila + if operation == ShiftReduceParser.SHIFT: + token = next(tokens) + stack.append(token) + # Si hay que hacer un reduce, tomamos los elementos + # necesarios de la pila y los sustituimos por el nonterminal + # correspondiente, el cual incluimos en la pila + elif operation == ShiftReduceParser.REDUCE: + production = next(right_parse) + head, body = production + attributes = production.attributes + assert all(rule is None for rule in attributes[1:]), 'There must be only syntheticed attributes.' + rule = attributes[0] + + if len(body): + syntheticed = [None] + stack[-len(body):] + value = rule(None, syntheticed) + stack[-len(body):] = [value] + else: + stack.append(rule(None, None)) + else: + raise Exception('Invalid action!!!') + + # La pila debe terminar con el program node + # correspondiente a la raiz del ast, todos los + # tokens deben haber sido analizados + assert len(stack) == 1 + assert next(tokens).token_type == '$'#######estaba EOF, creo que tiene que ver con lo que cambie al poner el tokentype en el lexer + return stack[0] \ No newline at end of file diff --git a/src/Tools/Tools/parsing.py b/src/Tools/Tools/parsing.py new file mode 100644 index 000000000..17eae0e2d --- /dev/null +++ b/src/Tools/Tools/parsing.py @@ -0,0 +1,95 @@ +from .Errors import * + +# Clase base para los parsers Shift-Reduce +class ShiftReduceParser: + SHIFT = 'SHIFT' + REDUCE = 'REDUCE' + OK = 'OK' + + def __init__(self, G, verbose=False): + self.G = G + self.verbose = verbose + self.action = {} + self.goto = {} + self.HasConflict = False + self.errors = [] + self._build_parsing_table() + + def _build_parsing_table(self): + raise NotImplementedError() + + ''' + Retorna las producciones y operaciones necesarias para el + proceso de parsing del string w. + ''' + def __call__(self, w): + stack = [0] + cursor = 0 + output = [] + operations = [] + self.errors = [] + + while True: + # Estado del automata en el que me encuentro + state = stack[-1] + # Siguiente simbolo a analizar + lookahead = w[cursor].token_type + if self.verbose: print(stack, '<---||--->', w[cursor:]) + #(Detect error) + try: + action, tag = self.action[state, lookahead][0] + except KeyError: + # Si no existe transicion desde el estado actual con el simbolo + # correspondiente es porque ha ocurrido un error + lookahead = w[cursor] + line = lookahead.line + column = lookahead.column + if lookahead.lex == '$': + self.errors.append(SyntacticError(0, 0, 'EOF')) + else: + self.errors.append(SyntacticError(line, column, f'"{lookahead.lex}"')) + return None, None + + # Si la accion es Shift, incluyo en la pila el simbolo actual y + # el estado al que necesito moverme + if action == self.SHIFT: + stack.append(lookahead) + stack.append(tag) + operations.append(self.SHIFT) + cursor += 1 + # Si la accion es Reduce, saco de la pila los simbolos correspondientes + # a la produccion q debo reducir + elif action == self.REDUCE: + output.append(tag) + for i in range(len(tag.Right)): + stack.pop() + assert stack[-1] == tag.Right[-i - 1].Name, 'Symbol does not match' + stack.pop() + # Tomo el estado al que debo moverme + index = self.goto[stack[-1], tag.Left.Name][0] + # Incluyo en la pila el simbolo reducido y el estado al + # que necesito moverme + stack.append(tag.Left.Name) + stack.append(index) + operations.append(self.REDUCE) + #(OK case) + elif action == self.OK: + return output, operations + #(Invalid case) + else: + lookahead = w[cursor] + line = lookahead.line + column = lookahead.column + if lookahead.lex == '$': + self.errors.append(SyntacticError(0, 0, 'EOF')) + else: + self.errors.append(SyntacticError(line, column, f'"{lookahead.lex}"')) + return None, None + + def _register(self, table, key, value): + if key not in table: + table[key] = [value] + elif not any(i for i in table[key] if i == value): + self.HasConflict = True + table[key].append(value) + diff --git a/src/Tools/Tools/pycompiler.py b/src/Tools/Tools/pycompiler.py new file mode 100644 index 000000000..74ad3bc0d --- /dev/null +++ b/src/Tools/Tools/pycompiler.py @@ -0,0 +1,560 @@ +import json + +#Clase base para los simbolos de la gramatica, terminales y noterminales +class Symbol(object): + + def __init__(self, name, grammar): + self.Name = name + self.Grammar = grammar + + def __str__(self): + return self.Name + + def __repr__(self): + return self.Name + + def __add__(self, other): + if isinstance(other, Symbol): + return Sentence(self, other) + + if isinstance(other, Sentence): + return Sentence(*((self,) + other._symbols)) + + raise TypeError(other) + + def __or__(self, other): + + if isinstance(other, (Sentence)): + return SentenceList(Sentence(self), other) + + raise TypeError(other) + + @property + def IsEpsilon(self): + return False + + def __len__(self): + return 1 + +class NonTerminal(Symbol): + + + def __init__(self, name, grammar): + super().__init__(name, grammar) + self.productions = [] + + + def __imod__(self, other): + + if isinstance(other, (Sentence)): + p = Production(self, other) + self.Grammar.Add_Production(p) + return self + + if isinstance(other, tuple): + assert len(other) > 1 + + if len(other) == 2: + other += (None,) * len(other[0]) + + assert len(other) == len(other[0]) + 2, "Debe definirse una, y solo una, regla por cada símbolo de la producción" + # assert len(other) == 2, "Tiene que ser una Tupla de 2 elementos (sentence, attribute)" + + if isinstance(other[0], Symbol) or isinstance(other[0], Sentence): + p = AttributeProduction(self, other[0], other[1:]) + else: + raise Exception("") + + self.Grammar.Add_Production(p) + return self + + if isinstance(other, Symbol): + p = Production(self, Sentence(other)) + self.Grammar.Add_Production(p) + return self + + if isinstance(other, SentenceList): + + for s in other: + p = Production(self, s) + self.Grammar.Add_Production(p) + + return self + + raise TypeError(other) + + def __add__(self, other): + if isinstance(other, Symbol): + return Sentence(self, other) + + if isinstance(other, Sentence): + return Sentence(self, *(other._symbols)) + + if other: + raise TypeError(other) + + def __iter__(self): + yield self + + @property + def IsTerminal(self): + return False + + @property + def IsNonTerminal(self): + return True + + @property + def IsEpsilon(self): + return False + +class Terminal(Symbol): + + def __init__(self, name, grammar): + super().__init__(name, grammar) + + def __iter__(self): + yield self + + def __add__(self, other): + if isinstance(other, Symbol): + return Sentence(self, other) + + if isinstance(other, Sentence): + return Sentence(self, *(other._symbols)) + + if other: + raise TypeError(other) + + @property + def IsTerminal(self): + return True + + @property + def IsNonTerminal(self): + return False + + @property + def IsEpsilon(self): + return False + +class EOF(Terminal): + + def __init__(self, Grammar): + super().__init__('$', Grammar) + +class Sentence(object): + + def __init__(self, *args): + self._symbols = tuple(x for x in args if not x.IsEpsilon) + self.hash = hash(self._symbols) + + def __len__(self): + return len(self._symbols) + + def __add__(self, other): + if isinstance(other, Symbol): + return Sentence(*(self._symbols + (other,))) + + if isinstance(other, Sentence): + return Sentence(*(self._symbols + other._symbols)) + + if other: + raise TypeError(other) + + def __or__(self, other): + if isinstance(other, Sentence): + return SentenceList(self, other) + + if isinstance(other, Symbol): + return SentenceList(self, Sentence(other)) + + if isinstance(other, SentenceList): + return SentenceList(self, *(other._sentences)) + + if other: + raise TypeError(other) + + def __repr__(self): + return str(self) + + def __str__(self): + return ("%s " * len(self._symbols) % tuple(self._symbols)).strip() + + def __iter__(self): + return iter(self._symbols) + + def __getitem__(self, index): + return self._symbols[index] + + def __eq__(self, other): + return self._symbols == other._symbols + + def __hash__(self): + return self.hash + + @property + def IsEpsilon(self): + return False + +class SentenceList(object): + + def __init__(self, *args): + self._sentences = list(args) + + def Add(self, symbol): + if not symbol and (symbol is None or not symbol.IsEpsilon): + raise ValueError(symbol) + + self._sentences.append(symbol) + + def __iter__(self): + return iter(self._sentences) + + def __or__(self, other): + if isinstance(other, Sentence): + self.Add(other) + return self + + if isinstance(other, Symbol): + return self | Sentence(other) + + if isinstance(other, SentenceList): + return SentenceList(self._sentences + other._sentences) + +class Epsilon(Terminal, Sentence): + + def __init__(self, grammar): + super().__init__('epsilon', grammar) + + + def __str__(self): + return "ε" + + def __repr__(self): + return 'epsilon' + + def __iter__(self): + yield from () + + def __len__(self): + return 0 + + def __add__(self, other): + return other + + def __eq__(self, other): + return isinstance(other, (Epsilon,)) + + def __hash__(self): + return hash("") + + @property + def IsEpsilon(self): + return True + +class Production(object): + + def __init__(self, nonTerminal, sentence): + + self.Left = nonTerminal + self.Right = sentence + + def __str__(self): + + return '%s --> %s' % (self.Left, self.Right) + + def __repr__(self): + return '%s -> %s' % (self.Left, self.Right) + + def __iter__(self): + yield self.Left + yield self.Right + + def __eq__(self, other): + return isinstance(other, Production) and self.Left == other.Left and self.Right == other.Right + + def __hash__(self): + return hash((self.Left, self.Right)) + + @property + def IsEpsilon(self): + return self.Right.IsEpsilon + +class AttributeProduction(Production): + + def __init__(self, nonTerminal, sentence, attributes): + if not isinstance(sentence, Sentence) and isinstance(sentence, Symbol): + sentence = Sentence(sentence) + super(AttributeProduction, self).__init__(nonTerminal, sentence) + + self.attributes = attributes + + def __str__(self): + return '%s := %s' % (self.Left, self.Right) + + def __repr__(self): + return '%s -> %s' % (self.Left, self.Right) + + def __iter__(self): + yield self.Left + yield self.Right + + + @property + def IsEpsilon(self): + return self.Right.IsEpsilon + + # sintetizar en ingles??????, pending aggrement + def syntetice(self): + pass + +class Grammar(): + + def __init__(self): + + self.Productions = [] + self.nonTerminals = [] + self.terminals = [] + self.startSymbol = None + # production type + self.pType = None + self.Epsilon = Epsilon(self) + self.EOF = EOF(self) + + self.symbDict = { '$': self.EOF } + + def NonTerminal(self, name, startSymbol = False): + + name = name.strip() + if not name: + raise Exception("Empty name") + + term = NonTerminal(name,self) + + if startSymbol: + + if self.startSymbol is None: + self.startSymbol = term + else: + raise Exception("Cannot define more than one start symbol.") + + self.nonTerminals.append(term) + self.symbDict[name] = term + return term + + def NonTerminals(self, names): + + ans = tuple((self.NonTerminal(x) for x in names.strip().split())) + + return ans + + + def Add_Production(self, production): + + if len(self.Productions) == 0: + self.pType = type(production) + + assert type(production) == self.pType, "The Productions most be of only 1 type." + + production.Left.productions.append(production) + self.Productions.append(production) + + + def Terminal(self, name): + + name = name.strip() + if not name: + raise Exception("Empty name") + + term = Terminal(name, self) + self.terminals.append(term) + self.symbDict[name] = term + return term + + def Terminals(self, names): + + ans = tuple((self.Terminal(x) for x in names.strip().split())) + + return ans + + + def __str__(self): + + mul = '%s, ' + + ans = 'Non-Terminals:\n\t' + + nonterminals = mul * (len(self.nonTerminals)-1) + '%s\n' + + ans += nonterminals % tuple(self.nonTerminals) + + ans += 'Terminals:\n\t' + + terminals = mul * (len(self.terminals)-1) + '%s\n' + + ans += terminals % tuple(self.terminals) + + ans += 'Productions:\n\t' + + ans += str(self.Productions) + + return ans + + def __getitem__(self, name): + try: + return self.symbDict[name] + except KeyError: + return None + + @property + def to_json(self): + + productions = [] + + for p in self.Productions: + head = p.Left.Name + + body = [] + + for s in p.Right: + body.append(s.Name) + + productions.append({'Head':head, 'Body':body}) + + d={'NonTerminals':[symb.Name for symb in self.nonTerminals], 'Terminals': [symb.Name for symb in self.terminals],\ + 'Productions':productions} + + # [{'Head':p.Left.Name, "Body": [s.Name for s in p.Right]} for p in self.Productions] + return json.dumps(d) + + @staticmethod + def from_json(data): + data = json.loads(data) + + G = Grammar() + dic = {'epsilon':G.Epsilon} + + for term in data['Terminals']: + dic[term] = G.Terminal(term) + + for noTerm in data['NonTerminals']: + dic[noTerm] = G.NonTerminal(noTerm) + + for p in data['Productions']: + head = p['Head'] + dic[head] %= Sentence(*[dic[term] for term in p['Body']]) + + return G + + def copy(self): + G = Grammar() + G.pType = self.pType + for terminal in self.terminals: + G.Terminal(terminal.Name) + for nonterminal in self.nonTerminals: + G.NonTerminal(nonterminal.Name, nonterminal == self.startSymbol) + for prod in self.Productions: + left = G.symbDict[prod.Left.Name] + if prod.IsEpsilon: + if self.pType is AttributeProduction: + G.Add_Production(AttributeProduction(left, G.Epsilon, prod.attributes)) + else: + G.Add_Production(Production(left, G.Epsilon)) + else: + right = Sentence() + for symbol in prod.Right: + right = right + G.symbDict[symbol.Name] + if self.pType is AttributeProduction: + G.Add_Production(AttributeProduction(left, right, prod.attributes)) + else: + G.Add_Production(Production(left, right)) + return G + + @property + def IsAugmentedGrammar(self): + augmented = 0 + for left, right in self.Productions: + if self.startSymbol == left: + augmented += 1 + if augmented <= 1: + return True + else: + return False + + def AugmentedGrammar(self, force=False): + if not self.IsAugmentedGrammar or force: + + G = self.copy() + # S, self.startSymbol, SS = self.startSymbol, None, self.NonTerminal('S\'', True) + S = G.startSymbol + G.startSymbol = None + SS = G.NonTerminal('S\'', True) + if G.pType is AttributeProduction: + SS %= S + G.Epsilon, lambda x : x + else: + SS %= S + G.Epsilon + + return G + else: + return self.copy() + #endchange + +class Item: + + def __init__(self, production, pos, lookaheads=[]): + self.production = production + self.pos = pos + self.lookaheads = frozenset(look for look in lookaheads) + + def __str__(self): + s = str(self.production.Left) + " -> " + if len(self.production.Right) > 0: + for i,c in enumerate(self.production.Right): + if i == self.pos: + s += "." + s += str(self.production.Right[i]) + if self.pos == len(self.production.Right): + s += "." + else: + s += "." + s += ", " + str(self.lookaheads)[10:-1] + return s + + def __repr__(self): + return str(self) + + + def __eq__(self, other): + return ( + (self.pos == other.pos) and + (self.production == other.production) and + (set(self.lookaheads) == set(other.lookaheads)) + ) + + def __hash__(self): + return hash((self.production,self.pos,self.lookaheads)) + + @property + def IsReduceItem(self): + return len(self.production.Right) == self.pos + + @property + def NextSymbol(self): + if self.pos < len(self.production.Right): + return self.production.Right[self.pos] + else: + return None + + def NextItem(self): + if self.pos < len(self.production.Right): + return Item(self.production,self.pos+1,self.lookaheads) + else: + return None + + def Preview(self, skip=1): + unseen = self.production.Right[self.pos+skip:] + return [ unseen + (lookahead,) for lookahead in self.lookaheads ] + + def Center(self): + return Item(self.production, self.pos) \ No newline at end of file diff --git a/src/Tools/Tools/utils.py b/src/Tools/Tools/utils.py new file mode 100644 index 000000000..1edd4972e --- /dev/null +++ b/src/Tools/Tools/utils.py @@ -0,0 +1,221 @@ +from .pycompiler import Production, Sentence, Symbol, EOF, Epsilon + +class ContainerSet: + def __init__(self, *values, contains_epsilon=False): + self.set = set(values) + self.contains_epsilon = contains_epsilon + + def add(self, value): + n = len(self.set) + self.set.add(value) + return n != len(self.set) + + def extend(self, values): + change = False + for value in values: + change |= self.add(value) + return change + + def set_epsilon(self, value=True): + last = self.contains_epsilon + self.contains_epsilon = value + return last != self.contains_epsilon + + def update(self, other): + n = len(self.set) + self.set.update(other.set) + return n != len(self.set) + + def epsilon_update(self, other): + return self.set_epsilon(self.contains_epsilon | other.contains_epsilon) + + def hard_update(self, other): + return self.update(other) | self.epsilon_update(other) + + def find_match(self, match): + for item in self.set: + if item == match: + return item + return None + + def __len__(self): + return len(self.set) + int(self.contains_epsilon) + + def __str__(self): + return '%s-%s' % (str(self.set), self.contains_epsilon) + + def __repr__(self): + return str(self) + + def __iter__(self): + return iter(self.set) + + def __nonzero__(self): + return len(self) > 0 + + def __eq__(self, other): + if isinstance(other, set): + return self.set == other + return isinstance(other, ContainerSet) and self.set == other.set and self.contains_epsilon == other.contains_epsilon + + +def inspect(item, grammar_name='G', mapper=None): + try: + return mapper[item] + except (TypeError, KeyError ): + if isinstance(item, dict): + items = ',\n '.join(f'{inspect(key, grammar_name, mapper)}: {inspect(value, grammar_name, mapper)}' for key, value in item.items() ) + return f'{{\n {items} \n}}' + elif isinstance(item, ContainerSet): + args = f'{ ", ".join(inspect(x, grammar_name, mapper) for x in item.set) } ,' if item.set else '' + return f'ContainerSet({args} contains_epsilon={item.contains_epsilon})' + elif isinstance(item, EOF): + return f'{grammar_name}.EOF' + elif isinstance(item, Epsilon): + return f'{grammar_name}.Epsilon' + elif isinstance(item, Symbol): + return f"G['{item.Name}']" + elif isinstance(item, Sentence): + items = ', '.join(inspect(s, grammar_name, mapper) for s in item._symbols) + return f'Sentence({items})' + elif isinstance(item, Production): + left = inspect(item.Left, grammar_name, mapper) + right = inspect(item.Right, grammar_name, mapper) + return f'Production({left}, {right})' + elif isinstance(item, tuple) or isinstance(item, list): + ctor = ('(', ')') if isinstance(item, tuple) else ('[',']') + return f'{ctor[0]} {("%s, " * len(item)) % tuple(inspect(x, grammar_name, mapper) for x in item)}{ctor[1]}' + else: + raise ValueError(f'Invalid: {item}') + +def pprint(item, header=""): + if header: + print(header) + + if isinstance(item, dict): + for key, value in item.items(): + print(f'{key} ---> {value}') + elif isinstance(item, list): + print('[') + for x in item: + print(f' {repr(x)}') + print(']') + else: + print(item) + +class Token: + """ + Basic token class. + + Parameters + ---------- + lex : str + Token's lexeme. + token_type : Enum + Token's type. + """ + + def __init__(self, lex, token_type, line = None, column = None): + self.lex = lex + self.token_type = token_type + self.line = line + self.column = column + + def __str__(self): + return str(self.token_type) + + def __repr__(self): + return repr(self.token_type) + + @property + def is_valid(self): + return True + +class UnknownToken(Token): + def __init__(self, lex): + Token.__init__(self, lex, None) + + def transform_to(self, token_type): + return Token(self.lex, token_type) + + @property + def is_valid(self): + return False + +def tokenizer(G, fixed_tokens): + def decorate(func): + def tokenize_text(text): + tokens = [] + for lex in text.split(): + try: + token = fixed_tokens[lex] + except KeyError: + token = UnknownToken(lex) + try: + token = func(token) + except TypeError: + pass + tokens.append(token) + tokens.append(Token('$', G.EOF)) + return tokens + + if hasattr(func, '__call__'): + return tokenize_text + elif isinstance(func, str): + return tokenize_text(func) + else: + raise TypeError('Argument must be "str" or a callable object.') + return decorate + +class DisjointSet: + def __init__(self, *items): + self.nodes = { x: DisjointNode(x) for x in items } + + def merge(self, items): + items = (self.nodes[x] for x in items) + try: + head, *others = items + for other in others: + head.merge(other) + except ValueError: + pass + + @property + def representatives(self): + return { n.representative for n in self.nodes.values() } + + @property + def groups(self): + return [[n for n in self.nodes.values() if n.representative == r] for r in self.representatives] + + def __len__(self): + return len(self.representatives) + + def __getitem__(self, item): + return self.nodes[item] + + def __str__(self): + return str(self.groups) + + def __repr__(self): + return str(self) + +class DisjointNode: + def __init__(self, value): + self.value = value + self.parent = self + + @property + def representative(self): + if self.parent != self: + self.parent = self.parent.representative + return self.parent + + def merge(self, other): + other.representative.parent = self.representative + + def __str__(self): + return str(self.value) + + def __repr__(self): + return str(self) \ No newline at end of file diff --git a/src/Tools/Tools/visitor.py b/src/Tools/Tools/visitor.py new file mode 100644 index 000000000..964842836 --- /dev/null +++ b/src/Tools/Tools/visitor.py @@ -0,0 +1,80 @@ +# The MIT License (MIT) +# +# Copyright (c) 2013 Curtis Schlak +# +# Permission is hereby granted, free of charge, to any person obtaining a copy +# of this software and associated documentation files (the "Software"), to deal +# in the Software without restriction, including without limitation the rights +# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +# copies of the Software, and to permit persons to whom the Software is +# furnished to do so, subject to the following conditions: +# +# The above copyright notice and this permission notice shall be included in +# all copies or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +# THE SOFTWARE. + +import inspect + +__all__ = ['on', 'when'] + +def on(param_name): + def f(fn): + dispatcher = Dispatcher(param_name, fn) + return dispatcher + return f + + +def when(param_type): + def f(fn): + frame = inspect.currentframe().f_back + func_name = fn.func_name if 'func_name' in dir(fn) else fn.__name__ + dispatcher = frame.f_locals[func_name] + if not isinstance(dispatcher, Dispatcher): + dispatcher = dispatcher.dispatcher + dispatcher.add_target(param_type, fn) + def ff(*args, **kw): + return dispatcher(*args, **kw) + ff.dispatcher = dispatcher + return ff + return f + + +class Dispatcher(object): + def __init__(self, param_name, fn): + frame = inspect.currentframe().f_back.f_back + top_level = frame.f_locals == frame.f_globals + self.param_index = self.__argspec(fn).args.index(param_name) + self.param_name = param_name + self.targets = {} + + def __call__(self, *args, **kw): + typ = args[self.param_index].__class__ + d = self.targets.get(typ) + if d is not None: + return d(*args, **kw) + else: + issub = issubclass + t = self.targets + ks = t.keys() + ans = [t[k](*args, **kw) for k in ks if issub(typ, k)] + if len(ans) == 1: + return ans.pop() + return ans + + def add_target(self, typ, target): + self.targets[typ] = target + + @staticmethod + def __argspec(fn): + # Support for Python 3 type hints requires inspect.getfullargspec + if hasattr(inspect, 'getfullargspec'): + return inspect.getfullargspec(fn) + else: + return inspect.getargspec(fn) diff --git a/src/Tools/format_visitor.py b/src/Tools/format_visitor.py new file mode 100644 index 000000000..d2a1f7d89 --- /dev/null +++ b/src/Tools/format_visitor.py @@ -0,0 +1,116 @@ +from Tools import visitor +from Parser import ProgramNode, ClassDeclarationNode, AttrDeclarationNode, FuncDeclarationNode +from Parser import IfThenElseNode, WhileLoopNode, BlockNode, LetInNode, CaseOfNode +from Parser import AssignNode, UnaryNode, BinaryNode +from Parser import FunctionCallNode, MemberCallNode, NewNode, AtomicNode + +# Visitor para imprimir el AST + +class FormatVisitor: + @visitor.on('node') + def visit(self, node, tabs): + pass + + @visitor.when(ProgramNode) + def visit(self, node, tabs=0): + ans = '\t' * tabs + f'\\__ProgramNode [ ... ]' + statements = '\n'.join(self.visit(child, tabs + 1) for child in node.declarations) + return f'{ans}\n{statements}' + + @visitor.when(ClassDeclarationNode) + def visit(self, node, tabs=0): + parent = '' if node.parent is None else f"inherits {node.parent.lex}" + ans = '\t' * tabs + f'\\__ClassDeclarationNode: class {node.id.lex} {parent} {{ ... }}' + features = '\n'.join(self.visit(child, tabs + 1) for child in node.features) + return f'{ans}\n{features}' + + @visitor.when(AttrDeclarationNode) + def visit(self, node, tabs=0): + ans = '\t' * tabs + f'\\__AttrDeclarationNode: {node.id.lex}: {node.type.lex}' + (' <- ' if node.expression else '') + ';' + expr = self.visit(node.expression, tabs + 1) if node.expression else None + return f'{ans}' + (f'\n{expr}' if expr else '') + + @visitor.when(FuncDeclarationNode) + def visit(self, node, tabs=0): + params = ', '.join(': '.join(tok.lex for tok in param) for param in node.params) + ans = '\t' * tabs + f'\\__FuncDeclarationNode: {node.id.lex}({params}): {node.type.lex} {{ }}' + body = self.visit(node.body, tabs + 1) + return f'{ans}\n{body}' + + @visitor.when(IfThenElseNode) + def visit(self, node, tabs=0): + ans = '\t' * tabs + f'\\_IfThenElseNode: if then else fi' + cond = self.visit(node.condition, tabs + 1) + if_body = self.visit(node.if_body, tabs + 1) + else_body = self.visit(node.else_body, tabs + 1) + return f'{ans}\n{cond}\n{if_body}\n{else_body}' + + @visitor.when(WhileLoopNode) + def visit(self, node, tabs=0): + ans = '\t' * tabs + f'\\_WhileNode: while loop pool' + cond = self.visit(node.condition, tabs + 1) + body = self.visit(node.body, tabs + 1) + return f'{ans}\n{cond}\n{body}' + + @visitor.when(BlockNode) + def visit(self, node, tabs=0): + ans = '\t' * tabs + f'\\_BlockNode: {{ ; ... ; }}' + expressions = '\n'.join(self.visit(expr, tabs + 1) for expr in node.expressions) + return f'{ans}\n{expressions}' + + @visitor.when(LetInNode) + def visit(self, node, tabs=0): + let_body = ', '.join(f'{idx.lex}: {typex.lex}' + (' <- ' if expr else '') for idx, typex, expr in node.let_body) + ans = '\t' * tabs + f'\\_LetInNode: let {let_body} in ' + lets = '\n'.join(self.visit(expr, tabs + 1) for _, _, expr in node.let_body if expr) + body = self.visit(node.in_body, tabs + 1) + return f'{ans}\n{lets}\n{body}' + + @visitor.when(CaseOfNode) + def visit(self, node, tabs=0): + case_body = ' '.join(f'{idx.lex}: {typex.lex} => ;' for idx, typex, expr in node.branches) + ans = '\t' * tabs + f'\\_CaseOfNode: case of {case_body} esac' + expression = self.visit(node.expression, tabs + 1) + body = '\n'.join(self.visit(expr, tabs + 1) for _, _, expr in node.branches) + return f'{ans}\n{expression}\n{body}' + + @visitor.when(AssignNode) + def visit(self, node, tabs=0): + ans = '\t' * tabs + f'\\_AssingNode: {node.id.lex} <- ' + expr = self.visit(node.expression, tabs + 1) + return f'{ans}\n{expr}' + + @visitor.when(UnaryNode) + def visit(self, node, tabs=0): + ans = '\t' * tabs + f'\\__{node.__class__.__name__} ' + expression = self.visit(node.expression, tabs + 1) + return f'{ans}\n{expression}' + + @visitor.when(BinaryNode) + def visit(self, node, tabs=0): + ans = '\t' * tabs + f'\\__ {node.__class__.__name__} ' + left = self.visit(node.left, tabs + 1) + right = self.visit(node.right, tabs + 1) + return f'{ans}\n{left}\n{right}' + + @visitor.when(FunctionCallNode) + def visit(self, node, tabs=0): + obj = self.visit(node.obj, tabs + 1) + typex = f'@{node.type.lex}' if node.type else '' + ans = '\t' * tabs + f'\\__FunctionCallNode: {typex}.{node.id.lex}(, ..., )' + args = '\n'.join(self.visit(arg, tabs + 1) for arg in node.args) + return f'{ans}\n{obj}\n{args}' + + @visitor.when(MemberCallNode) + def visit(self, node, tabs=0): + ans = '\t' * tabs + f'\\__MemberCallNode: {node.id.lex}(, ..., )' + args = '\n'.join(self.visit(arg, tabs + 1) for arg in node.args) + return f'{ans}\n{args}' + + @visitor.when(NewNode) + def visit(self, node, tabs=0): + return '\t' * tabs + f'\\__ NewNode: new {node.type.lex}' + + @visitor.when(AtomicNode) + def visit(self, node, tabs=0): + return '\t' * tabs + f'\\__ {node.__class__.__name__}: {node.token.lex}' \ No newline at end of file diff --git a/src/coolc.sh b/src/coolc.sh index 3088de4f9..fa9bb864a 100755 --- a/src/coolc.sh +++ b/src/coolc.sh @@ -1,4 +1,4 @@ -# Incluya aquí las instrucciones necesarias para ejecutar su compilador +#!/bin/bash INPUT_FILE=$1 OUTPUT_FILE=${INPUT_FILE:0: -2}mips @@ -8,4 +8,6 @@ echo "LINEA_CON_NOMBRE_Y_VERSION_DEL_COMPILADOR" # TODO: Recuerde cambiar echo "Copyright (c) 2019: Nombre1, Nombre2, Nombre3" # TODO: líneas a los valores correctos # Llamar al compilador -echo "Compiling $INPUT_FILE into $OUTPUT_FILE" + +#echo "Compiling $INPUT_FILE into $OUTPUT_FILE" +python3 Main.py -f $INPUT_FILE \ No newline at end of file diff --git a/tests/lexer/iis1.cl b/tests/lexer/iis1.cl index 12cb52beb..d5a4a096d 100644 --- a/tests/lexer/iis1.cl +++ b/tests/lexer/iis1.cl @@ -1,6 +1,6 @@ (* Integers, Identifiers, and Special Notation *) -0007 123 +1 -1 +90 -09 +11113 -4r *a *self* c++ +0007 123 +1 -1 +90 -09 +11113 -4r *a *self* c++ 5! = 120, 2 + 2 = 5 or E = mc2; p + 1 @ p = 1: for x in range(len(b)) new / <- <<==> {( Int: Objet, Bool; String.string SELF_TYPE isvoid }) class Class if then else fi testing Testing ~007agent_bond james_007B0N3SS___ diff --git a/tests/lexer/iis1_error.txt b/tests/lexer/iis1_error.txt index 9e6d66cac..a1d71cc56 100644 --- a/tests/lexer/iis1_error.txt +++ b/tests/lexer/iis1_error.txt @@ -1 +1 @@ -(4, 2) - LexicographicError: ERROR "!" +(4, 2) - LexicographicError: ERROR "!" \ No newline at end of file diff --git a/tests/lexer_test.py b/tests/lexer_test.py index 2a27223d3..f492b1959 100644 --- a/tests/lexer_test.py +++ b/tests/lexer_test.py @@ -10,4 +10,4 @@ @pytest.mark.run(order=1) @pytest.mark.parametrize("cool_file", tests) def test_lexer_errors(compiler_path, cool_file): - compare_errors(compiler_path, tests_dir + cool_file, tests_dir + cool_file[:-3] + '_error.txt') \ No newline at end of file + compare_errors(compiler_path, tests_dir + cool_file, tests_dir + cool_file[:-3] + '_error.txt') From 555acb0323b15f435d316fd75e33f81b784a7884 Mon Sep 17 00:00:00 2001 From: ClaudiaOM Date: Thu, 25 Feb 2021 15:32:59 -0500 Subject: [PATCH 02/75] Semantic Test Passed --- src/Main.py | 27 +- src/Tools/Parser.py | 103 ++++---- src/Tools/Tools/Errors.py | 34 ++- src/Tools/Tools/Semantic.py | 103 +++----- src/Tools/Type_Builder.py | 157 +++++++++++ src/Tools/Type_Checker.py | 509 ++++++++++++++++++++++++++++++++++++ src/Tools/Type_Collector.py | 38 +++ 7 files changed, 847 insertions(+), 124 deletions(-) create mode 100644 src/Tools/Type_Builder.py create mode 100644 src/Tools/Type_Checker.py create mode 100644 src/Tools/Type_Collector.py diff --git a/src/Main.py b/src/Main.py index a9b779ac0..70bcbd42e 100644 --- a/src/Main.py +++ b/src/Main.py @@ -1,5 +1,9 @@ from Tools.Lexer import Lexer from Tools.Parser import CoolParser +from Tools.Tools.evaluation import evaluate_reverse_parse +from Tools.Type_Collector import Type_Collector +from Tools.Type_Builder import Type_Builder +from Tools.Type_Checker import Type_Checker def main(args): try: @@ -11,23 +15,34 @@ def main(args): lexer = Lexer() tokens = lexer.tokenize(code) - for t in lexer.errors: print(t) - if any(lexer.errors): exit(1) - #print(CoolParser.HasConflict) - productions, operations = CoolParser(tokens) - for e in CoolParser.errors: print(e) - if any(CoolParser.errors): exit(1) + ast = evaluate_reverse_parse(productions, operations, tokens) + type_Collector = Type_Collector() + type_Collector.visit(ast) + for e in type_Collector.errors: + print(e) + if any(type_Collector.errors): exit(1) + context = type_Collector.Context + type_Builder = Type_Builder(context) + type_Builder.visit(ast) + for e in type_Builder.errors: + print(e) + if any(type_Builder.errors): exit(1) + type_Checker = Type_Checker(context) + type_Checker.visit(ast) + for e in type_Checker.errors: + print(e) + if any(type_Checker.errors): exit(1) diff --git a/src/Tools/Parser.py b/src/Tools/Parser.py index 038c7b00a..51788e0b9 100644 --- a/src/Tools/Parser.py +++ b/src/Tools/Parser.py @@ -19,12 +19,12 @@ class DeclarationNode(Node): class ClassDeclarationNode(DeclarationNode): - def __init__(self, idx, features, parent=None): + def __init__(self, classx, idx, features, parent=None): self.id = idx self.parent = parent self.features = features - self.line = idx.line - self.column = idx.column + self.line = classx.line + self.column = classx.column class AttrDeclarationNode(DeclarationNode): @@ -51,43 +51,43 @@ class ExpressionNode(Node): class IfThenElseNode(ExpressionNode): - def __init__(self, condition, if_body, else_body): + def __init__(self, ifx, condition, if_body, else_body): self.condition = condition self.if_body = if_body self.else_body = else_body - self.line = condition.line - self.column = condition.column + self.line = ifx.line + self.column = ifx.column class WhileLoopNode(ExpressionNode): - def __init__(self, condition, body): + def __init__(self, whilex, condition, body): self.condition = condition self.body = body - self.line = condition.line - self.column = condition.column + self.line = whilex.line + self.column = whilex.column class BlockNode(ExpressionNode): - def __init__(self, expressions): + def __init__(self, brace, expressions): self.expressions = expressions - self.line = expressions[-1].line - self.column = expressions[-1].column + self.line = brace.line + self.column = brace.column class LetInNode(ExpressionNode): - def __init__(self, let_body, in_body): + def __init__(self, let, let_body, in_body): self.let_body = let_body self.in_body = in_body - self.line = in_body.line - self.column = in_body.column + self.line = let.line + self.column = let.column class CaseOfNode(ExpressionNode): - def __init__(self, expression, branches): + def __init__(self, case, expression, branches): self.expression = expression self.branches = branches - self.line = expression.line - self.column = expression.column + self.line = case.line + self.column = case.column class AssignNode(ExpressionNode): @@ -106,15 +106,18 @@ def __init__(self, expression): class NotNode(UnaryNode): - pass + def __init__(self, notx, expression): + super().__init__(expression) + self.line = notx.line + self.column = notx.column class BinaryNode(ExpressionNode): - def __init__(self, left, right): + def __init__(self, left, operator, right): self.left = left self.right = right - self.line = left.line - self.column = left.column + self.line = operator.line + self.column = operator.column class LessEqualNode(BinaryNode): @@ -150,11 +153,17 @@ class DivNode(ArithmeticNode): class IsVoidNode(UnaryNode): - pass + def __init__(self, isvoid, expression): + super().__init__(expression) + self.line = isvoid.line + self.column = isvoid.column class ComplementNode(UnaryNode): - pass + def __init__(self, complement, expression): + super().__init__(expression) + self.line = complement.line + self.column = complement.column class FunctionCallNode(ExpressionNode): @@ -163,8 +172,8 @@ def __init__(self, obj, idx, args, typex=None): self.id = idx self.args = args self.type = typex - self.line = idx.line - self.column = idx.column + self.line = obj.line + self.column = obj.column class MemberCallNode(ExpressionNode): @@ -176,10 +185,10 @@ def __init__(self, idx, args): class NewNode(ExpressionNode): - def __init__(self, typex): + def __init__(self, new, typex): self.type = typex - self.line = typex.line - self.column = typex.column + self.line = new.line + self.column = new.column class AtomicNode(ExpressionNode): @@ -239,9 +248,9 @@ class BoolNode(AtomicNode): class_list %= def_class, lambda h, s: [s[1]] # Defincicion de la clase -def_class %= classx + typex + ocur + feature_list + ccur + semi, lambda h, s: ClassDeclarationNode(s[2], s[4]) +def_class %= classx + typex + ocur + feature_list + ccur + semi, lambda h, s: ClassDeclarationNode(s[1], s[2], s[4]) def_class %= classx + typex + inherits + typex + ocur + feature_list + ccur + semi, lambda h, s: ClassDeclarationNode( - s[2], s[6], s[4]) + s[1], s[2], s[6], s[4]) # Lista de propiedades de la clase feature_list %= feature + feature_list, lambda h, s: [s[1]] + s[2] @@ -270,32 +279,32 @@ class BoolNode(AtomicNode): ### Expresiones ### # Expresion Let-in -expr_1 %= let + let_list + inx + expr_1, lambda h, s: LetInNode(s[2], s[4]) +expr_1 %= let + let_list + inx + expr_1, lambda h, s: LetInNode(s[1], s[2], s[4]) let_list %= idx + colon + typex, lambda h, s: [(s[1], s[3], None)] let_list %= idx + colon + typex + larrow + expr_1, lambda h, s: [(s[1], s[3], s[5])] let_list %= idx + colon + typex + comma + let_list, lambda h, s: [(s[1], s[3], None)] + s[5] let_list %= idx + colon + typex + larrow + expr_1 + comma + let_list, lambda h, s: [(s[1], s[3], s[5])] + s[7] expr_1 %= idx + larrow + expr_1, lambda h, s: AssignNode(s[1], s[3]) -expr_1 %= notx + expr_1, lambda h, s: NotNode(s[2]) -expr_1 %= expr_2 + equal + expr_1, lambda h, s: EqualNode(s[1], s[3]) +expr_1 %= notx + expr_1, lambda h, s: NotNode(s[1], s[2]) +expr_1 %= expr_2 + equal + expr_1, lambda h, s: EqualNode(s[1], s[2], s[3]) expr_1 %= expr_2, lambda h, s: s[1] -expr_2 %= arith + less + arith, lambda h, s: LessNode(s[1], s[3]) -expr_2 %= arith + leq + arith, lambda h, s: LessEqualNode(s[1], s[3]) +expr_2 %= arith + less + arith, lambda h, s: LessNode(s[1], s[2], s[3]) +expr_2 %= arith + leq + arith, lambda h, s: LessEqualNode(s[1], s[2], s[3]) expr_2 %= arith, lambda h, s: s[1] #Expresiones aritmeticas -arith %= arith + plus + factor, lambda h, s: PlusNode(s[1], s[3]) -arith %= arith + minus + factor, lambda h, s: MinusNode(s[1], s[3]) +arith %= arith + plus + factor, lambda h, s: PlusNode(s[1], s[2], s[3]) +arith %= arith + minus + factor, lambda h, s: MinusNode(s[1], s[2], s[3]) arith %= factor, lambda h, s: s[1] -factor %= factor + star + atom, lambda h, s: StarNode(s[1], s[3]) -factor %= factor + div + atom, lambda h, s: DivNode(s[1], s[3]) +factor %= factor + star + atom, lambda h, s: StarNode(s[1], s[2], s[3]) +factor %= factor + div + atom, lambda h, s: DivNode(s[1], s[2], s[3]) factor %= term, lambda h, s: s[1] -term %= compl + term, lambda h, s: ComplementNode(s[2]) -term %= isvoid + term, lambda h, s: IsVoidNode(s[2]) +term %= compl + term, lambda h, s: ComplementNode(s[1], s[2]) +term %= isvoid + term, lambda h, s: IsVoidNode(s[1], s[2]) term %= atom, lambda h, s: s[1] # Encapsulaciones atomicas @@ -304,19 +313,19 @@ class BoolNode(AtomicNode): atom %= string, lambda h, s: StringNode(s[1]) atom %= boolx, lambda h, s: BoolNode(s[1]) atom %= idx, lambda h, s: IdNode(s[1]) -atom %= ifx + expr_1 + then + expr_1 + elsex + expr_1 + fi, lambda h, s: IfThenElseNode(s[2], s[4], s[6]) -atom %= whilex + expr_1 + loop + expr_1 + pool, lambda h, s: WhileLoopNode(s[2], s[4]) +atom %= ifx + expr_1 + then + expr_1 + elsex + expr_1 + fi, lambda h, s: IfThenElseNode(s[1], s[2], s[4], s[6]) +atom %= whilex + expr_1 + loop + expr_1 + pool, lambda h, s: WhileLoopNode(s[1], s[2], s[4]) # Expresion new -atom %= new + typex, lambda h, s: NewNode(s[2]) +atom %= new + typex, lambda h, s: NewNode(s[1], s[2]) # Encapsulamiento entre corchetes -atom %= ocur + expr_list + ccur, lambda h, s: BlockNode(s[2]) +atom %= ocur + expr_list + ccur, lambda h, s: BlockNode(s[1], s[2]) expr_list %= expr_1 + semi, lambda h, s: [s[1]] expr_list %= expr_1 + semi + expr_list, lambda h, s: [s[1]] + s[3] # Expresion Case of -atom %= case + expr_1 + of + case_list + esac, lambda h, s: CaseOfNode(s[2], s[4]) +atom %= case + expr_1 + of + case_list + esac, lambda h, s: CaseOfNode(s[1], s[2], s[4]) case_list %= idx + colon + typex + rarrow + expr_1 + semi, lambda h, s: [(s[1], s[3], s[5])] case_list %= idx + colon + typex + rarrow + expr_1 + semi + case_list, lambda h, s: [(s[1], s[3], s[5])] + s[7] diff --git a/src/Tools/Tools/Errors.py b/src/Tools/Tools/Errors.py index e40b9da9d..4d1b8e0f1 100644 --- a/src/Tools/Tools/Errors.py +++ b/src/Tools/Tools/Errors.py @@ -31,4 +31,36 @@ def __str__(self): f'SyntacticError: ERROR at or near {self.text}' def __repr__(self): return f'{self.line, self.column} - ' \ - f'SyntacticError: ERROR at or near {self.text}' \ No newline at end of file + f'SyntacticError: ERROR at or near {self.text}' + +class SemanticError(Error): + def __str__(self): + return f'{self.line, self.column} - ' \ + f'SemanticError: {self.text}' + def __repr__(self): + return f'{self.line, self.column} - ' \ + f'SemanticError: {self.text}' + +class TypeError(Error): + def __str__(self): + return f'{self.line, self.column} - ' \ + f'TypeError: {self.text}' + def __repr__(self): + return f'{self.line, self.column} - ' \ + f'TypeError: {self.text}' + +class NameError(Error): + def __str__(self): + return f'{self.line, self.column} - ' \ + f'NameError: {self.text}' + def __repr__(self): + return f'{self.line, self.column} - ' \ + f'NameError: {self.text}' + +class AttributeError(Error): + def __str__(self): + return f'{self.line, self.column} - ' \ + f'AttributeError: {self.text}' + def __repr__(self): + return f'{self.line, self.column} - ' \ + f'AttributeError: {self.text}' \ No newline at end of file diff --git a/src/Tools/Tools/Semantic.py b/src/Tools/Tools/Semantic.py index 44d94fd3a..1553857e3 100644 --- a/src/Tools/Tools/Semantic.py +++ b/src/Tools/Tools/Semantic.py @@ -6,6 +6,7 @@ class SemanticException(Exception): def text(self): return self.args[0] + # Representa un atributo en un tipo del programa class Attribute: def __init__(self, name : str, type, line : int, column : int, expression = None): @@ -27,7 +28,6 @@ def __init__(self, name : str, param_names : list, param_types : list, return_ty self.param_names = param_names self.param_types = param_types self.return_type = return_type - self.return_info = None def __str__(self): params = ', '.join(f'{n}: {t.name}' for n, t in zip(self.param_names, self.param_types)) @@ -55,15 +55,18 @@ def set_parent(self, parent): if self.parent is not None: raise SemanticException(f'Parent type is already set for {self.name}.') if parent.sealed: - raise SemanticException(f'Parent type "{parent.name}" is sealed. Can\'t inherit from it.') + raise SemanticException(f'Cannot inherit from sealed type {parent.name}') self.parent = parent self.depth = parent.depth + 1 # Retorna el tipo en el arbol de herencia de mayor profundidad # que es padre comun de ambos tipos def type_union(self, other): - if self is AutoType: return other - elif other is AutoType: return self + if self is VoidType or other is VoidType: + return VoidType() + + if self is ErrorType or other is ErrorType: + return ErrorType() x, y = self, other while x.depth > y.depth: @@ -92,13 +95,13 @@ def get_attribute(self, name:str): # Define un atributo para el tipo actual con el nombre y el tipo correspondiente def define_attribute(self, name:str, typex, line, column): try: - self.get_attribute(name) - except SemanticException: + attribute = next(attr for attr in self.attributes if attr.name == name) + except StopIteration: attribute = Attribute(name, typex, line, column) self.attributes.append(attribute) return attribute else: - raise SemanticException(f'Attribute "{name}" is already defined in {self.name}.') + raise SemanticException(f'Attribute "{name}" is multiply defined in class.') # Retorna el metodo del tipo actual con el nombre correspondiente def get_method(self, name:str): @@ -116,22 +119,26 @@ def get_method(self, name:str): # retorno correspondientes def define_method(self, name:str, param_names:list, param_types:list, return_type, line, column): if name in self.methods: - raise SemanticException(f'Method "{name}" already defined in {self.name}') + raise SemanticException(f'Method {name} is multiply defined') method = self.methods[name] = Method(name, param_names, param_types, return_type) method.return_info = VariableInfo(f'{self.name}_{name}_returnType', return_type, - line, column, f'{self.name}_{name}_returnType') + line, column) + return method # Returns true if other is a parent of current type - def inherits_from(self, other): + def conforms_to(self, other): return other.is_special_type() \ or self.name == other.name \ - or (self.parent is not None and self.parent.inherits_from(other)) + or (self.parent is not None and self.parent.conforms_to(other)) def is_special_type(self): return False + def is_void_type(self): + return False + def __str__(self): output = f'type {self.name}' parent = '' if self.parent is None else f' : {self.parent.name}' @@ -154,7 +161,7 @@ def __init__(self): Type.__init__(self, 'SELF_TYPE') self.sealed = True - def inherits_from(self, other): + def conforms_to(self, other): return False def is_special_type(self): @@ -163,20 +170,23 @@ def is_special_type(self): def __eq__(self, other): return isinstance(other, SelfType) -class AutoType(Type): +class VoidType(Type): def __init__(self): - Type.__init__(self, 'AUTO_TYPE') + Type.__init__(self, 'VOID_TYPE') self.sealed = True def type_union(self, other): return self - def inherits_from(self, other): + def conforms_to(self, other): return True def is_special_type(self): return True + def is_void_type(self): + return True + def __eq__(self, other): return isinstance(other, Type) @@ -188,7 +198,7 @@ def __init__(self): def type_union(self, other): return self - def inherits_from(self, other): + def conforms_to(self, other): return True def is_special_type(self): @@ -199,64 +209,17 @@ def __eq__(self, other): # Clase para representar una variable dentro del programa class VariableInfo: - def __init__(self, name, vtype, line, column, info): + def __init__(self, name, vtype, line, column): self.name = name self.type = vtype - self.info = info self.line = line self.column = column - self.infered = not isinstance(vtype, AutoType) + self.upper = [] self.lower = [] self.dependencies = [] self.weakDependencies = [] - # Incluye un tipo entre los tipos posibles de los que hereda la variable - def set_upper_type(self, typex): - if not self.infered and not isinstance(typex, AutoType): - self.upper.append(typex) - def set_lower_type(self, typex): - if not self.infered and not isinstance(typex, AutoType): - self.lower.append(typex) - - # Infiere el tipo de la variable, obtiene entre todos los tipos posibles - # uno de los que hereden todos los tipos definidos anteriormente, basicamente - # obtiene el lca de todos los tipos definidos anteriormente - def infer_type(self): - if not self.infered: - for d in self.dependencies: - if not isinstance(d, VariableInfo) or d.name == self.name: - continue - if isinstance(d.type, AutoType): - return False - self.lower.append(d.type) - - upper_type = None - for x in self.upper: - if not upper_type or x.inherits_from(upper_type): - upper_type = x - elif upper_type and not upper_type.inherits_from(x): - upper_type = ErrorType() - return False - - lower_type = None - for typex in self.lower: - lower_type = typex if not lower_type else lower_type.type_union(typex) - - if lower_type: - self.type = lower_type if not upper_type or lower_type.inherits_from(upper_type) else ErrorType() - else: - self.type = upper_type - - if not self.type or isinstance(self.type, ErrorType): - self.type = AutoType() - return False - - self.infered = not isinstance(self.type, AutoType) - self.upper, self.lower = [], [] - - return self.infered - return False # Clase para representar el contexto en el que se guardan las variables y los # tipos durante el chequeo semantico del programa @@ -267,14 +230,14 @@ def __init__(self): # Incluye un tipo dentro del contexto def add_type(self, type : Type): if type.name in self.types: - raise SemanticException(f'Type with the same name ({type.name}) already in context.') + raise SemanticException('Classes may not be redefined') self.types[type.name] = type return type # Crea un tipo dentro del contexto def create_type(self, name : str): if name in self.types: - raise SemanticException(f'Type with the same name ({name}) already in context.') + raise SemanticException('Classes may not be redefined') type = self.types[name] = Type(name) return type @@ -283,7 +246,7 @@ def get_type(self, name : str): try: return self.types[name] except KeyError: - raise SemanticException(f'Type ({name}) not defined.') + raise SemanticException(f'Type {name} not defined.') def __str__(self): return '{\n\t' + '\n\t'.join(y for x in self.types.values() for y in str(x).split('\n')) + '\n}' @@ -305,8 +268,8 @@ def create_child(self): return self.childs[-1] # Define una variable en el scope - def define_variable(self, name : str, type : Type, line : int, column : int, info : str = ''): - self.locals.append(VariableInfo(name, type, line, column, info)) + def define_variable(self, name : str, type : Type, line : int, column : int): + self.locals.append(VariableInfo(name, type, line, column)) return self.locals[-1] # Retorna una variable definida en el scope, None si no esta definida diff --git a/src/Tools/Type_Builder.py b/src/Tools/Type_Builder.py new file mode 100644 index 000000000..efa80bb6a --- /dev/null +++ b/src/Tools/Type_Builder.py @@ -0,0 +1,157 @@ +from .Tools import visitor +from .Tools.Semantic import * +from .Parser import ProgramNode, ClassDeclarationNode, AttrDeclarationNode, FuncDeclarationNode +from .Tools.Errors import * + +# Visitor encargado de contruir los tipos. Una vez que se conocen los nombres +# de los tipos que intervienen en el codifo COOL, este visitor les annade sus +# metodos y atributos, asi como el tipo padre. + +class Type_Builder: + def __init__(self, Context : Context): + self.Context = Context + self.Current_Type = None + + self.errors = [] + + # Construye los tipos builtin + self.Object_Type = self.Context.get_type('Object') + + self.IO_Type = self.Context.get_type('IO') + self.IO_Type.set_parent(self.Object_Type) + + self.Int_Type = self.Context.get_type('Int') + self.Int_Type.set_parent(self.Object_Type) + self.Int_Type.sealed = True + + self.String_Type = self.Context.get_type('String') + self.String_Type.set_parent(self.Object_Type) + self.String_Type.sealed = True + + self.Bool_Type = self.Context.get_type('Bool') + self.Bool_Type.set_parent(self.Object_Type) + self.Bool_Type.sealed = True + + self.IO_Type.define_method('out_string', ['x'], [self.String_Type], SelfType(), 0, 0) + self.IO_Type.define_method('out_int', ['x'], [self.Int_Type], SelfType(), 0, 0) + self.IO_Type.define_method('in_int', [], [], self.Int_Type, 0, 0) + self.IO_Type.define_method('in_string', [], [], self.String_Type, 0, 0) + + self.Object_Type.define_method('abort', [], [], self.Object_Type, 0, 0) + self.Object_Type.define_method('type_name', [], [], self.String_Type, 0, 0) + self.Object_Type.define_method('copy', [], [], SelfType(), 0, 0) + + self.String_Type.define_method('length', [], [], self.Int_Type, 0, 0) + self.String_Type.define_method('concat', ['x'], [self.String_Type], self.String_Type, 0, 0) + self.String_Type.define_method('substr', ['l', 'r'], [self.Int_Type, self.Int_Type], self.String_Type, 0, 0) + + @visitor.on('node') + def visit(self, node): + pass + + @visitor.when(ProgramNode) + def visit(self, node : ProgramNode): + for type in node.declarations: + self.visit(type) + + try: + self.Context.get_type('Main').get_method('main') + except SemanticException: + # Cada programa COOL debe tener una clase MAIN + self.errors.append(SemanticError(0, 0, + f'Class Main and its method main must be defined')) + + @visitor.when(ClassDeclarationNode) + def visit(self, node : ClassDeclarationNode): + self.Current_Type = self.Context.get_type(node.id.lex) + + if node.parent: + try: + parent_type = self.Context.get_type(node.parent.lex) + except SemanticException as ex: + self.errors.append(TypeError(node.parent.line, node.parent.column, + f'Class {node.id} inherits from an undefined class {node.parent.lex}')) + parent_type = self.Object_Type + + try: + self.Current_Type.set_parent(parent_type) + except SemanticException as ex: + self.errors.append(SemanticError(node.parent.line, node.parent.column, + f'Class {node.id.lex} cannot inherit class {parent_type.name}')) + + parent = self.Current_Type.parent + # Revisa que no haya herencia ciclica + while parent: + if parent == self.Current_Type: + self.errors.append(SemanticError(node.line, node.column, + f'Class {node.id.lex}, or an ancestor of {node.id.lex}, ' + f'is involved in an inheritance cycle')) + self.Current_Type.parent = self.Object_Type + break + parent = parent.parent + + else: + self.Current_Type.set_parent(self.Object_Type) + + + for feat in node.features: + self.visit(feat) + + @visitor.when(AttrDeclarationNode) + def visit(self, node : AttrDeclarationNode): + try: + attr_type = self.Context.get_type(node.type.lex) + except SemanticException as ex: + # Existio un error al tratar de obtener el tipo del atributo + self.errors.append(TypeError(node.line, node.column, ex.text)) + attr_type = ErrorType() + + try: + self.Current_Type.define_attribute(node.id.lex, attr_type, node.line, node.column) + except SemanticException as ex: + # Existio un error al tratar de definir el atributo + self.errors.append(SemanticError(node.line, node.column, ex.text)) + + @visitor.when(FuncDeclarationNode) + def visit(self, node : FuncDeclarationNode): + param_names, param_types = [], [] + + for name, type in node.params: + try: + type = self.Context.get_type(type.lex) + except SemanticException as ex: + # Existio un error al tratar de obtener el tipo del parametro + self.errors.append(TypeError(name.line, name.column, + f'Class {type.lex} of formal parameter {name.lex} is undefined')) + type = ErrorType() + else: + if isinstance(type, SelfType): + self.errors.append(SemanticError(name.line, name.column, + f'\'self\' cannot be the name of a formal parameter')) + arg_type = ErrorType() + + if name.lex in param_names: + self.errors.append(SemanticError(name.line, name.column, + f'Formal parameter {name.lex} is multiply defined')) + + param_names.append(name.lex) + param_types.append(type) + + + + try: + return_type = self.Context.get_type(node.type.lex) + except SemanticException as ex: + # Existio un error al tratar de obtener el tipo del parametro de retorno + self.errors.append(TypeError(node.type.line, node.type.column, + f'Undefined return type {node.type.lex} in method {node.id.lex}')) + return_type = ErrorType() + + if return_type is SelfType: + return_type = self.Current_Type + + try: + self.Current_Type.define_method(node.id.lex, param_names, param_types, return_type, node.line, node.column) + except SemanticException as ex: + # Existio un error al tratar de definir el metodo + self.errors.append(SemanticError(node.line, node.column, ex.text)) diff --git a/src/Tools/Type_Checker.py b/src/Tools/Type_Checker.py new file mode 100644 index 000000000..418a00254 --- /dev/null +++ b/src/Tools/Type_Checker.py @@ -0,0 +1,509 @@ +from .Tools import visitor +from .Parser import ProgramNode, ClassDeclarationNode, AttrDeclarationNode, FuncDeclarationNode,\ + IfThenElseNode, WhileLoopNode, BlockNode, LetInNode, CaseOfNode,\ + AssignNode, LessEqualNode, LessNode, EqualNode, ArithmeticNode,\ + NotNode, IsVoidNode, ComplementNode, FunctionCallNode, MemberCallNode, NewNode,\ + IntegerNode, IdNode, StringNode, BoolNode +from .Tools.Semantic import Context, Scope, SelfType, SemanticException, ErrorType +from .Tools.Errors import * + +# Este es el visitor encargado de terminar el chequeo semantico. +# Revisa la compatibilidad de tipos, la compatibilidad en la herencia, +# que las variables hayan sido previamente definidas, asi como los +# metodos y atributos de clase, crea el scope para las variables, el +# cual sera rehusado para inferir las variables que se requieran. +# Observar que cada vez que el visitor llama recursivamente crea un scope +# hijo en el scope actual, esto se hace para que las variables previamente +# declaradas en ambitos hermanos no sean utiles en el ambito actual. + +class Type_Checker: + + def __init__(self, Context : Context): + self.Context = Context + self.errors = [] + self.Current_Type = None + self.Current_Method = None + + self.Object_Type = self.Context.get_type('Object') + self.IO_Type = self.Context.get_type('IO') + self.String_Type = self.Context.get_type('String') + self.Int_Type = self.Context.get_type('Int') + self.Bool_Type = self.Context.get_type('Bool') + + self.builtin_Types = [ + self.Object_Type, + self.IO_Type, + self.String_Type, + self.Int_Type, + self.Bool_Type + ] + + @visitor.on('node') + def visit(self, node, scope): + pass + + @visitor.when(ProgramNode) + def visit(self, node : ProgramNode, scope : Scope = None): + scope = Scope() + for declaration in node.declarations: + self.visit(declaration, scope.create_child()) + return scope + + @visitor.when(ClassDeclarationNode) + def visit(self, node : ClassDeclarationNode, scope : Scope): + self.Current_Type = self.Context.get_type(node.id.lex) + + # Incluyo cada uno de los atributos de los padres en su resoectivo orden + current = self.Current_Type + attributtes = [] + while current.parent: + current = current.parent + for att in reversed(current.attributes): + attributtes.append(att) + + for att in reversed(attributtes): + scope.define_variable(att.name, att.type, att.line, att.column) + + for att in self.Current_Type.attributes: + if scope.is_defined(att.name): + self.errors.append(SemanticError(att.line, att.column, + f'Attribute {att.name} is an attribute of an inherited class')) + scope.define_variable(att.name, att.type, att.line, att.column) + + for feature in node.features: + self.visit(feature, scope.create_child()) + node.static_type = self.Current_Type + + @visitor.when(AttrDeclarationNode) + def visit(self, node : AttrDeclarationNode, scope : Scope): + expr = node.expression + attr = self.Current_Type.get_attribute(node.id.lex) + node_type = self.Current_Type if attr.type is SelfType else attr.type + + if expr: + self.visit(expr, scope.create_child()) + expr_type = expr.static_type + + # Chequeo compatibilidad de tipos + if not expr_type.conforms_to(node_type): + self.errors.append(TypeError(node.expression.line, node.expression.column, + f'Inferred type {expr_type.name} of initialization of attribute {attr.name} ' + f'does not conform to declared type {node_type.name}')) + + if attr.name.lower() == 'self': + self.errors.append(SemanticError(node.line, node.column, + '\'self\' cannot be the name of an attribute')) + + node.static_type = node_type + + @visitor.when(FuncDeclarationNode) + def visit(self, node : FuncDeclarationNode, scope : Scope): + self.Current_Method = self.Current_Type.get_method(node.id.lex) + + if self.Current_Type.parent: + try: + inherited_method = self.Current_Type.parent.get_method(node.id.lex) + + if len(self.Current_Method.param_names) != len(inherited_method.param_names): + self.errors.append(SemanticError(node.line, node.column, + f'Incompatible number of formal parameters in redefined method {self.Current_Method.name}')) + else: + for par1, par2, p in zip(self.Current_Method.param_types, inherited_method.param_types, node.params): + if par1.name != par2.name: + self.errors.append(SemanticError(p[0].line, p[0].column, + f'In redefined method {self.Current_Method.name}, parameter type {par1.name} ' + f'is different from original type {par2.name}')) + + if self.Current_Method.return_type.name != inherited_method.return_type.name: + self.errors.append(SemanticError(node.line, node.column, + f'In redefined method {self.Current_Method.name}, return type {self.Current_Method.return_type.name} ' + f'is different from original return type {inherited_method.return_type.name}')) + + except SemanticException: + pass + + scope.define_variable('self', self.Current_Type, node.line, node.column) + + # Defino cada uno de los parametros de metodo + for pname, ptype in zip(self.Current_Method.param_names, self.Current_Method.param_types): + scope.define_variable(pname, ptype, node.line, node.column) + + if pname.lower() == 'self': + self.errors.append(SemanticError(node.line, node.column, + '\'self\' cannot be the name of a formal parameter')) + + # Chequeo consistencia en el cuerpo del metodo + self.visit(node.body, scope.create_child()) + + expr_type = node.body.static_type + return_type = self.Current_Method.return_type + + # Chequeo consistencia entre el tipo de retorno definido y el tipo de retorno + # del cuerpo del metodo + if not expr_type.conforms_to(return_type): + self.errors.append(TypeError(node.line, node.column, + f'Inferred return type {expr_type.name} of method {self.Current_Method.name} ' + f'does not conform to declared return type {return_type.name}')) + node.static_type = return_type + + @visitor.when(IfThenElseNode) + def visit(self, node : IfThenElseNode, scope : Scope): + # Chequeo consistencia en la condicion del if + self.visit(node.condition, scope.create_child()) + + condition_type = node.condition.static_type + # Chequeo que el tipo de la condicion sea booleano + if not condition_type.conforms_to(self.Bool_Type): + self.errors.append(TypeError(node.condition.line, node.condition.column, + 'Predicate of \'if\' does not have type Bool')) + + # Chequeo consistencia en las expresiones del then y el else + self.visit(node.if_body, scope.create_child()) + self.visit(node.else_body, scope.create_child()) + + if_type = node.if_body.static_type + else_type = node.else_body.static_type + + node.static_type = if_type.type_union(else_type) + + @visitor.when(WhileLoopNode) + def visit(self, node : WhileLoopNode, scope : Scope): + self.visit(node.condition, scope.create_child()) + condition_type = node.condition.static_type + + # Chequeo que la condicion sea de tipo booleano + if not condition_type.conforms_to(self.Bool_Type): + self.errors.append(TypeError(node.condition.line, node.condition.column, + 'Loop condition does not have type Bool')) + + # Chequeo consistencias en el cuerpo del while + self.visit(node.body, scope.create_child()) + + node.static_type = self.Object_Type + + @visitor.when(BlockNode) + def visit(self, node : BlockNode, scope : Scope): + + # Chequeo consistencias en cada una de las instrucciones del cuerpo del bloque + for expr in node.expressions: + self.visit(expr, scope.create_child()) + + node.static_type = node.expressions[-1].static_type + + @visitor.when(LetInNode) + def visit(self, node : LetInNode, scope : Scope): + for id, type, expr in node.let_body: + + if id.lex.lower() == 'self': + self.errors.append(SemanticError(id.line, id.column, + '\'self\' cannot be bound in a \'let\' expression')) + + # Por cada una de las declaraciones del let + try: + type = self.Context.get_type(type.lex) + except SemanticException as ex: + # Chequeo que el tipo exista + self.errors.append(TypeError(id.line, id.column, + f'Class {type.lex} of let-bound identifier {id.lex} is undefined')) + type = ErrorType() + + # Si es Self_Type tomo el tipo correspondiente + type = self.Current_Type if isinstance(type, SelfType) else type + + child = scope.create_child() + if expr: + # Chequeo consistencias en la declaracion y la compatibilidad de tipos + self.visit(expr, child) + if not expr.static_type.conforms_to(type): + self.errors.append(TypeError(id.line, id.column, + f'Inferred type {expr.static_type.name} of initialization of ' + f'{id.lex} does not conform to identifier\'s declared type {type.name}')) + + # Defino la variable + scope.define_variable(id.lex, type, node.line, node.column) + + # Chequeo consistencias en el cuerpo del let in + self.visit(node.in_body, scope.create_child()) + node.static_type = node.in_body.static_type + + @visitor.when(CaseOfNode) + def visit(self, node : CaseOfNode, scope : Scope): + # Chequeo consistencias en el case + self.visit(node.expression, scope.create_child()) + + branchs = [] + + node.static_type = None + for id, type, expr in node.branches: + # Por cada instruccion en el cuerpo del case-of + try: + type = self.Context.get_type(type.lex) + except SemanticException as ex: + # Chequeo que el tipo exista + self.errors.append(TypeError(type.line, type.column, + f'Class {type.lex} of case branch is undefined')) + type = ErrorType() + + # Chequeo que no sea un tipo especial + if isinstance(type, SelfType): + self.errors.append(SemanticError(id.line, id.column, + f'SELF_TYPE cannot be used as a case branch')) + + child = scope.create_child() + # Declaro la variable y chequeo consistencias en la expresion + child.define_variable(id.lex, type, node.line, node.column) + self.visit(expr, child) + + if type.name in branchs: + self.errors.append(SemanticError(id.line, id.column, + f'Duplicate branch {type.name} in case statement')) + branchs.append(type.name) + + node.static_type = node.static_type.type_union(expr.static_type) if node.static_type else expr.static_type + + + @visitor.when(AssignNode) + def visit(self, node : AssignNode, scope : Scope): + # Chequeo consistencias en la expresion + self.visit(node.expression, scope.create_child()) + expr_type = node.expression.static_type + + if node.id.lex.lower() == 'self': + self.errors.append(SemanticError(node.line, node.column, + 'Cannot assign to \'self\'')) + + # Chequeo que la variable este declarada y que su tipo sea valido + if scope.is_defined(node.id.lex): + var_type = scope.find_variable(node.id.lex).type + if isinstance(var_type, SelfType): + var_type = self.Current_Type + + if not expr_type.conforms_to(var_type): + self.errors.append(TypeError(node.expression.line, node.expression.column, + f'Inferred type {expr_type.name} of initialization of attribute {node.id.lex} ' + f'does not conform to declared type {var_type.name}')) + else: + self.errors.append(NameError(node.line, node.column, + f'Undeclared identifier {node.id.lex}')) + + node.static_type = expr_type + + @visitor.when(NotNode) + def visit(self, node : NotNode, scope : Scope): + # Chequeo la consistencia de la expresion + self.visit(node.expression, scope.create_child()) + + # Chequeo que la expresion sea booleana + if not node.expression.static_type.conforms_to(self.Bool_Type): + self.errors.append(TypeError(node.expression.line, node.expression.column, + f'Argument of \'not\' has type {node.expression.static_type.name} instead of Bool')) + + node.static_type = self.Bool_Type + + @visitor.when(LessEqualNode) + def visit(self, node : LessEqualNode, scope : Scope): + # Chequeo la consistencia de ambos miembros + self.visit(node.left, scope.create_child()) + self.visit(node.right, scope.create_child()) + left_type = node.left.static_type + right_type = node.right.static_type + + # Chequeo que ambos miembros posean tipo int + if not left_type.conforms_to(self.Int_Type) or not right_type.conforms_to(self.Int_Type): + self.errors.append(TypeError(node.line, node.column, + f'non-Int arguments: {left_type.name} <= {right_type.name}')) + + node.static_type = self.Bool_Type + + @visitor.when(LessNode) + def visit(self, node: LessNode, scope: Scope): + # Chequeo la consistencia de ambos miembros + self.visit(node.left, scope.create_child()) + self.visit(node.right, scope.create_child()) + left_type = node.left.static_type + right_type = node.right.static_type + + # Chequeo que ambos miembros posean tipo int + if not left_type.conforms_to(self.Int_Type) or not right_type.conforms_to(self.Int_Type): + self.errors.append(TypeError(node.line, node.column, + f'non-Int arguments: {left_type.name} < {right_type.name}')) + + node.static_type = self.Bool_Type + + @visitor.when(EqualNode) + def visit(self, node: EqualNode, scope: Scope): + # Chequeo la consistencia de ambos miembros + self.visit(node.left, scope.create_child()) + self.visit(node.right, scope.create_child()) + left_type = node.left.static_type + right_type = node.right.static_type + + # Chequeo que ambos miembros posean tipos comparables + if left_type.conforms_to(self.Int_Type) ^ right_type.conforms_to(self.Int_Type): + self.errors.append(TypeError(node.line, node.column, + f'Illegal comparison with a basic type')) + elif left_type.conforms_to(self.String_Type) ^ right_type.conforms_to(self.String_Type): + self.errors.append(TypeError(node.line, node.column, + f'Illegal comparison with a basic type')) + elif left_type.conforms_to(self.Bool_Type) ^ right_type.conforms_to(self.Bool_Type): + self.errors.append(TypeError(node.line, node.column, + f'Illegal comparison with a basic type')) + + node.static_type = self.Bool_Type + + @visitor.when(ArithmeticNode) + def visit(self, node : ArithmeticNode, scope : Scope): + # Chequeo la consistencia de ambos miembros + self.visit(node.left, scope.create_child()) + self.visit(node.right, scope.create_child()) + left_type = node.left.static_type + right_type = node.right.static_type + + # Chequeo que ambos miembros posean tipo int + if not left_type.conforms_to(self.Int_Type) or not right_type.conforms_to(self.Int_Type): + self.errors.append(TypeError(node.line, node.column, + f'non-Int arguments: {left_type.name} and {right_type.name}')) + + node.static_type = self.Int_Type + + @visitor.when(IsVoidNode) + def visit(self, node : IsVoidNode, scope : Scope): + # Chequeo la consistencia de la expresion + self.visit(node.expression, scope.create_child()) + node.static_type = self.Bool_Type + + @visitor.when(ComplementNode) + def visit(self, node : ComplementNode, scope : Scope): + # Chequeo la consistencia de la expresion + self.visit(node.expression, scope.create_child()) + + # Chequeo que la expresion sea de tipo booleana + if not node.expression.static_type.conforms_to(self.Int_Type): + self.errors.append(TypeError(node.expression.line, node.expression.column, + f'Argument of \'~\' has type {node.expression.static_type.name} instead of Int')) + + node.static_type = self.Int_Type + + @visitor.when(FunctionCallNode) + def visit(self, node : FunctionCallNode, scope : Scope): + # Chequeo la consistencia de la expresion a la cual se le pide la funcion + self.visit(node.obj, scope.create_child()) + obj_type = node.obj.static_type + + try: + if node.type: + # Chequeo que el tipo exista + try: + node_type = self.Context.get_type(node.type.lex) + except SemanticException as ex: + self.errors.append(TypeError(node.line, node.column, + f'Class {node.type.lex} not defined')) + node_type = ErrorType() + + # Chequeo que el tipo no sea un tipo especial + if isinstance(node_type, SelfType): + self.errors.append(TypeError(node.line, node.column, + 'SELF_TYPE cannot be used in a dispatch')) + + # Chequeo que los tipos sean compatibles + if not obj_type.conforms_to(node_type): + self.errors.append(TypeError(node.line, node.column, + f'Expression type {obj_type.name} does not conform ' + f'to declared static dispatch type {node_type.name}')) + + obj_type = node_type + + + obj_method = obj_type.get_method(node.id.lex) + return_type = obj_type if isinstance(obj_method.return_type, SelfType) else obj_method.return_type + + except SemanticException as ex: + self.errors.append(AttributeError(node.id.line, node.id.column, + f'Dispatch to undefined method {node.id.lex}')) + return_type = ErrorType() + obj_method = None + + # Chequeo consistencias en los argumentos con los que se llama al metodo + for arg in node.args: + self.visit(arg, scope.create_child()) + + if obj_method and len(node.args) == len(obj_method.param_types): + for arg, param_type, param_name in zip(node.args, obj_method.param_types, obj_method.param_names): + if not arg.static_type.conforms_to(param_type): + # Chequeo compatibilidad de tipos entre los argumentos + self.errors.append(TypeError(arg.line, arg.column, + f'In call of method {obj_method.name}, type {arg.static_type.name} of ' + f'parameter {param_name} does not conform to declared type {param_type.name}')) + + elif obj_method: + # Chequeo que la cantidad de argumentos sea igual a las solicitadas por el metodo + self.errors.append(SemanticError(node.id.line, node.id.column, + f'Method {obj_method.name} called with wrong number of arguments')) + + node.static_type = return_type + + + @visitor.when(MemberCallNode) + def visit(self, node : MemberCallNode, scope : Scope): + # Chequeo que el metodo exista en el tipo actual + try: + obj_method = self.Current_Type.get_method(node.id.lex) + return_type = self.Current_Type if isinstance(obj_method.return_type, SelfType) else obj_method.return_type + except SemanticException as ex: + self.errors.append(AttributeError(node.id.line, node.id.column, + f'Dispatch to undefined method {node.id.lex}')) + obj_method = None + return_type = ErrorType() + + # Chequeo la consistencia en los argumentos + for arg in node.args: + self.visit(arg, scope.create_child()) + + if obj_method and len(node.args) == len(obj_method.param_types): + # Chequeo la compatibiidad entre los tipos de los argumentos + for arg, param_type, param_name in zip(node.args, obj_method.param_types, obj_method.param_names): + if not arg.static_type.conforms_to(param_type): + self.errors.append(TypeError(arg.line, arg.column, + f'In call of method {obj_method.name}, type {arg.static_type.name} of ' + f'parameter {param_name} does not conform to declared type {param_type.name}')) + + elif obj_method: + # Chequeo que la cantidad de argumentos coincida con los que requiere el metodo + self.errors.append(SemanticError(node.id.line, node.id.column, + f'Method {obj_method.name} called with wrong number of arguments')) + + node.static_type = return_type + + @visitor.when(NewNode) + def visit(self, node : NewNode, scope : Scope): + # Chequeo que el tipo exista + try: + type = self.Context.get_type(node.type.lex) + except SemanticException as ex: + self.errors.append(TypeError(node.type.line, node.type.column, + f'\'new\' used with undeclared class {node.type.lex}')) + type = ErrorType() + + node.static_type = type + + @visitor.when(IntegerNode) + def visit(self, node : IntegerNode, scope : Scope): + node.static_type = self.Int_Type + + @visitor.when(StringNode) + def visit(self, node: StringNode, scope: Scope): + node.static_type = self.String_Type + + @visitor.when(BoolNode) + def visit(self, node: BoolNode, scope: Scope): + node.static_type = self.Bool_Type + + @visitor.when(IdNode) + def visit(self, node: IntegerNode, scope: Scope): + # Chequeo que la variable exista + if scope.is_defined(node.token.lex): + node.static_type = scope.find_variable(node.token.lex).type + else: + self.errors.append(NameError(node.line, node.column, + f'Undeclared identifier {node.token.lex}')) + node.static_type = ErrorType() diff --git a/src/Tools/Type_Collector.py b/src/Tools/Type_Collector.py new file mode 100644 index 000000000..5982c158d --- /dev/null +++ b/src/Tools/Type_Collector.py @@ -0,0 +1,38 @@ +from .Tools import visitor +from .Tools.Semantic import * +from .Parser import ProgramNode, ClassDeclarationNode +from .Tools.Errors import SemanticError + +# Visitor encargado de coleccionar los nombres de las clases que se definen +# en el codigo del programa COOL, chequea ademas que no se redeclaren e +# incluye los tipos builtin dentro del contexto + +class Type_Collector: + def __init__(self): + self.errors = [] + + self.Context = Context() + + self.Context.add_type(SelfType()) + + self.Context.create_type('Object') + self.Context.create_type('String') + self.Context.create_type('IO') + self.Context.create_type('Int') + self.Context.create_type('Bool') + + @visitor.on('node') + def visit(self, node): + pass + + @visitor.when(ProgramNode) + def visit(self, node : ProgramNode): + for type in node.declarations: + self.visit(type) + + @visitor.when(ClassDeclarationNode) + def visit(self, node : ClassDeclarationNode): + try: + self.Context.create_type(node.id.lex) + except SemanticException as ex: + self.errors.append(SemanticError(node.line, node.column, ex.text)) From 7d87661ee00054f16f388b8a9955a8aedd787710 Mon Sep 17 00:00:00 2001 From: mavaldivie Date: Mon, 1 Mar 2021 18:24:05 -0500 Subject: [PATCH 03/75] Informacion del equipo y cambios en la organizacion --- doc/team.yml | 18 +- src/Main.py | 10 +- src/Tools/{ => Lexer}/Lexer.py | 9 +- src/Tools/{ => Parser}/Parser.py | 217 +-------------------- src/Tools/{ => Semantic}/Type_Builder.py | 12 +- src/Tools/{ => Semantic}/Type_Checker.py | 8 +- src/Tools/{ => Semantic}/Type_Collector.py | 8 +- src/Tools/Tools/COOLAst.py | 210 ++++++++++++++++++++ src/Tools/Tools/Semantic.py | 9 +- src/coolc.sh | 4 +- 10 files changed, 249 insertions(+), 256 deletions(-) rename src/Tools/{ => Lexer}/Lexer.py (96%) rename src/Tools/{ => Parser}/Parser.py (58%) rename src/Tools/{ => Semantic}/Type_Builder.py (94%) rename src/Tools/{ => Semantic}/Type_Checker.py (98%) rename src/Tools/{ => Semantic}/Type_Collector.py (85%) create mode 100644 src/Tools/Tools/COOLAst.py diff --git a/doc/team.yml b/doc/team.yml index c16162532..3e667bbea 100644 --- a/doc/team.yml +++ b/doc/team.yml @@ -1,10 +1,10 @@ members: - - name: Nombre Apellido1 Apellido2 - github: github_id - group: CXXX - - name: Nombre Apellido1 Apellido2 - github: github_id - group: CXXX - - name: Nombre Apellido1 Apellido2 - github: github_id - group: CXXX + - name: Marcos Adrian Valdivie Rodriguez + github: mavaldivie + group: C412 + - name: Claudia Olavarrieta Martinez + github: ClaudiOM + group: C411 + - name: Adrian Hernandez Perez + github: AdrianHP + group: C411 diff --git a/src/Main.py b/src/Main.py index 70bcbd42e..e78d26b3e 100644 --- a/src/Main.py +++ b/src/Main.py @@ -1,9 +1,9 @@ -from Tools.Lexer import Lexer -from Tools.Parser import CoolParser +from Tools.Lexer.Lexer import Lexer +from Tools.Parser.Parser import CoolParser from Tools.Tools.evaluation import evaluate_reverse_parse -from Tools.Type_Collector import Type_Collector -from Tools.Type_Builder import Type_Builder -from Tools.Type_Checker import Type_Checker +from Tools.Semantic.Type_Collector import Type_Collector +from Tools.Semantic.Type_Builder import Type_Builder +from Tools.Semantic.Type_Checker import Type_Checker def main(args): try: diff --git a/src/Tools/Lexer.py b/src/Tools/Lexer/Lexer.py similarity index 96% rename from src/Tools/Lexer.py rename to src/Tools/Lexer/Lexer.py index 43440f7ed..dcd995edb 100644 --- a/src/Tools/Lexer.py +++ b/src/Tools/Lexer/Lexer.py @@ -1,7 +1,7 @@ import ply.lex as lex -from .Tools.utils import Token -from .Parser import CoolGrammar -from .Tools.Errors import LexicographicError +from Tools.Tools.utils import Token +from Tools.Parser.Parser import CoolGrammar +from Tools.Tools.Errors import LexicographicError class Lexer: states = ( @@ -242,7 +242,8 @@ def t_ANY_newline(self, t): def find_column(self, token): line_start = self.code.rfind('\n', 0, token.lexpos) + 1 - return (token.lexpos - line_start) + 1 + return (token.lexpos - line_start) + 1 \ + + 3 * len([i for i in self.code[line_start:token.lexpos] if i == '\t']) def t_error(self, t): self.errors.append(LexicographicError(t.lineno, diff --git a/src/Tools/Parser.py b/src/Tools/Parser/Parser.py similarity index 58% rename from src/Tools/Parser.py rename to src/Tools/Parser/Parser.py index 51788e0b9..cb779829b 100644 --- a/src/Tools/Parser.py +++ b/src/Tools/Parser/Parser.py @@ -1,217 +1,6 @@ -from .Tools.pycompiler import Grammar -from .Tools.Parser_LR1 import LR1Parser - - -# Clases necesarias para representar el AST del programa COOL -class Node: - pass - -# Raiz del AST -class ProgramNode(Node): - def __init__(self, declarations): - self.declarations = declarations - self.line = declarations[0].line - self.column = declarations[0].column - - -class DeclarationNode(Node): - pass - - -class ClassDeclarationNode(DeclarationNode): - def __init__(self, classx, idx, features, parent=None): - self.id = idx - self.parent = parent - self.features = features - self.line = classx.line - self.column = classx.column - - -class AttrDeclarationNode(DeclarationNode): - def __init__(self, idx, typex, expression=None): - self.id = idx - self.type = typex - self.expression = expression - self.line = idx.line - self.column = idx.column - - -class FuncDeclarationNode(DeclarationNode): - def __init__(self, idx, params, return_type, body): - self.id = idx - self.params = params - self.type = return_type - self.body = body - self.line = idx.line - self.column = idx.column - - -class ExpressionNode(Node): - pass - - -class IfThenElseNode(ExpressionNode): - def __init__(self, ifx, condition, if_body, else_body): - self.condition = condition - self.if_body = if_body - self.else_body = else_body - self.line = ifx.line - self.column = ifx.column - - -class WhileLoopNode(ExpressionNode): - def __init__(self, whilex, condition, body): - self.condition = condition - self.body = body - self.line = whilex.line - self.column = whilex.column - - -class BlockNode(ExpressionNode): - def __init__(self, brace, expressions): - self.expressions = expressions - self.line = brace.line - self.column = brace.column - - -class LetInNode(ExpressionNode): - def __init__(self, let, let_body, in_body): - self.let_body = let_body - self.in_body = in_body - self.line = let.line - self.column = let.column - - -class CaseOfNode(ExpressionNode): - def __init__(self, case, expression, branches): - self.expression = expression - self.branches = branches - self.line = case.line - self.column = case.column - - -class AssignNode(ExpressionNode): - def __init__(self, idx, expression): - self.id = idx - self.expression = expression - self.line = idx.line - self.column = idx.column - - -class UnaryNode(ExpressionNode): - def __init__(self, expression): - self.expression = expression - self.line = expression.line - self.column = expression.column - - -class NotNode(UnaryNode): - def __init__(self, notx, expression): - super().__init__(expression) - self.line = notx.line - self.column = notx.column - - -class BinaryNode(ExpressionNode): - def __init__(self, left, operator, right): - self.left = left - self.right = right - self.line = operator.line - self.column = operator.column - - -class LessEqualNode(BinaryNode): - pass - - -class LessNode(BinaryNode): - pass - - -class EqualNode(BinaryNode): - pass - - -class ArithmeticNode(BinaryNode): - pass - - -class PlusNode(ArithmeticNode): - pass - - -class MinusNode(ArithmeticNode): - pass - - -class StarNode(ArithmeticNode): - pass - - -class DivNode(ArithmeticNode): - pass - - -class IsVoidNode(UnaryNode): - def __init__(self, isvoid, expression): - super().__init__(expression) - self.line = isvoid.line - self.column = isvoid.column - - -class ComplementNode(UnaryNode): - def __init__(self, complement, expression): - super().__init__(expression) - self.line = complement.line - self.column = complement.column - - -class FunctionCallNode(ExpressionNode): - def __init__(self, obj, idx, args, typex=None): - self.obj = obj - self.id = idx - self.args = args - self.type = typex - self.line = obj.line - self.column = obj.column - - -class MemberCallNode(ExpressionNode): - def __init__(self, idx, args): - self.id = idx - self.args = args - self.line = idx.line - self.column = idx.column - - -class NewNode(ExpressionNode): - def __init__(self, new, typex): - self.type = typex - self.line = new.line - self.column = new.column - - -class AtomicNode(ExpressionNode): - def __init__(self, token): - self.token = token - self.line = token.line - self.column = token.column - - -class IntegerNode(AtomicNode): - pass - - -class IdNode(AtomicNode): - pass - - -class StringNode(AtomicNode): - pass - - -class BoolNode(AtomicNode): - pass +from Tools.Tools.pycompiler import Grammar +from Tools.Tools.Parser_LR1 import LR1Parser +from Tools.Tools.COOLAst import * # Representacion de la gramatica de COOL utilizando la clase grammar diff --git a/src/Tools/Type_Builder.py b/src/Tools/Semantic/Type_Builder.py similarity index 94% rename from src/Tools/Type_Builder.py rename to src/Tools/Semantic/Type_Builder.py index efa80bb6a..be9425bf0 100644 --- a/src/Tools/Type_Builder.py +++ b/src/Tools/Semantic/Type_Builder.py @@ -1,7 +1,7 @@ -from .Tools import visitor -from .Tools.Semantic import * -from .Parser import ProgramNode, ClassDeclarationNode, AttrDeclarationNode, FuncDeclarationNode -from .Tools.Errors import * +from Tools.Tools import visitor +from Tools.Tools.Semantic import * +from Tools.Parser.Parser import ProgramNode, ClassDeclarationNode, AttrDeclarationNode, FuncDeclarationNode +from Tools.Tools.Errors import * # Visitor encargado de contruir los tipos. Una vez que se conocen los nombres # de los tipos que intervienen en el codifo COOL, este visitor les annade sus @@ -103,7 +103,7 @@ def visit(self, node : AttrDeclarationNode): attr_type = self.Context.get_type(node.type.lex) except SemanticException as ex: # Existio un error al tratar de obtener el tipo del atributo - self.errors.append(TypeError(node.line, node.column, ex.text)) + self.errors.append(TypeError(node.type.line, node.type.column, ex.text)) attr_type = ErrorType() try: @@ -121,7 +121,7 @@ def visit(self, node : FuncDeclarationNode): type = self.Context.get_type(type.lex) except SemanticException as ex: # Existio un error al tratar de obtener el tipo del parametro - self.errors.append(TypeError(name.line, name.column, + self.errors.append(TypeError(type.line, type.column, f'Class {type.lex} of formal parameter {name.lex} is undefined')) type = ErrorType() else: diff --git a/src/Tools/Type_Checker.py b/src/Tools/Semantic/Type_Checker.py similarity index 98% rename from src/Tools/Type_Checker.py rename to src/Tools/Semantic/Type_Checker.py index 418a00254..ac366b40c 100644 --- a/src/Tools/Type_Checker.py +++ b/src/Tools/Semantic/Type_Checker.py @@ -1,11 +1,11 @@ -from .Tools import visitor -from .Parser import ProgramNode, ClassDeclarationNode, AttrDeclarationNode, FuncDeclarationNode,\ +from Tools.Tools import visitor +from Tools.Parser.Parser import ProgramNode, ClassDeclarationNode, AttrDeclarationNode, FuncDeclarationNode,\ IfThenElseNode, WhileLoopNode, BlockNode, LetInNode, CaseOfNode,\ AssignNode, LessEqualNode, LessNode, EqualNode, ArithmeticNode,\ NotNode, IsVoidNode, ComplementNode, FunctionCallNode, MemberCallNode, NewNode,\ IntegerNode, IdNode, StringNode, BoolNode -from .Tools.Semantic import Context, Scope, SelfType, SemanticException, ErrorType -from .Tools.Errors import * +from Tools.Tools.Semantic import Context, Scope, SelfType, SemanticException, ErrorType +from Tools.Tools.Errors import * # Este es el visitor encargado de terminar el chequeo semantico. # Revisa la compatibilidad de tipos, la compatibilidad en la herencia, diff --git a/src/Tools/Type_Collector.py b/src/Tools/Semantic/Type_Collector.py similarity index 85% rename from src/Tools/Type_Collector.py rename to src/Tools/Semantic/Type_Collector.py index 5982c158d..ccaaf0671 100644 --- a/src/Tools/Type_Collector.py +++ b/src/Tools/Semantic/Type_Collector.py @@ -1,7 +1,7 @@ -from .Tools import visitor -from .Tools.Semantic import * -from .Parser import ProgramNode, ClassDeclarationNode -from .Tools.Errors import SemanticError +from Tools.Tools import visitor +from Tools.Tools.Semantic import * +from Tools.Parser.Parser import ProgramNode, ClassDeclarationNode +from Tools.Tools.Errors import SemanticError # Visitor encargado de coleccionar los nombres de las clases que se definen # en el codigo del programa COOL, chequea ademas que no se redeclaren e diff --git a/src/Tools/Tools/COOLAst.py b/src/Tools/Tools/COOLAst.py new file mode 100644 index 000000000..4efb93764 --- /dev/null +++ b/src/Tools/Tools/COOLAst.py @@ -0,0 +1,210 @@ +# Clases necesarias para representar el AST del programa COOL +class Node: + pass + +# Raiz del AST +class ProgramNode(Node): + def __init__(self, declarations): + self.declarations = declarations + self.line = declarations[0].line + self.column = declarations[0].column + + +class DeclarationNode(Node): + pass + + +class ClassDeclarationNode(DeclarationNode): + def __init__(self, classx, idx, features, parent=None): + self.id = idx + self.parent = parent + self.features = features + self.line = classx.line + self.column = classx.column + + +class AttrDeclarationNode(DeclarationNode): + def __init__(self, idx, typex, expression=None): + self.id = idx + self.type = typex + self.expression = expression + self.line = idx.line + self.column = idx.column + + +class FuncDeclarationNode(DeclarationNode): + def __init__(self, idx, params, return_type, body): + self.id = idx + self.params = params + self.type = return_type + self.body = body + self.line = idx.line + self.column = idx.column + + +class ExpressionNode(Node): + pass + + +class IfThenElseNode(ExpressionNode): + def __init__(self, ifx, condition, if_body, else_body): + self.condition = condition + self.if_body = if_body + self.else_body = else_body + self.line = ifx.line + self.column = ifx.column + + +class WhileLoopNode(ExpressionNode): + def __init__(self, whilex, condition, body): + self.condition = condition + self.body = body + self.line = whilex.line + self.column = whilex.column + + +class BlockNode(ExpressionNode): + def __init__(self, brace, expressions): + self.expressions = expressions + self.line = brace.line + self.column = brace.column + + +class LetInNode(ExpressionNode): + def __init__(self, let, let_body, in_body): + self.let_body = let_body + self.in_body = in_body + self.line = let.line + self.column = let.column + + +class CaseOfNode(ExpressionNode): + def __init__(self, case, expression, branches): + self.expression = expression + self.branches = branches + self.line = case.line + self.column = case.column + + +class AssignNode(ExpressionNode): + def __init__(self, idx, expression): + self.id = idx + self.expression = expression + self.line = idx.line + self.column = idx.column + + +class UnaryNode(ExpressionNode): + def __init__(self, expression): + self.expression = expression + self.line = expression.line + self.column = expression.column + + +class NotNode(UnaryNode): + def __init__(self, notx, expression): + super().__init__(expression) + self.line = notx.line + self.column = notx.column + + +class BinaryNode(ExpressionNode): + def __init__(self, left, operator, right): + self.left = left + self.right = right + self.line = operator.line + self.column = operator.column + + +class LessEqualNode(BinaryNode): + pass + + +class LessNode(BinaryNode): + pass + + +class EqualNode(BinaryNode): + pass + + +class ArithmeticNode(BinaryNode): + pass + + +class PlusNode(ArithmeticNode): + pass + + +class MinusNode(ArithmeticNode): + pass + + +class StarNode(ArithmeticNode): + pass + + +class DivNode(ArithmeticNode): + pass + + +class IsVoidNode(UnaryNode): + def __init__(self, isvoid, expression): + super().__init__(expression) + self.line = isvoid.line + self.column = isvoid.column + + +class ComplementNode(UnaryNode): + def __init__(self, complement, expression): + super().__init__(expression) + self.line = complement.line + self.column = complement.column + + +class FunctionCallNode(ExpressionNode): + def __init__(self, obj, idx, args, typex=None): + self.obj = obj + self.id = idx + self.args = args + self.type = typex + self.line = obj.line + self.column = obj.column + + +class MemberCallNode(ExpressionNode): + def __init__(self, idx, args): + self.id = idx + self.args = args + self.line = idx.line + self.column = idx.column + + +class NewNode(ExpressionNode): + def __init__(self, new, typex): + self.type = typex + self.line = new.line + self.column = new.column + + +class AtomicNode(ExpressionNode): + def __init__(self, token): + self.token = token + self.line = token.line + self.column = token.column + + +class IntegerNode(AtomicNode): + pass + + +class IdNode(AtomicNode): + pass + + +class StringNode(AtomicNode): + pass + + +class BoolNode(AtomicNode): + pass diff --git a/src/Tools/Tools/Semantic.py b/src/Tools/Tools/Semantic.py index 1553857e3..2384749cf 100644 --- a/src/Tools/Tools/Semantic.py +++ b/src/Tools/Tools/Semantic.py @@ -209,16 +209,9 @@ def __eq__(self, other): # Clase para representar una variable dentro del programa class VariableInfo: - def __init__(self, name, vtype, line, column): + def __init__(self, name, vtype): self.name = name self.type = vtype - self.line = line - self.column = column - - self.upper = [] - self.lower = [] - self.dependencies = [] - self.weakDependencies = [] # Clase para representar el contexto en el que se guardan las variables y los diff --git a/src/coolc.sh b/src/coolc.sh index fa9bb864a..73bc4cb0b 100755 --- a/src/coolc.sh +++ b/src/coolc.sh @@ -4,8 +4,8 @@ INPUT_FILE=$1 OUTPUT_FILE=${INPUT_FILE:0: -2}mips # Si su compilador no lo hace ya, aquí puede imprimir la información de contacto -echo "LINEA_CON_NOMBRE_Y_VERSION_DEL_COMPILADOR" # TODO: Recuerde cambiar estas -echo "Copyright (c) 2019: Nombre1, Nombre2, Nombre3" # TODO: líneas a los valores correctos +echo "CMA COOL COMPILER v1.0" # TODO: Recuerde cambiar estas +echo "Copyright (c) 2019: Marcos Valdivie, Claudia Olavarrieta, Adrian Hernandez" # TODO: líneas a los valores correctos # Llamar al compilador From a5bcd942065bef1b398f43fcbb3ccf1e7c607288 Mon Sep 17 00:00:00 2001 From: mavaldivie Date: Mon, 1 Mar 2021 18:37:30 -0500 Subject: [PATCH 04/75] Agregada la dependencia de Ply --- requirements.txt | 1 + 1 file changed, 1 insertion(+) diff --git a/requirements.txt b/requirements.txt index 9eb0cad1a..5a914fd88 100644 --- a/requirements.txt +++ b/requirements.txt @@ -1,2 +1,3 @@ pytest pytest-ordering +ply \ No newline at end of file From 268a87b92588eba5006026c04774ec4c48d2bed7 Mon Sep 17 00:00:00 2001 From: mavaldivie Date: Mon, 1 Mar 2021 20:05:12 -0500 Subject: [PATCH 05/75] Arreglados algunos errores en Semantic.py --- src/Tools/Tools/Semantic.py | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/src/Tools/Tools/Semantic.py b/src/Tools/Tools/Semantic.py index 2384749cf..82e93d47d 100644 --- a/src/Tools/Tools/Semantic.py +++ b/src/Tools/Tools/Semantic.py @@ -122,8 +122,7 @@ def define_method(self, name:str, param_names:list, param_types:list, return_typ raise SemanticException(f'Method {name} is multiply defined') method = self.methods[name] = Method(name, param_names, param_types, return_type) - method.return_info = VariableInfo(f'{self.name}_{name}_returnType', return_type, - line, column) + method.return_info = VariableInfo(f'{self.name}_{name}_returnType', return_type) return method @@ -262,7 +261,7 @@ def create_child(self): # Define una variable en el scope def define_variable(self, name : str, type : Type, line : int, column : int): - self.locals.append(VariableInfo(name, type, line, column)) + self.locals.append(VariableInfo(name, type)) return self.locals[-1] # Retorna una variable definida en el scope, None si no esta definida From e5ffd5adbdc5bca47db1657c800c3a081fe77945 Mon Sep 17 00:00:00 2001 From: mavaldivie Date: Mon, 26 Apr 2021 20:41:14 -0400 Subject: [PATCH 06/75] Arreglados algunos bugs e implementado el visitor de COOL para CIL --- src/Main.py | 15 +- src/Tools/CIL/BaseCOOLToCILVisitor.py | 293 ++++++++++++++ src/Tools/CIL/CILAst.py | 444 +++++++++++++++++++++ src/Tools/CIL/COOLToCILVisitor.py | 537 ++++++++++++++++++++++++++ src/Tools/Lexer/Lexer.py | 2 +- src/Tools/Tools/Semantic.py | 6 +- 6 files changed, 1291 insertions(+), 6 deletions(-) create mode 100644 src/Tools/CIL/BaseCOOLToCILVisitor.py create mode 100644 src/Tools/CIL/CILAst.py create mode 100644 src/Tools/CIL/COOLToCILVisitor.py diff --git a/src/Main.py b/src/Main.py index e78d26b3e..19c793fe7 100644 --- a/src/Main.py +++ b/src/Main.py @@ -4,6 +4,8 @@ from Tools.Semantic.Type_Collector import Type_Collector from Tools.Semantic.Type_Builder import Type_Builder from Tools.Semantic.Type_Checker import Type_Checker +from Tools.CIL.COOLToCILVisitor import COOLToCILVisitor +from Tools.CIL.CILAst import get_formatter def main(args): try: @@ -23,27 +25,32 @@ def main(args): for e in CoolParser.errors: print(e) if any(CoolParser.errors): exit(1) - ast = evaluate_reverse_parse(productions, operations, tokens) + COOLast = evaluate_reverse_parse(productions, operations, tokens) type_Collector = Type_Collector() - type_Collector.visit(ast) + type_Collector.visit(COOLast) for e in type_Collector.errors: print(e) if any(type_Collector.errors): exit(1) context = type_Collector.Context type_Builder = Type_Builder(context) - type_Builder.visit(ast) + type_Builder.visit(COOLast) for e in type_Builder.errors: print(e) if any(type_Builder.errors): exit(1) type_Checker = Type_Checker(context) - type_Checker.visit(ast) + scope = type_Checker.visit(COOLast) for e in type_Checker.errors: print(e) if any(type_Checker.errors): exit(1) + CILVisitor = COOLToCILVisitor(type_Checker.Context) + CILast = CILVisitor.visit(COOLast, scope) + print(get_formatter()(CILast)) + + if __name__ == "__main__": diff --git a/src/Tools/CIL/BaseCOOLToCILVisitor.py b/src/Tools/CIL/BaseCOOLToCILVisitor.py new file mode 100644 index 000000000..ca977f09e --- /dev/null +++ b/src/Tools/CIL/BaseCOOLToCILVisitor.py @@ -0,0 +1,293 @@ +from ..CIL import CILAst as cil +from ..Tools.Semantic import VariableInfo + + +class BaseCOOLToCILVisitor: + def __init__(self, context): + self.dottypes = [] + self.dotdata = [] + self.dotcode = [] + + self.current_type = None + self.current_method = None + self.current_function = None + + self.context = context + self.vself = VariableInfo('self', None) + self.value_types = ['String', 'Int', 'Bool'] + + self.var_names = {} + + self.breakline_data = self.register_data('\n') + self.emptystring_data = self.register_data('') + + @property + def params(self): + return self.current_function.params + + @property + def localvars(self): + return self.current_function.localvars + + @property + def ids(self): + return self.current_function.ids + + @property + def instructions(self): + return self.current_function.instructions + + def register_param(self, vinfo): + name = vinfo.name + vinfo.name = f'local_param_{self.current_function.name}_{name}_{len(self.params)}' + param_node = cil.ParamNode(vinfo.name) + self.params.append(param_node) + self.var_names[name] = vinfo.name + return vinfo.name + + def register_local(self, vinfo): + name = vinfo.name + vinfo.name = f'local_{self.current_function.name}_{vinfo.name}_{len(self.localvars)}' + local_node = cil.LocalNode(vinfo.name) + self.localvars.append(local_node) + self.var_names[name] = vinfo.name + return vinfo.name + + def define_internal_local(self): + vinfo = VariableInfo('internal', None) + return self.register_local(vinfo) + + def register_instruction(self, instruction): + self.instructions.append(instruction) + return instruction + + def to_function_name(self, method_name, type_name): + return f'function_{method_name}_at_{type_name}' + + def register_function(self, function_name): + function_node = cil.FunctionNode(function_name, [], [], []) + self.dotcode.append(function_node) + return function_node + + def register_type(self, name): + type_node = cil.TypeNode(name) + self.dottypes.append(type_node) + return type_node + + def register_data(self, value): + vname = f'data_{len(self.dotdata)}' + data_node = cil.DataNode(vname, value) + self.dotdata.append(data_node) + return data_node + + def register_label(self, label): + lname = f'{label}_{self.current_function.labels_count}' + self.current_function.labels_count += 1 + return cil.LabelNode(lname) + + def init_name(self, name): + return f'init_at_{name}' + + def init_attr_name(self, name): + return f'init_attr_at_{name}' + + def register_runtime_error(self, condition, msg): + error_node = self.register_label('error_label') + continue_node = self.register_label('continue_label') + self.register_instruction(cil.GotoIfNode(condition, error_node.label)) + self.register_instruction(cil.GotoNode(continue_node.label)) + self.register_instruction(error_node) + data_node = self.register_data(msg) + self.register_instruction(cil.ErrorNode(data_node)) + self.register_instruction(continue_node) + + def register_builtin(self): + # Object + type_node = self.register_type('Object') + + self.current_function = self.register_function(self.init_name('Object')) + instance = self.define_internal_local() + self.register_instruction(cil.AllocateNode('Object', instance)) + self.register_instruction(cil.ReturnNode(instance)) + + self.current_function = self.register_function(self.to_function_name('abort', 'Object')) + self.register_param(self.vself) + vname = self.define_internal_local() + abort_data = self.register_data('Abort called from class ') + self.register_instruction(cil.LoadNode(vname, abort_data)) + self.register_instruction(cil.PrintStringNode(vname)) + self.register_instruction(cil.TypeOfNode(vname, self.vself.name)) + self.register_instruction(cil.PrintStringNode(vname)) + self.register_instruction(cil.LoadNode(vname, self.breakline_data)) + self.register_instruction(cil.PrintStringNode(vname)) + self.register_instruction(cil.ExitNode()) + + self.current_function = self.register_function(self.to_function_name('type_name', 'Object')) + self.register_param(self.vself) + result = self.define_internal_local() + self.register_instruction(cil.TypeOfNode(result, self.vself.name)) + instance = self.define_internal_local() + self.register_instruction(cil.ArgNode(result)) + self.register_instruction(cil.StaticCallNode(self.init_name('String'), instance)) + self.register_instruction(cil.ReturnNode(instance)) + + self.current_function = self.register_function(self.to_function_name('copy', 'Object')) + self.register_param(self.vself) + result = self.define_internal_local() + self.register_instruction(cil.CopyNode(result, self.vself.name)) + self.register_instruction(cil.ReturnNode(result)) + + type_node.methods = [self.to_function_name(name, 'Object') for name in ['abort', 'type_name', 'copy']] + type_node.methods += [self.init_name('Object')] + obj_methods = ['abort', 'type_name', 'copy'] + + # IO + type_node = self.register_type('IO') + + self.current_function = self.register_function(self.init_name('IO')) + instance = self.define_internal_local() + self.register_instruction(cil.AllocateNode('IO', instance)) + self.register_instruction(cil.ReturnNode(instance)) + + self.current_function = self.register_function(self.to_function_name('out_string', 'IO')) + self.register_param(self.vself) + self.register_param(VariableInfo('x', None)) + vname = self.define_internal_local() + self.register_instruction(cil.GetAttribNode(vname, 'x', 'value', 'String')) + self.register_instruction(cil.PrintStringNode(vname)) + self.register_instruction(cil.ReturnNode(self.vself.name)) + + self.current_function = self.register_function(self.to_function_name('out_int', 'IO')) + self.register_param(self.vself) + self.register_param(VariableInfo('x', None)) + vname = self.define_internal_local() + self.register_instruction(cil.GetAttribNode(vname, 'x', 'value', 'Int')) + self.register_instruction(cil.PrintIntNode(vname)) + self.register_instruction(cil.ReturnNode(self.vself.name)) + + self.current_function = self.register_function(self.to_function_name('in_string', 'IO')) + self.register_param(self.vself) + result = self.define_internal_local() + self.register_instruction(cil.ReadNode(result)) + instance = self.define_internal_local() + self.register_instruction(cil.ArgNode(result)) + self.register_instruction(cil.StaticCallNode(self.init_name('String'), instance)) + self.register_instruction(cil.ReturnNode(instance)) + + self.current_function = self.register_function(self.to_function_name('in_int', 'IO')) + self.register_param(self.vself) + result = self.define_internal_local() + self.register_instruction(cil.ReadNode(result)) + instance = self.define_internal_local() + self.register_instruction(cil.ArgNode(result)) + self.register_instruction(cil.StaticCallNode(self.init_name('Int'), instance)) + self.register_instruction(cil.ReturnNode(instance)) + + type_node.methods = [self.to_function_name(method, 'Object') for method in obj_methods] + type_node.methods += [self.to_function_name(name, 'IO') for name in + ['out_string', 'out_int', 'in_string', 'in_int']] + type_node.methods += [self.init_name('IO')] + + # String + type_node = self.register_type('String') + type_node.attributes = ['value', 'length'] + + self.current_function = self.register_function(self.init_name('String')) + self.register_param(VariableInfo('val', None)) + instance = self.define_internal_local() + self.register_instruction(cil.AllocateNode('String', instance)) + self.register_instruction(cil.SetAttribNode(instance, 'value', 'val', 'String')) + result = self.define_internal_local() + self.register_instruction(cil.LengthNode(result, 'val')) + attr = self.define_internal_local() + self.register_instruction(cil.ArgNode(result)) + self.register_instruction(cil.StaticCallNode(self.init_name('Int'), attr)) + self.register_instruction(cil.SetAttribNode(instance, 'length', attr, 'String')) + self.register_instruction(cil.ReturnNode(instance)) + + self.current_function = self.register_function(self.to_function_name('length', 'String')) + self.register_param(self.vself) + result = self.define_internal_local() + self.register_instruction(cil.GetAttribNode(result, self.vself.name, 'length', 'String')) + self.register_instruction(cil.ReturnNode(result)) + + self.current_function = self.register_function(self.to_function_name('concat', 'String')) + self.register_param(self.vself) + self.register_param(VariableInfo('s', None)) + str_1 = self.define_internal_local() + str_2 = self.define_internal_local() + length_1 = self.define_internal_local() + length_2 = self.define_internal_local() + self.register_instruction(cil.GetAttribNode(str_1, self.vself.name, 'value', 'String')) + self.register_instruction(cil.GetAttribNode(str_2, 's', 'value', 'String')) + self.register_instruction(cil.GetAttribNode(length_1, self.vself.name, 'length', 'String')) + self.register_instruction(cil.GetAttribNode(length_2, 's', 'length', 'String')) + self.register_instruction(cil.GetAttribNode(length_1, length_1, 'value', 'Int')) + self.register_instruction(cil.GetAttribNode(length_2, length_2, 'value', 'Int')) + self.register_instruction(cil.PlusNode(length_1, length_1, length_2)) + + result = self.define_internal_local() + self.register_instruction(cil.ConcatNode(result, str_1, str_2, length_1)) + instance = self.define_internal_local() + self.register_instruction(cil.ArgNode(result)) + self.register_instruction(cil.StaticCallNode(self.init_name('String'), instance)) + self.register_instruction(cil.ReturnNode(instance)) + + self.current_function = self.register_function(self.to_function_name('substr', 'String')) + self.register_param(self.vself) + self.register_param(VariableInfo('i', None)) + self.register_param(VariableInfo('l', None)) + result = self.define_internal_local() + index_value = self.define_internal_local() + length_value = self.define_internal_local() + length_attr = self.define_internal_local() + length_substr = self.define_internal_local() + less_value = self.define_internal_local() + str_value = self.define_internal_local() + self.register_instruction(cil.GetAttribNode(str_value, self.vself.name, 'value', 'String')) + self.register_instruction(cil.GetAttribNode(index_value, 'i', 'value', 'Int')) + self.register_instruction(cil.GetAttribNode(length_value, 'l', 'value', 'Int')) + # Check Out of range error + self.register_instruction(cil.GetAttribNode(length_attr, self.vself.name, 'length', 'String')) + self.register_instruction(cil.PlusNode(length_substr, length_value, index_value)) + self.register_instruction(cil.LessNode(less_value, length_attr, length_substr)) + self.register_runtime_error(less_value, 'Substring out of range') + self.register_instruction(cil.SubstringNode(result, str_value, index_value, length_value)) + instance = self.define_internal_local() + self.register_instruction(cil.ArgNode(result)) + self.register_instruction(cil.StaticCallNode(self.init_name('String'), instance)) + self.register_instruction(cil.ReturnNode(instance)) + + type_node.methods = [self.to_function_name(method, 'Object') for method in obj_methods] + type_node.methods += [self.to_function_name(name, 'String') for name in ['length', 'concat', 'substr']] + type_node.methods += [self.init_name('String')] + + # Int + type_node = self.register_type('Int') + type_node.attributes = ['value'] + + self.current_function = self.register_function(self.init_name('Int')) + self.register_param(VariableInfo('val', None)) + instance = self.define_internal_local() + self.register_instruction(cil.AllocateNode('Int', instance)) + self.register_instruction(cil.SetAttribNode(instance, 'value', 'val', 'Int')) + self.register_instruction(cil.ReturnNode(instance)) + + type_node.methods = [self.to_function_name(method, 'Object') for method in obj_methods] + type_node.methods += [self.init_name('Int')] + + # Bool + type_node = self.register_type('Bool') + type_node.attributes = ['value'] + + self.current_function = self.register_function(self.init_name('Bool')) + self.register_param(VariableInfo('val', None)) + instance = self.define_internal_local() + self.register_instruction(cil.AllocateNode('Bool', instance)) + self.register_instruction(cil.SetAttribNode(instance, 'value', 'val', 'Bool')) + self.register_instruction(cil.ReturnNode(instance)) + + type_node.methods = [self.to_function_name(method, 'Object') for method in obj_methods] + type_node.methods += [self.init_name('Bool')] + + diff --git a/src/Tools/CIL/CILAst.py b/src/Tools/CIL/CILAst.py new file mode 100644 index 000000000..1e76865a7 --- /dev/null +++ b/src/Tools/CIL/CILAst.py @@ -0,0 +1,444 @@ +from ..Tools import visitor + +class Node: + pass + +class ProgramNode(Node): + def __init__(self, dottypes, dotdata, dotcode): + self.dottypes = dottypes + self.dotdata = dotdata + self.dotcode = dotcode + +class TypeNode(Node): + def __init__(self, name): + self.name = name + self.attributes = [] + self.methods = [] + +class DataNode(Node): + def __init__(self, vname, value): + self.name = vname + self.value = value + +class FunctionNode(Node): + def __init__(self, fname, params, localvars, instructions): + self.name = fname + self.params = params + self.localvars = localvars + self.instructions = instructions + self.labels_count = 0 + +class ParamNode(Node): + def __init__(self, name): + self.name = name + +class LocalNode(Node): + def __init__(self, name): + self.name = name + +class InstructionNode(Node): + pass + +class AssignNode(InstructionNode): + def __init__(self, dest, source): + self.dest = dest + self.source = source + +class ArithmeticNode(InstructionNode): + def __init__(self, dest, left, right): + self.dest = dest + self.left = left + self.right = right + +class PlusNode(ArithmeticNode): + pass + +class MinusNode(ArithmeticNode): + pass + +class StarNode(ArithmeticNode): + pass + +class DivNode(ArithmeticNode): + pass + +class LessNode(ArithmeticNode): + pass + +class EqualNode(ArithmeticNode): + pass + +class EqualStringNode(ArithmeticNode): + pass + +class LessEqualNode(ArithmeticNode): + pass + +class GetAttribNode(InstructionNode): + def __init__(self, dest, obj, attr, computed_type): + self.dest = dest + self.obj = obj + self.attr = attr + self.computed_type = computed_type + + def __repr__(self): + return f"{self.dest} = GETATTR {self.obj} {self.attr}" + + +class SetAttribNode(InstructionNode): + def __init__(self, obj, attr, value, computed_type): + self.obj = obj + self.attr = attr + self.value = value + self.computed_type = computed_type + +class GetIndexNode(InstructionNode): + pass + +class SetIndexNode(InstructionNode): + pass + +class AllocateNode(InstructionNode): + def __init__(self, itype, dest): + self.type = itype + self.dest = dest + +class TypeOfNode(InstructionNode): + def __init__(self, dest, obj): + self.obj = obj + self.dest = dest + + def __repr__(self): + return f"{self.dest} = TYPEOF {self.obj}" + + +class LabelNode(InstructionNode): + def __init__(self, label): + self.label = label + + def __repr__(self): + return f"LABEL {self.label}:" + + +class GotoNode(InstructionNode): + def __init__(self, label): + self.label = label + + def __repr__(self): + return f"GOTO {self.label}" + + +class GotoIfNode(InstructionNode): + def __init__(self, condition, label): + self.condition = condition + self.label = label + + def __repr__(self): + return f"GOTO {self.label} if {self.condition}" + +class StaticCallNode(InstructionNode): + def __init__(self, function, dest): + self.function = function + self.dest = dest + + def __repr__(self): + return f"{self.dest} = CALL {self.function}" + +class DynamicCallNode(InstructionNode): + def __init__(self, xtype, method, dest): + self.type = xtype + self.method = method + self.dest = dest + + def __repr__(self): + return f"{self.dest} = VCALL {self.type} {self.method}" + +class ArgNode(InstructionNode): + def __init__(self, name): + self.name = name + + def __repr__(self): + return f"ARG {self.name}" + + +class ReturnNode(InstructionNode): + def __init__(self, value=None): + self.value = value + + def __repr__(self): + return f"RETURN {self.value}" + + +class LoadNode(InstructionNode): + def __init__(self, dest, msg): + self.dest = dest + self.msg = msg + + def __repr__(self): + return f"{self.dest} LOAD {self.msg}" + +class LengthNode(InstructionNode): + def __init__(self, dest, source): + self.dest = dest + self.source = source + +class ConcatNode(InstructionNode): + def __init__(self, dest, prefix, suffix, length): + self.dest = dest + self.prefix = prefix + self.suffix = suffix + self.length = length + +class PrefixNode(InstructionNode): + pass + +class SubstringNode(InstructionNode): + def __init__(self, dest, str_value, index, length): + self.dest = dest + self.str_value = str_value + self.index = index + self.length = length + +class ToStrNode(InstructionNode): + def __init__(self, dest, value): + self.dest = dest + self.value = value + +class ReadNode(InstructionNode): + def __init__(self, dest): + self.dest = dest + +class PrintStringNode(InstructionNode): + def __init__(self, str_addr): + self.str_addr = str_addr + +class PrintIntNode(InstructionNode): + def __init__(self, value): + self.value = value + +class ExitNode(InstructionNode): + pass + +class CopyNode(InstructionNode): + def __init__(self, dest, value): + self.dest = dest + self.value = value + +class ErrorNode(InstructionNode): + def __init__(self, data): + self.data = data + +class VoidNode(InstructionNode): + pass + +class NameNode(InstructionNode): + def __init__(self, dest, value): + self.dest = dest + self.value = value + +class NotNode(InstructionNode): + def __init__(self, dest, value): + self.dest = dest + self.value = value + +class ComplementNode(InstructionNode): + def __init__(self, dest, value): + self.dest = dest + self.value = value + +def get_formatter(): + + class PrintVisitor(object): + @visitor.on('node') + def visit(self, node): + pass + + @visitor.when(ProgramNode) + def visit(self, node): + dottypes = '\n'.join(self.visit(t) for t in node.dottypes) + dotdata = '\n'.join(self.visit(t) for t in node.dotdata) + dotcode = '\n'.join(self.visit(t) for t in node.dotcode) + + return f'.TYPES\n{dottypes}\n\n.DATA\n{dotdata}\n\n.CODE\n{dotcode}' + + @visitor.when(TypeNode) + def visit(self, node): + attributes = '\n\t'.join(f'attribute {x}' for x in node.attributes) + methods = '\n\t'.join(f'method {x}' for x in node.methods) + + return f'type {node.name} {{\n\t{attributes}\n\t{methods}\n}}' + + @visitor.when(DataNode) + def visit(self, node): + return f'DATA "{node.value}"' + + @visitor.when(FunctionNode) + def visit(self, node): + params = '\n\t'.join(self.visit(x) for x in node.params) + localvars = '\n\t'.join(self.visit(x) for x in node.localvars) + instructions = '\n\t'.join(self.visit(x) for x in node.instructions) + + return f'function {node.name} {{\n\t{params}\n\t{localvars}\n\n\t{instructions}\n}}' + + @visitor.when(ParamNode) + def visit(self, node): + return f'PARAM {node.name}' + + @visitor.when(LocalNode) + def visit(self, node): + return f'LOCAL {node.name}' + + @visitor.when(AssignNode) + def visit(self, node): + return f'{node.dest} = {node.source}' + + @visitor.when(PlusNode) + def visit(self, node): + return f'{node.dest} = {node.left} + {node.right}' + + @visitor.when(MinusNode) + def visit(self, node): + return f'{node.dest} = {node.left} - {node.right}' + + @visitor.when(StarNode) + def visit(self, node): + return f'{node.dest} = {node.left} * {node.right}' + + @visitor.when(DivNode) + def visit(self, node): + return f'{node.dest} = {node.left} / {node.right}' + + @visitor.when(AllocateNode) + def visit(self, node): + return f'{node.dest} = ALLOCATE {node.type}' + + @visitor.when(TypeOfNode) + def visit(self, node): + return f'{node.dest} = TYPEOF {node.obj}' + + @visitor.when(StaticCallNode) + def visit(self, node): + return f'{node.dest} = CALL {node.function}' + + @visitor.when(DynamicCallNode) + def visit(self, node): + return f'{node.dest} = VCALL {node.type} {node.method.lex}' + + @visitor.when(ArgNode) + def visit(self, node): + return f'ARG {node.name}' + + @visitor.when(ReturnNode) + def visit(self, node): + return f'RETURN {node.value if node.value is not None else ""}' + + @visitor.when(LoadNode) + def visit(self, node): + return f'{node.dest} = LOAD {self.visit(node.msg)}' + + @visitor.when(PrintStringNode) + def visit(self, node: PrintStringNode): + return f'PRINTSTRING {node.str_addr}' + + @visitor.when(PrintIntNode) + def visit(self, node: PrintIntNode): + return f'PRINTINT {node.value}' + + @visitor.when(ExitNode) + def visit(self, node: ExitNode): + return f'EXIT' + + @visitor.when(CopyNode) + def visit(self, node): + return f'{node.dest} = COPY {node.value}' + + @visitor.when(GetAttribNode) + def visit(self, node: GetAttribNode): + return f'{node.dest} = GETATTRIB {node.obj}.{node.attr} {node.computed_type}' + + @visitor.when(ErrorNode) + def visit(self, node: ErrorNode): + return f'ERROR {self.visit(node.data)}' + + @visitor.when(ReadNode) + def visit(self, node: ReadNode): + return f'{node.dest} = READ' + + @visitor.when(SetAttribNode) + def visit(self, node: SetAttribNode): + return f'SETATTR {node.obj}.{node.attr}: {node.computed_type} = {node.value}' + + @visitor.when(LessNode) + def visit(self, node: LessNode): + return f'{node.dest} = {node.left} < {node.right}' + + @visitor.when(GotoIfNode) + def visit(self, node: GotoIfNode): + return f'GOTOIF {node.condition} {node.label}' + + @visitor.when(GotoNode) + def visit(self, node: GotoNode): + return f'GOTO {node.label}' + + @visitor.when(LabelNode) + def visit(self, node: LabelNode): + return f'LABEL {node.label}' + + @visitor.when(SubstringNode) + def visit(self, node: SubstringNode): + return f'{node.dest} = SUBSTRING {node.str_value}[{node.index}:{node.index + node.length}]' + + @visitor.when(ConcatNode) + def visit(self, node: ConcatNode): + return f'{node.dest} = CONCAT {node.prefix} + {node.suffix}' + + @visitor.when(LengthNode) + def visit(self, node: LengthNode): + return f'{node.dest} = {node.source}.lenght' + + @visitor.when(EqualNode) + def visit(self, node: EqualNode): + return f'{node.dest} = {node.left} == {node.right}' + + @visitor.when(NameNode) + def visit(self, node: NameNode): + return f'{node.dest} = NAME {node.value}' + + @visitor.when(EqualStringNode) + def visit(self, node: EqualStringNode): + return f'{node.dest} = {node.left} == {node.right}' + + @visitor.when(ComplementNode) + def visit(self, node: ComplementNode): + return f'{node.dest} = ~{node.value}' + + @visitor.when(LessEqualNode) + def visit(self, node: LessEqualNode): + return f'{node.dest} = {node.left} <= {node.right}' + + @visitor.when(GetIndexNode) + def visit(self, node: GetIndexNode): + return f'GETINDEXNODE' + + @visitor.when(SetIndexNode) + def visit(self, node: SetIndexNode): + return f'SETINDEXNODE' + + @visitor.when(PrefixNode) + def visit(self, node: PrefixNode): + return f'PREFFIXNODE' + + @visitor.when(ToStrNode) + def visit(self, node: ToStrNode): + return f'{node.dest} = str({node.value})' + + @visitor.when(VoidNode) + def visit(self, node: VoidNode): + return 'VOID' + + @visitor.when(NotNode) + def visit(self, node: NotNode): + return f'{node.dest} = NOT {node.value}' + + printer = PrintVisitor() + return (lambda ast: printer.visit(ast)) \ No newline at end of file diff --git a/src/Tools/CIL/COOLToCILVisitor.py b/src/Tools/CIL/COOLToCILVisitor.py new file mode 100644 index 000000000..a123868d2 --- /dev/null +++ b/src/Tools/CIL/COOLToCILVisitor.py @@ -0,0 +1,537 @@ +import Tools.Tools.COOLAst as cool +from .BaseCOOLToCILVisitor import * +from Tools.Tools import visitor +from ..Tools.Semantic import Scope + +class COOLToCILVisitor(BaseCOOLToCILVisitor): + def __init__(self, context): + super().__init__(context) + + @visitor.on('node') + def visit(self, node): + pass + + @visitor.when(cool.ProgramNode) + def visit(self, node : cool.ProgramNode, scope : Scope): + self.current_function = self.register_function('entry') + return_value = self.define_internal_local() + main_instance = self.define_internal_local() + self.register_instruction(cil.StaticCallNode(self.init_name('Main'), main_instance)) + self.register_instruction(cil.ArgNode(main_instance)) + self.register_instruction(cil.StaticCallNode(self.to_function_name('main', 'Main'), return_value)) + self.register_instruction(cil.ReturnNode(0)) + + #self.register_builtin() + self.current_function = None + + for x, y in zip(node.declarations, scope.childs): + self.visit(x, y) + + return cil.ProgramNode(self.dottypes, self.dotdata, self.dotcode) + + + @visitor.when(cool.ClassDeclarationNode) + def visit(self, node : cool.ClassDeclarationNode, scope : Scope): + self.current_type = self.context.get_type(node.id.lex) + + # Inicializando los atributos de la clase y llamando al constructor del padre + self.current_function = self.register_function(self.init_attr_name(node.id.lex)) + if self.current_type.parent.name not in ('Object', 'IO'): + # TODO: Este self deberia ser el que paso por parametro? + variable = self.define_internal_local() + self.register_instruction(cil.ArgNode(self.vself.name)) + self.register_instruction(cil.StaticCallNode( + self.init_attr_name(self.current_type.parent.name), variable)) + for feat, child in zip(node.features, scope.childs): + if isinstance(feat, cool.AttrDeclarationNode): + self.visit(feat, child) + # TODO: Deberia retornar algo aqui? + + type = self.register_type(node.id.lex) + type.attributes = [i.name for i in self.current_type.attributes] + iter_type = self.current_type + while iter_type is not None: + type.methods += [self.to_function_name(i, iter_type.name) for i in iter_type.methods.keys()] + iter_type = iter_type.parent + type.methods.reverse() + + for feat, child in zip(node.features, scope.childs): + if isinstance(feat, cool.FuncDeclarationNode): + self.visit(feat, child) + + # TODO: Que hacer si la clase ya tiene definido el metodo init? + self.current_function = self.register_function(self.init_name(node.id.lex)) + instance = self.define_internal_local() + self.register_instruction(cil.AllocateNode(node.id.lex, instance)) + + variable = self.define_internal_local() + self.register_instruction(cil.ArgNode(instance)) + self.register_instruction(cil.StaticCallNode(self.init_attr_name(node.id.lex), variable)) + self.register_instruction(cil.ReturnNode(instance)) + + self.current_function = None + self.current_type = None + + @visitor.when(cool.AttrDeclarationNode) + def visit(self, node : cool.AttrDeclarationNode, scope : Scope): + if node.expression: + self.visit(node.expression, scope.childs[0]) + self.register_instruction(cil.SetAttribNode( + self.vself.name, node.id, node.expression.ret_expr, self.current_type)) + elif node.type.lex in self.value_types: + variable = self.define_internal_local() + self.register_instruction(cil.AllocateNode(node.type.lex, variable)) + self.register_instruction(cil.SetAttribNode( + self.vself.name, node.id, variable, self.current_type)) + variable = self.register_local(VariableInfo(node.id.lex, node.type.lex)) + node.ret_expr = variable + + @visitor.when(cool.FuncDeclarationNode) + def visit(self, node : cool.FuncDeclarationNode, scope : Scope): + self.current_method = self.current_type.get_method(node.id.lex) + self.current_function = self.register_function( + self.to_function_name(self.current_method.name, self.current_type.name)) + + self.register_param(self.vself) + for param, type in node.params: + self.register_param(VariableInfo(param.lex, type.lex)) + + self.visit(node.body, scope.childs[0]) + self.register_instruction(cil.ReturnNode(node.body.ret_expr)) + self.current_method = None + + @visitor.when(cool.IfThenElseNode) + def visit(self, node : cool.IfThenElseNode, scope : Scope): + ret = self.define_internal_local() + condition = self.define_internal_local() + + then_label = self.register_label('then_label') + continue_label = self.register_label('continue_label') + + self.visit(node.condition, scope.childs[0]) + self.register_instruction(cil.GetAttribNode(condition, node.condition.ret_expr, 'value', 'Bool')) + self.register_instruction(cil.GotoIfNode(condition, then_label.label)) + + self.visit(node.else_body, scope.childs[2]) + self.register_instruction(cil.AssignNode(ret, node.else_body.ret_expr)) + self.register_instruction(cil.GotoNode(continue_label.label)) + + self.register_instruction(then_label) + self.visit(node.if_body, scope.childs[1]) + self.register_instruction(cil.AssignNode(ret, node.if_body.ret_expr)) + + self.register_instruction(continue_label) + node.ret_expr = ret + + @visitor.when(cool.WhileLoopNode) + def visit(self, node : cool.WhileLoopNode, scope : Scope): + while_label = self.register_label('while_label') + loop_label = self.register_label('loop_label') + pool_label = self.register_label('pool_label') + condition = self.define_internal_local() + + self.register_instruction(while_label) + self.visit(node.condition, scope.childs[0]) + self.register_instruction(cil.GetAttribNode(condition, node.condition.ret_expr, 'value', 'Bool')) + self.register_instruction(cil.GotoIfNode(condition, loop_label.label)) + self.register_instruction(cil.GotoNode(pool_label.label)) + + self.register_instruction(loop_label) + self.visit(node.body, scope.childs[1]) + self.register_instruction(cil.GotoNode(while_label.label)) + + self.register_instruction(pool_label) + # TODO: No estoy seguro de si deberia retornar el nodo directamente o guardarlo antes en una variable + node.ret_expr = cil.VoidNode() + + @visitor.when(cool.BlockNode) + def visit(self, node : cool.BlockNode, scope : Scope): + for expr, child in zip(node.expressions, scope.childs): + self.visit(expr, child) + node.ret_expr = node.expressions[-1].ret_expr + + @visitor.when(cool.LetInNode) + def visit(self, node : cool.LetInNode, scope : Scope): + for (id, type, expr), child in zip(node.let_body, scope.childs[:-1]): + variable = self.register_local(VariableInfo(id.lex, type.lex)) + if expr: + self.visit(expr, child) + self.register_instruction(cil.AssignNode(variable, expr.ret_expr)) + elif type.lex in self.value_types: + self.register_instruction(cil.AllocateNode(type.lex, variable)) + + self.visit(node.in_body, scope.childs[-1]) + node.ret_expr = node.in_body.ret_expr + + + @visitor.when(cool.CaseOfNode) + def visit(self, node : cool.CaseOfNode, scope : Scope): + ret = self.define_internal_local() + vtype = self.define_internal_local() + cond = self.define_internal_local() + + self.visit(node.expression, scope.childs[0]) + self.register_instruction(cil.TypeOfNode(vtype, node.expression.ret_expr)) + + void = cil.VoidNode() + isvoid = self.define_internal_local() + self.register_instruction(cil.EqualNode(isvoid, node.expression.ret_expr, void)) + self.register_runtime_error(isvoid, f'{node.expression.line, node.expression.column} - ' + f'RuntimeError: Case on void') + + end_label = self.register_label('case_end_label') + + branch_type = self.define_internal_local() + seen = [] + labels = [] + branches = sorted(node.branches, key=lambda x: self.context.get_type(x[1].lex).depth, reverse=True) + for p, (id, type, expr) in enumerate(branches): + labels.append(self.register_label(f'case_label_{p}')) + + for t in self.context.subtree(type.lex): + if not t in seen: + seen.append(t) + self.register_instruction(cil.NameNode(branch_type, t.name)) + self.register_instruction(cil.EqualNode(cond, branch_type, vtype)) + self.register_instruction(cil.GotoIfNode(cond, labels[-1].label)) + + data = self.register_data(f'{node.expression.line, node.expression.column} - ' + f'RuntimeError: Case statement without a match branch') + self.register_instruction(cil.ErrorNode(data)) + + for p, label in enumerate(labels): + id, type, expr = branches[p] + sc = scope.childs[p + 1] + + self.register_instruction(label) + var = self.register_local(VariableInfo(id.lex, vtype)) + self.register_instruction(cil.AssignNode(var, node.expression.ret_expr)) + self.visit(expr, sc) + self.register_instruction(cil.AssignNode(ret, expr.ret_expr)) + self.register_instruction(cil.GotoNode(end_label.label)) + + self.register_instruction(end_label) + node.ret_expr = ret + + + @visitor.when(cool.AssignNode) + def visit(self, node: cool.AssignNode, scope: Scope): + var = self.var_names[node.id.lex] + self.visit(node.expression, scope.childs[0]) + self.register_instruction(cil.AssignNode(var, node.expression.ret_expr)) + node.ret_expr = var + + @visitor.when(cool.NotNode) + def visit(self, node: cool.NotNode, scope: Scope): + ret = self.define_internal_local() + value = self.define_internal_local() + neg_value = self.define_internal_local() + + self.visit(node.expression, scope.childs[0]) + self.register_instruction(cil.GetAttribNode(value, node.expression.ret_expr, 'value', 'Bool')) + self.register_instruction(cil.NotNode(neg_value, value)) + self.register_instruction(cil.ArgNode(neg_value)) + self.register_instruction(cil.StaticCallNode(self.init_name('Bool'), ret)) + + node.ret_expr = ret + + + @visitor.when(cool.LessEqualNode) + def visit(self, node: cool.LessEqualNode, scope: Scope): + ret = self.define_internal_local() + left = self.define_internal_local() + right = self.define_internal_local() + value = self.define_internal_local() + + self.visit(node.left, scope.childs[0]) + self.register_instruction(cil.GetAttribNode(left, node.left.ret_expr, 'value', 'Int')) + self.visit(node.right, scope.childs[1]) + self.register_instruction(cil.GetAttribNode(right, node.right.ret_expr, 'value', 'Int')) + self.register_instruction(cil.LessEqualNode(value, left, right)) + + self.register_instruction(cil.ArgNode(value)) + self.register_instruction(cil.StaticCallNode(self.init_name('Bool'), ret)) + + node.ret_expr = ret + + @visitor.when(cool.LessNode) + def visit(self, node: cool.LessNode, scope: Scope): + ret = self.define_internal_local() + left = self.define_internal_local() + right = self.define_internal_local() + value = self.define_internal_local() + + self.visit(node.left, scope.childs[0]) + self.register_instruction(cil.GetAttribNode(left, node.left.ret_expr, 'value', 'Int')) + self.visit(node.right, scope.childs[1]) + self.register_instruction(cil.GetAttribNode(right, node.right.ret_expr, 'value', 'Int')) + self.register_instruction(cil.LessNode(value, left, right)) + + self.register_instruction(cil.ArgNode(value)) + self.register_instruction(cil.StaticCallNode(self.init_name('Bool'), ret)) + + node.ret_expr = ret + + @visitor.when(cool.PlusNode) + def visit(self, node: cool.PlusNode, scope: Scope): + ret = self.define_internal_local() + left = self.define_internal_local() + right = self.define_internal_local() + value = self.define_internal_local() + + self.visit(node.left, scope.childs[0]) + self.register_instruction(cil.GetAttribNode(left, node.left.ret_expr, 'value', 'Int')) + self.visit(node.right, scope.childs[1]) + self.register_instruction(cil.GetAttribNode(right, node.right.ret_expr, 'value', 'Int')) + self.register_instruction(cil.PlusNode(value, left, right)) + + self.register_instruction(cil.ArgNode(value)) + self.register_instruction(cil.StaticCallNode(self.init_name('Int'), ret)) + + node.ret_expr = ret + + @visitor.when(cool.MinusNode) + def visit(self, node: cool.MinusNode, scope: Scope): + ret = self.define_internal_local() + left = self.define_internal_local() + right = self.define_internal_local() + value = self.define_internal_local() + + self.visit(node.left, scope.childs[0]) + self.register_instruction(cil.GetAttribNode(left, node.left.ret_expr, 'value', 'Int')) + self.visit(node.right, scope.childs[1]) + self.register_instruction(cil.GetAttribNode(right, node.right.ret_expr, 'value', 'Int')) + self.register_instruction(cil.MinusNode(value, left, right)) + + self.register_instruction(cil.ArgNode(value)) + self.register_instruction(cil.StaticCallNode(self.init_name('Int'), ret)) + + node.ret_expr = ret + + @visitor.when(cool.StarNode) + def visit(self, node: cool.StarNode, scope: Scope): + ret = self.define_internal_local() + left = self.define_internal_local() + right = self.define_internal_local() + value = self.define_internal_local() + + self.visit(node.left, scope.childs[0]) + self.register_instruction(cil.GetAttribNode(left, node.left.ret_expr, 'value', 'Int')) + self.visit(node.right, scope.childs[1]) + self.register_instruction(cil.GetAttribNode(right, node.right.ret_expr, 'value', 'Int')) + self.register_instruction(cil.StarNode(value, left, right)) + + self.register_instruction(cil.ArgNode(value)) + self.register_instruction(cil.StaticCallNode(self.init_name('Int'), ret)) + node.ret_expr = ret + + @visitor.when(cool.DivNode) + def visit(self, node: cool.DivNode, scope: Scope): + ret = self.define_internal_local() + left = self.define_internal_local() + right = self.define_internal_local() + value = self.define_internal_local() + + self.visit(node.left, scope.childs[0]) + self.register_instruction(cil.GetAttribNode(left, node.left.ret_expr, 'value', 'Int')) + self.visit(node.right, scope.childs[1]) + self.register_instruction(cil.GetAttribNode(right, node.right.ret_expr, 'value', 'Int')) + self.register_instruction(cil.DivNode(value, left, right)) + + self.register_instruction(cil.ArgNode(value)) + self.register_instruction(cil.StaticCallNode(self.init_name('Int'), ret)) + node.ret_expr = ret + + + @visitor.when(cool.IsVoidNode) + def visit(self, node: cool.IsVoidNode, scope: Scope): + ret = self.define_internal_local() + value = self.define_internal_local() + answer = self.define_internal_local() + + void = cil.VoidNode() + self.visit(node.expression, scope.childs[0]) + self.register_instruction(cil.AssignNode(value, node.expression.ret_expr)) + self.register_instruction(cil.EqualNode(answer, value, void)) + + self.register_instruction(cil.ArgNode(answer)) + self.register_instruction(cil.StaticCallNode(self.init_name('Bool'), ret)) + node.ret_expr = ret + + @visitor.when(cool.ComplementNode) + def visit(self, node: cool.ComplementNode, scope: Scope): + ret = self.define_internal_local() + value = self.define_internal_local() + answer = self.define_internal_local() + + self.visit(node.expression, scope.childs[0]) + self.register_instruction(cil.GetAttribNode(value, node.expression.ret_expr, 'value', 'Int')) + self.register_instruction(cil.ComplementNode(answer, value)) + + self.register_instruction(cil.ArgNode(answer)) + self.register_instruction(cil.StaticCallNode(self.init_name('Int'), ret)) + node.ret_expr = ret + + @visitor.when(cool.EqualNode) + def visit(self, node: cool.EqualNode, scope: Scope): + ret = self.define_internal_local() + left = self.define_internal_local() + right = self.define_internal_local() + type_left = self.define_internal_local() + type_int = self.define_internal_local() + type_string = self.define_internal_local() + type_bool = self.define_internal_local() + type = self.define_internal_local() + equal = self.define_internal_local() + value = self.define_internal_local() + + int_comparisson = self.register_label('int_comparisson') + string_comparisson = self.register_label('string_comparisson') + bool_comparisson = self.register_label('bool_comparisson') + continue_label = self.register_label('continue_label') + + self.visit(node.left, scope.childs[0]) + self.visit(node.right, scope.childs[1]) + + self.register_instruction(cil.TypeOfNode(type_left, node.left.ret_expr)) + self.register_instruction(cil.NameNode(type_int, 'Int')) + self.register_instruction(cil.NameNode(type_string, 'String')) + self.register_instruction(cil.NameNode(type_bool, 'Bool')) + + self.register_instruction(cil.EqualNode(equal, type_left, type_int)) + self.register_instruction(cil.GotoIfNode(equal, int_comparisson.label)) + self.register_instruction(cil.EqualNode(equal, type_left, type_string)) + self.register_instruction(cil.GotoIfNode(equal, string_comparisson.label)) + self.register_instruction(cil.EqualNode(equal, type_left, type_bool)) + self.register_instruction(cil.GotoIfNode(equal, bool_comparisson.label)) + + self.register_instruction(cil.EqualNode(value, node.left.ret_expr, node.right.ret_expr)) + self.register_instruction(cil.GotoNode(continue_label)) + + self.register_instruction(int_comparisson) + self.register_instruction(cil.GetAttribNode(left, node.left.ret_expr, 'value', 'Int')) + self.register_instruction(cil.GetAttribNode(right, node.right.ret_expr, 'value', 'Int')) + self.register_instruction(cil.EqualNode(value, left, right)) + self.register_instruction(cil.GotoNode(continue_label)) + + self.register_instruction(string_comparisson) + self.register_instruction(cil.GetAttribNode(left, node.left.ret_expr, 'value', 'String')) + self.register_instruction(cil.GetAttribNode(right, node.right.ret_expr, 'value', 'String')) + self.register_instruction(cil.EqualStringNode(value, left, right)) + self.register_instruction(cil.GotoNode(continue_label)) + + self.register_instruction(bool_comparisson) + self.register_instruction(cil.GetAttribNode(left, node.left.ret_expr, 'value', 'Bool')) + self.register_instruction(cil.GetAttribNode(right, node.right.ret_expr, 'value', 'Bool')) + self.register_instruction(cil.EqualNode(value, left, right)) + self.register_instruction(cil.GotoNode(continue_label)) + + self.register_instruction(continue_label) + + self.register_instruction(cil.ArgNode(value)) + self.register_instruction(cil.StaticCallNode(self.init_name('Bool'), ret)) + node.ret_expr = ret + + + @visitor.when(cool.FunctionCallNode) + def visit(self, node: cool.FunctionCallNode, scope: Scope): + args = [] + for arg, child in zip(node.args, scope.childs[1:]): + self.visit(arg, child) + args.append(cil.ArgNode(arg.ret_expr)) + + self.visit(node.obj, scope.childs[0]) + + void = cil.VoidNode() + isvoid = self.define_internal_local() + self.register_instruction(cil.EqualNode(isvoid, node.obj.ret_expr, void)) + self.register_runtime_error(isvoid, f'{node.id.line, node.id.column} - RuntimeError: Dispatch on void') + + self.register_instruction(cil.ArgNode(node.obj.ret_expr)) + for arg in args: self.register_instruction(arg) + + ret = self.define_internal_local() + if node.type: + self.register_instruction(cil.StaticCallNode(self.to_function_name(node.id, node.type.name), ret)) + else: + type = self.define_internal_local() + self.register_instruction(cil.TypeOfNode(type, node.obj.ret_expr)) + self.register_instruction(cil.DynamicCallNode(type, node.id, ret)) + node.ret_expr = ret + + @visitor.when(cool.MemberCallNode) + def visit(self, node: cool.MemberCallNode, scope: Scope): + ret = self.define_internal_local() + type = self.define_internal_local() + self.register_instruction(cil.TypeOfNode(type, self.vself.name)) + + args = [] + for arg, child in zip(node.args, scope.childs): + self.visit(arg, child) + args.append(cil.ArgNode(arg.ret_expr)) + + self.register_instruction(cil.ArgNode(self.vself.name)) + for arg in args: self.register_instruction(arg) + + self.register_instruction(cil.DynamicCallNode(type, node.id, ret)) + node.ret_expr = ret + + @visitor.when(cool.NewNode) + def visit(self, node: cool.NewNode, scope: Scope): + ret = self.define_internal_local() + + if node.type == 'SELF_TYPE': + variable = self.define_internal_local() + self.register_instruction(cil.TypeOfNode(ret, self.vself.name)) + self.register_instruction(cil.AllocateNode(variable, ret)) + else: + if node.type == 'Int': + self.register_instruction(cil.ArgNode(0)) + elif node.type == 'Bool': + self.register_instruction(cil.ArgNode(False)) + elif node.type == 'String': + data = self.emptystring_data + variable = self.define_internal_local() + self.register_instruction(cil.LoadNode(variable, data)) + self.register_instruction(cil.ArgNode(variable)) + + self.register_instruction(cil.StaticCallNode(self.init_name(node.type.lex), ret)) + node.ret_expr = ret + + @visitor.when(cool.IdNode) + def visit(self, node: cool.IdNode, scope: Scope): + if node.token.lex == 'self': node.ret_expr = self.vself.name + else: node.ret_expr = self.var_names[node.token.lex] + + @visitor.when(cool.StringNode) + def visit(self, node: cool.StringNode, scope: Scope): + try: + data = [i for i in self.dotdata if i.value == node.token.lex][0] + except IndexError: + data = self.register_data(node.token.lex) + + variable = self.define_internal_local() + ret = self.define_internal_local() + + self.register_instruction(cil.LoadNode(variable, data)) + self.register_instruction(cil.ArgNode(variable)) + self.register_instruction(cil.StaticCallNode(self.init_name('String'), ret)) + node.ret_expr = ret + + @visitor.when(cool.IntegerNode) + def visit(self, node: cool.IntegerNode, scope: Scope): + ret = self.define_internal_local() + + self.register_instruction(cil.ArgNode(node.token.lex)) + self.register_instruction(cil.StaticCallNode(self.init_name('Int'), ret)) + node.ret_expr = ret + + @visitor.when(cool.BoolNode) + def visit(self, node: cool.BoolNode, scope: Scope): + ret = self.define_internal_local() + + self.register_instruction(cil.ArgNode(node.token.lex)) + self.register_instruction(cil.StaticCallNode(self.init_name('Bool'), ret)) + node.ret_expr = ret + + diff --git a/src/Tools/Lexer/Lexer.py b/src/Tools/Lexer/Lexer.py index dcd995edb..04b433961 100644 --- a/src/Tools/Lexer/Lexer.py +++ b/src/Tools/Lexer/Lexer.py @@ -211,7 +211,7 @@ def t_string_SCAPED_SPECIAL_CHARACTER(self, t): def t_string_SCAPED_CHARACTER(self, t): r'\\.' - self.current_string += t.value[1] + self.current_string += '\\' + t.value[1] def t_string_eof(self, t): self.errors.append(LexicographicError(t.lineno, diff --git a/src/Tools/Tools/Semantic.py b/src/Tools/Tools/Semantic.py index 82e93d47d..d531f2d57 100644 --- a/src/Tools/Tools/Semantic.py +++ b/src/Tools/Tools/Semantic.py @@ -240,6 +240,10 @@ def get_type(self, name : str): except KeyError: raise SemanticException(f'Type {name} not defined.') + def subtree(self, name : str): + type = self.get_type(name) + return (i for i in self.types.values() if i.conforms_to(type)) + def __str__(self): return '{\n\t' + '\n\t'.join(y for x in self.types.values() for y in str(x).split('\n')) + '\n}' @@ -252,7 +256,7 @@ class Scope: def __init__(self, parent = None): self.parent = parent self.locals = [] - self.childs = [] + self.childs : [Scope] = [] # Crea un scope hijo def create_child(self): From 7f244d2047b9a05db892c2c9935b79e35bef9e3f Mon Sep 17 00:00:00 2001 From: mavaldivie Date: Sun, 22 Aug 2021 12:53:03 -0400 Subject: [PATCH 07/75] Arreglados algunos errores en CoolToCilVisitor.py --- src/Main.py | 16 +++---- .../CIL => core/cil}/BaseCOOLToCILVisitor.py | 4 +- src/{Tools/CIL => core/cil}/CILAst.py | 4 +- .../CIL => core/cil}/COOLToCILVisitor.py | 47 +++++++++++-------- src/{Tools => core}/format_visitor.py | 2 +- src/{Tools/Lexer => core/lexer}/Lexer.py | 6 +-- src/core/mips/CilToMipsVisitor.py | 0 src/core/mips/MipsAst.py | 0 src/{Tools/Parser => core/parser}/Parser.py | 6 +-- .../semantic}/Type_Builder.py | 10 ++-- .../semantic}/Type_Checker.py | 8 ++-- .../semantic}/Type_Collector.py | 8 ++-- src/{Tools/Tools => core/tools}/COOLAst.py | 0 src/{Tools/Tools => core/tools}/Errors.py | 0 .../tools}/Firsts_and_Follows.py | 0 src/{Tools/Tools => core/tools}/Parser_LR1.py | 0 src/{Tools/Tools => core/tools}/Semantic.py | 0 src/{Tools/Tools => core/tools}/automata.py | 0 src/{Tools/Tools => core/tools}/evaluation.py | 0 src/{Tools/Tools => core/tools}/parsing.py | 0 src/{Tools/Tools => core/tools}/pycompiler.py | 0 src/{Tools/Tools => core/tools}/utils.py | 0 src/{Tools/Tools => core/tools}/visitor.py | 0 tests/utils/utils.py | 2 +- 24 files changed, 60 insertions(+), 53 deletions(-) rename src/{Tools/CIL => core/cil}/BaseCOOLToCILVisitor.py (99%) rename src/{Tools/CIL => core/cil}/CILAst.py (99%) rename src/{Tools/CIL => core/cil}/COOLToCILVisitor.py (93%) rename src/{Tools => core}/format_visitor.py (99%) rename src/{Tools/Lexer => core/lexer}/Lexer.py (98%) create mode 100644 src/core/mips/CilToMipsVisitor.py create mode 100644 src/core/mips/MipsAst.py rename src/{Tools/Parser => core/parser}/Parser.py (98%) rename src/{Tools/Semantic => core/semantic}/Type_Builder.py (95%) rename src/{Tools/Semantic => core/semantic}/Type_Checker.py (98%) rename src/{Tools/Semantic => core/semantic}/Type_Collector.py (85%) rename src/{Tools/Tools => core/tools}/COOLAst.py (100%) rename src/{Tools/Tools => core/tools}/Errors.py (100%) rename src/{Tools/Tools => core/tools}/Firsts_and_Follows.py (100%) rename src/{Tools/Tools => core/tools}/Parser_LR1.py (100%) rename src/{Tools/Tools => core/tools}/Semantic.py (100%) rename src/{Tools/Tools => core/tools}/automata.py (100%) rename src/{Tools/Tools => core/tools}/evaluation.py (100%) rename src/{Tools/Tools => core/tools}/parsing.py (100%) rename src/{Tools/Tools => core/tools}/pycompiler.py (100%) rename src/{Tools/Tools => core/tools}/utils.py (100%) rename src/{Tools/Tools => core/tools}/visitor.py (100%) diff --git a/src/Main.py b/src/Main.py index 19c793fe7..6fb4c8eb9 100644 --- a/src/Main.py +++ b/src/Main.py @@ -1,11 +1,11 @@ -from Tools.Lexer.Lexer import Lexer -from Tools.Parser.Parser import CoolParser -from Tools.Tools.evaluation import evaluate_reverse_parse -from Tools.Semantic.Type_Collector import Type_Collector -from Tools.Semantic.Type_Builder import Type_Builder -from Tools.Semantic.Type_Checker import Type_Checker -from Tools.CIL.COOLToCILVisitor import COOLToCILVisitor -from Tools.CIL.CILAst import get_formatter +from core.lexer.Lexer import Lexer +from core.parser.Parser import CoolParser +from core.tools.evaluation import evaluate_reverse_parse +from core.semantic.Type_Collector import Type_Collector +from core.semantic.Type_Builder import Type_Builder +from core.semantic.Type_Checker import Type_Checker +from core.cil.COOLToCILVisitor import COOLToCILVisitor +from core.cil.CILAst import get_formatter def main(args): try: diff --git a/src/Tools/CIL/BaseCOOLToCILVisitor.py b/src/core/cil/BaseCOOLToCILVisitor.py similarity index 99% rename from src/Tools/CIL/BaseCOOLToCILVisitor.py rename to src/core/cil/BaseCOOLToCILVisitor.py index ca977f09e..158478aba 100644 --- a/src/Tools/CIL/BaseCOOLToCILVisitor.py +++ b/src/core/cil/BaseCOOLToCILVisitor.py @@ -1,5 +1,5 @@ -from ..CIL import CILAst as cil -from ..Tools.Semantic import VariableInfo +from ..cil import CILAst as cil +from ..tools.Semantic import VariableInfo class BaseCOOLToCILVisitor: diff --git a/src/Tools/CIL/CILAst.py b/src/core/cil/CILAst.py similarity index 99% rename from src/Tools/CIL/CILAst.py rename to src/core/cil/CILAst.py index 1e76865a7..4a64fad45 100644 --- a/src/Tools/CIL/CILAst.py +++ b/src/core/cil/CILAst.py @@ -1,4 +1,4 @@ -from ..Tools import visitor +from ..tools import visitor class Node: pass @@ -394,7 +394,7 @@ def visit(self, node: ConcatNode): @visitor.when(LengthNode) def visit(self, node: LengthNode): - return f'{node.dest} = {node.source}.lenght' + return f'{node.dest} = LENGTH {node.source}' @visitor.when(EqualNode) def visit(self, node: EqualNode): diff --git a/src/Tools/CIL/COOLToCILVisitor.py b/src/core/cil/COOLToCILVisitor.py similarity index 93% rename from src/Tools/CIL/COOLToCILVisitor.py rename to src/core/cil/COOLToCILVisitor.py index a123868d2..292583bb1 100644 --- a/src/Tools/CIL/COOLToCILVisitor.py +++ b/src/core/cil/COOLToCILVisitor.py @@ -1,7 +1,7 @@ -import Tools.Tools.COOLAst as cool +import core.tools.COOLAst as cool from .BaseCOOLToCILVisitor import * -from Tools.Tools import visitor -from ..Tools.Semantic import Scope +from core.tools import visitor +from ..tools.Semantic import Scope class COOLToCILVisitor(BaseCOOLToCILVisitor): def __init__(self, context): @@ -14,11 +14,10 @@ def visit(self, node): @visitor.when(cool.ProgramNode) def visit(self, node : cool.ProgramNode, scope : Scope): self.current_function = self.register_function('entry') - return_value = self.define_internal_local() main_instance = self.define_internal_local() self.register_instruction(cil.StaticCallNode(self.init_name('Main'), main_instance)) self.register_instruction(cil.ArgNode(main_instance)) - self.register_instruction(cil.StaticCallNode(self.to_function_name('main', 'Main'), return_value)) + self.register_instruction(cil.StaticCallNode(self.to_function_name('main', 'Main'), main_instance)) self.register_instruction(cil.ReturnNode(0)) #self.register_builtin() @@ -35,16 +34,19 @@ def visit(self, node : cool.ClassDeclarationNode, scope : Scope): self.current_type = self.context.get_type(node.id.lex) # Inicializando los atributos de la clase y llamando al constructor del padre - self.current_function = self.register_function(self.init_attr_name(node.id.lex)) if self.current_type.parent.name not in ('Object', 'IO'): - # TODO: Este self deberia ser el que paso por parametro? variable = self.define_internal_local() self.register_instruction(cil.ArgNode(self.vself.name)) self.register_instruction(cil.StaticCallNode( self.init_attr_name(self.current_type.parent.name), variable)) + + self.current_function = self.register_function(self.init_attr_name(node.id.lex)) + instance = self.register_param(VariableInfo('instance', '')) for feat, child in zip(node.features, scope.childs): if isinstance(feat, cool.AttrDeclarationNode): self.visit(feat, child) + self.register_instruction(cil.SetAttribNode( + instance, feat.id.lex, feat.ret_expr, feat.type.lex)) # TODO: Deberia retornar algo aqui? type = self.register_type(node.id.lex) @@ -59,7 +61,6 @@ def visit(self, node : cool.ClassDeclarationNode, scope : Scope): if isinstance(feat, cool.FuncDeclarationNode): self.visit(feat, child) - # TODO: Que hacer si la clase ya tiene definido el metodo init? self.current_function = self.register_function(self.init_name(node.id.lex)) instance = self.define_internal_local() self.register_instruction(cil.AllocateNode(node.id.lex, instance)) @@ -67,6 +68,12 @@ def visit(self, node : cool.ClassDeclarationNode, scope : Scope): variable = self.define_internal_local() self.register_instruction(cil.ArgNode(instance)) self.register_instruction(cil.StaticCallNode(self.init_attr_name(node.id.lex), variable)) + + if 'init' in self.current_type.methods.keys(): + self.register_instruction(cil.ArgNode(instance)) + self.register_instruction(cil.StaticCallNode( + self.to_function_name('init', self.current_type.name), variable)) + self.register_instruction(cil.ReturnNode(instance)) self.current_function = None @@ -74,16 +81,13 @@ def visit(self, node : cool.ClassDeclarationNode, scope : Scope): @visitor.when(cool.AttrDeclarationNode) def visit(self, node : cool.AttrDeclarationNode, scope : Scope): + variable = self.define_internal_local() if node.expression: self.visit(node.expression, scope.childs[0]) - self.register_instruction(cil.SetAttribNode( - self.vself.name, node.id, node.expression.ret_expr, self.current_type)) + self.register_instruction(cil.AssignNode(variable, node.expression.ret_expr)) elif node.type.lex in self.value_types: - variable = self.define_internal_local() self.register_instruction(cil.AllocateNode(node.type.lex, variable)) - self.register_instruction(cil.SetAttribNode( - self.vself.name, node.id, variable, self.current_type)) - variable = self.register_local(VariableInfo(node.id.lex, node.type.lex)) + self.register_local(VariableInfo(node.id.lex, node.type.lex)) node.ret_expr = variable @visitor.when(cool.FuncDeclarationNode) @@ -173,9 +177,8 @@ def visit(self, node : cool.CaseOfNode, scope : Scope): self.visit(node.expression, scope.childs[0]) self.register_instruction(cil.TypeOfNode(vtype, node.expression.ret_expr)) - void = cil.VoidNode() isvoid = self.define_internal_local() - self.register_instruction(cil.EqualNode(isvoid, node.expression.ret_expr, void)) + self.register_instruction(cil.EqualNode(isvoid, node.expression.ret_expr, cil.VoidNode())) self.register_runtime_error(isvoid, f'{node.expression.line, node.expression.column} - ' f'RuntimeError: Case on void') @@ -346,13 +349,11 @@ def visit(self, node: cool.DivNode, scope: Scope): @visitor.when(cool.IsVoidNode) def visit(self, node: cool.IsVoidNode, scope: Scope): ret = self.define_internal_local() - value = self.define_internal_local() answer = self.define_internal_local() void = cil.VoidNode() self.visit(node.expression, scope.childs[0]) - self.register_instruction(cil.AssignNode(value, node.expression.ret_expr)) - self.register_instruction(cil.EqualNode(answer, value, void)) + self.register_instruction(cil.EqualNode(answer, node.expression.ret_expr, void)) self.register_instruction(cil.ArgNode(answer)) self.register_instruction(cil.StaticCallNode(self.init_name('Bool'), ret)) @@ -381,7 +382,6 @@ def visit(self, node: cool.EqualNode, scope: Scope): type_int = self.define_internal_local() type_string = self.define_internal_local() type_bool = self.define_internal_local() - type = self.define_internal_local() equal = self.define_internal_local() value = self.define_internal_local() @@ -447,6 +447,7 @@ def visit(self, node: cool.FunctionCallNode, scope: Scope): self.register_instruction(cil.EqualNode(isvoid, node.obj.ret_expr, void)) self.register_runtime_error(isvoid, f'{node.id.line, node.id.column} - RuntimeError: Dispatch on void') + # TODO: Creo que deberia annadir los parametros al reves para luego sacarlos en el orden correcto self.register_instruction(cil.ArgNode(node.obj.ret_expr)) for arg in args: self.register_instruction(arg) @@ -470,6 +471,7 @@ def visit(self, node: cool.MemberCallNode, scope: Scope): self.visit(arg, child) args.append(cil.ArgNode(arg.ret_expr)) + # TODO: Creo que deberia annadir los parametros al reves para luego sacarlos en el orden correcto self.register_instruction(cil.ArgNode(self.vself.name)) for arg in args: self.register_instruction(arg) @@ -483,6 +485,9 @@ def visit(self, node: cool.NewNode, scope: Scope): if node.type == 'SELF_TYPE': variable = self.define_internal_local() self.register_instruction(cil.TypeOfNode(ret, self.vself.name)) + # TODO: ALLOCATE a veces recibe el nombre de la clase como string, necesito cambiar este ya + # que el nombre de la clase se encuentra dentro de la variable, o cambiar los demas para + # que funcionen con self.register_instruction(cil.LoadNode(variable, data)) self.register_instruction(cil.AllocateNode(variable, ret)) else: if node.type == 'Int': @@ -522,6 +527,7 @@ def visit(self, node: cool.StringNode, scope: Scope): def visit(self, node: cool.IntegerNode, scope: Scope): ret = self.define_internal_local() + # TODO: Hay algunos ArgNode que reciben variables y otros valores especificos self.register_instruction(cil.ArgNode(node.token.lex)) self.register_instruction(cil.StaticCallNode(self.init_name('Int'), ret)) node.ret_expr = ret @@ -530,6 +536,7 @@ def visit(self, node: cool.IntegerNode, scope: Scope): def visit(self, node: cool.BoolNode, scope: Scope): ret = self.define_internal_local() + # TODO: Hay algunos ArgNode que reciben variables y otros valores especificos self.register_instruction(cil.ArgNode(node.token.lex)) self.register_instruction(cil.StaticCallNode(self.init_name('Bool'), ret)) node.ret_expr = ret diff --git a/src/Tools/format_visitor.py b/src/core/format_visitor.py similarity index 99% rename from src/Tools/format_visitor.py rename to src/core/format_visitor.py index d2a1f7d89..fd66ad9db 100644 --- a/src/Tools/format_visitor.py +++ b/src/core/format_visitor.py @@ -1,4 +1,4 @@ -from Tools import visitor +from core import visitor from Parser import ProgramNode, ClassDeclarationNode, AttrDeclarationNode, FuncDeclarationNode from Parser import IfThenElseNode, WhileLoopNode, BlockNode, LetInNode, CaseOfNode from Parser import AssignNode, UnaryNode, BinaryNode diff --git a/src/Tools/Lexer/Lexer.py b/src/core/lexer/Lexer.py similarity index 98% rename from src/Tools/Lexer/Lexer.py rename to src/core/lexer/Lexer.py index 04b433961..7a39de02f 100644 --- a/src/Tools/Lexer/Lexer.py +++ b/src/core/lexer/Lexer.py @@ -1,7 +1,7 @@ import ply.lex as lex -from Tools.Tools.utils import Token -from Tools.Parser.Parser import CoolGrammar -from Tools.Tools.Errors import LexicographicError +from core.tools.utils import Token +from core.parser.Parser import CoolGrammar +from core.tools.Errors import LexicographicError class Lexer: states = ( diff --git a/src/core/mips/CilToMipsVisitor.py b/src/core/mips/CilToMipsVisitor.py new file mode 100644 index 000000000..e69de29bb diff --git a/src/core/mips/MipsAst.py b/src/core/mips/MipsAst.py new file mode 100644 index 000000000..e69de29bb diff --git a/src/Tools/Parser/Parser.py b/src/core/parser/Parser.py similarity index 98% rename from src/Tools/Parser/Parser.py rename to src/core/parser/Parser.py index cb779829b..fc83e1c91 100644 --- a/src/Tools/Parser/Parser.py +++ b/src/core/parser/Parser.py @@ -1,6 +1,6 @@ -from Tools.Tools.pycompiler import Grammar -from Tools.Tools.Parser_LR1 import LR1Parser -from Tools.Tools.COOLAst import * +from core.tools.pycompiler import Grammar +from core.tools.Parser_LR1 import LR1Parser +from core.tools.COOLAst import * # Representacion de la gramatica de COOL utilizando la clase grammar diff --git a/src/Tools/Semantic/Type_Builder.py b/src/core/semantic/Type_Builder.py similarity index 95% rename from src/Tools/Semantic/Type_Builder.py rename to src/core/semantic/Type_Builder.py index be9425bf0..ea75f4ae0 100644 --- a/src/Tools/Semantic/Type_Builder.py +++ b/src/core/semantic/Type_Builder.py @@ -1,10 +1,10 @@ -from Tools.Tools import visitor -from Tools.Tools.Semantic import * -from Tools.Parser.Parser import ProgramNode, ClassDeclarationNode, AttrDeclarationNode, FuncDeclarationNode -from Tools.Tools.Errors import * +from core.tools import visitor +from core.tools.Semantic import * +from core.parser.Parser import ProgramNode, ClassDeclarationNode, AttrDeclarationNode, FuncDeclarationNode +from core.tools.Errors import * # Visitor encargado de contruir los tipos. Una vez que se conocen los nombres -# de los tipos que intervienen en el codifo COOL, este visitor les annade sus +# de los tipos que intervienen en el codigo COOL, este visitor les annade sus # metodos y atributos, asi como el tipo padre. class Type_Builder: diff --git a/src/Tools/Semantic/Type_Checker.py b/src/core/semantic/Type_Checker.py similarity index 98% rename from src/Tools/Semantic/Type_Checker.py rename to src/core/semantic/Type_Checker.py index ac366b40c..3d405ec45 100644 --- a/src/Tools/Semantic/Type_Checker.py +++ b/src/core/semantic/Type_Checker.py @@ -1,11 +1,11 @@ -from Tools.Tools import visitor -from Tools.Parser.Parser import ProgramNode, ClassDeclarationNode, AttrDeclarationNode, FuncDeclarationNode,\ +from core.tools import visitor +from core.parser.Parser import ProgramNode, ClassDeclarationNode, AttrDeclarationNode, FuncDeclarationNode,\ IfThenElseNode, WhileLoopNode, BlockNode, LetInNode, CaseOfNode,\ AssignNode, LessEqualNode, LessNode, EqualNode, ArithmeticNode,\ NotNode, IsVoidNode, ComplementNode, FunctionCallNode, MemberCallNode, NewNode,\ IntegerNode, IdNode, StringNode, BoolNode -from Tools.Tools.Semantic import Context, Scope, SelfType, SemanticException, ErrorType -from Tools.Tools.Errors import * +from core.tools.Semantic import Context, Scope, SelfType, SemanticException, ErrorType +from core.tools.Errors import * # Este es el visitor encargado de terminar el chequeo semantico. # Revisa la compatibilidad de tipos, la compatibilidad en la herencia, diff --git a/src/Tools/Semantic/Type_Collector.py b/src/core/semantic/Type_Collector.py similarity index 85% rename from src/Tools/Semantic/Type_Collector.py rename to src/core/semantic/Type_Collector.py index ccaaf0671..eb8578534 100644 --- a/src/Tools/Semantic/Type_Collector.py +++ b/src/core/semantic/Type_Collector.py @@ -1,7 +1,7 @@ -from Tools.Tools import visitor -from Tools.Tools.Semantic import * -from Tools.Parser.Parser import ProgramNode, ClassDeclarationNode -from Tools.Tools.Errors import SemanticError +from core.tools import visitor +from core.tools.Semantic import * +from core.parser.Parser import ProgramNode, ClassDeclarationNode +from core.tools.Errors import SemanticError # Visitor encargado de coleccionar los nombres de las clases que se definen # en el codigo del programa COOL, chequea ademas que no se redeclaren e diff --git a/src/Tools/Tools/COOLAst.py b/src/core/tools/COOLAst.py similarity index 100% rename from src/Tools/Tools/COOLAst.py rename to src/core/tools/COOLAst.py diff --git a/src/Tools/Tools/Errors.py b/src/core/tools/Errors.py similarity index 100% rename from src/Tools/Tools/Errors.py rename to src/core/tools/Errors.py diff --git a/src/Tools/Tools/Firsts_and_Follows.py b/src/core/tools/Firsts_and_Follows.py similarity index 100% rename from src/Tools/Tools/Firsts_and_Follows.py rename to src/core/tools/Firsts_and_Follows.py diff --git a/src/Tools/Tools/Parser_LR1.py b/src/core/tools/Parser_LR1.py similarity index 100% rename from src/Tools/Tools/Parser_LR1.py rename to src/core/tools/Parser_LR1.py diff --git a/src/Tools/Tools/Semantic.py b/src/core/tools/Semantic.py similarity index 100% rename from src/Tools/Tools/Semantic.py rename to src/core/tools/Semantic.py diff --git a/src/Tools/Tools/automata.py b/src/core/tools/automata.py similarity index 100% rename from src/Tools/Tools/automata.py rename to src/core/tools/automata.py diff --git a/src/Tools/Tools/evaluation.py b/src/core/tools/evaluation.py similarity index 100% rename from src/Tools/Tools/evaluation.py rename to src/core/tools/evaluation.py diff --git a/src/Tools/Tools/parsing.py b/src/core/tools/parsing.py similarity index 100% rename from src/Tools/Tools/parsing.py rename to src/core/tools/parsing.py diff --git a/src/Tools/Tools/pycompiler.py b/src/core/tools/pycompiler.py similarity index 100% rename from src/Tools/Tools/pycompiler.py rename to src/core/tools/pycompiler.py diff --git a/src/Tools/Tools/utils.py b/src/core/tools/utils.py similarity index 100% rename from src/Tools/Tools/utils.py rename to src/core/tools/utils.py diff --git a/src/Tools/Tools/visitor.py b/src/core/tools/visitor.py similarity index 100% rename from src/Tools/Tools/visitor.py rename to src/core/tools/visitor.py diff --git a/tests/utils/utils.py b/tests/utils/utils.py index 961cf7cbc..af476fbbe 100644 --- a/tests/utils/utils.py +++ b/tests/utils/utils.py @@ -45,7 +45,7 @@ def get_file_name(path: str): def compare_errors(compiler_path: str, cool_file_path: str, error_file_path: str, cmp=first_error, timeout=100): try: - sp = subprocess.run(['bash', compiler_path, cool_file_path], capture_output=True, timeout=timeout) + sp = subprocess.run(['bash', compiler_path, cool_file_path], stdout=subprocess.PIPE, timeout=timeout) return_code, output = sp.returncode, sp.stdout.decode() except subprocess.TimeoutExpired: assert False, COMPILER_TIMEOUT From ce51935009a1546551d33a74a310fe427a6203a4 Mon Sep 17 00:00:00 2001 From: mavaldivie Date: Fri, 26 Nov 2021 23:27:45 -0500 Subject: [PATCH 08/75] Implemented some of the methods in the CILtoMIPS visitor. --- src/core/mips/CilToMipsVisitor.py | 396 ++++++++++++++++++++++++++++++ src/core/mips/MipsAst.py | 278 +++++++++++++++++++++ 2 files changed, 674 insertions(+) diff --git a/src/core/mips/CilToMipsVisitor.py b/src/core/mips/CilToMipsVisitor.py index e69de29bb..b4d143e58 100644 --- a/src/core/mips/CilToMipsVisitor.py +++ b/src/core/mips/CilToMipsVisitor.py @@ -0,0 +1,396 @@ +import core.cil.CILAst as cil +import core.mips.MipsAst as mips +from ..tools import visitor +from ..cil.CILAst import * + +class CILtoMIPSvisitor: + def __init__(self): + self.type_label_count = 0 + self.data_label_count = 0 + self.code_label_count = 0 + + self._types_section = {} + self._data_section = {} + self._functions_section = {} + + self._current_function = None + self._function_names = {} + self._pushed_args = 0 + self._labels = {} + + self.registers = mips.REGISTERS + + def generate_type_label(self): + self.type_label_count += 1 + return f'type_{self.type_label_count}' + + def generate_data_label(self): + self.data_label_count += 1 + return f'data_{self.data_label_count}' + + def generate_code_label(self): + self.code_label_count += 1 + return f'label_{self.code_label_count}' + + def enter_function(self, name, function): + self._functions_section[name] = function + self._current_function = function + self._labels = {} + + def exit_function(self): + self._current_function = None + + def get_free_register(self): + r, rest = self.registers + self.registers = rest + return r + + def free_register(self, reg): + self.registers.append(reg) + + def register_label(self, old_label, new_label): + self._labels[old_label] = new_label + + def get_label(self, label): + return self._labels[label] + + def get_var_location(self, name): + return self._current_function.get_var_location(name) + + @visitor.on('node') + def collect_func_names(self, node): + pass + + @visitor.when(cil.ProgramNode) + def collect_func_names(self, node): + for f in node.dotcode: + self.collect_func_names(f) + + @visitor.when(cil.FunctionNode) + def collect_func_names(self, node): + if node.name == "entry": + self._function_names[node.name] = 'main' + else: + self._function_names[node.name] = self.generate_code_label() + + + @visitor.on('node') + def visit(self, node): + pass + + @visitor.when(cil.ProgramNode) + def visit(self, node): + self.collect_func_names(node) + self._data_section["default_str"] = mips.StringConst("default_str", "") + + for i in node.dottypes: + self.visit(i) + for i in node.dotdata: + self.visit(i) + for i in node.dotcode: + self.visit(i) + + return mips.ProgramNode([i for i in self._data_section.values()], + [i for i in self._types_section.values()], + [i for i in self._functions_section.values()]) + + @visitor.when(cil.TypeNode) + def visit(self, node): + name_label = self.generate_data_label() + self._data_section[node.name] = mips.StringConst(name_label, node.name) + + type_label = self.generate_type_label() + methods = {key: self._function_names[value] for key, value in node.methods} + defaults = [] + if node.name == "String": + defaults = [('value', 'default_str'), ('len', 'type_4_proto')] + + type = mips.MIPSType(type_label, name_label, node.attributes, methods, + len(self._types_section), defaults) + self._types_section[node.name] = type + + @visitor.when(cil.DataNode) + def visit(self, node): + label = self.generate_data_label() + self._data_section[node.name] = mips.StringConst(label, node.value) + + @visitor.when(cil.FunctionNode) + def visit(self, node): + raise NotImplementedError() + + @visitor.when(cil.ParamNode) + def visit(self, node): + raise NotImplementedError() + + @visitor.when(cil.LocalNode) + def visit(self, node): + raise NotImplementedError() + + @visitor.when(cil.AssignNode) + def visit(self, node): + instructions = [] + + if isinstance(node.source, cil.VoidNode): + reg1 = mips.ZERO_REG + elif node.source.isnumeric(): + reg1 = mips.REGISTERS[0] + instructions.append(mips.LoadInmediateNode(reg1, int(node.source))) + else: + reg1 = mips.REGISTERS[0] + instructions.append(mips.LoadWordNode(reg1, self.get_var_location(node.source))) + + instructions.append(mips.StoreWordNode(reg1, self.get_var_location(node.dest))) + return instructions + + @visitor.when(cil.PlusNode) + def visit(self, node): + instructions = [] + + reg1 = mips.REGISTERS[0] + if isinstance(node.left, int): + instructions.append(mips.LoadInmediateNode(reg1,node.left)) + else: + instructions.append(mips.LoadWordNode(reg1,self.get_var_location(node.left))) + + reg2 = mips.REGISTERS[1] + if isinstance(node.right, int): + instructions.append(mips.LoadInmediateNode(reg2, node.right)) + else: + instructions.append(mips.LoadWordNode(reg2, self.get_var_location(node.right))) + + reg3 = mips.REGISTERS[0] + instructions.append(mips.AdditionNode(reg3, reg1, reg2)) + instructions.append(mips.StoreWordNode(reg3, self.get_var_location(node.dest))) + + return instructions + + @visitor.when(MinusNode) + def visit(self, node): + instructions = [] + + reg1 = mips.REGISTERS[0] + if isinstance(node.left, int): + instructions.append(mips.LoadInmediateNode(reg1, node.left)) + else: + instructions.append(mips.LoadWordNode(reg1, self.get_var_location(node.left))) + + reg2 = mips.REGISTERS[1] + if isinstance(node.right, int): + instructions.append(mips.LoadInmediateNode(reg2, node.right)) + else: + instructions.append(mips.LoadWordNode(reg2, self.get_var_location(node.right))) + + reg3 = mips.REGISTERS[0] + instructions.append(mips.SubstractionNode(reg3, reg1, reg2)) + instructions.append(mips.StoreWordNode(reg3, self.get_var_location(node.dest))) + + return instructions + + @visitor.when(StarNode) + def visit(self, node): + instructions = [] + + reg1 = mips.REGISTERS[0] + if isinstance(node.left, int): + instructions.append(mips.LoadInmediateNode(reg1, node.left)) + else: + instructions.append(mips.LoadWordNode(reg1, self.get_var_location(node.left))) + + reg2 = mips.REGISTERS[1] + if isinstance(node.right, int): + instructions.append(mips.LoadInmediateNode(reg2, node.right)) + else: + instructions.append(mips.LoadWordNode(reg2, self.get_var_location(node.right))) + + reg3 = mips.REGISTERS[0] + instructions.append(mips.MultiplyNode(reg3, reg1, reg2)) + instructions.append(mips.StoreWordNode(reg3, self.get_var_location(node.dest))) + + return instructions + + @visitor.when(DivNode) + def visit(self, node): + instructions = [] + + reg1 = mips.REGISTERS[0] + if isinstance(node.left, int): + instructions.append(mips.LoadInmediateNode(reg1, node.left)) + else: + instructions.append(mips.LoadWordNode(reg1, self.get_var_location(node.left))) + + reg2 = mips.REGISTERS[1] + if isinstance(node.right, int): + instructions.append(mips.LoadInmediateNode(reg2, node.right)) + else: + instructions.append(mips.LoadWordNode(reg2, self.get_var_location(node.right))) + + reg3 = mips.LOW_REG + instructions.append(mips.DivideNode(reg1, reg2)) + instructions.append(mips.StoreWordNode(reg3, self.get_var_location(node.dest))) + + return instructions + + @visitor.when(AllocateNode) + def visit(self, node): + instructions = [] + + if isinstance(node.type, int): + type = node.type + else: + type = self._types_section[node.type].index + + reg1 = mips.REGISTERS[0] + reg2 = mips.REGISTERS[1] + + instructions.append(mips.LoadInmediateNode(reg1, type)) + instructions.append(mips.ShiftLeftLogicalNode(reg1, reg2, 2)) + instructions.append(mips.LoadAddressNode(reg2, mips.PROTO_TABLE_LABEL)) + instructions.append(mips.AdditionUnsignedNode(reg2, reg2, reg1)) + instructions.append(mips.LoadWordNode(reg2, mips.RegisterRelativeLocation(reg2, 0))) + reg3 = mips.ARG_REGISTERS[0] + instructions.append(mips.LoadWordNode(reg3, mips.RegisterRelativeLocation(reg2, 4))) + instructions.append(mips.ShiftLeftLogicalNode(reg3, reg3, 2)) + instructions.append(mips.JalNode('malloc')) + instructions.append(mips.MoveNode(mips.ARG_REGISTERS[2], reg3)) + instructions.append(mips.MoveNode(reg3, reg2)) + instructions.append(mips.MoveNode(mips.ARG_REGISTERS[1], mips.V0_REG)) + instructions.append(mips.JalNode('copy')) + + instructions.append(mips.StoreWordNode(mips.V0_REG, self.get_var_location(node.dest))) + + return instructions + + + + + + + @visitor.when(TypeOfNode) + def visit(self, node): + return f'{node.dest} = TYPEOF {node.obj}' + + @visitor.when(StaticCallNode) + def visit(self, node): + return f'{node.dest} = CALL {node.function}' + + @visitor.when(DynamicCallNode) + def visit(self, node): + return f'{node.dest} = VCALL {node.type} {node.method.lex}' + + @visitor.when(ArgNode) + def visit(self, node): + return f'ARG {node.name}' + + @visitor.when(ReturnNode) + def visit(self, node): + return f'RETURN {node.value if node.value is not None else ""}' + + @visitor.when(LoadNode) + def visit(self, node): + return f'{node.dest} = LOAD {self.visit(node.msg)}' + + @visitor.when(PrintStringNode) + def visit(self, node: PrintStringNode): + return f'PRINTSTRING {node.str_addr}' + + @visitor.when(PrintIntNode) + def visit(self, node: PrintIntNode): + return f'PRINTINT {node.value}' + + @visitor.when(ExitNode) + def visit(self, node: ExitNode): + return f'EXIT' + + @visitor.when(CopyNode) + def visit(self, node): + return f'{node.dest} = COPY {node.value}' + + @visitor.when(GetAttribNode) + def visit(self, node: GetAttribNode): + return f'{node.dest} = GETATTRIB {node.obj}.{node.attr} {node.computed_type}' + + @visitor.when(ErrorNode) + def visit(self, node: ErrorNode): + return f'ERROR {self.visit(node.data)}' + + @visitor.when(ReadNode) + def visit(self, node: ReadNode): + return f'{node.dest} = READ' + + @visitor.when(SetAttribNode) + def visit(self, node: SetAttribNode): + return f'SETATTR {node.obj}.{node.attr}: {node.computed_type} = {node.value}' + + @visitor.when(LessNode) + def visit(self, node: LessNode): + return f'{node.dest} = {node.left} < {node.right}' + + @visitor.when(GotoIfNode) + def visit(self, node: GotoIfNode): + return f'GOTOIF {node.condition} {node.label}' + + @visitor.when(GotoNode) + def visit(self, node: GotoNode): + return f'GOTO {node.label}' + + @visitor.when(LabelNode) + def visit(self, node: LabelNode): + return f'LABEL {node.label}' + + @visitor.when(SubstringNode) + def visit(self, node: SubstringNode): + return f'{node.dest} = SUBSTRING {node.str_value}[{node.index}:{node.index + node.length}]' + + @visitor.when(ConcatNode) + def visit(self, node: ConcatNode): + return f'{node.dest} = CONCAT {node.prefix} + {node.suffix}' + + @visitor.when(LengthNode) + def visit(self, node: LengthNode): + return f'{node.dest} = LENGTH {node.source}' + + @visitor.when(EqualNode) + def visit(self, node: EqualNode): + return f'{node.dest} = {node.left} == {node.right}' + + @visitor.when(NameNode) + def visit(self, node: NameNode): + return f'{node.dest} = NAME {node.value}' + + @visitor.when(EqualStringNode) + def visit(self, node: EqualStringNode): + return f'{node.dest} = {node.left} == {node.right}' + + @visitor.when(ComplementNode) + def visit(self, node: ComplementNode): + return f'{node.dest} = ~{node.value}' + + @visitor.when(LessEqualNode) + def visit(self, node: LessEqualNode): + return f'{node.dest} = {node.left} <= {node.right}' + + @visitor.when(GetIndexNode) + def visit(self, node: GetIndexNode): + return f'GETINDEXNODE' + + @visitor.when(SetIndexNode) + def visit(self, node: SetIndexNode): + return f'SETINDEXNODE' + + @visitor.when(PrefixNode) + def visit(self, node: PrefixNode): + return f'PREFFIXNODE' + + @visitor.when(ToStrNode) + def visit(self, node: ToStrNode): + return f'{node.dest} = str({node.value})' + + @visitor.when(VoidNode) + def visit(self, node: VoidNode): + return 'VOID' + + @visitor.when(NotNode) + def visit(self, node: NotNode): + return f'{node.dest} = NOT {node.value}' + + diff --git a/src/core/mips/MipsAst.py b/src/core/mips/MipsAst.py index e69de29bb..5902d50d3 100644 --- a/src/core/mips/MipsAst.py +++ b/src/core/mips/MipsAst.py @@ -0,0 +1,278 @@ + + +DOUBLE_WORD = 4 +REGISTERS_NAMES = ['t0', 't1', 't2', 't3', 't4', 't5', 't6', 't7', 't8'] +ARG_REGISTERS_NAMES = ['a0', 'a1', 'a2', 'a3'] + + +class Register: + def __init__(self, name): + self.name = name + + +REGISTERS = [Register(i) for i in REGISTERS_NAMES] +ARG_REGISTERS = [Register(i) for i in ARG_REGISTERS_NAMES] +FP_REG = Register('fp') +SP_REG = Register('sp') +RA_REG = Register('ra') +V0_REG = Register('v0') +V1_REG = Register('v1') +ZERO_REG = Register('zero') +LOW_REG = Register('low') + + +class Node: + pass + +class ProgramNode(Node): + def __init__(self, data, types, functions): + self.data = data + self.types = types + self.functions = functions + + +class FunctionNode(Node): + def __init__(self, label, instructions, params, localvars): + self.label = label + self.instructions = [] + self.params = params + self.localvars = localvars + + def add_instruction(self, instruction): + self.instructions.append(instruction) + + def get_param_stack_location(self, name): + # TODO Tener en cuenta que los primeros argumentos se guardan en los registros para argumentos + index = self.params.index(name) + offset = ((len(self.params) - 1) - index) * DOUBLE_WORD + return RegisterRelativeLocation(FP_REG, offset) + + def get_local_stack_location(self, name): + index = self.localvars.index(name) + offset = (index + 2) * -DOUBLE_WORD + return RegisterRelativeLocation(FP_REG, offset) + + def get_var_location(self, name): + try: + return self.get_param_stack_location(name) + except ValueError: + return self.get_local_stack_location(name) + + +class InstructionNode(Node): + pass + + +class PrintIntNode(InstructionNode): + # $a0 = integer + pass + +class PrintStringNode(InstructionNode): + # $a0 = string + pass + +class ReadIntNode(InstructionNode): + # integer(int $v0) + pass + +class ReadStringNode(InstructionNode): + # integer(int $v0) + pass + +class ExitNode(InstructionNode): + pass + + + +class AbsoluteNode(InstructionNode): + # rdest <- abs(rsrc) + def __init__(self, rdest, rsrc): + ''' + Put the absolute value of register rsrc in register rdest + ''' + self.rdest = rdest + self.rsrc = rsrc + +class AdditionNode(InstructionNode): + # rd <- rs + rt + def __init__(self, rdest, rsrc1, rsrc2): + ''' + Put the sum of registers rsrc1 and rsrc2 into register rdest. + ''' + self.rdest = rdest + self.rsrc1 = rsrc1 + self.rsrc2 = rsrc2 + +class AdditionInmediateNode(InstructionNode): + def __init__(self, rdest, rsrc, imm): + ''' + Put the sum of register rsrc and the sign-extended immediate into register rdest + ''' + self.rdest = rdest + self.rsrc = rsrc + self.imm = imm + +class DivideNode(InstructionNode): + def __init__(self, rsrc1, rsrc2): + ''' + Put the quotient of register rsrc1 and src2 into register Hi/Lo. + ''' + self.rsrc1 = rsrc1 + self.rsrc2 = rsrc2 + +class MultiplyNode(InstructionNode): + def __init__(self, rdest, rsrc1, rsrc2): + ''' + Put the product of register rsrc1 and src2 into register rdest. + ''' + self.rdest = rdest + self.rsrc1 = rsrc1 + self.rsrc2 = rsrc2 + +class NegateNode(InstructionNode): + def __init__(self, rdest, rsrc1): + ''' + Put the negation of register rsrc1 into register rdest. + ''' + self.rdest = rdest + self.rsrc1 = rsrc1 + +class SubstractionNode(InstructionNode): + def __init__(self, rdest, rsrc1, rsrc2): + ''' + Put the difference of register rsrc1 and src2 into register rdest. + ''' + self.rdest = rdest + self.rsrc1 = rsrc1 + self.rsrc2 = rsrc2 + +class LessNode(InstructionNode): + def __init__(self, rdest, rsrc1, rsrc2): + ''' + Set register rdest to 1 if register rsrc1 is less than rsrc2, and 0 otherwise + ''' + self.rdest = rdest + self.rsrc1 = rsrc1 + self.rsrc2 = rsrc2 + +class LessInmediateNode(InstructionNode): + def __init__(self, rdest, rsrc1, imm): + ''' + Set register rdest to 1 if register rsrc1 is less than imm, and 0 otherwise + ''' + self.rdest = rdest + self.rsrc1 = rsrc1 + self.imm = imm + +class EqualNode(InstructionNode): + def __init__(self, rdest, rsrc1, rsrc2): + ''' + Set register rdest to 1 if register rsrc1 equals rsrc2, and 0 otherwise + ''' + self.rdest = rdest + self.rsrc1 = rsrc1 + self.rsrc2 = rsrc2 + +class LessEqualNode(InstructionNode): + def __init__(self, rdest, rsrc1, rsrc2): + ''' + Set register rdest to 1 if register rsrc1 is less than or equal to rsrc2, and 0 otherwise + ''' + self.rdest = rdest + self.rsrc1 = rsrc1 + self.rsrc2 = rsrc2 + +class JumpNode(InstructionNode): + def __init__(self, label): + ''' + Unconditionally jump to the instruction at the label. + ''' + self.label = label + +class JalNode(InstructionNode): + def __init__(self, label): + ''' + Unconditionally jump to the instruction at target. Save the address of the next + instruction in register ra (rd said the manual). + ''' + self.label = label + +class MoveNode(InstructionNode): + def __init__(self, reg1, reg2): + self.reg1 = reg1 + self.reg2 = reg2 + +class StoreWordNode(InstructionNode): + def __init__(self, reg, addr): + self.reg = reg + self.addr = addr + +class LoadInmediateNode(InstructionNode): + def __init__(self, reg, value): + self.reg = reg + self.value = value + +class LoadWordNode(InstructionNode): + def __init__(self, reg, addr): + self.reg = reg + self.addr = addr + +class DataNode(Node): + def __init__(self, label): + self.label = label + +class StringConst(DataNode): + def __init__(self, label, string): + super().__init__(label) + self.string = string + +class MIPSType: + def __init__(self, label, name_addr, attributes, + methods, index, default=[]): + self.label = label + self.name = name_addr + self.attributes = attributes + self.default_attributes = dict(default) + self.methods = methods + self.index = index + + @property + def size(self): + return len(self.attributes) + DOUBLE_WORD + + @property + def string_name_label(self): + return self.name + + +class MemoryLocation: + pass + + +class RegisterRelativeLocation(MemoryLocation): + def __init__(self, register, offset): + self._register = register + self._offset = offset + + @property + def register(self): + return self._register + + @property + def offset(self): + return self._offset + + +class LabelRelativeLocation(MemoryLocation): + def __init__(self, label, offset): + self._label = label + self._offset = offset + + @property + def label(self): + return self._label + + @property + def offset(self): + return self._offset + From 7115995ff66647db0c054800455c1a41fc33cc3c Mon Sep 17 00:00:00 2001 From: mavaldivie Date: Wed, 9 Feb 2022 18:33:10 -0500 Subject: [PATCH 09/75] Implemented most of the methods in CILtoMIPS visitor. --- src/core/cil/CILAst.py | 14 - src/core/mips/CilToMipsVisitor.py | 441 ++++++++++++++++++++++++------ src/core/mips/MipsAst.py | 51 ++++ 3 files changed, 406 insertions(+), 100 deletions(-) diff --git a/src/core/cil/CILAst.py b/src/core/cil/CILAst.py index 4a64fad45..a34e9749a 100644 --- a/src/core/cil/CILAst.py +++ b/src/core/cil/CILAst.py @@ -92,12 +92,6 @@ def __init__(self, obj, attr, value, computed_type): self.value = value self.computed_type = computed_type -class GetIndexNode(InstructionNode): - pass - -class SetIndexNode(InstructionNode): - pass - class AllocateNode(InstructionNode): def __init__(self, itype, dest): self.type = itype @@ -416,14 +410,6 @@ def visit(self, node: ComplementNode): def visit(self, node: LessEqualNode): return f'{node.dest} = {node.left} <= {node.right}' - @visitor.when(GetIndexNode) - def visit(self, node: GetIndexNode): - return f'GETINDEXNODE' - - @visitor.when(SetIndexNode) - def visit(self, node: SetIndexNode): - return f'SETINDEXNODE' - @visitor.when(PrefixNode) def visit(self, node: PrefixNode): return f'PREFFIXNODE' diff --git a/src/core/mips/CilToMipsVisitor.py b/src/core/mips/CilToMipsVisitor.py index b4d143e58..1a513ccb7 100644 --- a/src/core/mips/CilToMipsVisitor.py +++ b/src/core/mips/CilToMipsVisitor.py @@ -1,7 +1,6 @@ import core.cil.CILAst as cil import core.mips.MipsAst as mips from ..tools import visitor -from ..cil.CILAst import * class CILtoMIPSvisitor: def __init__(self): @@ -164,7 +163,7 @@ def visit(self, node): return instructions - @visitor.when(MinusNode) + @visitor.when(cil.MinusNode) def visit(self, node): instructions = [] @@ -186,7 +185,7 @@ def visit(self, node): return instructions - @visitor.when(StarNode) + @visitor.when(cil.StarNode) def visit(self, node): instructions = [] @@ -208,7 +207,7 @@ def visit(self, node): return instructions - @visitor.when(DivNode) + @visitor.when(cil.DivNode) def visit(self, node): instructions = [] @@ -230,7 +229,7 @@ def visit(self, node): return instructions - @visitor.when(AllocateNode) + @visitor.when(cil.AllocateNode) def visit(self, node): instructions = [] @@ -245,7 +244,7 @@ def visit(self, node): instructions.append(mips.LoadInmediateNode(reg1, type)) instructions.append(mips.ShiftLeftLogicalNode(reg1, reg2, 2)) instructions.append(mips.LoadAddressNode(reg2, mips.PROTO_TABLE_LABEL)) - instructions.append(mips.AdditionUnsignedNode(reg2, reg2, reg1)) + instructions.append(mips.AdditionNode(reg2, reg2, reg1)) instructions.append(mips.LoadWordNode(reg2, mips.RegisterRelativeLocation(reg2, 0))) reg3 = mips.ARG_REGISTERS[0] instructions.append(mips.LoadWordNode(reg3, mips.RegisterRelativeLocation(reg2, 4))) @@ -260,123 +259,384 @@ def visit(self, node): return instructions + @visitor.when(cil.TypeOfNode) + def visit(self, node): + instructions = [] + reg1 = mips.REGISTERS[0] + reg2 = mips.REGISTERS[1] + instructions.append(mips.LoadWordNode(reg1, self.get_var_location(node.obj))) + instructions.append(mips.LoadWordNode(reg2, mips.RegisterRelativeLocation(reg1, 0))) + instructions.append(mips.StoreWordNode(reg2, self.get_var_location(node.dest))) + return instructions - - @visitor.when(TypeOfNode) + @visitor.when(cil.StaticCallNode) def visit(self, node): - return f'{node.dest} = TYPEOF {node.obj}' + instructions = [] + func_name = self._function_names[node.function] + instructions.append(mips.JalNode(func_name)) - @visitor.when(StaticCallNode) - def visit(self, node): - return f'{node.dest} = CALL {node.function}' + instructions.append(mips.StoreWordNode(mips.V0_REG, self.get_var_location(node.dest))) + if self._pushed_args > 0: + instructions.append(mips.AdditionInmediateNode(mips.SP_REG, mips.SP_REG, self._pushed_args * mips.DOUBLE_WORD)) + self._pushed_args = 0 - @visitor.when(DynamicCallNode) + return instructions + ''' + @visitor.when(cil.DynamicCallNode) def visit(self, node): - return f'{node.dest} = VCALL {node.type} {node.method.lex}' + instructions = [] - @visitor.when(ArgNode) + type = self._types_section[node.type] + method = type.methods.index(node.method) + + reg = mips.REGISTERS[0] + instructions.append(mips.LoadWordNode(reg, self.get_var_location(node.type))) + instructions.append(mips.LoadAddressNode(mips.ARG_REGISTERS[1], mips.PROTO_TABLE_LABEL)) + instructions.append(mips.ShiftLeftLogicalNode(mips.ARG_REGISTERS[2], reg, 2)) + instructions.append(mips.AddUnsignedNode(mips.ARG_REGISTERS[1], mips.ARG_REGISTERS[1], mips.ARG_REGISTERS[2])) + instructions.append( + mips.LoadWordNode(mips.ARG_REGISTERS[1], mips.RegisterRelativeLocation(mips.ARG_REGISTERS[1], 0))) + instructions.append( + mips.LoadWordNode(mips.ARG_REGISTERS[1], mips.RegisterRelativeLocation(mips.ARG_REGISTERS[1], 8))) + instructions.append( + mips.AddInmediateUnsignedNode(mips.ARG_REGISTERS[1], mips.ARG_REGISTERS[1], method_index * 4)) + instructions.append( + mips.LoadWordNode(mips.ARG_REGISTERS[1], mips.RegisterRelativeLocation(mips.ARG_REGISTERS[1], 0))) + instructions.append(mips.JumpRegisterAndLinkNode(mips.ARG_REGISTERS[1])) + ''' + + @visitor.when(cil.ArgNode) def visit(self, node): - return f'ARG {node.name}' + self._pushed_args += 1 + instructions = [] + reg = mips.REGISTERS[0] + if isinstance(node.name, int): + instructions.append(mips.LoadInmediateNode(mips.ARG_REGISTERS[0], node.name)) + else: + instructions.append(mips.LoadWordNode(reg, self.get_var_location(node.name))) + instructions.extend(mips.push_register(reg)) + return instructions - @visitor.when(ReturnNode) + @visitor.when(cil.ReturnNode) def visit(self, node): - return f'RETURN {node.value if node.value is not None else ""}' + instructions = [] - @visitor.when(LoadNode) + if node.value is None or isinstance(node.value, cil.VoidNode): + instructions.append(mips.LoadInmediateNode(mips.V0_REG, 0)) + elif isinstance(node.value, int): + instructions.append(mips.LoadInmediateNode(mips.V0_REG, node.value)) + else: + instructions.append(mips.LoadWordNode(mips.V0_REG, self.get_var_location(node.value))) + return instructions + + @visitor.when(cil.LoadNode) def visit(self, node): - return f'{node.dest} = LOAD {self.visit(node.msg)}' + instructions = [] - @visitor.when(PrintStringNode) - def visit(self, node: PrintStringNode): - return f'PRINTSTRING {node.str_addr}' + string = mips.LabelRelativeLocation(self._data_section[node.msg.name].label, 0) + reg = mips.REGISTERS[0] + instructions.append(mips.LoadAddressNode(reg, string)) + + return instructions + + ''' + @visitor.when(cil.PrintStringNode) + def visit(self, node: cil.PrintStringNode): + instructions = [] + reg = mips.REGISTERS[0] + instructions.append(mips.MoveNode(reg, self.get_var_location(node.str_addr))) + instructions.append(mips.SyscallNode()) + + return instructions + ''' + + ''' @visitor.when(PrintIntNode) def visit(self, node: PrintIntNode): - return f'PRINTINT {node.value}' + instructions = [] + instructions.append(mips.LoadInmediateNode(mips.V0_REG, 1)) + + reg = self.memory_manager.get_reg_for_var(node.value) + if reg is None: + instructions.append(mips.LoadWordNode(mips.ARG_REGISTERS[0], self.get_var_location(node.value))) + else: + instructions.append(mips.MoveNode(mips.ARG_REGISTERS[0], reg)) + + instructions.append(mips.SyscallNode()) - @visitor.when(ExitNode) - def visit(self, node: ExitNode): - return f'EXIT' + return instructions + ''' - @visitor.when(CopyNode) + @visitor.when(cil.ExitNode) + def visit(self, node: cil.ExitNode): + instructions = [] + instructions.append(mips.ExitNode()) + return instructions + + ''' + @visitor.when(cil.CopyNode) def visit(self, node): - return f'{node.dest} = COPY {node.value}' + instructions = [] - @visitor.when(GetAttribNode) - def visit(self, node: GetAttribNode): - return f'{node.dest} = GETATTRIB {node.obj}.{node.attr} {node.computed_type}' + reg = mips.REGISTERS[0] + instructions.append(mips.LoadWordNode(reg, self.get_var_location(node.source))) + instructions.append(mips.LoadWordNode(mips.ARG_REGISTERS[0], mips.RegisterRelativeLocation(reg, 4))) + instructions.append(mips.ShiftLeftLogicalNode(mips.ARG_REGISTERS[0], mips.ARG_REGISTERS[0], 2)) + instructions.append(mips.JalNode("malloc")) + instructions.append(mips.MoveNode(mips.ARG_REGISTERS[2], mips.ARG_REGISTERS[0])) + instructions.append(mips.MoveNode(mips.ARG_REGISTERS[0], reg)) + instructions.append(mips.MoveNode(mips.ARG_REGISTERS[1], mips.V0_REG)) + instructions.append(mips.JalNode("copy")) - @visitor.when(ErrorNode) - def visit(self, node: ErrorNode): - return f'ERROR {self.visit(node.data)}' + if pushed: + instructions.extend(mips.pop_register(reg)) - @visitor.when(ReadNode) - def visit(self, node: ReadNode): - return f'{node.dest} = READ' + reg = self.memory_manager.get_reg_for_var(node.dest) + if reg is None: + instructions.append(mips.StoreWordNode(mips.V0_REG, self.get_var_location(node.dest))) + else: + instructions.append(mips.MoveNode(reg, mips.V0_REG)) - @visitor.when(SetAttribNode) - def visit(self, node: SetAttribNode): - return f'SETATTR {node.obj}.{node.attr}: {node.computed_type} = {node.value}' + return instructions + ''' + + @visitor.when(cil.GetAttribNode) + def visit(self, node: cil.GetAttribNode): + instructions = [] + + reg1 = mips.REGISTERS[0] + reg2 = mips.REGISTERS[1] + instructions.append(mips.LoadWordNode(reg1, self.get_var_location(node.obj))) + + tp = self._types_section[node.computed_type] + offset = (tp.attributes.index(node.attr) + 3) * mips.DOUBLE_WORD + instructions.append(mips.LoadWordNode(reg2, mips.RegisterRelativeLocation(reg1, offset))) + instructions.append(mips.StoreWordNode(reg2, self.get_var_location(node.dest))) + + return instructions + + ''' + @visitor.when(cil.ErrorNode) + def visit(self, node: cil.ErrorNode): + instructions = [] + + mips_label = self._data_section[node.data.name].label + + instructions.append(mips.LoadInmediateNode(mips.V0_REG, 4)) + instructions.append(mips.LoadAddressNode(mips.ARG_REGISTERS[0], mips_label)) + instructions.append(mips.SyscallNode()) + instructions.append(mips.LoadInmediateNode(mips.V0_REG, 10)) + instructions.append(mips.SyscallNode()) + + return instructions + ''' + + ''' + @visitor.when(cil.ReadNode) + def visit(self, node: cil.ReadNode): + instructions = [] + + instructions.append(mips.LoadInmediateNode(mips.V0_REG, 5)) + instructions.append(mips.SyscallNode()) + reg = self.memory_manager.get_reg_for_var(node.dest) + if reg is None: + instructions.append(mips.StoreWordNode(mips.V0_REG, self.get_var_location(node.dest))) + else: + instructions.append(mips.MoveNode(reg, mips.V0_REG)) - @visitor.when(LessNode) - def visit(self, node: LessNode): - return f'{node.dest} = {node.left} < {node.right}' + return instructions + ''' - @visitor.when(GotoIfNode) - def visit(self, node: GotoIfNode): - return f'GOTOIF {node.condition} {node.label}' + @visitor.when(cil.SetAttribNode) + def visit(self, node: cil.SetAttribNode): + instructions = [] - @visitor.when(GotoNode) - def visit(self, node: GotoNode): - return f'GOTO {node.label}' + tp = self._types_section[node.computed_type] + offset = (tp.attributes.index(node.attr) + 3) * mips.DOUBLE_WORD + + reg1 = mips.REGISTERS[0] + instructions.append(mips.LoadWordNode(reg1, self.get_var_location(node.obj))) - @visitor.when(LabelNode) - def visit(self, node: LabelNode): - return f'LABEL {node.label}' + reg2 = mips.REGISTERS[1] + if type(node.value) == int: + instructions.append(mips.LoadInmediateNode(reg2, node.value)) + else: + instructions.append(mips.LoadWordNode(reg2, self.get_var_location(node.value))) + + instructions.append(mips.StoreWordNode(reg2, mips.RegisterRelativeLocation(reg1, offset))) + return instructions - @visitor.when(SubstringNode) - def visit(self, node: SubstringNode): - return f'{node.dest} = SUBSTRING {node.str_value}[{node.index}:{node.index + node.length}]' + @visitor.when(cil.LessNode) + def visit(self, node: cil.LessNode): + instructions = [] - @visitor.when(ConcatNode) - def visit(self, node: ConcatNode): - return f'{node.dest} = CONCAT {node.prefix} + {node.suffix}' + reg1 = mips.REGISTERS[0] + instructions.append(mips.LoadWordNode(reg1, self.get_var_location(node.left))) - @visitor.when(LengthNode) - def visit(self, node: LengthNode): - return f'{node.dest} = LENGTH {node.source}' + reg2 = mips.REGISTERS[1] + instructions.append(mips.LoadWordNode(reg2, self.get_var_location(node.right))) + instructions.append(mips.LessNode(reg2, reg1, reg2)) + instructions.append(mips.StoreWordNode(reg2, self.get_var_location(node.dest))) - @visitor.when(EqualNode) - def visit(self, node: EqualNode): - return f'{node.dest} = {node.left} == {node.right}' + return instructions - @visitor.when(NameNode) - def visit(self, node: NameNode): - return f'{node.dest} = NAME {node.value}' + @visitor.when(cil.GotoIfNode) + def visit(self, node: cil.GotoIfNode): + instructions = [] + mips_label = self.get_label(node.label) - @visitor.when(EqualStringNode) - def visit(self, node: EqualStringNode): - return f'{node.dest} = {node.left} == {node.right}' + reg1 = mips.REGISTERS[0] + instructions.append(mips.LoadWordNode(reg1, self.get_var_location(node.condition))) + instructions.append(mips.BranchOnNotEqualNode(reg1, mips.ZERO_REG, mips_label)) - @visitor.when(ComplementNode) - def visit(self, node: ComplementNode): - return f'{node.dest} = ~{node.value}' + return instructions - @visitor.when(LessEqualNode) - def visit(self, node: LessEqualNode): - return f'{node.dest} = {node.left} <= {node.right}' + @visitor.when(cil.GotoNode) + def visit(self, node: cil.GotoNode): + mips_label = self.get_label(node.label) + return [mips.JumpNode(mips_label)] - @visitor.when(GetIndexNode) - def visit(self, node: GetIndexNode): - return f'GETINDEXNODE' + @visitor.when(cil.LabelNode) + def visit(self, node: cil.LabelNode): + return [mips.LabelNode(self.get_label(node.label))] - @visitor.when(SetIndexNode) - def visit(self, node: SetIndexNode): - return f'SETINDEXNODE' + @visitor.when(cil.SubstringNode) + def visit(self, node: cil.SubstringNode): + instructions = [] + reg1 = mips.REGISTERS[0] + instructions.append(mips.LoadWordNode(reg1, self.get_var_location(node.str_value))) + instructions.extend(mips.push_register(reg1)) + + if type(node.index) == int: + instructions.append(mips.LoadInmediateNode(reg1, node.index)) + else: + instructions.append(mips.LoadWordNode(reg1, self.get_var_location(node.index))) + instructions.extend(mips.push_register(reg1)) + + if type(node.index) == int: + instructions.append(mips.LoadInmediateNode(reg1, node.length)) + else: + instructions.append(mips.LoadWordNode(reg1, self.get_var_location(node.length))) + instructions.extend(mips.push_register(reg1)) + + instructions.append(mips.JalNode("substr")) + instructions.append(mips.StoreWordNode(mips.V0_REG, self.get_var_location(node.dest))) + return instructions + + @visitor.when(cil.ConcatNode) + def visit(self, node: cil.ConcatNode): + instructions = [] + + reg = mips.REGISTERS[0] + instructions.append(mips.LoadWordNode(reg, self.get_var_location(node.prefix))) + instructions.extend(mips.push_register(reg)) + + instructions.append(mips.LoadWordNode(reg, self.get_var_location(node.suffix))) + instructions.extend(mips.push_register(reg)) + + instructions.append(mips.LoadWordNode(reg, self.get_var_location(node.suffix))) + instructions.extend(mips.push_register(reg)) + + instructions.append(mips.JalNode("concat")) + instructions.append(mips.StoreWordNode(mips.V0_REG, self.get_var_location(node.dest))) + return instructions + + @visitor.when(cil.LengthNode) + def visit(self, node: cil.LengthNode): + instructions = [] + + reg = mips.REGISTERS[0] + instructions.append(mips.LoadWordNode(reg, self.get_var_location(node.source))) + instructions.append(mips.push_register(reg)) + + instructions.append(mips.JalNode("len")) + instructions.append(mips.StoreWordNode(mips.V0_REG, self.get_var_location(node.dest))) + + return instructions + + @visitor.when(cil.EqualNode) + def visit(self, node: cil.EqualNode): + instructions = [] + + reg1 = mips.REGISTERS[0] + if type(node.left) == cil.VoidNode: + instructions.append(mips.LoadInmediateNode(reg1, 0)) + else: + instructions.append(mips.LoadWordNode(reg1, self.get_var_location(node.left))) + + reg2 = mips.REGISTERS[0] + if type(node.left) == cil.VoidNode: + instructions.append(mips.LoadInmediateNode(reg2, 0)) + else: + instructions.append(mips.LoadWordNode(reg2, self.get_var_location(node.left))) + + instructions.append(mips.EqualNode(reg1, reg1, reg2)) + instructions.append(mips.StoreWordNode(reg1, self.get_var_location(node.dest))) + return instructions + + @visitor.when(cil.NameNode) + def visit(self, node: cil.NameNode): + instructions = [] + + reg = mips.REGISTERS[0] + instructions.append(mips.LoadAddressNode(reg, mips.TYPENAMES_TABLE_LABEL)) + + tp_number = self._types_section[node.value].index + instructions.append(mips.AdditionInmediateNode(reg, reg, tp_number * 4)) + instructions.append(mips.LoadWordNode(reg, mips.RegisterRelativeLocation(reg, 0))) + + instructions.append(mips.StoreWordNode(reg, self.get_var_location(node.dest))) + return instructions + + @visitor.when(cil.EqualStringNode) + def visit(self, node: cil.EqualStringNode): + instructions = [] + + reg1 = mips.REGISTERS[0] + instructions.append(mips.LoadWordNode(reg1, self.get_var_location(node.left))) + instructions.extend(mips.push_register(reg1)) + + instructions.append(mips.LoadWordNode(reg1, self.get_var_location(node.right))) + instructions.extend(mips.push_register(reg1)) + + instructions.append(mips.JalNode("equal_str")) + instructions.append(mips.StoreWordNode(mips.V0_REG, self.get_var_location(node.dest))) + + return instructions + + @visitor.when(cil.ComplementNode) + def visit(self, node: cil.ComplementNode): + instructions = [] + + reg1 = mips.REGISTERS[0] + instructions.append(mips.LoadWordNode(reg1, self.get_var_location(node.value))) + + instructions.append(mips.NotNode(reg1, reg1)) + instructions.append(mips.AdditionInmediateNode(reg1, reg1, 1)) + instructions.append(mips.StoreWordNode(reg1, self.get_var_location(node.dest))) + + return instructions + + @visitor.when(cil.LessEqualNode) + def visit(self, node: cil.LessEqualNode): + instructions = [] + + reg1 = mips.REGISTERS[0] + instructions.append(mips.LoadWordNode(reg1, self.get_var_location(node.left))) + + reg2 = mips.REGISTERS[0] + instructions.append(mips.LoadWordNode(reg2, self.get_var_location(node.right))) + + instructions.append(mips.LessEqualNode(reg1, reg1, reg2)) + instructions.append(mips.StoreWordNode(reg1, self.get_var_location(node.dest))) + return instructions + + ''' @visitor.when(PrefixNode) def visit(self, node: PrefixNode): return f'PREFFIXNODE' @@ -388,9 +648,18 @@ def visit(self, node: ToStrNode): @visitor.when(VoidNode) def visit(self, node: VoidNode): return 'VOID' + ''' + + @visitor.when(cil.NotNode) + def visit(self, node: cil.NotNode): + instructions = [] + + reg1 = mips.REGISTERS[0] + instructions.append(mips.LoadWordNode(reg1, self.get_var_location(node.value))) + instructions.append(mips.NotNode(reg1, reg1)) + instructions.append(mips.StoreWordNode(reg1, self.get_var_location(node.dest))) + + return instructions - @visitor.when(NotNode) - def visit(self, node: NotNode): - return f'{node.dest} = NOT {node.value}' diff --git a/src/core/mips/MipsAst.py b/src/core/mips/MipsAst.py index 5902d50d3..24515867a 100644 --- a/src/core/mips/MipsAst.py +++ b/src/core/mips/MipsAst.py @@ -217,10 +217,32 @@ def __init__(self, reg, addr): self.reg = reg self.addr = addr +class LoadAddressNode(InstructionNode): + def __init__(self, reg, label): + self.reg = reg + self.label = label + +class BranchOnNotEqualNode(InstructionNode): + def __init__(self, reg1, reg2, label): + self.reg1 = reg1 + self.reg2 = reg2 + self.label = label + +class LabelNode(InstructionNode): + def __init__(self, name): + self.name = name + +class NotNode(InstructionNode): + def __init__(self, dest, src): + self.dest = dest + self.src = src + class DataNode(Node): def __init__(self, label): self.label = label + + class StringConst(DataNode): def __init__(self, label, string): super().__init__(label) @@ -276,3 +298,32 @@ def label(self): def offset(self): return self._offset + +def push_register(reg): + move_stack = AdditionInmediateNode(SP_REG, SP_REG, -DOUBLE_WORD) + save_location = RegisterRelativeLocation(SP_REG, 0) + save_register = StoreWordNode(reg, save_location) + return [move_stack, save_register] + +def pop_register(reg): + load_value = LoadWordNode(reg, RegisterRelativeLocation(SP_REG, 0)) + move_stack = AdditionInmediateNode(SP_REG, SP_REG, DOUBLE_WORD) + return [load_value, move_stack] + + +def create_object(reg1, reg2): + instructions = [] + + instructions.append(ShiftLeftLogicalNode(reg1, reg1, 2)) + instructions.append(LoadAddressNode(reg2, PROTO_TABLE_LABEL)) + instructions.append(AddUnsignedNode(reg2, reg2, reg1)) + instructions.append(LoadWordNode(reg2, RegisterRelativeLocation(reg2, 0))) + instructions.append(LoadWordNode(ARG_REGISTERS[0], RegisterRelativeLocation(reg2, 4))) + instructions.append(ShiftLeftLogicalNode(ARG_REGISTERS[0], ARG_REGISTERS[0], 2)) + instructions.append(JumpAndLinkNode("malloc")) + instructions.append(MoveNode(ARG_REGISTERS[2], ARG_REGISTERS[0])) + instructions.append(MoveNode(ARG_REGISTERS[0], reg2)) + instructions.append(MoveNode(ARG_REGISTERS[1], V0_REG)) + instructions.append(JumpAndLinkNode("copy")) + + return instructions From f88d04d502d68c007812002cffd5c87e74fe360b Mon Sep 17 00:00:00 2001 From: mavaldivie Date: Fri, 11 Feb 2022 17:25:10 -0500 Subject: [PATCH 10/75] Finished CILtoMIPS visitor. --- src/Main.py | 5 + src/core/cil/BaseCOOLToCILVisitor.py | 68 +++++------ src/core/cil/CILAst.py | 23 ++-- src/core/cil/COOLToCILVisitor.py | 11 +- src/core/mips/CilToMipsVisitor.py | 168 +++++++++++++-------------- src/core/mips/MipsAst.py | 27 +++-- 6 files changed, 163 insertions(+), 139 deletions(-) diff --git a/src/Main.py b/src/Main.py index 6fb4c8eb9..f8c887abd 100644 --- a/src/Main.py +++ b/src/Main.py @@ -6,6 +6,7 @@ from core.semantic.Type_Checker import Type_Checker from core.cil.COOLToCILVisitor import COOLToCILVisitor from core.cil.CILAst import get_formatter +from core.mips.CilToMipsVisitor import CILToMIPSVisitor def main(args): try: @@ -50,6 +51,10 @@ def main(args): CILast = CILVisitor.visit(COOLast, scope) print(get_formatter()(CILast)) + MIPSVisitor = CILToMIPSVisitor() + MIPSAst = MIPSVisitor.visit(CILast) + print("here") + diff --git a/src/core/cil/BaseCOOLToCILVisitor.py b/src/core/cil/BaseCOOLToCILVisitor.py index 158478aba..0a479ef99 100644 --- a/src/core/cil/BaseCOOLToCILVisitor.py +++ b/src/core/cil/BaseCOOLToCILVisitor.py @@ -8,6 +8,7 @@ def __init__(self, context): self.dotdata = [] self.dotcode = [] + self.types_map = {} self.current_type = None self.current_method = None self.current_function = None @@ -71,6 +72,7 @@ def register_function(self, function_name): def register_type(self, name): type_node = cil.TypeNode(name) + self.types_map[name] = type_node self.dottypes.append(type_node) return type_node @@ -137,8 +139,8 @@ def register_builtin(self): self.register_instruction(cil.CopyNode(result, self.vself.name)) self.register_instruction(cil.ReturnNode(result)) - type_node.methods = [self.to_function_name(name, 'Object') for name in ['abort', 'type_name', 'copy']] - type_node.methods += [self.init_name('Object')] + type_node.methods = {name: self.to_function_name(name, 'Object') for name in ['abort', 'type_name', 'copy']} + type_node.methods['init'] = self.init_name('Object') obj_methods = ['abort', 'type_name', 'copy'] # IO @@ -151,24 +153,24 @@ def register_builtin(self): self.current_function = self.register_function(self.to_function_name('out_string', 'IO')) self.register_param(self.vself) - self.register_param(VariableInfo('x', None)) + x = self.register_param(VariableInfo('x', None)) vname = self.define_internal_local() - self.register_instruction(cil.GetAttribNode(vname, 'x', 'value', 'String')) + self.register_instruction(cil.GetAttribNode(vname, x, 'value', 'String')) self.register_instruction(cil.PrintStringNode(vname)) self.register_instruction(cil.ReturnNode(self.vself.name)) self.current_function = self.register_function(self.to_function_name('out_int', 'IO')) self.register_param(self.vself) - self.register_param(VariableInfo('x', None)) + x = self.register_param(VariableInfo('x', None)) vname = self.define_internal_local() - self.register_instruction(cil.GetAttribNode(vname, 'x', 'value', 'Int')) + self.register_instruction(cil.GetAttribNode(vname, x, 'value', 'Int')) self.register_instruction(cil.PrintIntNode(vname)) self.register_instruction(cil.ReturnNode(self.vself.name)) self.current_function = self.register_function(self.to_function_name('in_string', 'IO')) self.register_param(self.vself) result = self.define_internal_local() - self.register_instruction(cil.ReadNode(result)) + self.register_instruction(cil.ReadStringNode(result)) instance = self.define_internal_local() self.register_instruction(cil.ArgNode(result)) self.register_instruction(cil.StaticCallNode(self.init_name('String'), instance)) @@ -177,28 +179,28 @@ def register_builtin(self): self.current_function = self.register_function(self.to_function_name('in_int', 'IO')) self.register_param(self.vself) result = self.define_internal_local() - self.register_instruction(cil.ReadNode(result)) + self.register_instruction(cil.ReadIntNode(result)) instance = self.define_internal_local() self.register_instruction(cil.ArgNode(result)) self.register_instruction(cil.StaticCallNode(self.init_name('Int'), instance)) self.register_instruction(cil.ReturnNode(instance)) - type_node.methods = [self.to_function_name(method, 'Object') for method in obj_methods] - type_node.methods += [self.to_function_name(name, 'IO') for name in - ['out_string', 'out_int', 'in_string', 'in_int']] - type_node.methods += [self.init_name('IO')] + type_node.methods = {method: self.to_function_name(method, 'Object') for method in obj_methods} + type_node.methods.update({name: self.to_function_name(name, 'IO') for name in + ['out_string', 'out_int', 'in_string', 'in_int']}) + type_node.methods['init'] = self.init_name('IO') # String type_node = self.register_type('String') type_node.attributes = ['value', 'length'] self.current_function = self.register_function(self.init_name('String')) - self.register_param(VariableInfo('val', None)) + val = self.register_param(VariableInfo('val', None)) instance = self.define_internal_local() self.register_instruction(cil.AllocateNode('String', instance)) - self.register_instruction(cil.SetAttribNode(instance, 'value', 'val', 'String')) + self.register_instruction(cil.SetAttribNode(instance, 'value', val, 'String')) result = self.define_internal_local() - self.register_instruction(cil.LengthNode(result, 'val')) + self.register_instruction(cil.LengthNode(result, val)) attr = self.define_internal_local() self.register_instruction(cil.ArgNode(result)) self.register_instruction(cil.StaticCallNode(self.init_name('Int'), attr)) @@ -213,15 +215,15 @@ def register_builtin(self): self.current_function = self.register_function(self.to_function_name('concat', 'String')) self.register_param(self.vself) - self.register_param(VariableInfo('s', None)) + s = self.register_param(VariableInfo('s', None)) str_1 = self.define_internal_local() str_2 = self.define_internal_local() length_1 = self.define_internal_local() length_2 = self.define_internal_local() self.register_instruction(cil.GetAttribNode(str_1, self.vself.name, 'value', 'String')) - self.register_instruction(cil.GetAttribNode(str_2, 's', 'value', 'String')) + self.register_instruction(cil.GetAttribNode(str_2, s, 'value', 'String')) self.register_instruction(cil.GetAttribNode(length_1, self.vself.name, 'length', 'String')) - self.register_instruction(cil.GetAttribNode(length_2, 's', 'length', 'String')) + self.register_instruction(cil.GetAttribNode(length_2, s, 'length', 'String')) self.register_instruction(cil.GetAttribNode(length_1, length_1, 'value', 'Int')) self.register_instruction(cil.GetAttribNode(length_2, length_2, 'value', 'Int')) self.register_instruction(cil.PlusNode(length_1, length_1, length_2)) @@ -235,8 +237,8 @@ def register_builtin(self): self.current_function = self.register_function(self.to_function_name('substr', 'String')) self.register_param(self.vself) - self.register_param(VariableInfo('i', None)) - self.register_param(VariableInfo('l', None)) + i = self.register_param(VariableInfo('i', None)) + l = self.register_param(VariableInfo('l', None)) result = self.define_internal_local() index_value = self.define_internal_local() length_value = self.define_internal_local() @@ -245,8 +247,8 @@ def register_builtin(self): less_value = self.define_internal_local() str_value = self.define_internal_local() self.register_instruction(cil.GetAttribNode(str_value, self.vself.name, 'value', 'String')) - self.register_instruction(cil.GetAttribNode(index_value, 'i', 'value', 'Int')) - self.register_instruction(cil.GetAttribNode(length_value, 'l', 'value', 'Int')) + self.register_instruction(cil.GetAttribNode(index_value, i, 'value', 'Int')) + self.register_instruction(cil.GetAttribNode(length_value, l, 'value', 'Int')) # Check Out of range error self.register_instruction(cil.GetAttribNode(length_attr, self.vself.name, 'length', 'String')) self.register_instruction(cil.PlusNode(length_substr, length_value, index_value)) @@ -258,36 +260,36 @@ def register_builtin(self): self.register_instruction(cil.StaticCallNode(self.init_name('String'), instance)) self.register_instruction(cil.ReturnNode(instance)) - type_node.methods = [self.to_function_name(method, 'Object') for method in obj_methods] - type_node.methods += [self.to_function_name(name, 'String') for name in ['length', 'concat', 'substr']] - type_node.methods += [self.init_name('String')] + type_node.methods = {method: self.to_function_name(method, 'Object') for method in obj_methods} + type_node.methods.update({name: self.to_function_name(name, 'String') for name in ['length', 'concat', 'substr']}) + type_node.methods['init'] = self.init_name('String') # Int type_node = self.register_type('Int') type_node.attributes = ['value'] self.current_function = self.register_function(self.init_name('Int')) - self.register_param(VariableInfo('val', None)) + val = self.register_param(VariableInfo('val', None)) instance = self.define_internal_local() self.register_instruction(cil.AllocateNode('Int', instance)) - self.register_instruction(cil.SetAttribNode(instance, 'value', 'val', 'Int')) + self.register_instruction(cil.SetAttribNode(instance, 'value', val, 'Int')) self.register_instruction(cil.ReturnNode(instance)) - type_node.methods = [self.to_function_name(method, 'Object') for method in obj_methods] - type_node.methods += [self.init_name('Int')] + type_node.methods = {method:self.to_function_name(method, 'Object') for method in obj_methods} + type_node.methods['init'] = self.init_name('Int') # Bool type_node = self.register_type('Bool') type_node.attributes = ['value'] self.current_function = self.register_function(self.init_name('Bool')) - self.register_param(VariableInfo('val', None)) + val = self.register_param(VariableInfo('val', None)) instance = self.define_internal_local() self.register_instruction(cil.AllocateNode('Bool', instance)) - self.register_instruction(cil.SetAttribNode(instance, 'value', 'val', 'Bool')) + self.register_instruction(cil.SetAttribNode(instance, 'value', val, 'Bool')) self.register_instruction(cil.ReturnNode(instance)) - type_node.methods = [self.to_function_name(method, 'Object') for method in obj_methods] - type_node.methods += [self.init_name('Bool')] + type_node.methods = {method: self.to_function_name(method, 'Object') for method in obj_methods} + type_node.methods['init'] = self.init_name('Bool') diff --git a/src/core/cil/CILAst.py b/src/core/cil/CILAst.py index a34e9749a..29656407e 100644 --- a/src/core/cil/CILAst.py +++ b/src/core/cil/CILAst.py @@ -13,7 +13,7 @@ class TypeNode(Node): def __init__(self, name): self.name = name self.attributes = [] - self.methods = [] + self.methods = {} class DataNode(Node): def __init__(self, vname, value): @@ -139,8 +139,9 @@ def __repr__(self): return f"{self.dest} = CALL {self.function}" class DynamicCallNode(InstructionNode): - def __init__(self, xtype, method, dest): - self.type = xtype + def __init__(self, type, obj, method, dest): + self.type = type + self.obj = obj self.method = method self.dest = dest @@ -198,7 +199,11 @@ def __init__(self, dest, value): self.dest = dest self.value = value -class ReadNode(InstructionNode): +class ReadStringNode(InstructionNode): + def __init__(self, dest): + self.dest = dest + +class ReadIntNode(InstructionNode): def __init__(self, dest): self.dest = dest @@ -316,7 +321,7 @@ def visit(self, node): @visitor.when(DynamicCallNode) def visit(self, node): - return f'{node.dest} = VCALL {node.type} {node.method.lex}' + return f'{node.dest} = VCALL {node.type} {node.method}' @visitor.when(ArgNode) def visit(self, node): @@ -354,8 +359,12 @@ def visit(self, node: GetAttribNode): def visit(self, node: ErrorNode): return f'ERROR {self.visit(node.data)}' - @visitor.when(ReadNode) - def visit(self, node: ReadNode): + @visitor.when(ReadStringNode) + def visit(self, node: ReadStringNode): + return f'{node.dest} = READ' + + @visitor.when(ReadIntNode) + def visit(self, node: ReadIntNode): return f'{node.dest} = READ' @visitor.when(SetAttribNode) diff --git a/src/core/cil/COOLToCILVisitor.py b/src/core/cil/COOLToCILVisitor.py index 292583bb1..77fc9d8fc 100644 --- a/src/core/cil/COOLToCILVisitor.py +++ b/src/core/cil/COOLToCILVisitor.py @@ -20,7 +20,7 @@ def visit(self, node : cool.ProgramNode, scope : Scope): self.register_instruction(cil.StaticCallNode(self.to_function_name('main', 'Main'), main_instance)) self.register_instruction(cil.ReturnNode(0)) - #self.register_builtin() + self.register_builtin() self.current_function = None for x, y in zip(node.declarations, scope.childs): @@ -53,9 +53,8 @@ def visit(self, node : cool.ClassDeclarationNode, scope : Scope): type.attributes = [i.name for i in self.current_type.attributes] iter_type = self.current_type while iter_type is not None: - type.methods += [self.to_function_name(i, iter_type.name) for i in iter_type.methods.keys()] + type.methods.update({i: self.to_function_name(i, iter_type.name) for i in iter_type.methods.keys()}) iter_type = iter_type.parent - type.methods.reverse() for feat, child in zip(node.features, scope.childs): if isinstance(feat, cool.FuncDeclarationNode): @@ -457,7 +456,8 @@ def visit(self, node: cool.FunctionCallNode, scope: Scope): else: type = self.define_internal_local() self.register_instruction(cil.TypeOfNode(type, node.obj.ret_expr)) - self.register_instruction(cil.DynamicCallNode(type, node.id, ret)) + stype = node.obj.static_type.name + self.register_instruction(cil.DynamicCallNode(stype, type, self.types_map[stype].methods[node.id.lex], ret)) node.ret_expr = ret @visitor.when(cool.MemberCallNode) @@ -475,7 +475,8 @@ def visit(self, node: cool.MemberCallNode, scope: Scope): self.register_instruction(cil.ArgNode(self.vself.name)) for arg in args: self.register_instruction(arg) - self.register_instruction(cil.DynamicCallNode(type, node.id, ret)) + stype = self.current_type.name + self.register_instruction(cil.DynamicCallNode(stype, type, self.types_map[stype].methods[node.id.lex], ret)) node.ret_expr = ret @visitor.when(cool.NewNode) diff --git a/src/core/mips/CilToMipsVisitor.py b/src/core/mips/CilToMipsVisitor.py index 1a513ccb7..3fb1f1d74 100644 --- a/src/core/mips/CilToMipsVisitor.py +++ b/src/core/mips/CilToMipsVisitor.py @@ -2,7 +2,7 @@ import core.mips.MipsAst as mips from ..tools import visitor -class CILtoMIPSvisitor: +class CILToMIPSVisitor: def __init__(self): self.type_label_count = 0 self.data_label_count = 0 @@ -99,7 +99,7 @@ def visit(self, node): self._data_section[node.name] = mips.StringConst(name_label, node.name) type_label = self.generate_type_label() - methods = {key: self._function_names[value] for key, value in node.methods} + methods = [self._function_names[key] for key in node.methods.values()] defaults = [] if node.name == "String": defaults = [('value', 'default_str'), ('len', 'type_4_proto')] @@ -115,15 +115,43 @@ def visit(self, node): @visitor.when(cil.FunctionNode) def visit(self, node): - raise NotImplementedError() + label = self._function_names[node.name] + params = [param.name for param in node.params] + localvars = [local.name for local in node.localvars] + size_for_locals = len(localvars) * mips.DOUBLE_WORD + + new_func = mips.FunctionNode(label, params, localvars) + self.enter_function(node.name, new_func) + self._current_function = new_func + self._labels = {} - @visitor.when(cil.ParamNode) - def visit(self, node): - raise NotImplementedError() + for instruction in node.instructions: + if isinstance(instruction, cil.LabelNode): + mips_label = self.generate_code_label() + self.register_label(instruction.label, mips_label) - @visitor.when(cil.LocalNode) - def visit(self, node): - raise NotImplementedError() + instructions = [] + instructions.extend(mips.push_register(mips.FP_REG)) + instructions.append(mips.AdditionInmediateNode(mips.FP_REG, mips.SP_REG, 4)) + instructions.append(mips.AdditionInmediateNode(mips.SP_REG, mips.SP_REG, -size_for_locals)) + + reg = mips.REGISTERS[0] + for param in params: + instructions.append(mips.LoadWordNode(reg, self.get_var_location(param))) + for i in node.instructions: + instructions.extend(self.visit(i)) + + instructions.append(mips.AdditionInmediateNode(mips.SP_REG, mips.SP_REG, size_for_locals)) + instructions.extend(mips.pop_register(mips.FP_REG)) + + if self._current_function.label != 'main': + instructions.append(mips.JumpNode(mips.RA_REG)) + else: + instructions.append(mips.LoadInmediateNode(mips.V0_REG, 10)) + instructions.append(mips.SyscallNode()) + + new_func.instructions = instructions + self._current_function = None @visitor.when(cil.AssignNode) def visit(self, node): @@ -242,19 +270,7 @@ def visit(self, node): reg2 = mips.REGISTERS[1] instructions.append(mips.LoadInmediateNode(reg1, type)) - instructions.append(mips.ShiftLeftLogicalNode(reg1, reg2, 2)) - instructions.append(mips.LoadAddressNode(reg2, mips.PROTO_TABLE_LABEL)) - instructions.append(mips.AdditionNode(reg2, reg2, reg1)) - instructions.append(mips.LoadWordNode(reg2, mips.RegisterRelativeLocation(reg2, 0))) - reg3 = mips.ARG_REGISTERS[0] - instructions.append(mips.LoadWordNode(reg3, mips.RegisterRelativeLocation(reg2, 4))) - instructions.append(mips.ShiftLeftLogicalNode(reg3, reg3, 2)) - instructions.append(mips.JalNode('malloc')) - instructions.append(mips.MoveNode(mips.ARG_REGISTERS[2], reg3)) - instructions.append(mips.MoveNode(reg3, reg2)) - instructions.append(mips.MoveNode(mips.ARG_REGISTERS[1], mips.V0_REG)) - instructions.append(mips.JalNode('copy')) - + instructions.extend(mips.create_object(reg1, reg2)) instructions.append(mips.StoreWordNode(mips.V0_REG, self.get_var_location(node.dest))) return instructions @@ -284,29 +300,33 @@ def visit(self, node): self._pushed_args = 0 return instructions - ''' + @visitor.when(cil.DynamicCallNode) def visit(self, node): instructions = [] type = self._types_section[node.type] - method = type.methods.index(node.method) + method = type.methods.index(self._function_names[node.method]) - reg = mips.REGISTERS[0] - instructions.append(mips.LoadWordNode(reg, self.get_var_location(node.type))) - instructions.append(mips.LoadAddressNode(mips.ARG_REGISTERS[1], mips.PROTO_TABLE_LABEL)) - instructions.append(mips.ShiftLeftLogicalNode(mips.ARG_REGISTERS[2], reg, 2)) - instructions.append(mips.AddUnsignedNode(mips.ARG_REGISTERS[1], mips.ARG_REGISTERS[1], mips.ARG_REGISTERS[2])) - instructions.append( - mips.LoadWordNode(mips.ARG_REGISTERS[1], mips.RegisterRelativeLocation(mips.ARG_REGISTERS[1], 0))) - instructions.append( - mips.LoadWordNode(mips.ARG_REGISTERS[1], mips.RegisterRelativeLocation(mips.ARG_REGISTERS[1], 8))) - instructions.append( - mips.AddInmediateUnsignedNode(mips.ARG_REGISTERS[1], mips.ARG_REGISTERS[1], method_index * 4)) - instructions.append( - mips.LoadWordNode(mips.ARG_REGISTERS[1], mips.RegisterRelativeLocation(mips.ARG_REGISTERS[1], 0))) - instructions.append(mips.JumpRegisterAndLinkNode(mips.ARG_REGISTERS[1])) - ''' + reg1 = mips.REGISTERS[0] + reg2 = mips.REGISTERS[1] + instructions.append(mips.LoadWordNode(reg1, self.get_var_location(node.obj))) + instructions.append(mips.LoadAddressNode(reg2, mips.PROTOTYPE_LABEL)) + instructions.append(mips.ShiftLeftNode(reg2, reg1, 2)) + instructions.append(mips.AdditionNode(reg1, reg1, reg2)) + instructions.append(mips.LoadWordNode(reg1, mips.RegisterRelativeLocation(reg1, 0))) + instructions.append(mips.LoadWordNode(reg1, mips.RegisterRelativeLocation(reg1, 8))) + instructions.append(mips.AdditionInmediateNode(reg1, reg1, method * 4)) + instructions.append(mips.LoadWordNode(reg1, mips.RegisterRelativeLocation(reg1, 0))) + instructions.append(mips.JalNode(reg1)) + + instructions.append(mips.StoreWordNode(mips.V0_REG, self.get_var_location(node.dest))) + + if self._pushed_args > 0: + instructions.append(mips.AdditionInmediateNode(mips.SP_REG, mips.SP_REG, self._pushed_args * mips.DOUBLE_WORD)) + self._pushed_args = 0 + + return instructions @visitor.when(cil.ArgNode) def visit(self, node): @@ -343,66 +363,45 @@ def visit(self, node): return instructions - ''' @visitor.when(cil.PrintStringNode) def visit(self, node: cil.PrintStringNode): instructions = [] - reg = mips.REGISTERS[0] - instructions.append(mips.MoveNode(reg, self.get_var_location(node.str_addr))) + instructions.append(mips.LoadInmediateNode(mips.V0_REG, 4)) + instructions.append(mips.LoadWordNode(mips.ARG_REGISTERS[0], self.get_var_location(node.str_addr))) instructions.append(mips.SyscallNode()) - return instructions - ''' - ''' - @visitor.when(PrintIntNode) - def visit(self, node: PrintIntNode): + @visitor.when(cil.PrintIntNode) + def visit(self, node: cil.PrintIntNode): instructions = [] instructions.append(mips.LoadInmediateNode(mips.V0_REG, 1)) - - reg = self.memory_manager.get_reg_for_var(node.value) - if reg is None: - instructions.append(mips.LoadWordNode(mips.ARG_REGISTERS[0], self.get_var_location(node.value))) - else: - instructions.append(mips.MoveNode(mips.ARG_REGISTERS[0], reg)) - + instructions.append(mips.LoadWordNode(mips.ARG_REGISTERS[0], self.get_var_location(node.value))) instructions.append(mips.SyscallNode()) - return instructions - ''' @visitor.when(cil.ExitNode) def visit(self, node: cil.ExitNode): instructions = [] - instructions.append(mips.ExitNode()) + instructions.append(mips.LoadInmediateNode(mips.V0_REG, 10)) + instructions.append(mips.SyscallNode()) return instructions - ''' @visitor.when(cil.CopyNode) def visit(self, node): instructions = [] reg = mips.REGISTERS[0] - instructions.append(mips.LoadWordNode(reg, self.get_var_location(node.source))) + instructions.append(mips.LoadWordNode(reg, self.get_var_location(node.value))) instructions.append(mips.LoadWordNode(mips.ARG_REGISTERS[0], mips.RegisterRelativeLocation(reg, 4))) - instructions.append(mips.ShiftLeftLogicalNode(mips.ARG_REGISTERS[0], mips.ARG_REGISTERS[0], 2)) + instructions.append(mips.ShiftLeftNode(mips.ARG_REGISTERS[0], mips.ARG_REGISTERS[0], 2)) instructions.append(mips.JalNode("malloc")) instructions.append(mips.MoveNode(mips.ARG_REGISTERS[2], mips.ARG_REGISTERS[0])) instructions.append(mips.MoveNode(mips.ARG_REGISTERS[0], reg)) instructions.append(mips.MoveNode(mips.ARG_REGISTERS[1], mips.V0_REG)) instructions.append(mips.JalNode("copy")) - - if pushed: - instructions.extend(mips.pop_register(reg)) - - reg = self.memory_manager.get_reg_for_var(node.dest) - if reg is None: - instructions.append(mips.StoreWordNode(mips.V0_REG, self.get_var_location(node.dest))) - else: - instructions.append(mips.MoveNode(reg, mips.V0_REG)) + instructions.append(mips.StoreWordNode(mips.V0_REG, self.get_var_location(node.dest))) return instructions - ''' @visitor.when(cil.GetAttribNode) def visit(self, node: cil.GetAttribNode): @@ -419,13 +418,11 @@ def visit(self, node: cil.GetAttribNode): return instructions - ''' @visitor.when(cil.ErrorNode) def visit(self, node: cil.ErrorNode): instructions = [] mips_label = self._data_section[node.data.name].label - instructions.append(mips.LoadInmediateNode(mips.V0_REG, 4)) instructions.append(mips.LoadAddressNode(mips.ARG_REGISTERS[0], mips_label)) instructions.append(mips.SyscallNode()) @@ -433,20 +430,25 @@ def visit(self, node: cil.ErrorNode): instructions.append(mips.SyscallNode()) return instructions - ''' + + @visitor.when(cil.ReadIntNode) + def visit(self, node: cil.ReadIntNode): + instructions = [] + + instructions.append(mips.LoadInmediateNode(mips.V0_REG, 5)) + instructions.append(mips.SyscallNode()) + instructions.append(mips.StoreWordNode(mips.V0_REG, self.get_var_location(node.dest))) + + return instructions ''' - @visitor.when(cil.ReadNode) - def visit(self, node: cil.ReadNode): + @visitor.when(cil.ReadStringNode) + def visit(self, node: cil.ReadStringNode): instructions = [] instructions.append(mips.LoadInmediateNode(mips.V0_REG, 5)) instructions.append(mips.SyscallNode()) - reg = self.memory_manager.get_reg_for_var(node.dest) - if reg is None: - instructions.append(mips.StoreWordNode(mips.V0_REG, self.get_var_location(node.dest))) - else: - instructions.append(mips.MoveNode(reg, mips.V0_REG)) + instructions.append(mips.StoreWordNode(mips.V0_REG, self.get_var_location(node.dest))) return instructions ''' @@ -584,7 +586,7 @@ def visit(self, node: cil.NameNode): instructions = [] reg = mips.REGISTERS[0] - instructions.append(mips.LoadAddressNode(reg, mips.TYPENAMES_TABLE_LABEL)) + instructions.append(mips.LoadAddressNode(reg, mips.TYPES_LABEL)) tp_number = self._types_section[node.value].index instructions.append(mips.AdditionInmediateNode(reg, reg, tp_number * 4)) @@ -644,10 +646,6 @@ def visit(self, node: PrefixNode): @visitor.when(ToStrNode) def visit(self, node: ToStrNode): return f'{node.dest} = str({node.value})' - - @visitor.when(VoidNode) - def visit(self, node: VoidNode): - return 'VOID' ''' @visitor.when(cil.NotNode) diff --git a/src/core/mips/MipsAst.py b/src/core/mips/MipsAst.py index 24515867a..d3c5b9c8f 100644 --- a/src/core/mips/MipsAst.py +++ b/src/core/mips/MipsAst.py @@ -1,9 +1,9 @@ - - DOUBLE_WORD = 4 REGISTERS_NAMES = ['t0', 't1', 't2', 't3', 't4', 't5', 't6', 't7', 't8'] ARG_REGISTERS_NAMES = ['a0', 'a1', 'a2', 'a3'] +TYPES_LABEL = "types_table" +PROTOTYPE_LABEL = "prototype_table" class Register: def __init__(self, name): @@ -32,7 +32,7 @@ def __init__(self, data, types, functions): class FunctionNode(Node): - def __init__(self, label, instructions, params, localvars): + def __init__(self, label, params, localvars): self.label = label self.instructions = [] self.params = params @@ -237,6 +237,15 @@ def __init__(self, dest, src): self.dest = dest self.src = src +class ShiftLeftNode(InstructionNode): + def __init__(self, dest, src, bits): + self.dest = dest + self.src = src + self.bits = bits + +class SyscallNode(InstructionNode): + pass + class DataNode(Node): def __init__(self, label): self.label = label @@ -314,16 +323,16 @@ def pop_register(reg): def create_object(reg1, reg2): instructions = [] - instructions.append(ShiftLeftLogicalNode(reg1, reg1, 2)) - instructions.append(LoadAddressNode(reg2, PROTO_TABLE_LABEL)) - instructions.append(AddUnsignedNode(reg2, reg2, reg1)) + instructions.append(ShiftLeftNode(reg1, reg1, 2)) + instructions.append(LoadAddressNode(reg2, PROTOTYPE_LABEL)) + instructions.append(AdditionNode(reg2, reg2, reg1)) instructions.append(LoadWordNode(reg2, RegisterRelativeLocation(reg2, 0))) instructions.append(LoadWordNode(ARG_REGISTERS[0], RegisterRelativeLocation(reg2, 4))) - instructions.append(ShiftLeftLogicalNode(ARG_REGISTERS[0], ARG_REGISTERS[0], 2)) - instructions.append(JumpAndLinkNode("malloc")) + instructions.append(ShiftLeftNode(ARG_REGISTERS[0], ARG_REGISTERS[0], 2)) + instructions.append(JalNode("malloc")) instructions.append(MoveNode(ARG_REGISTERS[2], ARG_REGISTERS[0])) instructions.append(MoveNode(ARG_REGISTERS[0], reg2)) instructions.append(MoveNode(ARG_REGISTERS[1], V0_REG)) - instructions.append(JumpAndLinkNode("copy")) + instructions.append(JalNode("copy")) return instructions From 64dc8f9b29e82441fbee49e9b10a2d7083573f45 Mon Sep 17 00:00:00 2001 From: ClaudiaOM Date: Fri, 11 Feb 2022 22:26:54 -0500 Subject: [PATCH 11/75] Line and column for CIL Classes --- src/Main.py | 3 +- src/core/cil/BaseCOOLToCILVisitor.py | 347 ++++++++-------- src/core/cil/CILAst.py | 101 +++-- src/core/cil/COOLToCILVisitor.py | 567 +++++++++++++++------------ 4 files changed, 561 insertions(+), 457 deletions(-) diff --git a/src/Main.py b/src/Main.py index 6fb4c8eb9..39fb777b5 100644 --- a/src/Main.py +++ b/src/Main.py @@ -7,6 +7,7 @@ from core.cil.COOLToCILVisitor import COOLToCILVisitor from core.cil.CILAst import get_formatter + def main(args): try: with open(args.file, 'r') as fd: @@ -51,8 +52,6 @@ def main(args): print(get_formatter()(CILast)) - - if __name__ == "__main__": import argparse diff --git a/src/core/cil/BaseCOOLToCILVisitor.py b/src/core/cil/BaseCOOLToCILVisitor.py index 158478aba..e0bb52665 100644 --- a/src/core/cil/BaseCOOLToCILVisitor.py +++ b/src/core/cil/BaseCOOLToCILVisitor.py @@ -18,8 +18,8 @@ def __init__(self, context): self.var_names = {} - self.breakline_data = self.register_data('\n') - self.emptystring_data = self.register_data('') + self.breakline_data = self.register_data('\n', 0, 0) + self.emptystring_data = self.register_data('', 0, 0) @property def params(self): @@ -37,25 +37,25 @@ def ids(self): def instructions(self): return self.current_function.instructions - def register_param(self, vinfo): + def register_param(self, vinfo, line, column): name = vinfo.name vinfo.name = f'local_param_{self.current_function.name}_{name}_{len(self.params)}' - param_node = cil.ParamNode(vinfo.name) + param_node = cil.ParamNode(vinfo.name, line, column) self.params.append(param_node) self.var_names[name] = vinfo.name return vinfo.name - def register_local(self, vinfo): + def register_local(self, vinfo, line, column): name = vinfo.name vinfo.name = f'local_{self.current_function.name}_{vinfo.name}_{len(self.localvars)}' - local_node = cil.LocalNode(vinfo.name) + local_node = cil.LocalNode(vinfo.name, line, column) self.localvars.append(local_node) self.var_names[name] = vinfo.name return vinfo.name - def define_internal_local(self): + def define_internal_local(self, line, column): vinfo = VariableInfo('internal', None) - return self.register_local(vinfo) + return self.register_local(vinfo, line, column) def register_instruction(self, instruction): self.instructions.append(instruction) @@ -64,26 +64,26 @@ def register_instruction(self, instruction): def to_function_name(self, method_name, type_name): return f'function_{method_name}_at_{type_name}' - def register_function(self, function_name): - function_node = cil.FunctionNode(function_name, [], [], []) + def register_function(self, function_name, line, column): + function_node = cil.FunctionNode(function_name, [], [], [], line, column) self.dotcode.append(function_node) return function_node - def register_type(self, name): - type_node = cil.TypeNode(name) + def register_type(self, name, line, column): + type_node = cil.TypeNode(name, line, column) self.dottypes.append(type_node) return type_node - def register_data(self, value): + def register_data(self, value, line, column): vname = f'data_{len(self.dotdata)}' - data_node = cil.DataNode(vname, value) + data_node = cil.DataNode(vname, value, line, column) self.dotdata.append(data_node) return data_node - def register_label(self, label): + def register_label(self, label, line, column): lname = f'{label}_{self.current_function.labels_count}' self.current_function.labels_count += 1 - return cil.LabelNode(lname) + return cil.LabelNode(lname, line, column) def init_name(self, name): return f'init_at_{name}' @@ -91,97 +91,98 @@ def init_name(self, name): def init_attr_name(self, name): return f'init_attr_at_{name}' - def register_runtime_error(self, condition, msg): - error_node = self.register_label('error_label') - continue_node = self.register_label('continue_label') - self.register_instruction(cil.GotoIfNode(condition, error_node.label)) - self.register_instruction(cil.GotoNode(continue_node.label)) + def register_runtime_error(self, condition, msg, line, column): + error_node = self.register_label('error_label', line, column) + continue_node = self.register_label('continue_label', line, column) + self.register_instruction(cil.GotoIfNode(condition, error_node.label, line, column)) + self.register_instruction(cil.GotoNode(continue_node.label, line, column)) self.register_instruction(error_node) - data_node = self.register_data(msg) - self.register_instruction(cil.ErrorNode(data_node)) + data_node = self.register_data(msg, line, column) + self.register_instruction(cil.ErrorNode(data_node, line, column)) self.register_instruction(continue_node) def register_builtin(self): # Object - type_node = self.register_type('Object') - - self.current_function = self.register_function(self.init_name('Object')) - instance = self.define_internal_local() - self.register_instruction(cil.AllocateNode('Object', instance)) - self.register_instruction(cil.ReturnNode(instance)) - - self.current_function = self.register_function(self.to_function_name('abort', 'Object')) - self.register_param(self.vself) - vname = self.define_internal_local() - abort_data = self.register_data('Abort called from class ') - self.register_instruction(cil.LoadNode(vname, abort_data)) - self.register_instruction(cil.PrintStringNode(vname)) - self.register_instruction(cil.TypeOfNode(vname, self.vself.name)) - self.register_instruction(cil.PrintStringNode(vname)) - self.register_instruction(cil.LoadNode(vname, self.breakline_data)) - self.register_instruction(cil.PrintStringNode(vname)) + line, column = 0, 0 + type_node = self.register_type('Object', line, column) + + self.current_function = self.register_function(self.init_name('Object'), line, column) + instance = self.define_internal_local(line, column) + self.register_instruction(cil.AllocateNode('Object', instance, line, column)) + self.register_instruction(cil.ReturnNode(instance, line, column)) + + self.current_function = self.register_function(self.to_function_name('abort', 'Object'), line, column) + self.register_param(self.vself, line, column) + vname = self.define_internal_local(line, column) + abort_data = self.register_data('Abort called from class ', line, column) + self.register_instruction(cil.LoadNode(vname, abort_data, line, column)) + self.register_instruction(cil.PrintStringNode(vname, line, column)) + self.register_instruction(cil.TypeOfNode(vname, self.vself.name, line, column)) + self.register_instruction(cil.PrintStringNode(vname, line, column)) + self.register_instruction(cil.LoadNode(vname, self.breakline_data, line, column)) + self.register_instruction(cil.PrintStringNode(vname, line, column)) self.register_instruction(cil.ExitNode()) - self.current_function = self.register_function(self.to_function_name('type_name', 'Object')) - self.register_param(self.vself) - result = self.define_internal_local() - self.register_instruction(cil.TypeOfNode(result, self.vself.name)) - instance = self.define_internal_local() - self.register_instruction(cil.ArgNode(result)) - self.register_instruction(cil.StaticCallNode(self.init_name('String'), instance)) - self.register_instruction(cil.ReturnNode(instance)) - - self.current_function = self.register_function(self.to_function_name('copy', 'Object')) - self.register_param(self.vself) - result = self.define_internal_local() - self.register_instruction(cil.CopyNode(result, self.vself.name)) - self.register_instruction(cil.ReturnNode(result)) + self.current_function = self.register_function(self.to_function_name('type_name', 'Object'), line, column) + self.register_param(self.vself, line, column) + result = self.define_internal_local(line, column) + self.register_instruction(cil.TypeOfNode(result, self.vself, line, column)) + instance = self.define_internal_local(line, column) + self.register_instruction(cil.ArgNode(result, line, column)) + self.register_instruction(cil.StaticCallNode(self.init_name('String'), instance, line, column)) + self.register_instruction(cil.ReturnNode(instance, line, column)) + + self.current_function = self.register_function(self.to_function_name('copy', 'Object'), line, column) + self.register_param(self.vself, line, column) + result = self.define_internal_local(line, column) + self.register_instruction(cil.CopyNode(result, self.vself.name, line, column)) + self.register_instruction(cil.ReturnNode(result, line, column)) type_node.methods = [self.to_function_name(name, 'Object') for name in ['abort', 'type_name', 'copy']] type_node.methods += [self.init_name('Object')] obj_methods = ['abort', 'type_name', 'copy'] # IO - type_node = self.register_type('IO') - - self.current_function = self.register_function(self.init_name('IO')) - instance = self.define_internal_local() - self.register_instruction(cil.AllocateNode('IO', instance)) - self.register_instruction(cil.ReturnNode(instance)) - - self.current_function = self.register_function(self.to_function_name('out_string', 'IO')) - self.register_param(self.vself) - self.register_param(VariableInfo('x', None)) - vname = self.define_internal_local() - self.register_instruction(cil.GetAttribNode(vname, 'x', 'value', 'String')) - self.register_instruction(cil.PrintStringNode(vname)) - self.register_instruction(cil.ReturnNode(self.vself.name)) - - self.current_function = self.register_function(self.to_function_name('out_int', 'IO')) - self.register_param(self.vself) - self.register_param(VariableInfo('x', None)) - vname = self.define_internal_local() - self.register_instruction(cil.GetAttribNode(vname, 'x', 'value', 'Int')) - self.register_instruction(cil.PrintIntNode(vname)) - self.register_instruction(cil.ReturnNode(self.vself.name)) - - self.current_function = self.register_function(self.to_function_name('in_string', 'IO')) - self.register_param(self.vself) - result = self.define_internal_local() - self.register_instruction(cil.ReadNode(result)) - instance = self.define_internal_local() - self.register_instruction(cil.ArgNode(result)) - self.register_instruction(cil.StaticCallNode(self.init_name('String'), instance)) - self.register_instruction(cil.ReturnNode(instance)) - - self.current_function = self.register_function(self.to_function_name('in_int', 'IO')) - self.register_param(self.vself) - result = self.define_internal_local() - self.register_instruction(cil.ReadNode(result)) - instance = self.define_internal_local() - self.register_instruction(cil.ArgNode(result)) - self.register_instruction(cil.StaticCallNode(self.init_name('Int'), instance)) - self.register_instruction(cil.ReturnNode(instance)) + type_node = self.register_type('IO', line, column) + + self.current_function = self.register_function(self.init_name('IO'), line, column) + instance = self.define_internal_local(line, column) + self.register_instruction(cil.AllocateNode('IO', instance, line, column)) + self.register_instruction(cil.ReturnNode(instance, line, column)) + + self.current_function = self.register_function(self.to_function_name('out_string', 'IO'), line, column) + self.register_param(self.vself, line, column) + self.register_param(VariableInfo('x', None), line, column) + vname = self.define_internal_local(line, column) + self.register_instruction(cil.GetAttribNode(vname, 'x', 'value', 'String', line, column)) + self.register_instruction(cil.PrintStringNode(vname, line, column)) + self.register_instruction(cil.ReturnNode(self.vself.name, line, column)) + + self.current_function = self.register_function(self.to_function_name('out_int', 'IO'), line, column) + self.register_param(self.vself, line, column) + self.register_param(VariableInfo('x', None), line, column) + vname = self.define_internal_local(line, column) + self.register_instruction(cil.GetAttribNode(vname, 'x', 'value', 'Int', line, column)) + self.register_instruction(cil.PrintIntNode(vname, line, column)) + self.register_instruction(cil.ReturnNode(self.vself.name, line, column)) + + self.current_function = self.register_function(self.to_function_name('in_string', 'IO'), line, column) + self.register_param(self.vself, line, column) + result = self.define_internal_local(line, column) + self.register_instruction(cil.ReadNode(result, line, column)) + instance = self.define_internal_local(line, column) + self.register_instruction(cil.ArgNode(result, line, column)) + self.register_instruction(cil.StaticCallNode(self.init_name('String'), instance, line, column)) + self.register_instruction(cil.ReturnNode(instance, line, column)) + + self.current_function = self.register_function(self.to_function_name('in_int', 'IO'), line, column) + self.register_param(self.vself, line, column) + result = self.define_internal_local(line, column) + self.register_instruction(cil.ReadNode(result, line, column)) + instance = self.define_internal_local(line, column) + self.register_instruction(cil.ArgNode(result, line, column)) + self.register_instruction(cil.StaticCallNode(self.init_name('Int'), instance, line, column)) + self.register_instruction(cil.ReturnNode(instance, line, column)) type_node.methods = [self.to_function_name(method, 'Object') for method in obj_methods] type_node.methods += [self.to_function_name(name, 'IO') for name in @@ -189,103 +190,103 @@ def register_builtin(self): type_node.methods += [self.init_name('IO')] # String - type_node = self.register_type('String') + type_node = self.register_type('String', line, column) type_node.attributes = ['value', 'length'] - self.current_function = self.register_function(self.init_name('String')) - self.register_param(VariableInfo('val', None)) - instance = self.define_internal_local() - self.register_instruction(cil.AllocateNode('String', instance)) - self.register_instruction(cil.SetAttribNode(instance, 'value', 'val', 'String')) - result = self.define_internal_local() - self.register_instruction(cil.LengthNode(result, 'val')) - attr = self.define_internal_local() - self.register_instruction(cil.ArgNode(result)) - self.register_instruction(cil.StaticCallNode(self.init_name('Int'), attr)) - self.register_instruction(cil.SetAttribNode(instance, 'length', attr, 'String')) - self.register_instruction(cil.ReturnNode(instance)) - - self.current_function = self.register_function(self.to_function_name('length', 'String')) - self.register_param(self.vself) - result = self.define_internal_local() - self.register_instruction(cil.GetAttribNode(result, self.vself.name, 'length', 'String')) - self.register_instruction(cil.ReturnNode(result)) - - self.current_function = self.register_function(self.to_function_name('concat', 'String')) - self.register_param(self.vself) - self.register_param(VariableInfo('s', None)) - str_1 = self.define_internal_local() - str_2 = self.define_internal_local() - length_1 = self.define_internal_local() - length_2 = self.define_internal_local() - self.register_instruction(cil.GetAttribNode(str_1, self.vself.name, 'value', 'String')) - self.register_instruction(cil.GetAttribNode(str_2, 's', 'value', 'String')) - self.register_instruction(cil.GetAttribNode(length_1, self.vself.name, 'length', 'String')) - self.register_instruction(cil.GetAttribNode(length_2, 's', 'length', 'String')) - self.register_instruction(cil.GetAttribNode(length_1, length_1, 'value', 'Int')) - self.register_instruction(cil.GetAttribNode(length_2, length_2, 'value', 'Int')) - self.register_instruction(cil.PlusNode(length_1, length_1, length_2)) - - result = self.define_internal_local() - self.register_instruction(cil.ConcatNode(result, str_1, str_2, length_1)) - instance = self.define_internal_local() - self.register_instruction(cil.ArgNode(result)) - self.register_instruction(cil.StaticCallNode(self.init_name('String'), instance)) - self.register_instruction(cil.ReturnNode(instance)) - - self.current_function = self.register_function(self.to_function_name('substr', 'String')) - self.register_param(self.vself) - self.register_param(VariableInfo('i', None)) - self.register_param(VariableInfo('l', None)) - result = self.define_internal_local() - index_value = self.define_internal_local() - length_value = self.define_internal_local() - length_attr = self.define_internal_local() - length_substr = self.define_internal_local() - less_value = self.define_internal_local() - str_value = self.define_internal_local() - self.register_instruction(cil.GetAttribNode(str_value, self.vself.name, 'value', 'String')) - self.register_instruction(cil.GetAttribNode(index_value, 'i', 'value', 'Int')) - self.register_instruction(cil.GetAttribNode(length_value, 'l', 'value', 'Int')) + self.current_function = self.register_function(self.init_name('String'), line, column) + self.register_param(VariableInfo('val', None), line, column) + instance = self.define_internal_local(line, column) + self.register_instruction(cil.AllocateNode('String', instance, line, column)) + self.register_instruction(cil.SetAttribNode(instance, 'value', 'val', 'String', line, column)) + result = self.define_internal_local(line, column) + self.register_instruction(cil.LengthNode(result, 'val', line, column)) + attr = self.define_internal_local(line, column) + self.register_instruction(cil.ArgNode(result, line, column)) + self.register_instruction(cil.StaticCallNode(self.init_name('Int'), attr, line, column)) + self.register_instruction(cil.SetAttribNode(instance, 'length', attr, 'String', line, column)) + self.register_instruction(cil.ReturnNode(instance, line, column)) + + self.current_function = self.register_function(self.to_function_name('length', 'String'), line, column) + self.register_param(self.vself, line, column) + result = self.define_internal_local(line, column) + self.register_instruction(cil.GetAttribNode(result, self.vself.name, 'length', 'String', line, column)) + self.register_instruction(cil.ReturnNode(result, line, column)) + + self.current_function = self.register_function(self.to_function_name('concat', 'String'), line, column) + self.register_param(self.vself, line, column) + self.register_param(VariableInfo('s', None), line, column) + str_1 = self.define_internal_local(line, column) + str_2 = self.define_internal_local(line, column) + length_1 = self.define_internal_local(line, column) + length_2 = self.define_internal_local(line, column) + self.register_instruction(cil.GetAttribNode(str_1, self.vself.name, 'value', 'String', line, column)) + self.register_instruction(cil.GetAttribNode(str_2, 's', 'value', 'String', line, column)) + self.register_instruction(cil.GetAttribNode(length_1, self.vself.name, 'length', 'String', line, column)) + self.register_instruction(cil.GetAttribNode(length_2, 's', 'length', 'String', line, column)) + self.register_instruction(cil.GetAttribNode(length_1, length_1, 'value', 'Int', line, column)) + self.register_instruction(cil.GetAttribNode(length_2, length_2, 'value', 'Int', line, column)) + self.register_instruction(cil.PlusNode(length_1, length_1, length_2, line, column)) + + result = self.define_internal_local(line, column) + self.register_instruction(cil.ConcatNode(result, str_1, str_2, length_1, line, column)) + instance = self.define_internal_local(line, column) + self.register_instruction(cil.ArgNode(result, line, column)) + self.register_instruction(cil.StaticCallNode(self.init_name('String'), instance, line, column)) + self.register_instruction(cil.ReturnNode(instance, line, column)) + + self.current_function = self.register_function(self.to_function_name('substr', 'String'), line, column) + self.register_param(self.vself, line, column) + self.register_param(VariableInfo('i', None), line, column) + self.register_param(VariableInfo('l', None), line, column) + result = self.define_internal_local(line, column) + index_value = self.define_internal_local(line, column) + length_value = self.define_internal_local(line, column) + length_attr = self.define_internal_local(line, column) + length_substr = self.define_internal_local(line, column) + less_value = self.define_internal_local(line, column) + str_value = self.define_internal_local(line, column) + self.register_instruction(cil.GetAttribNode(str_value, self.vself.name, 'value', 'String', line, column)) + self.register_instruction(cil.GetAttribNode(index_value, 'i', 'value', 'Int', line, column)) + self.register_instruction(cil.GetAttribNode(length_value, 'l', 'value', 'Int', line, column)) # Check Out of range error - self.register_instruction(cil.GetAttribNode(length_attr, self.vself.name, 'length', 'String')) - self.register_instruction(cil.PlusNode(length_substr, length_value, index_value)) - self.register_instruction(cil.LessNode(less_value, length_attr, length_substr)) - self.register_runtime_error(less_value, 'Substring out of range') - self.register_instruction(cil.SubstringNode(result, str_value, index_value, length_value)) - instance = self.define_internal_local() - self.register_instruction(cil.ArgNode(result)) - self.register_instruction(cil.StaticCallNode(self.init_name('String'), instance)) - self.register_instruction(cil.ReturnNode(instance)) + self.register_instruction(cil.GetAttribNode(length_attr, self.vself.name, 'length', 'String', line, column)) + self.register_instruction(cil.PlusNode(length_substr, length_value, index_value, line, column)) + self.register_instruction(cil.LessNode(less_value, length_attr, length_substr, line, column)) + self.register_runtime_error(less_value, 'Substring out of range', line, column) + self.register_instruction(cil.SubstringNode(result, str_value, index_value, length_value, line, column)) + instance = self.define_internal_local(line, column) + self.register_instruction(cil.ArgNode(result, line, column)) + self.register_instruction(cil.StaticCallNode(self.init_name('String'), instance, line, column)) + self.register_instruction(cil.ReturnNode(instance, line, column)) type_node.methods = [self.to_function_name(method, 'Object') for method in obj_methods] type_node.methods += [self.to_function_name(name, 'String') for name in ['length', 'concat', 'substr']] type_node.methods += [self.init_name('String')] # Int - type_node = self.register_type('Int') + type_node = self.register_type('Int', line, column) type_node.attributes = ['value'] - self.current_function = self.register_function(self.init_name('Int')) - self.register_param(VariableInfo('val', None)) - instance = self.define_internal_local() - self.register_instruction(cil.AllocateNode('Int', instance)) - self.register_instruction(cil.SetAttribNode(instance, 'value', 'val', 'Int')) - self.register_instruction(cil.ReturnNode(instance)) + self.current_function = self.register_function(self.init_name('Int'), line, column) + self.register_param(VariableInfo('val', None), line, column) + instance = self.define_internal_local(line, column) + self.register_instruction(cil.AllocateNode('Int', instance, line, column)) + self.register_instruction(cil.SetAttribNode(instance, 'value', 'val', 'Int', line, column)) + self.register_instruction(cil.ReturnNode(instance, line, column)) type_node.methods = [self.to_function_name(method, 'Object') for method in obj_methods] type_node.methods += [self.init_name('Int')] # Bool - type_node = self.register_type('Bool') + type_node = self.register_type('Bool', line, column) type_node.attributes = ['value'] - self.current_function = self.register_function(self.init_name('Bool')) - self.register_param(VariableInfo('val', None)) - instance = self.define_internal_local() - self.register_instruction(cil.AllocateNode('Bool', instance)) - self.register_instruction(cil.SetAttribNode(instance, 'value', 'val', 'Bool')) - self.register_instruction(cil.ReturnNode(instance)) + self.current_function = self.register_function(self.init_name('Bool'), line, column) + self.register_param(VariableInfo('val', None), line, column) + instance = self.define_internal_local(line, column) + self.register_instruction(cil.AllocateNode('Bool', instance, line, column)) + self.register_instruction(cil.SetAttribNode(instance, 'value', 'val', 'Bool', line, column)) + self.register_instruction(cil.ReturnNode(instance, line, column)) type_node.methods = [self.to_function_name(method, 'Object') for method in obj_methods] type_node.methods += [self.init_name('Bool')] diff --git a/src/core/cil/CILAst.py b/src/core/cil/CILAst.py index 4a64fad45..f36fb0767 100644 --- a/src/core/cil/CILAst.py +++ b/src/core/cil/CILAst.py @@ -1,27 +1,34 @@ from ..tools import visitor class Node: - pass + def __init__(self, line=0, column=0): + self.line = line + self.column = column + class ProgramNode(Node): - def __init__(self, dottypes, dotdata, dotcode): + def __init__(self, dottypes, dotdata, dotcode, line, column): + super().__init__(line,column) self.dottypes = dottypes self.dotdata = dotdata self.dotcode = dotcode class TypeNode(Node): - def __init__(self, name): + def __init__(self, name, line, column): + super().__init__(line, column) self.name = name self.attributes = [] self.methods = [] class DataNode(Node): - def __init__(self, vname, value): + def __init__(self, vname, value, line, column): + super().__init__(line, column) self.name = vname self.value = value class FunctionNode(Node): - def __init__(self, fname, params, localvars, instructions): + def __init__(self, fname, params, localvars, instructions, line, column): + super().__init__(line, column) self.name = fname self.params = params self.localvars = localvars @@ -29,23 +36,27 @@ def __init__(self, fname, params, localvars, instructions): self.labels_count = 0 class ParamNode(Node): - def __init__(self, name): + def __init__(self, name, line, column): + super().__init__(line, column) self.name = name class LocalNode(Node): - def __init__(self, name): + def __init__(self, name, line, column): + super().__init__(line, column) self.name = name class InstructionNode(Node): pass class AssignNode(InstructionNode): - def __init__(self, dest, source): + def __init__(self, dest, source, line, column): + super().__init__(line, column) self.dest = dest self.source = source class ArithmeticNode(InstructionNode): - def __init__(self, dest, left, right): + def __init__(self, dest, left, right, line, column): + super().__init__(line, column) self.dest = dest self.left = left self.right = right @@ -75,7 +86,8 @@ class LessEqualNode(ArithmeticNode): pass class GetAttribNode(InstructionNode): - def __init__(self, dest, obj, attr, computed_type): + def __init__(self, dest, obj, attr, computed_type, line, column): + super().__init__(line, column) self.dest = dest self.obj = obj self.attr = attr @@ -86,7 +98,8 @@ def __repr__(self): class SetAttribNode(InstructionNode): - def __init__(self, obj, attr, value, computed_type): + def __init__(self, obj, attr, value, computed_type, line, column): + super().__init__(line, column) self.obj = obj self.attr = attr self.value = value @@ -99,12 +112,14 @@ class SetIndexNode(InstructionNode): pass class AllocateNode(InstructionNode): - def __init__(self, itype, dest): + def __init__(self, itype, dest, line, column): + super().__init__(line, column) self.type = itype self.dest = dest class TypeOfNode(InstructionNode): - def __init__(self, dest, obj): + def __init__(self, dest, obj, line, column): + super().__init__(line, column) self.obj = obj self.dest = dest @@ -113,7 +128,8 @@ def __repr__(self): class LabelNode(InstructionNode): - def __init__(self, label): + def __init__(self, label, line, column): + super().__init__(line, column) self.label = label def __repr__(self): @@ -121,7 +137,8 @@ def __repr__(self): class GotoNode(InstructionNode): - def __init__(self, label): + def __init__(self, label, line, column): + super().__init__(line, column) self.label = label def __repr__(self): @@ -129,7 +146,8 @@ def __repr__(self): class GotoIfNode(InstructionNode): - def __init__(self, condition, label): + def __init__(self, condition, label, line, column): + super().__init__(line, column) self.condition = condition self.label = label @@ -137,7 +155,8 @@ def __repr__(self): return f"GOTO {self.label} if {self.condition}" class StaticCallNode(InstructionNode): - def __init__(self, function, dest): + def __init__(self, function, dest, line, column): + super().__init__(line, column) self.function = function self.dest = dest @@ -145,7 +164,8 @@ def __repr__(self): return f"{self.dest} = CALL {self.function}" class DynamicCallNode(InstructionNode): - def __init__(self, xtype, method, dest): + def __init__(self, xtype, method, dest, line, column): + super().__init__(line, column) self.type = xtype self.method = method self.dest = dest @@ -154,7 +174,8 @@ def __repr__(self): return f"{self.dest} = VCALL {self.type} {self.method}" class ArgNode(InstructionNode): - def __init__(self, name): + def __init__(self, name, line, column): + super().__init__(line, column) self.name = name def __repr__(self): @@ -162,7 +183,8 @@ def __repr__(self): class ReturnNode(InstructionNode): - def __init__(self, value=None): + def __init__(self, line, column, value=None): + super().__init__(line, column) self.value = value def __repr__(self): @@ -170,7 +192,8 @@ def __repr__(self): class LoadNode(InstructionNode): - def __init__(self, dest, msg): + def __init__(self, dest, msg, line, column): + super().__init__(line, column) self.dest = dest self.msg = msg @@ -178,12 +201,14 @@ def __repr__(self): return f"{self.dest} LOAD {self.msg}" class LengthNode(InstructionNode): - def __init__(self, dest, source): + def __init__(self, dest, source, line, column): + super().__init__(line, column) self.dest = dest self.source = source class ConcatNode(InstructionNode): - def __init__(self, dest, prefix, suffix, length): + def __init__(self, dest, prefix, suffix, length, line, column): + super().__init__(line, column) self.dest = dest self.prefix = prefix self.suffix = suffix @@ -193,56 +218,66 @@ class PrefixNode(InstructionNode): pass class SubstringNode(InstructionNode): - def __init__(self, dest, str_value, index, length): + def __init__(self, dest, str_value, index, length, line, column): + super().__init__(line, column) self.dest = dest self.str_value = str_value self.index = index self.length = length class ToStrNode(InstructionNode): - def __init__(self, dest, value): + def __init__(self, dest, value, line, column): + super().__init__(line, column) self.dest = dest self.value = value class ReadNode(InstructionNode): - def __init__(self, dest): + def __init__(self, dest, line, column): + super().__init__(line, column) self.dest = dest class PrintStringNode(InstructionNode): - def __init__(self, str_addr): + def __init__(self, str_addr, line, column): + super().__init__(line, column) self.str_addr = str_addr class PrintIntNode(InstructionNode): - def __init__(self, value): + def __init__(self, value, line, column): + super().__init__(line, column) self.value = value class ExitNode(InstructionNode): pass class CopyNode(InstructionNode): - def __init__(self, dest, value): + def __init__(self, dest, value, line, column): + super().__init__(line, column) self.dest = dest self.value = value class ErrorNode(InstructionNode): - def __init__(self, data): + def __init__(self, data, line, column): + super().__init__(line, column) self.data = data class VoidNode(InstructionNode): pass class NameNode(InstructionNode): - def __init__(self, dest, value): + def __init__(self, dest, value, line, column): + super().__init__(line, column) self.dest = dest self.value = value class NotNode(InstructionNode): - def __init__(self, dest, value): + def __init__(self, dest, value, line, column): + super().__init__(line, column) self.dest = dest self.value = value class ComplementNode(InstructionNode): - def __init__(self, dest, value): + def __init__(self, dest, value, line, column): + super().__init__(line, column) self.dest = dest self.value = value diff --git a/src/core/cil/COOLToCILVisitor.py b/src/core/cil/COOLToCILVisitor.py index 292583bb1..9ad3ba8c2 100644 --- a/src/core/cil/COOLToCILVisitor.py +++ b/src/core/cil/COOLToCILVisitor.py @@ -3,6 +3,7 @@ from core.tools import visitor from ..tools.Semantic import Scope + class COOLToCILVisitor(BaseCOOLToCILVisitor): def __init__(self, context): super().__init__(context) @@ -12,533 +13,601 @@ def visit(self, node): pass @visitor.when(cool.ProgramNode) - def visit(self, node : cool.ProgramNode, scope : Scope): - self.current_function = self.register_function('entry') - main_instance = self.define_internal_local() - self.register_instruction(cil.StaticCallNode(self.init_name('Main'), main_instance)) - self.register_instruction(cil.ArgNode(main_instance)) - self.register_instruction(cil.StaticCallNode(self.to_function_name('main', 'Main'), main_instance)) - self.register_instruction(cil.ReturnNode(0)) - - #self.register_builtin() + def visit(self, node: cool.ProgramNode, scope: Scope): + self.current_function = self.register_function('entry', line=0, column=0) + main_instance = self.define_internal_local(line=0, column=0) + self.register_instruction(cil.StaticCallNode(self.init_name('Main'), main_instance, line=0, column=0)) + self.register_instruction(cil.ArgNode(main_instance, line=0, column=0)) + self.register_instruction(cil.StaticCallNode(self.to_function_name('main', 'Main'), + main_instance, line=0, column=0)) + self.register_instruction(cil.ReturnNode(line=0, column=0, value=0)) + + # self.register_builtin() self.current_function = None for x, y in zip(node.declarations, scope.childs): self.visit(x, y) - return cil.ProgramNode(self.dottypes, self.dotdata, self.dotcode) - + return cil.ProgramNode(self.dottypes, self.dotdata, self.dotcode, line=0, column=0) @visitor.when(cool.ClassDeclarationNode) - def visit(self, node : cool.ClassDeclarationNode, scope : Scope): + def visit(self, node: cool.ClassDeclarationNode, scope: Scope): self.current_type = self.context.get_type(node.id.lex) # Inicializando los atributos de la clase y llamando al constructor del padre if self.current_type.parent.name not in ('Object', 'IO'): - variable = self.define_internal_local() - self.register_instruction(cil.ArgNode(self.vself.name)) + variable = self.define_internal_local(line=node.line, column=node.column) + self.register_instruction(cil.ArgNode(self.vself.name, line=node.line, column=node.column)) self.register_instruction(cil.StaticCallNode( - self.init_attr_name(self.current_type.parent.name), variable)) + self.init_attr_name(self.current_type.parent.name), variable, line=node.line, column=node.column)) - self.current_function = self.register_function(self.init_attr_name(node.id.lex)) - instance = self.register_param(VariableInfo('instance', '')) + # Inicializando los atributos de la clase + self.current_function = self.register_function(self.init_attr_name(node.id.lex), + line=node.line, column=node.column) + instance = self.register_param(VariableInfo('instance', ''), line=node.line, column=node.column) for feat, child in zip(node.features, scope.childs): if isinstance(feat, cool.AttrDeclarationNode): self.visit(feat, child) - self.register_instruction(cil.SetAttribNode( - instance, feat.id.lex, feat.ret_expr, feat.type.lex)) + self.register_instruction(cil.SetAttribNode(instance, feat.id.lex, feat.ret_expr, feat.type.lex, + line=node.line, column=node.column)) # TODO: Deberia retornar algo aqui? - type = self.register_type(node.id.lex) + # TypeNode de la clase + type = self.register_type(node.id.lex, line=node.line, column=node.column) type.attributes = [i.name for i in self.current_type.attributes] + + # Guardar métodos de las clases padres iter_type = self.current_type while iter_type is not None: type.methods += [self.to_function_name(i, iter_type.name) for i in iter_type.methods.keys()] iter_type = iter_type.parent type.methods.reverse() + # Visitar funciones dentro de la clase for feat, child in zip(node.features, scope.childs): if isinstance(feat, cool.FuncDeclarationNode): self.visit(feat, child) - self.current_function = self.register_function(self.init_name(node.id.lex)) - instance = self.define_internal_local() - self.register_instruction(cil.AllocateNode(node.id.lex, instance)) + # Allocate de la clase + self.current_function = self.register_function(self.init_name(node.id.lex), line=node.line, column=node.column) + instance = self.define_internal_local(line=node.line, column=node.column) + self.register_instruction(cil.AllocateNode(node.id.lex, instance, line=node.line, column=node.column)) - variable = self.define_internal_local() - self.register_instruction(cil.ArgNode(instance)) - self.register_instruction(cil.StaticCallNode(self.init_attr_name(node.id.lex), variable)) + variable = self.define_internal_local(line=node.line, column=node.column) + self.register_instruction(cil.ArgNode(instance, line=node.line, column=node.column)) + self.register_instruction(cil.StaticCallNode(self.init_attr_name(node.id.lex), variable, + line=node.line, column=node.column)) if 'init' in self.current_type.methods.keys(): - self.register_instruction(cil.ArgNode(instance)) + self.register_instruction(cil.ArgNode(instance, line=node.line, column=node.column)) self.register_instruction(cil.StaticCallNode( - self.to_function_name('init', self.current_type.name), variable)) + self.to_function_name('init', self.current_type.name), variable, line=node.line, column=node.column)) - self.register_instruction(cil.ReturnNode(instance)) + self.register_instruction(cil.ReturnNode(value=instance, line=node.line, column=node.column)) self.current_function = None self.current_type = None @visitor.when(cool.AttrDeclarationNode) - def visit(self, node : cool.AttrDeclarationNode, scope : Scope): - variable = self.define_internal_local() + def visit(self, node: cool.AttrDeclarationNode, scope: Scope): + variable = self.define_internal_local(line=node.line, column=node.column) if node.expression: self.visit(node.expression, scope.childs[0]) - self.register_instruction(cil.AssignNode(variable, node.expression.ret_expr)) + self.register_instruction(cil.AssignNode(variable, node.expression.ret_expr, + line=node.expression.line, column=node.expression.column)) elif node.type.lex in self.value_types: - self.register_instruction(cil.AllocateNode(node.type.lex, variable)) - self.register_local(VariableInfo(node.id.lex, node.type.lex)) + self.register_instruction(cil.AllocateNode(node.type.lex, variable, line=node.line, column=node.column)) + self.register_local(VariableInfo(node.id.lex, node.type.lex), line=node.line, column=node.column) node.ret_expr = variable @visitor.when(cool.FuncDeclarationNode) - def visit(self, node : cool.FuncDeclarationNode, scope : Scope): + def visit(self, node: cool.FuncDeclarationNode, scope: Scope): self.current_method = self.current_type.get_method(node.id.lex) - self.current_function = self.register_function( - self.to_function_name(self.current_method.name, self.current_type.name)) + self.current_function = self.register_function(self.to_function_name(self.current_method.name, + self.current_type.name), + line=node.line, column=node.column) - self.register_param(self.vself) + self.register_param(self.vself, line=node.line, column=node.column) for param, type in node.params: - self.register_param(VariableInfo(param.lex, type.lex)) + self.register_param(VariableInfo(param.lex, type.lex), line=param.line, column=param.column) self.visit(node.body, scope.childs[0]) - self.register_instruction(cil.ReturnNode(node.body.ret_expr)) + self.register_instruction(cil.ReturnNode(value=node.body.ret_expr, + line=node.body.line, column=node.body.column)) self.current_method = None @visitor.when(cool.IfThenElseNode) - def visit(self, node : cool.IfThenElseNode, scope : Scope): - ret = self.define_internal_local() - condition = self.define_internal_local() + def visit(self, node: cool.IfThenElseNode, scope: Scope): + ret = self.define_internal_local(line=node.line, column=node.column) + condition = self.define_internal_local(line=node.line, column=node.column) - then_label = self.register_label('then_label') - continue_label = self.register_label('continue_label') + then_label = self.register_label('then_label', line=node.line, column=node.column) + continue_label = self.register_label('continue_label', line=node.line, column=node.column) + # IF self.visit(node.condition, scope.childs[0]) - self.register_instruction(cil.GetAttribNode(condition, node.condition.ret_expr, 'value', 'Bool')) - self.register_instruction(cil.GotoIfNode(condition, then_label.label)) + self.register_instruction(cil.GetAttribNode(condition, node.condition.ret_expr, 'value', 'Bool', + line=node.condition.line, column=node.condition.column)) + self.register_instruction(cil.GotoIfNode(condition, then_label.label, line=node.condition.line, + column=node.condition.column)) + # ELSE self.visit(node.else_body, scope.childs[2]) - self.register_instruction(cil.AssignNode(ret, node.else_body.ret_expr)) - self.register_instruction(cil.GotoNode(continue_label.label)) + self.register_instruction(cil.AssignNode(ret, node.else_body.ret_expr, line=node.else_body.line, + column=node.else_body.column)) + self.register_instruction(cil.GotoNode(continue_label.label, line=node.else_body.line, + column=node.else_body.column)) + # THEN self.register_instruction(then_label) self.visit(node.if_body, scope.childs[1]) - self.register_instruction(cil.AssignNode(ret, node.if_body.ret_expr)) + self.register_instruction(cil.AssignNode(ret, node.if_body.ret_expr, line=node.if_body.line, + column=node.if_body.column)) self.register_instruction(continue_label) node.ret_expr = ret @visitor.when(cool.WhileLoopNode) - def visit(self, node : cool.WhileLoopNode, scope : Scope): - while_label = self.register_label('while_label') - loop_label = self.register_label('loop_label') - pool_label = self.register_label('pool_label') - condition = self.define_internal_local() + def visit(self, node: cool.WhileLoopNode, scope: Scope): + while_label = self.register_label('while_label', line=node.line, column=node.column) + loop_label = self.register_label('loop_label', line=node.line, column=node.column) + pool_label = self.register_label('pool_label', line=node.line, column=node.column) + condition = self.define_internal_local(line=node.line, column=node.column) self.register_instruction(while_label) self.visit(node.condition, scope.childs[0]) - self.register_instruction(cil.GetAttribNode(condition, node.condition.ret_expr, 'value', 'Bool')) - self.register_instruction(cil.GotoIfNode(condition, loop_label.label)) - self.register_instruction(cil.GotoNode(pool_label.label)) + self.register_instruction(cil.GetAttribNode(condition, node.condition.ret_expr, 'value', 'Bool', + line=node.condition.line, column=node.condition.column)) + self.register_instruction(cil.GotoIfNode(condition, loop_label.label, line=node.condition.line, + column=node.condition.column)) + self.register_instruction(cil.GotoNode(pool_label.label, line=node.condition.line, + column=node.condition.column)) self.register_instruction(loop_label) self.visit(node.body, scope.childs[1]) - self.register_instruction(cil.GotoNode(while_label.label)) + self.register_instruction( + cil.GotoNode(while_label.label, line=node.condition.line, column=node.condition.column)) self.register_instruction(pool_label) # TODO: No estoy seguro de si deberia retornar el nodo directamente o guardarlo antes en una variable node.ret_expr = cil.VoidNode() @visitor.when(cool.BlockNode) - def visit(self, node : cool.BlockNode, scope : Scope): + def visit(self, node: cool.BlockNode, scope: Scope): for expr, child in zip(node.expressions, scope.childs): self.visit(expr, child) node.ret_expr = node.expressions[-1].ret_expr @visitor.when(cool.LetInNode) - def visit(self, node : cool.LetInNode, scope : Scope): + def visit(self, node: cool.LetInNode, scope: Scope): for (id, type, expr), child in zip(node.let_body, scope.childs[:-1]): - variable = self.register_local(VariableInfo(id.lex, type.lex)) + # TODO TYPE OF ID + variable = self.register_local(VariableInfo(id.lex, type.lex), line=id.line, column=id.column) if expr: self.visit(expr, child) - self.register_instruction(cil.AssignNode(variable, expr.ret_expr)) + self.register_instruction(cil.AssignNode(variable, expr.ret_expr, line=expr.line, column=expr.column)) elif type.lex in self.value_types: - self.register_instruction(cil.AllocateNode(type.lex, variable)) + self.register_instruction(cil.AllocateNode(type.lex, variable, line=type.line, column=type.column)) self.visit(node.in_body, scope.childs[-1]) node.ret_expr = node.in_body.ret_expr - @visitor.when(cool.CaseOfNode) - def visit(self, node : cool.CaseOfNode, scope : Scope): - ret = self.define_internal_local() - vtype = self.define_internal_local() - cond = self.define_internal_local() + def visit(self, node: cool.CaseOfNode, scope: Scope): + ret = self.define_internal_local(line=node.line, column=node.column) + vtype = self.define_internal_local(line=node.line, column=node.column) + cond = self.define_internal_local(line=node.line, column=node.column) self.visit(node.expression, scope.childs[0]) - self.register_instruction(cil.TypeOfNode(vtype, node.expression.ret_expr)) + self.register_instruction(cil.TypeOfNode(vtype, node.expression.ret_expr, line=node.expression.line, + column=node.expression.column)) - isvoid = self.define_internal_local() - self.register_instruction(cil.EqualNode(isvoid, node.expression.ret_expr, cil.VoidNode())) + isvoid = self.define_internal_local(line=node.line, column=node.column) + self.register_instruction(cil.EqualNode(isvoid, node.expression.ret_expr, cil.VoidNode(), + line=node.expression.line, column=node.expression.column)) self.register_runtime_error(isvoid, f'{node.expression.line, node.expression.column} - ' - f'RuntimeError: Case on void') + f'RuntimeError: Case on void', + line=node.expression.line, column=node.expression.column) - end_label = self.register_label('case_end_label') + end_label = self.register_label('case_end_label', line=node.line, column=node.column) - branch_type = self.define_internal_local() + branch_type = self.define_internal_local(line=node.line, column=node.column) seen = [] labels = [] branches = sorted(node.branches, key=lambda x: self.context.get_type(x[1].lex).depth, reverse=True) for p, (id, type, expr) in enumerate(branches): - labels.append(self.register_label(f'case_label_{p}')) + # TODO Revisar tipo de id para la linea y columna + labels.append(self.register_label(f'case_label_{p}', line=id.line, column=id.column)) for t in self.context.subtree(type.lex): - if not t in seen: + if t not in seen: seen.append(t) - self.register_instruction(cil.NameNode(branch_type, t.name)) - self.register_instruction(cil.EqualNode(cond, branch_type, vtype)) - self.register_instruction(cil.GotoIfNode(cond, labels[-1].label)) + self.register_instruction(cil.NameNode(branch_type, t.name, line=id.line, column=id.column)) + self.register_instruction(cil.EqualNode(cond, branch_type, vtype, line=id.line, column=id.column)) + self.register_instruction(cil.GotoIfNode(cond, labels[-1].label, line=id.line, column=id.column)) data = self.register_data(f'{node.expression.line, node.expression.column} - ' - f'RuntimeError: Case statement without a match branch') - self.register_instruction(cil.ErrorNode(data)) + f'RuntimeError: Case statement without a match branch', + line=node.expression.line, column=node.expression.column) + self.register_instruction(cil.ErrorNode(data, line=node.expression.line, column=node.expression.column)) for p, label in enumerate(labels): id, type, expr = branches[p] sc = scope.childs[p + 1] self.register_instruction(label) - var = self.register_local(VariableInfo(id.lex, vtype)) - self.register_instruction(cil.AssignNode(var, node.expression.ret_expr)) + var = self.register_local(VariableInfo(id.lex, vtype), line=id.line, column=id.column) + self.register_instruction(cil.AssignNode(var, node.expression.ret_expr, line=node.expression.line, + column=node.expression.column)) self.visit(expr, sc) - self.register_instruction(cil.AssignNode(ret, expr.ret_expr)) - self.register_instruction(cil.GotoNode(end_label.label)) + self.register_instruction(cil.AssignNode(ret, expr.ret_expr, line=expr.line, column=expr.column)) + self.register_instruction(cil.GotoNode(end_label.label, line=expr.line, column=expr.column)) self.register_instruction(end_label) node.ret_expr = ret - @visitor.when(cool.AssignNode) def visit(self, node: cool.AssignNode, scope: Scope): var = self.var_names[node.id.lex] self.visit(node.expression, scope.childs[0]) - self.register_instruction(cil.AssignNode(var, node.expression.ret_expr)) + self.register_instruction(cil.AssignNode(var, node.expression.ret_expr, + line=node.expression.line, column=node.expression.column)) node.ret_expr = var @visitor.when(cool.NotNode) def visit(self, node: cool.NotNode, scope: Scope): - ret = self.define_internal_local() - value = self.define_internal_local() - neg_value = self.define_internal_local() + ret = self.define_internal_local(line=node.line, column=node.column) + value = self.define_internal_local(line=node.line, column=node.column) + neg_value = self.define_internal_local(line=node.line, column=node.column) self.visit(node.expression, scope.childs[0]) - self.register_instruction(cil.GetAttribNode(value, node.expression.ret_expr, 'value', 'Bool')) - self.register_instruction(cil.NotNode(neg_value, value)) - self.register_instruction(cil.ArgNode(neg_value)) - self.register_instruction(cil.StaticCallNode(self.init_name('Bool'), ret)) + self.register_instruction(cil.GetAttribNode(value, node.expression.ret_expr, 'value', 'Bool', + line=node.expression.line, column=node.expression.column)) + self.register_instruction(cil.NotNode(neg_value, value, + line=node.expression.line, column=node.expression.column)) + self.register_instruction(cil.ArgNode(neg_value, line=node.expression.line, column=node.expression.column)) + self.register_instruction(cil.StaticCallNode(self.init_name('Bool'), ret, line=node.line, column=node.column)) node.ret_expr = ret - @visitor.when(cool.LessEqualNode) def visit(self, node: cool.LessEqualNode, scope: Scope): - ret = self.define_internal_local() - left = self.define_internal_local() - right = self.define_internal_local() - value = self.define_internal_local() + ret = self.define_internal_local(line=node.line, column=node.column) + left = self.define_internal_local(line=node.line, column=node.column) + right = self.define_internal_local(line=node.line, column=node.column) + value = self.define_internal_local(line=node.line, column=node.column) self.visit(node.left, scope.childs[0]) - self.register_instruction(cil.GetAttribNode(left, node.left.ret_expr, 'value', 'Int')) + self.register_instruction(cil.GetAttribNode(left, node.left.ret_expr, 'value', 'Int', + line=node.left.line, column=node.left.line)) self.visit(node.right, scope.childs[1]) - self.register_instruction(cil.GetAttribNode(right, node.right.ret_expr, 'value', 'Int')) - self.register_instruction(cil.LessEqualNode(value, left, right)) + self.register_instruction(cil.GetAttribNode(right, node.right.ret_expr, 'value', 'Int', + line=node.right.line, column=node.right.line)) + self.register_instruction(cil.LessEqualNode(value, left, right, line=node.line, column=node.column)) - self.register_instruction(cil.ArgNode(value)) - self.register_instruction(cil.StaticCallNode(self.init_name('Bool'), ret)) + self.register_instruction(cil.ArgNode(value, line=node.line, column=node.column)) + self.register_instruction(cil.StaticCallNode(self.init_name('Bool'), ret, line=node.line, column=node.column)) node.ret_expr = ret @visitor.when(cool.LessNode) def visit(self, node: cool.LessNode, scope: Scope): - ret = self.define_internal_local() - left = self.define_internal_local() - right = self.define_internal_local() - value = self.define_internal_local() + ret = self.define_internal_local(line=node.line, column=node.column) + left = self.define_internal_local(line=node.line, column=node.column) + right = self.define_internal_local(line=node.line, column=node.column) + value = self.define_internal_local(line=node.line, column=node.column) self.visit(node.left, scope.childs[0]) - self.register_instruction(cil.GetAttribNode(left, node.left.ret_expr, 'value', 'Int')) + self.register_instruction(cil.GetAttribNode(left, node.left.ret_expr, 'value', 'Int', + line=node.left.line, column=node.left.line)) self.visit(node.right, scope.childs[1]) - self.register_instruction(cil.GetAttribNode(right, node.right.ret_expr, 'value', 'Int')) - self.register_instruction(cil.LessNode(value, left, right)) + self.register_instruction(cil.GetAttribNode(right, node.right.ret_expr, 'value', 'Int', + line=node.right.line, column=node.right.line)) + self.register_instruction(cil.LessNode(value, left, right, line=node.line, column=node.column)) - self.register_instruction(cil.ArgNode(value)) - self.register_instruction(cil.StaticCallNode(self.init_name('Bool'), ret)) + self.register_instruction(cil.ArgNode(value, line=node.line, column=node.column)) + self.register_instruction(cil.StaticCallNode(self.init_name('Bool'), ret, line=node.line, column=node.column)) node.ret_expr = ret @visitor.when(cool.PlusNode) def visit(self, node: cool.PlusNode, scope: Scope): - ret = self.define_internal_local() - left = self.define_internal_local() - right = self.define_internal_local() - value = self.define_internal_local() + ret = self.define_internal_local(line=node.line, column=node.column) + left = self.define_internal_local(line=node.line, column=node.column) + right = self.define_internal_local(line=node.line, column=node.column) + value = self.define_internal_local(line=node.line, column=node.column) self.visit(node.left, scope.childs[0]) - self.register_instruction(cil.GetAttribNode(left, node.left.ret_expr, 'value', 'Int')) + self.register_instruction(cil.GetAttribNode(left, node.left.ret_expr, 'value', 'Int', + line=node.left.line, column=node.left.line)) self.visit(node.right, scope.childs[1]) - self.register_instruction(cil.GetAttribNode(right, node.right.ret_expr, 'value', 'Int')) - self.register_instruction(cil.PlusNode(value, left, right)) + self.register_instruction(cil.GetAttribNode(right, node.right.ret_expr, 'value', 'Int', + line=node.right.line, column=node.right.line)) + self.register_instruction(cil.PlusNode(value, left, right, line=node.line, column=node.column)) - self.register_instruction(cil.ArgNode(value)) - self.register_instruction(cil.StaticCallNode(self.init_name('Int'), ret)) + self.register_instruction(cil.ArgNode(value, line=node.line, column=node.column)) + self.register_instruction(cil.StaticCallNode(self.init_name('Int'), ret,line=node.line, column=node.column)) node.ret_expr = ret @visitor.when(cool.MinusNode) def visit(self, node: cool.MinusNode, scope: Scope): - ret = self.define_internal_local() - left = self.define_internal_local() - right = self.define_internal_local() - value = self.define_internal_local() + ret = self.define_internal_local(line=node.line, column=node.column) + left = self.define_internal_local(line=node.line, column=node.column) + right = self.define_internal_local(line=node.line, column=node.column) + value = self.define_internal_local(line=node.line, column=node.column) self.visit(node.left, scope.childs[0]) - self.register_instruction(cil.GetAttribNode(left, node.left.ret_expr, 'value', 'Int')) + self.register_instruction(cil.GetAttribNode(left, node.left.ret_expr, 'value', 'Int', + line=node.left.line, column=node.left.line)) self.visit(node.right, scope.childs[1]) - self.register_instruction(cil.GetAttribNode(right, node.right.ret_expr, 'value', 'Int')) - self.register_instruction(cil.MinusNode(value, left, right)) + self.register_instruction(cil.GetAttribNode(right, node.right.ret_expr, 'value', 'Int', + line=node.right.line, column=node.right.line)) + self.register_instruction(cil.MinusNode(value, left, right, line=node.line, column=node.column)) - self.register_instruction(cil.ArgNode(value)) - self.register_instruction(cil.StaticCallNode(self.init_name('Int'), ret)) + self.register_instruction(cil.ArgNode(value, line=node.line, column=node.column)) + self.register_instruction(cil.StaticCallNode(self.init_name('Int'), ret, line=node.line, column=node.column)) node.ret_expr = ret @visitor.when(cool.StarNode) def visit(self, node: cool.StarNode, scope: Scope): - ret = self.define_internal_local() - left = self.define_internal_local() - right = self.define_internal_local() - value = self.define_internal_local() + ret = self.define_internal_local(line=node.line, column=node.column) + left = self.define_internal_local(line=node.line, column=node.column) + right = self.define_internal_local(line=node.line, column=node.column) + value = self.define_internal_local(line=node.line, column=node.column) self.visit(node.left, scope.childs[0]) - self.register_instruction(cil.GetAttribNode(left, node.left.ret_expr, 'value', 'Int')) + self.register_instruction(cil.GetAttribNode(left, node.left.ret_expr, 'value', 'Int', + line=node.left.line, column=node.left.line)) self.visit(node.right, scope.childs[1]) - self.register_instruction(cil.GetAttribNode(right, node.right.ret_expr, 'value', 'Int')) - self.register_instruction(cil.StarNode(value, left, right)) + self.register_instruction(cil.GetAttribNode(right, node.right.ret_expr, 'value', 'Int', + line=node.right.line, column=node.right.line)) + self.register_instruction(cil.StarNode(value, left, right, line=node.line, column=node.column)) - self.register_instruction(cil.ArgNode(value)) - self.register_instruction(cil.StaticCallNode(self.init_name('Int'), ret)) + self.register_instruction(cil.ArgNode(value, line=node.line, column=node.column)) + self.register_instruction(cil.StaticCallNode(self.init_name('Int'), ret, line=node.line, column=node.column)) node.ret_expr = ret @visitor.when(cool.DivNode) def visit(self, node: cool.DivNode, scope: Scope): - ret = self.define_internal_local() - left = self.define_internal_local() - right = self.define_internal_local() - value = self.define_internal_local() + ret = self.define_internal_local(line=node.line, column=node.column) + left = self.define_internal_local(line=node.line, column=node.column) + right = self.define_internal_local(line=node.line, column=node.column) + value = self.define_internal_local(line=node.line, column=node.column) self.visit(node.left, scope.childs[0]) - self.register_instruction(cil.GetAttribNode(left, node.left.ret_expr, 'value', 'Int')) + self.register_instruction(cil.GetAttribNode(left, node.left.ret_expr, 'value', 'Int', + line=node.left.line, column=node.left.line)) self.visit(node.right, scope.childs[1]) - self.register_instruction(cil.GetAttribNode(right, node.right.ret_expr, 'value', 'Int')) - self.register_instruction(cil.DivNode(value, left, right)) + self.register_instruction(cil.GetAttribNode(right, node.right.ret_expr, 'value', 'Int', + line=node.right.line, column=node.right.line)) + self.register_instruction(cil.DivNode(value, left, right, line=node.line, column=node.column)) - self.register_instruction(cil.ArgNode(value)) - self.register_instruction(cil.StaticCallNode(self.init_name('Int'), ret)) + self.register_instruction(cil.ArgNode(value, line=node.line, column=node.column)) + self.register_instruction(cil.StaticCallNode(self.init_name('Int'), ret, line=node.line, column=node.column)) node.ret_expr = ret - @visitor.when(cool.IsVoidNode) def visit(self, node: cool.IsVoidNode, scope: Scope): - ret = self.define_internal_local() - answer = self.define_internal_local() + ret = self.define_internal_local(line=node.line, column=node.column) + answer = self.define_internal_local(line=node.line, column=node.column) void = cil.VoidNode() self.visit(node.expression, scope.childs[0]) - self.register_instruction(cil.EqualNode(answer, node.expression.ret_expr, void)) + self.register_instruction(cil.EqualNode(answer, node.expression.ret_expr, void, + line=node.expression.line, column=node.expression.node)) - self.register_instruction(cil.ArgNode(answer)) - self.register_instruction(cil.StaticCallNode(self.init_name('Bool'), ret)) + self.register_instruction(cil.ArgNode(answer, line=node.expression.line, column=node.expression.node)) + self.register_instruction(cil.StaticCallNode(self.init_name('Bool'), ret, line=node.line, column=node.column)) node.ret_expr = ret @visitor.when(cool.ComplementNode) def visit(self, node: cool.ComplementNode, scope: Scope): - ret = self.define_internal_local() - value = self.define_internal_local() - answer = self.define_internal_local() + ret = self.define_internal_local(line=node.line, column=node.column) + value = self.define_internal_local(line=node.line, column=node.column) + answer = self.define_internal_local(line=node.line, column=node.column) self.visit(node.expression, scope.childs[0]) - self.register_instruction(cil.GetAttribNode(value, node.expression.ret_expr, 'value', 'Int')) - self.register_instruction(cil.ComplementNode(answer, value)) + self.register_instruction(cil.GetAttribNode(value, node.expression.ret_expr, 'value', 'Int', + line=node.expression.line, column=node.expression.node)) + self.register_instruction(cil.ComplementNode(answer, value, + line=node.expression.line, column=node.expression.node)) - self.register_instruction(cil.ArgNode(answer)) - self.register_instruction(cil.StaticCallNode(self.init_name('Int'), ret)) + self.register_instruction(cil.ArgNode(answer,line=node.expression.line, column=node.expression.node)) + self.register_instruction(cil.StaticCallNode(self.init_name('Int'), ret, line=node.line, column=node.column)) node.ret_expr = ret @visitor.when(cool.EqualNode) def visit(self, node: cool.EqualNode, scope: Scope): - ret = self.define_internal_local() - left = self.define_internal_local() - right = self.define_internal_local() - type_left = self.define_internal_local() - type_int = self.define_internal_local() - type_string = self.define_internal_local() - type_bool = self.define_internal_local() - equal = self.define_internal_local() - value = self.define_internal_local() - - int_comparisson = self.register_label('int_comparisson') - string_comparisson = self.register_label('string_comparisson') - bool_comparisson = self.register_label('bool_comparisson') - continue_label = self.register_label('continue_label') + ret = self.define_internal_local(line=node.line, column=node.column) + left = self.define_internal_local(line=node.line, column=node.column) + right = self.define_internal_local(line=node.line, column=node.column) + type_left = self.define_internal_local(line=node.line, column=node.column) + type_int = self.define_internal_local(line=node.line, column=node.column) + type_string = self.define_internal_local(line=node.line, column=node.column) + type_bool = self.define_internal_local(line=node.line, column=node.column) + equal = self.define_internal_local(line=node.line, column=node.column) + value = self.define_internal_local(line=node.line, column=node.column) + + int_comparisson = self.register_label('int_comparisson', line=node.line, column=node.column) + string_comparisson = self.register_label('string_comparisson', line=node.line, column=node.column) + bool_comparisson = self.register_label('bool_comparisson', line=node.line, column=node.column) + continue_label = self.register_label('continue_label', line=node.line, column=node.column) self.visit(node.left, scope.childs[0]) self.visit(node.right, scope.childs[1]) - self.register_instruction(cil.TypeOfNode(type_left, node.left.ret_expr)) - self.register_instruction(cil.NameNode(type_int, 'Int')) - self.register_instruction(cil.NameNode(type_string, 'String')) - self.register_instruction(cil.NameNode(type_bool, 'Bool')) - - self.register_instruction(cil.EqualNode(equal, type_left, type_int)) - self.register_instruction(cil.GotoIfNode(equal, int_comparisson.label)) - self.register_instruction(cil.EqualNode(equal, type_left, type_string)) - self.register_instruction(cil.GotoIfNode(equal, string_comparisson.label)) - self.register_instruction(cil.EqualNode(equal, type_left, type_bool)) - self.register_instruction(cil.GotoIfNode(equal, bool_comparisson.label)) - - self.register_instruction(cil.EqualNode(value, node.left.ret_expr, node.right.ret_expr)) - self.register_instruction(cil.GotoNode(continue_label)) + self.register_instruction(cil.TypeOfNode(type_left, node.left.ret_expr, + line=node.left.line, column=node.left.column)) + self.register_instruction(cil.NameNode(type_int, 'Int', line=node.line, column=node.column)) + self.register_instruction(cil.NameNode(type_string, 'String', line=node.line, column=node.column)) + self.register_instruction(cil.NameNode(type_bool, 'Bool', line=node.line, column=node.column)) + + self.register_instruction(cil.EqualNode(equal, type_left, type_int, + line=node.left.line, column=node.left.column)) + self.register_instruction(cil.GotoIfNode(equal, int_comparisson.label, + line=node.left.line, column=node.left.column)) + self.register_instruction(cil.EqualNode(equal, type_left, type_string, + line=node.left.line, column=node.left.column)) + self.register_instruction(cil.GotoIfNode(equal, string_comparisson.label, + line=node.left.line, column=node.left.column)) + self.register_instruction(cil.EqualNode(equal, type_left, type_bool, + line=node.left.line, column=node.left.column)) + self.register_instruction(cil.GotoIfNode(equal, bool_comparisson.label, + line=node.left.line, column=node.left.column)) + + self.register_instruction(cil.EqualNode(value, node.left.ret_expr, node.right.ret_expr, + line=node.left.line, column=node.left.column)) + self.register_instruction(cil.GotoNode(continue_label, + line=node.left.line, column=node.left.column)) self.register_instruction(int_comparisson) - self.register_instruction(cil.GetAttribNode(left, node.left.ret_expr, 'value', 'Int')) - self.register_instruction(cil.GetAttribNode(right, node.right.ret_expr, 'value', 'Int')) - self.register_instruction(cil.EqualNode(value, left, right)) - self.register_instruction(cil.GotoNode(continue_label)) + self.register_instruction(cil.GetAttribNode(left, node.left.ret_expr, 'value', 'Int', + line=node.right.line, column=node.right.column)) + self.register_instruction(cil.GetAttribNode(right, node.right.ret_expr, 'value', 'Int', + line=node.right.line, column=node.right.column)) + self.register_instruction(cil.EqualNode(value, left, right, line=node.line, column=node.column)) + self.register_instruction(cil.GotoNode(continue_label, line=node.line, column=node.column)) self.register_instruction(string_comparisson) - self.register_instruction(cil.GetAttribNode(left, node.left.ret_expr, 'value', 'String')) - self.register_instruction(cil.GetAttribNode(right, node.right.ret_expr, 'value', 'String')) - self.register_instruction(cil.EqualStringNode(value, left, right)) - self.register_instruction(cil.GotoNode(continue_label)) + self.register_instruction(cil.GetAttribNode(left, node.left.ret_expr, 'value', 'String', + line=node.left.line, column=node.left.column)) + self.register_instruction(cil.GetAttribNode(right, node.right.ret_expr, 'value', 'String', + line=node.right.line, column=node.right.column)) + self.register_instruction(cil.EqualStringNode(value, left, right, + line=node.right.line, column=node.right.column)) + self.register_instruction(cil.GotoNode(continue_label, line=node.line, column=node.column)) self.register_instruction(bool_comparisson) - self.register_instruction(cil.GetAttribNode(left, node.left.ret_expr, 'value', 'Bool')) - self.register_instruction(cil.GetAttribNode(right, node.right.ret_expr, 'value', 'Bool')) - self.register_instruction(cil.EqualNode(value, left, right)) - self.register_instruction(cil.GotoNode(continue_label)) + self.register_instruction(cil.GetAttribNode(left, node.left.ret_expr, 'value', 'Bool', + line=node.left.line, column=node.left.column)) + self.register_instruction(cil.GetAttribNode(right, node.right.ret_expr, 'value', 'Bool', + line=node.right.line, column=node.right.column)) + self.register_instruction(cil.EqualNode(value, left, right, + line=node.right.line, column=node.right.column)) + self.register_instruction(cil.GotoNode(continue_label, line=node.line, column=node.column)) self.register_instruction(continue_label) - self.register_instruction(cil.ArgNode(value)) - self.register_instruction(cil.StaticCallNode(self.init_name('Bool'), ret)) + self.register_instruction(cil.ArgNode(value, line=node.line, column=node.column)) + self.register_instruction(cil.StaticCallNode(self.init_name('Bool'), ret, line=node.line, column=node.column)) node.ret_expr = ret - @visitor.when(cool.FunctionCallNode) def visit(self, node: cool.FunctionCallNode, scope: Scope): args = [] for arg, child in zip(node.args, scope.childs[1:]): self.visit(arg, child) - args.append(cil.ArgNode(arg.ret_expr)) + args.append(cil.ArgNode(arg.ret_expr, line=arg.line, column=arg.column)) self.visit(node.obj, scope.childs[0]) void = cil.VoidNode() - isvoid = self.define_internal_local() - self.register_instruction(cil.EqualNode(isvoid, node.obj.ret_expr, void)) - self.register_runtime_error(isvoid, f'{node.id.line, node.id.column} - RuntimeError: Dispatch on void') + isvoid = self.define_internal_local(line=node.line, column=node.column) + self.register_instruction(cil.EqualNode(isvoid, node.obj.ret_expr, void, + line=node.obj.line, column=node.obj.column)) + self.register_runtime_error(isvoid, f'{node.id.line, node.id.column} - RuntimeError: Dispatch on void', + line=node.id.line, column=node.id.column) # TODO: Creo que deberia annadir los parametros al reves para luego sacarlos en el orden correcto - self.register_instruction(cil.ArgNode(node.obj.ret_expr)) - for arg in args: self.register_instruction(arg) + self.register_instruction(cil.ArgNode(node.obj.ret_expr, line=node.obj.line, column=node.obj.column)) + for arg in args: + self.register_instruction(arg) - ret = self.define_internal_local() + ret = self.define_internal_local(line=node.line, column=node.column) if node.type: - self.register_instruction(cil.StaticCallNode(self.to_function_name(node.id, node.type.name), ret)) + self.register_instruction(cil.StaticCallNode(self.to_function_name(node.id, node.type.name), ret, + line=node.line, column=node.column)) else: - type = self.define_internal_local() - self.register_instruction(cil.TypeOfNode(type, node.obj.ret_expr)) - self.register_instruction(cil.DynamicCallNode(type, node.id, ret)) + type = self.define_internal_local(line=node.line, column=node.column) + self.register_instruction(cil.TypeOfNode(type, node.obj.ret_expr, + line=node.obj.line, column=node.obj.column)) + self.register_instruction(cil.DynamicCallNode(type, node.id, ret, + line=node.id.line, column=node.id.column)) node.ret_expr = ret @visitor.when(cool.MemberCallNode) def visit(self, node: cool.MemberCallNode, scope: Scope): - ret = self.define_internal_local() - type = self.define_internal_local() - self.register_instruction(cil.TypeOfNode(type, self.vself.name)) + ret = self.define_internal_local(line=node.line, column=node.column) + type = self.define_internal_local(line=node.line, column=node.column) + self.register_instruction(cil.TypeOfNode(type, self.vself.name, line=node.line, column=node.column)) args = [] for arg, child in zip(node.args, scope.childs): self.visit(arg, child) - args.append(cil.ArgNode(arg.ret_expr)) + args.append(cil.ArgNode(arg.ret_expr, line=arg.line, column=arg.column)) # TODO: Creo que deberia annadir los parametros al reves para luego sacarlos en el orden correcto - self.register_instruction(cil.ArgNode(self.vself.name)) + self.register_instruction(cil.ArgNode(self.vself.name, line=node.line, column=node.column)) for arg in args: self.register_instruction(arg) - self.register_instruction(cil.DynamicCallNode(type, node.id, ret)) + self.register_instruction(cil.DynamicCallNode(type, node.id, ret, line=node.id.line, column=node.id.column)) node.ret_expr = ret @visitor.when(cool.NewNode) def visit(self, node: cool.NewNode, scope: Scope): - ret = self.define_internal_local() + ret = self.define_internal_local(line=node.line, column=node.column) if node.type == 'SELF_TYPE': - variable = self.define_internal_local() - self.register_instruction(cil.TypeOfNode(ret, self.vself.name)) + variable = self.define_internal_local(line=node.line, column=node.column) + self.register_instruction(cil.TypeOfNode(ret, self.vself.name, line=node.line, column=node.column)) # TODO: ALLOCATE a veces recibe el nombre de la clase como string, necesito cambiar este ya # que el nombre de la clase se encuentra dentro de la variable, o cambiar los demas para # que funcionen con self.register_instruction(cil.LoadNode(variable, data)) - self.register_instruction(cil.AllocateNode(variable, ret)) + self.register_instruction(cil.AllocateNode(variable, ret, line=node.line, column=node.column)) else: if node.type == 'Int': - self.register_instruction(cil.ArgNode(0)) + self.register_instruction(cil.ArgNode(0, line=node.type.line, column=node.type.column)) elif node.type == 'Bool': - self.register_instruction(cil.ArgNode(False)) + self.register_instruction(cil.ArgNode(False, line=node.type.line, column=node.type.column)) elif node.type == 'String': data = self.emptystring_data - variable = self.define_internal_local() - self.register_instruction(cil.LoadNode(variable, data)) - self.register_instruction(cil.ArgNode(variable)) + variable = self.define_internal_local(line=node.line, column=node.column) + self.register_instruction(cil.LoadNode(variable, data, line=node.line, column=node.column)) + self.register_instruction(cil.ArgNode(variable, line=node.line, column=node.column)) - self.register_instruction(cil.StaticCallNode(self.init_name(node.type.lex), ret)) + self.register_instruction(cil.StaticCallNode(self.init_name(node.type.lex), ret, + line=node.type.line, column=node.type.column)) node.ret_expr = ret @visitor.when(cool.IdNode) def visit(self, node: cool.IdNode, scope: Scope): - if node.token.lex == 'self': node.ret_expr = self.vself.name - else: node.ret_expr = self.var_names[node.token.lex] + if node.token.lex == 'self': + node.ret_expr = self.vself.name + else: + node.ret_expr = self.var_names[node.token.lex] @visitor.when(cool.StringNode) def visit(self, node: cool.StringNode, scope: Scope): try: data = [i for i in self.dotdata if i.value == node.token.lex][0] except IndexError: - data = self.register_data(node.token.lex) + data = self.register_data(node.token.lex, line=node.token.line, column=node.token.column) - variable = self.define_internal_local() - ret = self.define_internal_local() + variable = self.define_internal_local(line=node.line, column=node.column) + ret = self.define_internal_local(line=node.line, column=node.column) - self.register_instruction(cil.LoadNode(variable, data)) - self.register_instruction(cil.ArgNode(variable)) - self.register_instruction(cil.StaticCallNode(self.init_name('String'), ret)) + self.register_instruction(cil.LoadNode(variable, data, line=node.line, column=node.column)) + self.register_instruction(cil.ArgNode(variable, line=node.line, column=node.column)) + self.register_instruction(cil.StaticCallNode(self.init_name('String'), ret, + line=node.line, column=node.column)) node.ret_expr = ret @visitor.when(cool.IntegerNode) def visit(self, node: cool.IntegerNode, scope: Scope): - ret = self.define_internal_local() + ret = self.define_internal_local(line=node.line, column=node.column) # TODO: Hay algunos ArgNode que reciben variables y otros valores especificos - self.register_instruction(cil.ArgNode(node.token.lex)) - self.register_instruction(cil.StaticCallNode(self.init_name('Int'), ret)) + self.register_instruction(cil.ArgNode(node.token.lex, line=node.token.line, column=node.token.column)) + self.register_instruction(cil.StaticCallNode(self.init_name('Int'), ret, line=node.line, column=node.column)) node.ret_expr = ret @visitor.when(cool.BoolNode) def visit(self, node: cool.BoolNode, scope: Scope): - ret = self.define_internal_local() + ret = self.define_internal_local(line=node.line, column=node.column) # TODO: Hay algunos ArgNode que reciben variables y otros valores especificos - self.register_instruction(cil.ArgNode(node.token.lex)) - self.register_instruction(cil.StaticCallNode(self.init_name('Bool'), ret)) + self.register_instruction(cil.ArgNode(node.token.lex, line=node.token.line, column=node.token.column)) + self.register_instruction(cil.StaticCallNode(self.init_name('Bool'), ret, line=node.line, column=node.column)) node.ret_expr = ret - - From 9a90fc8c9bce06e4d685e08dbacde9963909081f Mon Sep 17 00:00:00 2001 From: mavaldivie Date: Fri, 11 Feb 2022 22:32:01 -0500 Subject: [PATCH 12/75] Created MIPSAstFormatter file and class --- src/core/mips/MIPSAstFormatter.py | 8 ++++++++ 1 file changed, 8 insertions(+) create mode 100644 src/core/mips/MIPSAstFormatter.py diff --git a/src/core/mips/MIPSAstFormatter.py b/src/core/mips/MIPSAstFormatter.py new file mode 100644 index 000000000..08457ba43 --- /dev/null +++ b/src/core/mips/MIPSAstFormatter.py @@ -0,0 +1,8 @@ +from core.tools import visitor +import core.mips.MipsAst as mips + +class MIPSAstFormatter: + @visitor.on('node') + def visit(self, node): + pass + From 454abd47eafb164ed22f7a90767bc5fe8e0bab8f Mon Sep 17 00:00:00 2001 From: mavaldivie Date: Fri, 11 Feb 2022 23:32:19 -0500 Subject: [PATCH 13/75] Fixed bugs --- src/core/cil/BaseCOOLToCILVisitor.py | 51 ++++++++++++++-------------- src/core/cil/COOLToCILVisitor.py | 20 ++++------- 2 files changed, 32 insertions(+), 39 deletions(-) diff --git a/src/core/cil/BaseCOOLToCILVisitor.py b/src/core/cil/BaseCOOLToCILVisitor.py index 33d2cdef1..fa1826c1b 100644 --- a/src/core/cil/BaseCOOLToCILVisitor.py +++ b/src/core/cil/BaseCOOLToCILVisitor.py @@ -39,12 +39,11 @@ def instructions(self): return self.current_function.instructions def register_param(self, vinfo, line, column): - name = vinfo.name - vinfo.name = f'local_param_{self.current_function.name}_{name}_{len(self.params)}' - param_node = cil.ParamNode(vinfo.name, line, column) + name = f'local_param_{self.current_function.name}_{vinfo.name}_{len(self.params)}' + param_node = cil.ParamNode(name, line, column) self.params.append(param_node) - self.var_names[name] = vinfo.name - return vinfo.name + self.var_names[vinfo.name] = name + return name def register_local(self, vinfo, line, column): name = vinfo.name @@ -114,30 +113,30 @@ def register_builtin(self): self.register_instruction(cil.ReturnNode(instance, line, column)) self.current_function = self.register_function(self.to_function_name('abort', 'Object'), line, column) - self.register_param(self.vself, line, column) + self_param = self.register_param(self.vself, line, column) vname = self.define_internal_local(line, column) abort_data = self.register_data('Abort called from class ', line, column) self.register_instruction(cil.LoadNode(vname, abort_data, line, column)) self.register_instruction(cil.PrintStringNode(vname, line, column)) - self.register_instruction(cil.TypeOfNode(vname, self.vself.name, line, column)) + self.register_instruction(cil.TypeOfNode(vname, self_param, line, column)) self.register_instruction(cil.PrintStringNode(vname, line, column)) self.register_instruction(cil.LoadNode(vname, self.breakline_data, line, column)) self.register_instruction(cil.PrintStringNode(vname, line, column)) self.register_instruction(cil.ExitNode()) self.current_function = self.register_function(self.to_function_name('type_name', 'Object'), line, column) - self.register_param(self.vself, line, column) + self_param = self.register_param(self.vself, line, column) result = self.define_internal_local(line, column) - self.register_instruction(cil.TypeOfNode(result, self.vself, line, column)) + self.register_instruction(cil.TypeOfNode(result, self_param, line, column)) instance = self.define_internal_local(line, column) self.register_instruction(cil.ArgNode(result, line, column)) self.register_instruction(cil.StaticCallNode(self.init_name('String'), instance, line, column)) self.register_instruction(cil.ReturnNode(instance, line, column)) self.current_function = self.register_function(self.to_function_name('copy', 'Object'), line, column) - self.register_param(self.vself, line, column) + self_param = self.register_param(self.vself, line, column) result = self.define_internal_local(line, column) - self.register_instruction(cil.CopyNode(result, self.vself.name, line, column)) + self.register_instruction(cil.CopyNode(result, self_param, line, column)) self.register_instruction(cil.ReturnNode(result, line, column)) type_node.methods = {name: self.to_function_name(name, 'Object') for name in ['abort', 'type_name', 'copy']} @@ -153,23 +152,23 @@ def register_builtin(self): self.register_instruction(cil.ReturnNode(instance, line, column)) self.current_function = self.register_function(self.to_function_name('out_string', 'IO'), line, column) - self.register_param(self.vself, line, column) + self_param = self.register_param(self.vself, line, column) x = self.register_param(VariableInfo('x', None), line, column) vname = self.define_internal_local(line, column) self.register_instruction(cil.GetAttribNode(vname, x, 'value', 'String', line, column)) self.register_instruction(cil.PrintStringNode(vname, line, column)) - self.register_instruction(cil.ReturnNode(self.vself.name, line, column)) + self.register_instruction(cil.ReturnNode(self_param, line, column)) self.current_function = self.register_function(self.to_function_name('out_int', 'IO'), line, column) - self.register_param(self.vself, line, column) + self_param = self.register_param(self.vself, line, column) x = self.register_param(VariableInfo('x', None), line, column) vname = self.define_internal_local(line, column) self.register_instruction(cil.GetAttribNode(vname, x, 'value', 'Int', line, column)) self.register_instruction(cil.PrintIntNode(vname, line, column)) - self.register_instruction(cil.ReturnNode(self.vself.name, line, column)) + self.register_instruction(cil.ReturnNode(self_param, line, column)) self.current_function = self.register_function(self.to_function_name('in_string', 'IO'), line, column) - self.register_param(self.vself, line, column) + self_param = self.register_param(self.vself, line, column) result = self.define_internal_local(line, column) self.register_instruction(cil.ReadStringNode(result, line, column)) instance = self.define_internal_local(line, column) @@ -178,7 +177,7 @@ def register_builtin(self): self.register_instruction(cil.ReturnNode(instance, line, column)) self.current_function = self.register_function(self.to_function_name('in_int', 'IO'), line, column) - self.register_param(self.vself, line, column) + self_param = self.register_param(self.vself, line, column) result = self.define_internal_local(line, column) self.register_instruction(cil.ReadIntNode(result, line, column)) instance = self.define_internal_local(line, column) @@ -192,7 +191,7 @@ def register_builtin(self): type_node.methods['init'] = self.init_name('IO') # String - type_node = self.register_type('String') + type_node = self.register_type('String', line, column) type_node.attributes = ['value', 'length'] self.current_function = self.register_function(self.init_name('String'), line, column) @@ -209,21 +208,21 @@ def register_builtin(self): self.register_instruction(cil.ReturnNode(instance, line, column)) self.current_function = self.register_function(self.to_function_name('length', 'String'), line, column) - self.register_param(self.vself, line, column) + self_param = self.register_param(self.vself, line, column) result = self.define_internal_local(line, column) - self.register_instruction(cil.GetAttribNode(result, self.vself.name, 'length', 'String', line, column)) + self.register_instruction(cil.GetAttribNode(result, self_param, 'length', 'String', line, column)) self.register_instruction(cil.ReturnNode(result, line, column)) self.current_function = self.register_function(self.to_function_name('concat', 'String'), line, column) - self.register_param(self.vself, line, column) + self_param = self.register_param(self.vself, line, column) s = self.register_param(VariableInfo('s', None), line, column) str_1 = self.define_internal_local(line, column) str_2 = self.define_internal_local(line, column) length_1 = self.define_internal_local(line, column) length_2 = self.define_internal_local(line, column) - self.register_instruction(cil.GetAttribNode(str_1, self.vself.name, 'value', 'String', line, column)) + self.register_instruction(cil.GetAttribNode(str_1, self_param, 'value', 'String', line, column)) self.register_instruction(cil.GetAttribNode(str_2, s, 'value', 'String', line, column)) - self.register_instruction(cil.GetAttribNode(length_1, self.vself.name, 'length', 'String', line, column)) + self.register_instruction(cil.GetAttribNode(length_1, self_param, 'length', 'String', line, column)) self.register_instruction(cil.GetAttribNode(length_2, s, 'length', 'String', line, column)) self.register_instruction(cil.GetAttribNode(length_1, length_1, 'value', 'Int', line, column)) self.register_instruction(cil.GetAttribNode(length_2, length_2, 'value', 'Int', line, column)) @@ -237,7 +236,7 @@ def register_builtin(self): self.register_instruction(cil.ReturnNode(instance, line, column)) self.current_function = self.register_function(self.to_function_name('substr', 'String'), line, column) - self.register_param(self.vself, line, column) + self_param = self.register_param(self.vself, line, column) i = self.register_param(VariableInfo('i', None), line, column) l = self.register_param(VariableInfo('l', None), line, column) result = self.define_internal_local(line, column) @@ -247,11 +246,11 @@ def register_builtin(self): length_substr = self.define_internal_local(line, column) less_value = self.define_internal_local(line, column) str_value = self.define_internal_local(line, column) - self.register_instruction(cil.GetAttribNode(str_value, self.vself.name, 'value', 'String', line, column)) + self.register_instruction(cil.GetAttribNode(str_value, self_param, 'value', 'String', line, column)) self.register_instruction(cil.GetAttribNode(index_value, i, 'value', 'Int', line, column)) self.register_instruction(cil.GetAttribNode(length_value, l, 'value', 'Int', line, column)) # Check Out of range error - self.register_instruction(cil.GetAttribNode(length_attr, self.vself.name, 'length', 'String', line, column)) + self.register_instruction(cil.GetAttribNode(length_attr, self_param, 'length', 'String', line, column)) self.register_instruction(cil.PlusNode(length_substr, length_value, index_value, line, column)) self.register_instruction(cil.LessNode(less_value, length_attr, length_substr, line, column)) self.register_runtime_error(less_value, 'Substring out of range', line, column) diff --git a/src/core/cil/COOLToCILVisitor.py b/src/core/cil/COOLToCILVisitor.py index d95e4a38b..14ba8f323 100644 --- a/src/core/cil/COOLToCILVisitor.py +++ b/src/core/cil/COOLToCILVisitor.py @@ -106,7 +106,8 @@ def visit(self, node: cool.FuncDeclarationNode, scope: Scope): self.current_type.name), line=node.line, column=node.column) - self.register_param(self.vself, line=node.line, column=node.column) + self_param = self.register_param(self.vself, line=node.line, column=node.column) + self.vself.name = self_param for param, type in node.params: self.register_param(VariableInfo(param.lex, type.lex), line=param.line, column=param.column) @@ -114,6 +115,7 @@ def visit(self, node: cool.FuncDeclarationNode, scope: Scope): self.register_instruction(cil.ReturnNode(value=node.body.ret_expr, line=node.body.line, column=node.body.column)) self.current_method = None + self.vself.name = 'self' @visitor.when(cool.IfThenElseNode) def visit(self, node: cool.IfThenElseNode, scope: Scope): @@ -515,23 +517,15 @@ def visit(self, node: cool.FunctionCallNode, scope: Scope): self.register_instruction(arg) ret = self.define_internal_local(line=node.line, column=node.column) - if node.type: - self.register_instruction(cil.StaticCallNode(self.to_function_name(node.id, node.type.name), ret, - line=node.line, column=node.column)) - else: - type = self.define_internal_local(line=node.line, column=node.column) - self.register_instruction(cil.TypeOfNode(type, node.obj.ret_expr, - line=node.obj.line, column=node.obj.column)) - stype = node.obj.static_type.name - self.register_instruction(cil.DynamicCallNode(stype, type, self.types_map[stype].methods[node.id.lex], ret, + stype = node.type + if stype is None: stype = node.obj.static_type.name + self.register_instruction(cil.StaticCallNode(self.types_map[stype].methods[node.id.lex], ret, line=node.id.line, column=node.id.column)) node.ret_expr = ret @visitor.when(cool.MemberCallNode) def visit(self, node: cool.MemberCallNode, scope: Scope): ret = self.define_internal_local(line=node.line, column=node.column) - type = self.define_internal_local(line=node.line, column=node.column) - self.register_instruction(cil.TypeOfNode(type, self.vself.name, line=node.line, column=node.column)) args = [] for arg, child in zip(node.args, scope.childs): @@ -543,7 +537,7 @@ def visit(self, node: cool.MemberCallNode, scope: Scope): for arg in args: self.register_instruction(arg) stype = self.current_type.name - self.register_instruction(cil.DynamicCallNode(stype, type, self.types_map[stype].methods[node.id.lex], ret, line=node.id.line, column=node.id.column)) + self.register_instruction(cil.StaticCallNode(self.types_map[stype].methods[node.id.lex], ret, line=node.id.line, column=node.id.column)) node.ret_expr = ret @visitor.when(cool.NewNode) From 64c4ed02aaf6d952a9d5a64f9b501dd7fecfc1a7 Mon Sep 17 00:00:00 2001 From: mavaldivie Date: Sun, 13 Feb 2022 13:04:57 -0500 Subject: [PATCH 14/75] Created MIPS formatter class --- src/Main.py | 6 +- src/core/mips/MIPSAstFormatter.py | 116 +++++++++++++++++++++++++++++- src/core/mips/MipsAst.py | 28 ++------ 3 files changed, 123 insertions(+), 27 deletions(-) diff --git a/src/Main.py b/src/Main.py index d3d8e1b12..8bc580c79 100644 --- a/src/Main.py +++ b/src/Main.py @@ -7,6 +7,7 @@ from core.cil.COOLToCILVisitor import COOLToCILVisitor from core.cil.CILAst import get_formatter from core.mips.CilToMipsVisitor import CILToMIPSVisitor +from core.mips.MIPSAstFormatter import MIPSAstFormatter def main(args): @@ -50,11 +51,12 @@ def main(args): CILVisitor = COOLToCILVisitor(type_Checker.Context) CILast = CILVisitor.visit(COOLast, scope) - print(get_formatter()(CILast)) MIPSVisitor = CILToMIPSVisitor() MIPSAst = MIPSVisitor.visit(CILast) - print("here") + MIPSFormatter = MIPSAstFormatter() + mipsCode = MIPSFormatter.visit(MIPSAst) + print(mipsCode) if __name__ == "__main__": diff --git a/src/core/mips/MIPSAstFormatter.py b/src/core/mips/MIPSAstFormatter.py index 08457ba43..44f9aa3bf 100644 --- a/src/core/mips/MIPSAstFormatter.py +++ b/src/core/mips/MIPSAstFormatter.py @@ -1,8 +1,122 @@ from core.tools import visitor -import core.mips.MipsAst as mips +from core.mips.MipsAst import * class MIPSAstFormatter: @visitor.on('node') def visit(self, node): pass + @visitor.when(ProgramNode) + def visit(self, node): + return f'.data\n' \ + '\n'.join(self.visit(i) for i in node.data) + '\n'\ + '\n'.join(self.visit(i) for i in node.functions) + '\n' + + @visitor.when(FunctionNode) + def visit(self, node): + return f'{node.label}:\n\t' \ + "\n\t".join(self.visit(i) for i in node.instructions) + + @visitor.when(AbsoluteNode) + def visit(self, node): + pass + + @visitor.when(AdditionNode) + def visit(self, node): + return f'add {self.visit(node.rdest)} {self.visit(node.rsrc1)} {self.visit(node.rsrc2)}' + + @visitor.when(AdditionInmediateNode) + def visit(self, node): + pass + + @visitor.when(DivideNode) + def visit(self, node): + pass + + @visitor.when(MultiplyNode) + def visit(self, node): + pass + + @visitor.when(NegateNode) + def visit(self, node): + pass + + @visitor.when(SubstractionNode) + def visit(self, node): + pass + + @visitor.when(LessNode) + def visit(self, node): + pass + + @visitor.when(LessInmediateNode) + def visit(self, node): + pass + + @visitor.when(EqualNode) + def visit(self, node): + pass + + @visitor.when(LessEqualNode) + def visit(self, node): + pass + + @visitor.when(JumpNode) + def visit(self, node): + pass + + @visitor.when(JalNode) + def visit(self, node): + pass + + @visitor.when(MoveNode) + def visit(self, node): + pass + + @visitor.when(StoreWordNode) + def visit(self, node): + pass + + @visitor.when(LoadInmediateNode) + def visit(self, node): + pass + + @visitor.when(LoadWordNode) + def visit(self, node): + pass + + @visitor.when(LoadAddressNode) + def visit(self, node): + pass + + @visitor.when(BranchOnNotEqualNode) + def visit(self, node): + pass + + @visitor.when(LabelNode) + def visit(self, node): + pass + + @visitor.when(NotNode) + def visit(self, node): + pass + + @visitor.when(ShiftLeftNode) + def visit(self, node): + pass + + @visitor.when(SyscallNode) + def visit(self, node): + pass + + @visitor.when(StringConst) + def visit(self, node): + pass + + @visitor.when(RegisterRelativeLocation) + def visit(self, node): + pass + + @visitor.when(LabelRelativeLocation) + def visit(self, node): + pass \ No newline at end of file diff --git a/src/core/mips/MipsAst.py b/src/core/mips/MipsAst.py index d3c5b9c8f..1b8f2739e 100644 --- a/src/core/mips/MipsAst.py +++ b/src/core/mips/MipsAst.py @@ -62,28 +62,6 @@ def get_var_location(self, name): class InstructionNode(Node): pass - -class PrintIntNode(InstructionNode): - # $a0 = integer - pass - -class PrintStringNode(InstructionNode): - # $a0 = string - pass - -class ReadIntNode(InstructionNode): - # integer(int $v0) - pass - -class ReadStringNode(InstructionNode): - # integer(int $v0) - pass - -class ExitNode(InstructionNode): - pass - - - class AbsoluteNode(InstructionNode): # rdest <- abs(rsrc) def __init__(self, rdest, rsrc): @@ -246,17 +224,19 @@ def __init__(self, dest, src, bits): class SyscallNode(InstructionNode): pass + + + class DataNode(Node): def __init__(self, label): self.label = label - - class StringConst(DataNode): def __init__(self, label, string): super().__init__(label) self.string = string + class MIPSType: def __init__(self, label, name_addr, attributes, methods, index, default=[]): From a684a7c9a3c7420099c6b741d1474bf77d7f66f8 Mon Sep 17 00:00:00 2001 From: ClaudiaOM Date: Mon, 14 Feb 2022 15:22:34 -0600 Subject: [PATCH 15/75] Implemented MIPSAstFormatter --- src/Main.py | 8 ++++ src/core/mips/CilToMipsVisitor.py | 2 +- src/core/mips/MIPSAstFormatter.py | 71 ++++++++++++++++++------------- src/core/mips/MipsAst.py | 10 ++--- 4 files changed, 56 insertions(+), 35 deletions(-) diff --git a/src/Main.py b/src/Main.py index 8bc580c79..147d281a0 100644 --- a/src/Main.py +++ b/src/Main.py @@ -16,6 +16,7 @@ def main(args): code = fd.read() except Exception as e: print(f"(0,0) - CompilerError: {e.args}") + print(args.file) exit(1) lexer = Lexer() @@ -58,6 +59,13 @@ def main(args): mipsCode = MIPSFormatter.visit(MIPSAst) print(mipsCode) + out_file = args.file.split(".") + out_file[-1] = "mips" + out_file = ".".join(out_file) + + with open(out_file, 'w') as f: + f.write(mipsCode) + if __name__ == "__main__": import argparse diff --git a/src/core/mips/CilToMipsVisitor.py b/src/core/mips/CilToMipsVisitor.py index 3fb1f1d74..3409dc6ff 100644 --- a/src/core/mips/CilToMipsVisitor.py +++ b/src/core/mips/CilToMipsVisitor.py @@ -554,7 +554,7 @@ def visit(self, node: cil.LengthNode): reg = mips.REGISTERS[0] instructions.append(mips.LoadWordNode(reg, self.get_var_location(node.source))) - instructions.append(mips.push_register(reg)) + instructions.extend(mips.push_register(reg)) instructions.append(mips.JalNode("len")) instructions.append(mips.StoreWordNode(mips.V0_REG, self.get_var_location(node.dest))) diff --git a/src/core/mips/MIPSAstFormatter.py b/src/core/mips/MIPSAstFormatter.py index 44f9aa3bf..f3eb15828 100644 --- a/src/core/mips/MIPSAstFormatter.py +++ b/src/core/mips/MIPSAstFormatter.py @@ -8,18 +8,19 @@ def visit(self, node): @visitor.when(ProgramNode) def visit(self, node): - return f'.data\n' \ - '\n'.join(self.visit(i) for i in node.data) + '\n'\ + return f'.data\n' + \ + '\n'.join(self.visit(i) for i in node.data) + '\n' + \ '\n'.join(self.visit(i) for i in node.functions) + '\n' @visitor.when(FunctionNode) def visit(self, node): - return f'{node.label}:\n\t' \ - "\n\t".join(self.visit(i) for i in node.instructions) + print([self.visit(i) for i in node.instructions]) + return f'{node.label}:\n\t' + \ + f'\n\t'.join(self.visit(i) for i in node.instructions) @visitor.when(AbsoluteNode) def visit(self, node): - pass + return f'abs {self.visit(node.rdest)} {self.visit(node.rsrc)}' @visitor.when(AdditionNode) def visit(self, node): @@ -27,96 +28,108 @@ def visit(self, node): @visitor.when(AdditionInmediateNode) def visit(self, node): - pass + return f'addi {self.visit(node.rdest)} {self.visit(node.rsrc)} {self.visit(node.imm)}' @visitor.when(DivideNode) def visit(self, node): - pass + return f'div {self.visit(node.rsrc1)} {self.visit(node.rsrc2)}' @visitor.when(MultiplyNode) def visit(self, node): - pass + return f'mul {self.visit(node.rdest)} {self.visit(node.rsrc1)} {self.visit(node.rsrc2)}' @visitor.when(NegateNode) def visit(self, node): - pass + return f'neg {self.visit(node.rdest)} {self.visit(node.rsrc1)}' @visitor.when(SubstractionNode) def visit(self, node): - pass + return f'sub {self.visit(node.rdest)} {self.visit(node.rsrc1)} {self.visit(node.rsrc2)}' @visitor.when(LessNode) def visit(self, node): - pass + return f'slt {self.visit(node.rdest)} {self.visit(node.rsrc1)} {self.visit(node.rsrc2)}' @visitor.when(LessInmediateNode) def visit(self, node): - pass + return f'slti {self.visit(node.rdest)} {self.visit(node.rsrc1)} {self.visit(node.imm)}' @visitor.when(EqualNode) def visit(self, node): - pass + return f'seq {self.visit(node.rdest)} {self.visit(node.rsrc1)} {self.visit(node.rsrc2)}' @visitor.when(LessEqualNode) def visit(self, node): - pass + return f'sle {self.visit(node.rdest)} {self.visit(node.rsrc1)} {self.visit(node.rsrc2)}' @visitor.when(JumpNode) def visit(self, node): - pass + return f'j {self.visit(node.label)}' @visitor.when(JalNode) def visit(self, node): - pass + return f'jal {self.visit(node.label)}' @visitor.when(MoveNode) def visit(self, node): - pass + return f'move {self.visit(node.reg1)} {self.visit(node.reg2)}' @visitor.when(StoreWordNode) def visit(self, node): - pass + return f'sw {self.visit(node.reg)} {self.visit(node.addr)}' @visitor.when(LoadInmediateNode) def visit(self, node): - pass + return f'li {self.visit(node.reg)} {self.visit(node.value)}' @visitor.when(LoadWordNode) def visit(self, node): - pass + return f'lw {self.visit(node.reg)} {self.visit(node.addr)}' @visitor.when(LoadAddressNode) def visit(self, node): - pass + return f'la {self.visit(node.reg)} {self.visit(node.label)}' @visitor.when(BranchOnNotEqualNode) def visit(self, node): - pass + return f'bne {self.visit(node.reg1)} {self.visit(node.reg2)} {self.visit(node.label)}' @visitor.when(LabelNode) def visit(self, node): - pass + return f'{self.visit(node.name)}' @visitor.when(NotNode) def visit(self, node): - pass + return f'not {self.visit(node.dest)} {self.visit(node.src)}' @visitor.when(ShiftLeftNode) def visit(self, node): - pass + return f'sll {self.visit(node.dest)} {self.visit(node.src)} {self.visit(node.bits)}' @visitor.when(SyscallNode) def visit(self, node): - pass + return 'syscall' @visitor.when(StringConst) def visit(self, node): - pass + return f'{node.label}: .asciiz "{node.string}"' @visitor.when(RegisterRelativeLocation) def visit(self, node): - pass + return f'{node._offset}({self.visit(node._register)})' @visitor.when(LabelRelativeLocation) def visit(self, node): - pass \ No newline at end of file + return f'{node._label} + {node._offset}' + + @visitor.when(Register) + def visit(self, node): + return f'{node.name}' + + @visitor.when(int) + def visit(self, node): + return str(node) + + @visitor.when(str) + def visit(self, node): + return node \ No newline at end of file diff --git a/src/core/mips/MipsAst.py b/src/core/mips/MipsAst.py index 1b8f2739e..cdea86a26 100644 --- a/src/core/mips/MipsAst.py +++ b/src/core/mips/MipsAst.py @@ -118,7 +118,7 @@ def __init__(self, rdest, rsrc1): class SubstractionNode(InstructionNode): def __init__(self, rdest, rsrc1, rsrc2): ''' - Put the difference of register rsrc1 and src2 into register rdest. + Put the difference of register rsrc1 and rsrc2 into register rdest. ''' self.rdest = rdest self.rsrc1 = rsrc1 @@ -197,6 +197,9 @@ def __init__(self, reg, addr): class LoadAddressNode(InstructionNode): def __init__(self, reg, label): + ''' + Load computed address , not the contents of the location, into register rdest + ''' self.reg = reg self.label = label @@ -215,7 +218,7 @@ def __init__(self, dest, src): self.dest = dest self.src = src -class ShiftLeftNode(InstructionNode): +class ShiftLeftNode(InstructionNode): # Shift Left Logical def __init__(self, dest, src, bits): self.dest = dest self.src = src @@ -224,9 +227,6 @@ def __init__(self, dest, src, bits): class SyscallNode(InstructionNode): pass - - - class DataNode(Node): def __init__(self, label): self.label = label From 58e00e3fe72e314325a21c18511be204e460c9c3 Mon Sep 17 00:00:00 2001 From: ClaudiaOM Date: Mon, 14 Feb 2022 22:46:31 -0600 Subject: [PATCH 16/75] Implemented debug line and column for CilToMips --- src/core/mips/CilToMipsVisitor.py | 392 ++++++++++++++++++------------ src/core/mips/MIPSAstFormatter.py | 1 - src/core/mips/MipsAst.py | 175 ++++++++----- 3 files changed, 350 insertions(+), 218 deletions(-) diff --git a/src/core/mips/CilToMipsVisitor.py b/src/core/mips/CilToMipsVisitor.py index 3409dc6ff..02bdde5a4 100644 --- a/src/core/mips/CilToMipsVisitor.py +++ b/src/core/mips/CilToMipsVisitor.py @@ -2,6 +2,7 @@ import core.mips.MipsAst as mips from ..tools import visitor + class CILToMIPSVisitor: def __init__(self): self.type_label_count = 0 @@ -70,8 +71,7 @@ def collect_func_names(self, node): if node.name == "entry": self._function_names[node.name] = 'main' else: - self._function_names[node.name] = self.generate_code_label() - + self._function_names[node.name] = node.name @visitor.on('node') def visit(self, node): @@ -80,7 +80,7 @@ def visit(self, node): @visitor.when(cil.ProgramNode) def visit(self, node): self.collect_func_names(node) - self._data_section["default_str"] = mips.StringConst("default_str", "") + self._data_section["default_str"] = mips.StringConst("default_str", "", line=node.line, column=node.column) for i in node.dottypes: self.visit(i) @@ -91,12 +91,13 @@ def visit(self, node): return mips.ProgramNode([i for i in self._data_section.values()], [i for i in self._types_section.values()], - [i for i in self._functions_section.values()]) + [i for i in self._functions_section.values()], + line=node.line, column=node.column) @visitor.when(cil.TypeNode) def visit(self, node): name_label = self.generate_data_label() - self._data_section[node.name] = mips.StringConst(name_label, node.name) + self._data_section[node.name] = mips.StringConst(name_label, node.name, line=node.line, column=node.column) type_label = self.generate_type_label() methods = [self._function_names[key] for key in node.methods.values()] @@ -111,7 +112,7 @@ def visit(self, node): @visitor.when(cil.DataNode) def visit(self, node): label = self.generate_data_label() - self._data_section[node.name] = mips.StringConst(label, node.value) + self._data_section[node.name] = mips.StringConst(label, node.value, line=node.line, column=node.column) @visitor.when(cil.FunctionNode) def visit(self, node): @@ -120,7 +121,7 @@ def visit(self, node): localvars = [local.name for local in node.localvars] size_for_locals = len(localvars) * mips.DOUBLE_WORD - new_func = mips.FunctionNode(label, params, localvars) + new_func = mips.FunctionNode(label, params, localvars, line=node.line, column=node.column) self.enter_function(node.name, new_func) self._current_function = new_func self._labels = {} @@ -131,23 +132,26 @@ def visit(self, node): self.register_label(instruction.label, mips_label) instructions = [] - instructions.extend(mips.push_register(mips.FP_REG)) - instructions.append(mips.AdditionInmediateNode(mips.FP_REG, mips.SP_REG, 4)) - instructions.append(mips.AdditionInmediateNode(mips.SP_REG, mips.SP_REG, -size_for_locals)) + instructions.extend(mips.push_register(mips.FP_REG, line=node.line, column=node.column)) + instructions.append(mips.AdditionInmediateNode(mips.FP_REG, mips.SP_REG, 4, line=node.line, column=node.column)) + instructions.append(mips.AdditionInmediateNode(mips.SP_REG, mips.SP_REG, -size_for_locals, + line=node.line, column=node.column)) reg = mips.REGISTERS[0] for param in params: - instructions.append(mips.LoadWordNode(reg, self.get_var_location(param))) + instructions.append(mips.LoadWordNode(reg, self.get_var_location(param), + line=node.line, column=node.column)) for i in node.instructions: instructions.extend(self.visit(i)) - instructions.append(mips.AdditionInmediateNode(mips.SP_REG, mips.SP_REG, size_for_locals)) - instructions.extend(mips.pop_register(mips.FP_REG)) + instructions.append(mips.AdditionInmediateNode(mips.SP_REG, mips.SP_REG, size_for_locals, + line=node.line, column=node.column)) + instructions.extend(mips.pop_register(mips.FP_REG, line=node.line, column=node.column)) if self._current_function.label != 'main': - instructions.append(mips.JumpNode(mips.RA_REG)) + instructions.append(mips.JumpNode(mips.RA_REG, line=node.line, column=node.column)) else: - instructions.append(mips.LoadInmediateNode(mips.V0_REG, 10)) + instructions.append(mips.LoadInmediateNode(mips.V0_REG, 10, line=node.line, column=node.column)) instructions.append(mips.SyscallNode()) new_func.instructions = instructions @@ -161,12 +165,14 @@ def visit(self, node): reg1 = mips.ZERO_REG elif node.source.isnumeric(): reg1 = mips.REGISTERS[0] - instructions.append(mips.LoadInmediateNode(reg1, int(node.source))) + instructions.append(mips.LoadInmediateNode(reg1, int(node.source), line=node.line, column=node.column)) else: reg1 = mips.REGISTERS[0] - instructions.append(mips.LoadWordNode(reg1, self.get_var_location(node.source))) + instructions.append(mips.LoadWordNode(reg1, self.get_var_location(node.source), + line=node.line, column=node.column)) - instructions.append(mips.StoreWordNode(reg1, self.get_var_location(node.dest))) + instructions.append(mips.StoreWordNode(reg1, self.get_var_location(node.dest), + line=node.line, column=node.column)) return instructions @visitor.when(cil.PlusNode) @@ -175,19 +181,22 @@ def visit(self, node): reg1 = mips.REGISTERS[0] if isinstance(node.left, int): - instructions.append(mips.LoadInmediateNode(reg1,node.left)) + instructions.append(mips.LoadInmediateNode(reg1, node.left, line=node.line, column=node.column)) else: - instructions.append(mips.LoadWordNode(reg1,self.get_var_location(node.left))) + instructions.append(mips.LoadWordNode(reg1, self.get_var_location(node.left), + line=node.line, column=node.column)) reg2 = mips.REGISTERS[1] if isinstance(node.right, int): - instructions.append(mips.LoadInmediateNode(reg2, node.right)) + instructions.append(mips.LoadInmediateNode(reg2, node.right, line=node.line, column=node.column)) else: - instructions.append(mips.LoadWordNode(reg2, self.get_var_location(node.right))) + instructions.append(mips.LoadWordNode(reg2, self.get_var_location(node.right), + line=node.line, column=node.column)) reg3 = mips.REGISTERS[0] - instructions.append(mips.AdditionNode(reg3, reg1, reg2)) - instructions.append(mips.StoreWordNode(reg3, self.get_var_location(node.dest))) + instructions.append(mips.AdditionNode(reg3, reg1, reg2, line=node.line, column=node.column)) + instructions.append(mips.StoreWordNode(reg3, self.get_var_location(node.dest), + line=node.line, column=node.column)) return instructions @@ -197,19 +206,22 @@ def visit(self, node): reg1 = mips.REGISTERS[0] if isinstance(node.left, int): - instructions.append(mips.LoadInmediateNode(reg1, node.left)) + instructions.append(mips.LoadInmediateNode(reg1, node.left, line=node.line, column=node.column)) else: - instructions.append(mips.LoadWordNode(reg1, self.get_var_location(node.left))) + instructions.append(mips.LoadWordNode(reg1, self.get_var_location(node.left), + line=node.line, column=node.column)) reg2 = mips.REGISTERS[1] if isinstance(node.right, int): - instructions.append(mips.LoadInmediateNode(reg2, node.right)) + instructions.append(mips.LoadInmediateNode(reg2, node.right, line=node.line, column=node.column)) else: - instructions.append(mips.LoadWordNode(reg2, self.get_var_location(node.right))) + instructions.append(mips.LoadWordNode(reg2, self.get_var_location(node.right), + line=node.line, column=node.column)) reg3 = mips.REGISTERS[0] - instructions.append(mips.SubstractionNode(reg3, reg1, reg2)) - instructions.append(mips.StoreWordNode(reg3, self.get_var_location(node.dest))) + instructions.append(mips.SubstractionNode(reg3, reg1, reg2, line=node.line, column=node.column)) + instructions.append(mips.StoreWordNode(reg3, self.get_var_location(node.dest), + line=node.line, column=node.column)) return instructions @@ -219,19 +231,22 @@ def visit(self, node): reg1 = mips.REGISTERS[0] if isinstance(node.left, int): - instructions.append(mips.LoadInmediateNode(reg1, node.left)) + instructions.append(mips.LoadInmediateNode(reg1, node.left, line=node.line, column=node.column)) else: - instructions.append(mips.LoadWordNode(reg1, self.get_var_location(node.left))) + instructions.append(mips.LoadWordNode(reg1, self.get_var_location(node.left), + line=node.line, column=node.column)) reg2 = mips.REGISTERS[1] if isinstance(node.right, int): - instructions.append(mips.LoadInmediateNode(reg2, node.right)) + instructions.append(mips.LoadInmediateNode(reg2, node.right, line=node.line, column=node.column)) else: - instructions.append(mips.LoadWordNode(reg2, self.get_var_location(node.right))) + instructions.append(mips.LoadWordNode(reg2, self.get_var_location(node.right), + line=node.line, column=node.column)) reg3 = mips.REGISTERS[0] - instructions.append(mips.MultiplyNode(reg3, reg1, reg2)) - instructions.append(mips.StoreWordNode(reg3, self.get_var_location(node.dest))) + instructions.append(mips.MultiplyNode(reg3, reg1, reg2, line=node.line, column=node.column)) + instructions.append(mips.StoreWordNode(reg3, self.get_var_location(node.dest), + line=node.line, column=node.column)) return instructions @@ -241,19 +256,22 @@ def visit(self, node): reg1 = mips.REGISTERS[0] if isinstance(node.left, int): - instructions.append(mips.LoadInmediateNode(reg1, node.left)) + instructions.append(mips.LoadInmediateNode(reg1, node.left, line=node.line, column=node.column)) else: - instructions.append(mips.LoadWordNode(reg1, self.get_var_location(node.left))) + instructions.append(mips.LoadWordNode(reg1, self.get_var_location(node.left), + line=node.line, column=node.column)) reg2 = mips.REGISTERS[1] if isinstance(node.right, int): - instructions.append(mips.LoadInmediateNode(reg2, node.right)) + instructions.append(mips.LoadInmediateNode(reg2, node.right, line=node.line, column=node.column)) else: - instructions.append(mips.LoadWordNode(reg2, self.get_var_location(node.right))) + instructions.append(mips.LoadWordNode(reg2, self.get_var_location(node.right), + line=node.line, column=node.column)) reg3 = mips.LOW_REG - instructions.append(mips.DivideNode(reg1, reg2)) - instructions.append(mips.StoreWordNode(reg3, self.get_var_location(node.dest))) + instructions.append(mips.DivideNode(reg1, reg2, line=node.line, column=node.column)) + instructions.append(mips.StoreWordNode(reg3, self.get_var_location(node.dest), + line=node.line, column=node.column)) return instructions @@ -269,9 +287,10 @@ def visit(self, node): reg1 = mips.REGISTERS[0] reg2 = mips.REGISTERS[1] - instructions.append(mips.LoadInmediateNode(reg1, type)) - instructions.extend(mips.create_object(reg1, reg2)) - instructions.append(mips.StoreWordNode(mips.V0_REG, self.get_var_location(node.dest))) + instructions.append(mips.LoadInmediateNode(reg1, type, line=node.line, column=node.column)) + instructions.extend(mips.create_object(reg1, reg2, line=node.line, column=node.column)) + instructions.append(mips.StoreWordNode(mips.V0_REG, self.get_var_location(node.dest), + line=node.line, column=node.column)) return instructions @@ -282,9 +301,12 @@ def visit(self, node): reg1 = mips.REGISTERS[0] reg2 = mips.REGISTERS[1] - instructions.append(mips.LoadWordNode(reg1, self.get_var_location(node.obj))) - instructions.append(mips.LoadWordNode(reg2, mips.RegisterRelativeLocation(reg1, 0))) - instructions.append(mips.StoreWordNode(reg2, self.get_var_location(node.dest))) + instructions.append(mips.LoadWordNode(reg1, self.get_var_location(node.obj), + line=node.line, column=node.column)) + instructions.append(mips.LoadWordNode(reg2, mips.RegisterRelativeLocation(reg1, 0), + line=node.line, column=node.column)) + instructions.append(mips.StoreWordNode(reg2, self.get_var_location(node.dest), + line=node.line, column=node.column)) return instructions @@ -292,11 +314,14 @@ def visit(self, node): def visit(self, node): instructions = [] func_name = self._function_names[node.function] - instructions.append(mips.JalNode(func_name)) + instructions.append(mips.JalNode(func_name, line=node.line, column=node.column)) - instructions.append(mips.StoreWordNode(mips.V0_REG, self.get_var_location(node.dest))) + instructions.append(mips.StoreWordNode(mips.V0_REG, self.get_var_location(node.dest), + line=node.line, column=node.column)) if self._pushed_args > 0: - instructions.append(mips.AdditionInmediateNode(mips.SP_REG, mips.SP_REG, self._pushed_args * mips.DOUBLE_WORD)) + instructions.append(mips.AdditionInmediateNode(mips.SP_REG, mips.SP_REG, + self._pushed_args * mips.DOUBLE_WORD, + line=node.line, column=node.column)) self._pushed_args = 0 return instructions @@ -310,20 +335,27 @@ def visit(self, node): reg1 = mips.REGISTERS[0] reg2 = mips.REGISTERS[1] - instructions.append(mips.LoadWordNode(reg1, self.get_var_location(node.obj))) - instructions.append(mips.LoadAddressNode(reg2, mips.PROTOTYPE_LABEL)) - instructions.append(mips.ShiftLeftNode(reg2, reg1, 2)) - instructions.append(mips.AdditionNode(reg1, reg1, reg2)) - instructions.append(mips.LoadWordNode(reg1, mips.RegisterRelativeLocation(reg1, 0))) - instructions.append(mips.LoadWordNode(reg1, mips.RegisterRelativeLocation(reg1, 8))) - instructions.append(mips.AdditionInmediateNode(reg1, reg1, method * 4)) - instructions.append(mips.LoadWordNode(reg1, mips.RegisterRelativeLocation(reg1, 0))) - instructions.append(mips.JalNode(reg1)) - - instructions.append(mips.StoreWordNode(mips.V0_REG, self.get_var_location(node.dest))) + instructions.append(mips.LoadWordNode(reg1, self.get_var_location(node.obj), + line=node.line, column=node.column)) + instructions.append(mips.LoadAddressNode(reg2, mips.PROTOTYPE_LABEL, line=node.line, column=node.column)) + instructions.append(mips.ShiftLeftNode(reg2, reg1, 2, line=node.line, column=node.column)) + instructions.append(mips.AdditionNode(reg1, reg1, reg2, line=node.line, column=node.column)) + instructions.append(mips.LoadWordNode(reg1, mips.RegisterRelativeLocation(reg1, 0), + line=node.line, column=node.column)) + instructions.append(mips.LoadWordNode(reg1, mips.RegisterRelativeLocation(reg1, 8), + line=node.line, column=node.column)) + instructions.append(mips.AdditionInmediateNode(reg1, reg1, method * 4, line=node.line, column=node.column)) + instructions.append(mips.LoadWordNode(reg1, mips.RegisterRelativeLocation(reg1, 0), + line=node.line, column=node.column)) + instructions.append(mips.JalNode(reg1, line=node.line, column=node.column)) + + instructions.append(mips.StoreWordNode(mips.V0_REG, self.get_var_location(node.dest), + line=node.line, column=node.column)) if self._pushed_args > 0: - instructions.append(mips.AdditionInmediateNode(mips.SP_REG, mips.SP_REG, self._pushed_args * mips.DOUBLE_WORD)) + instructions.append(mips.AdditionInmediateNode(mips.SP_REG, mips.SP_REG, + self._pushed_args * mips.DOUBLE_WORD, + line=node.line, column=node.column)) self._pushed_args = 0 return instructions @@ -334,10 +366,12 @@ def visit(self, node): instructions = [] reg = mips.REGISTERS[0] if isinstance(node.name, int): - instructions.append(mips.LoadInmediateNode(mips.ARG_REGISTERS[0], node.name)) + instructions.append(mips.LoadInmediateNode(mips.ARG_REGISTERS[0], node.name, + line=node.line, column=node.column)) else: - instructions.append(mips.LoadWordNode(reg, self.get_var_location(node.name))) - instructions.extend(mips.push_register(reg)) + instructions.append(mips.LoadWordNode(reg, self.get_var_location(node.name), + line=node.line, column=node.column)) + instructions.extend(mips.push_register(reg, line=node.line, column=node.column)) return instructions @visitor.when(cil.ReturnNode) @@ -345,11 +379,13 @@ def visit(self, node): instructions = [] if node.value is None or isinstance(node.value, cil.VoidNode): - instructions.append(mips.LoadInmediateNode(mips.V0_REG, 0)) + instructions.append(mips.LoadInmediateNode(mips.V0_REG, 0, line=node.value.line, column=node.value.column)) elif isinstance(node.value, int): - instructions.append(mips.LoadInmediateNode(mips.V0_REG, node.value)) + instructions.append(mips.LoadInmediateNode(mips.V0_REG, node.value, + line=node.line, column=node.column)) else: - instructions.append(mips.LoadWordNode(mips.V0_REG, self.get_var_location(node.value))) + instructions.append(mips.LoadWordNode(mips.V0_REG, self.get_var_location(node.value), + line=node.line, column=node.column)) return instructions @visitor.when(cil.LoadNode) @@ -359,30 +395,32 @@ def visit(self, node): string = mips.LabelRelativeLocation(self._data_section[node.msg.name].label, 0) reg = mips.REGISTERS[0] - instructions.append(mips.LoadAddressNode(reg, string)) + instructions.append(mips.LoadAddressNode(reg, string, line=node.line, column=node.column)) return instructions @visitor.when(cil.PrintStringNode) def visit(self, node: cil.PrintStringNode): instructions = [] - instructions.append(mips.LoadInmediateNode(mips.V0_REG, 4)) - instructions.append(mips.LoadWordNode(mips.ARG_REGISTERS[0], self.get_var_location(node.str_addr))) + instructions.append(mips.LoadInmediateNode(mips.V0_REG, 4, line=node.line, column=node.column)) + instructions.append(mips.LoadWordNode(mips.ARG_REGISTERS[0], self.get_var_location(node.str_addr), + line=node.line, column=node.column)) instructions.append(mips.SyscallNode()) return instructions @visitor.when(cil.PrintIntNode) def visit(self, node: cil.PrintIntNode): instructions = [] - instructions.append(mips.LoadInmediateNode(mips.V0_REG, 1)) - instructions.append(mips.LoadWordNode(mips.ARG_REGISTERS[0], self.get_var_location(node.value))) + instructions.append(mips.LoadInmediateNode(mips.V0_REG, 1, line=node.line, column=node.column)) + instructions.append(mips.LoadWordNode(mips.ARG_REGISTERS[0], self.get_var_location(node.value), + line=node.line, column=node.column)) instructions.append(mips.SyscallNode()) return instructions @visitor.when(cil.ExitNode) def visit(self, node: cil.ExitNode): instructions = [] - instructions.append(mips.LoadInmediateNode(mips.V0_REG, 10)) + instructions.append(mips.LoadInmediateNode(mips.V0_REG, 10, line=node.line, column=node.column)) instructions.append(mips.SyscallNode()) return instructions @@ -391,15 +429,20 @@ def visit(self, node): instructions = [] reg = mips.REGISTERS[0] - instructions.append(mips.LoadWordNode(reg, self.get_var_location(node.value))) - instructions.append(mips.LoadWordNode(mips.ARG_REGISTERS[0], mips.RegisterRelativeLocation(reg, 4))) - instructions.append(mips.ShiftLeftNode(mips.ARG_REGISTERS[0], mips.ARG_REGISTERS[0], 2)) - instructions.append(mips.JalNode("malloc")) - instructions.append(mips.MoveNode(mips.ARG_REGISTERS[2], mips.ARG_REGISTERS[0])) - instructions.append(mips.MoveNode(mips.ARG_REGISTERS[0], reg)) - instructions.append(mips.MoveNode(mips.ARG_REGISTERS[1], mips.V0_REG)) - instructions.append(mips.JalNode("copy")) - instructions.append(mips.StoreWordNode(mips.V0_REG, self.get_var_location(node.dest))) + instructions.append(mips.LoadWordNode(reg, self.get_var_location(node.value), + line=node.line, column=node.column)) + instructions.append(mips.LoadWordNode(mips.ARG_REGISTERS[0], mips.RegisterRelativeLocation(reg, 4), + line=node.line, column=node.column)) + instructions.append(mips.ShiftLeftNode(mips.ARG_REGISTERS[0], mips.ARG_REGISTERS[0], 2, + line=node.line, column=node.column)) + instructions.append(mips.JalNode("malloc", line=node.line, column=node.column)) + instructions.append(mips.MoveNode(mips.ARG_REGISTERS[2], mips.ARG_REGISTERS[0] + , line=node.line, column=node.column)) + instructions.append(mips.MoveNode(mips.ARG_REGISTERS[0], reg, line=node.line, column=node.column)) + instructions.append(mips.MoveNode(mips.ARG_REGISTERS[1], mips.V0_REG, line=node.line, column=node.column)) + instructions.append(mips.JalNode("copy", line=node.line, column=node.column)) + instructions.append(mips.StoreWordNode(mips.V0_REG, self.get_var_location(node.dest) + , line=node.line, column=node.column)) return instructions @@ -409,12 +452,15 @@ def visit(self, node: cil.GetAttribNode): reg1 = mips.REGISTERS[0] reg2 = mips.REGISTERS[1] - instructions.append(mips.LoadWordNode(reg1, self.get_var_location(node.obj))) + instructions.append(mips.LoadWordNode(reg1, self.get_var_location(node.obj), + line=node.line, column=node.column)) tp = self._types_section[node.computed_type] offset = (tp.attributes.index(node.attr) + 3) * mips.DOUBLE_WORD - instructions.append(mips.LoadWordNode(reg2, mips.RegisterRelativeLocation(reg1, offset))) - instructions.append(mips.StoreWordNode(reg2, self.get_var_location(node.dest))) + instructions.append(mips.LoadWordNode(reg2, mips.RegisterRelativeLocation(reg1, offset), + line=node.line, column=node.column)) + instructions.append(mips.StoreWordNode(reg2, self.get_var_location(node.dest), + line=node.line, column=node.column)) return instructions @@ -423,10 +469,10 @@ def visit(self, node: cil.ErrorNode): instructions = [] mips_label = self._data_section[node.data.name].label - instructions.append(mips.LoadInmediateNode(mips.V0_REG, 4)) - instructions.append(mips.LoadAddressNode(mips.ARG_REGISTERS[0], mips_label)) + instructions.append(mips.LoadInmediateNode(mips.V0_REG, 4, line=node.line, column=node.column)) + instructions.append(mips.LoadAddressNode(mips.ARG_REGISTERS[0], mips_label, line=node.line, column=node.column)) instructions.append(mips.SyscallNode()) - instructions.append(mips.LoadInmediateNode(mips.V0_REG, 10)) + instructions.append(mips.LoadInmediateNode(mips.V0_REG, 10, line=node.line, column=node.column)) instructions.append(mips.SyscallNode()) return instructions @@ -435,9 +481,10 @@ def visit(self, node: cil.ErrorNode): def visit(self, node: cil.ReadIntNode): instructions = [] - instructions.append(mips.LoadInmediateNode(mips.V0_REG, 5)) + instructions.append(mips.LoadInmediateNode(mips.V0_REG, 5, line=node.line, column=node.column)) instructions.append(mips.SyscallNode()) - instructions.append(mips.StoreWordNode(mips.V0_REG, self.get_var_location(node.dest))) + instructions.append(mips.StoreWordNode(mips.V0_REG, self.get_var_location(node.dest), + line=node.line, column=node.column)) return instructions @@ -461,15 +508,19 @@ def visit(self, node: cil.SetAttribNode): offset = (tp.attributes.index(node.attr) + 3) * mips.DOUBLE_WORD reg1 = mips.REGISTERS[0] - instructions.append(mips.LoadWordNode(reg1, self.get_var_location(node.obj))) + instructions.append(mips.LoadWordNode(reg1, self.get_var_location(node.obj), + line=node.line, column=node.column)) reg2 = mips.REGISTERS[1] if type(node.value) == int: - instructions.append(mips.LoadInmediateNode(reg2, node.value)) + instructions.append(mips.LoadInmediateNode(reg2, node.value, + line=node.line, column=node.column)) else: - instructions.append(mips.LoadWordNode(reg2, self.get_var_location(node.value))) + instructions.append(mips.LoadWordNode(reg2, self.get_var_location(node.value), + line=node.line, column=node.column)) - instructions.append(mips.StoreWordNode(reg2, mips.RegisterRelativeLocation(reg1, offset))) + instructions.append(mips.StoreWordNode(reg2, mips.RegisterRelativeLocation(reg1, offset), + line=node.line, column=node.column)) return instructions @visitor.when(cil.LessNode) @@ -477,12 +528,15 @@ def visit(self, node: cil.LessNode): instructions = [] reg1 = mips.REGISTERS[0] - instructions.append(mips.LoadWordNode(reg1, self.get_var_location(node.left))) + instructions.append(mips.LoadWordNode(reg1, self.get_var_location(node.left), + line=node.line, column=node.column)) reg2 = mips.REGISTERS[1] - instructions.append(mips.LoadWordNode(reg2, self.get_var_location(node.right))) - instructions.append(mips.LessNode(reg2, reg1, reg2)) - instructions.append(mips.StoreWordNode(reg2, self.get_var_location(node.dest))) + instructions.append(mips.LoadWordNode(reg2, self.get_var_location(node.right), + line=node.line, column=node.column)) + instructions.append(mips.LessNode(reg2, reg1, reg2, line=node.line, column=node.column)) + instructions.append(mips.StoreWordNode(reg2, self.get_var_location(node.dest), + line=node.line, column=node.column)) return instructions @@ -492,42 +546,48 @@ def visit(self, node: cil.GotoIfNode): mips_label = self.get_label(node.label) reg1 = mips.REGISTERS[0] - instructions.append(mips.LoadWordNode(reg1, self.get_var_location(node.condition))) - instructions.append(mips.BranchOnNotEqualNode(reg1, mips.ZERO_REG, mips_label)) + instructions.append(mips.LoadWordNode(reg1, self.get_var_location(node.condition), + line=node.line, column=node.column)) + instructions.append(mips.BranchOnNotEqualNode(reg1, mips.ZERO_REG, mips_label, + line=node.line, column=node.column)) return instructions @visitor.when(cil.GotoNode) def visit(self, node: cil.GotoNode): mips_label = self.get_label(node.label) - return [mips.JumpNode(mips_label)] + return [mips.JumpNode(mips_label, line=node.line, column=node.column)] @visitor.when(cil.LabelNode) def visit(self, node: cil.LabelNode): - return [mips.LabelNode(self.get_label(node.label))] + return [mips.LabelNode(self.get_label(node.label), line=node.line, column=node.column)] @visitor.when(cil.SubstringNode) def visit(self, node: cil.SubstringNode): instructions = [] reg1 = mips.REGISTERS[0] - instructions.append(mips.LoadWordNode(reg1, self.get_var_location(node.str_value))) - instructions.extend(mips.push_register(reg1)) + instructions.append(mips.LoadWordNode(reg1, self.get_var_location(node.str_value), + line=node.line, column=node.column)) + instructions.extend(mips.push_register(reg1, line=node.line, column=node.column)) if type(node.index) == int: - instructions.append(mips.LoadInmediateNode(reg1, node.index)) + instructions.append(mips.LoadInmediateNode(reg1, node.index, line=node.line, column=node.column)) else: - instructions.append(mips.LoadWordNode(reg1, self.get_var_location(node.index))) - instructions.extend(mips.push_register(reg1)) + instructions.append(mips.LoadWordNode(reg1, self.get_var_location(node.index), + line=node.line, column=node.column)) + instructions.extend(mips.push_register(reg1, line=node.line, column=node.column)) if type(node.index) == int: - instructions.append(mips.LoadInmediateNode(reg1, node.length)) + instructions.append(mips.LoadInmediateNode(reg1, node.length, line=node.line, column=node.column)) else: - instructions.append(mips.LoadWordNode(reg1, self.get_var_location(node.length))) - instructions.extend(mips.push_register(reg1)) + instructions.append(mips.LoadWordNode(reg1, self.get_var_location(node.length), + line=node.line, column=node.column)) + instructions.extend(mips.push_register(reg1, line=node.line, column=node.column)) - instructions.append(mips.JalNode("substr")) - instructions.append(mips.StoreWordNode(mips.V0_REG, self.get_var_location(node.dest))) + instructions.append(mips.JalNode("substr", line=node.line, column=node.column)) + instructions.append(mips.StoreWordNode(mips.V0_REG, self.get_var_location(node.dest), + line=node.line, column=node.column)) return instructions @visitor.when(cil.ConcatNode) @@ -535,17 +595,21 @@ def visit(self, node: cil.ConcatNode): instructions = [] reg = mips.REGISTERS[0] - instructions.append(mips.LoadWordNode(reg, self.get_var_location(node.prefix))) - instructions.extend(mips.push_register(reg)) + instructions.append( + mips.LoadWordNode(reg, self.get_var_location(node.prefix), line=node.line, column=node.column)) + instructions.extend(mips.push_register(reg, line=node.line, column=node.column)) - instructions.append(mips.LoadWordNode(reg, self.get_var_location(node.suffix))) - instructions.extend(mips.push_register(reg)) + instructions.append( + mips.LoadWordNode(reg, self.get_var_location(node.suffix), line=node.line, column=node.column)) + instructions.extend(mips.push_register(reg, line=node.line, column=node.column)) - instructions.append(mips.LoadWordNode(reg, self.get_var_location(node.suffix))) - instructions.extend(mips.push_register(reg)) + instructions.append( + mips.LoadWordNode(reg, self.get_var_location(node.suffix), line=node.line, column=node.column)) + instructions.extend(mips.push_register(reg, line=node.line, column=node.column)) - instructions.append(mips.JalNode("concat")) - instructions.append(mips.StoreWordNode(mips.V0_REG, self.get_var_location(node.dest))) + instructions.append(mips.JalNode("concat", line=node.line, column=node.column)) + instructions.append(mips.StoreWordNode(mips.V0_REG, self.get_var_location(node.dest), + line=node.line, column=node.column)) return instructions @visitor.when(cil.LengthNode) @@ -553,11 +617,13 @@ def visit(self, node: cil.LengthNode): instructions = [] reg = mips.REGISTERS[0] - instructions.append(mips.LoadWordNode(reg, self.get_var_location(node.source))) - instructions.extend(mips.push_register(reg)) + instructions.append(mips.LoadWordNode(reg, self.get_var_location(node.source), + line=node.line, column=node.column)) + instructions.extend(mips.push_register(reg, line=node.line, column=node.column)) - instructions.append(mips.JalNode("len")) - instructions.append(mips.StoreWordNode(mips.V0_REG, self.get_var_location(node.dest))) + instructions.append(mips.JalNode("len", line=node.line, column=node.column)) + instructions.append(mips.StoreWordNode(mips.V0_REG, self.get_var_location(node.dest), + line=node.line, column=node.column)) return instructions @@ -567,18 +633,21 @@ def visit(self, node: cil.EqualNode): reg1 = mips.REGISTERS[0] if type(node.left) == cil.VoidNode: - instructions.append(mips.LoadInmediateNode(reg1, 0)) + instructions.append(mips.LoadInmediateNode(reg1, 0, line=node.line, column=node.column)) else: - instructions.append(mips.LoadWordNode(reg1, self.get_var_location(node.left))) + instructions.append(mips.LoadWordNode(reg1, self.get_var_location(node.left), + line=node.line, column=node.column)) reg2 = mips.REGISTERS[0] - if type(node.left) == cil.VoidNode: - instructions.append(mips.LoadInmediateNode(reg2, 0)) + if type(node.right) == cil.VoidNode: + instructions.append(mips.LoadInmediateNode(reg2, 0, line=node.line, column=node.column)) else: - instructions.append(mips.LoadWordNode(reg2, self.get_var_location(node.left))) + instructions.append(mips.LoadWordNode(reg2, self.get_var_location(node.right), + line=node.line, column=node.column)) - instructions.append(mips.EqualNode(reg1, reg1, reg2)) - instructions.append(mips.StoreWordNode(reg1, self.get_var_location(node.dest))) + instructions.append(mips.EqualNode(reg1, reg1, reg2, line=node.line, column=node.column)) + instructions.append(mips.StoreWordNode(reg1, self.get_var_location(node.dest), + line=node.line, column=node.column)) return instructions @visitor.when(cil.NameNode) @@ -586,13 +655,15 @@ def visit(self, node: cil.NameNode): instructions = [] reg = mips.REGISTERS[0] - instructions.append(mips.LoadAddressNode(reg, mips.TYPES_LABEL)) + instructions.append(mips.LoadAddressNode(reg, mips.TYPES_LABEL, line=node.line, column=node.column)) tp_number = self._types_section[node.value].index - instructions.append(mips.AdditionInmediateNode(reg, reg, tp_number * 4)) - instructions.append(mips.LoadWordNode(reg, mips.RegisterRelativeLocation(reg, 0))) + instructions.append(mips.AdditionInmediateNode(reg, reg, tp_number * 4, line=node.line, column=node.column)) + instructions.append(mips.LoadWordNode(reg, mips.RegisterRelativeLocation(reg, 0), + line=node.line, column=node.column)) - instructions.append(mips.StoreWordNode(reg, self.get_var_location(node.dest))) + instructions.append(mips.StoreWordNode(reg, self.get_var_location(node.dest), + line=node.line, column=node.column)) return instructions @visitor.when(cil.EqualStringNode) @@ -600,14 +671,17 @@ def visit(self, node: cil.EqualStringNode): instructions = [] reg1 = mips.REGISTERS[0] - instructions.append(mips.LoadWordNode(reg1, self.get_var_location(node.left))) - instructions.extend(mips.push_register(reg1)) + instructions.append(mips.LoadWordNode(reg1, self.get_var_location(node.left), + line=node.line, column=node.column)) + instructions.extend(mips.push_register(reg1, line=node.line, column=node.column)) - instructions.append(mips.LoadWordNode(reg1, self.get_var_location(node.right))) - instructions.extend(mips.push_register(reg1)) + instructions.append(mips.LoadWordNode(reg1, self.get_var_location(node.right), + line=node.line, column=node.column)) + instructions.extend(mips.push_register(reg1, line=node.line, column=node.column)) - instructions.append(mips.JalNode("equal_str")) - instructions.append(mips.StoreWordNode(mips.V0_REG, self.get_var_location(node.dest))) + instructions.append(mips.JalNode("equal_str", line=node.line, column=node.column)) + instructions.append(mips.StoreWordNode(mips.V0_REG, self.get_var_location(node.dest), + line=node.line, column=node.column)) return instructions @@ -616,11 +690,13 @@ def visit(self, node: cil.ComplementNode): instructions = [] reg1 = mips.REGISTERS[0] - instructions.append(mips.LoadWordNode(reg1, self.get_var_location(node.value))) + instructions.append(mips.LoadWordNode(reg1, self.get_var_location(node.value), + line=node.value.line, column=node.value.column)) - instructions.append(mips.NotNode(reg1, reg1)) - instructions.append(mips.AdditionInmediateNode(reg1, reg1, 1)) - instructions.append(mips.StoreWordNode(reg1, self.get_var_location(node.dest))) + instructions.append(mips.NotNode(reg1, reg1, line=node.line, column=node.column)) + instructions.append(mips.AdditionInmediateNode(reg1, reg1, 1, line=node.line, column=node.column)) + instructions.append(mips.StoreWordNode(reg1, self.get_var_location(node.dest), + line=node.line, column=node.column)) return instructions @@ -629,13 +705,16 @@ def visit(self, node: cil.LessEqualNode): instructions = [] reg1 = mips.REGISTERS[0] - instructions.append(mips.LoadWordNode(reg1, self.get_var_location(node.left))) + instructions.append(mips.LoadWordNode(reg1, self.get_var_location(node.left), + line=node.line, column=node.column)) reg2 = mips.REGISTERS[0] - instructions.append(mips.LoadWordNode(reg2, self.get_var_location(node.right))) + instructions.append(mips.LoadWordNode(reg2, self.get_var_location(node.right), + line=node.line, column=node.column)) - instructions.append(mips.LessEqualNode(reg1, reg1, reg2)) - instructions.append(mips.StoreWordNode(reg1, self.get_var_location(node.dest))) + instructions.append(mips.LessEqualNode(reg1, reg1, reg2, line=node.line, column=node.column)) + instructions.append(mips.StoreWordNode(reg1, self.get_var_location(node.dest), + line=node.line, column=node.column)) return instructions ''' @@ -653,11 +732,10 @@ def visit(self, node: cil.NotNode): instructions = [] reg1 = mips.REGISTERS[0] - instructions.append(mips.LoadWordNode(reg1, self.get_var_location(node.value))) - instructions.append(mips.NotNode(reg1, reg1)) - instructions.append(mips.StoreWordNode(reg1, self.get_var_location(node.dest))) + instructions.append(mips.LoadWordNode(reg1, self.get_var_location(node.value), + line=node.value.line, column=node.value.column)) + instructions.append(mips.NotNode(reg1, reg1, line=node.line, column=node.column)) + instructions.append(mips.StoreWordNode(reg1, self.get_var_location(node.dest), + line=node.line, column=node.column)) return instructions - - - diff --git a/src/core/mips/MIPSAstFormatter.py b/src/core/mips/MIPSAstFormatter.py index f3eb15828..f739f61f1 100644 --- a/src/core/mips/MIPSAstFormatter.py +++ b/src/core/mips/MIPSAstFormatter.py @@ -14,7 +14,6 @@ def visit(self, node): @visitor.when(FunctionNode) def visit(self, node): - print([self.visit(i) for i in node.instructions]) return f'{node.label}:\n\t' + \ f'\n\t'.join(self.visit(i) for i in node.instructions) diff --git a/src/core/mips/MipsAst.py b/src/core/mips/MipsAst.py index cdea86a26..64aa96d22 100644 --- a/src/core/mips/MipsAst.py +++ b/src/core/mips/MipsAst.py @@ -1,9 +1,10 @@ DOUBLE_WORD = 4 -REGISTERS_NAMES = ['t0', 't1', 't2', 't3', 't4', 't5', 't6', 't7', 't8'] +REGISTERS_NAMES = ['t0', 't1', 't2', 't3', 't4', 't5', 't6', 't7', 't8'] ARG_REGISTERS_NAMES = ['a0', 'a1', 'a2', 'a3'] -TYPES_LABEL = "types_table" -PROTOTYPE_LABEL = "prototype_table" +TYPES_LABEL = "types_table" +PROTOTYPE_LABEL = "prototype_table" + class Register: def __init__(self, name): @@ -12,27 +13,32 @@ def __init__(self, name): REGISTERS = [Register(i) for i in REGISTERS_NAMES] ARG_REGISTERS = [Register(i) for i in ARG_REGISTERS_NAMES] -FP_REG = Register('fp') -SP_REG = Register('sp') -RA_REG = Register('ra') -V0_REG = Register('v0') -V1_REG = Register('v1') -ZERO_REG = Register('zero') -LOW_REG = Register('low') +FP_REG = Register('fp') +SP_REG = Register('sp') +RA_REG = Register('ra') +V0_REG = Register('v0') +V1_REG = Register('v1') +ZERO_REG = Register('zero') +LOW_REG = Register('low') class Node: - pass + def __init__(self, line=0, column=0): + self.line = line + self.column = column + class ProgramNode(Node): - def __init__(self, data, types, functions): + def __init__(self, data, types, functions, line, column): + super().__init__(line, column) self.data = data self.types = types self.functions = functions class FunctionNode(Node): - def __init__(self, label, params, localvars): + def __init__(self, label, params, localvars, line, column): + super().__init__(line, column) self.label = label self.instructions = [] self.params = params @@ -62,178 +68,226 @@ def get_var_location(self, name): class InstructionNode(Node): pass + class AbsoluteNode(InstructionNode): # rdest <- abs(rsrc) - def __init__(self, rdest, rsrc): + def __init__(self, rdest, rsrc, line, column): ''' Put the absolute value of register rsrc in register rdest ''' + super().__init__(line, column) self.rdest = rdest self.rsrc = rsrc + class AdditionNode(InstructionNode): # rd <- rs + rt - def __init__(self, rdest, rsrc1, rsrc2): + def __init__(self, rdest, rsrc1, rsrc2, line, column): ''' Put the sum of registers rsrc1 and rsrc2 into register rdest. ''' + super().__init__(line, column) self.rdest = rdest self.rsrc1 = rsrc1 self.rsrc2 = rsrc2 + class AdditionInmediateNode(InstructionNode): - def __init__(self, rdest, rsrc, imm): + def __init__(self, rdest, rsrc, imm, line, column): ''' Put the sum of register rsrc and the sign-extended immediate into register rdest ''' + super().__init__(line, column) self.rdest = rdest self.rsrc = rsrc self.imm = imm + class DivideNode(InstructionNode): - def __init__(self, rsrc1, rsrc2): + def __init__(self, rsrc1, rsrc2, line, column): ''' Put the quotient of register rsrc1 and src2 into register Hi/Lo. ''' + super().__init__(line, column) self.rsrc1 = rsrc1 self.rsrc2 = rsrc2 + class MultiplyNode(InstructionNode): - def __init__(self, rdest, rsrc1, rsrc2): + def __init__(self, rdest, rsrc1, rsrc2, line, column): ''' Put the product of register rsrc1 and src2 into register rdest. ''' + super().__init__(line, column) self.rdest = rdest self.rsrc1 = rsrc1 self.rsrc2 = rsrc2 + class NegateNode(InstructionNode): - def __init__(self, rdest, rsrc1): + def __init__(self, rdest, rsrc1, line, column): ''' Put the negation of register rsrc1 into register rdest. ''' + super().__init__(line, column) self.rdest = rdest self.rsrc1 = rsrc1 + class SubstractionNode(InstructionNode): - def __init__(self, rdest, rsrc1, rsrc2): + def __init__(self, rdest, rsrc1, rsrc2, line, column): ''' Put the difference of register rsrc1 and rsrc2 into register rdest. ''' + super().__init__(line, column) self.rdest = rdest self.rsrc1 = rsrc1 self.rsrc2 = rsrc2 + class LessNode(InstructionNode): - def __init__(self, rdest, rsrc1, rsrc2): + def __init__(self, rdest, rsrc1, rsrc2, line, column): ''' Set register rdest to 1 if register rsrc1 is less than rsrc2, and 0 otherwise ''' + super().__init__(line, column) self.rdest = rdest self.rsrc1 = rsrc1 self.rsrc2 = rsrc2 + class LessInmediateNode(InstructionNode): - def __init__(self, rdest, rsrc1, imm): + def __init__(self, rdest, rsrc1, imm, line, column): ''' Set register rdest to 1 if register rsrc1 is less than imm, and 0 otherwise ''' + super().__init__(line, column) self.rdest = rdest self.rsrc1 = rsrc1 self.imm = imm + class EqualNode(InstructionNode): - def __init__(self, rdest, rsrc1, rsrc2): + def __init__(self, rdest, rsrc1, rsrc2, line, column): ''' Set register rdest to 1 if register rsrc1 equals rsrc2, and 0 otherwise ''' + super().__init__(line, column) self.rdest = rdest self.rsrc1 = rsrc1 self.rsrc2 = rsrc2 + class LessEqualNode(InstructionNode): - def __init__(self, rdest, rsrc1, rsrc2): + def __init__(self, rdest, rsrc1, rsrc2, line, column): ''' Set register rdest to 1 if register rsrc1 is less than or equal to rsrc2, and 0 otherwise ''' + super().__init__(line, column) self.rdest = rdest self.rsrc1 = rsrc1 self.rsrc2 = rsrc2 + class JumpNode(InstructionNode): - def __init__(self, label): + def __init__(self, label, line, column): ''' Unconditionally jump to the instruction at the label. ''' + super().__init__(line, column) self.label = label + class JalNode(InstructionNode): - def __init__(self, label): + def __init__(self, label, line, column): ''' Unconditionally jump to the instruction at target. Save the address of the next instruction in register ra (rd said the manual). ''' + super().__init__(line, column) self.label = label + class MoveNode(InstructionNode): - def __init__(self, reg1, reg2): + def __init__(self, reg1, reg2, line, column): + super().__init__(line, column) self.reg1 = reg1 self.reg2 = reg2 + class StoreWordNode(InstructionNode): - def __init__(self, reg, addr): - self.reg = reg + def __init__(self, reg, addr, line, column): + super().__init__(line, column) + self.reg = reg self.addr = addr + class LoadInmediateNode(InstructionNode): - def __init__(self, reg, value): + def __init__(self, reg, value, line, column): + super().__init__(line, column) self.reg = reg self.value = value + class LoadWordNode(InstructionNode): - def __init__(self, reg, addr): + def __init__(self, reg, addr, line, column): + super().__init__(line, column) self.reg = reg self.addr = addr + class LoadAddressNode(InstructionNode): - def __init__(self, reg, label): + def __init__(self, reg, label, line, column): ''' Load computed address , not the contents of the location, into register rdest ''' - self.reg = reg + super().__init__(line, column) + self.reg = reg self.label = label + class BranchOnNotEqualNode(InstructionNode): - def __init__(self, reg1, reg2, label): + def __init__(self, reg1, reg2, label, line, column): + super().__init__(line, column) self.reg1 = reg1 self.reg2 = reg2 self.label = label + class LabelNode(InstructionNode): - def __init__(self, name): + def __init__(self, name, line, column): + super().__init__(line, column) self.name = name + class NotNode(InstructionNode): - def __init__(self, dest, src): + def __init__(self, dest, src, line, column): + super().__init__(line, column) self.dest = dest self.src = src -class ShiftLeftNode(InstructionNode): # Shift Left Logical - def __init__(self, dest, src, bits): + +class ShiftLeftNode(InstructionNode): # Shift Left Logical + def __init__(self, dest, src, bits, line, column): + super().__init__(line, column) self.dest = dest - self.src = src + self.src = src self.bits = bits + class SyscallNode(InstructionNode): pass + class DataNode(Node): - def __init__(self, label): + def __init__(self, label, line, column): + super().__init__(line, column) self.label = label + class StringConst(DataNode): - def __init__(self, label, string): - super().__init__(label) + def __init__(self, label, string, line, column): + super().__init__(label, line, column) self.string = string @@ -288,31 +342,32 @@ def offset(self): return self._offset -def push_register(reg): - move_stack = AdditionInmediateNode(SP_REG, SP_REG, -DOUBLE_WORD) +def push_register(reg, line, column): + move_stack = AdditionInmediateNode(SP_REG, SP_REG, -DOUBLE_WORD, line, column) save_location = RegisterRelativeLocation(SP_REG, 0) - save_register = StoreWordNode(reg, save_location) + save_register = StoreWordNode(reg, save_location, line, column) return [move_stack, save_register] -def pop_register(reg): - load_value = LoadWordNode(reg, RegisterRelativeLocation(SP_REG, 0)) - move_stack = AdditionInmediateNode(SP_REG, SP_REG, DOUBLE_WORD) + +def pop_register(reg, line, column): + load_value = LoadWordNode(reg, RegisterRelativeLocation(SP_REG, 0), line, column) + move_stack = AdditionInmediateNode(SP_REG, SP_REG, DOUBLE_WORD, line, column) return [load_value, move_stack] -def create_object(reg1, reg2): +def create_object(reg1, reg2, line, column): instructions = [] - instructions.append(ShiftLeftNode(reg1, reg1, 2)) - instructions.append(LoadAddressNode(reg2, PROTOTYPE_LABEL)) - instructions.append(AdditionNode(reg2, reg2, reg1)) - instructions.append(LoadWordNode(reg2, RegisterRelativeLocation(reg2, 0))) - instructions.append(LoadWordNode(ARG_REGISTERS[0], RegisterRelativeLocation(reg2, 4))) - instructions.append(ShiftLeftNode(ARG_REGISTERS[0], ARG_REGISTERS[0], 2)) - instructions.append(JalNode("malloc")) - instructions.append(MoveNode(ARG_REGISTERS[2], ARG_REGISTERS[0])) - instructions.append(MoveNode(ARG_REGISTERS[0], reg2)) - instructions.append(MoveNode(ARG_REGISTERS[1], V0_REG)) - instructions.append(JalNode("copy")) + instructions.append(ShiftLeftNode(reg1, reg1, 2, line, column)) + instructions.append(LoadAddressNode(reg2, PROTOTYPE_LABEL, line, column)) + instructions.append(AdditionNode(reg2, reg2, reg1, line, column)) + instructions.append(LoadWordNode(reg2, RegisterRelativeLocation(reg2, 0), line, column)) + instructions.append(LoadWordNode(ARG_REGISTERS[0], RegisterRelativeLocation(reg2, 4), line, column)) + instructions.append(ShiftLeftNode(ARG_REGISTERS[0], ARG_REGISTERS[0], 2, line, column)) + instructions.append(JalNode("malloc", line, column)) + instructions.append(MoveNode(ARG_REGISTERS[2], ARG_REGISTERS[0], line, column)) + instructions.append(MoveNode(ARG_REGISTERS[0], reg2, line, column)) + instructions.append(MoveNode(ARG_REGISTERS[1], V0_REG, line, column)) + instructions.append(JalNode("copy", line, column)) return instructions From b36bee83fa72923908fadc0f87eed1033777d782 Mon Sep 17 00:00:00 2001 From: mavaldivie Date: Tue, 15 Feb 2022 09:19:56 -0500 Subject: [PATCH 17/75] Fixed bug in Parser --- src/core/cil/COOLToCILVisitor.py | 32 ++++++++++++++++++-------------- src/core/parser/Parser.py | 4 ++-- tests/utils/utils.py | 5 +++-- 3 files changed, 23 insertions(+), 18 deletions(-) diff --git a/src/core/cil/COOLToCILVisitor.py b/src/core/cil/COOLToCILVisitor.py index 14ba8f323..fe64bccd3 100644 --- a/src/core/cil/COOLToCILVisitor.py +++ b/src/core/cil/COOLToCILVisitor.py @@ -8,6 +8,14 @@ class COOLToCILVisitor(BaseCOOLToCILVisitor): def __init__(self, context): super().__init__(context) + def collect_types(self, node): + self.types_map[node.id.lex] = type = self.register_type(node.id.lex, node.id.line, node.id.column) + # Guardar métodos de las clases padres + iter_type = self.context.get_type(node.id.lex) + while iter_type is not None: + type.methods.update({i: self.to_function_name(i, iter_type.name) for i in iter_type.methods.keys()}) + iter_type = iter_type.parent + @visitor.on('node') def visit(self, node): pass @@ -24,6 +32,8 @@ def visit(self, node: cool.ProgramNode, scope: Scope): self.register_builtin() self.current_function = None + for x in node.declarations: + self.collect_types(x) for x, y in zip(node.declarations, scope.childs): self.visit(x, y) @@ -34,6 +44,8 @@ def visit(self, node: cool.ProgramNode, scope: Scope): def visit(self, node: cool.ClassDeclarationNode, scope: Scope): self.current_type = self.context.get_type(node.id.lex) + self.current_function = self.register_function(self.init_attr_name(node.id.lex), + line=node.line, column=node.column) # Inicializando los atributos de la clase y llamando al constructor del padre if self.current_type.parent.name not in ('Object', 'IO'): variable = self.define_internal_local(line=node.line, column=node.column) @@ -42,8 +54,6 @@ def visit(self, node: cool.ClassDeclarationNode, scope: Scope): self.init_attr_name(self.current_type.parent.name), variable, line=node.line, column=node.column)) # Inicializando los atributos de la clase - self.current_function = self.register_function(self.init_attr_name(node.id.lex), - line=node.line, column=node.column) instance = self.register_param(VariableInfo('instance', ''), line=node.line, column=node.column) for feat, child in zip(node.features, scope.childs): if isinstance(feat, cool.AttrDeclarationNode): @@ -53,15 +63,9 @@ def visit(self, node: cool.ClassDeclarationNode, scope: Scope): # TODO: Deberia retornar algo aqui? # TypeNode de la clase - type = self.register_type(node.id.lex, line=node.line, column=node.column) + type = self.types_map[node.id.lex] type.attributes = [i.name for i in self.current_type.attributes] - # Guardar métodos de las clases padres - iter_type = self.current_type - while iter_type is not None: - type.methods.update({i: self.to_function_name(i, iter_type.name) for i in iter_type.methods.keys()}) - iter_type = iter_type.parent - # Visitar funciones dentro de la clase for feat, child in zip(node.features, scope.childs): if isinstance(feat, cool.FuncDeclarationNode): @@ -397,9 +401,9 @@ def visit(self, node: cool.IsVoidNode, scope: Scope): void = cil.VoidNode() self.visit(node.expression, scope.childs[0]) self.register_instruction(cil.EqualNode(answer, node.expression.ret_expr, void, - line=node.expression.line, column=node.expression.node)) + line=node.expression.line, column=node.expression.column)) - self.register_instruction(cil.ArgNode(answer, line=node.expression.line, column=node.expression.node)) + self.register_instruction(cil.ArgNode(answer, line=node.expression.line, column=node.expression.column)) self.register_instruction(cil.StaticCallNode(self.init_name('Bool'), ret, line=node.line, column=node.column)) node.ret_expr = ret @@ -411,11 +415,11 @@ def visit(self, node: cool.ComplementNode, scope: Scope): self.visit(node.expression, scope.childs[0]) self.register_instruction(cil.GetAttribNode(value, node.expression.ret_expr, 'value', 'Int', - line=node.expression.line, column=node.expression.node)) + line=node.expression.line, column=node.expression.column)) self.register_instruction(cil.ComplementNode(answer, value, - line=node.expression.line, column=node.expression.node)) + line=node.expression.line, column=node.expression.column)) - self.register_instruction(cil.ArgNode(answer,line=node.expression.line, column=node.expression.node)) + self.register_instruction(cil.ArgNode(answer,line=node.expression.line, column=node.expression.column)) self.register_instruction(cil.StaticCallNode(self.init_name('Int'), ret, line=node.line, column=node.column)) node.ret_expr = ret diff --git a/src/core/parser/Parser.py b/src/core/parser/Parser.py index fc83e1c91..46f6b193e 100644 --- a/src/core/parser/Parser.py +++ b/src/core/parser/Parser.py @@ -88,8 +88,8 @@ arith %= arith + minus + factor, lambda h, s: MinusNode(s[1], s[2], s[3]) arith %= factor, lambda h, s: s[1] -factor %= factor + star + atom, lambda h, s: StarNode(s[1], s[2], s[3]) -factor %= factor + div + atom, lambda h, s: DivNode(s[1], s[2], s[3]) +factor %= factor + star + term, lambda h, s: StarNode(s[1], s[2], s[3]) +factor %= factor + div + term, lambda h, s: DivNode(s[1], s[2], s[3]) factor %= term, lambda h, s: s[1] term %= compl + term, lambda h, s: ComplementNode(s[1], s[2]) diff --git a/tests/utils/utils.py b/tests/utils/utils.py index af476fbbe..77ead16f4 100644 --- a/tests/utils/utils.py +++ b/tests/utils/utils.py @@ -66,8 +66,9 @@ def compare_errors(compiler_path: str, cool_file_path: str, error_file_path: str See the file README for a full copyright notice\. (?:Loaded: .+\n)*''' def compare_outputs(compiler_path: str, cool_file_path: str, input_file_path: str, output_file_path: str, timeout=100): + print(compiler_path, cool_file_path, input_file_path, output_file_path) try: - sp = subprocess.run(['bash', compiler_path, cool_file_path], capture_output=True, timeout=timeout) + sp = subprocess.run(['bash', compiler_path, cool_file_path], timeout=timeout) assert sp.returncode == 0, TEST_MUST_COMPILE % get_file_name(cool_file_path) except subprocess.TimeoutExpired: assert False, COMPILER_TIMEOUT @@ -76,7 +77,7 @@ def compare_outputs(compiler_path: str, cool_file_path: str, input_file_path: st try: fd = open(input_file_path, 'rb') - sp = subprocess.run(['spim', '-file', spim_file], input=fd.read(), capture_output=True, timeout=timeout) + sp = subprocess.run(['spim', '-file', spim_file], input=fd.read(), timeout=timeout) fd.close() mo = re.match(SPIM_HEADER, sp.stdout.decode()) if mo: From fca9c5199400314b159f4c55f11e2d6164cb2340 Mon Sep 17 00:00:00 2001 From: ClaudiaOM Date: Tue, 15 Feb 2022 08:57:15 -0600 Subject: [PATCH 18/75] Fixed bug with LabelNode in GotoNode.label --- src/Main.py | 18 ++++++++++++++++++ src/core/cil/COOLToCILVisitor.py | 8 ++++---- src/core/mips/CilToMipsVisitor.py | 3 ++- 3 files changed, 24 insertions(+), 5 deletions(-) diff --git a/src/Main.py b/src/Main.py index 147d281a0..47629c37f 100644 --- a/src/Main.py +++ b/src/Main.py @@ -8,6 +8,7 @@ from core.cil.CILAst import get_formatter from core.mips.CilToMipsVisitor import CILToMIPSVisitor from core.mips.MIPSAstFormatter import MIPSAstFormatter +import subprocess, re def main(args): @@ -66,6 +67,23 @@ def main(args): with open(out_file, 'w') as f: f.write(mipsCode) + # TODO: Comment this lines + try: + fd = open(args.file, 'rb') + sp = subprocess.run(['spim', '-file', mipsCode], input=fd.read(), capture_output=True, timeout=100) + fd.close() + SPIM_HEADER = r'''^SPIM Version .+ of .+ + Copyright .+\, James R\. Larus\. + All Rights Reserved\. + See the file README for a full copyright notice\. + (?:Loaded: .+\n)*''' + mo = re.match(SPIM_HEADER, sp.stdout.decode()) + if mo: + output = mo.string[mo.end():] + print(output) + except subprocess.TimeoutExpired: + assert False, "Too Long" + if __name__ == "__main__": import argparse diff --git a/src/core/cil/COOLToCILVisitor.py b/src/core/cil/COOLToCILVisitor.py index fe64bccd3..c500b0a6f 100644 --- a/src/core/cil/COOLToCILVisitor.py +++ b/src/core/cil/COOLToCILVisitor.py @@ -464,7 +464,7 @@ def visit(self, node: cool.EqualNode, scope: Scope): self.register_instruction(cil.EqualNode(value, node.left.ret_expr, node.right.ret_expr, line=node.left.line, column=node.left.column)) - self.register_instruction(cil.GotoNode(continue_label, + self.register_instruction(cil.GotoNode(continue_label.label, line=node.left.line, column=node.left.column)) self.register_instruction(int_comparisson) @@ -473,7 +473,7 @@ def visit(self, node: cool.EqualNode, scope: Scope): self.register_instruction(cil.GetAttribNode(right, node.right.ret_expr, 'value', 'Int', line=node.right.line, column=node.right.column)) self.register_instruction(cil.EqualNode(value, left, right, line=node.line, column=node.column)) - self.register_instruction(cil.GotoNode(continue_label, line=node.line, column=node.column)) + self.register_instruction(cil.GotoNode(continue_label.label, line=node.line, column=node.column)) self.register_instruction(string_comparisson) self.register_instruction(cil.GetAttribNode(left, node.left.ret_expr, 'value', 'String', @@ -482,7 +482,7 @@ def visit(self, node: cool.EqualNode, scope: Scope): line=node.right.line, column=node.right.column)) self.register_instruction(cil.EqualStringNode(value, left, right, line=node.right.line, column=node.right.column)) - self.register_instruction(cil.GotoNode(continue_label, line=node.line, column=node.column)) + self.register_instruction(cil.GotoNode(continue_label.label, line=node.line, column=node.column)) self.register_instruction(bool_comparisson) self.register_instruction(cil.GetAttribNode(left, node.left.ret_expr, 'value', 'Bool', @@ -491,7 +491,7 @@ def visit(self, node: cool.EqualNode, scope: Scope): line=node.right.line, column=node.right.column)) self.register_instruction(cil.EqualNode(value, left, right, line=node.right.line, column=node.right.column)) - self.register_instruction(cil.GotoNode(continue_label, line=node.line, column=node.column)) + self.register_instruction(cil.GotoNode(continue_label.label, line=node.line, column=node.column)) self.register_instruction(continue_label) diff --git a/src/core/mips/CilToMipsVisitor.py b/src/core/mips/CilToMipsVisitor.py index 02bdde5a4..3f89b253e 100644 --- a/src/core/mips/CilToMipsVisitor.py +++ b/src/core/mips/CilToMipsVisitor.py @@ -560,7 +560,8 @@ def visit(self, node: cil.GotoNode): @visitor.when(cil.LabelNode) def visit(self, node: cil.LabelNode): - return [mips.LabelNode(self.get_label(node.label), line=node.line, column=node.column)] + mips_label = self.get_label(node.label) + return [mips.LabelNode(mips_label, line=node.line, column=node.column)] @visitor.when(cil.SubstringNode) def visit(self, node: cil.SubstringNode): From bc2ef470a3c7092c5716fecf2e292d12e39f2e5b Mon Sep 17 00:00:00 2001 From: ClaudiaOM Date: Tue, 15 Feb 2022 08:57:57 -0600 Subject: [PATCH 19/75] Fixed small bug in line and column in debug for Mips --- src/core/mips/CilToMipsVisitor.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/core/mips/CilToMipsVisitor.py b/src/core/mips/CilToMipsVisitor.py index 3f89b253e..32b420d66 100644 --- a/src/core/mips/CilToMipsVisitor.py +++ b/src/core/mips/CilToMipsVisitor.py @@ -734,7 +734,7 @@ def visit(self, node: cil.NotNode): reg1 = mips.REGISTERS[0] instructions.append(mips.LoadWordNode(reg1, self.get_var_location(node.value), - line=node.value.line, column=node.value.column)) + line=node.line, column=node.column)) instructions.append(mips.NotNode(reg1, reg1, line=node.line, column=node.column)) instructions.append(mips.StoreWordNode(reg1, self.get_var_location(node.dest), line=node.line, column=node.column)) From ac9e333ed0aee83496930f7aa15b79f310bda654 Mon Sep 17 00:00:00 2001 From: ClaudiaOM Date: Tue, 15 Feb 2022 09:59:23 -0600 Subject: [PATCH 20/75] Fixed stype bug in FunctionCallNode in CooltoCilVisitor --- src/core/cil/COOLToCILVisitor.py | 6 ++++-- src/core/mips/CilToMipsVisitor.py | 2 +- 2 files changed, 5 insertions(+), 3 deletions(-) diff --git a/src/core/cil/COOLToCILVisitor.py b/src/core/cil/COOLToCILVisitor.py index c500b0a6f..a266ed848 100644 --- a/src/core/cil/COOLToCILVisitor.py +++ b/src/core/cil/COOLToCILVisitor.py @@ -521,8 +521,10 @@ def visit(self, node: cool.FunctionCallNode, scope: Scope): self.register_instruction(arg) ret = self.define_internal_local(line=node.line, column=node.column) - stype = node.type - if stype is None: stype = node.obj.static_type.name + if node.type is None: + stype = node.obj.static_type.name + else: + stype = node.type.lex self.register_instruction(cil.StaticCallNode(self.types_map[stype].methods[node.id.lex], ret, line=node.id.line, column=node.id.column)) node.ret_expr = ret diff --git a/src/core/mips/CilToMipsVisitor.py b/src/core/mips/CilToMipsVisitor.py index 32b420d66..73c8b33e2 100644 --- a/src/core/mips/CilToMipsVisitor.py +++ b/src/core/mips/CilToMipsVisitor.py @@ -692,7 +692,7 @@ def visit(self, node: cil.ComplementNode): reg1 = mips.REGISTERS[0] instructions.append(mips.LoadWordNode(reg1, self.get_var_location(node.value), - line=node.value.line, column=node.value.column)) + line=node.line, column=node.column)) instructions.append(mips.NotNode(reg1, reg1, line=node.line, column=node.column)) instructions.append(mips.AdditionInmediateNode(reg1, reg1, 1, line=node.line, column=node.column)) From 04352fa497682cc63d360d097322b14a5e266c20 Mon Sep 17 00:00:00 2001 From: ClaudiaOM Date: Tue, 15 Feb 2022 10:31:36 -0600 Subject: [PATCH 21/75] Fixed depth bug in ClassDeclarationNode in TypeChecker --- src/core/semantic/Type_Checker.py | 12 +++++++++++- src/core/tools/Semantic.py | 1 - 2 files changed, 11 insertions(+), 2 deletions(-) diff --git a/src/core/semantic/Type_Checker.py b/src/core/semantic/Type_Checker.py index 3d405ec45..b329a3787 100644 --- a/src/core/semantic/Type_Checker.py +++ b/src/core/semantic/Type_Checker.py @@ -45,15 +45,25 @@ def visit(self, node, scope): @visitor.when(ProgramNode) def visit(self, node : ProgramNode, scope : Scope = None): scope = Scope() + + # Calculate depth for classes + for declaration in node.declarations: + current = self.Context.get_type(declaration.id.lex) + parent = current.parent + while parent is not None: + current.depth += 1 + parent = parent.parent + for declaration in node.declarations: self.visit(declaration, scope.create_child()) + return scope @visitor.when(ClassDeclarationNode) def visit(self, node : ClassDeclarationNode, scope : Scope): self.Current_Type = self.Context.get_type(node.id.lex) - # Incluyo cada uno de los atributos de los padres en su resoectivo orden + # Incluyo cada uno de los atributos de los padres en su respectivo orden current = self.Current_Type attributtes = [] while current.parent: diff --git a/src/core/tools/Semantic.py b/src/core/tools/Semantic.py index d531f2d57..4da79a81c 100644 --- a/src/core/tools/Semantic.py +++ b/src/core/tools/Semantic.py @@ -57,7 +57,6 @@ def set_parent(self, parent): if parent.sealed: raise SemanticException(f'Cannot inherit from sealed type {parent.name}') self.parent = parent - self.depth = parent.depth + 1 # Retorna el tipo en el arbol de herencia de mayor profundidad # que es padre comun de ambos tipos From 662821fcf993710132c85c4548410f25949b012d Mon Sep 17 00:00:00 2001 From: ClaudiaOM Date: Tue, 15 Feb 2022 10:42:56 -0600 Subject: [PATCH 22/75] Fixed depth bug in ClassDeclarationNode in TypeBuilder for IO,String,Bool,Int --- src/core/semantic/Type_Builder.py | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/core/semantic/Type_Builder.py b/src/core/semantic/Type_Builder.py index ea75f4ae0..3c8624411 100644 --- a/src/core/semantic/Type_Builder.py +++ b/src/core/semantic/Type_Builder.py @@ -19,17 +19,21 @@ def __init__(self, Context : Context): self.IO_Type = self.Context.get_type('IO') self.IO_Type.set_parent(self.Object_Type) + self.IO_Type.depth = 1 self.Int_Type = self.Context.get_type('Int') self.Int_Type.set_parent(self.Object_Type) + self.Int_Type.depth = 1 self.Int_Type.sealed = True self.String_Type = self.Context.get_type('String') self.String_Type.set_parent(self.Object_Type) + self.String_Type.depth = 1 self.String_Type.sealed = True self.Bool_Type = self.Context.get_type('Bool') self.Bool_Type.set_parent(self.Object_Type) + self.Bool_Type.depth = 1 self.Bool_Type.sealed = True self.IO_Type.define_method('out_string', ['x'], [self.String_Type], SelfType(), 0, 0) From 8ded6853249f4f051243784da53dfd3b5cd0acc4 Mon Sep 17 00:00:00 2001 From: ClaudiaOM Date: Tue, 15 Feb 2022 11:06:27 -0600 Subject: [PATCH 23/75] Fixed self bug in FunctionCallNode --- src/core/cil/COOLToCILVisitor.py | 4 +++- src/core/mips/CilToMipsVisitor.py | 2 +- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/src/core/cil/COOLToCILVisitor.py b/src/core/cil/COOLToCILVisitor.py index a266ed848..66115e4c3 100644 --- a/src/core/cil/COOLToCILVisitor.py +++ b/src/core/cil/COOLToCILVisitor.py @@ -46,6 +46,8 @@ def visit(self, node: cool.ClassDeclarationNode, scope: Scope): self.current_function = self.register_function(self.init_attr_name(node.id.lex), line=node.line, column=node.column) + self_param = self.register_param(self.vself, line=node.line, column=node.column) + self.vself.name = self_param # Inicializando los atributos de la clase y llamando al constructor del padre if self.current_type.parent.name not in ('Object', 'IO'): variable = self.define_internal_local(line=node.line, column=node.column) @@ -70,6 +72,7 @@ def visit(self, node: cool.ClassDeclarationNode, scope: Scope): for feat, child in zip(node.features, scope.childs): if isinstance(feat, cool.FuncDeclarationNode): self.visit(feat, child) + self.vself.name = 'self' # Allocate de la clase self.current_function = self.register_function(self.init_name(node.id.lex), line=node.line, column=node.column) @@ -519,7 +522,6 @@ def visit(self, node: cool.FunctionCallNode, scope: Scope): self.register_instruction(cil.ArgNode(node.obj.ret_expr, line=node.obj.line, column=node.obj.column)) for arg in args: self.register_instruction(arg) - ret = self.define_internal_local(line=node.line, column=node.column) if node.type is None: stype = node.obj.static_type.name diff --git a/src/core/mips/CilToMipsVisitor.py b/src/core/mips/CilToMipsVisitor.py index 73c8b33e2..787dfebf5 100644 --- a/src/core/mips/CilToMipsVisitor.py +++ b/src/core/mips/CilToMipsVisitor.py @@ -366,7 +366,7 @@ def visit(self, node): instructions = [] reg = mips.REGISTERS[0] if isinstance(node.name, int): - instructions.append(mips.LoadInmediateNode(mips.ARG_REGISTERS[0], node.name, + instructions.append(mips.LoadInmediateNode(reg, node.name, line=node.line, column=node.column)) else: instructions.append(mips.LoadWordNode(reg, self.get_var_location(node.name), From f77ba45914826b5f9efea04e5b81e4102d49ccdf Mon Sep 17 00:00:00 2001 From: ClaudiaOM Date: Tue, 15 Feb 2022 20:09:18 -0600 Subject: [PATCH 24/75] Fixed bug --- src/core/cil/COOLToCILVisitor.py | 8 +++++--- src/core/semantic/Type_Checker.py | 4 ++-- 2 files changed, 7 insertions(+), 5 deletions(-) diff --git a/src/core/cil/COOLToCILVisitor.py b/src/core/cil/COOLToCILVisitor.py index 66115e4c3..de8391e42 100644 --- a/src/core/cil/COOLToCILVisitor.py +++ b/src/core/cil/COOLToCILVisitor.py @@ -12,8 +12,10 @@ def collect_types(self, node): self.types_map[node.id.lex] = type = self.register_type(node.id.lex, node.id.line, node.id.column) # Guardar métodos de las clases padres iter_type = self.context.get_type(node.id.lex) + while iter_type is not None: type.methods.update({i: self.to_function_name(i, iter_type.name) for i in iter_type.methods.keys()}) + type.attributes.extend([i.name for i in iter_type.attributes]) iter_type = iter_type.parent @visitor.on('node') @@ -60,13 +62,13 @@ def visit(self, node: cool.ClassDeclarationNode, scope: Scope): for feat, child in zip(node.features, scope.childs): if isinstance(feat, cool.AttrDeclarationNode): self.visit(feat, child) - self.register_instruction(cil.SetAttribNode(instance, feat.id.lex, feat.ret_expr, feat.type.lex, + self.register_instruction(cil.SetAttribNode(instance, feat.id.lex, feat.ret_expr, node.id.lex, line=node.line, column=node.column)) # TODO: Deberia retornar algo aqui? # TypeNode de la clase - type = self.types_map[node.id.lex] - type.attributes = [i.name for i in self.current_type.attributes] + # type = self.types_map[node.id.lex] + # type.attributes = [i.name for i in self.current_type.attributes] # Visitar funciones dentro de la clase for feat, child in zip(node.features, scope.childs): diff --git a/src/core/semantic/Type_Checker.py b/src/core/semantic/Type_Checker.py index b329a3787..11c3911a9 100644 --- a/src/core/semantic/Type_Checker.py +++ b/src/core/semantic/Type_Checker.py @@ -71,6 +71,7 @@ def visit(self, node : ClassDeclarationNode, scope : Scope): for att in reversed(current.attributes): attributtes.append(att) + scope.define_variable('self', self.Current_Type, node.line, node.column) for att in reversed(attributtes): scope.define_variable(att.name, att.type, att.line, att.column) @@ -132,7 +133,6 @@ def visit(self, node : FuncDeclarationNode, scope : Scope): except SemanticException: pass - scope.define_variable('self', self.Current_Type, node.line, node.column) # Defino cada uno de los parametros de metodo for pname, ptype in zip(self.Current_Method.param_names, self.Current_Method.param_types): @@ -509,7 +509,7 @@ def visit(self, node: BoolNode, scope: Scope): node.static_type = self.Bool_Type @visitor.when(IdNode) - def visit(self, node: IntegerNode, scope: Scope): + def visit(self, node: IdNode, scope: Scope): # Chequeo que la variable exista if scope.is_defined(node.token.lex): node.static_type = scope.find_variable(node.token.lex).type From 250b2a40c18b8146fff729a2c1adc2c0ce26903b Mon Sep 17 00:00:00 2001 From: ClaudiaOM Date: Tue, 15 Feb 2022 20:20:43 -0600 Subject: [PATCH 25/75] Print line and column Mips Formatter --- src/core/mips/MIPSAstFormatter.py | 78 ++++++++++++++++++++----------- 1 file changed, 52 insertions(+), 26 deletions(-) diff --git a/src/core/mips/MIPSAstFormatter.py b/src/core/mips/MIPSAstFormatter.py index f739f61f1..cc842f63f 100644 --- a/src/core/mips/MIPSAstFormatter.py +++ b/src/core/mips/MIPSAstFormatter.py @@ -19,71 +19,88 @@ def visit(self, node): @visitor.when(AbsoluteNode) def visit(self, node): - return f'abs {self.visit(node.rdest)} {self.visit(node.rsrc)}' + return f'abs {self.visit(node.rdest)} {self.visit(node.rsrc)} '\ + f'#line: {node.line} column: {node.column}' @visitor.when(AdditionNode) def visit(self, node): - return f'add {self.visit(node.rdest)} {self.visit(node.rsrc1)} {self.visit(node.rsrc2)}' + return f'add {self.visit(node.rdest)} {self.visit(node.rsrc1)} {self.visit(node.rsrc2)} '\ + f'#line: {node.line} column: {node.column}' @visitor.when(AdditionInmediateNode) def visit(self, node): - return f'addi {self.visit(node.rdest)} {self.visit(node.rsrc)} {self.visit(node.imm)}' + return f'addi {self.visit(node.rdest)} {self.visit(node.rsrc)} {self.visit(node.imm)} '\ + f'#line: {node.line} column: {node.column}' @visitor.when(DivideNode) def visit(self, node): - return f'div {self.visit(node.rsrc1)} {self.visit(node.rsrc2)}' + return f'div {self.visit(node.rsrc1)} {self.visit(node.rsrc2)} '\ + f'#line: {node.line} column: {node.column}' @visitor.when(MultiplyNode) def visit(self, node): - return f'mul {self.visit(node.rdest)} {self.visit(node.rsrc1)} {self.visit(node.rsrc2)}' + return f'mul {self.visit(node.rdest)} {self.visit(node.rsrc1)} {self.visit(node.rsrc2)} '\ + f'#line: {node.line} column: {node.column}' @visitor.when(NegateNode) def visit(self, node): - return f'neg {self.visit(node.rdest)} {self.visit(node.rsrc1)}' + return f'neg {self.visit(node.rdest)} {self.visit(node.rsrc1)} '\ + f'#line: {node.line} column: {node.column}' @visitor.when(SubstractionNode) def visit(self, node): - return f'sub {self.visit(node.rdest)} {self.visit(node.rsrc1)} {self.visit(node.rsrc2)}' + return f'sub {self.visit(node.rdest)} {self.visit(node.rsrc1)} {self.visit(node.rsrc2)} '\ + f'#line: {node.line} column: {node.column}' @visitor.when(LessNode) def visit(self, node): - return f'slt {self.visit(node.rdest)} {self.visit(node.rsrc1)} {self.visit(node.rsrc2)}' + return f'slt {self.visit(node.rdest)} {self.visit(node.rsrc1)} {self.visit(node.rsrc2)} '\ + f'#line: {node.line} column: {node.column}' @visitor.when(LessInmediateNode) def visit(self, node): - return f'slti {self.visit(node.rdest)} {self.visit(node.rsrc1)} {self.visit(node.imm)}' + return f'slti {self.visit(node.rdest)} {self.visit(node.rsrc1)} {self.visit(node.imm)} '\ + f'#line: {node.line} column: {node.column}' @visitor.when(EqualNode) def visit(self, node): - return f'seq {self.visit(node.rdest)} {self.visit(node.rsrc1)} {self.visit(node.rsrc2)}' + return f'seq {self.visit(node.rdest)} {self.visit(node.rsrc1)} {self.visit(node.rsrc2)} '\ + f'#line: {node.line} column: {node.column}' @visitor.when(LessEqualNode) def visit(self, node): - return f'sle {self.visit(node.rdest)} {self.visit(node.rsrc1)} {self.visit(node.rsrc2)}' + return f'sle {self.visit(node.rdest)} {self.visit(node.rsrc1)} {self.visit(node.rsrc2)} '\ + f'#line: {node.line} column: {node.column}' @visitor.when(JumpNode) def visit(self, node): - return f'j {self.visit(node.label)}' + return f'j {self.visit(node.label)} '\ + f'#line: {node.line} column: {node.column}' @visitor.when(JalNode) def visit(self, node): - return f'jal {self.visit(node.label)}' + return f'jal {self.visit(node.label)} '\ + f'#line: {node.line} column: {node.column}' @visitor.when(MoveNode) def visit(self, node): - return f'move {self.visit(node.reg1)} {self.visit(node.reg2)}' + return f'move {self.visit(node.reg1)} {self.visit(node.reg2)} '\ + f'#line: {node.line} column: {node.column}' @visitor.when(StoreWordNode) def visit(self, node): - return f'sw {self.visit(node.reg)} {self.visit(node.addr)}' + return f'sw {self.visit(node.reg)} {self.visit(node.addr)} '\ + f'#line: {node.line} column: {node.column}' @visitor.when(LoadInmediateNode) def visit(self, node): - return f'li {self.visit(node.reg)} {self.visit(node.value)}' + return f'li {self.visit(node.reg)} {self.visit(node.value)} '\ + f'#line: {node.line} column: {node.column}' @visitor.when(LoadWordNode) def visit(self, node): - return f'lw {self.visit(node.reg)} {self.visit(node.addr)}' + return f'lw {self.visit(node.reg)} {self.visit(node.addr)} '\ + f'#line: {node.line} column: {node.column}' @visitor.when(LoadAddressNode) def visit(self, node): @@ -91,39 +108,48 @@ def visit(self, node): @visitor.when(BranchOnNotEqualNode) def visit(self, node): - return f'bne {self.visit(node.reg1)} {self.visit(node.reg2)} {self.visit(node.label)}' + return f'bne {self.visit(node.reg1)} {self.visit(node.reg2)} {self.visit(node.label)} '\ + f'#line: {node.line} column: {node.column}' @visitor.when(LabelNode) def visit(self, node): - return f'{self.visit(node.name)}' + return f'{self.visit(node.name)}: '\ + f'#line: {node.line} column: {node.column}' @visitor.when(NotNode) def visit(self, node): - return f'not {self.visit(node.dest)} {self.visit(node.src)}' + return f'not {self.visit(node.dest)} {self.visit(node.src)} '\ + f'#line: {node.line} column: {node.column}' @visitor.when(ShiftLeftNode) def visit(self, node): - return f'sll {self.visit(node.dest)} {self.visit(node.src)} {self.visit(node.bits)}' + return f'sll {self.visit(node.dest)} {self.visit(node.src)} {self.visit(node.bits)} '\ + f'#line: {node.line} column: {node.column}' @visitor.when(SyscallNode) def visit(self, node): - return 'syscall' + return 'syscall '\ + f'#line: {node.line} column: {node.column}' @visitor.when(StringConst) def visit(self, node): - return f'{node.label}: .asciiz "{node.string}"' + return f'{node.label}: .asciiz "{node.string}" '\ + f'#line: {node.line} column: {node.column}' @visitor.when(RegisterRelativeLocation) def visit(self, node): - return f'{node._offset}({self.visit(node._register)})' + return f'{node._offset}({self.visit(node._register)}) '\ + f'#line: {node.line} column: {node.column}' @visitor.when(LabelRelativeLocation) def visit(self, node): - return f'{node._label} + {node._offset}' + return f'{node._label} + {node._offset} '\ + f'#line: {node.line} column: {node.column}' @visitor.when(Register) def visit(self, node): - return f'{node.name}' + return f'{node.name} '\ + f'#line: {node.line} column: {node.column}' @visitor.when(int) def visit(self, node): From 54ffa32d8a64cd070b1d92cfa4c3e710639e5614 Mon Sep 17 00:00:00 2001 From: mavaldivie Date: Tue, 15 Feb 2022 23:09:43 -0500 Subject: [PATCH 26/75] Fixed bug with attribute declarations --- src/Main.py | 3 ++- src/core/cil/BaseCOOLToCILVisitor.py | 11 ++++++++--- src/core/cil/CILAst.py | 11 ++++++++++- src/core/cil/COOLToCILVisitor.py | 12 ++++++++++-- src/core/mips/CilToMipsVisitor.py | 2 +- src/core/mips/MIPSAstFormatter.py | 2 +- src/core/semantic/Type_Collector.py | 12 ++++++------ src/core/tools/Semantic.py | 14 ++++++++------ 8 files changed, 46 insertions(+), 21 deletions(-) diff --git a/src/Main.py b/src/Main.py index 47629c37f..045fa316c 100644 --- a/src/Main.py +++ b/src/Main.py @@ -53,12 +53,13 @@ def main(args): CILVisitor = COOLToCILVisitor(type_Checker.Context) CILast = CILVisitor.visit(COOLast, scope) + # print(get_formatter()(CILast)) MIPSVisitor = CILToMIPSVisitor() MIPSAst = MIPSVisitor.visit(CILast) MIPSFormatter = MIPSAstFormatter() mipsCode = MIPSFormatter.visit(MIPSAst) - print(mipsCode) + # print(mipsCode) out_file = args.file.split(".") out_file[-1] = "mips" diff --git a/src/core/cil/BaseCOOLToCILVisitor.py b/src/core/cil/BaseCOOLToCILVisitor.py index fa1826c1b..4aa34484c 100644 --- a/src/core/cil/BaseCOOLToCILVisitor.py +++ b/src/core/cil/BaseCOOLToCILVisitor.py @@ -53,6 +53,11 @@ def register_local(self, vinfo, line, column): self.var_names[name] = vinfo.name return vinfo.name + def register_attribute(self, name, type, line, column): + name = f'attr_{type}_{name}' + return cil.AttributeNode(name, line, column) + + def define_internal_local(self, line, column): vinfo = VariableInfo('internal', None) return self.register_local(vinfo, line, column) @@ -192,7 +197,7 @@ def register_builtin(self): # String type_node = self.register_type('String', line, column) - type_node.attributes = ['value', 'length'] + type_node.attributes = {name:self.register_attribute(name, 'String', 0, 0) for name in ['value', 'length']} self.current_function = self.register_function(self.init_name('String'), line, column) val = self.register_param(VariableInfo('val', None), line, column) @@ -266,7 +271,7 @@ def register_builtin(self): # Int type_node = self.register_type('Int', line, column) - type_node.attributes = ['value'] + type_node.attributes = {name:self.register_attribute(name, 'Int', 0, 0) for name in ['value']} self.current_function = self.register_function(self.init_name('Int'), line, column) val = self.register_param(VariableInfo('val', None), line, column) @@ -280,7 +285,7 @@ def register_builtin(self): # Bool type_node = self.register_type('Bool', line, column) - type_node.attributes = ['value'] + type_node.attributes = {name:self.register_attribute(name, 'Bool', 0, 0) for name in ['value']} self.current_function = self.register_function(self.init_name('Bool'), line, column) val = self.register_param(VariableInfo('val', None), line, column) diff --git a/src/core/cil/CILAst.py b/src/core/cil/CILAst.py index 46e5f04b3..fdba78ce2 100644 --- a/src/core/cil/CILAst.py +++ b/src/core/cil/CILAst.py @@ -17,7 +17,7 @@ class TypeNode(Node): def __init__(self, name, line, column): super().__init__(line, column) self.name = name - self.attributes = [] + self.attributes = {} self.methods = {} class DataNode(Node): @@ -45,6 +45,11 @@ def __init__(self, name, line, column): super().__init__(line, column) self.name = name +class AttributeNode(Node): + def __init__(self, name, line, column): + super().__init__(line, column) + self.name = name + class InstructionNode(Node): pass @@ -323,6 +328,10 @@ def visit(self, node): def visit(self, node): return f'LOCAL {node.name}' + @visitor.when(AttributeNode) + def visit(self, node): + return f'ATTRIBUTE {node.name}' + @visitor.when(AssignNode) def visit(self, node): return f'{node.dest} = {node.source}' diff --git a/src/core/cil/COOLToCILVisitor.py b/src/core/cil/COOLToCILVisitor.py index de8391e42..624b79652 100644 --- a/src/core/cil/COOLToCILVisitor.py +++ b/src/core/cil/COOLToCILVisitor.py @@ -15,7 +15,7 @@ def collect_types(self, node): while iter_type is not None: type.methods.update({i: self.to_function_name(i, iter_type.name) for i in iter_type.methods.keys()}) - type.attributes.extend([i.name for i in iter_type.attributes]) + type.attributes.update({i.name:self.register_attribute(i.name, iter_type.name, iter_type.line, iter_type.column) for i in iter_type.attributes}) iter_type = iter_type.parent @visitor.on('node') @@ -45,10 +45,13 @@ def visit(self, node: cool.ProgramNode, scope: Scope): @visitor.when(cool.ClassDeclarationNode) def visit(self, node: cool.ClassDeclarationNode, scope: Scope): self.current_type = self.context.get_type(node.id.lex) + type = self.types_map[node.id.lex] self.current_function = self.register_function(self.init_attr_name(node.id.lex), line=node.line, column=node.column) self_param = self.register_param(self.vself, line=node.line, column=node.column) + self.localvars.extend(type.attributes.values()) + self.vself.name = self_param # Inicializando los atributos de la clase y llamando al constructor del padre if self.current_type.parent.name not in ('Object', 'IO'): @@ -78,6 +81,7 @@ def visit(self, node: cool.ClassDeclarationNode, scope: Scope): # Allocate de la clase self.current_function = self.register_function(self.init_name(node.id.lex), line=node.line, column=node.column) + self.localvars.extend(type.attributes.values()) instance = self.define_internal_local(line=node.line, column=node.column) self.register_instruction(cil.AllocateNode(node.id.lex, instance, line=node.line, column=node.column)) @@ -105,15 +109,19 @@ def visit(self, node: cool.AttrDeclarationNode, scope: Scope): line=node.expression.line, column=node.expression.column)) elif node.type.lex in self.value_types: self.register_instruction(cil.AllocateNode(node.type.lex, variable, line=node.line, column=node.column)) - self.register_local(VariableInfo(node.id.lex, node.type.lex), line=node.line, column=node.column) + type = self.types_map[self.current_type.name] + attr = type.attributes[node.id.lex] + self.var_names[node.id.lex] = attr.name node.ret_expr = variable @visitor.when(cool.FuncDeclarationNode) def visit(self, node: cool.FuncDeclarationNode, scope: Scope): self.current_method = self.current_type.get_method(node.id.lex) + type = self.types_map[self.current_type.name] self.current_function = self.register_function(self.to_function_name(self.current_method.name, self.current_type.name), line=node.line, column=node.column) + self.localvars.extend(type.attributes.values()) self_param = self.register_param(self.vself, line=node.line, column=node.column) self.vself.name = self_param diff --git a/src/core/mips/CilToMipsVisitor.py b/src/core/mips/CilToMipsVisitor.py index 787dfebf5..6e6d211de 100644 --- a/src/core/mips/CilToMipsVisitor.py +++ b/src/core/mips/CilToMipsVisitor.py @@ -105,7 +105,7 @@ def visit(self, node): if node.name == "String": defaults = [('value', 'default_str'), ('len', 'type_4_proto')] - type = mips.MIPSType(type_label, name_label, node.attributes, methods, + type = mips.MIPSType(type_label, name_label, list(node.attributes.keys()), methods, len(self._types_section), defaults) self._types_section[node.name] = type diff --git a/src/core/mips/MIPSAstFormatter.py b/src/core/mips/MIPSAstFormatter.py index f739f61f1..79c44f35e 100644 --- a/src/core/mips/MIPSAstFormatter.py +++ b/src/core/mips/MIPSAstFormatter.py @@ -95,7 +95,7 @@ def visit(self, node): @visitor.when(LabelNode) def visit(self, node): - return f'{self.visit(node.name)}' + return f'{self.visit(node.name)}:' @visitor.when(NotNode) def visit(self, node): diff --git a/src/core/semantic/Type_Collector.py b/src/core/semantic/Type_Collector.py index eb8578534..bde0032a2 100644 --- a/src/core/semantic/Type_Collector.py +++ b/src/core/semantic/Type_Collector.py @@ -15,11 +15,11 @@ def __init__(self): self.Context.add_type(SelfType()) - self.Context.create_type('Object') - self.Context.create_type('String') - self.Context.create_type('IO') - self.Context.create_type('Int') - self.Context.create_type('Bool') + self.Context.create_type('Object', 0, 0) + self.Context.create_type('String', 0, 0) + self.Context.create_type('IO', 0, 0) + self.Context.create_type('Int', 0, 0) + self.Context.create_type('Bool', 0, 0) @visitor.on('node') def visit(self, node): @@ -33,6 +33,6 @@ def visit(self, node : ProgramNode): @visitor.when(ClassDeclarationNode) def visit(self, node : ClassDeclarationNode): try: - self.Context.create_type(node.id.lex) + self.Context.create_type(node.id.lex, node.line, node.column) except SemanticException as ex: self.errors.append(SemanticError(node.line, node.column, ex.text)) diff --git a/src/core/tools/Semantic.py b/src/core/tools/Semantic.py index 4da79a81c..fc2d693c9 100644 --- a/src/core/tools/Semantic.py +++ b/src/core/tools/Semantic.py @@ -40,7 +40,7 @@ def __eq__(self, other): #Clase base para representar los tipos del programa class Type: - def __init__(self, name:str, sealed=False): + def __init__(self, name:str, line, column, sealed=False): self.name = name self.attributes = [] self.methods = {} @@ -49,6 +49,8 @@ def __init__(self, name:str, sealed=False): self.depth = 0 # True si la clase esta sellada (no se puede heredar de ella) self.sealed = sealed + self.line = line + self.column = column def set_parent(self, parent): # Si el padre esta definido o es un tipo sellado entonces hay un error semantico @@ -156,7 +158,7 @@ def __repr__(self): # Special_Types class SelfType(Type): def __init__(self): - Type.__init__(self, 'SELF_TYPE') + Type.__init__(self, 'SELF_TYPE', 0, 0) self.sealed = True def conforms_to(self, other): @@ -170,7 +172,7 @@ def __eq__(self, other): class VoidType(Type): def __init__(self): - Type.__init__(self, 'VOID_TYPE') + Type.__init__(self, 'VOID_TYPE', 0, 0) self.sealed = True def type_union(self, other): @@ -190,7 +192,7 @@ def __eq__(self, other): class ErrorType(Type): def __init__(self): - Type.__init__(self, '') + Type.__init__(self, '', 0, 0) self.sealed = True def type_union(self, other): @@ -226,10 +228,10 @@ def add_type(self, type : Type): return type # Crea un tipo dentro del contexto - def create_type(self, name : str): + def create_type(self, name : str, line, column): if name in self.types: raise SemanticException('Classes may not be redefined') - type = self.types[name] = Type(name) + type = self.types[name] = Type(name, line, column) return type # Obtiene un tipo del contexto con el nombre correspondiente From 844877f2b134ab4a8e681a95feff15ae60f5166e Mon Sep 17 00:00:00 2001 From: mavaldivie Date: Tue, 15 Feb 2022 23:43:11 -0500 Subject: [PATCH 27/75] Fixed bug with attribute declarations and herency --- src/Main.py | 2 +- src/core/cil/COOLToCILVisitor.py | 4 +--- 2 files changed, 2 insertions(+), 4 deletions(-) diff --git a/src/Main.py b/src/Main.py index 045fa316c..c9e62d4fb 100644 --- a/src/Main.py +++ b/src/Main.py @@ -59,7 +59,7 @@ def main(args): MIPSAst = MIPSVisitor.visit(CILast) MIPSFormatter = MIPSAstFormatter() mipsCode = MIPSFormatter.visit(MIPSAst) - # print(mipsCode) + print(mipsCode) out_file = args.file.split(".") out_file[-1] = "mips" diff --git a/src/core/cil/COOLToCILVisitor.py b/src/core/cil/COOLToCILVisitor.py index 624b79652..d13e95ab9 100644 --- a/src/core/cil/COOLToCILVisitor.py +++ b/src/core/cil/COOLToCILVisitor.py @@ -51,6 +51,7 @@ def visit(self, node: cool.ClassDeclarationNode, scope: Scope): line=node.line, column=node.column) self_param = self.register_param(self.vself, line=node.line, column=node.column) self.localvars.extend(type.attributes.values()) + self.var_names.update({i:j.name for i,j in type.attributes.items()}) self.vself.name = self_param # Inicializando los atributos de la clase y llamando al constructor del padre @@ -109,9 +110,6 @@ def visit(self, node: cool.AttrDeclarationNode, scope: Scope): line=node.expression.line, column=node.expression.column)) elif node.type.lex in self.value_types: self.register_instruction(cil.AllocateNode(node.type.lex, variable, line=node.line, column=node.column)) - type = self.types_map[self.current_type.name] - attr = type.attributes[node.id.lex] - self.var_names[node.id.lex] = attr.name node.ret_expr = variable @visitor.when(cool.FuncDeclarationNode) From 2a95c2d83601b031374c7ee0a1cba096b7c877de Mon Sep 17 00:00:00 2001 From: mavaldivie Date: Tue, 15 Feb 2022 23:54:21 -0500 Subject: [PATCH 28/75] Fixed bugs with line comments in MIPS formatter class --- src/core/mips/MIPSAstFormatter.py | 9 +++------ 1 file changed, 3 insertions(+), 6 deletions(-) diff --git a/src/core/mips/MIPSAstFormatter.py b/src/core/mips/MIPSAstFormatter.py index cc842f63f..7d0877e5f 100644 --- a/src/core/mips/MIPSAstFormatter.py +++ b/src/core/mips/MIPSAstFormatter.py @@ -138,18 +138,15 @@ def visit(self, node): @visitor.when(RegisterRelativeLocation) def visit(self, node): - return f'{node._offset}({self.visit(node._register)}) '\ - f'#line: {node.line} column: {node.column}' + return f'{node._offset}({self.visit(node._register)}' @visitor.when(LabelRelativeLocation) def visit(self, node): - return f'{node._label} + {node._offset} '\ - f'#line: {node.line} column: {node.column}' + return f'{node._label} + {node._offset}' @visitor.when(Register) def visit(self, node): - return f'{node.name} '\ - f'#line: {node.line} column: {node.column}' + return f'{node.name}' @visitor.when(int) def visit(self, node): From 53008f0388d83de88737116d35c04ae9cd8cdf2a Mon Sep 17 00:00:00 2001 From: mavaldivie Date: Wed, 16 Feb 2022 00:16:36 -0500 Subject: [PATCH 29/75] Fixed bugs while printing mips code --- src/Main.py | 4 ++-- src/core/mips/MIPSAstFormatter.py | 5 +++-- 2 files changed, 5 insertions(+), 4 deletions(-) diff --git a/src/Main.py b/src/Main.py index c9e62d4fb..0fb136d39 100644 --- a/src/Main.py +++ b/src/Main.py @@ -59,7 +59,7 @@ def main(args): MIPSAst = MIPSVisitor.visit(CILast) MIPSFormatter = MIPSAstFormatter() mipsCode = MIPSFormatter.visit(MIPSAst) - print(mipsCode) + # print(mipsCode) out_file = args.file.split(".") out_file[-1] = "mips" @@ -71,7 +71,7 @@ def main(args): # TODO: Comment this lines try: fd = open(args.file, 'rb') - sp = subprocess.run(['spim', '-file', mipsCode], input=fd.read(), capture_output=True, timeout=100) + sp = subprocess.run(['spim', '-file', out_file], input=fd.read(), timeout=100) fd.close() SPIM_HEADER = r'''^SPIM Version .+ of .+ Copyright .+\, James R\. Larus\. diff --git a/src/core/mips/MIPSAstFormatter.py b/src/core/mips/MIPSAstFormatter.py index 7d0877e5f..ae9cf5b50 100644 --- a/src/core/mips/MIPSAstFormatter.py +++ b/src/core/mips/MIPSAstFormatter.py @@ -10,6 +10,7 @@ def visit(self, node): def visit(self, node): return f'.data\n' + \ '\n'.join(self.visit(i) for i in node.data) + '\n' + \ + '.text\n.globl main\n' + \ '\n'.join(self.visit(i) for i in node.functions) + '\n' @visitor.when(FunctionNode) @@ -138,7 +139,7 @@ def visit(self, node): @visitor.when(RegisterRelativeLocation) def visit(self, node): - return f'{node._offset}({self.visit(node._register)}' + return f'{node._offset}({self.visit(node._register)})' @visitor.when(LabelRelativeLocation) def visit(self, node): @@ -146,7 +147,7 @@ def visit(self, node): @visitor.when(Register) def visit(self, node): - return f'{node.name}' + return f'${node.name}' @visitor.when(int) def visit(self, node): From beef9a767d71e5fe961ee222889e63476bc8cb6e Mon Sep 17 00:00:00 2001 From: ClaudiaOM Date: Wed, 16 Feb 2022 20:10:37 -0600 Subject: [PATCH 30/75] Added types formats to MIPS Formatter class --- src/Main.py | 36 ++++++++++++++++++------------- src/core/cil/COOLToCILVisitor.py | 2 +- src/core/mips/CilToMipsVisitor.py | 2 +- src/core/mips/MIPSAstFormatter.py | 25 +++++++++++++++++---- src/core/mips/MipsAst.py | 2 +- 5 files changed, 45 insertions(+), 22 deletions(-) diff --git a/src/Main.py b/src/Main.py index 0fb136d39..59ac64df9 100644 --- a/src/Main.py +++ b/src/Main.py @@ -59,6 +59,10 @@ def main(args): MIPSAst = MIPSVisitor.visit(CILast) MIPSFormatter = MIPSAstFormatter() mipsCode = MIPSFormatter.visit(MIPSAst) + + with open('core/mips/mips_basics.asm', 'r') as f: + mipsCode = mipsCode + "".join(f.readlines()) + # print(mipsCode) out_file = args.file.split(".") @@ -69,21 +73,23 @@ def main(args): f.write(mipsCode) # TODO: Comment this lines - try: - fd = open(args.file, 'rb') - sp = subprocess.run(['spim', '-file', out_file], input=fd.read(), timeout=100) - fd.close() - SPIM_HEADER = r'''^SPIM Version .+ of .+ - Copyright .+\, James R\. Larus\. - All Rights Reserved\. - See the file README for a full copyright notice\. - (?:Loaded: .+\n)*''' - mo = re.match(SPIM_HEADER, sp.stdout.decode()) - if mo: - output = mo.string[mo.end():] - print(output) - except subprocess.TimeoutExpired: - assert False, "Too Long" + + #try: + # fd = open(args.file, 'rb') + # sp = subprocess.run(['spim', '-file', out_file], input=fd.read(), timeout=100) + # fd.close() + # SPIM_HEADER = r'''^SPIM Version .+ of .+ + # Copyright .+\, James R\. Larus\. + # All Rights Reserved\. + # See the file README for a full copyright notice\. + # (?:Loaded: .+\n)*''' + # mo = re.match(SPIM_HEADER, sp.stdout.decode()) + # if mo: + # output = mo.string[mo.end():] + # print(output) + #except subprocess.TimeoutExpired: + # assert False, "Too Long" + if __name__ == "__main__": diff --git a/src/core/cil/COOLToCILVisitor.py b/src/core/cil/COOLToCILVisitor.py index d13e95ab9..7aee57ef4 100644 --- a/src/core/cil/COOLToCILVisitor.py +++ b/src/core/cil/COOLToCILVisitor.py @@ -619,6 +619,6 @@ def visit(self, node: cool.BoolNode, scope: Scope): ret = self.define_internal_local(line=node.line, column=node.column) # TODO: Hay algunos ArgNode que reciben variables y otros valores especificos - self.register_instruction(cil.ArgNode(node.token.lex, line=node.token.line, column=node.token.column)) + self.register_instruction(cil.ArgNode(1 if node.token.lex else 0, line=node.token.line, column=node.token.column)) self.register_instruction(cil.StaticCallNode(self.init_name('Bool'), ret, line=node.line, column=node.column)) node.ret_expr = ret diff --git a/src/core/mips/CilToMipsVisitor.py b/src/core/mips/CilToMipsVisitor.py index 6e6d211de..f35737b34 100644 --- a/src/core/mips/CilToMipsVisitor.py +++ b/src/core/mips/CilToMipsVisitor.py @@ -622,7 +622,7 @@ def visit(self, node: cil.LengthNode): line=node.line, column=node.column)) instructions.extend(mips.push_register(reg, line=node.line, column=node.column)) - instructions.append(mips.JalNode("len", line=node.line, column=node.column)) + instructions.append(mips.JalNode("length", line=node.line, column=node.column)) instructions.append(mips.StoreWordNode(mips.V0_REG, self.get_var_location(node.dest), line=node.line, column=node.column)) diff --git a/src/core/mips/MIPSAstFormatter.py b/src/core/mips/MIPSAstFormatter.py index ae9cf5b50..eb64050ef 100644 --- a/src/core/mips/MIPSAstFormatter.py +++ b/src/core/mips/MIPSAstFormatter.py @@ -8,10 +8,27 @@ def visit(self, node): @visitor.when(ProgramNode) def visit(self, node): - return f'.data\n' + \ - '\n'.join(self.visit(i) for i in node.data) + '\n' + \ - '.text\n.globl main\n' + \ - '\n'.join(self.visit(i) for i in node.functions) + '\n' + + data = f'.data\n' + '\n'.join(self.visit(i) for i in node.data) + '\n' + + names_table = f"{TYPES_LABEL}:\n" + "\n".join([f"\t.word\t{tp.name}" for tp in node.types]) + proto_table = f"{PROTOTYPE_LABEL}:\n" + "\n".join([f"\t.word\t{tp.label}_prototype" for tp in node.types]) + + types = "\n\n\n".join([self.visit(tp) for tp in node.types]) + code = '\n\n'.join(self.visit(i) for i in node.functions) + '\n' + + return f'{data}\n\n{names_table}\n\n{proto_table}\n\n{types}\n\n\n.text\n.globl main\n{code}' + + @visitor.when(MIPSType) + def visit(self, node): + methods = "\n".join([f"\t.word\t {m}" for m in node.methods]) + dispatch_table = f"{node.label}_dispatch:\n{methods}" + proto_begin = f"{node.label}_prototype:\n\t.word\t{node.index}\n\t.word\t{node.size}\n\t.word\t{node.label}_dispatch" + proto_attr = "\n".join([f'\t.word\t0' for attr in node.attributes]) + proto_end = f"\t.word\t-1" + proto = f"{proto_begin}\n{proto_attr}\n{proto_end}" if proto_attr != "" else f"{proto_begin}\n{proto_end}" + + return f'{dispatch_table}\n\n{proto}' @visitor.when(FunctionNode) def visit(self, node): diff --git a/src/core/mips/MipsAst.py b/src/core/mips/MipsAst.py index 64aa96d22..9fe43cb4f 100644 --- a/src/core/mips/MipsAst.py +++ b/src/core/mips/MipsAst.py @@ -19,7 +19,7 @@ def __init__(self, name): V0_REG = Register('v0') V1_REG = Register('v1') ZERO_REG = Register('zero') -LOW_REG = Register('low') +LOW_REG = Register('lo') class Node: From 8416d7ad350602dcb198242235ce4b7bd0155254 Mon Sep 17 00:00:00 2001 From: ClaudiaOM Date: Wed, 16 Feb 2022 20:15:53 -0600 Subject: [PATCH 31/75] Added mips_basics.asm file --- src/core/mips/mips_basics.asm | 222 ++++++++++++++++++++++++++++++++++ 1 file changed, 222 insertions(+) create mode 100644 src/core/mips/mips_basics.asm diff --git a/src/core/mips/mips_basics.asm b/src/core/mips/mips_basics.asm new file mode 100644 index 000000000..d7fa0dfe7 --- /dev/null +++ b/src/core/mips/mips_basics.asm @@ -0,0 +1,222 @@ + +# Args: +# $a0 size to alloc +# Return: +# $v0 address of allocated block + + +# t0 address of empty memory +# a0 size to alloc +# a1 return memory address + +#TODO Parametros en la pila +malloc: + # Save content of reisters + addiu $sp $sp -12 + sw $a0 0($sp) + sw $a1 4($sp) + sw $t0 8($sp) + + move $a1 $t0 + add $t0 $a0 $t0 + + # Return original content to registers + lw $a0 0($sp) + lw $a1 4($sp) + lw $t0 8($sp) + addiu $sp $sp 12 + + jr $ra + + +# COPY +# $a0 address from +# $a1 address to +# $a2 size + +copy: + addiu $sp $sp -16 # Save content of registers in sp + sw $a0 0($sp) + sw $a1 4($sp) + sw $a2 8($sp) + sw $t0 12($sp) + +copy_loop: + beq $a2 $zero copy_end # Copy finished (copy size is 0) + lw $t0 0($a0) # Load in t0 content of source address a0 + sw $t0 0($a1) # Save in destiny a1 content of t0 + addiu $a0 $a0 4 # Increase source address a0 + addiu $a1 $a1 4 # Increase destiny addres a1 + addi $a2 $a2 -4 # Decrease copy size + j copy_loop + +copy_end: + lw $a0 0($sp) # Return original content to registers + lw $a1 4($sp) + lw $a2 8($sp) + lw $t0 12($sp) + addiu $sp $sp 16 + + jr $ra + + + + +# LEN +# a0 begin address +# return size in v0 +length: + # Save content of registers + addiu $sp $sp -8 + sw $t0 0($sp) + sw $t1 4($sp) + + move $t0 $a0 # Move to t0 the address to begin + move $v0 $zero # Set v0 to zero + +len_loop: + lb $t1 0($t0) # Save in t1 first byte of address + beq $t1 $zero len_end # Finish object if t1 is zero + addi $v0 $v0 1 # Increase count in v0 + addiu $t0 $t0 1 # Increase address pointer + j len_loop # Finish loop + +len_end: + # Return original content to registers + lw $t0 0($sp) + lw $t1 4($sp) + + jr $ra + + + +# SUBSTRING +# a0 Pointer to beginning of string +# a2 Size of substring + + +substr: + # Save content of registers + addiu $sp $sp -32 + sw $t0 0($sp) + sw $t1 4($sp) + sw $t2 8($sp) + sw $t3 12($sp) + sw $a0 16($sp) + sw $a1 20($sp) + sw $a2 24($sp) + sw $ra 28($sp) + + move $t0 $a0 # t0 points to beginning o string + li $t1 4 # t1 Word size + + div $a2 $t1 # Size of substring / wordsize + mfhi $t2 # t2 holds remainder of division + + bne $t2 $zero substr_allign_size # Branch if division is not exact + move $t1 $a2 # t1 size of substring + j substr_new_block + +substr_allign_size: + sub $t1 $t1 $t2 # Convert t1 to multiple of 4 to... + add $t1 $t1 $a2 # reserve memory via malloc + +substr_new_block: + move $a0 $t1 # Store in a0 size of space to reserve via malloc + jal malloc # Malloc + move $t3 $a1 # Pointer to beginning of reserved space + move $t1 $zero # Count + addu $t0 $t0 $a0 # t0 Beginning of string + +substr_copy_loop: + beq $t1 $a2 substr_end # Copy finished + lb $t2 0($t0) # Load byte from string into t2 temporal + sb $t2 0($t3) # Savebyte from t2 into t3 + addiu $t0 $t0 1 # Increase pointer to string + addiu $t3 $t3 1 # Increase pointer to reserved space + addiu $t1 $t1 1 # Increase count + j substr_copy_loop + +substr_end: + sb $zero 0($t3) # Set next byte of substring to zero + + # Return original values to registers + lw $t0 0($sp) + lw $t1 4($sp) + lw $t2 8($sp) + lw $t3 12($sp) + lw $a0 16($sp) + lw $a1 20($sp) + lw $a2 24($sp) + lw $ra 28($sp) + addiu $sp $sp 32 + + jr $ra + + + +# CONCAT +# a0 pointer to string 1 +# a1 pointer to string 2 +# a2 size of string 1 + size of string 2 + +concat: + # Save content of registers + addiu $sp $sp -24 + sw $t0 0($sp) + sw $t1 4($sp) + sw $t2 8($sp) + sw $a0 12($sp) + sw $a1 16($sp) + sw $ra 20($sp) + + move $t0 $a0 # t0 pointer to string 1 + move $t1 $a1 # t1 pointer to string 2 + + + addiu $a0 $a2 1 # Save in a0 size in a2 + 1 + li $t2 4 # t2 = 4 + div $a0 $t2 # a0 / t2 + mfhi $a0 # a0 remainder of division + bne $a0 $zero concat_allign_size # Branch if size is multiple of 4 + addiu $a0 $a2 1 # Add 1 t size + +concat_allign_size: + sub $t2 $t2 $a0 # Convert t1 to multiple of 4 to... + add $a0 $a2 $t2 # reserve memory via malloc + addiu $a0 $a0 1 # Add 1 t size + j concat_size_alligned + +concat_size_alligned: + jal malloc # a0 stores size to reserve + move $t2 $a1 # t2 is pointer to empty space + j concat_copy_first_loop + +concat_copy_first_loop: + lb $a0 0($t0) # move to a0 content of t0 + beq $a0 $zero concat_copy_second_loop # a0 == 0 finish + sb $a0 0($t2) # move to t2 content of a0 + addiu $t0 $t0 1 # Increase t0 pointer + addiu $t2 $t2 1 # Increase t2 pointer + j concat_copy_first_loop + +concat_copy_second_loop: + lb $a0 0($t1) # move to a0 content of t1 + beq $a0 $zero concat_end # a0 == 0 finish + sb $a0 0($t2) # move to t2 content of a0 + addiu $t1 $t1 1 # Increase t1 pointer + addiu $t2 $t2 1 # Increase t2 pointer + j concat_copy_second_loop + +concat_end: + # Return original values to regiters + sb $zero 0($t2) + lw $t0 0($sp) + lw $t1 4($sp) + lw $t2 8($sp) + lw $a0 12($sp) + lw $a1 16($sp) + lw $ra 20($sp) + addiu $sp $sp 24 + + jr $ra \ No newline at end of file From 5aaec0ae168c0538e784d8bd6f312678ca9cdcfa Mon Sep 17 00:00:00 2001 From: ClaudiaOM Date: Wed, 16 Feb 2022 20:48:00 -0600 Subject: [PATCH 32/75] Fixed bug of line and column in ResultNode --- src/core/cil/BaseCOOLToCILVisitor.py | 28 ++++++++++++++-------------- 1 file changed, 14 insertions(+), 14 deletions(-) diff --git a/src/core/cil/BaseCOOLToCILVisitor.py b/src/core/cil/BaseCOOLToCILVisitor.py index 4aa34484c..20b4fcde6 100644 --- a/src/core/cil/BaseCOOLToCILVisitor.py +++ b/src/core/cil/BaseCOOLToCILVisitor.py @@ -115,7 +115,7 @@ def register_builtin(self): self.current_function = self.register_function(self.init_name('Object'), line, column) instance = self.define_internal_local(line, column) self.register_instruction(cil.AllocateNode('Object', instance, line, column)) - self.register_instruction(cil.ReturnNode(instance, line, column)) + self.register_instruction(cil.ReturnNode(line, column, instance)) self.current_function = self.register_function(self.to_function_name('abort', 'Object'), line, column) self_param = self.register_param(self.vself, line, column) @@ -136,13 +136,13 @@ def register_builtin(self): instance = self.define_internal_local(line, column) self.register_instruction(cil.ArgNode(result, line, column)) self.register_instruction(cil.StaticCallNode(self.init_name('String'), instance, line, column)) - self.register_instruction(cil.ReturnNode(instance, line, column)) + self.register_instruction(cil.ReturnNode(line, column, instance)) self.current_function = self.register_function(self.to_function_name('copy', 'Object'), line, column) self_param = self.register_param(self.vself, line, column) result = self.define_internal_local(line, column) self.register_instruction(cil.CopyNode(result, self_param, line, column)) - self.register_instruction(cil.ReturnNode(result, line, column)) + self.register_instruction(cil.ReturnNode(line, column, result)) type_node.methods = {name: self.to_function_name(name, 'Object') for name in ['abort', 'type_name', 'copy']} type_node.methods['init'] = self.init_name('Object') @@ -154,7 +154,7 @@ def register_builtin(self): self.current_function = self.register_function(self.init_name('IO'), line, column) instance = self.define_internal_local(line, column) self.register_instruction(cil.AllocateNode('IO', instance, line, column)) - self.register_instruction(cil.ReturnNode(instance, line, column)) + self.register_instruction(cil.ReturnNode(line, column, instance)) self.current_function = self.register_function(self.to_function_name('out_string', 'IO'), line, column) self_param = self.register_param(self.vself, line, column) @@ -162,7 +162,7 @@ def register_builtin(self): vname = self.define_internal_local(line, column) self.register_instruction(cil.GetAttribNode(vname, x, 'value', 'String', line, column)) self.register_instruction(cil.PrintStringNode(vname, line, column)) - self.register_instruction(cil.ReturnNode(self_param, line, column)) + self.register_instruction(cil.ReturnNode(line, column, self_param)) self.current_function = self.register_function(self.to_function_name('out_int', 'IO'), line, column) self_param = self.register_param(self.vself, line, column) @@ -170,7 +170,7 @@ def register_builtin(self): vname = self.define_internal_local(line, column) self.register_instruction(cil.GetAttribNode(vname, x, 'value', 'Int', line, column)) self.register_instruction(cil.PrintIntNode(vname, line, column)) - self.register_instruction(cil.ReturnNode(self_param, line, column)) + self.register_instruction(cil.ReturnNode(line, column, self_param)) self.current_function = self.register_function(self.to_function_name('in_string', 'IO'), line, column) self_param = self.register_param(self.vself, line, column) @@ -179,7 +179,7 @@ def register_builtin(self): instance = self.define_internal_local(line, column) self.register_instruction(cil.ArgNode(result, line, column)) self.register_instruction(cil.StaticCallNode(self.init_name('String'), instance, line, column)) - self.register_instruction(cil.ReturnNode(instance, line, column)) + self.register_instruction(cil.ReturnNode(line, column, value=instance)) self.current_function = self.register_function(self.to_function_name('in_int', 'IO'), line, column) self_param = self.register_param(self.vself, line, column) @@ -188,7 +188,7 @@ def register_builtin(self): instance = self.define_internal_local(line, column) self.register_instruction(cil.ArgNode(result, line, column)) self.register_instruction(cil.StaticCallNode(self.init_name('Int'), instance, line, column)) - self.register_instruction(cil.ReturnNode(instance, line, column)) + self.register_instruction(cil.ReturnNode(line, column, instance)) type_node.methods = {method: self.to_function_name(method, 'Object') for method in obj_methods} type_node.methods.update({name: self.to_function_name(name, 'IO') for name in @@ -210,13 +210,13 @@ def register_builtin(self): self.register_instruction(cil.ArgNode(result, line, column)) self.register_instruction(cil.StaticCallNode(self.init_name('Int'), attr, line, column)) self.register_instruction(cil.SetAttribNode(instance, 'length', attr, 'String', line, column)) - self.register_instruction(cil.ReturnNode(instance, line, column)) + self.register_instruction(cil.ReturnNode(line, column, instance)) self.current_function = self.register_function(self.to_function_name('length', 'String'), line, column) self_param = self.register_param(self.vself, line, column) result = self.define_internal_local(line, column) self.register_instruction(cil.GetAttribNode(result, self_param, 'length', 'String', line, column)) - self.register_instruction(cil.ReturnNode(result, line, column)) + self.register_instruction(cil.ReturnNode(line, column, result)) self.current_function = self.register_function(self.to_function_name('concat', 'String'), line, column) self_param = self.register_param(self.vself, line, column) @@ -238,7 +238,7 @@ def register_builtin(self): instance = self.define_internal_local(line, column) self.register_instruction(cil.ArgNode(result, line, column)) self.register_instruction(cil.StaticCallNode(self.init_name('String'), instance, line, column)) - self.register_instruction(cil.ReturnNode(instance, line, column)) + self.register_instruction(cil.ReturnNode(line, column, instance)) self.current_function = self.register_function(self.to_function_name('substr', 'String'), line, column) self_param = self.register_param(self.vself, line, column) @@ -263,7 +263,7 @@ def register_builtin(self): instance = self.define_internal_local(line, column) self.register_instruction(cil.ArgNode(result, line, column)) self.register_instruction(cil.StaticCallNode(self.init_name('String'), instance, line, column)) - self.register_instruction(cil.ReturnNode(instance, line, column)) + self.register_instruction(cil.ReturnNode(line, column, instance)) type_node.methods = {method: self.to_function_name(method, 'Object') for method in obj_methods} type_node.methods.update({name: self.to_function_name(name, 'String') for name in ['length', 'concat', 'substr']}) @@ -278,7 +278,7 @@ def register_builtin(self): instance = self.define_internal_local(line, column) self.register_instruction(cil.AllocateNode('Int', instance, line, column)) self.register_instruction(cil.SetAttribNode(instance, 'value', val, 'Int', line, column)) - self.register_instruction(cil.ReturnNode(instance, line, column)) + self.register_instruction(cil.ReturnNode(line, column, instance)) type_node.methods = {method:self.to_function_name(method, 'Object') for method in obj_methods} type_node.methods['init'] = self.init_name('Int') @@ -292,7 +292,7 @@ def register_builtin(self): instance = self.define_internal_local(line, column) self.register_instruction(cil.AllocateNode('Bool', instance, line, column)) self.register_instruction(cil.SetAttribNode(instance, 'value', val, 'Bool', line, column)) - self.register_instruction(cil.ReturnNode(instance, line, column)) + self.register_instruction(cil.ReturnNode(line, column, instance)) type_node.methods = {method: self.to_function_name(method, 'Object') for method in obj_methods} type_node.methods['init'] = self.init_name('Bool') From d2874e57f6c83edd6d773cebef12467354e93a4f Mon Sep 17 00:00:00 2001 From: mavaldivie Date: Thu, 17 Feb 2022 00:21:13 -0500 Subject: [PATCH 33/75] Fixed comments in mips code --- src/core/mips/MIPSAstFormatter.py | 63 +++++++++++++++++++------------ 1 file changed, 39 insertions(+), 24 deletions(-) diff --git a/src/core/mips/MIPSAstFormatter.py b/src/core/mips/MIPSAstFormatter.py index eb64050ef..d36e11f43 100644 --- a/src/core/mips/MIPSAstFormatter.py +++ b/src/core/mips/MIPSAstFormatter.py @@ -17,7 +17,12 @@ def visit(self, node): types = "\n\n\n".join([self.visit(tp) for tp in node.types]) code = '\n\n'.join(self.visit(i) for i in node.functions) + '\n' - return f'{data}\n\n{names_table}\n\n{proto_table}\n\n{types}\n\n\n.text\n.globl main\n{code}' + mipsCode = f'{data}\n\n{names_table}\n\n{proto_table}\n\n{types}\n\n\n.text\n.globl main\n{code}\n\n' + + with open('core/mips/mips_basics.asm', 'r') as f: + mipsCode += "".join(f.readlines()) + return mipsCode + @visitor.when(MIPSType) def visit(self, node): @@ -37,87 +42,92 @@ def visit(self, node): @visitor.when(AbsoluteNode) def visit(self, node): - return f'abs {self.visit(node.rdest)} {self.visit(node.rsrc)} '\ + return f'abs {self.visit(node.rdest)} {self.visit(node.rsrc)}'.ljust(50) + \ f'#line: {node.line} column: {node.column}' @visitor.when(AdditionNode) def visit(self, node): - return f'add {self.visit(node.rdest)} {self.visit(node.rsrc1)} {self.visit(node.rsrc2)} '\ + return f'add {self.visit(node.rdest)} {self.visit(node.rsrc1)} {self.visit(node.rsrc2)}'.ljust(50) + \ f'#line: {node.line} column: {node.column}' @visitor.when(AdditionInmediateNode) def visit(self, node): - return f'addi {self.visit(node.rdest)} {self.visit(node.rsrc)} {self.visit(node.imm)} '\ + return f'addi {self.visit(node.rdest)} {self.visit(node.rsrc)} {self.visit(node.imm)}'.ljust(50) + \ f'#line: {node.line} column: {node.column}' @visitor.when(DivideNode) def visit(self, node): - return f'div {self.visit(node.rsrc1)} {self.visit(node.rsrc2)} '\ + return f'div {self.visit(node.rsrc1)} {self.visit(node.rsrc2)}'.ljust(50) + \ f'#line: {node.line} column: {node.column}' @visitor.when(MultiplyNode) def visit(self, node): - return f'mul {self.visit(node.rdest)} {self.visit(node.rsrc1)} {self.visit(node.rsrc2)} '\ + return f'mul {self.visit(node.rdest)} {self.visit(node.rsrc1)} {self.visit(node.rsrc2)}'.ljust(50) + \ f'#line: {node.line} column: {node.column}' @visitor.when(NegateNode) def visit(self, node): - return f'neg {self.visit(node.rdest)} {self.visit(node.rsrc1)} '\ + return f'neg {self.visit(node.rdest)} {self.visit(node.rsrc1)}'.ljust(50) + \ f'#line: {node.line} column: {node.column}' @visitor.when(SubstractionNode) def visit(self, node): - return f'sub {self.visit(node.rdest)} {self.visit(node.rsrc1)} {self.visit(node.rsrc2)} '\ + return f'sub {self.visit(node.rdest)} {self.visit(node.rsrc1)} {self.visit(node.rsrc2)}'.ljust(50) + \ f'#line: {node.line} column: {node.column}' @visitor.when(LessNode) def visit(self, node): - return f'slt {self.visit(node.rdest)} {self.visit(node.rsrc1)} {self.visit(node.rsrc2)} '\ + return f'slt {self.visit(node.rdest)} {self.visit(node.rsrc1)} {self.visit(node.rsrc2)}'.ljust(50) + \ f'#line: {node.line} column: {node.column}' @visitor.when(LessInmediateNode) def visit(self, node): - return f'slti {self.visit(node.rdest)} {self.visit(node.rsrc1)} {self.visit(node.imm)} '\ + return f'slti {self.visit(node.rdest)} {self.visit(node.rsrc1)} {self.visit(node.imm)}'.ljust(50) + \ f'#line: {node.line} column: {node.column}' @visitor.when(EqualNode) def visit(self, node): - return f'seq {self.visit(node.rdest)} {self.visit(node.rsrc1)} {self.visit(node.rsrc2)} '\ + return f'seq {self.visit(node.rdest)} {self.visit(node.rsrc1)} {self.visit(node.rsrc2)}'.ljust(50) + \ f'#line: {node.line} column: {node.column}' @visitor.when(LessEqualNode) def visit(self, node): - return f'sle {self.visit(node.rdest)} {self.visit(node.rsrc1)} {self.visit(node.rsrc2)} '\ + return f'sle {self.visit(node.rdest)} {self.visit(node.rsrc1)} {self.visit(node.rsrc2)}'.ljust(50) + \ f'#line: {node.line} column: {node.column}' @visitor.when(JumpNode) def visit(self, node): - return f'j {self.visit(node.label)} '\ + return f'j {self.visit(node.label)}'.ljust(50) + \ + f'#line: {node.line} column: {node.column}' + + @visitor.when(JumpRegisterNode) + def visit(self, node): + return f'jr {self.visit(node.reg)}'.ljust(50) + \ f'#line: {node.line} column: {node.column}' @visitor.when(JalNode) def visit(self, node): - return f'jal {self.visit(node.label)} '\ + return f'jal {self.visit(node.label)}'.ljust(50) + \ f'#line: {node.line} column: {node.column}' @visitor.when(MoveNode) def visit(self, node): - return f'move {self.visit(node.reg1)} {self.visit(node.reg2)} '\ + return f'move {self.visit(node.reg1)} {self.visit(node.reg2)}'.ljust(50) + \ f'#line: {node.line} column: {node.column}' @visitor.when(StoreWordNode) def visit(self, node): - return f'sw {self.visit(node.reg)} {self.visit(node.addr)} '\ + return f'sw {self.visit(node.reg)} {self.visit(node.addr)}'.ljust(50) + \ f'#line: {node.line} column: {node.column}' @visitor.when(LoadInmediateNode) def visit(self, node): - return f'li {self.visit(node.reg)} {self.visit(node.value)} '\ + return f'li {self.visit(node.reg)} {self.visit(node.value)}'.ljust(50) + \ f'#line: {node.line} column: {node.column}' @visitor.when(LoadWordNode) def visit(self, node): - return f'lw {self.visit(node.reg)} {self.visit(node.addr)} '\ + return f'lw {self.visit(node.reg)} {self.visit(node.addr)}'.ljust(50) + \ f'#line: {node.line} column: {node.column}' @visitor.when(LoadAddressNode) @@ -126,32 +136,37 @@ def visit(self, node): @visitor.when(BranchOnNotEqualNode) def visit(self, node): - return f'bne {self.visit(node.reg1)} {self.visit(node.reg2)} {self.visit(node.label)} '\ + return f'bne {self.visit(node.reg1)} {self.visit(node.reg2)} {self.visit(node.label)}'.ljust(50) + \ f'#line: {node.line} column: {node.column}' @visitor.when(LabelNode) def visit(self, node): - return f'{self.visit(node.name)}: '\ + return f'{self.visit(node.name)}:'.ljust(50) + \ f'#line: {node.line} column: {node.column}' @visitor.when(NotNode) def visit(self, node): - return f'not {self.visit(node.dest)} {self.visit(node.src)} '\ + return f'not {self.visit(node.dest)} {self.visit(node.src)}'.ljust(50) + \ + f'#line: {node.line} column: {node.column}' + + @visitor.when(MoveLowNode) + def visit(self, node): + return f'mflo {self.visit(node.dest)}'.ljust(50) + \ f'#line: {node.line} column: {node.column}' @visitor.when(ShiftLeftNode) def visit(self, node): - return f'sll {self.visit(node.dest)} {self.visit(node.src)} {self.visit(node.bits)} '\ + return f'sll {self.visit(node.dest)} {self.visit(node.src)} {self.visit(node.bits)}'.ljust(50) + \ f'#line: {node.line} column: {node.column}' @visitor.when(SyscallNode) def visit(self, node): - return 'syscall '\ + return 'syscall'.ljust(50) + \ f'#line: {node.line} column: {node.column}' @visitor.when(StringConst) def visit(self, node): - return f'{node.label}: .asciiz "{node.string}" '\ + return f'{node.label}: .asciiz "{node.string}"'.ljust(50) + \ f'#line: {node.line} column: {node.column}' @visitor.when(RegisterRelativeLocation) From 012468dd7ce72498ec3f1acd972cc6853e81da53 Mon Sep 17 00:00:00 2001 From: mavaldivie Date: Thu, 17 Feb 2022 00:22:06 -0500 Subject: [PATCH 34/75] Added JumpRegister and MoveLow Nodes --- src/core/mips/MipsAst.py | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) diff --git a/src/core/mips/MipsAst.py b/src/core/mips/MipsAst.py index 9fe43cb4f..40db22835 100644 --- a/src/core/mips/MipsAst.py +++ b/src/core/mips/MipsAst.py @@ -20,6 +20,7 @@ def __init__(self, name): V1_REG = Register('v1') ZERO_REG = Register('zero') LOW_REG = Register('lo') +GP_REG = Register('gp') class Node: @@ -112,6 +113,16 @@ def __init__(self, rsrc1, rsrc2, line, column): self.rsrc1 = rsrc1 self.rsrc2 = rsrc2 +class MoveLowNode(InstructionNode): + + def __init__(self, dest, line, column): + ''' + Put the content of register low into register rsrc. + ''' + super().__init__(line, column) + self.dest = dest + + class MultiplyNode(InstructionNode): def __init__(self, rdest, rsrc1, rsrc2, line, column): @@ -197,6 +208,14 @@ def __init__(self, label, line, column): super().__init__(line, column) self.label = label +class JumpRegisterNode(InstructionNode): + def __init__(self, reg, line, column): + ''' + Unconditionally jump to the instruction at the label. + ''' + super().__init__(line, column) + self.reg = reg + class JalNode(InstructionNode): def __init__(self, label, line, column): From 5000a5e3b18de1fbe1a6a7c056c04cc7c5d74f99 Mon Sep 17 00:00:00 2001 From: mavaldivie Date: Thu, 17 Feb 2022 00:22:46 -0500 Subject: [PATCH 35/75] Fixed initial Data Pointer --- src/core/mips/CilToMipsVisitor.py | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/src/core/mips/CilToMipsVisitor.py b/src/core/mips/CilToMipsVisitor.py index f35737b34..b5658c58f 100644 --- a/src/core/mips/CilToMipsVisitor.py +++ b/src/core/mips/CilToMipsVisitor.py @@ -132,6 +132,8 @@ def visit(self, node): self.register_label(instruction.label, mips_label) instructions = [] + if node.name == 'entry': + instructions.append(mips.MoveNode(mips.V1_REG, mips.GP_REG, 0, 0)) instructions.extend(mips.push_register(mips.FP_REG, line=node.line, column=node.column)) instructions.append(mips.AdditionInmediateNode(mips.FP_REG, mips.SP_REG, 4, line=node.line, column=node.column)) instructions.append(mips.AdditionInmediateNode(mips.SP_REG, mips.SP_REG, -size_for_locals, @@ -149,7 +151,7 @@ def visit(self, node): instructions.extend(mips.pop_register(mips.FP_REG, line=node.line, column=node.column)) if self._current_function.label != 'main': - instructions.append(mips.JumpNode(mips.RA_REG, line=node.line, column=node.column)) + instructions.append(mips.JumpRegisterNode(mips.RA_REG, line=node.line, column=node.column)) else: instructions.append(mips.LoadInmediateNode(mips.V0_REG, 10, line=node.line, column=node.column)) instructions.append(mips.SyscallNode()) @@ -268,9 +270,9 @@ def visit(self, node): instructions.append(mips.LoadWordNode(reg2, self.get_var_location(node.right), line=node.line, column=node.column)) - reg3 = mips.LOW_REG instructions.append(mips.DivideNode(reg1, reg2, line=node.line, column=node.column)) - instructions.append(mips.StoreWordNode(reg3, self.get_var_location(node.dest), + instructions.append(mips.MoveLowNode(reg1, line=node.line, column=node.column)) + instructions.append(mips.StoreWordNode(reg1, self.get_var_location(node.dest), line=node.line, column=node.column)) return instructions From 253c66b7bc44bbac6c19998108241847c6990d29 Mon Sep 17 00:00:00 2001 From: mavaldivie Date: Thu, 17 Feb 2022 00:23:10 -0500 Subject: [PATCH 36/75] Fixed malloc to use v1 register --- src/Main.py | 3 - src/core/mips/mips_basics.asm | 136 ++++++++++++++++------------------ 2 files changed, 62 insertions(+), 77 deletions(-) diff --git a/src/Main.py b/src/Main.py index 59ac64df9..265f121b8 100644 --- a/src/Main.py +++ b/src/Main.py @@ -60,9 +60,6 @@ def main(args): MIPSFormatter = MIPSAstFormatter() mipsCode = MIPSFormatter.visit(MIPSAst) - with open('core/mips/mips_basics.asm', 'r') as f: - mipsCode = mipsCode + "".join(f.readlines()) - # print(mipsCode) out_file = args.file.split(".") diff --git a/src/core/mips/mips_basics.asm b/src/core/mips/mips_basics.asm index d7fa0dfe7..a88509fb1 100644 --- a/src/core/mips/mips_basics.asm +++ b/src/core/mips/mips_basics.asm @@ -5,26 +5,14 @@ # $v0 address of allocated block -# t0 address of empty memory +# v1 address of empty memory # a0 size to alloc -# a1 return memory address +# v0 return memory address #TODO Parametros en la pila malloc: - # Save content of reisters - addiu $sp $sp -12 - sw $a0 0($sp) - sw $a1 4($sp) - sw $t0 8($sp) - - move $a1 $t0 - add $t0 $a0 $t0 - - # Return original content to registers - lw $a0 0($sp) - lw $a1 4($sp) - lw $t0 8($sp) - addiu $sp $sp 12 + move $v0 $v1 + add $v1 $a0 $v1 jr $ra @@ -35,23 +23,23 @@ malloc: # $a2 size copy: - addiu $sp $sp -16 # Save content of registers in sp + addiu $sp $sp -16 # Save content of registers in sp sw $a0 0($sp) sw $a1 4($sp) sw $a2 8($sp) sw $t0 12($sp) copy_loop: - beq $a2 $zero copy_end # Copy finished (copy size is 0) - lw $t0 0($a0) # Load in t0 content of source address a0 - sw $t0 0($a1) # Save in destiny a1 content of t0 - addiu $a0 $a0 4 # Increase source address a0 - addiu $a1 $a1 4 # Increase destiny addres a1 - addi $a2 $a2 -4 # Decrease copy size + beq $a2 $zero copy_end # Copy finished (copy size is 0) + lw $t0 0($a0) # Load in t0 content of source address a0 + sw $t0 0($a1) # Save in destiny a1 content of t0 + addiu $a0 $a0 4 # Increase source address a0 + addiu $a1 $a1 4 # Increase destiny addres a1 + addi $a2 $a2 -4 # Decrease copy size j copy_loop copy_end: - lw $a0 0($sp) # Return original content to registers + lw $a0 0($sp) # Return original content to registers lw $a1 4($sp) lw $a2 8($sp) lw $t0 12($sp) @@ -71,15 +59,15 @@ length: sw $t0 0($sp) sw $t1 4($sp) - move $t0 $a0 # Move to t0 the address to begin - move $v0 $zero # Set v0 to zero + move $t0 $a0 # Move to t0 the address to begin + move $v0 $zero # Set v0 to zero len_loop: - lb $t1 0($t0) # Save in t1 first byte of address - beq $t1 $zero len_end # Finish object if t1 is zero - addi $v0 $v0 1 # Increase count in v0 - addiu $t0 $t0 1 # Increase address pointer - j len_loop # Finish loop + lb $t1 0($t0) # Save in t1 first byte of address + beq $t1 $zero len_end # Finish object if t1 is zero + addi $v0 $v0 1 # Increase count in v0 + addiu $t0 $t0 1 # Increase address pointer + j len_loop # Finish loop len_end: # Return original content to registers @@ -107,38 +95,38 @@ substr: sw $a2 24($sp) sw $ra 28($sp) - move $t0 $a0 # t0 points to beginning o string - li $t1 4 # t1 Word size + move $t0 $a0 # t0 points to beginning o string + li $t1 4 # t1 Word size - div $a2 $t1 # Size of substring / wordsize - mfhi $t2 # t2 holds remainder of division + div $a2 $t1 # Size of substring / wordsize + mfhi $t2 # t2 holds remainder of division - bne $t2 $zero substr_allign_size # Branch if division is not exact - move $t1 $a2 # t1 size of substring + bne $t2 $zero substr_allign_size # Branch if division is not exact + move $t1 $a2 # t1 size of substring j substr_new_block substr_allign_size: - sub $t1 $t1 $t2 # Convert t1 to multiple of 4 to... - add $t1 $t1 $a2 # reserve memory via malloc + sub $t1 $t1 $t2 # Convert t1 to multiple of 4 to... + add $t1 $t1 $a2 # reserve memory via malloc substr_new_block: - move $a0 $t1 # Store in a0 size of space to reserve via malloc - jal malloc # Malloc - move $t3 $a1 # Pointer to beginning of reserved space - move $t1 $zero # Count - addu $t0 $t0 $a0 # t0 Beginning of string + move $a0 $t1 # Store in a0 size of space to reserve via malloc + jal malloc # Malloc + move $t3 $a1 # Pointer to beginning of reserved space + move $t1 $zero # Count + addu $t0 $t0 $a0 # t0 Beginning of string substr_copy_loop: - beq $t1 $a2 substr_end # Copy finished - lb $t2 0($t0) # Load byte from string into t2 temporal - sb $t2 0($t3) # Savebyte from t2 into t3 - addiu $t0 $t0 1 # Increase pointer to string - addiu $t3 $t3 1 # Increase pointer to reserved space - addiu $t1 $t1 1 # Increase count + beq $t1 $a2 substr_end # Copy finished + lb $t2 0($t0) # Load byte from string into t2 temporal + sb $t2 0($t3) # Savebyte from t2 into t3 +addiu $t0 $t0 1 # Increase pointer to string + addiu $t3 $t3 1 # Increase pointer to reserved space + addiu $t1 $t1 1 # Increase count j substr_copy_loop substr_end: - sb $zero 0($t3) # Set next byte of substring to zero + sb $zero 0($t3) # Set next byte of substring to zero # Return original values to registers lw $t0 0($sp) @@ -170,42 +158,42 @@ concat: sw $a1 16($sp) sw $ra 20($sp) - move $t0 $a0 # t0 pointer to string 1 - move $t1 $a1 # t1 pointer to string 2 +move $t0 $a0 # t0 pointer to string 1 + move $t1 $a1 # t1 pointer to string 2 - addiu $a0 $a2 1 # Save in a0 size in a2 + 1 - li $t2 4 # t2 = 4 - div $a0 $t2 # a0 / t2 - mfhi $a0 # a0 remainder of division - bne $a0 $zero concat_allign_size # Branch if size is multiple of 4 - addiu $a0 $a2 1 # Add 1 t size + addiu $a0 $a2 1 # Save in a0 size in a2 + 1 + li $t2 4 # t2 = 4 + div $a0 $t2 # a0 / t2 + mfhi $a0 # a0 remainder of division + bne $a0 $zero concat_allign_size # Branch if size is multiple of 4 + addiu $a0 $a2 1 # Add 1 t size concat_allign_size: - sub $t2 $t2 $a0 # Convert t1 to multiple of 4 to... - add $a0 $a2 $t2 # reserve memory via malloc - addiu $a0 $a0 1 # Add 1 t size + sub $t2 $t2 $a0 # Convert t1 to multiple of 4 to... + add $a0 $a2 $t2 # reserve memory via malloc + addiu $a0 $a0 1 # Add 1 t size j concat_size_alligned concat_size_alligned: - jal malloc # a0 stores size to reserve - move $t2 $a1 # t2 is pointer to empty space + jal malloc # a0 stores size to reserve + move $t2 $a1 # t2 is pointer to empty space j concat_copy_first_loop concat_copy_first_loop: - lb $a0 0($t0) # move to a0 content of t0 - beq $a0 $zero concat_copy_second_loop # a0 == 0 finish - sb $a0 0($t2) # move to t2 content of a0 - addiu $t0 $t0 1 # Increase t0 pointer - addiu $t2 $t2 1 # Increase t2 pointer + lb $a0 0($t0) # move to a0 content of t0 + beq $a0 $zero concat_copy_second_loop # a0 == 0 finish + sb $a0 0($t2) # move to t2 content of a0 + addiu $t0 $t0 1 # Increase t0 pointer + addiu $t2 $t2 1 # Increase t2 pointer j concat_copy_first_loop concat_copy_second_loop: - lb $a0 0($t1) # move to a0 content of t1 - beq $a0 $zero concat_end # a0 == 0 finish - sb $a0 0($t2) # move to t2 content of a0 - addiu $t1 $t1 1 # Increase t1 pointer - addiu $t2 $t2 1 # Increase t2 pointer + lb $a0 0($t1) # move to a0 content of t1 + beq $a0 $zero concat_end # a0 == 0 finish + sb $a0 0($t2) # move to t2 content of a0 + addiu $t1 $t1 1 # Increase t1 pointer + addiu $t2 $t2 1 # Increase t2 pointer j concat_copy_second_loop concat_end: From 7f0faa05b247f8fbda95a3fc6391fd0ea1e03a83 Mon Sep 17 00:00:00 2001 From: mavaldivie Date: Thu, 17 Feb 2022 02:27:12 -0500 Subject: [PATCH 37/75] Fixed bugs in code generation --- src/core/mips/CilToMipsVisitor.py | 30 ++++++++++++++---------------- src/core/mips/MipsAst.py | 4 ++-- src/core/mips/mips_basics.asm | 2 +- 3 files changed, 17 insertions(+), 19 deletions(-) diff --git a/src/core/mips/CilToMipsVisitor.py b/src/core/mips/CilToMipsVisitor.py index b5658c58f..45d4cad0a 100644 --- a/src/core/mips/CilToMipsVisitor.py +++ b/src/core/mips/CilToMipsVisitor.py @@ -134,21 +134,19 @@ def visit(self, node): instructions = [] if node.name == 'entry': instructions.append(mips.MoveNode(mips.V1_REG, mips.GP_REG, 0, 0)) + instructions.extend(mips.push_register(mips.RA_REG, node.line, node.column)) instructions.extend(mips.push_register(mips.FP_REG, line=node.line, column=node.column)) - instructions.append(mips.AdditionInmediateNode(mips.FP_REG, mips.SP_REG, 4, line=node.line, column=node.column)) + instructions.append(mips.AdditionInmediateNode(mips.FP_REG, mips.SP_REG, 8, line=node.line, column=node.column)) instructions.append(mips.AdditionInmediateNode(mips.SP_REG, mips.SP_REG, -size_for_locals, line=node.line, column=node.column)) - reg = mips.REGISTERS[0] - for param in params: - instructions.append(mips.LoadWordNode(reg, self.get_var_location(param), - line=node.line, column=node.column)) for i in node.instructions: instructions.extend(self.visit(i)) instructions.append(mips.AdditionInmediateNode(mips.SP_REG, mips.SP_REG, size_for_locals, line=node.line, column=node.column)) instructions.extend(mips.pop_register(mips.FP_REG, line=node.line, column=node.column)) + instructions.extend(mips.pop_register(mips.RA_REG, node.line, node.column)) if self._current_function.label != 'main': instructions.append(mips.JumpRegisterNode(mips.RA_REG, line=node.line, column=node.column)) @@ -569,24 +567,23 @@ def visit(self, node: cil.LabelNode): def visit(self, node: cil.SubstringNode): instructions = [] - reg1 = mips.REGISTERS[0] + reg1 = mips.ARG_REGISTERS[0] instructions.append(mips.LoadWordNode(reg1, self.get_var_location(node.str_value), line=node.line, column=node.column)) - instructions.extend(mips.push_register(reg1, line=node.line, column=node.column)) + reg1 = mips.ARG_REGISTERS[1] if type(node.index) == int: instructions.append(mips.LoadInmediateNode(reg1, node.index, line=node.line, column=node.column)) else: instructions.append(mips.LoadWordNode(reg1, self.get_var_location(node.index), line=node.line, column=node.column)) - instructions.extend(mips.push_register(reg1, line=node.line, column=node.column)) + reg1 = mips.ARG_REGISTERS[2] if type(node.index) == int: instructions.append(mips.LoadInmediateNode(reg1, node.length, line=node.line, column=node.column)) else: instructions.append(mips.LoadWordNode(reg1, self.get_var_location(node.length), line=node.line, column=node.column)) - instructions.extend(mips.push_register(reg1, line=node.line, column=node.column)) instructions.append(mips.JalNode("substr", line=node.line, column=node.column)) instructions.append(mips.StoreWordNode(mips.V0_REG, self.get_var_location(node.dest), @@ -597,18 +594,17 @@ def visit(self, node: cil.SubstringNode): def visit(self, node: cil.ConcatNode): instructions = [] - reg = mips.REGISTERS[0] + reg = mips.ARG_REGISTERS[0] instructions.append( mips.LoadWordNode(reg, self.get_var_location(node.prefix), line=node.line, column=node.column)) - instructions.extend(mips.push_register(reg, line=node.line, column=node.column)) + reg = mips.ARG_REGISTERS[1] instructions.append( mips.LoadWordNode(reg, self.get_var_location(node.suffix), line=node.line, column=node.column)) - instructions.extend(mips.push_register(reg, line=node.line, column=node.column)) + reg = mips.ARG_REGISTERS[2] instructions.append( - mips.LoadWordNode(reg, self.get_var_location(node.suffix), line=node.line, column=node.column)) - instructions.extend(mips.push_register(reg, line=node.line, column=node.column)) + mips.LoadWordNode(reg, self.get_var_location(node.length), line=node.line, column=node.column)) instructions.append(mips.JalNode("concat", line=node.line, column=node.column)) instructions.append(mips.StoreWordNode(mips.V0_REG, self.get_var_location(node.dest), @@ -619,10 +615,9 @@ def visit(self, node: cil.ConcatNode): def visit(self, node: cil.LengthNode): instructions = [] - reg = mips.REGISTERS[0] + reg = mips.ARG_REGISTERS[0] instructions.append(mips.LoadWordNode(reg, self.get_var_location(node.source), line=node.line, column=node.column)) - instructions.extend(mips.push_register(reg, line=node.line, column=node.column)) instructions.append(mips.JalNode("length", line=node.line, column=node.column)) instructions.append(mips.StoreWordNode(mips.V0_REG, self.get_var_location(node.dest), @@ -676,6 +671,7 @@ def visit(self, node: cil.EqualStringNode): reg1 = mips.REGISTERS[0] instructions.append(mips.LoadWordNode(reg1, self.get_var_location(node.left), line=node.line, column=node.column)) + # TODO: Ver si se hace usando los registros de parametros instructions.extend(mips.push_register(reg1, line=node.line, column=node.column)) instructions.append(mips.LoadWordNode(reg1, self.get_var_location(node.right), @@ -685,6 +681,8 @@ def visit(self, node: cil.EqualStringNode): instructions.append(mips.JalNode("equal_str", line=node.line, column=node.column)) instructions.append(mips.StoreWordNode(mips.V0_REG, self.get_var_location(node.dest), line=node.line, column=node.column)) + instructions.extend(mips.pop_register(reg1, line=node.line, column=node.column)) + instructions.extend(mips.pop_register(reg1, line=node.line, column=node.column)) return instructions diff --git a/src/core/mips/MipsAst.py b/src/core/mips/MipsAst.py index 40db22835..9a9519956 100644 --- a/src/core/mips/MipsAst.py +++ b/src/core/mips/MipsAst.py @@ -56,8 +56,8 @@ def get_param_stack_location(self, name): def get_local_stack_location(self, name): index = self.localvars.index(name) - offset = (index + 2) * -DOUBLE_WORD - return RegisterRelativeLocation(FP_REG, offset) + offset = index * DOUBLE_WORD + return RegisterRelativeLocation(SP_REG, offset) def get_var_location(self, name): try: diff --git a/src/core/mips/mips_basics.asm b/src/core/mips/mips_basics.asm index a88509fb1..b7e3ea0a5 100644 --- a/src/core/mips/mips_basics.asm +++ b/src/core/mips/mips_basics.asm @@ -77,7 +77,7 @@ len_end: jr $ra - +# TODO: Necesita tambien un puntero al indice del substring en a1 # SUBSTRING # a0 Pointer to beginning of string # a2 Size of substring From 14b21ca6d8370c6961af20b81925b0a6c91e745a Mon Sep 17 00:00:00 2001 From: ClaudiaOM Date: Thu, 17 Feb 2022 12:45:04 -0600 Subject: [PATCH 38/75] Fixed bugs with stack manipulation --- src/core/mips/CilToMipsVisitor.py | 26 +++++++++++--------------- src/core/mips/MIPSAstFormatter.py | 4 ++-- src/core/mips/MipsAst.py | 4 ++-- src/core/mips/mips_basics.asm | 4 ++++ 4 files changed, 19 insertions(+), 19 deletions(-) diff --git a/src/core/mips/CilToMipsVisitor.py b/src/core/mips/CilToMipsVisitor.py index 45d4cad0a..c2345a78f 100644 --- a/src/core/mips/CilToMipsVisitor.py +++ b/src/core/mips/CilToMipsVisitor.py @@ -316,13 +316,13 @@ def visit(self, node): func_name = self._function_names[node.function] instructions.append(mips.JalNode(func_name, line=node.line, column=node.column)) - instructions.append(mips.StoreWordNode(mips.V0_REG, self.get_var_location(node.dest), - line=node.line, column=node.column)) if self._pushed_args > 0: instructions.append(mips.AdditionInmediateNode(mips.SP_REG, mips.SP_REG, self._pushed_args * mips.DOUBLE_WORD, line=node.line, column=node.column)) self._pushed_args = 0 + instructions.append(mips.StoreWordNode(mips.V0_REG, self.get_var_location(node.dest), + line=node.line, column=node.column)) return instructions @@ -349,14 +349,13 @@ def visit(self, node): line=node.line, column=node.column)) instructions.append(mips.JalNode(reg1, line=node.line, column=node.column)) - instructions.append(mips.StoreWordNode(mips.V0_REG, self.get_var_location(node.dest), - line=node.line, column=node.column)) - if self._pushed_args > 0: instructions.append(mips.AdditionInmediateNode(mips.SP_REG, mips.SP_REG, self._pushed_args * mips.DOUBLE_WORD, line=node.line, column=node.column)) self._pushed_args = 0 + instructions.append(mips.StoreWordNode(mips.V0_REG, self.get_var_location(node.dest), + line=node.line, column=node.column)) return instructions @@ -392,10 +391,10 @@ def visit(self, node): def visit(self, node): instructions = [] - string = mips.LabelRelativeLocation(self._data_section[node.msg.name].label, 0) reg = mips.REGISTERS[0] - - instructions.append(mips.LoadAddressNode(reg, string, line=node.line, column=node.column)) + instructions.append(mips.LoadAddressNode(reg, self._data_section[node.msg.name].label, line=node.line, column=node.column)) + instructions.append(mips.StoreWordNode(reg, self.get_var_location(node.dest) + , line=node.line, column=node.column)) return instructions @@ -488,17 +487,14 @@ def visit(self, node: cil.ReadIntNode): return instructions - ''' @visitor.when(cil.ReadStringNode) def visit(self, node: cil.ReadStringNode): instructions = [] - instructions.append(mips.LoadInmediateNode(mips.V0_REG, 5)) - instructions.append(mips.SyscallNode()) - instructions.append(mips.StoreWordNode(mips.V0_REG, self.get_var_location(node.dest))) + instructions.append(mips.JalNode('read_string', node.line, node.column)) + instructions.append(mips.StoreWordNode(mips.V0_REG, self.get_var_location(node.dest), node.line, node.column)) return instructions - ''' @visitor.when(cil.SetAttribNode) def visit(self, node: cil.SetAttribNode): @@ -679,10 +675,10 @@ def visit(self, node: cil.EqualStringNode): instructions.extend(mips.push_register(reg1, line=node.line, column=node.column)) instructions.append(mips.JalNode("equal_str", line=node.line, column=node.column)) - instructions.append(mips.StoreWordNode(mips.V0_REG, self.get_var_location(node.dest), - line=node.line, column=node.column)) instructions.extend(mips.pop_register(reg1, line=node.line, column=node.column)) instructions.extend(mips.pop_register(reg1, line=node.line, column=node.column)) + instructions.append(mips.StoreWordNode(mips.V0_REG, self.get_var_location(node.dest), + line=node.line, column=node.column)) return instructions diff --git a/src/core/mips/MIPSAstFormatter.py b/src/core/mips/MIPSAstFormatter.py index d36e11f43..5b906dab9 100644 --- a/src/core/mips/MIPSAstFormatter.py +++ b/src/core/mips/MIPSAstFormatter.py @@ -9,7 +9,7 @@ def visit(self, node): @visitor.when(ProgramNode) def visit(self, node): - data = f'.data\n' + '\n'.join(self.visit(i) for i in node.data) + '\n' + data = f'.data\n' + '\n.word 0\n'.join(self.visit(i) for i in node.data) + '\n' names_table = f"{TYPES_LABEL}:\n" + "\n".join([f"\t.word\t{tp.name}" for tp in node.types]) proto_table = f"{PROTOTYPE_LABEL}:\n" + "\n".join([f"\t.word\t{tp.label}_prototype" for tp in node.types]) @@ -175,7 +175,7 @@ def visit(self, node): @visitor.when(LabelRelativeLocation) def visit(self, node): - return f'{node._label} + {node._offset}' + return f'{node._label}' @visitor.when(Register) def visit(self, node): diff --git a/src/core/mips/MipsAst.py b/src/core/mips/MipsAst.py index 9a9519956..30b8c78f3 100644 --- a/src/core/mips/MipsAst.py +++ b/src/core/mips/MipsAst.py @@ -56,8 +56,8 @@ def get_param_stack_location(self, name): def get_local_stack_location(self, name): index = self.localvars.index(name) - offset = index * DOUBLE_WORD - return RegisterRelativeLocation(SP_REG, offset) + offset = (len(self.localvars) + 2 - index) * DOUBLE_WORD + return RegisterRelativeLocation(FP_REG, -offset) def get_var_location(self, name): try: diff --git a/src/core/mips/mips_basics.asm b/src/core/mips/mips_basics.asm index b7e3ea0a5..4f44c3069 100644 --- a/src/core/mips/mips_basics.asm +++ b/src/core/mips/mips_basics.asm @@ -73,6 +73,7 @@ len_end: # Return original content to registers lw $t0 0($sp) lw $t1 4($sp) + addiu $sp $sp 8 jr $ra @@ -207,4 +208,7 @@ concat_end: lw $ra 20($sp) addiu $sp $sp 24 + jr $ra + +read_string: jr $ra \ No newline at end of file From 0d43fd82b719100b8ec9d14eb860ebdbe1c02dda Mon Sep 17 00:00:00 2001 From: ClaudiaOM Date: Thu, 17 Feb 2022 18:15:24 -0600 Subject: [PATCH 39/75] Added equal_str procedure --- src/core/mips/mips_basics.asm | 55 ++++++++++++++++++++++++++++++++++- tests/utils/utils.py | 2 +- 2 files changed, 55 insertions(+), 2 deletions(-) diff --git a/src/core/mips/mips_basics.asm b/src/core/mips/mips_basics.asm index 4f44c3069..384ab71bc 100644 --- a/src/core/mips/mips_basics.asm +++ b/src/core/mips/mips_basics.asm @@ -121,7 +121,7 @@ substr_copy_loop: beq $t1 $a2 substr_end # Copy finished lb $t2 0($t0) # Load byte from string into t2 temporal sb $t2 0($t3) # Savebyte from t2 into t3 -addiu $t0 $t0 1 # Increase pointer to string + addiu $t0 $t0 1 # Increase pointer to string addiu $t3 $t3 1 # Increase pointer to reserved space addiu $t1 $t1 1 # Increase count j substr_copy_loop @@ -211,4 +211,57 @@ concat_end: jr $ra read_string: + jr $ra + + +# EQUAL STRING + +equal_str: + # a0 pointer to string 1 + # a1 pointer to string 2 + # v0 answer + + # Save content of registers + addiu $sp $sp -24 + sw $t0 0($sp) + sw $t1 4($sp) + sw $a0 8($sp) + sw $a1 12($sp) + sw $t2 16($sp) + sw $t3 20($sp) + + move $t0 $a0 + move $t1 $a1 + +equal_str_loop: + lb $t2 0($t0) + lb $t3 0($t1) + bne $t2 $t3 equal_str_different_strings + beq $t2 $zero equal_str_finished_first + beq $t3 $zero equal_str_finished_second + addi $t1 $t1 1 + addi $t0 $t0 1 + j equal_str_loop + +equal_str_different_strings: + move $v0 $zero + j equal_str_end + +equal_str_finished_first: + beq $t1 $zero equal_str_finished_second + move $v0 $zero + j equal_str_end + +equal_str_finished_second: + li $v0 1 + +equal_str_end: + lw $t0 0($sp) + lw $t1 4($sp) + lw $a0 8($sp) + lw $a1 12($sp) + lw $t2 16($sp) + lw $t3 20($sp) + addiu $sp $sp 24 + jr $ra \ No newline at end of file diff --git a/tests/utils/utils.py b/tests/utils/utils.py index 77ead16f4..4e7a35363 100644 --- a/tests/utils/utils.py +++ b/tests/utils/utils.py @@ -77,7 +77,7 @@ def compare_outputs(compiler_path: str, cool_file_path: str, input_file_path: st try: fd = open(input_file_path, 'rb') - sp = subprocess.run(['spim', '-file', spim_file], input=fd.read(), timeout=timeout) + sp = subprocess.run(['spim', '-file', spim_file], input=fd.read(), timeout=timeout, capture_output=True) fd.close() mo = re.match(SPIM_HEADER, sp.stdout.decode()) if mo: From 38b990e606b459a31dab954d6c514c7f3f43af1d Mon Sep 17 00:00:00 2001 From: ClaudiaOM Date: Thu, 17 Feb 2022 18:16:33 -0600 Subject: [PATCH 40/75] Added MoveHigh Node and fixed bugs --- src/core/mips/CilToMipsVisitor.py | 11 ++++++++--- src/core/mips/MIPSAstFormatter.py | 5 +++++ src/core/mips/MipsAst.py | 9 +++++++++ 3 files changed, 22 insertions(+), 3 deletions(-) diff --git a/src/core/mips/CilToMipsVisitor.py b/src/core/mips/CilToMipsVisitor.py index c2345a78f..b729f19e0 100644 --- a/src/core/mips/CilToMipsVisitor.py +++ b/src/core/mips/CilToMipsVisitor.py @@ -269,7 +269,7 @@ def visit(self, node): line=node.line, column=node.column)) instructions.append(mips.DivideNode(reg1, reg2, line=node.line, column=node.column)) - instructions.append(mips.MoveLowNode(reg1, line=node.line, column=node.column)) + instructions.append(mips.MoveHighNode(reg1, line=node.line, column=node.column)) instructions.append(mips.StoreWordNode(reg1, self.get_var_location(node.dest), line=node.line, column=node.column)) @@ -303,6 +303,11 @@ def visit(self, node): instructions.append(mips.LoadWordNode(reg1, self.get_var_location(node.obj), line=node.line, column=node.column)) + instructions.append(mips.LoadWordNode(reg2, mips.RegisterRelativeLocation(reg1, 0), + line=node.line, column=node.column)) + instructions.append(mips.ShiftLeftNode(reg2, reg2, 2, node.line, node.column)) + instructions.append(mips.LoadAddressNode(reg1, mips.TYPES_LABEL, node.line, node.column)) + instructions.append(mips.AdditionNode(reg1, reg1, reg2, node.line, node.column)) instructions.append(mips.LoadWordNode(reg2, mips.RegisterRelativeLocation(reg1, 0), line=node.line, column=node.column)) instructions.append(mips.StoreWordNode(reg2, self.get_var_location(node.dest), @@ -632,7 +637,7 @@ def visit(self, node: cil.EqualNode): instructions.append(mips.LoadWordNode(reg1, self.get_var_location(node.left), line=node.line, column=node.column)) - reg2 = mips.REGISTERS[0] + reg2 = mips.REGISTERS[1] if type(node.right) == cil.VoidNode: instructions.append(mips.LoadInmediateNode(reg2, 0, line=node.line, column=node.column)) else: @@ -705,7 +710,7 @@ def visit(self, node: cil.LessEqualNode): instructions.append(mips.LoadWordNode(reg1, self.get_var_location(node.left), line=node.line, column=node.column)) - reg2 = mips.REGISTERS[0] + reg2 = mips.REGISTERS[1] instructions.append(mips.LoadWordNode(reg2, self.get_var_location(node.right), line=node.line, column=node.column)) diff --git a/src/core/mips/MIPSAstFormatter.py b/src/core/mips/MIPSAstFormatter.py index 5b906dab9..f48540602 100644 --- a/src/core/mips/MIPSAstFormatter.py +++ b/src/core/mips/MIPSAstFormatter.py @@ -154,6 +154,11 @@ def visit(self, node): return f'mflo {self.visit(node.dest)}'.ljust(50) + \ f'#line: {node.line} column: {node.column}' + @visitor.when(MoveHighNode) + def visit(self, node): + return f'mfhi {self.visit(node.dest)}'.ljust(50) + \ + f'#line: {node.line} column: {node.column}' + @visitor.when(ShiftLeftNode) def visit(self, node): return f'sll {self.visit(node.dest)} {self.visit(node.src)} {self.visit(node.bits)}'.ljust(50) + \ diff --git a/src/core/mips/MipsAst.py b/src/core/mips/MipsAst.py index 30b8c78f3..d9dea06c7 100644 --- a/src/core/mips/MipsAst.py +++ b/src/core/mips/MipsAst.py @@ -122,6 +122,15 @@ def __init__(self, dest, line, column): super().__init__(line, column) self.dest = dest +class MoveHighNode(InstructionNode): + + def __init__(self, dest, line, column): + ''' + Put the content of register Hi into register rsrc. + ''' + super().__init__(line, column) + self.dest = dest + class MultiplyNode(InstructionNode): From 9f3c6bcfa062a0a471a0664fa1b8bb2bb88bcbcc Mon Sep 17 00:00:00 2001 From: mavaldivie Date: Thu, 17 Feb 2022 21:55:55 -0500 Subject: [PATCH 41/75] Implemented read_string default procedure --- src/core/mips/CilToMipsVisitor.py | 2 +- src/core/mips/mips_basics.asm | 41 +++++++++++++++++++++++++++++++ 2 files changed, 42 insertions(+), 1 deletion(-) diff --git a/src/core/mips/CilToMipsVisitor.py b/src/core/mips/CilToMipsVisitor.py index b729f19e0..dc7c060ba 100644 --- a/src/core/mips/CilToMipsVisitor.py +++ b/src/core/mips/CilToMipsVisitor.py @@ -269,7 +269,7 @@ def visit(self, node): line=node.line, column=node.column)) instructions.append(mips.DivideNode(reg1, reg2, line=node.line, column=node.column)) - instructions.append(mips.MoveHighNode(reg1, line=node.line, column=node.column)) + instructions.append(mips.MoveLowNode(reg1, line=node.line, column=node.column)) instructions.append(mips.StoreWordNode(reg1, self.get_var_location(node.dest), line=node.line, column=node.column)) diff --git a/src/core/mips/mips_basics.asm b/src/core/mips/mips_basics.asm index 384ab71bc..95422963a 100644 --- a/src/core/mips/mips_basics.asm +++ b/src/core/mips/mips_basics.asm @@ -211,9 +211,50 @@ concat_end: jr $ra read_string: + addiu $sp $sp -4 + sw $ra 0($sp) + + move $t0 $v1 + move $t1 $zero + move $t2 $t0 + li $t3 10 + + read_loop: + + li $v0 8 + move $a0 $t0 + li $a1 4 + syscall + addiu $t2 $t2 4 + + check_newline_loop: + + lb $t1 0($t0) + addiu $t0 $t0 1 + + beq $t1 $t3 read_loop_continue + beq $t0 $t2 check_newline_loop_continue + j check_newline_loop + check_newline_loop_continue: + j read_loop + + read_loop_continue: + + bne $t0 $t2 null_terminated + addiu $t0 $t0 4 + addiu $t2 $t2 4 + null_terminated: + + move $v0 $v1 + move $v1 $t0 + + lw $ra 0($sp) + addiu $sp $sp 4 jr $ra + + # EQUAL STRING equal_str: From c86301522190e22406d4a3d55a16774bce6e26a8 Mon Sep 17 00:00:00 2001 From: ClaudiaOM Date: Thu, 17 Feb 2022 20:56:49 -0600 Subject: [PATCH 42/75] Fixed SUBSTR procedure --- src/core/mips/mips_basics.asm | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/core/mips/mips_basics.asm b/src/core/mips/mips_basics.asm index 384ab71bc..d55570de0 100644 --- a/src/core/mips/mips_basics.asm +++ b/src/core/mips/mips_basics.asm @@ -81,6 +81,7 @@ len_end: # TODO: Necesita tambien un puntero al indice del substring en a1 # SUBSTRING # a0 Pointer to beginning of string +# a1 Pointer to beginning of substring # a2 Size of substring @@ -97,6 +98,7 @@ substr: sw $ra 28($sp) move $t0 $a0 # t0 points to beginning o string + add $t0 $t0 $a1 li $t1 4 # t1 Word size div $a2 $t1 # Size of substring / wordsize @@ -113,7 +115,7 @@ substr_allign_size: substr_new_block: move $a0 $t1 # Store in a0 size of space to reserve via malloc jal malloc # Malloc - move $t3 $a1 # Pointer to beginning of reserved space + move $t3 $v0 # Pointer to beginning of reserved space move $t1 $zero # Count addu $t0 $t0 $a0 # t0 Beginning of string From c68fe2d3566fe7097d9a0f35c338eb2370cf1b9e Mon Sep 17 00:00:00 2001 From: mavaldivie Date: Thu, 17 Feb 2022 22:27:15 -0500 Subject: [PATCH 43/75] Implemented Fixed set attribute workflow in CoolToCilVisitor --- src/core/cil/COOLToCILVisitor.py | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/core/cil/COOLToCILVisitor.py b/src/core/cil/COOLToCILVisitor.py index 7aee57ef4..1b5448b66 100644 --- a/src/core/cil/COOLToCILVisitor.py +++ b/src/core/cil/COOLToCILVisitor.py @@ -62,11 +62,10 @@ def visit(self, node: cool.ClassDeclarationNode, scope: Scope): self.init_attr_name(self.current_type.parent.name), variable, line=node.line, column=node.column)) # Inicializando los atributos de la clase - instance = self.register_param(VariableInfo('instance', ''), line=node.line, column=node.column) for feat, child in zip(node.features, scope.childs): if isinstance(feat, cool.AttrDeclarationNode): self.visit(feat, child) - self.register_instruction(cil.SetAttribNode(instance, feat.id.lex, feat.ret_expr, node.id.lex, + self.register_instruction(cil.SetAttribNode(self_param, feat.id.lex, feat.ret_expr, node.id.lex, line=node.line, column=node.column)) # TODO: Deberia retornar algo aqui? From 5f180b2dd51612a0e7117f8cb8b70d406f6bb059 Mon Sep 17 00:00:00 2001 From: mavaldivie Date: Thu, 17 Feb 2022 23:31:28 -0500 Subject: [PATCH 44/75] Fixed bug with Int wrapper in SubstringNode --- src/core/cil/BaseCOOLToCILVisitor.py | 4 +++- tests/codegen/fib.cl | 29 ---------------------------- tests/codegen/fib_input.txt | 1 - tests/codegen/fib_output.txt | 2 -- 4 files changed, 3 insertions(+), 33 deletions(-) delete mode 100644 tests/codegen/fib.cl delete mode 100644 tests/codegen/fib_input.txt delete mode 100644 tests/codegen/fib_output.txt diff --git a/src/core/cil/BaseCOOLToCILVisitor.py b/src/core/cil/BaseCOOLToCILVisitor.py index 20b4fcde6..6242ccde1 100644 --- a/src/core/cil/BaseCOOLToCILVisitor.py +++ b/src/core/cil/BaseCOOLToCILVisitor.py @@ -247,6 +247,7 @@ def register_builtin(self): result = self.define_internal_local(line, column) index_value = self.define_internal_local(line, column) length_value = self.define_internal_local(line, column) + length_wrapper = self.define_internal_local(line, column) length_attr = self.define_internal_local(line, column) length_substr = self.define_internal_local(line, column) less_value = self.define_internal_local(line, column) @@ -255,7 +256,8 @@ def register_builtin(self): self.register_instruction(cil.GetAttribNode(index_value, i, 'value', 'Int', line, column)) self.register_instruction(cil.GetAttribNode(length_value, l, 'value', 'Int', line, column)) # Check Out of range error - self.register_instruction(cil.GetAttribNode(length_attr, self_param, 'length', 'String', line, column)) + self.register_instruction(cil.GetAttribNode(length_wrapper, self_param, 'length', 'String', line, column)) + self.register_instruction(cil.GetAttribNode(length_attr, length_wrapper, 'value', 'Int', line, column)) self.register_instruction(cil.PlusNode(length_substr, length_value, index_value, line, column)) self.register_instruction(cil.LessNode(less_value, length_attr, length_substr, line, column)) self.register_runtime_error(less_value, 'Substring out of range', line, column) diff --git a/tests/codegen/fib.cl b/tests/codegen/fib.cl deleted file mode 100644 index 08ceaede8..000000000 --- a/tests/codegen/fib.cl +++ /dev/null @@ -1,29 +0,0 @@ -class Main inherits IO { - -- the class has features. Only methods in this case. - main(): Object { - { - out_string("Enter n to find nth fibonacci number!\n"); - out_int(fib(in_int())); - out_string("\n"); - } - }; - - fib(i : Int) : Int { -- list of formals. And the return type of the method. - let a : Int <- 1, - b : Int <- 0, - c : Int <- 0 - in - { - while (not (i = 0)) loop -- expressions are nested. - { - c <- a + b; - i <- i - 1; - b <- a; - a <- c; - } - pool; - c; - } - }; - -}; diff --git a/tests/codegen/fib_input.txt b/tests/codegen/fib_input.txt deleted file mode 100644 index f599e28b8..000000000 --- a/tests/codegen/fib_input.txt +++ /dev/null @@ -1 +0,0 @@ -10 diff --git a/tests/codegen/fib_output.txt b/tests/codegen/fib_output.txt deleted file mode 100644 index d402da6af..000000000 --- a/tests/codegen/fib_output.txt +++ /dev/null @@ -1,2 +0,0 @@ -Enter n to find nth fibonacci number! -89 From 9fc098554173144345ea5e570d81a0189da31589 Mon Sep 17 00:00:00 2001 From: mavaldivie Date: Thu, 17 Feb 2022 23:32:50 -0500 Subject: [PATCH 45/75] Fixed bug with read_string procedure --- src/core/mips/mips_basics.asm | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/src/core/mips/mips_basics.asm b/src/core/mips/mips_basics.asm index dc5ff1bb6..251b614c9 100644 --- a/src/core/mips/mips_basics.asm +++ b/src/core/mips/mips_basics.asm @@ -117,7 +117,6 @@ substr_new_block: jal malloc # Malloc move $t3 $v0 # Pointer to beginning of reserved space move $t1 $zero # Count - addu $t0 $t0 $a0 # t0 Beginning of string substr_copy_loop: beq $t1 $a2 substr_end # Copy finished @@ -243,12 +242,11 @@ read_string: read_loop_continue: bne $t0 $t2 null_terminated - addiu $t0 $t0 4 addiu $t2 $t2 4 null_terminated: move $v0 $v1 - move $v1 $t0 + move $v1 $t2 lw $ra 0($sp) addiu $sp $sp 4 From d6596ade2bf685e9b31c6207108e921cc993dbd6 Mon Sep 17 00:00:00 2001 From: ClaudiaOM Date: Thu, 17 Feb 2022 22:33:05 -0600 Subject: [PATCH 46/75] Changed Malloc to multiple of 4 --- src/core/mips/mips_basics.asm | 26 ++++++++++++++++++++++++-- 1 file changed, 24 insertions(+), 2 deletions(-) diff --git a/src/core/mips/mips_basics.asm b/src/core/mips/mips_basics.asm index dc5ff1bb6..b7f812f43 100644 --- a/src/core/mips/mips_basics.asm +++ b/src/core/mips/mips_basics.asm @@ -11,9 +11,31 @@ #TODO Parametros en la pila malloc: + addiu $sp $sp -12 # Save content of registers in sp + sw $a0 0($sp) + sw $t1 4($sp) + sw $t2 8($sp) + + li $t1 4 + div $a0 $t1 # Size of string / wordsize + mfhi $t2 # t2 holds remainder of division + + bne $t2 $zero malloc_allign_size + move $t1 $a0 + j malloc_end + +malloc_allign_size: + sub $t1 $t1 $t2 # Convert t1 to multiple of 4 + add $t1 $t1 $a0 + +malloc_end: move $v0 $v1 - add $v1 $a0 $v1 + add $v1 $t1 $v1 + lw $a0 0($sp) # Return original content to registers + lw $t1 4($sp) + lw $t2 8($sp) + addiu $sp $sp 12 jr $ra @@ -248,7 +270,7 @@ read_string: null_terminated: move $v0 $v1 - move $v1 $t0 + move $v1 $t2 lw $ra 0($sp) addiu $sp $sp 4 From 0f48733de5f97ab3bcdbaeb0e64818542c92b0ca Mon Sep 17 00:00:00 2001 From: ClaudiaOM Date: Fri, 18 Feb 2022 02:13:54 -0600 Subject: [PATCH 47/75] fixup! Fixed SUBSTR procedure --- src/core/mips/CilToMipsVisitor.py | 6 ++---- src/core/mips/mips_basics.asm | 12 ++++++------ 2 files changed, 8 insertions(+), 10 deletions(-) diff --git a/src/core/mips/CilToMipsVisitor.py b/src/core/mips/CilToMipsVisitor.py index dc7c060ba..3460c8617 100644 --- a/src/core/mips/CilToMipsVisitor.py +++ b/src/core/mips/CilToMipsVisitor.py @@ -673,15 +673,13 @@ def visit(self, node: cil.EqualStringNode): instructions.append(mips.LoadWordNode(reg1, self.get_var_location(node.left), line=node.line, column=node.column)) # TODO: Ver si se hace usando los registros de parametros - instructions.extend(mips.push_register(reg1, line=node.line, column=node.column)) + instructions.append(mips.MoveNode(mips.ARG_REGISTERS[0], reg1, line=node.line, column=node.column)) instructions.append(mips.LoadWordNode(reg1, self.get_var_location(node.right), line=node.line, column=node.column)) - instructions.extend(mips.push_register(reg1, line=node.line, column=node.column)) + instructions.append(mips.MoveNode(mips.ARG_REGISTERS[1], reg1, line=node.line, column=node.column)) instructions.append(mips.JalNode("equal_str", line=node.line, column=node.column)) - instructions.extend(mips.pop_register(reg1, line=node.line, column=node.column)) - instructions.extend(mips.pop_register(reg1, line=node.line, column=node.column)) instructions.append(mips.StoreWordNode(mips.V0_REG, self.get_var_location(node.dest), line=node.line, column=node.column)) diff --git a/src/core/mips/mips_basics.asm b/src/core/mips/mips_basics.asm index 29eb483ad..f52d4eca4 100644 --- a/src/core/mips/mips_basics.asm +++ b/src/core/mips/mips_basics.asm @@ -182,7 +182,7 @@ concat: sw $a1 16($sp) sw $ra 20($sp) -move $t0 $a0 # t0 pointer to string 1 + move $t0 $a0 # t0 pointer to string 1 move $t1 $a1 # t1 pointer to string 2 @@ -201,7 +201,7 @@ concat_allign_size: concat_size_alligned: jal malloc # a0 stores size to reserve - move $t2 $a1 # t2 is pointer to empty space + move $t2 $v0 # t2 is pointer to empty space j concat_copy_first_loop concat_copy_first_loop: @@ -246,23 +246,23 @@ read_string: li $v0 8 move $a0 $t0 - li $a1 4 + li $a1 5 syscall addiu $t2 $t2 4 check_newline_loop: lb $t1 0($t0) - addiu $t0 $t0 1 beq $t1 $t3 read_loop_continue + addiu $t0 $t0 1 beq $t0 $t2 check_newline_loop_continue j check_newline_loop check_newline_loop_continue: j read_loop read_loop_continue: - + sb $zero 0($t0) bne $t0 $t2 null_terminated addiu $t2 $t2 4 null_terminated: @@ -311,7 +311,7 @@ equal_str_different_strings: j equal_str_end equal_str_finished_first: - beq $t1 $zero equal_str_finished_second + beq $t3 $zero equal_str_finished_second move $v0 $zero j equal_str_end From 48e8a6f96eae2ab71f463d29d6b381d1b29e7449 Mon Sep 17 00:00:00 2001 From: mavaldivie Date: Fri, 18 Feb 2022 03:29:07 -0500 Subject: [PATCH 48/75] Fixed type herency --- src/Main.py | 2 +- src/core/cil/CILAst.py | 3 +-- src/core/cil/COOLToCILVisitor.py | 20 +++++++++++++------- src/core/mips/CilToMipsVisitor.py | 16 ++++++---------- src/core/mips/MIPSAstFormatter.py | 5 +++++ src/core/mips/MipsAst.py | 9 +++++++++ src/core/mips/mips_basics.asm | 4 ++-- 7 files changed, 37 insertions(+), 22 deletions(-) diff --git a/src/Main.py b/src/Main.py index 265f121b8..7abd6ed35 100644 --- a/src/Main.py +++ b/src/Main.py @@ -53,7 +53,7 @@ def main(args): CILVisitor = COOLToCILVisitor(type_Checker.Context) CILast = CILVisitor.visit(COOLast, scope) - # print(get_formatter()(CILast)) + print(get_formatter()(CILast)) MIPSVisitor = CILToMIPSVisitor() MIPSAst = MIPSVisitor.visit(CILast) diff --git a/src/core/cil/CILAst.py b/src/core/cil/CILAst.py index fdba78ce2..4f8c0a91c 100644 --- a/src/core/cil/CILAst.py +++ b/src/core/cil/CILAst.py @@ -163,10 +163,9 @@ def __repr__(self): return f"{self.dest} = CALL {self.function}" class DynamicCallNode(InstructionNode): - def __init__(self, type, obj, method, dest, line, column): + def __init__(self, type, method, dest, line, column): super().__init__(line, column) self.type = type - self.obj = obj self.method = method self.dest = dest diff --git a/src/core/cil/COOLToCILVisitor.py b/src/core/cil/COOLToCILVisitor.py index 1b5448b66..436849551 100644 --- a/src/core/cil/COOLToCILVisitor.py +++ b/src/core/cil/COOLToCILVisitor.py @@ -13,11 +13,18 @@ def collect_types(self, node): # Guardar métodos de las clases padres iter_type = self.context.get_type(node.id.lex) + generation = [] while iter_type is not None: - type.methods.update({i: self.to_function_name(i, iter_type.name) for i in iter_type.methods.keys()}) - type.attributes.update({i.name:self.register_attribute(i.name, iter_type.name, iter_type.line, iter_type.column) for i in iter_type.attributes}) + generation.append(iter_type) iter_type = iter_type.parent + generation.reverse() + for i in generation: + for meth in i.methods: + type.methods[meth] = self.to_function_name(meth, i.name) + for attr in i.attributes: + type.attributes[attr.name] = self.register_attribute(attr.name, i.name, i.line, i.column) + @visitor.on('node') def visit(self, node): pass @@ -525,17 +532,16 @@ def visit(self, node: cool.FunctionCallNode, scope: Scope): self.register_runtime_error(isvoid, f'{node.id.line, node.id.column} - RuntimeError: Dispatch on void', line=node.id.line, column=node.id.column) - # TODO: Creo que deberia annadir los parametros al reves para luego sacarlos en el orden correcto self.register_instruction(cil.ArgNode(node.obj.ret_expr, line=node.obj.line, column=node.obj.column)) - for arg in args: - self.register_instruction(arg) + for arg in args: self.register_instruction(arg) + self.register_instruction(cil.ArgNode(node.obj.ret_expr, line=node.obj.line, column=node.obj.column)) ret = self.define_internal_local(line=node.line, column=node.column) if node.type is None: stype = node.obj.static_type.name else: stype = node.type.lex - self.register_instruction(cil.StaticCallNode(self.types_map[stype].methods[node.id.lex], ret, - line=node.id.line, column=node.id.column)) + self.register_instruction(cil.DynamicCallNode(stype, self.types_map[stype].methods[node.id.lex], + ret, line=node.id.line, column=node.id.column)) node.ret_expr = ret @visitor.when(cool.MemberCallNode) diff --git a/src/core/mips/CilToMipsVisitor.py b/src/core/mips/CilToMipsVisitor.py index 3460c8617..ae7211bd9 100644 --- a/src/core/mips/CilToMipsVisitor.py +++ b/src/core/mips/CilToMipsVisitor.py @@ -339,20 +339,16 @@ def visit(self, node): method = type.methods.index(self._function_names[node.method]) reg1 = mips.REGISTERS[0] - reg2 = mips.REGISTERS[1] - instructions.append(mips.LoadWordNode(reg1, self.get_var_location(node.obj), - line=node.line, column=node.column)) - instructions.append(mips.LoadAddressNode(reg2, mips.PROTOTYPE_LABEL, line=node.line, column=node.column)) - instructions.append(mips.ShiftLeftNode(reg2, reg1, 2, line=node.line, column=node.column)) - instructions.append(mips.AdditionNode(reg1, reg1, reg2, line=node.line, column=node.column)) - instructions.append(mips.LoadWordNode(reg1, mips.RegisterRelativeLocation(reg1, 0), + instructions.append(mips.LoadWordNode(reg1, mips.RegisterRelativeLocation(mips.SP_REG, 0), line=node.line, column=node.column)) + instructions.append(mips.AdditionInmediateNode(mips.SP_REG, mips.SP_REG, 4, node.line, node.column)) + self._pushed_args -= 1 + instructions.append(mips.LoadWordNode(reg1, mips.RegisterRelativeLocation(reg1, 8), line=node.line, column=node.column)) - instructions.append(mips.AdditionInmediateNode(reg1, reg1, method * 4, line=node.line, column=node.column)) - instructions.append(mips.LoadWordNode(reg1, mips.RegisterRelativeLocation(reg1, 0), + instructions.append(mips.LoadWordNode(reg1, mips.RegisterRelativeLocation(reg1, method * 4), line=node.line, column=node.column)) - instructions.append(mips.JalNode(reg1, line=node.line, column=node.column)) + instructions.append(mips.JalrNode(mips.RA_REG, reg1, line=node.line, column=node.column)) if self._pushed_args > 0: instructions.append(mips.AdditionInmediateNode(mips.SP_REG, mips.SP_REG, diff --git a/src/core/mips/MIPSAstFormatter.py b/src/core/mips/MIPSAstFormatter.py index f48540602..34ed53418 100644 --- a/src/core/mips/MIPSAstFormatter.py +++ b/src/core/mips/MIPSAstFormatter.py @@ -110,6 +110,11 @@ def visit(self, node): return f'jal {self.visit(node.label)}'.ljust(50) + \ f'#line: {node.line} column: {node.column}' + @visitor.when(JalrNode) + def visit(self, node): + return f'jalr {self.visit(node.reg1)} {self.visit(node.reg2)}'.ljust(50) + \ + f'#line: {node.line} column: {node.column}' + @visitor.when(MoveNode) def visit(self, node): return f'move {self.visit(node.reg1)} {self.visit(node.reg2)}'.ljust(50) + \ diff --git a/src/core/mips/MipsAst.py b/src/core/mips/MipsAst.py index d9dea06c7..62bde106a 100644 --- a/src/core/mips/MipsAst.py +++ b/src/core/mips/MipsAst.py @@ -235,6 +235,15 @@ def __init__(self, label, line, column): super().__init__(line, column) self.label = label +class JalrNode(InstructionNode): + def __init__(self, reg1, reg2, line, column): + ''' + Unconditionally jump to the instruction at target register. Save the address of the next + instruction in register ra (rd said the manual). + ''' + super().__init__(line, column) + self.reg1 = reg1 + self.reg2 = reg2 class MoveNode(InstructionNode): def __init__(self, reg1, reg2, line, column): diff --git a/src/core/mips/mips_basics.asm b/src/core/mips/mips_basics.asm index f52d4eca4..4cd392b74 100644 --- a/src/core/mips/mips_basics.asm +++ b/src/core/mips/mips_basics.asm @@ -9,7 +9,6 @@ # a0 size to alloc # v0 return memory address -#TODO Parametros en la pila malloc: addiu $sp $sp -12 # Save content of registers in sp sw $a0 0($sp) @@ -100,7 +99,6 @@ len_end: jr $ra -# TODO: Necesita tambien un puntero al indice del substring en a1 # SUBSTRING # a0 Pointer to beginning of string # a1 Pointer to beginning of substring @@ -256,6 +254,7 @@ read_string: beq $t1 $t3 read_loop_continue addiu $t0 $t0 1 + beq $t0 $t2 check_newline_loop_continue j check_newline_loop check_newline_loop_continue: @@ -263,6 +262,7 @@ read_string: read_loop_continue: sb $zero 0($t0) + bne $t0 $t2 null_terminated addiu $t2 $t2 4 null_terminated: From ee8d01fa91bea6d30bc8380b9470e2d677b8e068 Mon Sep 17 00:00:00 2001 From: ClaudiaOM Date: Fri, 18 Feb 2022 20:40:07 -0600 Subject: [PATCH 49/75] Fixed up when accesing attributes --- src/core/cil/BaseCOOLToCILVisitor.py | 14 +- src/core/cil/CILAst.py | 26 +-- src/core/cil/COOLToCILVisitor.py | 13 +- src/core/mips/CilToMipsVisitor.py | 276 ++++++++++++++++++++++++++- src/core/tools/Semantic.py | 3 + 5 files changed, 303 insertions(+), 29 deletions(-) diff --git a/src/core/cil/BaseCOOLToCILVisitor.py b/src/core/cil/BaseCOOLToCILVisitor.py index 6242ccde1..f51dd4321 100644 --- a/src/core/cil/BaseCOOLToCILVisitor.py +++ b/src/core/cil/BaseCOOLToCILVisitor.py @@ -42,20 +42,20 @@ def register_param(self, vinfo, line, column): name = f'local_param_{self.current_function.name}_{vinfo.name}_{len(self.params)}' param_node = cil.ParamNode(name, line, column) self.params.append(param_node) - self.var_names[vinfo.name] = name - return name + self.var_names[vinfo.name] = cil.VarNode(name, line, column) + return self.var_names[vinfo.name] def register_local(self, vinfo, line, column): name = vinfo.name vinfo.name = f'local_{self.current_function.name}_{vinfo.name}_{len(self.localvars)}' local_node = cil.LocalNode(vinfo.name, line, column) self.localvars.append(local_node) - self.var_names[name] = vinfo.name - return vinfo.name + self.var_names[name] = cil.VarNode(vinfo.name, line, column) + return self.var_names[name] def register_attribute(self, name, type, line, column): name = f'attr_{type}_{name}' - return cil.AttributeNode(name, line, column) + return cil.AttributeNode(name, type, line, column) def define_internal_local(self, line, column): @@ -92,10 +92,10 @@ def register_label(self, label, line, column): return cil.LabelNode(lname, line, column) def init_name(self, name): - return f'init_at_{name}' + return f'_init_at_{name}' def init_attr_name(self, name): - return f'init_attr_at_{name}' + return f'_init_attr_at_{name}' def register_runtime_error(self, condition, msg, line, column): error_node = self.register_label('error_label', line, column) diff --git a/src/core/cil/CILAst.py b/src/core/cil/CILAst.py index 4f8c0a91c..b60f69239 100644 --- a/src/core/cil/CILAst.py +++ b/src/core/cil/CILAst.py @@ -35,21 +35,11 @@ def __init__(self, fname, params, localvars, instructions, line, column): self.instructions = instructions self.labels_count = 0 -class ParamNode(Node): - def __init__(self, name, line, column): - super().__init__(line, column) - self.name = name - class LocalNode(Node): def __init__(self, name, line, column): super().__init__(line, column) self.name = name -class AttributeNode(Node): - def __init__(self, name, line, column): - super().__init__(line, column) - self.name = name - class InstructionNode(Node): pass @@ -285,6 +275,20 @@ def __init__(self, dest, value, line, column): self.dest = dest self.value = value +class VarNode(InstructionNode): + def __init__(self, name, line, column): + super().__init__(line, column) + self.name = name + +class AttributeNode(VarNode): + def __init__(self, name, type, line, column): + super().__init__(name, line, column) + self.type = type + +class ParamNode(VarNode): + def __init__(self, name, line, column): + super().__init__(name, line, column) + def get_formatter(): class PrintVisitor(object): @@ -433,7 +437,7 @@ def visit(self, node: LabelNode): @visitor.when(SubstringNode) def visit(self, node: SubstringNode): - return f'{node.dest} = SUBSTRING {node.str_value}[{node.index}:{node.index + node.length}]' + return f'{node.dest} = SUBSTRING {node.str_value}[{node.index}:{node.index} up to {node.length}]' @visitor.when(ConcatNode) def visit(self, node: ConcatNode): diff --git a/src/core/cil/COOLToCILVisitor.py b/src/core/cil/COOLToCILVisitor.py index 436849551..8f4ec0282 100644 --- a/src/core/cil/COOLToCILVisitor.py +++ b/src/core/cil/COOLToCILVisitor.py @@ -20,10 +20,12 @@ def collect_types(self, node): generation.reverse() for i in generation: - for meth in i.methods: + methods = sorted(i.methods) + attributes = sorted(i.attributes) + for meth in methods: type.methods[meth] = self.to_function_name(meth, i.name) - for attr in i.attributes: - type.attributes[attr.name] = self.register_attribute(attr.name, i.name, i.line, i.column) + for attr in attributes: + type.attributes[attr.name] = cil.AttributeNode(attr.name, i.name, i.line, i.column) @visitor.on('node') def visit(self, node): @@ -56,9 +58,11 @@ def visit(self, node: cool.ClassDeclarationNode, scope: Scope): self.current_function = self.register_function(self.init_attr_name(node.id.lex), line=node.line, column=node.column) + type.methods['init_attr'] = self.current_function.name self_param = self.register_param(self.vself, line=node.line, column=node.column) self.localvars.extend(type.attributes.values()) - self.var_names.update({i:j.name for i,j in type.attributes.items()}) + self.var_names.update({i:cil.AttributeNode(j.name, type.name, node.line, node.column) + for i,j in type.attributes.items()}) self.vself.name = self_param # Inicializando los atributos de la clase y llamando al constructor del padre @@ -88,6 +92,7 @@ def visit(self, node: cool.ClassDeclarationNode, scope: Scope): # Allocate de la clase self.current_function = self.register_function(self.init_name(node.id.lex), line=node.line, column=node.column) + type.methods['_init'] = self.current_function.name self.localvars.extend(type.attributes.values()) instance = self.define_internal_local(line=node.line, column=node.column) self.register_instruction(cil.AllocateNode(node.id.lex, instance, line=node.line, column=node.column)) diff --git a/src/core/mips/CilToMipsVisitor.py b/src/core/mips/CilToMipsVisitor.py index ae7211bd9..a36575325 100644 --- a/src/core/mips/CilToMipsVisitor.py +++ b/src/core/mips/CilToMipsVisitor.py @@ -54,8 +54,12 @@ def register_label(self, old_label, new_label): def get_label(self, label): return self._labels[label] - def get_var_location(self, name): - return self._current_function.get_var_location(name) + def get_var_location(self, node): + if isinstance(node, cil.AttributeNode): + return mips.RegisterRelativeLocation(mips.SP_REG, 0) + if isinstance(node, str): + return self._current_function.get_var_location(node) + return self._current_function.get_var_location(node.name) @visitor.on('node') def collect_func_names(self, node): @@ -163,16 +167,24 @@ def visit(self, node): if isinstance(node.source, cil.VoidNode): reg1 = mips.ZERO_REG - elif node.source.isnumeric(): + elif isinstance(node.source, int): reg1 = mips.REGISTERS[0] instructions.append(mips.LoadInmediateNode(reg1, int(node.source), line=node.line, column=node.column)) else: reg1 = mips.REGISTERS[0] + instructions.extend(self.visit(node.source)) instructions.append(mips.LoadWordNode(reg1, self.get_var_location(node.source), line=node.line, column=node.column)) + if isinstance(node.source, cil.AttributeNode): + instructions.append(mips.AdditionInmediateNode(mips.SP_REG, mips.SP_REG, 4, + node.line, node.column)) + instructions.extend(self.visit(node.dest)) instructions.append(mips.StoreWordNode(reg1, self.get_var_location(node.dest), line=node.line, column=node.column)) + if isinstance(node.dest, cil.AttributeNode): + instructions.append(mips.AdditionInmediateNode(mips.SP_REG, mips.SP_REG, 4, + node.line, node.column)) return instructions @visitor.when(cil.PlusNode) @@ -183,20 +195,32 @@ def visit(self, node): if isinstance(node.left, int): instructions.append(mips.LoadInmediateNode(reg1, node.left, line=node.line, column=node.column)) else: + instructions.extend(self.visit(node.left)) instructions.append(mips.LoadWordNode(reg1, self.get_var_location(node.left), line=node.line, column=node.column)) + if isinstance(node.left, cil.AttributeNode): + instructions.append(mips.AdditionInmediateNode(mips.SP_REG, mips.SP_REG, 4, + node.line, node.column)) reg2 = mips.REGISTERS[1] if isinstance(node.right, int): instructions.append(mips.LoadInmediateNode(reg2, node.right, line=node.line, column=node.column)) else: + instructions.extend(self.visit(node.right)) instructions.append(mips.LoadWordNode(reg2, self.get_var_location(node.right), line=node.line, column=node.column)) + if isinstance(node.right, cil.AttributeNode): + instructions.append(mips.AdditionInmediateNode(mips.SP_REG, mips.SP_REG, 4, + node.line, node.column)) reg3 = mips.REGISTERS[0] instructions.append(mips.AdditionNode(reg3, reg1, reg2, line=node.line, column=node.column)) + instructions.extend(self.visit(node.dest)) instructions.append(mips.StoreWordNode(reg3, self.get_var_location(node.dest), line=node.line, column=node.column)) + if isinstance(node.dest, cil.AttributeNode): + instructions.append(mips.AdditionInmediateNode(mips.SP_REG, mips.SP_REG, 4, + node.line, node.column)) return instructions @@ -208,20 +232,32 @@ def visit(self, node): if isinstance(node.left, int): instructions.append(mips.LoadInmediateNode(reg1, node.left, line=node.line, column=node.column)) else: + instructions.extend(self.visit(node.left)) instructions.append(mips.LoadWordNode(reg1, self.get_var_location(node.left), line=node.line, column=node.column)) + if isinstance(node.left, cil.AttributeNode): + instructions.append(mips.AdditionInmediateNode(mips.SP_REG, mips.SP_REG, 4, + node.line, node.column)) reg2 = mips.REGISTERS[1] if isinstance(node.right, int): instructions.append(mips.LoadInmediateNode(reg2, node.right, line=node.line, column=node.column)) else: + instructions.extend(self.visit(node.right)) instructions.append(mips.LoadWordNode(reg2, self.get_var_location(node.right), line=node.line, column=node.column)) + if isinstance(node.right, cil.AttributeNode): + instructions.append(mips.AdditionInmediateNode(mips.SP_REG, mips.SP_REG, 4, + node.line, node.column)) reg3 = mips.REGISTERS[0] instructions.append(mips.SubstractionNode(reg3, reg1, reg2, line=node.line, column=node.column)) + instructions.extend(self.visit(node.dest)) instructions.append(mips.StoreWordNode(reg3, self.get_var_location(node.dest), line=node.line, column=node.column)) + if isinstance(node.dest, cil.AttributeNode): + instructions.append(mips.AdditionInmediateNode(mips.SP_REG, mips.SP_REG, 4, + node.line, node.column)) return instructions @@ -233,20 +269,32 @@ def visit(self, node): if isinstance(node.left, int): instructions.append(mips.LoadInmediateNode(reg1, node.left, line=node.line, column=node.column)) else: + instructions.extend(self.visit(node.left)) instructions.append(mips.LoadWordNode(reg1, self.get_var_location(node.left), line=node.line, column=node.column)) + if isinstance(node.left, cil.AttributeNode): + instructions.append(mips.AdditionInmediateNode(mips.SP_REG, mips.SP_REG, 4, + node.line, node.column)) reg2 = mips.REGISTERS[1] if isinstance(node.right, int): instructions.append(mips.LoadInmediateNode(reg2, node.right, line=node.line, column=node.column)) else: + instructions.extend(self.visit(node.right)) instructions.append(mips.LoadWordNode(reg2, self.get_var_location(node.right), line=node.line, column=node.column)) + if isinstance(node.right, cil.AttributeNode): + instructions.append(mips.AdditionInmediateNode(mips.SP_REG, mips.SP_REG, 4, + node.line, node.column)) reg3 = mips.REGISTERS[0] instructions.append(mips.MultiplyNode(reg3, reg1, reg2, line=node.line, column=node.column)) + instructions.extend(self.visit(node.dest)) instructions.append(mips.StoreWordNode(reg3, self.get_var_location(node.dest), line=node.line, column=node.column)) + if isinstance(node.dest, cil.AttributeNode): + instructions.append(mips.AdditionInmediateNode(mips.SP_REG, mips.SP_REG, 4, + node.line, node.column)) return instructions @@ -258,20 +306,32 @@ def visit(self, node): if isinstance(node.left, int): instructions.append(mips.LoadInmediateNode(reg1, node.left, line=node.line, column=node.column)) else: + instructions.extend(self.visit(node.left)) instructions.append(mips.LoadWordNode(reg1, self.get_var_location(node.left), line=node.line, column=node.column)) + if isinstance(node.left, cil.AttributeNode): + instructions.append(mips.AdditionInmediateNode(mips.SP_REG, mips.SP_REG, 4, + node.line, node.column)) reg2 = mips.REGISTERS[1] if isinstance(node.right, int): instructions.append(mips.LoadInmediateNode(reg2, node.right, line=node.line, column=node.column)) else: + instructions.extend(self.visit(node.right)) instructions.append(mips.LoadWordNode(reg2, self.get_var_location(node.right), line=node.line, column=node.column)) + if isinstance(node.right, cil.AttributeNode): + instructions.append(mips.AdditionInmediateNode(mips.SP_REG, mips.SP_REG, 4, + node.line, node.column)) instructions.append(mips.DivideNode(reg1, reg2, line=node.line, column=node.column)) instructions.append(mips.MoveLowNode(reg1, line=node.line, column=node.column)) + instructions.extend(self.visit(node.dest)) instructions.append(mips.StoreWordNode(reg1, self.get_var_location(node.dest), line=node.line, column=node.column)) + if isinstance(node.dest, cil.AttributeNode): + instructions.append(mips.AdditionInmediateNode(mips.SP_REG, mips.SP_REG, 4, + node.line, node.column)) return instructions @@ -289,8 +349,12 @@ def visit(self, node): instructions.append(mips.LoadInmediateNode(reg1, type, line=node.line, column=node.column)) instructions.extend(mips.create_object(reg1, reg2, line=node.line, column=node.column)) + instructions.extend(self.visit(node.dest)) instructions.append(mips.StoreWordNode(mips.V0_REG, self.get_var_location(node.dest), line=node.line, column=node.column)) + if isinstance(node.dest, cil.AttributeNode): + instructions.append(mips.AdditionInmediateNode(mips.SP_REG, mips.SP_REG, 4, + node.line, node.column)) return instructions @@ -301,8 +365,12 @@ def visit(self, node): reg1 = mips.REGISTERS[0] reg2 = mips.REGISTERS[1] + instructions.extend(self.visit(node.obj)) instructions.append(mips.LoadWordNode(reg1, self.get_var_location(node.obj), line=node.line, column=node.column)) + if isinstance(node.obj, cil.AttributeNode): + instructions.append(mips.AdditionInmediateNode(mips.SP_REG, mips.SP_REG, 4, + node.line, node.column)) instructions.append(mips.LoadWordNode(reg2, mips.RegisterRelativeLocation(reg1, 0), line=node.line, column=node.column)) instructions.append(mips.ShiftLeftNode(reg2, reg2, 2, node.line, node.column)) @@ -310,8 +378,12 @@ def visit(self, node): instructions.append(mips.AdditionNode(reg1, reg1, reg2, node.line, node.column)) instructions.append(mips.LoadWordNode(reg2, mips.RegisterRelativeLocation(reg1, 0), line=node.line, column=node.column)) + instructions.extend(self.visit(node.dest)) instructions.append(mips.StoreWordNode(reg2, self.get_var_location(node.dest), line=node.line, column=node.column)) + if isinstance(node.dest, cil.AttributeNode): + instructions.append(mips.AdditionInmediateNode(mips.SP_REG, mips.SP_REG, 4, + node.line, node.column)) return instructions @@ -326,8 +398,12 @@ def visit(self, node): self._pushed_args * mips.DOUBLE_WORD, line=node.line, column=node.column)) self._pushed_args = 0 + instructions.extend(self.visit(node.dest)) instructions.append(mips.StoreWordNode(mips.V0_REG, self.get_var_location(node.dest), line=node.line, column=node.column)) + if isinstance(node.dest, cil.AttributeNode): + instructions.append(mips.AdditionInmediateNode(mips.SP_REG, mips.SP_REG, 4, + node.line, node.column)) return instructions @@ -355,8 +431,12 @@ def visit(self, node): self._pushed_args * mips.DOUBLE_WORD, line=node.line, column=node.column)) self._pushed_args = 0 + instructions.extend(self.visit(node.dest)) instructions.append(mips.StoreWordNode(mips.V0_REG, self.get_var_location(node.dest), line=node.line, column=node.column)) + if isinstance(node.dest, cil.AttributeNode): + instructions.append(mips.AdditionInmediateNode(mips.SP_REG, mips.SP_REG, 4, + node.line, node.column)) return instructions @@ -369,8 +449,12 @@ def visit(self, node): instructions.append(mips.LoadInmediateNode(reg, node.name, line=node.line, column=node.column)) else: + instructions.extend(self.visit(node.name)) instructions.append(mips.LoadWordNode(reg, self.get_var_location(node.name), line=node.line, column=node.column)) + if isinstance(node.name, cil.AttributeNode): + instructions.append(mips.AdditionInmediateNode(mips.SP_REG, mips.SP_REG, 4, + node.line, node.column)) instructions.extend(mips.push_register(reg, line=node.line, column=node.column)) return instructions @@ -384,8 +468,12 @@ def visit(self, node): instructions.append(mips.LoadInmediateNode(mips.V0_REG, node.value, line=node.line, column=node.column)) else: + instructions.extend(self.visit(node.value)) instructions.append(mips.LoadWordNode(mips.V0_REG, self.get_var_location(node.value), line=node.line, column=node.column)) + if isinstance(node.value, cil.AttributeNode): + instructions.append(mips.AdditionInmediateNode(mips.SP_REG, mips.SP_REG, 4, + node.line, node.column)) return instructions @visitor.when(cil.LoadNode) @@ -394,8 +482,12 @@ def visit(self, node): reg = mips.REGISTERS[0] instructions.append(mips.LoadAddressNode(reg, self._data_section[node.msg.name].label, line=node.line, column=node.column)) + instructions.extend(self.visit(node.dest)) instructions.append(mips.StoreWordNode(reg, self.get_var_location(node.dest) , line=node.line, column=node.column)) + if isinstance(node.dest, cil.AttributeNode): + instructions.append(mips.AdditionInmediateNode(mips.SP_REG, mips.SP_REG, 4, + node.line, node.column)) return instructions @@ -403,8 +495,12 @@ def visit(self, node): def visit(self, node: cil.PrintStringNode): instructions = [] instructions.append(mips.LoadInmediateNode(mips.V0_REG, 4, line=node.line, column=node.column)) + instructions.extend(self.visit(node.str_addr)) instructions.append(mips.LoadWordNode(mips.ARG_REGISTERS[0], self.get_var_location(node.str_addr), line=node.line, column=node.column)) + if isinstance(node.str_addr, cil.AttributeNode): + instructions.append(mips.AdditionInmediateNode(mips.SP_REG, mips.SP_REG, 4, + node.line, node.column)) instructions.append(mips.SyscallNode()) return instructions @@ -412,8 +508,12 @@ def visit(self, node: cil.PrintStringNode): def visit(self, node: cil.PrintIntNode): instructions = [] instructions.append(mips.LoadInmediateNode(mips.V0_REG, 1, line=node.line, column=node.column)) + instructions.extend(self.visit(node.value)) instructions.append(mips.LoadWordNode(mips.ARG_REGISTERS[0], self.get_var_location(node.value), line=node.line, column=node.column)) + if isinstance(node.value, cil.AttributeNode): + instructions.append(mips.AdditionInmediateNode(mips.SP_REG, mips.SP_REG, 4, + node.line, node.column)) instructions.append(mips.SyscallNode()) return instructions @@ -429,8 +529,12 @@ def visit(self, node): instructions = [] reg = mips.REGISTERS[0] + instructions.extend(self.visit(node.value)) instructions.append(mips.LoadWordNode(reg, self.get_var_location(node.value), line=node.line, column=node.column)) + if isinstance(node.value, cil.AttributeNode): + instructions.append(mips.AdditionInmediateNode(mips.SP_REG, mips.SP_REG, 4, + node.line, node.column)) instructions.append(mips.LoadWordNode(mips.ARG_REGISTERS[0], mips.RegisterRelativeLocation(reg, 4), line=node.line, column=node.column)) instructions.append(mips.ShiftLeftNode(mips.ARG_REGISTERS[0], mips.ARG_REGISTERS[0], 2, @@ -441,8 +545,12 @@ def visit(self, node): instructions.append(mips.MoveNode(mips.ARG_REGISTERS[0], reg, line=node.line, column=node.column)) instructions.append(mips.MoveNode(mips.ARG_REGISTERS[1], mips.V0_REG, line=node.line, column=node.column)) instructions.append(mips.JalNode("copy", line=node.line, column=node.column)) + instructions.extend(self.visit(node.dest)) instructions.append(mips.StoreWordNode(mips.V0_REG, self.get_var_location(node.dest) , line=node.line, column=node.column)) + if isinstance(node.dest, cil.AttributeNode): + instructions.append(mips.AdditionInmediateNode(mips.SP_REG, mips.SP_REG, 4, + node.line, node.column)) return instructions @@ -452,15 +560,23 @@ def visit(self, node: cil.GetAttribNode): reg1 = mips.REGISTERS[0] reg2 = mips.REGISTERS[1] + instructions.extend(self.visit(node.obj)) instructions.append(mips.LoadWordNode(reg1, self.get_var_location(node.obj), line=node.line, column=node.column)) + if isinstance(node.obj, cil.AttributeNode): + instructions.append(mips.AdditionInmediateNode(mips.SP_REG, mips.SP_REG, 4, + node.line, node.column)) tp = self._types_section[node.computed_type] offset = (tp.attributes.index(node.attr) + 3) * mips.DOUBLE_WORD instructions.append(mips.LoadWordNode(reg2, mips.RegisterRelativeLocation(reg1, offset), line=node.line, column=node.column)) + instructions.extend(self.visit(node.dest)) instructions.append(mips.StoreWordNode(reg2, self.get_var_location(node.dest), line=node.line, column=node.column)) + if isinstance(node.dest, cil.AttributeNode): + instructions.append(mips.AdditionInmediateNode(mips.SP_REG, mips.SP_REG, 4, + node.line, node.column)) return instructions @@ -483,8 +599,12 @@ def visit(self, node: cil.ReadIntNode): instructions.append(mips.LoadInmediateNode(mips.V0_REG, 5, line=node.line, column=node.column)) instructions.append(mips.SyscallNode()) + instructions.extend(self.visit(node.dest)) instructions.append(mips.StoreWordNode(mips.V0_REG, self.get_var_location(node.dest), line=node.line, column=node.column)) + if isinstance(node.dest, cil.AttributeNode): + instructions.append(mips.AdditionInmediateNode(mips.SP_REG, mips.SP_REG, 4, + node.line, node.column)) return instructions @@ -493,8 +613,11 @@ def visit(self, node: cil.ReadStringNode): instructions = [] instructions.append(mips.JalNode('read_string', node.line, node.column)) + instructions.extend(self.visit(node.dest)) instructions.append(mips.StoreWordNode(mips.V0_REG, self.get_var_location(node.dest), node.line, node.column)) - + if isinstance(node.dest, cil.AttributeNode): + instructions.append(mips.AdditionInmediateNode(mips.SP_REG, mips.SP_REG, 4, + node.line, node.column)) return instructions @visitor.when(cil.SetAttribNode) @@ -505,16 +628,24 @@ def visit(self, node: cil.SetAttribNode): offset = (tp.attributes.index(node.attr) + 3) * mips.DOUBLE_WORD reg1 = mips.REGISTERS[0] + instructions.extend(self.visit(node.obj)) instructions.append(mips.LoadWordNode(reg1, self.get_var_location(node.obj), line=node.line, column=node.column)) + if isinstance(node.obj, cil.AttributeNode): + instructions.append(mips.AdditionInmediateNode(mips.SP_REG, mips.SP_REG, 4, + node.line, node.column)) reg2 = mips.REGISTERS[1] if type(node.value) == int: instructions.append(mips.LoadInmediateNode(reg2, node.value, line=node.line, column=node.column)) else: + instructions.extend(self.visit(node.value)) instructions.append(mips.LoadWordNode(reg2, self.get_var_location(node.value), line=node.line, column=node.column)) + if isinstance(node.value, cil.AttributeNode): + instructions.append(mips.AdditionInmediateNode(mips.SP_REG, mips.SP_REG, 4, + node.line, node.column)) instructions.append(mips.StoreWordNode(reg2, mips.RegisterRelativeLocation(reg1, offset), line=node.line, column=node.column)) @@ -525,15 +656,27 @@ def visit(self, node: cil.LessNode): instructions = [] reg1 = mips.REGISTERS[0] + instructions.extend(self.visit(node.left)) instructions.append(mips.LoadWordNode(reg1, self.get_var_location(node.left), line=node.line, column=node.column)) + if isinstance(node.left, cil.AttributeNode): + instructions.append(mips.AdditionInmediateNode(mips.SP_REG, mips.SP_REG, 4, + node.line, node.column)) reg2 = mips.REGISTERS[1] + instructions.extend(self.visit(node.right)) instructions.append(mips.LoadWordNode(reg2, self.get_var_location(node.right), line=node.line, column=node.column)) + if isinstance(node.right, cil.AttributeNode): + instructions.append(mips.AdditionInmediateNode(mips.SP_REG, mips.SP_REG, 4, + node.line, node.column)) instructions.append(mips.LessNode(reg2, reg1, reg2, line=node.line, column=node.column)) + instructions.extend(self.visit(node.dest)) instructions.append(mips.StoreWordNode(reg2, self.get_var_location(node.dest), line=node.line, column=node.column)) + if isinstance(node.dest, cil.AttributeNode): + instructions.append(mips.AdditionInmediateNode(mips.SP_REG, mips.SP_REG, 4, + node.line, node.column)) return instructions @@ -543,8 +686,12 @@ def visit(self, node: cil.GotoIfNode): mips_label = self.get_label(node.label) reg1 = mips.REGISTERS[0] + instructions.extend(self.visit(node.condition)) instructions.append(mips.LoadWordNode(reg1, self.get_var_location(node.condition), line=node.line, column=node.column)) + if isinstance(node.condition, cil.AttributeNode): + instructions.append(mips.AdditionInmediateNode(mips.SP_REG, mips.SP_REG, 4, + node.line, node.column)) instructions.append(mips.BranchOnNotEqualNode(reg1, mips.ZERO_REG, mips_label, line=node.line, column=node.column)) @@ -565,26 +712,42 @@ def visit(self, node: cil.SubstringNode): instructions = [] reg1 = mips.ARG_REGISTERS[0] + instructions.extend(self.visit(node.str_value)) instructions.append(mips.LoadWordNode(reg1, self.get_var_location(node.str_value), line=node.line, column=node.column)) + if isinstance(node.str_value, cil.AttributeNode): + instructions.append(mips.AdditionInmediateNode(mips.SP_REG, mips.SP_REG, 4, + node.line, node.column)) reg1 = mips.ARG_REGISTERS[1] if type(node.index) == int: instructions.append(mips.LoadInmediateNode(reg1, node.index, line=node.line, column=node.column)) else: + instructions.extend(self.visit(node.index)) instructions.append(mips.LoadWordNode(reg1, self.get_var_location(node.index), line=node.line, column=node.column)) + if isinstance(node.index, cil.AttributeNode): + instructions.append(mips.AdditionInmediateNode(mips.SP_REG, mips.SP_REG, 4, + node.line, node.column)) reg1 = mips.ARG_REGISTERS[2] if type(node.index) == int: instructions.append(mips.LoadInmediateNode(reg1, node.length, line=node.line, column=node.column)) else: + instructions.extend(self.visit(node.length)) instructions.append(mips.LoadWordNode(reg1, self.get_var_location(node.length), line=node.line, column=node.column)) + if isinstance(node.length, cil.AttributeNode): + instructions.append(mips.AdditionInmediateNode(mips.SP_REG, mips.SP_REG, 4, + node.line, node.column)) instructions.append(mips.JalNode("substr", line=node.line, column=node.column)) + instructions.extend(self.visit(node.dest)) instructions.append(mips.StoreWordNode(mips.V0_REG, self.get_var_location(node.dest), line=node.line, column=node.column)) + if isinstance(node.dest, cil.AttributeNode): + instructions.append(mips.AdditionInmediateNode(mips.SP_REG, mips.SP_REG, 4, + node.line, node.column)) return instructions @visitor.when(cil.ConcatNode) @@ -592,20 +755,33 @@ def visit(self, node: cil.ConcatNode): instructions = [] reg = mips.ARG_REGISTERS[0] + instructions.extend(self.visit(node.prefix)) instructions.append( mips.LoadWordNode(reg, self.get_var_location(node.prefix), line=node.line, column=node.column)) - + if isinstance(node.prefix, cil.AttributeNode): + instructions.append(mips.AdditionInmediateNode(mips.SP_REG, mips.SP_REG, 4, + node.line, node.column)) reg = mips.ARG_REGISTERS[1] + instructions.extend(self.visit(node.suffix)) instructions.append( mips.LoadWordNode(reg, self.get_var_location(node.suffix), line=node.line, column=node.column)) - + if isinstance(node.suffix, cil.AttributeNode): + instructions.append(mips.AdditionInmediateNode(mips.SP_REG, mips.SP_REG, 4, + node.line, node.column)) reg = mips.ARG_REGISTERS[2] + instructions.extend(self.visit(node.length)) instructions.append( mips.LoadWordNode(reg, self.get_var_location(node.length), line=node.line, column=node.column)) - + if isinstance(node.length, cil.AttributeNode): + instructions.append(mips.AdditionInmediateNode(mips.SP_REG, mips.SP_REG, 4, + node.line, node.column)) instructions.append(mips.JalNode("concat", line=node.line, column=node.column)) + instructions.extend(self.visit(node.dest)) instructions.append(mips.StoreWordNode(mips.V0_REG, self.get_var_location(node.dest), line=node.line, column=node.column)) + if isinstance(node.dest, cil.AttributeNode): + instructions.append(mips.AdditionInmediateNode(mips.SP_REG, mips.SP_REG, 4, + node.line, node.column)) return instructions @visitor.when(cil.LengthNode) @@ -613,12 +789,20 @@ def visit(self, node: cil.LengthNode): instructions = [] reg = mips.ARG_REGISTERS[0] + instructions.extend(self.visit(node.source)) instructions.append(mips.LoadWordNode(reg, self.get_var_location(node.source), line=node.line, column=node.column)) + if isinstance(node.source, cil.AttributeNode): + instructions.append(mips.AdditionInmediateNode(mips.SP_REG, mips.SP_REG, 4, + node.line, node.column)) instructions.append(mips.JalNode("length", line=node.line, column=node.column)) + instructions.extend(self.visit(node.dest)) instructions.append(mips.StoreWordNode(mips.V0_REG, self.get_var_location(node.dest), line=node.line, column=node.column)) + if isinstance(node.dest, cil.AttributeNode): + instructions.append(mips.AdditionInmediateNode(mips.SP_REG, mips.SP_REG, 4, + node.line, node.column)) return instructions @@ -630,19 +814,31 @@ def visit(self, node: cil.EqualNode): if type(node.left) == cil.VoidNode: instructions.append(mips.LoadInmediateNode(reg1, 0, line=node.line, column=node.column)) else: + instructions.extend(self.visit(node.left)) instructions.append(mips.LoadWordNode(reg1, self.get_var_location(node.left), line=node.line, column=node.column)) + if isinstance(node.left, cil.AttributeNode): + instructions.append(mips.AdditionInmediateNode(mips.SP_REG, mips.SP_REG, 4, + node.line, node.column)) reg2 = mips.REGISTERS[1] if type(node.right) == cil.VoidNode: instructions.append(mips.LoadInmediateNode(reg2, 0, line=node.line, column=node.column)) else: + instructions.extend(self.visit(node.right)) instructions.append(mips.LoadWordNode(reg2, self.get_var_location(node.right), line=node.line, column=node.column)) + if isinstance(node.right, cil.AttributeNode): + instructions.append(mips.AdditionInmediateNode(mips.SP_REG, mips.SP_REG, 4, + node.line, node.column)) instructions.append(mips.EqualNode(reg1, reg1, reg2, line=node.line, column=node.column)) + instructions.extend(self.visit(node.dest)) instructions.append(mips.StoreWordNode(reg1, self.get_var_location(node.dest), line=node.line, column=node.column)) + if isinstance(node.dest, cil.AttributeNode): + instructions.append(mips.AdditionInmediateNode(mips.SP_REG, mips.SP_REG, 4, + node.line, node.column)) return instructions @visitor.when(cil.NameNode) @@ -657,8 +853,12 @@ def visit(self, node: cil.NameNode): instructions.append(mips.LoadWordNode(reg, mips.RegisterRelativeLocation(reg, 0), line=node.line, column=node.column)) + instructions.extend(self.visit(node.dest)) instructions.append(mips.StoreWordNode(reg, self.get_var_location(node.dest), line=node.line, column=node.column)) + if isinstance(node.dest, cil.AttributeNode): + instructions.append(mips.AdditionInmediateNode(mips.SP_REG, mips.SP_REG, 4, + node.line, node.column)) return instructions @visitor.when(cil.EqualStringNode) @@ -666,18 +866,30 @@ def visit(self, node: cil.EqualStringNode): instructions = [] reg1 = mips.REGISTERS[0] + instructions.extend(self.visit(node.left)) instructions.append(mips.LoadWordNode(reg1, self.get_var_location(node.left), line=node.line, column=node.column)) + if isinstance(node.left, cil.AttributeNode): + instructions.append(mips.AdditionInmediateNode(mips.SP_REG, mips.SP_REG, 4, + node.line, node.column)) # TODO: Ver si se hace usando los registros de parametros instructions.append(mips.MoveNode(mips.ARG_REGISTERS[0], reg1, line=node.line, column=node.column)) + instructions.extend(self.visit(node.right)) instructions.append(mips.LoadWordNode(reg1, self.get_var_location(node.right), line=node.line, column=node.column)) + if isinstance(node.right, cil.AttributeNode): + instructions.append(mips.AdditionInmediateNode(mips.SP_REG, mips.SP_REG, 4, + node.line, node.column)) instructions.append(mips.MoveNode(mips.ARG_REGISTERS[1], reg1, line=node.line, column=node.column)) instructions.append(mips.JalNode("equal_str", line=node.line, column=node.column)) + instructions.extend(self.visit(node.dest)) instructions.append(mips.StoreWordNode(mips.V0_REG, self.get_var_location(node.dest), line=node.line, column=node.column)) + if isinstance(node.dest, cil.AttributeNode): + instructions.append(mips.AdditionInmediateNode(mips.SP_REG, mips.SP_REG, 4, + node.line, node.column)) return instructions @@ -686,13 +898,21 @@ def visit(self, node: cil.ComplementNode): instructions = [] reg1 = mips.REGISTERS[0] + instructions.extend(self.visit(node.value)) instructions.append(mips.LoadWordNode(reg1, self.get_var_location(node.value), line=node.line, column=node.column)) + if isinstance(node.value, cil.AttributeNode): + instructions.append(mips.AdditionInmediateNode(mips.SP_REG, mips.SP_REG, 4, + node.line, node.column)) instructions.append(mips.NotNode(reg1, reg1, line=node.line, column=node.column)) instructions.append(mips.AdditionInmediateNode(reg1, reg1, 1, line=node.line, column=node.column)) + instructions.extend(self.visit(node.dest)) instructions.append(mips.StoreWordNode(reg1, self.get_var_location(node.dest), line=node.line, column=node.column)) + if isinstance(node.dest, cil.AttributeNode): + instructions.append(mips.AdditionInmediateNode(mips.SP_REG, mips.SP_REG, 4, + node.line, node.column)) return instructions @@ -701,16 +921,28 @@ def visit(self, node: cil.LessEqualNode): instructions = [] reg1 = mips.REGISTERS[0] + instructions.extend(self.visit(node.left)) instructions.append(mips.LoadWordNode(reg1, self.get_var_location(node.left), line=node.line, column=node.column)) + if isinstance(node.left, cil.AttributeNode): + instructions.append(mips.AdditionInmediateNode(mips.SP_REG, mips.SP_REG, 4, + node.line, node.column)) reg2 = mips.REGISTERS[1] + instructions.extend(self.visit(node.right)) instructions.append(mips.LoadWordNode(reg2, self.get_var_location(node.right), line=node.line, column=node.column)) + if isinstance(node.right, cil.AttributeNode): + instructions.append(mips.AdditionInmediateNode(mips.SP_REG, mips.SP_REG, 4, + node.line, node.column)) instructions.append(mips.LessEqualNode(reg1, reg1, reg2, line=node.line, column=node.column)) + instructions.extend(self.visit(node.dest)) instructions.append(mips.StoreWordNode(reg1, self.get_var_location(node.dest), line=node.line, column=node.column)) + if isinstance(node.dest, cil.AttributeNode): + instructions.append(mips.AdditionInmediateNode(mips.SP_REG, mips.SP_REG, 4, + node.line, node.column)) return instructions ''' @@ -728,10 +960,40 @@ def visit(self, node: cil.NotNode): instructions = [] reg1 = mips.REGISTERS[0] + instructions.extend(self.visit(node.value)) instructions.append(mips.LoadWordNode(reg1, self.get_var_location(node.value), line=node.line, column=node.column)) + if isinstance(node.value, cil.AttributeNode): + instructions.append(mips.AdditionInmediateNode(mips.SP_REG, mips.SP_REG, 4, + node.line, node.column)) instructions.append(mips.NotNode(reg1, reg1, line=node.line, column=node.column)) + instructions.extend(self.visit(node.dest)) instructions.append(mips.StoreWordNode(reg1, self.get_var_location(node.dest), line=node.line, column=node.column)) + if isinstance(node.dest, cil.AttributeNode): + instructions.append(mips.AdditionInmediateNode(mips.SP_REG, mips.SP_REG, 4, + node.line, node.column)) return instructions + + @visitor.when(cil.VarNode) + def visit(self, node: cil.VarNode): + return [] + + @visitor.when(cil.ParamNode) + def visit(self, node: cil.ParamNode): + return [] + + @visitor.when(cil.AttributeNode) + def visit(self, node: cil.AttributeNode): + instructions = [] + + self_var = self._current_function.params[0] + reg1 = mips.REGISTERS[0] + instructions.append(mips.LoadWordNode(reg1, self.get_var_location(self_var), node.line, node.column)) + + index = self._types_section[node.type].attributes.index(node.name) + 3 + instructions.append(mips.LoadWordNode(reg1, mips.RegisterRelativeLocation(reg1, index * 4), + line=node.line, column=node.column)) + instructions.extend(mips.push_register(reg1, node.line, node.column)) + return instructions \ No newline at end of file diff --git a/src/core/tools/Semantic.py b/src/core/tools/Semantic.py index fc2d693c9..10006a697 100644 --- a/src/core/tools/Semantic.py +++ b/src/core/tools/Semantic.py @@ -21,6 +21,9 @@ def __str__(self): def __repr__(self): return str(self) + def __lt__(self, other): + return self.name < other.name + # Representa un metodo en un tipo del programa class Method: def __init__(self, name : str, param_names : list, param_types : list, return_type): From f1796e30f9b6deca76090551fb6120aaa01c1988 Mon Sep 17 00:00:00 2001 From: mavaldivie Date: Fri, 18 Feb 2022 22:01:28 -0500 Subject: [PATCH 50/75] Fixed not Node in Mips --- src/core/mips/CilToMipsVisitor.py | 2 +- src/core/mips/MIPSAstFormatter.py | 5 +++++ src/core/mips/MipsAst.py | 6 ++++++ 3 files changed, 12 insertions(+), 1 deletion(-) diff --git a/src/core/mips/CilToMipsVisitor.py b/src/core/mips/CilToMipsVisitor.py index a36575325..8f2f951ac 100644 --- a/src/core/mips/CilToMipsVisitor.py +++ b/src/core/mips/CilToMipsVisitor.py @@ -905,7 +905,7 @@ def visit(self, node: cil.ComplementNode): instructions.append(mips.AdditionInmediateNode(mips.SP_REG, mips.SP_REG, 4, node.line, node.column)) - instructions.append(mips.NotNode(reg1, reg1, line=node.line, column=node.column)) + instructions.append(mips.ComplementNode(reg1, reg1, line=node.line, column=node.column)) instructions.append(mips.AdditionInmediateNode(reg1, reg1, 1, line=node.line, column=node.column)) instructions.extend(self.visit(node.dest)) instructions.append(mips.StoreWordNode(reg1, self.get_var_location(node.dest), diff --git a/src/core/mips/MIPSAstFormatter.py b/src/core/mips/MIPSAstFormatter.py index 34ed53418..e74b91a7a 100644 --- a/src/core/mips/MIPSAstFormatter.py +++ b/src/core/mips/MIPSAstFormatter.py @@ -150,6 +150,11 @@ def visit(self, node): f'#line: {node.line} column: {node.column}' @visitor.when(NotNode) + def visit(self, node): + return f'xori {self.visit(node.dest)} {self.visit(node.src)} 1'.ljust(50) + \ + f'#line: {node.line} column: {node.column}' + + @visitor.when(ComplementNode) def visit(self, node): return f'not {self.visit(node.dest)} {self.visit(node.src)}'.ljust(50) + \ f'#line: {node.line} column: {node.column}' diff --git a/src/core/mips/MipsAst.py b/src/core/mips/MipsAst.py index 62bde106a..f90ce8eb2 100644 --- a/src/core/mips/MipsAst.py +++ b/src/core/mips/MipsAst.py @@ -303,6 +303,12 @@ def __init__(self, dest, src, line, column): self.dest = dest self.src = src +class ComplementNode(InstructionNode): + def __init__(self, dest, src, line, column): + super().__init__(line, column) + self.dest = dest + self.src = src + class ShiftLeftNode(InstructionNode): # Shift Left Logical def __init__(self, dest, src, bits, line, column): From 4e22e807fcd3532f2e6f6a59100f61d4975f8317 Mon Sep 17 00:00:00 2001 From: mavaldivie Date: Fri, 18 Feb 2022 23:13:29 -0500 Subject: [PATCH 51/75] Returning the object location on method init_attributes --- src/Main.py | 2 +- src/core/cil/BaseCOOLToCILVisitor.py | 4 ++-- src/core/cil/COOLToCILVisitor.py | 11 +++-------- tests/codegen/complex.cl | 12 ++++++------ tests/codegen/new_complex.cl | 12 ++++++------ 5 files changed, 18 insertions(+), 23 deletions(-) diff --git a/src/Main.py b/src/Main.py index 7abd6ed35..37f5d2496 100644 --- a/src/Main.py +++ b/src/Main.py @@ -53,7 +53,7 @@ def main(args): CILVisitor = COOLToCILVisitor(type_Checker.Context) CILast = CILVisitor.visit(COOLast, scope) - print(get_formatter()(CILast)) + #print(get_formatter()(CILast)) MIPSVisitor = CILToMIPSVisitor() MIPSAst = MIPSVisitor.visit(CILast) diff --git a/src/core/cil/BaseCOOLToCILVisitor.py b/src/core/cil/BaseCOOLToCILVisitor.py index f51dd4321..00d01a368 100644 --- a/src/core/cil/BaseCOOLToCILVisitor.py +++ b/src/core/cil/BaseCOOLToCILVisitor.py @@ -92,10 +92,10 @@ def register_label(self, label, line, column): return cil.LabelNode(lname, line, column) def init_name(self, name): - return f'_init_at_{name}' + return f'__init_at_{name}' def init_attr_name(self, name): - return f'_init_attr_at_{name}' + return f'__init_attr_at_{name}' def register_runtime_error(self, condition, msg, line, column): error_node = self.register_label('error_label', line, column) diff --git a/src/core/cil/COOLToCILVisitor.py b/src/core/cil/COOLToCILVisitor.py index 8f4ec0282..246a1f097 100644 --- a/src/core/cil/COOLToCILVisitor.py +++ b/src/core/cil/COOLToCILVisitor.py @@ -58,7 +58,7 @@ def visit(self, node: cool.ClassDeclarationNode, scope: Scope): self.current_function = self.register_function(self.init_attr_name(node.id.lex), line=node.line, column=node.column) - type.methods['init_attr'] = self.current_function.name + type.methods['__init_attr'] = self.current_function.name self_param = self.register_param(self.vself, line=node.line, column=node.column) self.localvars.extend(type.attributes.values()) self.var_names.update({i:cil.AttributeNode(j.name, type.name, node.line, node.column) @@ -78,7 +78,7 @@ def visit(self, node: cool.ClassDeclarationNode, scope: Scope): self.visit(feat, child) self.register_instruction(cil.SetAttribNode(self_param, feat.id.lex, feat.ret_expr, node.id.lex, line=node.line, column=node.column)) - # TODO: Deberia retornar algo aqui? + self.register_instruction(cil.ReturnNode(node.line, node.column, self_param)) # TypeNode de la clase # type = self.types_map[node.id.lex] @@ -92,7 +92,7 @@ def visit(self, node: cool.ClassDeclarationNode, scope: Scope): # Allocate de la clase self.current_function = self.register_function(self.init_name(node.id.lex), line=node.line, column=node.column) - type.methods['_init'] = self.current_function.name + type.methods['__init'] = self.current_function.name self.localvars.extend(type.attributes.values()) instance = self.define_internal_local(line=node.line, column=node.column) self.register_instruction(cil.AllocateNode(node.id.lex, instance, line=node.line, column=node.column)) @@ -102,11 +102,6 @@ def visit(self, node: cool.ClassDeclarationNode, scope: Scope): self.register_instruction(cil.StaticCallNode(self.init_attr_name(node.id.lex), variable, line=node.line, column=node.column)) - if 'init' in self.current_type.methods.keys(): - self.register_instruction(cil.ArgNode(instance, line=node.line, column=node.column)) - self.register_instruction(cil.StaticCallNode( - self.to_function_name('init', self.current_type.name), variable, line=node.line, column=node.column)) - self.register_instruction(cil.ReturnNode(value=instance, line=node.line, column=node.column)) self.current_function = None diff --git a/tests/codegen/complex.cl b/tests/codegen/complex.cl index 0b7aa44e9..38cb9fe73 100755 --- a/tests/codegen/complex.cl +++ b/tests/codegen/complex.cl @@ -15,8 +15,8 @@ class Complex inherits IO { init(a : Int, b : Int) : Complex { { - x = a; - y = b; + x <- a; + y <- b; self; } }; @@ -30,22 +30,22 @@ class Complex inherits IO { reflect_0() : Complex { { - x = ~x; - y = ~y; + x <- ~x; + y <- ~y; self; } }; reflect_X() : Complex { { - y = ~y; + y <- ~y; self; } }; reflect_Y() : Complex { { - x = ~x; + x <- ~x; self; } }; diff --git a/tests/codegen/new_complex.cl b/tests/codegen/new_complex.cl index a4fe714ce..5e7240b1c 100755 --- a/tests/codegen/new_complex.cl +++ b/tests/codegen/new_complex.cl @@ -23,8 +23,8 @@ class Complex inherits IO { init(a : Int, b : Int) : Complex { { - x = a; - y = b; + x <- a; + y <- b; self; } }; @@ -38,22 +38,22 @@ class Complex inherits IO { reflect_0() : Complex { { - x = ~x; - y = ~y; + x <- ~x; + y <- ~y; self; } }; reflect_X() : Complex { { - y = ~y; + y <- ~y; self; } }; reflect_Y() : Complex { { - x = ~x; + x <- ~x; self; } }; From 6ac9833cbba41b499e1df029fba67f554cde580a Mon Sep 17 00:00:00 2001 From: mavaldivie Date: Sat, 19 Feb 2022 01:20:35 -0500 Subject: [PATCH 52/75] Fixed bug when setting attribute location --- src/core/cil/COOLToCILVisitor.py | 22 +++++++++++++--------- src/core/mips/CilToMipsVisitor.py | 5 ++--- 2 files changed, 15 insertions(+), 12 deletions(-) diff --git a/src/core/cil/COOLToCILVisitor.py b/src/core/cil/COOLToCILVisitor.py index 246a1f097..a5d8a73ad 100644 --- a/src/core/cil/COOLToCILVisitor.py +++ b/src/core/cil/COOLToCILVisitor.py @@ -68,7 +68,7 @@ def visit(self, node: cool.ClassDeclarationNode, scope: Scope): # Inicializando los atributos de la clase y llamando al constructor del padre if self.current_type.parent.name not in ('Object', 'IO'): variable = self.define_internal_local(line=node.line, column=node.column) - self.register_instruction(cil.ArgNode(self.vself.name, line=node.line, column=node.column)) + self.register_instruction(cil.ArgNode(self_param, line=node.line, column=node.column)) self.register_instruction(cil.StaticCallNode( self.init_attr_name(self.current_type.parent.name), variable, line=node.line, column=node.column)) @@ -102,7 +102,7 @@ def visit(self, node: cool.ClassDeclarationNode, scope: Scope): self.register_instruction(cil.StaticCallNode(self.init_attr_name(node.id.lex), variable, line=node.line, column=node.column)) - self.register_instruction(cil.ReturnNode(value=instance, line=node.line, column=node.column)) + self.register_instruction(cil.ReturnNode(value=variable, line=node.line, column=node.column)) self.current_function = None self.current_type = None @@ -114,7 +114,7 @@ def visit(self, node: cool.AttrDeclarationNode, scope: Scope): self.visit(node.expression, scope.childs[0]) self.register_instruction(cil.AssignNode(variable, node.expression.ret_expr, line=node.expression.line, column=node.expression.column)) - elif node.type.lex in self.value_types: + else: self.register_instruction(cil.AllocateNode(node.type.lex, variable, line=node.line, column=node.column)) node.ret_expr = variable @@ -534,14 +534,18 @@ def visit(self, node: cool.FunctionCallNode, scope: Scope): self.register_instruction(cil.ArgNode(node.obj.ret_expr, line=node.obj.line, column=node.obj.column)) for arg in args: self.register_instruction(arg) - self.register_instruction(cil.ArgNode(node.obj.ret_expr, line=node.obj.line, column=node.obj.column)) ret = self.define_internal_local(line=node.line, column=node.column) - if node.type is None: - stype = node.obj.static_type.name - else: + if node.type is not None: stype = node.type.lex - self.register_instruction(cil.DynamicCallNode(stype, self.types_map[stype].methods[node.id.lex], - ret, line=node.id.line, column=node.id.column)) + self.register_instruction(cil.StaticCallNode(self.types_map[stype].methods[node.id.lex], + ret, line=node.id.line, column=node.id.column)) + else: + stype = node.obj.static_type.name + self.register_instruction(cil.ArgNode(node.obj.ret_expr, line=node.obj.line, column=node.obj.column)) + self.register_instruction(cil.DynamicCallNode(stype, self.types_map[stype].methods[node.id.lex], + ret, line=node.id.line, column=node.id.column)) + + node.ret_expr = ret @visitor.when(cool.MemberCallNode) diff --git a/src/core/mips/CilToMipsVisitor.py b/src/core/mips/CilToMipsVisitor.py index 8f2f951ac..977d6fed5 100644 --- a/src/core/mips/CilToMipsVisitor.py +++ b/src/core/mips/CilToMipsVisitor.py @@ -989,11 +989,10 @@ def visit(self, node: cil.AttributeNode): instructions = [] self_var = self._current_function.params[0] - reg1 = mips.REGISTERS[0] + reg1 = mips.REGISTERS[4] instructions.append(mips.LoadWordNode(reg1, self.get_var_location(self_var), node.line, node.column)) index = self._types_section[node.type].attributes.index(node.name) + 3 - instructions.append(mips.LoadWordNode(reg1, mips.RegisterRelativeLocation(reg1, index * 4), - line=node.line, column=node.column)) + instructions.append(mips.AdditionInmediateNode(reg1, reg1, index * 4, line=node.line, column=node.column)) instructions.extend(mips.push_register(reg1, node.line, node.column)) return instructions \ No newline at end of file From 71008debb595a2f55576c0ffe32d90d70325076c Mon Sep 17 00:00:00 2001 From: mavaldivie Date: Sat, 19 Feb 2022 22:30:56 -0500 Subject: [PATCH 53/75] Fixed assignment to class attributes --- src/Main.py | 2 +- src/core/mips/CilToMipsVisitor.py | 21 +++++++++++++++------ 2 files changed, 16 insertions(+), 7 deletions(-) diff --git a/src/Main.py b/src/Main.py index 37f5d2496..7abd6ed35 100644 --- a/src/Main.py +++ b/src/Main.py @@ -53,7 +53,7 @@ def main(args): CILVisitor = COOLToCILVisitor(type_Checker.Context) CILast = CILVisitor.visit(COOLast, scope) - #print(get_formatter()(CILast)) + print(get_formatter()(CILast)) MIPSVisitor = CILToMIPSVisitor() MIPSAst = MIPSVisitor.visit(CILast) diff --git a/src/core/mips/CilToMipsVisitor.py b/src/core/mips/CilToMipsVisitor.py index 977d6fed5..2767e824c 100644 --- a/src/core/mips/CilToMipsVisitor.py +++ b/src/core/mips/CilToMipsVisitor.py @@ -179,12 +179,20 @@ def visit(self, node): instructions.append(mips.AdditionInmediateNode(mips.SP_REG, mips.SP_REG, 4, node.line, node.column)) - instructions.extend(self.visit(node.dest)) - instructions.append(mips.StoreWordNode(reg1, self.get_var_location(node.dest), - line=node.line, column=node.column)) if isinstance(node.dest, cil.AttributeNode): - instructions.append(mips.AdditionInmediateNode(mips.SP_REG, mips.SP_REG, 4, - node.line, node.column)) + self_var = self._current_function.params[0] + reg2 = mips.REGISTERS[1] + instructions.append(mips.LoadWordNode(reg2, self.get_var_location(self_var), node.line, node.column)) + + index = self._types_section[node.dest.type].attributes.index(node.dest.name) + 3 + instructions.append(mips.AdditionInmediateNode(reg2, reg2, index * 4, + line=node.line, column=node.column)) + instructions.append(mips.StoreWordNode(reg1, mips.RegisterRelativeLocation(reg2, 0), + line=node.line, column=node.column)) + else: + instructions.append(mips.StoreWordNode(reg1, self.get_var_location(node.dest), + line=node.line, column=node.column)) + return instructions @visitor.when(cil.PlusNode) @@ -993,6 +1001,7 @@ def visit(self, node: cil.AttributeNode): instructions.append(mips.LoadWordNode(reg1, self.get_var_location(self_var), node.line, node.column)) index = self._types_section[node.type].attributes.index(node.name) + 3 - instructions.append(mips.AdditionInmediateNode(reg1, reg1, index * 4, line=node.line, column=node.column)) + instructions.append(mips.LoadWordNode(reg1, mips.RegisterRelativeLocation(reg1, index * 4), + line=node.line, column=node.column)) instructions.extend(mips.push_register(reg1, node.line, node.column)) return instructions \ No newline at end of file From 20f061a7a4198b4b911b4a252937ffd5b26d2486 Mon Sep 17 00:00:00 2001 From: mavaldivie Date: Sun, 20 Feb 2022 00:02:30 -0500 Subject: [PATCH 54/75] Fixed malloc to use syscall 9 --- src/core/mips/mips_basics.asm | 68 ++++++++++++++++++++++++++--------- 1 file changed, 51 insertions(+), 17 deletions(-) diff --git a/src/core/mips/mips_basics.asm b/src/core/mips/mips_basics.asm index 4cd392b74..69eee2442 100644 --- a/src/core/mips/mips_basics.asm +++ b/src/core/mips/mips_basics.asm @@ -12,28 +12,22 @@ malloc: addiu $sp $sp -12 # Save content of registers in sp sw $a0 0($sp) - sw $t1 4($sp) - sw $t2 8($sp) - - li $t1 4 - div $a0 $t1 # Size of string / wordsize - mfhi $t2 # t2 holds remainder of division + sw $t0 4($sp) + sw $t1 8($sp) - bne $t2 $zero malloc_allign_size - move $t1 $a0 - j malloc_end + li $t0 4 + div $a0 $t0 # Size of string / wordsize + mfhi $t1 # t2 holds remainder of division -malloc_allign_size: - sub $t1 $t1 $t2 # Convert t1 to multiple of 4 - add $t1 $t1 $a0 + sub $t0 $t0 $t1 # Convert t1 to multiple of 4 + add $a0 $a0 $t0 -malloc_end: - move $v0 $v1 - add $v1 $t1 $v1 + li $v0 9 + syscall lw $a0 0($sp) # Return original content to registers - lw $t1 4($sp) - lw $t2 8($sp) + lw $t0 4($sp) + lw $t1 8($sp) addiu $sp $sp 12 jr $ra @@ -231,6 +225,7 @@ concat_end: jr $ra +# TODO: Change this procedure to use the new malloc read_string: addiu $sp $sp -4 sw $ra 0($sp) @@ -274,6 +269,45 @@ read_string: addiu $sp $sp 4 jr $ra +# Lee a lo sumo $a1 caracteres y los guarda en la direccion +# a la que apunta $a0, $a1 debe ser divisible por 4 +# retorna en $v0 1 si se leyo un \n +read_string_up_to: + move $t0 $a0 + move $t1 $zero + move $t2 $t0 + li $t3 10 + move $t4 $zero + move $t5 $a1 + + read_loop: + + li $v0 8 + move $a0 $t0 + li $a1 5 + syscall + addiu $t2 $t2 4 + + check_newline_loop: + + lb $t1 0($t0) + + beq $t1 $t3 read_loop_continue + addiu $t0 $t0 1 + + beq $t0 $t2 check_newline_loop_continue + j check_newline_loop + check_newline_loop_continue: + j read_loop + + read_loop_continue: + sb $zero 0($t0) + + bne $t0 $t2 null_terminated + addiu $t2 $t2 4 + null_terminated: + + From 987d2b09bfb0164666d547730fc054acedcd7630 Mon Sep 17 00:00:00 2001 From: mavaldivie Date: Mon, 21 Feb 2022 00:21:29 -0500 Subject: [PATCH 55/75] Fixed graph_input --- tests/codegen/graph_input.txt | 1 + 1 file changed, 1 insertion(+) diff --git a/tests/codegen/graph_input.txt b/tests/codegen/graph_input.txt index b67d90210..939229b0d 100644 --- a/tests/codegen/graph_input.txt +++ b/tests/codegen/graph_input.txt @@ -3,3 +3,4 @@ 3 2,10 4 3,55 5,100 5 1,1 2,2 3,3 4,4 5,5 + From 3ff82d0a3684bda94d9be9d96998663bdc5dfb05 Mon Sep 17 00:00:00 2001 From: mavaldivie Date: Mon, 21 Feb 2022 00:22:03 -0500 Subject: [PATCH 56/75] Fixed read_string procedure to use the new malloc --- src/core/mips/mips_basics.asm | 127 +++++++++++++++++++--------------- 1 file changed, 71 insertions(+), 56 deletions(-) diff --git a/src/core/mips/mips_basics.asm b/src/core/mips/mips_basics.asm index 69eee2442..cc52a3eb8 100644 --- a/src/core/mips/mips_basics.asm +++ b/src/core/mips/mips_basics.asm @@ -4,11 +4,6 @@ # Return: # $v0 address of allocated block - -# v1 address of empty memory -# a0 size to alloc -# v0 return memory address - malloc: addiu $sp $sp -12 # Save content of registers in sp sw $a0 0($sp) @@ -225,90 +220,110 @@ concat_end: jr $ra -# TODO: Change this procedure to use the new malloc +# TODO: Change this procedure to use the new malloc, +# And remove the use of the $v1 register at the end +# of the main function read_string: - addiu $sp $sp -4 + addiu $sp $sp -28 sw $ra 0($sp) + sw $t0 4($sp) + sw $t1 8($sp) + sw $a0 12($sp) + sw $a1 16($sp) + sw $a2 20($sp) + sw $t2 24($sp) - move $t0 $v1 - move $t1 $zero - move $t2 $t0 - li $t3 10 + li $t0 8 - read_loop: + addi $a0 $t0 4 + jal malloc + move $t1 $v0 + move $t2 $zero - li $v0 8 - move $a0 $t0 - li $a1 5 - syscall - addiu $t2 $t2 4 + read_string_loop: - check_newline_loop: + addu $a0 $t1 $t2 + subu $a1 $t0 $t2 + addu $t2 $t2 $a1 + jal read_string_up_to - lb $t1 0($t0) + beq $v0 $zero read_string_not_finished + move $v0 $t1 + j read_string_finished - beq $t1 $t3 read_loop_continue - addiu $t0 $t0 1 + read_string_not_finished: + sll $t0 $t0 1 - beq $t0 $t2 check_newline_loop_continue - j check_newline_loop - check_newline_loop_continue: - j read_loop + addi $a0 $t0 4 + jal malloc - read_loop_continue: - sb $zero 0($t0) + move $a0 $t1 + move $t1 $v0 + move $a1 $v0 + move $a2 $t2 + jal copy - bne $t0 $t2 null_terminated - addiu $t2 $t2 4 - null_terminated: + j read_string_loop - move $v0 $v1 - move $v1 $t2 + read_string_finished: lw $ra 0($sp) - addiu $sp $sp 4 + lw $t0 4($sp) + lw $t1 8($sp) + lw $a0 12($sp) + lw $a1 16($sp) + lw $a2 20($sp) + lw $t2 24($sp) + addiu $sp $sp 28 jr $ra # Lee a lo sumo $a1 caracteres y los guarda en la direccion # a la que apunta $a0, $a1 debe ser divisible por 4 # retorna en $v0 1 si se leyo un \n read_string_up_to: + addiu $sp $sp -28 + sw $ra 0($sp) + sw $t0 4($sp) + sw $t1 8($sp) + sw $t2 12($sp) + sw $t3 16($sp) + sw $t4 20($sp) + sw $t5 24($sp) + move $t0 $a0 move $t1 $zero - move $t2 $t0 - li $t3 10 - move $t4 $zero - move $t5 $a1 - - read_loop: + li $t2 10 + addu $t3 $t0 $a1 + addiu $a1 $a1 1 li $v0 8 - move $a0 $t0 - li $a1 5 syscall - addiu $t2 $t2 4 - check_newline_loop: + li $v0 0 + + eol_check: + beq $t0 $t3 read_loop_continue lb $t1 0($t0) + beq $t1 $t2 eol_terminated - beq $t1 $t3 read_loop_continue addiu $t0 $t0 1 + j eol_check - beq $t0 $t2 check_newline_loop_continue - j check_newline_loop - check_newline_loop_continue: - j read_loop - + eol_terminated: + sb $zero 0($t0) # put null character at the end of string + li $v0 1 read_loop_continue: - sb $zero 0($t0) - - bne $t0 $t2 null_terminated - addiu $t2 $t2 4 - null_terminated: - - + lw $ra 0($sp) + lw $t0 4($sp) + lw $t1 8($sp) + lw $t2 12($sp) + lw $t3 16($sp) + lw $t4 20($sp) + lw $t5 24($sp) + addiu $sp $sp 28 + jr $ra # EQUAL STRING From 373dc6d9910b0e8bf695141a65aef60b58648390 Mon Sep 17 00:00:00 2001 From: mavaldivie Date: Mon, 21 Feb 2022 00:23:05 -0500 Subject: [PATCH 57/75] Fixed default variable declaration nodes --- src/core/cil/CILAst.py | 29 +++++++++++--- src/core/cil/COOLToCILVisitor.py | 68 +++++++++++++++++++++----------- 2 files changed, 70 insertions(+), 27 deletions(-) diff --git a/src/core/cil/CILAst.py b/src/core/cil/CILAst.py index b60f69239..e09df143a 100644 --- a/src/core/cil/CILAst.py +++ b/src/core/cil/CILAst.py @@ -255,7 +255,8 @@ def __init__(self, data, line, column): self.data = data class VoidNode(InstructionNode): - pass + def __str__(self): + return 'VOID' class NameNode(InstructionNode): def __init__(self, dest, value, line, column): @@ -280,15 +281,24 @@ def __init__(self, name, line, column): super().__init__(line, column) self.name = name + def __str__(self): + return f'{self.name}' + class AttributeNode(VarNode): def __init__(self, name, type, line, column): super().__init__(name, line, column) self.type = type + def __str__(self): + return f'{self.type}.{self.name}' + class ParamNode(VarNode): def __init__(self, name, line, column): super().__init__(name, line, column) + def __str__(self): + return f'PARAM {self.name}' + def get_formatter(): class PrintVisitor(object): @@ -331,10 +341,6 @@ def visit(self, node): def visit(self, node): return f'LOCAL {node.name}' - @visitor.when(AttributeNode) - def visit(self, node): - return f'ATTRIBUTE {node.name}' - @visitor.when(AssignNode) def visit(self, node): return f'{node.dest} = {node.source}' @@ -483,5 +489,18 @@ def visit(self, node: VoidNode): def visit(self, node: NotNode): return f'{node.dest} = NOT {node.value}' + @visitor.when(VarNode) + def visit(self, node: VarNode): + return f'{node.name}' + + @visitor.when(AttributeNode) + def visit(self, node: AttributeNode): + return f'ATTRIBUTE {node.type}.{node.name}' + + @visitor.when(ParamNode) + def visit(self, node: ParamNode): + return f'{node.name}' + + printer = PrintVisitor() return (lambda ast: printer.visit(ast)) \ No newline at end of file diff --git a/src/core/cil/COOLToCILVisitor.py b/src/core/cil/COOLToCILVisitor.py index a5d8a73ad..d69219109 100644 --- a/src/core/cil/COOLToCILVisitor.py +++ b/src/core/cil/COOLToCILVisitor.py @@ -114,8 +114,22 @@ def visit(self, node: cool.AttrDeclarationNode, scope: Scope): self.visit(node.expression, scope.childs[0]) self.register_instruction(cil.AssignNode(variable, node.expression.ret_expr, line=node.expression.line, column=node.expression.column)) - else: - self.register_instruction(cil.AllocateNode(node.type.lex, variable, line=node.line, column=node.column)) + elif node.type.lex in self.value_types: + if node.type.lex == 'SELF_TYPE': + stype = self.current_type.name + else: + stype = node.type.lex + + if stype == 'Int': + self.register_instruction(cil.ArgNode(0, line=node.type.line, column=node.type.column)) + elif stype == 'Bool': + self.register_instruction(cil.ArgNode(0, line=node.type.line, column=node.type.column)) + elif stype == 'String': + data = self.emptystring_data + self.register_instruction(cil.LoadNode(variable, data, line=node.line, column=node.column)) + self.register_instruction(cil.ArgNode(variable, line=node.line, column=node.column)) + self.register_instruction(cil.StaticCallNode(self.init_name(stype), variable, + line=node.line, column=node.column)) node.ret_expr = variable @visitor.when(cool.FuncDeclarationNode) @@ -209,7 +223,20 @@ def visit(self, node: cool.LetInNode, scope: Scope): self.visit(expr, child) self.register_instruction(cil.AssignNode(variable, expr.ret_expr, line=expr.line, column=expr.column)) elif type.lex in self.value_types: - self.register_instruction(cil.AllocateNode(type.lex, variable, line=type.line, column=type.column)) + if type.lex == 'SELF_TYPE': + stype = self.current_type.name + else: + stype = type.lex + if stype == 'Int': + self.register_instruction(cil.ArgNode(0, line=node.line, column=node.column)) + elif stype == 'Bool': + self.register_instruction(cil.ArgNode(0, line=node.line, column=node.column)) + elif stype == 'String': + data = self.emptystring_data + self.register_instruction(cil.LoadNode(variable, data, line=node.line, column=node.column)) + self.register_instruction(cil.ArgNode(variable, line=node.line, column=node.column)) + self.register_instruction(cil.StaticCallNode(self.init_name(stype), variable, + line=node.line, column=node.column)) self.visit(node.in_body, scope.childs[-1]) node.ret_expr = node.in_body.ret_expr @@ -560,35 +587,32 @@ def visit(self, node: cool.MemberCallNode, scope: Scope): # TODO: Creo que deberia annadir los parametros al reves para luego sacarlos en el orden correcto self.register_instruction(cil.ArgNode(self.vself.name, line=node.line, column=node.column)) for arg in args: self.register_instruction(arg) + self.register_instruction(cil.ArgNode(self.vself.name, line=node.line, column=node.column)) stype = self.current_type.name - self.register_instruction(cil.StaticCallNode(self.types_map[stype].methods[node.id.lex], ret, line=node.id.line, column=node.id.column)) + self.register_instruction(cil.DynamicCallNode(stype, self.types_map[stype].methods[node.id.lex], ret, line=node.id.line, column=node.id.column)) node.ret_expr = ret @visitor.when(cool.NewNode) def visit(self, node: cool.NewNode, scope: Scope): ret = self.define_internal_local(line=node.line, column=node.column) - if node.type == 'SELF_TYPE': - variable = self.define_internal_local(line=node.line, column=node.column) - self.register_instruction(cil.TypeOfNode(ret, self.vself.name, line=node.line, column=node.column)) - # TODO: ALLOCATE a veces recibe el nombre de la clase como string, necesito cambiar este ya - # que el nombre de la clase se encuentra dentro de la variable, o cambiar los demas para - # que funcionen con self.register_instruction(cil.LoadNode(variable, data)) - self.register_instruction(cil.AllocateNode(variable, ret, line=node.line, column=node.column)) + if node.type.lex == 'SELF_TYPE': + stype = self.current_type.name else: - if node.type == 'Int': - self.register_instruction(cil.ArgNode(0, line=node.type.line, column=node.type.column)) - elif node.type == 'Bool': - self.register_instruction(cil.ArgNode(False, line=node.type.line, column=node.type.column)) - elif node.type == 'String': - data = self.emptystring_data - variable = self.define_internal_local(line=node.line, column=node.column) - self.register_instruction(cil.LoadNode(variable, data, line=node.line, column=node.column)) - self.register_instruction(cil.ArgNode(variable, line=node.line, column=node.column)) + stype = node.type.lex - self.register_instruction(cil.StaticCallNode(self.init_name(node.type.lex), ret, - line=node.type.line, column=node.type.column)) + if stype == 'Int': + self.register_instruction(cil.ArgNode(0, line=node.type.line, column=node.type.column)) + elif stype == 'Bool': + self.register_instruction(cil.ArgNode(0, line=node.type.line, column=node.type.column)) + elif stype == 'String': + data = self.emptystring_data + variable = self.define_internal_local(line=node.line, column=node.column) + self.register_instruction(cil.LoadNode(variable, data, line=node.line, column=node.column)) + self.register_instruction(cil.ArgNode(variable, line=node.line, column=node.column)) + self.register_instruction(cil.StaticCallNode(self.init_name(stype), ret, + line=node.type.line, column=node.type.column)) node.ret_expr = ret @visitor.when(cool.IdNode) From 2ca2d50ff695063b664178d0c03dd04287f92b75 Mon Sep 17 00:00:00 2001 From: mavaldivie Date: Mon, 21 Feb 2022 22:35:42 -0500 Subject: [PATCH 58/75] Fixed bug in concat procedure --- src/Main.py | 21 +-------------------- src/core/mips/CilToMipsVisitor.py | 2 -- src/core/mips/mips_basics.asm | 9 +++------ 3 files changed, 4 insertions(+), 28 deletions(-) diff --git a/src/Main.py b/src/Main.py index 7abd6ed35..a2f61207e 100644 --- a/src/Main.py +++ b/src/Main.py @@ -53,7 +53,7 @@ def main(args): CILVisitor = COOLToCILVisitor(type_Checker.Context) CILast = CILVisitor.visit(COOLast, scope) - print(get_formatter()(CILast)) + # print(get_formatter()(CILast)) MIPSVisitor = CILToMIPSVisitor() MIPSAst = MIPSVisitor.visit(CILast) @@ -69,25 +69,6 @@ def main(args): with open(out_file, 'w') as f: f.write(mipsCode) - # TODO: Comment this lines - - #try: - # fd = open(args.file, 'rb') - # sp = subprocess.run(['spim', '-file', out_file], input=fd.read(), timeout=100) - # fd.close() - # SPIM_HEADER = r'''^SPIM Version .+ of .+ - # Copyright .+\, James R\. Larus\. - # All Rights Reserved\. - # See the file README for a full copyright notice\. - # (?:Loaded: .+\n)*''' - # mo = re.match(SPIM_HEADER, sp.stdout.decode()) - # if mo: - # output = mo.string[mo.end():] - # print(output) - #except subprocess.TimeoutExpired: - # assert False, "Too Long" - - if __name__ == "__main__": import argparse diff --git a/src/core/mips/CilToMipsVisitor.py b/src/core/mips/CilToMipsVisitor.py index 2767e824c..58544a64d 100644 --- a/src/core/mips/CilToMipsVisitor.py +++ b/src/core/mips/CilToMipsVisitor.py @@ -136,8 +136,6 @@ def visit(self, node): self.register_label(instruction.label, mips_label) instructions = [] - if node.name == 'entry': - instructions.append(mips.MoveNode(mips.V1_REG, mips.GP_REG, 0, 0)) instructions.extend(mips.push_register(mips.RA_REG, node.line, node.column)) instructions.extend(mips.push_register(mips.FP_REG, line=node.line, column=node.column)) instructions.append(mips.AdditionInmediateNode(mips.FP_REG, mips.SP_REG, 8, line=node.line, column=node.column)) diff --git a/src/core/mips/mips_basics.asm b/src/core/mips/mips_basics.asm index cc52a3eb8..c3a03a19e 100644 --- a/src/core/mips/mips_basics.asm +++ b/src/core/mips/mips_basics.asm @@ -169,7 +169,7 @@ concat: sw $a1 16($sp) sw $ra 20($sp) - move $t0 $a0 # t0 pointer to string 1 + move $t0 $a0 # t0 pointer to string 1 move $t1 $a1 # t1 pointer to string 2 @@ -179,12 +179,12 @@ concat: mfhi $a0 # a0 remainder of division bne $a0 $zero concat_allign_size # Branch if size is multiple of 4 addiu $a0 $a2 1 # Add 1 t size + j concat_size_alligned concat_allign_size: sub $t2 $t2 $a0 # Convert t1 to multiple of 4 to... add $a0 $a2 $t2 # reserve memory via malloc addiu $a0 $a0 1 # Add 1 t size - j concat_size_alligned concat_size_alligned: jal malloc # a0 stores size to reserve @@ -208,7 +208,7 @@ concat_copy_second_loop: j concat_copy_second_loop concat_end: - # Return original values to regiters + # Return original values to registers sb $zero 0($t2) lw $t0 0($sp) lw $t1 4($sp) @@ -220,9 +220,6 @@ concat_end: jr $ra -# TODO: Change this procedure to use the new malloc, -# And remove the use of the $v1 register at the end -# of the main function read_string: addiu $sp $sp -28 sw $ra 0($sp) From 613e8989ef79d84ccb986d2c3c5b22a0f449d686 Mon Sep 17 00:00:00 2001 From: mavaldivie Date: Mon, 21 Feb 2022 22:39:41 -0500 Subject: [PATCH 59/75] Removed TODOs --- src/coolc.sh | 4 ++-- src/core/cil/COOLToCILVisitor.py | 6 ------ src/core/mips/CilToMipsVisitor.py | 1 - src/core/mips/MipsAst.py | 1 - 4 files changed, 2 insertions(+), 10 deletions(-) diff --git a/src/coolc.sh b/src/coolc.sh index 73bc4cb0b..cdfddb895 100755 --- a/src/coolc.sh +++ b/src/coolc.sh @@ -4,8 +4,8 @@ INPUT_FILE=$1 OUTPUT_FILE=${INPUT_FILE:0: -2}mips # Si su compilador no lo hace ya, aquí puede imprimir la información de contacto -echo "CMA COOL COMPILER v1.0" # TODO: Recuerde cambiar estas -echo "Copyright (c) 2019: Marcos Valdivie, Claudia Olavarrieta, Adrian Hernandez" # TODO: líneas a los valores correctos +echo "CMA COOL COMPILER v1.0" +echo "Copyright (c) 2019: Marcos Valdivie, Claudia Olavarrieta, Adrian Hernandez" # Llamar al compilador diff --git a/src/core/cil/COOLToCILVisitor.py b/src/core/cil/COOLToCILVisitor.py index d69219109..a6735a510 100644 --- a/src/core/cil/COOLToCILVisitor.py +++ b/src/core/cil/COOLToCILVisitor.py @@ -205,7 +205,6 @@ def visit(self, node: cool.WhileLoopNode, scope: Scope): cil.GotoNode(while_label.label, line=node.condition.line, column=node.condition.column)) self.register_instruction(pool_label) - # TODO: No estoy seguro de si deberia retornar el nodo directamente o guardarlo antes en una variable node.ret_expr = cil.VoidNode() @visitor.when(cool.BlockNode) @@ -217,7 +216,6 @@ def visit(self, node: cool.BlockNode, scope: Scope): @visitor.when(cool.LetInNode) def visit(self, node: cool.LetInNode, scope: Scope): for (id, type, expr), child in zip(node.let_body, scope.childs[:-1]): - # TODO TYPE OF ID variable = self.register_local(VariableInfo(id.lex, type.lex), line=id.line, column=id.column) if expr: self.visit(expr, child) @@ -265,7 +263,6 @@ def visit(self, node: cool.CaseOfNode, scope: Scope): labels = [] branches = sorted(node.branches, key=lambda x: self.context.get_type(x[1].lex).depth, reverse=True) for p, (id, type, expr) in enumerate(branches): - # TODO Revisar tipo de id para la linea y columna labels.append(self.register_label(f'case_label_{p}', line=id.line, column=id.column)) for t in self.context.subtree(type.lex): @@ -584,7 +581,6 @@ def visit(self, node: cool.MemberCallNode, scope: Scope): self.visit(arg, child) args.append(cil.ArgNode(arg.ret_expr, line=arg.line, column=arg.column)) - # TODO: Creo que deberia annadir los parametros al reves para luego sacarlos en el orden correcto self.register_instruction(cil.ArgNode(self.vself.name, line=node.line, column=node.column)) for arg in args: self.register_instruction(arg) self.register_instruction(cil.ArgNode(self.vself.name, line=node.line, column=node.column)) @@ -642,7 +638,6 @@ def visit(self, node: cool.StringNode, scope: Scope): def visit(self, node: cool.IntegerNode, scope: Scope): ret = self.define_internal_local(line=node.line, column=node.column) - # TODO: Hay algunos ArgNode que reciben variables y otros valores especificos self.register_instruction(cil.ArgNode(node.token.lex, line=node.token.line, column=node.token.column)) self.register_instruction(cil.StaticCallNode(self.init_name('Int'), ret, line=node.line, column=node.column)) node.ret_expr = ret @@ -651,7 +646,6 @@ def visit(self, node: cool.IntegerNode, scope: Scope): def visit(self, node: cool.BoolNode, scope: Scope): ret = self.define_internal_local(line=node.line, column=node.column) - # TODO: Hay algunos ArgNode que reciben variables y otros valores especificos self.register_instruction(cil.ArgNode(1 if node.token.lex else 0, line=node.token.line, column=node.token.column)) self.register_instruction(cil.StaticCallNode(self.init_name('Bool'), ret, line=node.line, column=node.column)) node.ret_expr = ret diff --git a/src/core/mips/CilToMipsVisitor.py b/src/core/mips/CilToMipsVisitor.py index 58544a64d..030599536 100644 --- a/src/core/mips/CilToMipsVisitor.py +++ b/src/core/mips/CilToMipsVisitor.py @@ -878,7 +878,6 @@ def visit(self, node: cil.EqualStringNode): if isinstance(node.left, cil.AttributeNode): instructions.append(mips.AdditionInmediateNode(mips.SP_REG, mips.SP_REG, 4, node.line, node.column)) - # TODO: Ver si se hace usando los registros de parametros instructions.append(mips.MoveNode(mips.ARG_REGISTERS[0], reg1, line=node.line, column=node.column)) instructions.extend(self.visit(node.right)) diff --git a/src/core/mips/MipsAst.py b/src/core/mips/MipsAst.py index f90ce8eb2..ddfe2331e 100644 --- a/src/core/mips/MipsAst.py +++ b/src/core/mips/MipsAst.py @@ -49,7 +49,6 @@ def add_instruction(self, instruction): self.instructions.append(instruction) def get_param_stack_location(self, name): - # TODO Tener en cuenta que los primeros argumentos se guardan en los registros para argumentos index = self.params.index(name) offset = ((len(self.params) - 1) - index) * DOUBLE_WORD return RegisterRelativeLocation(FP_REG, offset) From e9e7ca4ef6a4ae005ee6abe7c4be38a618a72f61 Mon Sep 17 00:00:00 2001 From: ClaudiaOM Date: Tue, 22 Feb 2022 14:22:24 -0600 Subject: [PATCH 60/75] Begin Readme --- doc/Readme.md | 105 ++++++++++++++++++++++++++++++++++++++++---------- 1 file changed, 85 insertions(+), 20 deletions(-) diff --git a/doc/Readme.md b/doc/Readme.md index 3b2569f5c..ad20d7a26 100644 --- a/doc/Readme.md +++ b/doc/Readme.md @@ -1,33 +1,98 @@ -# Documentación +# Informe de Complementos de Compilacion +## Compilador de Cool +*** -## Readme -Modifique el contenido de este documento para documentar de forma clara y concisa los siguientes aspectos: +## Autores +- Claudia Olavarrieta Martinez +- Marcos Adrian Valdivie Rodriguez +- Adrian Hernandez Perez -- Cómo ejecutar (y compilar si es necesario) su compilador. -- Requisitos adicionales, dependencias, configuración, etc. -- Opciones adicionales que tenga su compilador. +## Uso del compilador -## Sobre los Equipos de Desarrollo +## Arquitectura del compilador + +### Fases (_Pipeline_) -Para desarrollar el compilador del lenguaje COOL se trabajará en equipos de 2 o 3 integrantes. El proyecto de Compilación será recogido y evaluado únicamente a través de Github. Es imprescindible tener una cuenta de Github para cada participante, y que su proyecto esté correctamente hosteado en esta plataforma. +El fichero _Main.py_ contiene los llamados en orden de los metodos que +componen el pipeline de ejecucion del compilador +1. Lexer +2. Parser +3. Recoleccion de Tipos +4. Construccion de Tipos +5. Chequeo de Tipos +6. COOL a CIL +7. CIL a Mips -**⚠️ NOTA**: Debe completar el archivo `team.yml` con los datos correctos de cada miembro de su equipo. +#### Lexer -## Sobre los Materiales a Entregar +El Lexer es el encargado de dado un string con el código del programa COOL separar el mismo en tokens +para luego ser usado por el parser. En esta fase se utilizo el paquete _ply_ el cual permite ...breve descripcion de ply... . Se definieron +las expresiones regulares y simbolos que correspondian a los tokens de la +gramatica. Ademas se almacena por cada Token la linea y la columna correspondiente +en el codigo, lo que permite tener mayor informacion en los mensajes de error +y para nuestro uso en la depuracion. -Para la evaluación del proyecto Ud. debe entregar un informe en formato PDF (`report.pdf`) en esta carpeta, que resuma de manera organizada y comprensible la arquitectura e implementación de su compilador. -El documento no tiene límite de extensión. -En él explicará en más detalle su solución a los problemas que, durante la implementación de cada una de las fases del proceso de compilación, hayan requerido de Ud. especial atención. -## Estructura del reporte +#### Parser -Usted es libre de estructurar su reporte escrito como más conveniente le parezca. A continuación le sugerimos algunas secciones que no deberían faltar, aunque puede mezclar, renombrar y organizarlas de la manera que mejor le parezca: +#### Recoleccion de tipos -- **Uso del compilador**: detalles sobre las opciones de líneas de comando, si tiene opciones adicionales (e.j., `--ast` genera un AST en JSON, etc.). Básicamente lo mismo que pondrá en este Readme. -- **Arquitectura del compilador**: una explicación general de la arquitectura, en cuántos módulos se divide el proyecto, cuantas fases tiene, qué tipo de gramática se utiliza, y en general, como se organiza el proyecto. Una buena imagen siempre ayuda. -- **Problemas técnicos**: detalles sobre cualquier problema teórico o técnico interesante que haya necesitado resolver de forma particular. +En esta fase se recorren todas las declaraciones de clases, se crean los tipos asociados +y se valida que no se este redefiniendo una clase. -## Sobre la Fecha de Entrega -Se realizarán recogidas parciales del proyecto a lo largo del curso. En el Canal de Telegram se anunciará la fecha y requisitos de cada entrega. +### Modulos + +### Gramatica + +### Organizacion + +La estructura de archivos del proyecto es la siguiente: + + + +``` +cool-compiler-2021 +|___doc + img + src + |__Main.py + core + |__cil + |__BaseCOOLToCilVisitor.py + CILAst.py + COOLToCILVisitor.py + |__lexer + |__lexer.py + |__mips + |__CilToMipsVisitor.py + mips_basic.asm + MipsAst.py + MIPSAstFormatter.py + |__parser + |__Parser.py + |__semantic + |__Type_Builder.py + Type_Checker.py + Type_Collector.py + |__tools + |__automata.py + COOLAst.py + Errors.py + evaluation.py + First_and_Follow.py + Parser_LR1.py + parsing.py + pycompiler.py + Semantic.py + utils.py + visitor.py + + +``` + +Se omitieron algunos archivos pues no son relevantes en la implementacion del compilador. + +## Principales Problemas + From 79f204757701b1b768ef99605363589737bb3593 Mon Sep 17 00:00:00 2001 From: ClaudiaOM Date: Tue, 22 Feb 2022 21:02:01 -0600 Subject: [PATCH 61/75] Fix read end of file in read_str mips_code --- src/core/mips/mips_basics.asm | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/core/mips/mips_basics.asm b/src/core/mips/mips_basics.asm index cc52a3eb8..d589238f1 100644 --- a/src/core/mips/mips_basics.asm +++ b/src/core/mips/mips_basics.asm @@ -298,6 +298,8 @@ read_string_up_to: li $v0 8 syscall + lw $a0 0($a0) + beq $a0 $zero eol_terminated li $v0 0 From 64d8640ca855180617297414c73fbe7680c31592 Mon Sep 17 00:00:00 2001 From: mavaldivie Date: Tue, 22 Feb 2022 23:38:41 -0500 Subject: [PATCH 62/75] Merge branch 'Marcos' of /home/adrian/PycharmProjects/cool-compiler-2021 with conflicts. --- src/Tools/CodeGen/Mips/mips _syntax.py | 371 ++++++++++++++++ src/Tools/Lexer/Lexer.py | 272 ++++++++++++ src/Tools/Parser/Parser.py | 138 ++++++ src/Tools/Semantic/Type_Builder.py | 157 +++++++ src/Tools/Semantic/Type_Checker.py | 509 ++++++++++++++++++++++ src/Tools/Semantic/Type_Collector.py | 38 ++ src/Tools/Tools/COOLAst.py | 210 ++++++++++ src/Tools/Tools/Errors.py | 66 +++ src/Tools/Tools/Firsts_and_Follows.py | 128 ++++++ src/Tools/Tools/Parser_LR1.py | 185 ++++++++ src/Tools/Tools/Semantic.py | 288 +++++++++++++ src/Tools/Tools/automata.py | 210 ++++++++++ src/Tools/Tools/evaluation.py | 44 ++ src/Tools/Tools/parsing.py | 95 +++++ src/Tools/Tools/pycompiler.py | 560 +++++++++++++++++++++++++ src/Tools/Tools/utils.py | 221 ++++++++++ src/Tools/Tools/visitor.py | 80 ++++ src/Tools/format_visitor.py | 116 +++++ 18 files changed, 3688 insertions(+) create mode 100644 src/Tools/CodeGen/Mips/mips _syntax.py create mode 100644 src/Tools/Lexer/Lexer.py create mode 100644 src/Tools/Parser/Parser.py create mode 100644 src/Tools/Semantic/Type_Builder.py create mode 100644 src/Tools/Semantic/Type_Checker.py create mode 100644 src/Tools/Semantic/Type_Collector.py create mode 100644 src/Tools/Tools/COOLAst.py create mode 100644 src/Tools/Tools/Errors.py create mode 100644 src/Tools/Tools/Firsts_and_Follows.py create mode 100644 src/Tools/Tools/Parser_LR1.py create mode 100644 src/Tools/Tools/Semantic.py create mode 100644 src/Tools/Tools/automata.py create mode 100644 src/Tools/Tools/evaluation.py create mode 100644 src/Tools/Tools/parsing.py create mode 100644 src/Tools/Tools/pycompiler.py create mode 100644 src/Tools/Tools/utils.py create mode 100644 src/Tools/Tools/visitor.py create mode 100644 src/Tools/format_visitor.py diff --git a/src/Tools/CodeGen/Mips/mips _syntax.py b/src/Tools/CodeGen/Mips/mips _syntax.py new file mode 100644 index 000000000..d0b7bd83c --- /dev/null +++ b/src/Tools/CodeGen/Mips/mips _syntax.py @@ -0,0 +1,371 @@ +from enum import Enum +from typing import List, Optional + +DATA_SIZE = 4 + + +class Register(str, Enum): + zero = "$zero" #constant 0 + # used for system calls and procedure return values + v0 = "$v0" + v1 = "$v1" + + # used for passing arguments to procedures,temporary (not preserved across call) + a0 = "$a0" + a1 = "$a1" + a2 = "$a2" + a3 = "$a3" + + # used for local storage; calling procedure saves these,saved temporary (preserved across call) + t0 = "$t0" + t1 = "$t1" + t2 = "$t2" + t3 = "$t3" + t4 = "$t4" + t5 = "$t5" + t6 = "$t6" + t7 = "$t7" + t8 = "$t8" + t9 = "$t9" + + # used for local storage; called procedure saves these + s0 = "$s0" + s1 = "$s1" + s2 = "$s2" + s3 = "$s3" + s4 = "$s4" + s5 = "$s5" + s6 = "$s6" + s7 = "$s7" + sp = "$sp" # stack pointer + fp = "$fp" # frame pointer + ra = "$ra" # used to store return address in procedure call + gp = "$gp" # pointer to area storing global data (data segment) + at = "$at" # reserved for use by the assembler + + # reserved for use by OS kernel + k0 = "$k0" + k1 = "$k1" + + +class Directive(str, Enum): + word = ".word" + half = ".half" + byte = ".byte" + ascii = ".ascii" + asciiz = ".asciiz" + space = ".space" + aling = ".align" + text = ".text" + data = ".data" + + +Reg = Register + +# autowrite a instruction when execute string return function +def autowrite(tabs: int = 1): + def inner(func): + + def wrapper(self: "Mips", *args, **kwargs): + instruction = func(self, *args, **kwargs) + if instruction is not None: + self.write_inst(instruction, tabs) + + return wrapper + + return inner + + +def autowritedata(func): + + + def inner(*args, **kwargs): + instructions = func(*args, **kwargs) + if instructions is list: + for instruction in instructions: + args[0].write_data(instruction) + else: + args[0].write_data(instructions) + + return inner + + +class Mips: + def __init__(self): + self.DOTTEXT: List[str] = [] + self.DOTDATA: List[str] = [] + + + def write_inst(self, instruction: str, tabs: int = 0): + tabstr = "" + for _ in range(tabs): + tabstr += "\t" + self.DOTTEXT.append(f"{tabstr}{instruction}") + + def write_data(self, data: str, tabs: int = 0): + self.DOTDATA.append(f"{data}") + + def compile(self): + return "\n".join( + [Directive.data.value] + + self.DOTDATA + + [] + + [Directive.text.value] + + self.DOTTEXT + ) + + def push(self, register: Register): + self.addi(Reg.sp, Reg.sp, -DATA_SIZE) + self.store_memory(register, self.offset(Reg.sp)) + +# First, load from to address `0($sp)` and then write `addi $sp , $sp , 8` to restore the stack pointer + def pop(self, register: Optional[Register]): + + if register: + self.load_memory(register, self.offset(Reg.sp)) + self.addi(Reg.sp, Reg.sp, DATA_SIZE) + +#Load from a specific address a 32 bits register + def load_memory(self, dst: Register, address: str): + self.lw(dst, address) + + +# Write to a specific address a 32 bits register + def store_memory(self, src: Register, address: str): + self.sw(src, address) + + # Arithmetics + @autowrite() + def add(self, dst: Register, rl: Register, rr: Register): + return f"add {dst}, {rl}, {rr}" + + @autowrite() + def sub(self, dst: Register, rl: Register, rr: Register): + return f"sub {dst}, {rl}, {rr}" + + @autowrite() + def addi(self, dst: Register, rl: Register, value: int): + return f"addi {dst}, {rl}, {int(value)}" + + @autowrite() + def addu(self, dst: Register, rl: Register, rr: Register): + return f"addu {dst}, {rl}, {rr}" + + @autowrite() + def subu(self, dst: Register, rl: Register, rr: Register): + return f"subu {dst}, {rl}, {rr}" + + @autowrite() + def addiu(self, dst: Register, rl: Register, value: int): + return f"addiu {dst}, {rl}, {int(value)}" + + @autowrite() + def multu(self, dst: Register, rl: Register, rr: Register): + return f"multu {dst}, {rl}, {rr}" + + @autowrite() + def mult(self, rl: Register, rr: Register): + return f"mult {rl}, {rr}" + + @autowrite() + def div(self, rl: Register, rr: Register): + return f"div {rl}, {rr}" + + # Logical: + @autowrite() + def andd(self, dst: Register, rl: Register, rr: Register): + return f"and {dst}, {rl}, {rr}" + + @autowrite() + def orr(self, dst: Register, rl: Register, rr: Register): + return f"or {dst}, {rl}, {rr}" + + @autowrite() + def nor(self, dst: Register, rl: Register, rr: Register): + return f"nor {dst}, {rl}, {rr}" + + @autowrite() + def andi(self, dst: Register, rl: Register, value: int): + return f"andi {dst}, {rl}, {int(value)}" + + # TODO: Check this instrucction + @autowrite() + def ori(self, dst: Register, rl: Register, value: int): + return f"or {dst}, {rl}, {int(value)}" + + @autowrite() + def sll(self, dst: Register, rl: Register, value: int): + return f"sll {dst}, {rl}, {int(value)}" + + @autowrite() + def srl(self, dst: Register, rl: Register, value: int): + return f"srl {dst}, {rl}, {int(value)}" + + # DataTransfer: + @autowrite() + def lw(self, dst: Register, address: str): + return f"lw {dst}, {address}" + + @autowrite() + def lb(self, dst: Register, address: str): + return f"lb {dst}, {address}" + + @autowrite() + def sw(self, dst: Register, address: str): + return f"sw {dst}, {address}" + + @autowrite() + def sb(self, dst: Register, address: str): + return f"sb {dst}, {address}" + + @autowrite() + def lui(self, dst: Register, value: int): + return f"lui {dst}, {int(value)}" + + @autowrite() + def la(self, dst: Register, label: str): + return f"la {dst}, {label}" + + @autowrite() + def li(self, dst: Register, value: int): + return f"li {dst}, {int(value)}" + + @autowrite() + def mfhi(self, dst: Register): + return f"mfhi {dst}" + + @autowrite() + def mflo(self, dst: Register): + return f"mflo {dst}" + + @autowrite() + def move(self, dst: Register, rl: Register): + return f"move {dst}, {rl}" + + # Brancing + @autowrite() + def beq(self, rl: Register, rr: Register, address: str): + return f"beq {rl}, {rr}, {address}" + + @autowrite() + def bne(self, rl: Register, rr: Register, address: str): + return f"bne {rl}, {rr}, {address}" + + @autowrite() + def beqz(self, rl: Register, address: str): + return f"beqz {rl}, {address}" + + @autowrite() + def bgt(self, rl: Register, rr: Register, address: str): + return f"bgt {rl}, {rr}, {address}" + + @autowrite() + def bge(self, rl: Register, rr: Register, address: str): + return f"bge {rl}, {rr}, {address}" + + @autowrite() + def blt(self, rl: Register, rr: Register, address: str): + return f"blt {rl}, {rr}, {address}" + + @autowrite() + def ble(self, rl: Register, rr: Register, address: str): + return f"ble {rl}, {rr}, {address}" + + # Comparison + @autowrite() + def slt(self, dest: Register, rl: Register, rr: Register): + return f"slt {dest}, {rl}, {rr}" + + @autowrite() + def slti(self, dest: Register, rl: Register, value: int): + return f"slt {dest}, {rl}, {int(value)}" + + # Unconditional Jump + + @autowrite() + def j(self, address: str): + return f"j {address}" + + @autowrite() + def jr(self, r: Register): + return f"jr {r}" + + @autowrite() + def jal(self, address: str): + return f"jal {address}" + + @autowrite() + def jalr(self, dest: Register): + return f"jalr {dest}" + + # System Calls + @autowrite() + def syscall(self, code: int): + self.li(Register.v0, code) + return "syscall" + + def print_int(self): + return self.syscall(1) + + def print_string(self): + return self.syscall(4) + + def read_int(self): + return self.syscall(5) + + def read_string(self): + return self.syscall(8) + + def sbrk(self): + return self.syscall(9) + + def exit(self): + return self.syscall(10) + + def print_char(self): + return self.syscall(11) + + def read_char(self): + return self.syscall(12) + + def exit2(self): + return self.syscall(17) + + + + def offset(self, r: Register, offset: int = 0): + return f"{int(offset)}({r})" + + @autowrite() + def comment(self, text: str): + return f"# {text}" + + @autowrite(0) + def label(self, name: str): + return f"{name}:" + + @autowrite() + def empty(self): + return "" + + # Data Section + + @autowrite() + def data_empty(self): + return "" + + @autowritedata + def data_label(self, name: str): + return f"{name}:" + + @autowritedata + def ascii(self, string: str): + return f'{Directive.ascii} "{string}"' + + @autowritedata + def asciiz(self, string: str): + return f'{Directive.asciiz} "{string}"' + + @autowritedata + def data_comment(self, text: str): + return f"#{text}" diff --git a/src/Tools/Lexer/Lexer.py b/src/Tools/Lexer/Lexer.py new file mode 100644 index 000000000..dcd995edb --- /dev/null +++ b/src/Tools/Lexer/Lexer.py @@ -0,0 +1,272 @@ +import ply.lex as lex +from Tools.Tools.utils import Token +from Tools.Parser.Parser import CoolGrammar +from Tools.Tools.Errors import LexicographicError + +class Lexer: + states = ( + ('comment', 'exclusive'), + ('string', 'exclusive') + ) + + # Palabras reservadas del lenguaje COOL + reserved = { + 'class': 'CLASS', + 'inherits': 'INHERITS', + 'function': 'FUNCTION', + 'if': 'IF', + 'then': 'THEN', + 'else': 'ELSE', + 'fi': 'FI', + 'while': 'WHILE', + 'loop': 'LOOP', + 'pool': 'POOL', + 'let': 'LET', + 'in': 'IN', + 'case': 'CASE', + 'of': 'OF', + 'esac': 'ESAC', + 'new': 'NEW', + 'isvoid': 'ISVOID' + } + + t_ignore = ' \f\r\t\v' + t_comment_ignore = '' + t_string_ignore = '' + + tokens = [ + # Identifiers + 'TYPE', 'ID', + # Primitive data types + 'INTEGER', 'STRING', 'BOOL', + # Special keywords + 'ACTION', + # Operators + 'ASSIGN', 'LESS', 'LESSEQUAL', 'EQUAL', 'INT_COMPLEMENT', 'NOT', + # Literals + 'PLUS', 'MINUS', 'STAR', 'DIVIDE', 'COLON', 'SEMICOLON', + 'OPAR', 'CPAR', 'OCUR', 'CCUR', 'AT', 'DOT', 'COMMA', + ] + list(reserved.values()) + + tokens_dict = {} + for tok in tokens: + try: + tokens_dict[tok] = CoolGrammar[tok.lower()].Name + except: + pass + + tokens_dict['ACTION'] = CoolGrammar['=>'].Name + tokens_dict['ASSIGN'] = CoolGrammar['<-'].Name + tokens_dict['LESS'] = CoolGrammar['<'].Name + tokens_dict['LESSEQUAL'] = CoolGrammar['<='].Name + tokens_dict['EQUAL'] = CoolGrammar['='].Name + tokens_dict['INT_COMPLEMENT'] = CoolGrammar['~'].Name + + tokens_dict['PLUS'] = CoolGrammar['+'].Name + tokens_dict['MINUS'] = CoolGrammar['-'].Name + tokens_dict['STAR'] = CoolGrammar['*'].Name + tokens_dict['DIVIDE'] = CoolGrammar['/'].Name + tokens_dict['COLON'] = CoolGrammar[':'].Name + tokens_dict['SEMICOLON'] = CoolGrammar[';'].Name + tokens_dict['OPAR'] = CoolGrammar['('].Name + tokens_dict['CPAR'] = CoolGrammar[')'].Name + tokens_dict['OCUR'] = CoolGrammar['{'].Name + tokens_dict['CCUR'] = CoolGrammar['}'].Name + tokens_dict['AT'] = CoolGrammar['@'].Name + tokens_dict['DOT'] = CoolGrammar['.'].Name + tokens_dict['COMMA'] = CoolGrammar[','].Name + + + def __init__(self): + self.lexer = lex.lex(module=self) + self.comment_level = 0 + self.code = '' + self.current_string = '' + self.errors = [] + + + # Expresiones regulares + + def t_INTEGER(self, t): + r'[0-9]+' + t.value = int(t.value) + return t + + def t_BOOL(self, t): + r't[rR][uU][eE]|f[aA][lL][sS][eE]' + t.value = True if t.value == 'true' else False + return t + + + # Other tokens with precedence before TYPE and ID + + def t_NOT(self, t): + r'[nN][oO][tT]' + return t + + # Identifiers + + def t_TYPE(self, t): + r'[A-Z][A-Za-z0-9_]*' + + try: + t.type = self.reserved[t.value.lower()] + except KeyError: + pass + + return t + + def t_ID(self, t): + r'[a-z][A-Za-z0-9_]*' + + try: + t.type = self.reserved[t.value.lower()] + except KeyError: + pass + + return t + + + t_ASSIGN = r'<-' + t_LESS = r'<' + t_LESSEQUAL = r'<=' + t_EQUAL = r'=' + t_INT_COMPLEMENT = r'~' + t_ACTION = r'=>' + + t_PLUS = r'\+' + t_MINUS = r'-' + t_STAR = r'\*' + t_DIVIDE = r'/' + t_COLON = r':' + t_SEMICOLON = r';' + t_OPAR = r'\(' + t_CPAR = r'\)' + t_OCUR = r'{' + t_CCUR = r'}' + t_AT = r'@' + t_DOT = r'\.' + t_COMMA = r',' + + #################### + ##### COMMENTS ##### + #################### + def t_LINECOMMENT(self, t): + r'--.*' + pass + + def t_COMMENTBEGIN(self, t): + r'\(\*' + self.comment_level += 1 + t.lexer.begin('comment') + + def t_comment_COMMENTBEGIN(self, t): + r'\(\*' + self.comment_level += 1 + + def t_comment_COMMENTEND(self, t): + r'\*\)' + self.comment_level -= 1 + if self.comment_level == 0: + t.lexer.begin('INITIAL') + + def t_comment_eof(self, t): + self.errors.append(LexicographicError(t.lineno, + self.find_column(t), 'EOF in comment')) + self.lexer.begin('INITIAL') + + def t_comment_error(self, t): + t.lexer.skip(1) + + ############################ + ##### STRING CONSTANTS ##### + ############################ + + def t_STRINGBEGIN(self, t): + r'"' + self.current_string = '' + self.lexer.begin('string') + + def t_string_NULL(self, t): + r'\0' + self.errors.append(LexicographicError(t.lineno, + self.find_column(t), 'Null caracter in string')) + self.lexer.begin('INITIAL') + + def t_string_NEWLINE(self, t): + r'\\\n' + self.current_string += '\n' + t.lexer.lineno += 1 + + def t_string_INVALID_NEWLINE(self, t): + r'\n' + t.lexer.lineno += 1 + self.errors.append(LexicographicError(t.lineno, + self.find_column(t), 'Unterminated string constant')) + self.lexer.begin('INITIAL') + + def t_string_SCAPED_SPECIAL_CHARACTER(self, t): + r'\\(b|t|f)' + self.current_string += t.value + + def t_string_SCAPED_CHARACTER(self, t): + r'\\.' + self.current_string += t.value[1] + + def t_string_eof(self, t): + self.errors.append(LexicographicError(t.lineno, + self.find_column(t), 'EOF in string constant')) + self.lexer.begin('INITIAL') + + def t_string_STRINGEND(self, t): + r'"' + t.value = self.current_string + t.type = 'STRING' + self.lexer.begin('INITIAL') + return t + + def t_string_CHARACTER(self, t): + r'.' + self.current_string += t.value + + def t_string_error(self, t): + return t + + ########################### + ###### SPECIAL RULES ###### + ########################### + + def t_ANY_newline(self, t): + r'\n+' + t.lexer.lineno += len(t.value) + + def find_column(self, token): + line_start = self.code.rfind('\n', 0, token.lexpos) + 1 + return (token.lexpos - line_start) + 1 \ + + 3 * len([i for i in self.code[line_start:token.lexpos] if i == '\t']) + + def t_error(self, t): + self.errors.append(LexicographicError(t.lineno, + self.find_column(t), f'ERROR "{t.value[0]}"')) + t.lexer.skip(1) + + + ''' + Dado un string retorna el arreglo de tokens resultante de analizar dicho string + ''' + def tokenize(self, code): + tokens = [] + self.code = code + + self.lexer.input(code) + while True: + token = self.lexer.token() + if token is None: + break + + tokens.append(Token(token.value, self.tokens_dict[token.type], + token.lineno, self.find_column(token))) + + tokens.append(Token('$', CoolGrammar.EOF.Name)) + + return tokens diff --git a/src/Tools/Parser/Parser.py b/src/Tools/Parser/Parser.py new file mode 100644 index 000000000..cb779829b --- /dev/null +++ b/src/Tools/Parser/Parser.py @@ -0,0 +1,138 @@ +from Tools.Tools.pycompiler import Grammar +from Tools.Tools.Parser_LR1 import LR1Parser +from Tools.Tools.COOLAst import * + + +# Representacion de la gramatica de COOL utilizando la clase grammar +CoolGrammar = Grammar() + +# noterminales +program = CoolGrammar.NonTerminal('', startSymbol=True) +class_list, def_class = CoolGrammar.NonTerminals(' ') +feature_list, feature = CoolGrammar.NonTerminals(' ') +param_list, param = CoolGrammar.NonTerminals(' ') +expr_1, expr_2, member_call, expr_list, let_list, case_list = CoolGrammar.NonTerminals( + ' ') +comp_expr, arith, arith_2, term, factor, factor_2 = CoolGrammar.NonTerminals( + ' ') +atom, func_call, arg_list = CoolGrammar.NonTerminals(' ') + +# terminales +classx, inherits, function = CoolGrammar.Terminals('class inherits function') +ifx, then, elsex, fi = CoolGrammar.Terminals('if then else fi') +whilex, loop, pool = CoolGrammar.Terminals('while loop pool') +let, inx = CoolGrammar.Terminals('let in') +case, of, esac = CoolGrammar.Terminals('case of esac') +semi, colon, comma, dot, at, opar, cpar, ocur, ccur, larrow, rarrow = CoolGrammar.Terminals( + '; : , . @ ( ) { } <- =>') +plus, minus, star, div, isvoid, compl = CoolGrammar.Terminals('+ - * / isvoid ~') +notx, less, leq, equal = CoolGrammar.Terminals('not < <= =') +new, idx, typex, integer, string, boolx = CoolGrammar.Terminals('new id type integer string bool') + +# Producciones +program %= class_list, lambda h, s: ProgramNode(s[1]) + +# Lista de clases +class_list %= def_class + class_list, lambda h, s: [s[1]] + s[2] +class_list %= def_class, lambda h, s: [s[1]] + +# Defincicion de la clase +def_class %= classx + typex + ocur + feature_list + ccur + semi, lambda h, s: ClassDeclarationNode(s[1], s[2], s[4]) +def_class %= classx + typex + inherits + typex + ocur + feature_list + ccur + semi, lambda h, s: ClassDeclarationNode( + s[1], s[2], s[6], s[4]) + +# Lista de propiedades de la clase +feature_list %= feature + feature_list, lambda h, s: [s[1]] + s[2] +feature_list %= CoolGrammar.Epsilon, lambda h, s: [] + +# Atributos de la clase +feature %= idx + colon + typex + semi, lambda h, s: AttrDeclarationNode(s[1], s[3]) +feature %= idx + colon + typex + larrow + expr_1 + semi, lambda h, s: AttrDeclarationNode(s[1], s[3], s[5]) + +# Metodos constructores de la clase +feature %= idx + opar + param_list + cpar + colon + typex + ocur + expr_1 + ccur + semi, lambda h, s: FuncDeclarationNode( + s[1], s[3], s[6], s[8]) +feature %= idx + opar + cpar + colon + typex + ocur + expr_1 + ccur + semi, lambda h, s: FuncDeclarationNode(s[1], [], + s[5], s[7]) +# Metodos de la clase +feature %= function + idx + opar + param_list + cpar + colon + typex + ocur + expr_1 + ccur + semi, lambda h, s: FuncDeclarationNode( + s[2], s[4], s[7], s[9]) +feature %= function + idx + opar + cpar + colon + typex + ocur + expr_1 + ccur + semi, lambda h, s: FuncDeclarationNode(s[2], [], + s[6], s[8]) +# Lista de parametros de funcion +param_list %= param, lambda h, s: [s[1]] +param_list %= param + comma + param_list, lambda h, s: [s[1]] + s[3] + +# parametro de funcion +param %= idx + colon + typex, lambda h, s: (s[1], s[3]) + +### Expresiones ### +# Expresion Let-in +expr_1 %= let + let_list + inx + expr_1, lambda h, s: LetInNode(s[1], s[2], s[4]) +let_list %= idx + colon + typex, lambda h, s: [(s[1], s[3], None)] +let_list %= idx + colon + typex + larrow + expr_1, lambda h, s: [(s[1], s[3], s[5])] +let_list %= idx + colon + typex + comma + let_list, lambda h, s: [(s[1], s[3], None)] + s[5] +let_list %= idx + colon + typex + larrow + expr_1 + comma + let_list, lambda h, s: [(s[1], s[3], s[5])] + s[7] + +expr_1 %= idx + larrow + expr_1, lambda h, s: AssignNode(s[1], s[3]) +expr_1 %= notx + expr_1, lambda h, s: NotNode(s[1], s[2]) +expr_1 %= expr_2 + equal + expr_1, lambda h, s: EqualNode(s[1], s[2], s[3]) +expr_1 %= expr_2, lambda h, s: s[1] + +expr_2 %= arith + less + arith, lambda h, s: LessNode(s[1], s[2], s[3]) +expr_2 %= arith + leq + arith, lambda h, s: LessEqualNode(s[1], s[2], s[3]) +expr_2 %= arith, lambda h, s: s[1] + +#Expresiones aritmeticas +arith %= arith + plus + factor, lambda h, s: PlusNode(s[1], s[2], s[3]) +arith %= arith + minus + factor, lambda h, s: MinusNode(s[1], s[2], s[3]) +arith %= factor, lambda h, s: s[1] + +factor %= factor + star + atom, lambda h, s: StarNode(s[1], s[2], s[3]) +factor %= factor + div + atom, lambda h, s: DivNode(s[1], s[2], s[3]) +factor %= term, lambda h, s: s[1] + +term %= compl + term, lambda h, s: ComplementNode(s[1], s[2]) +term %= isvoid + term, lambda h, s: IsVoidNode(s[1], s[2]) +term %= atom, lambda h, s: s[1] + +# Encapsulaciones atomicas +atom %= opar + expr_1 + cpar, lambda h, s: s[2] +atom %= integer, lambda h, s: IntegerNode(s[1]) +atom %= string, lambda h, s: StringNode(s[1]) +atom %= boolx, lambda h, s: BoolNode(s[1]) +atom %= idx, lambda h, s: IdNode(s[1]) +atom %= ifx + expr_1 + then + expr_1 + elsex + expr_1 + fi, lambda h, s: IfThenElseNode(s[1], s[2], s[4], s[6]) +atom %= whilex + expr_1 + loop + expr_1 + pool, lambda h, s: WhileLoopNode(s[1], s[2], s[4]) + +# Expresion new +atom %= new + typex, lambda h, s: NewNode(s[1], s[2]) + +# Encapsulamiento entre corchetes +atom %= ocur + expr_list + ccur, lambda h, s: BlockNode(s[1], s[2]) +expr_list %= expr_1 + semi, lambda h, s: [s[1]] +expr_list %= expr_1 + semi + expr_list, lambda h, s: [s[1]] + s[3] + +# Expresion Case of +atom %= case + expr_1 + of + case_list + esac, lambda h, s: CaseOfNode(s[1], s[2], s[4]) +case_list %= idx + colon + typex + rarrow + expr_1 + semi, lambda h, s: [(s[1], s[3], s[5])] +case_list %= idx + colon + typex + rarrow + expr_1 + semi + case_list, lambda h, s: [(s[1], s[3], s[5])] + s[7] + +atom %= func_call, lambda h, s: s[1] + +# Llamado a funcion +func_call %= atom + at + typex + dot + idx + opar + arg_list + cpar, lambda h, s: FunctionCallNode(s[1], s[5], s[7], s[3]) +func_call %= atom + at + typex + dot + idx + opar + cpar, lambda h, s: FunctionCallNode(s[1], s[5], [], s[3]) +func_call %= atom + dot + idx + opar + arg_list + cpar, lambda h, s: FunctionCallNode(s[1], s[3], s[5]) +func_call %= atom + dot + idx + opar + cpar, lambda h, s: FunctionCallNode(s[1], s[3], []) + +# Llamado a miembro de clase +func_call %= idx + opar + arg_list + cpar, lambda h, s: MemberCallNode(s[1], s[3]) +func_call %= idx + opar + cpar, lambda h, s: MemberCallNode(s[1], []) + +# Lista de argumentos +arg_list %= expr_1, lambda h, s: [s[1]] +arg_list %= expr_1 + comma + arg_list, lambda h, s: [s[1]] + s[3] + +# parser +CoolParser = LR1Parser(CoolGrammar) diff --git a/src/Tools/Semantic/Type_Builder.py b/src/Tools/Semantic/Type_Builder.py new file mode 100644 index 000000000..be9425bf0 --- /dev/null +++ b/src/Tools/Semantic/Type_Builder.py @@ -0,0 +1,157 @@ +from Tools.Tools import visitor +from Tools.Tools.Semantic import * +from Tools.Parser.Parser import ProgramNode, ClassDeclarationNode, AttrDeclarationNode, FuncDeclarationNode +from Tools.Tools.Errors import * + +# Visitor encargado de contruir los tipos. Una vez que se conocen los nombres +# de los tipos que intervienen en el codifo COOL, este visitor les annade sus +# metodos y atributos, asi como el tipo padre. + +class Type_Builder: + def __init__(self, Context : Context): + self.Context = Context + self.Current_Type = None + + self.errors = [] + + # Construye los tipos builtin + self.Object_Type = self.Context.get_type('Object') + + self.IO_Type = self.Context.get_type('IO') + self.IO_Type.set_parent(self.Object_Type) + + self.Int_Type = self.Context.get_type('Int') + self.Int_Type.set_parent(self.Object_Type) + self.Int_Type.sealed = True + + self.String_Type = self.Context.get_type('String') + self.String_Type.set_parent(self.Object_Type) + self.String_Type.sealed = True + + self.Bool_Type = self.Context.get_type('Bool') + self.Bool_Type.set_parent(self.Object_Type) + self.Bool_Type.sealed = True + + self.IO_Type.define_method('out_string', ['x'], [self.String_Type], SelfType(), 0, 0) + self.IO_Type.define_method('out_int', ['x'], [self.Int_Type], SelfType(), 0, 0) + self.IO_Type.define_method('in_int', [], [], self.Int_Type, 0, 0) + self.IO_Type.define_method('in_string', [], [], self.String_Type, 0, 0) + + self.Object_Type.define_method('abort', [], [], self.Object_Type, 0, 0) + self.Object_Type.define_method('type_name', [], [], self.String_Type, 0, 0) + self.Object_Type.define_method('copy', [], [], SelfType(), 0, 0) + + self.String_Type.define_method('length', [], [], self.Int_Type, 0, 0) + self.String_Type.define_method('concat', ['x'], [self.String_Type], self.String_Type, 0, 0) + self.String_Type.define_method('substr', ['l', 'r'], [self.Int_Type, self.Int_Type], self.String_Type, 0, 0) + + @visitor.on('node') + def visit(self, node): + pass + + @visitor.when(ProgramNode) + def visit(self, node : ProgramNode): + for type in node.declarations: + self.visit(type) + + try: + self.Context.get_type('Main').get_method('main') + except SemanticException: + # Cada programa COOL debe tener una clase MAIN + self.errors.append(SemanticError(0, 0, + f'Class Main and its method main must be defined')) + + @visitor.when(ClassDeclarationNode) + def visit(self, node : ClassDeclarationNode): + self.Current_Type = self.Context.get_type(node.id.lex) + + if node.parent: + try: + parent_type = self.Context.get_type(node.parent.lex) + except SemanticException as ex: + self.errors.append(TypeError(node.parent.line, node.parent.column, + f'Class {node.id} inherits from an undefined class {node.parent.lex}')) + parent_type = self.Object_Type + + try: + self.Current_Type.set_parent(parent_type) + except SemanticException as ex: + self.errors.append(SemanticError(node.parent.line, node.parent.column, + f'Class {node.id.lex} cannot inherit class {parent_type.name}')) + + parent = self.Current_Type.parent + # Revisa que no haya herencia ciclica + while parent: + if parent == self.Current_Type: + self.errors.append(SemanticError(node.line, node.column, + f'Class {node.id.lex}, or an ancestor of {node.id.lex}, ' + f'is involved in an inheritance cycle')) + self.Current_Type.parent = self.Object_Type + break + parent = parent.parent + + else: + self.Current_Type.set_parent(self.Object_Type) + + + for feat in node.features: + self.visit(feat) + + @visitor.when(AttrDeclarationNode) + def visit(self, node : AttrDeclarationNode): + try: + attr_type = self.Context.get_type(node.type.lex) + except SemanticException as ex: + # Existio un error al tratar de obtener el tipo del atributo + self.errors.append(TypeError(node.type.line, node.type.column, ex.text)) + attr_type = ErrorType() + + try: + self.Current_Type.define_attribute(node.id.lex, attr_type, node.line, node.column) + except SemanticException as ex: + # Existio un error al tratar de definir el atributo + self.errors.append(SemanticError(node.line, node.column, ex.text)) + + @visitor.when(FuncDeclarationNode) + def visit(self, node : FuncDeclarationNode): + param_names, param_types = [], [] + + for name, type in node.params: + try: + type = self.Context.get_type(type.lex) + except SemanticException as ex: + # Existio un error al tratar de obtener el tipo del parametro + self.errors.append(TypeError(type.line, type.column, + f'Class {type.lex} of formal parameter {name.lex} is undefined')) + type = ErrorType() + else: + if isinstance(type, SelfType): + self.errors.append(SemanticError(name.line, name.column, + f'\'self\' cannot be the name of a formal parameter')) + arg_type = ErrorType() + + if name.lex in param_names: + self.errors.append(SemanticError(name.line, name.column, + f'Formal parameter {name.lex} is multiply defined')) + + param_names.append(name.lex) + param_types.append(type) + + + + try: + return_type = self.Context.get_type(node.type.lex) + except SemanticException as ex: + # Existio un error al tratar de obtener el tipo del parametro de retorno + self.errors.append(TypeError(node.type.line, node.type.column, + f'Undefined return type {node.type.lex} in method {node.id.lex}')) + return_type = ErrorType() + + if return_type is SelfType: + return_type = self.Current_Type + + try: + self.Current_Type.define_method(node.id.lex, param_names, param_types, return_type, node.line, node.column) + except SemanticException as ex: + # Existio un error al tratar de definir el metodo + self.errors.append(SemanticError(node.line, node.column, ex.text)) diff --git a/src/Tools/Semantic/Type_Checker.py b/src/Tools/Semantic/Type_Checker.py new file mode 100644 index 000000000..ac366b40c --- /dev/null +++ b/src/Tools/Semantic/Type_Checker.py @@ -0,0 +1,509 @@ +from Tools.Tools import visitor +from Tools.Parser.Parser import ProgramNode, ClassDeclarationNode, AttrDeclarationNode, FuncDeclarationNode,\ + IfThenElseNode, WhileLoopNode, BlockNode, LetInNode, CaseOfNode,\ + AssignNode, LessEqualNode, LessNode, EqualNode, ArithmeticNode,\ + NotNode, IsVoidNode, ComplementNode, FunctionCallNode, MemberCallNode, NewNode,\ + IntegerNode, IdNode, StringNode, BoolNode +from Tools.Tools.Semantic import Context, Scope, SelfType, SemanticException, ErrorType +from Tools.Tools.Errors import * + +# Este es el visitor encargado de terminar el chequeo semantico. +# Revisa la compatibilidad de tipos, la compatibilidad en la herencia, +# que las variables hayan sido previamente definidas, asi como los +# metodos y atributos de clase, crea el scope para las variables, el +# cual sera rehusado para inferir las variables que se requieran. +# Observar que cada vez que el visitor llama recursivamente crea un scope +# hijo en el scope actual, esto se hace para que las variables previamente +# declaradas en ambitos hermanos no sean utiles en el ambito actual. + +class Type_Checker: + + def __init__(self, Context : Context): + self.Context = Context + self.errors = [] + self.Current_Type = None + self.Current_Method = None + + self.Object_Type = self.Context.get_type('Object') + self.IO_Type = self.Context.get_type('IO') + self.String_Type = self.Context.get_type('String') + self.Int_Type = self.Context.get_type('Int') + self.Bool_Type = self.Context.get_type('Bool') + + self.builtin_Types = [ + self.Object_Type, + self.IO_Type, + self.String_Type, + self.Int_Type, + self.Bool_Type + ] + + @visitor.on('node') + def visit(self, node, scope): + pass + + @visitor.when(ProgramNode) + def visit(self, node : ProgramNode, scope : Scope = None): + scope = Scope() + for declaration in node.declarations: + self.visit(declaration, scope.create_child()) + return scope + + @visitor.when(ClassDeclarationNode) + def visit(self, node : ClassDeclarationNode, scope : Scope): + self.Current_Type = self.Context.get_type(node.id.lex) + + # Incluyo cada uno de los atributos de los padres en su resoectivo orden + current = self.Current_Type + attributtes = [] + while current.parent: + current = current.parent + for att in reversed(current.attributes): + attributtes.append(att) + + for att in reversed(attributtes): + scope.define_variable(att.name, att.type, att.line, att.column) + + for att in self.Current_Type.attributes: + if scope.is_defined(att.name): + self.errors.append(SemanticError(att.line, att.column, + f'Attribute {att.name} is an attribute of an inherited class')) + scope.define_variable(att.name, att.type, att.line, att.column) + + for feature in node.features: + self.visit(feature, scope.create_child()) + node.static_type = self.Current_Type + + @visitor.when(AttrDeclarationNode) + def visit(self, node : AttrDeclarationNode, scope : Scope): + expr = node.expression + attr = self.Current_Type.get_attribute(node.id.lex) + node_type = self.Current_Type if attr.type is SelfType else attr.type + + if expr: + self.visit(expr, scope.create_child()) + expr_type = expr.static_type + + # Chequeo compatibilidad de tipos + if not expr_type.conforms_to(node_type): + self.errors.append(TypeError(node.expression.line, node.expression.column, + f'Inferred type {expr_type.name} of initialization of attribute {attr.name} ' + f'does not conform to declared type {node_type.name}')) + + if attr.name.lower() == 'self': + self.errors.append(SemanticError(node.line, node.column, + '\'self\' cannot be the name of an attribute')) + + node.static_type = node_type + + @visitor.when(FuncDeclarationNode) + def visit(self, node : FuncDeclarationNode, scope : Scope): + self.Current_Method = self.Current_Type.get_method(node.id.lex) + + if self.Current_Type.parent: + try: + inherited_method = self.Current_Type.parent.get_method(node.id.lex) + + if len(self.Current_Method.param_names) != len(inherited_method.param_names): + self.errors.append(SemanticError(node.line, node.column, + f'Incompatible number of formal parameters in redefined method {self.Current_Method.name}')) + else: + for par1, par2, p in zip(self.Current_Method.param_types, inherited_method.param_types, node.params): + if par1.name != par2.name: + self.errors.append(SemanticError(p[0].line, p[0].column, + f'In redefined method {self.Current_Method.name}, parameter type {par1.name} ' + f'is different from original type {par2.name}')) + + if self.Current_Method.return_type.name != inherited_method.return_type.name: + self.errors.append(SemanticError(node.line, node.column, + f'In redefined method {self.Current_Method.name}, return type {self.Current_Method.return_type.name} ' + f'is different from original return type {inherited_method.return_type.name}')) + + except SemanticException: + pass + + scope.define_variable('self', self.Current_Type, node.line, node.column) + + # Defino cada uno de los parametros de metodo + for pname, ptype in zip(self.Current_Method.param_names, self.Current_Method.param_types): + scope.define_variable(pname, ptype, node.line, node.column) + + if pname.lower() == 'self': + self.errors.append(SemanticError(node.line, node.column, + '\'self\' cannot be the name of a formal parameter')) + + # Chequeo consistencia en el cuerpo del metodo + self.visit(node.body, scope.create_child()) + + expr_type = node.body.static_type + return_type = self.Current_Method.return_type + + # Chequeo consistencia entre el tipo de retorno definido y el tipo de retorno + # del cuerpo del metodo + if not expr_type.conforms_to(return_type): + self.errors.append(TypeError(node.line, node.column, + f'Inferred return type {expr_type.name} of method {self.Current_Method.name} ' + f'does not conform to declared return type {return_type.name}')) + node.static_type = return_type + + @visitor.when(IfThenElseNode) + def visit(self, node : IfThenElseNode, scope : Scope): + # Chequeo consistencia en la condicion del if + self.visit(node.condition, scope.create_child()) + + condition_type = node.condition.static_type + # Chequeo que el tipo de la condicion sea booleano + if not condition_type.conforms_to(self.Bool_Type): + self.errors.append(TypeError(node.condition.line, node.condition.column, + 'Predicate of \'if\' does not have type Bool')) + + # Chequeo consistencia en las expresiones del then y el else + self.visit(node.if_body, scope.create_child()) + self.visit(node.else_body, scope.create_child()) + + if_type = node.if_body.static_type + else_type = node.else_body.static_type + + node.static_type = if_type.type_union(else_type) + + @visitor.when(WhileLoopNode) + def visit(self, node : WhileLoopNode, scope : Scope): + self.visit(node.condition, scope.create_child()) + condition_type = node.condition.static_type + + # Chequeo que la condicion sea de tipo booleano + if not condition_type.conforms_to(self.Bool_Type): + self.errors.append(TypeError(node.condition.line, node.condition.column, + 'Loop condition does not have type Bool')) + + # Chequeo consistencias en el cuerpo del while + self.visit(node.body, scope.create_child()) + + node.static_type = self.Object_Type + + @visitor.when(BlockNode) + def visit(self, node : BlockNode, scope : Scope): + + # Chequeo consistencias en cada una de las instrucciones del cuerpo del bloque + for expr in node.expressions: + self.visit(expr, scope.create_child()) + + node.static_type = node.expressions[-1].static_type + + @visitor.when(LetInNode) + def visit(self, node : LetInNode, scope : Scope): + for id, type, expr in node.let_body: + + if id.lex.lower() == 'self': + self.errors.append(SemanticError(id.line, id.column, + '\'self\' cannot be bound in a \'let\' expression')) + + # Por cada una de las declaraciones del let + try: + type = self.Context.get_type(type.lex) + except SemanticException as ex: + # Chequeo que el tipo exista + self.errors.append(TypeError(id.line, id.column, + f'Class {type.lex} of let-bound identifier {id.lex} is undefined')) + type = ErrorType() + + # Si es Self_Type tomo el tipo correspondiente + type = self.Current_Type if isinstance(type, SelfType) else type + + child = scope.create_child() + if expr: + # Chequeo consistencias en la declaracion y la compatibilidad de tipos + self.visit(expr, child) + if not expr.static_type.conforms_to(type): + self.errors.append(TypeError(id.line, id.column, + f'Inferred type {expr.static_type.name} of initialization of ' + f'{id.lex} does not conform to identifier\'s declared type {type.name}')) + + # Defino la variable + scope.define_variable(id.lex, type, node.line, node.column) + + # Chequeo consistencias en el cuerpo del let in + self.visit(node.in_body, scope.create_child()) + node.static_type = node.in_body.static_type + + @visitor.when(CaseOfNode) + def visit(self, node : CaseOfNode, scope : Scope): + # Chequeo consistencias en el case + self.visit(node.expression, scope.create_child()) + + branchs = [] + + node.static_type = None + for id, type, expr in node.branches: + # Por cada instruccion en el cuerpo del case-of + try: + type = self.Context.get_type(type.lex) + except SemanticException as ex: + # Chequeo que el tipo exista + self.errors.append(TypeError(type.line, type.column, + f'Class {type.lex} of case branch is undefined')) + type = ErrorType() + + # Chequeo que no sea un tipo especial + if isinstance(type, SelfType): + self.errors.append(SemanticError(id.line, id.column, + f'SELF_TYPE cannot be used as a case branch')) + + child = scope.create_child() + # Declaro la variable y chequeo consistencias en la expresion + child.define_variable(id.lex, type, node.line, node.column) + self.visit(expr, child) + + if type.name in branchs: + self.errors.append(SemanticError(id.line, id.column, + f'Duplicate branch {type.name} in case statement')) + branchs.append(type.name) + + node.static_type = node.static_type.type_union(expr.static_type) if node.static_type else expr.static_type + + + @visitor.when(AssignNode) + def visit(self, node : AssignNode, scope : Scope): + # Chequeo consistencias en la expresion + self.visit(node.expression, scope.create_child()) + expr_type = node.expression.static_type + + if node.id.lex.lower() == 'self': + self.errors.append(SemanticError(node.line, node.column, + 'Cannot assign to \'self\'')) + + # Chequeo que la variable este declarada y que su tipo sea valido + if scope.is_defined(node.id.lex): + var_type = scope.find_variable(node.id.lex).type + if isinstance(var_type, SelfType): + var_type = self.Current_Type + + if not expr_type.conforms_to(var_type): + self.errors.append(TypeError(node.expression.line, node.expression.column, + f'Inferred type {expr_type.name} of initialization of attribute {node.id.lex} ' + f'does not conform to declared type {var_type.name}')) + else: + self.errors.append(NameError(node.line, node.column, + f'Undeclared identifier {node.id.lex}')) + + node.static_type = expr_type + + @visitor.when(NotNode) + def visit(self, node : NotNode, scope : Scope): + # Chequeo la consistencia de la expresion + self.visit(node.expression, scope.create_child()) + + # Chequeo que la expresion sea booleana + if not node.expression.static_type.conforms_to(self.Bool_Type): + self.errors.append(TypeError(node.expression.line, node.expression.column, + f'Argument of \'not\' has type {node.expression.static_type.name} instead of Bool')) + + node.static_type = self.Bool_Type + + @visitor.when(LessEqualNode) + def visit(self, node : LessEqualNode, scope : Scope): + # Chequeo la consistencia de ambos miembros + self.visit(node.left, scope.create_child()) + self.visit(node.right, scope.create_child()) + left_type = node.left.static_type + right_type = node.right.static_type + + # Chequeo que ambos miembros posean tipo int + if not left_type.conforms_to(self.Int_Type) or not right_type.conforms_to(self.Int_Type): + self.errors.append(TypeError(node.line, node.column, + f'non-Int arguments: {left_type.name} <= {right_type.name}')) + + node.static_type = self.Bool_Type + + @visitor.when(LessNode) + def visit(self, node: LessNode, scope: Scope): + # Chequeo la consistencia de ambos miembros + self.visit(node.left, scope.create_child()) + self.visit(node.right, scope.create_child()) + left_type = node.left.static_type + right_type = node.right.static_type + + # Chequeo que ambos miembros posean tipo int + if not left_type.conforms_to(self.Int_Type) or not right_type.conforms_to(self.Int_Type): + self.errors.append(TypeError(node.line, node.column, + f'non-Int arguments: {left_type.name} < {right_type.name}')) + + node.static_type = self.Bool_Type + + @visitor.when(EqualNode) + def visit(self, node: EqualNode, scope: Scope): + # Chequeo la consistencia de ambos miembros + self.visit(node.left, scope.create_child()) + self.visit(node.right, scope.create_child()) + left_type = node.left.static_type + right_type = node.right.static_type + + # Chequeo que ambos miembros posean tipos comparables + if left_type.conforms_to(self.Int_Type) ^ right_type.conforms_to(self.Int_Type): + self.errors.append(TypeError(node.line, node.column, + f'Illegal comparison with a basic type')) + elif left_type.conforms_to(self.String_Type) ^ right_type.conforms_to(self.String_Type): + self.errors.append(TypeError(node.line, node.column, + f'Illegal comparison with a basic type')) + elif left_type.conforms_to(self.Bool_Type) ^ right_type.conforms_to(self.Bool_Type): + self.errors.append(TypeError(node.line, node.column, + f'Illegal comparison with a basic type')) + + node.static_type = self.Bool_Type + + @visitor.when(ArithmeticNode) + def visit(self, node : ArithmeticNode, scope : Scope): + # Chequeo la consistencia de ambos miembros + self.visit(node.left, scope.create_child()) + self.visit(node.right, scope.create_child()) + left_type = node.left.static_type + right_type = node.right.static_type + + # Chequeo que ambos miembros posean tipo int + if not left_type.conforms_to(self.Int_Type) or not right_type.conforms_to(self.Int_Type): + self.errors.append(TypeError(node.line, node.column, + f'non-Int arguments: {left_type.name} and {right_type.name}')) + + node.static_type = self.Int_Type + + @visitor.when(IsVoidNode) + def visit(self, node : IsVoidNode, scope : Scope): + # Chequeo la consistencia de la expresion + self.visit(node.expression, scope.create_child()) + node.static_type = self.Bool_Type + + @visitor.when(ComplementNode) + def visit(self, node : ComplementNode, scope : Scope): + # Chequeo la consistencia de la expresion + self.visit(node.expression, scope.create_child()) + + # Chequeo que la expresion sea de tipo booleana + if not node.expression.static_type.conforms_to(self.Int_Type): + self.errors.append(TypeError(node.expression.line, node.expression.column, + f'Argument of \'~\' has type {node.expression.static_type.name} instead of Int')) + + node.static_type = self.Int_Type + + @visitor.when(FunctionCallNode) + def visit(self, node : FunctionCallNode, scope : Scope): + # Chequeo la consistencia de la expresion a la cual se le pide la funcion + self.visit(node.obj, scope.create_child()) + obj_type = node.obj.static_type + + try: + if node.type: + # Chequeo que el tipo exista + try: + node_type = self.Context.get_type(node.type.lex) + except SemanticException as ex: + self.errors.append(TypeError(node.line, node.column, + f'Class {node.type.lex} not defined')) + node_type = ErrorType() + + # Chequeo que el tipo no sea un tipo especial + if isinstance(node_type, SelfType): + self.errors.append(TypeError(node.line, node.column, + 'SELF_TYPE cannot be used in a dispatch')) + + # Chequeo que los tipos sean compatibles + if not obj_type.conforms_to(node_type): + self.errors.append(TypeError(node.line, node.column, + f'Expression type {obj_type.name} does not conform ' + f'to declared static dispatch type {node_type.name}')) + + obj_type = node_type + + + obj_method = obj_type.get_method(node.id.lex) + return_type = obj_type if isinstance(obj_method.return_type, SelfType) else obj_method.return_type + + except SemanticException as ex: + self.errors.append(AttributeError(node.id.line, node.id.column, + f'Dispatch to undefined method {node.id.lex}')) + return_type = ErrorType() + obj_method = None + + # Chequeo consistencias en los argumentos con los que se llama al metodo + for arg in node.args: + self.visit(arg, scope.create_child()) + + if obj_method and len(node.args) == len(obj_method.param_types): + for arg, param_type, param_name in zip(node.args, obj_method.param_types, obj_method.param_names): + if not arg.static_type.conforms_to(param_type): + # Chequeo compatibilidad de tipos entre los argumentos + self.errors.append(TypeError(arg.line, arg.column, + f'In call of method {obj_method.name}, type {arg.static_type.name} of ' + f'parameter {param_name} does not conform to declared type {param_type.name}')) + + elif obj_method: + # Chequeo que la cantidad de argumentos sea igual a las solicitadas por el metodo + self.errors.append(SemanticError(node.id.line, node.id.column, + f'Method {obj_method.name} called with wrong number of arguments')) + + node.static_type = return_type + + + @visitor.when(MemberCallNode) + def visit(self, node : MemberCallNode, scope : Scope): + # Chequeo que el metodo exista en el tipo actual + try: + obj_method = self.Current_Type.get_method(node.id.lex) + return_type = self.Current_Type if isinstance(obj_method.return_type, SelfType) else obj_method.return_type + except SemanticException as ex: + self.errors.append(AttributeError(node.id.line, node.id.column, + f'Dispatch to undefined method {node.id.lex}')) + obj_method = None + return_type = ErrorType() + + # Chequeo la consistencia en los argumentos + for arg in node.args: + self.visit(arg, scope.create_child()) + + if obj_method and len(node.args) == len(obj_method.param_types): + # Chequeo la compatibiidad entre los tipos de los argumentos + for arg, param_type, param_name in zip(node.args, obj_method.param_types, obj_method.param_names): + if not arg.static_type.conforms_to(param_type): + self.errors.append(TypeError(arg.line, arg.column, + f'In call of method {obj_method.name}, type {arg.static_type.name} of ' + f'parameter {param_name} does not conform to declared type {param_type.name}')) + + elif obj_method: + # Chequeo que la cantidad de argumentos coincida con los que requiere el metodo + self.errors.append(SemanticError(node.id.line, node.id.column, + f'Method {obj_method.name} called with wrong number of arguments')) + + node.static_type = return_type + + @visitor.when(NewNode) + def visit(self, node : NewNode, scope : Scope): + # Chequeo que el tipo exista + try: + type = self.Context.get_type(node.type.lex) + except SemanticException as ex: + self.errors.append(TypeError(node.type.line, node.type.column, + f'\'new\' used with undeclared class {node.type.lex}')) + type = ErrorType() + + node.static_type = type + + @visitor.when(IntegerNode) + def visit(self, node : IntegerNode, scope : Scope): + node.static_type = self.Int_Type + + @visitor.when(StringNode) + def visit(self, node: StringNode, scope: Scope): + node.static_type = self.String_Type + + @visitor.when(BoolNode) + def visit(self, node: BoolNode, scope: Scope): + node.static_type = self.Bool_Type + + @visitor.when(IdNode) + def visit(self, node: IntegerNode, scope: Scope): + # Chequeo que la variable exista + if scope.is_defined(node.token.lex): + node.static_type = scope.find_variable(node.token.lex).type + else: + self.errors.append(NameError(node.line, node.column, + f'Undeclared identifier {node.token.lex}')) + node.static_type = ErrorType() diff --git a/src/Tools/Semantic/Type_Collector.py b/src/Tools/Semantic/Type_Collector.py new file mode 100644 index 000000000..ccaaf0671 --- /dev/null +++ b/src/Tools/Semantic/Type_Collector.py @@ -0,0 +1,38 @@ +from Tools.Tools import visitor +from Tools.Tools.Semantic import * +from Tools.Parser.Parser import ProgramNode, ClassDeclarationNode +from Tools.Tools.Errors import SemanticError + +# Visitor encargado de coleccionar los nombres de las clases que se definen +# en el codigo del programa COOL, chequea ademas que no se redeclaren e +# incluye los tipos builtin dentro del contexto + +class Type_Collector: + def __init__(self): + self.errors = [] + + self.Context = Context() + + self.Context.add_type(SelfType()) + + self.Context.create_type('Object') + self.Context.create_type('String') + self.Context.create_type('IO') + self.Context.create_type('Int') + self.Context.create_type('Bool') + + @visitor.on('node') + def visit(self, node): + pass + + @visitor.when(ProgramNode) + def visit(self, node : ProgramNode): + for type in node.declarations: + self.visit(type) + + @visitor.when(ClassDeclarationNode) + def visit(self, node : ClassDeclarationNode): + try: + self.Context.create_type(node.id.lex) + except SemanticException as ex: + self.errors.append(SemanticError(node.line, node.column, ex.text)) diff --git a/src/Tools/Tools/COOLAst.py b/src/Tools/Tools/COOLAst.py new file mode 100644 index 000000000..4efb93764 --- /dev/null +++ b/src/Tools/Tools/COOLAst.py @@ -0,0 +1,210 @@ +# Clases necesarias para representar el AST del programa COOL +class Node: + pass + +# Raiz del AST +class ProgramNode(Node): + def __init__(self, declarations): + self.declarations = declarations + self.line = declarations[0].line + self.column = declarations[0].column + + +class DeclarationNode(Node): + pass + + +class ClassDeclarationNode(DeclarationNode): + def __init__(self, classx, idx, features, parent=None): + self.id = idx + self.parent = parent + self.features = features + self.line = classx.line + self.column = classx.column + + +class AttrDeclarationNode(DeclarationNode): + def __init__(self, idx, typex, expression=None): + self.id = idx + self.type = typex + self.expression = expression + self.line = idx.line + self.column = idx.column + + +class FuncDeclarationNode(DeclarationNode): + def __init__(self, idx, params, return_type, body): + self.id = idx + self.params = params + self.type = return_type + self.body = body + self.line = idx.line + self.column = idx.column + + +class ExpressionNode(Node): + pass + + +class IfThenElseNode(ExpressionNode): + def __init__(self, ifx, condition, if_body, else_body): + self.condition = condition + self.if_body = if_body + self.else_body = else_body + self.line = ifx.line + self.column = ifx.column + + +class WhileLoopNode(ExpressionNode): + def __init__(self, whilex, condition, body): + self.condition = condition + self.body = body + self.line = whilex.line + self.column = whilex.column + + +class BlockNode(ExpressionNode): + def __init__(self, brace, expressions): + self.expressions = expressions + self.line = brace.line + self.column = brace.column + + +class LetInNode(ExpressionNode): + def __init__(self, let, let_body, in_body): + self.let_body = let_body + self.in_body = in_body + self.line = let.line + self.column = let.column + + +class CaseOfNode(ExpressionNode): + def __init__(self, case, expression, branches): + self.expression = expression + self.branches = branches + self.line = case.line + self.column = case.column + + +class AssignNode(ExpressionNode): + def __init__(self, idx, expression): + self.id = idx + self.expression = expression + self.line = idx.line + self.column = idx.column + + +class UnaryNode(ExpressionNode): + def __init__(self, expression): + self.expression = expression + self.line = expression.line + self.column = expression.column + + +class NotNode(UnaryNode): + def __init__(self, notx, expression): + super().__init__(expression) + self.line = notx.line + self.column = notx.column + + +class BinaryNode(ExpressionNode): + def __init__(self, left, operator, right): + self.left = left + self.right = right + self.line = operator.line + self.column = operator.column + + +class LessEqualNode(BinaryNode): + pass + + +class LessNode(BinaryNode): + pass + + +class EqualNode(BinaryNode): + pass + + +class ArithmeticNode(BinaryNode): + pass + + +class PlusNode(ArithmeticNode): + pass + + +class MinusNode(ArithmeticNode): + pass + + +class StarNode(ArithmeticNode): + pass + + +class DivNode(ArithmeticNode): + pass + + +class IsVoidNode(UnaryNode): + def __init__(self, isvoid, expression): + super().__init__(expression) + self.line = isvoid.line + self.column = isvoid.column + + +class ComplementNode(UnaryNode): + def __init__(self, complement, expression): + super().__init__(expression) + self.line = complement.line + self.column = complement.column + + +class FunctionCallNode(ExpressionNode): + def __init__(self, obj, idx, args, typex=None): + self.obj = obj + self.id = idx + self.args = args + self.type = typex + self.line = obj.line + self.column = obj.column + + +class MemberCallNode(ExpressionNode): + def __init__(self, idx, args): + self.id = idx + self.args = args + self.line = idx.line + self.column = idx.column + + +class NewNode(ExpressionNode): + def __init__(self, new, typex): + self.type = typex + self.line = new.line + self.column = new.column + + +class AtomicNode(ExpressionNode): + def __init__(self, token): + self.token = token + self.line = token.line + self.column = token.column + + +class IntegerNode(AtomicNode): + pass + + +class IdNode(AtomicNode): + pass + + +class StringNode(AtomicNode): + pass + + +class BoolNode(AtomicNode): + pass diff --git a/src/Tools/Tools/Errors.py b/src/Tools/Tools/Errors.py new file mode 100644 index 000000000..4d1b8e0f1 --- /dev/null +++ b/src/Tools/Tools/Errors.py @@ -0,0 +1,66 @@ + +class Error: + def __init__(self, line = None, column = None, text = ''): + self.line = line + self.column = column + self.text = text + def __str__(self): + raise NotImplementedError() + def __repr__(self): + raise NotImplementedError() + +class CompilerError(Error): + def __str__(self): + return f'{self.line, self.column} - ' \ + f'CompilerError: {self.text}' + def __repr__(self): + return f'{self.line, self.column} - ' \ + f'CompilerError: {self.text}' + +class LexicographicError(Error): + def __str__(self): + return f'{self.line, self.column} - ' \ + f'LexicographicError: {self.text}' + def __repr__(self): + return f'{self.line, self.column} - ' \ + f'LexicographicError: {self.text}' + +class SyntacticError(Error): + def __str__(self): + return f'{self.line, self.column} - ' \ + f'SyntacticError: ERROR at or near {self.text}' + def __repr__(self): + return f'{self.line, self.column} - ' \ + f'SyntacticError: ERROR at or near {self.text}' + +class SemanticError(Error): + def __str__(self): + return f'{self.line, self.column} - ' \ + f'SemanticError: {self.text}' + def __repr__(self): + return f'{self.line, self.column} - ' \ + f'SemanticError: {self.text}' + +class TypeError(Error): + def __str__(self): + return f'{self.line, self.column} - ' \ + f'TypeError: {self.text}' + def __repr__(self): + return f'{self.line, self.column} - ' \ + f'TypeError: {self.text}' + +class NameError(Error): + def __str__(self): + return f'{self.line, self.column} - ' \ + f'NameError: {self.text}' + def __repr__(self): + return f'{self.line, self.column} - ' \ + f'NameError: {self.text}' + +class AttributeError(Error): + def __str__(self): + return f'{self.line, self.column} - ' \ + f'AttributeError: {self.text}' + def __repr__(self): + return f'{self.line, self.column} - ' \ + f'AttributeError: {self.text}' \ No newline at end of file diff --git a/src/Tools/Tools/Firsts_and_Follows.py b/src/Tools/Tools/Firsts_and_Follows.py new file mode 100644 index 000000000..029b33aa0 --- /dev/null +++ b/src/Tools/Tools/Firsts_and_Follows.py @@ -0,0 +1,128 @@ +from .utils import ContainerSet + +''' +Dada una forma oracional alpha computa sus firsts +''' +def compute_local_first(firsts, alpha): + first_alpha = ContainerSet() + + try: + alpha_is_epsilon = alpha.IsEpsilon + except: + alpha_is_epsilon = False + + # alpha == epsilon ? First(alpha) = { epsilon } + if alpha_is_epsilon: + first_alpha.set_epsilon() + return first_alpha + + # alpha = X1 ... XN + # First(Xi) subconjunto First(alpha) + # epsilon pertenece a First(X1)...First(Xi) ? First(Xi+1) subconjunto de First(X) y First(alpha) + # epsilon pertenece a First(X1)...First(XN) ? epsilon pertence a First(X) y al First(alpha) + for symbol in alpha: + first_alpha.update(firsts[symbol]) + if not firsts[symbol].contains_epsilon: + break + else: + first_alpha.set_epsilon() + + return first_alpha + + +''' +Computa los firsts de todos los simbolos de la gramatica +''' +def compute_firsts(G): + firsts = {} + change = True + + # Los firsts de los terminales son ellos mismos + for terminal in G.terminals: + firsts[terminal] = ContainerSet(terminal) + + # Inicializa los firsts de los noterminales como un conjunto vacio + for nonterminal in G.nonTerminals: + firsts[nonterminal] = ContainerSet() + + # Metodo de punto fijo, mientras halla algun cambio en los firsts + # de algun simbolo, itera por todas las producciones recalculando los firsts + # del noterminal correspondiente + while change: + change = False + + # P: X -> alpha + for production in G.Productions: + X = production.Left + alpha = production.Right + + # get current First(X) + first_X = firsts[X] + + # init First(alpha) + try: + first_alpha = firsts[alpha] + except KeyError: + first_alpha = firsts[alpha] = ContainerSet() + + # CurrentFirst(alpha) + local_first = compute_local_first(firsts, alpha) + + # update First(X) and First(alpha) from CurrentFirst(alpha) + change |= first_alpha.hard_update(local_first) + change |= first_X.hard_update(local_first) + + return firsts + +''' +Computa los follows de cada noterminal de la gramatica +''' +from itertools import islice +def compute_follows(G, firsts): + follows = {} + change = True + + local_firsts = {} + + # Inicializa los follows de los noterminales como un conjunto vacio + for nonterminal in G.nonTerminals: + follows[nonterminal] = ContainerSet() + # EOF pertenece a los follows del simbolo inicial + follows[G.startSymbol] = ContainerSet(G.EOF) + + # Metodo de punto fijo, mientras haya cambios en los follows + # de algun noterminal, itera por todas las producciones y recalcula + # los follows de cada noterminal + while change: + change = False + + # P: X -> alpha + for production in G.Productions: + X = production.Left + alpha = production.Right + + follow_X = follows[X] + + # Si la produccion actual es de la forma X -> v Y w + # donde v y w son formas oracionales entonces cada elemento + # que pertenece al first de w (excepto epsilon) pertenece tambien al follow + # de Y. Si ademas w ->* epsilon entonces los follows de X pertenecen + # al follows de Y. + # X -> zeta Y beta + # First(beta) - { epsilon } subset of Follow(Y) + # beta ->* epsilon or X -> zeta Y ? Follow(X) subset of Follow(Y) + for i, symbol in enumerate(alpha): + if symbol.IsNonTerminal: + try: + first_beta = local_firsts[alpha, i] + except KeyError: + first_beta = local_firsts[alpha, i] = compute_local_first(firsts, islice(alpha, i + 1, None)) + + change |= follows[symbol].update(first_beta) + + if first_beta.contains_epsilon: + change |= follows[symbol].update(follow_X) + + return follows + + diff --git a/src/Tools/Tools/Parser_LR1.py b/src/Tools/Tools/Parser_LR1.py new file mode 100644 index 000000000..f12ba3162 --- /dev/null +++ b/src/Tools/Tools/Parser_LR1.py @@ -0,0 +1,185 @@ +from .pycompiler import Item +from .utils import ContainerSet +from .Firsts_and_Follows import compute_firsts, compute_local_first +from .automata import State +from .parsing import ShiftReduceParser + +''' +Recibe un item LR(1) y devuelve el conjunto de items que +sugiere incluir (directamente) debido a la presencia de +un . delante de un no terminal. +expand("𝑌→𝛼.𝑋𝛿,𝑐") = { "𝑋→.𝛽,𝑏" | 𝑏∈𝐹𝑖𝑟𝑠𝑡(𝛿𝑐) } +''' +def expand(item, firsts): + next_symbol = item.NextSymbol + if next_symbol is None or not next_symbol.IsNonTerminal: + return [] + + lookaheads = ContainerSet() + # (Compute lookahead for child items) + # Preview retorna los elementos que se encuantran detras del punto + # en el item mas sus lookaheads + for prev in item.Preview(): + lookaheads.update(compute_local_first(firsts, prev)) + + assert not lookaheads.contains_epsilon + + # (Build and return child items) + return [Item(x, 0, lookaheads) for x in next_symbol.productions] + +''' +Recibe un conjunto de items LR(1) y devuelve el mismo conjunto pero +en el que los items con mismo centro están unidos (se combinan los lookahead). +''' +def compress(items): + centers = {} + + for item in items: + center = item.Center() + try: + lookaheads = centers[center] + except KeyError: + centers[center] = lookaheads = set() + lookaheads.update(item.lookaheads) + + return {Item(x.production, x.pos, set(lookahead)) for x, lookahead in centers.items()} + +''' +Computa la clausura de un conjunto de items +''' +# Metodo de punto fijo, mientras haya cambios en el conjunto de items, +# itero por todos los items X -> v.Yw,p, donde X y Y son noterminales y +# v y w son formas oracionales, e incluyo los items de la forma +# Y -> Z,b donde b ∈ First(wp), para todas las producciones de Y. +# Dischos items se calculan mediantes el metodo expand. +# 𝐶𝐿(𝐼) = 𝐼 ∪ { 𝑋→.𝛽,𝑏 } +# tales que 𝑌→𝛼.𝑋𝛿,𝑐 ∈ 𝐶𝐿(𝐼) y 𝑏 ∈ 𝐹𝑖𝑟𝑠𝑡(𝛿𝑐) + +def closure_lr1(items, firsts): + closure = ContainerSet(*items) + + changed = True + while changed: + changed = False + + new_items = ContainerSet() + for item in closure: + new_items.extend(expand(item, firsts)) + + changed = closure.update(new_items) + + return compress(closure) + +''' +Recibe como parámetro un conjunto de items y un símbolo, y devuelve el +conjunto goto(items, symbol). El método permite setear el parámentro +just_kernel=True para calcular solamente el conjunto de items kernels +en lugar de todo el conjunto de items. En caso contrario, se debe proveer +el conjunto con los firsts de la gramática, puesto que serán usados al +calcular la clausura. +''' +# 𝐺𝑜𝑡𝑜(𝐼,𝑋) = 𝐶𝐿({ 𝑌→𝛼𝑋.𝛽,𝑐 | 𝑌→𝛼.𝑋𝛽,𝑐 ∈ 𝐼}) +def goto_lr1(items, symbol, firsts=None, just_kernel=False): + assert just_kernel or firsts is not None, '`firsts` must be provided if `just_kernel=False`' + items = frozenset(item.NextItem() for item in items if item.NextSymbol == symbol) + return items if just_kernel else closure_lr1(items, firsts) + +''' +Computa el automata LR1 correspondiente a la gramatica +''' +# El estado inicial es la clausura del item S' -> .S, $. +# Todos los estados son finales. +# Las transiciones ocurren con terminales y no terminales. +# La función de transición está dada por la función goto. +# f(Ii, c) = Goto(Ii, c) +def build_LR1_automaton(G): + assert len(G.startSymbol.productions) == 1, 'Grammar must be augmented' + + firsts = compute_firsts(G) + firsts[G.EOF] = ContainerSet(G.EOF) + + start_production = G.startSymbol.productions[0] + start_item = Item(start_production, 0, lookaheads=(G.EOF,)) + start = frozenset([start_item]) + + # El estado inicial es la clausura del item S' -> .S, $ + closure = closure_lr1(start, firsts) + automaton = State(frozenset(closure), True) + + pending = [start] + visited = {start: automaton} + + # BFS para construir el automata + # Mientras hallan estados pendientes + while pending: + # Tomo el siguiente estado a analizar + current = pending.pop() + current_state = visited[current] + + # Itero por cada simbolo de la gramatica + for symbol in G.terminals + G.nonTerminals: + # Chequeo si el estado actual posee transicion con ese simbolo a algun estado x + next_state_key = goto_lr1(current_state.state, symbol, just_kernel=True) + + # Si no la posee, continuo al siguiente simbolo + if not next_state_key: + continue + try: + next_state = visited[next_state_key] + except KeyError: + # Si el estado x no ha sido visto por el bfs, lo incluyo + # en la lista de pending + next_state_items = goto_lr1(current_state.state, symbol, firsts) + next_state = State(frozenset(next_state_items), True) + pending.append(next_state_key) + visited[next_state_key] = next_state + + # incluto la transicion del estado actual a x con el simbolo actual + current_state.add_transition(symbol.Name, next_state) + + #automaton.set_formatter(multiline_formatter) + return automaton + + +# Recordar que la diferencia entre los parsers Shift-Reduce es solo la forma +# en que se llenan las tablas ACTION y GOTO +class LR1Parser(ShiftReduceParser): + def _build_parsing_table(self): + G = self.G.AugmentedGrammar(True) + + automaton = build_LR1_automaton(G) + for i, node in enumerate(automaton): + node.idx = i + + for node in automaton: + idx = node.idx + for item in node.state: + # - Fill `self.Action` and `self.Goto` according to `item`) + # - Feel free to use `self._register(...)`) + if item.IsReduceItem: + if item.production.Left == G.startSymbol: + # Sea 𝐼𝑖 el estado que contiene el item "𝑆′→𝑆.,$" (𝑆′ distinguido). + # Entonces 𝐴𝐶𝑇𝐼𝑂𝑁[𝐼𝑖,$]=‘𝑂𝐾‘ + self._register(self.action, (idx, G.EOF.Name), (self.OK, None)) + else: + # Sea "𝑋→𝛼.,𝑠" un item del estado 𝐼𝑖. + # Entonces 𝐴𝐶𝑇𝐼𝑂𝑁[𝐼𝑖,𝑠]=‘𝑅𝑘‘ (producción k es 𝑋→𝛼) + for c in item.lookaheads: + self._register(self.action, (idx, c.Name), (self.REDUCE, item.production)) + else: + next_symbol = item.NextSymbol + try: + next_state = node[next_symbol.Name][0] + if next_symbol.IsNonTerminal: + # Sea "𝑋→𝛼.𝑌𝜔,𝑠" item del estado 𝐼𝑖 y 𝐺𝑜𝑡𝑜(𝐼𝑖,𝑌)=𝐼𝑗. + # Entonces 𝐺𝑂𝑇𝑂[𝐼𝑖,𝑌]=𝑗 + self._register(self.goto, (idx, next_symbol.Name), next_state.idx) + else: + # Sea "𝑋→𝛼.𝑐𝜔,𝑠" un item del estado 𝐼𝑖 y 𝐺𝑜𝑡𝑜(𝐼𝑖,𝑐)=𝐼𝑗. + # Entonces 𝐴𝐶𝑇𝐼𝑂𝑁[𝐼𝑖,𝑐]=‘𝑆𝑗‘ + self._register(self.action, (idx, next_symbol.Name), (self.SHIFT, next_state.idx)) + except KeyError: + print(f'Node: {node} without transition with symbol {next_symbol}') + return + + diff --git a/src/Tools/Tools/Semantic.py b/src/Tools/Tools/Semantic.py new file mode 100644 index 000000000..82e93d47d --- /dev/null +++ b/src/Tools/Tools/Semantic.py @@ -0,0 +1,288 @@ +# Aqui estan las clases necesarias para representar los tipos del programa + +# Necesario para enviar errores semanticos +class SemanticException(Exception): + @property + def text(self): + return self.args[0] + + +# Representa un atributo en un tipo del programa +class Attribute: + def __init__(self, name : str, type, line : int, column : int, expression = None): + self.name = name + self.type = type + self.line = line + self.column = column + + def __str__(self): + return f'[attrib] {self.name}: {self.type.name};' + + def __repr__(self): + return str(self) + +# Representa un metodo en un tipo del programa +class Method: + def __init__(self, name : str, param_names : list, param_types : list, return_type): + self.name = name + self.param_names = param_names + self.param_types = param_types + self.return_type = return_type + + def __str__(self): + params = ', '.join(f'{n}: {t.name}' for n, t in zip(self.param_names, self.param_types)) + return f'[method] {self.name}({params}): {self.return_type.name};' + + def __eq__(self, other): + return other.name == self.name and \ + other.return_type == self.return_type and \ + other.param_types == self.param_types + +#Clase base para representar los tipos del programa +class Type: + def __init__(self, name:str, sealed=False): + self.name = name + self.attributes = [] + self.methods = {} + self.parent = None + # Profundidad en el arbol de herencia + self.depth = 0 + # True si la clase esta sellada (no se puede heredar de ella) + self.sealed = sealed + + def set_parent(self, parent): + # Si el padre esta definido o es un tipo sellado entonces hay un error semantico + if self.parent is not None: + raise SemanticException(f'Parent type is already set for {self.name}.') + if parent.sealed: + raise SemanticException(f'Cannot inherit from sealed type {parent.name}') + self.parent = parent + self.depth = parent.depth + 1 + + # Retorna el tipo en el arbol de herencia de mayor profundidad + # que es padre comun de ambos tipos + def type_union(self, other): + if self is VoidType or other is VoidType: + return VoidType() + + if self is ErrorType or other is ErrorType: + return ErrorType() + + x, y = self, other + while x.depth > y.depth: + x = x.parent + while y.depth > x.depth: + y = y.parent + + while x and x != y: + x = x.parent + y = y.parent + + return x + + # Retorna el atributo del tipo actual con el nombre correspondiente + def get_attribute(self, name:str): + try: + return next(attr for attr in self.attributes if attr.name == name) + except StopIteration: + if self.parent is None: + raise SemanticException(f'Attribute "{name}" is not defined in {self.name}.') + try: + return self.parent.get_attribute(name) + except SemanticException: + raise SemanticException(f'Attribute "{name}" is not defined in {self.name}.') + + # Define un atributo para el tipo actual con el nombre y el tipo correspondiente + def define_attribute(self, name:str, typex, line, column): + try: + attribute = next(attr for attr in self.attributes if attr.name == name) + except StopIteration: + attribute = Attribute(name, typex, line, column) + self.attributes.append(attribute) + return attribute + else: + raise SemanticException(f'Attribute "{name}" is multiply defined in class.') + + # Retorna el metodo del tipo actual con el nombre correspondiente + def get_method(self, name:str): + try: + return self.methods[name] + except KeyError: + if self.parent is None: + raise SemanticException(f'Method "{name}" is not defined in {self.name}.') + try: + return self.parent.get_method(name) + except SemanticException: + raise SemanticException(f'Method "{name}" is not defined in {self.name}.') + + # Define un metodo para el tipo actual con el nombre, los parametros y el tipo de + # retorno correspondientes + def define_method(self, name:str, param_names:list, param_types:list, return_type, line, column): + if name in self.methods: + raise SemanticException(f'Method {name} is multiply defined') + + method = self.methods[name] = Method(name, param_names, param_types, return_type) + method.return_info = VariableInfo(f'{self.name}_{name}_returnType', return_type) + + return method + + # Returns true if other is a parent of current type + def conforms_to(self, other): + return other.is_special_type() \ + or self.name == other.name \ + or (self.parent is not None and self.parent.conforms_to(other)) + + def is_special_type(self): + return False + + def is_void_type(self): + return False + + def __str__(self): + output = f'type {self.name}' + parent = '' if self.parent is None else f' : {self.parent.name}' + output += parent + output += ' {' + output += '\n\t' if self.attributes or self.methods else '' + output += '\n\t'.join(str(x) for x in self.attributes) + output += '\n\t' if self.attributes else '' + output += '\n\t'.join(str(x) for x in self.methods.values()) + output += '\n' if self.methods else '' + output += '}\n' + return output + + def __repr__(self): + return str(self) + +# Special_Types +class SelfType(Type): + def __init__(self): + Type.__init__(self, 'SELF_TYPE') + self.sealed = True + + def conforms_to(self, other): + return False + + def is_special_type(self): + return True + + def __eq__(self, other): + return isinstance(other, SelfType) + +class VoidType(Type): + def __init__(self): + Type.__init__(self, 'VOID_TYPE') + self.sealed = True + + def type_union(self, other): + return self + + def conforms_to(self, other): + return True + + def is_special_type(self): + return True + + def is_void_type(self): + return True + + def __eq__(self, other): + return isinstance(other, Type) + +class ErrorType(Type): + def __init__(self): + Type.__init__(self, '') + self.sealed = True + + def type_union(self, other): + return self + + def conforms_to(self, other): + return True + + def is_special_type(self): + return True + + def __eq__(self, other): + return isinstance(other, Type) + +# Clase para representar una variable dentro del programa +class VariableInfo: + def __init__(self, name, vtype): + self.name = name + self.type = vtype + + +# Clase para representar el contexto en el que se guardan las variables y los +# tipos durante el chequeo semantico del programa +class Context: + def __init__(self): + self.types = {} + + # Incluye un tipo dentro del contexto + def add_type(self, type : Type): + if type.name in self.types: + raise SemanticException('Classes may not be redefined') + self.types[type.name] = type + return type + + # Crea un tipo dentro del contexto + def create_type(self, name : str): + if name in self.types: + raise SemanticException('Classes may not be redefined') + type = self.types[name] = Type(name) + return type + + # Obtiene un tipo del contexto con el nombre correspondiente + def get_type(self, name : str): + try: + return self.types[name] + except KeyError: + raise SemanticException(f'Type {name} not defined.') + + def __str__(self): + return '{\n\t' + '\n\t'.join(y for x in self.types.values() for y in str(x).split('\n')) + '\n}' + + def __repr__(self): + return str(self) + + +# Clase para representar el scope durante el chequeo semantico +class Scope: + def __init__(self, parent = None): + self.parent = parent + self.locals = [] + self.childs = [] + + # Crea un scope hijo + def create_child(self): + self.childs.append(Scope(self)) + return self.childs[-1] + + # Define una variable en el scope + def define_variable(self, name : str, type : Type, line : int, column : int): + self.locals.append(VariableInfo(name, type)) + return self.locals[-1] + + # Retorna una variable definida en el scope, None si no esta definida + def find_variable(self, name : str): + try: + return next(i for i in self.locals if i.name == name) + except StopIteration: + return self.parent.find_variable(name) if self.parent is not None else None + + # True si la variable esta definida en el scope + def is_defined(self, name : str): + return self.find_variable(name) is not None + + # True si la variable fue definida en el scope actual y no en alguno + # de sus scope padres + def is_local(self, name : str): + return any(i for i in self.locals if i.name == name) + + def __iter__(self): + for var in self.locals: + yield var + for ch in self.childs: + for var in ch: + yield var diff --git a/src/Tools/Tools/automata.py b/src/Tools/Tools/automata.py new file mode 100644 index 000000000..2bdadd2d1 --- /dev/null +++ b/src/Tools/Tools/automata.py @@ -0,0 +1,210 @@ +try: + import pydot +except: + pass + +# Esta es la clase dada en clases practicas +# para representar un DFA, +# o cualkier grafo en general +class State: + def __init__(self, state, final=False, formatter=lambda x: str(x), shape='circle'): + self.state = state + self.final = final + self.transitions = {} + self.epsilon_transitions = set() + self.tag = None + self.formatter = formatter + self.shape = shape + + # The method name is set this way from compatibility issues. + def set_formatter(self, value, attr='formatter', visited=None): + if visited is None: + visited = set() + elif self in visited: + return + + visited.add(self) + self.__setattr__(attr, value) + for destinations in self.transitions.values(): + for node in destinations: + node.set_formatter(value, attr, visited) + for node in self.epsilon_transitions: + node.set_formatter(value, attr, visited) + return self + + def has_transition(self, symbol): + return symbol in self.transitions + + def add_transition(self, symbol, state): + try: + self.transitions[symbol].append(state) + except: + self.transitions[symbol] = [state] + return self + + def add_epsilon_transition(self, state): + self.epsilon_transitions.add(state) + return self + + def recognize(self, string): + states = self.epsilon_closure + for symbol in string: + states = self.move_by_state(symbol, *states) + states = self.epsilon_closure_by_state(*states) + return any(s.final for s in states) + + def to_deterministic(self, formatter=lambda x: str(x)): + closure = self.epsilon_closure + start = State(tuple(closure), any(s.final for s in closure), formatter) + + closures = [ closure ] + states = [ start ] + pending = [ start ] + + while pending: + state = pending.pop() + symbols = { symbol for s in state.state for symbol in s.transitions } + + for symbol in symbols: + move = self.move_by_state(symbol, *state.state) + closure = self.epsilon_closure_by_state(*move) + + if closure not in closures: + new_state = State(tuple(closure), any(s.final for s in closure), formatter) + closures.append(closure) + states.append(new_state) + pending.append(new_state) + else: + index = closures.index(closure) + new_state = states[index] + + state.add_transition(symbol, new_state) + + return start + + @staticmethod + def from_nfa(nfa, get_states=False): + states = [] + for n in range(nfa.states): + state = State(n, n in nfa.finals) + states.append(state) + + for (origin, symbol), destinations in nfa.map.items(): + origin = states[origin] + origin[symbol] = [ states[d] for d in destinations ] + + if get_states: + return states[nfa.start], states + return states[nfa.start] + + @staticmethod + def move_by_state(symbol, *states): + return { s for state in states if state.has_transition(symbol) for s in state[symbol]} + + @staticmethod + def epsilon_closure_by_state(*states): + closure = { state for state in states } + + l = 0 + while l != len(closure): + l = len(closure) + tmp = [s for s in closure] + for s in tmp: + for epsilon_state in s.epsilon_transitions: + closure.add(epsilon_state) + return closure + + @property + def epsilon_closure(self): + return self.epsilon_closure_by_state(self) + + @property + def name(self): + return self.formatter(self.state) + + def get(self, symbol): + target = self.transitions[symbol] + assert len(target) == 1 + return target[0] + + def __getitem__(self, symbol): + if symbol == '': + return self.epsilon_transitions + try: + return self.transitions[symbol] + except KeyError: + return None + + def __setitem__(self, symbol, value): + if symbol == '': + self.epsilon_transitions = value + else: + self.transitions[symbol] = value + + def __repr__(self): + return str(self) + + def __str__(self): + return str(self.state) + + def __hash__(self): + return hash(self.state) + + def __iter__(self): + yield from self._visit() + + def _visit(self, visited=None): + if visited is None: + visited = set() + elif self in visited: + return + + visited.add(self) + yield self + + for destinations in self.transitions.values(): + for node in destinations: + yield from node._visit(visited) + for node in self.epsilon_transitions: + yield from node._visit(visited) + + def graph(self, dir = 'LR'): + G = pydot.Dot(rankdir=dir, margin=0.1) + G.add_node(pydot.Node('start', shape='plaintext', label='', width=0, height=0)) + + visited = set() + def visit(start): + ids = id(start) + if ids not in visited: + visited.add(ids) + G.add_node(pydot.Node(ids, label = f'\'{start.name}\'', shape=self.shape, style='bold' if start.final else '')) + for tran, destinations in start.transitions.items(): + for end in destinations: + visit(end) + G.add_edge(pydot.Edge(ids, id(end), label=tran, labeldistance=2)) + for end in start.epsilon_transitions: + visit(end) + G.add_edge(pydot.Edge(ids, id(end), label='ε', labeldistance=2)) + + visit(self) + G.add_edge(pydot.Edge('start', id(self), label='', style='dashed')) + + return G + + def _repr_svg_(self): + try: + return self.graph().create_svg().decode('utf8') + except: + pass + + def write_to(self, fname): + return self.graph().write_svg(fname) + +def multiline_formatter(state): + return '\n'.join(str(item) for item in state) + +def lr0_formatter(state): + try: + return '\n'.join(str(item)[:-4] for item in state) + except TypeError: + return str(state)[:-4] \ No newline at end of file diff --git a/src/Tools/Tools/evaluation.py b/src/Tools/Tools/evaluation.py new file mode 100644 index 000000000..941113216 --- /dev/null +++ b/src/Tools/Tools/evaluation.py @@ -0,0 +1,44 @@ +from .parsing import ShiftReduceParser + +''' +Este metodo retorna el ast dado el conjunto de producciones, +las operaciones y los tokens +''' +def evaluate_reverse_parse(right_parse, operations, tokens): + if not right_parse or not operations or not tokens: + return + + right_parse = iter(right_parse) + tokens = iter(tokens) + stack = [] + for operation in operations: + # Si hay que hacer un shift, pasemos a analizar + # el proximo token e incluyamoslo en la pila + if operation == ShiftReduceParser.SHIFT: + token = next(tokens) + stack.append(token) + # Si hay que hacer un reduce, tomamos los elementos + # necesarios de la pila y los sustituimos por el nonterminal + # correspondiente, el cual incluimos en la pila + elif operation == ShiftReduceParser.REDUCE: + production = next(right_parse) + head, body = production + attributes = production.attributes + assert all(rule is None for rule in attributes[1:]), 'There must be only syntheticed attributes.' + rule = attributes[0] + + if len(body): + syntheticed = [None] + stack[-len(body):] + value = rule(None, syntheticed) + stack[-len(body):] = [value] + else: + stack.append(rule(None, None)) + else: + raise Exception('Invalid action!!!') + + # La pila debe terminar con el program node + # correspondiente a la raiz del ast, todos los + # tokens deben haber sido analizados + assert len(stack) == 1 + assert next(tokens).token_type == '$'#######estaba EOF, creo que tiene que ver con lo que cambie al poner el tokentype en el lexer + return stack[0] \ No newline at end of file diff --git a/src/Tools/Tools/parsing.py b/src/Tools/Tools/parsing.py new file mode 100644 index 000000000..17eae0e2d --- /dev/null +++ b/src/Tools/Tools/parsing.py @@ -0,0 +1,95 @@ +from .Errors import * + +# Clase base para los parsers Shift-Reduce +class ShiftReduceParser: + SHIFT = 'SHIFT' + REDUCE = 'REDUCE' + OK = 'OK' + + def __init__(self, G, verbose=False): + self.G = G + self.verbose = verbose + self.action = {} + self.goto = {} + self.HasConflict = False + self.errors = [] + self._build_parsing_table() + + def _build_parsing_table(self): + raise NotImplementedError() + + ''' + Retorna las producciones y operaciones necesarias para el + proceso de parsing del string w. + ''' + def __call__(self, w): + stack = [0] + cursor = 0 + output = [] + operations = [] + self.errors = [] + + while True: + # Estado del automata en el que me encuentro + state = stack[-1] + # Siguiente simbolo a analizar + lookahead = w[cursor].token_type + if self.verbose: print(stack, '<---||--->', w[cursor:]) + #(Detect error) + try: + action, tag = self.action[state, lookahead][0] + except KeyError: + # Si no existe transicion desde el estado actual con el simbolo + # correspondiente es porque ha ocurrido un error + lookahead = w[cursor] + line = lookahead.line + column = lookahead.column + if lookahead.lex == '$': + self.errors.append(SyntacticError(0, 0, 'EOF')) + else: + self.errors.append(SyntacticError(line, column, f'"{lookahead.lex}"')) + return None, None + + # Si la accion es Shift, incluyo en la pila el simbolo actual y + # el estado al que necesito moverme + if action == self.SHIFT: + stack.append(lookahead) + stack.append(tag) + operations.append(self.SHIFT) + cursor += 1 + # Si la accion es Reduce, saco de la pila los simbolos correspondientes + # a la produccion q debo reducir + elif action == self.REDUCE: + output.append(tag) + for i in range(len(tag.Right)): + stack.pop() + assert stack[-1] == tag.Right[-i - 1].Name, 'Symbol does not match' + stack.pop() + # Tomo el estado al que debo moverme + index = self.goto[stack[-1], tag.Left.Name][0] + # Incluyo en la pila el simbolo reducido y el estado al + # que necesito moverme + stack.append(tag.Left.Name) + stack.append(index) + operations.append(self.REDUCE) + #(OK case) + elif action == self.OK: + return output, operations + #(Invalid case) + else: + lookahead = w[cursor] + line = lookahead.line + column = lookahead.column + if lookahead.lex == '$': + self.errors.append(SyntacticError(0, 0, 'EOF')) + else: + self.errors.append(SyntacticError(line, column, f'"{lookahead.lex}"')) + return None, None + + def _register(self, table, key, value): + if key not in table: + table[key] = [value] + elif not any(i for i in table[key] if i == value): + self.HasConflict = True + table[key].append(value) + diff --git a/src/Tools/Tools/pycompiler.py b/src/Tools/Tools/pycompiler.py new file mode 100644 index 000000000..74ad3bc0d --- /dev/null +++ b/src/Tools/Tools/pycompiler.py @@ -0,0 +1,560 @@ +import json + +#Clase base para los simbolos de la gramatica, terminales y noterminales +class Symbol(object): + + def __init__(self, name, grammar): + self.Name = name + self.Grammar = grammar + + def __str__(self): + return self.Name + + def __repr__(self): + return self.Name + + def __add__(self, other): + if isinstance(other, Symbol): + return Sentence(self, other) + + if isinstance(other, Sentence): + return Sentence(*((self,) + other._symbols)) + + raise TypeError(other) + + def __or__(self, other): + + if isinstance(other, (Sentence)): + return SentenceList(Sentence(self), other) + + raise TypeError(other) + + @property + def IsEpsilon(self): + return False + + def __len__(self): + return 1 + +class NonTerminal(Symbol): + + + def __init__(self, name, grammar): + super().__init__(name, grammar) + self.productions = [] + + + def __imod__(self, other): + + if isinstance(other, (Sentence)): + p = Production(self, other) + self.Grammar.Add_Production(p) + return self + + if isinstance(other, tuple): + assert len(other) > 1 + + if len(other) == 2: + other += (None,) * len(other[0]) + + assert len(other) == len(other[0]) + 2, "Debe definirse una, y solo una, regla por cada símbolo de la producción" + # assert len(other) == 2, "Tiene que ser una Tupla de 2 elementos (sentence, attribute)" + + if isinstance(other[0], Symbol) or isinstance(other[0], Sentence): + p = AttributeProduction(self, other[0], other[1:]) + else: + raise Exception("") + + self.Grammar.Add_Production(p) + return self + + if isinstance(other, Symbol): + p = Production(self, Sentence(other)) + self.Grammar.Add_Production(p) + return self + + if isinstance(other, SentenceList): + + for s in other: + p = Production(self, s) + self.Grammar.Add_Production(p) + + return self + + raise TypeError(other) + + def __add__(self, other): + if isinstance(other, Symbol): + return Sentence(self, other) + + if isinstance(other, Sentence): + return Sentence(self, *(other._symbols)) + + if other: + raise TypeError(other) + + def __iter__(self): + yield self + + @property + def IsTerminal(self): + return False + + @property + def IsNonTerminal(self): + return True + + @property + def IsEpsilon(self): + return False + +class Terminal(Symbol): + + def __init__(self, name, grammar): + super().__init__(name, grammar) + + def __iter__(self): + yield self + + def __add__(self, other): + if isinstance(other, Symbol): + return Sentence(self, other) + + if isinstance(other, Sentence): + return Sentence(self, *(other._symbols)) + + if other: + raise TypeError(other) + + @property + def IsTerminal(self): + return True + + @property + def IsNonTerminal(self): + return False + + @property + def IsEpsilon(self): + return False + +class EOF(Terminal): + + def __init__(self, Grammar): + super().__init__('$', Grammar) + +class Sentence(object): + + def __init__(self, *args): + self._symbols = tuple(x for x in args if not x.IsEpsilon) + self.hash = hash(self._symbols) + + def __len__(self): + return len(self._symbols) + + def __add__(self, other): + if isinstance(other, Symbol): + return Sentence(*(self._symbols + (other,))) + + if isinstance(other, Sentence): + return Sentence(*(self._symbols + other._symbols)) + + if other: + raise TypeError(other) + + def __or__(self, other): + if isinstance(other, Sentence): + return SentenceList(self, other) + + if isinstance(other, Symbol): + return SentenceList(self, Sentence(other)) + + if isinstance(other, SentenceList): + return SentenceList(self, *(other._sentences)) + + if other: + raise TypeError(other) + + def __repr__(self): + return str(self) + + def __str__(self): + return ("%s " * len(self._symbols) % tuple(self._symbols)).strip() + + def __iter__(self): + return iter(self._symbols) + + def __getitem__(self, index): + return self._symbols[index] + + def __eq__(self, other): + return self._symbols == other._symbols + + def __hash__(self): + return self.hash + + @property + def IsEpsilon(self): + return False + +class SentenceList(object): + + def __init__(self, *args): + self._sentences = list(args) + + def Add(self, symbol): + if not symbol and (symbol is None or not symbol.IsEpsilon): + raise ValueError(symbol) + + self._sentences.append(symbol) + + def __iter__(self): + return iter(self._sentences) + + def __or__(self, other): + if isinstance(other, Sentence): + self.Add(other) + return self + + if isinstance(other, Symbol): + return self | Sentence(other) + + if isinstance(other, SentenceList): + return SentenceList(self._sentences + other._sentences) + +class Epsilon(Terminal, Sentence): + + def __init__(self, grammar): + super().__init__('epsilon', grammar) + + + def __str__(self): + return "ε" + + def __repr__(self): + return 'epsilon' + + def __iter__(self): + yield from () + + def __len__(self): + return 0 + + def __add__(self, other): + return other + + def __eq__(self, other): + return isinstance(other, (Epsilon,)) + + def __hash__(self): + return hash("") + + @property + def IsEpsilon(self): + return True + +class Production(object): + + def __init__(self, nonTerminal, sentence): + + self.Left = nonTerminal + self.Right = sentence + + def __str__(self): + + return '%s --> %s' % (self.Left, self.Right) + + def __repr__(self): + return '%s -> %s' % (self.Left, self.Right) + + def __iter__(self): + yield self.Left + yield self.Right + + def __eq__(self, other): + return isinstance(other, Production) and self.Left == other.Left and self.Right == other.Right + + def __hash__(self): + return hash((self.Left, self.Right)) + + @property + def IsEpsilon(self): + return self.Right.IsEpsilon + +class AttributeProduction(Production): + + def __init__(self, nonTerminal, sentence, attributes): + if not isinstance(sentence, Sentence) and isinstance(sentence, Symbol): + sentence = Sentence(sentence) + super(AttributeProduction, self).__init__(nonTerminal, sentence) + + self.attributes = attributes + + def __str__(self): + return '%s := %s' % (self.Left, self.Right) + + def __repr__(self): + return '%s -> %s' % (self.Left, self.Right) + + def __iter__(self): + yield self.Left + yield self.Right + + + @property + def IsEpsilon(self): + return self.Right.IsEpsilon + + # sintetizar en ingles??????, pending aggrement + def syntetice(self): + pass + +class Grammar(): + + def __init__(self): + + self.Productions = [] + self.nonTerminals = [] + self.terminals = [] + self.startSymbol = None + # production type + self.pType = None + self.Epsilon = Epsilon(self) + self.EOF = EOF(self) + + self.symbDict = { '$': self.EOF } + + def NonTerminal(self, name, startSymbol = False): + + name = name.strip() + if not name: + raise Exception("Empty name") + + term = NonTerminal(name,self) + + if startSymbol: + + if self.startSymbol is None: + self.startSymbol = term + else: + raise Exception("Cannot define more than one start symbol.") + + self.nonTerminals.append(term) + self.symbDict[name] = term + return term + + def NonTerminals(self, names): + + ans = tuple((self.NonTerminal(x) for x in names.strip().split())) + + return ans + + + def Add_Production(self, production): + + if len(self.Productions) == 0: + self.pType = type(production) + + assert type(production) == self.pType, "The Productions most be of only 1 type." + + production.Left.productions.append(production) + self.Productions.append(production) + + + def Terminal(self, name): + + name = name.strip() + if not name: + raise Exception("Empty name") + + term = Terminal(name, self) + self.terminals.append(term) + self.symbDict[name] = term + return term + + def Terminals(self, names): + + ans = tuple((self.Terminal(x) for x in names.strip().split())) + + return ans + + + def __str__(self): + + mul = '%s, ' + + ans = 'Non-Terminals:\n\t' + + nonterminals = mul * (len(self.nonTerminals)-1) + '%s\n' + + ans += nonterminals % tuple(self.nonTerminals) + + ans += 'Terminals:\n\t' + + terminals = mul * (len(self.terminals)-1) + '%s\n' + + ans += terminals % tuple(self.terminals) + + ans += 'Productions:\n\t' + + ans += str(self.Productions) + + return ans + + def __getitem__(self, name): + try: + return self.symbDict[name] + except KeyError: + return None + + @property + def to_json(self): + + productions = [] + + for p in self.Productions: + head = p.Left.Name + + body = [] + + for s in p.Right: + body.append(s.Name) + + productions.append({'Head':head, 'Body':body}) + + d={'NonTerminals':[symb.Name for symb in self.nonTerminals], 'Terminals': [symb.Name for symb in self.terminals],\ + 'Productions':productions} + + # [{'Head':p.Left.Name, "Body": [s.Name for s in p.Right]} for p in self.Productions] + return json.dumps(d) + + @staticmethod + def from_json(data): + data = json.loads(data) + + G = Grammar() + dic = {'epsilon':G.Epsilon} + + for term in data['Terminals']: + dic[term] = G.Terminal(term) + + for noTerm in data['NonTerminals']: + dic[noTerm] = G.NonTerminal(noTerm) + + for p in data['Productions']: + head = p['Head'] + dic[head] %= Sentence(*[dic[term] for term in p['Body']]) + + return G + + def copy(self): + G = Grammar() + G.pType = self.pType + for terminal in self.terminals: + G.Terminal(terminal.Name) + for nonterminal in self.nonTerminals: + G.NonTerminal(nonterminal.Name, nonterminal == self.startSymbol) + for prod in self.Productions: + left = G.symbDict[prod.Left.Name] + if prod.IsEpsilon: + if self.pType is AttributeProduction: + G.Add_Production(AttributeProduction(left, G.Epsilon, prod.attributes)) + else: + G.Add_Production(Production(left, G.Epsilon)) + else: + right = Sentence() + for symbol in prod.Right: + right = right + G.symbDict[symbol.Name] + if self.pType is AttributeProduction: + G.Add_Production(AttributeProduction(left, right, prod.attributes)) + else: + G.Add_Production(Production(left, right)) + return G + + @property + def IsAugmentedGrammar(self): + augmented = 0 + for left, right in self.Productions: + if self.startSymbol == left: + augmented += 1 + if augmented <= 1: + return True + else: + return False + + def AugmentedGrammar(self, force=False): + if not self.IsAugmentedGrammar or force: + + G = self.copy() + # S, self.startSymbol, SS = self.startSymbol, None, self.NonTerminal('S\'', True) + S = G.startSymbol + G.startSymbol = None + SS = G.NonTerminal('S\'', True) + if G.pType is AttributeProduction: + SS %= S + G.Epsilon, lambda x : x + else: + SS %= S + G.Epsilon + + return G + else: + return self.copy() + #endchange + +class Item: + + def __init__(self, production, pos, lookaheads=[]): + self.production = production + self.pos = pos + self.lookaheads = frozenset(look for look in lookaheads) + + def __str__(self): + s = str(self.production.Left) + " -> " + if len(self.production.Right) > 0: + for i,c in enumerate(self.production.Right): + if i == self.pos: + s += "." + s += str(self.production.Right[i]) + if self.pos == len(self.production.Right): + s += "." + else: + s += "." + s += ", " + str(self.lookaheads)[10:-1] + return s + + def __repr__(self): + return str(self) + + + def __eq__(self, other): + return ( + (self.pos == other.pos) and + (self.production == other.production) and + (set(self.lookaheads) == set(other.lookaheads)) + ) + + def __hash__(self): + return hash((self.production,self.pos,self.lookaheads)) + + @property + def IsReduceItem(self): + return len(self.production.Right) == self.pos + + @property + def NextSymbol(self): + if self.pos < len(self.production.Right): + return self.production.Right[self.pos] + else: + return None + + def NextItem(self): + if self.pos < len(self.production.Right): + return Item(self.production,self.pos+1,self.lookaheads) + else: + return None + + def Preview(self, skip=1): + unseen = self.production.Right[self.pos+skip:] + return [ unseen + (lookahead,) for lookahead in self.lookaheads ] + + def Center(self): + return Item(self.production, self.pos) \ No newline at end of file diff --git a/src/Tools/Tools/utils.py b/src/Tools/Tools/utils.py new file mode 100644 index 000000000..1edd4972e --- /dev/null +++ b/src/Tools/Tools/utils.py @@ -0,0 +1,221 @@ +from .pycompiler import Production, Sentence, Symbol, EOF, Epsilon + +class ContainerSet: + def __init__(self, *values, contains_epsilon=False): + self.set = set(values) + self.contains_epsilon = contains_epsilon + + def add(self, value): + n = len(self.set) + self.set.add(value) + return n != len(self.set) + + def extend(self, values): + change = False + for value in values: + change |= self.add(value) + return change + + def set_epsilon(self, value=True): + last = self.contains_epsilon + self.contains_epsilon = value + return last != self.contains_epsilon + + def update(self, other): + n = len(self.set) + self.set.update(other.set) + return n != len(self.set) + + def epsilon_update(self, other): + return self.set_epsilon(self.contains_epsilon | other.contains_epsilon) + + def hard_update(self, other): + return self.update(other) | self.epsilon_update(other) + + def find_match(self, match): + for item in self.set: + if item == match: + return item + return None + + def __len__(self): + return len(self.set) + int(self.contains_epsilon) + + def __str__(self): + return '%s-%s' % (str(self.set), self.contains_epsilon) + + def __repr__(self): + return str(self) + + def __iter__(self): + return iter(self.set) + + def __nonzero__(self): + return len(self) > 0 + + def __eq__(self, other): + if isinstance(other, set): + return self.set == other + return isinstance(other, ContainerSet) and self.set == other.set and self.contains_epsilon == other.contains_epsilon + + +def inspect(item, grammar_name='G', mapper=None): + try: + return mapper[item] + except (TypeError, KeyError ): + if isinstance(item, dict): + items = ',\n '.join(f'{inspect(key, grammar_name, mapper)}: {inspect(value, grammar_name, mapper)}' for key, value in item.items() ) + return f'{{\n {items} \n}}' + elif isinstance(item, ContainerSet): + args = f'{ ", ".join(inspect(x, grammar_name, mapper) for x in item.set) } ,' if item.set else '' + return f'ContainerSet({args} contains_epsilon={item.contains_epsilon})' + elif isinstance(item, EOF): + return f'{grammar_name}.EOF' + elif isinstance(item, Epsilon): + return f'{grammar_name}.Epsilon' + elif isinstance(item, Symbol): + return f"G['{item.Name}']" + elif isinstance(item, Sentence): + items = ', '.join(inspect(s, grammar_name, mapper) for s in item._symbols) + return f'Sentence({items})' + elif isinstance(item, Production): + left = inspect(item.Left, grammar_name, mapper) + right = inspect(item.Right, grammar_name, mapper) + return f'Production({left}, {right})' + elif isinstance(item, tuple) or isinstance(item, list): + ctor = ('(', ')') if isinstance(item, tuple) else ('[',']') + return f'{ctor[0]} {("%s, " * len(item)) % tuple(inspect(x, grammar_name, mapper) for x in item)}{ctor[1]}' + else: + raise ValueError(f'Invalid: {item}') + +def pprint(item, header=""): + if header: + print(header) + + if isinstance(item, dict): + for key, value in item.items(): + print(f'{key} ---> {value}') + elif isinstance(item, list): + print('[') + for x in item: + print(f' {repr(x)}') + print(']') + else: + print(item) + +class Token: + """ + Basic token class. + + Parameters + ---------- + lex : str + Token's lexeme. + token_type : Enum + Token's type. + """ + + def __init__(self, lex, token_type, line = None, column = None): + self.lex = lex + self.token_type = token_type + self.line = line + self.column = column + + def __str__(self): + return str(self.token_type) + + def __repr__(self): + return repr(self.token_type) + + @property + def is_valid(self): + return True + +class UnknownToken(Token): + def __init__(self, lex): + Token.__init__(self, lex, None) + + def transform_to(self, token_type): + return Token(self.lex, token_type) + + @property + def is_valid(self): + return False + +def tokenizer(G, fixed_tokens): + def decorate(func): + def tokenize_text(text): + tokens = [] + for lex in text.split(): + try: + token = fixed_tokens[lex] + except KeyError: + token = UnknownToken(lex) + try: + token = func(token) + except TypeError: + pass + tokens.append(token) + tokens.append(Token('$', G.EOF)) + return tokens + + if hasattr(func, '__call__'): + return tokenize_text + elif isinstance(func, str): + return tokenize_text(func) + else: + raise TypeError('Argument must be "str" or a callable object.') + return decorate + +class DisjointSet: + def __init__(self, *items): + self.nodes = { x: DisjointNode(x) for x in items } + + def merge(self, items): + items = (self.nodes[x] for x in items) + try: + head, *others = items + for other in others: + head.merge(other) + except ValueError: + pass + + @property + def representatives(self): + return { n.representative for n in self.nodes.values() } + + @property + def groups(self): + return [[n for n in self.nodes.values() if n.representative == r] for r in self.representatives] + + def __len__(self): + return len(self.representatives) + + def __getitem__(self, item): + return self.nodes[item] + + def __str__(self): + return str(self.groups) + + def __repr__(self): + return str(self) + +class DisjointNode: + def __init__(self, value): + self.value = value + self.parent = self + + @property + def representative(self): + if self.parent != self: + self.parent = self.parent.representative + return self.parent + + def merge(self, other): + other.representative.parent = self.representative + + def __str__(self): + return str(self.value) + + def __repr__(self): + return str(self) \ No newline at end of file diff --git a/src/Tools/Tools/visitor.py b/src/Tools/Tools/visitor.py new file mode 100644 index 000000000..964842836 --- /dev/null +++ b/src/Tools/Tools/visitor.py @@ -0,0 +1,80 @@ +# The MIT License (MIT) +# +# Copyright (c) 2013 Curtis Schlak +# +# Permission is hereby granted, free of charge, to any person obtaining a copy +# of this software and associated documentation files (the "Software"), to deal +# in the Software without restriction, including without limitation the rights +# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +# copies of the Software, and to permit persons to whom the Software is +# furnished to do so, subject to the following conditions: +# +# The above copyright notice and this permission notice shall be included in +# all copies or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +# THE SOFTWARE. + +import inspect + +__all__ = ['on', 'when'] + +def on(param_name): + def f(fn): + dispatcher = Dispatcher(param_name, fn) + return dispatcher + return f + + +def when(param_type): + def f(fn): + frame = inspect.currentframe().f_back + func_name = fn.func_name if 'func_name' in dir(fn) else fn.__name__ + dispatcher = frame.f_locals[func_name] + if not isinstance(dispatcher, Dispatcher): + dispatcher = dispatcher.dispatcher + dispatcher.add_target(param_type, fn) + def ff(*args, **kw): + return dispatcher(*args, **kw) + ff.dispatcher = dispatcher + return ff + return f + + +class Dispatcher(object): + def __init__(self, param_name, fn): + frame = inspect.currentframe().f_back.f_back + top_level = frame.f_locals == frame.f_globals + self.param_index = self.__argspec(fn).args.index(param_name) + self.param_name = param_name + self.targets = {} + + def __call__(self, *args, **kw): + typ = args[self.param_index].__class__ + d = self.targets.get(typ) + if d is not None: + return d(*args, **kw) + else: + issub = issubclass + t = self.targets + ks = t.keys() + ans = [t[k](*args, **kw) for k in ks if issub(typ, k)] + if len(ans) == 1: + return ans.pop() + return ans + + def add_target(self, typ, target): + self.targets[typ] = target + + @staticmethod + def __argspec(fn): + # Support for Python 3 type hints requires inspect.getfullargspec + if hasattr(inspect, 'getfullargspec'): + return inspect.getfullargspec(fn) + else: + return inspect.getargspec(fn) diff --git a/src/Tools/format_visitor.py b/src/Tools/format_visitor.py new file mode 100644 index 000000000..d2a1f7d89 --- /dev/null +++ b/src/Tools/format_visitor.py @@ -0,0 +1,116 @@ +from Tools import visitor +from Parser import ProgramNode, ClassDeclarationNode, AttrDeclarationNode, FuncDeclarationNode +from Parser import IfThenElseNode, WhileLoopNode, BlockNode, LetInNode, CaseOfNode +from Parser import AssignNode, UnaryNode, BinaryNode +from Parser import FunctionCallNode, MemberCallNode, NewNode, AtomicNode + +# Visitor para imprimir el AST + +class FormatVisitor: + @visitor.on('node') + def visit(self, node, tabs): + pass + + @visitor.when(ProgramNode) + def visit(self, node, tabs=0): + ans = '\t' * tabs + f'\\__ProgramNode [ ... ]' + statements = '\n'.join(self.visit(child, tabs + 1) for child in node.declarations) + return f'{ans}\n{statements}' + + @visitor.when(ClassDeclarationNode) + def visit(self, node, tabs=0): + parent = '' if node.parent is None else f"inherits {node.parent.lex}" + ans = '\t' * tabs + f'\\__ClassDeclarationNode: class {node.id.lex} {parent} {{ ... }}' + features = '\n'.join(self.visit(child, tabs + 1) for child in node.features) + return f'{ans}\n{features}' + + @visitor.when(AttrDeclarationNode) + def visit(self, node, tabs=0): + ans = '\t' * tabs + f'\\__AttrDeclarationNode: {node.id.lex}: {node.type.lex}' + (' <- ' if node.expression else '') + ';' + expr = self.visit(node.expression, tabs + 1) if node.expression else None + return f'{ans}' + (f'\n{expr}' if expr else '') + + @visitor.when(FuncDeclarationNode) + def visit(self, node, tabs=0): + params = ', '.join(': '.join(tok.lex for tok in param) for param in node.params) + ans = '\t' * tabs + f'\\__FuncDeclarationNode: {node.id.lex}({params}): {node.type.lex} {{ }}' + body = self.visit(node.body, tabs + 1) + return f'{ans}\n{body}' + + @visitor.when(IfThenElseNode) + def visit(self, node, tabs=0): + ans = '\t' * tabs + f'\\_IfThenElseNode: if then else fi' + cond = self.visit(node.condition, tabs + 1) + if_body = self.visit(node.if_body, tabs + 1) + else_body = self.visit(node.else_body, tabs + 1) + return f'{ans}\n{cond}\n{if_body}\n{else_body}' + + @visitor.when(WhileLoopNode) + def visit(self, node, tabs=0): + ans = '\t' * tabs + f'\\_WhileNode: while loop pool' + cond = self.visit(node.condition, tabs + 1) + body = self.visit(node.body, tabs + 1) + return f'{ans}\n{cond}\n{body}' + + @visitor.when(BlockNode) + def visit(self, node, tabs=0): + ans = '\t' * tabs + f'\\_BlockNode: {{ ; ... ; }}' + expressions = '\n'.join(self.visit(expr, tabs + 1) for expr in node.expressions) + return f'{ans}\n{expressions}' + + @visitor.when(LetInNode) + def visit(self, node, tabs=0): + let_body = ', '.join(f'{idx.lex}: {typex.lex}' + (' <- ' if expr else '') for idx, typex, expr in node.let_body) + ans = '\t' * tabs + f'\\_LetInNode: let {let_body} in ' + lets = '\n'.join(self.visit(expr, tabs + 1) for _, _, expr in node.let_body if expr) + body = self.visit(node.in_body, tabs + 1) + return f'{ans}\n{lets}\n{body}' + + @visitor.when(CaseOfNode) + def visit(self, node, tabs=0): + case_body = ' '.join(f'{idx.lex}: {typex.lex} => ;' for idx, typex, expr in node.branches) + ans = '\t' * tabs + f'\\_CaseOfNode: case of {case_body} esac' + expression = self.visit(node.expression, tabs + 1) + body = '\n'.join(self.visit(expr, tabs + 1) for _, _, expr in node.branches) + return f'{ans}\n{expression}\n{body}' + + @visitor.when(AssignNode) + def visit(self, node, tabs=0): + ans = '\t' * tabs + f'\\_AssingNode: {node.id.lex} <- ' + expr = self.visit(node.expression, tabs + 1) + return f'{ans}\n{expr}' + + @visitor.when(UnaryNode) + def visit(self, node, tabs=0): + ans = '\t' * tabs + f'\\__{node.__class__.__name__} ' + expression = self.visit(node.expression, tabs + 1) + return f'{ans}\n{expression}' + + @visitor.when(BinaryNode) + def visit(self, node, tabs=0): + ans = '\t' * tabs + f'\\__ {node.__class__.__name__} ' + left = self.visit(node.left, tabs + 1) + right = self.visit(node.right, tabs + 1) + return f'{ans}\n{left}\n{right}' + + @visitor.when(FunctionCallNode) + def visit(self, node, tabs=0): + obj = self.visit(node.obj, tabs + 1) + typex = f'@{node.type.lex}' if node.type else '' + ans = '\t' * tabs + f'\\__FunctionCallNode: {typex}.{node.id.lex}(, ..., )' + args = '\n'.join(self.visit(arg, tabs + 1) for arg in node.args) + return f'{ans}\n{obj}\n{args}' + + @visitor.when(MemberCallNode) + def visit(self, node, tabs=0): + ans = '\t' * tabs + f'\\__MemberCallNode: {node.id.lex}(, ..., )' + args = '\n'.join(self.visit(arg, tabs + 1) for arg in node.args) + return f'{ans}\n{args}' + + @visitor.when(NewNode) + def visit(self, node, tabs=0): + return '\t' * tabs + f'\\__ NewNode: new {node.type.lex}' + + @visitor.when(AtomicNode) + def visit(self, node, tabs=0): + return '\t' * tabs + f'\\__ {node.__class__.__name__}: {node.token.lex}' \ No newline at end of file From 42f70ade3d30a3faafa8a55352da7560b72104a2 Mon Sep 17 00:00:00 2001 From: mavaldivie Date: Tue, 22 Feb 2022 23:42:52 -0500 Subject: [PATCH 63/75] Fixed codegen test to its original state --- tests/codegen/complex.cl | 12 ++++++------ tests/codegen/fib.cl | 29 +++++++++++++++++++++++++++++ tests/codegen/fib_input.txt | 1 + tests/codegen/fib_output.txt | 2 ++ tests/codegen/graph_input.txt | 1 - tests/codegen/new_complex.cl | 12 ++++++------ 6 files changed, 44 insertions(+), 13 deletions(-) create mode 100644 tests/codegen/fib.cl create mode 100644 tests/codegen/fib_input.txt create mode 100644 tests/codegen/fib_output.txt diff --git a/tests/codegen/complex.cl b/tests/codegen/complex.cl index 38cb9fe73..0b7aa44e9 100755 --- a/tests/codegen/complex.cl +++ b/tests/codegen/complex.cl @@ -15,8 +15,8 @@ class Complex inherits IO { init(a : Int, b : Int) : Complex { { - x <- a; - y <- b; + x = a; + y = b; self; } }; @@ -30,22 +30,22 @@ class Complex inherits IO { reflect_0() : Complex { { - x <- ~x; - y <- ~y; + x = ~x; + y = ~y; self; } }; reflect_X() : Complex { { - y <- ~y; + y = ~y; self; } }; reflect_Y() : Complex { { - x <- ~x; + x = ~x; self; } }; diff --git a/tests/codegen/fib.cl b/tests/codegen/fib.cl new file mode 100644 index 000000000..08ceaede8 --- /dev/null +++ b/tests/codegen/fib.cl @@ -0,0 +1,29 @@ +class Main inherits IO { + -- the class has features. Only methods in this case. + main(): Object { + { + out_string("Enter n to find nth fibonacci number!\n"); + out_int(fib(in_int())); + out_string("\n"); + } + }; + + fib(i : Int) : Int { -- list of formals. And the return type of the method. + let a : Int <- 1, + b : Int <- 0, + c : Int <- 0 + in + { + while (not (i = 0)) loop -- expressions are nested. + { + c <- a + b; + i <- i - 1; + b <- a; + a <- c; + } + pool; + c; + } + }; + +}; diff --git a/tests/codegen/fib_input.txt b/tests/codegen/fib_input.txt new file mode 100644 index 000000000..f599e28b8 --- /dev/null +++ b/tests/codegen/fib_input.txt @@ -0,0 +1 @@ +10 diff --git a/tests/codegen/fib_output.txt b/tests/codegen/fib_output.txt new file mode 100644 index 000000000..d402da6af --- /dev/null +++ b/tests/codegen/fib_output.txt @@ -0,0 +1,2 @@ +Enter n to find nth fibonacci number! +89 diff --git a/tests/codegen/graph_input.txt b/tests/codegen/graph_input.txt index 939229b0d..b67d90210 100644 --- a/tests/codegen/graph_input.txt +++ b/tests/codegen/graph_input.txt @@ -3,4 +3,3 @@ 3 2,10 4 3,55 5,100 5 1,1 2,2 3,3 4,4 5,5 - diff --git a/tests/codegen/new_complex.cl b/tests/codegen/new_complex.cl index 5e7240b1c..a4fe714ce 100755 --- a/tests/codegen/new_complex.cl +++ b/tests/codegen/new_complex.cl @@ -23,8 +23,8 @@ class Complex inherits IO { init(a : Int, b : Int) : Complex { { - x <- a; - y <- b; + x = a; + y = b; self; } }; @@ -38,22 +38,22 @@ class Complex inherits IO { reflect_0() : Complex { { - x <- ~x; - y <- ~y; + x = ~x; + y = ~y; self; } }; reflect_X() : Complex { { - y <- ~y; + y = ~y; self; } }; reflect_Y() : Complex { { - x <- ~x; + x = ~x; self; } }; From f22da46231d50263315941313f6fd07de849bb15 Mon Sep 17 00:00:00 2001 From: ClaudiaOM Date: Tue, 22 Feb 2022 22:58:35 -0600 Subject: [PATCH 64/75] Update Readme --- doc/Readme.md | 27 +++++++++++++++++++++++---- 1 file changed, 23 insertions(+), 4 deletions(-) diff --git a/doc/Readme.md b/doc/Readme.md index ad20d7a26..bf1cbec7f 100644 --- a/doc/Readme.md +++ b/doc/Readme.md @@ -27,7 +27,8 @@ componen el pipeline de ejecucion del compilador #### Lexer El Lexer es el encargado de dado un string con el código del programa COOL separar el mismo en tokens -para luego ser usado por el parser. En esta fase se utilizo el paquete _ply_ el cual permite ...breve descripcion de ply... . Se definieron +para luego ser usado por el parser. En esta fase se utilizo el paquete _ply_ el cual contiene +herramientas de parser en Python . Se definieron las expresiones regulares y simbolos que correspondian a los tokens de la gramatica. Ademas se almacena por cada Token la linea y la columna correspondiente en el codigo, lo que permite tener mayor informacion en los mensajes de error @@ -36,15 +37,33 @@ y para nuestro uso en la depuracion. #### Parser +El Parser define la estructura que tendrá el Árbol de Sintaxis Abstracta (AST) de la aplicación, además +de la gramática que se usará para parsear el código COOL dado. +El archivo donde se definen los simbolos y producciones de la gramatica puede verse en +[Gramatica COOL](https://github.com/NinjaProgrammers/cool-compiler-2021/blob/Proyecto-CMP/src/core/parser/Parser.py) + #### Recoleccion de tipos En esta fase se recorren todas las declaraciones de clases, se crean los tipos asociados -y se valida que no se este redefiniendo una clase. +y se valida que no se este redefiniendo una clase. Primeramente se annaden los tipos builtin +(Object, IO, Bool, String, Int) y luego se anaden los tipos definidos por el usuario, revisando +que no existan nombres de clases repetidos. + +#### Construccion de Tipos +En esta fase se recorren nuevamente las declaraciones de clases annadiendo los metodos y +atributos de cada clase. Se encarga de definir la herencia, en caso que no exista se hereda de la +clase Object. Ademas se revisa que exista una clase Main con un metodo main que indica el inicio +de la ejecucion del programa COOL. -### Modulos +#### Chequeo de tipos + +En esta fase se revisa la compatibilidad de tipos (Ej: que no se sume int y string), que variables +o metodos hayan sido definidos previamente, correctitud de la herencia (que no exista herencia ciclica) + +#### COOL a CIL -### Gramatica + ### Organizacion From 1773168e7056bb5cf2833697e9bbeac3a33d15c4 Mon Sep 17 00:00:00 2001 From: mavaldivie Date: Wed, 23 Feb 2022 00:00:28 -0500 Subject: [PATCH 65/75] Added divide by zero runtime exception --- src/core/cil/COOLToCILVisitor.py | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/src/core/cil/COOLToCILVisitor.py b/src/core/cil/COOLToCILVisitor.py index a6735a510..9ec636a5f 100644 --- a/src/core/cil/COOLToCILVisitor.py +++ b/src/core/cil/COOLToCILVisitor.py @@ -421,13 +421,25 @@ def visit(self, node: cool.DivNode, scope: Scope): left = self.define_internal_local(line=node.line, column=node.column) right = self.define_internal_local(line=node.line, column=node.column) value = self.define_internal_local(line=node.line, column=node.column) + zero = self.define_internal_local(line=node.line, column=node.column) self.visit(node.left, scope.childs[0]) self.register_instruction(cil.GetAttribNode(left, node.left.ret_expr, 'value', 'Int', line=node.left.line, column=node.left.line)) self.visit(node.right, scope.childs[1]) + + self.register_instruction(cil.ArgNode(0, line=node.line, column=node.column)) + self.register_instruction(cil.StaticCallNode(self.init_name('Int'), zero, line=node.line, column=node.column)) + self.register_instruction(cil.GetAttribNode(zero, zero, 'value', 'Int', + line=node.right.line, column=node.right.line)) self.register_instruction(cil.GetAttribNode(right, node.right.ret_expr, 'value', 'Int', line=node.right.line, column=node.right.line)) + self.register_instruction(cil.EqualNode(zero, zero, right, + line=node.line, column=node.column)) + self.register_runtime_error(zero, f'{node.right.line, node.right.column} - ' + f'RuntimeError: Division by zero', + line=node.right.line, column=node.right.column) + self.register_instruction(cil.DivNode(value, left, right, line=node.line, column=node.column)) self.register_instruction(cil.ArgNode(value, line=node.line, column=node.column)) From a51ca71975338520d662cc333ea2a3b2b465fcc6 Mon Sep 17 00:00:00 2001 From: ClaudiaOM Date: Wed, 23 Feb 2022 10:47:50 -0600 Subject: [PATCH 66/75] Update Readme. Return complex and graph to original state --- doc/Readme.md | 18 ++++++++++++++---- tests/codegen/complex.cl | 10 +++++----- tests/codegen/graph_input.txt | 1 - 3 files changed, 19 insertions(+), 10 deletions(-) diff --git a/doc/Readme.md b/doc/Readme.md index bf1cbec7f..ebd1fd8c0 100644 --- a/doc/Readme.md +++ b/doc/Readme.md @@ -63,13 +63,23 @@ o metodos hayan sido definidos previamente, correctitud de la herencia (que no e #### COOL a CIL - +Durante esta fase se realiza la conversion del lenguaje COOL al lenguaje intermedio +CIL. +En el fichero BaseCOOLtoCILVisitor se definen los metodos basicos para registrar +una variable, parametro, funcion, atributo, entre otros. Ademas es aqui donde se +registran los tipos builtin, es decir se escriben en codigo CIL las instrucciones +para registrar los tipos Object, IO, String, Int y Bool. +El fichero COOLToCILVisitor .... ???????? -### Organizacion +#### CIL a MIPS + +Esta es la fase final donde se traduce del lenguaje intermedio al lenguaje MIPS que +es el que va a ser ejecutado. +Dentro del fichero mips_basics.asm -La estructura de archivos del proyecto es la siguiente: +### Organizacion - +La estructura de archivos del proyecto es la siguiente: ``` cool-compiler-2021 diff --git a/tests/codegen/complex.cl b/tests/codegen/complex.cl index 38cb9fe73..afe81b381 100755 --- a/tests/codegen/complex.cl +++ b/tests/codegen/complex.cl @@ -15,8 +15,8 @@ class Complex inherits IO { init(a : Int, b : Int) : Complex { { - x <- a; - y <- b; + x = a; + y = b; self; } }; @@ -30,15 +30,15 @@ class Complex inherits IO { reflect_0() : Complex { { - x <- ~x; - y <- ~y; + x = ~x; + y = ~y; self; } }; reflect_X() : Complex { { - y <- ~y; + y = ~y; self; } }; diff --git a/tests/codegen/graph_input.txt b/tests/codegen/graph_input.txt index 939229b0d..b67d90210 100644 --- a/tests/codegen/graph_input.txt +++ b/tests/codegen/graph_input.txt @@ -3,4 +3,3 @@ 3 2,10 4 3,55 5,100 5 1,1 2,2 3,3 4,4 5,5 - From d7eb709ee245a67dcb07af90d03e2e2e3dcb358d Mon Sep 17 00:00:00 2001 From: ClaudiaOM Date: Wed, 23 Feb 2022 16:50:54 -0600 Subject: [PATCH 67/75] Update Readme. --- doc/Readme.md | 42 +++++++++++++++++++++++++++++++++++++++++- 1 file changed, 41 insertions(+), 1 deletion(-) diff --git a/doc/Readme.md b/doc/Readme.md index ebd1fd8c0..3d8c32318 100644 --- a/doc/Readme.md +++ b/doc/Readme.md @@ -75,7 +75,11 @@ El fichero COOLToCILVisitor .... ???????? Esta es la fase final donde se traduce del lenguaje intermedio al lenguaje MIPS que es el que va a ser ejecutado. -Dentro del fichero mips_basics.asm +Dentro del fichero mips_basics.asm se encuentran funciones predefinidas en mips: +Malloc, Copy, Read String, Equal String, Length, Substring y Concat. +En CILToMipsVisitor se visita cada nodo del arbol de CIL y se traduce la instruccion +a las correspondientes instrucciones en codigo Mips. .............?????????/?? + ### Organizacion @@ -125,3 +129,39 @@ Se omitieron algunos archivos pues no son relevantes en la implementacion del co ## Principales Problemas + + + + +# Gramatica + +### Terminales +A continuacion se muestran los terminales de la gramatica, donde entre parentesis se muestran +los simbolos que corresponden a COOL en los casos en que era requerido: + +- *class, inherits, function* +- *ifx (if), then, elsex (else), fi* +- *whilex (while), loop, pool* +- *let, inx (in)* +- *case, of, esac* +- *semi (;), colon (:), comma (,), dot(.), at (@), opar((), cpar ())* +- *ocur ({), ccur (}), larrow (<-), rarrow (=>)* +- *plus (+), minus (-), star (\*), div (/), isvoid, compl (~)* +- *notx (not), less (<), leq (<=), equal (=)* +- *new, idx (id), typex (type), integer, string, boolx (bool)* + + +### No Terminales + +A continuacion se muestran los no terminales de la gramatica definida: +- __program__ +- __class_list, def_class__ +- __feature_list, feature__ +- __param_list, param__ +- __expr_1, expr_2, member_call, expr_list, let_list, case_list__ +- __comp_expr, arith, arith_2, term, factor, factor_2__ +- __atom, func_call, arg_list__ + +### Producciones de la Gramatica + +__program__ Ⅵ From 27c1d0f22969620b404afbac4bb1f2d423653717 Mon Sep 17 00:00:00 2001 From: ClaudiaOM Date: Wed, 23 Feb 2022 21:47:17 -0600 Subject: [PATCH 68/75] Update Readme an requirements.txt --- doc/Readme.md | 137 ++++++++++++++++++++++++++++++++++++++++++----- requirements.txt | 6 +-- 2 files changed, 128 insertions(+), 15 deletions(-) diff --git a/doc/Readme.md b/doc/Readme.md index 3d8c32318..b1aa06197 100644 --- a/doc/Readme.md +++ b/doc/Readme.md @@ -10,7 +10,34 @@ ## Uso del compilador +**Requerimientos** + +Para ejecutar el proyecto es necesario instalar Python y las dependencias que +se listan en requirements.txt + +Para instalar las dependencias puede ubicarse en la carpeta del proyecto y ejecutar + +```bash +pip install -r requirements.txt +``` + +**Ejecucion** + +Para ejecutar el proyecto debe situarse en la carpeta *src* y utilizar algun de los + dos comandos que se muestran a continuacion +```bash +python Main.py -f path/to/file.cl +python Main.py --file path/to/file.cl +``` + +Como salida se guarda en un fichero del mismo nombre del introducido como parametro +pero con extension *.mips* el codigo compilado y listo para ejecutarse. + ## Arquitectura del compilador + +El compilador esta compuesto por distintos modulos que estan encargados de tomar +el codigo escrito inicialmente en COOL y obtener el resultado final en codigo MIPS que +permita su ejecucion. ### Fases (_Pipeline_) @@ -78,13 +105,18 @@ es el que va a ser ejecutado. Dentro del fichero mips_basics.asm se encuentran funciones predefinidas en mips: Malloc, Copy, Read String, Equal String, Length, Substring y Concat. En CILToMipsVisitor se visita cada nodo del arbol de CIL y se traduce la instruccion -a las correspondientes instrucciones en codigo Mips. .............?????????/?? +a las correspondientes instrucciones en codigo Mips. + + + +## Principales Problemas + -### Organizacion +## Organizacion -La estructura de archivos del proyecto es la siguiente: - +La estructura de archivos del proyecto es la siguiente: + ``` cool-compiler-2021 |___doc @@ -127,19 +159,14 @@ cool-compiler-2021 Se omitieron algunos archivos pues no son relevantes en la implementacion del compilador. -## Principales Problemas - - - - -# Gramatica +## Gramatica ### Terminales A continuacion se muestran los terminales de la gramatica, donde entre parentesis se muestran los simbolos que corresponden a COOL en los casos en que era requerido: -- *class, inherits, function* +- *classx (class), inherits, function* - *ifx (if), then, elsex (else), fi* - *whilex (while), loop, pool* - *let, inx (in)* @@ -154,6 +181,7 @@ los simbolos que corresponden a COOL en los casos en que era requerido: ### No Terminales A continuacion se muestran los no terminales de la gramatica definida: + - __program__ - __class_list, def_class__ - __feature_list, feature__ @@ -163,5 +191,90 @@ A continuacion se muestran los no terminales de la gramatica definida: - __atom, func_call, arg_list__ ### Producciones de la Gramatica +A continuacion se muestran las producciones de la gramatica. En *cursiva* se muestran los terminales +y en **negrita** los no terminales + +__program__ ⟶ __class_list__ + +__class_list__ ⟶ __def_class class_list__ +__class_list__ ⟶ __def_class__ + +**def_class** ⟶ *class* *typex* { **feature_list** } ; +**def_class** ⟶ *class* *typex* *inherits* *typex* { **feature_list** } ; + +**feature_list** ⟶ **feature feature_list** +**feature_list** ⟶ 𝜺 + +**feature** ⟶ *idx* : *typex* ; +**feature** ⟶ *idx* : *typex* <- **expr_1**; +**feature** ⟶ *idx* (**param_list**) : *typex* { **expr_1** } ; +**feature** ⟶ *idx* () : *typex* { **expr_1** }; +**feature** ⟶ **function** *idx* (**param_list**) : *typex* {**expr_1**}; +**feature** ⟶ **function** *idx* () : *typex* {**expr_1**}; + +**param_list** ⟶ **param** +**param_list** ⟶ **param** , **param_list** + +**param** ⟶ *idx* : *typex* + +**expr_1** ⟶ *idx* <- **expr_1** +**expr_1** ⟶ *not* **expr_1** +**expr_1** ⟶ **expr_2** = **expr_1** +**expr_1** ⟶ **expr_2** + +**expr_2** ⟶ **arith** < **arith** +**expr_2** ⟶ **arith** <= **arith** +**expr_2** ⟶ **arith** + +**arith** ⟶ **arith** + **factor** +**arith** ⟶ **arith** - **factor** +**arith** ⟶ **factor** + +**factor** ⟶ **factor** \* **term** +**factor** ⟶ **factor** / **term** +**factor** ⟶ **term** + +**term** ⟶ ~ **term** +**term** ⟶ *isvoid* **term** +**term** ⟶ **atom** + +**atom** ⟶ (**expr_1**) +**atom** ⟶ *string* +**atom** ⟶ *bool* +**atom** ⟶ *idx* +**atom** ⟶ *ifx* **expr_1** *then* **expr_1** *else* **expr_1** *fi* +**atom** ⟶ *whilex* **expr_1** *loop* **expr_1** *pool* +**atom** ⟶ *new* *typex* + +**atom** ⟶ { **expr_list** } +**expr_list** ⟶ **expr_1** ; +**expr_list** ⟶ **expr_1** ; **expr_list** + +**atom** ⟶ *case* **expr_1** *of* **case_list** *esac* +**case_list** ⟶ *idx* : *typex* => **expr_1** ; +**case_list** ⟶ *idx* : *typex* => **expr_1** ; **case_list** + +**atom** ⟶ **func_call** +**func_call** ⟶ **atom** @ *typex* . *idx* (**arg_list**) +**func_call** ⟶ **atom** @ *typex* . *idx* () +**func_call** ⟶ **atom** . *idx* (**arg_list**) +**func_call** ⟶ **atom** . *idx* () + +**func_call** ⟶ *idx* (**arg_list**) +**func_call** ⟶ *idx* () + +**arg_list** ⟶ **expr_list** +**arg_list** ⟶ **expr_list** , **arg_list** + +## Licencia + +[MIT](https://github.com/NinjaProgrammers/cool-compiler-2021/blob/Proyecto-CMP/LICENSE) + + + + + + + + -__program__ Ⅵ diff --git a/requirements.txt b/requirements.txt index 5a914fd88..46bf2782f 100644 --- a/requirements.txt +++ b/requirements.txt @@ -1,3 +1,3 @@ -pytest -pytest-ordering -ply \ No newline at end of file +ply>=3.11 +pydot>=1.4.2 +pytest>=7.0.1 From 61375f2984043c31f5050b2131fa404f0e1194b7 Mon Sep 17 00:00:00 2001 From: ClaudiaOM Date: Thu, 24 Feb 2022 11:52:02 -0500 Subject: [PATCH 69/75] Added info to Readme file --- doc/Readme.md | 234 ++++++++++++++++++++++++++++++-------------------- 1 file changed, 142 insertions(+), 92 deletions(-) diff --git a/doc/Readme.md b/doc/Readme.md index b1aa06197..d5fd59011 100644 --- a/doc/Readme.md +++ b/doc/Readme.md @@ -1,12 +1,12 @@ -# Informe de Complementos de Compilacion +# Informe de Complementos de Compilación ## Compilador de Cool *** ## Autores -- Claudia Olavarrieta Martinez -- Marcos Adrian Valdivie Rodriguez -- Adrian Hernandez Perez +- Claudia Olavarrieta Martínez +- Marcos Adrián Valdivié Rodríguez +- Adrián Hernández Pérez ## Uso del compilador @@ -21,97 +21,23 @@ Para instalar las dependencias puede ubicarse en la carpeta del proyecto y ejecu pip install -r requirements.txt ``` -**Ejecucion** +**Ejecución** -Para ejecutar el proyecto debe situarse en la carpeta *src* y utilizar algun de los - dos comandos que se muestran a continuacion +Para ejecutar el proyecto debe situarse en la carpeta *src* y utilizar alguno de los +dos comandos que se muestran a continuación: ```bash python Main.py -f path/to/file.cl python Main.py --file path/to/file.cl ``` -Como salida se guarda en un fichero del mismo nombre del introducido como parametro -pero con extension *.mips* el codigo compilado y listo para ejecutarse. +Como salida se guarda en un fichero del mismo nombre del introducido como parámetro +con extensión *.mips* el código compilado y listo para ejecutarse. ## Arquitectura del compilador -El compilador esta compuesto por distintos modulos que estan encargados de tomar -el codigo escrito inicialmente en COOL y obtener el resultado final en codigo MIPS que -permita su ejecucion. - -### Fases (_Pipeline_) - -El fichero _Main.py_ contiene los llamados en orden de los metodos que -componen el pipeline de ejecucion del compilador -1. Lexer -2. Parser -3. Recoleccion de Tipos -4. Construccion de Tipos -5. Chequeo de Tipos -6. COOL a CIL -7. CIL a Mips - -#### Lexer - -El Lexer es el encargado de dado un string con el código del programa COOL separar el mismo en tokens -para luego ser usado por el parser. En esta fase se utilizo el paquete _ply_ el cual contiene -herramientas de parser en Python . Se definieron -las expresiones regulares y simbolos que correspondian a los tokens de la -gramatica. Ademas se almacena por cada Token la linea y la columna correspondiente -en el codigo, lo que permite tener mayor informacion en los mensajes de error -y para nuestro uso en la depuracion. - - -#### Parser - -El Parser define la estructura que tendrá el Árbol de Sintaxis Abstracta (AST) de la aplicación, además -de la gramática que se usará para parsear el código COOL dado. -El archivo donde se definen los simbolos y producciones de la gramatica puede verse en -[Gramatica COOL](https://github.com/NinjaProgrammers/cool-compiler-2021/blob/Proyecto-CMP/src/core/parser/Parser.py) - -#### Recoleccion de tipos - -En esta fase se recorren todas las declaraciones de clases, se crean los tipos asociados -y se valida que no se este redefiniendo una clase. Primeramente se annaden los tipos builtin -(Object, IO, Bool, String, Int) y luego se anaden los tipos definidos por el usuario, revisando -que no existan nombres de clases repetidos. - -#### Construccion de Tipos - -En esta fase se recorren nuevamente las declaraciones de clases annadiendo los metodos y -atributos de cada clase. Se encarga de definir la herencia, en caso que no exista se hereda de la -clase Object. Ademas se revisa que exista una clase Main con un metodo main que indica el inicio -de la ejecucion del programa COOL. - -#### Chequeo de tipos - -En esta fase se revisa la compatibilidad de tipos (Ej: que no se sume int y string), que variables -o metodos hayan sido definidos previamente, correctitud de la herencia (que no exista herencia ciclica) - -#### COOL a CIL - -Durante esta fase se realiza la conversion del lenguaje COOL al lenguaje intermedio -CIL. -En el fichero BaseCOOLtoCILVisitor se definen los metodos basicos para registrar -una variable, parametro, funcion, atributo, entre otros. Ademas es aqui donde se -registran los tipos builtin, es decir se escriben en codigo CIL las instrucciones -para registrar los tipos Object, IO, String, Int y Bool. -El fichero COOLToCILVisitor .... ???????? - -#### CIL a MIPS - -Esta es la fase final donde se traduce del lenguaje intermedio al lenguaje MIPS que -es el que va a ser ejecutado. -Dentro del fichero mips_basics.asm se encuentran funciones predefinidas en mips: -Malloc, Copy, Read String, Equal String, Length, Substring y Concat. -En CILToMipsVisitor se visita cada nodo del arbol de CIL y se traduce la instruccion -a las correspondientes instrucciones en codigo Mips. - - - -## Principales Problemas - - +El compilador está compuesto por distintos módulos que son los encargados de tomar +el código escrito inicialmente en COOL y obtener el resultado final en código MIPS que +permita su ejecución. ## Organizacion @@ -158,13 +84,47 @@ cool-compiler-2021 ``` Se omitieron algunos archivos pues no son relevantes en la implementacion del compilador. + +### Fases (_Pipeline_) +El fichero _Main.py_ contiene el pipeline de ejecución del compilador +1. Lexer +2. Parser +3. Recolección de Tipos +4. Construcción de Tipos +5. Chequeo de Tipos +6. COOL a CIL +7. CIL a Mips +8. MIPS a representación como string +9. Escritura en el archivo final + +#### Lexer + +El Lexer es el encargado de dado un string con el código del programa COOL separar el mismo en tokens +para luego ser usado por el parser. En esta fase se utilizó el paquete _ply_ el cual contiene +herramientas de tokenización. Se definieron las expresiones regulares y símbolos que correspondian a los +tokens de la gramática, definiendo reglas especiales para poder también reconocer los string y comentarios +anidados. Además se almacena por cada Token la línea y la columna correspondiente +en el código, lo que permite tener mayor información en los mensajes de error +y para nuestro uso en la depuración. + +#### Parser + +El Parser define la estructura que tendrá el Árbol de Sintaxis Abstracta (AST) del lenguaje COOL, además +de la gramática que se usará para parsear este. +El archivo donde se definen los símbolos y producciones de la gramática puede verse en +[Gramatica COOL](https://github.com/NinjaProgrammers/cool-compiler-2021/blob/Proyecto-CMP/src/core/parser/Parser.py) -## Gramatica +Se utilizó un parser LR1 que había sido implementado y probado en proyectos anteriores +de la asignatura. Fue necesaria la inclusión de nuevas reglas y la edición de algunas anteriores para la +solución de problemas con la precedencia de los operadores y la ambiguedad de la gramática. +Durante esta fase se reconocen y reportan los errores léxicos del programa. + +## Gramática ### Terminales -A continuacion se muestran los terminales de la gramatica, donde entre parentesis se muestran -los simbolos que corresponden a COOL en los casos en que era requerido: +A continuación se muestran los terminales de la gramática, donde entre paréntesis se muestran +los símbolos que corresponden a COOL en los casos en que era requerido: - *classx (class), inherits, function* - *ifx (if), then, elsex (else), fi* @@ -180,7 +140,7 @@ los simbolos que corresponden a COOL en los casos en que era requerido: ### No Terminales -A continuacion se muestran los no terminales de la gramatica definida: +A continuación se muestran los no terminales de la gramática definida: - __program__ - __class_list, def_class__ @@ -190,8 +150,8 @@ A continuacion se muestran los no terminales de la gramatica definida: - __comp_expr, arith, arith_2, term, factor, factor_2__ - __atom, func_call, arg_list__ -### Producciones de la Gramatica -A continuacion se muestran las producciones de la gramatica. En *cursiva* se muestran los terminales +### Producciones de la Gramática +A continuación se muestran las producciones de la gramática. En *cursiva* se muestran los terminales y en **negrita** los no terminales __program__ ⟶ __class_list__ @@ -266,6 +226,96 @@ __class_list__ ⟶ __def_class__ **arg_list** ⟶ **expr_list** **arg_list** ⟶ **expr_list** , **arg_list** +#### Recolección de tipos + +En esta fase se recorren todas las declaraciones de clases, se crean los tipos asociados +y se valida que no se estén redefiniendo estos. Primeramente se añaden los tipos builtin +(Object, IO, Bool, String, Int) y luego los tipos definidos por el usuario, revisando +que no existan nombres de clases repetidos. + +#### Construcción de Tipos + +En esta fase se recorren nuevamente las declaraciones de clases añadiendo los métodos y +atributos de cada clase. Se define la herencia para cada clase, en caso que no exista se hereda de la +clase Object. Además se revisa que exista una clase Main con un método main que indica el inicio +de la ejecución del programa COOL. + +#### Chequeo de tipos + +En esta fase se hace el chequeo semántico de cada tipo. Se evalúa para cada expresión su tipo de retorno +y se valida que estos cumplan las reglas semánticas definidas en el lenguaje. Además se chequea nuevamente +las clases definidas por el usuario en busca de la existencia de herencia cíclica. +Algunos de los errores que se chequean en esta fase son: +- Las operaciones aritméticas solo están definidas para el tipo Int. +- Que las variables y los métodos hayan sido definidos previamente a su uso. +- Que no exista redefinición de métodos o atributos en las clases. +- Que no exista herencia cíclica. +- Que las palabras claves self y SELF_TYPE sean usadas correctamente. +- Que las funciones de clases sean llamadas con la cantidad correcta de parámetros. +- Que el objeto usado en cada expresión sea compatible con el tipo declarado para la misma. + +#### COOL a CIL + +Durante esta fase se realiza la conversión del lenguaje COOL a un lenguaje intermedio(CIL). +El fichero CilAst contiene la definición de las clases usadas para conformar el AST del lenguaje CIL. +En el fichero BaseCOOLtoCILVisitor se definen los métodos básicos para registrar +una variable, parámetro, función y atributo, entre otros. Ademas se +registran los tipos builtin, es decir, se escriben en código CIL las instrucciones +para registrar los tipos Object, IO, String, Int y Bool, así como las funciones y atributos de cada uno de estos.. +El fichero COOLToCILVisitor es el encargado de transformar el AST de COOL en un AST de CIL, para facilitar +luego la traducción de este al lenguaje MIPS. Este fichero cuenta con un visitor que se encarga de transformar +cada nodo del AST de un lenguaje a otro, algunos de los aspectos a destacar son: +- En el visitor del ProgramNode se define la función entry, que es por donde se comenzará la ejecución del +programa MIPS. +- En el visitor del ClassDeclarationNode se definen las funciones init e init_attr corespondientes a cada +clase, estas funciones son las encargadas de reservar la memoria necesaria para cada uno de los tipos +definidos por el usuario. +- Especial énfasis en el visitor del CaseOfNode, este se encarga de generar todas las instrucciones +necesarias para validar correctamente la rama que debe ejecutarse, o mostrar un error en tiempo de ejecución +en caso de no haber ninguna válida. Para lograr esto primero se ordenan los tipo involucrados en las ramas del +case según su profundidad en el árbol de herencia del programa, de mayor a menor. Luego se visitan estos, +revisando para cada uno todos sus descendientes en dicho árbol de herencia y comprobando, en tiempo de +ejecución, si estos coinciden con el tipo del objeto que se está analizando. El primer objeto para el que +se halle una correspondencia define la rama por la que debe continuar la ejecución del programa. +- En el visitor del DivNode se añaden las instrucciones necesarias para verificar que el divisor es distinto +de cero, y lanzar un error en tiempo de ejecución en caso contrario. +- El visitor del EqualNode se encarga de verificar primeramente los tipos de los objetos que están siendo +comparados, Int-Int, String-String y Bool-Bool son comparados por valor, mientras que cualquier otro par +es comparado por referencia. +- El visitor del FunctionCallNode se encarga de verificar que el objeto al cual se le hace el dispatch sea +distinto de Void y mostrar un error en ejecución en caso contrario. + +#### CIL a MIPS + +Esta es la fase final donde se traduce de CIL al lenguaje MIPS que da la salida del programa. +Dentro del fichero mips_basics.asm se encuentran algunas funciones predefinidas en mips: malloc, copy, +read_string, equal_string, length, substring y concat. +El fichero MIPSAst contiene la definición de las clases necesarias para representar el código MIPS. +El fichero CILToMipsVisitor visita cada nodo del AST de CIL y lo traduce s su correspondientes +instrucciones en codigo Mips. Gran dificultad trajo en esta fase el uso correcto de las tablas de dispatch +y los atributos de clase en combinación con la herencia, haciendo necesaria una especificación sobre la +representación en memoria que tendría cada objeto. Sobre esto útimo podemos explicar que se decidió representar +los objetos como: +- Marca de clase (4 bytes): Un entero usado para identificar cada tipo del programa. +- Tamaño (4 bytes): Un entero usado para representar el tamaño, en doble palabras, de la representación del objeto +en memoria. +- Puntero a la tabla de dispatch (4 bytes): Un entero que representa la posición en memoria donde se encuentra +la tabla de dispatch del objeto. +- Definición de atributos de la clase padre. +- Definición de atributos de la clase hijo. Primero son definidos los atributos de la clase padre de forma recursiva, +luego son definidos los atributos de la clase hijo, colocando estos de forma ordenada según los nombres que tenían +en el código COOL inicial. +Las tablas de dispatch de cada tipo se definen de forma similar, primero las direcciones de memoria correspondientes +a las funciones de las clases padres, o a las de la clase hijo en caso de que hayan sido redefinidas, y luego las +direcciones de memoria de las funciones de la clase hijo, ordenadas alfabéticamente según sus nombres iniciales. +Finalmente las funciones init e init_attr de la clase correspondiente. Cada dirección de memoria corresponde a un +entero de 32 bits. El orden de las funciones en la tabla de dispatch inicia por las del padre para poder ejecutar +correctamente el llamado a una función redefinida en un objeto del tipo hijo cuando este es tratado como un +objeto del tipo padre (polimorfismo). + +Finalmente, el fichero MIPSAstFormatter es el encargado de transformar el AST de MIPS a formato string para +luego escribir este en el archivo final. + ## Licencia [MIT](https://github.com/NinjaProgrammers/cool-compiler-2021/blob/Proyecto-CMP/LICENSE) From fbd42d9baeecec8f28e514bc39c53788e38e41dd Mon Sep 17 00:00:00 2001 From: ClaudiaOM Date: Thu, 24 Feb 2022 12:11:36 -0500 Subject: [PATCH 70/75] Refactored folders and file names --- src/Main.py | 14 +- src/Tools/CodeGen/Mips/mips _syntax.py | 371 ------------ src/Tools/Lexer/Lexer.py | 272 --------- src/Tools/Parser/Parser.py | 138 ----- src/Tools/Semantic/Type_Builder.py | 157 ----- src/Tools/Semantic/Type_Checker.py | 509 ---------------- src/Tools/Semantic/Type_Collector.py | 38 -- src/Tools/Tools/Errors.py | 66 --- src/Tools/Tools/Parser_LR1.py | 185 ------ src/Tools/Tools/Semantic.py | 288 --------- src/Tools/Tools/utils.py | 221 ------- src/Tools/Tools/visitor.py | 80 --- ...oCILVisitor.py => BaseCoolToCilVisitor.py} | 2 +- src/core/cil/{CILAst.py => CilAst.py} | 0 ...OOLToCILVisitor.py => CoolToCilVisitor.py} | 4 +- .../Tools/COOLAst.py => core/cool/CoolAst.py} | 0 .../cool/CoolAstFormatter.py} | 10 +- src/core/format_visitor.py | 116 ---- src/core/lexer/Lexer.py | 2 +- src/core/mips/CilToMipsVisitor.py | 2 +- ...IPSAstFormatter.py => MipsAstFormatter.py} | 2 +- .../mips/{mips_basics.asm => MipsBasics.asm} | 0 src/core/parser/Parser.py | 6 +- .../{Type_Builder.py => TypeBuilder.py} | 0 .../{Type_Checker.py => TypeChecker.py} | 0 .../{Type_Collector.py => TypeCollector.py} | 0 .../automata.py => core/tools/Automata.py} | 0 src/core/tools/COOLAst.py | 210 ------- .../tools/Evaluation.py} | 2 +- .../tools/FirstsAndFollows.py} | 2 +- src/core/tools/Firsts_and_Follows.py | 128 ---- .../tools/{Parser_LR1.py => ParserLR1.py} | 10 +- .../parsing.py => core/tools/Parsing.py} | 0 .../tools/Pycompiler.py} | 0 src/core/tools/{utils.py => Utils.py} | 2 +- src/core/tools/automata.py | 210 ------- src/core/tools/evaluation.py | 44 -- src/core/tools/parsing.py | 95 --- src/core/tools/pycompiler.py | 560 ------------------ 39 files changed, 29 insertions(+), 3717 deletions(-) delete mode 100644 src/Tools/CodeGen/Mips/mips _syntax.py delete mode 100644 src/Tools/Lexer/Lexer.py delete mode 100644 src/Tools/Parser/Parser.py delete mode 100644 src/Tools/Semantic/Type_Builder.py delete mode 100644 src/Tools/Semantic/Type_Checker.py delete mode 100644 src/Tools/Semantic/Type_Collector.py delete mode 100644 src/Tools/Tools/Errors.py delete mode 100644 src/Tools/Tools/Parser_LR1.py delete mode 100644 src/Tools/Tools/Semantic.py delete mode 100644 src/Tools/Tools/utils.py delete mode 100644 src/Tools/Tools/visitor.py rename src/core/cil/{BaseCOOLToCILVisitor.py => BaseCoolToCilVisitor.py} (99%) rename src/core/cil/{CILAst.py => CilAst.py} (100%) rename src/core/cil/{COOLToCILVisitor.py => CoolToCilVisitor.py} (99%) rename src/{Tools/Tools/COOLAst.py => core/cool/CoolAst.py} (100%) rename src/{Tools/format_visitor.py => core/cool/CoolAstFormatter.py} (92%) delete mode 100644 src/core/format_visitor.py rename src/core/mips/{MIPSAstFormatter.py => MipsAstFormatter.py} (99%) rename src/core/mips/{mips_basics.asm => MipsBasics.asm} (100%) rename src/core/semantic/{Type_Builder.py => TypeBuilder.py} (100%) rename src/core/semantic/{Type_Checker.py => TypeChecker.py} (100%) rename src/core/semantic/{Type_Collector.py => TypeCollector.py} (100%) rename src/{Tools/Tools/automata.py => core/tools/Automata.py} (100%) delete mode 100644 src/core/tools/COOLAst.py rename src/{Tools/Tools/evaluation.py => core/tools/Evaluation.py} (97%) rename src/{Tools/Tools/Firsts_and_Follows.py => core/tools/FirstsAndFollows.py} (99%) delete mode 100644 src/core/tools/Firsts_and_Follows.py rename src/core/tools/{Parser_LR1.py => ParserLR1.py} (97%) rename src/{Tools/Tools/parsing.py => core/tools/Parsing.py} (100%) rename src/{Tools/Tools/pycompiler.py => core/tools/Pycompiler.py} (100%) rename src/core/tools/{utils.py => Utils.py} (99%) delete mode 100644 src/core/tools/automata.py delete mode 100644 src/core/tools/evaluation.py delete mode 100644 src/core/tools/parsing.py delete mode 100644 src/core/tools/pycompiler.py diff --git a/src/Main.py b/src/Main.py index a2f61207e..b634491a3 100644 --- a/src/Main.py +++ b/src/Main.py @@ -1,13 +1,13 @@ from core.lexer.Lexer import Lexer from core.parser.Parser import CoolParser -from core.tools.evaluation import evaluate_reverse_parse -from core.semantic.Type_Collector import Type_Collector -from core.semantic.Type_Builder import Type_Builder -from core.semantic.Type_Checker import Type_Checker -from core.cil.COOLToCILVisitor import COOLToCILVisitor -from core.cil.CILAst import get_formatter +from core.tools.Evaluation import evaluate_reverse_parse +from core.semantic.TypeCollector import Type_Collector +from core.semantic.TypeBuilder import Type_Builder +from core.semantic.TypeChecker import Type_Checker +from core.cil.CoolToCilVisitor import COOLToCILVisitor +from core.cil.CilAst import get_formatter from core.mips.CilToMipsVisitor import CILToMIPSVisitor -from core.mips.MIPSAstFormatter import MIPSAstFormatter +from core.mips.MipsAstFormatter import MIPSAstFormatter import subprocess, re diff --git a/src/Tools/CodeGen/Mips/mips _syntax.py b/src/Tools/CodeGen/Mips/mips _syntax.py deleted file mode 100644 index d0b7bd83c..000000000 --- a/src/Tools/CodeGen/Mips/mips _syntax.py +++ /dev/null @@ -1,371 +0,0 @@ -from enum import Enum -from typing import List, Optional - -DATA_SIZE = 4 - - -class Register(str, Enum): - zero = "$zero" #constant 0 - # used for system calls and procedure return values - v0 = "$v0" - v1 = "$v1" - - # used for passing arguments to procedures,temporary (not preserved across call) - a0 = "$a0" - a1 = "$a1" - a2 = "$a2" - a3 = "$a3" - - # used for local storage; calling procedure saves these,saved temporary (preserved across call) - t0 = "$t0" - t1 = "$t1" - t2 = "$t2" - t3 = "$t3" - t4 = "$t4" - t5 = "$t5" - t6 = "$t6" - t7 = "$t7" - t8 = "$t8" - t9 = "$t9" - - # used for local storage; called procedure saves these - s0 = "$s0" - s1 = "$s1" - s2 = "$s2" - s3 = "$s3" - s4 = "$s4" - s5 = "$s5" - s6 = "$s6" - s7 = "$s7" - sp = "$sp" # stack pointer - fp = "$fp" # frame pointer - ra = "$ra" # used to store return address in procedure call - gp = "$gp" # pointer to area storing global data (data segment) - at = "$at" # reserved for use by the assembler - - # reserved for use by OS kernel - k0 = "$k0" - k1 = "$k1" - - -class Directive(str, Enum): - word = ".word" - half = ".half" - byte = ".byte" - ascii = ".ascii" - asciiz = ".asciiz" - space = ".space" - aling = ".align" - text = ".text" - data = ".data" - - -Reg = Register - -# autowrite a instruction when execute string return function -def autowrite(tabs: int = 1): - def inner(func): - - def wrapper(self: "Mips", *args, **kwargs): - instruction = func(self, *args, **kwargs) - if instruction is not None: - self.write_inst(instruction, tabs) - - return wrapper - - return inner - - -def autowritedata(func): - - - def inner(*args, **kwargs): - instructions = func(*args, **kwargs) - if instructions is list: - for instruction in instructions: - args[0].write_data(instruction) - else: - args[0].write_data(instructions) - - return inner - - -class Mips: - def __init__(self): - self.DOTTEXT: List[str] = [] - self.DOTDATA: List[str] = [] - - - def write_inst(self, instruction: str, tabs: int = 0): - tabstr = "" - for _ in range(tabs): - tabstr += "\t" - self.DOTTEXT.append(f"{tabstr}{instruction}") - - def write_data(self, data: str, tabs: int = 0): - self.DOTDATA.append(f"{data}") - - def compile(self): - return "\n".join( - [Directive.data.value] - + self.DOTDATA - + [] - + [Directive.text.value] - + self.DOTTEXT - ) - - def push(self, register: Register): - self.addi(Reg.sp, Reg.sp, -DATA_SIZE) - self.store_memory(register, self.offset(Reg.sp)) - -# First, load from to address `0($sp)` and then write `addi $sp , $sp , 8` to restore the stack pointer - def pop(self, register: Optional[Register]): - - if register: - self.load_memory(register, self.offset(Reg.sp)) - self.addi(Reg.sp, Reg.sp, DATA_SIZE) - -#Load from a specific address a 32 bits register - def load_memory(self, dst: Register, address: str): - self.lw(dst, address) - - -# Write to a specific address a 32 bits register - def store_memory(self, src: Register, address: str): - self.sw(src, address) - - # Arithmetics - @autowrite() - def add(self, dst: Register, rl: Register, rr: Register): - return f"add {dst}, {rl}, {rr}" - - @autowrite() - def sub(self, dst: Register, rl: Register, rr: Register): - return f"sub {dst}, {rl}, {rr}" - - @autowrite() - def addi(self, dst: Register, rl: Register, value: int): - return f"addi {dst}, {rl}, {int(value)}" - - @autowrite() - def addu(self, dst: Register, rl: Register, rr: Register): - return f"addu {dst}, {rl}, {rr}" - - @autowrite() - def subu(self, dst: Register, rl: Register, rr: Register): - return f"subu {dst}, {rl}, {rr}" - - @autowrite() - def addiu(self, dst: Register, rl: Register, value: int): - return f"addiu {dst}, {rl}, {int(value)}" - - @autowrite() - def multu(self, dst: Register, rl: Register, rr: Register): - return f"multu {dst}, {rl}, {rr}" - - @autowrite() - def mult(self, rl: Register, rr: Register): - return f"mult {rl}, {rr}" - - @autowrite() - def div(self, rl: Register, rr: Register): - return f"div {rl}, {rr}" - - # Logical: - @autowrite() - def andd(self, dst: Register, rl: Register, rr: Register): - return f"and {dst}, {rl}, {rr}" - - @autowrite() - def orr(self, dst: Register, rl: Register, rr: Register): - return f"or {dst}, {rl}, {rr}" - - @autowrite() - def nor(self, dst: Register, rl: Register, rr: Register): - return f"nor {dst}, {rl}, {rr}" - - @autowrite() - def andi(self, dst: Register, rl: Register, value: int): - return f"andi {dst}, {rl}, {int(value)}" - - # TODO: Check this instrucction - @autowrite() - def ori(self, dst: Register, rl: Register, value: int): - return f"or {dst}, {rl}, {int(value)}" - - @autowrite() - def sll(self, dst: Register, rl: Register, value: int): - return f"sll {dst}, {rl}, {int(value)}" - - @autowrite() - def srl(self, dst: Register, rl: Register, value: int): - return f"srl {dst}, {rl}, {int(value)}" - - # DataTransfer: - @autowrite() - def lw(self, dst: Register, address: str): - return f"lw {dst}, {address}" - - @autowrite() - def lb(self, dst: Register, address: str): - return f"lb {dst}, {address}" - - @autowrite() - def sw(self, dst: Register, address: str): - return f"sw {dst}, {address}" - - @autowrite() - def sb(self, dst: Register, address: str): - return f"sb {dst}, {address}" - - @autowrite() - def lui(self, dst: Register, value: int): - return f"lui {dst}, {int(value)}" - - @autowrite() - def la(self, dst: Register, label: str): - return f"la {dst}, {label}" - - @autowrite() - def li(self, dst: Register, value: int): - return f"li {dst}, {int(value)}" - - @autowrite() - def mfhi(self, dst: Register): - return f"mfhi {dst}" - - @autowrite() - def mflo(self, dst: Register): - return f"mflo {dst}" - - @autowrite() - def move(self, dst: Register, rl: Register): - return f"move {dst}, {rl}" - - # Brancing - @autowrite() - def beq(self, rl: Register, rr: Register, address: str): - return f"beq {rl}, {rr}, {address}" - - @autowrite() - def bne(self, rl: Register, rr: Register, address: str): - return f"bne {rl}, {rr}, {address}" - - @autowrite() - def beqz(self, rl: Register, address: str): - return f"beqz {rl}, {address}" - - @autowrite() - def bgt(self, rl: Register, rr: Register, address: str): - return f"bgt {rl}, {rr}, {address}" - - @autowrite() - def bge(self, rl: Register, rr: Register, address: str): - return f"bge {rl}, {rr}, {address}" - - @autowrite() - def blt(self, rl: Register, rr: Register, address: str): - return f"blt {rl}, {rr}, {address}" - - @autowrite() - def ble(self, rl: Register, rr: Register, address: str): - return f"ble {rl}, {rr}, {address}" - - # Comparison - @autowrite() - def slt(self, dest: Register, rl: Register, rr: Register): - return f"slt {dest}, {rl}, {rr}" - - @autowrite() - def slti(self, dest: Register, rl: Register, value: int): - return f"slt {dest}, {rl}, {int(value)}" - - # Unconditional Jump - - @autowrite() - def j(self, address: str): - return f"j {address}" - - @autowrite() - def jr(self, r: Register): - return f"jr {r}" - - @autowrite() - def jal(self, address: str): - return f"jal {address}" - - @autowrite() - def jalr(self, dest: Register): - return f"jalr {dest}" - - # System Calls - @autowrite() - def syscall(self, code: int): - self.li(Register.v0, code) - return "syscall" - - def print_int(self): - return self.syscall(1) - - def print_string(self): - return self.syscall(4) - - def read_int(self): - return self.syscall(5) - - def read_string(self): - return self.syscall(8) - - def sbrk(self): - return self.syscall(9) - - def exit(self): - return self.syscall(10) - - def print_char(self): - return self.syscall(11) - - def read_char(self): - return self.syscall(12) - - def exit2(self): - return self.syscall(17) - - - - def offset(self, r: Register, offset: int = 0): - return f"{int(offset)}({r})" - - @autowrite() - def comment(self, text: str): - return f"# {text}" - - @autowrite(0) - def label(self, name: str): - return f"{name}:" - - @autowrite() - def empty(self): - return "" - - # Data Section - - @autowrite() - def data_empty(self): - return "" - - @autowritedata - def data_label(self, name: str): - return f"{name}:" - - @autowritedata - def ascii(self, string: str): - return f'{Directive.ascii} "{string}"' - - @autowritedata - def asciiz(self, string: str): - return f'{Directive.asciiz} "{string}"' - - @autowritedata - def data_comment(self, text: str): - return f"#{text}" diff --git a/src/Tools/Lexer/Lexer.py b/src/Tools/Lexer/Lexer.py deleted file mode 100644 index dcd995edb..000000000 --- a/src/Tools/Lexer/Lexer.py +++ /dev/null @@ -1,272 +0,0 @@ -import ply.lex as lex -from Tools.Tools.utils import Token -from Tools.Parser.Parser import CoolGrammar -from Tools.Tools.Errors import LexicographicError - -class Lexer: - states = ( - ('comment', 'exclusive'), - ('string', 'exclusive') - ) - - # Palabras reservadas del lenguaje COOL - reserved = { - 'class': 'CLASS', - 'inherits': 'INHERITS', - 'function': 'FUNCTION', - 'if': 'IF', - 'then': 'THEN', - 'else': 'ELSE', - 'fi': 'FI', - 'while': 'WHILE', - 'loop': 'LOOP', - 'pool': 'POOL', - 'let': 'LET', - 'in': 'IN', - 'case': 'CASE', - 'of': 'OF', - 'esac': 'ESAC', - 'new': 'NEW', - 'isvoid': 'ISVOID' - } - - t_ignore = ' \f\r\t\v' - t_comment_ignore = '' - t_string_ignore = '' - - tokens = [ - # Identifiers - 'TYPE', 'ID', - # Primitive data types - 'INTEGER', 'STRING', 'BOOL', - # Special keywords - 'ACTION', - # Operators - 'ASSIGN', 'LESS', 'LESSEQUAL', 'EQUAL', 'INT_COMPLEMENT', 'NOT', - # Literals - 'PLUS', 'MINUS', 'STAR', 'DIVIDE', 'COLON', 'SEMICOLON', - 'OPAR', 'CPAR', 'OCUR', 'CCUR', 'AT', 'DOT', 'COMMA', - ] + list(reserved.values()) - - tokens_dict = {} - for tok in tokens: - try: - tokens_dict[tok] = CoolGrammar[tok.lower()].Name - except: - pass - - tokens_dict['ACTION'] = CoolGrammar['=>'].Name - tokens_dict['ASSIGN'] = CoolGrammar['<-'].Name - tokens_dict['LESS'] = CoolGrammar['<'].Name - tokens_dict['LESSEQUAL'] = CoolGrammar['<='].Name - tokens_dict['EQUAL'] = CoolGrammar['='].Name - tokens_dict['INT_COMPLEMENT'] = CoolGrammar['~'].Name - - tokens_dict['PLUS'] = CoolGrammar['+'].Name - tokens_dict['MINUS'] = CoolGrammar['-'].Name - tokens_dict['STAR'] = CoolGrammar['*'].Name - tokens_dict['DIVIDE'] = CoolGrammar['/'].Name - tokens_dict['COLON'] = CoolGrammar[':'].Name - tokens_dict['SEMICOLON'] = CoolGrammar[';'].Name - tokens_dict['OPAR'] = CoolGrammar['('].Name - tokens_dict['CPAR'] = CoolGrammar[')'].Name - tokens_dict['OCUR'] = CoolGrammar['{'].Name - tokens_dict['CCUR'] = CoolGrammar['}'].Name - tokens_dict['AT'] = CoolGrammar['@'].Name - tokens_dict['DOT'] = CoolGrammar['.'].Name - tokens_dict['COMMA'] = CoolGrammar[','].Name - - - def __init__(self): - self.lexer = lex.lex(module=self) - self.comment_level = 0 - self.code = '' - self.current_string = '' - self.errors = [] - - - # Expresiones regulares - - def t_INTEGER(self, t): - r'[0-9]+' - t.value = int(t.value) - return t - - def t_BOOL(self, t): - r't[rR][uU][eE]|f[aA][lL][sS][eE]' - t.value = True if t.value == 'true' else False - return t - - - # Other tokens with precedence before TYPE and ID - - def t_NOT(self, t): - r'[nN][oO][tT]' - return t - - # Identifiers - - def t_TYPE(self, t): - r'[A-Z][A-Za-z0-9_]*' - - try: - t.type = self.reserved[t.value.lower()] - except KeyError: - pass - - return t - - def t_ID(self, t): - r'[a-z][A-Za-z0-9_]*' - - try: - t.type = self.reserved[t.value.lower()] - except KeyError: - pass - - return t - - - t_ASSIGN = r'<-' - t_LESS = r'<' - t_LESSEQUAL = r'<=' - t_EQUAL = r'=' - t_INT_COMPLEMENT = r'~' - t_ACTION = r'=>' - - t_PLUS = r'\+' - t_MINUS = r'-' - t_STAR = r'\*' - t_DIVIDE = r'/' - t_COLON = r':' - t_SEMICOLON = r';' - t_OPAR = r'\(' - t_CPAR = r'\)' - t_OCUR = r'{' - t_CCUR = r'}' - t_AT = r'@' - t_DOT = r'\.' - t_COMMA = r',' - - #################### - ##### COMMENTS ##### - #################### - def t_LINECOMMENT(self, t): - r'--.*' - pass - - def t_COMMENTBEGIN(self, t): - r'\(\*' - self.comment_level += 1 - t.lexer.begin('comment') - - def t_comment_COMMENTBEGIN(self, t): - r'\(\*' - self.comment_level += 1 - - def t_comment_COMMENTEND(self, t): - r'\*\)' - self.comment_level -= 1 - if self.comment_level == 0: - t.lexer.begin('INITIAL') - - def t_comment_eof(self, t): - self.errors.append(LexicographicError(t.lineno, - self.find_column(t), 'EOF in comment')) - self.lexer.begin('INITIAL') - - def t_comment_error(self, t): - t.lexer.skip(1) - - ############################ - ##### STRING CONSTANTS ##### - ############################ - - def t_STRINGBEGIN(self, t): - r'"' - self.current_string = '' - self.lexer.begin('string') - - def t_string_NULL(self, t): - r'\0' - self.errors.append(LexicographicError(t.lineno, - self.find_column(t), 'Null caracter in string')) - self.lexer.begin('INITIAL') - - def t_string_NEWLINE(self, t): - r'\\\n' - self.current_string += '\n' - t.lexer.lineno += 1 - - def t_string_INVALID_NEWLINE(self, t): - r'\n' - t.lexer.lineno += 1 - self.errors.append(LexicographicError(t.lineno, - self.find_column(t), 'Unterminated string constant')) - self.lexer.begin('INITIAL') - - def t_string_SCAPED_SPECIAL_CHARACTER(self, t): - r'\\(b|t|f)' - self.current_string += t.value - - def t_string_SCAPED_CHARACTER(self, t): - r'\\.' - self.current_string += t.value[1] - - def t_string_eof(self, t): - self.errors.append(LexicographicError(t.lineno, - self.find_column(t), 'EOF in string constant')) - self.lexer.begin('INITIAL') - - def t_string_STRINGEND(self, t): - r'"' - t.value = self.current_string - t.type = 'STRING' - self.lexer.begin('INITIAL') - return t - - def t_string_CHARACTER(self, t): - r'.' - self.current_string += t.value - - def t_string_error(self, t): - return t - - ########################### - ###### SPECIAL RULES ###### - ########################### - - def t_ANY_newline(self, t): - r'\n+' - t.lexer.lineno += len(t.value) - - def find_column(self, token): - line_start = self.code.rfind('\n', 0, token.lexpos) + 1 - return (token.lexpos - line_start) + 1 \ - + 3 * len([i for i in self.code[line_start:token.lexpos] if i == '\t']) - - def t_error(self, t): - self.errors.append(LexicographicError(t.lineno, - self.find_column(t), f'ERROR "{t.value[0]}"')) - t.lexer.skip(1) - - - ''' - Dado un string retorna el arreglo de tokens resultante de analizar dicho string - ''' - def tokenize(self, code): - tokens = [] - self.code = code - - self.lexer.input(code) - while True: - token = self.lexer.token() - if token is None: - break - - tokens.append(Token(token.value, self.tokens_dict[token.type], - token.lineno, self.find_column(token))) - - tokens.append(Token('$', CoolGrammar.EOF.Name)) - - return tokens diff --git a/src/Tools/Parser/Parser.py b/src/Tools/Parser/Parser.py deleted file mode 100644 index cb779829b..000000000 --- a/src/Tools/Parser/Parser.py +++ /dev/null @@ -1,138 +0,0 @@ -from Tools.Tools.pycompiler import Grammar -from Tools.Tools.Parser_LR1 import LR1Parser -from Tools.Tools.COOLAst import * - - -# Representacion de la gramatica de COOL utilizando la clase grammar -CoolGrammar = Grammar() - -# noterminales -program = CoolGrammar.NonTerminal('', startSymbol=True) -class_list, def_class = CoolGrammar.NonTerminals(' ') -feature_list, feature = CoolGrammar.NonTerminals(' ') -param_list, param = CoolGrammar.NonTerminals(' ') -expr_1, expr_2, member_call, expr_list, let_list, case_list = CoolGrammar.NonTerminals( - ' ') -comp_expr, arith, arith_2, term, factor, factor_2 = CoolGrammar.NonTerminals( - ' ') -atom, func_call, arg_list = CoolGrammar.NonTerminals(' ') - -# terminales -classx, inherits, function = CoolGrammar.Terminals('class inherits function') -ifx, then, elsex, fi = CoolGrammar.Terminals('if then else fi') -whilex, loop, pool = CoolGrammar.Terminals('while loop pool') -let, inx = CoolGrammar.Terminals('let in') -case, of, esac = CoolGrammar.Terminals('case of esac') -semi, colon, comma, dot, at, opar, cpar, ocur, ccur, larrow, rarrow = CoolGrammar.Terminals( - '; : , . @ ( ) { } <- =>') -plus, minus, star, div, isvoid, compl = CoolGrammar.Terminals('+ - * / isvoid ~') -notx, less, leq, equal = CoolGrammar.Terminals('not < <= =') -new, idx, typex, integer, string, boolx = CoolGrammar.Terminals('new id type integer string bool') - -# Producciones -program %= class_list, lambda h, s: ProgramNode(s[1]) - -# Lista de clases -class_list %= def_class + class_list, lambda h, s: [s[1]] + s[2] -class_list %= def_class, lambda h, s: [s[1]] - -# Defincicion de la clase -def_class %= classx + typex + ocur + feature_list + ccur + semi, lambda h, s: ClassDeclarationNode(s[1], s[2], s[4]) -def_class %= classx + typex + inherits + typex + ocur + feature_list + ccur + semi, lambda h, s: ClassDeclarationNode( - s[1], s[2], s[6], s[4]) - -# Lista de propiedades de la clase -feature_list %= feature + feature_list, lambda h, s: [s[1]] + s[2] -feature_list %= CoolGrammar.Epsilon, lambda h, s: [] - -# Atributos de la clase -feature %= idx + colon + typex + semi, lambda h, s: AttrDeclarationNode(s[1], s[3]) -feature %= idx + colon + typex + larrow + expr_1 + semi, lambda h, s: AttrDeclarationNode(s[1], s[3], s[5]) - -# Metodos constructores de la clase -feature %= idx + opar + param_list + cpar + colon + typex + ocur + expr_1 + ccur + semi, lambda h, s: FuncDeclarationNode( - s[1], s[3], s[6], s[8]) -feature %= idx + opar + cpar + colon + typex + ocur + expr_1 + ccur + semi, lambda h, s: FuncDeclarationNode(s[1], [], - s[5], s[7]) -# Metodos de la clase -feature %= function + idx + opar + param_list + cpar + colon + typex + ocur + expr_1 + ccur + semi, lambda h, s: FuncDeclarationNode( - s[2], s[4], s[7], s[9]) -feature %= function + idx + opar + cpar + colon + typex + ocur + expr_1 + ccur + semi, lambda h, s: FuncDeclarationNode(s[2], [], - s[6], s[8]) -# Lista de parametros de funcion -param_list %= param, lambda h, s: [s[1]] -param_list %= param + comma + param_list, lambda h, s: [s[1]] + s[3] - -# parametro de funcion -param %= idx + colon + typex, lambda h, s: (s[1], s[3]) - -### Expresiones ### -# Expresion Let-in -expr_1 %= let + let_list + inx + expr_1, lambda h, s: LetInNode(s[1], s[2], s[4]) -let_list %= idx + colon + typex, lambda h, s: [(s[1], s[3], None)] -let_list %= idx + colon + typex + larrow + expr_1, lambda h, s: [(s[1], s[3], s[5])] -let_list %= idx + colon + typex + comma + let_list, lambda h, s: [(s[1], s[3], None)] + s[5] -let_list %= idx + colon + typex + larrow + expr_1 + comma + let_list, lambda h, s: [(s[1], s[3], s[5])] + s[7] - -expr_1 %= idx + larrow + expr_1, lambda h, s: AssignNode(s[1], s[3]) -expr_1 %= notx + expr_1, lambda h, s: NotNode(s[1], s[2]) -expr_1 %= expr_2 + equal + expr_1, lambda h, s: EqualNode(s[1], s[2], s[3]) -expr_1 %= expr_2, lambda h, s: s[1] - -expr_2 %= arith + less + arith, lambda h, s: LessNode(s[1], s[2], s[3]) -expr_2 %= arith + leq + arith, lambda h, s: LessEqualNode(s[1], s[2], s[3]) -expr_2 %= arith, lambda h, s: s[1] - -#Expresiones aritmeticas -arith %= arith + plus + factor, lambda h, s: PlusNode(s[1], s[2], s[3]) -arith %= arith + minus + factor, lambda h, s: MinusNode(s[1], s[2], s[3]) -arith %= factor, lambda h, s: s[1] - -factor %= factor + star + atom, lambda h, s: StarNode(s[1], s[2], s[3]) -factor %= factor + div + atom, lambda h, s: DivNode(s[1], s[2], s[3]) -factor %= term, lambda h, s: s[1] - -term %= compl + term, lambda h, s: ComplementNode(s[1], s[2]) -term %= isvoid + term, lambda h, s: IsVoidNode(s[1], s[2]) -term %= atom, lambda h, s: s[1] - -# Encapsulaciones atomicas -atom %= opar + expr_1 + cpar, lambda h, s: s[2] -atom %= integer, lambda h, s: IntegerNode(s[1]) -atom %= string, lambda h, s: StringNode(s[1]) -atom %= boolx, lambda h, s: BoolNode(s[1]) -atom %= idx, lambda h, s: IdNode(s[1]) -atom %= ifx + expr_1 + then + expr_1 + elsex + expr_1 + fi, lambda h, s: IfThenElseNode(s[1], s[2], s[4], s[6]) -atom %= whilex + expr_1 + loop + expr_1 + pool, lambda h, s: WhileLoopNode(s[1], s[2], s[4]) - -# Expresion new -atom %= new + typex, lambda h, s: NewNode(s[1], s[2]) - -# Encapsulamiento entre corchetes -atom %= ocur + expr_list + ccur, lambda h, s: BlockNode(s[1], s[2]) -expr_list %= expr_1 + semi, lambda h, s: [s[1]] -expr_list %= expr_1 + semi + expr_list, lambda h, s: [s[1]] + s[3] - -# Expresion Case of -atom %= case + expr_1 + of + case_list + esac, lambda h, s: CaseOfNode(s[1], s[2], s[4]) -case_list %= idx + colon + typex + rarrow + expr_1 + semi, lambda h, s: [(s[1], s[3], s[5])] -case_list %= idx + colon + typex + rarrow + expr_1 + semi + case_list, lambda h, s: [(s[1], s[3], s[5])] + s[7] - -atom %= func_call, lambda h, s: s[1] - -# Llamado a funcion -func_call %= atom + at + typex + dot + idx + opar + arg_list + cpar, lambda h, s: FunctionCallNode(s[1], s[5], s[7], s[3]) -func_call %= atom + at + typex + dot + idx + opar + cpar, lambda h, s: FunctionCallNode(s[1], s[5], [], s[3]) -func_call %= atom + dot + idx + opar + arg_list + cpar, lambda h, s: FunctionCallNode(s[1], s[3], s[5]) -func_call %= atom + dot + idx + opar + cpar, lambda h, s: FunctionCallNode(s[1], s[3], []) - -# Llamado a miembro de clase -func_call %= idx + opar + arg_list + cpar, lambda h, s: MemberCallNode(s[1], s[3]) -func_call %= idx + opar + cpar, lambda h, s: MemberCallNode(s[1], []) - -# Lista de argumentos -arg_list %= expr_1, lambda h, s: [s[1]] -arg_list %= expr_1 + comma + arg_list, lambda h, s: [s[1]] + s[3] - -# parser -CoolParser = LR1Parser(CoolGrammar) diff --git a/src/Tools/Semantic/Type_Builder.py b/src/Tools/Semantic/Type_Builder.py deleted file mode 100644 index be9425bf0..000000000 --- a/src/Tools/Semantic/Type_Builder.py +++ /dev/null @@ -1,157 +0,0 @@ -from Tools.Tools import visitor -from Tools.Tools.Semantic import * -from Tools.Parser.Parser import ProgramNode, ClassDeclarationNode, AttrDeclarationNode, FuncDeclarationNode -from Tools.Tools.Errors import * - -# Visitor encargado de contruir los tipos. Una vez que se conocen los nombres -# de los tipos que intervienen en el codifo COOL, este visitor les annade sus -# metodos y atributos, asi como el tipo padre. - -class Type_Builder: - def __init__(self, Context : Context): - self.Context = Context - self.Current_Type = None - - self.errors = [] - - # Construye los tipos builtin - self.Object_Type = self.Context.get_type('Object') - - self.IO_Type = self.Context.get_type('IO') - self.IO_Type.set_parent(self.Object_Type) - - self.Int_Type = self.Context.get_type('Int') - self.Int_Type.set_parent(self.Object_Type) - self.Int_Type.sealed = True - - self.String_Type = self.Context.get_type('String') - self.String_Type.set_parent(self.Object_Type) - self.String_Type.sealed = True - - self.Bool_Type = self.Context.get_type('Bool') - self.Bool_Type.set_parent(self.Object_Type) - self.Bool_Type.sealed = True - - self.IO_Type.define_method('out_string', ['x'], [self.String_Type], SelfType(), 0, 0) - self.IO_Type.define_method('out_int', ['x'], [self.Int_Type], SelfType(), 0, 0) - self.IO_Type.define_method('in_int', [], [], self.Int_Type, 0, 0) - self.IO_Type.define_method('in_string', [], [], self.String_Type, 0, 0) - - self.Object_Type.define_method('abort', [], [], self.Object_Type, 0, 0) - self.Object_Type.define_method('type_name', [], [], self.String_Type, 0, 0) - self.Object_Type.define_method('copy', [], [], SelfType(), 0, 0) - - self.String_Type.define_method('length', [], [], self.Int_Type, 0, 0) - self.String_Type.define_method('concat', ['x'], [self.String_Type], self.String_Type, 0, 0) - self.String_Type.define_method('substr', ['l', 'r'], [self.Int_Type, self.Int_Type], self.String_Type, 0, 0) - - @visitor.on('node') - def visit(self, node): - pass - - @visitor.when(ProgramNode) - def visit(self, node : ProgramNode): - for type in node.declarations: - self.visit(type) - - try: - self.Context.get_type('Main').get_method('main') - except SemanticException: - # Cada programa COOL debe tener una clase MAIN - self.errors.append(SemanticError(0, 0, - f'Class Main and its method main must be defined')) - - @visitor.when(ClassDeclarationNode) - def visit(self, node : ClassDeclarationNode): - self.Current_Type = self.Context.get_type(node.id.lex) - - if node.parent: - try: - parent_type = self.Context.get_type(node.parent.lex) - except SemanticException as ex: - self.errors.append(TypeError(node.parent.line, node.parent.column, - f'Class {node.id} inherits from an undefined class {node.parent.lex}')) - parent_type = self.Object_Type - - try: - self.Current_Type.set_parent(parent_type) - except SemanticException as ex: - self.errors.append(SemanticError(node.parent.line, node.parent.column, - f'Class {node.id.lex} cannot inherit class {parent_type.name}')) - - parent = self.Current_Type.parent - # Revisa que no haya herencia ciclica - while parent: - if parent == self.Current_Type: - self.errors.append(SemanticError(node.line, node.column, - f'Class {node.id.lex}, or an ancestor of {node.id.lex}, ' - f'is involved in an inheritance cycle')) - self.Current_Type.parent = self.Object_Type - break - parent = parent.parent - - else: - self.Current_Type.set_parent(self.Object_Type) - - - for feat in node.features: - self.visit(feat) - - @visitor.when(AttrDeclarationNode) - def visit(self, node : AttrDeclarationNode): - try: - attr_type = self.Context.get_type(node.type.lex) - except SemanticException as ex: - # Existio un error al tratar de obtener el tipo del atributo - self.errors.append(TypeError(node.type.line, node.type.column, ex.text)) - attr_type = ErrorType() - - try: - self.Current_Type.define_attribute(node.id.lex, attr_type, node.line, node.column) - except SemanticException as ex: - # Existio un error al tratar de definir el atributo - self.errors.append(SemanticError(node.line, node.column, ex.text)) - - @visitor.when(FuncDeclarationNode) - def visit(self, node : FuncDeclarationNode): - param_names, param_types = [], [] - - for name, type in node.params: - try: - type = self.Context.get_type(type.lex) - except SemanticException as ex: - # Existio un error al tratar de obtener el tipo del parametro - self.errors.append(TypeError(type.line, type.column, - f'Class {type.lex} of formal parameter {name.lex} is undefined')) - type = ErrorType() - else: - if isinstance(type, SelfType): - self.errors.append(SemanticError(name.line, name.column, - f'\'self\' cannot be the name of a formal parameter')) - arg_type = ErrorType() - - if name.lex in param_names: - self.errors.append(SemanticError(name.line, name.column, - f'Formal parameter {name.lex} is multiply defined')) - - param_names.append(name.lex) - param_types.append(type) - - - - try: - return_type = self.Context.get_type(node.type.lex) - except SemanticException as ex: - # Existio un error al tratar de obtener el tipo del parametro de retorno - self.errors.append(TypeError(node.type.line, node.type.column, - f'Undefined return type {node.type.lex} in method {node.id.lex}')) - return_type = ErrorType() - - if return_type is SelfType: - return_type = self.Current_Type - - try: - self.Current_Type.define_method(node.id.lex, param_names, param_types, return_type, node.line, node.column) - except SemanticException as ex: - # Existio un error al tratar de definir el metodo - self.errors.append(SemanticError(node.line, node.column, ex.text)) diff --git a/src/Tools/Semantic/Type_Checker.py b/src/Tools/Semantic/Type_Checker.py deleted file mode 100644 index ac366b40c..000000000 --- a/src/Tools/Semantic/Type_Checker.py +++ /dev/null @@ -1,509 +0,0 @@ -from Tools.Tools import visitor -from Tools.Parser.Parser import ProgramNode, ClassDeclarationNode, AttrDeclarationNode, FuncDeclarationNode,\ - IfThenElseNode, WhileLoopNode, BlockNode, LetInNode, CaseOfNode,\ - AssignNode, LessEqualNode, LessNode, EqualNode, ArithmeticNode,\ - NotNode, IsVoidNode, ComplementNode, FunctionCallNode, MemberCallNode, NewNode,\ - IntegerNode, IdNode, StringNode, BoolNode -from Tools.Tools.Semantic import Context, Scope, SelfType, SemanticException, ErrorType -from Tools.Tools.Errors import * - -# Este es el visitor encargado de terminar el chequeo semantico. -# Revisa la compatibilidad de tipos, la compatibilidad en la herencia, -# que las variables hayan sido previamente definidas, asi como los -# metodos y atributos de clase, crea el scope para las variables, el -# cual sera rehusado para inferir las variables que se requieran. -# Observar que cada vez que el visitor llama recursivamente crea un scope -# hijo en el scope actual, esto se hace para que las variables previamente -# declaradas en ambitos hermanos no sean utiles en el ambito actual. - -class Type_Checker: - - def __init__(self, Context : Context): - self.Context = Context - self.errors = [] - self.Current_Type = None - self.Current_Method = None - - self.Object_Type = self.Context.get_type('Object') - self.IO_Type = self.Context.get_type('IO') - self.String_Type = self.Context.get_type('String') - self.Int_Type = self.Context.get_type('Int') - self.Bool_Type = self.Context.get_type('Bool') - - self.builtin_Types = [ - self.Object_Type, - self.IO_Type, - self.String_Type, - self.Int_Type, - self.Bool_Type - ] - - @visitor.on('node') - def visit(self, node, scope): - pass - - @visitor.when(ProgramNode) - def visit(self, node : ProgramNode, scope : Scope = None): - scope = Scope() - for declaration in node.declarations: - self.visit(declaration, scope.create_child()) - return scope - - @visitor.when(ClassDeclarationNode) - def visit(self, node : ClassDeclarationNode, scope : Scope): - self.Current_Type = self.Context.get_type(node.id.lex) - - # Incluyo cada uno de los atributos de los padres en su resoectivo orden - current = self.Current_Type - attributtes = [] - while current.parent: - current = current.parent - for att in reversed(current.attributes): - attributtes.append(att) - - for att in reversed(attributtes): - scope.define_variable(att.name, att.type, att.line, att.column) - - for att in self.Current_Type.attributes: - if scope.is_defined(att.name): - self.errors.append(SemanticError(att.line, att.column, - f'Attribute {att.name} is an attribute of an inherited class')) - scope.define_variable(att.name, att.type, att.line, att.column) - - for feature in node.features: - self.visit(feature, scope.create_child()) - node.static_type = self.Current_Type - - @visitor.when(AttrDeclarationNode) - def visit(self, node : AttrDeclarationNode, scope : Scope): - expr = node.expression - attr = self.Current_Type.get_attribute(node.id.lex) - node_type = self.Current_Type if attr.type is SelfType else attr.type - - if expr: - self.visit(expr, scope.create_child()) - expr_type = expr.static_type - - # Chequeo compatibilidad de tipos - if not expr_type.conforms_to(node_type): - self.errors.append(TypeError(node.expression.line, node.expression.column, - f'Inferred type {expr_type.name} of initialization of attribute {attr.name} ' - f'does not conform to declared type {node_type.name}')) - - if attr.name.lower() == 'self': - self.errors.append(SemanticError(node.line, node.column, - '\'self\' cannot be the name of an attribute')) - - node.static_type = node_type - - @visitor.when(FuncDeclarationNode) - def visit(self, node : FuncDeclarationNode, scope : Scope): - self.Current_Method = self.Current_Type.get_method(node.id.lex) - - if self.Current_Type.parent: - try: - inherited_method = self.Current_Type.parent.get_method(node.id.lex) - - if len(self.Current_Method.param_names) != len(inherited_method.param_names): - self.errors.append(SemanticError(node.line, node.column, - f'Incompatible number of formal parameters in redefined method {self.Current_Method.name}')) - else: - for par1, par2, p in zip(self.Current_Method.param_types, inherited_method.param_types, node.params): - if par1.name != par2.name: - self.errors.append(SemanticError(p[0].line, p[0].column, - f'In redefined method {self.Current_Method.name}, parameter type {par1.name} ' - f'is different from original type {par2.name}')) - - if self.Current_Method.return_type.name != inherited_method.return_type.name: - self.errors.append(SemanticError(node.line, node.column, - f'In redefined method {self.Current_Method.name}, return type {self.Current_Method.return_type.name} ' - f'is different from original return type {inherited_method.return_type.name}')) - - except SemanticException: - pass - - scope.define_variable('self', self.Current_Type, node.line, node.column) - - # Defino cada uno de los parametros de metodo - for pname, ptype in zip(self.Current_Method.param_names, self.Current_Method.param_types): - scope.define_variable(pname, ptype, node.line, node.column) - - if pname.lower() == 'self': - self.errors.append(SemanticError(node.line, node.column, - '\'self\' cannot be the name of a formal parameter')) - - # Chequeo consistencia en el cuerpo del metodo - self.visit(node.body, scope.create_child()) - - expr_type = node.body.static_type - return_type = self.Current_Method.return_type - - # Chequeo consistencia entre el tipo de retorno definido y el tipo de retorno - # del cuerpo del metodo - if not expr_type.conforms_to(return_type): - self.errors.append(TypeError(node.line, node.column, - f'Inferred return type {expr_type.name} of method {self.Current_Method.name} ' - f'does not conform to declared return type {return_type.name}')) - node.static_type = return_type - - @visitor.when(IfThenElseNode) - def visit(self, node : IfThenElseNode, scope : Scope): - # Chequeo consistencia en la condicion del if - self.visit(node.condition, scope.create_child()) - - condition_type = node.condition.static_type - # Chequeo que el tipo de la condicion sea booleano - if not condition_type.conforms_to(self.Bool_Type): - self.errors.append(TypeError(node.condition.line, node.condition.column, - 'Predicate of \'if\' does not have type Bool')) - - # Chequeo consistencia en las expresiones del then y el else - self.visit(node.if_body, scope.create_child()) - self.visit(node.else_body, scope.create_child()) - - if_type = node.if_body.static_type - else_type = node.else_body.static_type - - node.static_type = if_type.type_union(else_type) - - @visitor.when(WhileLoopNode) - def visit(self, node : WhileLoopNode, scope : Scope): - self.visit(node.condition, scope.create_child()) - condition_type = node.condition.static_type - - # Chequeo que la condicion sea de tipo booleano - if not condition_type.conforms_to(self.Bool_Type): - self.errors.append(TypeError(node.condition.line, node.condition.column, - 'Loop condition does not have type Bool')) - - # Chequeo consistencias en el cuerpo del while - self.visit(node.body, scope.create_child()) - - node.static_type = self.Object_Type - - @visitor.when(BlockNode) - def visit(self, node : BlockNode, scope : Scope): - - # Chequeo consistencias en cada una de las instrucciones del cuerpo del bloque - for expr in node.expressions: - self.visit(expr, scope.create_child()) - - node.static_type = node.expressions[-1].static_type - - @visitor.when(LetInNode) - def visit(self, node : LetInNode, scope : Scope): - for id, type, expr in node.let_body: - - if id.lex.lower() == 'self': - self.errors.append(SemanticError(id.line, id.column, - '\'self\' cannot be bound in a \'let\' expression')) - - # Por cada una de las declaraciones del let - try: - type = self.Context.get_type(type.lex) - except SemanticException as ex: - # Chequeo que el tipo exista - self.errors.append(TypeError(id.line, id.column, - f'Class {type.lex} of let-bound identifier {id.lex} is undefined')) - type = ErrorType() - - # Si es Self_Type tomo el tipo correspondiente - type = self.Current_Type if isinstance(type, SelfType) else type - - child = scope.create_child() - if expr: - # Chequeo consistencias en la declaracion y la compatibilidad de tipos - self.visit(expr, child) - if not expr.static_type.conforms_to(type): - self.errors.append(TypeError(id.line, id.column, - f'Inferred type {expr.static_type.name} of initialization of ' - f'{id.lex} does not conform to identifier\'s declared type {type.name}')) - - # Defino la variable - scope.define_variable(id.lex, type, node.line, node.column) - - # Chequeo consistencias en el cuerpo del let in - self.visit(node.in_body, scope.create_child()) - node.static_type = node.in_body.static_type - - @visitor.when(CaseOfNode) - def visit(self, node : CaseOfNode, scope : Scope): - # Chequeo consistencias en el case - self.visit(node.expression, scope.create_child()) - - branchs = [] - - node.static_type = None - for id, type, expr in node.branches: - # Por cada instruccion en el cuerpo del case-of - try: - type = self.Context.get_type(type.lex) - except SemanticException as ex: - # Chequeo que el tipo exista - self.errors.append(TypeError(type.line, type.column, - f'Class {type.lex} of case branch is undefined')) - type = ErrorType() - - # Chequeo que no sea un tipo especial - if isinstance(type, SelfType): - self.errors.append(SemanticError(id.line, id.column, - f'SELF_TYPE cannot be used as a case branch')) - - child = scope.create_child() - # Declaro la variable y chequeo consistencias en la expresion - child.define_variable(id.lex, type, node.line, node.column) - self.visit(expr, child) - - if type.name in branchs: - self.errors.append(SemanticError(id.line, id.column, - f'Duplicate branch {type.name} in case statement')) - branchs.append(type.name) - - node.static_type = node.static_type.type_union(expr.static_type) if node.static_type else expr.static_type - - - @visitor.when(AssignNode) - def visit(self, node : AssignNode, scope : Scope): - # Chequeo consistencias en la expresion - self.visit(node.expression, scope.create_child()) - expr_type = node.expression.static_type - - if node.id.lex.lower() == 'self': - self.errors.append(SemanticError(node.line, node.column, - 'Cannot assign to \'self\'')) - - # Chequeo que la variable este declarada y que su tipo sea valido - if scope.is_defined(node.id.lex): - var_type = scope.find_variable(node.id.lex).type - if isinstance(var_type, SelfType): - var_type = self.Current_Type - - if not expr_type.conforms_to(var_type): - self.errors.append(TypeError(node.expression.line, node.expression.column, - f'Inferred type {expr_type.name} of initialization of attribute {node.id.lex} ' - f'does not conform to declared type {var_type.name}')) - else: - self.errors.append(NameError(node.line, node.column, - f'Undeclared identifier {node.id.lex}')) - - node.static_type = expr_type - - @visitor.when(NotNode) - def visit(self, node : NotNode, scope : Scope): - # Chequeo la consistencia de la expresion - self.visit(node.expression, scope.create_child()) - - # Chequeo que la expresion sea booleana - if not node.expression.static_type.conforms_to(self.Bool_Type): - self.errors.append(TypeError(node.expression.line, node.expression.column, - f'Argument of \'not\' has type {node.expression.static_type.name} instead of Bool')) - - node.static_type = self.Bool_Type - - @visitor.when(LessEqualNode) - def visit(self, node : LessEqualNode, scope : Scope): - # Chequeo la consistencia de ambos miembros - self.visit(node.left, scope.create_child()) - self.visit(node.right, scope.create_child()) - left_type = node.left.static_type - right_type = node.right.static_type - - # Chequeo que ambos miembros posean tipo int - if not left_type.conforms_to(self.Int_Type) or not right_type.conforms_to(self.Int_Type): - self.errors.append(TypeError(node.line, node.column, - f'non-Int arguments: {left_type.name} <= {right_type.name}')) - - node.static_type = self.Bool_Type - - @visitor.when(LessNode) - def visit(self, node: LessNode, scope: Scope): - # Chequeo la consistencia de ambos miembros - self.visit(node.left, scope.create_child()) - self.visit(node.right, scope.create_child()) - left_type = node.left.static_type - right_type = node.right.static_type - - # Chequeo que ambos miembros posean tipo int - if not left_type.conforms_to(self.Int_Type) or not right_type.conforms_to(self.Int_Type): - self.errors.append(TypeError(node.line, node.column, - f'non-Int arguments: {left_type.name} < {right_type.name}')) - - node.static_type = self.Bool_Type - - @visitor.when(EqualNode) - def visit(self, node: EqualNode, scope: Scope): - # Chequeo la consistencia de ambos miembros - self.visit(node.left, scope.create_child()) - self.visit(node.right, scope.create_child()) - left_type = node.left.static_type - right_type = node.right.static_type - - # Chequeo que ambos miembros posean tipos comparables - if left_type.conforms_to(self.Int_Type) ^ right_type.conforms_to(self.Int_Type): - self.errors.append(TypeError(node.line, node.column, - f'Illegal comparison with a basic type')) - elif left_type.conforms_to(self.String_Type) ^ right_type.conforms_to(self.String_Type): - self.errors.append(TypeError(node.line, node.column, - f'Illegal comparison with a basic type')) - elif left_type.conforms_to(self.Bool_Type) ^ right_type.conforms_to(self.Bool_Type): - self.errors.append(TypeError(node.line, node.column, - f'Illegal comparison with a basic type')) - - node.static_type = self.Bool_Type - - @visitor.when(ArithmeticNode) - def visit(self, node : ArithmeticNode, scope : Scope): - # Chequeo la consistencia de ambos miembros - self.visit(node.left, scope.create_child()) - self.visit(node.right, scope.create_child()) - left_type = node.left.static_type - right_type = node.right.static_type - - # Chequeo que ambos miembros posean tipo int - if not left_type.conforms_to(self.Int_Type) or not right_type.conforms_to(self.Int_Type): - self.errors.append(TypeError(node.line, node.column, - f'non-Int arguments: {left_type.name} and {right_type.name}')) - - node.static_type = self.Int_Type - - @visitor.when(IsVoidNode) - def visit(self, node : IsVoidNode, scope : Scope): - # Chequeo la consistencia de la expresion - self.visit(node.expression, scope.create_child()) - node.static_type = self.Bool_Type - - @visitor.when(ComplementNode) - def visit(self, node : ComplementNode, scope : Scope): - # Chequeo la consistencia de la expresion - self.visit(node.expression, scope.create_child()) - - # Chequeo que la expresion sea de tipo booleana - if not node.expression.static_type.conforms_to(self.Int_Type): - self.errors.append(TypeError(node.expression.line, node.expression.column, - f'Argument of \'~\' has type {node.expression.static_type.name} instead of Int')) - - node.static_type = self.Int_Type - - @visitor.when(FunctionCallNode) - def visit(self, node : FunctionCallNode, scope : Scope): - # Chequeo la consistencia de la expresion a la cual se le pide la funcion - self.visit(node.obj, scope.create_child()) - obj_type = node.obj.static_type - - try: - if node.type: - # Chequeo que el tipo exista - try: - node_type = self.Context.get_type(node.type.lex) - except SemanticException as ex: - self.errors.append(TypeError(node.line, node.column, - f'Class {node.type.lex} not defined')) - node_type = ErrorType() - - # Chequeo que el tipo no sea un tipo especial - if isinstance(node_type, SelfType): - self.errors.append(TypeError(node.line, node.column, - 'SELF_TYPE cannot be used in a dispatch')) - - # Chequeo que los tipos sean compatibles - if not obj_type.conforms_to(node_type): - self.errors.append(TypeError(node.line, node.column, - f'Expression type {obj_type.name} does not conform ' - f'to declared static dispatch type {node_type.name}')) - - obj_type = node_type - - - obj_method = obj_type.get_method(node.id.lex) - return_type = obj_type if isinstance(obj_method.return_type, SelfType) else obj_method.return_type - - except SemanticException as ex: - self.errors.append(AttributeError(node.id.line, node.id.column, - f'Dispatch to undefined method {node.id.lex}')) - return_type = ErrorType() - obj_method = None - - # Chequeo consistencias en los argumentos con los que se llama al metodo - for arg in node.args: - self.visit(arg, scope.create_child()) - - if obj_method and len(node.args) == len(obj_method.param_types): - for arg, param_type, param_name in zip(node.args, obj_method.param_types, obj_method.param_names): - if not arg.static_type.conforms_to(param_type): - # Chequeo compatibilidad de tipos entre los argumentos - self.errors.append(TypeError(arg.line, arg.column, - f'In call of method {obj_method.name}, type {arg.static_type.name} of ' - f'parameter {param_name} does not conform to declared type {param_type.name}')) - - elif obj_method: - # Chequeo que la cantidad de argumentos sea igual a las solicitadas por el metodo - self.errors.append(SemanticError(node.id.line, node.id.column, - f'Method {obj_method.name} called with wrong number of arguments')) - - node.static_type = return_type - - - @visitor.when(MemberCallNode) - def visit(self, node : MemberCallNode, scope : Scope): - # Chequeo que el metodo exista en el tipo actual - try: - obj_method = self.Current_Type.get_method(node.id.lex) - return_type = self.Current_Type if isinstance(obj_method.return_type, SelfType) else obj_method.return_type - except SemanticException as ex: - self.errors.append(AttributeError(node.id.line, node.id.column, - f'Dispatch to undefined method {node.id.lex}')) - obj_method = None - return_type = ErrorType() - - # Chequeo la consistencia en los argumentos - for arg in node.args: - self.visit(arg, scope.create_child()) - - if obj_method and len(node.args) == len(obj_method.param_types): - # Chequeo la compatibiidad entre los tipos de los argumentos - for arg, param_type, param_name in zip(node.args, obj_method.param_types, obj_method.param_names): - if not arg.static_type.conforms_to(param_type): - self.errors.append(TypeError(arg.line, arg.column, - f'In call of method {obj_method.name}, type {arg.static_type.name} of ' - f'parameter {param_name} does not conform to declared type {param_type.name}')) - - elif obj_method: - # Chequeo que la cantidad de argumentos coincida con los que requiere el metodo - self.errors.append(SemanticError(node.id.line, node.id.column, - f'Method {obj_method.name} called with wrong number of arguments')) - - node.static_type = return_type - - @visitor.when(NewNode) - def visit(self, node : NewNode, scope : Scope): - # Chequeo que el tipo exista - try: - type = self.Context.get_type(node.type.lex) - except SemanticException as ex: - self.errors.append(TypeError(node.type.line, node.type.column, - f'\'new\' used with undeclared class {node.type.lex}')) - type = ErrorType() - - node.static_type = type - - @visitor.when(IntegerNode) - def visit(self, node : IntegerNode, scope : Scope): - node.static_type = self.Int_Type - - @visitor.when(StringNode) - def visit(self, node: StringNode, scope: Scope): - node.static_type = self.String_Type - - @visitor.when(BoolNode) - def visit(self, node: BoolNode, scope: Scope): - node.static_type = self.Bool_Type - - @visitor.when(IdNode) - def visit(self, node: IntegerNode, scope: Scope): - # Chequeo que la variable exista - if scope.is_defined(node.token.lex): - node.static_type = scope.find_variable(node.token.lex).type - else: - self.errors.append(NameError(node.line, node.column, - f'Undeclared identifier {node.token.lex}')) - node.static_type = ErrorType() diff --git a/src/Tools/Semantic/Type_Collector.py b/src/Tools/Semantic/Type_Collector.py deleted file mode 100644 index ccaaf0671..000000000 --- a/src/Tools/Semantic/Type_Collector.py +++ /dev/null @@ -1,38 +0,0 @@ -from Tools.Tools import visitor -from Tools.Tools.Semantic import * -from Tools.Parser.Parser import ProgramNode, ClassDeclarationNode -from Tools.Tools.Errors import SemanticError - -# Visitor encargado de coleccionar los nombres de las clases que se definen -# en el codigo del programa COOL, chequea ademas que no se redeclaren e -# incluye los tipos builtin dentro del contexto - -class Type_Collector: - def __init__(self): - self.errors = [] - - self.Context = Context() - - self.Context.add_type(SelfType()) - - self.Context.create_type('Object') - self.Context.create_type('String') - self.Context.create_type('IO') - self.Context.create_type('Int') - self.Context.create_type('Bool') - - @visitor.on('node') - def visit(self, node): - pass - - @visitor.when(ProgramNode) - def visit(self, node : ProgramNode): - for type in node.declarations: - self.visit(type) - - @visitor.when(ClassDeclarationNode) - def visit(self, node : ClassDeclarationNode): - try: - self.Context.create_type(node.id.lex) - except SemanticException as ex: - self.errors.append(SemanticError(node.line, node.column, ex.text)) diff --git a/src/Tools/Tools/Errors.py b/src/Tools/Tools/Errors.py deleted file mode 100644 index 4d1b8e0f1..000000000 --- a/src/Tools/Tools/Errors.py +++ /dev/null @@ -1,66 +0,0 @@ - -class Error: - def __init__(self, line = None, column = None, text = ''): - self.line = line - self.column = column - self.text = text - def __str__(self): - raise NotImplementedError() - def __repr__(self): - raise NotImplementedError() - -class CompilerError(Error): - def __str__(self): - return f'{self.line, self.column} - ' \ - f'CompilerError: {self.text}' - def __repr__(self): - return f'{self.line, self.column} - ' \ - f'CompilerError: {self.text}' - -class LexicographicError(Error): - def __str__(self): - return f'{self.line, self.column} - ' \ - f'LexicographicError: {self.text}' - def __repr__(self): - return f'{self.line, self.column} - ' \ - f'LexicographicError: {self.text}' - -class SyntacticError(Error): - def __str__(self): - return f'{self.line, self.column} - ' \ - f'SyntacticError: ERROR at or near {self.text}' - def __repr__(self): - return f'{self.line, self.column} - ' \ - f'SyntacticError: ERROR at or near {self.text}' - -class SemanticError(Error): - def __str__(self): - return f'{self.line, self.column} - ' \ - f'SemanticError: {self.text}' - def __repr__(self): - return f'{self.line, self.column} - ' \ - f'SemanticError: {self.text}' - -class TypeError(Error): - def __str__(self): - return f'{self.line, self.column} - ' \ - f'TypeError: {self.text}' - def __repr__(self): - return f'{self.line, self.column} - ' \ - f'TypeError: {self.text}' - -class NameError(Error): - def __str__(self): - return f'{self.line, self.column} - ' \ - f'NameError: {self.text}' - def __repr__(self): - return f'{self.line, self.column} - ' \ - f'NameError: {self.text}' - -class AttributeError(Error): - def __str__(self): - return f'{self.line, self.column} - ' \ - f'AttributeError: {self.text}' - def __repr__(self): - return f'{self.line, self.column} - ' \ - f'AttributeError: {self.text}' \ No newline at end of file diff --git a/src/Tools/Tools/Parser_LR1.py b/src/Tools/Tools/Parser_LR1.py deleted file mode 100644 index f12ba3162..000000000 --- a/src/Tools/Tools/Parser_LR1.py +++ /dev/null @@ -1,185 +0,0 @@ -from .pycompiler import Item -from .utils import ContainerSet -from .Firsts_and_Follows import compute_firsts, compute_local_first -from .automata import State -from .parsing import ShiftReduceParser - -''' -Recibe un item LR(1) y devuelve el conjunto de items que -sugiere incluir (directamente) debido a la presencia de -un . delante de un no terminal. -expand("𝑌→𝛼.𝑋𝛿,𝑐") = { "𝑋→.𝛽,𝑏" | 𝑏∈𝐹𝑖𝑟𝑠𝑡(𝛿𝑐) } -''' -def expand(item, firsts): - next_symbol = item.NextSymbol - if next_symbol is None or not next_symbol.IsNonTerminal: - return [] - - lookaheads = ContainerSet() - # (Compute lookahead for child items) - # Preview retorna los elementos que se encuantran detras del punto - # en el item mas sus lookaheads - for prev in item.Preview(): - lookaheads.update(compute_local_first(firsts, prev)) - - assert not lookaheads.contains_epsilon - - # (Build and return child items) - return [Item(x, 0, lookaheads) for x in next_symbol.productions] - -''' -Recibe un conjunto de items LR(1) y devuelve el mismo conjunto pero -en el que los items con mismo centro están unidos (se combinan los lookahead). -''' -def compress(items): - centers = {} - - for item in items: - center = item.Center() - try: - lookaheads = centers[center] - except KeyError: - centers[center] = lookaheads = set() - lookaheads.update(item.lookaheads) - - return {Item(x.production, x.pos, set(lookahead)) for x, lookahead in centers.items()} - -''' -Computa la clausura de un conjunto de items -''' -# Metodo de punto fijo, mientras haya cambios en el conjunto de items, -# itero por todos los items X -> v.Yw,p, donde X y Y son noterminales y -# v y w son formas oracionales, e incluyo los items de la forma -# Y -> Z,b donde b ∈ First(wp), para todas las producciones de Y. -# Dischos items se calculan mediantes el metodo expand. -# 𝐶𝐿(𝐼) = 𝐼 ∪ { 𝑋→.𝛽,𝑏 } -# tales que 𝑌→𝛼.𝑋𝛿,𝑐 ∈ 𝐶𝐿(𝐼) y 𝑏 ∈ 𝐹𝑖𝑟𝑠𝑡(𝛿𝑐) - -def closure_lr1(items, firsts): - closure = ContainerSet(*items) - - changed = True - while changed: - changed = False - - new_items = ContainerSet() - for item in closure: - new_items.extend(expand(item, firsts)) - - changed = closure.update(new_items) - - return compress(closure) - -''' -Recibe como parámetro un conjunto de items y un símbolo, y devuelve el -conjunto goto(items, symbol). El método permite setear el parámentro -just_kernel=True para calcular solamente el conjunto de items kernels -en lugar de todo el conjunto de items. En caso contrario, se debe proveer -el conjunto con los firsts de la gramática, puesto que serán usados al -calcular la clausura. -''' -# 𝐺𝑜𝑡𝑜(𝐼,𝑋) = 𝐶𝐿({ 𝑌→𝛼𝑋.𝛽,𝑐 | 𝑌→𝛼.𝑋𝛽,𝑐 ∈ 𝐼}) -def goto_lr1(items, symbol, firsts=None, just_kernel=False): - assert just_kernel or firsts is not None, '`firsts` must be provided if `just_kernel=False`' - items = frozenset(item.NextItem() for item in items if item.NextSymbol == symbol) - return items if just_kernel else closure_lr1(items, firsts) - -''' -Computa el automata LR1 correspondiente a la gramatica -''' -# El estado inicial es la clausura del item S' -> .S, $. -# Todos los estados son finales. -# Las transiciones ocurren con terminales y no terminales. -# La función de transición está dada por la función goto. -# f(Ii, c) = Goto(Ii, c) -def build_LR1_automaton(G): - assert len(G.startSymbol.productions) == 1, 'Grammar must be augmented' - - firsts = compute_firsts(G) - firsts[G.EOF] = ContainerSet(G.EOF) - - start_production = G.startSymbol.productions[0] - start_item = Item(start_production, 0, lookaheads=(G.EOF,)) - start = frozenset([start_item]) - - # El estado inicial es la clausura del item S' -> .S, $ - closure = closure_lr1(start, firsts) - automaton = State(frozenset(closure), True) - - pending = [start] - visited = {start: automaton} - - # BFS para construir el automata - # Mientras hallan estados pendientes - while pending: - # Tomo el siguiente estado a analizar - current = pending.pop() - current_state = visited[current] - - # Itero por cada simbolo de la gramatica - for symbol in G.terminals + G.nonTerminals: - # Chequeo si el estado actual posee transicion con ese simbolo a algun estado x - next_state_key = goto_lr1(current_state.state, symbol, just_kernel=True) - - # Si no la posee, continuo al siguiente simbolo - if not next_state_key: - continue - try: - next_state = visited[next_state_key] - except KeyError: - # Si el estado x no ha sido visto por el bfs, lo incluyo - # en la lista de pending - next_state_items = goto_lr1(current_state.state, symbol, firsts) - next_state = State(frozenset(next_state_items), True) - pending.append(next_state_key) - visited[next_state_key] = next_state - - # incluto la transicion del estado actual a x con el simbolo actual - current_state.add_transition(symbol.Name, next_state) - - #automaton.set_formatter(multiline_formatter) - return automaton - - -# Recordar que la diferencia entre los parsers Shift-Reduce es solo la forma -# en que se llenan las tablas ACTION y GOTO -class LR1Parser(ShiftReduceParser): - def _build_parsing_table(self): - G = self.G.AugmentedGrammar(True) - - automaton = build_LR1_automaton(G) - for i, node in enumerate(automaton): - node.idx = i - - for node in automaton: - idx = node.idx - for item in node.state: - # - Fill `self.Action` and `self.Goto` according to `item`) - # - Feel free to use `self._register(...)`) - if item.IsReduceItem: - if item.production.Left == G.startSymbol: - # Sea 𝐼𝑖 el estado que contiene el item "𝑆′→𝑆.,$" (𝑆′ distinguido). - # Entonces 𝐴𝐶𝑇𝐼𝑂𝑁[𝐼𝑖,$]=‘𝑂𝐾‘ - self._register(self.action, (idx, G.EOF.Name), (self.OK, None)) - else: - # Sea "𝑋→𝛼.,𝑠" un item del estado 𝐼𝑖. - # Entonces 𝐴𝐶𝑇𝐼𝑂𝑁[𝐼𝑖,𝑠]=‘𝑅𝑘‘ (producción k es 𝑋→𝛼) - for c in item.lookaheads: - self._register(self.action, (idx, c.Name), (self.REDUCE, item.production)) - else: - next_symbol = item.NextSymbol - try: - next_state = node[next_symbol.Name][0] - if next_symbol.IsNonTerminal: - # Sea "𝑋→𝛼.𝑌𝜔,𝑠" item del estado 𝐼𝑖 y 𝐺𝑜𝑡𝑜(𝐼𝑖,𝑌)=𝐼𝑗. - # Entonces 𝐺𝑂𝑇𝑂[𝐼𝑖,𝑌]=𝑗 - self._register(self.goto, (idx, next_symbol.Name), next_state.idx) - else: - # Sea "𝑋→𝛼.𝑐𝜔,𝑠" un item del estado 𝐼𝑖 y 𝐺𝑜𝑡𝑜(𝐼𝑖,𝑐)=𝐼𝑗. - # Entonces 𝐴𝐶𝑇𝐼𝑂𝑁[𝐼𝑖,𝑐]=‘𝑆𝑗‘ - self._register(self.action, (idx, next_symbol.Name), (self.SHIFT, next_state.idx)) - except KeyError: - print(f'Node: {node} without transition with symbol {next_symbol}') - return - - diff --git a/src/Tools/Tools/Semantic.py b/src/Tools/Tools/Semantic.py deleted file mode 100644 index 82e93d47d..000000000 --- a/src/Tools/Tools/Semantic.py +++ /dev/null @@ -1,288 +0,0 @@ -# Aqui estan las clases necesarias para representar los tipos del programa - -# Necesario para enviar errores semanticos -class SemanticException(Exception): - @property - def text(self): - return self.args[0] - - -# Representa un atributo en un tipo del programa -class Attribute: - def __init__(self, name : str, type, line : int, column : int, expression = None): - self.name = name - self.type = type - self.line = line - self.column = column - - def __str__(self): - return f'[attrib] {self.name}: {self.type.name};' - - def __repr__(self): - return str(self) - -# Representa un metodo en un tipo del programa -class Method: - def __init__(self, name : str, param_names : list, param_types : list, return_type): - self.name = name - self.param_names = param_names - self.param_types = param_types - self.return_type = return_type - - def __str__(self): - params = ', '.join(f'{n}: {t.name}' for n, t in zip(self.param_names, self.param_types)) - return f'[method] {self.name}({params}): {self.return_type.name};' - - def __eq__(self, other): - return other.name == self.name and \ - other.return_type == self.return_type and \ - other.param_types == self.param_types - -#Clase base para representar los tipos del programa -class Type: - def __init__(self, name:str, sealed=False): - self.name = name - self.attributes = [] - self.methods = {} - self.parent = None - # Profundidad en el arbol de herencia - self.depth = 0 - # True si la clase esta sellada (no se puede heredar de ella) - self.sealed = sealed - - def set_parent(self, parent): - # Si el padre esta definido o es un tipo sellado entonces hay un error semantico - if self.parent is not None: - raise SemanticException(f'Parent type is already set for {self.name}.') - if parent.sealed: - raise SemanticException(f'Cannot inherit from sealed type {parent.name}') - self.parent = parent - self.depth = parent.depth + 1 - - # Retorna el tipo en el arbol de herencia de mayor profundidad - # que es padre comun de ambos tipos - def type_union(self, other): - if self is VoidType or other is VoidType: - return VoidType() - - if self is ErrorType or other is ErrorType: - return ErrorType() - - x, y = self, other - while x.depth > y.depth: - x = x.parent - while y.depth > x.depth: - y = y.parent - - while x and x != y: - x = x.parent - y = y.parent - - return x - - # Retorna el atributo del tipo actual con el nombre correspondiente - def get_attribute(self, name:str): - try: - return next(attr for attr in self.attributes if attr.name == name) - except StopIteration: - if self.parent is None: - raise SemanticException(f'Attribute "{name}" is not defined in {self.name}.') - try: - return self.parent.get_attribute(name) - except SemanticException: - raise SemanticException(f'Attribute "{name}" is not defined in {self.name}.') - - # Define un atributo para el tipo actual con el nombre y el tipo correspondiente - def define_attribute(self, name:str, typex, line, column): - try: - attribute = next(attr for attr in self.attributes if attr.name == name) - except StopIteration: - attribute = Attribute(name, typex, line, column) - self.attributes.append(attribute) - return attribute - else: - raise SemanticException(f'Attribute "{name}" is multiply defined in class.') - - # Retorna el metodo del tipo actual con el nombre correspondiente - def get_method(self, name:str): - try: - return self.methods[name] - except KeyError: - if self.parent is None: - raise SemanticException(f'Method "{name}" is not defined in {self.name}.') - try: - return self.parent.get_method(name) - except SemanticException: - raise SemanticException(f'Method "{name}" is not defined in {self.name}.') - - # Define un metodo para el tipo actual con el nombre, los parametros y el tipo de - # retorno correspondientes - def define_method(self, name:str, param_names:list, param_types:list, return_type, line, column): - if name in self.methods: - raise SemanticException(f'Method {name} is multiply defined') - - method = self.methods[name] = Method(name, param_names, param_types, return_type) - method.return_info = VariableInfo(f'{self.name}_{name}_returnType', return_type) - - return method - - # Returns true if other is a parent of current type - def conforms_to(self, other): - return other.is_special_type() \ - or self.name == other.name \ - or (self.parent is not None and self.parent.conforms_to(other)) - - def is_special_type(self): - return False - - def is_void_type(self): - return False - - def __str__(self): - output = f'type {self.name}' - parent = '' if self.parent is None else f' : {self.parent.name}' - output += parent - output += ' {' - output += '\n\t' if self.attributes or self.methods else '' - output += '\n\t'.join(str(x) for x in self.attributes) - output += '\n\t' if self.attributes else '' - output += '\n\t'.join(str(x) for x in self.methods.values()) - output += '\n' if self.methods else '' - output += '}\n' - return output - - def __repr__(self): - return str(self) - -# Special_Types -class SelfType(Type): - def __init__(self): - Type.__init__(self, 'SELF_TYPE') - self.sealed = True - - def conforms_to(self, other): - return False - - def is_special_type(self): - return True - - def __eq__(self, other): - return isinstance(other, SelfType) - -class VoidType(Type): - def __init__(self): - Type.__init__(self, 'VOID_TYPE') - self.sealed = True - - def type_union(self, other): - return self - - def conforms_to(self, other): - return True - - def is_special_type(self): - return True - - def is_void_type(self): - return True - - def __eq__(self, other): - return isinstance(other, Type) - -class ErrorType(Type): - def __init__(self): - Type.__init__(self, '') - self.sealed = True - - def type_union(self, other): - return self - - def conforms_to(self, other): - return True - - def is_special_type(self): - return True - - def __eq__(self, other): - return isinstance(other, Type) - -# Clase para representar una variable dentro del programa -class VariableInfo: - def __init__(self, name, vtype): - self.name = name - self.type = vtype - - -# Clase para representar el contexto en el que se guardan las variables y los -# tipos durante el chequeo semantico del programa -class Context: - def __init__(self): - self.types = {} - - # Incluye un tipo dentro del contexto - def add_type(self, type : Type): - if type.name in self.types: - raise SemanticException('Classes may not be redefined') - self.types[type.name] = type - return type - - # Crea un tipo dentro del contexto - def create_type(self, name : str): - if name in self.types: - raise SemanticException('Classes may not be redefined') - type = self.types[name] = Type(name) - return type - - # Obtiene un tipo del contexto con el nombre correspondiente - def get_type(self, name : str): - try: - return self.types[name] - except KeyError: - raise SemanticException(f'Type {name} not defined.') - - def __str__(self): - return '{\n\t' + '\n\t'.join(y for x in self.types.values() for y in str(x).split('\n')) + '\n}' - - def __repr__(self): - return str(self) - - -# Clase para representar el scope durante el chequeo semantico -class Scope: - def __init__(self, parent = None): - self.parent = parent - self.locals = [] - self.childs = [] - - # Crea un scope hijo - def create_child(self): - self.childs.append(Scope(self)) - return self.childs[-1] - - # Define una variable en el scope - def define_variable(self, name : str, type : Type, line : int, column : int): - self.locals.append(VariableInfo(name, type)) - return self.locals[-1] - - # Retorna una variable definida en el scope, None si no esta definida - def find_variable(self, name : str): - try: - return next(i for i in self.locals if i.name == name) - except StopIteration: - return self.parent.find_variable(name) if self.parent is not None else None - - # True si la variable esta definida en el scope - def is_defined(self, name : str): - return self.find_variable(name) is not None - - # True si la variable fue definida en el scope actual y no en alguno - # de sus scope padres - def is_local(self, name : str): - return any(i for i in self.locals if i.name == name) - - def __iter__(self): - for var in self.locals: - yield var - for ch in self.childs: - for var in ch: - yield var diff --git a/src/Tools/Tools/utils.py b/src/Tools/Tools/utils.py deleted file mode 100644 index 1edd4972e..000000000 --- a/src/Tools/Tools/utils.py +++ /dev/null @@ -1,221 +0,0 @@ -from .pycompiler import Production, Sentence, Symbol, EOF, Epsilon - -class ContainerSet: - def __init__(self, *values, contains_epsilon=False): - self.set = set(values) - self.contains_epsilon = contains_epsilon - - def add(self, value): - n = len(self.set) - self.set.add(value) - return n != len(self.set) - - def extend(self, values): - change = False - for value in values: - change |= self.add(value) - return change - - def set_epsilon(self, value=True): - last = self.contains_epsilon - self.contains_epsilon = value - return last != self.contains_epsilon - - def update(self, other): - n = len(self.set) - self.set.update(other.set) - return n != len(self.set) - - def epsilon_update(self, other): - return self.set_epsilon(self.contains_epsilon | other.contains_epsilon) - - def hard_update(self, other): - return self.update(other) | self.epsilon_update(other) - - def find_match(self, match): - for item in self.set: - if item == match: - return item - return None - - def __len__(self): - return len(self.set) + int(self.contains_epsilon) - - def __str__(self): - return '%s-%s' % (str(self.set), self.contains_epsilon) - - def __repr__(self): - return str(self) - - def __iter__(self): - return iter(self.set) - - def __nonzero__(self): - return len(self) > 0 - - def __eq__(self, other): - if isinstance(other, set): - return self.set == other - return isinstance(other, ContainerSet) and self.set == other.set and self.contains_epsilon == other.contains_epsilon - - -def inspect(item, grammar_name='G', mapper=None): - try: - return mapper[item] - except (TypeError, KeyError ): - if isinstance(item, dict): - items = ',\n '.join(f'{inspect(key, grammar_name, mapper)}: {inspect(value, grammar_name, mapper)}' for key, value in item.items() ) - return f'{{\n {items} \n}}' - elif isinstance(item, ContainerSet): - args = f'{ ", ".join(inspect(x, grammar_name, mapper) for x in item.set) } ,' if item.set else '' - return f'ContainerSet({args} contains_epsilon={item.contains_epsilon})' - elif isinstance(item, EOF): - return f'{grammar_name}.EOF' - elif isinstance(item, Epsilon): - return f'{grammar_name}.Epsilon' - elif isinstance(item, Symbol): - return f"G['{item.Name}']" - elif isinstance(item, Sentence): - items = ', '.join(inspect(s, grammar_name, mapper) for s in item._symbols) - return f'Sentence({items})' - elif isinstance(item, Production): - left = inspect(item.Left, grammar_name, mapper) - right = inspect(item.Right, grammar_name, mapper) - return f'Production({left}, {right})' - elif isinstance(item, tuple) or isinstance(item, list): - ctor = ('(', ')') if isinstance(item, tuple) else ('[',']') - return f'{ctor[0]} {("%s, " * len(item)) % tuple(inspect(x, grammar_name, mapper) for x in item)}{ctor[1]}' - else: - raise ValueError(f'Invalid: {item}') - -def pprint(item, header=""): - if header: - print(header) - - if isinstance(item, dict): - for key, value in item.items(): - print(f'{key} ---> {value}') - elif isinstance(item, list): - print('[') - for x in item: - print(f' {repr(x)}') - print(']') - else: - print(item) - -class Token: - """ - Basic token class. - - Parameters - ---------- - lex : str - Token's lexeme. - token_type : Enum - Token's type. - """ - - def __init__(self, lex, token_type, line = None, column = None): - self.lex = lex - self.token_type = token_type - self.line = line - self.column = column - - def __str__(self): - return str(self.token_type) - - def __repr__(self): - return repr(self.token_type) - - @property - def is_valid(self): - return True - -class UnknownToken(Token): - def __init__(self, lex): - Token.__init__(self, lex, None) - - def transform_to(self, token_type): - return Token(self.lex, token_type) - - @property - def is_valid(self): - return False - -def tokenizer(G, fixed_tokens): - def decorate(func): - def tokenize_text(text): - tokens = [] - for lex in text.split(): - try: - token = fixed_tokens[lex] - except KeyError: - token = UnknownToken(lex) - try: - token = func(token) - except TypeError: - pass - tokens.append(token) - tokens.append(Token('$', G.EOF)) - return tokens - - if hasattr(func, '__call__'): - return tokenize_text - elif isinstance(func, str): - return tokenize_text(func) - else: - raise TypeError('Argument must be "str" or a callable object.') - return decorate - -class DisjointSet: - def __init__(self, *items): - self.nodes = { x: DisjointNode(x) for x in items } - - def merge(self, items): - items = (self.nodes[x] for x in items) - try: - head, *others = items - for other in others: - head.merge(other) - except ValueError: - pass - - @property - def representatives(self): - return { n.representative for n in self.nodes.values() } - - @property - def groups(self): - return [[n for n in self.nodes.values() if n.representative == r] for r in self.representatives] - - def __len__(self): - return len(self.representatives) - - def __getitem__(self, item): - return self.nodes[item] - - def __str__(self): - return str(self.groups) - - def __repr__(self): - return str(self) - -class DisjointNode: - def __init__(self, value): - self.value = value - self.parent = self - - @property - def representative(self): - if self.parent != self: - self.parent = self.parent.representative - return self.parent - - def merge(self, other): - other.representative.parent = self.representative - - def __str__(self): - return str(self.value) - - def __repr__(self): - return str(self) \ No newline at end of file diff --git a/src/Tools/Tools/visitor.py b/src/Tools/Tools/visitor.py deleted file mode 100644 index 964842836..000000000 --- a/src/Tools/Tools/visitor.py +++ /dev/null @@ -1,80 +0,0 @@ -# The MIT License (MIT) -# -# Copyright (c) 2013 Curtis Schlak -# -# Permission is hereby granted, free of charge, to any person obtaining a copy -# of this software and associated documentation files (the "Software"), to deal -# in the Software without restriction, including without limitation the rights -# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -# copies of the Software, and to permit persons to whom the Software is -# furnished to do so, subject to the following conditions: -# -# The above copyright notice and this permission notice shall be included in -# all copies or substantial portions of the Software. -# -# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN -# THE SOFTWARE. - -import inspect - -__all__ = ['on', 'when'] - -def on(param_name): - def f(fn): - dispatcher = Dispatcher(param_name, fn) - return dispatcher - return f - - -def when(param_type): - def f(fn): - frame = inspect.currentframe().f_back - func_name = fn.func_name if 'func_name' in dir(fn) else fn.__name__ - dispatcher = frame.f_locals[func_name] - if not isinstance(dispatcher, Dispatcher): - dispatcher = dispatcher.dispatcher - dispatcher.add_target(param_type, fn) - def ff(*args, **kw): - return dispatcher(*args, **kw) - ff.dispatcher = dispatcher - return ff - return f - - -class Dispatcher(object): - def __init__(self, param_name, fn): - frame = inspect.currentframe().f_back.f_back - top_level = frame.f_locals == frame.f_globals - self.param_index = self.__argspec(fn).args.index(param_name) - self.param_name = param_name - self.targets = {} - - def __call__(self, *args, **kw): - typ = args[self.param_index].__class__ - d = self.targets.get(typ) - if d is not None: - return d(*args, **kw) - else: - issub = issubclass - t = self.targets - ks = t.keys() - ans = [t[k](*args, **kw) for k in ks if issub(typ, k)] - if len(ans) == 1: - return ans.pop() - return ans - - def add_target(self, typ, target): - self.targets[typ] = target - - @staticmethod - def __argspec(fn): - # Support for Python 3 type hints requires inspect.getfullargspec - if hasattr(inspect, 'getfullargspec'): - return inspect.getfullargspec(fn) - else: - return inspect.getargspec(fn) diff --git a/src/core/cil/BaseCOOLToCILVisitor.py b/src/core/cil/BaseCoolToCilVisitor.py similarity index 99% rename from src/core/cil/BaseCOOLToCILVisitor.py rename to src/core/cil/BaseCoolToCilVisitor.py index 00d01a368..7da7756b7 100644 --- a/src/core/cil/BaseCOOLToCILVisitor.py +++ b/src/core/cil/BaseCoolToCilVisitor.py @@ -1,4 +1,4 @@ -from ..cil import CILAst as cil +from ..cil import CilAst as cil from ..tools.Semantic import VariableInfo diff --git a/src/core/cil/CILAst.py b/src/core/cil/CilAst.py similarity index 100% rename from src/core/cil/CILAst.py rename to src/core/cil/CilAst.py diff --git a/src/core/cil/COOLToCILVisitor.py b/src/core/cil/CoolToCilVisitor.py similarity index 99% rename from src/core/cil/COOLToCILVisitor.py rename to src/core/cil/CoolToCilVisitor.py index 9ec636a5f..1078be34e 100644 --- a/src/core/cil/COOLToCILVisitor.py +++ b/src/core/cil/CoolToCilVisitor.py @@ -1,5 +1,5 @@ -import core.tools.COOLAst as cool -from .BaseCOOLToCILVisitor import * +import core.cool.CoolAst as cool +from .BaseCoolToCilVisitor import * from core.tools import visitor from ..tools.Semantic import Scope diff --git a/src/Tools/Tools/COOLAst.py b/src/core/cool/CoolAst.py similarity index 100% rename from src/Tools/Tools/COOLAst.py rename to src/core/cool/CoolAst.py diff --git a/src/Tools/format_visitor.py b/src/core/cool/CoolAstFormatter.py similarity index 92% rename from src/Tools/format_visitor.py rename to src/core/cool/CoolAstFormatter.py index d2a1f7d89..8d0d2f4bf 100644 --- a/src/Tools/format_visitor.py +++ b/src/core/cool/CoolAstFormatter.py @@ -1,8 +1,8 @@ -from Tools import visitor -from Parser import ProgramNode, ClassDeclarationNode, AttrDeclarationNode, FuncDeclarationNode -from Parser import IfThenElseNode, WhileLoopNode, BlockNode, LetInNode, CaseOfNode -from Parser import AssignNode, UnaryNode, BinaryNode -from Parser import FunctionCallNode, MemberCallNode, NewNode, AtomicNode +from core.tools import visitor +from core.cool.CoolAst import ProgramNode, ClassDeclarationNode, AttrDeclarationNode, FuncDeclarationNode +from core.cool.CoolAst import IfThenElseNode, WhileLoopNode, BlockNode, LetInNode, CaseOfNode +from core.cool.CoolAst import AssignNode, UnaryNode, BinaryNode +from core.cool.CoolAst import FunctionCallNode, MemberCallNode, NewNode, AtomicNode # Visitor para imprimir el AST diff --git a/src/core/format_visitor.py b/src/core/format_visitor.py deleted file mode 100644 index fd66ad9db..000000000 --- a/src/core/format_visitor.py +++ /dev/null @@ -1,116 +0,0 @@ -from core import visitor -from Parser import ProgramNode, ClassDeclarationNode, AttrDeclarationNode, FuncDeclarationNode -from Parser import IfThenElseNode, WhileLoopNode, BlockNode, LetInNode, CaseOfNode -from Parser import AssignNode, UnaryNode, BinaryNode -from Parser import FunctionCallNode, MemberCallNode, NewNode, AtomicNode - -# Visitor para imprimir el AST - -class FormatVisitor: - @visitor.on('node') - def visit(self, node, tabs): - pass - - @visitor.when(ProgramNode) - def visit(self, node, tabs=0): - ans = '\t' * tabs + f'\\__ProgramNode [ ... ]' - statements = '\n'.join(self.visit(child, tabs + 1) for child in node.declarations) - return f'{ans}\n{statements}' - - @visitor.when(ClassDeclarationNode) - def visit(self, node, tabs=0): - parent = '' if node.parent is None else f"inherits {node.parent.lex}" - ans = '\t' * tabs + f'\\__ClassDeclarationNode: class {node.id.lex} {parent} {{ ... }}' - features = '\n'.join(self.visit(child, tabs + 1) for child in node.features) - return f'{ans}\n{features}' - - @visitor.when(AttrDeclarationNode) - def visit(self, node, tabs=0): - ans = '\t' * tabs + f'\\__AttrDeclarationNode: {node.id.lex}: {node.type.lex}' + (' <- ' if node.expression else '') + ';' - expr = self.visit(node.expression, tabs + 1) if node.expression else None - return f'{ans}' + (f'\n{expr}' if expr else '') - - @visitor.when(FuncDeclarationNode) - def visit(self, node, tabs=0): - params = ', '.join(': '.join(tok.lex for tok in param) for param in node.params) - ans = '\t' * tabs + f'\\__FuncDeclarationNode: {node.id.lex}({params}): {node.type.lex} {{ }}' - body = self.visit(node.body, tabs + 1) - return f'{ans}\n{body}' - - @visitor.when(IfThenElseNode) - def visit(self, node, tabs=0): - ans = '\t' * tabs + f'\\_IfThenElseNode: if then else fi' - cond = self.visit(node.condition, tabs + 1) - if_body = self.visit(node.if_body, tabs + 1) - else_body = self.visit(node.else_body, tabs + 1) - return f'{ans}\n{cond}\n{if_body}\n{else_body}' - - @visitor.when(WhileLoopNode) - def visit(self, node, tabs=0): - ans = '\t' * tabs + f'\\_WhileNode: while loop pool' - cond = self.visit(node.condition, tabs + 1) - body = self.visit(node.body, tabs + 1) - return f'{ans}\n{cond}\n{body}' - - @visitor.when(BlockNode) - def visit(self, node, tabs=0): - ans = '\t' * tabs + f'\\_BlockNode: {{ ; ... ; }}' - expressions = '\n'.join(self.visit(expr, tabs + 1) for expr in node.expressions) - return f'{ans}\n{expressions}' - - @visitor.when(LetInNode) - def visit(self, node, tabs=0): - let_body = ', '.join(f'{idx.lex}: {typex.lex}' + (' <- ' if expr else '') for idx, typex, expr in node.let_body) - ans = '\t' * tabs + f'\\_LetInNode: let {let_body} in ' - lets = '\n'.join(self.visit(expr, tabs + 1) for _, _, expr in node.let_body if expr) - body = self.visit(node.in_body, tabs + 1) - return f'{ans}\n{lets}\n{body}' - - @visitor.when(CaseOfNode) - def visit(self, node, tabs=0): - case_body = ' '.join(f'{idx.lex}: {typex.lex} => ;' for idx, typex, expr in node.branches) - ans = '\t' * tabs + f'\\_CaseOfNode: case of {case_body} esac' - expression = self.visit(node.expression, tabs + 1) - body = '\n'.join(self.visit(expr, tabs + 1) for _, _, expr in node.branches) - return f'{ans}\n{expression}\n{body}' - - @visitor.when(AssignNode) - def visit(self, node, tabs=0): - ans = '\t' * tabs + f'\\_AssingNode: {node.id.lex} <- ' - expr = self.visit(node.expression, tabs + 1) - return f'{ans}\n{expr}' - - @visitor.when(UnaryNode) - def visit(self, node, tabs=0): - ans = '\t' * tabs + f'\\__{node.__class__.__name__} ' - expression = self.visit(node.expression, tabs + 1) - return f'{ans}\n{expression}' - - @visitor.when(BinaryNode) - def visit(self, node, tabs=0): - ans = '\t' * tabs + f'\\__ {node.__class__.__name__} ' - left = self.visit(node.left, tabs + 1) - right = self.visit(node.right, tabs + 1) - return f'{ans}\n{left}\n{right}' - - @visitor.when(FunctionCallNode) - def visit(self, node, tabs=0): - obj = self.visit(node.obj, tabs + 1) - typex = f'@{node.type.lex}' if node.type else '' - ans = '\t' * tabs + f'\\__FunctionCallNode: {typex}.{node.id.lex}(, ..., )' - args = '\n'.join(self.visit(arg, tabs + 1) for arg in node.args) - return f'{ans}\n{obj}\n{args}' - - @visitor.when(MemberCallNode) - def visit(self, node, tabs=0): - ans = '\t' * tabs + f'\\__MemberCallNode: {node.id.lex}(, ..., )' - args = '\n'.join(self.visit(arg, tabs + 1) for arg in node.args) - return f'{ans}\n{args}' - - @visitor.when(NewNode) - def visit(self, node, tabs=0): - return '\t' * tabs + f'\\__ NewNode: new {node.type.lex}' - - @visitor.when(AtomicNode) - def visit(self, node, tabs=0): - return '\t' * tabs + f'\\__ {node.__class__.__name__}: {node.token.lex}' \ No newline at end of file diff --git a/src/core/lexer/Lexer.py b/src/core/lexer/Lexer.py index 7a39de02f..48ddbbf27 100644 --- a/src/core/lexer/Lexer.py +++ b/src/core/lexer/Lexer.py @@ -1,5 +1,5 @@ import ply.lex as lex -from core.tools.utils import Token +from core.tools.Utils import Token from core.parser.Parser import CoolGrammar from core.tools.Errors import LexicographicError diff --git a/src/core/mips/CilToMipsVisitor.py b/src/core/mips/CilToMipsVisitor.py index 030599536..4e8271284 100644 --- a/src/core/mips/CilToMipsVisitor.py +++ b/src/core/mips/CilToMipsVisitor.py @@ -1,4 +1,4 @@ -import core.cil.CILAst as cil +import core.cil.CilAst as cil import core.mips.MipsAst as mips from ..tools import visitor diff --git a/src/core/mips/MIPSAstFormatter.py b/src/core/mips/MipsAstFormatter.py similarity index 99% rename from src/core/mips/MIPSAstFormatter.py rename to src/core/mips/MipsAstFormatter.py index e74b91a7a..c07c10925 100644 --- a/src/core/mips/MIPSAstFormatter.py +++ b/src/core/mips/MipsAstFormatter.py @@ -19,7 +19,7 @@ def visit(self, node): mipsCode = f'{data}\n\n{names_table}\n\n{proto_table}\n\n{types}\n\n\n.text\n.globl main\n{code}\n\n' - with open('core/mips/mips_basics.asm', 'r') as f: + with open('core/mips/MipsBasics.asm', 'r') as f: mipsCode += "".join(f.readlines()) return mipsCode diff --git a/src/core/mips/mips_basics.asm b/src/core/mips/MipsBasics.asm similarity index 100% rename from src/core/mips/mips_basics.asm rename to src/core/mips/MipsBasics.asm diff --git a/src/core/parser/Parser.py b/src/core/parser/Parser.py index 46f6b193e..4a393ecc2 100644 --- a/src/core/parser/Parser.py +++ b/src/core/parser/Parser.py @@ -1,6 +1,6 @@ -from core.tools.pycompiler import Grammar -from core.tools.Parser_LR1 import LR1Parser -from core.tools.COOLAst import * +from core.tools.Pycompiler import Grammar +from core.tools.ParserLR1 import LR1Parser +from core.cool.CoolAst import * # Representacion de la gramatica de COOL utilizando la clase grammar diff --git a/src/core/semantic/Type_Builder.py b/src/core/semantic/TypeBuilder.py similarity index 100% rename from src/core/semantic/Type_Builder.py rename to src/core/semantic/TypeBuilder.py diff --git a/src/core/semantic/Type_Checker.py b/src/core/semantic/TypeChecker.py similarity index 100% rename from src/core/semantic/Type_Checker.py rename to src/core/semantic/TypeChecker.py diff --git a/src/core/semantic/Type_Collector.py b/src/core/semantic/TypeCollector.py similarity index 100% rename from src/core/semantic/Type_Collector.py rename to src/core/semantic/TypeCollector.py diff --git a/src/Tools/Tools/automata.py b/src/core/tools/Automata.py similarity index 100% rename from src/Tools/Tools/automata.py rename to src/core/tools/Automata.py diff --git a/src/core/tools/COOLAst.py b/src/core/tools/COOLAst.py deleted file mode 100644 index 4efb93764..000000000 --- a/src/core/tools/COOLAst.py +++ /dev/null @@ -1,210 +0,0 @@ -# Clases necesarias para representar el AST del programa COOL -class Node: - pass - -# Raiz del AST -class ProgramNode(Node): - def __init__(self, declarations): - self.declarations = declarations - self.line = declarations[0].line - self.column = declarations[0].column - - -class DeclarationNode(Node): - pass - - -class ClassDeclarationNode(DeclarationNode): - def __init__(self, classx, idx, features, parent=None): - self.id = idx - self.parent = parent - self.features = features - self.line = classx.line - self.column = classx.column - - -class AttrDeclarationNode(DeclarationNode): - def __init__(self, idx, typex, expression=None): - self.id = idx - self.type = typex - self.expression = expression - self.line = idx.line - self.column = idx.column - - -class FuncDeclarationNode(DeclarationNode): - def __init__(self, idx, params, return_type, body): - self.id = idx - self.params = params - self.type = return_type - self.body = body - self.line = idx.line - self.column = idx.column - - -class ExpressionNode(Node): - pass - - -class IfThenElseNode(ExpressionNode): - def __init__(self, ifx, condition, if_body, else_body): - self.condition = condition - self.if_body = if_body - self.else_body = else_body - self.line = ifx.line - self.column = ifx.column - - -class WhileLoopNode(ExpressionNode): - def __init__(self, whilex, condition, body): - self.condition = condition - self.body = body - self.line = whilex.line - self.column = whilex.column - - -class BlockNode(ExpressionNode): - def __init__(self, brace, expressions): - self.expressions = expressions - self.line = brace.line - self.column = brace.column - - -class LetInNode(ExpressionNode): - def __init__(self, let, let_body, in_body): - self.let_body = let_body - self.in_body = in_body - self.line = let.line - self.column = let.column - - -class CaseOfNode(ExpressionNode): - def __init__(self, case, expression, branches): - self.expression = expression - self.branches = branches - self.line = case.line - self.column = case.column - - -class AssignNode(ExpressionNode): - def __init__(self, idx, expression): - self.id = idx - self.expression = expression - self.line = idx.line - self.column = idx.column - - -class UnaryNode(ExpressionNode): - def __init__(self, expression): - self.expression = expression - self.line = expression.line - self.column = expression.column - - -class NotNode(UnaryNode): - def __init__(self, notx, expression): - super().__init__(expression) - self.line = notx.line - self.column = notx.column - - -class BinaryNode(ExpressionNode): - def __init__(self, left, operator, right): - self.left = left - self.right = right - self.line = operator.line - self.column = operator.column - - -class LessEqualNode(BinaryNode): - pass - - -class LessNode(BinaryNode): - pass - - -class EqualNode(BinaryNode): - pass - - -class ArithmeticNode(BinaryNode): - pass - - -class PlusNode(ArithmeticNode): - pass - - -class MinusNode(ArithmeticNode): - pass - - -class StarNode(ArithmeticNode): - pass - - -class DivNode(ArithmeticNode): - pass - - -class IsVoidNode(UnaryNode): - def __init__(self, isvoid, expression): - super().__init__(expression) - self.line = isvoid.line - self.column = isvoid.column - - -class ComplementNode(UnaryNode): - def __init__(self, complement, expression): - super().__init__(expression) - self.line = complement.line - self.column = complement.column - - -class FunctionCallNode(ExpressionNode): - def __init__(self, obj, idx, args, typex=None): - self.obj = obj - self.id = idx - self.args = args - self.type = typex - self.line = obj.line - self.column = obj.column - - -class MemberCallNode(ExpressionNode): - def __init__(self, idx, args): - self.id = idx - self.args = args - self.line = idx.line - self.column = idx.column - - -class NewNode(ExpressionNode): - def __init__(self, new, typex): - self.type = typex - self.line = new.line - self.column = new.column - - -class AtomicNode(ExpressionNode): - def __init__(self, token): - self.token = token - self.line = token.line - self.column = token.column - - -class IntegerNode(AtomicNode): - pass - - -class IdNode(AtomicNode): - pass - - -class StringNode(AtomicNode): - pass - - -class BoolNode(AtomicNode): - pass diff --git a/src/Tools/Tools/evaluation.py b/src/core/tools/Evaluation.py similarity index 97% rename from src/Tools/Tools/evaluation.py rename to src/core/tools/Evaluation.py index 941113216..52cbd4958 100644 --- a/src/Tools/Tools/evaluation.py +++ b/src/core/tools/Evaluation.py @@ -1,4 +1,4 @@ -from .parsing import ShiftReduceParser +from .Parsing import ShiftReduceParser ''' Este metodo retorna el ast dado el conjunto de producciones, diff --git a/src/Tools/Tools/Firsts_and_Follows.py b/src/core/tools/FirstsAndFollows.py similarity index 99% rename from src/Tools/Tools/Firsts_and_Follows.py rename to src/core/tools/FirstsAndFollows.py index 029b33aa0..1a8e05c61 100644 --- a/src/Tools/Tools/Firsts_and_Follows.py +++ b/src/core/tools/FirstsAndFollows.py @@ -1,4 +1,4 @@ -from .utils import ContainerSet +from .Utils import ContainerSet ''' Dada una forma oracional alpha computa sus firsts diff --git a/src/core/tools/Firsts_and_Follows.py b/src/core/tools/Firsts_and_Follows.py deleted file mode 100644 index 029b33aa0..000000000 --- a/src/core/tools/Firsts_and_Follows.py +++ /dev/null @@ -1,128 +0,0 @@ -from .utils import ContainerSet - -''' -Dada una forma oracional alpha computa sus firsts -''' -def compute_local_first(firsts, alpha): - first_alpha = ContainerSet() - - try: - alpha_is_epsilon = alpha.IsEpsilon - except: - alpha_is_epsilon = False - - # alpha == epsilon ? First(alpha) = { epsilon } - if alpha_is_epsilon: - first_alpha.set_epsilon() - return first_alpha - - # alpha = X1 ... XN - # First(Xi) subconjunto First(alpha) - # epsilon pertenece a First(X1)...First(Xi) ? First(Xi+1) subconjunto de First(X) y First(alpha) - # epsilon pertenece a First(X1)...First(XN) ? epsilon pertence a First(X) y al First(alpha) - for symbol in alpha: - first_alpha.update(firsts[symbol]) - if not firsts[symbol].contains_epsilon: - break - else: - first_alpha.set_epsilon() - - return first_alpha - - -''' -Computa los firsts de todos los simbolos de la gramatica -''' -def compute_firsts(G): - firsts = {} - change = True - - # Los firsts de los terminales son ellos mismos - for terminal in G.terminals: - firsts[terminal] = ContainerSet(terminal) - - # Inicializa los firsts de los noterminales como un conjunto vacio - for nonterminal in G.nonTerminals: - firsts[nonterminal] = ContainerSet() - - # Metodo de punto fijo, mientras halla algun cambio en los firsts - # de algun simbolo, itera por todas las producciones recalculando los firsts - # del noterminal correspondiente - while change: - change = False - - # P: X -> alpha - for production in G.Productions: - X = production.Left - alpha = production.Right - - # get current First(X) - first_X = firsts[X] - - # init First(alpha) - try: - first_alpha = firsts[alpha] - except KeyError: - first_alpha = firsts[alpha] = ContainerSet() - - # CurrentFirst(alpha) - local_first = compute_local_first(firsts, alpha) - - # update First(X) and First(alpha) from CurrentFirst(alpha) - change |= first_alpha.hard_update(local_first) - change |= first_X.hard_update(local_first) - - return firsts - -''' -Computa los follows de cada noterminal de la gramatica -''' -from itertools import islice -def compute_follows(G, firsts): - follows = {} - change = True - - local_firsts = {} - - # Inicializa los follows de los noterminales como un conjunto vacio - for nonterminal in G.nonTerminals: - follows[nonterminal] = ContainerSet() - # EOF pertenece a los follows del simbolo inicial - follows[G.startSymbol] = ContainerSet(G.EOF) - - # Metodo de punto fijo, mientras haya cambios en los follows - # de algun noterminal, itera por todas las producciones y recalcula - # los follows de cada noterminal - while change: - change = False - - # P: X -> alpha - for production in G.Productions: - X = production.Left - alpha = production.Right - - follow_X = follows[X] - - # Si la produccion actual es de la forma X -> v Y w - # donde v y w son formas oracionales entonces cada elemento - # que pertenece al first de w (excepto epsilon) pertenece tambien al follow - # de Y. Si ademas w ->* epsilon entonces los follows de X pertenecen - # al follows de Y. - # X -> zeta Y beta - # First(beta) - { epsilon } subset of Follow(Y) - # beta ->* epsilon or X -> zeta Y ? Follow(X) subset of Follow(Y) - for i, symbol in enumerate(alpha): - if symbol.IsNonTerminal: - try: - first_beta = local_firsts[alpha, i] - except KeyError: - first_beta = local_firsts[alpha, i] = compute_local_first(firsts, islice(alpha, i + 1, None)) - - change |= follows[symbol].update(first_beta) - - if first_beta.contains_epsilon: - change |= follows[symbol].update(follow_X) - - return follows - - diff --git a/src/core/tools/Parser_LR1.py b/src/core/tools/ParserLR1.py similarity index 97% rename from src/core/tools/Parser_LR1.py rename to src/core/tools/ParserLR1.py index f12ba3162..1829ecca8 100644 --- a/src/core/tools/Parser_LR1.py +++ b/src/core/tools/ParserLR1.py @@ -1,8 +1,8 @@ -from .pycompiler import Item -from .utils import ContainerSet -from .Firsts_and_Follows import compute_firsts, compute_local_first -from .automata import State -from .parsing import ShiftReduceParser +from .Pycompiler import Item +from .Utils import ContainerSet +from .FirstsAndFollows import compute_firsts, compute_local_first +from .Automata import State +from .Parsing import ShiftReduceParser ''' Recibe un item LR(1) y devuelve el conjunto de items que diff --git a/src/Tools/Tools/parsing.py b/src/core/tools/Parsing.py similarity index 100% rename from src/Tools/Tools/parsing.py rename to src/core/tools/Parsing.py diff --git a/src/Tools/Tools/pycompiler.py b/src/core/tools/Pycompiler.py similarity index 100% rename from src/Tools/Tools/pycompiler.py rename to src/core/tools/Pycompiler.py diff --git a/src/core/tools/utils.py b/src/core/tools/Utils.py similarity index 99% rename from src/core/tools/utils.py rename to src/core/tools/Utils.py index 1edd4972e..f8dcc227c 100644 --- a/src/core/tools/utils.py +++ b/src/core/tools/Utils.py @@ -1,4 +1,4 @@ -from .pycompiler import Production, Sentence, Symbol, EOF, Epsilon +from .Pycompiler import Production, Sentence, Symbol, EOF, Epsilon class ContainerSet: def __init__(self, *values, contains_epsilon=False): diff --git a/src/core/tools/automata.py b/src/core/tools/automata.py deleted file mode 100644 index 2bdadd2d1..000000000 --- a/src/core/tools/automata.py +++ /dev/null @@ -1,210 +0,0 @@ -try: - import pydot -except: - pass - -# Esta es la clase dada en clases practicas -# para representar un DFA, -# o cualkier grafo en general -class State: - def __init__(self, state, final=False, formatter=lambda x: str(x), shape='circle'): - self.state = state - self.final = final - self.transitions = {} - self.epsilon_transitions = set() - self.tag = None - self.formatter = formatter - self.shape = shape - - # The method name is set this way from compatibility issues. - def set_formatter(self, value, attr='formatter', visited=None): - if visited is None: - visited = set() - elif self in visited: - return - - visited.add(self) - self.__setattr__(attr, value) - for destinations in self.transitions.values(): - for node in destinations: - node.set_formatter(value, attr, visited) - for node in self.epsilon_transitions: - node.set_formatter(value, attr, visited) - return self - - def has_transition(self, symbol): - return symbol in self.transitions - - def add_transition(self, symbol, state): - try: - self.transitions[symbol].append(state) - except: - self.transitions[symbol] = [state] - return self - - def add_epsilon_transition(self, state): - self.epsilon_transitions.add(state) - return self - - def recognize(self, string): - states = self.epsilon_closure - for symbol in string: - states = self.move_by_state(symbol, *states) - states = self.epsilon_closure_by_state(*states) - return any(s.final for s in states) - - def to_deterministic(self, formatter=lambda x: str(x)): - closure = self.epsilon_closure - start = State(tuple(closure), any(s.final for s in closure), formatter) - - closures = [ closure ] - states = [ start ] - pending = [ start ] - - while pending: - state = pending.pop() - symbols = { symbol for s in state.state for symbol in s.transitions } - - for symbol in symbols: - move = self.move_by_state(symbol, *state.state) - closure = self.epsilon_closure_by_state(*move) - - if closure not in closures: - new_state = State(tuple(closure), any(s.final for s in closure), formatter) - closures.append(closure) - states.append(new_state) - pending.append(new_state) - else: - index = closures.index(closure) - new_state = states[index] - - state.add_transition(symbol, new_state) - - return start - - @staticmethod - def from_nfa(nfa, get_states=False): - states = [] - for n in range(nfa.states): - state = State(n, n in nfa.finals) - states.append(state) - - for (origin, symbol), destinations in nfa.map.items(): - origin = states[origin] - origin[symbol] = [ states[d] for d in destinations ] - - if get_states: - return states[nfa.start], states - return states[nfa.start] - - @staticmethod - def move_by_state(symbol, *states): - return { s for state in states if state.has_transition(symbol) for s in state[symbol]} - - @staticmethod - def epsilon_closure_by_state(*states): - closure = { state for state in states } - - l = 0 - while l != len(closure): - l = len(closure) - tmp = [s for s in closure] - for s in tmp: - for epsilon_state in s.epsilon_transitions: - closure.add(epsilon_state) - return closure - - @property - def epsilon_closure(self): - return self.epsilon_closure_by_state(self) - - @property - def name(self): - return self.formatter(self.state) - - def get(self, symbol): - target = self.transitions[symbol] - assert len(target) == 1 - return target[0] - - def __getitem__(self, symbol): - if symbol == '': - return self.epsilon_transitions - try: - return self.transitions[symbol] - except KeyError: - return None - - def __setitem__(self, symbol, value): - if symbol == '': - self.epsilon_transitions = value - else: - self.transitions[symbol] = value - - def __repr__(self): - return str(self) - - def __str__(self): - return str(self.state) - - def __hash__(self): - return hash(self.state) - - def __iter__(self): - yield from self._visit() - - def _visit(self, visited=None): - if visited is None: - visited = set() - elif self in visited: - return - - visited.add(self) - yield self - - for destinations in self.transitions.values(): - for node in destinations: - yield from node._visit(visited) - for node in self.epsilon_transitions: - yield from node._visit(visited) - - def graph(self, dir = 'LR'): - G = pydot.Dot(rankdir=dir, margin=0.1) - G.add_node(pydot.Node('start', shape='plaintext', label='', width=0, height=0)) - - visited = set() - def visit(start): - ids = id(start) - if ids not in visited: - visited.add(ids) - G.add_node(pydot.Node(ids, label = f'\'{start.name}\'', shape=self.shape, style='bold' if start.final else '')) - for tran, destinations in start.transitions.items(): - for end in destinations: - visit(end) - G.add_edge(pydot.Edge(ids, id(end), label=tran, labeldistance=2)) - for end in start.epsilon_transitions: - visit(end) - G.add_edge(pydot.Edge(ids, id(end), label='ε', labeldistance=2)) - - visit(self) - G.add_edge(pydot.Edge('start', id(self), label='', style='dashed')) - - return G - - def _repr_svg_(self): - try: - return self.graph().create_svg().decode('utf8') - except: - pass - - def write_to(self, fname): - return self.graph().write_svg(fname) - -def multiline_formatter(state): - return '\n'.join(str(item) for item in state) - -def lr0_formatter(state): - try: - return '\n'.join(str(item)[:-4] for item in state) - except TypeError: - return str(state)[:-4] \ No newline at end of file diff --git a/src/core/tools/evaluation.py b/src/core/tools/evaluation.py deleted file mode 100644 index 941113216..000000000 --- a/src/core/tools/evaluation.py +++ /dev/null @@ -1,44 +0,0 @@ -from .parsing import ShiftReduceParser - -''' -Este metodo retorna el ast dado el conjunto de producciones, -las operaciones y los tokens -''' -def evaluate_reverse_parse(right_parse, operations, tokens): - if not right_parse or not operations or not tokens: - return - - right_parse = iter(right_parse) - tokens = iter(tokens) - stack = [] - for operation in operations: - # Si hay que hacer un shift, pasemos a analizar - # el proximo token e incluyamoslo en la pila - if operation == ShiftReduceParser.SHIFT: - token = next(tokens) - stack.append(token) - # Si hay que hacer un reduce, tomamos los elementos - # necesarios de la pila y los sustituimos por el nonterminal - # correspondiente, el cual incluimos en la pila - elif operation == ShiftReduceParser.REDUCE: - production = next(right_parse) - head, body = production - attributes = production.attributes - assert all(rule is None for rule in attributes[1:]), 'There must be only syntheticed attributes.' - rule = attributes[0] - - if len(body): - syntheticed = [None] + stack[-len(body):] - value = rule(None, syntheticed) - stack[-len(body):] = [value] - else: - stack.append(rule(None, None)) - else: - raise Exception('Invalid action!!!') - - # La pila debe terminar con el program node - # correspondiente a la raiz del ast, todos los - # tokens deben haber sido analizados - assert len(stack) == 1 - assert next(tokens).token_type == '$'#######estaba EOF, creo que tiene que ver con lo que cambie al poner el tokentype en el lexer - return stack[0] \ No newline at end of file diff --git a/src/core/tools/parsing.py b/src/core/tools/parsing.py deleted file mode 100644 index 17eae0e2d..000000000 --- a/src/core/tools/parsing.py +++ /dev/null @@ -1,95 +0,0 @@ -from .Errors import * - -# Clase base para los parsers Shift-Reduce -class ShiftReduceParser: - SHIFT = 'SHIFT' - REDUCE = 'REDUCE' - OK = 'OK' - - def __init__(self, G, verbose=False): - self.G = G - self.verbose = verbose - self.action = {} - self.goto = {} - self.HasConflict = False - self.errors = [] - self._build_parsing_table() - - def _build_parsing_table(self): - raise NotImplementedError() - - ''' - Retorna las producciones y operaciones necesarias para el - proceso de parsing del string w. - ''' - def __call__(self, w): - stack = [0] - cursor = 0 - output = [] - operations = [] - self.errors = [] - - while True: - # Estado del automata en el que me encuentro - state = stack[-1] - # Siguiente simbolo a analizar - lookahead = w[cursor].token_type - if self.verbose: print(stack, '<---||--->', w[cursor:]) - #(Detect error) - try: - action, tag = self.action[state, lookahead][0] - except KeyError: - # Si no existe transicion desde el estado actual con el simbolo - # correspondiente es porque ha ocurrido un error - lookahead = w[cursor] - line = lookahead.line - column = lookahead.column - if lookahead.lex == '$': - self.errors.append(SyntacticError(0, 0, 'EOF')) - else: - self.errors.append(SyntacticError(line, column, f'"{lookahead.lex}"')) - return None, None - - # Si la accion es Shift, incluyo en la pila el simbolo actual y - # el estado al que necesito moverme - if action == self.SHIFT: - stack.append(lookahead) - stack.append(tag) - operations.append(self.SHIFT) - cursor += 1 - # Si la accion es Reduce, saco de la pila los simbolos correspondientes - # a la produccion q debo reducir - elif action == self.REDUCE: - output.append(tag) - for i in range(len(tag.Right)): - stack.pop() - assert stack[-1] == tag.Right[-i - 1].Name, 'Symbol does not match' - stack.pop() - # Tomo el estado al que debo moverme - index = self.goto[stack[-1], tag.Left.Name][0] - # Incluyo en la pila el simbolo reducido y el estado al - # que necesito moverme - stack.append(tag.Left.Name) - stack.append(index) - operations.append(self.REDUCE) - #(OK case) - elif action == self.OK: - return output, operations - #(Invalid case) - else: - lookahead = w[cursor] - line = lookahead.line - column = lookahead.column - if lookahead.lex == '$': - self.errors.append(SyntacticError(0, 0, 'EOF')) - else: - self.errors.append(SyntacticError(line, column, f'"{lookahead.lex}"')) - return None, None - - def _register(self, table, key, value): - if key not in table: - table[key] = [value] - elif not any(i for i in table[key] if i == value): - self.HasConflict = True - table[key].append(value) - diff --git a/src/core/tools/pycompiler.py b/src/core/tools/pycompiler.py deleted file mode 100644 index 74ad3bc0d..000000000 --- a/src/core/tools/pycompiler.py +++ /dev/null @@ -1,560 +0,0 @@ -import json - -#Clase base para los simbolos de la gramatica, terminales y noterminales -class Symbol(object): - - def __init__(self, name, grammar): - self.Name = name - self.Grammar = grammar - - def __str__(self): - return self.Name - - def __repr__(self): - return self.Name - - def __add__(self, other): - if isinstance(other, Symbol): - return Sentence(self, other) - - if isinstance(other, Sentence): - return Sentence(*((self,) + other._symbols)) - - raise TypeError(other) - - def __or__(self, other): - - if isinstance(other, (Sentence)): - return SentenceList(Sentence(self), other) - - raise TypeError(other) - - @property - def IsEpsilon(self): - return False - - def __len__(self): - return 1 - -class NonTerminal(Symbol): - - - def __init__(self, name, grammar): - super().__init__(name, grammar) - self.productions = [] - - - def __imod__(self, other): - - if isinstance(other, (Sentence)): - p = Production(self, other) - self.Grammar.Add_Production(p) - return self - - if isinstance(other, tuple): - assert len(other) > 1 - - if len(other) == 2: - other += (None,) * len(other[0]) - - assert len(other) == len(other[0]) + 2, "Debe definirse una, y solo una, regla por cada símbolo de la producción" - # assert len(other) == 2, "Tiene que ser una Tupla de 2 elementos (sentence, attribute)" - - if isinstance(other[0], Symbol) or isinstance(other[0], Sentence): - p = AttributeProduction(self, other[0], other[1:]) - else: - raise Exception("") - - self.Grammar.Add_Production(p) - return self - - if isinstance(other, Symbol): - p = Production(self, Sentence(other)) - self.Grammar.Add_Production(p) - return self - - if isinstance(other, SentenceList): - - for s in other: - p = Production(self, s) - self.Grammar.Add_Production(p) - - return self - - raise TypeError(other) - - def __add__(self, other): - if isinstance(other, Symbol): - return Sentence(self, other) - - if isinstance(other, Sentence): - return Sentence(self, *(other._symbols)) - - if other: - raise TypeError(other) - - def __iter__(self): - yield self - - @property - def IsTerminal(self): - return False - - @property - def IsNonTerminal(self): - return True - - @property - def IsEpsilon(self): - return False - -class Terminal(Symbol): - - def __init__(self, name, grammar): - super().__init__(name, grammar) - - def __iter__(self): - yield self - - def __add__(self, other): - if isinstance(other, Symbol): - return Sentence(self, other) - - if isinstance(other, Sentence): - return Sentence(self, *(other._symbols)) - - if other: - raise TypeError(other) - - @property - def IsTerminal(self): - return True - - @property - def IsNonTerminal(self): - return False - - @property - def IsEpsilon(self): - return False - -class EOF(Terminal): - - def __init__(self, Grammar): - super().__init__('$', Grammar) - -class Sentence(object): - - def __init__(self, *args): - self._symbols = tuple(x for x in args if not x.IsEpsilon) - self.hash = hash(self._symbols) - - def __len__(self): - return len(self._symbols) - - def __add__(self, other): - if isinstance(other, Symbol): - return Sentence(*(self._symbols + (other,))) - - if isinstance(other, Sentence): - return Sentence(*(self._symbols + other._symbols)) - - if other: - raise TypeError(other) - - def __or__(self, other): - if isinstance(other, Sentence): - return SentenceList(self, other) - - if isinstance(other, Symbol): - return SentenceList(self, Sentence(other)) - - if isinstance(other, SentenceList): - return SentenceList(self, *(other._sentences)) - - if other: - raise TypeError(other) - - def __repr__(self): - return str(self) - - def __str__(self): - return ("%s " * len(self._symbols) % tuple(self._symbols)).strip() - - def __iter__(self): - return iter(self._symbols) - - def __getitem__(self, index): - return self._symbols[index] - - def __eq__(self, other): - return self._symbols == other._symbols - - def __hash__(self): - return self.hash - - @property - def IsEpsilon(self): - return False - -class SentenceList(object): - - def __init__(self, *args): - self._sentences = list(args) - - def Add(self, symbol): - if not symbol and (symbol is None or not symbol.IsEpsilon): - raise ValueError(symbol) - - self._sentences.append(symbol) - - def __iter__(self): - return iter(self._sentences) - - def __or__(self, other): - if isinstance(other, Sentence): - self.Add(other) - return self - - if isinstance(other, Symbol): - return self | Sentence(other) - - if isinstance(other, SentenceList): - return SentenceList(self._sentences + other._sentences) - -class Epsilon(Terminal, Sentence): - - def __init__(self, grammar): - super().__init__('epsilon', grammar) - - - def __str__(self): - return "ε" - - def __repr__(self): - return 'epsilon' - - def __iter__(self): - yield from () - - def __len__(self): - return 0 - - def __add__(self, other): - return other - - def __eq__(self, other): - return isinstance(other, (Epsilon,)) - - def __hash__(self): - return hash("") - - @property - def IsEpsilon(self): - return True - -class Production(object): - - def __init__(self, nonTerminal, sentence): - - self.Left = nonTerminal - self.Right = sentence - - def __str__(self): - - return '%s --> %s' % (self.Left, self.Right) - - def __repr__(self): - return '%s -> %s' % (self.Left, self.Right) - - def __iter__(self): - yield self.Left - yield self.Right - - def __eq__(self, other): - return isinstance(other, Production) and self.Left == other.Left and self.Right == other.Right - - def __hash__(self): - return hash((self.Left, self.Right)) - - @property - def IsEpsilon(self): - return self.Right.IsEpsilon - -class AttributeProduction(Production): - - def __init__(self, nonTerminal, sentence, attributes): - if not isinstance(sentence, Sentence) and isinstance(sentence, Symbol): - sentence = Sentence(sentence) - super(AttributeProduction, self).__init__(nonTerminal, sentence) - - self.attributes = attributes - - def __str__(self): - return '%s := %s' % (self.Left, self.Right) - - def __repr__(self): - return '%s -> %s' % (self.Left, self.Right) - - def __iter__(self): - yield self.Left - yield self.Right - - - @property - def IsEpsilon(self): - return self.Right.IsEpsilon - - # sintetizar en ingles??????, pending aggrement - def syntetice(self): - pass - -class Grammar(): - - def __init__(self): - - self.Productions = [] - self.nonTerminals = [] - self.terminals = [] - self.startSymbol = None - # production type - self.pType = None - self.Epsilon = Epsilon(self) - self.EOF = EOF(self) - - self.symbDict = { '$': self.EOF } - - def NonTerminal(self, name, startSymbol = False): - - name = name.strip() - if not name: - raise Exception("Empty name") - - term = NonTerminal(name,self) - - if startSymbol: - - if self.startSymbol is None: - self.startSymbol = term - else: - raise Exception("Cannot define more than one start symbol.") - - self.nonTerminals.append(term) - self.symbDict[name] = term - return term - - def NonTerminals(self, names): - - ans = tuple((self.NonTerminal(x) for x in names.strip().split())) - - return ans - - - def Add_Production(self, production): - - if len(self.Productions) == 0: - self.pType = type(production) - - assert type(production) == self.pType, "The Productions most be of only 1 type." - - production.Left.productions.append(production) - self.Productions.append(production) - - - def Terminal(self, name): - - name = name.strip() - if not name: - raise Exception("Empty name") - - term = Terminal(name, self) - self.terminals.append(term) - self.symbDict[name] = term - return term - - def Terminals(self, names): - - ans = tuple((self.Terminal(x) for x in names.strip().split())) - - return ans - - - def __str__(self): - - mul = '%s, ' - - ans = 'Non-Terminals:\n\t' - - nonterminals = mul * (len(self.nonTerminals)-1) + '%s\n' - - ans += nonterminals % tuple(self.nonTerminals) - - ans += 'Terminals:\n\t' - - terminals = mul * (len(self.terminals)-1) + '%s\n' - - ans += terminals % tuple(self.terminals) - - ans += 'Productions:\n\t' - - ans += str(self.Productions) - - return ans - - def __getitem__(self, name): - try: - return self.symbDict[name] - except KeyError: - return None - - @property - def to_json(self): - - productions = [] - - for p in self.Productions: - head = p.Left.Name - - body = [] - - for s in p.Right: - body.append(s.Name) - - productions.append({'Head':head, 'Body':body}) - - d={'NonTerminals':[symb.Name for symb in self.nonTerminals], 'Terminals': [symb.Name for symb in self.terminals],\ - 'Productions':productions} - - # [{'Head':p.Left.Name, "Body": [s.Name for s in p.Right]} for p in self.Productions] - return json.dumps(d) - - @staticmethod - def from_json(data): - data = json.loads(data) - - G = Grammar() - dic = {'epsilon':G.Epsilon} - - for term in data['Terminals']: - dic[term] = G.Terminal(term) - - for noTerm in data['NonTerminals']: - dic[noTerm] = G.NonTerminal(noTerm) - - for p in data['Productions']: - head = p['Head'] - dic[head] %= Sentence(*[dic[term] for term in p['Body']]) - - return G - - def copy(self): - G = Grammar() - G.pType = self.pType - for terminal in self.terminals: - G.Terminal(terminal.Name) - for nonterminal in self.nonTerminals: - G.NonTerminal(nonterminal.Name, nonterminal == self.startSymbol) - for prod in self.Productions: - left = G.symbDict[prod.Left.Name] - if prod.IsEpsilon: - if self.pType is AttributeProduction: - G.Add_Production(AttributeProduction(left, G.Epsilon, prod.attributes)) - else: - G.Add_Production(Production(left, G.Epsilon)) - else: - right = Sentence() - for symbol in prod.Right: - right = right + G.symbDict[symbol.Name] - if self.pType is AttributeProduction: - G.Add_Production(AttributeProduction(left, right, prod.attributes)) - else: - G.Add_Production(Production(left, right)) - return G - - @property - def IsAugmentedGrammar(self): - augmented = 0 - for left, right in self.Productions: - if self.startSymbol == left: - augmented += 1 - if augmented <= 1: - return True - else: - return False - - def AugmentedGrammar(self, force=False): - if not self.IsAugmentedGrammar or force: - - G = self.copy() - # S, self.startSymbol, SS = self.startSymbol, None, self.NonTerminal('S\'', True) - S = G.startSymbol - G.startSymbol = None - SS = G.NonTerminal('S\'', True) - if G.pType is AttributeProduction: - SS %= S + G.Epsilon, lambda x : x - else: - SS %= S + G.Epsilon - - return G - else: - return self.copy() - #endchange - -class Item: - - def __init__(self, production, pos, lookaheads=[]): - self.production = production - self.pos = pos - self.lookaheads = frozenset(look for look in lookaheads) - - def __str__(self): - s = str(self.production.Left) + " -> " - if len(self.production.Right) > 0: - for i,c in enumerate(self.production.Right): - if i == self.pos: - s += "." - s += str(self.production.Right[i]) - if self.pos == len(self.production.Right): - s += "." - else: - s += "." - s += ", " + str(self.lookaheads)[10:-1] - return s - - def __repr__(self): - return str(self) - - - def __eq__(self, other): - return ( - (self.pos == other.pos) and - (self.production == other.production) and - (set(self.lookaheads) == set(other.lookaheads)) - ) - - def __hash__(self): - return hash((self.production,self.pos,self.lookaheads)) - - @property - def IsReduceItem(self): - return len(self.production.Right) == self.pos - - @property - def NextSymbol(self): - if self.pos < len(self.production.Right): - return self.production.Right[self.pos] - else: - return None - - def NextItem(self): - if self.pos < len(self.production.Right): - return Item(self.production,self.pos+1,self.lookaheads) - else: - return None - - def Preview(self, skip=1): - unseen = self.production.Right[self.pos+skip:] - return [ unseen + (lookahead,) for lookahead in self.lookaheads ] - - def Center(self): - return Item(self.production, self.pos) \ No newline at end of file From bfc6d9da65accf50e81976a1befc1e97392aceec Mon Sep 17 00:00:00 2001 From: ClaudiaOM Date: Thu, 24 Feb 2022 13:28:57 -0600 Subject: [PATCH 71/75] Reformated code and files --- doc/Readme.md | 44 +++---- src/Main.py | 5 - src/core/cil/CilAst.py | 208 +------------------------------- src/core/cil/CilAstFormatter.py | 206 +++++++++++++++++++++++++++++++ 4 files changed, 230 insertions(+), 233 deletions(-) create mode 100644 src/core/cil/CilAstFormatter.py diff --git a/doc/Readme.md b/doc/Readme.md index d5fd59011..bff552c91 100644 --- a/doc/Readme.md +++ b/doc/Readme.md @@ -51,33 +51,35 @@ cool-compiler-2021 |__Main.py core |__cil - |__BaseCOOLToCilVisitor.py - CILAst.py - COOLToCILVisitor.py + |__BaseCoolToCilVisitor.py + CilAst.py + CoolToCilVisitor.py + |__cool + |__CoolAst.py + CoolAstFormatter.py |__lexer - |__lexer.py + |__Lexer.py |__mips |__CilToMipsVisitor.py - mips_basic.asm + MipsBasics.asm MipsAst.py - MIPSAstFormatter.py + MipsAstFormatter.py |__parser |__Parser.py |__semantic - |__Type_Builder.py - Type_Checker.py - Type_Collector.py + |__TypeBuilder.py + TypeChecker.py + TypeCollector.py |__tools - |__automata.py - COOLAst.py + |__Automata.py Errors.py - evaluation.py - First_and_Follow.py - Parser_LR1.py - parsing.py - pycompiler.py + Evaluation.py + FirstAndFollows.py + ParserLR1.py + Parsing.py + Pycompiler.py Semantic.py - utils.py + Utils.py visitor.py @@ -258,11 +260,11 @@ Algunos de los errores que se chequean en esta fase son: Durante esta fase se realiza la conversión del lenguaje COOL a un lenguaje intermedio(CIL). El fichero CilAst contiene la definición de las clases usadas para conformar el AST del lenguaje CIL. -En el fichero BaseCOOLtoCILVisitor se definen los métodos básicos para registrar +En el fichero BaseCooltoCilVisitor se definen los métodos básicos para registrar una variable, parámetro, función y atributo, entre otros. Ademas se registran los tipos builtin, es decir, se escriben en código CIL las instrucciones para registrar los tipos Object, IO, String, Int y Bool, así como las funciones y atributos de cada uno de estos.. -El fichero COOLToCILVisitor es el encargado de transformar el AST de COOL en un AST de CIL, para facilitar +El fichero CoolToCilVisitor es el encargado de transformar el AST de COOL en un AST de CIL, para facilitar luego la traducción de este al lenguaje MIPS. Este fichero cuenta con un visitor que se encarga de transformar cada nodo del AST de un lenguaje a otro, algunos de los aspectos a destacar son: - En el visitor del ProgramNode se define la función entry, que es por donde se comenzará la ejecución del @@ -291,7 +293,7 @@ Esta es la fase final donde se traduce de CIL al lenguaje MIPS que da la salida Dentro del fichero mips_basics.asm se encuentran algunas funciones predefinidas en mips: malloc, copy, read_string, equal_string, length, substring y concat. El fichero MIPSAst contiene la definición de las clases necesarias para representar el código MIPS. -El fichero CILToMipsVisitor visita cada nodo del AST de CIL y lo traduce s su correspondientes +El fichero CilToMipsVisitor visita cada nodo del AST de CIL y lo traduce s su correspondientes instrucciones en codigo Mips. Gran dificultad trajo en esta fase el uso correcto de las tablas de dispatch y los atributos de clase en combinación con la herencia, haciendo necesaria una especificación sobre la representación en memoria que tendría cada objeto. Sobre esto útimo podemos explicar que se decidió representar @@ -313,7 +315,7 @@ entero de 32 bits. El orden de las funciones en la tabla de dispatch inicia por correctamente el llamado a una función redefinida en un objeto del tipo hijo cuando este es tratado como un objeto del tipo padre (polimorfismo). -Finalmente, el fichero MIPSAstFormatter es el encargado de transformar el AST de MIPS a formato string para +Finalmente, el fichero MipsAstFormatter es el encargado de transformar el AST de MIPS a formato string para luego escribir este en el archivo final. ## Licencia diff --git a/src/Main.py b/src/Main.py index b634491a3..d35d66565 100644 --- a/src/Main.py +++ b/src/Main.py @@ -5,10 +5,8 @@ from core.semantic.TypeBuilder import Type_Builder from core.semantic.TypeChecker import Type_Checker from core.cil.CoolToCilVisitor import COOLToCILVisitor -from core.cil.CilAst import get_formatter from core.mips.CilToMipsVisitor import CILToMIPSVisitor from core.mips.MipsAstFormatter import MIPSAstFormatter -import subprocess, re def main(args): @@ -53,15 +51,12 @@ def main(args): CILVisitor = COOLToCILVisitor(type_Checker.Context) CILast = CILVisitor.visit(COOLast, scope) - # print(get_formatter()(CILast)) MIPSVisitor = CILToMIPSVisitor() MIPSAst = MIPSVisitor.visit(CILast) MIPSFormatter = MIPSAstFormatter() mipsCode = MIPSFormatter.visit(MIPSAst) - # print(mipsCode) - out_file = args.file.split(".") out_file[-1] = "mips" out_file = ".".join(out_file) diff --git a/src/core/cil/CilAst.py b/src/core/cil/CilAst.py index e09df143a..ae3b85863 100644 --- a/src/core/cil/CilAst.py +++ b/src/core/cil/CilAst.py @@ -297,210 +297,4 @@ def __init__(self, name, line, column): super().__init__(name, line, column) def __str__(self): - return f'PARAM {self.name}' - -def get_formatter(): - - class PrintVisitor(object): - @visitor.on('node') - def visit(self, node): - pass - - @visitor.when(ProgramNode) - def visit(self, node): - dottypes = '\n'.join(self.visit(t) for t in node.dottypes) - dotdata = '\n'.join(self.visit(t) for t in node.dotdata) - dotcode = '\n'.join(self.visit(t) for t in node.dotcode) - - return f'.TYPES\n{dottypes}\n\n.DATA\n{dotdata}\n\n.CODE\n{dotcode}' - - @visitor.when(TypeNode) - def visit(self, node): - attributes = '\n\t'.join(f'attribute {x}' for x in node.attributes) - methods = '\n\t'.join(f'method {x}' for x in node.methods) - - return f'type {node.name} {{\n\t{attributes}\n\t{methods}\n}}' - - @visitor.when(DataNode) - def visit(self, node): - return f'DATA "{node.value}"' - - @visitor.when(FunctionNode) - def visit(self, node): - params = '\n\t'.join(self.visit(x) for x in node.params) - localvars = '\n\t'.join(self.visit(x) for x in node.localvars) - instructions = '\n\t'.join(self.visit(x) for x in node.instructions) - - return f'function {node.name} {{\n\t{params}\n\t{localvars}\n\n\t{instructions}\n}}' - - @visitor.when(ParamNode) - def visit(self, node): - return f'PARAM {node.name}' - - @visitor.when(LocalNode) - def visit(self, node): - return f'LOCAL {node.name}' - - @visitor.when(AssignNode) - def visit(self, node): - return f'{node.dest} = {node.source}' - - @visitor.when(PlusNode) - def visit(self, node): - return f'{node.dest} = {node.left} + {node.right}' - - @visitor.when(MinusNode) - def visit(self, node): - return f'{node.dest} = {node.left} - {node.right}' - - @visitor.when(StarNode) - def visit(self, node): - return f'{node.dest} = {node.left} * {node.right}' - - @visitor.when(DivNode) - def visit(self, node): - return f'{node.dest} = {node.left} / {node.right}' - - @visitor.when(AllocateNode) - def visit(self, node): - return f'{node.dest} = ALLOCATE {node.type}' - - @visitor.when(TypeOfNode) - def visit(self, node): - return f'{node.dest} = TYPEOF {node.obj}' - - @visitor.when(StaticCallNode) - def visit(self, node): - return f'{node.dest} = CALL {node.function}' - - @visitor.when(DynamicCallNode) - def visit(self, node): - return f'{node.dest} = VCALL {node.type} {node.method}' - - @visitor.when(ArgNode) - def visit(self, node): - return f'ARG {node.name}' - - @visitor.when(ReturnNode) - def visit(self, node): - return f'RETURN {node.value if node.value is not None else ""}' - - @visitor.when(LoadNode) - def visit(self, node): - return f'{node.dest} = LOAD {self.visit(node.msg)}' - - @visitor.when(PrintStringNode) - def visit(self, node: PrintStringNode): - return f'PRINTSTRING {node.str_addr}' - - @visitor.when(PrintIntNode) - def visit(self, node: PrintIntNode): - return f'PRINTINT {node.value}' - - @visitor.when(ExitNode) - def visit(self, node: ExitNode): - return f'EXIT' - - @visitor.when(CopyNode) - def visit(self, node): - return f'{node.dest} = COPY {node.value}' - - @visitor.when(GetAttribNode) - def visit(self, node: GetAttribNode): - return f'{node.dest} = GETATTRIB {node.obj}.{node.attr} {node.computed_type}' - - @visitor.when(ErrorNode) - def visit(self, node: ErrorNode): - return f'ERROR {self.visit(node.data)}' - - @visitor.when(ReadStringNode) - def visit(self, node: ReadStringNode): - return f'{node.dest} = READ' - - @visitor.when(ReadIntNode) - def visit(self, node: ReadIntNode): - return f'{node.dest} = READ' - - @visitor.when(SetAttribNode) - def visit(self, node: SetAttribNode): - return f'SETATTR {node.obj}.{node.attr}: {node.computed_type} = {node.value}' - - @visitor.when(LessNode) - def visit(self, node: LessNode): - return f'{node.dest} = {node.left} < {node.right}' - - @visitor.when(GotoIfNode) - def visit(self, node: GotoIfNode): - return f'GOTOIF {node.condition} {node.label}' - - @visitor.when(GotoNode) - def visit(self, node: GotoNode): - return f'GOTO {node.label}' - - @visitor.when(LabelNode) - def visit(self, node: LabelNode): - return f'LABEL {node.label}' - - @visitor.when(SubstringNode) - def visit(self, node: SubstringNode): - return f'{node.dest} = SUBSTRING {node.str_value}[{node.index}:{node.index} up to {node.length}]' - - @visitor.when(ConcatNode) - def visit(self, node: ConcatNode): - return f'{node.dest} = CONCAT {node.prefix} + {node.suffix}' - - @visitor.when(LengthNode) - def visit(self, node: LengthNode): - return f'{node.dest} = LENGTH {node.source}' - - @visitor.when(EqualNode) - def visit(self, node: EqualNode): - return f'{node.dest} = {node.left} == {node.right}' - - @visitor.when(NameNode) - def visit(self, node: NameNode): - return f'{node.dest} = NAME {node.value}' - - @visitor.when(EqualStringNode) - def visit(self, node: EqualStringNode): - return f'{node.dest} = {node.left} == {node.right}' - - @visitor.when(ComplementNode) - def visit(self, node: ComplementNode): - return f'{node.dest} = ~{node.value}' - - @visitor.when(LessEqualNode) - def visit(self, node: LessEqualNode): - return f'{node.dest} = {node.left} <= {node.right}' - - @visitor.when(PrefixNode) - def visit(self, node: PrefixNode): - return f'PREFFIXNODE' - - @visitor.when(ToStrNode) - def visit(self, node: ToStrNode): - return f'{node.dest} = str({node.value})' - - @visitor.when(VoidNode) - def visit(self, node: VoidNode): - return 'VOID' - - @visitor.when(NotNode) - def visit(self, node: NotNode): - return f'{node.dest} = NOT {node.value}' - - @visitor.when(VarNode) - def visit(self, node: VarNode): - return f'{node.name}' - - @visitor.when(AttributeNode) - def visit(self, node: AttributeNode): - return f'ATTRIBUTE {node.type}.{node.name}' - - @visitor.when(ParamNode) - def visit(self, node: ParamNode): - return f'{node.name}' - - - printer = PrintVisitor() - return (lambda ast: printer.visit(ast)) \ No newline at end of file + return f'PARAM {self.name}' \ No newline at end of file diff --git a/src/core/cil/CilAstFormatter.py b/src/core/cil/CilAstFormatter.py new file mode 100644 index 000000000..96bfbe31b --- /dev/null +++ b/src/core/cil/CilAstFormatter.py @@ -0,0 +1,206 @@ +from ..tools import visitor +from .CilAst import * + + +class PrintVisitor(object): + @visitor.on('node') + def visit(self, node): + pass + + @visitor.when(ProgramNode) + def visit(self, node): + dottypes = '\n'.join(self.visit(t) for t in node.dottypes) + dotdata = '\n'.join(self.visit(t) for t in node.dotdata) + dotcode = '\n'.join(self.visit(t) for t in node.dotcode) + + return f'.TYPES\n{dottypes}\n\n.DATA\n{dotdata}\n\n.CODE\n{dotcode}' + + @visitor.when(TypeNode) + def visit(self, node): + attributes = '\n\t'.join(f'attribute {x}' for x in node.attributes) + methods = '\n\t'.join(f'method {x}' for x in node.methods) + + return f'type {node.name} {{\n\t{attributes}\n\t{methods}\n}}' + + @visitor.when(DataNode) + def visit(self, node): + return f'DATA "{node.value}"' + + @visitor.when(FunctionNode) + def visit(self, node): + params = '\n\t'.join(self.visit(x) for x in node.params) + localvars = '\n\t'.join(self.visit(x) for x in node.localvars) + instructions = '\n\t'.join(self.visit(x) for x in node.instructions) + + return f'function {node.name} {{\n\t{params}\n\t{localvars}\n\n\t{instructions}\n}}' + + @visitor.when(ParamNode) + def visit(self, node): + return f'PARAM {node.name}' + + @visitor.when(LocalNode) + def visit(self, node): + return f'LOCAL {node.name}' + + @visitor.when(AssignNode) + def visit(self, node): + return f'{node.dest} = {node.source}' + + @visitor.when(PlusNode) + def visit(self, node): + return f'{node.dest} = {node.left} + {node.right}' + + @visitor.when(MinusNode) + def visit(self, node): + return f'{node.dest} = {node.left} - {node.right}' + + @visitor.when(StarNode) + def visit(self, node): + return f'{node.dest} = {node.left} * {node.right}' + + @visitor.when(DivNode) + def visit(self, node): + return f'{node.dest} = {node.left} / {node.right}' + + @visitor.when(AllocateNode) + def visit(self, node): + return f'{node.dest} = ALLOCATE {node.type}' + + @visitor.when(TypeOfNode) + def visit(self, node): + return f'{node.dest} = TYPEOF {node.obj}' + + @visitor.when(StaticCallNode) + def visit(self, node): + return f'{node.dest} = CALL {node.function}' + + @visitor.when(DynamicCallNode) + def visit(self, node): + return f'{node.dest} = VCALL {node.type} {node.method}' + + @visitor.when(ArgNode) + def visit(self, node): + return f'ARG {node.name}' + + @visitor.when(ReturnNode) + def visit(self, node): + return f'RETURN {node.value if node.value is not None else ""}' + + @visitor.when(LoadNode) + def visit(self, node): + return f'{node.dest} = LOAD {self.visit(node.msg)}' + + @visitor.when(PrintStringNode) + def visit(self, node: PrintStringNode): + return f'PRINTSTRING {node.str_addr}' + + @visitor.when(PrintIntNode) + def visit(self, node: PrintIntNode): + return f'PRINTINT {node.value}' + + @visitor.when(ExitNode) + def visit(self, node: ExitNode): + return f'EXIT' + + @visitor.when(CopyNode) + def visit(self, node): + return f'{node.dest} = COPY {node.value}' + + @visitor.when(GetAttribNode) + def visit(self, node: GetAttribNode): + return f'{node.dest} = GETATTRIB {node.obj}.{node.attr} {node.computed_type}' + + @visitor.when(ErrorNode) + def visit(self, node: ErrorNode): + return f'ERROR {self.visit(node.data)}' + + @visitor.when(ReadStringNode) + def visit(self, node: ReadStringNode): + return f'{node.dest} = READ' + + @visitor.when(ReadIntNode) + def visit(self, node: ReadIntNode): + return f'{node.dest} = READ' + + @visitor.when(SetAttribNode) + def visit(self, node: SetAttribNode): + return f'SETATTR {node.obj}.{node.attr}: {node.computed_type} = {node.value}' + + @visitor.when(LessNode) + def visit(self, node: LessNode): + return f'{node.dest} = {node.left} < {node.right}' + + @visitor.when(GotoIfNode) + def visit(self, node: GotoIfNode): + return f'GOTOIF {node.condition} {node.label}' + + @visitor.when(GotoNode) + def visit(self, node: GotoNode): + return f'GOTO {node.label}' + + @visitor.when(LabelNode) + def visit(self, node: LabelNode): + return f'LABEL {node.label}' + + @visitor.when(SubstringNode) + def visit(self, node: SubstringNode): + return f'{node.dest} = SUBSTRING {node.str_value}[{node.index}:{node.index} up to {node.length}]' + + @visitor.when(ConcatNode) + def visit(self, node: ConcatNode): + return f'{node.dest} = CONCAT {node.prefix} + {node.suffix}' + + @visitor.when(LengthNode) + def visit(self, node: LengthNode): + return f'{node.dest} = LENGTH {node.source}' + + @visitor.when(EqualNode) + def visit(self, node: EqualNode): + return f'{node.dest} = {node.left} == {node.right}' + + @visitor.when(NameNode) + def visit(self, node: NameNode): + return f'{node.dest} = NAME {node.value}' + + @visitor.when(EqualStringNode) + def visit(self, node: EqualStringNode): + return f'{node.dest} = {node.left} == {node.right}' + + @visitor.when(ComplementNode) + def visit(self, node: ComplementNode): + return f'{node.dest} = ~{node.value}' + + @visitor.when(LessEqualNode) + def visit(self, node: LessEqualNode): + return f'{node.dest} = {node.left} <= {node.right}' + + @visitor.when(PrefixNode) + def visit(self, node: PrefixNode): + return f'PREFFIXNODE' + + @visitor.when(ToStrNode) + def visit(self, node: ToStrNode): + return f'{node.dest} = str({node.value})' + + @visitor.when(VoidNode) + def visit(self, node: VoidNode): + return 'VOID' + + @visitor.when(NotNode) + def visit(self, node: NotNode): + return f'{node.dest} = NOT {node.value}' + + @visitor.when(VarNode) + def visit(self, node: VarNode): + return f'{node.name}' + + @visitor.when(AttributeNode) + def visit(self, node: AttributeNode): + return f'ATTRIBUTE {node.type}.{node.name}' + + @visitor.when(ParamNode) + def visit(self, node: ParamNode): + return f'{node.name}' + + +printer = PrintVisitor() From 3e2f2d8a7e10fda3cedb4bd99e598e20196cebfc Mon Sep 17 00:00:00 2001 From: ClaudiaOM Date: Thu, 24 Feb 2022 21:24:06 -0600 Subject: [PATCH 72/75] Update Readme --- doc/Readme.md | 212 ++++++++++++++++++++++++++------------------------ 1 file changed, 109 insertions(+), 103 deletions(-) diff --git a/doc/Readme.md b/doc/Readme.md index bff552c91..34885937e 100644 --- a/doc/Readme.md +++ b/doc/Readme.md @@ -85,7 +85,7 @@ cool-compiler-2021 ``` -Se omitieron algunos archivos pues no son relevantes en la implementacion del compilador. +Se omitieron algunos archivos pues no son relevantes en la implementación del compilador. ### Fases (_Pipeline_) @@ -102,27 +102,122 @@ El fichero _Main.py_ contiene el pipeline de ejecución del compilador #### Lexer -El Lexer es el encargado de dado un string con el código del programa COOL separar el mismo en tokens -para luego ser usado por el parser. En esta fase se utilizó el paquete _ply_ el cual contiene -herramientas de tokenización. Se definieron las expresiones regulares y símbolos que correspondian a los -tokens de la gramática, definiendo reglas especiales para poder también reconocer los string y comentarios -anidados. Además se almacena por cada Token la línea y la columna correspondiente -en el código, lo que permite tener mayor información en los mensajes de error -y para nuestro uso en la depuración. +El Lexer es el encargado de dado un string con el código del programa COOL separar el +mismo en tokenspara luego ser usado por el parser. En esta fase se utilizó el paquete +_ply_ el cual contieneherramientas de tokenización. Se definieron las expresiones +regulares y símbolos que correspondían a lostokens de la gramática, definiendo reglas +especiales para poder también reconocer los string y comentariosanidados. Además, +se almacena por cada Token la línea y la columna correspondienteen el código, +lo que permite tener mayor información en los mensajes de errory para nuestro uso +en la depuración. + + #### Parser El Parser define la estructura que tendrá el Árbol de Sintaxis Abstracta (AST) del lenguaje COOL, además -de la gramática que se usará para parsear este. -El archivo donde se definen los símbolos y producciones de la gramática puede verse en -[Gramatica COOL](https://github.com/NinjaProgrammers/cool-compiler-2021/blob/Proyecto-CMP/src/core/parser/Parser.py) +de la gramática que se usará para parsear este. +El archivo donde se definen los símbolos y producciones de la gramática puede verse al final +de este fichero en la sección Gramática. + Se utilizó un parser LR1 que había sido implementado y probado en proyectos anteriores -de la asignatura. Fue necesaria la inclusión de nuevas reglas y la edición de algunas anteriores para la -solución de problemas con la precedencia de los operadores y la ambiguedad de la gramática. +de la asignatura. Fue necesaria la inclusión de nuevas reglas y la edición de algunas anteriores para la +solución de problemas con la precedencia de los operadores y la ambigüedad de la gramática. Durante esta fase se reconocen y reportan los errores léxicos del programa. -## Gramática + +#### Recolección de tipos + +En esta fase se recorren todas las declaraciones de clases, se crean los tipos asociados +y se valida que no se estén redefiniendo estos. Primeramente, se añaden los tipos builtin +(Object, IO, Bool, String, Int) y luego los tipos definidos por el usuario, revisando +que no existan nombres de clases repetidos. + +#### Construcción de Tipos + +En esta fase se recorren nuevamente las declaraciones de clases añadiendo los métodos y +atributos de cada clase. Se define la herencia para cada clase, en caso de que no exista se hereda de la +clase Object. Además, se revisa que exista una clase Main con un método main que indica el inicio +de la ejecución del programa COOL. + +#### Chequeo de tipos + +En esta fase se hace el chequeo semántico de cada tipo. Se evalúa para cada expresión su tipo de retorno +y se valida que estos cumplan las reglas semánticas definidas en el lenguaje. Además, se chequea nuevamente +las clases definidas por el usuario en busca de la existencia de herencia cíclica. +Algunos de los errores que se chequean en esta fase son: +- Las operaciones aritméticas solo están definidas para el tipo Int. +- Que las variables y los métodos hayan sido definidos previamente a su uso. +- Que no exista redefinición de métodos o atributos en las clases. +- Que no exista herencia cíclica. +- Que las palabras claves self y SELF_TYPE sean usadas correctamente. +- Que las funciones de clases sean llamadas con la cantidad correcta de parámetros. +- Que el objeto empleado en cada expresión sea compatible con el tipo declarado para la misma. + +#### COOL a CIL + +Durante esta fase se realiza la conversión del lenguaje COOL a un lenguaje intermedio(CIL). +El fichero CilAst contiene la definición de las clases usadas para conformar el AST del lenguaje CIL. +En el fichero BaseCooltoCilVisitor se definen los métodos básicos para registrar +una variable, parámetro, función y atributo, entre otros. Además se +registran los tipos builtin, es decir, se escriben en código CIL las instrucciones +para registrar los tipos Object, IO, String, Int y Bool, así como las funciones y atributos de cada uno de estos.. +El fichero CoolToCilVisitor es el encargado de transformar el AST de COOL en un AST de CIL, para facilitar +luego la traducción de este al lenguaje MIPS. Este fichero cuenta con un visitor que se encarga de transformar +cada nodo del AST de un lenguaje a otro, algunos de los aspectos a destacar son: +- En el visitor del ProgramNode se define la función entry, que es por donde se comenzará la ejecución del +programa MIPS. +- En el visitor del ClassDeclarationNode se definen las funciones init e init_attr corespondientes a cada +clase, estas funciones son las encargadas de reservar la memoria necesaria para cada uno de los tipos +definidos por el usuario. +- Especial énfasis en el visitor del CaseOfNode, este se encarga de generar todas las instrucciones +necesarias para validar correctamente la rama que debe ejecutarse, o mostrar un error en tiempo de ejecución +en caso de no haber ninguna válida. Para lograr esto primero se ordenan los tipos involucrados en las ramas del +case según su profundidad en el árbol de herencia del programa, de mayor a menor. Luego se visitan estos, +revisando para cada uno todos sus descendientes en dicho árbol de herencia y comprobando, en tiempo de +ejecución, si estos coinciden con el tipo del objeto que se está analizando. El primer objeto para el que +se halle una correspondencia define la rama por la que debe continuar la ejecución del programa. +- En el visitor del DivNode se añaden las instrucciones necesarias para verificar que el divisor es distinto +de cero, y lanzar un error en tiempo de ejecución en caso contrario. +- El visitor del EqualNode se encarga de revisar primeramente los tipos de los objetos que están siendo +comparados, Int-Int, String-String y Bool-Bool son comparados por valor, mientras que cualquier otro par +es comparado por referencia. +- El visitor del FunctionCallNode se encarga de comprobar que el objeto al cual se le hace el dispatch sea +distinto de Void y mostrar un error en ejecución en caso contrario. + +#### CIL a MIPS +Esta es la fase final donde se traduce de CIL al lenguaje MIPS que da la salida del programa. +Dentro del fichero mips_basics.asm se encuentran algunas funciones predefinidas en mips: malloc, copy, +read_string, equal_string, length, substring y concat. +El fichero MIPSAst contiene la definición de las clases necesarias para representar el código MIPS. +El fichero CilToMipsVisitor visita cada nodo del AST de CIL y lo traduce a sus correspondientes +instrucciones en codigo Mips. Gran dificultad trajo en esta fase el uso correcto de las tablas de dispatch +y los atributos de clase en combinación con la herencia, haciendo necesaria una especificación sobre la +representación en memoria que tendría cada objeto. Sobre esto útimo podemos explicar que se decidió representar +los objetos como: +- Marca de clase (4 bytes): Un entero usado para identificar cada tipo del programa. +- Tamaño (4 bytes): Un entero empleado para representar el tamaño, en doble palabras, de la representación del objeto +en memoria. +- Puntero a la tabla de dispatch (4 bytes): Un entero que representa la posición en memoria donde se encuentra +la tabla de dispatch del objeto. +- Definición de atributos de la clase padre. +- Definición de atributos de la clase hijo. Primero son definidos los atributos de la clase padre de forma recursiva, +luego son definidos los atributos de la clase hijo, colocando estos de forma ordenada según los nombres que tenían +en el código COOL inicial. +Las tablas de dispatch de cada tipo se definen de manera similar, primero las direcciones de memoria correspondientes +a las funciones de las clases padres, o a las de la clase hijo en caso de que hayan sido redefinidas, y luego las +direcciones de memoria de las funciones de la clase hijo, ordenadas alfabéticamente según sus nombres iniciales. +Finalmente las funciones init e init_attr de la clase correspondiente. Cada dirección de memoria corresponde a un +entero de 32 bits. El orden de las funciones en la tabla de dispatch inicia por las del padre para poder ejecutar +correctamente el llamado a una función redefinida en un objeto del tipo hijo cuando este es tratado como un +objeto del tipo padre (polimorfismo). + +Finalmente, el fichero MipsAstFormatter es el encargado de transformar el AST de MIPS a formato string para +luego escribir este en el archivo final. + + +# Gramática ### Terminales A continuación se muestran los terminales de la gramática, donde entre paréntesis se muestran @@ -228,95 +323,6 @@ __class_list__ ⟶ __def_class__ **arg_list** ⟶ **expr_list** **arg_list** ⟶ **expr_list** , **arg_list** -#### Recolección de tipos - -En esta fase se recorren todas las declaraciones de clases, se crean los tipos asociados -y se valida que no se estén redefiniendo estos. Primeramente se añaden los tipos builtin -(Object, IO, Bool, String, Int) y luego los tipos definidos por el usuario, revisando -que no existan nombres de clases repetidos. - -#### Construcción de Tipos - -En esta fase se recorren nuevamente las declaraciones de clases añadiendo los métodos y -atributos de cada clase. Se define la herencia para cada clase, en caso que no exista se hereda de la -clase Object. Además se revisa que exista una clase Main con un método main que indica el inicio -de la ejecución del programa COOL. - -#### Chequeo de tipos - -En esta fase se hace el chequeo semántico de cada tipo. Se evalúa para cada expresión su tipo de retorno -y se valida que estos cumplan las reglas semánticas definidas en el lenguaje. Además se chequea nuevamente -las clases definidas por el usuario en busca de la existencia de herencia cíclica. -Algunos de los errores que se chequean en esta fase son: -- Las operaciones aritméticas solo están definidas para el tipo Int. -- Que las variables y los métodos hayan sido definidos previamente a su uso. -- Que no exista redefinición de métodos o atributos en las clases. -- Que no exista herencia cíclica. -- Que las palabras claves self y SELF_TYPE sean usadas correctamente. -- Que las funciones de clases sean llamadas con la cantidad correcta de parámetros. -- Que el objeto usado en cada expresión sea compatible con el tipo declarado para la misma. - -#### COOL a CIL - -Durante esta fase se realiza la conversión del lenguaje COOL a un lenguaje intermedio(CIL). -El fichero CilAst contiene la definición de las clases usadas para conformar el AST del lenguaje CIL. -En el fichero BaseCooltoCilVisitor se definen los métodos básicos para registrar -una variable, parámetro, función y atributo, entre otros. Ademas se -registran los tipos builtin, es decir, se escriben en código CIL las instrucciones -para registrar los tipos Object, IO, String, Int y Bool, así como las funciones y atributos de cada uno de estos.. -El fichero CoolToCilVisitor es el encargado de transformar el AST de COOL en un AST de CIL, para facilitar -luego la traducción de este al lenguaje MIPS. Este fichero cuenta con un visitor que se encarga de transformar -cada nodo del AST de un lenguaje a otro, algunos de los aspectos a destacar son: -- En el visitor del ProgramNode se define la función entry, que es por donde se comenzará la ejecución del -programa MIPS. -- En el visitor del ClassDeclarationNode se definen las funciones init e init_attr corespondientes a cada -clase, estas funciones son las encargadas de reservar la memoria necesaria para cada uno de los tipos -definidos por el usuario. -- Especial énfasis en el visitor del CaseOfNode, este se encarga de generar todas las instrucciones -necesarias para validar correctamente la rama que debe ejecutarse, o mostrar un error en tiempo de ejecución -en caso de no haber ninguna válida. Para lograr esto primero se ordenan los tipo involucrados en las ramas del -case según su profundidad en el árbol de herencia del programa, de mayor a menor. Luego se visitan estos, -revisando para cada uno todos sus descendientes en dicho árbol de herencia y comprobando, en tiempo de -ejecución, si estos coinciden con el tipo del objeto que se está analizando. El primer objeto para el que -se halle una correspondencia define la rama por la que debe continuar la ejecución del programa. -- En el visitor del DivNode se añaden las instrucciones necesarias para verificar que el divisor es distinto -de cero, y lanzar un error en tiempo de ejecución en caso contrario. -- El visitor del EqualNode se encarga de verificar primeramente los tipos de los objetos que están siendo -comparados, Int-Int, String-String y Bool-Bool son comparados por valor, mientras que cualquier otro par -es comparado por referencia. -- El visitor del FunctionCallNode se encarga de verificar que el objeto al cual se le hace el dispatch sea -distinto de Void y mostrar un error en ejecución en caso contrario. - -#### CIL a MIPS - -Esta es la fase final donde se traduce de CIL al lenguaje MIPS que da la salida del programa. -Dentro del fichero mips_basics.asm se encuentran algunas funciones predefinidas en mips: malloc, copy, -read_string, equal_string, length, substring y concat. -El fichero MIPSAst contiene la definición de las clases necesarias para representar el código MIPS. -El fichero CilToMipsVisitor visita cada nodo del AST de CIL y lo traduce s su correspondientes -instrucciones en codigo Mips. Gran dificultad trajo en esta fase el uso correcto de las tablas de dispatch -y los atributos de clase en combinación con la herencia, haciendo necesaria una especificación sobre la -representación en memoria que tendría cada objeto. Sobre esto útimo podemos explicar que se decidió representar -los objetos como: -- Marca de clase (4 bytes): Un entero usado para identificar cada tipo del programa. -- Tamaño (4 bytes): Un entero usado para representar el tamaño, en doble palabras, de la representación del objeto -en memoria. -- Puntero a la tabla de dispatch (4 bytes): Un entero que representa la posición en memoria donde se encuentra -la tabla de dispatch del objeto. -- Definición de atributos de la clase padre. -- Definición de atributos de la clase hijo. Primero son definidos los atributos de la clase padre de forma recursiva, -luego son definidos los atributos de la clase hijo, colocando estos de forma ordenada según los nombres que tenían -en el código COOL inicial. -Las tablas de dispatch de cada tipo se definen de forma similar, primero las direcciones de memoria correspondientes -a las funciones de las clases padres, o a las de la clase hijo en caso de que hayan sido redefinidas, y luego las -direcciones de memoria de las funciones de la clase hijo, ordenadas alfabéticamente según sus nombres iniciales. -Finalmente las funciones init e init_attr de la clase correspondiente. Cada dirección de memoria corresponde a un -entero de 32 bits. El orden de las funciones en la tabla de dispatch inicia por las del padre para poder ejecutar -correctamente el llamado a una función redefinida en un objeto del tipo hijo cuando este es tratado como un -objeto del tipo padre (polimorfismo). - -Finalmente, el fichero MipsAstFormatter es el encargado de transformar el AST de MIPS a formato string para -luego escribir este en el archivo final. ## Licencia From a8499c4f2c2af3b0da538cef00c6ebacb682fece Mon Sep 17 00:00:00 2001 From: ClaudiaOM Date: Thu, 24 Feb 2022 21:44:59 -0600 Subject: [PATCH 73/75] Final Readme update. --- doc/Readme.md | 33 +++++++++++++++++---------------- 1 file changed, 17 insertions(+), 16 deletions(-) diff --git a/doc/Readme.md b/doc/Readme.md index 34885937e..1c1257227 100644 --- a/doc/Readme.md +++ b/doc/Readme.md @@ -39,7 +39,7 @@ El compilador está compuesto por distintos módulos que son los encargados de t el código escrito inicialmente en COOL y obtener el resultado final en código MIPS que permita su ejecución. -## Organizacion +## Organización La estructura de archivos del proyecto es la siguiente: @@ -92,23 +92,23 @@ Se omitieron algunos archivos pues no son relevantes en la implementación del c El fichero _Main.py_ contiene el pipeline de ejecución del compilador 1. Lexer 2. Parser -3. Recolección de Tipos -4. Construcción de Tipos -5. Chequeo de Tipos -6. COOL a CIL -7. CIL a Mips -8. MIPS a representación como string -9. Escritura en el archivo final +3. Recolección de Tipos +4. Construcción de Tipos +5. Chequeo de Tipos +6. COOL a CIL +7. CIL a Mips +8. MIPS a representación como string +9. Escritura en el archivo final #### Lexer El Lexer es el encargado de dado un string con el código del programa COOL separar el -mismo en tokenspara luego ser usado por el parser. En esta fase se utilizó el paquete -_ply_ el cual contieneherramientas de tokenización. Se definieron las expresiones -regulares y símbolos que correspondían a lostokens de la gramática, definiendo reglas -especiales para poder también reconocer los string y comentariosanidados. Además, -se almacena por cada Token la línea y la columna correspondienteen el código, -lo que permite tener mayor información en los mensajes de errory para nuestro uso +mismo en tokens para luego ser usado por el parser. En esta fase se utilizó el paquete +_ply_ el cual contiene herramientas de tokenización. Se definieron las expresiones +regulares y símbolos que correspondían a los tokens de la gramática, definiendo reglas +especiales para poder también reconocer los string y comentarios anidados. Además, +se almacena por cada Token la línea y la columna correspondiente en el código, +lo que permite tener mayor información en los mensajes de error y para nuestro uso en la depuración. @@ -116,7 +116,7 @@ en la depuración. #### Parser El Parser define la estructura que tendrá el Árbol de Sintaxis Abstracta (AST) del lenguaje COOL, además -de la gramática que se usará para parsear este. +de la gramática que se usará para parsear. El archivo donde se definen los símbolos y producciones de la gramática puede verse al final de este fichero en la sección Gramática. @@ -202,7 +202,8 @@ en memoria. - Puntero a la tabla de dispatch (4 bytes): Un entero que representa la posición en memoria donde se encuentra la tabla de dispatch del objeto. - Definición de atributos de la clase padre. -- Definición de atributos de la clase hijo. Primero son definidos los atributos de la clase padre de forma recursiva, +- Definición de atributos de la clase hijo. +Primero son definidos los atributos de la clase padre de forma recursiva, luego son definidos los atributos de la clase hijo, colocando estos de forma ordenada según los nombres que tenían en el código COOL inicial. Las tablas de dispatch de cada tipo se definen de manera similar, primero las direcciones de memoria correspondientes From bf51c4157848dcbb8a11b73ce667df642469f2a1 Mon Sep 17 00:00:00 2001 From: mavaldivie Date: Mon, 7 Mar 2022 23:07:17 -0500 Subject: [PATCH 74/75] Added virtual calls and boxing-unboxing sections to README file --- doc/Readme.md | 102 +++++++++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 101 insertions(+), 1 deletion(-) diff --git a/doc/Readme.md b/doc/Readme.md index 1c1257227..9a74a5c6b 100644 --- a/doc/Readme.md +++ b/doc/Readme.md @@ -194,7 +194,7 @@ El fichero MIPSAst contiene la definición de las clases necesarias para represe El fichero CilToMipsVisitor visita cada nodo del AST de CIL y lo traduce a sus correspondientes instrucciones en codigo Mips. Gran dificultad trajo en esta fase el uso correcto de las tablas de dispatch y los atributos de clase en combinación con la herencia, haciendo necesaria una especificación sobre la -representación en memoria que tendría cada objeto. Sobre esto útimo podemos explicar que se decidió representar +**representación en memoria** que tendría cada objeto. Sobre esto útimo podemos explicar que se decidió representar los objetos como: - Marca de clase (4 bytes): Un entero usado para identificar cada tipo del programa. - Tamaño (4 bytes): Un entero empleado para representar el tamaño, en doble palabras, de la representación del objeto @@ -217,6 +217,106 @@ objeto del tipo padre (polimorfismo). Finalmente, el fichero MipsAstFormatter es el encargado de transformar el AST de MIPS a formato string para luego escribir este en el archivo final. +# Invocación de métodos virtuales +Para mostrar como se resolvió la invocación de métodos virtuales usaremos un ejemplo. Supongamos que tenemos una clase A +que posee las funciones foo1 y foo2, y una clase B que hereda de A que posee las funciones foo1 (Sobreescribiendo a foo1 +en A) y foo3. + +El código MIPS a continuación corresponde a la plantilla que se genera para las clases A y B respectivamente cuando +el programa es compilado. + +```MIPS +type_6_dispatch: + .word function_abort_at_Object + .word function_copy_at_Object + .word function_type_name_at_Object + .word function_foo1_at_A + .word function_foo2_at_A + .word __init_attr_at_A + .word __init_at_A + +type_6_prototype: + .word 5 + .word 4 + .word type_6_dispatch + .word -1 + + +type_7_dispatch: + .word function_abort_at_Object + .word function_copy_at_Object + .word function_type_name_at_Object + .word function_foo1_at_B + .word function_foo2_at_A + .word function_foo3_at_B + .word __init_attr_at_B + .word __init_at_B + +type_7_prototype: + .word 6 + .word 4 + .word type_7_dispatch + .word -1 +``` + +Al instanciar un objeto se copia la plantilla correspondiente a la representacion en memoria de dicho objeto, es decir, +se copia la sección prototype del objeto, y se instancian cada uno de sus atributos de forma recursiva. La sección +prototype posee la dirección de memoria correspondiente a la tabla de dispatch del objeto. Esta dirección es la que es +usada para hacer los llamados a los métodos de la clase. + +Cada vez que se intente llamar de un método de una clase lo primero que se hace es buscar la dirección de la tabla de +dispatch del objeto al cual se le está haciendo el llamado, y luego el índice del método que se está llamando, +para luego efectuar un jump and link (jal) a la dirección a la que apunta la tabla de dispatch sumando cuatro veces el +índice de dicho método. + +Ahora, observemos que la tabla de dispatch de la clase B posee todos los métodos de la clase A en +el mismo orden que esta, es decir, el offset correspondiente al método foo1 es el mismo para la clase A y para la clase +B. De esta forma si se utiliza un objeto de +tipo B como si fuera de tipo A, llamando a un método del mismo, digamos foo1, entonces el compilador solo tiene que +ocuparse de buscar el índice del método dentro de la clase A y la dirección de la tabla de dispatch del objeto que se +está usando (guardada en la sección de memoria correspondiente a dicho objeto), luego, como el índice del método en A +corresponde con el índice del método sobreescrito en B solo faltaría sumarle dicho offset a la tabla de dispatch para +obtener el método que debe usarse, en este caso ```function_foo1_at_B```. La clase ***DynamicCallNode*** de Cil es la +encargada de manejar la lógica de los llamados a métodos virtuales a objetos cuando se traduce de Cil a Mips. + +La clase ***StaticCallNode*** de Cil es usada explícitamente para los llamados a un método de un tipo en específico +usando la sintaxis ```object@TYPE.method()``` definida en COOL. Esta clase, en vez de buscar la tabla de dispatch +correspondiente al objeto que se está llamando, busca la tabla de dispatch del objeto que se especifica luego del +caracter @, de esta forma, una vez calculado el offset de dicho método, este corresponderá precisamente al método +correspondiente al tipo especificado. + +En conclusión, si se hace ```(new B).foo1()``` se llama al método foo1 en B haciendo uso de la tabla de dispatch del +objeto B que se creó, sin embargo, si se hace ```(new B)@A.foo1()``` se llama al método foo1 en A haciendo uso de la +tabla de dispatch del prototype del objeto A. + +El índice del método en la tabla de dispatch es calculado en tiempo de compilación usando el índice del método +en el array de métodos de cada tipo recolectado durante la traducción de Cil a Mips. Para que estos índices coincidan +se colocaron, para cada tipo, primeramente los métodos correspondientes al padre, y luego los métodos correspondientes +específicamente al hijo, ordenados estos últimos de forma lexicográfica. + +# Boxing/Unboxing + +Para el proceso de boxing-unboxing se utilizó un mecanismo similar al de las funciones virtuales. Cuando se necesite +acceder a un atributo de un objeto, se toma la dirección en memoria del objeto y el índice de dicho atributo dentro del +objeto, utilizando el índice del nombre del atributo en el +array de attributos recolectado durante la traducción de Cil a Mips. Estos índices son los que se usan siempre que es +necesario obtener o cambiar un atributo a un objeto, y se definen de forma unívoca durante el ya mencionado proceso de +recolección. + +Los atributos de cada objeto se encuentran colocados luego de la dirección de la tabla de dispatch del mismo (tres doble +palabras luego de la dirección de memoria del objeto). Además, el índice de un atributo para una clase hijo coincide con +la del mismo atributo para la clase padre, haciendo posible de esta forma el uso de los atributos durante el polimorfismo. + +Los objetos de tipo Int, Bool y String poseen un atributo llamado _value_ que es usado para guardar el valor del objeto, +en los dos primeros casos, y la dirección de memoria donde se encuentra la cadena de caracteres real, en el tercer caso. +Este atributo es el usado para el trabajo con dichos objetos. + +Para las operaciones aritméticas y de comparación fue necesario tomar el valor real de los objetos que se operan haciéndole +unboxing a los mismos, y luego operando dichos valores directamente. Haciéndole boxing a la respuesta para poder luego +guardarla. Por ejemplo, si se desea comparar dos enteros, estos serán dos objetos en memoria que poseen luego de la tabla +dispatch un entero correspondiente al valor real de los mismos, el proceso de unboxing consiste en tomar dichos valores +y compararlos, para luego instanciar un objeto de tipo Bool y colocarle luego de la tabla de dispatch (en el +atributo value) el resultado. # Gramática From 7568e6d1cc1acc354b8866c625d1b8025edec272 Mon Sep 17 00:00:00 2001 From: mavaldivie Date: Mon, 7 Mar 2022 23:16:27 -0500 Subject: [PATCH 75/75] Added explanation to virtual calls section --- doc/Readme.md | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/doc/Readme.md b/doc/Readme.md index 9a74a5c6b..a01d8c49a 100644 --- a/doc/Readme.md +++ b/doc/Readme.md @@ -264,7 +264,7 @@ se copia la sección prototype del objeto, y se instancian cada uno de sus atrib prototype posee la dirección de memoria correspondiente a la tabla de dispatch del objeto. Esta dirección es la que es usada para hacer los llamados a los métodos de la clase. -Cada vez que se intente llamar de un método de una clase lo primero que se hace es buscar la dirección de la tabla de +Cada vez que se intente llamar a un método de una clase lo primero que se hace es buscar la dirección de la tabla de dispatch del objeto al cual se le está haciendo el llamado, y luego el índice del método que se está llamando, para luego efectuar un jump and link (jal) a la dirección a la que apunta la tabla de dispatch sumando cuatro veces el índice de dicho método. @@ -287,7 +287,9 @@ correspondiente al tipo especificado. En conclusión, si se hace ```(new B).foo1()``` se llama al método foo1 en B haciendo uso de la tabla de dispatch del objeto B que se creó, sin embargo, si se hace ```(new B)@A.foo1()``` se llama al método foo1 en A haciendo uso de la -tabla de dispatch del prototype del objeto A. +tabla de dispatch del prototype del objeto A. Mientras que si se hace ```object.foo1()``` en una función que reciba +un parámetro de tipo A, pero a la que se le está pasando un objeto de tipo B, se llamará al método correspondiente en B, +utilizando la tabla de dispatch del objeto que se está pasando como parámetro. El índice del método en la tabla de dispatch es calculado en tiempo de compilación usando el índice del método en el array de métodos de cada tipo recolectado durante la traducción de Cil a Mips. Para que estos índices coincidan