diff --git a/doc/report.pdf b/doc/report.pdf new file mode 100644 index 000000000..6cc907b01 Binary files /dev/null and b/doc/report.pdf differ diff --git a/doc/team.yml b/doc/team.yml index c16162532..a018a49b1 100644 --- a/doc/team.yml +++ b/doc/team.yml @@ -1,10 +1,7 @@ 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: Abel Antonio Cruz Suarez + github: abelo98 + group: C411 + - name: Daniel Reynel Dominguez Ceballos + github: zondekallix + group: C411 diff --git a/requirements.txt b/requirements.txt index 9eb0cad1a..4d792d537 100644 --- a/requirements.txt +++ b/requirements.txt @@ -1,2 +1,3 @@ pytest pytest-ordering +ply~=3.11 diff --git a/src/__main__.py b/src/__main__.py new file mode 100644 index 000000000..916aff10e --- /dev/null +++ b/src/__main__.py @@ -0,0 +1,37 @@ +import sys +from cool.lexer.lexer import Lexer, main +from cool.utils.LexerTokens import * +from cool.Parser.parser import Parser +from cool.semantic.semanticAnalizer import run_semantic_pipeline +from cool.cil_builder.cilAnalizer import run_code_gen_pipeline + + +if __name__ == '__main__': + if len(sys.argv) > 1: + input_file = sys.argv[1] + with open(input_file, encoding="utf-8") as file: + text = file.read() + + lexer = main(text) + tokens = lexer.tokenize() + + if (len(lexer.errors)!= 0): + for e in lexer.errors: + print(e) + raise Exception() + + + parser = Parser(Lexer(text)) + ast = parser.parse(text) + + if parser.found_error: + print(parser.errors[0]) + raise Exception() + + context,scope = run_semantic_pipeline(ast) + mips_output = run_code_gen_pipeline(ast,context,scope) + + with open(sys.argv[2], 'w') as f: + f.write(f'{mips_output}') + + diff --git a/src/cool/Parser/AstNodes.py b/src/cool/Parser/AstNodes.py new file mode 100644 index 000000000..d0c760b09 --- /dev/null +++ b/src/cool/Parser/AstNodes.py @@ -0,0 +1,190 @@ +class Node: + pass + +class ProgramNode(Node): + def __init__(self, declarations): + self.declarations = declarations +class DeclarationNode(Node): + pass +class ExpressionNode(Node): + pass + +class ClassDeclarationNode(DeclarationNode): + def __init__(self, idx, features, parent, row , column): + self.id = idx + self.parent = parent + self.features = features + self.place_holder = None + self.row = row + self.column = column + + +class FuncDeclarationNode(DeclarationNode): + def __init__(self, idx, params, return_type, body, row, column): + self.id = idx + self.params = params + self.type = return_type + self.body = body + self.place_holder = None + self.row = row + self.column = column + +class IfNode(ExpressionNode): + def __init__(self,ifexp, thenexp,elseexp, row, column): + self.ifexp = ifexp + self.thenexp = thenexp + self.elseexp = elseexp + self.place_holder = None + self.row = row + self.column = column + +class WhileNode(ExpressionNode): + def __init__(self, condition, body, row, column): + self.condition = condition + self.body = body + self.place_holder = None + self.row = row + self.column = column + +class CaseNode(ExpressionNode): + def __init__(self,case,body, row, column): + self.case = case + self.body = body + self.place_holder = None + self.row = row + self.column = column + +class LetNode(ExpressionNode): + def __init__(self, params, body, row, column): + self.params = params + self.body = body + self.place_holder = None + self.row = row + self.column = column + +class ExpressionGroupNode(ExpressionNode): + def __init__(self, body, row, column): + self.body = body + self.place_holder = None + self.row = row + self.column = column + +class AttrDeclarationNode(DeclarationNode): + def __init__(self, idx, typex, expr, row = 0, column = 0): + self.id = idx + self.type = typex + self.expr = expr + self.place_holder = None + self.row = row + self.column = column + +class ParamDeclarationNode(DeclarationNode): + def __init__(self, idx, typex, row = 0, column = 0): + self.id = idx + self.type = typex + self.row = row + self.column = column + +class VarDeclarationNode(ExpressionNode): #Case expr + def __init__(self, idx, typex, expr, row = 0, column = 0): + self.id = idx + self.type = typex + self.expr = expr + self.place_holder = None + self.row = row + self.column = column + +class LetDeclarationNode(ExpressionNode): #Let expr + def __init__(self, idx, typex, expr, row = 0, column = 0): + self.id = idx + self.type = typex + self.expr = expr + self.place_holder = None + self.row = row + self.column = column + +class AssignNode(ExpressionNode): + def __init__(self, idx, expr, row, column): + self.id = idx + self.expr = expr + self.place_holder = None + self.row = row + self.column = column + +class CallNode(ExpressionNode): + def __init__(self, obj, idx, args, parent,call_type, row = 0, column = 0): + self.obj = obj + self.id = idx + self.args = args + self.parent = parent + self.place_holder = None + self.call_type = call_type + self.row = row + self.column = column + +class ExpressionGroupNode(ExpressionNode): + def __init__(self, body, row, column): + self.body = body + self.place_holder = None + self.row = row + self.column = column + +class AtomicNode(ExpressionNode): + def __init__(self, lex, row, column): + self.lex = lex + self.place_holder = None + self.row = row + self.column = column + +class BinaryNode(ExpressionNode): + def __init__(self, tok, left, right, row, column): + self.left = left + self.right = right + self.lex = tok.value + self.row = row + self.column = column + +class BinaryIntNode(BinaryNode): + pass +class BinaryBoolNode(BinaryNode): + pass + +class UnaryNode(ExpressionNode): + def __init__(self,right, row, column): + self.right = right + self.place_holder = None + self.row = row + self.column = column + +class StringNode(AtomicNode): + pass +class ConstantNumNode(AtomicNode): + pass +class VariableNode(AtomicNode): + pass +class InstantiateNode(AtomicNode): + pass +class BooleanNode(AtomicNode): + pass +class SelfNode(AtomicNode): + pass +class PlusNode(BinaryIntNode): + pass +class MinusNode(BinaryIntNode): + pass +class StarNode(BinaryIntNode): + pass +class DivNode(BinaryIntNode): + pass +class EqualNode(BinaryNode): + pass +class LessEqual(BinaryBoolNode): + pass +class LessNode(BinaryBoolNode): + pass +class IsVoidNode(UnaryNode): + pass +class NotNode(UnaryNode): + pass +class NegateNode(UnaryNode): + pass \ No newline at end of file diff --git a/src/cool/Parser/__init__.py b/src/cool/Parser/__init__.py new file mode 100644 index 000000000..e69de29bb diff --git a/src/cool/Parser/parser.py b/src/cool/Parser/parser.py new file mode 100644 index 000000000..32813ad64 --- /dev/null +++ b/src/cool/Parser/parser.py @@ -0,0 +1,298 @@ +from cool.lexer.lexer import Lexer +from cool.utils.LexerTokens.Tokens import tokens +import ply.yacc as yacc +from cool.Parser.AstNodes import * +from cool.utils.Errors.parser_errors import * + +class Parser(): + def __init__(self, lexer:Lexer = None) -> None: + self.lexer = Lexer() if not lexer else lexer + + self.tokens = tokens + self.errors = [] + self.parser = yacc.yacc(start='program', + module=self, + optimize=1, + debug=False, + write_tables=True, + debuglog=None, + errorlog=None, ) + + def parse(self, program, debug=False): + self.found_error = False + return self.parser.parse(program, self.lexer.lexer) + + def p_empty(self,p): + 'empty :' + pass + + def p_program(self,p): + 'program : class_list' + p[0]=ProgramNode(p[1]) + + def p_class_list(self,p): + '''class_list : def_class SEMI class_list + | def_class SEMI''' + p[0]= [p[1]]+p[3] if len(p)==4 else [p[1]] + + def p_def_class(self,p): + '''def_class : class TYPE OCUR feature_list CCUR + | class TYPE inherits TYPE OCUR feature_list CCUR''' + col = p.slice[2].column + line = p.lineno(1) + if len(p) == 6: + p[0] = ClassDeclarationNode(p[2], p[4],'Object', line, col) + else: + col = p.slice[4].column + p[0] = ClassDeclarationNode(p[2], p[6], p[4], line, col) + + + def p_feature_list(self,p): + '''feature_list : def_attr SEMI feature_list + | def_func SEMI feature_list + | empty''' + p[0] = [p[1]] + p[3] if len(p) == 4 else [] + + + def p_def_attr(self,p): + '''def_attr : ID COLON TYPE + | ID COLON TYPE LARROW expr''' + col = p.slice[3].column + line = p.lineno(1) + p[0] = AttrDeclarationNode(p[1], p[3], None, line, col) if len(p)==4\ + else AttrDeclarationNode(p[1],p[3],p[5], line, col) + + + def p_def_func(self,p): + '''def_func : ID OPAR param_list_call CPAR COLON TYPE OCUR expr CCUR''' + line = p.lineno(1) + col = p.slice[1].column + p[0] = FuncDeclarationNode(p[1], p[3], p[6], p[8], line, col) + + def p_param_list_call(self,p): + '''param_list_call : param_list + | param_list_empty''' + p[0] = p[1] + + def p_param_list(self,p): + '''param_list : param + | param COMMA param_list''' + if len(p) == 2: + p[0] = [p[1]] + else: + p[0] = [p[1]] + p[3] + + def p_param_list_empty(self,p): + '''param_list_empty : empty''' + p[0] = [] + + + def p_param(self,p): + '''param : ID COLON TYPE''' + col = p.slice[1].column + line = p.lineno(1) + p[0] = ParamDeclarationNode(p[1],p[3],line,col) + + def p_expr_list(self,p): + '''expr_list : expr SEMI expr_list + | expr SEMI''' + p[0] = [p[1]] + p[3] if len(p) == 4\ + else [p[1]] + + def p_declar_list(self,p): + '''declar_list : declar + | declar COMMA declar_list''' + p[0] = [p[1]] if len(p) == 2 else [p[1]]+p[3] + + def p_declar(self,p): + '''declar : ID COLON TYPE + | ID COLON TYPE LARROW expr''' + col = p.slice[3].column + line = p.lineno(1) + p[0] = LetDeclarationNode(p[1], p[3], None, line, col) if len(p) == 4\ + else LetDeclarationNode(p[1],p[3],p[5], line, col) + + def p_assign_list(self,p): + '''assign_list : case_assign + | case_assign assign_list''' + p[0] = [p[1]] if len(p) == 2 else [p[1]]+p[2] + + + def p_case_assign(self,p): + '''case_assign : ID COLON TYPE RARROW expr SEMI''' + col = p.slice[3].column + line = p.lineno(1) + p[0] = VarDeclarationNode(p[1],p[3],p[5], line, col) + + def p_expr_boolean(self,p): + '''expr : boolean''' + p[0] = p[1] + + def p_boolean(self,p): + '''boolean : not comparison + | comparison''' + line = p.lineno(1) + p[0] = NotNode(p[2], line, p.slice[1].column) if len(p) == 3 else p[1] + + def p_comparison(self,p): + '''comparison : comparison EQUAL boolean + | comparison LESS boolean + | comparison LESSEQ boolean + | arith''' + if len(p) ==4 and p[2] == '=': + p[0] = EqualNode(p.slice[2], p[1],p[3], p.lineno(2), p.slice[2].column) + elif len(p) ==4 and p[2] == '<': + p[0] = LessNode(p.slice[2], p[1],p[3], p.lineno(2), p.slice[2].column) + elif len(p) ==4 and p[2] == '<=': + p[0] = LessEqual(p.slice[2], p[1],p[3], p.lineno(2), p.slice[2].column) + else: p[0] = p[1] + + def p_arith(self,p): + '''arith : arith PLUS term + | arith MINUS term + | term''' + if len(p) ==4 and p[2] == '+': + p[0] = PlusNode(p.slice[2], p[1], p[3], p.lineno(2), p.slice[2].column) + elif len(p) ==4 and p[2] == '-': + p[0] = MinusNode(p.slice[2], p[1], p[3], p.lineno(2), p.slice[2].column) + else: p[0] = p[1] + + def p_term(self,p): + '''term : term STAR unary + | term DIV unary + | unary''' + if len(p) == 4 and p[2] == '*': + p[0] = StarNode(p.slice[2], p[1], p[3], p.lineno(2), p.slice[2].column) + elif len(p) == 4 and p[2] == '/': + p[0] = DivNode(p.slice[2], p[1], p[3], p.lineno(2), p.slice[2].column) + else: p[0] = p[1] + + def p_unary_factor(self,p): + '''unary : factor''' + p[0] = p[1] + + def p_unary_negate(self,p): + '''unary : NOX unary''' + col = p.slice[1].column + line = p.lineno(1) + p[0] = NegateNode(p[2], line, col) + + def p_unary_isvoid(self,p): + '''unary : isvoid expr''' + col = p.slice[1].column + line = p.lineno(1) + p[0] = IsVoidNode(p[2], line, col) + + def p_expr_let(self,p): + '''atom : let declar_list in expr''' + col = p.slice[1].column + line = p.lineno(1) + p[0] = LetNode(p[2],p[4], line, col) + + def p_expr_while(self,p): + '''atom : while expr loop expr pool''' + col = p.slice[1].column + line = p.lineno(1) + p[0] = WhileNode(p[2], p[4], line, col) + + def p_expr_if(self,p): + '''atom : if expr then expr else expr fi''' + col = p.slice[1].column + line = p.lineno(1) + p[0] = IfNode(p[2],p[4],p[6], line, col) + + def p_expr_case(self,p): + '''atom : case expr of assign_list esac''' + col = p.slice[1].column + line = p.lineno(1) + p[0] = CaseNode(p[2], p[4], line, col) + + def p_expr_group(self,p): + '''atom : OCUR expr_list CCUR''' + col = p.slice[1].column + line = p.lineno(1) + p[0] = ExpressionGroupNode(p[2], line, col) + + def p_expr_assign(self,p): + '''atom : ID LARROW expr''' + col = p.slice[1].column + line = p.lineno(1) + p[0] = AssignNode(p[1],p[3], line, col) + + def p_factor_atom(self,p): + '''factor : atom''' + p[0] = p[1] + + def p_factor_expr(self,p): + '''factor : OPAR expr CPAR''' + p[0] = p[2] + + def p_factor_call(self,p): + '''factor : factor DOT ID OPAR arg_list_call CPAR + | ID OPAR arg_list_call CPAR + | factor ARROBA TYPE DOT ID OPAR arg_list_call CPAR''' + if p[2] == '.': + p[0] = CallNode(p[1], p[3], p[5], None,1, p.lineno(2), p.slice[2].column) + elif p[2] == '(': + p[0] = CallNode(None, p[1], p[3], None,2, p.lineno(1), p.slice[1].column) + elif p[2] == '@': + p[0] = CallNode(p[1], p[5], p[7], p[3],3, p.lineno(3), p.slice[3].column) + + def p_arg_list_call(self,p): + '''arg_list_call : arg_list + | arg_list_empty''' + p[0] = p[1] + + def p_atom_num(self,p): + '''atom : NUMBER''' + col = p.slice[1].column + line = p.lineno(1) + p[0] = ConstantNumNode(p[1], line, col) + + def p_atom_boolean(self, p): + '''atom : true + | false''' + col = p.slice[1].column + line = p.lineno(1) + p[0] = BooleanNode(p[1], line, col) + + def p_atom_id(self,p): + '''atom : ID''' + col = p.slice[1].column + line = p.lineno(1) + p[0] = VariableNode(p[1], line, col) + + def p_atom_instantiate(self,p): + '''atom : new TYPE''' + col = p.slice[1].column + line = p.lineno(2) + p[0] = InstantiateNode(p[2], line, col) + + def p_atom_string(self,p): + '''atom : STRING''' + col = p.slice[1].column + line = p.lineno(1) + p[0] = StringNode(p[1], line, col) + + def p_arg_list(self,p): + '''arg_list : expr + | expr COMMA arg_list''' + if len(p) == 2: + p[0] = [p[1]] + else: + p[0] = [p[1]] + p[3] + + def p_arg_list_empty(self,p): + '''arg_list_empty : empty''' + p[0] = [] + + def p_error(self,p): + self.found_error = True + if not p: + error = ParserError(0,0,"EOF") + self.errors.append(error) + return + msg = f'ERROR at or near "{p.value}"' + error = ParserError(p.column,p.lineno,msg) + self.errors.append(error) + self.parser.errok() diff --git a/src/cool/Parser/parsetab.py b/src/cool/Parser/parsetab.py new file mode 100644 index 000000000..27e410f3a --- /dev/null +++ b/src/cool/Parser/parsetab.py @@ -0,0 +1,94 @@ + +# parsetab.py +# This file is automatically generated. Do not edit. +_tabversion = '3.10' + +_lr_method = 'LALR' + +_lr_signature = 'programisvoid not if else then fi class inherits while loop pool let in case esac of new true false SEMI COLON COMMA DOT OPAR CPAR OCUR CCUR LARROW ARROBA RARROW NOX EQUAL PLUS MINUS STAR DIV LESS LESSEQ ID TYPE NUMBER STRINGempty :program : class_listclass_list : def_class SEMI class_list\n | def_class SEMIdef_class : class TYPE OCUR feature_list CCUR\n | class TYPE inherits TYPE OCUR feature_list CCURfeature_list : def_attr SEMI feature_list\n | def_func SEMI feature_list\n | emptydef_attr : ID COLON TYPE\n | ID COLON TYPE LARROW exprdef_func : ID OPAR param_list_call CPAR COLON TYPE OCUR expr CCURparam_list_call : param_list\n | param_list_emptyparam_list : param\n | param COMMA param_listparam_list_empty : emptyparam : ID COLON TYPEexpr_list : expr SEMI expr_list\n | expr SEMIdeclar_list : declar\n | declar COMMA declar_listdeclar : ID COLON TYPE\n | ID COLON TYPE LARROW exprassign_list : case_assign\n | case_assign assign_listcase_assign : ID COLON TYPE RARROW expr SEMIatom : let declar_list in expratom : while expr loop expr poolatom : if expr then expr else expr fiatom : case expr of assign_list esacatom : OCUR expr_list CCURatom : ID LARROW exprexpr : booleanboolean : not comparison\n | comparisoncomparison : comparison EQUAL boolean\n | comparison LESS boolean\n | comparison LESSEQ boolean \n | aritharith : arith PLUS term\n | arith MINUS term\n | termterm : term STAR unary\n | term DIV unary\n | unaryunary : factorunary : NOX unaryunary : isvoid exprfactor : atomfactor : OPAR expr CPARfactor : factor DOT ID OPAR arg_list_call CPAR\n | ID OPAR arg_list_call CPAR\n | factor ARROBA TYPE DOT ID OPAR arg_list_call CPARarg_list_call : arg_list\n | arg_list_emptyatom : NUMBERatom : true\n | falseatom : IDatom : new TYPEatom : STRINGarg_list : expr\n | expr COMMA arg_listarg_list_empty : empty' + +_lr_action_items = {'class':([0,5,],[4,4,]),'$end':([1,2,5,7,],[0,-2,-4,-3,]),'SEMI':([3,11,12,16,24,36,37,38,39,41,42,43,44,45,48,55,56,57,59,65,75,76,85,86,93,94,95,96,97,98,99,100,103,110,113,117,131,133,136,137,143,145,146,],[5,17,18,-5,-10,-6,-60,-11,-34,-36,-40,-43,-46,-47,-50,-57,-58,-59,-62,-35,-48,-49,111,-61,-33,-37,-38,-39,-41,-42,-44,-45,-51,-32,-53,-28,-29,-31,-12,-52,-30,-54,147,]),'TYPE':([4,9,19,33,58,61,74,106,135,],[6,15,24,60,86,87,102,119,141,]),'OCUR':([6,15,32,40,46,47,49,51,52,53,54,63,64,66,67,68,69,70,71,72,87,104,107,108,111,112,114,115,130,132,138,144,],[8,21,54,54,54,54,54,54,54,54,54,54,54,54,54,54,54,54,54,54,112,54,54,54,54,54,54,54,54,54,54,54,]),'inherits':([6,],[9,]),'ID':([8,17,18,20,21,32,35,40,46,47,49,50,51,52,53,54,63,64,66,67,68,69,70,71,72,73,104,105,107,108,109,111,112,114,115,116,123,130,132,138,144,147,],[14,14,14,25,14,37,25,37,37,37,37,80,37,37,37,37,37,37,37,37,37,37,37,37,37,101,37,80,37,37,124,37,37,37,37,129,124,37,37,37,37,-27,]),'CCUR':([8,10,13,17,18,21,22,23,31,37,39,41,42,43,44,45,48,55,56,57,59,65,75,76,84,86,93,94,95,96,97,98,99,100,103,110,111,113,117,125,126,131,133,137,143,145,],[-1,16,-9,-1,-1,-1,-7,-8,36,-60,-34,-36,-40,-43,-46,-47,-50,-57,-58,-59,-62,-35,-48,-49,110,-61,-33,-37,-38,-39,-41,-42,-44,-45,-51,-32,-20,-53,-28,-19,136,-29,-31,-52,-30,-54,]),'COLON':([14,25,34,80,124,],[19,33,61,106,135,]),'OPAR':([14,32,37,40,46,47,49,51,52,53,54,63,64,66,67,68,69,70,71,72,101,104,107,108,111,112,114,115,129,130,132,138,144,],[20,49,63,49,49,49,49,49,49,49,49,49,49,49,49,49,49,49,49,49,115,49,49,49,49,49,49,49,138,49,49,49,49,]),'CPAR':([20,26,27,28,29,30,37,39,41,42,43,44,45,48,55,56,57,59,60,62,63,65,75,76,77,86,88,89,90,91,92,93,94,95,96,97,98,99,100,103,110,113,115,117,127,128,131,133,137,138,142,143,145,],[-1,34,-13,-14,-15,-17,-60,-34,-36,-40,-43,-46,-47,-50,-57,-58,-59,-62,-18,-16,-1,-35,-48,-49,103,-61,113,-55,-56,-63,-65,-33,-37,-38,-39,-41,-42,-44,-45,-51,-32,-53,-1,-28,-64,137,-29,-31,-52,-1,145,-30,-54,]),'LARROW':([24,37,119,],[32,64,130,]),'COMMA':([29,37,39,41,42,43,44,45,48,55,56,57,59,60,65,75,76,79,86,91,93,94,95,96,97,98,99,100,103,110,113,117,119,131,133,137,139,143,145,],[35,-60,-34,-36,-40,-43,-46,-47,-50,-57,-58,-59,-62,-18,-35,-48,-49,105,-61,114,-33,-37,-38,-39,-41,-42,-44,-45,-51,-32,-53,-28,-23,-29,-31,-52,-24,-30,-54,]),'not':([32,47,49,51,52,53,54,63,64,66,67,68,104,107,108,111,112,114,115,130,132,138,144,],[40,40,40,40,40,40,40,40,40,40,40,40,40,40,40,40,40,40,40,40,40,40,40,]),'NOX':([32,40,46,47,49,51,52,53,54,63,64,66,67,68,69,70,71,72,104,107,108,111,112,114,115,130,132,138,144,],[46,46,46,46,46,46,46,46,46,46,46,46,46,46,46,46,46,46,46,46,46,46,46,46,46,46,46,46,46,]),'isvoid':([32,40,46,47,49,51,52,53,54,63,64,66,67,68,69,70,71,72,104,107,108,111,112,114,115,130,132,138,144,],[47,47,47,47,47,47,47,47,47,47,47,47,47,47,47,47,47,47,47,47,47,47,47,47,47,47,47,47,47,]),'let':([32,40,46,47,49,51,52,53,54,63,64,66,67,68,69,70,71,72,104,107,108,111,112,114,115,130,132,138,144,],[50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,]),'while':([32,40,46,47,49,51,52,53,54,63,64,66,67,68,69,70,71,72,104,107,108,111,112,114,115,130,132,138,144,],[51,51,51,51,51,51,51,51,51,51,51,51,51,51,51,51,51,51,51,51,51,51,51,51,51,51,51,51,51,]),'if':([32,40,46,47,49,51,52,53,54,63,64,66,67,68,69,70,71,72,104,107,108,111,112,114,115,130,132,138,144,],[52,52,52,52,52,52,52,52,52,52,52,52,52,52,52,52,52,52,52,52,52,52,52,52,52,52,52,52,52,]),'case':([32,40,46,47,49,51,52,53,54,63,64,66,67,68,69,70,71,72,104,107,108,111,112,114,115,130,132,138,144,],[53,53,53,53,53,53,53,53,53,53,53,53,53,53,53,53,53,53,53,53,53,53,53,53,53,53,53,53,53,]),'NUMBER':([32,40,46,47,49,51,52,53,54,63,64,66,67,68,69,70,71,72,104,107,108,111,112,114,115,130,132,138,144,],[55,55,55,55,55,55,55,55,55,55,55,55,55,55,55,55,55,55,55,55,55,55,55,55,55,55,55,55,55,]),'true':([32,40,46,47,49,51,52,53,54,63,64,66,67,68,69,70,71,72,104,107,108,111,112,114,115,130,132,138,144,],[56,56,56,56,56,56,56,56,56,56,56,56,56,56,56,56,56,56,56,56,56,56,56,56,56,56,56,56,56,]),'false':([32,40,46,47,49,51,52,53,54,63,64,66,67,68,69,70,71,72,104,107,108,111,112,114,115,130,132,138,144,],[57,57,57,57,57,57,57,57,57,57,57,57,57,57,57,57,57,57,57,57,57,57,57,57,57,57,57,57,57,]),'new':([32,40,46,47,49,51,52,53,54,63,64,66,67,68,69,70,71,72,104,107,108,111,112,114,115,130,132,138,144,],[58,58,58,58,58,58,58,58,58,58,58,58,58,58,58,58,58,58,58,58,58,58,58,58,58,58,58,58,58,]),'STRING':([32,40,46,47,49,51,52,53,54,63,64,66,67,68,69,70,71,72,104,107,108,111,112,114,115,130,132,138,144,],[59,59,59,59,59,59,59,59,59,59,59,59,59,59,59,59,59,59,59,59,59,59,59,59,59,59,59,59,59,]),'DOT':([37,39,41,42,43,44,45,48,55,56,57,59,65,75,76,86,93,94,95,96,97,98,99,100,102,103,110,113,117,131,133,137,143,145,],[-60,-34,-36,-40,-43,-46,73,-50,-57,-58,-59,-62,-35,-48,-49,-61,-33,-37,-38,-39,-41,-42,-44,-45,116,-51,-32,-53,-28,-29,-31,-52,-30,-54,]),'ARROBA':([37,39,41,42,43,44,45,48,55,56,57,59,65,75,76,86,93,94,95,96,97,98,99,100,103,110,113,117,131,133,137,143,145,],[-60,-34,-36,-40,-43,-46,74,-50,-57,-58,-59,-62,-35,-48,-49,-61,-33,-37,-38,-39,-41,-42,-44,-45,-51,-32,-53,-28,-29,-31,-52,-30,-54,]),'STAR':([37,39,41,42,43,44,45,48,55,56,57,59,65,75,76,86,93,94,95,96,97,98,99,100,103,110,113,117,131,133,137,143,145,],[-60,-34,-36,-40,71,-46,-47,-50,-57,-58,-59,-62,-35,-48,-49,-61,-33,-37,-38,-39,71,71,-44,-45,-51,-32,-53,-28,-29,-31,-52,-30,-54,]),'DIV':([37,39,41,42,43,44,45,48,55,56,57,59,65,75,76,86,93,94,95,96,97,98,99,100,103,110,113,117,131,133,137,143,145,],[-60,-34,-36,-40,72,-46,-47,-50,-57,-58,-59,-62,-35,-48,-49,-61,-33,-37,-38,-39,72,72,-44,-45,-51,-32,-53,-28,-29,-31,-52,-30,-54,]),'PLUS':([37,39,41,42,43,44,45,48,55,56,57,59,65,75,76,86,93,94,95,96,97,98,99,100,103,110,113,117,131,133,137,143,145,],[-60,-34,-36,69,-43,-46,-47,-50,-57,-58,-59,-62,-35,-48,-49,-61,-33,-37,-38,-39,-41,-42,-44,-45,-51,-32,-53,-28,-29,-31,-52,-30,-54,]),'MINUS':([37,39,41,42,43,44,45,48,55,56,57,59,65,75,76,86,93,94,95,96,97,98,99,100,103,110,113,117,131,133,137,143,145,],[-60,-34,-36,70,-43,-46,-47,-50,-57,-58,-59,-62,-35,-48,-49,-61,-33,-37,-38,-39,-41,-42,-44,-45,-51,-32,-53,-28,-29,-31,-52,-30,-54,]),'EQUAL':([37,39,41,42,43,44,45,48,55,56,57,59,65,75,76,86,93,94,95,96,97,98,99,100,103,110,113,117,131,133,137,143,145,],[-60,-34,66,-40,-43,-46,-47,-50,-57,-58,-59,-62,66,-48,-49,-61,-33,-37,-38,-39,-41,-42,-44,-45,-51,-32,-53,-28,-29,-31,-52,-30,-54,]),'LESS':([37,39,41,42,43,44,45,48,55,56,57,59,65,75,76,86,93,94,95,96,97,98,99,100,103,110,113,117,131,133,137,143,145,],[-60,-34,67,-40,-43,-46,-47,-50,-57,-58,-59,-62,67,-48,-49,-61,-33,-37,-38,-39,-41,-42,-44,-45,-51,-32,-53,-28,-29,-31,-52,-30,-54,]),'LESSEQ':([37,39,41,42,43,44,45,48,55,56,57,59,65,75,76,86,93,94,95,96,97,98,99,100,103,110,113,117,131,133,137,143,145,],[-60,-34,68,-40,-43,-46,-47,-50,-57,-58,-59,-62,68,-48,-49,-61,-33,-37,-38,-39,-41,-42,-44,-45,-51,-32,-53,-28,-29,-31,-52,-30,-54,]),'loop':([37,39,41,42,43,44,45,48,55,56,57,59,65,75,76,81,86,93,94,95,96,97,98,99,100,103,110,113,117,131,133,137,143,145,],[-60,-34,-36,-40,-43,-46,-47,-50,-57,-58,-59,-62,-35,-48,-49,107,-61,-33,-37,-38,-39,-41,-42,-44,-45,-51,-32,-53,-28,-29,-31,-52,-30,-54,]),'then':([37,39,41,42,43,44,45,48,55,56,57,59,65,75,76,82,86,93,94,95,96,97,98,99,100,103,110,113,117,131,133,137,143,145,],[-60,-34,-36,-40,-43,-46,-47,-50,-57,-58,-59,-62,-35,-48,-49,108,-61,-33,-37,-38,-39,-41,-42,-44,-45,-51,-32,-53,-28,-29,-31,-52,-30,-54,]),'of':([37,39,41,42,43,44,45,48,55,56,57,59,65,75,76,83,86,93,94,95,96,97,98,99,100,103,110,113,117,131,133,137,143,145,],[-60,-34,-36,-40,-43,-46,-47,-50,-57,-58,-59,-62,-35,-48,-49,109,-61,-33,-37,-38,-39,-41,-42,-44,-45,-51,-32,-53,-28,-29,-31,-52,-30,-54,]),'pool':([37,39,41,42,43,44,45,48,55,56,57,59,65,75,76,86,93,94,95,96,97,98,99,100,103,110,113,117,120,131,133,137,143,145,],[-60,-34,-36,-40,-43,-46,-47,-50,-57,-58,-59,-62,-35,-48,-49,-61,-33,-37,-38,-39,-41,-42,-44,-45,-51,-32,-53,-28,131,-29,-31,-52,-30,-54,]),'else':([37,39,41,42,43,44,45,48,55,56,57,59,65,75,76,86,93,94,95,96,97,98,99,100,103,110,113,117,121,131,133,137,143,145,],[-60,-34,-36,-40,-43,-46,-47,-50,-57,-58,-59,-62,-35,-48,-49,-61,-33,-37,-38,-39,-41,-42,-44,-45,-51,-32,-53,-28,132,-29,-31,-52,-30,-54,]),'in':([37,39,41,42,43,44,45,48,55,56,57,59,65,75,76,78,79,86,93,94,95,96,97,98,99,100,103,110,113,117,118,119,131,133,137,139,143,145,],[-60,-34,-36,-40,-43,-46,-47,-50,-57,-58,-59,-62,-35,-48,-49,104,-21,-61,-33,-37,-38,-39,-41,-42,-44,-45,-51,-32,-53,-28,-22,-23,-29,-31,-52,-24,-30,-54,]),'fi':([37,39,41,42,43,44,45,48,55,56,57,59,65,75,76,86,93,94,95,96,97,98,99,100,103,110,113,117,131,133,137,140,143,145,],[-60,-34,-36,-40,-43,-46,-47,-50,-57,-58,-59,-62,-35,-48,-49,-61,-33,-37,-38,-39,-41,-42,-44,-45,-51,-32,-53,-28,-29,-31,-52,143,-30,-54,]),'esac':([122,123,134,147,],[133,-25,-26,-27,]),'RARROW':([141,],[144,]),} + +_lr_action = {} +for _k, _v in _lr_action_items.items(): + for _x,_y in zip(_v[0],_v[1]): + if not _x in _lr_action: _lr_action[_x] = {} + _lr_action[_x][_k] = _y +del _lr_action_items + +_lr_goto_items = {'program':([0,],[1,]),'class_list':([0,5,],[2,7,]),'def_class':([0,5,],[3,3,]),'feature_list':([8,17,18,21,],[10,22,23,31,]),'def_attr':([8,17,18,21,],[11,11,11,11,]),'def_func':([8,17,18,21,],[12,12,12,12,]),'empty':([8,17,18,20,21,63,115,138,],[13,13,13,30,13,92,92,92,]),'param_list_call':([20,],[26,]),'param_list':([20,35,],[27,62,]),'param_list_empty':([20,],[28,]),'param':([20,35,],[29,29,]),'expr':([32,47,49,51,52,53,54,63,64,104,107,108,111,112,114,115,130,132,138,144,],[38,76,77,81,82,83,85,91,93,117,120,121,85,126,91,91,139,140,91,146,]),'boolean':([32,47,49,51,52,53,54,63,64,66,67,68,104,107,108,111,112,114,115,130,132,138,144,],[39,39,39,39,39,39,39,39,39,94,95,96,39,39,39,39,39,39,39,39,39,39,39,]),'comparison':([32,40,47,49,51,52,53,54,63,64,66,67,68,104,107,108,111,112,114,115,130,132,138,144,],[41,65,41,41,41,41,41,41,41,41,41,41,41,41,41,41,41,41,41,41,41,41,41,41,]),'arith':([32,40,47,49,51,52,53,54,63,64,66,67,68,104,107,108,111,112,114,115,130,132,138,144,],[42,42,42,42,42,42,42,42,42,42,42,42,42,42,42,42,42,42,42,42,42,42,42,42,]),'term':([32,40,47,49,51,52,53,54,63,64,66,67,68,69,70,104,107,108,111,112,114,115,130,132,138,144,],[43,43,43,43,43,43,43,43,43,43,43,43,43,97,98,43,43,43,43,43,43,43,43,43,43,43,]),'unary':([32,40,46,47,49,51,52,53,54,63,64,66,67,68,69,70,71,72,104,107,108,111,112,114,115,130,132,138,144,],[44,44,75,44,44,44,44,44,44,44,44,44,44,44,44,44,99,100,44,44,44,44,44,44,44,44,44,44,44,]),'factor':([32,40,46,47,49,51,52,53,54,63,64,66,67,68,69,70,71,72,104,107,108,111,112,114,115,130,132,138,144,],[45,45,45,45,45,45,45,45,45,45,45,45,45,45,45,45,45,45,45,45,45,45,45,45,45,45,45,45,45,]),'atom':([32,40,46,47,49,51,52,53,54,63,64,66,67,68,69,70,71,72,104,107,108,111,112,114,115,130,132,138,144,],[48,48,48,48,48,48,48,48,48,48,48,48,48,48,48,48,48,48,48,48,48,48,48,48,48,48,48,48,48,]),'declar_list':([50,105,],[78,118,]),'declar':([50,105,],[79,79,]),'expr_list':([54,111,],[84,125,]),'arg_list_call':([63,115,138,],[88,128,142,]),'arg_list':([63,114,115,138,],[89,127,89,89,]),'arg_list_empty':([63,115,138,],[90,90,90,]),'assign_list':([109,123,],[122,134,]),'case_assign':([109,123,],[123,123,]),} + +_lr_goto = {} +for _k, _v in _lr_goto_items.items(): + for _x, _y in zip(_v[0], _v[1]): + if not _x in _lr_goto: _lr_goto[_x] = {} + _lr_goto[_x][_k] = _y +del _lr_goto_items +_lr_productions = [ + ("S' -> program","S'",1,None,None,None), + ('empty -> ','empty',0,'p_empty','parser.py',26), + ('program -> class_list','program',1,'p_program','parser.py',30), + ('class_list -> def_class SEMI class_list','class_list',3,'p_class_list','parser.py',34), + ('class_list -> def_class SEMI','class_list',2,'p_class_list','parser.py',35), + ('def_class -> class TYPE OCUR feature_list CCUR','def_class',5,'p_def_class','parser.py',39), + ('def_class -> class TYPE inherits TYPE OCUR feature_list CCUR','def_class',7,'p_def_class','parser.py',40), + ('feature_list -> def_attr SEMI feature_list','feature_list',3,'p_feature_list','parser.py',52), + ('feature_list -> def_func SEMI feature_list','feature_list',3,'p_feature_list','parser.py',53), + ('feature_list -> empty','feature_list',1,'p_feature_list','parser.py',54), + ('def_attr -> ID COLON TYPE','def_attr',3,'p_def_attr','parser.py',59), + ('def_attr -> ID COLON TYPE LARROW expr','def_attr',5,'p_def_attr','parser.py',60), + ('def_func -> ID OPAR param_list_call CPAR COLON TYPE OCUR expr CCUR','def_func',9,'p_def_func','parser.py',69), + ('param_list_call -> param_list','param_list_call',1,'p_param_list_call','parser.py',76), + ('param_list_call -> param_list_empty','param_list_call',1,'p_param_list_call','parser.py',77), + ('param_list -> param','param_list',1,'p_param_list','parser.py',81), + ('param_list -> param COMMA param_list','param_list',3,'p_param_list','parser.py',82), + ('param_list_empty -> empty','param_list_empty',1,'p_param_list_empty','parser.py',89), + ('param -> ID COLON TYPE','param',3,'p_param','parser.py',94), + ('expr_list -> expr SEMI expr_list','expr_list',3,'p_expr_list','parser.py',100), + ('expr_list -> expr SEMI','expr_list',2,'p_expr_list','parser.py',101), + ('declar_list -> declar','declar_list',1,'p_declar_list','parser.py',106), + ('declar_list -> declar COMMA declar_list','declar_list',3,'p_declar_list','parser.py',107), + ('declar -> ID COLON TYPE','declar',3,'p_declar','parser.py',111), + ('declar -> ID COLON TYPE LARROW expr','declar',5,'p_declar','parser.py',112), + ('assign_list -> case_assign','assign_list',1,'p_assign_list','parser.py',121), + ('assign_list -> case_assign assign_list','assign_list',2,'p_assign_list','parser.py',122), + ('case_assign -> ID COLON TYPE RARROW expr SEMI','case_assign',6,'p_case_assign','parser.py',127), + ('atom -> let declar_list in expr','atom',4,'p_expr_let','parser.py',134), + ('atom -> while expr loop expr pool','atom',5,'p_expr_while','parser.py',141), + ('atom -> if expr then expr else expr fi','atom',7,'p_expr_if','parser.py',148), + ('atom -> case expr of assign_list esac','atom',5,'p_expr_case','parser.py',155), + ('atom -> OCUR expr_list CCUR','atom',3,'p_expr_group','parser.py',162), + ('atom -> ID LARROW expr','atom',3,'p_expr_assign','parser.py',169), + ('expr -> boolean','expr',1,'p_expr_boolean','parser.py',176), + ('boolean -> not comparison','boolean',2,'p_boolean','parser.py',180), + ('boolean -> comparison','boolean',1,'p_boolean','parser.py',181), + ('comparison -> comparison EQUAL boolean','comparison',3,'p_comparison','parser.py',187), + ('comparison -> comparison LESS boolean','comparison',3,'p_comparison','parser.py',188), + ('comparison -> comparison LESSEQ boolean','comparison',3,'p_comparison','parser.py',189), + ('comparison -> arith','comparison',1,'p_comparison','parser.py',190), + ('arith -> arith PLUS term','arith',3,'p_arith','parser.py',200), + ('arith -> arith MINUS term','arith',3,'p_arith','parser.py',201), + ('arith -> term','arith',1,'p_arith','parser.py',202), + ('term -> term STAR unary','term',3,'p_term','parser.py',210), + ('term -> term DIV unary','term',3,'p_term','parser.py',211), + ('term -> unary','term',1,'p_term','parser.py',212), + ('unary -> factor','unary',1,'p_unary_factor','parser.py',220), + ('unary -> NOX unary','unary',2,'p_unary_negate','parser.py',224), + ('unary -> isvoid expr','unary',2,'p_unary_isvoid','parser.py',231), + ('factor -> atom','factor',1,'p_factor_atom','parser.py',238), + ('factor -> OPAR expr CPAR','factor',3,'p_factor_expr','parser.py',242), + ('factor -> factor DOT ID OPAR arg_list_call CPAR','factor',6,'p_factor_call','parser.py',246), + ('factor -> ID OPAR arg_list_call CPAR','factor',4,'p_factor_call','parser.py',247), + ('factor -> factor ARROBA TYPE DOT ID OPAR arg_list_call CPAR','factor',8,'p_factor_call','parser.py',248), + ('arg_list_call -> arg_list','arg_list_call',1,'p_arg_list_call','parser.py',257), + ('arg_list_call -> arg_list_empty','arg_list_call',1,'p_arg_list_call','parser.py',258), + ('atom -> NUMBER','atom',1,'p_atom_num','parser.py',262), + ('atom -> true','atom',1,'p_atom_boolean','parser.py',269), + ('atom -> false','atom',1,'p_atom_boolean','parser.py',270), + ('atom -> ID','atom',1,'p_atom_id','parser.py',277), + ('atom -> new TYPE','atom',2,'p_atom_instantiate','parser.py',284), + ('atom -> STRING','atom',1,'p_atom_string','parser.py',291), + ('arg_list -> expr','arg_list',1,'p_arg_list','parser.py',298), + ('arg_list -> expr COMMA arg_list','arg_list',3,'p_arg_list','parser.py',299), + ('arg_list_empty -> empty','arg_list_empty',1,'p_arg_list_empty','parser.py',306), +] diff --git a/src/cool/cil_builder/cilAnalizer.py b/src/cool/cil_builder/cilAnalizer.py new file mode 100644 index 000000000..3422bb5aa --- /dev/null +++ b/src/cool/cil_builder/cilAnalizer.py @@ -0,0 +1,11 @@ +from cool.cil_builder.cool_to_cil_visitor import COOLToCILVisitor +from cool.mips_builder.cil_to_mips_visitor import CILtoMIPSVisitor + +def run_code_gen_pipeline(cool_ast,context,scope): + cool_to_cilVisitor = COOLToCILVisitor(context) + cil_ast = cool_to_cilVisitor.visit(cool_ast,scope) + + cil_to_mipsVisitor = CILtoMIPSVisitor(context) + mips_text = cil_to_mipsVisitor.visit(cil_ast) + + return mips_text \ No newline at end of file diff --git a/src/cool/cil_builder/cil_ast.py b/src/cool/cil_builder/cil_ast.py new file mode 100644 index 000000000..33984a720 --- /dev/null +++ b/src/cool/cil_builder/cil_ast.py @@ -0,0 +1,431 @@ +import cool.cmp.visitor as visitor + + +class CilNode: + pass + +class ProgramCilNode(CilNode): + def __init__(self, dottypes, dotdata, dotcode): + self.dottypes = dottypes + self.dotdata = dotdata + self.dotcode = dotcode + +class TypeCilNode(CilNode): + def __init__(self, name): + self.name = name + self.attributes = [] + self.methods = [] + +class DataCilNode(CilNode): + def __init__(self, vname, value): + self.name = vname + self.value = value + +class FunctionCilNode(CilNode): + def __init__(self, fname, params, localvars, instructions): + self.name = fname + self.params = params + self.localvars = localvars + self.instructions = instructions + +class ParamCilNode(CilNode): + def __init__(self, name): + self.name = name + +class LocalCilNode(CilNode): + def __init__(self, name): + self.name = name + +class InstructionCilNode(CilNode): + pass + + +class BinaryCilNode(InstructionCilNode): #Binary Operations + def __init__(self, dest, left, right): + self.dest = dest + self.left = left + self.right = right + +class UnaryCilNode(InstructionCilNode): #Unary Operations + def __init__(self, dest, right): + self.dest = dest + self.right = right + + + +class ConstantCilNode(InstructionCilNode): #7.1 Constant + def __init__(self,vname,value): + self.vname = vname + self.value = value + +#7.2 Identifiers, not necesary cil Node + +class AssignCilNode(InstructionCilNode): # 7.3 Assignment + def __init__(self, dest, source): + self.dest = dest + self.source = source + +class StaticCallCilNode(InstructionCilNode): #7.4 Distaptch Static id.() + def __init__(self,type,method_name,args, result): + # self.expresion_instance = expresion_instance + self.type = type + self.method_name = method_name + self.args = args + self.result = result + +class DynamicCallCilNode(InstructionCilNode): #7.4 Dispatch Dynamic .id() + def __init__(self, expresion_instance,static_type, method_name,args, result): + self.expresion_instance = expresion_instance + self.static_type = static_type + self.method_name = method_name + self.args = args + self.result = result + +class DynamicParentCallCilNode(InstructionCilNode): #7.4 Dispatch @type.id() + def __init__(self, expresion_instance,static_type, method_name,args, result): + self.expresion_instance = expresion_instance + self.static_type = static_type + self.method_name = method_name + self.args = args + self.result = result + + +# class IfCilNode(InstructionCilNode): #7.5 If Conditional +# def __init__(self,if_expr,then_label,else_label): +# self.if_expr = if_expr +# self.then_label = then_label +# self.else_label = else_label + +# class WhileCilNode(InstructionCilNode): #7.6 Loops +# def __init__(self,condition,body): +# self.condition = condition +# self.body = body + +class BlockCilNode(InstructionCilNode): #7.7 Blocks + pass + +class LetCilNode(InstructionCilNode): #7.8 Let + pass + +class CaseCilNode(InstructionCilNode): #7.9 Case + def __init__(self,expr): + self.main_expr = expr + +class BranchCilNode(InstructionCilNode): #7.9 Case + def __init__(self,type_k): + self.type_k = type_k + +class CaseEndCilNode(InstructionCilNode): #7.9 Case + def __init__(self,result): + self.result = result + + +class InstantiateCilNode(InstructionCilNode): #7.10 New + pass + +class IsVoidCilNode(InstructionCilNode): #7.11 IsVoid + def __init__(self, expresion, result): + self.expresion = expresion + self.result = result + pass + + +#Binary Aritmetic Operations +class PlusCilNode(BinaryCilNode): + pass + +class MinusCilNode(BinaryCilNode): + pass + +class StarCilNode(BinaryCilNode): + pass +class DivCilNode(BinaryCilNode): + pass + +class EqualCilNode(BinaryCilNode): + pass + +class CompareStringCilNode(BinaryCilNode): + pass + +class EqualRefCilNode(BinaryCilNode): + pass +class LessEqualCilNode(BinaryCilNode): + pass + +class LessCilNode(BinaryCilNode): + pass +class IsVoidCilNode(UnaryCilNode): + pass +class NotCilNode(UnaryCilNode): + pass +class NegateCilNode(UnaryCilNode): + pass + + + + +# Attributes operations +class GetAttribCilNode(InstructionCilNode): + #instance es la direccion de memoria donde se encuentra la instancia en memoria + #Type es el tipo estatico de esa instancia + #attribute es el atributo de ese tipo al que se quiere acceder + #result es la direccion de memoria (el local internal) donde voy a guardar el valor del atributo + #Con el tipo estatico + el atributo puedo saber el offset + def __init__(self,instance,type,attribute,result): + self.instance = instance + self.type = type + self.attribute = attribute + self.result = result + +class SetAttribCilNode(InstructionCilNode): + #instance es la direccion de memoria donde se encuentra la instancia en memoria + #Type es el tipo estatico de esa instancia + #attribute es el atributo de ese tipo al que se quiere guardar la informacion + #value es la direccion de memoria (el local internal) donde se encuentra el valor que deseo guardar en el atributo + #Con el tipo estatico + el atributo puedo saber el offset + def __init__(self,instance,type,attribute,value): + self.instance = instance + self.type = type + self.attribute = attribute + self.value = value + +class SetDefaultCilNode(InstructionCilNode): + def __init__(self,instance,type,attribute,value): + self.instance = instance + self.type = type + self.attribute = attribute + self.value = value + +class AllocateCilNode(InstructionCilNode): + #Liberar espacio en el heap para la instancia y la direccion de memoria donde se creo dejarla en result + #result seria una local internal con la direccion en memoria de la instancia creada + def __init__(self, type, result): + self.type = type + self.result = result + +class AllocateDynamicCilNode(InstructionCilNode): + #Liberar espacio en memoria a partir de una cantidad que se encuentra en una direccion de memoria. + def __init__(self,address_in_local,result): + self.address_in_local = address_in_local + self.result = result + +class InternalCopyCilNode(InstructionCilNode): + def __init__(self,dir_child,dir_ancestor): + self.dir_child = dir_child + self.dir_ancestor = dir_ancestor + +class GetDataCilNode(InstructionCilNode): + def __init__(self,name,result): + self.name = name + self.result = result + +class TypeOfCilNode(InstructionCilNode): + #Dado una instancia (local internal que posee una direccion en memoria con el tipo dinamico) devolver el string correspondiente a ese tipo dinamico + def __init__(self, instance, result): + self.instance = instance + self.result = result + +class LabelCilNode(InstructionCilNode): + def __init__(self,label): + self.label = label + +class GotoCilNode(InstructionCilNode): + def __init__(self,label): + self.label = label + +class GotoIfCilNode(InstructionCilNode): + def __init__(self,val_dir,label): + self.val_dir = val_dir + self.label = label + +class GotoBoolIfCilNode(InstructionCilNode): + def __init__(self,val_dir,label): + self.val_dir = val_dir + self.label = label +class NotGotoIfCilNode: + def __init__(self,val_dir,label): + self.val_dir = val_dir + self.label = label + +class ArgCilNode(InstructionCilNode): + #En result se guardaria la direccion en memoria donde esta el resultado de evaluar la expresion del argumento + def __init__(self, result): + self.result = result + +class ReturnCilNode(InstructionCilNode): + #Direccion en memoria donde se guarda el resultado de la funcion + def __init__(self, value=None): + self.value = value + + +#TypesCilNodes +class IntCilNode(InstructionCilNode): + def __init__(self,value,result): + self.value = value + self.result = result + +class StringCilNode(InstructionCilNode): + def __init__(self,dir1,dir2): + self.dir1 = dir1 + self.dir2 = dir2 + +#Function CilNodes +class ToStrCilNode(InstructionCilNode): + def __init__(self, dest, ivalue): + self.dest = dest + self.ivalue = ivalue + +class ReadCilNode(InstructionCilNode): + def __init__(self,dest): + self.dest = dest + +class PrintCilNode(InstructionCilNode): + def __init__(self,to_print): + self.to_print = to_print +class AbortCilNode(InstructionCilNode): + def __init__(self,self_from_call): + self.self_from_call = self_from_call +class TypeNameCilNode(InstructionCilNode): + def __init__(self, self_param, result): + self.self_param = self_param + self.result = result +class CopyCilNode(InstructionCilNode): + def __init__(self, self_param, result): + self.self_param = self_param + self.result = result +class PrintStringCilNode(PrintCilNode): + pass +class PrintIntCilNode(PrintCilNode): + pass +class ReadStringCilNode(ReadCilNode): + pass +class ReadStrEndCilNode(InstructionCilNode): + def __init__(self, length,result): + self.length = length + self.result = result +class ReadIntCilNode(ReadCilNode): + pass +class LengthCilNode(InstructionCilNode): + def __init__(self, self_dir, length): + self.self_dir = self_dir + self.length = length +class ConcatCilNode(InstructionCilNode): + def __init__(self, self_param, param_1,result_addr): + self.self_param = self_param + self.param_1 = param_1 + self.result_addr = result_addr + +class SubstringCilNode(InstructionCilNode): + def __init__(self, self_param,value1, value2,result): + self.self_param = self_param + self.value1 = value1 + self.value2 = value2 + self.result = result + + +#Given 2 memory location calculate the distance of his types +class TypeDistanceCilNode(InstructionCilNode): + def __init__(self,type1,type2,result): + self.type1 = type1 + self.type2 = type2 + self.result = result + + +class MinCilNode(InstructionCilNode): + def __init__(self,num1,num2,result): + self.num1 = num1 + self.num2 = num2 + self.result = result + + + +class ErrorCilNode(InstructionCilNode): + def __init__(self,name): + self.name = name + + +############# New Nodes ################### +def get_formatter(): + class PrintVisitor(object): + @visitor.on('CilNode') + def visit(self, CilNode): + pass + + @visitor.when(ProgramCilNode) + def visit(self, CilNode): + dottypes = '\n'.join(self.visit(t) for t in CilNode.dottypes) + dotdata = '\n'.join(self.visit(t) for t in CilNode.dotdata) + dotcode = '\n'.join(self.visit(t) for t in CilNode.dotcode) + + return f'.TYPES\n{dottypes}\n\n.DATA\n{dotdata}\n\n.CODE\n{dotcode}' + + @visitor.when(TypeCilNode) + def visit(self, CilNode): + attributes = '\n\t'.join(f'attribute {x}' for x in CilNode.attributes) + methods = '\n\t'.join(f'method {x}: {y}' for x,y in CilNode.methods) + + return f'type {CilNode.name} {{\n\t{attributes}\n\n\t{methods}\n}}' + + @visitor.when(FunctionCilNode) + def visit(self, CilNode): + params = '\n\t'.join(self.visit(x) for x in CilNode.params) + localvars = '\n\t'.join(self.visit(x) for x in CilNode.localvars) + instructions = '\n\t'.join(self.visit(x) for x in CilNode.instructions) + + return f'function {CilNode.name} {{\n\t{params}\n\n\t{localvars}\n\n\t{instructions}\n}}' + + @visitor.when(ParamCilNode) + def visit(self, CilNode): + return f'PARAM {CilNode.name}' + + @visitor.when(LocalCilNode) + def visit(self, CilNode): + return f'LOCAL {CilNode.name}' + + @visitor.when(AssignCilNode) + def visit(self, CilNode): + return f'{CilNode.dest} = {CilNode.source}' + + @visitor.when(PlusCilNode) + def visit(self, CilNode): + return f'{CilNode.dest} = {CilNode.left} + {CilNode.right}' + + @visitor.when(MinusCilNode) + def visit(self, CilNode): + return f'{CilNode.dest} = {CilNode.left} - {CilNode.right}' + + @visitor.when(StarCilNode) + def visit(self, CilNode): + return f'{CilNode.dest} = {CilNode.left} * {CilNode.right}' + + @visitor.when(DivCilNode) + def visit(self, CilNode): + return f'{CilNode.dest} = {CilNode.left} / {CilNode.right}' + + @visitor.when(AllocateCilNode) + def visit(self, CilNode): + return f'{CilNode.dest} = ALLOCATE {CilNode.type}' + + @visitor.when(TypeOfCilNode) + def visit(self, CilNode): + return f'{CilNode.dest} = TYPEOF {CilNode.type}' + + @visitor.when(StaticCallCilNode) + def visit(self, CilNode): + return f'{CilNode.dest} = CALL {CilNode.function}' + + @visitor.when(DynamicCallCilNode) + def visit(self, CilNode): + return f'{CilNode.dest} = VCALL {CilNode.type} {CilNode.method}' + + @visitor.when(ArgCilNode) + def visit(self, CilNode): + return f'ARG {CilNode.name}' + + @visitor.when(ReturnCilNode) + def visit(self, CilNode): + return f'RETURN {CilNode.value if CilNode.value is not None else ""}' + + printer = PrintVisitor() + return (lambda ast: printer.visit(ast)) \ No newline at end of file diff --git a/src/cool/cil_builder/cool_to_cil_visitor.py b/src/cool/cil_builder/cool_to_cil_visitor.py new file mode 100644 index 000000000..d5e0b70d6 --- /dev/null +++ b/src/cool/cil_builder/cool_to_cil_visitor.py @@ -0,0 +1,958 @@ +import cool.cil_builder.cil_ast as cil +from cool.Parser.AstNodes import * +from cool.semantic import visitor +from cool.utils.Errors.semantic_errors import * +from cool.semantic.semantic import * + +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.current_type_dir = None + self.type_node_dict = {} + self.dottype_dict = {} + self.type_tree = {} + self.context = context + self.label_id = 0 + self.tag_id = 0 + + @property + def params(self): + return self.current_function.params + + @property + def localvars(self): + return self.current_function.localvars + + @property + def instructions(self): + return self.current_function.instructions + + def register_param(self,vinfo): + vinfo.name = f'{vinfo.name}' + param_node = cil.ParamCilNode(vinfo.name) + self.params.append(param_node) + return vinfo.name + + def is_in_actual_params(self,param_name): + return f'param_{param_name}' in (param.name for param in self.params) + + + def register_local(self, vinfo,scope = None): + cool_name = vinfo.name + vinfo.name = f'local_{self.current_function.name[9:]}_{vinfo.name}_{len(self.localvars)}' + local_node = cil.LocalCilNode(vinfo.name) + self.localvars.append(local_node) + if scope is not None: + scope.define_cil_local(cool_name,vinfo.name) + return vinfo.name + + def define_internal_local(self,scope = None): + vinfo = VariableInfo('internal', None) + return self.register_local(vinfo,scope) + + 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.FunctionCilNode(function_name, [], [], []) + self.dotcode.append(function_node) + return function_node + + def register_type(self, name): + type_node = cil.TypeCilNode(name) + self.dottypes.append(type_node) + self.dottype_dict[name] = type_node + return type_node + + def register_data(self, value): + vname = f'data_{len(self.dotdata)}' + data_node = cil.DataCilNode(vname, value) + self.dotdata.append(data_node) + return data_node + + def fill_cil_types(self,type_name): + try: + object_type_childs = self.type_tree[type_name] + + for child in object_type_childs: + new_methods = self.fill_cil_types_(child.name) + self.dottype_dict[child.name].methods = new_methods + self.fill_cil_types(child.name) + + except KeyError: + pass + + + def fill_cil_types_(self,type_): + dot_type = self.dottype_dict[type_] + type_name = dot_type.name + parent_type = self.context.get_type(type_name).parent + parent_name = parent_type.name + dot_parent = self.dottype_dict[parent_name] + + new_method_list = [] + + for (old_methName,new_methName) in dot_parent.methods: + #Tener acceso a los nuevos nombres + method_original = {} + for method in dot_type.methods: + method_original[method[0]] = method[1] + + if old_methName in (method[0] for method in dot_type.methods): + new_method_list.append((old_methName,method_original[old_methName])) + else: + new_method_list.append((old_methName,new_methName)) + for method_touple in dot_type.methods: + if method_touple not in new_method_list: + new_method_list.append(method_touple) + return new_method_list + + def create_label(self): + self.label_id += 1 + return f'label{self.label_id}' + + def create_tag(self): + self.tag_id += 1 + return self.tag_id - 1 + + def get_parentAttr_declarations(self,programNode): + for classNode in programNode.declarations: + class_type = self.context.get_type(classNode.id) + attr_list = self.put_attr_on_type(class_type) + + classNode.features = attr_list + classNode.features + + + + def put_attr_on_type(self,type_): + parent_ = self.context.get_type(type_.name).parent + + if parent_.name == 'Object' or parent_.name == 'IO': + return [] + else: + list_attr = self.type_node_dict[parent_.name] + return self.put_attr_on_type(parent_)+list_attr + + + + def generateTree(self): + classList = {} + for classNode in self.context.types.values(): + if classNode.name == 'Error': + continue + if classNode.parent is not None: + try: + classList[classNode.parent.name].append(classNode) + except KeyError: + classList[classNode.parent.name] = [classNode] + return classList + + def enumerateTypes(self): + parentTree = self.generateTree() + self.context.types['Object'].class_tag = self.create_tag() + for Node in parentTree.values(): + for child in Node: + child.class_tag = self.create_tag() + + def set_default_values(self,node): + class_dir = self.current_type_dir + if node.type == 'Int': + int_internal = self.define_internal_local() + result_location = self.define_internal_local() + self.register_instruction(cil.IntCilNode(0,int_internal)) + self.register_instruction(cil.AllocateCilNode('Int',result_location)) + self.register_instruction(cil.StaticCallCilNode('Int','INIT_Int',[result_location,int_internal],result_location)) + self.register_instruction(cil.SetAttribCilNode(class_dir,self.current_type.name,node.id,result_location)) + elif node.type == 'String': + result_location = self.define_internal_local() + data_location = self.define_internal_local() + len_str = self.define_internal_local() + self.register_instruction(cil.AllocateCilNode('String',result_location)) #Creo el espacio en memoria para guardar el String (Method_dir,String_value,Length) + self.register_instruction(cil.GetDataCilNode('empty_str_data',data_location)) #Paso al sp en una local interna el data + self.register_instruction(cil.IntCilNode(0,len_str)) + self.register_instruction(cil.StaticCallCilNode('String','INIT_String',[result_location,data_location,len_str],result_location)) #LLamo al init del string y lo relleno con los valores que se calcularon de: [Direccion de string val], [Len del string] + self.register_instruction(cil.SetAttribCilNode(class_dir,self.current_type.name,node.id,result_location)) + elif node.type == 'Bool': + bool_internal = self.define_internal_local() + result_location = self.define_internal_local() + self.register_instruction(cil.IntCilNode(0,bool_internal)) + self.register_instruction(cil.AllocateCilNode('Bool',result_location)) + self.register_instruction(cil.StaticCallCilNode('Bool','INIT_Bool',[result_location,bool_internal],result_location)) + self.register_instruction(cil.SetAttribCilNode(class_dir,self.current_type.name,node.id,result_location)) + else: + void_location = self.define_internal_local() + self.register_instruction(cil.GetDataCilNode('void_data',void_location)) + self.register_instruction(cil.SetAttribCilNode(class_dir,self.current_type.name,node.id,void_location)) + + + def fill_builtin(self): + built_in_types = [ + self.context.get_type('Object'), + self.context.get_type('IO'), + self.context.get_type('Int'), + self.context.get_type('String'), + self.context.get_type('Bool')] + for t in built_in_types: + cilType = self.register_type(t.name) + cilType.methods = [(m,self.to_function_name(m,t.name)) for m in t.methods] + if cilType.name in ['Int','String','Bool']: + cilType.attributes.append('value') + if cilType.name == 'String': + cilType.attributes.append('len') + + + #=============================Object========================================= + self.current_type = self.context.get_type('Object') + + #INIT_Object + self.current_function = self.register_function('INIT_Object') + self_param = self.register_param(VariableInfo('self',self.current_type.name)) + self.register_instruction(cil.ReturnCilNode(self_param)) + + #function abort + self.current_function = self.register_function(self.to_function_name('abort',self.current_type.name)) + self_param = self.register_param(VariableInfo('self',self.current_type.name)) + self.register_instruction(cil.AbortCilNode(self_param)) + self.register_instruction(cil.ReturnCilNode()) + + #function type_name + self.current_function = self.register_function(self.to_function_name('type_name',self.current_type.name)) + param_self = self.register_param(VariableInfo('self',self.current_type.name)) + result = self.define_internal_local() + adress = self.define_internal_local() + self.register_instruction(cil.TypeNameCilNode(param_self,adress)) + self.register_instruction(cil.AllocateCilNode('String',result)) + self.register_instruction(cil.StaticCallCilNode('String','INIT_String',[result,adress],result)) + self.register_instruction(cil.ReturnCilNode(result)) + + #function copy + self.current_function = self.register_function(self.to_function_name('copy',self.current_type.name)) + param_self = self.register_param(VariableInfo('self',self.current_type.name)) + result = self.define_internal_local() + self.register_instruction(cil.CopyCilNode(param_self,result)) + self.register_instruction(cil.ReturnCilNode(result)) + + #=========================Int========================================= + self.current_type = self.context.get_type('Int') + + #INIT_Int + self.current_function = self.register_function('INIT_Int') + self_param = self.register_param(VariableInfo('self',self.current_type.name)) + param1 = self.register_param(VariableInfo('value',self.current_type.name)) + self.register_instruction(cil.SetAttribCilNode(self_param,'Int','value', param1)) + self.register_instruction(cil.ReturnCilNode(self_param)) + + #=========================String========================================= + self.current_type = self.context.get_type('String') + + #INIT_String + self.current_function = self.register_function('INIT_String') + self_param = self.register_param(VariableInfo('self',self.current_type.name)) + param1_data = self.register_param(VariableInfo('value',self.current_type.name)) + param2_len = self.register_param(VariableInfo('len',self.current_type.name)) + + self.register_instruction(cil.SetAttribCilNode(self_param,'String','value',param1_data)) + self.register_instruction(cil.SetAttribCilNode(self_param,'String','len',param2_len)) + self.register_instruction(cil.ReturnCilNode(self_param)) + + #function length + self.current_function = self.register_function(self.to_function_name('length',self.current_type.name)) + param_self = self.register_param(VariableInfo('self',self.current_type.name)) + len_value = self.define_internal_local() + result = self.define_internal_local() + self.register_instruction(cil.GetAttribCilNode(param_self,'String','len',len_value)) + self.register_instruction(cil.AllocateCilNode('Int',result)) + self.register_instruction(cil.StaticCallCilNode('Int','INIT_Int',[result,len_value],result)) + self.register_instruction(cil.ReturnCilNode(result)) + + #function INIT_length + self.current_function = self.register_function('INIT_length') + param_self = self.register_param(VariableInfo('self',self.current_type.name)) + result = self.define_internal_local() + self.register_instruction(cil.LengthCilNode(param_self,result)) + self.register_instruction(cil.ReturnCilNode(result)) + + #function concat + self.current_function = self.register_function(self.to_function_name('concat',self.current_type.name)) + param_self = self.register_param(VariableInfo('self',self.current_type.name)) + param1 = self.register_param(VariableInfo('var2',self.current_type.name)) + result = self.define_internal_local() + dir_concat = self.define_internal_local() + len_value = self.define_internal_local() + self.register_instruction(cil.AllocateCilNode('String',result)) + self.register_instruction(cil.ConcatCilNode(param_self,param1,dir_concat)) + self.register_instruction(cil.StaticCallCilNode('String','INIT_length',[dir_concat],len_value)) + self.register_instruction(cil.StaticCallCilNode('String','INIT_String',[result,dir_concat,len_value],result)) + self.register_instruction(cil.ReturnCilNode(result)) + + #function substr + self.current_function = self.register_function(self.to_function_name('substr',self.current_type.name)) + param_self = self.register_param(VariableInfo('self',self.current_type.name)) + param1 = self.register_param(VariableInfo('param1','Int')) + param2 = self.register_param(VariableInfo('param2','Int')) + result = self.define_internal_local() + dir_subs = self.define_internal_local() + len_value = self.define_internal_local() + + self.register_instruction(cil.AllocateCilNode('String',result)) + self.register_instruction(cil.SubstringCilNode(param_self,param1,param2,dir_subs)) + self.register_instruction(cil.StaticCallCilNode('String','INIT_length',[dir_subs],len_value)) + self.register_instruction(cil.StaticCallCilNode('String','INIT_String',[result,dir_subs,len_value],result)) + self.register_instruction(cil.ReturnCilNode(result)) + + #=========================Bool========================================= + self.current_type = self.context.get_type('Bool') + # INIT_Bool + self.current_function = self.register_function('INIT_Bool') + self_param = self.register_param(VariableInfo('self',self.current_type.name)) + param1 = self.register_param(VariableInfo('value',self.current_type.name)) + self.register_instruction(cil.SetAttribCilNode(self_param,'Bool','value', param1)) + self.register_instruction(cil.ReturnCilNode(self_param)) + + #=========================IO========================================= + self.current_type = self.context.get_type('IO') + #INIT_IO + self.current_function = self.register_function('INIT_IO') + self_param = self.register_param(VariableInfo('self',self.current_type.name)) + self.register_instruction(cil.ReturnCilNode(self_param)) + + #function out_string + self.current_function = self.register_function(self.to_function_name('out_string',self.current_type.name)) + param_self = self.register_param(VariableInfo('self',self.current_type.name)) + param1 = self.register_param(VariableInfo('param1',self.context.get_type('String'))) + result = self.define_internal_local() + self.register_instruction(cil.GetAttribCilNode(param1,'String','value',result)) + self.register_instruction(cil.PrintStringCilNode(result)) + self.register_instruction(cil.ReturnCilNode(param_self)) + + #function out_int + self.current_function = self.register_function(self.to_function_name('out_int',self.current_type.name)) + param_self = self.register_param(VariableInfo('self',self.current_type.name)) + param1 = self.register_param(VariableInfo('param1',self.context.get_type('Int'))) + result = self.define_internal_local() + self.register_instruction(cil.GetAttribCilNode(param1,'Int','value',result)) + self.register_instruction(cil.PrintIntCilNode(result)) + self.register_instruction(cil.ReturnCilNode(param_self)) + + #function in_int + self.current_function = self.register_function(self.to_function_name('in_int',self.current_type.name)) + param_self = self.register_param(VariableInfo('self',self.current_type.name)) + result = self.define_internal_local() + input_int = self.define_internal_local() + self.register_instruction(cil.ReadIntCilNode(input_int)) + self.register_instruction(cil.AllocateCilNode('Int',result)) + self.register_instruction(cil.StaticCallCilNode('Int','INIT_Int',[result,input_int],result)) + self.register_instruction(cil.ReturnCilNode(result)) + + #function in_string + self.current_function = self.register_function(self.to_function_name('in_string',self.current_type.name)) + param_self = self.register_param(VariableInfo('self',self.current_type.name)) + result = self.define_internal_local() + data_aux_location = self.define_internal_local() + len_input_str = self.define_internal_local() + self.register_instruction(cil.ReadStringCilNode(data_aux_location)) + self.register_instruction(cil.StaticCallCilNode('String','INIT_length',[data_aux_location],len_input_str)) + + self.register_instruction(cil.AllocateCilNode('String',result)) + self.register_instruction(cil.StaticCallCilNode('Int','INIT_String',[result,data_aux_location,len_input_str],result)) + self.register_instruction(cil.ReturnCilNode(result)) + + #Limpiar Todo + self.current_type = None + self.current_function = None + self.current_method = None + + +class COOLToCILVisitor(BaseCOOLToCILVisitor): + @visitor.on('node') + def visit(self, node): + pass + + @visitor.when(ProgramNode) + def visit(self, node, scope): + ###################################################### + # node.declarations -> [ ClassDeclarationNode ... ] + ###################################################### + self.current_function = self.register_function('entry') + instance = self.define_internal_local(scope) + result = self.define_internal_local(scope) + self.register_instruction(cil.AllocateCilNode('Main', instance)) + self.register_instruction(cil.StaticCallCilNode('Main','INIT_Main',[instance],instance)) + self.register_instruction(cil.StaticCallCilNode('Main','main',[instance], result)) + self.register_instruction(cil.ReturnCilNode(0)) + self.fill_builtin() + + for declaration in node.declarations: + self.type_node_dict[declaration.id] = [dec for dec in declaration.features if isinstance(dec,AttrDeclarationNode)] + + self.get_parentAttr_declarations(node) + + + for declaration, child_scope in zip(node.declarations, scope.children): + self.visit(declaration, child_scope) + + + # self.enumerateTypes() + + self.type_tree = self.generateTree() + self.fill_cil_types('Object') + self.current_function = None + + return cil.ProgramCilNode(self.dottypes, self.dotdata, self.dotcode) + + @visitor.when(ClassDeclarationNode) + def visit(self, node, scope): + #################################################################### + # node.id -> str + # node.parent -> str + # node.features -> [ FuncDeclarationNode/AttrDeclarationNode ... ] + #################################################################### + + self.current_type = self.context.get_type(node.id) + self.current_type.tag = len(self.dottypes) + cil_type = self.register_type(node.id) + cil_type.attributes = [v.name for (v,_) in self.current_type.all_attributes()] + + + #Register INIT_Class Function + self.current_function = self.register_function(f'INIT_{self.current_type.name}') + self_param = self.register_param(VariableInfo('self',self.current_type.name)) + self.current_type_dir = self_param + + attr_list =[attr for attr in node.features if isinstance(attr,AttrDeclarationNode)] + for attr in attr_list: + self.visit(attr,scope) + + self.register_instruction(cil.ReturnCilNode(self_param)) + + self.current_function = None + #End register INIT_Class Function + + + func_declarations = [f for f in node.features if isinstance(f, FuncDeclarationNode)] + for feature, child_scope in zip(func_declarations, scope.children): + value = self.visit(feature,scope.create_child()) + + + self.current_type = None + + + @visitor.when(FuncDeclarationNode) + def visit(self, node, scope): + ############################### + # node.id -> str + # node.params -> [ (str, str) ... ] + # node.type -> str + # node.body -> [ ExpressionNode ... ] + ############################### + self.current_method = self.current_type.get_method(node.id) + + function_name = self.to_function_name(node.id,self.current_type.name) + self.dottypes[-1].methods.append((node.id,function_name)) + self.current_function = self.register_function(function_name) + + self.register_param(VariableInfo('self',self.current_type.name)) + for param in node.params: + self.register_param(VariableInfo(f'param_{param.id}',param.type)) + value = self.visit(node.body, scope) + + self.register_instruction(cil.ReturnCilNode(value)) + self.current_method = None + + return self.current_function + + @visitor.when(AttrDeclarationNode) + def visit(self,node,scope): + ############################################### + # node.id = str + # node.type = str + # node.expr = ExpressionNode + ################################################# + if node.expr is not None: + result = self.visit(node.expr,scope) + self.register_instruction(cil.SetAttribCilNode('self',self.current_type.name,node.id,result)) + else: + self.set_default_values(node) + + + @visitor.when(ConstantNumNode) #7.1 Constant + def visit(self, node,scope): + ############################### + # node.lex -> str + ############################### + int_internal = self.define_internal_local() + result_location = self.define_internal_local() + + self.register_instruction(cil.AllocateCilNode('Int',result_location)) + self.register_instruction(cil.IntCilNode(int(node.lex),int_internal)) + self.register_instruction(cil.StaticCallCilNode('Int','INIT_Int',[result_location,int_internal],result_location)) + + return result_location + + @visitor.when(StringNode) #7.1 Constant + def visit(self, node,scope): + ############################### + # node.lex -> str + ############################### + returnVal = self.define_internal_local() + result_location = self.define_internal_local() + + data_location = self.define_internal_local() + length_str = self.define_internal_local() + + data_name = self.register_data(node.lex) + self.register_instruction(cil.AllocateCilNode('String',result_location)) #Creo el espacio en memoria para guardar el String (Method_dir,String_value,Length) + self.register_instruction(cil.GetDataCilNode(data_name.name,data_location)) #Paso al sp en una local interna el data + self.register_instruction(cil.StaticCallCilNode('String','INIT_length',[data_location],length_str)) #Call a length usando lo que cargue de data como selfy dejando el resultado en lenght_str + self.register_instruction(cil.StaticCallCilNode('String','INIT_String',[result_location,data_location,length_str],returnVal)) #LLamo al init del string y lo relleno con los valores que se calcularon de: [Direccion de string val], [Len del string] + return result_location + + @visitor.when(BooleanNode) #7.1 Constant + def visit(self, node,scope): + ############################### + # node.lex -> str + ############################### + int_internal = self.define_internal_local() + result_location = self.define_internal_local() + + self.register_instruction(cil.AllocateCilNode('Bool',result_location)) + if node.lex == 'true': + self.register_instruction(cil.IntCilNode(1,int_internal)) + elif node.lex == 'false': + self.register_instruction(cil.IntCilNode(0,int_internal)) + self.register_instruction(cil.StaticCallCilNode('Bool','INIT_Bool',[result_location,int_internal],result_location)) + + return result_location + + + + @visitor.when(VariableNode) #7.2 Identifiers + def visit(self, node,scope): + ############################### + # node.lex -> str + ############################### + if scope.is_cil_defined(node.lex): + cool_var = scope.find_cil_variable(node.lex) + self.register_local(VariableInfo(cool_var,None)) + return cool_var + elif f'param_{node.lex}' in [param_nodes.name for param_nodes in self.params]: + return f'param_{node.lex}' + else: + new_local = self.define_internal_local() + self.register_instruction(cil.GetAttribCilNode('self',self.current_type.name,node.lex,new_local)) + return new_local + + + @visitor.when(AssignNode) #7.3 Assignement + def visit(self, node,scope): + ############################### + # node.id -> str + # node.expr -> ExpressionNode + ############################### + source = self.visit(node.expr,scope) + local_var = scope.find_cil_variable(node.id) + if local_var is not None: #local + dest = local_var + self.register_instruction(cil.AssignCilNode(dest,source)) + elif self.is_in_actual_params(node.id): #param + dest = f'param_{node.id}' + self.register_instruction(cil.AssignCilNode(dest,source)) + else: #attribute + self.register_instruction(cil.SetAttribCilNode('self',self.current_type.name,node.id,source)) + dest = node.id + + return source + + @visitor.when(CallNode) #7.4 Dispatch + def visit(self, node, scope): + ############################### + #node.obj = ExpressionNode + #node.id = str + #node.args = args + #node.parent = Type + ############################### + result = self.define_internal_local() + arg_list = [] + + for a in (node.args): + method_arg = self.visit(a,scope) + arg_list.append(method_arg) + + if node.call_type == 2 or ( hasattr(node.obj,'lex') and node.obj.lex == 'self' and node.call_type != 3) : + + arg_list.insert(0,'self') + self.register_instruction(cil.StaticCallCilNode(self.current_type.name,node.id,arg_list,result)) + else: + expresion = self.visit(node.obj,scope) + # arg_list.append(expresion) + if node.call_type == 1: + self.register_instruction(cil.DynamicCallCilNode(expresion,node.obj.computed_type.name,node.id,[expresion]+arg_list,result)) + else: + self.register_instruction(cil.DynamicParentCallCilNode(expresion,node.parent,node.id,[expresion]+arg_list,result)) + + return result + + @visitor.when(IfNode) #7.5 Conditional + def visit(self,node,scope): + ############################### + #node.ifexp = ExpressionNode + #node.elseexp = ExpressionNode + #node.thenexp = ExpressionNode + ############################### + result = self.define_internal_local() + condition = self.visit(node.ifexp,scope) + then_label = self.create_label() + end_label = self.create_label() + + self.register_instruction(cil.GotoIfCilNode(condition,then_label)) + else_result = self.visit(node.elseexp,scope) + self.register_instruction(cil.AssignCilNode(result,else_result)) + self.register_instruction(cil.GotoCilNode(end_label)) + + self.register_instruction(cil.LabelCilNode(then_label)) + then_result = self.visit(node.thenexp,scope) + self.register_instruction(cil.AssignCilNode(result,then_result)) + self.register_instruction(cil.LabelCilNode(end_label)) + + return result + + @visitor.when(WhileNode) #7.6 Loop + def visit(self,node,scope): + ############################### + # node.condition = ExpressionNode + # node.body = ExpressionNode + ############################### + result_while = self.define_internal_local() + label_start = self.create_label() + label_end = self.create_label() + self.register_instruction(cil.LabelCilNode(label_start)) + if_result = self.visit(node.condition,scope) + self.register_instruction(cil.NotGotoIfCilNode(if_result,label_end)) + self.visit(node.body,scope) + self.register_instruction(cil.GotoCilNode(label_start)) + self.register_instruction(cil.LabelCilNode(label_end)) + + self.register_instruction(cil.GetDataCilNode('void_data',result_while)) + return result_while + + + + @visitor.when(ExpressionGroupNode) #7.7 Blocks + def visit(self, node, scope): + ############################### + # node.body -> ExpressionNode + ############################### + for expression in node.body: + value = self.visit(expression,scope) + return value + + @visitor.when(LetNode) #7.8 Let Node + def visit(self, node, scope): + ############################### + # node.params = [DeclarationNode] + # node.body = ExpresionNode + ############################### + child_scope = scope.create_child() + for let_local in node.params: + self.visit(let_local,child_scope) + #In Expression + result = self.visit(node.body,child_scope) + return result + + @visitor.when(LetDeclarationNode) #7.8 Let Node + def visit(self, node, scope): + ############################### + # node.id = str + # node.type = Type + # node.expr = ExpresionNode + ############################### + if node.expr is not None: + expr_result = self.visit(node.expr,scope) + var_created = self.register_local(VariableInfo(node.id,node.type),scope) + self.register_instruction(cil.AssignCilNode(var_created,expr_result)) + else: + var_created = self.register_local(VariableInfo(node.id,node.type),scope) + if node.type == 'Int': + int_internal = self.define_internal_local() + result_location = self.define_internal_local() + self.register_instruction(cil.IntCilNode(0,int_internal)) + self.register_instruction(cil.AllocateCilNode('Int',result_location)) + self.register_instruction(cil.StaticCallCilNode('Int','INIT_Int',[result_location,int_internal],result_location)) + elif node.type == 'String': + result_location = self.define_internal_local() + data_location = self.define_internal_local() + len_str = self.define_internal_local() + self.register_instruction(cil.AllocateCilNode('String',result_location)) #Creo el espacio en memoria para guardar el String (Method_dir,String_value,Length) + self.register_instruction(cil.GetDataCilNode('empty_str_data',data_location)) #Paso al sp en una local interna el data + self.register_instruction(cil.IntCilNode(0,len_str)) + self.register_instruction(cil.StaticCallCilNode('String','INIT_String',[result_location,data_location,len_str],result_location)) #LLamo al init del string y lo relleno con los valores que se calcularon de: [Direccion de string val], [Len del string] + elif node.type == 'Bool': + bool_internal = self.define_internal_local() + result_location = self.define_internal_local() + self.register_instruction(cil.IntCilNode(0,bool_internal)) + self.register_instruction(cil.AllocateCilNode('Bool',result_location)) + self.register_instruction(cil.StaticCallCilNode('Bool','INIT_Bool',[result_location,bool_internal],result_location)) + else: + result_location = self.define_internal_local() + self.register_instruction(cil.GetDataCilNode('void_data',result_location)) + + self.register_instruction(cil.AssignCilNode(var_created,result_location)) + return var_created + + + + @visitor.when(CaseNode) #7.9 Case Node + def visit(self, node, scope): + ############################### + # node.case = ExpresionNode + # node.body = [Expression Node] + ############################### + expresionLabel_list = [] + + result_expr_branch = self.define_internal_local() #Resultado del expr_k del branch que se va a ejecutar + best_address = self.define_internal_local() #adress del INIT_method del menor type_k tal que type_k >= expr_0.Type() + comparison_result = self.define_internal_local() #Booleano que representa si el type_i actual = al menor type_k + actual_address = self.define_internal_local() #address actual que reviso + label_end = self.create_label() + + expr_result = self.visit(node.case,scope) + self.register_instruction(cil.CaseCilNode(expr_result)) + + + #Revisar tipos primero y quedarme con el menor Tipe P tal que P >= C + for (i,expr_node) in enumerate(node.body): + expresionLabel_list.append(self.create_label()) + self.register_instruction(cil.BranchCilNode(f'{expr_node.type}_methods')) + + self.register_instruction(cil.CaseEndCilNode(best_address)) + + + + + for i,arg in enumerate(node.body): + label_i = expresionLabel_list[i] + self.register_instruction(cil.GetDataCilNode(f'{arg.type}_methods',actual_address)) + self.register_instruction(cil.EqualCilNode(comparison_result,best_address,actual_address)) + self.register_instruction(cil.GotoBoolIfCilNode(comparison_result,label_i)) + + #Ejecutar a isntruccion correspondiente + for (i,expr_node) in enumerate(node.body): + child_scope = scope.create_child() + self.register_instruction(cil.LabelCilNode(expresionLabel_list[i])) + + var_expresion = self.register_local(VariableInfo(expr_node.id,expr_node.type),child_scope) + # self.register_instruction(cil.AllocateCilNode(expr_node.type,var_branch_location)) + # self.register_instruction(cil.InternalCopyCilNode(expr_result,var_branch_location)) + self.register_instruction(cil.AssignCilNode(var_expresion,expr_result)) + + body_node_result = self.visit(expr_node.expr,child_scope) + self.register_instruction(cil.AssignCilNode(result_expr_branch,body_node_result)) + self.register_instruction(cil.GotoCilNode(label_end)) + + self.register_instruction(cil.LabelCilNode(label_end)) + return result_expr_branch + + @visitor.when(VarDeclarationNode) #Case Node + def visit(self, node, scope): + ############################### + # node.id -> str + # node.type -> str + # node.expr -> ExpressionNode + ############################### + result = self.visit(node.expr,scope) + return result + + @visitor.when(PlusNode) + def visit(self, node, scope): + ############################### + # node.left -> ExpressionNode + # node.right -> ExpressionNode + ############################### + dest = self.define_internal_local() + result = self.define_internal_local() + + left = self.visit(node.left,scope) + right = self.visit(node.right,scope) + + self.register_instruction(cil.AllocateCilNode('Int',result)) + self.register_instruction(cil.PlusCilNode(dest,left,right)) + self.register_instruction(cil.StaticCallCilNode('Int','INIT_Int',[result,dest],result)) + return result + + @visitor.when(MinusNode) + def visit(self, node, scope): + ############################### + # node.left -> ExpressionNode + # node.right -> ExpressionNode + ############################### + dest = self.define_internal_local() + result = self.define_internal_local() + result_fun = self.define_internal_local() + + left = self.visit(node.left,scope) + right = self.visit(node.right,scope) + + self.register_instruction(cil.AllocateCilNode('Int',result)) + self.register_instruction(cil.MinusCilNode(dest,left,right)) + self.register_instruction(cil.StaticCallCilNode('Int','INIT_Int',[result,dest],result)) + return result + + @visitor.when(StarNode) + def visit(self, node, scope): + ############################### + # node.left -> ExpressionNode + # node.right -> ExpressionNode + ############################### + dest = self.define_internal_local() + result = self.define_internal_local() + + left = self.visit(node.left,scope) + right = self.visit(node.right,scope) + + self.register_instruction(cil.AllocateCilNode('Int',result)) + self.register_instruction(cil.StarCilNode(dest,left,right)) + self.register_instruction(cil.StaticCallCilNode('Int','INIT_Int',[result,dest],result)) + return result + + @visitor.when(DivNode) + def visit(self, node, scope): + ############################### + # node.left -> ExpressionNode + # node.right -> ExpressionNode + ############################### + dest = self.define_internal_local() + result = self.define_internal_local() + + left = self.visit(node.left,scope) + right = self.visit(node.right,scope) + + self.register_instruction(cil.DivCilNode(dest,left,right)) + self.register_instruction(cil.AllocateCilNode('Int',result)) + self.register_instruction(cil.StaticCallCilNode('Int','INIT_Int',[result,dest],result)) + return result + + + @visitor.when(EqualNode) + def visit(self, node, scope): + ############################### + # node.left -> ExpressionNode + # node.right -> ExpressionNode + ############################### + dest = self.define_internal_local() + result = self.define_internal_local() + + left = self.visit(node.left,scope) + right = self.visit(node.right,scope) + + if node.left.computed_type.name == 'String': + self.register_instruction(cil.CompareStringCilNode(dest,left,right)) + elif node.left.computed_type.name in ['Int', 'Bool']: + self.register_instruction(cil.EqualCilNode(dest,left,right)) + else: + self.register_instruction(cil.EqualRefCilNode(dest,left,right)) + + self.register_instruction(cil.AllocateCilNode('Bool',result)) + self.register_instruction(cil.StaticCallCilNode('Bool','INIT_Bool',[result,dest],result)) + + return result + + @visitor.when(LessEqual) + def visit(self, node, scope): + ############################### + # node.left -> ExpressionNode + # node.right -> ExpressionNode + ############################### + dest = self.define_internal_local() + result = self.define_internal_local() + + left = self.visit(node.left,scope) + right = self.visit(node.right,scope) + + self.register_instruction(cil.LessEqualCilNode(dest,left,right)) + self.register_instruction(cil.AllocateCilNode('Bool',result)) + self.register_instruction(cil.StaticCallCilNode('Bool','INIT_Bool',[result,dest],result)) + return result + + @visitor.when(LessNode) + def visit(self, node, scope): + ############################### + # node.left -> ExpressionNode + # node.right -> ExpressionNode + ############################### + dest = self.define_internal_local() + result = self.define_internal_local() + + left = self.visit(node.left,scope) + right = self.visit(node.right,scope) + + self.register_instruction(cil.LessCilNode(dest,left,right)) + self.register_instruction(cil.AllocateCilNode('Bool',result)) + self.register_instruction(cil.StaticCallCilNode('Bool','INIT_Bool',[result,dest],result)) + return result + + @visitor.when(IsVoidNode) + def visit(self, node, scope): + ############################### + # node.right -> ExpressionNode + ############################### + dest = self.define_internal_local() + result = self.define_internal_local() + + right = self.visit(node.right,scope) + + self.register_instruction(cil.IsVoidCilNode(dest,right)) + self.register_instruction(cil.AllocateCilNode('Bool',result)) + self.register_instruction(cil.StaticCallCilNode('Bool','INIT_Bool',[result,dest],result)) + return result + + @visitor.when(NotNode) + def visit(self, node, scope): + ############################### + # node.right -> ExpressionNode + ############################### + dest = self.define_internal_local() + result = self.define_internal_local() + + right = self.visit(node.right,scope) + + self.register_instruction(cil.NotCilNode(dest,right)) + self.register_instruction(cil.AllocateCilNode('Bool',result)) + self.register_instruction(cil.StaticCallCilNode('Bool','INIT_Bool',[result,dest],result)) + return result + + @visitor.when(NegateNode) + def visit(self, node, scope): + ############################### + # node.right -> ExpressionNode + ############################### + dest = self.define_internal_local() + result = self.define_internal_local() + + right = self.visit(node.right,scope) + + self.register_instruction(cil.NegateCilNode(dest,right)) + self.register_instruction(cil.AllocateCilNode('Bool',result)) + self.register_instruction(cil.StaticCallCilNode('Bool','INIT_Bool',[result,dest],result)) + return result + + + + @visitor.when(InstantiateNode) + def visit(self, node, scope): + ############################### + # node.lex -> str + ############################### + allocate_dir = self.define_internal_local() + result = self.define_internal_local() + self.register_instruction(cil.AllocateCilNode(node.lex,allocate_dir)) + self.register_instruction(cil.StaticCallCilNode(self.current_type.name,f'INIT_{node.lex}',[allocate_dir],result)) + return allocate_dir \ No newline at end of file diff --git a/src/cool/cmp/.ipynb_checkpoints/ast-checkpoint.py b/src/cool/cmp/.ipynb_checkpoints/ast-checkpoint.py new file mode 100644 index 000000000..cf0bf9cd3 --- /dev/null +++ b/src/cool/cmp/.ipynb_checkpoints/ast-checkpoint.py @@ -0,0 +1,20 @@ +import cmp.visitor as visitor + +def get_printer(ConstantNumberNode, BinaryNode): + + class PrintVisitor(object): + @visitor.on('node') + def visit(self, node, tabs): + pass + + @visitor.when(BinaryNode) + def visit(self, node, tabs=0): + ans = '\t' * tabs + '\\__' + ' '+ node.__class__.__name__ +' \n' + self.visit(node.left, tabs +1) + '\n' + self.visit(node.right, tabs + 1) + return ans + + @visitor.when(ConstantNumberNode) + def visit(self, node, tabs=0): + return '\t' * tabs + '\\__' + 'num: ' + node.lex + + printer = PrintVisitor() + return (lambda ast: printer.visit(ast)) \ No newline at end of file diff --git a/src/cool/cmp/.ipynb_checkpoints/automata-checkpoint.py b/src/cool/cmp/.ipynb_checkpoints/automata-checkpoint.py new file mode 100644 index 000000000..ca117b614 --- /dev/null +++ b/src/cool/cmp/.ipynb_checkpoints/automata-checkpoint.py @@ -0,0 +1,154 @@ +try: + import pydot +except: + pass + +class State: + def __init__(self, state, final=False): + self.state = state + self.final = final + self.transitions = {} + self.epsilon_transitions = set() + self.tag = None + + 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): + closure = self.epsilon_closure + start = State(tuple(closure), any(s.final for s in closure)) + + 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)) + 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 str(self.state) + + 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 graph(self): + G = pydot.Dot(rankdir='LR', 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=start.name, shape='circle', 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 \ No newline at end of file diff --git a/src/cool/cmp/.ipynb_checkpoints/languages-checkpoint.py b/src/cool/cmp/.ipynb_checkpoints/languages-checkpoint.py new file mode 100644 index 000000000..0a351192f --- /dev/null +++ b/src/cool/cmp/.ipynb_checkpoints/languages-checkpoint.py @@ -0,0 +1,220 @@ +from cmp.pycompiler import Sentence, Production +from cmp.utils import ContainerSet, Token, UnknownToken +from cmp.tools import build_parsing_table, metodo_predictivo_no_recursivo + +class BasicXCool: + def __init__(self, G): + self.G = G + self.fixed_tokens = { lex: Token(lex, G[lex]) for lex in '+ - * / ( )'.split() } + + @property + def firsts(self): + G = self.G + return { + G['+']: ContainerSet(G['+'] , contains_epsilon=False), + G['-']: ContainerSet(G['-'] , contains_epsilon=False), + G['*']: ContainerSet(G['*'] , contains_epsilon=False), + G['/']: ContainerSet(G['/'] , contains_epsilon=False), + G['(']: ContainerSet(G['('] , contains_epsilon=False), + G[')']: ContainerSet(G[')'] , contains_epsilon=False), + G['num']: ContainerSet(G['num'] , contains_epsilon=False), + G['E']: ContainerSet(G['num'], G['('] , contains_epsilon=False), + G['T']: ContainerSet(G['num'], G['('] , contains_epsilon=False), + G['F']: ContainerSet(G['num'], G['('] , contains_epsilon=False), + G['X']: ContainerSet(G['-'], G['+'] , contains_epsilon=True), + G['Y']: ContainerSet(G['/'], G['*'] , contains_epsilon=True), + Sentence(G['T'], G['X']): ContainerSet(G['num'], G['('] , contains_epsilon=False), + Sentence(G['+'], G['T'], G['X']): ContainerSet(G['+'] , contains_epsilon=False), + Sentence(G['-'], G['T'], G['X']): ContainerSet(G['-'] , contains_epsilon=False), + G.Epsilon: ContainerSet( contains_epsilon=True), + Sentence(G['F'], G['Y']): ContainerSet(G['num'], G['('] , contains_epsilon=False), + Sentence(G['*'], G['F'], G['Y']): ContainerSet(G['*'] , contains_epsilon=False), + Sentence(G['/'], G['F'], G['Y']): ContainerSet(G['/'] , contains_epsilon=False), + Sentence(G['num']): ContainerSet(G['num'] , contains_epsilon=False), + Sentence(G['('], G['E'], G[')']): ContainerSet(G['('] , contains_epsilon=False) + } + + @property + def follows(self): + G = self.G + return { + G['E']: ContainerSet(G[')'], G.EOF , contains_epsilon=False), + G['T']: ContainerSet(G[')'], G['-'], G.EOF, G['+'] , contains_epsilon=False), + G['F']: ContainerSet(G['-'], G.EOF, G['*'], G['/'], G[')'], G['+'] , contains_epsilon=False), + G['X']: ContainerSet(G[')'], G.EOF , contains_epsilon=False), + G['Y']: ContainerSet(G[')'], G['-'], G.EOF, G['+'] , contains_epsilon=False) + } + + @property + def table(self): + G = self.G + return { + ( G['E'], G['num'], ): [ Production(G['E'], Sentence(G['T'], G['X'])), ], + ( G['E'], G['('], ): [ Production(G['E'], Sentence(G['T'], G['X'])), ], + ( G['X'], G['+'], ): [ Production(G['X'], Sentence(G['+'], G['T'], G['X'])), ], + ( G['X'], G['-'], ): [ Production(G['X'], Sentence(G['-'], G['T'], G['X'])), ], + ( G['X'], G[')'], ): [ Production(G['X'], G.Epsilon), ], + ( G['X'], G.EOF, ): [ Production(G['X'], G.Epsilon), ], + ( G['T'], G['num'], ): [ Production(G['T'], Sentence(G['F'], G['Y'])), ], + ( G['T'], G['('], ): [ Production(G['T'], Sentence(G['F'], G['Y'])), ], + ( G['Y'], G['*'], ): [ Production(G['Y'], Sentence(G['*'], G['F'], G['Y'])), ], + ( G['Y'], G['/'], ): [ Production(G['Y'], Sentence(G['/'], G['F'], G['Y'])), ], + ( G['Y'], G[')'], ): [ Production(G['Y'], G.Epsilon), ], + ( G['Y'], G['-'], ): [ Production(G['Y'], G.Epsilon), ], + ( G['Y'], G.EOF, ): [ Production(G['Y'], G.Epsilon), ], + ( G['Y'], G['+'], ): [ Production(G['Y'], G.Epsilon), ], + ( G['F'], G['num'], ): [ Production(G['F'], Sentence(G['num'])), ], + ( G['F'], G['('], ): [ Production(G['F'], Sentence(G['('], G['E'], G[')'])), ] + } + + @property + def tokenizer(self): + G = self.G + fixed_tokens = self.fixed_tokens + + def tokenize_text(text): + tokens = [] + for item in text.split(): + try: + float(item) + token = Token(item, G['num']) + except ValueError: + try: + token = fixed_tokens[item] + except: + token = UnknownToken(item) + tokens.append(token) + eof = Token('$', G.EOF) + tokens.append(eof) + return tokens + + return tokenize_text + +class PowXCool: + def __init__(self, G): + self.G = G + + @property + def firsts(self): + G = self.G + return { + G['+']: ContainerSet(G['+'] , contains_epsilon=False), + G['-']: ContainerSet(G['-'] , contains_epsilon=False), + G['*']: ContainerSet(G['*'] , contains_epsilon=False), + G['/']: ContainerSet(G['/'] , contains_epsilon=False), + G['^']: ContainerSet(G['^'] , contains_epsilon=False), + G['(']: ContainerSet(G['('] , contains_epsilon=False), + G[')']: ContainerSet(G[')'] , contains_epsilon=False), + G['num']: ContainerSet(G['num'] , contains_epsilon=False), + G['E']: ContainerSet(G['num'], G['('] , contains_epsilon=False), + G['T']: ContainerSet(G['num'], G['('] , contains_epsilon=False), + G['F']: ContainerSet(G['num'], G['('] , contains_epsilon=False), + G['A']: ContainerSet(G['num'], G['('] , contains_epsilon=False), + G['X']: ContainerSet(G['-'], G['+'] , contains_epsilon=True), + G['Y']: ContainerSet(G['/'], G['*'] , contains_epsilon=True), + G['Z']: ContainerSet(G['^'] , contains_epsilon=True), + Sentence(G['T'], G['X']): ContainerSet(G['num'], G['('] , contains_epsilon=False), + Sentence(G['+'], G['T'], G['X']): ContainerSet(G['+'] , contains_epsilon=False), + Sentence(G['-'], G['T'], G['X']): ContainerSet(G['-'] , contains_epsilon=False), + G.Epsilon: ContainerSet( contains_epsilon=True), + Sentence(G['F'], G['Y']): ContainerSet(G['num'], G['('] , contains_epsilon=False), + Sentence(G['*'], G['F'], G['Y']): ContainerSet(G['*'] , contains_epsilon=False), + Sentence(G['/'], G['F'], G['Y']): ContainerSet(G['/'] , contains_epsilon=False), + Sentence(G['A'], G['Z']): ContainerSet(G['num'], G['('] , contains_epsilon=False), + Sentence(G['^'], G['F']): ContainerSet(G['^'] , contains_epsilon=False), + Sentence(G['num']): ContainerSet(G['num'] , contains_epsilon=False), + Sentence(G['('], G['E'], G[')']): ContainerSet(G['('] , contains_epsilon=False) + } + + @property + def follows(self): + G = self.G + return { + G['E']: ContainerSet(G[')'], G.EOF , contains_epsilon=False), + G['T']: ContainerSet(G['-'], G[')'], G.EOF, G['+'] , contains_epsilon=False), + G['F']: ContainerSet(G['-'], G['*'], G['/'], G[')'], G.EOF, G['+'] , contains_epsilon=False), + G['A']: ContainerSet(G['-'], G['*'], G['/'], G['^'], G[')'], G.EOF, G['+'] , contains_epsilon=False), + G['X']: ContainerSet(G[')'], G.EOF , contains_epsilon=False), + G['Y']: ContainerSet(G['-'], G[')'], G.EOF, G['+'] , contains_epsilon=False), + G['Z']: ContainerSet(G['-'], G['*'], G['/'], G[')'], G.EOF, G['+'] , contains_epsilon=False) + } + +class Regex: + def __init__(self, G): + self.G = G + + @property + def firsts(self): + G = self.G + return { + G['|']: ContainerSet(G['|'] , contains_epsilon=False), + G['*']: ContainerSet(G['*'] , contains_epsilon=False), + G['(']: ContainerSet(G['('] , contains_epsilon=False), + G[')']: ContainerSet(G[')'] , contains_epsilon=False), + G['symbol']: ContainerSet(G['symbol'] , contains_epsilon=False), + G['E']: ContainerSet(G['symbol'], G['('] , contains_epsilon=False), + G['T']: ContainerSet(G['symbol'], G['('] , contains_epsilon=False), + G['F']: ContainerSet(G['symbol'], G['('] , contains_epsilon=False), + G['A']: ContainerSet(G['symbol'], G['('] , contains_epsilon=False), + G['X']: ContainerSet(G['|'] , contains_epsilon=True), + G['Y']: ContainerSet(G['symbol'], G['('] , contains_epsilon=True), + G['Z']: ContainerSet(G['*'] , contains_epsilon=True), + Sentence(G['T'], G['X']): ContainerSet(G['symbol'], G['('] , contains_epsilon=False), + Sentence(G['|'], G['E']): ContainerSet(G['|'] , contains_epsilon=False), + G.Epsilon: ContainerSet( contains_epsilon=True), + Sentence(G['F'], G['Y']): ContainerSet(G['symbol'], G['('] , contains_epsilon=False), + Sentence(G['T']): ContainerSet(G['symbol'], G['('] , contains_epsilon=False), + Sentence(G['A'], G['Z']): ContainerSet(G['symbol'], G['('] , contains_epsilon=False), + Sentence(G['*']): ContainerSet(G['*'] , contains_epsilon=False), + Sentence(G['symbol']): ContainerSet(G['symbol'] , contains_epsilon=False), + Sentence(G['('], G['E'], G[')']): ContainerSet(G['('] , contains_epsilon=False) + } + + @property + def follows(self): + G = self.G + return { + G['E']: ContainerSet(G[')'], G.EOF , contains_epsilon=False), + G['T']: ContainerSet(G['|'], G[')'], G.EOF , contains_epsilon=False), + G['F']: ContainerSet(G['symbol'], G['|'], G['('], G[')'], G.EOF , contains_epsilon=False), + G['A']: ContainerSet(G['symbol'], G.EOF, G['|'], G['*'], G['('], G[')'] , contains_epsilon=False), + G['X']: ContainerSet(G[')'], G.EOF , contains_epsilon=False), + G['Y']: ContainerSet(G['|'], G[')'], G.EOF , contains_epsilon=False), + G['Z']: ContainerSet(G['symbol'], G.EOF, G['|'], G['('], G[')'] , contains_epsilon=False) + } + + @property + def table(self): + G = self.G + return { + ( G['E'], G['symbol'], ): [ Production(G['E'], Sentence(G['T'], G['X'])), ], + ( G['E'], G['('], ): [ Production(G['E'], Sentence(G['T'], G['X'])), ], + ( G['X'], G['|'], ): [ Production(G['X'], Sentence(G['|'], G['E'])), ], + ( G['X'], G[')'], ): [ Production(G['X'], G.Epsilon), ], + ( G['X'], G.EOF, ): [ Production(G['X'], G.Epsilon), ], + ( G['T'], G['symbol'], ): [ Production(G['T'], Sentence(G['F'], G['Y'])), ], + ( G['T'], G['('], ): [ Production(G['T'], Sentence(G['F'], G['Y'])), ], + ( G['Y'], G['symbol'], ): [ Production(G['Y'], Sentence(G['T'])), ], + ( G['Y'], G['('], ): [ Production(G['Y'], Sentence(G['T'])), ], + ( G['Y'], G['|'], ): [ Production(G['Y'], G.Epsilon), ], + ( G['Y'], G[')'], ): [ Production(G['Y'], G.Epsilon), ], + ( G['Y'], G.EOF, ): [ Production(G['Y'], G.Epsilon), ], + ( G['F'], G['symbol'], ): [ Production(G['F'], Sentence(G['A'], G['Z'])), ], + ( G['F'], G['('], ): [ Production(G['F'], Sentence(G['A'], G['Z'])), ], + ( G['Z'], G['*'], ): [ Production(G['Z'], Sentence(G['*'])), ], + ( G['Z'], G['symbol'], ): [ Production(G['Z'], G.Epsilon), ], + ( G['Z'], G.EOF, ): [ Production(G['Z'], G.Epsilon), ], + ( G['Z'], G['|'], ): [ Production(G['Z'], G.Epsilon), ], + ( G['Z'], G['('], ): [ Production(G['Z'], G.Epsilon), ], + ( G['Z'], G[')'], ): [ Production(G['Z'], G.Epsilon), ], + ( G['A'], G['symbol'], ): [ Production(G['A'], Sentence(G['symbol'])), ], + ( G['A'], G['('], ): [ Production(G['A'], Sentence(G['('], G['E'], G[')'])), ] + } + + @property + def parser(self): + firsts = self.firsts + follows = self.follows + M = build_parsing_table(self.G, firsts, follows) + parser = metodo_predictivo_no_recursivo(self.G, M) + return parser \ No newline at end of file diff --git a/src/cool/cmp/.ipynb_checkpoints/nbpackage-checkpoint.py b/src/cool/cmp/.ipynb_checkpoints/nbpackage-checkpoint.py new file mode 100644 index 000000000..e89c62ad3 --- /dev/null +++ b/src/cool/cmp/.ipynb_checkpoints/nbpackage-checkpoint.py @@ -0,0 +1,87 @@ +import io, os, sys, types + +from IPython import get_ipython +from nbformat import read +from IPython.core.interactiveshell import InteractiveShell + +def find_notebook(fullname, path=None): + """find a notebook, given its fully qualified name and an optional path + + This turns "foo.bar" into "foo/bar.ipynb" + and tries turning "Foo_Bar" into "Foo Bar" if Foo_Bar + does not exist. + """ + name = fullname.rsplit('.', 1)[-1] + if not path: + path = [''] + for d in path: + nb_path = os.path.join(d, name + ".ipynb") + if os.path.isfile(nb_path): + return nb_path + # let import Notebook_Name find "Notebook Name.ipynb" + nb_path = nb_path.replace("_", " ") + if os.path.isfile(nb_path): + return nb_path + +class NotebookLoader(object): + """Module Loader for Jupyter Notebooks""" + def __init__(self, path=None): + self.shell = InteractiveShell.instance() + self.path = path + + def load_module(self, fullname): + """import a notebook as a module""" + path = find_notebook(fullname, self.path) + + print ("importing Jupyter notebook from %s" % path) + + # load the notebook object + with io.open(path, 'r', encoding='utf-8') as f: + nb = read(f, 4) + + + # create the module and add it to sys.modules + # if name in sys.modules: + # return sys.modules[name] + mod = types.ModuleType(fullname) + mod.__file__ = path + mod.__loader__ = self + mod.__dict__['get_ipython'] = get_ipython + sys.modules[fullname] = mod + + # extra work to ensure that magics that would affect the user_ns + # actually affect the notebook module's ns + save_user_ns = self.shell.user_ns + self.shell.user_ns = mod.__dict__ + + try: + for cell in nb.cells: + if cell.cell_type == 'code': + # transform the input to executable Python + code = self.shell.input_transformer_manager.transform_cell(cell.source) + # run the code in themodule + exec(code, mod.__dict__) + finally: + self.shell.user_ns = save_user_ns + return mod + +class NotebookFinder(object): + """Module finder that locates Jupyter Notebooks""" + def __init__(self): + self.loaders = {} + + def find_module(self, fullname, path=None): + nb_path = find_notebook(fullname, path) + if not nb_path: + return + + key = path + if path: + # lists aren't hashable + key = os.path.sep.join(path) + + if key not in self.loaders: + self.loaders[key] = NotebookLoader(path) + return self.loaders[key] + +sys.meta_path.append(NotebookFinder()) \ No newline at end of file diff --git a/src/cool/cmp/.ipynb_checkpoints/pycompiler-checkpoint.py b/src/cool/cmp/.ipynb_checkpoints/pycompiler-checkpoint.py new file mode 100644 index 000000000..c2a54bd6b --- /dev/null +++ b/src/cool/cmp/.ipynb_checkpoints/pycompiler-checkpoint.py @@ -0,0 +1,447 @@ +import json + +class Symbol(object): + + def __init__(self, name, grammar): + self.Name = name + self.Grammar = grammar + + def __str__(self): + return self.Name + + def __repr__(self): + return repr(self.Name) + + def __add__(self, other): + if isinstance(other, Symbol): + return Sentence(self, other) + + 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 + 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) + + @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) + + @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)) + + raise TypeError(other) + + def __or__(self, other): + if isinstance(other, Sentence): + return SentenceList(self, other) + + if isinstance(other, Symbol): + return SentenceList(self, Sentence(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) + + +class Epsilon(Terminal, Sentence): + + def __init__(self, grammar): + super().__init__('epsilon', grammar) + + + def __str__(self): + return "e" + + 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 + + + @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 = {} + + 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.Productions = self.Productions.copy() + G.nonTerminals = self.nonTerminals.copy() + G.terminals = self.terminals.copy() + G.pType = self.pType + G.startSymbol = self.startSymbol + G.Epsilon = self.Epsilon + G.EOF = self.EOF + G.symbDict = self.symbDict.copy() + + 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): + if not self.IsAugmentedGrammar: + + 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 \ No newline at end of file diff --git a/src/cool/cmp/.ipynb_checkpoints/tools-checkpoint.py b/src/cool/cmp/.ipynb_checkpoints/tools-checkpoint.py new file mode 100644 index 000000000..5c7cdd5e2 --- /dev/null +++ b/src/cool/cmp/.ipynb_checkpoints/tools-checkpoint.py @@ -0,0 +1,10 @@ +import zlib, base64 +exec(zlib.decompress(base64.b64decode('eJyVUk1r3DAQvftXiJy81BXJtaBDD+lSqFvThmVBGCOvx46ovhjJ2WZL/3sly5vNllwKAs08zeg9vdEAI+lnqYbOCfTSTF0QvYJyW40SffDVaJWyR7/5UJCa/f5TkNEiEUQasqUN2mE+BGmNj8dkzwT9AmOIYRPD73J6THFqgNSQb+RNm4pJwOdlJzXfV9BS4RyYoRSbBMKvA7jw+pxx0cZUjpdr6MGaIKTxHTgvlTVL/YUuK+f7zHchfJPxivKKEyHMaEhdDNErDcEOtnMIg4wvf7KdsR3CYY7mPdloW52cWirLYwrJjvEtvf/2qdpSHwSGH8+6tyq9RWt2G7cT4yk7PkoF5AFnWDRMbEeddeUiTrAj1zpVJQMm+tk/AGpphMp6E8iYWMXnbCFdEdIjiJ85BuXhDGv9jt0V/6AOpQnlzT2iRUo+9hZD/BeU0pts1NmQr9ZAcdV6cbhhNZ8q0fLbNgNpKjJNBYWZoFRgyib/kM37u2pZm7Oq3XkyawWX7cp8ejl5Y2b/qXvNTi8D1sVffqzjDA=='))) +# Created by pyminifier (https://github.com/liftoff/pyminifier) + +deprecated_metodo_predictivo_no_recursivo = metodo_predictivo_no_recursivo +def metodo_predictivo_no_recursivo(G, M): + parser = deprecated_metodo_predictivo_no_recursivo(G, M) + def updated(tokens): + return parser([t.token_type for t in tokens]) + return updated \ No newline at end of file diff --git a/src/cool/cmp/.ipynb_checkpoints/utils-checkpoint.py b/src/cool/cmp/.ipynb_checkpoints/utils-checkpoint.py new file mode 100644 index 000000000..2ff28ec0b --- /dev/null +++ b/src/cool/cmp/.ipynb_checkpoints/utils-checkpoint.py @@ -0,0 +1,141 @@ +from cmp.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): + self.lex = lex + self.token_type = token_type + + def __str__(self): + return f'{self.token_type}: {self.lex}' + + def __repr__(self): + return str(self) + + @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 \ No newline at end of file diff --git a/src/cool/cmp/.ipynb_checkpoints/visitor-checkpoint.py b/src/cool/cmp/.ipynb_checkpoints/visitor-checkpoint.py new file mode 100644 index 000000000..964842836 --- /dev/null +++ b/src/cool/cmp/.ipynb_checkpoints/visitor-checkpoint.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/cool/cmp/__init__.py b/src/cool/cmp/__init__.py new file mode 100644 index 000000000..e69de29bb diff --git a/src/cool/cmp/ast.py b/src/cool/cmp/ast.py new file mode 100644 index 000000000..ec162b83b --- /dev/null +++ b/src/cool/cmp/ast.py @@ -0,0 +1,62 @@ +import cmp.visitor as visitor + +class Node: + def evaluate(self): + raise NotImplementedError() + +class AtomicNode(Node): + def __init__(self, lex): + self.lex = lex + +class UnaryNode(Node): + def __init__(self, node): + self.node = node + + def evaluate(self): + value = self.node.evaluate() + return self.operate(value) + + @staticmethod + def operate(value): + raise NotImplementedError() + +class BinaryNode(Node): + def __init__(self, left, right): + self.left = left + self.right = right + + def evaluate(self): + lvalue = self.left.evaluate() + rvalue = self.right.evaluate() + return self.operate(lvalue, rvalue) + + @staticmethod + def operate(lvalue, rvalue): + raise NotImplementedError() + +def get_printer(AtomicNode=AtomicNode, UnaryNode=UnaryNode, BinaryNode=BinaryNode, ): + + class PrintVisitor(object): + @visitor.on('node') + def visit(self, node, tabs): + pass + + @visitor.when(UnaryNode) + def visit(self, node, tabs=0): + ans = '\t' * tabs + f'\\__ {node.__class__.__name__}' + child = self.visit(node.node, tabs + 1) + return f'{ans}\n{child}' + + @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(AtomicNode) + def visit(self, node, tabs=0): + return '\t' * tabs + f'\\__ {node.__class__.__name__}: {node.lex}' + + printer = PrintVisitor() + return (lambda ast: printer.visit(ast)) \ No newline at end of file diff --git a/src/cool/cmp/automata.py b/src/cool/cmp/automata.py new file mode 100644 index 000000000..f42b6f00c --- /dev/null +++ b/src/cool/cmp/automata.py @@ -0,0 +1,207 @@ +try: + import pydot +except: + pass + +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): + G = pydot.Dot(rankdir='LR', 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=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/cool/cmp/cil.py b/src/cool/cmp/cil.py new file mode 100644 index 000000000..de6782c16 --- /dev/null +++ b/src/cool/cmp/cil.py @@ -0,0 +1,231 @@ +import cmp.visitor as visitor + + +class Node: + pass + +class ProgramNode(Node): + def __init__(self, dottypes, dotdata, dotcode): + self.dottypes = dottypes + self.dotdata = dotdata + self.dotcode = dotcode + +class TypeNode(Node): + def __init__(self, name): + self.name = name + self.attributes = [] + self.methods = [] + +class DataNode(Node): + def __init__(self, vname, value): + self.name = vname + self.value = value + +class FunctionNode(Node): + def __init__(self, fname, params, localvars, instructions): + self.name = fname + self.params = params + self.localvars = localvars + self.instructions = instructions + +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 GetAttribNode(InstructionNode): + pass + +class SetAttribNode(InstructionNode): + pass + +class GetIndexNode(InstructionNode): + pass + +class SetIndexNode(InstructionNode): + pass + +class AllocateNode(InstructionNode): + def __init__(self, itype, dest): + self.type = itype + self.dest = dest + +class ArrayNode(InstructionNode): + pass + +class TypeOfNode(InstructionNode): + def __init__(self, obj, dest): + self.obj = obj + self.dest = dest + +class LabelNode(InstructionNode): + pass + +class GotoNode(InstructionNode): + pass + +class GotoIfNode(InstructionNode): + pass + +class StaticCallNode(InstructionNode): + def __init__(self, function, dest): + self.function = function + self.dest = dest + +class DynamicCallNode(InstructionNode): + def __init__(self, xtype, method, dest): + self.type = xtype + self.method = method + self.dest = dest + +class ArgNode(InstructionNode): + def __init__(self, name): + self.name = name + +class ReturnNode(InstructionNode): + def __init__(self, value=None): + self.value = value + +class LoadNode(InstructionNode): + def __init__(self, dest, msg): + self.dest = dest + self.msg = msg + +class LengthNode(InstructionNode): + pass + +class ConcatNode(InstructionNode): + pass + +class PrefixNode(InstructionNode): + pass + +class SubstringNode(InstructionNode): + pass + +class ToStrNode(InstructionNode): + def __init__(self, dest, ivalue): + self.dest = dest + self.ivalue = ivalue + +class ReadNode(InstructionNode): + def __init__(self, dest): + self.dest = dest + +class PrintNode(InstructionNode): + def __init__(self, str_addr): + self.str_addr = str_addr + +def get_formatter(): + + class PrintVisitor(object): + @visitor.on('node') + def visit(self, node): + pass + + @visitor.when(ProgramNode) + def visit(self, node): + dottypes = '\n'.join(self.visit(t) for t in node.dottypes) + dotdata = '\n'.join(self.visit(t) for t in node.dotdata) + dotcode = '\n'.join(self.visit(t) for t in node.dotcode) + + return f'.TYPES\n{dottypes}\n\n.DATA\n{dotdata}\n\n.CODE\n{dotcode}' + + @visitor.when(TypeNode) + def visit(self, node): + attributes = '\n\t'.join(f'attribute {x}' for x in node.attributes) + methods = '\n\t'.join(f'method {x}: {y}' for x,y in node.methods) + + return f'type {node.name} {{\n\t{attributes}\n\n\t{methods}\n}}' + + @visitor.when(FunctionNode) + def visit(self, node): + params = '\n\t'.join(self.visit(x) for x in node.params) + localvars = '\n\t'.join(self.visit(x) for x in node.localvars) + instructions = '\n\t'.join(self.visit(x) for x in node.instructions) + + return f'function {node.name} {{\n\t{params}\n\n\t{localvars}\n\n\t{instructions}\n}}' + + @visitor.when(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.type}' + + @visitor.when(StaticCallNode) + def visit(self, node): + return f'{node.dest} = CALL {node.function}' + + @visitor.when(DynamicCallNode) + def visit(self, node): + return f'{node.dest} = VCALL {node.type} {node.method}' + + @visitor.when(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 ""}' + + printer = PrintVisitor() + return (lambda ast: printer.visit(ast)) \ No newline at end of file diff --git a/src/cool/cmp/evaluation.py b/src/cool/cmp/evaluation.py new file mode 100644 index 000000000..55ff38e9d --- /dev/null +++ b/src/cool/cmp/evaluation.py @@ -0,0 +1,33 @@ +from cmp.pycompiler import EOF +from cmp.tools.parsing import ShiftReduceParser + +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: + if operation == ShiftReduceParser.SHIFT: + token = next(tokens) + stack.append(token.lex) + 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 synteticed attributes.' + rule = attributes[0] + + if len(body): + synteticed = [None] + stack[-len(body):] + value = rule(None, synteticed) + stack[-len(body):] = [value] + else: + stack.append(rule(None, None)) + else: + raise Exception('Invalid action!!!') + + assert len(stack) == 1 + assert isinstance(next(tokens).token_type, EOF) + return stack[0] \ No newline at end of file diff --git a/src/cool/cmp/languages.py b/src/cool/cmp/languages.py new file mode 100644 index 000000000..e2c0c33da --- /dev/null +++ b/src/cool/cmp/languages.py @@ -0,0 +1,228 @@ +from cmp.pycompiler import Sentence, Production +from cmp.utils import ContainerSet, Token, UnknownToken +from cmp.tools.parsing import build_parsing_table, metodo_predictivo_no_recursivo + +class BasicXCool: + def __init__(self, G): + self.G = G + self.fixed_tokens = { lex: Token(lex, G[lex]) for lex in '+ - * / ( )'.split() } + + @property + def firsts(self): + G = self.G + return { + G['+']: ContainerSet(G['+'] , contains_epsilon=False), + G['-']: ContainerSet(G['-'] , contains_epsilon=False), + G['*']: ContainerSet(G['*'] , contains_epsilon=False), + G['/']: ContainerSet(G['/'] , contains_epsilon=False), + G['(']: ContainerSet(G['('] , contains_epsilon=False), + G[')']: ContainerSet(G[')'] , contains_epsilon=False), + G['num']: ContainerSet(G['num'] , contains_epsilon=False), + G['E']: ContainerSet(G['num'], G['('] , contains_epsilon=False), + G['T']: ContainerSet(G['num'], G['('] , contains_epsilon=False), + G['F']: ContainerSet(G['num'], G['('] , contains_epsilon=False), + G['X']: ContainerSet(G['-'], G['+'] , contains_epsilon=True), + G['Y']: ContainerSet(G['/'], G['*'] , contains_epsilon=True), + Sentence(G['T'], G['X']): ContainerSet(G['num'], G['('] , contains_epsilon=False), + Sentence(G['+'], G['T'], G['X']): ContainerSet(G['+'] , contains_epsilon=False), + Sentence(G['-'], G['T'], G['X']): ContainerSet(G['-'] , contains_epsilon=False), + G.Epsilon: ContainerSet( contains_epsilon=True), + Sentence(G['F'], G['Y']): ContainerSet(G['num'], G['('] , contains_epsilon=False), + Sentence(G['*'], G['F'], G['Y']): ContainerSet(G['*'] , contains_epsilon=False), + Sentence(G['/'], G['F'], G['Y']): ContainerSet(G['/'] , contains_epsilon=False), + Sentence(G['num']): ContainerSet(G['num'] , contains_epsilon=False), + Sentence(G['('], G['E'], G[')']): ContainerSet(G['('] , contains_epsilon=False) + } + + @property + def follows(self): + G = self.G + return { + G['E']: ContainerSet(G[')'], G.EOF , contains_epsilon=False), + G['T']: ContainerSet(G[')'], G['-'], G.EOF, G['+'] , contains_epsilon=False), + G['F']: ContainerSet(G['-'], G.EOF, G['*'], G['/'], G[')'], G['+'] , contains_epsilon=False), + G['X']: ContainerSet(G[')'], G.EOF , contains_epsilon=False), + G['Y']: ContainerSet(G[')'], G['-'], G.EOF, G['+'] , contains_epsilon=False) + } + + @property + def table(self): + G = self.G + return { + ( G['E'], G['num'], ): [ Production(G['E'], Sentence(G['T'], G['X'])), ], + ( G['E'], G['('], ): [ Production(G['E'], Sentence(G['T'], G['X'])), ], + ( G['X'], G['+'], ): [ Production(G['X'], Sentence(G['+'], G['T'], G['X'])), ], + ( G['X'], G['-'], ): [ Production(G['X'], Sentence(G['-'], G['T'], G['X'])), ], + ( G['X'], G[')'], ): [ Production(G['X'], G.Epsilon), ], + ( G['X'], G.EOF, ): [ Production(G['X'], G.Epsilon), ], + ( G['T'], G['num'], ): [ Production(G['T'], Sentence(G['F'], G['Y'])), ], + ( G['T'], G['('], ): [ Production(G['T'], Sentence(G['F'], G['Y'])), ], + ( G['Y'], G['*'], ): [ Production(G['Y'], Sentence(G['*'], G['F'], G['Y'])), ], + ( G['Y'], G['/'], ): [ Production(G['Y'], Sentence(G['/'], G['F'], G['Y'])), ], + ( G['Y'], G[')'], ): [ Production(G['Y'], G.Epsilon), ], + ( G['Y'], G['-'], ): [ Production(G['Y'], G.Epsilon), ], + ( G['Y'], G.EOF, ): [ Production(G['Y'], G.Epsilon), ], + ( G['Y'], G['+'], ): [ Production(G['Y'], G.Epsilon), ], + ( G['F'], G['num'], ): [ Production(G['F'], Sentence(G['num'])), ], + ( G['F'], G['('], ): [ Production(G['F'], Sentence(G['('], G['E'], G[')'])), ] + } + + @property + def tokenizer(self): + G = self.G + fixed_tokens = self.fixed_tokens + + def tokenize_text(text): + tokens = [] + for item in text.split(): + try: + float(item) + token = Token(item, G['num']) + except ValueError: + try: + token = fixed_tokens[item] + except: + token = UnknownToken(item) + tokens.append(token) + eof = Token('$', G.EOF) + tokens.append(eof) + return tokens + + return tokenize_text + +class PowXCool: + def __init__(self, G): + self.G = G + + @property + def firsts(self): + G = self.G + return { + G['+']: ContainerSet(G['+'] , contains_epsilon=False), + G['-']: ContainerSet(G['-'] , contains_epsilon=False), + G['*']: ContainerSet(G['*'] , contains_epsilon=False), + G['/']: ContainerSet(G['/'] , contains_epsilon=False), + G['^']: ContainerSet(G['^'] , contains_epsilon=False), + G['(']: ContainerSet(G['('] , contains_epsilon=False), + G[')']: ContainerSet(G[')'] , contains_epsilon=False), + G['num']: ContainerSet(G['num'] , contains_epsilon=False), + G['E']: ContainerSet(G['num'], G['('] , contains_epsilon=False), + G['T']: ContainerSet(G['num'], G['('] , contains_epsilon=False), + G['F']: ContainerSet(G['num'], G['('] , contains_epsilon=False), + G['A']: ContainerSet(G['num'], G['('] , contains_epsilon=False), + G['X']: ContainerSet(G['-'], G['+'] , contains_epsilon=True), + G['Y']: ContainerSet(G['/'], G['*'] , contains_epsilon=True), + G['Z']: ContainerSet(G['^'] , contains_epsilon=True), + Sentence(G['T'], G['X']): ContainerSet(G['num'], G['('] , contains_epsilon=False), + Sentence(G['+'], G['T'], G['X']): ContainerSet(G['+'] , contains_epsilon=False), + Sentence(G['-'], G['T'], G['X']): ContainerSet(G['-'] , contains_epsilon=False), + G.Epsilon: ContainerSet( contains_epsilon=True), + Sentence(G['F'], G['Y']): ContainerSet(G['num'], G['('] , contains_epsilon=False), + Sentence(G['*'], G['F'], G['Y']): ContainerSet(G['*'] , contains_epsilon=False), + Sentence(G['/'], G['F'], G['Y']): ContainerSet(G['/'] , contains_epsilon=False), + Sentence(G['A'], G['Z']): ContainerSet(G['num'], G['('] , contains_epsilon=False), + Sentence(G['^'], G['F']): ContainerSet(G['^'] , contains_epsilon=False), + Sentence(G['num']): ContainerSet(G['num'] , contains_epsilon=False), + Sentence(G['('], G['E'], G[')']): ContainerSet(G['('] , contains_epsilon=False) + } + + @property + def follows(self): + G = self.G + return { + G['E']: ContainerSet(G[')'], G.EOF , contains_epsilon=False), + G['T']: ContainerSet(G['-'], G[')'], G.EOF, G['+'] , contains_epsilon=False), + G['F']: ContainerSet(G['-'], G['*'], G['/'], G[')'], G.EOF, G['+'] , contains_epsilon=False), + G['A']: ContainerSet(G['-'], G['*'], G['/'], G['^'], G[')'], G.EOF, G['+'] , contains_epsilon=False), + G['X']: ContainerSet(G[')'], G.EOF , contains_epsilon=False), + G['Y']: ContainerSet(G['-'], G[')'], G.EOF, G['+'] , contains_epsilon=False), + G['Z']: ContainerSet(G['-'], G['*'], G['/'], G[')'], G.EOF, G['+'] , contains_epsilon=False) + } + +class Regex: + def __init__(self, G): + self.G = G + + @property + def firsts(self): + G = self.G + return { + G['|']: ContainerSet(G['|'] , contains_epsilon=False), + G['*']: ContainerSet(G['*'] , contains_epsilon=False), + G['(']: ContainerSet(G['('] , contains_epsilon=False), + G[')']: ContainerSet(G[')'] , contains_epsilon=False), + G['symbol']: ContainerSet(G['symbol'] , contains_epsilon=False), + G['ε']: ContainerSet(G['ε'] , contains_epsilon=False), + G['E']: ContainerSet(G['symbol'], G['ε'], G['('] , contains_epsilon=False), + G['T']: ContainerSet(G['symbol'], G['ε'], G['('] , contains_epsilon=False), + G['F']: ContainerSet(G['symbol'], G['ε'], G['('] , contains_epsilon=False), + G['A']: ContainerSet(G['ε'], G['symbol'], G['('] , contains_epsilon=False), + G['X']: ContainerSet(G['|'] , contains_epsilon=True), + G['Y']: ContainerSet(G['symbol'], G['ε'], G['('] , contains_epsilon=True), + G['Z']: ContainerSet(G['*'] , contains_epsilon=True), + Sentence(G['T'], G['X']): ContainerSet(G['symbol'], G['ε'], G['('] , contains_epsilon=False), + Sentence(G['|'], G['E']): ContainerSet(G['|'] , contains_epsilon=False), + G.Epsilon: ContainerSet( contains_epsilon=True), + Sentence(G['F'], G['Y']): ContainerSet(G['symbol'], G['ε'], G['('] , contains_epsilon=False), + Sentence(G['T']): ContainerSet(G['symbol'], G['ε'], G['('] , contains_epsilon=False), + Sentence(G['A'], G['Z']): ContainerSet(G['symbol'], G['ε'], G['('] , contains_epsilon=False), + Sentence(G['*']): ContainerSet(G['*'] , contains_epsilon=False), + Sentence(G['symbol']): ContainerSet(G['symbol'] , contains_epsilon=False), + Sentence(G['ε']): ContainerSet(G['ε'] , contains_epsilon=False), + Sentence(G['('], G['E'], G[')']): ContainerSet(G['('] , contains_epsilon=False) + } + + @property + def follows(self): + G = self.G + return { + G['E']: ContainerSet(G[')'], G.EOF , contains_epsilon=False), + G['T']: ContainerSet(G[')'], G.EOF, G['|'] , contains_epsilon=False), + G['F']: ContainerSet(G[')'], G.EOF, G['symbol'], G['|'], G['ε'], G['('] , contains_epsilon=False), + G['A']: ContainerSet(G.EOF, G['|'], G['*'], G['('], G[')'], G['symbol'], G['ε'] , contains_epsilon=False), + G['X']: ContainerSet(G[')'], G.EOF , contains_epsilon=False), + G['Y']: ContainerSet(G[')'], G.EOF, G['|'] , contains_epsilon=False), + G['Z']: ContainerSet(G.EOF, G['|'], G['('], G[')'], G['symbol'], G['ε'] , contains_epsilon=False) + } + + @property + def table(self): + G = self.G + return { + ( G['E'], G['symbol'], ): [ Production(G['E'], Sentence(G['T'], G['X'])), ], + ( G['E'], G['ε'], ): [ Production(G['E'], Sentence(G['T'], G['X'])), ], + ( G['E'], G['('], ): [ Production(G['E'], Sentence(G['T'], G['X'])), ], + ( G['X'], G['|'], ): [ Production(G['X'], Sentence(G['|'], G['E'])), ], + ( G['X'], G[')'], ): [ Production(G['X'], G.Epsilon), ], + ( G['X'], G.EOF, ): [ Production(G['X'], G.Epsilon), ], + ( G['T'], G['symbol'], ): [ Production(G['T'], Sentence(G['F'], G['Y'])), ], + ( G['T'], G['ε'], ): [ Production(G['T'], Sentence(G['F'], G['Y'])), ], + ( G['T'], G['('], ): [ Production(G['T'], Sentence(G['F'], G['Y'])), ], + ( G['Y'], G['symbol'], ): [ Production(G['Y'], Sentence(G['T'])), ], + ( G['Y'], G['ε'], ): [ Production(G['Y'], Sentence(G['T'])), ], + ( G['Y'], G['('], ): [ Production(G['Y'], Sentence(G['T'])), ], + ( G['Y'], G[')'], ): [ Production(G['Y'], G.Epsilon), ], + ( G['Y'], G.EOF, ): [ Production(G['Y'], G.Epsilon), ], + ( G['Y'], G['|'], ): [ Production(G['Y'], G.Epsilon), ], + ( G['F'], G['symbol'], ): [ Production(G['F'], Sentence(G['A'], G['Z'])), ], + ( G['F'], G['ε'], ): [ Production(G['F'], Sentence(G['A'], G['Z'])), ], + ( G['F'], G['('], ): [ Production(G['F'], Sentence(G['A'], G['Z'])), ], + ( G['Z'], G['*'], ): [ Production(G['Z'], Sentence(G['*'])), ], + ( G['Z'], G.EOF, ): [ Production(G['Z'], G.Epsilon), ], + ( G['Z'], G['|'], ): [ Production(G['Z'], G.Epsilon), ], + ( G['Z'], G['('], ): [ Production(G['Z'], G.Epsilon), ], + ( G['Z'], G[')'], ): [ Production(G['Z'], G.Epsilon), ], + ( G['Z'], G['symbol'], ): [ Production(G['Z'], G.Epsilon), ], + ( G['Z'], G['ε'], ): [ Production(G['Z'], G.Epsilon), ], + ( G['A'], G['symbol'], ): [ Production(G['A'], Sentence(G['symbol'])), ], + ( G['A'], G['ε'], ): [ Production(G['A'], Sentence(G['ε'])), ], + ( G['A'], G['('], ): [ Production(G['A'], Sentence(G['('], G['E'], G[')'])), ] + } + + @property + def parser(self): + firsts = self.firsts + follows = self.follows + M = build_parsing_table(self.G, firsts, follows) + parser = metodo_predictivo_no_recursivo(self.G, M) + return parser \ No newline at end of file diff --git a/src/cool/cmp/nbpackage.py b/src/cool/cmp/nbpackage.py new file mode 100644 index 000000000..e89c62ad3 --- /dev/null +++ b/src/cool/cmp/nbpackage.py @@ -0,0 +1,87 @@ +import io, os, sys, types + +from IPython import get_ipython +from nbformat import read +from IPython.core.interactiveshell import InteractiveShell + +def find_notebook(fullname, path=None): + """find a notebook, given its fully qualified name and an optional path + + This turns "foo.bar" into "foo/bar.ipynb" + and tries turning "Foo_Bar" into "Foo Bar" if Foo_Bar + does not exist. + """ + name = fullname.rsplit('.', 1)[-1] + if not path: + path = [''] + for d in path: + nb_path = os.path.join(d, name + ".ipynb") + if os.path.isfile(nb_path): + return nb_path + # let import Notebook_Name find "Notebook Name.ipynb" + nb_path = nb_path.replace("_", " ") + if os.path.isfile(nb_path): + return nb_path + +class NotebookLoader(object): + """Module Loader for Jupyter Notebooks""" + def __init__(self, path=None): + self.shell = InteractiveShell.instance() + self.path = path + + def load_module(self, fullname): + """import a notebook as a module""" + path = find_notebook(fullname, self.path) + + print ("importing Jupyter notebook from %s" % path) + + # load the notebook object + with io.open(path, 'r', encoding='utf-8') as f: + nb = read(f, 4) + + + # create the module and add it to sys.modules + # if name in sys.modules: + # return sys.modules[name] + mod = types.ModuleType(fullname) + mod.__file__ = path + mod.__loader__ = self + mod.__dict__['get_ipython'] = get_ipython + sys.modules[fullname] = mod + + # extra work to ensure that magics that would affect the user_ns + # actually affect the notebook module's ns + save_user_ns = self.shell.user_ns + self.shell.user_ns = mod.__dict__ + + try: + for cell in nb.cells: + if cell.cell_type == 'code': + # transform the input to executable Python + code = self.shell.input_transformer_manager.transform_cell(cell.source) + # run the code in themodule + exec(code, mod.__dict__) + finally: + self.shell.user_ns = save_user_ns + return mod + +class NotebookFinder(object): + """Module finder that locates Jupyter Notebooks""" + def __init__(self): + self.loaders = {} + + def find_module(self, fullname, path=None): + nb_path = find_notebook(fullname, path) + if not nb_path: + return + + key = path + if path: + # lists aren't hashable + key = os.path.sep.join(path) + + if key not in self.loaders: + self.loaders[key] = NotebookLoader(path) + return self.loaders[key] + +sys.meta_path.append(NotebookFinder()) \ No newline at end of file diff --git a/src/cool/cmp/pycompiler.py b/src/cool/cmp/pycompiler.py new file mode 100644 index 000000000..3ef02deeb --- /dev/null +++ b/src/cool/cmp/pycompiler.py @@ -0,0 +1,512 @@ +import json + +class Symbol(object): + + def __init__(self, name, grammar): + self.Name = name + self.Grammar = grammar + + def __str__(self): + return self.Name + + def __repr__(self): + return repr(self.Name) + + def __add__(self, other): + if isinstance(other, Symbol): + return Sentence(self, other) + + 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) + + @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) + + @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)) + + raise TypeError(other) + + def __or__(self, other): + if isinstance(other, Sentence): + return SentenceList(self, other) + + if isinstance(other, Symbol): + return SentenceList(self, Sentence(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) + + +class Epsilon(Terminal, Sentence): + + def __init__(self, grammar): + super().__init__('epsilon', grammar) + + + def __str__(self): + return "e" + + 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.Productions = self.Productions.copy() + G.nonTerminals = self.nonTerminals.copy() + G.terminals = self.terminals.copy() + G.pType = self.pType + G.startSymbol = self.startSymbol + G.Epsilon = self.Epsilon + G.EOF = self.EOF + G.symbDict = self.symbDict.copy() + + 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/cool/cmp/semantic.py b/src/cool/cmp/semantic.py new file mode 100644 index 000000000..575fc3f44 --- /dev/null +++ b/src/cool/cmp/semantic.py @@ -0,0 +1,225 @@ +import itertools as itt +from collections import OrderedDict + + +class SemanticError(Exception): + @property + def text(self): + return self.args[0] + +class Attribute: + def __init__(self, name, typex): + self.name = name + self.type = typex + + def __str__(self): + return f'[attrib] {self.name} : {self.type.name};' + + def __repr__(self): + return str(self) + +class Method: + def __init__(self, name, param_names, params_types, return_type): + self.name = name + self.param_names = param_names + self.param_types = params_types + self.return_type = return_type + + def __str__(self): + params = ', '.join(f'{n}:{t.name}' for n,t in zip(self.param_names, self.param_types)) + return f'[method] {self.name}({params}): {self.return_type.name};' + + def __eq__(self, other): + return other.name == self.name and \ + other.return_type == self.return_type and \ + other.param_types == self.param_types + +class Type: + def __init__(self, name:str): + self.name = name + self.attributes = [] + self.methods = [] + self.parent = None + + def set_parent(self, parent): + if self.parent is not None: + raise SemanticError(f'Parent type is already set for {self.name}.') + self.parent = parent + + def get_attribute(self, name:str): + try: + return next(attr for attr in self.attributes if attr.name == name) + except StopIteration: + if self.parent is None: + raise SemanticError(f'Attribute "{name}" is not defined in {self.name}.') + try: + return self.parent.get_attribute(name) + except SemanticError: + raise SemanticError(f'Attribute "{name}" is not defined in {self.name}.') + + def define_attribute(self, name:str, typex): + try: + self.get_attribute(name) + except SemanticError: + attribute = Attribute(name, typex) + self.attributes.append(attribute) + return attribute + else: + raise SemanticError(f'Attribute "{name}" is already defined in {self.name}.') + + def get_method(self, name:str): + try: + return next(method for method in self.methods if method.name == name) + except StopIteration: + if self.parent is None: + raise SemanticError(f'Method "{name}" is not defined in {self.name}.') + try: + return self.parent.get_method(name) + except SemanticError: + raise SemanticError(f'Method "{name}" is not defined in {self.name}.') + + def define_method(self, name:str, param_names:list, param_types:list, return_type): + if name in (method.name for method in self.methods): + raise SemanticError(f'Method "{name}" already defined in {self.name}') + + method = Method(name, param_names, param_types, return_type) + self.methods.append(method) + return method + + def all_attributes(self, clean=True): + plain = OrderedDict() if self.parent is None else self.parent.all_attributes(False) + for attr in self.attributes: + plain[attr.name] = (attr, self) + return plain.values() if clean else plain + + def all_attributes_names(self, clean=True): + plain = OrderedDict() if self.parent is None else self.parent.all_attributes(False) + for attr in self.attributes: + plain[attr.name] = (attr, self) + return plain.keys() if clean else plain + + def all_methods(self, clean=True): + plain = OrderedDict() if self.parent is None else self.parent.all_methods(False) + for method in self.methods: + plain[method.name] = (method, self) + return plain.values() if clean else plain + + def conforms_to(self, other): + return other.bypass() or self == other or self.parent is not None and self.parent.conforms_to(other) + + def bypass(self): + return False + + def get_all_parents(self): + if self.parent is None: return [] + return [self.parent]+self.parent.get_all_parents() + + + def __str__(self): + output = f'type {self.name}' + parent = '' if self.parent is None else f' : {self.parent.name}' + output += parent + output += ' {' + output += '\n\t' if self.attributes or self.methods else '' + output += '\n\t'.join(str(x) for x in self.attributes) + output += '\n\t' if self.attributes else '' + output += '\n\t'.join(str(x) for x in self.methods) + output += '\n' if self.methods else '' + output += '}\n' + return output + + def __repr__(self): + return str(self) + +class ErrorType(Type): + def __init__(self): + Type.__init__(self, '') + + def conforms_to(self, other): + return True + + def bypass(self): + return True + + def __eq__(self, other): + return isinstance(other, Type) + +class VoidType(Type): + def __init__(self): + Type.__init__(self, '') + + def conforms_to(self, other): + raise Exception('Invalid type: void type.') + + def bypass(self): + return True + + def __eq__(self, other): + return isinstance(other, VoidType) + +class IntType(Type): + def __init__(self): + Type.__init__(self, 'int') + + def __eq__(self, other): + return other.name == self.name or isinstance(other, IntType) + +class Context: + def __init__(self): + self.types = {} + + def create_type(self, name:str): + if name in self.types: + raise SemanticError(f'Type with the same name ({name}) already in context.') + typex = self.types[name] = Type(name) + return typex + + def get_type(self, name:str): + try: + return self.types[name] + except KeyError: + raise SemanticError(f'Type "{name}" is not defined.') + + def __str__(self): + return '{\n\t' + '\n\t'.join(y for x in self.types.values() for y in str(x).split('\n')) + '\n}' + + def __repr__(self): + return str(self) + +class VariableInfo: + def __init__(self, name, vtype): + self.name = name + self.type = vtype + +class Scope: + def __init__(self, parent=None): + self.locals = [] + self.parent = parent + self.children = [] + self.index = 0 if parent is None else len(parent) + + def __len__(self): + return len(self.locals) + + def create_child(self): + child = Scope(self) + self.children.append(child) + return child + + def define_variable(self, vname, vtype): + info = VariableInfo(vname, vtype) + self.locals.append(info) + return info + + def find_variable(self, vname, index=None): + locals = self.locals if index is None else itt.islice(self.locals, index) + try: + return next(x for x in locals if x.name == vname) + except StopIteration: + return self.parent.find_variable(vname, self.index) if self.parent is None else None + + def is_defined(self, vname): + return self.find_variable(vname) is not None + + def is_local(self, vname): + return any(True for x in self.locals if x.name == vname) diff --git a/src/cool/cmp/tools/.ipynb_checkpoints/__init__-checkpoint.py b/src/cool/cmp/tools/.ipynb_checkpoints/__init__-checkpoint.py new file mode 100644 index 000000000..e69de29bb diff --git a/src/cool/cmp/tools/.ipynb_checkpoints/automata-checkpoint.py b/src/cool/cmp/tools/.ipynb_checkpoints/automata-checkpoint.py new file mode 100644 index 000000000..59643cc5b --- /dev/null +++ b/src/cool/cmp/tools/.ipynb_checkpoints/automata-checkpoint.py @@ -0,0 +1,3 @@ +import zlib, base64 +exec(zlib.decompress(base64.b64decode('eJzNWN1um0gUvucp6BWwRSjx1SrSrBRt4iR20rRJW9VCCI1hbE+LgTBDGjvKY+1r7DPtmR9gjIm3q+5KexPDzPn9zjdnDuGIEW4xVOF8SawHtMIMc15ZZ2hKNudVVVTWNcJZZm0QZTRnHOcJsQiiObeWKCO5tUIfNyVRojka44wRa4o+VjWxPiOcbyyMtrS0OEckr9ekwhz2tUBZIl6XGbFYEwGvNieWTddlUXG73KQFt8hTQkpuX8k16QckSsyYtaiKtZ2sy6DmNGON1u9FzjHNSXUPmSUZCNrvxqegk5KFHcc0pzyOXUayhQ/pcML8Bc0hbJ9DCIxyWuRM7FQcHXmgZgvRQIki9WMsgpT82ywpW4i76sFr1tcYcu0cNMuPRYLndYarDai0woYget6ePL8sisre2jS3masi8F5AFlbdU3/u+bdiy1AKKCdr5srobQCAACwP7q3vQPqcVHHseL5zlT/ijKZ2UmQZSYSeXSxsZd4Riv1QwtMonEfott3rgg9wmrpzbz+rIKUswVXqOo6nKkBKRrMijw3DRjFkyDpiuSCT7gXSxS5FRLCKN7ZdEV5X+xqhFIxCx4lASjPqzNBwdXTLCpcrGY+M5AJJDgZnBXfB2reUVsi5vnP8Na6WNEdHwbFI+kLkH+dFSlwl/048OpIYjs9WuCTIKTNgJSdPsJLhOcmQ4/jfacpX6MhfEbpcCb55uqyZT3VZG/bs1JQi588/HJsu4Ans2AROnU3FznAoWRNEQqskIxAT32TwOi+yVJrJWleKt8oiFE0EJCKyx0LiVnp/xcn475yMDzrRVkm6bKyei8fMH2u8qPoFRskuhEZeh31fq8W+PaYd6DqsFLMVSZVzTZsL3SQqUlYxe1zGHROGCKbI4gVJRYBdQgFeUpLI4td88avgvGabVJZdS7Wks/GpC23J+4m+pI/Jtbtx4TTUxCeeKJR87jcEuQjs8Uy9JRzZ345kdaGN9FQ8mbTRh76RzUko7URCA179QVeap6JBQYLBP8qs7SBJXVUkb3urhGhdPBJtZbMGUkkQgFbqzc4LPtQsQtNeZNYw33N2UDVUfqLwKOoYM20ZA/dox5Z9s0YiFTBkmdNtkwyvaL7s1LQt3QnsRCYlZdTRX8hMlaiEJFE9wUzLZKkOo3f2LBGJVMc1L9aYF3lTnA7dG3UpdbePFBDeUtSq7SC2jYyzcovSBrR+171FMsGboC5TsOnewpsO+sYyr4kkK1hd7UcpwpuhkMngWBcceHpCz3vLwMbvK5oReybcb9EsKItShjBwHxm5Dd1W26Y0F8L+gERTp4uGk0+qQE/ymrxQ3W4W4LIkuXrXqZuji/vLkyeRyBc45kWcLnAHgsg+Qc+QVYZeRyrs8pD0i8BTFtAUHclfFksqoM+uBKoT1t25hTADvQkKRRVn6ucVLIXGfNdWNwpIDG5Qj3NbX44N9uWBPG48g/iXCssEoKI5zJBq57KBeqK2L0WaS3fiNW8/nOylUpk05bnslUu9i5tLeSrQJKB5Sp4ayUs0CQtB+PYc2Em4hXj8uVxt+m/ejTFwGbx580YOXTunpFNEIiHLvkdyoTuRE5H8tk0PHLxH4moRqfv3ftJx670kk84bx3UOTHXxsY9HJpmAGekxOoa/I4SP9dT7NoWFGuFR+z6yesMnyPZmlCQ8BT0R+vMYHowZ4mVPeTSoPGqUR7vKSZj5MMihUAVYceFHBSeeR5HqWOmTfy+tuyHsw7IvFPTNA+LqKZL+WjzvT3YK9wVBBd6CJTU49qpj7urx3f4iz3gtT0wHYAfdSBTxuX5p6wJ3pDsRpfIzb7dEQPEEdHLMD5Tq6H9dqhbXFnlzltL4HfsDY7m5p7FV0BqFNq+mtqADDkYHHIz2HPw7tWua2PHg6ap7FfupCg2ehzr6T9CvjUJ03nqIHcZp+Kv9jLKvBXwfiY92gaSY8mHkqSlbxcqse+d3V8JUwPJJwnqOytIdunA0PWqBwJ1Ia/bK0FKr8VigcI1cdxYyMeSJyVKoztRnyrsiV/O1XDwXOEyEZ3caplEgvhhgbIPrm9NHIppyKsSmgfhIYj0LcuvaM2rxKZxEzQWjo/H6NREyyIhVYRveySTvhMlP7ZgfSQwlbvEa5u813eouYk4QU2Sg7v7CmLszMIgpC6KYBmtSwSeVcUf2b89hqSF7ElR9VQ9YUXPFdCo5/0PRddlP4XusqEs1fYnVlVj9ER6Ji1aHv/LUN8USpmoEA4Ten8MX3jd4mKLE6kb/nRN/COaDhbDsq505dtojE5Nj17PuptRv/y3CuXslw3tUDlT1NSRj/3oXY5Pwj9HOfxPmaBpeA+P7NBZ7X9GVHm/UnNZejbOQ+uPdgWb/hlRC6Ktlf0AhHUyAap4G7cdknxfg5KGNYro31PaC7iYeNQddef4Hf+Y/eNZffhu9yA=='))) +# Created by pyminifier (https://github.com/liftoff/pyminifier) diff --git a/src/cool/cmp/tools/.ipynb_checkpoints/evaluation-checkpoint.py b/src/cool/cmp/tools/.ipynb_checkpoints/evaluation-checkpoint.py new file mode 100644 index 000000000..d49df0ce5 --- /dev/null +++ b/src/cool/cmp/tools/.ipynb_checkpoints/evaluation-checkpoint.py @@ -0,0 +1,3 @@ +import zlib, base64 +exec(zlib.decompress(base64.b64decode('eJxdUMFuwjAMvecrfGy3qoLrJN86YGViCO2GECrF1SLSpErcqf37OUWwsZPtZ/v5+TXetVC3Xd6NtWs7bciDbjvnGV4/FmqJmsmrE1oaWFnUQdvAla1JFbhxltQaDVm1QLJ9S75iUmdqgL4r00tx7CofKGmyMn1RoBuwjqEB56ekFAw8ce+tggaXSZMqKCWWEge8kSQnaWSRQ0EVAok2K1iZ5uwuZI88dpSJWmlfyWB4EJF03p37mrWzkSXT9ou8/HU+xgHCImrbZAZ/5xSMf6q8Yvb61DMFBYz74vCUrBOTPs/l5OV/vZ8d8N8J+U5e1tkOtIVFYrJ5PBn92OVv4ZN8q21lInR78LLXBx2giBBLjtO/hgYByASaZld4miy7b+0QV/k7NRyxLY6yGDO5swVhi54X0+bEj9vkknF6P3H3azXZFEekGWlmh7u1150HxkmQSP0BhJm25Q=='))) +# Created by pyminifier (https://github.com/liftoff/pyminifier) diff --git a/src/cool/cmp/tools/.ipynb_checkpoints/parsing-checkpoint.py b/src/cool/cmp/tools/.ipynb_checkpoints/parsing-checkpoint.py new file mode 100644 index 000000000..02d672f4b --- /dev/null +++ b/src/cool/cmp/tools/.ipynb_checkpoints/parsing-checkpoint.py @@ -0,0 +1,10 @@ +import zlib, base64 +exec(zlib.decompress(base64.b64decode('eJytVVtr2zAUfvevEH2yqWfa14AeypKGlXlNL5SCMUZO5FTMlhRZbpZu++87ujiOm3QwGJRU56pzvu8cuVKiQUxTpYWoW8QaKZRGrK3ZkgaVMS4bmXSaDcbPgmvCOFUPVAcrWqGlaGSnaVGLJamLiqlWh/a3jUktX0g0CVCFnSZAEltlgKb4MFMYBUirHbiiBZbJl3YmW1YLHiD6Y0mldoZrUrc0QKxCC6OYJi3VBXWeJgMFszFUQqE5YhxJI6EUV9k8N6dp0skV0TRMIyNCIi40SpOlK6Xtk9kwVCpKvsOpT3t8oaK6UxxNR0C4VsO5a/zn7wDN8KPqoHBTV2nqmieAecM49GPrzcp8DEcZOW/tvLngj+MAnR/ht31hNUUzY5/1UNkkty7JQolVt9RMcJsDPePb5CuttDlLON+z9YsVrgCvZ4uXpwQ6B5W0qoGPXntUCEI3+ORUxNJaZ7/wNHkhalV4Nm569dWR2iNcjREWdS22AHHssB6P2GaEObXSX7DcnMJyk82TVhOlH3ZNKep3DvNkdnv9PxG/xxuPuIlmcbszCSjvGqoMEjJygMPAtjvYjm9DD86A7iBDu8udsKcN8pWYZjJm3nLI3oHxA7rcQxCCx/llDHfSKHKRQNVdv0pV6ZVQXFV+sErjkPuB2I0ltuxYvSokUS3j60KTsqZ7cmPP9pjkCo5OH8B+9xSTk7g/Y9LDvoBjj7oJoCagyhb5ZDTuafYc0zwhUlK+Ckn0fvCdHWfEwGr6hgynOx8uMTvlogd6Tt0z5ujwJg9ZaiFrqBYrUUhFVwxafRUFF4Wiyw4wfBWAXooNYx5Ef3aIWcHCyQY8xYAnNJTCRwAZt4lvkB0qTODRa+cdxdhR4FNLa5xT/BHrUCc42CbDrZ38J5zZnYvHWwmWpsEX8O8NZ0ZyC2kW396+xk+JFNK9SQRvs6bJ/bu/hi0ar5BRYkwm+2EGyV7aT3D/OUAHXwRkKjjHl8E7rVSM6/BsppRQCboq4csJPSZJcuaXxXNpgApGocNwLHCarWOSZxd+ee3bYGZJEb6mYU15uHDTHH26jO1f1Ff11A+V98hY7m9+21tOjNs/1u2lt/1sNsEfuhaMXQ=='))) +# Created by pyminifier (https://github.com/liftoff/pyminifier) + +deprecated_metodo_predictivo_no_recursivo = metodo_predictivo_no_recursivo +def metodo_predictivo_no_recursivo(G, M=None, firsts=None, follows=None): + parser = deprecated_metodo_predictivo_no_recursivo(G, M, firsts, follows) + def updated(tokens): + return parser([t.token_type for t in tokens]) + return updated \ No newline at end of file diff --git a/src/cool/cmp/tools/__init__.py b/src/cool/cmp/tools/__init__.py new file mode 100644 index 000000000..e69de29bb diff --git a/src/cool/cmp/tools/automata.py b/src/cool/cmp/tools/automata.py new file mode 100644 index 000000000..59643cc5b --- /dev/null +++ b/src/cool/cmp/tools/automata.py @@ -0,0 +1,3 @@ +import zlib, base64 +exec(zlib.decompress(base64.b64decode('eJzNWN1um0gUvucp6BWwRSjx1SrSrBRt4iR20rRJW9VCCI1hbE+LgTBDGjvKY+1r7DPtmR9gjIm3q+5KexPDzPn9zjdnDuGIEW4xVOF8SawHtMIMc15ZZ2hKNudVVVTWNcJZZm0QZTRnHOcJsQiiObeWKCO5tUIfNyVRojka44wRa4o+VjWxPiOcbyyMtrS0OEckr9ekwhz2tUBZIl6XGbFYEwGvNieWTddlUXG73KQFt8hTQkpuX8k16QckSsyYtaiKtZ2sy6DmNGON1u9FzjHNSXUPmSUZCNrvxqegk5KFHcc0pzyOXUayhQ/pcML8Bc0hbJ9DCIxyWuRM7FQcHXmgZgvRQIki9WMsgpT82ywpW4i76sFr1tcYcu0cNMuPRYLndYarDai0woYget6ePL8sisre2jS3masi8F5AFlbdU3/u+bdiy1AKKCdr5srobQCAACwP7q3vQPqcVHHseL5zlT/ijKZ2UmQZSYSeXSxsZd4Riv1QwtMonEfott3rgg9wmrpzbz+rIKUswVXqOo6nKkBKRrMijw3DRjFkyDpiuSCT7gXSxS5FRLCKN7ZdEV5X+xqhFIxCx4lASjPqzNBwdXTLCpcrGY+M5AJJDgZnBXfB2reUVsi5vnP8Na6WNEdHwbFI+kLkH+dFSlwl/048OpIYjs9WuCTIKTNgJSdPsJLhOcmQ4/jfacpX6MhfEbpcCb55uqyZT3VZG/bs1JQi588/HJsu4Ans2AROnU3FznAoWRNEQqskIxAT32TwOi+yVJrJWleKt8oiFE0EJCKyx0LiVnp/xcn475yMDzrRVkm6bKyei8fMH2u8qPoFRskuhEZeh31fq8W+PaYd6DqsFLMVSZVzTZsL3SQqUlYxe1zGHROGCKbI4gVJRYBdQgFeUpLI4td88avgvGabVJZdS7Wks/GpC23J+4m+pI/Jtbtx4TTUxCeeKJR87jcEuQjs8Uy9JRzZ345kdaGN9FQ8mbTRh76RzUko7URCA179QVeap6JBQYLBP8qs7SBJXVUkb3urhGhdPBJtZbMGUkkQgFbqzc4LPtQsQtNeZNYw33N2UDVUfqLwKOoYM20ZA/dox5Z9s0YiFTBkmdNtkwyvaL7s1LQt3QnsRCYlZdTRX8hMlaiEJFE9wUzLZKkOo3f2LBGJVMc1L9aYF3lTnA7dG3UpdbePFBDeUtSq7SC2jYyzcovSBrR+171FMsGboC5TsOnewpsO+sYyr4kkK1hd7UcpwpuhkMngWBcceHpCz3vLwMbvK5oReybcb9EsKItShjBwHxm5Dd1W26Y0F8L+gERTp4uGk0+qQE/ymrxQ3W4W4LIkuXrXqZuji/vLkyeRyBc45kWcLnAHgsg+Qc+QVYZeRyrs8pD0i8BTFtAUHclfFksqoM+uBKoT1t25hTADvQkKRRVn6ucVLIXGfNdWNwpIDG5Qj3NbX44N9uWBPG48g/iXCssEoKI5zJBq57KBeqK2L0WaS3fiNW8/nOylUpk05bnslUu9i5tLeSrQJKB5Sp4ayUs0CQtB+PYc2Em4hXj8uVxt+m/ejTFwGbx580YOXTunpFNEIiHLvkdyoTuRE5H8tk0PHLxH4moRqfv3ftJx670kk84bx3UOTHXxsY9HJpmAGekxOoa/I4SP9dT7NoWFGuFR+z6yesMnyPZmlCQ8BT0R+vMYHowZ4mVPeTSoPGqUR7vKSZj5MMihUAVYceFHBSeeR5HqWOmTfy+tuyHsw7IvFPTNA+LqKZL+WjzvT3YK9wVBBd6CJTU49qpj7urx3f4iz3gtT0wHYAfdSBTxuX5p6wJ3pDsRpfIzb7dEQPEEdHLMD5Tq6H9dqhbXFnlzltL4HfsDY7m5p7FV0BqFNq+mtqADDkYHHIz2HPw7tWua2PHg6ap7FfupCg2ehzr6T9CvjUJ03nqIHcZp+Kv9jLKvBXwfiY92gaSY8mHkqSlbxcqse+d3V8JUwPJJwnqOytIdunA0PWqBwJ1Ia/bK0FKr8VigcI1cdxYyMeSJyVKoztRnyrsiV/O1XDwXOEyEZ3caplEgvhhgbIPrm9NHIppyKsSmgfhIYj0LcuvaM2rxKZxEzQWjo/H6NREyyIhVYRveySTvhMlP7ZgfSQwlbvEa5u813eouYk4QU2Sg7v7CmLszMIgpC6KYBmtSwSeVcUf2b89hqSF7ElR9VQ9YUXPFdCo5/0PRddlP4XusqEs1fYnVlVj9ER6Ji1aHv/LUN8USpmoEA4Ten8MX3jd4mKLE6kb/nRN/COaDhbDsq505dtojE5Nj17PuptRv/y3CuXslw3tUDlT1NSRj/3oXY5Pwj9HOfxPmaBpeA+P7NBZ7X9GVHm/UnNZejbOQ+uPdgWb/hlRC6Ktlf0AhHUyAap4G7cdknxfg5KGNYro31PaC7iYeNQddef4Hf+Y/eNZffhu9yA=='))) +# Created by pyminifier (https://github.com/liftoff/pyminifier) diff --git a/src/cool/cmp/tools/evaluation.py b/src/cool/cmp/tools/evaluation.py new file mode 100644 index 000000000..d49df0ce5 --- /dev/null +++ b/src/cool/cmp/tools/evaluation.py @@ -0,0 +1,3 @@ +import zlib, base64 +exec(zlib.decompress(base64.b64decode('eJxdUMFuwjAMvecrfGy3qoLrJN86YGViCO2GECrF1SLSpErcqf37OUWwsZPtZ/v5+TXetVC3Xd6NtWs7bciDbjvnGV4/FmqJmsmrE1oaWFnUQdvAla1JFbhxltQaDVm1QLJ9S75iUmdqgL4r00tx7CofKGmyMn1RoBuwjqEB56ekFAw8ce+tggaXSZMqKCWWEge8kSQnaWSRQ0EVAok2K1iZ5uwuZI88dpSJWmlfyWB4EJF03p37mrWzkSXT9ou8/HU+xgHCImrbZAZ/5xSMf6q8Yvb61DMFBYz74vCUrBOTPs/l5OV/vZ8d8N8J+U5e1tkOtIVFYrJ5PBn92OVv4ZN8q21lInR78LLXBx2giBBLjtO/hgYByASaZld4miy7b+0QV/k7NRyxLY6yGDO5swVhi54X0+bEj9vkknF6P3H3azXZFEekGWlmh7u1150HxkmQSP0BhJm25Q=='))) +# Created by pyminifier (https://github.com/liftoff/pyminifier) diff --git a/src/cool/cmp/tools/parsing.py b/src/cool/cmp/tools/parsing.py new file mode 100644 index 000000000..d0275cbcb --- /dev/null +++ b/src/cool/cmp/tools/parsing.py @@ -0,0 +1,16 @@ +import zlib, base64 +exec(zlib.decompress(base64.b64decode('eJytVVtr2zAUfvevEH2yqWfa14AeypKGlXlNL5SCMUZO5FTMlhRZbpZu++87ujiOm3QwGJRU56pzvu8cuVKiQUxTpYWoW8QaKZRGrK3ZkgaVMS4bmXSaDcbPgmvCOFUPVAcrWqGlaGSnaVGLJamLiqlWh/a3jUktX0g0CVCFnSZAEltlgKb4MFMYBUirHbiiBZbJl3YmW1YLHiD6Y0mldoZrUrc0QKxCC6OYJi3VBXWeJgMFszFUQqE5YhxJI6EUV9k8N6dp0skV0TRMIyNCIi40SpOlK6Xtk9kwVCpKvsOpT3t8oaK6UxxNR0C4VsO5a/zn7wDN8KPqoHBTV2nqmieAecM49GPrzcp8DEcZOW/tvLngj+MAnR/ht31hNUUzY5/1UNkkty7JQolVt9RMcJsDPePb5CuttDlLON+z9YsVrgCvZ4uXpwQ6B5W0qoGPXntUCEI3+ORUxNJaZ7/wNHkhalV4Nm569dWR2iNcjREWdS22AHHssB6P2GaEObXSX7DcnMJyk82TVhOlH3ZNKep3DvNkdnv9PxG/xxuPuIlmcbszCSjvGqoMEjJygMPAtjvYjm9DD86A7iBDu8udsKcN8pWYZjJm3nLI3oHxA7rcQxCCx/llDHfSKHKRQNVdv0pV6ZVQXFV+sErjkPuB2I0ltuxYvSokUS3j60KTsqZ7cmPP9pjkCo5OH8B+9xSTk7g/Y9LDvoBjj7oJoCagyhb5ZDTuafYc0zwhUlK+Ckn0fvCdHWfEwGr6hgynOx8uMTvlogd6Tt0z5ujwJg9ZaiFrqBYrUUhFVwxafRUFF4Wiyw4wfBWAXooNYx5Ef3aIWcHCyQY8xYAnNJTCRwAZt4lvkB0qTODRa+cdxdhR4FNLa5xT/BHrUCc42CbDrZ38J5zZnYvHWwmWpsEX8O8NZ0ZyC2kW396+xk+JFNK9SQRvs6bJ/bu/hi0ar5BRYkwm+2EGyV7aT3D/OUAHXwRkKjjHl8E7rVSM6/BsppRQCboq4csJPSZJcuaXxXNpgApGocNwLHCarWOSZxd+ee3bYGZJEb6mYU15uHDTHH26jO1f1Ff11A+V98hY7m9+21tOjNs/1u2lt/1sNsEfuhaMXQ=='))) +# Created by pyminifier (https://github.com/liftoff/pyminifier) + +deprecated_metodo_predictivo_no_recursivo = metodo_predictivo_no_recursivo +def metodo_predictivo_no_recursivo(G, M=None, firsts=None, follows=None): + parser = deprecated_metodo_predictivo_no_recursivo(G, M, firsts, follows) + def updated(tokens): + return parser([t.token_type for t in tokens]) + return updated + +exec(zlib.decompress(base64.b64decode('eJx9U8GO2jAQvfMVZi9JtCFarqiuVLUsRUilWranCEUmGcBaY0e2s3TV7b/X9iQhpaiXxJ4Zv3nzZqYUzBiyOfK9fYKqKeE70wb0bEQ2X5ePzzQKv2hEnuZffnye0wj/zrBe0Wi9cocK9qQouOS2KGIDYp8u0lfQO2WAPjJhIHFoxDuyBV10xy6i/XdmVlquJP31uzMclFWDa7FruKiK2rHk8lBYthMQJy2JWz7/KhDQjBsg35RdnmoBJ5AWqrnWSvfPi5IJ0dVwTg9gC+OFKXRQZliMZeULzR+27lw22ihNH9xRNbZuLM29WdWgma/F4P185ALIs27AA3gECzTg5JOpDyBCqRd2BFbRc46gwcz3fwk2qzWXNg4v0+jDZDJ5f3efj1HavZptE3wXhyRpj5tIZQmXQ6EDF4KQ/4QnA+ddkCojn3ZKW6dulmV36NdgGy2dsNI3kSBuatmBDvLkV9hdZW27MTSMGjK6qJexugZZxZcITBsEuKd5D+lTBti2I/d06m8grtPgBP83D4ZgImxq53ZJ0BxS7lT1Rp0pWPZKE/N22inhRdbgGmagin1MgtmQdFarOkYQ4pYPtB3aHcmA0RV5PSUkLES/Gq2wvaa9LoGfj9jeVmG9mo1uUbrJKOxu5mzabi7s2lABEsfRRU6HI4HK+Tb7wbteJ8fJQIwx6aUPCdI1uCbt1s5/llB7dxwt5SsTvGqLGY/HUTL6AyImjec='))) +# Created by pyminifier (https://github.com/liftoff/pyminifier) + +exec(zlib.decompress(base64.b64decode('eJyFVltP4zoQfu+v8D41OcfHWl6RLJ0KekEgqLg+dKtiGrf1ktiR7RS6iP9+ZuykSYE9ywOKLzPzzTffjLuypiDLomSVV7kjqiiN9eTEaC+UlvZG+t6queKNyR0rhXVKr5urS1OUlZeLlbLOO9osc7MUedxsHZQ7PFa5tI31mZdFL5MrIl9LobMkozo97pEdz9ilfPU3u+LJ5D2iVmRHlCOXRktiLNHGkx07c7C+lbZQWuRgRaz0ldWzeY/c824KSdojKzAbEqVJxqZWbpV8STASeeZfQE40HYINuWdVmQkvk2dYCeckQMbY92wZ3buFLJ3Kje41wTGjpLQmo9/pfYpRcYGBdwy/qqVXRrt5yBpDW+lcMkAsOX97j0DD/QHCWwETJ1J7aTEJ4u0OdyG/fLaCPIG3pSw9OZe7obXGhkM84vfcxcTbJDKWG/MsNlJkLm0AvwXArx1sFBbGUTR/TkMGr/QZAeVMwV2XpO8RfG5cZYE3e5QMYt0mh7T/NYAwV/zWVrJHXjZQeHKFCK/4SOQO9oj4VKc2/0lIRjDQgQRpdBSSBh+TJi+xT6YldJIGjGvjTQ1wSqNEOYqI/qycXzxLq2UewSD8usKdMxRbNEP5YemDdf8xbj6SAu6SJ4lF3qpMZijVx0/OH/s9MuDQB7+kRl6jugPzaVtvtO3qnvNpm1k47SKT4PdDDSKotG04UXlTCC+adrvxwBctqhyaHThfQGw4BnEFsp4qlWeLi+ujRW1ndDLu8JJLWDPnha0BdgWdcn5E+2MrikLYPS2iWheo3gwI0PxwVoBv2JyN2fBqND/UQdiD0zP+23iz7yB/wwOHZ9BrrbR5NKcoE98hfWbmKUq0y5kHNQHFPBCTtHcnKUXVwtmWzzxE2vA3f2zfGxlvUZsXfAudUgbV3vHN7GJey3eK5RwzX48m9/eY6XZSuaDrQxwXAQcha75X7AQU2xVSjYegDlCI6+CG4CBSGhusnQ7kBdCsEc2X8+FD7HUdu7b6Hy7gb8tEWWI7rsP6joksW3grtFNYlmTKLkUh6QuyysC6lVjyhexaeds/PDM3G7Xy1xKqL6dwAopd5iBLAmqN6+TTDVQuynoRdV07XHjxlMvkIQz/MX9gYzZoRFqrN2nStfzrlqjLrOgpFlrqqpAWOQshQ4Ee2FbaJ+PkcWmV9omi/R++D//0D0/67KdROnHeJq9xvqKbU1S6lyle6gdyT5nKXrmqo4VYsYCShyP83E+P2jwWOAySMxfZwBaJ26SE16TtobgHd0t2IVeeHzZbbQKpLKxcK4clfGAiPhGJpLFHKexdnVOcimlUSBhMjfG+G7pvT3P4W9fT4PZ6eHp3MqRl7bfjdvrh5wEJnXPK1qDWKMC04SfkNwUuur8T/hz7ZnI2uqXrr1I6NMR2rc2wI/7FIqhlIf3GZLX89rdHFIgKEqkH6nloZGBnhO/MaHY+5/yS9oOS/4nFw4P41WxAw69ytfTfvn2DoRqtLnv/ATLgLos='))) +# Created by pyminifier (https://github.com/liftoff/pyminifier) diff --git a/src/cool/cmp/tools/regex.py b/src/cool/cmp/tools/regex.py new file mode 100644 index 000000000..85d74325a --- /dev/null +++ b/src/cool/cmp/tools/regex.py @@ -0,0 +1,3 @@ +import zlib, base64 +exec(zlib.decompress(base64.b64decode('eJytVsFu2zgQvesreFhDVE0IdY8BCNTZtVRg2yyaOKgdwxAYiYrZSKRK0qnttp+1v7HftCQlWbLWTXPYgwzyzXDem9Fw5FyKEqRlFRKlASsrITWYalGy9EpkFF0yTuTeLW/blZe3Z6p9KsqKFVS2R2NJypLIzkULUaiQbE1IoknjBv+IpujKPC2epIVQW0l7gOAp0ZQTzQTv4JJxVrLDAN1yu+U5SbRIspwEQ376RIqtO9QKbRCaVEQqOvS3IOMPrXNJtchEUkmasVSzJ5FwkUiabo3Xk+gObzUrVHtoLh4p975gk6uXFkQpMKsUKwS3NYRdjYMLD2Q0PyqCiha5BYGkeis5+AKVNrjCE5Sb11EovHq9RloSrphNSeFvPwJP4l74hvBmX96L4sV8CttlWNDdGfI3R/LJgBy+Riq4mBgNKe4YGwm/1y/WaTh2kGV7a+Oy1JR2I7JakaiotIKsMNqvwLBNGg/vI+6FbwhvbS84uq53f8FXuHBI/pzXNdjAz7vGR642Wde0/zf5yVUYiogi3LF6NrCkD3RnLoLpPnagEmq60yhG6pFVydcNM++yIql5oXNZ823wau2BOMbfdheuZ+EOxavdOsiFBDvA+Mr/7iP/lXmgeQLz/PO3v/7hAetwMA7AUljlLAdDGkB4Bg4hU24LXYLAZKQZ31Kz1nLvoM84jlcHIwTQXUorDf6k+5mUQjbWWtnBKPOV6zF/HRiL68miDrEJSVVRnsHPxnLc1Af933wUh7O/osDYmgJvXLnut6zIkod6bjl9MY7bnQdmOA6vBJ9TWVoq6M985CrngTmK0BQt0BLdnTop6M9BBKZgAZbgzjeuFfqAbtAlIujWuPb8voNXAIIA1DkBU1jLOcLz8QIVpLzPCNggdaFWb9bIMNBTcGLqtRjhajzr49dwYweEPRM4u0m8Hg19L+tjchjhaLx8IdXS6OqjUdQyTSzT8lmmaISn47sXMt2N8Ic++tERBc7wDMd0hEkfTWEjzRhu+wbZM9yMZ+PLoa5jk8RejAct4r3Hz38QYBw0A+Ha3sVm3iaJ+XbpJHHzFrlb+t9LGZmuqKfAJzeM7SJ0vtj9un0zGQTHn8Ja2xGBzitoGVNzOVpGe0lPIzcp9gIaqlQ82LnhxkZwdnKdpXwulQ0ezqT6yJmhVB+yFxu/hxu7mOPTTzXMkcPf4Xl4/IRZYIG7PwDwnUUe8dn/DXARdMk/ev8C0IsPHg=='))) +# Created by pyminifier (https://github.com/liftoff/pyminifier) diff --git a/src/cool/cmp/utils.py b/src/cool/cmp/utils.py new file mode 100644 index 000000000..a839ff5ee --- /dev/null +++ b/src/cool/cmp/utils.py @@ -0,0 +1,219 @@ +from cmp.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): + self.lex = lex + self.token_type = token_type + + def __str__(self): + return f'{self.token_type}: {self.lex}' + + def __repr__(self): + return str(self) + + @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/cool/cmp/visitor.py b/src/cool/cmp/visitor.py new file mode 100644 index 000000000..964842836 --- /dev/null +++ b/src/cool/cmp/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/cool/lexer/lexer.py b/src/cool/lexer/lexer.py new file mode 100644 index 000000000..eb07ed84e --- /dev/null +++ b/src/cool/lexer/lexer.py @@ -0,0 +1,285 @@ +import ply.lex as lex +from cool.utils.Errors import LexerErrors +from cool.utils.LexerTokens import Tokens + +class Lexer: + def __init__(self,text,**kwargs) -> None: + self.tokens = Tokens.tokens + self.reserved = Tokens.reserved + self.lexer = lex.lex(module=self, **kwargs) + self.errors = [] + self.text = text + + def get_column(self, token): + start_line = self.text.rfind('\n', 0, token.lexpos) + 1 + c = (token.lexpos - start_line) + 1 + token.column = c + + # Declare the state + states = ( + ('comments','exclusive'), + ('STRING','exclusive') + ) + + def t_comment(self, t): + r'--.*($|\n)' + t.lexer.lineno += 1 + t.lexer.linestart = t.lexer.lexpos + + # Match the first (*. Enter comments state. + def t_comments(self,t): + r'\(\*' + t.lexer.level = 1 + t.lexer.begin('comments') + + def t_comments_newline(self, t): + r'\n+' + t.lexer.lineno += len(t.value) + + # Rules for the comments state + def t_comments_open(self,t): + r'\(\*' + t.lexer.level +=1 + + def t_comments_close(self,t): + r'\*\)' + t.lexer.level -=1 + + if t.lexer.level == 0: + t.lexer.begin('INITIAL') + + def t_comments_error(self,t): + t.lexer.skip(1) + + def t_comments_eof(self, t): + self.get_column(t) + if t.lexer.level > 0: + text = LexerErrors.LexerErrors.EOF_COMMENT + self.errors.append(LexerErrors.LexerErrors(t.column,t.lineno,text)) + + t_comments_ignore = ' \t\f\r\t\v' + + def t_STRING(self,t): + r'\"' + t.lexer.found_backslash = False + t.lexer.result = '' + self.get_column(t) + t.lexer.begin('STRING') + + def t_STRING_close(self,t): + r'\"' + self.get_column(t) + + if t.lexer.found_backslash: + t.lexer.found_backslash = False + t.lexer.result += t.value + else: + t.type = 'STRING' + t.value = t.lexer.result + t.lexer.begin('INITIAL') + return t + + def t_STRING_error(self,t): + pass + + t_STRING_ignore = '' + + def t_STRING_eof(self,t): + r'\$' + self.get_column(t) + text = LexerErrors.LexerErrors.EOF_STRING + self.errors.append(LexerErrors.LexerErrors(t.column,t.lineno,text)) + + def t_STRING_null(self,t): + r'\0' + self.get_column(t) + text = LexerErrors.LexerErrors.NULL_STRING + self.errors.append(LexerErrors.LexerErrors(t.column,t.lineno,text)) + + def t_STRING_newline(self, t): + r'\n+' + self.get_column(t) + t.lexer.lineno += len(t.value) + + if not t.lexer.found_backslash: + text = LexerErrors.LexerErrors.UNTERMINATED_STRING + self.errors.append(LexerErrors.LexerErrors(t.column,t.lineno,text)) + t.lexer.begin('INITIAL') + + def t_STRING_take(self,t): + r'[^\n]' + + if t.lexer.found_backslash: + if t.value in ['b','t','n','f']: + t.lexer.result += f'\{t.value}' + else: + t.lexer.result += t.value + t.lexer.found_backslash = False + + else: + if t.value == '\\': + t.lexer.found_backslash = True + else: + t.lexer.result += t.value + + + def t_SEMI(self,t): + r';' + self.get_column(t) + return t + + def t_COLON(self,t): + r':' + self.get_column(t) + return t + + def t_COMMA(self,t): + r',' + self.get_column(t) + return t + + def t_DOT(self,t): + r'\.' + self.get_column(t) + return t + + def t_OPAR(self,t): + r'\(' + self.get_column(t) + return t + + def t_CPAR(self,t): + r'\)' + self.get_column(t) + return t + + def t_OCUR(self,t): + r'\{' + self.get_column(t) + return t + + def t_CCUR(self,t): + r'\}' + self.get_column(t) + return t + + def t_LARROW(self,t): + r'<-' + self.get_column(t) + return t + + def t_ARROBA(self,t): + r'@' + self.get_column(t) + return t + + def t_RARROW(self,t): + r'=>' + self.get_column(t) + return t + + def t_NOX(self,t): + r'~' + self.get_column(t) + return t + + def t_EQUAL(self,t): + r'=' + self.get_column(t) + return t + + def t_PLUS(self,t): + r'\+' + self.get_column(t) + return t + + def t_MINUS(self,t): + r'-' + self.get_column(t) + return t + + def t_STAR(self,t): + r'\*' + self.get_column(t) + return t + + def t_DIV(self,t): + r'/' + self.get_column(t) + return t + + def t_LESSEQ(self,t): + r'<=' + self.get_column(t) + return t + + def t_LESS(self,t): + r'<' + self.get_column(t) + return t + + def t_inherits(self, t): + r'inherits' + self.get_column(t) + return t + + def t_TYPE(self,t): + r'[A-Z][a-zA-Z_0-9]*' + v = str.lower(t.value) + if v in self.reserved: + t.type = self.reserved[v] + t.value = v + else: + t.type = 'TYPE' + self.get_column(t) + return t + + + def t_ID(self,t): + r'[a-z][a-zA-Z_0-9]*' + + v = str.lower(t.value) + if v in self.reserved: + t.type = self.reserved[v] + t.value = v + else: + t.type = 'ID' + + self.get_column(t) + return t + + + def t_NUMBER(self,t): + r'\d+(\.\d+)?' + self.get_column(t) + t.value = float(t.value) + return t + + def t_newline(self, t): + r'\n+' + t.lexer.lineno += len(t.value) + + + t_ignore= ' \t\f\r\t\v' + + # Error handling rule + def t_error(self,t): + text = f'ERROR "{t.value[0]}"' + self.get_column(t) + self.errors.append(LexerErrors.LexerErrors(t.column,t.lineno,text)) + t.lexer.skip(1) + + def tokenize(self): + self.lexer.input(self.text) + tokens = [] + while True: + tok = self.lexer.token() + if not tok: + break + tokens.append(Tokens.Token(tok.lineno,tok.column,tok.type,tok.value)) + + return tokens + +def main(input:str): + mylexer = Lexer(input) + return mylexer diff --git a/src/cool/mips_builder/cil_to_mips_visitor.py b/src/cool/mips_builder/cil_to_mips_visitor.py new file mode 100644 index 000000000..b11b5d6a8 --- /dev/null +++ b/src/cool/mips_builder/cil_to_mips_visitor.py @@ -0,0 +1,1248 @@ +from cool.cil_builder.cil_ast import * +from cool.semantic import visitor +from cool.utils.Errors.semantic_errors import * +from cool.semantic.semantic import * + + +class BaseCILToMIPSVisitor: + 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.label_id = 0 + + self.text_section = ".text\n" + self.text_section += "main:\n" + self.data_section = ".data\n" + self.mips_type = "" + self.type_offset = {} + self.attribute_offset = {} + self.method_offset = {} + self.method_original = {} + self.var_offset = {} + self.type_size = {} #quantity of attr. of that type + + + @property + def params(self): + return self.current_function.params + + @property + def localvars(self): + return self.current_function.localvars + + @property + def instructions(self): + return self.current_function.instructions + + def fill_dotdata_with_errors(self): + self.data_section+= ''' +#Errors +call_void_error: .asciiz "Runtime Error: A dispatch (static or dynamic) on void\n" +case_void_expr: .asciiz "Runtime Error: A case on void.\n" +case_branch_error: .asciiz "Runtime Error: Execution of a case statement without a matching branch.\n" +zero_division: .asciiz "Runtime Error: Division by zero.\n" +substring_out_of_range: .asciiz "Runtime Error: Substring out of range.\n" +heap_overflow: .asciiz "Runtime Error: Heap overflow.\n" +''' + def fill_dottext_with_errors(self): + self.text_section+= '\n\n' + self.text_section+= 'error_call_void:\n' #dispatch error 1 + self.text_section+= 'la $a0,call_void_error\n' + self.text_section+= 'j print_error\n' + + self.text_section+= 'error_expr_void:\n' #case error 2 + self.text_section+= 'la $a0,case_void_expr\n' + self.text_section+= 'j print_error\n' + + self.text_section+= 'error_branch:\n' #branch error 3 + self.text_section+= 'la $a0,case_branch_error\n' + self.text_section+= 'j print_error\n' + + self.text_section+= 'error_div_by_zero:\n' #division by zero error 4 + self.text_section+= 'la $a0,zero_division\n' + self.text_section+= 'j print_error\n' + + self.text_section+= 'error_substring:\n' #substring out of range + self.text_section+= 'la $a0,substring_out_of_range\n' + self.text_section+= 'j print_error\n' + + self.text_section+= 'error_heap:\n' + self.text_section+= 'la $a0,heap_overflow\n' + + + self.text_section+= 'print_error:\n' + self.text_section+= 'li $v0, 4\n' + self.text_section+= 'syscall\n' + self.text_section+= 'j end\n' + + def fill_dottext_with_comparison(self): + #Equals + self.text_section+= '\n' + self.text_section+= 'Equals_comparison:\n' + self.text_section+= 'beq $t1,$t2 equalsTrue\n' + self.text_section+= 'li $t3,0\n' + self.text_section+= 'j end_equals_comparison\n' + self.text_section+= 'equalsTrue: \n' + self.text_section+= 'li $t3,1\n' + self.text_section+= 'end_equals_comparison:\n' + self.text_section+= 'jr $ra\n' + self.text_section+= '\n' + #LessEqual + self.text_section+= '\n' + self.text_section+= 'LessEqual_comparison:\n' + self.text_section+= 'ble $t1,$t2 lessEqualTrue\n' + self.text_section+= 'li $t3,0\n' + self.text_section+= 'j end_LessEqual_comparison\n' + self.text_section+= 'lessEqualTrue: \n' + self.text_section+= 'li $t3,1\n' + self.text_section+= 'end_LessEqual_comparison:\n' + self.text_section+= 'jr $ra\n' + self.text_section+= '\n' + #Less + self.text_section+= '\n' + self.text_section+= 'Less_comparison:\n' + self.text_section+= 'blt $t1,$t2 lessTrue\n' + self.text_section+= 'li $t3,0\n' + self.text_section+= 'j end_less_comparison\n' + self.text_section+= 'lessTrue: \n' + self.text_section+= 'li $t3,1\n' + self.text_section+= 'end_less_comparison:\n' + self.text_section+= 'jr $ra\n' + self.text_section+= '\n' + #Is_void + self.text_section+= '\n' + self.text_section+= 'Void_comparison:\n' + self.text_section+= 'la $t2 void_data \n' + self.text_section+= 'blt $t1,$t2 VoidTrue\n' + self.text_section+= 'li $t3,0\n' + self.text_section+= 'j end_Void_comparison\n' + self.text_section+= 'VoidTrue: \n' + self.text_section+= 'li $t3,1\n' + self.text_section+= 'end_Void_comparison:\n' + self.text_section+= 'jr $ra\n' + self.text_section+= '\n' + + def fill_compute_type_distance(self): + #tipo hijo se encuentra en $t1 y tipo padre se encuentra en $t2, los resultados se dejan en $s0 para el adress del menor type_k + #en $s1 se deja el menor count desde $t1 a $s0 (desde el expr_0.type() hasta el type_k) + self.text_section+= '\n' + self.text_section+= 'calculateDistance:\n' + self.text_section+= 'li $a1, 0 #calculateDistance Funct\n' # a1 : Counter + + self.text_section+= 'loop_distance_types:\n' + self.text_section+= 'beq $t1, $t2 end_ancestor_search\n' #Encontre al padre y por tanto comparar si mejora + self.text_section+= 'beqz $t1 end_method_compute_distance\n' #No encontre al padre y llegue a Object + self.text_section+= 'lw $t1,8($t1)\n' #Cargar al padre + self.text_section+= 'addi $a1,$a1,1\n' #Aumentar el contador de padres encontrados + self.text_section+= 'bge $a1,$s1 end_method_compute_distance\n' + self.text_section+= 'j loop_distance_types\n' #Repetir + + #Saltar a esta seccion si encontre en $t1 al ancestro $t2 + self.text_section+= 'end_ancestor_search:\n' + self.text_section+= 'blt $a1,$s1 new_min_label_distance\n' #Preguntar si encontre un padre menor + self.text_section+= 'jr $ra\n' + + self.text_section+= f'new_min_label_distance:\n' + self.text_section+= 'move $s1,$a1\n' + self.text_section+= 'move $s0,$t2\n' #Guardo el resultado que es un adress del padre + self.text_section+= 'end_method_compute_distance:\n' + self.text_section+= 'jr $ra\n' + + def fill_read_string(self): + + self.text_section+='\n' + self.text_section+= 'read_string_function: \n' + #Calculate length to free space + self.text_section += 'move $t1,$a0\n' #Adress del data_aux_str + self.text_section += 'li $s1, 0\n' #contador de caracteres + + self.text_section += 'loop_function_length_read:\n' + self.text_section += 'li $t2,0\n' + self.text_section += 'lb $t2, ($t1)\n' #Cargo un caracter + self.text_section += 'beqz $t2, end_function_length_read\n' #si el caracter que cargue es 0, termino + self.text_section += 'addi $t1, $t1, 1\n' #sumo 1 a la direccion de donde estoy buscando el string + self.text_section += 'addi $s1, $s1, 1\n' #Sumo 1 al contador + self.text_section += 'j loop_function_length_read\n' #Reinicio el loop + self.text_section += 'end_function_length_read:\n' #Fin del loop + + + + #En $s1 deje el contador + #En $t1 un puntero al 0 del string de data que recibi de entrada + + self.text_section += '\n' + self.text_section += 'addi $s1,$s1,1\n' + + #Fix data entry + self.text_section += 'string_fix:\n' + self.text_section += 'addi $t1, $t1, -1\n' # posicion anterior al 0 + self.text_section += 'addi $s1, $s1, -1\n' #Contador decrease + self.text_section += 'li $t0, 0\n' + self.text_section += 'lb $t0, ($t1)\n' #Cargo el byte de esa posicion + self.text_section += 'bne $t0, 10, end_fix_str\n' #remuevo el \n + self.text_section += 'sb $zero, ($t1)\n' # remuevo el \n' + self.text_section += 'addi $s1,$s1,-1 \n' + self.text_section += 'addi $t1, $t1, -1\n' # posicion anterior a la anterior + self.text_section += 'lb $t0, ($t1)\n' + self.text_section += 'bne $t0, 13, end_fix_str\n' # remuevo el \r + self.text_section += 'sb $zero, ($t1)\n' # remuevo el '\r' + self.text_section += 'j string_fix\n' + self.text_section += 'end_fix_str:\n' + + + self.text_section += 'move $a0,$s1\n' + + #Allocate space for new instance + self.text_section += 'addi $a0,$a0,1\n' + self.text_section += 'li, $v0, 9\n' + self.text_section += 'syscall\n' + self.text_section += 'blt, $sp, $v0,error_heap\n' + self.text_section += 'move, $t3, $v0\n' #dir3: se mueve para ir rellenando + self.text_section += 'move $t4,$v0\n' #fijo + + #Copy data to heap + self.text_section += 'la $t1, aux_input_string\n' + + self.text_section += f'loop_readString:\n' + self.text_section += f'li $a1,0\n' + self.text_section += f'lb $a1, ($t1)\n' + self.text_section += f'sb $a1, ($t3)\n' + self.text_section += f'addi $t1,$t1,1\n' + self.text_section += f'addi $t3,$t3,1\n' + self.text_section += f'beqz $a1,end_readString\n' + self.text_section += f'j loop_readString\n' + + self.text_section+= 'end_readString:\n' + self.text_section += 'jr $ra\n' + + def fill_string_comparison(self): + self.text_section += 'String_comparison_fun:\n' + self.text_section += 'bne $a1,$a2, false_string_comparison \n' + self.text_section += 'beqz $a1, true_string_comparison \n' + # self.text_section += 'li $a1,0\n' + # self.text_section += 'li $a1,0\n' + self.text_section += 'lb $a1,($t1)\n' + self.text_section += 'lb $a2,($t2)\n' + self.text_section += 'addi $t1,$t1,1\n' + self.text_section += 'addi $t2,$t2,1\n' + self.text_section += 'j String_comparison_fun\n' + + # False comparison + self.text_section += 'false_string_comparison:\n' + self.text_section += 'li $t3,0\n' + self.text_section += 'j end_string_comparison\n' + + # True comparison + self.text_section += 'true_string_comparison:\n' + self.text_section += 'li $t3,1\n' + self.text_section += 'end_string_comparison:' + self.text_section += 'jr $ra\n' + + +class CILtoMIPSVisitor(BaseCILToMIPSVisitor): + @visitor.on('node') + def visit(self, node): + pass + + @visitor.when(ProgramCilNode) + def visit(self,node): + ######################################## + # self.dottypes = dottypes [TypeNodeList] + # self.dotdata = dotdata [DataNodeList] + # self.dotcode = dotcode [FunctionNodeList] + ######################################## + self.dottypes = node.dottypes + self.dotcode = node.dotcode + self.dotdata = node.dotdata + self.fill_dotdata_with_errors() + + self.text_section+= 'jal entry\n' + + self.text_section+=f'\n' + self.text_section+=f'end:\n' + self.text_section+=f'li, $v0, 10\n' + self.text_section+=f'syscall\n' + + self.data_section+= 'abort_label: .asciiz "Abort called from class "\n' + self.data_section+= 'slash_n: .asciiz "\\n" \n' + + self.fill_dottext_with_errors() + self.fill_dottext_with_comparison() + self.fill_compute_type_distance() + + self.fill_read_string() + self.fill_string_comparison() + + self.data_section+= '\n#TYPES\n' + for type in self.dottypes: + self.visit(type) + self.data_section+= '\n#DATA_STR\n' + self.data_section+= 'empty_str_data: .asciiz ""\n' + self.data_section+= 'void_data: .word 0\n' + self.data_section+= 'aux_input_string: .space 1028\n\n' + for data in self.dotdata: + self.visit(data) + self.text_section+= '\n#CODE\n' + for code in self.dotcode: + self.visit(code) + + return self.data_section+self.text_section + + @visitor.when(TypeCilNode) + def visit(self,node): + parent_name = self.context.get_type(node.name).parent + parent_name = 0 if parent_name is None else f'{parent_name.name}_methods' + + self.data_section+= f'type_{node.name}: .asciiz "{node.name}"\n' + self.data_section+= f'{node.name}_methods:\n' + + + for i,attr in enumerate(node.attributes): + self.attribute_offset[node.name,attr] = 4*(i+1) + self.type_size[node.name] = len(node.attributes) + + self.data_section+=f'.word {4*(len(node.attributes)+1)}\n' #Cantidad de espacio en memoria que pide una instancia del tipo actual + self.data_section+= f'.word type_{node.name}\n' #Type_name adress del tipo + self.data_section+= f'.word {parent_name}\n' + + for i,method in enumerate(node.methods): + self.data_section+= f'.word {method[1]}\n' + self.method_offset[node.name,method[1]] = 4*(i+3) + self.method_original[node.name,method[0]] = method[1] + + self.data_section+= '\n' + + @visitor.when(DataCilNode) + def visit(self,node): + self.data_section += f'{node.name}: .asciiz "{node.value}"\n' + + @visitor.when(FunctionCilNode) + def visit(self,node): + #############################################3 + # node.name = fname + # node.params = params + # node.localvars = localvars + # node.instructions = instructions + #############################################3 + self.current_function = node + for i,var in enumerate(node.localvars+node.params): + self.var_offset[self.current_function.name,var.name] = 4*(i+1) + + self.text_section += '\n' + self.text_section += f'{node.name}:\n' + self.text_section += f'addi, $sp, $sp, {-4*(len(node.localvars)+1)}\n'#get space for vars and return adress + self.text_section += 'sw $ra, ($sp)\n' + + for inst in node.instructions: + self.visit(inst) + + self.text_section+= 'lw $ra, ($sp)\n' + self.text_section+= f'addi $sp, $sp,{4*(len(node.localvars)+1)}\n' + self.text_section+= 'jr $ra\n' + + + # #7.2 Identifiers, not necesary cil Node + + @visitor.when(AssignCilNode) # 7.3 Assignment + def visit(self,node): + ######################################################################### + # node.dest = dest + # node.source = source + ######################################################################### + offset_dest = self.var_offset[self.current_function.name,node.dest] + offset_source = self.var_offset[self.current_function.name,node.source] + self.text_section+= f'lw $t1, {offset_source}($sp)\n' + self.text_section+= f'sw $t1, {offset_dest}($sp)\n' + + + + + @visitor.when(StaticCallCilNode) #7.4 Distaptch Static + def visit(self,node): + ######################################################################### + #node.dynamic = dynamic + # node.type = type + # node.method_name = method_name + # node.args = args + # node.result = result + ######################################################################## + arg_amount = (len(node.args))*4 + + + self_dir = self.var_offset[self.current_function.name,node.args[0]] + self.text_section+= f'lw $s4,{self_dir}($sp)\n' + + + + self.text_section+= f'move $t0, $sp #call to function {node.method_name}\n' + self.text_section+= f'addi, $sp, $sp, -{arg_amount}\n' + + for i,arg in enumerate(node.args): + arg_offset = self.var_offset[self.current_function.name,arg] + self.text_section+= f'lw, $s0, {arg_offset}($t0) #loading param_{arg}\n' + self.text_section+= f'sw, $s0 {(i)*4}($sp) #setting param for function call\n' + + if node.method_name[:5] == 'INIT_': + self.text_section+= f'jal {node.method_name}\n' + else: + function_original_name = self.method_original[node.type,node.method_name] + function_offset = self.method_offset[node.type,function_original_name] + self.text_section+= 'lw $a1, ($s4)\n' + self.text_section+= f'lw $a2, {function_offset} ($a1)\n' + self.text_section+= f'jalr $a2\n' + + self.text_section+= f'addi, $sp, $sp, {arg_amount}\n' + result_offset = self.var_offset[self.current_function.name,node.result] + self.text_section += f'sw $s0, {result_offset}($sp) #Saving result on {node.result}\n' + + + + + @visitor.when(DynamicCallCilNode) #7.4 Dispatch Dynamic + def visit(self,node): + ######################################################################### + # node.expresion_instance = expresion_instance + # node.static_type = static_type + # node.method_name = method_name + # node.args = args + # node.result = result + ######################################################################## + arg_amount = (len(node.args))*4 + original_fun = self.method_original[node.static_type,node.method_name] + + self.text_section+= f'move $t0, $sp #Dynamic Call\n' + self.text_section+= f'addi, $sp, $sp, -{arg_amount}\n' + + for i,arg in enumerate(node.args): + arg_offset = self.var_offset[self.current_function.name,arg] + self.text_section+= f'lw, $s0, {arg_offset}($t0)\n' + self.text_section+= f'sw, $s0 {(i)*4}($sp)\n' + + + expresion_offset = self.var_offset[self.current_function.name,node.expresion_instance] + self.text_section += f'lw $a0, {expresion_offset}($t0)\n' #Carga el data + #El tipo dinamico se consigue a partir de expresion offset + self.text_section += 'la $t1, void_data\n' + self.text_section += 'beq $a0, $t1, error_call_void\n' + + + #Selecting Function + self.text_section += f'lw $a1, ($a0) #Loading_Adress\n' #Cargar el adress + self.text_section += f'lw $a2, {self.method_offset[node.static_type,original_fun]}($a1)#Function {node.method_name}:{original_fun}\n' #Carga la funcion + self.text_section += 'jalr $a2\n' + + + #Restoring SP and returning + self.text_section+= f'addi, $sp, $sp, {arg_amount}\n' + result_offset = self.var_offset[self.current_function.name,node.result] + self.text_section += f'sw $s0, {result_offset}($sp)\n' + + + @visitor.when(DynamicParentCallCilNode) #7.4 Dispatch Dynamic + def visit(self,node): + ######################################################################### + # node.expresion_instance = expresion_instance + # node.static_type = static_type + # node.method_name = method_name + # node.args = args + # node.result = result + ######################################################################## + arg_amount = (len(node.args))*4 + self.text_section+= f'move $t0, $sp #Dynamic_Parent_Call\n' + self.text_section+= f'addi, $sp, $sp, -{arg_amount}\n' + + original_fun = self.method_original[node.static_type,node.method_name] + + for i,arg in enumerate(node.args): + arg_offset = self.var_offset[self.current_function.name,arg] + self.text_section+= f'lw, $s0, {arg_offset}($t0)\n' + self.text_section+= f'sw, $s0 {(i)*4}($sp)\n' + + #Conseguir el tipo estatico a partir de node.static_type aue es lo que existe en el @type + self.text_section += 'la $t1, void_data\n' + self.text_section += 'beq $v0, $t1, error_call_void\n' + + self.text_section += f'la $a1, {node.static_type}_methods\n' + self.text_section += f'lw $a2, {self.method_offset[node.static_type,original_fun]}($a1) #FunctionToCall {original_fun}\n' + self.text_section += 'jalr $a2\n' + + + self.text_section+= f'addi, $sp, $sp, {arg_amount}\n' + result_offset = self.var_offset[self.current_function.name,node.result] + self.text_section += f'sw $s0, {result_offset}($sp)\n' + + + @visitor.when(CaseCilNode) + def visit(self,node): + offset_expr_0 = self.var_offset[self.current_function.name,node.main_expr] + self.text_section+= '\n' + self.text_section+= f'lw $t1,{offset_expr_0}($sp) \n' + #comparar con void y dar error + self.text_section += 'la $t3, void_data\n' + self.text_section += 'beq $t1, $t3, error_expr_void\n' + + self.text_section+= f'lw $v1, ($t1) #Adress Method\n' #$t1 : adress del expr_0.type() + self.text_section+= f'li $s0,0\n' #s0: adress del menor type_P tal que P>=C (0 si no se encuentra ninguno) + self.text_section+= f'li $s1, 2147483647\n' #s1: distancia desde expr_0 hasta menor type_k actual(empieza en int.max) + + @visitor.when(BranchCilNode) #7.9 Case + def visit(self,node): + ############################## + # node.type_k = type_k + ############################## + self.text_section+= 'move $t1,$v1\n' + self.text_section+= f'la $t2,{node.type_k}\n' + self.text_section+= 'jal calculateDistance\n' #tipo hijo(C) tiene que estar en $t1 y tipo ancestro(P) tiene que estar en $t2 + + @visitor.when(CaseEndCilNode) + def visit(self,node): + ############################## + # node.result + ############################## + result_offset = self.var_offset[self.current_function.name,node.result] + self.text_section+='\n' + self.text_section+= 'beqz $s0, error_branch\n' + self.text_section+=f'sw $s0, {result_offset}($sp)\n' + + + + + #Binary Aritmetic Operations + @visitor.when(PlusCilNode) + def visit(self, node): + ###################################### + # node.dest = dest + # node.left = left + # node.right = right + ###################################### + offset_right = self.var_offset[self.current_function.name,node.right] + offset_left = self.var_offset[self.current_function.name,node.left] + offset_dest = self.var_offset[self.current_function.name,node.dest] + self.text_section += '\n' + self.text_section+= f'lw, $t3, {offset_right}($sp)\n' + self.text_section+=f'lw,$t1,4($t3) #Load sum value\n' + self.text_section+= f'lw, $t3, {offset_left}($sp)\n' + self.text_section+=f'lw,$t2,4($t3) #Load sum value\n' + self.text_section+= f'add $t3,$t1,$t2\n' #resultado de la suma + self.text_section+=f'sw, $t3, {offset_dest}($sp)\n' + + + @visitor.when(MinusCilNode) + ###################################### + # node.dest = dest + # node.left = left + # node.right = right + ###################################### + def visit(self, node): + offset_right = self.var_offset[self.current_function.name,node.right] + offset_left = self.var_offset[self.current_function.name,node.left] + offset_dest = self.var_offset[self.current_function.name,node.dest] + self.text_section += '\n' + self.text_section+= f'lw, $t3, {offset_right}($sp)\n' + self.text_section+=f'lw,$t1,4($t3) #Load minus value\n' + self.text_section+= f'lw, $t3, {offset_left}($sp)\n' + self.text_section+=f'lw,$t2,4($t3) #Load minus value\n' + self.text_section+= f'sub $t3,$t2,$t1\n' #resultado de la resta + self.text_section+=f'sw, $t3, {offset_dest}($sp)\n' + + @visitor.when(StarCilNode) + ###################################### + # node.dest = dest + # node.left = left + # node.right = right + ###################################### + def visit(self, node): + offset_right = self.var_offset[self.current_function.name,node.right] + offset_left = self.var_offset[self.current_function.name,node.left] + offset_dest = self.var_offset[self.current_function.name,node.dest] + self.text_section += '\n' + self.text_section+= f'lw, $t3, {offset_right}($sp)\n' + self.text_section+=f'lw,$t1,4($t3) #Load Star value\n' + self.text_section+= f'lw, $t3, {offset_left}($sp)\n' + self.text_section+=f'lw,$t2,4($t3) #Load Star value\n' + self.text_section+= f'mul $t3,$t1,$t2\n' #Operacion mul + self.text_section+=f'sw, $t3, {offset_dest}($sp)\n' + + @visitor.when(DivCilNode) + ###################################### + # node.dest = dest + # node.left = left + # node.right = right + ###################################### + def visit(self, node): + offset_right = self.var_offset[self.current_function.name,node.right] + offset_left = self.var_offset[self.current_function.name,node.left] + offset_dest = self.var_offset[self.current_function.name,node.dest] + self.text_section += '\n' + self.text_section+= f'lw, $t3, {offset_right}($sp)\n' + self.text_section+=f'lw,$t1,4($t3) #Load Div value\n' + self.text_section+= f'lw, $t3, {offset_left}($sp)\n' + self.text_section+=f'lw,$t2,4($t3) #Load Div value\n' + + self.text_section+= f'beqz $t1 error_div_by_zero\n' + + self.text_section+= f'div $t3,$t2,$t1\n' #Operacion div + self.text_section+=f'sw, $t3, {offset_dest}($sp)\n' + + @visitor.when(EqualCilNode) + ###################################### + # node.dest = dest + # node.left = left + # node.right = right + ###################################### + def visit(self, node): + offset_right = self.var_offset[self.current_function.name,node.right] + offset_left = self.var_offset[self.current_function.name,node.left] + offset_dest = self.var_offset[self.current_function.name,node.dest] + self.text_section += '\n' + self.text_section+= f'lw, $t3, {offset_right}($sp)\n' + self.text_section+=f'lw,$t1,4($t3) #Load Equal Node\n' + self.text_section+= f'lw, $t3, {offset_left}($sp)\n' + self.text_section+=f'lw,$t2,4($t3)\n' + + self.text_section+= f'jal Equals_comparison\n' + + self.text_section+=f'sw, $t3, {offset_dest}($sp)\n' + + @visitor.when(EqualRefCilNode) + ###################################### + # node.dest = dest + # node.left = left + # node.right = right + ###################################### + def visit(self, node): + offset_right = self.var_offset[self.current_function.name,node.right] + offset_left = self.var_offset[self.current_function.name,node.left] + offset_dest = self.var_offset[self.current_function.name,node.dest] + self.text_section += '\n' + self.text_section+= f'lw, $t1, {offset_right}($sp) #Load EqualRefNode\n' + self.text_section+= f'lw, $t2, {offset_left}($sp)\n' + + self.text_section+= f'jal Equals_comparison\n' + + self.text_section+=f'sw, $t3, {offset_dest}($sp)\n' + + @visitor.when(CompareStringCilNode) + ###################################### + # node.dest = dest + # node.left = left + # node.right = right + ###################################### + def visit(self, node): + offset_right = self.var_offset[self.current_function.name,node.right] + offset_left = self.var_offset[self.current_function.name,node.left] + offset_dest = self.var_offset[self.current_function.name,node.dest] + self.text_section += '\n' + self.text_section+= f'lw, $t3, {offset_right}($sp)\n' + self.text_section+=f'lw,$t1,4($t3) #Load String Adress Node\n' + self.text_section+= f'lw $a1,8($t3)\n' + self.text_section+= f'lw, $t3, {offset_left}($sp)\n' + self.text_section+=f'lw,$t2,4($t3)\n' + self.text_section+= f'lw $a2,8($t3)\n' + + + self.text_section+= f'jal String_comparison_fun\n' + + self.text_section+=f'sw, $t3, {offset_dest}($sp)\n' + + @visitor.when(LessEqualCilNode) + ###################################### + # node.dest = dest + # node.left = left + # node.right = right + ###################################### + def visit(self, node): + offset_right = self.var_offset[self.current_function.name,node.right] + offset_left = self.var_offset[self.current_function.name,node.left] + offset_dest = self.var_offset[self.current_function.name,node.dest] + self.text_section += '\n' + self.text_section+= f'lw, $t3, {offset_left}($sp)\n' + self.text_section+=f'lw,$t1,4($t3) #Load Less Equal\n' + self.text_section+= f'lw, $t3, {offset_right}($sp)\n' + self.text_section+=f'lw,$t2,4($t3)\n' + + self.text_section+= f'jal LessEqual_comparison\n' + + self.text_section+=f'sw, $t3, {offset_dest}($sp)\n' + + @visitor.when(LessCilNode) + ###################################### + # node.dest = dest + # node.left = left + # node.right = right + ###################################### + def visit(self, node): + offset_right = self.var_offset[self.current_function.name,node.right] + offset_left = self.var_offset[self.current_function.name,node.left] + offset_dest = self.var_offset[self.current_function.name,node.dest] + self.text_section += '\n' + self.text_section+= f'lw, $t3, {offset_left}($sp)\n' + self.text_section+=f'lw,$t1,4($t3) #Load Less \n' + self.text_section+= f'lw, $t3, {offset_right}($sp)\n' + self.text_section+=f'lw,$t2,4($t3)\n' + + self.text_section+= f'jal Less_comparison\n' + + self.text_section+=f'sw, $t3, {offset_dest}($sp)\n' + + + + @visitor.when(IsVoidCilNode) + ###################################### + # node.dest = dest + # node.right = right + ###################################### + def visit(self, node): + offset_right = self.var_offset[self.current_function.name,node.right] + offset_dest = self.var_offset[self.current_function.name,node.dest] + self.text_section += '\n' + self.text_section+= f'lw, $t3, {offset_right}($sp)\n' + self.text_section+=f'move,$t1,$t3\n' + + self.text_section+= f'jal Void_comparison\n' + + self.text_section+=f'sw, $t3, {offset_dest}($sp)\n' + + @visitor.when(NotCilNode) + ###################################### + # node.dest = dest + # node.right = right + ###################################### + def visit(self, node): + offset_right = self.var_offset[self.current_function.name,node.right] + offset_dest = self.var_offset[self.current_function.name,node.dest] + self.text_section += '\n' + self.text_section+= f'lw, $t3, {offset_right}($sp)\n' + self.text_section+=f'lw,$t1,4($t3) #Load Not Node\n' + + self.text_section+= f'xor $t3,$t1,1\n' + + self.text_section+=f'sw, $t3, {offset_dest}($sp)\n' + + @visitor.when(NegateCilNode) + ###################################### + # node.dest = dest + # node.right = right + ###################################### + def visit(self, node): + offset_right = self.var_offset[self.current_function.name,node.right] + offset_dest = self.var_offset[self.current_function.name,node.dest] + self.text_section += '\n' + self.text_section+= f'lw, $t3, {offset_right}($sp)\n' + self.text_section+=f'lw,$t1,4($t3) #Load Negate\n' + + self.text_section+= f'neg $t3,$t1\n' + + self.text_section+=f'sw, $t3, {offset_dest}($sp)\n' + + + + @visitor.when(GetAttribCilNode) + def visit(self,node): + ####################################### + # node.instance = instance + # node.type = type + # node.attribute = attribute + # node.result = result + ####################################### + result_offset = self.var_offset[self.current_function.name,node.result] + instance_offset = self.var_offset[self.current_function.name,node.instance] + self.text_section+= f'lw $t3, {instance_offset}($sp) #getting instance {node.instance} \n' #Buscar la local que tiene la direccion del heap + + if node.attribute == 'self': + self.text_section+= f'sw $t3, {result_offset}($sp)\n' + else: + attr_offset = self.attribute_offset[node.type,node.attribute] + + # (instance_offset+attr_offset) + self.text_section += '\n' + self.text_section+= f'lw, $t3, {instance_offset}($sp) #getting instance {node.instance} \n' #Buscar la local que tiene la direccion del heap + self.text_section+= f'lw, $t1, {attr_offset}($t3) #getting offset {node.attribute} \n' #Cargar en un registro el valor del atributo + self.text_section+= f'sw, $t1, {result_offset}($sp) \n' #Guardo el valor + + @visitor.when(SetAttribCilNode) + def visit(self,node): + # class SetAttribCilNode(InstructionCilNode): + ####################################### + # node.instance = instance + # node.type = type + # node.attribute = attribute + # node.value = value + ####################################### + attr_offset = self.attribute_offset[node.type,node.attribute] + instance_offset = self.var_offset[self.current_function.name,node.instance] + value_offset = self.var_offset[self.current_function.name,node.value] + self.text_section += '\n' + self.text_section+= f'lw, $t1, {value_offset}($sp) \n' #Guardo el valor + self.text_section+= f'lw, $t3, {instance_offset}($sp) \n' #Buscar la local que tiene la direccion del heap + self.text_section+= f'sw, $t1, {attr_offset}($t3) \n' #Cargar en un registro el valor del atributo(ojo puede q no haya q restar) + + + @visitor.when(AllocateCilNode) + def visit(self, node): + ####################################### + #node.type = type + #node.result = result + ####################################### + result_offset = self.var_offset[self.current_function.name,node.result] + type_size = (self.type_size[node.type] + 1) * 4 #mas 1 para guardar el addr del tipo + self.text_section += '\n' + + self.text_section += f'addi $a0, $zero, {type_size}\n' #ojo pudiera faltar un +4 + self.text_section += 'li, $v0, 9\n' + self.text_section += 'syscall\n' + self.text_section += 'blt, $sp, $v0,error_heap\n' + self.text_section += 'move, $t3, $v0\n' + + self.text_section += f'la $t1,{node.type}_methods\n'#recupero el addr del tipo + self.text_section += 'sw $t1, ($t3)\n' #guardo en el primer espacio de memoria de la nueva instancia el addr del tipo + self.text_section += f'sw, $t3, {result_offset}($sp)\n' + + @visitor.when(AllocateDynamicCilNode) + def visit(self,node): + ####################################### + # node.address_in_local = address_in_local + # node.result = result + ####################################### + address_in_local_offset = self.var_offset[self.current_function.name,node.address_in_local_offset] + result_offset = self.var_offset[self.current_function.name,node.result] + + self.text_section += '\n' + self.text_section+= f'lw $t1,{address_in_local_offset}$(sp)\n' + self.text_section+= f'lw $a0,($t1)\n' + self.text_section += 'li, $v0, 9\n' + self.text_section += 'syscall\n' + self.text_section += 'blt, $sp, $v0,error_heap\n' + self.text_section += 'move, $t3, $v0\n' + + self.text_section += 'sw $t1, ($t3)\n' #guardo en el primer espacio de memoria de la nueva instancia el addr del tipo + self.text_section += f'sw, $t3, {result_offset}($sp)\n' + + + @visitor.when(GetDataCilNode) + def visit(self,node): + ####################################### + # node.name = name + # node.result = result + ####################################### + self.text_section += f'\n' + result_offset = self.var_offset[self.current_function.name,node.result] + self.text_section += f'la $t1, {node.name}\n' + self.text_section += f'sw $t1, {result_offset}($sp)\n' + + + + @visitor.when(ReturnCilNode) + def visit(self,node): + ########################################### + #node.value = value + ########################################### + self.text_section += '\n' + if node.value: + offset_value = self.var_offset[self.current_function.name,node.value] + self.text_section += f'lw $s0, {offset_value}($sp)\n' + else: + self.text_section += f'move $s0, $zero\n' + +#Function Mips Implementattion + @visitor.when(PrintStringCilNode) + def visit(self, node): + ########################################### + # node.self_param = self_param + # node.to_print = to_print + ########################################### + str_offset = self.var_offset[self.current_function.name,node.to_print] + + self.text_section += '\n' + self.text_section+= f'li, $v0, 4\n' + self.text_section+= f'lw, $a0, {str_offset}($sp)\n' + self.text_section+= f'syscall\n' + + @visitor.when (PrintIntCilNode) + def visit(self, node): + ########################################### + # node.to_print = to_print + ########################################### + str_offset = self.var_offset[self.current_function.name,node.to_print] + self.text_section += '\n' + self.text_section+= f'li, $v0, 1\n' + self.text_section+= f'lw, $a0, {str_offset}($sp)\n' + self.text_section+= f'syscall\n' + + + @visitor.when (LabelCilNode) + def visit(self, node): + ########################################### + # self.label = label + ########################################### + self.text_section += f'{node.label}:\n' + + + @visitor.when (GotoCilNode) + def visit(self, node): + # self.label = label + self.text_section += f'j {node.label}\n' + + @visitor.when (GotoIfCilNode) + def visit(self, node): + ########################################### + #node.val_dir = val_dir + #node.label = label + ########################################### + offset_val_dir = self.var_offset[self.current_function.name,node.val_dir] + self.text_section += f'lw $t0, {offset_val_dir}($sp) #Goto If Label\n' #Carga el bool + self.text_section += f'lw $t0, 4($t0) #Load Bool Value\n' #Carga el valor del bool + self.text_section += f'beq $t0, 1, {node.label}\n' + + @visitor.when (GotoBoolIfCilNode) + def visit(self, node): + ########################################### + #node.val_dir = val_dir + #node.label = label + ########################################### + offset_val_dir = self.var_offset[self.current_function.name,node.val_dir] + self.text_section += f'lw $t0, {offset_val_dir}($sp) #Goto If Label\n' #Carga el bool + self.text_section += f'beq $t0, 1, {node.label}\n' + + @visitor.when (NotGotoIfCilNode) + def visit(self, node): + ########################################### + #node.val_dir = val_dir + #node.label = label + ########################################### + offset_val_dir = self.var_offset[self.current_function.name,node.val_dir] + self.text_section += f'lw $t0, {offset_val_dir}($sp) #If Label\n' #Carga el bool + self.text_section += f'lw $t0, 4($t0)\n' #Carga el valor del bool + self.text_section += f'beqz $t0 {node.label}\n' + + #TypesCilNodes falta bool ojo + @visitor.when(IntCilNode) + def visit(self, node): + # node.value = value + # node.result = result + offset_value = self.var_offset[self.current_function.name,node.result] + self.text_section += '\n' + self.text_section += f'addi, $t1, $zero, {node.value}\n' + self.text_section += f'sw, $t1, {offset_value}($sp)\n' + + + @visitor.when(StringCilNode) + def visit(self, node): + ############################### + # node.dir1 = dir1 + # node.dir2 = dir2 + ############################### + dir1_offset = self.var_offset[self.current_function.name,node.dir1] + dir2_offset = self.var_offset[self.current_function.name,node.dir2] + self.text_section += '\n' + self.text_section += f'lw, $t1, {dir1_offset}($sp)\n' #Cargo en t1 la direccion del string en data + self.text_section += f'lw, $t2, {dir2_offset}($sp)\n' #Cargo en t2 la direccion donde guardare el string en el heap + + # self.text_section += + + @visitor.when(LengthCilNode) + def visit(self, node): + ##################################### + # node.self_dir = self_dir + # node.length = length + ##################################### + + offset_self = self.var_offset[self.current_function.name,node.self_dir] + offset_length = self.var_offset[self.current_function.name,node.length] + self.text_section += f'lw $t1, {offset_self}($sp)\n' + + self.text_section += 'li $s1, 0\n' #contador de caracteres (el ultimo es 0 y no se cuenta) + self.text_section += 'loop_function_length:\n' + self.text_section += 'lb $t2, ($t1)\n' #Cargo un caracter + self.text_section += 'beqz $t2, end_function_length\n' #si el caracter que cargue es 0, termino + self.text_section += 'addi $t1, $t1, 1\n' #sumo 1 a la direccion de donde estoy buscando el string + self.text_section += 'addi $s1, $s1, 1\n' #Sumo 1 al contador + self.text_section += 'j loop_function_length\n' #Reinicio el loop + self.text_section += 'end_function_length:\n' #Fin del loop + + self.text_section += f'sw $s1, {offset_length}($sp)\n' # + + + # #Function CilNodes + + + @visitor.when(AbortCilNode) + def visit(self, node): + ##################################### + # node.self_from_call + ##################################### + self_offset = self.var_offset[self.current_function.name,node.self_from_call] + self.text_section+= 'la $a0 abort_label\n' + self.text_section+= 'li $v0,4\n' + self.text_section+= 'syscall\n' + self.text_section+= f'lw $a0 {self_offset}($sp)\n' + self.text_section+= 'lw $a0 ($a0)\n' + self.text_section+= 'lw $a0 4($a0)\n' + self.text_section += 'syscall\n' + self.text_section+='la $a0, slash_n\n' + self.text_section+='syscall\n' + self.text_section+= 'j end\n' + + + @visitor.when(TypeNameCilNode) + def visit(self, node): + ##################################### + # node.self_param = self_param + # node.result = result + ##################################### + + self_param_offset = self.var_offset[self.current_function.name,node.self_param] + result_offset = self.var_offset[self.current_function.name,node.result] + self.text_section+= '\n' + self.text_section += f'lw $a0, {self_param_offset}($sp)\n' #Carga direccion del data + #El tipo dinamico se consigue a partir de expresion offset + #Selecting Function + self.text_section += f'lw $a1, ($a0)\n' #Cargar el adress + self.text_section += f'lw $a2, 4($a1)\n' #Carga el adress del string + self.text_section += f'sw $a2, {result_offset}($sp)' #Guardo el adress del string + + + @visitor.when(CopyCilNode) + def visit(self, node): + ##################################### + # node.self_param = self_param + # node.result = result + ##################################### + self_param_offset = self.var_offset[self.current_function.name,node.self_param] + result_offset = self.var_offset[self.current_function.name,node.result] + self.text_section+= '\n' + self.text_section += f'lw $t1, {self_param_offset}($sp)\n' #Carga direccion del data + self.text_section += f'lw $t2, ($t1)\n' #Cargar el adress + self.text_section += f'lw $a0, ($t2)\n' #Carga el adress del string $a2 = Amount_attr + + #Allocate space for new instance + self.text_section += '\n' + self.text_section += 'li, $v0, 9\n' + self.text_section += 'syscall\n' + self.text_section += 'blt, $sp, $v0,error_heap\n' + self.text_section += 'move, $t3, $v0\n' + self.text_section += 'move $t4,$v0\n' + + #t1 = old instance + #t3 = new instance + #t2 = aux + #loop to copy t1 to t3 + self.text_section += f'loop_copyNode:\n' + self.text_section += f'lw $t2, ($t1)\n' + self.text_section += f'sw $t2, ($t3)\n' + self.text_section += f'addi $t1,$t1,4\n' + self.text_section += f'addi $t3,$t3,4\n' + self.text_section += f'subu $a0,$a0,4\n' + self.text_section += f'beqz $a0,end_loop_copy\n' + self.text_section += f'j loop_copyNode\n' + + + self.text_section += f'end_loop_copy:\n' + self.text_section += f'sw $t4, {result_offset}($sp)\n' + + @visitor.when(InternalCopyCilNode) + def visit(self, node): + ##################################### + # node.dir_child = dir_child + # node.dir_ancestor = dir_ancestor + ##################################### + child_offset = self.var_offset[self.current_function.name,node.dir_child] + ancestor_offset = self.var_offset[self.current_function.name,node.dir_ancestor] + + self.text_section+= '\n' + self.text_section += f'lw $t1, {child_offset}($sp)\n' #Carga direccion del data_child + self.text_section += f'lw $t2, {ancestor_offset}($sp)\n' #Carga direccion del data_ancestor + + self.text_section += f'lw $a2, ($t2)\n' #Cargar el adress + self.text_section += f'lw $a2, ($a2)\n' #Catga el numero + + self.text_section+=f'addi $a2,$a2,-4\n'#cantidad de atributos a copiar + self.text_section+=f'addi $t1,$t1,4\n' + self.text_section+=f'addi $t2,$t2,4\n' + + + self.text_section+= f'jal function_internalcopy\n' + + self.text_section += '\n' + + + @visitor.when(ReadIntCilNode) + def visit(self, node): + ##################################### + # node.dest = dest + ##################################### + dest_offset = self.var_offset[self.current_function.name,node.dest] + self.text_section+='\n' + self.text_section+= 'li $v0,5 # Read_Int_Section\n' + self.text_section+= 'syscall\n' + self.text_section+= f'sw $v0,{dest_offset}($sp)\n' + + @visitor.when(ReadStringCilNode) + def visit(self, node): + ##################################### + # node.dest = dest + ##################################### + dest_offset = self.var_offset[self.current_function.name,node.dest] + #Save input string on aux data + self.text_section+='\n' + self.text_section+= 'li $v0,8 # Read_string_Section\n' + self.text_section+= 'la $a0,aux_input_string \n' + self.text_section+= 'li $a1,1024\n' + self.text_section+= 'syscall \n' + + self.text_section+= 'jal read_string_function\n' + + + self.text_section+= f'sw $t4,{dest_offset}($sp)\n' + + # + + + + @visitor.when(ReadStrEndCilNode) + def visit(self, node): + ##################################### + # node.dest = result + # node.length = length + ##################################### + result_offset = self.var_offset[self.current_function.name,node.result] + length_offset = self.var_offset[self.current_function.name,node.length] + + self.text_section += '\n' + self.text_section+= f'lw $a0,{length_offset}($sp)\n #END part of read' + #Allocate space for new instance + self.text_section += 'addi $a0,$a0,1\n' + self.text_section += 'li, $v0, 9\n' + self.text_section += 'syscall\n' + self.text_section += 'blt, $sp, $v0,error_heap\n' + self.text_section += 'move, $t3, $v0\n' #dir3: se mueve para ir rellenando + self.text_section += 'move $t4,$v0\n' #fijo + #Copy data to heap + self.text_section += 'la $t1, aux_input_string\n' + + self.text_section += f'loop_readString:\n' + self.text_section += f'beqz $a0,end_readString\n' + self.text_section += f'lb $a1, ($t1)\n' + self.text_section += f'sb $a1, ($t3)\n' + self.text_section += f'addi $a0,$a0,-1\n' + self.text_section += f'addi $t1,$t1,1\n' + self.text_section += f'addi $t3,$t3,1\n' + self.text_section += f'j loop_readString\n' + + self.text_section+= 'end_readString:\n' + self.text_section += 'sb,$zero,($t3)\n' + self.text_section+= f'sw $t4,{result_offset}($sp)\n' + + + @visitor.when(ConcatCilNode) + def visit(self, node): + ##################################### + # node.self_param = self_param + # node.param_1 = param_1 + # node.result_addr = result_addr + ##################################### + self_param_offset = self.var_offset[self.current_function.name,node.self_param] + param1_offset = self.var_offset[self.current_function.name,node.param_1] + result_offset = self.var_offset[self.current_function.name,node.result_addr] + self.text_section+= '\n' + self.text_section += f'lw $t1, {self_param_offset}($sp)\n' #Carga direccion del data + self.text_section += f'lw $t2, {param1_offset}($sp)\n' + + #Capturo los len de las instancias + self.text_section += f'lw $a1, 8($t1)\n' #Cargar el len de t1 + self.text_section += f'lw $a2, 8($t2)\n' #Carga el len de t2 + self.text_section += f'add $a0, $a1, $a2\n' #a0 = a1 + a2 + + #Capturo los adress de los str + self.text_section += f'lw $t1, 4($t1)\n' #Cargar el addr de t1 + self.text_section += f'lw $t2, 4($t2)\n' #Carga el addr de t2 + + #Allocate space for new instance + self.text_section += '\n' + self.text_section += 'addi $a0,$a0, 1\n' #OJO: Test generated space if correct or not + self.text_section += 'li, $v0, 9\n' + self.text_section += 'syscall\n' + self.text_section += 'blt, $sp, $v0,error_heap\n' + self.text_section += 'move, $t3, $v0\n' #dir3: se mueve para ir rellenando + self.text_section += 'move $t4,$v0\n' #fijo + + #Loop to fill + self.text_section += f'loop_concat_dirSelf:\n' + self.text_section += f'lb $a1, ($t1)\n' + self.text_section += f'beqz $a1,loop_concat_dirParam\n' + self.text_section += f'sb $a1, ($t3)\n' + self.text_section += f'addi $t1,$t1,1\n' + self.text_section += f'addi $t3,$t3,1\n' + self.text_section += f'j loop_concat_dirSelf\n' + + self.text_section += f'loop_concat_dirParam:\n' + self.text_section += f'lb $a1, ($t2)\n' + self.text_section += f'sb $a1, ($t3)\n' + self.text_section += f'addi $t2,$t2,1\n' + self.text_section += f'addi $t3,$t3,1\n' + self.text_section += f'beqz $a1,end_loop_concat\n' + self.text_section += f'j loop_concat_dirParam\n' + + #End + self.text_section += f'end_loop_concat:\n' + self.text_section += f'sw $t4, {result_offset}($sp)\n' + + + @visitor.when(SubstringCilNode) + def visit(self, node): + ################################################### + # node.self_param = self_param + # node.value1 = value1 + # node.value2 = value2 + # node.result = result + ################################################### + self_param_offset = self.var_offset[self.current_function.name,node.self_param] + value1_offset = self.var_offset[self.current_function.name,node.value1] + value2_offset = self.var_offset[self.current_function.name,node.value2] + result_offset = self.var_offset[self.current_function.name,node.result] + + self.text_section+= '\n' + self.text_section += f'lw $t0, {self_param_offset}($sp)\n' #Carga direccion de la instancia + self.text_section += f'lw $t1, {value1_offset}($sp)\n' #Carga direccion de Int i + self.text_section += f'lw $t2, {value2_offset}($sp)\n' #Carga direccion de Int l + + self.text_section += f'lw $a2, 8($t0)\n' #len del string + self.text_section += f'lw $a1, 4($t1)\n' #valor de i + self.text_section += f'lw $a0, 4($t2)\n' #valor de l + + self.text_section += f'add $a3,$a1,$a0\n' # a3 = i + l + self.text_section += f'bgt $a3,$a2, substring_out_of_range\n' #if (i+l)> len-> Erorr + + #Allocate space for isntance + self.text_section += '\n' + self.text_section += 'addi $a0,$a0,1\n' #OJO: Test generated space if correct or not + self.text_section += 'li, $v0, 9\n' + self.text_section += 'syscall\n' + self.text_section += 'blt, $sp, $v0,error_heap\n' + self.text_section += 'move, $t3, $v0\n' #dir3: se mueve para ir rellenando + self.text_section += 'move $t4,$v0\n' #fijo + + self.text_section += f'lw $a2, 4($t0)\n' #adress del string + self.text_section += f'add $a2,$a2,$a1\n' #empezar a partir de string[i] + #Loop to fill + + self.text_section += f'lw $a0, 4($t2)\n' #valor de l + + self.text_section += f'loop_substring_dirSelf:\n' + self.text_section += f'beqz $a0,end_loop_substr\n' + self.text_section += f'lb $a1, ($a2)\n' + self.text_section += f'sb $a1, ($t3)\n' + self.text_section += f'addi $a2,$a2,1\n' + self.text_section += f'addi $t3,$t3,1\n' + self.text_section += f'addi $a0,$a0,-1\n' + self.text_section += f'j loop_substring_dirSelf\n' + + #End + self.text_section += f'end_loop_substr:\n' + self.text_section += f'sb $zero, ($t3)\n' + self.text_section += f'sw $t4, {result_offset}($sp)\n' diff --git a/src/cool/semantic/semantic.py b/src/cool/semantic/semantic.py new file mode 100644 index 000000000..7786cf0ac --- /dev/null +++ b/src/cool/semantic/semantic.py @@ -0,0 +1,439 @@ +import itertools as itt +from collections import OrderedDict + +class SemanticException(Exception): + @property + def text(self): + return self.args[0] + +class NameException(Exception): + @property + def text(self): + return self.args[0] + +class Attribute: + def __init__(self, name, typex): + self.name = name + self.type = typex + + def __str__(self): + return f'[attrib] {self.name} : {self.type.name};' + + def __repr__(self): + return str(self) + +class Method: + def __init__(self, name, param_names, params_types, return_type): + self.name = name + self.param_names = param_names + self.param_types = params_types + self.return_type = return_type + + def __str__(self): + params = ', '.join(f'{n}:{t.name}' for n,t in zip(self.param_names, self.param_types)) + return f'[method] {self.name}({params}): {self.return_type.name};' + + def __eq__(self, other): + return other.name == self.name and \ + other.return_type == self.return_type and \ + other.param_types == self.param_types + +class Type: + def __init__(self, name:str): + self.name = name + self.attributes = [] + self.methods = {} + self.parent = None + self.is_autotype = False + + def set_parent(self, parent): + if self.parent is not None: + raise SemanticException(f'Parent type is already set for {self.name}.') + self.parent = parent + + def get_attribute(self, name:str): + try: + return next(attr for attr in self.attributes if attr.name == name) + except StopIteration: + if self.parent is None: + raise 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}.') + + def define_attribute(self, name:str, typex): + if name == 'self': + raise SemanticException( + "'self' cannot be the name of an attribute") + try: + self.get_attribute(name) + except SemanticException: + attribute = Attribute(name, typex) + self.attributes.append(attribute) + return attribute + else: + raise SemanticException(f'Attribute "{name}" is already defined in {self.name}.') + + def change_attr(self,name:str, new_typex): + try: + attr = self.get_attribute(name) + attr.type = new_typex + except SemanticException: + print('REVISAR TYPE.CHANGE_TYPE') + + def change_method(self,name:str,new_typex): + try: + method = self.get_method(name) + method.return_type = new_typex + except: + print('REVISAR TYPE.CHANGE_METHOD') + + def change_param(self,name:str,new_param_list): + try: + method = self.get_method(name) + param_name = [] + param_type = [] + for var in new_param_list.locals: + param_name.append(var.name) + param_type.append(var.type) + method.param_names = param_name + method.param_types = param_type + except: + print('REVISAR TYPE.CHANGE_PARAM') + + def substitute_type(self, typeA , typeB): + for attr in self.attributes: + if attr.type == typeA: + attr.type = typeB + for meth in self.methods.values(): + if meth.return_type == typeA: + meth.return_type = typeB + for p_type in meth.param_types: + if p_type == typeA: + p_type = typeB + + def has_parent(self,typeA): + return (self == typeA) or (self.parent and self.parent.has_parent(typeA)) + + 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}.') + + def get_all_parents(self): + if self.parent is None: return [] + return [self.parent]+self.parent.get_all_parents() + + def all_attributes(self, clean=True): + plain = OrderedDict() if self.parent is None else self.parent.all_attributes(False) + for attr in self.attributes: + plain[attr.name] = (attr, self) + return plain.values() if clean else plain + + + + # def define_method(self, name:str, param_names:list, param_types:list, return_type): + # if name in self.methods: + # raise SemanticException(f'Method "{name}" already defined in {self.name}') + # raise SemanticError(f'Method "{name}" already defined in {self.name} with a different signature.') + + method = self.methods[name] = Method(name, param_names, param_types, return_type) + return method + + # my method, change it in future + def define_method(self, name:str, param_names:list, param_types:list, return_type): + if name in self.methods: + raise SemanticException(f'Method "{name}" already defined in {self.name}') + try: + method = self.get_method(name) + except SemanticException: + pass + else: + if method.return_type != return_type or method.param_types != param_types: + raise SemanticException(f'Method "{name}" already defined in {self.name} with a different signature.') + ##############METHOD NO HEREDA AUTO)TYPE + + method = self.methods[name] = Method(name, param_names, param_types, return_type) + return method + + def conforms_to(self, other): + a = other.bypass() or self.name == other.name + b = self.parent is not None and self.parent.conforms_to(other) + return a or b + + def bypass(self): + return False + + def __str__(self): + output = f'type {self.name}' + parent = '' if self.parent is None else f' : {self.parent.name}' + output += parent + output += ' {' + output += '\n\t' if self.attributes or self.methods else '' + output += '\n\t'.join(str(x) for x in self.attributes) + output += '\n\t' if self.attributes else '' + output += '\n\t'.join(str(x) for x in self.methods.values()) + output += '\n' if self.methods else '' + output += '}\n' + return output + + def __repr__(self): + return str(self) + +class ObjectType(Type): + def __init__(self): + Type.__init__(self, 'Object') + + def __eq__(self,other): + return other.name == self.name or isinstance(other, ObjectType) + +class ErrorType(Type): + def __init__(self): + Type.__init__(self, 'Error') + + def conforms_to(self, other): + return True + + def bypass(self): + return True + + def __eq__(self, other): + return isinstance(other, Type) + +class VoidType(Type): + def __init__(self): + Type.__init__(self, 'Void') + + def conforms_to(self, other): + raise Exception('Invalid type: void type.') + + def bypass(self): + return True + + def __eq__(self, other): + return isinstance(other, VoidType) + +class IntType(Type): + + def __init__(self): + Type.__init__(self, 'Int') + self.parent = ObjectType() + + def __eq__(self, other): + return other.name == self.name or isinstance(other, IntType) + +class BoolType(Type): + def __init__(self): + Type.__init__(self , 'Bool') + self.parent = ObjectType() + + def __eq__(self,other): + return other.name == self.name or isinstance(other, BoolType) + +class StringType(Type): + def __init__(self): + Type.__init__(self,'String') + self.parent = ObjectType() + + def __eq__(self,other): + return other.name == self.name or isinstance(other, StringType) + +class IOType(Type): + def __init__(self): + Type.__init__(self,'IO') + self.parent = ObjectType() + + def __eq__(self,other): + return other.name == self.name or isinstance(other, StringType) + +class SelfType(Type): + def __init__(self): + Type.__init__(self,'self') + +class Context: + def __init__(self): + self.types = {} + self.special_types = [] + object_type = ObjectType() + + IO_type = IOType() + + int_type = IntType() + bool_type = BoolType() + error_type = ErrorType() + self_type = SelfType() + string_type = StringType() + + self.types['Object'] = object_type + self.types['Int'] = int_type + self.types['String'] = string_type + self.types['Bool'] = bool_type + self.types['Error'] = error_type + self.types['IO'] = IO_type + + object_type.define_method('abort',[],[],object_type) + object_type.define_method('type_name',[],[],string_type) + object_type.define_method('copy',[],[],self_type) + IO_type.define_method('out_string',['x'],[string_type],self_type) + IO_type.define_method('out_int',['x'],[int_type],self_type) + IO_type.define_method('in_string',[],[],string_type) + IO_type.define_method('in_int',[],[],int_type) + string_type.define_method('length',[],[],int_type) + string_type.define_method('concat',['s'],[string_type],string_type) + string_type.define_method('substr', ['i','l'] ,[int_type,int_type],string_type) + + int_type.parent = object_type + string_type.parent = object_type + bool_type.parent = object_type + error_type.parent = object_type + IO_type.parent = object_type + + + + def create_type(self, name:str): + if name in self.types: + raise SemanticException(f'Redefinition of basic class {name}.') + typex = self.types[name] = Type(name) + return typex + + def change_type(self,name:str,typex:str): + self.types[name] = self.types[typex] + + def get_type(self, name:str): + try: + return self.types[name] + except KeyError: + raise SemanticException(f'Type "{name}" is not defined.') + + def __str__(self): + return '{\n\t' + '\n\t'.join(y for x in self.types.values() for y in str(x).split('\n')) + '\n}' + + def __repr__(self): + return str(self) + +class VariableInfo: + def __init__(self, name, vtype): + self.name = name + self.type = vtype + +class Scope: + def __init__(self, parent=None): + self.locals = [] + self.parent = parent + self.cil_locals = {} + self.children = [] + self.index = 0 if parent is None else len(parent) + + def __len__(self): + return len(self.locals) + + def create_child(self): + child = Scope(self) + self.children.append(child) + return child + + def define_variable(self, vname, vtype): + info = VariableInfo(vname, vtype) + self.locals.append(info) + return info + + def define_cil_local(self,cool_var_name,cil_var_name): + cil_local = self.cil_locals[cool_var_name] = cil_var_name + return cil_local + + def find_variable(self, vname, index=None): + locals = self.locals if index is None else itt.islice(self.locals, index) + try: + return next(x for x in locals if x.name == vname) + except : + if self.parent is not None: + a = self.parent.find_variable(vname, self.index) + else : + a = None + return a + + def find_cil_variable(self,vname,index = None): + locals = self.cil_locals if index is None else itt.islice(self.cil_locals, index) + try: + return next(self.cil_locals[x] for x in locals.keys() if x == vname) + except: + if self.parent is not None: + a = self.parent.find_cil_variable(vname) + else: + a = None + return a + + def is_defined(self, vname): + a = self.find_variable(vname) is not None + return a + + def is_cil_defined(self,cool_name): + a = self.find_cil_variable(cool_name) is not None + return a + + def is_local(self, vname): + return any(True for x in self.locals if x.name == vname) + + def change_type(self,vname:str,vtype): + var = self.find_variable(vname) + var.type = vtype + + def substitute_type(self,typeA,typeB): + + for var in self.locals: + if var.type == typeA: + var.type = typeB + + if self.parent: + self.parent.substitute_type(typeA,typeB) + + + +def get_common_parent(type_A, type_B = None, context = None): + if type_A.name == 'Error' or type_B.name == 'Error': + return ErrorType() + + if type_B: + parent_listA = [type_A] + parent_listB = [type_B] + common_parent = type_B + while type_A.parent is not None: + parent_listA.append(type_A.parent) + type_A = context.get_type(type_A.parent.name) + parent_listA = reversed(parent_listA) + + while type_B.parent is not None: + parent_listB.append(type_B.parent) + type_B = context.get_type(type_B.parent.name) + parent_listB = reversed(parent_listB) + + for itemA, itemB in zip(parent_listA,parent_listB): + if(itemA == itemB): + common_parent = itemA + else: + break + else: + common_parent = type_A + + return common_parent + +def multiple_get_common_parent(args, context): + least_type = args.pop(0) + + for t in args: + if isinstance(least_type, ErrorType) or isinstance(t, ErrorType): + least_type = ErrorType() + return least_type + least_type = get_common_parent(least_type,t,context) + + return least_type + +def is_local(vars,new_var): + return any(new_var == var.name for var in vars) \ No newline at end of file diff --git a/src/cool/semantic/semanticAnalizer.py b/src/cool/semantic/semanticAnalizer.py new file mode 100644 index 000000000..ee01d3a78 --- /dev/null +++ b/src/cool/semantic/semanticAnalizer.py @@ -0,0 +1,31 @@ +from cool.semantic.type_builder import TypeBuilder +from cool.semantic.type_checker import TypeChecker +from cool.semantic.type_collector import TypeCollector + + + +def run_semantic_pipeline(ast): + errors = [] + # ============== COLLECTING TYPES =============== + errors = [] + collector = TypeCollector(errors) + collector.visit(ast) + context = collector.context + print_error(errors) + + # =============== BUILDING TYPES ================ + builder = TypeBuilder(context, errors) + builder.visit(ast) + actual_counter = builder.counter + print_error(errors) + + # =============== CHECKING TYPES ================ + checker = TypeChecker(context, errors,actual_counter) + scope = checker.visit(ast) + print_error(errors) + return context,scope + +def print_error(errors): + if errors: + print(errors[0]) + raise Exception() \ No newline at end of file diff --git a/src/cool/semantic/type_builder.py b/src/cool/semantic/type_builder.py new file mode 100644 index 000000000..89970057c --- /dev/null +++ b/src/cool/semantic/type_builder.py @@ -0,0 +1,110 @@ +from cool.Parser.AstNodes import * +from cool.semantic import visitor +from cool.semantic.semantic import SemanticException +from cool.semantic.semantic import VoidType, ErrorType +from cool.utils.Errors.semantic_errors import * + +class TypeBuilder: + def __init__(self, context, errors=[]): + self.context = context + self.current_type = None + self.errors = errors + self.counter = 1 + + @visitor.on('node') + def visit(self, node): + pass + + @visitor.when(ProgramNode) + def visit(self, node): + main_row = 0 + main_column = 0 + for def_class in node.declarations: + self.visit(def_class) + if(def_class.id=='Main'): + main_row = def_class.row + main_column = def_class.column + if not self.context.types.__contains__('Main'): + text = "The class Main is not defined" + error = TypeError(0,0,text) + self.errors.append(error) + else: + if not self.context.types['Main'].methods.__contains__('main'): + text = "The main method is not defined in class Main" + error = AttributeError(main_column,main_row,text) + self.errors.append(error) + + + @visitor.when(ClassDeclarationNode) + def visit(self, node): + self.current_type = self.context.get_type(node.id) + + if node.parent: + try: + parent_type = self.context.get_type(node.parent) + self.current_type.set_parent(parent_type) + except SemanticException as ex: + error = TypeError(node.column,node.row,ex.text) + self.errors.append(error) + else: + parent_type = self.context.get_type('Object') + self.current_type.set_parent(parent_type) + + for feature in node.features: + self.visit(feature) + + @visitor.when(AttrDeclarationNode) + def visit(self, node): + try: + if node.type == 'SELF_TYPE': + node.type = self.current_type.name + attr_type = self.current_type + else: + attr_type = self.context.get_type(node.type) + except SemanticException as ex: + error = TypeError(node.column,node.row,ex.text) + self.errors.append(error) + attr_type = ErrorType() + + try: + self.current_type.define_attribute(node.id, attr_type) + except SemanticException as ex: + error = SemanticError(node.column,node.row,ex.text) + self.errors.append(error) + + @visitor.when(FuncDeclarationNode) + def visit(self, node): + arg_names, arg_types, params_found = [], [], [] + + for i,_ in enumerate(node.params): + idx = node.params[i].id + typex = node.params[i].type + params_found.append((idx,typex)) + if typex == 'SELF_TYPE': + node.params[i] = (idx,self.current_type.name) + + if node.type == 'SELF_TYPE': + node.type = self.current_type.name + + for idx, typex in params_found: + try: + arg_type = self.context.get_type(typex) + except SemanticException as ex: + error = SemanticError(node.column,node.row,ex.text) + arg_type = ErrorType() + + arg_names.append(idx) + arg_types.append(arg_type) + + try: + ret_type = VoidType() if node.type == 'void' else self.context.get_type(node.type) + except SemanticException as ex: + error = TypeError(node.column,node.row,ex.text) + self.errors.append(error) + ret_type = ErrorType() + + try: + self.current_type.define_method(node.id, arg_names, arg_types, ret_type) + except SemanticException as ex: + error = SemanticError(node.column,node.row,ex.text) + self.errors.append(error) diff --git a/src/cool/semantic/type_checker.py b/src/cool/semantic/type_checker.py new file mode 100644 index 000000000..0057ceb59 --- /dev/null +++ b/src/cool/semantic/type_checker.py @@ -0,0 +1,491 @@ +from cool.Parser.AstNodes import * +from cool.semantic import visitor +from cool.semantic.semantic import ObjectType, Scope +from cool.semantic.semantic import get_common_parent,multiple_get_common_parent,is_local +from cool.semantic.semantic import SemanticException +from cool.semantic.semantic import ErrorType, IntType, BoolType +from cool.utils.Errors.semantic_errors import * + + +class TypeChecker: + def __init__(self, context, errors=[],counter = 1): + self.context = context + self.current_type = None + self.current_feature = None + self.errors = errors + self.counter = counter + self.inference = [] + + @visitor.on('node') + def visit(self, node, scope,expected = None): + pass + + @visitor.when(ProgramNode) + def visit(self, node, scope=None, expected = None): + scope = Scope() + for declaration in node.declarations: + self.visit(declaration, scope.create_child()) + return scope + + @visitor.when(ClassDeclarationNode) + def visit(self, node, scope, expected = None): + self.current_type = self.context.get_type(node.id) + + scope.define_variable('self', self.current_type) + for feature in node.features: + if type(feature) == FuncDeclarationNode: + self.visit(feature, scope.create_child()) + else: + self.visit(feature, scope) + attr = self.current_type.get_attribute(feature.id) + scope.define_variable(attr.name, attr.type) + + + @visitor.when(AttrDeclarationNode) + def visit(self, node, scope, expected = None): + if node.expr: + expected_child = None + self.current_feature = self.current_type + + expected_child = node.type + + node_type = self.context.get_type(node.type) + self.visit(node.expr, scope,expected_child) + expr_type = node.expr.computed_type + + if not expr_type.conforms_to(node_type): + text = INCOMPATIBLE_TYPES.replace('%s',expr_type.name,1).\ + replace('%s', node.id, 1).replace('%s',node_type.name,1) + error = TypeError(node.column,node.row,text) + self.errors.append(error) + + + + @visitor.when(FuncDeclarationNode) + def visit(self, node, scope,expected = None): + self.current_feature = self.current_type.get_method(node.id) + + for param in node.params: + self.visit(param, scope) + + method_rtn_type = self.current_feature.return_type + if method_rtn_type.name != 'Void': + new_expected = self.current_feature.return_type.name + else: + new_expected = None + + self.visit(node.body, scope, expected = new_expected) + expr_type = node.body.computed_type + + method_rtn_type = self.current_feature.return_type + if method_rtn_type.name != 'Void': + try: + if not expr_type.conforms_to(method_rtn_type): + text = f'In class "{self.current_type.name}" in method "{self.current_feature.name}" return type:' + INCOMPATIBLE_TYPES.replace('%s', expr_type.name, 1).\ + replace('%s', self.current_feature.name, 1).replace('%s', method_rtn_type.name, 1) + error = TypeError(node.column,node.row,text) + self.errors.append(error) + except Exception: + text = f'In class "{self.current_type.name}" in method "{self.current_feature.name}" : ' + INCOMPATIBLE_TYPES.replace('%s', expr_type.name, 1).\ + replace('%s', self.current_feature.name, 1).replace('%s', method_rtn_type.name, 1) + error = TypeError(node.column,node.row,text) + self.errors.append(error) + + self.current_type.change_param(node.id,scope) + + @visitor.when(ParamDeclarationNode) + def visit(self, node, scope,expected = None): + try: + var_type = self.context.get_type(node.type) + except SemanticException as ex: + var_type = ErrorType() + text = f'{ex.text} Of formal parameter {node.id}' + error = TypeError(node.column,node.row,text) + self.errors.append(error) + + if node.id == 'self': + text = "self cannot be the name of a formal parameter" + error = SemanticError(node.column, node.row, text) + self.errors.append(error) + elif not is_local(scope.locals,node.id): + scope.define_variable(node.id, var_type) + else: + text = MULTIPLY_DIFINED_PARAMTER.replace('%s', node.id, 1) + error = SemanticError(node.column,node.row,text) + self.errors.append(error) + + @visitor.when(VarDeclarationNode) + def visit(self, node, scope,expected = None): + try: + var_type = self.context.get_type(node.type) + except SemanticException as ex: + text = f'In class {self.current_type.name}: '+ ex.text + var_type = ErrorType() + error = TypeError(node.column,node.row,text) + self.errors.append(error) + + scope.define_variable(node.id, var_type) + self.visit(node.expr, scope) + expr_type = node.expr.computed_type + + node.computed_type = expr_type + + @visitor.when(ExpressionGroupNode) + def visit(self, node, scope,expected = None): + scope_child = scope.create_child() + for i,child in enumerate(node.body): + if(i == len(node.body)-1): + self.visit(child , scope_child, expected) + else: + self.visit(child , scope_child) + + + body_type = node.body[-1].computed_type + node_type = body_type + node.computed_type = node_type + + @visitor.when(IfNode) + def visit(self, node, scope,expected = None): + self.visit(node.ifexp,scope,'Bool') + if_type = node.ifexp.computed_type + + self.visit(node.thenexp, scope,expected) + then_type = node.thenexp.computed_type + + self.visit(node.elseexp, scope,expected) + else_type = node.elseexp.computed_type + + if not if_type.conforms_to(BoolType()): + text = INVALID_CONDITION.replace('%s', 'if', 1) + node_type = ErrorType() + error = TypeError(node.column,node.row,text) + self.errors.append(error) + + node.computed_type = get_common_parent(then_type,else_type ,self.context) + + + @visitor.when(WhileNode) + def visit(self, node, scope,expected = None): + self.visit(node.condition, scope, 'Bool' ) + condition_type = node.condition.computed_type + + self.visit(node.body, scope) + body_type = node.body.computed_type + + if not condition_type.conforms_to(BoolType()): + text = INVALID_CONDITION.replace('%s', 'while', 1) + error = TypeError(node.column,node.row,text) + self.errors.append(error) + + node.computed_type = ObjectType() + + @visitor.when(LetNode) + def visit(self, node, scope,expected = None): + child_scope = scope.create_child() + for arg in node.params: + if arg.type == 'SELF_TYPE': + arg.type = self.current_type.name + + self.visit(arg,child_scope) + arg_type = arg.computed_type + + self.visit(node.body, child_scope,expected) + body_type = node.body.computed_type + node_type = body_type + + node.computed_type = node_type + + @visitor.when(LetDeclarationNode) + def visit(self, node, scope,expected = None): + expected_child = node.type + if node.id == 'self': + text = '\'self\' cannot be bound in a \'let\' expression.' + error = SemanticError(node.column,node.row,text) + self.errors.append(error) + try: + node_type = self.context.get_type(node.type) + except SemanticException as ex: + text = f'In class "{self.current_type.name}": '+ ex.text + node_type = ErrorType() + error = TypeError(node.column,node.row,text) + self.errors.append(error) + + if node.expr: + self.visit(node.expr, scope,expected_child) + expr_type = node.expr.computed_type + + if not expr_type.conforms_to(node_type): + text = INCOMPATIBLE_TYPES.replace('%s', expr_type.name, 1).\ + replace('%s', node.id, 1).replace('%s', node_type.name, 1) + error = TypeError(node.column,node.row,text) + self.errors.append(error) + + if not scope.is_local(node.id): + scope.define_variable(node.id, node_type) + else: + var = scope.find_variable(node.id) + var.type = node_type + + node.computed_type = node_type + + + @visitor.when(CaseNode) + def visit(self, node, scope,expected = None): + + self.visit(node.case, scope) + body_type = None + brnaches_type = [] + expr_types = [] + + for branch in node.body: + if branch.type in brnaches_type: + text = f'Duplicate branch {branch.type} in case statement.' + error = SemanticError(branch.column,branch.row,text) + self.errors.append(error) + else: + brnaches_type.append(branch.type) + + self.visit(branch,scope.create_child()) + expr_types.append(branch.computed_type) + + + node_type = multiple_get_common_parent(expr_types,self.context) + node.computed_type = node_type + + + @visitor.when(AssignNode) + def visit(self, node, scope, expected=None): + self.visit(node.expr, scope) + node_type = node.expr.computed_type + + if scope.is_defined(node.id): + var = scope.find_variable(node.id) + + if var.name == 'self': + text = SELF_IS_READONLY + error = SemanticError(node.column, node.row,text) + self.errors.append(error) + node_type = ErrorType() + elif not node_type.conforms_to(var.type): + text = INCOMPATIBLE_TYPES.replace( + '%s', node_type.name, 1).replace('%s', var.name, 1).replace('%s', var.type.name, 1) + error = TypeError(node.column, node.row, text) + self.errors.append(error) + node_type = ErrorType() + + else: + try: + var = self.current_type.get_attribute(node.id) + node_type = self.context.get_type(var.type.name) + except SemanticException as ex: + text = VARIABLE_NOT_DEFINED.replace('%s', node.id, 1) + error = NameError(node.column,node.row,text) + self.errors.append(error) + node_type = ErrorType() + + node.computed_type = node_type + + @visitor.when(CallNode) + def visit(self, node, scope,expected = None): + try: + if node.obj is None: + node.obj = InstantiateNode(self.current_type.name,node.row,node.column) + obj_type = self.current_type + node.obj.computed_type = None + else: + self.visit(node.obj, scope) + obj_type = node.obj.computed_type + + if node.parent and not obj_type.has_parent(self.context.get_type(node.parent)): + text = f'In class {self.current_type.name}: '+ f'Expression type "{obj_type.name}" does not conform to declared static dispatch type "{node.parent}" in function call of "{node.id}"' + error = TypeError(node.column,node.row,text) + self.errors.append(error) + + try: + obj_method = obj_type.get_method(node.id) + + if len(node.args) == len(obj_method.param_types): + for _,params in enumerate(zip(node.args, obj_method.param_types)): + arg, param_type = params + try: + self.visit(arg, scope,param_type.name) + arg_type = arg.computed_type + except SemanticException as ex: + text = f'In class {self.current_type.name}: '+ ex.text + node_type = ErrorType() + error = SemanticError(node.column,node.row,text) + self.errors.append(error) + + if not arg_type.conforms_to(param_type): + + try: + name_arg=arg.lex + except: + name_arg = arg.id + text = f'In class {self.current_type.name} in function call {node.id}: ' + INCOMPATIBLE_TYPES.replace('%s', arg_type.name, 1).replace('%s', name_arg, 1).replace('%s', param_type.name, 1) + error = TypeError(node.column,node.row,text) + self.errors.append(error) + else: + text = f' In class {self.current_type.name}: Method "{obj_method.name}" from "{obj_type.name}" only accepts {len(obj_method.param_types)} argument(s)' + error = SemanticError(node.column,node.row,text) + self.errors.append(error) + + if obj_method.return_type.name == 'self': + node_type = obj_type + else: + node_type = obj_method.return_type + + except SemanticException as ex: + text = f'In class {self.current_type.name}: '+ ex.text + node_type = ErrorType() + error = AttributeError(node.column,node.row,text) + self.errors.append(error) + + except SemanticException as ex: + text = f'In class {self.current_type.name}: '+ ex.text + error = SemanticError(node.column,node.row,text) + self.errors.append(error) + for arg in node.args: + self.visit(arg, scope) + node_type = ErrorType() + + node.computed_type = node_type + + @visitor.when(BinaryIntNode) + def visit(self, node, scope,expected = None): + self.visit(node.left, scope,'Int') + left_type = node.left.computed_type + + self.visit(node.right, scope,'Int') + right_type = node.right.computed_type + + if not left_type.conforms_to(IntType()) or not right_type.conforms_to(IntType()): + text = INVALID_OPERATION.replace('%s', left_type.name, 1).\ + replace('%s',node.lex,1).replace('%s', right_type.name, 1) + + node_type = ErrorType() + error = TypeError(node.column,node.row,text) + self.errors.append(error) + else: + node_type = IntType() + + node.computed_type = node_type + + @visitor.when(BinaryBoolNode) + def visit(self, node, scope,expected = None): + self.visit(node.left, scope,'Int') + left_type = node.left.computed_type + + self.visit(node.right, scope,'Int') + right_type = node.right.computed_type + + if not left_type.conforms_to(IntType()) or not right_type.conforms_to(IntType()): + text = INVALID_OPERATION.replace('%s', left_type.name, 1).\ + replace('%s',node.lex,1).replace('%s', right_type.name, 1) + node_type = ErrorType() + error = TypeError(node.column,node.row,text) + self.errors.append(error) + else: + node_type = BoolType() + + node.computed_type = node_type + + @visitor.when(EqualNode) + def visit(self, node, scope,expected = None): + self.visit(node.left, scope) + left_type = node.left.computed_type + + self.visit(node.right, scope) + right_type = node.right.computed_type + + node_type = BoolType() + + if (left_type.name in ['Int', 'Bool', 'String'] or\ + right_type.name in ['Int', 'Bool', 'String']) and\ + left_type.name != right_type.name: + text = WRONG_COMPARISON.replace('%s', left_type.name, 1).\ + replace('%s', right_type.name, 1) + error = TypeError( node.column,node.row,text) + self.errors.append(error) + node_type = ErrorType() + + node.computed_type = node_type + + @visitor.when(ConstantNumNode) + def visit(self, node, scope,expected = None): + node.computed_type = self.context.get_type('Int') + + @visitor.when(BooleanNode) + def visit(self, node, scope,expected = None): + node.computed_type = self.context.get_type('Bool') + + @visitor.when(StringNode) + def visit(self, node, scope,expected = None): + node.computed_type = self.context.get_type('String') + + @visitor.when(VariableNode) + def visit(self, node, scope,expected = None): + + if scope.is_defined(node.lex): + var = scope.find_variable(node.lex) + node_type = var.type + else: + try: + var = self.current_type.get_attribute(node.lex) + node_type = var.type + except SemanticException as ex: + node_type = ErrorType() + error = NameError(node.column,node.row,ex.text) + self.errors.append(error) + + node.computed_type = node_type + + @visitor.when(InstantiateNode) + def visit(self, node, scope,expected = None): + try: + node_type = self.context.get_type(node.lex) + if node_type.name == 'SELF_TYPE': + node_type = self.current_type + except SemanticException as ex: + text = f'In class {self.current_type.name}: '+ ex.text + node_type = ErrorType() + error = TypeError(node.column,node.row,text) + self.errors.append(error) + + node.computed_type = node_type + + @visitor.when(IsVoidNode) + def visit(self, node, scope,expected = None): + self.visit(node.right, scope) + node.computed_type = self.context.get_type('Bool') + + @visitor.when(NotNode) + def visit(self, node, scope,expected = None): + self.visit(node.right, scope) + expr_type = node.right.computed_type + node_type = self.context.get_type('Bool') + + if expr_type.name != 'Bool': + text = WRONG_TYPE_EXPECTED.replace('%s', 'not', 1).\ + replace('%s', expr_type.name, 1).replace('%s','Bool', 1) + error = TypeError(node.column, node.row, text) + self.errors.append(error) + node_type = ErrorType() + + node.computed_type = node_type + + + @visitor.when(NegateNode) + def visit(self, node, scope,expected = None): + self.visit(node.right, scope) + expr_type = node.right.computed_type + node_type = self.context.get_type('Int') + + if expr_type.name != 'Int': + text = WRONG_TYPE_EXPECTED.replace('%s', '~', 1).\ + replace('%s', expr_type.name, 1).replace('%s','Int', 1) + error = TypeError(node.column, node.row, text) + self.errors.append(error) + node_type = ErrorType() + + node.computed_type = node_type diff --git a/src/cool/semantic/type_collector.py b/src/cool/semantic/type_collector.py new file mode 100644 index 000000000..6d5407c31 --- /dev/null +++ b/src/cool/semantic/type_collector.py @@ -0,0 +1,60 @@ +from cool.Parser.AstNodes import * +from cool.semantic import visitor +from cool.semantic.semantic import SemanticException +from cool.semantic.semantic import ErrorType +from cool.semantic.semantic import Context +from cool.utils.Errors.semantic_errors import * + +class TypeCollector(object): + def __init__(self, errors=[]): + self.context = None + self.errors = errors + self.type_level = {} + self.BUILT_IN_TYPES = ['Int','String','Bool','Object','SELF_TYPE'] + + @visitor.on('node') + def visit(self, node): + pass + + + @visitor.when(ProgramNode) + def visit(self, node): + self.context = Context() + self.context.create_type('SELF_TYPE') + + for def_class in node.declarations: + self.visit(def_class) + + def get_type_level(typex): + try: + parent = self.type_level[typex] + except KeyError: + return 0 + if parent == 0: + text = f'Class {def_class.id}, or an ancestor of {def_class.id}, is involved in an inheritance cycle.' + self.errors.append(SemanticError(def_class.column,def_class.row,text)) + elif type(parent) is not int: + self.type_level[typex] = 0 if parent else 1 + if type(parent) is str: + self.type_level[typex] = get_type_level(parent) + 1 + + return self.type_level[typex] + + node.declarations.sort(key = lambda node: get_type_level(node.id)) + + @visitor.when(ClassDeclarationNode) + def visit(self, node): + try: + self.context.create_type(node.id) + + if node.parent in ['Int','String','Bool','SELF_TYPE']: + error = SemanticError(node.column,node.row,\ + f"Class {node.id} cannot inherit class {node.parent}.") + self.errors.append(error) + + self.type_level[node.id] = node.parent + + except SemanticException as ex: + node.parent = ErrorType().name + error = SemanticError(node.column,node.row,ex.text) + self.errors.append(error) \ No newline at end of file diff --git a/src/cool/semantic/visitor.py b/src/cool/semantic/visitor.py new file mode 100644 index 000000000..964842836 --- /dev/null +++ b/src/cool/semantic/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/cool/utils/Errors/CoolError.py b/src/cool/utils/Errors/CoolError.py new file mode 100644 index 000000000..ea71d8e3d --- /dev/null +++ b/src/cool/utils/Errors/CoolError.py @@ -0,0 +1,19 @@ +from typing import Text + + +class CoolError(Exception): + def __init__(self, column: int, line: int, text: str): + super().__init__(text) + self.column = column + self.line = line + self.text = text + + @property + def errorType(self): + return "Cool Compiler Error" + + def __str__(self): + return f'({self.line}, {self.column}) - {self.errorType}: {self.text}' + + def __repr__(self): + return str(self) diff --git a/src/cool/utils/Errors/LexerErrors.py b/src/cool/utils/Errors/LexerErrors.py new file mode 100644 index 000000000..241283ddd --- /dev/null +++ b/src/cool/utils/Errors/LexerErrors.py @@ -0,0 +1,16 @@ +from ply.lex import LexError +from cool.utils.Errors.CoolError import CoolError + +class LexerErrors(CoolError): + def __init__(self, column: int, line: int, text: str): + super().__init__(column, line, text) + + UNKNOWN_TOKEN = 'Unknown token' + NULL_STRING = 'Null character in string' + UNTERMINATED_STRING = 'Unterminated string' + EOF_STRING = 'EOF in string' + EOF_COMMENT = 'EOF in comment' + + @property + def errorType(self): + return "LexicographicError" diff --git a/src/cool/utils/Errors/__init__.py b/src/cool/utils/Errors/__init__.py new file mode 100644 index 000000000..e69de29bb diff --git a/src/cool/utils/Errors/parser_errors.py b/src/cool/utils/Errors/parser_errors.py new file mode 100644 index 000000000..97940df08 --- /dev/null +++ b/src/cool/utils/Errors/parser_errors.py @@ -0,0 +1,10 @@ +from cool.utils.Errors.CoolError import CoolError + +class ParserError(CoolError): + def __init__(self, column: int, line: int, text: str): + super().__init__(column, line, text) + + @property + def errorType(self): + return "SyntacticError" + \ No newline at end of file diff --git a/src/cool/utils/Errors/semantic_errors.py b/src/cool/utils/Errors/semantic_errors.py new file mode 100644 index 000000000..736f0b2a8 --- /dev/null +++ b/src/cool/utils/Errors/semantic_errors.py @@ -0,0 +1,48 @@ +from cool.utils.Errors.CoolError import CoolError + +WRONG_SIGNATURE = 'Incompatible number of formal parameters in redefined method %s' +SELF_IS_READONLY = 'Variable "self" is read-only.' +LOCAL_ALREADY_DEFINED = 'Variable "%s" is already defined in method "%s".' +INCOMPATIBLE_TYPES = 'Inferred type %s in "%s" does not conform to declared type %s.' +VARIABLE_NOT_DEFINED = 'Variable "%s" is not defined in "%s".' +INVALID_OPERATION = 'non-Int arguments: %s %s %s.' +TYPE_AS_VARIABLE = 'Type %s used as variable.' +INVALID_CONDITION = 'Predicate of \'%s\' does not have type Bool ' +INVALID_HERITAGE = 'Cant heritage from %s' +INVALID_UNARY_OPERATION = 'Operation is not defined with %s' +MULTIPLY_DIFINED_PARAMTER = 'Formal parameter %s is multiply defined' +WRONG_COMPARISON = 'Illegal comparison between basic types %s and %s.' +WRONG_TYPE_EXPECTED = ' Argument of \'%s\' has type %s instead of %s.' + + +class SemanticError(CoolError): + def __init__(self, column: int, line: int, text: str): + super().__init__(column, line, text) + + @property + def errorType(self): + return "SemanticError" + +class TypeError(SemanticError): + def __init__(self, column: int, line: int, text: str): + super().__init__(column, line, text) + + @property + def errorType(self): + return "TypeError" + +class NameError(SemanticError): + def __init__(self, column: int, line: int, text: str): + super().__init__(column, line, text) + + @property + def errorType(self): + return "NameError" + +class AttributeError(SemanticError): + def __init__(self, column: int, line: int, text: str): + super().__init__(column, line, text) + + @property + def errorType(self): + return "AttributeError" \ No newline at end of file diff --git a/src/cool/utils/LexerTokens/Tokens.py b/src/cool/utils/LexerTokens/Tokens.py new file mode 100644 index 000000000..3cc29fcc7 --- /dev/null +++ b/src/cool/utils/LexerTokens/Tokens.py @@ -0,0 +1,64 @@ +literals=[ + 'SEMI', # ; + 'COLON', # : + 'COMMA', # , + 'DOT', # . + 'OPAR', # ( + 'CPAR', # ) + 'OCUR', # { + 'CCUR', # } + 'LARROW', # <- + 'ARROBA', # @ + 'RARROW', # => + 'NOX', # ~ + 'EQUAL', # = + 'PLUS', # + + 'MINUS', # - + 'STAR', # * + 'DIV', # / + 'LESS', # < + 'LESSEQ', # <= + 'ID', + 'TYPE', + 'NUMBER', + 'STRING' +] + +reserved = { + 'isvoid' : 'isvoid', + 'not' : 'not', + 'if' : 'if', + 'else' : 'else', + 'then' : 'then', + 'fi' : 'fi', + 'class' : 'class', + 'inherits' : 'inherits', + 'while' : 'while', + 'loop' : 'loop', + 'pool' : 'pool', + 'let' : 'let', + 'in' : 'in', + 'case' : 'case', + 'esac' : 'esac', + 'of' : 'of', + 'new' : 'new', + 'true': 'true', + 'false':'false' + + } + +tokens = list(reserved.values()) + literals + +class Token: + def __init__(self, line: int, column: int, type: str, value: str) -> None: + self.line = line + self.column = column + self.type = type + self.value = value + + def __str__(self) -> str: + return f'{self.type} {self.value} {self.line} {self.column}' + + + def __repr__(self): + return str(self) \ No newline at end of file diff --git a/src/cool/utils/LexerTokens/__init__.py b/src/cool/utils/LexerTokens/__init__.py new file mode 100644 index 000000000..e69de29bb diff --git a/src/coolc.sh b/src/coolc.sh index 3088de4f9..8a5d166ba 100755 --- a/src/coolc.sh +++ b/src/coolc.sh @@ -4,8 +4,10 @@ INPUT_FILE=$1 OUTPUT_FILE=${INPUT_FILE:0: -2}mips # Si su compilador no lo hace ya, aquí puede imprimir la información de contacto -echo "LINEA_CON_NOMBRE_Y_VERSION_DEL_COMPILADOR" # TODO: Recuerde cambiar estas -echo "Copyright (c) 2019: Nombre1, Nombre2, Nombre3" # TODO: líneas a los valores correctos +echo "Cool Compiler 2021 v1" # TODO: Recuerde cambiar estas +echo "Copyright (c) 2021: Abel A. Cruz Suarez, Daniel Reynel Dominguez" # TODO: líneas a los valores correctos + +FILE="__main__.py" # Llamar al compilador -echo "Compiling $INPUT_FILE into $OUTPUT_FILE" +python ${FILE} $INPUT_FILE $OUTPUT_FILE