From d7a3f1ead7ff9e68684892c522612b8f39ad8051 Mon Sep 17 00:00:00 2001 From: luisoibarra Date: Thu, 4 Mar 2021 15:55:06 -0500 Subject: [PATCH 001/143] Update team.yml Team information filled --- doc/team.yml | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/doc/team.yml b/doc/team.yml index c16162532..0664fefc4 100644 --- a/doc/team.yml +++ b/doc/team.yml @@ -1,10 +1,10 @@ members: - - name: Nombre Apellido1 Apellido2 - github: github_id - group: CXXX - - name: Nombre Apellido1 Apellido2 - github: github_id - group: CXXX - - name: Nombre Apellido1 Apellido2 - github: github_id - group: CXXX + - name: Damián O´Hallorans Toledo + github: NazDia + group: C412 + - name: Luis Ernesto Ibarra Vázquez + github: luisoibarra + group: C411 + - name: Luis Enrique Dalmau Coopat + github: LukeDalmau + group: C411 From 1a3b1397da6fd4ebea71f3e8c107e0af15345ecc Mon Sep 17 00:00:00 2001 From: luisoibarra Date: Thu, 4 Mar 2021 16:01:49 -0500 Subject: [PATCH 002/143] Initial Infrastructure Proposal Basic packages with specific tasks created. - lexer: Contains the logic of the lexical analysis - parser: Contains the logic of the parsing process - semantic: Contains the logic of the semantic analysis - cil: Contains the logic for processing CIL middle code - mips: Contains the logic for processing MIPS code Use example for Pipes in main.py using Mocks --- src/cool_cmp/__init__.py | 3 + src/cool_cmp/cil/__init__.py | 15 ++ src/cool_cmp/cil/errors.py | 5 + src/cool_cmp/cil/interface.py | 14 ++ src/cool_cmp/cil/visitors/__init__.py | 3 + src/cool_cmp/lexer/__init__.py | 22 ++ src/cool_cmp/lexer/errors.py | 5 + src/cool_cmp/lexer/interface.py | 11 + src/cool_cmp/mips/__init__.py | 15 ++ src/cool_cmp/mips/errors.py | 5 + src/cool_cmp/mips/interface.py | 16 ++ src/cool_cmp/mips/visitors/__init__.py | 3 + src/cool_cmp/parser/__init__.py | 15 ++ src/cool_cmp/parser/errors.py | 5 + src/cool_cmp/parser/interface.py | 11 + src/cool_cmp/semantic/__init__.py | 17 ++ src/cool_cmp/semantic/errors.py | 5 + src/cool_cmp/semantic/interface.py | 15 ++ src/cool_cmp/semantic/visitors/__init__.py | 3 + src/cool_cmp/shared/__init__.py | 3 + src/cool_cmp/shared/ast/__init__.py | 22 ++ src/cool_cmp/shared/ast/cil.py | 4 + src/cool_cmp/shared/ast/cool.py | 265 +++++++++++++++++++++ src/cool_cmp/shared/ast/mips.py | 4 + src/cool_cmp/shared/errors.py | 18 ++ src/cool_cmp/shared/pipeline/__init__.py | 52 ++++ src/cool_cmp/shared/pipeline/pipes.py | 39 +++ src/cool_cmp/shared/token.py | 12 + src/main.py | 151 ++++++++++++ 29 files changed, 758 insertions(+) create mode 100644 src/cool_cmp/__init__.py create mode 100644 src/cool_cmp/cil/__init__.py create mode 100644 src/cool_cmp/cil/errors.py create mode 100644 src/cool_cmp/cil/interface.py create mode 100644 src/cool_cmp/cil/visitors/__init__.py create mode 100644 src/cool_cmp/lexer/__init__.py create mode 100644 src/cool_cmp/lexer/errors.py create mode 100644 src/cool_cmp/lexer/interface.py create mode 100644 src/cool_cmp/mips/__init__.py create mode 100644 src/cool_cmp/mips/errors.py create mode 100644 src/cool_cmp/mips/interface.py create mode 100644 src/cool_cmp/mips/visitors/__init__.py create mode 100644 src/cool_cmp/parser/__init__.py create mode 100644 src/cool_cmp/parser/errors.py create mode 100644 src/cool_cmp/parser/interface.py create mode 100644 src/cool_cmp/semantic/__init__.py create mode 100644 src/cool_cmp/semantic/errors.py create mode 100644 src/cool_cmp/semantic/interface.py create mode 100644 src/cool_cmp/semantic/visitors/__init__.py create mode 100644 src/cool_cmp/shared/__init__.py create mode 100644 src/cool_cmp/shared/ast/__init__.py create mode 100644 src/cool_cmp/shared/ast/cil.py create mode 100644 src/cool_cmp/shared/ast/cool.py create mode 100644 src/cool_cmp/shared/ast/mips.py create mode 100644 src/cool_cmp/shared/errors.py create mode 100644 src/cool_cmp/shared/pipeline/__init__.py create mode 100644 src/cool_cmp/shared/pipeline/pipes.py create mode 100644 src/cool_cmp/shared/token.py create mode 100644 src/main.py diff --git a/src/cool_cmp/__init__.py b/src/cool_cmp/__init__.py new file mode 100644 index 000000000..f9ed839e3 --- /dev/null +++ b/src/cool_cmp/__init__.py @@ -0,0 +1,3 @@ +""" +Cool Compiler +""" \ No newline at end of file diff --git a/src/cool_cmp/cil/__init__.py b/src/cool_cmp/cil/__init__.py new file mode 100644 index 000000000..d3c124e42 --- /dev/null +++ b/src/cool_cmp/cil/__init__.py @@ -0,0 +1,15 @@ +""" +CIL package +""" + +from typing import List +from cool_cmp.shared.pipeline import PipeResult, InterfacePipeline +from cool_cmp.semantic import SemanticPipeline +from cool_cmp.cil.interface import ICil + +class CilPipeline(InterfacePipeline): + def __init__(self, semantic_pipeline:SemanticPipeline, *cils:List[ICil]): + super().__init__(semantic_pipeline, *cils) + + def __call__(self, program:str)->PipeResult: + return super().__call__(program) diff --git a/src/cool_cmp/cil/errors.py b/src/cool_cmp/cil/errors.py new file mode 100644 index 000000000..0139c4ec8 --- /dev/null +++ b/src/cool_cmp/cil/errors.py @@ -0,0 +1,5 @@ +""" +CIL errors +""" + +from cool_cmp.shared.errors import CoolError \ No newline at end of file diff --git a/src/cool_cmp/cil/interface.py b/src/cool_cmp/cil/interface.py new file mode 100644 index 000000000..b07905cd3 --- /dev/null +++ b/src/cool_cmp/cil/interface.py @@ -0,0 +1,14 @@ +from cool_cmp.shared.ast import BaseAST +from cool_cmp.shared.pipeline import IPipeable + +class ICil(IPipeable): + """ + CIL interface to implement + """ + + @property + def name(self)->str: + raise NotImplementedError() + + def __call__(self, ast:BaseAST) -> BaseAST: + raise NotImplementedError() diff --git a/src/cool_cmp/cil/visitors/__init__.py b/src/cool_cmp/cil/visitors/__init__.py new file mode 100644 index 000000000..6a5be3d16 --- /dev/null +++ b/src/cool_cmp/cil/visitors/__init__.py @@ -0,0 +1,3 @@ +""" +Middle code visitors +""" \ No newline at end of file diff --git a/src/cool_cmp/lexer/__init__.py b/src/cool_cmp/lexer/__init__.py new file mode 100644 index 000000000..96fa5bb7b --- /dev/null +++ b/src/cool_cmp/lexer/__init__.py @@ -0,0 +1,22 @@ +""" +Cool lexical analysis package +""" + +from cool_cmp.shared.token import Token +from cool_cmp.lexer.interface import ILexer +from cool_cmp.shared.pipeline import PipeResult, Pipeline, Pipe + +class LexerPipeline(Pipeline): + + def __init__(self, lexer:ILexer): + result = PipeResult() + + def get_tokens(program:str): + tokens = lexer(program) + result.tokens = tokens + return result + + super().__init__(Pipe(get_tokens)) + + def __call__(self, program:str)->PipeResult: + return super().__call__(program) diff --git a/src/cool_cmp/lexer/errors.py b/src/cool_cmp/lexer/errors.py new file mode 100644 index 000000000..fe68e4d09 --- /dev/null +++ b/src/cool_cmp/lexer/errors.py @@ -0,0 +1,5 @@ +""" +Lexer errors +""" + +from cool_cmp.shared.errors import CoolError \ No newline at end of file diff --git a/src/cool_cmp/lexer/interface.py b/src/cool_cmp/lexer/interface.py new file mode 100644 index 000000000..8d81a0881 --- /dev/null +++ b/src/cool_cmp/lexer/interface.py @@ -0,0 +1,11 @@ +from typing import List +from cool_cmp.shared.token import Token +from cool_cmp.shared.pipeline import IPipeable + +class ILexer(IPipeable): + """ + Lexer interface to implement + """ + + def __call__(self, program_string:str) -> List[Token]: + raise NotImplementedError() diff --git a/src/cool_cmp/mips/__init__.py b/src/cool_cmp/mips/__init__.py new file mode 100644 index 000000000..1d15067f2 --- /dev/null +++ b/src/cool_cmp/mips/__init__.py @@ -0,0 +1,15 @@ +""" +MIPS package +""" + +from typing import List +from cool_cmp.mips.interface import IMips +from cool_cmp.cil import CilPipeline +from cool_cmp.shared.pipeline import PipeResult, InterfacePipeline + +class MipsPipeline(InterfacePipeline): + def __init__(self, cil_pipeline:CilPipeline, *mipses:List[IMips]): + super().__init__(cil_pipeline, *mipses) + + def __call__(self, program:str)->PipeResult: + return super().__call__(program) \ No newline at end of file diff --git a/src/cool_cmp/mips/errors.py b/src/cool_cmp/mips/errors.py new file mode 100644 index 000000000..76123768b --- /dev/null +++ b/src/cool_cmp/mips/errors.py @@ -0,0 +1,5 @@ +""" +MIPS errors +""" + +from cool_cmp.shared.errors import CoolError \ No newline at end of file diff --git a/src/cool_cmp/mips/interface.py b/src/cool_cmp/mips/interface.py new file mode 100644 index 000000000..55e2a053f --- /dev/null +++ b/src/cool_cmp/mips/interface.py @@ -0,0 +1,16 @@ + +from cool_cmp.shared.ast import BaseAST +from cool_cmp.shared.pipeline import IPipeable + +class IMips(IPipeable): + """ + MIPS interface to implement + """ + + @property + def name(self)->str: + raise NotImplementedError() + + def __call__(self, ast:BaseAST) -> BaseAST: + raise NotImplementedError() + diff --git a/src/cool_cmp/mips/visitors/__init__.py b/src/cool_cmp/mips/visitors/__init__.py new file mode 100644 index 000000000..69c434f60 --- /dev/null +++ b/src/cool_cmp/mips/visitors/__init__.py @@ -0,0 +1,3 @@ +""" +MIPS Visitors +""" \ No newline at end of file diff --git a/src/cool_cmp/parser/__init__.py b/src/cool_cmp/parser/__init__.py new file mode 100644 index 000000000..19d052b20 --- /dev/null +++ b/src/cool_cmp/parser/__init__.py @@ -0,0 +1,15 @@ +""" +Cool parsing package +""" + +from cool_cmp.parser.interface import IParser +from cool_cmp.lexer import LexerPipeline +from cool_cmp.shared.pipeline import PipeResult, InterfacePipeline + +class ParserPipeline(InterfacePipeline): + + def __init__(self, lexer_pipeline:LexerPipeline, parser:IParser): + super().__init__(lexer_pipeline, *[parser,]) + + def __call__(self, program:str)->PipeResult: + return super().__call__(program) diff --git a/src/cool_cmp/parser/errors.py b/src/cool_cmp/parser/errors.py new file mode 100644 index 000000000..33bcce770 --- /dev/null +++ b/src/cool_cmp/parser/errors.py @@ -0,0 +1,5 @@ +""" +Parser errors +""" + +from cool_cmp.shared.errors import CoolError \ No newline at end of file diff --git a/src/cool_cmp/parser/interface.py b/src/cool_cmp/parser/interface.py new file mode 100644 index 000000000..b46883774 --- /dev/null +++ b/src/cool_cmp/parser/interface.py @@ -0,0 +1,11 @@ +from typing import List +from cool_cmp.shared.token import Token +from cool_cmp.shared.ast import BaseAST +from cool_cmp.shared.pipeline import IPipeable + +class IParser(IPipeable): + """ + Parser interface to implement + """ + def __call__(self, tokens:List[Token]) -> BaseAST: + raise NotImplementedError() diff --git a/src/cool_cmp/semantic/__init__.py b/src/cool_cmp/semantic/__init__.py new file mode 100644 index 000000000..ecc7a2bcc --- /dev/null +++ b/src/cool_cmp/semantic/__init__.py @@ -0,0 +1,17 @@ +""" +Cool semantic package +""" + +from typing import List +from cool_cmp.semantic.interface import ISemantic +from cool_cmp.parser import ParserPipeline +from cool_cmp.shared.pipeline import PipeResult, InterfacePipeline + + +class SemanticPipeline(InterfacePipeline): + + def __init__(self, parser_pipeline:ParserPipeline, *semantics:List[ISemantic]): + super().__init__(parser_pipeline, *semantics) + + def __call__(self, program:str)->PipeResult: + return super().__call__(program) diff --git a/src/cool_cmp/semantic/errors.py b/src/cool_cmp/semantic/errors.py new file mode 100644 index 000000000..82b4d957f --- /dev/null +++ b/src/cool_cmp/semantic/errors.py @@ -0,0 +1,5 @@ +""" +Semantic errors +""" + +from cool_cmp.shared.errors import CoolError \ No newline at end of file diff --git a/src/cool_cmp/semantic/interface.py b/src/cool_cmp/semantic/interface.py new file mode 100644 index 000000000..ea0a171e6 --- /dev/null +++ b/src/cool_cmp/semantic/interface.py @@ -0,0 +1,15 @@ +from cool_cmp.shared.ast import BaseAST +from cool_cmp.shared.pipeline import IPipeable + +class ISemantic(IPipeable): + """ + Semantic interface to implement + """ + + @property + def name(self)->str: + raise NotImplementedError() + + def __call__(self, ast:BaseAST) -> BaseAST: + raise NotImplementedError() + diff --git a/src/cool_cmp/semantic/visitors/__init__.py b/src/cool_cmp/semantic/visitors/__init__.py new file mode 100644 index 000000000..f2b87d7a1 --- /dev/null +++ b/src/cool_cmp/semantic/visitors/__init__.py @@ -0,0 +1,3 @@ +""" +Semantic visitors +""" \ No newline at end of file diff --git a/src/cool_cmp/shared/__init__.py b/src/cool_cmp/shared/__init__.py new file mode 100644 index 000000000..cb590d145 --- /dev/null +++ b/src/cool_cmp/shared/__init__.py @@ -0,0 +1,3 @@ +""" +Shared code between packages +""" \ No newline at end of file diff --git a/src/cool_cmp/shared/ast/__init__.py b/src/cool_cmp/shared/ast/__init__.py new file mode 100644 index 000000000..b63d1ffd6 --- /dev/null +++ b/src/cool_cmp/shared/ast/__init__.py @@ -0,0 +1,22 @@ +""" +AST package +""" + +from cool_cmp.shared.errors import ErrorTracker + +class Node: + """ + Base node for AST + """ + def __init__(self,row:int=None,column:int=None): + self.row = row + self.column = column + +class BaseAST(ErrorTracker): + """ + Base AST class + """ + + def __init__(self, initial_node:Node): + self.node = initial_node + super().__init__() diff --git a/src/cool_cmp/shared/ast/cil.py b/src/cool_cmp/shared/ast/cil.py new file mode 100644 index 000000000..562f0be50 --- /dev/null +++ b/src/cool_cmp/shared/ast/cil.py @@ -0,0 +1,4 @@ +""" +CIL AST nodes +""" +from cool_cmp.shared.ast import Node diff --git a/src/cool_cmp/shared/ast/cool.py b/src/cool_cmp/shared/ast/cool.py new file mode 100644 index 000000000..ab082a5b3 --- /dev/null +++ b/src/cool_cmp/shared/ast/cool.py @@ -0,0 +1,265 @@ +""" +Cool AST nodes +""" +from cool_cmp.shared.ast import Node + +class ProgramNode(Node): + def __init__(self, declarations,row=None,column=None): + super().__init__(row,column) + self.declarations = declarations + + def __iter__(self): + for x in self.declarations: + yield from x + +class DeclarationNode(Node): + pass + +class ExpressionNode(Node): + pass + +class ClassDeclarationNode(DeclarationNode): + def __init__(self, idx, features, parent=None,row=None,column=None): + super().__init__(row,column) + self.id = idx + self.parent = parent if parent else 'Object' + self.features = features + + def __iter__(self): + yield self + for x in self.features: + yield from x + +class FuncDeclarationNode(DeclarationNode): + def __init__(self, idx, params, return_type, body,row=None,column=None): + super().__init__(row,column) + self.id = idx + self.params = params + self.type = return_type + self.body = body + + def __iter__(self): + yield self + yield from self.params + yield from self.body + +class AttrDeclarationNode(DeclarationNode): + def __init__(self, idx, typex, expr=None,row=None,column=None): + super().__init__(row,column) + self.id = idx + self.type = typex + self.expr = expr + + def __iter__(self): + yield self + if self.expr: + yield from self.expr + +class ParamNode(DeclarationNode): + def __init__(self, idx, typex, row=None,column=None): + super().__init__(row,column) + self.id = idx + self.type = typex + + def __iter__(self): + yield self + +class SpecialNode(ExpressionNode): + def __init__(self, func,row=None,column=None): + super().__init__(row,column) + self.func = func + + def __iter__(self): + yield self + +class VarDeclarationNode(ExpressionNode): + def __init__(self, idx, typex, expr,row=None,column=None): + super().__init__(row,column) + self.id = idx + self.type = typex + self.expr = expr + + def __iter__(self): + yield self + if self.expr: + yield from self.expr + +class AssignNode(ExpressionNode): + def __init__(self, idx, expr,row=None,column=None): + super().__init__(row,column) + self.id = idx + self.expr = expr + + def __iter__(self): + yield self + if self.expr: + yield from self.expr + +class CallNode(ExpressionNode): + def __init__(self, obj, idx, args,at_type,row=None,column=None): + super().__init__(row,column) + self.obj = obj + self.id = idx + self.args = args + self.at = at_type + + def __iter__(self): + yield from self.obj + for x in self.args: + yield from x + yield self + +class BlockNode(ExpressionNode): + def __init__(self, expr_list,row=None,column=None): + super().__init__(row,column) + self.expr_list = expr_list + + def __iter__(self): + yield self + for x in self.expr_list: + yield from x + +class ConditionalNode(ExpressionNode): + def __init__(self, condition,then_expr,else_expr,row=None,column=None): + super().__init__(row,column) + self.condition = condition + self.then_expr = then_expr + self.else_expr = else_expr + + def get_return_type(self,current_type): + else_type = self.else_expr.type + then_type = self.then_expr.type + return else_type.join(then_type,current_type) + + def __iter__(self): + yield self + for x in [self.condition,self.then_expr,self.else_expr]: + yield from x + +class CheckNode(ExpressionNode): + def __init__(self, idx, typex, expr,row=None,column=None): + super().__init__(row,column) + self.id = idx + self.type = typex + self.expr = expr + + def __iter__(self): + yield self + yield from self.expr + +class LetNode(ExpressionNode): + def __init__(self, dec_var_list, expr,row=None,column=None): + super().__init__(row,column) + self.params = dec_var_list + self.expr = expr + + def __iter__(self): + yield self + for x in self.params: + yield from x + + +class CaseNode(ExpressionNode): + def __init__(self, expr, check_list,row=None,column=None): + super().__init__(row,column) + self.expr = expr + self.params = check_list + + def __iter__(self): + yield self + yield from self.expr + for x in self.params: + yield from x + +class WhileNode(ExpressionNode): + def __init__(self, condition, expr,row=None,column=None): + super().__init__(row,column) + self.condition = condition + self.expr = expr + + def __iter__(self): + yield self + yield self.condition + yield self.expr + +class AtomicNode(ExpressionNode): + def __init__(self, lex,row=None,column=None): + super().__init__(row,column) + self.lex = lex + + def __iter__(self): + yield self + +class UnaryNode(ExpressionNode): + def __init__(self, member,row=None,column=None): + super().__init__(row,column) + self.member = member + + def __iter__(self): + yield self + yield self.member + +class BinaryNode(ExpressionNode): + def __init__(self, left, right,row=None,column=None): + super().__init__(row,column) + self.left = left + self.right = right + + def __iter__(self): + yield self + yield self.left + yield self.right + +class StringNode(AtomicNode): + pass + +class BoolNode(AtomicNode): + pass + +class ConstantNumNode(AtomicNode): + pass + +class VoidNode(AtomicNode): + pass + +class VariableNode(AtomicNode): + pass + +class InstantiateNode(AtomicNode): + pass + +class NotNode(UnaryNode): + pass + +class RoofNode(UnaryNode): + pass + +class IsVoidNode(UnaryNode): + pass + +class PlusNode(BinaryNode): + pass + +class MinusNode(BinaryNode): + pass + +class StarNode(BinaryNode): + pass + +class DivNode(BinaryNode): + pass + +class EqualNode(BinaryNode): + pass + +class GreaterNode(BinaryNode): + pass + +class GreaterEqualNode(BinaryNode): + pass + +class LesserNode(BinaryNode): + pass + +class LesserEqualNode(BinaryNode): + pass \ No newline at end of file diff --git a/src/cool_cmp/shared/ast/mips.py b/src/cool_cmp/shared/ast/mips.py new file mode 100644 index 000000000..ccac1af73 --- /dev/null +++ b/src/cool_cmp/shared/ast/mips.py @@ -0,0 +1,4 @@ +""" +Mips AST Nodes +""" +from cool_cmp.shared.ast import Node diff --git a/src/cool_cmp/shared/errors.py b/src/cool_cmp/shared/errors.py new file mode 100644 index 000000000..b3a8e3871 --- /dev/null +++ b/src/cool_cmp/shared/errors.py @@ -0,0 +1,18 @@ +class CoolError: + """ + Base Cool Error + """ + + def __init__(self, msg:str): + self.error = msg + + def print_error(self): + print(self.error) + +class ErrorTracker: + + def __init__(self): + self.errors = [] + + def add_error(self, error:CoolError): + self.errors.append(error) \ No newline at end of file diff --git a/src/cool_cmp/shared/pipeline/__init__.py b/src/cool_cmp/shared/pipeline/__init__.py new file mode 100644 index 000000000..4c385b2a3 --- /dev/null +++ b/src/cool_cmp/shared/pipeline/__init__.py @@ -0,0 +1,52 @@ +""" +Cool pipes package +""" + +from typing import List +from cool_cmp.shared.pipeline.pipes import * + +class IPipeable: + + @property + def name(self)->str: + raise NotImplementedError() + + def __call__(self, arg): + raise NotImplementedError() + +class PipeResult: + """ + Class that should return all Cool pipelines + Defines a last property that returns the last object setted as an atribute + """ + + def __init__(self): + self.last = None + + def __setattr__(self, name:str, value): + if name != "last": + self.last = value + super().__setattr__(name, value) + + def __str__(self): + result = "PipeResult:\n" + for attr in self.__dict__.keys(): + result += f" {attr}\n" + return result + +class InterfacePipeline(Pipeline): + + def __init__(self, pipeline:'InterfacePipeline', *interfaces:List[IPipeable]): + + def make_pipe(result:PipeResult, interface:IPipeable): + returned = interface(result.last) + result.__setattr__(interface.name, returned) + return result + + pipes = [] + for interface in interfaces: + pipes.append(Pipe(lambda x, y=interface: make_pipe(x,y))) + if pipeline: + super().__init__(pipeline, *pipes) + else: + super().__init__(*pipes) \ No newline at end of file diff --git a/src/cool_cmp/shared/pipeline/pipes.py b/src/cool_cmp/shared/pipeline/pipes.py new file mode 100644 index 000000000..c72ec43b9 --- /dev/null +++ b/src/cool_cmp/shared/pipeline/pipes.py @@ -0,0 +1,39 @@ +from typing import Callable + +class Pipe: + def __init__(self, pipe_func:Callable[[object],object], pipe_check:Callable[[object],bool]=None): + """ + pipe_func: callable that recieve an argument and return a value + pipe_check: callable that recieve an argument and return if the pipe can be executed + """ + self.func = pipe_func + if pipe_check: + self.check = pipe_check + else: + self.check = lambda x: True + + def __call__(self, *args, **kwargs): + return self.func(*args, **kwargs) + + def can_execute(self, dictionary): + return self.check(dictionary) + +class Pipeline(Pipe): + + def __init__(self, *pipes): + super().__init__(self.__call__) + self.pipes = [pipe for pipe in pipes] + + def __call__(self, *args, **kwargs): + start_pipe = self.pipes[0] + + result = start_pipe(*args, **kwargs) + + for pipe in self.pipes[1:]: + if pipe.can_execute(result): + result = pipe(result) + else: + break + + return result + diff --git a/src/cool_cmp/shared/token.py b/src/cool_cmp/shared/token.py new file mode 100644 index 000000000..9a2d8cdd5 --- /dev/null +++ b/src/cool_cmp/shared/token.py @@ -0,0 +1,12 @@ +class Token: + + def __init__(self, lex:str, line:int, column:int): + self.lex = lex + self.line = line + self.column = column + + def __str__(self): + return f"{self.lex} Line:{self.line} Column:{self.column}" + + def __repr__(self): + return str(self) \ No newline at end of file diff --git a/src/main.py b/src/main.py new file mode 100644 index 000000000..a02443d9f --- /dev/null +++ b/src/main.py @@ -0,0 +1,151 @@ +from cool_cmp.lexer.interface import ILexer +from cool_cmp.lexer import LexerPipeline +from cool_cmp.parser.interface import IParser +from cool_cmp.parser import ParserPipeline +from cool_cmp.semantic.interface import ISemantic +from cool_cmp.semantic import SemanticPipeline +from cool_cmp.cil.interface import ICil +from cool_cmp.cil import CilPipeline +from cool_cmp.mips.interface import IMips +from cool_cmp.mips import MipsPipeline +from cool_cmp.shared.token import Token +from cool_cmp.shared.ast import BaseAST +from cool_cmp.shared.ast.cool import ClassDeclarationNode, ProgramNode +from typing import List + +# Mock implementations + +class MockLexer(ILexer): + """ + Mock implementation of lexer + """ + program = "class \nCoolClass\n{}" + mock_tokens = [ + Token("class",0,0), + Token("CoolClass",1,0), + Token("{",2,0), + Token("}",2,1) + ] + + @property + def name(self)->str: + return "lexer" + + + def __call__(self, program_string:str) -> List[Token]: + print("Tokenizing program:\n", program_string) + return self.mock_tokens + +class MockParser(IParser): + """ + Mock implementation of parser + """ + + @property + def name(self)->str: + return "parser" + + def __call__(self, tokens:List[Token]) -> BaseAST: + print("Building AST from tokens",tokens) + class_token = MockLexer.mock_tokens[0] + return BaseAST(ProgramNode([ClassDeclarationNode(class_token.lex, [], None, class_token.line, class_token.column)])) + +class MockSemantic1(ISemantic): + """ + Mock Semantic interface 1 + """ + + @property + def name(self)->str: + return "semantic1" + + def __call__(self, ast:BaseAST) -> BaseAST: + print("Doing some crazy shit with ast, apply visitor, etc") + print("Collecting Types...") + print("Building Types...") + print("Type Checking...") + print("Setted mock1 property to true as a way of communicate between pipes") + ast.mock1 = True + return ast + +class MockSemantic2(ISemantic): + """ + Mock Semantic interface 2 + """ + + @property + def name(self)->str: + return "semantic2" + + def __call__(self, ast:BaseAST) -> BaseAST: + print("Doing another crazy shit with previously returned ast") + print("Verifying that ast has mock1 property", hasattr(ast, "mock1")) + print("Converting COOL AST into CIL AST...") + return ast + +class MockCil1(ICil): + """ + Mock Cil interface 1 + """ + + @property + def name(self)->str: + return "cil1" + + def __call__(self, ast:BaseAST) -> BaseAST: + print("Optimizing CIL AST...") + return ast + +class MockCil2(ICil): + """ + Mock Cil interface 2 + """ + + @property + def name(self)->str: + print("Converting CIL AST into MIPS AST...") + return "cil2" + + def __call__(self, ast:BaseAST) -> BaseAST: + return ast + +class MockMips1(IMips): + """ + Mock MIPS interface 1 + """ + + @property + def name(self)->str: + return "mips1" + + def __call__(self, ast:BaseAST) -> BaseAST: + print("Doing MIPS things...") + return ast + +class MockMips2(IMips): + """ + Mock MIPS interface 2 + """ + + @property + def name(self)->str: + return "mips2" + + def __call__(self, ast:BaseAST) -> BaseAST: + print("Generating MIPS code...") + return ast + + +# Testing pipeline + +pipe = LexerPipeline(MockLexer()) + +pipe = ParserPipeline(pipe, MockParser()) + +pipe = SemanticPipeline(pipe, MockSemantic1(), MockSemantic2()) + +pipe = CilPipeline(pipe, MockCil1(), MockCil2()) + +pipe = MipsPipeline(pipe, MockMips1(), MockMips2()) + +print(pipe(MockLexer.program)) \ No newline at end of file From c0084eec037227d7430ee88aea5b0d532b0e3e51 Mon Sep 17 00:00:00 2001 From: NazDia Date: Tue, 9 Mar 2021 21:24:01 +0100 Subject: [PATCH 003/143] Initial lexer commit --- src/cool_cmp/lexer/lexer.py | 158 +++++++++++++++++++++++++++++++++++ src/cool_cmp/shared/token.py | 20 +++-- src/main.py | 27 +++--- 3 files changed, 188 insertions(+), 17 deletions(-) create mode 100644 src/cool_cmp/lexer/lexer.py diff --git a/src/cool_cmp/lexer/lexer.py b/src/cool_cmp/lexer/lexer.py new file mode 100644 index 000000000..436fcce9e --- /dev/null +++ b/src/cool_cmp/lexer/lexer.py @@ -0,0 +1,158 @@ +""" +Lexer usando ply +""" + +from cool_cmp.lexer.interface import ILexer +from cool_cmp.shared.token import Token +import ply.lex as lex + +class Ply_Mix_Token(lex.LexToken, Token): + + def __init__(self, lex, typex, line, column): + self.set_lex(lex) + self.set_type(typex) + self.set_position(line, column) + + def set_lex(self, lex): + self.lex = lex + self.value = lex + + def set_type(self, typex): + self.type = typex + self.typex = typex + + def set_position(self, line, column): + self.lineno = line + self.line = line + self.column = column + self.lexpos = column + + def __str__(self): + return f"{self.lex}:{self.type} Line {self.line} Column{self.column}" + + def __repr__(self): + return str(self) + +class Lexer(ILexer): + + @staticmethod + def find_column(input, token): + line_start = input.rfind('\n', 0, token.lexpos) + 1 + return (token.lexpos - line_start) + 1 + + def __init__(self): + reserved = { + 'if' : 'IF', + 'then' : 'THEN', + 'fi' : 'FI', + 'else' : 'ELSE', + 'case' : 'CASE', + 'of' : 'OF', + 'esac' : 'ESAC', + 'class' : 'CLASS', + 'inherits' : 'INHERITS', + 'let' : 'LET', + 'in' : 'IN', + 'while' : 'WHILE', + 'loop' : 'LOOP', + 'pool' : 'POOL', + 'new' : 'NEW', + 'isvoid' : 'ISVOID', + 'not' : 'NOT', + 'true' : 'TRUE', + 'false' : 'FALSE' + } + + tokens = ( + 'ID', + 'NUMBER', + 'PLUS', + 'MINUS', + 'STAR', + 'DIV', + 'EQ', + 'LEQ', + 'LESS', + 'NEG', + 'OPAR', + 'CPAR', + 'OCUR', + 'CCUR', + 'ASSIGN', + 'SEMI', + 'COLON', + 'SIGNALER', + 'ARROBA', + 'STRING', + 'COMMENT', + 'DOT', + 'COMMA' + ) + tuple(reserved[x] for x in reserved.keys()) + + + t_STRING = r'"([^\n"\\]|(\\.)){0,1024}"' + + def t_NUMBER(t): + r'\d+' + try: + int(t.value) + except ValueError: + print("Integer value too large %d", t.value) + t.value = 'Invalid' + return t + + def t_ID(t): + r'[a-zA-Z_][a-zA-Z0-9_]*' + low = t.value.lower() + if (t.value[0] == 't' or t.value[0] == 'f') and (reserved.get(low) == 'TRUE' or reserved.get(low) == 'FALSE'): + t.type = reserved.get(low) + else: + t.type = reserved.get(t.value,'ID') + return t + + def t_COMMENT(t): + r'(--.*)|(\(\*(.|\n)*\*\))' + + t_PLUS = r'\+' + t_MINUS = r'-' + t_STAR = r'\*' + t_DIV = r'/' + t_OPAR = r'\(' + t_CPAR = r'\)' + t_OCUR = r'\{' + t_CCUR = r'\}' + t_EQ = r'=' + t_ASSIGN = r'<-' + t_LEQ = r'<=' + t_LESS = r'<' + t_NEG = r'~' + t_SEMI = r';' + t_COLON = r':' + t_SIGNALER = r'=>' + t_ARROBA = r'@' + t_DOT = r'\.' + t_COMMA = r',' + + t_ignore = " \t" + + def t_newline(t): + r'\n+' + t.lexer.lineno += t.value.count("\n") + + def t_error(t): + print("Illegal character '%s'" % t.value[0]) + t.lexer.skip(1) + + self.lexer = lex.lex() + + def __call__(self, program_string:str): + self.lexer.input(program_string) + result = [] + while True: + tok = self.lexer.token() + if not tok: + break + result.append(Ply_Mix_Token(tok.value, tok.type, tok.lineno, self.find_column(program_string, tok))) + + return result + diff --git a/src/cool_cmp/shared/token.py b/src/cool_cmp/shared/token.py index 9a2d8cdd5..fd10b677b 100644 --- a/src/cool_cmp/shared/token.py +++ b/src/cool_cmp/shared/token.py @@ -1,12 +1,22 @@ class Token: - def __init__(self, lex:str, line:int, column:int): - self.lex = lex - self.line = line - self.column = column + # def __init__(self, lex:str, typex:str, line:int, column:int): + # self.lex = lex + # self.typex = typex + # self.line = line + # self.column = column + + def set_lex(self, lex:str): + raise NotImplementedError() + + def set_type(self, typex:str): + raise NotImplementedError() + + def set_position(self, line, column): + raise NotImplementedError def __str__(self): - return f"{self.lex} Line:{self.line} Column:{self.column}" + return f"{self.lex}:{self.typex} Line:{self.line} Column:{self.column}" def __repr__(self): return str(self) \ No newline at end of file diff --git a/src/main.py b/src/main.py index a02443d9f..f2b34c644 100644 --- a/src/main.py +++ b/src/main.py @@ -11,6 +11,7 @@ from cool_cmp.shared.token import Token from cool_cmp.shared.ast import BaseAST from cool_cmp.shared.ast.cool import ClassDeclarationNode, ProgramNode +from cool_cmp.lexer.lexer import Lexer from typing import List # Mock implementations @@ -19,13 +20,13 @@ class MockLexer(ILexer): """ Mock implementation of lexer """ - program = "class \nCoolClass\n{}" - mock_tokens = [ - Token("class",0,0), - Token("CoolClass",1,0), - Token("{",2,0), - Token("}",2,1) - ] + program = "class \n\tCoolClass\n{}" + # mock_tokens = [ + # Token("class", "CLASS",0,0), + # Token("CoolClass","ID",1,0), + # Token("{","OCUR",2,0), + # Token("}","CCUR",2,1) + # ] @property def name(self)->str: @@ -138,14 +139,16 @@ def __call__(self, ast:BaseAST) -> BaseAST: # Testing pipeline -pipe = LexerPipeline(MockLexer()) +# pipe = LexerPipeline(MockLexer()) -pipe = ParserPipeline(pipe, MockParser()) +pipe = LexerPipeline(Lexer()) -pipe = SemanticPipeline(pipe, MockSemantic1(), MockSemantic2()) +# pipe = ParserPipeline(pipe, MockParser()) -pipe = CilPipeline(pipe, MockCil1(), MockCil2()) +# pipe = SemanticPipeline(pipe, MockSemantic1(), MockSemantic2()) -pipe = MipsPipeline(pipe, MockMips1(), MockMips2()) +# pipe = CilPipeline(pipe, MockCil1(), MockCil2()) + +# pipe = MipsPipeline(pipe, MockMips1(), MockMips2()) print(pipe(MockLexer.program)) \ No newline at end of file From 787f2a84c2ec013c9423331509d6b0fe5d5a204d Mon Sep 17 00:00:00 2001 From: luisoibarra Date: Tue, 9 Mar 2021 22:39:24 -0500 Subject: [PATCH 004/143] Infrastructure Updated Added error saving infrastructure Enhanced interface organization. ICoolService, ICoolToken Enforced coding styles: - Class names is Camellcase - All params and method should annotate its expected type - Add documentation to packages, classes, methods when needed --- src/cool_cmp/cil/__init__.py | 4 +- src/cool_cmp/cil/interface.py | 8 +- src/cool_cmp/lexer/__init__.py | 12 +- src/cool_cmp/lexer/errors.py | 12 +- src/cool_cmp/lexer/interface.py | 8 +- src/cool_cmp/lexer/lexer.py | 42 +++++-- src/cool_cmp/mips/__init__.py | 4 +- src/cool_cmp/mips/interface.py | 10 +- src/cool_cmp/parser/__init__.py | 4 +- src/cool_cmp/parser/interface.py | 9 +- src/cool_cmp/semantic/__init__.py | 5 +- src/cool_cmp/semantic/interface.py | 8 +- src/cool_cmp/shared/__init__.py | 60 +++++++++- src/cool_cmp/shared/errors.py | 30 ++++- src/cool_cmp/shared/pipeline/__init__.py | 37 ------- src/cool_cmp/shared/token.py | 24 ++-- src/main.py | 135 +++++++++++++++++++---- 17 files changed, 292 insertions(+), 120 deletions(-) diff --git a/src/cool_cmp/cil/__init__.py b/src/cool_cmp/cil/__init__.py index d3c124e42..afee2a42e 100644 --- a/src/cool_cmp/cil/__init__.py +++ b/src/cool_cmp/cil/__init__.py @@ -3,7 +3,7 @@ """ from typing import List -from cool_cmp.shared.pipeline import PipeResult, InterfacePipeline +from cool_cmp.shared import SymbolTable, InterfacePipeline from cool_cmp.semantic import SemanticPipeline from cool_cmp.cil.interface import ICil @@ -11,5 +11,5 @@ class CilPipeline(InterfacePipeline): def __init__(self, semantic_pipeline:SemanticPipeline, *cils:List[ICil]): super().__init__(semantic_pipeline, *cils) - def __call__(self, program:str)->PipeResult: + def __call__(self, program:str)->SymbolTable: return super().__call__(program) diff --git a/src/cool_cmp/cil/interface.py b/src/cool_cmp/cil/interface.py index b07905cd3..9ddf31490 100644 --- a/src/cool_cmp/cil/interface.py +++ b/src/cool_cmp/cil/interface.py @@ -1,14 +1,10 @@ from cool_cmp.shared.ast import BaseAST -from cool_cmp.shared.pipeline import IPipeable +from cool_cmp.shared import ICoolService -class ICil(IPipeable): +class ICil(ICoolService): """ CIL interface to implement """ - @property - def name(self)->str: - raise NotImplementedError() - def __call__(self, ast:BaseAST) -> BaseAST: raise NotImplementedError() diff --git a/src/cool_cmp/lexer/__init__.py b/src/cool_cmp/lexer/__init__.py index 96fa5bb7b..6122315c5 100644 --- a/src/cool_cmp/lexer/__init__.py +++ b/src/cool_cmp/lexer/__init__.py @@ -2,21 +2,25 @@ Cool lexical analysis package """ -from cool_cmp.shared.token import Token +from cool_cmp.shared.token import ICoolToken from cool_cmp.lexer.interface import ILexer -from cool_cmp.shared.pipeline import PipeResult, Pipeline, Pipe +from cool_cmp.shared.pipeline import Pipeline, Pipe +from cool_cmp.shared import SymbolTable class LexerPipeline(Pipeline): def __init__(self, lexer:ILexer): - result = PipeResult() + result = SymbolTable() def get_tokens(program:str): tokens = lexer(program) result.tokens = tokens + errors = lexer.get_errors() + for error in errors: + result.add_error(error) return result super().__init__(Pipe(get_tokens)) - def __call__(self, program:str)->PipeResult: + def __call__(self, program:str)->SymbolTable: return super().__call__(program) diff --git a/src/cool_cmp/lexer/errors.py b/src/cool_cmp/lexer/errors.py index fe68e4d09..041121bad 100644 --- a/src/cool_cmp/lexer/errors.py +++ b/src/cool_cmp/lexer/errors.py @@ -2,4 +2,14 @@ Lexer errors """ -from cool_cmp.shared.errors import CoolError \ No newline at end of file +from cool_cmp.shared.errors import CoolError +from cool_cmp.shared.token import ICoolToken + +class LexerCoolError(CoolError): + """ + Error class for lexical errors + """ + + def __init__(self, error_message:str, error_token:ICoolToken): + super().__init__(error_message) + self.token = error_token \ No newline at end of file diff --git a/src/cool_cmp/lexer/interface.py b/src/cool_cmp/lexer/interface.py index 8d81a0881..606caae8b 100644 --- a/src/cool_cmp/lexer/interface.py +++ b/src/cool_cmp/lexer/interface.py @@ -1,11 +1,11 @@ from typing import List -from cool_cmp.shared.token import Token -from cool_cmp.shared.pipeline import IPipeable +from cool_cmp.shared.token import ICoolToken +from cool_cmp.shared import ICoolService -class ILexer(IPipeable): +class ILexer(ICoolService): """ Lexer interface to implement """ - def __call__(self, program_string:str) -> List[Token]: + def __call__(self, program_string:str) -> List[ICoolToken]: raise NotImplementedError() diff --git a/src/cool_cmp/lexer/lexer.py b/src/cool_cmp/lexer/lexer.py index 436fcce9e..c55b33d96 100644 --- a/src/cool_cmp/lexer/lexer.py +++ b/src/cool_cmp/lexer/lexer.py @@ -1,39 +1,51 @@ """ Lexer usando ply """ +from typing import List, Tuple from cool_cmp.lexer.interface import ILexer -from cool_cmp.shared.token import Token +from cool_cmp.shared.token import ICoolToken +from cool_cmp.shared.errors import ErrorTracker +from cool_cmp.lexer.errors import LexerCoolError import ply.lex as lex -class Ply_Mix_Token(lex.LexToken, Token): +class PlyCoolToken(lex.LexToken, ICoolToken): - def __init__(self, lex, typex, line, column): + def __init__(self, lex:str, typex:str, line:int, column:int): self.set_lex(lex) self.set_type(typex) self.set_position(line, column) - def set_lex(self, lex): + def set_lex(self, lex:str): self.lex = lex self.value = lex - def set_type(self, typex): + def set_type(self, typex:str): self.type = typex self.typex = typex - def set_position(self, line, column): + def set_position(self, line:int, column:int): self.lineno = line self.line = line self.column = column self.lexpos = column + def get_lex(self)->str: + return self.lex + + def get_type(self)->str: + return self.typex + + def get_position(self)->Tuple[int,int]: + return (self.line, self.column) + def __str__(self): return f"{self.lex}:{self.type} Line {self.line} Column{self.column}" def __repr__(self): return str(self) -class Lexer(ILexer): +class PlyLexer(ILexer): @staticmethod def find_column(input, token): @@ -41,6 +53,8 @@ def find_column(input, token): return (token.lexpos - line_start) + 1 def __init__(self): + self.error_tracker = ErrorTracker() # Error tracker implementation + reserved = { 'if' : 'IF', 'then' : 'THEN', @@ -140,7 +154,8 @@ def t_newline(t): t.lexer.lineno += t.value.count("\n") def t_error(t): - print("Illegal character '%s'" % t.value[0]) + msg = f"Illegal character '{t.value[0]}'" + self.add_error(LexerCoolError(msg, PlyCoolToken(t.value, t.type, t.lineno, t.lexpos))) # TODO Set Token column t.lexer.skip(1) self.lexer = lex.lex() @@ -152,7 +167,14 @@ def __call__(self, program_string:str): tok = self.lexer.token() if not tok: break - result.append(Ply_Mix_Token(tok.value, tok.type, tok.lineno, self.find_column(program_string, tok))) - + result.append(PlyCoolToken(tok.value, tok.type, tok.lineno, self.find_column(program_string, tok))) + for tok in result: + print(tok) return result + def add_error(self, error:LexerCoolError): + self.error_tracker.add_error(error) + + def get_errors(self)->List[LexerCoolError]: + errors = self.error_tracker.get_errors() + return errors diff --git a/src/cool_cmp/mips/__init__.py b/src/cool_cmp/mips/__init__.py index 1d15067f2..df89fcb16 100644 --- a/src/cool_cmp/mips/__init__.py +++ b/src/cool_cmp/mips/__init__.py @@ -5,11 +5,11 @@ from typing import List from cool_cmp.mips.interface import IMips from cool_cmp.cil import CilPipeline -from cool_cmp.shared.pipeline import PipeResult, InterfacePipeline +from cool_cmp.shared import SymbolTable, InterfacePipeline class MipsPipeline(InterfacePipeline): def __init__(self, cil_pipeline:CilPipeline, *mipses:List[IMips]): super().__init__(cil_pipeline, *mipses) - def __call__(self, program:str)->PipeResult: + def __call__(self, program:str)->SymbolTable: return super().__call__(program) \ No newline at end of file diff --git a/src/cool_cmp/mips/interface.py b/src/cool_cmp/mips/interface.py index 55e2a053f..0045dedbd 100644 --- a/src/cool_cmp/mips/interface.py +++ b/src/cool_cmp/mips/interface.py @@ -1,16 +1,12 @@ from cool_cmp.shared.ast import BaseAST -from cool_cmp.shared.pipeline import IPipeable +from cool_cmp.shared import ICoolService -class IMips(IPipeable): +class IMips(ICoolService): """ MIPS interface to implement """ - - @property - def name(self)->str: - raise NotImplementedError() - + def __call__(self, ast:BaseAST) -> BaseAST: raise NotImplementedError() diff --git a/src/cool_cmp/parser/__init__.py b/src/cool_cmp/parser/__init__.py index 19d052b20..fa6b4056f 100644 --- a/src/cool_cmp/parser/__init__.py +++ b/src/cool_cmp/parser/__init__.py @@ -4,12 +4,12 @@ from cool_cmp.parser.interface import IParser from cool_cmp.lexer import LexerPipeline -from cool_cmp.shared.pipeline import PipeResult, InterfacePipeline +from cool_cmp.shared import SymbolTable, InterfacePipeline class ParserPipeline(InterfacePipeline): def __init__(self, lexer_pipeline:LexerPipeline, parser:IParser): super().__init__(lexer_pipeline, *[parser,]) - def __call__(self, program:str)->PipeResult: + def __call__(self, program:str)->SymbolTable: return super().__call__(program) diff --git a/src/cool_cmp/parser/interface.py b/src/cool_cmp/parser/interface.py index b46883774..2617a2031 100644 --- a/src/cool_cmp/parser/interface.py +++ b/src/cool_cmp/parser/interface.py @@ -1,11 +1,12 @@ from typing import List -from cool_cmp.shared.token import Token +from cool_cmp.shared.token import ICoolToken from cool_cmp.shared.ast import BaseAST -from cool_cmp.shared.pipeline import IPipeable +from cool_cmp.shared import ICoolService -class IParser(IPipeable): +class IParser(ICoolService): """ Parser interface to implement """ - def __call__(self, tokens:List[Token]) -> BaseAST: + + def __call__(self, tokens:List[ICoolToken]) -> BaseAST: raise NotImplementedError() diff --git a/src/cool_cmp/semantic/__init__.py b/src/cool_cmp/semantic/__init__.py index ecc7a2bcc..513c8dfe8 100644 --- a/src/cool_cmp/semantic/__init__.py +++ b/src/cool_cmp/semantic/__init__.py @@ -5,13 +5,12 @@ from typing import List from cool_cmp.semantic.interface import ISemantic from cool_cmp.parser import ParserPipeline -from cool_cmp.shared.pipeline import PipeResult, InterfacePipeline - +from cool_cmp.shared import SymbolTable, InterfacePipeline class SemanticPipeline(InterfacePipeline): def __init__(self, parser_pipeline:ParserPipeline, *semantics:List[ISemantic]): super().__init__(parser_pipeline, *semantics) - def __call__(self, program:str)->PipeResult: + def __call__(self, program:str)->SymbolTable: return super().__call__(program) diff --git a/src/cool_cmp/semantic/interface.py b/src/cool_cmp/semantic/interface.py index ea0a171e6..b59649c96 100644 --- a/src/cool_cmp/semantic/interface.py +++ b/src/cool_cmp/semantic/interface.py @@ -1,15 +1,11 @@ from cool_cmp.shared.ast import BaseAST -from cool_cmp.shared.pipeline import IPipeable +from cool_cmp.shared import ICoolService -class ISemantic(IPipeable): +class ISemantic(ICoolService): """ Semantic interface to implement """ - @property - def name(self)->str: - raise NotImplementedError() - def __call__(self, ast:BaseAST) -> BaseAST: raise NotImplementedError() diff --git a/src/cool_cmp/shared/__init__.py b/src/cool_cmp/shared/__init__.py index cb590d145..0ed021144 100644 --- a/src/cool_cmp/shared/__init__.py +++ b/src/cool_cmp/shared/__init__.py @@ -1,3 +1,61 @@ """ Shared code between packages -""" \ No newline at end of file +""" + +from cool_cmp.shared.pipeline import IPipeable, Pipe, Pipeline +from cool_cmp.shared.errors import ErrorTracker, IErrorTraceable, CoolError +from typing import List + +class ICoolService(IErrorTraceable, IPipeable): + """ + Interface that should implement all parts of the cool pipeline + """ + pass + +class SymbolTable(IErrorTraceable): + """ + Class that should return all Cool pipelines + It holds all the shared information between stages + Defines a last property that returns the last object setted as an atribute + """ + + def __init__(self): + self.last = None + self.__errors = ErrorTracker() + + def __setattr__(self, name:str, value): + if name != "last": + self.last = value + super().__setattr__(name, value) + + def add_error(self, error:CoolError): + self.__errors.add_error(error) + + def get_errors(self)->List[CoolError]: + return self.__errors.get_errors() + + def __str__(self): + result = "SymbolTable:\n" + for attr in self.__dict__.keys(): + result += f" {attr}\n" + return result + + +class InterfacePipeline(Pipeline): + + def __init__(self, pipeline:'InterfacePipeline', *interfaces:List[IPipeable]): + + def make_pipe(result:SymbolTable, interface:ICoolService): + returned = interface(result.last) + for error in interface.get_errors(): + result.add_error(error) + result.__setattr__(interface.name, returned) + return result + + pipes = [] + for interface in interfaces: + pipes.append(Pipe(lambda x, y=interface: make_pipe(x,y))) + if pipeline: + super().__init__(pipeline, *pipes) + else: + super().__init__(*pipes) \ No newline at end of file diff --git a/src/cool_cmp/shared/errors.py b/src/cool_cmp/shared/errors.py index b3a8e3871..e26853663 100644 --- a/src/cool_cmp/shared/errors.py +++ b/src/cool_cmp/shared/errors.py @@ -1,3 +1,4 @@ +from typing import List class CoolError: """ Base Cool Error @@ -9,10 +10,33 @@ def __init__(self, msg:str): def print_error(self): print(self.error) -class ErrorTracker: + def __str__(self): + return f"Error: {self.error}" + + def __repr__(self): + return str(self) + +class IErrorTraceable: + """ + Interface for error tracking system + """ + + def add_error(self, error:CoolError): + raise NotImplementedError() + + def get_errors(self)->List[CoolError]: + raise NotImplementedError() + +class ErrorTracker(IErrorTraceable): + """ + Basic Error tracking system + """ def __init__(self): - self.errors = [] + self.__errors = [] def add_error(self, error:CoolError): - self.errors.append(error) \ No newline at end of file + self.__errors.append(error) + + def get_errors(self)->List[CoolError]: + return self.__errors \ No newline at end of file diff --git a/src/cool_cmp/shared/pipeline/__init__.py b/src/cool_cmp/shared/pipeline/__init__.py index 4c385b2a3..e292662eb 100644 --- a/src/cool_cmp/shared/pipeline/__init__.py +++ b/src/cool_cmp/shared/pipeline/__init__.py @@ -13,40 +13,3 @@ def name(self)->str: def __call__(self, arg): raise NotImplementedError() - -class PipeResult: - """ - Class that should return all Cool pipelines - Defines a last property that returns the last object setted as an atribute - """ - - def __init__(self): - self.last = None - - def __setattr__(self, name:str, value): - if name != "last": - self.last = value - super().__setattr__(name, value) - - def __str__(self): - result = "PipeResult:\n" - for attr in self.__dict__.keys(): - result += f" {attr}\n" - return result - -class InterfacePipeline(Pipeline): - - def __init__(self, pipeline:'InterfacePipeline', *interfaces:List[IPipeable]): - - def make_pipe(result:PipeResult, interface:IPipeable): - returned = interface(result.last) - result.__setattr__(interface.name, returned) - return result - - pipes = [] - for interface in interfaces: - pipes.append(Pipe(lambda x, y=interface: make_pipe(x,y))) - if pipeline: - super().__init__(pipeline, *pipes) - else: - super().__init__(*pipes) \ No newline at end of file diff --git a/src/cool_cmp/shared/token.py b/src/cool_cmp/shared/token.py index fd10b677b..879df7de9 100644 --- a/src/cool_cmp/shared/token.py +++ b/src/cool_cmp/shared/token.py @@ -1,22 +1,30 @@ -class Token: +from typing import Tuple - # def __init__(self, lex:str, typex:str, line:int, column:int): - # self.lex = lex - # self.typex = typex - # self.line = line - # self.column = column +class ICoolToken: + """ + Cool Token Interface + """ def set_lex(self, lex:str): raise NotImplementedError() + def get_lex(self)->str: + raise NotImplementedError() + def set_type(self, typex:str): raise NotImplementedError() - def set_position(self, line, column): + def get_type(self)->str: + raise NotImplementedError() + + def set_position(self, line:int, column:int): + raise NotImplementedError + + def get_position(self)->Tuple[int,int]: raise NotImplementedError def __str__(self): - return f"{self.lex}:{self.typex} Line:{self.line} Column:{self.column}" + return f"{self.get_lex()}:{self.get_type()} Line:{self.get_position()[0]} Column:{self.get_position()[1]}" def __repr__(self): return str(self) \ No newline at end of file diff --git a/src/main.py b/src/main.py index f2b34c644..227a92dd8 100644 --- a/src/main.py +++ b/src/main.py @@ -8,11 +8,12 @@ from cool_cmp.cil import CilPipeline from cool_cmp.mips.interface import IMips from cool_cmp.mips import MipsPipeline -from cool_cmp.shared.token import Token +from cool_cmp.shared.token import ICoolToken from cool_cmp.shared.ast import BaseAST from cool_cmp.shared.ast.cool import ClassDeclarationNode, ProgramNode -from cool_cmp.lexer.lexer import Lexer -from typing import List +from cool_cmp.lexer.lexer import PlyLexer +from cool_cmp.shared.errors import CoolError +from typing import List, Tuple # Mock implementations @@ -20,20 +21,56 @@ class MockLexer(ILexer): """ Mock implementation of lexer """ - program = "class \n\tCoolClass\n{}" - # mock_tokens = [ - # Token("class", "CLASS",0,0), - # Token("CoolClass","ID",1,0), - # Token("{","OCUR",2,0), - # Token("}","CCUR",2,1) - # ] + program = "class ` \n\tCoolClass\n{}" + class MockToken(ICoolToken): + def set_lex(self, lex:str): + self.lex = lex + + def set_type(self, typex:str): + self.typex = typex + + def set_position(self, line:int, column:int): + self.line = line + self.column = column + + def get_lex(self)->str: + return self.lex + + def get_type(self)->str: + return self.typex + + def get_position(self)->Tuple[int,int]: + return (self.line, self.column) + + @property + def mock_tokens(self)->List[ICoolToken]: + info = [ + ("class", "CLASS", 0, 0), + ("CoolClass", "ID", 1, 0), + ("{", "OCUR", 2, 0), + ("}", "CCUR", 2, 1), + ] + tokens = [] + for lex, typex, line, col in info: + token = MockLexer.MockToken() + token.set_lex(lex); token.set_type(typex); token.set_position(line, col) + tokens.append(token) + return tokens + @property def name(self)->str: return "lexer" + + errors = [] + def add_error(self, error:CoolError): + self.errors.append(error) + + def get_errors(self)->List[CoolError]: + return self.errors - def __call__(self, program_string:str) -> List[Token]: + def __call__(self, program_string:str) -> List[ICoolToken]: print("Tokenizing program:\n", program_string) return self.mock_tokens @@ -46,11 +83,20 @@ class MockParser(IParser): def name(self)->str: return "parser" - def __call__(self, tokens:List[Token]) -> BaseAST: + def __call__(self, tokens:List[ICoolToken]) -> BaseAST: print("Building AST from tokens",tokens) - class_token = MockLexer.mock_tokens[0] + class_token = MockLexer().mock_tokens + class_token = class_token[0] return BaseAST(ProgramNode([ClassDeclarationNode(class_token.lex, [], None, class_token.line, class_token.column)])) + errors = [] + + def add_error(self, error:CoolError): + self.errors.append(error) + + def get_errors(self)->List[CoolError]: + return self.errors + class MockSemantic1(ISemantic): """ Mock Semantic interface 1 @@ -68,6 +114,14 @@ def __call__(self, ast:BaseAST) -> BaseAST: print("Setted mock1 property to true as a way of communicate between pipes") ast.mock1 = True return ast + + errors = [] + + def add_error(self, error:CoolError): + self.errors.append(error) + + def get_errors(self)->List[CoolError]: + return self.errors class MockSemantic2(ISemantic): """ @@ -83,6 +137,14 @@ def __call__(self, ast:BaseAST) -> BaseAST: print("Verifying that ast has mock1 property", hasattr(ast, "mock1")) print("Converting COOL AST into CIL AST...") return ast + + errors = [] + + def add_error(self, error:CoolError): + self.errors.append(error) + + def get_errors(self)->List[CoolError]: + return self.errors class MockCil1(ICil): """ @@ -97,6 +159,14 @@ def __call__(self, ast:BaseAST) -> BaseAST: print("Optimizing CIL AST...") return ast + errors = [] + + def add_error(self, error:CoolError): + self.errors.append(error) + + def get_errors(self)->List[CoolError]: + return self.errors + class MockCil2(ICil): """ Mock Cil interface 2 @@ -110,6 +180,14 @@ def name(self)->str: def __call__(self, ast:BaseAST) -> BaseAST: return ast + errors = [] + + def add_error(self, error:CoolError): + self.errors.append(error) + + def get_errors(self)->List[CoolError]: + return self.errors + class MockMips1(IMips): """ Mock MIPS interface 1 @@ -123,6 +201,14 @@ def __call__(self, ast:BaseAST) -> BaseAST: print("Doing MIPS things...") return ast + errors = [] + + def add_error(self, error:CoolError): + self.errors.append(error) + + def get_errors(self)->List[CoolError]: + return self.errors + class MockMips2(IMips): """ Mock MIPS interface 2 @@ -136,19 +222,28 @@ def __call__(self, ast:BaseAST) -> BaseAST: print("Generating MIPS code...") return ast + errors = [] + + def add_error(self, error:CoolError): + self.errors.append(error) + + def get_errors(self)->List[CoolError]: + return self.errors # Testing pipeline -# pipe = LexerPipeline(MockLexer()) +pipe = LexerPipeline(MockLexer()) + +# pipe = LexerPipeline(PlyLexer()) -pipe = LexerPipeline(Lexer()) +pipe = ParserPipeline(pipe, MockParser()) -# pipe = ParserPipeline(pipe, MockParser()) +pipe = SemanticPipeline(pipe, MockSemantic1(), MockSemantic2()) -# pipe = SemanticPipeline(pipe, MockSemantic1(), MockSemantic2()) +pipe = CilPipeline(pipe, MockCil1(), MockCil2()) -# pipe = CilPipeline(pipe, MockCil1(), MockCil2()) +pipe = MipsPipeline(pipe, MockMips1(), MockMips2()) -# pipe = MipsPipeline(pipe, MockMips1(), MockMips2()) +result = pipe(MockLexer.program) -print(pipe(MockLexer.program)) \ No newline at end of file +print(result.get_errors()) \ No newline at end of file From f6f54faf41dde19b34ea7d7c9b0777ab77c7d1b3 Mon Sep 17 00:00:00 2001 From: Luiso Date: Sat, 13 Mar 2021 19:21:25 -0500 Subject: [PATCH 005/143] Available Lexer Testing To test Lexer execute: $ make test TAG=lexer --- .github/workflows/tests.yml | 166 +-- .gitignore | 820 +++++------ LICENSE | 42 +- Readme.md | 344 ++--- doc/Readme.md | 66 +- doc/team.yml | 20 +- requirements.txt | 4 +- src/Readme.md | 156 +- src/cas.cl | 2 + src/cas.mips | 1 + src/cool_cmp/__init__.py | 4 +- src/cool_cmp/cil/__init__.py | 30 +- src/cool_cmp/cil/errors.py | 8 +- src/cool_cmp/cil/interface.py | 20 +- src/cool_cmp/cil/visitors/__init__.py | 4 +- src/cool_cmp/lexer/__init__.py | 52 +- src/cool_cmp/lexer/errors.py | 37 +- src/cool_cmp/lexer/interface.py | 22 +- src/cool_cmp/lexer/lexer.py | 359 +++-- src/cool_cmp/mips/__init__.py | 28 +- src/cool_cmp/mips/errors.py | 8 +- src/cool_cmp/mips/interface.py | 24 +- src/cool_cmp/mips/visitors/__init__.py | 4 +- src/cool_cmp/parser/__init__.py | 30 +- src/cool_cmp/parser/errors.py | 28 +- src/cool_cmp/parser/interface.py | 24 +- src/cool_cmp/semantic/__init__.py | 32 +- src/cool_cmp/semantic/errors.py | 8 +- src/cool_cmp/semantic/interface.py | 22 +- src/cool_cmp/semantic/visitors/__init__.py | 4 +- src/cool_cmp/shared/__init__.py | 120 +- src/cool_cmp/shared/ast/__init__.py | 44 +- src/cool_cmp/shared/ast/cil.py | 8 +- src/cool_cmp/shared/ast/cool.py | 528 +++---- src/cool_cmp/shared/ast/mips.py | 8 +- src/cool_cmp/shared/errors.py | 86 +- src/cool_cmp/shared/pipeline/__init__.py | 30 +- src/cool_cmp/shared/pipeline/pipes.py | 78 +- src/cool_cmp/shared/token.py | 58 +- src/coolc.sh | 8 +- src/main.py | 513 +++---- src/makefile | 24 +- tests/codegen/arith.cl | 860 +++++------ tests/codegen/arith_input.txt | 26 +- tests/codegen/arith_output.txt | 316 ++-- tests/codegen/atoi_output.txt | 2 +- tests/codegen/book_list.cl | 264 ++-- tests/codegen/book_list_output.txt | 14 +- tests/codegen/cells.cl | 194 +-- tests/codegen/cells_output.txt | 42 +- tests/codegen/complex.cl | 104 +- tests/codegen/complex_output.txt | 2 +- tests/codegen/fib.cl | 58 +- tests/codegen/fib_input.txt | 2 +- tests/codegen/fib_output.txt | 4 +- tests/codegen/graph.cl | 762 +++++----- tests/codegen/graph_input.txt | 10 +- tests/codegen/graph_output.txt | 14 +- tests/codegen/hairyscary.cl | 134 +- tests/codegen/hello_world.cl | 10 +- tests/codegen/hello_world_output.txt | 2 +- tests/codegen/io.cl | 206 +-- tests/codegen/io_output.txt | 10 +- tests/codegen/life.cl | 872 +++++------ tests/codegen/life_input.txt | 130 +- tests/codegen/life_output.txt | 1554 ++++++++++---------- tests/codegen/list_output.txt | 10 +- tests/codegen/new_complex.cl | 158 +- tests/codegen/new_complex_output.txt | 4 +- tests/codegen/palindrome.cl | 50 +- tests/codegen/palindrome_input.txt | 2 +- tests/codegen/palindrome_output.txt | 4 +- tests/codegen/primes_output.txt | 192 +-- tests/codegen/print-cool_output.txt | 2 +- tests/codegen/sort-list_input.txt | 2 +- tests/codegen/sort-list_output.txt | 20 +- tests/codegen_test.py | 32 +- tests/conftest.py | 10 +- tests/lexer/comment1.cl | 108 +- tests/lexer/comment1.mips | 1 + tests/lexer/comment1_error.txt | 2 +- tests/lexer/iis1.cl | 220 +-- tests/lexer/iis1.mips | 1 + tests/lexer/iis1_error.txt | 2 +- tests/lexer/iis2.cl | 238 +-- tests/lexer/iis2.mips | 1 + tests/lexer/iis2_error.txt | 2 +- tests/lexer/iis3.cl | 240 +-- tests/lexer/iis3.mips | 1 + tests/lexer/iis3_error.txt | 2 +- tests/lexer/iis4.cl | 238 +-- tests/lexer/iis4.mips | 1 + tests/lexer/iis4_error.txt | 2 +- tests/lexer/iis5.cl | 242 +-- tests/lexer/iis5.mips | 1 + tests/lexer/iis5_error.txt | 4 +- tests/lexer/iis6.cl | 248 ++-- tests/lexer/iis6.mips | 1 + tests/lexer/iis6_error.txt | 2 +- tests/lexer/mixed1.cl | 26 +- tests/lexer/mixed1.mips | 1 + tests/lexer/mixed1_error.txt | 2 +- tests/lexer/mixed2.cl | 38 +- tests/lexer/mixed2.mips | 1 + tests/lexer/mixed2_error.txt | 6 +- tests/lexer/string1.cl | 10 +- tests/lexer/string1.mips | 1 + tests/lexer/string1_error.txt | 2 +- tests/lexer/string2.cl | 36 +- tests/lexer/string2.mips | 1 + tests/lexer/string3.mips | 1 + tests/lexer/string4.cl | 74 +- tests/lexer/string4.mips | 1 + tests/lexer/string4_error.txt | 4 +- tests/lexer_test.py | 24 +- tests/parser/assignment1.cl | 72 +- tests/parser/assignment2.cl | 72 +- tests/parser/assignment3.cl | 72 +- tests/parser/attribute1.cl | 66 +- tests/parser/attribute2.cl | 66 +- tests/parser/attribute3.cl | 66 +- tests/parser/block1.cl | 172 +-- tests/parser/block2.cl | 172 +-- tests/parser/block3.cl | 172 +-- tests/parser/block4.cl | 174 +-- tests/parser/case1.cl | 180 +-- tests/parser/case2.cl | 184 +-- tests/parser/case3.cl | 184 +-- tests/parser/case4.cl | 184 +-- tests/parser/case5.cl | 184 +-- tests/parser/case6.cl | 184 +-- tests/parser/class1.cl | 40 +- tests/parser/class2.cl | 40 +- tests/parser/class3.cl | 68 +- tests/parser/class4.cl | 72 +- tests/parser/class5.cl | 68 +- tests/parser/class6.cl | 68 +- tests/parser/conditional1.cl | 136 +- tests/parser/conditional2.cl | 136 +- tests/parser/conditional3.cl | 136 +- tests/parser/conditional4.cl | 144 +- tests/parser/conditional5.cl | 144 +- tests/parser/conditional6.cl | 144 +- tests/parser/dispatch1.cl | 88 +- tests/parser/dispatch2.cl | 88 +- tests/parser/dispatch3.cl | 88 +- tests/parser/dispatch4.cl | 104 +- tests/parser/dispatch5.cl | 104 +- tests/parser/dispatch6.cl | 112 +- tests/parser/dispatch7.cl | 112 +- tests/parser/dispatch8.cl | 112 +- tests/parser/dispatch9.cl | 120 +- tests/parser/let1.cl | 168 +-- tests/parser/let2.cl | 168 +-- tests/parser/let3.cl | 168 +-- tests/parser/let4.cl | 168 +-- tests/parser/let5.cl | 168 +-- tests/parser/let6.cl | 146 +- tests/parser/let7.cl | 168 +-- tests/parser/loop1.cl | 154 +- tests/parser/loop2.cl | 154 +- tests/parser/loop3.cl | 154 +- tests/parser/loop4.cl | 154 +- tests/parser/method1.cl | 66 +- tests/parser/method2.cl | 66 +- tests/parser/method3.cl | 66 +- tests/parser/method4.cl | 66 +- tests/parser/method5.cl | 66 +- tests/parser/method6.cl | 64 +- tests/parser/mixed1.cl | 198 +-- tests/parser/mixed2.cl | 28 +- tests/parser/mixed3.cl | 80 +- tests/parser/mixed4.cl | 42 +- tests/parser/mixed5.cl | 40 +- tests/parser/mixed6.cl | 10 +- tests/parser/operation1.cl | 200 +-- tests/parser/operation2.cl | 200 +-- tests/parser/operation3.cl | 200 +-- tests/parser/operation4.cl | 200 +-- tests/parser/program2.cl | 40 +- tests/parser/program3.cl | 48 +- tests/parser_test.py | 24 +- tests/semantic/arithmetic1.cl | 20 +- tests/semantic/arithmetic10.cl | 28 +- tests/semantic/arithmetic11.cl | 26 +- tests/semantic/arithmetic12.cl | 26 +- tests/semantic/arithmetic1_error.txt | 2 +- tests/semantic/arithmetic2.cl | 20 +- tests/semantic/arithmetic2_error.txt | 2 +- tests/semantic/arithmetic3.cl | 20 +- tests/semantic/arithmetic3_error.txt | 2 +- tests/semantic/arithmetic4.cl | 20 +- tests/semantic/arithmetic5.cl | 20 +- tests/semantic/arithmetic5_error.txt | 2 +- tests/semantic/arithmetic6.cl | 22 +- tests/semantic/arithmetic6_error.txt | 2 +- tests/semantic/arithmetic7.cl | 24 +- tests/semantic/arithmetic7_error.txt | 2 +- tests/semantic/arithmetic8.cl | 26 +- tests/semantic/arithmetic8_error.txt | 2 +- tests/semantic/arithmetic9.cl | 28 +- tests/semantic/assignment1.cl | 14 +- tests/semantic/assignment1_error.txt | 2 +- tests/semantic/assignment2.cl | 26 +- tests/semantic/assignment2_error.txt | 2 +- tests/semantic/assignment3.cl | 28 +- tests/semantic/attributes1.cl | 24 +- tests/semantic/attributes1_error.txt | 2 +- tests/semantic/attributes2.cl | 24 +- tests/semantic/attributes2_error.txt | 2 +- tests/semantic/attributes3.cl | 48 +- tests/semantic/attributes3_error.txt | 2 +- tests/semantic/attributes4.cl | 76 +- tests/semantic/basics1.cl | 18 +- tests/semantic/basics1_error.txt | 2 +- tests/semantic/basics2.cl | 18 +- tests/semantic/basics2_error.txt | 2 +- tests/semantic/basics3.cl | 16 +- tests/semantic/basics3_error.txt | 2 +- tests/semantic/basics4.cl | 16 +- tests/semantic/basics4_error.txt | 2 +- tests/semantic/basics5.cl | 16 +- tests/semantic/basics5_error.txt | 2 +- tests/semantic/basics6.cl | 16 +- tests/semantic/basics6_error.txt | 2 +- tests/semantic/basics7.cl | 16 +- tests/semantic/basics7_error.txt | 2 +- tests/semantic/basics8.cl | 16 +- tests/semantic/basics8_error.txt | 2 +- tests/semantic/blocks1.cl | 60 +- tests/semantic/blocks1_error.txt | 2 +- tests/semantic/case1.cl | 46 +- tests/semantic/case1_error.txt | 2 +- tests/semantic/case2.cl | 44 +- tests/semantic/case3.cl | 44 +- tests/semantic/class1.cl | 16 +- tests/semantic/class1_error.txt | 4 +- tests/semantic/conditionals1.cl | 26 +- tests/semantic/conditionals1_error.txt | 2 +- tests/semantic/conditionals2.cl | 48 +- tests/semantic/conditionals2_error.txt | 4 +- tests/semantic/dispatch1.cl | 64 +- tests/semantic/dispatch1_error.txt | 2 +- tests/semantic/dispatch2.cl | 66 +- tests/semantic/dispatch2_error.txt | 2 +- tests/semantic/dispatch3.cl | 70 +- tests/semantic/dispatch3_error.txt | 2 +- tests/semantic/dispatch4.cl | 70 +- tests/semantic/dispatch5.cl | 60 +- tests/semantic/dispatch5_error.txt | 2 +- tests/semantic/dispatch6.cl | 64 +- tests/semantic/dispatch6_error.txt | 2 +- tests/semantic/eq1.cl | 32 +- tests/semantic/eq1_error.txt | 2 +- tests/semantic/eq2.cl | 34 +- tests/semantic/eq2_error.txt | 2 +- tests/semantic/eq3.cl | 34 +- tests/semantic/eq3_error.txt | 2 +- tests/semantic/eq4.cl | 34 +- tests/semantic/eq4_error.txt | 2 +- tests/semantic/isvoid1.cl | 50 +- tests/semantic/isvoid1_error.txt | 2 +- tests/semantic/let1.cl | 28 +- tests/semantic/let1_error.txt | 2 +- tests/semantic/let2.cl | 28 +- tests/semantic/let2_error.txt | 2 +- tests/semantic/let3.cl | 28 +- tests/semantic/loops1.cl | 14 +- tests/semantic/loops2.cl | 18 +- tests/semantic/loops2_error.txt | 2 +- tests/semantic/methods1.cl | 22 +- tests/semantic/methods1_error.txt | 2 +- tests/semantic/methods2.cl | 22 +- tests/semantic/methods2_error.txt | 2 +- tests/semantic/methods3.cl | 26 +- tests/semantic/methods3_error.txt | 2 +- tests/semantic/methods4.cl | 36 +- tests/semantic/methods4_error.txt | 2 +- tests/semantic/methods5.cl | 40 +- tests/semantic/methods5_error.txt | 2 +- tests/semantic/methods6.cl | 52 +- tests/semantic/methods6_error.txt | 2 +- tests/semantic/methods7.cl | 22 +- tests/semantic/methods8.cl | 22 +- tests/semantic/new1.cl | 60 +- tests/semantic/self1.cl | 22 +- tests/semantic/self1_error.txt | 2 +- tests/semantic/self2.cl | 20 +- tests/semantic/self2_error.txt | 2 +- tests/semantic/self3.cl | 20 +- tests/semantic/self3_error.txt | 2 +- tests/semantic/self4.cl | 18 +- tests/semantic/self4_error.txt | 2 +- tests/semantic_test.py | 26 +- tests/utils/utils.py | 182 +-- 295 files changed, 10960 insertions(+), 10899 deletions(-) create mode 100644 src/cas.cl create mode 100644 src/cas.mips create mode 100644 tests/lexer/comment1.mips create mode 100644 tests/lexer/iis1.mips create mode 100644 tests/lexer/iis2.mips create mode 100644 tests/lexer/iis3.mips create mode 100644 tests/lexer/iis4.mips create mode 100644 tests/lexer/iis5.mips create mode 100644 tests/lexer/iis6.mips create mode 100644 tests/lexer/mixed1.mips create mode 100644 tests/lexer/mixed2.mips create mode 100644 tests/lexer/string1.mips create mode 100644 tests/lexer/string2.mips create mode 100644 tests/lexer/string3.mips create mode 100644 tests/lexer/string4.mips diff --git a/.github/workflows/tests.yml b/.github/workflows/tests.yml index 62ff3680b..9fb3b7bf2 100644 --- a/.github/workflows/tests.yml +++ b/.github/workflows/tests.yml @@ -1,83 +1,83 @@ -name: Tests - -on: [push, pull_request] - -jobs: - lexer: - runs-on: ubuntu-latest - - steps: - - uses: actions/checkout@v2 - - - name: Setup Python - uses: actions/setup-python@v2 - - - name: Install requirements - run: pip install -r requirements.txt - - - name: Run tests - run: | - cd src - make clean - make - make test TAG=lexer - - parser: - runs-on: ubuntu-latest - - steps: - - uses: actions/checkout@v2 - - - name: Setup Python - uses: actions/setup-python@v2 - - - name: Install requirements - run: pip install -r requirements.txt - - - name: Run tests - run: | - cd src - make clean - make - make test TAG=parser - - semantic: - runs-on: ubuntu-latest - - steps: - - uses: actions/checkout@v2 - - - name: Setup Python - uses: actions/setup-python@v2 - - - name: Install requirements - run: pip install -r requirements.txt - - - name: Run tests - run: | - cd src - make clean - make - make test TAG=semantic - - codegen: - runs-on: ubuntu-latest - - steps: - - uses: actions/checkout@v2 - - - name: Setup Python - uses: actions/setup-python@v2 - - - name: Install requirements - run: pip install -r requirements.txt - - - name: Install spim - run: sudo apt-get install spim - - - name: Run tests - run: | - cd src - make clean - make - make test TAG=codegen +name: Tests + +on: [push, pull_request] + +jobs: + lexer: + runs-on: ubuntu-latest + + steps: + - uses: actions/checkout@v2 + + - name: Setup Python + uses: actions/setup-python@v2 + + - name: Install requirements + run: pip install -r requirements.txt + + - name: Run tests + run: | + cd src + make clean + make + make test TAG=lexer + + parser: + runs-on: ubuntu-latest + + steps: + - uses: actions/checkout@v2 + + - name: Setup Python + uses: actions/setup-python@v2 + + - name: Install requirements + run: pip install -r requirements.txt + + - name: Run tests + run: | + cd src + make clean + make + make test TAG=parser + + semantic: + runs-on: ubuntu-latest + + steps: + - uses: actions/checkout@v2 + + - name: Setup Python + uses: actions/setup-python@v2 + + - name: Install requirements + run: pip install -r requirements.txt + + - name: Run tests + run: | + cd src + make clean + make + make test TAG=semantic + + codegen: + runs-on: ubuntu-latest + + steps: + - uses: actions/checkout@v2 + + - name: Setup Python + uses: actions/setup-python@v2 + + - name: Install requirements + run: pip install -r requirements.txt + + - name: Install spim + run: sudo apt-get install spim + + - name: Run tests + run: | + cd src + make clean + make + make test TAG=codegen diff --git a/.gitignore b/.gitignore index 4acafde18..0b07f7b23 100644 --- a/.gitignore +++ b/.gitignore @@ -1,410 +1,410 @@ -# File created using '.gitignore Generator' for Visual Studio Code: https://bit.ly/vscode-gig - -# Created by https://www.gitignore.io/api/visualstudiocode,linux,latex,python -# Edit at https://www.gitignore.io/?templates=visualstudiocode,linux,latex,python - -### LaTeX ### -## Core latex/pdflatex auxiliary files: -*.aux -*.lof -*.log -*.lot -*.fls -*.out -*.toc -*.fmt -*.fot -*.cb -*.cb2 -.*.lb - -## Intermediate documents: -*.dvi -*.xdv -*-converted-to.* -# these rules might exclude image files for figures etc. -# *.ps -# *.eps -# *.pdf - -## Generated if empty string is given at "Please type another file name for output:" -.pdf - -## Bibliography auxiliary files (bibtex/biblatex/biber): -*.bbl -*.bcf -*.blg -*-blx.aux -*-blx.bib -*.run.xml - -## Build tool auxiliary files: -*.fdb_latexmk -*.synctex -*.synctex(busy) -*.synctex.gz -*.synctex.gz(busy) -*.pdfsync - -## Build tool directories for auxiliary files -# latexrun -latex.out/ - -## Auxiliary and intermediate files from other packages: -# algorithms -*.alg -*.loa - -# achemso -acs-*.bib - -# amsthm -*.thm - -# beamer -*.nav -*.pre -*.snm -*.vrb - -# changes -*.soc - -# comment -*.cut - -# cprotect -*.cpt - -# elsarticle (documentclass of Elsevier journals) -*.spl - -# endnotes -*.ent - -# fixme -*.lox - -# feynmf/feynmp -*.mf -*.mp -*.t[1-9] -*.t[1-9][0-9] -*.tfm - -#(r)(e)ledmac/(r)(e)ledpar -*.end -*.?end -*.[1-9] -*.[1-9][0-9] -*.[1-9][0-9][0-9] -*.[1-9]R -*.[1-9][0-9]R -*.[1-9][0-9][0-9]R -*.eledsec[1-9] -*.eledsec[1-9]R -*.eledsec[1-9][0-9] -*.eledsec[1-9][0-9]R -*.eledsec[1-9][0-9][0-9] -*.eledsec[1-9][0-9][0-9]R - -# glossaries -*.acn -*.acr -*.glg -*.glo -*.gls -*.glsdefs - -# uncomment this for glossaries-extra (will ignore makeindex's style files!) -# *.ist - -# gnuplottex -*-gnuplottex-* - -# gregoriotex -*.gaux -*.gtex - -# htlatex -*.4ct -*.4tc -*.idv -*.lg -*.trc -*.xref - -# hyperref -*.brf - -# knitr -*-concordance.tex -# TODO Comment the next line if you want to keep your tikz graphics files -*.tikz -*-tikzDictionary - -# listings -*.lol - -# luatexja-ruby -*.ltjruby - -# makeidx -*.idx -*.ilg -*.ind - -# minitoc -*.maf -*.mlf -*.mlt -*.mtc[0-9]* -*.slf[0-9]* -*.slt[0-9]* -*.stc[0-9]* - -# minted -_minted* -*.pyg - -# morewrites -*.mw - -# nomencl -*.nlg -*.nlo -*.nls - -# pax -*.pax - -# pdfpcnotes -*.pdfpc - -# sagetex -*.sagetex.sage -*.sagetex.py -*.sagetex.scmd - -# scrwfile -*.wrt - -# sympy -*.sout -*.sympy -sympy-plots-for-*.tex/ - -# pdfcomment -*.upa -*.upb - -# pythontex -*.pytxcode -pythontex-files-*/ - -# tcolorbox -*.listing - -# thmtools -*.loe - -# TikZ & PGF -*.dpth -*.md5 -*.auxlock - -# todonotes -*.tdo - -# vhistory -*.hst -*.ver - -# easy-todo -*.lod - -# xcolor -*.xcp - -# xmpincl -*.xmpi - -# xindy -*.xdy - -# xypic precompiled matrices -*.xyc - -# endfloat -*.ttt -*.fff - -# Latexian -TSWLatexianTemp* - -## Editors: -# WinEdt -*.bak -*.sav - -# Texpad -.texpadtmp - -# LyX -*.lyx~ - -# Kile -*.backup - -# KBibTeX -*~[0-9]* - -# auto folder when using emacs and auctex -./auto/* -*.el - -# expex forward references with \gathertags -*-tags.tex - -# standalone packages -*.sta - -### LaTeX Patch ### -# glossaries -*.glstex - -### Linux ### -*~ - -# temporary files which can be created if a process still has a handle open of a deleted file -.fuse_hidden* - -# KDE directory preferences -.directory - -# Linux trash folder which might appear on any partition or disk -.Trash-* - -# .nfs files are created when an open file is removed but is still being accessed -.nfs* - -### Python ### -# Byte-compiled / optimized / DLL files -__pycache__/ -*.py[cod] -*$py.class - -# C extensions -*.so - -# Distribution / packaging -.Python -build/ -develop-eggs/ -dist/ -downloads/ -eggs/ -.eggs/ -lib/ -lib64/ -parts/ -sdist/ -var/ -wheels/ -pip-wheel-metadata/ -share/python-wheels/ -*.egg-info/ -.installed.cfg -*.egg -MANIFEST - -# PyInstaller -# Usually these files are written by a python script from a template -# before PyInstaller builds the exe, so as to inject date/other infos into it. -*.manifest -*.spec - -# Installer logs -pip-log.txt -pip-delete-this-directory.txt - -# Unit test / coverage reports -htmlcov/ -.tox/ -.nox/ -.coverage -.coverage.* -.cache -nosetests.xml -coverage.xml -*.cover -.hypothesis/ -.pytest_cache/ - -# Translations -*.mo -*.pot - -# Scrapy stuff: -.scrapy - -# Sphinx documentation -docs/_build/ - -# PyBuilder -target/ - -# pyenv -.python-version - -# pipenv -# According to pypa/pipenv#598, it is recommended to include Pipfile.lock in version control. -# However, in case of collaboration, if having platform-specific dependencies or dependencies -# having no cross-platform support, pipenv may install dependencies that don't work, or not -# install all needed dependencies. -#Pipfile.lock - -# celery beat schedule file -celerybeat-schedule - -# SageMath parsed files -*.sage.py - -# Spyder project settings -.spyderproject -.spyproject - -# Rope project settings -.ropeproject - -# Mr Developer -.mr.developer.cfg -.project -.pydevproject - -# mkdocs documentation -/site - -# mypy -.mypy_cache/ -.dmypy.json -dmypy.json - -# Pyre type checker -.pyre/ - -### VisualStudioCode ### -.vscode/* -!.vscode/settings.json -!.vscode/tasks.json -!.vscode/launch.json -!.vscode/extensions.json - -### VisualStudioCode Patch ### -# Ignore all local history of files -.history - -# End of https://www.gitignore.io/api/visualstudiocode,linux,latex,python - -# Custom rules (everything added below won't be overriden by 'Generate .gitignore File' if you use 'Update' option) - +# File created using '.gitignore Generator' for Visual Studio Code: https://bit.ly/vscode-gig + +# Created by https://www.gitignore.io/api/visualstudiocode,linux,latex,python +# Edit at https://www.gitignore.io/?templates=visualstudiocode,linux,latex,python + +### LaTeX ### +## Core latex/pdflatex auxiliary files: +*.aux +*.lof +*.log +*.lot +*.fls +*.out +*.toc +*.fmt +*.fot +*.cb +*.cb2 +.*.lb + +## Intermediate documents: +*.dvi +*.xdv +*-converted-to.* +# these rules might exclude image files for figures etc. +# *.ps +# *.eps +# *.pdf + +## Generated if empty string is given at "Please type another file name for output:" +.pdf + +## Bibliography auxiliary files (bibtex/biblatex/biber): +*.bbl +*.bcf +*.blg +*-blx.aux +*-blx.bib +*.run.xml + +## Build tool auxiliary files: +*.fdb_latexmk +*.synctex +*.synctex(busy) +*.synctex.gz +*.synctex.gz(busy) +*.pdfsync + +## Build tool directories for auxiliary files +# latexrun +latex.out/ + +## Auxiliary and intermediate files from other packages: +# algorithms +*.alg +*.loa + +# achemso +acs-*.bib + +# amsthm +*.thm + +# beamer +*.nav +*.pre +*.snm +*.vrb + +# changes +*.soc + +# comment +*.cut + +# cprotect +*.cpt + +# elsarticle (documentclass of Elsevier journals) +*.spl + +# endnotes +*.ent + +# fixme +*.lox + +# feynmf/feynmp +*.mf +*.mp +*.t[1-9] +*.t[1-9][0-9] +*.tfm + +#(r)(e)ledmac/(r)(e)ledpar +*.end +*.?end +*.[1-9] +*.[1-9][0-9] +*.[1-9][0-9][0-9] +*.[1-9]R +*.[1-9][0-9]R +*.[1-9][0-9][0-9]R +*.eledsec[1-9] +*.eledsec[1-9]R +*.eledsec[1-9][0-9] +*.eledsec[1-9][0-9]R +*.eledsec[1-9][0-9][0-9] +*.eledsec[1-9][0-9][0-9]R + +# glossaries +*.acn +*.acr +*.glg +*.glo +*.gls +*.glsdefs + +# uncomment this for glossaries-extra (will ignore makeindex's style files!) +# *.ist + +# gnuplottex +*-gnuplottex-* + +# gregoriotex +*.gaux +*.gtex + +# htlatex +*.4ct +*.4tc +*.idv +*.lg +*.trc +*.xref + +# hyperref +*.brf + +# knitr +*-concordance.tex +# TODO Comment the next line if you want to keep your tikz graphics files +*.tikz +*-tikzDictionary + +# listings +*.lol + +# luatexja-ruby +*.ltjruby + +# makeidx +*.idx +*.ilg +*.ind + +# minitoc +*.maf +*.mlf +*.mlt +*.mtc[0-9]* +*.slf[0-9]* +*.slt[0-9]* +*.stc[0-9]* + +# minted +_minted* +*.pyg + +# morewrites +*.mw + +# nomencl +*.nlg +*.nlo +*.nls + +# pax +*.pax + +# pdfpcnotes +*.pdfpc + +# sagetex +*.sagetex.sage +*.sagetex.py +*.sagetex.scmd + +# scrwfile +*.wrt + +# sympy +*.sout +*.sympy +sympy-plots-for-*.tex/ + +# pdfcomment +*.upa +*.upb + +# pythontex +*.pytxcode +pythontex-files-*/ + +# tcolorbox +*.listing + +# thmtools +*.loe + +# TikZ & PGF +*.dpth +*.md5 +*.auxlock + +# todonotes +*.tdo + +# vhistory +*.hst +*.ver + +# easy-todo +*.lod + +# xcolor +*.xcp + +# xmpincl +*.xmpi + +# xindy +*.xdy + +# xypic precompiled matrices +*.xyc + +# endfloat +*.ttt +*.fff + +# Latexian +TSWLatexianTemp* + +## Editors: +# WinEdt +*.bak +*.sav + +# Texpad +.texpadtmp + +# LyX +*.lyx~ + +# Kile +*.backup + +# KBibTeX +*~[0-9]* + +# auto folder when using emacs and auctex +./auto/* +*.el + +# expex forward references with \gathertags +*-tags.tex + +# standalone packages +*.sta + +### LaTeX Patch ### +# glossaries +*.glstex + +### Linux ### +*~ + +# temporary files which can be created if a process still has a handle open of a deleted file +.fuse_hidden* + +# KDE directory preferences +.directory + +# Linux trash folder which might appear on any partition or disk +.Trash-* + +# .nfs files are created when an open file is removed but is still being accessed +.nfs* + +### Python ### +# Byte-compiled / optimized / DLL files +__pycache__/ +*.py[cod] +*$py.class + +# C extensions +*.so + +# Distribution / packaging +.Python +build/ +develop-eggs/ +dist/ +downloads/ +eggs/ +.eggs/ +lib/ +lib64/ +parts/ +sdist/ +var/ +wheels/ +pip-wheel-metadata/ +share/python-wheels/ +*.egg-info/ +.installed.cfg +*.egg +MANIFEST + +# PyInstaller +# Usually these files are written by a python script from a template +# before PyInstaller builds the exe, so as to inject date/other infos into it. +*.manifest +*.spec + +# Installer logs +pip-log.txt +pip-delete-this-directory.txt + +# Unit test / coverage reports +htmlcov/ +.tox/ +.nox/ +.coverage +.coverage.* +.cache +nosetests.xml +coverage.xml +*.cover +.hypothesis/ +.pytest_cache/ + +# Translations +*.mo +*.pot + +# Scrapy stuff: +.scrapy + +# Sphinx documentation +docs/_build/ + +# PyBuilder +target/ + +# pyenv +.python-version + +# pipenv +# According to pypa/pipenv#598, it is recommended to include Pipfile.lock in version control. +# However, in case of collaboration, if having platform-specific dependencies or dependencies +# having no cross-platform support, pipenv may install dependencies that don't work, or not +# install all needed dependencies. +#Pipfile.lock + +# celery beat schedule file +celerybeat-schedule + +# SageMath parsed files +*.sage.py + +# Spyder project settings +.spyderproject +.spyproject + +# Rope project settings +.ropeproject + +# Mr Developer +.mr.developer.cfg +.project +.pydevproject + +# mkdocs documentation +/site + +# mypy +.mypy_cache/ +.dmypy.json +dmypy.json + +# Pyre type checker +.pyre/ + +### VisualStudioCode ### +.vscode/* +!.vscode/settings.json +!.vscode/tasks.json +!.vscode/launch.json +!.vscode/extensions.json + +### VisualStudioCode Patch ### +# Ignore all local history of files +.history + +# End of https://www.gitignore.io/api/visualstudiocode,linux,latex,python + +# Custom rules (everything added below won't be overriden by 'Generate .gitignore File' if you use 'Update' option) + diff --git a/LICENSE b/LICENSE index 718bd210a..7f19c2d8c 100644 --- a/LICENSE +++ b/LICENSE @@ -1,21 +1,21 @@ -MIT License - -Copyright (c) 2021 School of Math and Computer Science, University of Havana - -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. +MIT License + +Copyright (c) 2021 School of Math and Computer Science, University of Havana + +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. diff --git a/Readme.md b/Readme.md index a47d48b9e..0e33ddb08 100644 --- a/Readme.md +++ b/Readme.md @@ -1,172 +1,172 @@ -# COOL: Proyecto de Compilación - -> Proyecto base para el compilador de 4to año en Ciencia de la Computación. - -## Generalidades - -La evaluación de la asignatura Complementos de Compilación, inscrita en el programa del 4to año de la Licenciatura en Ciencia de la Computación de la Facultad de Matemática y Computación de la -Universidad de La Habana, consiste este curso en la implementación de un compilador completamente -funcional para el lenguaje _COOL_. - -_COOL (Classroom Object-Oriented Language)_ es un pequeño lenguaje que puede ser implementado con un esfuerzo razonable en un semestre del curso. Aun así, _COOL_ mantiene muchas de las características de los lenguajes de programación modernos, incluyendo orientación a objetos, tipado estático y manejo automático de memoria. - -## Cómo comenzar (o terminar) - -El proyecto de Compilación será recogido y evaluado **únicamente** a través de Github. Es imprescindible tener una cuenta de Github para cada participante, y que su proyecto esté correctamente hosteado en esta plataforma. A continuación le damos las instrucciones mínimas necesarias para ello: - -### 1. Si no lo han hecho ya, regístrense en [Github](https://github.com) todos los miembros del equipo (es gratis). - -![](img/img1.png) - -### 2. Haga click en el botón **Fork** para hacer una copia del proyecto en el perfil de Github de uno de los miembros. - -Opcionalmente pueden [crear una organización](https://github.com/organizations/new) y copiar el proyecto en el perfil de la misma. - -![](img/img2.png) - -### 3. Una vez hecho esto, tendrá un nuevo repositorio en `github/`. - -Revise que el repositorio de su equipo está en su perfil. -En este ejemplo se ha copiado a la cuenta de `github.com/apiad`. - -Debe indicar bajo el nombre del repositorio: `"forked from matcom/cool-compiler-2021"`. - -![](img/img3.png) - -### 4. Clone este proyecto en un repositorio local. - -Busque la URL de su proyecto en la interfaz web de Github. - -Asegúrese de clonar **su copia** y no el proyecto original en `matcom/cool-compiler-2021`. - -![](img/img4.png) - -```bash -$ git clone git@github.com:/cool-compiler-2021.git -``` - -> Donde `` es posiblemente el nombre de su equipo o del miembro donde se hizo el _fork_. - -A partir de este punto debe tener un proyecto `cool-compiler-2021` local. -El siguiente paso depende de si usted ya tiene su código versionado con `git` o no. - -### 5.A. Si tiene su proyecto en git (y no quiere perder la historia): - -#### 5.1. Mezcle hacia el nuevo respositorio su repositorio anterior: - -```bash -$ cd cool-compiler-2021 -$ git pull --allow-unrelated-histories master -``` - -#### 5.2. Organice su proyecto, código fuente y documentación, de acuerdo a las instrucciones de este documento, y vuelva a hacer `commit`. - -```bash -$ mv src/ -$ git add . -$ git commit -a -m "Mezclado con el proyecto base" -``` - -#### 5.3. A partir de este punto puede hacer `push` cada vez que tenga cambios que subir. - -```bash -$ git push origin master -``` - -### 5.B Si aún no tiene su proyecto en git (o no le importa la historia): - -#### 5.1. Simplemente copie el código de su proyecto en la carpeta correspondiente `src` y haga su primer commit. - -```bash -$ mv src/ -$ git commit -a -m "Hello Git!" -``` - -#### 5.2. A partir de este punto asegúrese de hacer `commit` de forma regular para mantener su repositorio actualizado. - -Si necesita saber más sobre `git`, todo lo imprescindible está en [esta guía](doc/github-git-cheat-sheet.pdf). - -#### 5.3. A partir de este punto puede hacer `push` cada vez que tenga cambios que subir. - -```bash -$ git push origin master -``` - -## Entregas - -En este proyecto se realizarán entregas parciales a lo largo del curso. Para realizar una entrega, siga los siguientes pasos. - -### 1. Cree un pull request al proyecto original desde su copia. - -![](img/img5.png) - -### 2. Asegúrese de tener la siguiente configuración antes de hacer click en **Create pull request**. - -- **base repository**: `matcom/cool-compiler-2021` (repositorio original) - - **branch**: `master` -- **head repository**: `/cool-compiler-2021` (repositorio propio) - - **branch**: `master` (o la que corresponda) - -> Asegúrese que se indica **Able to merge**. De lo contrario, existen cambios en el repositorio original que usted no tiene, y debe actualizarlos. - -> **NOTA**: Asegúrese que el _pull request_ se hace a la rama `master`. - -![](img/img6.png) - -### 3. Introduzca un título y descripción adecuados, y haga click en **Create pull request**. - -![](img/img7.png) - -### 4. Espere mientras se ejecutan las pruebas. - -Verá la indicación **Some checks haven't completed yet**. - -![](img/img8.png) - -Es posible que tenga que actualizar los cambios que se hayan hecho en el repositorio original, por ejemplo, si se han agregado nuevos tests. En este caso obtendrá el siguiente mensaje: - -> **This branch is out-of-date with base branch** - -Haga click en **Update branch** y siga las instrucciones. -### 5. Verifique que no hubo errores en las pruebas. - -Si ve el mensaje **(All | Some) checks have failed**, significa que su código no pasó las pruebas. - -![](img/img9.png) - -Para ver los resultados de las pruebas haga click en el link **Details**. - -![](img/img10.png) - - -### 6. Arregle los errores y repita el paso 5 hasta que todas las pruebas pasen. - -Para cualquier modificación que haga a su proyecto, haga _commit_ y _push_ para **su repositorio personal** y automáticamente se actualizará el estado del _pull request_ y se volverán a ejecutar las pruebas. **No es necesario** abrir un _pull request_ nuevo por cada entrega, sino actualizar el anterior. - -> **Por favor asegúrese de mantener un solo _pull request_ activo por equipo**. En caso de abrir uno nuevo, cerrar el anterior. - -## Sobre la implementación - -Ponga todo su código e instrucciones necesarias en la carpeta `src`. Más información en [`src/Readme.md`](src/Readme.md). - -## Sobre la documentación - -Usted debe presentar un reporte escrito documentando el proceso de construcción de su compilador y los detalles más importantes de su funcionamiento. Más información en [`doc/Readme.md`](doc/Readme.md). - -## Sobre los equipos de desarrollo - -Para desarrollar el compilador del lenguaje COOL se trabajará en equipos de 2 o 3 integrantes. - -## Sobre los casos de prueba - -La carpeta `tests` contiene todos los casos de prueba que son obligatorios de pasar para que su proyecto tenga derecho a ser evaluado. - -Estos tests se ejecutan automáticamente cada vez que hace un _pull request_ al repositorio `matcom/cool-compiler-2021`. Solo aquellos proyectos que pasen todas las pruebas con éxito serán evaluados. - -Para ejecutar las pruebas localmente, debe tener instalado `Python 3.7`, `pip` y `make` (normalmente viene con Linux). Ejecute: - -```bash -$ pip install -r requirements.txt -$ cd src -$ make test -``` +# COOL: Proyecto de Compilación + +> Proyecto base para el compilador de 4to año en Ciencia de la Computación. + +## Generalidades + +La evaluación de la asignatura Complementos de Compilación, inscrita en el programa del 4to año de la Licenciatura en Ciencia de la Computación de la Facultad de Matemática y Computación de la +Universidad de La Habana, consiste este curso en la implementación de un compilador completamente +funcional para el lenguaje _COOL_. + +_COOL (Classroom Object-Oriented Language)_ es un pequeño lenguaje que puede ser implementado con un esfuerzo razonable en un semestre del curso. Aun así, _COOL_ mantiene muchas de las características de los lenguajes de programación modernos, incluyendo orientación a objetos, tipado estático y manejo automático de memoria. + +## Cómo comenzar (o terminar) + +El proyecto de Compilación será recogido y evaluado **únicamente** a través de Github. Es imprescindible tener una cuenta de Github para cada participante, y que su proyecto esté correctamente hosteado en esta plataforma. A continuación le damos las instrucciones mínimas necesarias para ello: + +### 1. Si no lo han hecho ya, regístrense en [Github](https://github.com) todos los miembros del equipo (es gratis). + +![](img/img1.png) + +### 2. Haga click en el botón **Fork** para hacer una copia del proyecto en el perfil de Github de uno de los miembros. + +Opcionalmente pueden [crear una organización](https://github.com/organizations/new) y copiar el proyecto en el perfil de la misma. + +![](img/img2.png) + +### 3. Una vez hecho esto, tendrá un nuevo repositorio en `github/`. + +Revise que el repositorio de su equipo está en su perfil. +En este ejemplo se ha copiado a la cuenta de `github.com/apiad`. + +Debe indicar bajo el nombre del repositorio: `"forked from matcom/cool-compiler-2021"`. + +![](img/img3.png) + +### 4. Clone este proyecto en un repositorio local. + +Busque la URL de su proyecto en la interfaz web de Github. + +Asegúrese de clonar **su copia** y no el proyecto original en `matcom/cool-compiler-2021`. + +![](img/img4.png) + +```bash +$ git clone git@github.com:/cool-compiler-2021.git +``` + +> Donde `` es posiblemente el nombre de su equipo o del miembro donde se hizo el _fork_. + +A partir de este punto debe tener un proyecto `cool-compiler-2021` local. +El siguiente paso depende de si usted ya tiene su código versionado con `git` o no. + +### 5.A. Si tiene su proyecto en git (y no quiere perder la historia): + +#### 5.1. Mezcle hacia el nuevo respositorio su repositorio anterior: + +```bash +$ cd cool-compiler-2021 +$ git pull --allow-unrelated-histories master +``` + +#### 5.2. Organice su proyecto, código fuente y documentación, de acuerdo a las instrucciones de este documento, y vuelva a hacer `commit`. + +```bash +$ mv src/ +$ git add . +$ git commit -a -m "Mezclado con el proyecto base" +``` + +#### 5.3. A partir de este punto puede hacer `push` cada vez que tenga cambios que subir. + +```bash +$ git push origin master +``` + +### 5.B Si aún no tiene su proyecto en git (o no le importa la historia): + +#### 5.1. Simplemente copie el código de su proyecto en la carpeta correspondiente `src` y haga su primer commit. + +```bash +$ mv src/ +$ git commit -a -m "Hello Git!" +``` + +#### 5.2. A partir de este punto asegúrese de hacer `commit` de forma regular para mantener su repositorio actualizado. + +Si necesita saber más sobre `git`, todo lo imprescindible está en [esta guía](doc/github-git-cheat-sheet.pdf). + +#### 5.3. A partir de este punto puede hacer `push` cada vez que tenga cambios que subir. + +```bash +$ git push origin master +``` + +## Entregas + +En este proyecto se realizarán entregas parciales a lo largo del curso. Para realizar una entrega, siga los siguientes pasos. + +### 1. Cree un pull request al proyecto original desde su copia. + +![](img/img5.png) + +### 2. Asegúrese de tener la siguiente configuración antes de hacer click en **Create pull request**. + +- **base repository**: `matcom/cool-compiler-2021` (repositorio original) + - **branch**: `master` +- **head repository**: `/cool-compiler-2021` (repositorio propio) + - **branch**: `master` (o la que corresponda) + +> Asegúrese que se indica **Able to merge**. De lo contrario, existen cambios en el repositorio original que usted no tiene, y debe actualizarlos. + +> **NOTA**: Asegúrese que el _pull request_ se hace a la rama `master`. + +![](img/img6.png) + +### 3. Introduzca un título y descripción adecuados, y haga click en **Create pull request**. + +![](img/img7.png) + +### 4. Espere mientras se ejecutan las pruebas. + +Verá la indicación **Some checks haven't completed yet**. + +![](img/img8.png) + +Es posible que tenga que actualizar los cambios que se hayan hecho en el repositorio original, por ejemplo, si se han agregado nuevos tests. En este caso obtendrá el siguiente mensaje: + +> **This branch is out-of-date with base branch** + +Haga click en **Update branch** y siga las instrucciones. +### 5. Verifique que no hubo errores en las pruebas. + +Si ve el mensaje **(All | Some) checks have failed**, significa que su código no pasó las pruebas. + +![](img/img9.png) + +Para ver los resultados de las pruebas haga click en el link **Details**. + +![](img/img10.png) + + +### 6. Arregle los errores y repita el paso 5 hasta que todas las pruebas pasen. + +Para cualquier modificación que haga a su proyecto, haga _commit_ y _push_ para **su repositorio personal** y automáticamente se actualizará el estado del _pull request_ y se volverán a ejecutar las pruebas. **No es necesario** abrir un _pull request_ nuevo por cada entrega, sino actualizar el anterior. + +> **Por favor asegúrese de mantener un solo _pull request_ activo por equipo**. En caso de abrir uno nuevo, cerrar el anterior. + +## Sobre la implementación + +Ponga todo su código e instrucciones necesarias en la carpeta `src`. Más información en [`src/Readme.md`](src/Readme.md). + +## Sobre la documentación + +Usted debe presentar un reporte escrito documentando el proceso de construcción de su compilador y los detalles más importantes de su funcionamiento. Más información en [`doc/Readme.md`](doc/Readme.md). + +## Sobre los equipos de desarrollo + +Para desarrollar el compilador del lenguaje COOL se trabajará en equipos de 2 o 3 integrantes. + +## Sobre los casos de prueba + +La carpeta `tests` contiene todos los casos de prueba que son obligatorios de pasar para que su proyecto tenga derecho a ser evaluado. + +Estos tests se ejecutan automáticamente cada vez que hace un _pull request_ al repositorio `matcom/cool-compiler-2021`. Solo aquellos proyectos que pasen todas las pruebas con éxito serán evaluados. + +Para ejecutar las pruebas localmente, debe tener instalado `Python 3.7`, `pip` y `make` (normalmente viene con Linux). Ejecute: + +```bash +$ pip install -r requirements.txt +$ cd src +$ make test +``` diff --git a/doc/Readme.md b/doc/Readme.md index 3b2569f5c..a7ed0e32d 100644 --- a/doc/Readme.md +++ b/doc/Readme.md @@ -1,33 +1,33 @@ -# Documentación - -## Readme - -Modifique el contenido de este documento para documentar de forma clara y concisa los siguientes aspectos: - -- Cómo ejecutar (y compilar si es necesario) su compilador. -- Requisitos adicionales, dependencias, configuración, etc. -- Opciones adicionales que tenga su compilador. - -## Sobre los Equipos de Desarrollo - -Para desarrollar el compilador del lenguaje COOL se trabajará en equipos de 2 o 3 integrantes. El proyecto de Compilación será recogido y evaluado únicamente a través de Github. Es imprescindible tener una cuenta de Github para cada participante, y que su proyecto esté correctamente hosteado en esta plataforma. - -**⚠️ NOTA**: Debe completar el archivo `team.yml` con los datos correctos de cada miembro de su equipo. - -## Sobre los Materiales a Entregar - -Para la evaluación del proyecto Ud. debe entregar un informe en formato PDF (`report.pdf`) en esta carpeta, que resuma de manera organizada y comprensible la arquitectura e implementación de su compilador. -El documento no tiene límite de extensión. -En él explicará en más detalle su solución a los problemas que, durante la implementación de cada una de las fases del proceso de compilación, hayan requerido de Ud. especial atención. - -## Estructura del reporte - -Usted es libre de estructurar su reporte escrito como más conveniente le parezca. A continuación le sugerimos algunas secciones que no deberían faltar, aunque puede mezclar, renombrar y organizarlas de la manera que mejor le parezca: - -- **Uso del compilador**: detalles sobre las opciones de líneas de comando, si tiene opciones adicionales (e.j., `--ast` genera un AST en JSON, etc.). Básicamente lo mismo que pondrá en este Readme. -- **Arquitectura del compilador**: una explicación general de la arquitectura, en cuántos módulos se divide el proyecto, cuantas fases tiene, qué tipo de gramática se utiliza, y en general, como se organiza el proyecto. Una buena imagen siempre ayuda. -- **Problemas técnicos**: detalles sobre cualquier problema teórico o técnico interesante que haya necesitado resolver de forma particular. - -## Sobre la Fecha de Entrega - -Se realizarán recogidas parciales del proyecto a lo largo del curso. En el Canal de Telegram se anunciará la fecha y requisitos de cada entrega. +# Documentación + +## Readme + +Modifique el contenido de este documento para documentar de forma clara y concisa los siguientes aspectos: + +- Cómo ejecutar (y compilar si es necesario) su compilador. +- Requisitos adicionales, dependencias, configuración, etc. +- Opciones adicionales que tenga su compilador. + +## Sobre los Equipos de Desarrollo + +Para desarrollar el compilador del lenguaje COOL se trabajará en equipos de 2 o 3 integrantes. El proyecto de Compilación será recogido y evaluado únicamente a través de Github. Es imprescindible tener una cuenta de Github para cada participante, y que su proyecto esté correctamente hosteado en esta plataforma. + +**⚠️ NOTA**: Debe completar el archivo `team.yml` con los datos correctos de cada miembro de su equipo. + +## Sobre los Materiales a Entregar + +Para la evaluación del proyecto Ud. debe entregar un informe en formato PDF (`report.pdf`) en esta carpeta, que resuma de manera organizada y comprensible la arquitectura e implementación de su compilador. +El documento no tiene límite de extensión. +En él explicará en más detalle su solución a los problemas que, durante la implementación de cada una de las fases del proceso de compilación, hayan requerido de Ud. especial atención. + +## Estructura del reporte + +Usted es libre de estructurar su reporte escrito como más conveniente le parezca. A continuación le sugerimos algunas secciones que no deberían faltar, aunque puede mezclar, renombrar y organizarlas de la manera que mejor le parezca: + +- **Uso del compilador**: detalles sobre las opciones de líneas de comando, si tiene opciones adicionales (e.j., `--ast` genera un AST en JSON, etc.). Básicamente lo mismo que pondrá en este Readme. +- **Arquitectura del compilador**: una explicación general de la arquitectura, en cuántos módulos se divide el proyecto, cuantas fases tiene, qué tipo de gramática se utiliza, y en general, como se organiza el proyecto. Una buena imagen siempre ayuda. +- **Problemas técnicos**: detalles sobre cualquier problema teórico o técnico interesante que haya necesitado resolver de forma particular. + +## Sobre la Fecha de Entrega + +Se realizarán recogidas parciales del proyecto a lo largo del curso. En el Canal de Telegram se anunciará la fecha y requisitos de cada entrega. diff --git a/doc/team.yml b/doc/team.yml index 0664fefc4..b16415109 100644 --- a/doc/team.yml +++ b/doc/team.yml @@ -1,10 +1,10 @@ -members: - - name: Damián O´Hallorans Toledo - github: NazDia - group: C412 - - name: Luis Ernesto Ibarra Vázquez - github: luisoibarra - group: C411 - - name: Luis Enrique Dalmau Coopat - github: LukeDalmau - group: C411 +members: + - name: Damián O´Hallorans Toledo + github: NazDia + group: C412 + - name: Luis Ernesto Ibarra Vázquez + github: luisoibarra + group: C411 + - name: Luis Enrique Dalmau Coopat + github: LukeDalmau + group: C411 diff --git a/requirements.txt b/requirements.txt index 9eb0cad1a..c250faba6 100644 --- a/requirements.txt +++ b/requirements.txt @@ -1,2 +1,2 @@ -pytest -pytest-ordering +pytest +pytest-ordering diff --git a/src/Readme.md b/src/Readme.md index 1200371b5..cdca282ec 100644 --- a/src/Readme.md +++ b/src/Readme.md @@ -1,78 +1,78 @@ -# COOL: Proyecto de Compilación - -La evaluación de la asignatura Complementos de Compilación, inscrita en el programa del 4to año de la Licenciatura en Ciencia de la Computación de la Facultad de Matemática y Computación de la -Universidad de La Habana, consiste este curso en la implementación de un compilador completamente -funcional para el lenguaje _COOL_. - -_COOL (Classroom Object-Oriented Language)_ es un pequeño lenguaje que puede ser implementado con un esfuerzo razonable en un semestre del curso. Aun así, _COOL_ mantiene muchas de las características de los lenguajes de programación modernos, incluyendo orientación a objetos, tipado estático y manejo automático de memoria. - -### Sobre el Lenguaje COOL - -Ud. podrá encontrar la especificación formal del lenguaje COOL en el documento _"COOL Language Reference Manual"_, que se distribuye junto con el presente texto. - -## Código Fuente - -### Compilando su proyecto - -Si es necesario compilar su proyecto, incluya todas las instrucciones necesarias en un archivo [`/src/makefile`](/src/makefile). -Durante la evaluación su proyecto se compilará ejecutando la siguiente secuencia: - -```bash -$ cd source -$ make clean -$ make -``` - -### Ejecutando su proyecto - -Incluya en un archivo [`/src/coolc.sh`](/src/coolc.sh) todas las instrucciones que hacen falta para lanzar su compilador. Recibirá como entrada un archivo con extensión `.cl` y debe generar como salida un archivo `.mips` cuyo nombre será el mismo que la entrada. - -Para lanzar el compilador, se ejecutará la siguiente instrucción: - -```bash -$ cd source -$ ./coolc.sh -``` - -### Sobre el Compilador de COOL - -El compilador de COOL se ejecutará como se ha definido anteriormente. -En caso de que no ocurran errores durante la operación del compilador, **coolc.sh** deberá terminar con código de salida 0, generar (o sobrescribir si ya existe) en la misma carpeta del archivo **.cl** procesado, y con el mismo nombre que éste, un archivo con extension **.mips** que pueda ser ejecutado con **spim**. Además, reportar a la salida estándar solamente lo siguiente: - - - - -En caso de que ocurran errores durante la operación del compilador, **coolc.sh** deberá terminar con código -de salida (exit code) 1 y reportar a la salida estándar (standard output stream) lo que sigue... - - - - _1 - ... - _n - -... donde `_i` tiene el siguiente formato: - - (,) - : - -Los campos `` y `` indican la ubicación del error en el fichero **.cl** procesado. En caso -de que la naturaleza del error sea tal que no pueda asociárselo a una línea y columna en el archivo de -código fuente, el valor de dichos campos debe ser 0. - -El campo `` será alguno entre: - -- `CompilerError`: se reporta al detectar alguna anomalía con la entrada del compilador. Por ejemplo, si el fichero a compilar no existe. -- `LexicographicError`: errores detectados por el lexer. -- `SyntacticError`: errores detectados por el parser. -- `NameError`: se reporta al referenciar un `identificador` en un ámbito en el que no es visible. -- `TypeError`: se reporta al detectar un problema de tipos. Incluye: - - incompatibilidad de tipos entre `rvalue` y `lvalue`, - - operación no definida entre objetos de ciertos tipos, y - - tipo referenciado pero no definido. -- `AttributeError`: se reporta cuando un atributo o método se referencia pero no está definido. -- `SemanticError`: cualquier otro error semántico. - -### Sobre la Implementación del Compilador de COOL - -El compilador debe estar implementado en `python`. Usted debe utilizar una herramienta generadora de analizadores -lexicográficos y sintácticos. Puede utilizar la que sea de su preferencia. +# COOL: Proyecto de Compilación + +La evaluación de la asignatura Complementos de Compilación, inscrita en el programa del 4to año de la Licenciatura en Ciencia de la Computación de la Facultad de Matemática y Computación de la +Universidad de La Habana, consiste este curso en la implementación de un compilador completamente +funcional para el lenguaje _COOL_. + +_COOL (Classroom Object-Oriented Language)_ es un pequeño lenguaje que puede ser implementado con un esfuerzo razonable en un semestre del curso. Aun así, _COOL_ mantiene muchas de las características de los lenguajes de programación modernos, incluyendo orientación a objetos, tipado estático y manejo automático de memoria. + +### Sobre el Lenguaje COOL + +Ud. podrá encontrar la especificación formal del lenguaje COOL en el documento _"COOL Language Reference Manual"_, que se distribuye junto con el presente texto. + +## Código Fuente + +### Compilando su proyecto + +Si es necesario compilar su proyecto, incluya todas las instrucciones necesarias en un archivo [`/src/makefile`](/src/makefile). +Durante la evaluación su proyecto se compilará ejecutando la siguiente secuencia: + +```bash +$ cd source +$ make clean +$ make +``` + +### Ejecutando su proyecto + +Incluya en un archivo [`/src/coolc.sh`](/src/coolc.sh) todas las instrucciones que hacen falta para lanzar su compilador. Recibirá como entrada un archivo con extensión `.cl` y debe generar como salida un archivo `.mips` cuyo nombre será el mismo que la entrada. + +Para lanzar el compilador, se ejecutará la siguiente instrucción: + +```bash +$ cd source +$ ./coolc.sh +``` + +### Sobre el Compilador de COOL + +El compilador de COOL se ejecutará como se ha definido anteriormente. +En caso de que no ocurran errores durante la operación del compilador, **coolc.sh** deberá terminar con código de salida 0, generar (o sobrescribir si ya existe) en la misma carpeta del archivo **.cl** procesado, y con el mismo nombre que éste, un archivo con extension **.mips** que pueda ser ejecutado con **spim**. Además, reportar a la salida estándar solamente lo siguiente: + + + + +En caso de que ocurran errores durante la operación del compilador, **coolc.sh** deberá terminar con código +de salida (exit code) 1 y reportar a la salida estándar (standard output stream) lo que sigue... + + + + _1 + ... + _n + +... donde `_i` tiene el siguiente formato: + + (,) - : + +Los campos `` y `` indican la ubicación del error en el fichero **.cl** procesado. En caso +de que la naturaleza del error sea tal que no pueda asociárselo a una línea y columna en el archivo de +código fuente, el valor de dichos campos debe ser 0. + +El campo `` será alguno entre: + +- `CompilerError`: se reporta al detectar alguna anomalía con la entrada del compilador. Por ejemplo, si el fichero a compilar no existe. +- `LexicographicError`: errores detectados por el lexer. +- `SyntacticError`: errores detectados por el parser. +- `NameError`: se reporta al referenciar un `identificador` en un ámbito en el que no es visible. +- `TypeError`: se reporta al detectar un problema de tipos. Incluye: + - incompatibilidad de tipos entre `rvalue` y `lvalue`, + - operación no definida entre objetos de ciertos tipos, y + - tipo referenciado pero no definido. +- `AttributeError`: se reporta cuando un atributo o método se referencia pero no está definido. +- `SemanticError`: cualquier otro error semántico. + +### Sobre la Implementación del Compilador de COOL + +El compilador debe estar implementado en `python`. Usted debe utilizar una herramienta generadora de analizadores +lexicográficos y sintácticos. Puede utilizar la que sea de su preferencia. diff --git a/src/cas.cl b/src/cas.cl new file mode 100644 index 000000000..408bc5588 --- /dev/null +++ b/src/cas.cl @@ -0,0 +1,2 @@ +class A +` {} \ No newline at end of file diff --git a/src/cas.mips b/src/cas.mips new file mode 100644 index 000000000..3592fad3f --- /dev/null +++ b/src/cas.mips @@ -0,0 +1 @@ +GENERATED MIPS \ No newline at end of file diff --git a/src/cool_cmp/__init__.py b/src/cool_cmp/__init__.py index f9ed839e3..4907db10f 100644 --- a/src/cool_cmp/__init__.py +++ b/src/cool_cmp/__init__.py @@ -1,3 +1,3 @@ -""" -Cool Compiler +""" +Cool Compiler """ \ No newline at end of file diff --git a/src/cool_cmp/cil/__init__.py b/src/cool_cmp/cil/__init__.py index afee2a42e..3827461e0 100644 --- a/src/cool_cmp/cil/__init__.py +++ b/src/cool_cmp/cil/__init__.py @@ -1,15 +1,15 @@ -""" -CIL package -""" - -from typing import List -from cool_cmp.shared import SymbolTable, InterfacePipeline -from cool_cmp.semantic import SemanticPipeline -from cool_cmp.cil.interface import ICil - -class CilPipeline(InterfacePipeline): - def __init__(self, semantic_pipeline:SemanticPipeline, *cils:List[ICil]): - super().__init__(semantic_pipeline, *cils) - - def __call__(self, program:str)->SymbolTable: - return super().__call__(program) +""" +CIL package +""" + +from typing import List +from cool_cmp.shared import SymbolTable, InterfacePipeline +from cool_cmp.semantic import SemanticPipeline +from cool_cmp.cil.interface import ICil + +class CilPipeline(InterfacePipeline): + def __init__(self, semantic_pipeline:SemanticPipeline, *cils:List[ICil]): + super().__init__(semantic_pipeline, *cils) + + def __call__(self, program:str)->SymbolTable: + return super().__call__(program) diff --git a/src/cool_cmp/cil/errors.py b/src/cool_cmp/cil/errors.py index 0139c4ec8..ec5ebfbcd 100644 --- a/src/cool_cmp/cil/errors.py +++ b/src/cool_cmp/cil/errors.py @@ -1,5 +1,5 @@ -""" -CIL errors -""" - +""" +CIL errors +""" + from cool_cmp.shared.errors import CoolError \ No newline at end of file diff --git a/src/cool_cmp/cil/interface.py b/src/cool_cmp/cil/interface.py index 9ddf31490..fef155074 100644 --- a/src/cool_cmp/cil/interface.py +++ b/src/cool_cmp/cil/interface.py @@ -1,10 +1,10 @@ -from cool_cmp.shared.ast import BaseAST -from cool_cmp.shared import ICoolService - -class ICil(ICoolService): - """ - CIL interface to implement - """ - - def __call__(self, ast:BaseAST) -> BaseAST: - raise NotImplementedError() +from cool_cmp.shared.ast import BaseAST +from cool_cmp.shared import ICoolService + +class ICil(ICoolService): + """ + CIL interface to implement + """ + + def __call__(self, ast:BaseAST) -> BaseAST: + raise NotImplementedError() diff --git a/src/cool_cmp/cil/visitors/__init__.py b/src/cool_cmp/cil/visitors/__init__.py index 6a5be3d16..89f4a99e2 100644 --- a/src/cool_cmp/cil/visitors/__init__.py +++ b/src/cool_cmp/cil/visitors/__init__.py @@ -1,3 +1,3 @@ -""" -Middle code visitors +""" +Middle code visitors """ \ No newline at end of file diff --git a/src/cool_cmp/lexer/__init__.py b/src/cool_cmp/lexer/__init__.py index 6122315c5..53deab303 100644 --- a/src/cool_cmp/lexer/__init__.py +++ b/src/cool_cmp/lexer/__init__.py @@ -1,26 +1,26 @@ -""" -Cool lexical analysis package -""" - -from cool_cmp.shared.token import ICoolToken -from cool_cmp.lexer.interface import ILexer -from cool_cmp.shared.pipeline import Pipeline, Pipe -from cool_cmp.shared import SymbolTable - -class LexerPipeline(Pipeline): - - def __init__(self, lexer:ILexer): - result = SymbolTable() - - def get_tokens(program:str): - tokens = lexer(program) - result.tokens = tokens - errors = lexer.get_errors() - for error in errors: - result.add_error(error) - return result - - super().__init__(Pipe(get_tokens)) - - def __call__(self, program:str)->SymbolTable: - return super().__call__(program) +""" +Cool lexical analysis package +""" + +from cool_cmp.shared.token import ICoolToken +from cool_cmp.lexer.interface import ILexer +from cool_cmp.shared.pipeline import Pipeline, Pipe +from cool_cmp.shared import SymbolTable + +class LexerPipeline(Pipeline): + + def __init__(self, lexer:ILexer): + result = SymbolTable() + + def get_tokens(program:str): + tokens = lexer(program) + result.tokens = tokens + errors = lexer.get_errors() + for error in errors: + result.add_error(error) + return result + + super().__init__(Pipe(get_tokens)) + + def __call__(self, program:str)->SymbolTable: + return super().__call__(program) diff --git a/src/cool_cmp/lexer/errors.py b/src/cool_cmp/lexer/errors.py index 041121bad..4416779b4 100644 --- a/src/cool_cmp/lexer/errors.py +++ b/src/cool_cmp/lexer/errors.py @@ -1,15 +1,22 @@ -""" -Lexer errors -""" - -from cool_cmp.shared.errors import CoolError -from cool_cmp.shared.token import ICoolToken - -class LexerCoolError(CoolError): - """ - Error class for lexical errors - """ - - def __init__(self, error_message:str, error_token:ICoolToken): - super().__init__(error_message) - self.token = error_token \ No newline at end of file +""" +Lexer errors +""" + +from cool_cmp.shared.errors import CoolError +from cool_cmp.shared.token import ICoolToken + +class LexerCoolError(CoolError): + """ + Error class for lexical errors + """ + + ERROR_TYPE = "LexicographicError" + + FORMAT = "({}, {}) - {}: {}" + + def __init__(self, error_message:str, error_token:ICoolToken): + super().__init__(error_message) + self.token = error_token + + def __str__(self): + return self.FORMAT.format(self.token.get_position()[0],self.token.get_position()[1], self.ERROR_TYPE, f'ERROR "{self.token.get_lex()}"') diff --git a/src/cool_cmp/lexer/interface.py b/src/cool_cmp/lexer/interface.py index 606caae8b..8f7f381e1 100644 --- a/src/cool_cmp/lexer/interface.py +++ b/src/cool_cmp/lexer/interface.py @@ -1,11 +1,11 @@ -from typing import List -from cool_cmp.shared.token import ICoolToken -from cool_cmp.shared import ICoolService - -class ILexer(ICoolService): - """ - Lexer interface to implement - """ - - def __call__(self, program_string:str) -> List[ICoolToken]: - raise NotImplementedError() +from typing import List +from cool_cmp.shared.token import ICoolToken +from cool_cmp.shared import ICoolService + +class ILexer(ICoolService): + """ + Lexer interface to implement + """ + + def __call__(self, program_string:str) -> List[ICoolToken]: + raise NotImplementedError() diff --git a/src/cool_cmp/lexer/lexer.py b/src/cool_cmp/lexer/lexer.py index c55b33d96..46e6821c6 100644 --- a/src/cool_cmp/lexer/lexer.py +++ b/src/cool_cmp/lexer/lexer.py @@ -1,180 +1,179 @@ -""" -Lexer usando ply -""" -from typing import List, Tuple - -from cool_cmp.lexer.interface import ILexer -from cool_cmp.shared.token import ICoolToken -from cool_cmp.shared.errors import ErrorTracker -from cool_cmp.lexer.errors import LexerCoolError -import ply.lex as lex - -class PlyCoolToken(lex.LexToken, ICoolToken): - - def __init__(self, lex:str, typex:str, line:int, column:int): - self.set_lex(lex) - self.set_type(typex) - self.set_position(line, column) - - def set_lex(self, lex:str): - self.lex = lex - self.value = lex - - def set_type(self, typex:str): - self.type = typex - self.typex = typex - - def set_position(self, line:int, column:int): - self.lineno = line - self.line = line - self.column = column - self.lexpos = column - - def get_lex(self)->str: - return self.lex - - def get_type(self)->str: - return self.typex - - def get_position(self)->Tuple[int,int]: - return (self.line, self.column) - - def __str__(self): - return f"{self.lex}:{self.type} Line {self.line} Column{self.column}" - - def __repr__(self): - return str(self) - -class PlyLexer(ILexer): - - @staticmethod - def find_column(input, token): - line_start = input.rfind('\n', 0, token.lexpos) + 1 - return (token.lexpos - line_start) + 1 - - def __init__(self): - self.error_tracker = ErrorTracker() # Error tracker implementation - - reserved = { - 'if' : 'IF', - 'then' : 'THEN', - 'fi' : 'FI', - 'else' : 'ELSE', - 'case' : 'CASE', - 'of' : 'OF', - 'esac' : 'ESAC', - 'class' : 'CLASS', - 'inherits' : 'INHERITS', - 'let' : 'LET', - 'in' : 'IN', - 'while' : 'WHILE', - 'loop' : 'LOOP', - 'pool' : 'POOL', - 'new' : 'NEW', - 'isvoid' : 'ISVOID', - 'not' : 'NOT', - 'true' : 'TRUE', - 'false' : 'FALSE' - } - - tokens = ( - 'ID', - 'NUMBER', - 'PLUS', - 'MINUS', - 'STAR', - 'DIV', - 'EQ', - 'LEQ', - 'LESS', - 'NEG', - 'OPAR', - 'CPAR', - 'OCUR', - 'CCUR', - 'ASSIGN', - 'SEMI', - 'COLON', - 'SIGNALER', - 'ARROBA', - 'STRING', - 'COMMENT', - 'DOT', - 'COMMA' - ) + tuple(reserved[x] for x in reserved.keys()) - - - t_STRING = r'"([^\n"\\]|(\\.)){0,1024}"' - - def t_NUMBER(t): - r'\d+' - try: - int(t.value) - except ValueError: - print("Integer value too large %d", t.value) - t.value = 'Invalid' - return t - - def t_ID(t): - r'[a-zA-Z_][a-zA-Z0-9_]*' - low = t.value.lower() - if (t.value[0] == 't' or t.value[0] == 'f') and (reserved.get(low) == 'TRUE' or reserved.get(low) == 'FALSE'): - t.type = reserved.get(low) - else: - t.type = reserved.get(t.value,'ID') - return t - - def t_COMMENT(t): - r'(--.*)|(\(\*(.|\n)*\*\))' - - t_PLUS = r'\+' - t_MINUS = r'-' - t_STAR = r'\*' - t_DIV = r'/' - t_OPAR = r'\(' - t_CPAR = r'\)' - t_OCUR = r'\{' - t_CCUR = r'\}' - t_EQ = r'=' - t_ASSIGN = r'<-' - t_LEQ = r'<=' - t_LESS = r'<' - t_NEG = r'~' - t_SEMI = r';' - t_COLON = r':' - t_SIGNALER = r'=>' - t_ARROBA = r'@' - t_DOT = r'\.' - t_COMMA = r',' - - t_ignore = " \t" - - def t_newline(t): - r'\n+' - t.lexer.lineno += t.value.count("\n") - - def t_error(t): - msg = f"Illegal character '{t.value[0]}'" - self.add_error(LexerCoolError(msg, PlyCoolToken(t.value, t.type, t.lineno, t.lexpos))) # TODO Set Token column - t.lexer.skip(1) - - self.lexer = lex.lex() - - def __call__(self, program_string:str): - self.lexer.input(program_string) - result = [] - while True: - tok = self.lexer.token() - if not tok: - break - result.append(PlyCoolToken(tok.value, tok.type, tok.lineno, self.find_column(program_string, tok))) - for tok in result: - print(tok) - return result - - def add_error(self, error:LexerCoolError): - self.error_tracker.add_error(error) - - def get_errors(self)->List[LexerCoolError]: - errors = self.error_tracker.get_errors() - return errors +""" +Lexer usando ply +""" +from typing import List, Tuple + +from cool_cmp.lexer.interface import ILexer +from cool_cmp.shared.token import ICoolToken +from cool_cmp.shared.errors import ErrorTracker +from cool_cmp.lexer.errors import LexerCoolError +import ply.lex as lex + +class PlyCoolToken(lex.LexToken, ICoolToken): + + def __init__(self, lex:str, typex:str, line:int, column:int): + self.set_lex(lex) + self.set_type(typex) + self.set_position(line, column) + + def set_lex(self, lex:str): + self.lex = lex + self.value = lex + + def set_type(self, typex:str): + self.type = typex + self.typex = typex + + def set_position(self, line:int, column:int): + self.lineno = line + self.line = line + self.column = column + self.lexpos = column + + def get_lex(self)->str: + return self.lex + + def get_type(self)->str: + return self.typex + + def get_position(self)->Tuple[int,int]: + return (self.line, self.column) + + def __str__(self): + return f"{self.lex}:{self.type} Line {self.line} Column{self.column}" + + def __repr__(self): + return str(self) + +class PlyLexer(ILexer): + + @staticmethod + def find_column(input, token): + line_start = input.rfind('\n', 0, token.lexpos) + 1 + return (token.lexpos - line_start) + 1 + + def __init__(self): + self.error_tracker = ErrorTracker() # Error tracker implementation + + reserved = { + 'if' : 'IF', + 'then' : 'THEN', + 'fi' : 'FI', + 'else' : 'ELSE', + 'case' : 'CASE', + 'of' : 'OF', + 'esac' : 'ESAC', + 'class' : 'CLASS', + 'inherits' : 'INHERITS', + 'let' : 'LET', + 'in' : 'IN', + 'while' : 'WHILE', + 'loop' : 'LOOP', + 'pool' : 'POOL', + 'new' : 'NEW', + 'isvoid' : 'ISVOID', + 'not' : 'NOT', + 'true' : 'TRUE', + 'false' : 'FALSE' + } + + tokens = ( + 'ID', + 'NUMBER', + 'PLUS', + 'MINUS', + 'STAR', + 'DIV', + 'EQ', + 'LEQ', + 'LESS', + 'NEG', + 'OPAR', + 'CPAR', + 'OCUR', + 'CCUR', + 'ASSIGN', + 'SEMI', + 'COLON', + 'SIGNALER', + 'ARROBA', + 'STRING', + 'COMMENT', + 'DOT', + 'COMMA' + ) + tuple(reserved[x] for x in reserved.keys()) + + + t_STRING = r'"([^\n"\\]|(\\.)){0,1024}"' + + def t_NUMBER(t): + r'\d+' + try: + int(t.value) + except ValueError: + msg = "Integer value too large %d", t.value + self.add_error(LexerCoolError(msg, PlyCoolToken(t.value, t.type, t.lineno, t.lexpos))) # TODO Set Token column + t.value = 'Invalid' + return t + + def t_ID(t): + r'[a-zA-Z_][a-zA-Z0-9_]*' + low = t.value.lower() + if (t.value[0] == 't' or t.value[0] == 'f') and (reserved.get(low) == 'TRUE' or reserved.get(low) == 'FALSE'): + t.type = reserved.get(low) + else: + t.type = reserved.get(t.value,'ID') + return t + + def t_COMMENT(t): + r'(--.*)|(\(\*(.|\n)*\*\))' + + t_PLUS = r'\+' + t_MINUS = r'-' + t_STAR = r'\*' + t_DIV = r'/' + t_OPAR = r'\(' + t_CPAR = r'\)' + t_OCUR = r'\{' + t_CCUR = r'\}' + t_EQ = r'=' + t_ASSIGN = r'<-' + t_LEQ = r'<=' + t_LESS = r'<' + t_NEG = r'~' + t_SEMI = r';' + t_COLON = r':' + t_SIGNALER = r'=>' + t_ARROBA = r'@' + t_DOT = r'\.' + t_COMMA = r',' + + t_ignore = " \t" + + def t_newline(t): + r'\n+' + t.lexer.lineno += t.value.count("\n") + + def t_error(t): + msg = f'ERROR "{t.value[0]}"' + self.add_error(LexerCoolError(msg, PlyCoolToken(t.value[0], t.type, t.lineno, t.lexpos))) # TODO Set Token column + t.lexer.skip(1) + + self.lexer = lex.lex() + + def __call__(self, program_string:str): + self.lexer.input(program_string) + result = [] + while True: + tok = self.lexer.token() + if not tok: + break + result.append(PlyCoolToken(tok.value, tok.type, tok.lineno, self.find_column(program_string, tok))) + return result + + def add_error(self, error:LexerCoolError): + self.error_tracker.add_error(error) + + def get_errors(self)->List[LexerCoolError]: + errors = self.error_tracker.get_errors() + return errors diff --git a/src/cool_cmp/mips/__init__.py b/src/cool_cmp/mips/__init__.py index df89fcb16..55fed0448 100644 --- a/src/cool_cmp/mips/__init__.py +++ b/src/cool_cmp/mips/__init__.py @@ -1,15 +1,15 @@ -""" -MIPS package -""" - -from typing import List -from cool_cmp.mips.interface import IMips -from cool_cmp.cil import CilPipeline -from cool_cmp.shared import SymbolTable, InterfacePipeline - -class MipsPipeline(InterfacePipeline): - def __init__(self, cil_pipeline:CilPipeline, *mipses:List[IMips]): - super().__init__(cil_pipeline, *mipses) - - def __call__(self, program:str)->SymbolTable: +""" +MIPS package +""" + +from typing import List +from cool_cmp.mips.interface import IMips +from cool_cmp.cil import CilPipeline +from cool_cmp.shared import SymbolTable, InterfacePipeline + +class MipsPipeline(InterfacePipeline): + def __init__(self, cil_pipeline:CilPipeline, *mipses:List[IMips]): + super().__init__(cil_pipeline, *mipses) + + def __call__(self, program:str)->SymbolTable: return super().__call__(program) \ No newline at end of file diff --git a/src/cool_cmp/mips/errors.py b/src/cool_cmp/mips/errors.py index 76123768b..98b03911c 100644 --- a/src/cool_cmp/mips/errors.py +++ b/src/cool_cmp/mips/errors.py @@ -1,5 +1,5 @@ -""" -MIPS errors -""" - +""" +MIPS errors +""" + from cool_cmp.shared.errors import CoolError \ No newline at end of file diff --git a/src/cool_cmp/mips/interface.py b/src/cool_cmp/mips/interface.py index 0045dedbd..a9e5571bc 100644 --- a/src/cool_cmp/mips/interface.py +++ b/src/cool_cmp/mips/interface.py @@ -1,12 +1,12 @@ - -from cool_cmp.shared.ast import BaseAST -from cool_cmp.shared import ICoolService - -class IMips(ICoolService): - """ - MIPS interface to implement - """ - - def __call__(self, ast:BaseAST) -> BaseAST: - raise NotImplementedError() - + +from cool_cmp.shared.ast import BaseAST +from cool_cmp.shared import ICoolService + +class IMips(ICoolService): + """ + MIPS interface to implement + """ + + def __call__(self, ast:BaseAST) -> BaseAST: + raise NotImplementedError() + diff --git a/src/cool_cmp/mips/visitors/__init__.py b/src/cool_cmp/mips/visitors/__init__.py index 69c434f60..7b708afa2 100644 --- a/src/cool_cmp/mips/visitors/__init__.py +++ b/src/cool_cmp/mips/visitors/__init__.py @@ -1,3 +1,3 @@ -""" -MIPS Visitors +""" +MIPS Visitors """ \ No newline at end of file diff --git a/src/cool_cmp/parser/__init__.py b/src/cool_cmp/parser/__init__.py index fa6b4056f..7401daef3 100644 --- a/src/cool_cmp/parser/__init__.py +++ b/src/cool_cmp/parser/__init__.py @@ -1,15 +1,15 @@ -""" -Cool parsing package -""" - -from cool_cmp.parser.interface import IParser -from cool_cmp.lexer import LexerPipeline -from cool_cmp.shared import SymbolTable, InterfacePipeline - -class ParserPipeline(InterfacePipeline): - - def __init__(self, lexer_pipeline:LexerPipeline, parser:IParser): - super().__init__(lexer_pipeline, *[parser,]) - - def __call__(self, program:str)->SymbolTable: - return super().__call__(program) +""" +Cool parsing package +""" + +from cool_cmp.parser.interface import IParser +from cool_cmp.lexer import LexerPipeline +from cool_cmp.shared import SymbolTable, InterfacePipeline + +class ParserPipeline(InterfacePipeline): + + def __init__(self, lexer_pipeline:LexerPipeline, parser:IParser): + super().__init__(lexer_pipeline, *[parser,]) + + def __call__(self, program:str)->SymbolTable: + return super().__call__(program) diff --git a/src/cool_cmp/parser/errors.py b/src/cool_cmp/parser/errors.py index 33bcce770..8b7c9d914 100644 --- a/src/cool_cmp/parser/errors.py +++ b/src/cool_cmp/parser/errors.py @@ -1,5 +1,23 @@ -""" -Parser errors -""" - -from cool_cmp.shared.errors import CoolError \ No newline at end of file +""" +Parser errors +""" + +from cool_cmp.shared.errors import CoolError +from cool_cmp.shared.token import ICoolToken + +class SyntacticCoolError(CoolError): + """ + Error class for syntactic errors + """ + + ERROR_TYPE = "SyntacticError" + + FORMAT = "({}, {}) - {}: {}" + + def __init__(self, error_message:str, error_token:ICoolToken): + super().__init__(error_message) + self.token = error_token + + def __str__(self): + return self.FORMAT.format(self.token.get_position()[0],self.token.get_position()[1], self.ERROR_TYPE, f'ERROR at or near "{self.token.get_lex()}"') + diff --git a/src/cool_cmp/parser/interface.py b/src/cool_cmp/parser/interface.py index 2617a2031..c26b988a1 100644 --- a/src/cool_cmp/parser/interface.py +++ b/src/cool_cmp/parser/interface.py @@ -1,12 +1,12 @@ -from typing import List -from cool_cmp.shared.token import ICoolToken -from cool_cmp.shared.ast import BaseAST -from cool_cmp.shared import ICoolService - -class IParser(ICoolService): - """ - Parser interface to implement - """ - - def __call__(self, tokens:List[ICoolToken]) -> BaseAST: - raise NotImplementedError() +from typing import List +from cool_cmp.shared.token import ICoolToken +from cool_cmp.shared.ast import BaseAST +from cool_cmp.shared import ICoolService + +class IParser(ICoolService): + """ + Parser interface to implement + """ + + def __call__(self, tokens:List[ICoolToken]) -> BaseAST: + raise NotImplementedError() diff --git a/src/cool_cmp/semantic/__init__.py b/src/cool_cmp/semantic/__init__.py index 513c8dfe8..b8a7df09a 100644 --- a/src/cool_cmp/semantic/__init__.py +++ b/src/cool_cmp/semantic/__init__.py @@ -1,16 +1,16 @@ -""" -Cool semantic package -""" - -from typing import List -from cool_cmp.semantic.interface import ISemantic -from cool_cmp.parser import ParserPipeline -from cool_cmp.shared import SymbolTable, InterfacePipeline - -class SemanticPipeline(InterfacePipeline): - - def __init__(self, parser_pipeline:ParserPipeline, *semantics:List[ISemantic]): - super().__init__(parser_pipeline, *semantics) - - def __call__(self, program:str)->SymbolTable: - return super().__call__(program) +""" +Cool semantic package +""" + +from typing import List +from cool_cmp.semantic.interface import ISemantic +from cool_cmp.parser import ParserPipeline +from cool_cmp.shared import SymbolTable, InterfacePipeline + +class SemanticPipeline(InterfacePipeline): + + def __init__(self, parser_pipeline:ParserPipeline, *semantics:List[ISemantic]): + super().__init__(parser_pipeline, *semantics) + + def __call__(self, program:str)->SymbolTable: + return super().__call__(program) diff --git a/src/cool_cmp/semantic/errors.py b/src/cool_cmp/semantic/errors.py index 82b4d957f..d9c36c46f 100644 --- a/src/cool_cmp/semantic/errors.py +++ b/src/cool_cmp/semantic/errors.py @@ -1,5 +1,5 @@ -""" -Semantic errors -""" - +""" +Semantic errors +""" + from cool_cmp.shared.errors import CoolError \ No newline at end of file diff --git a/src/cool_cmp/semantic/interface.py b/src/cool_cmp/semantic/interface.py index b59649c96..7bc31f869 100644 --- a/src/cool_cmp/semantic/interface.py +++ b/src/cool_cmp/semantic/interface.py @@ -1,11 +1,11 @@ -from cool_cmp.shared.ast import BaseAST -from cool_cmp.shared import ICoolService - -class ISemantic(ICoolService): - """ - Semantic interface to implement - """ - - def __call__(self, ast:BaseAST) -> BaseAST: - raise NotImplementedError() - +from cool_cmp.shared.ast import BaseAST +from cool_cmp.shared import ICoolService + +class ISemantic(ICoolService): + """ + Semantic interface to implement + """ + + def __call__(self, ast:BaseAST) -> BaseAST: + raise NotImplementedError() + diff --git a/src/cool_cmp/semantic/visitors/__init__.py b/src/cool_cmp/semantic/visitors/__init__.py index f2b87d7a1..7965bc611 100644 --- a/src/cool_cmp/semantic/visitors/__init__.py +++ b/src/cool_cmp/semantic/visitors/__init__.py @@ -1,3 +1,3 @@ -""" -Semantic visitors +""" +Semantic visitors """ \ No newline at end of file diff --git a/src/cool_cmp/shared/__init__.py b/src/cool_cmp/shared/__init__.py index 0ed021144..01f9d920c 100644 --- a/src/cool_cmp/shared/__init__.py +++ b/src/cool_cmp/shared/__init__.py @@ -1,61 +1,61 @@ -""" -Shared code between packages -""" - -from cool_cmp.shared.pipeline import IPipeable, Pipe, Pipeline -from cool_cmp.shared.errors import ErrorTracker, IErrorTraceable, CoolError -from typing import List - -class ICoolService(IErrorTraceable, IPipeable): - """ - Interface that should implement all parts of the cool pipeline - """ - pass - -class SymbolTable(IErrorTraceable): - """ - Class that should return all Cool pipelines - It holds all the shared information between stages - Defines a last property that returns the last object setted as an atribute - """ - - def __init__(self): - self.last = None - self.__errors = ErrorTracker() - - def __setattr__(self, name:str, value): - if name != "last": - self.last = value - super().__setattr__(name, value) - - def add_error(self, error:CoolError): - self.__errors.add_error(error) - - def get_errors(self)->List[CoolError]: - return self.__errors.get_errors() - - def __str__(self): - result = "SymbolTable:\n" - for attr in self.__dict__.keys(): - result += f" {attr}\n" - return result - - -class InterfacePipeline(Pipeline): - - def __init__(self, pipeline:'InterfacePipeline', *interfaces:List[IPipeable]): - - def make_pipe(result:SymbolTable, interface:ICoolService): - returned = interface(result.last) - for error in interface.get_errors(): - result.add_error(error) - result.__setattr__(interface.name, returned) - return result - - pipes = [] - for interface in interfaces: - pipes.append(Pipe(lambda x, y=interface: make_pipe(x,y))) - if pipeline: - super().__init__(pipeline, *pipes) - else: +""" +Shared code between packages +""" + +from cool_cmp.shared.pipeline import IPipeable, Pipe, Pipeline +from cool_cmp.shared.errors import ErrorTracker, IErrorTraceable, CoolError +from typing import List + +class ICoolService(IErrorTraceable, IPipeable): + """ + Interface that should implement all parts of the cool pipeline + """ + pass + +class SymbolTable(IErrorTraceable): + """ + Class that should return all Cool pipelines + It holds all the shared information between stages + Defines a last property that returns the last object setted as an atribute + """ + + def __init__(self): + self.last = None + self.__errors = ErrorTracker() + + def __setattr__(self, name:str, value): + if name != "last": + self.last = value + super().__setattr__(name, value) + + def add_error(self, error:CoolError): + self.__errors.add_error(error) + + def get_errors(self)->List[CoolError]: + return self.__errors.get_errors() + + def __str__(self): + result = "SymbolTable:\n" + for attr in self.__dict__.keys(): + result += f" {attr}\n" + return result + + +class InterfacePipeline(Pipeline): + + def __init__(self, pipeline:'InterfacePipeline', *interfaces:List[IPipeable]): + + def make_pipe(result:SymbolTable, interface:ICoolService): + returned = interface(result.last) + for error in interface.get_errors(): + result.add_error(error) + result.__setattr__(interface.name, returned) + return result + + pipes = [] + for interface in interfaces: + pipes.append(Pipe(lambda x, y=interface: make_pipe(x,y))) + if pipeline: + super().__init__(pipeline, *pipes) + else: super().__init__(*pipes) \ No newline at end of file diff --git a/src/cool_cmp/shared/ast/__init__.py b/src/cool_cmp/shared/ast/__init__.py index b63d1ffd6..fafc67d0e 100644 --- a/src/cool_cmp/shared/ast/__init__.py +++ b/src/cool_cmp/shared/ast/__init__.py @@ -1,22 +1,22 @@ -""" -AST package -""" - -from cool_cmp.shared.errors import ErrorTracker - -class Node: - """ - Base node for AST - """ - def __init__(self,row:int=None,column:int=None): - self.row = row - self.column = column - -class BaseAST(ErrorTracker): - """ - Base AST class - """ - - def __init__(self, initial_node:Node): - self.node = initial_node - super().__init__() +""" +AST package +""" + +from cool_cmp.shared.errors import ErrorTracker + +class Node: + """ + Base node for AST + """ + def __init__(self,row:int=None,column:int=None): + self.row = row + self.column = column + +class BaseAST(ErrorTracker): + """ + Base AST class + """ + + def __init__(self, initial_node:Node): + self.node = initial_node + super().__init__() diff --git a/src/cool_cmp/shared/ast/cil.py b/src/cool_cmp/shared/ast/cil.py index 562f0be50..51809dafb 100644 --- a/src/cool_cmp/shared/ast/cil.py +++ b/src/cool_cmp/shared/ast/cil.py @@ -1,4 +1,4 @@ -""" -CIL AST nodes -""" -from cool_cmp.shared.ast import Node +""" +CIL AST nodes +""" +from cool_cmp.shared.ast import Node diff --git a/src/cool_cmp/shared/ast/cool.py b/src/cool_cmp/shared/ast/cool.py index ab082a5b3..61a837d91 100644 --- a/src/cool_cmp/shared/ast/cool.py +++ b/src/cool_cmp/shared/ast/cool.py @@ -1,265 +1,265 @@ -""" -Cool AST nodes -""" -from cool_cmp.shared.ast import Node - -class ProgramNode(Node): - def __init__(self, declarations,row=None,column=None): - super().__init__(row,column) - self.declarations = declarations - - def __iter__(self): - for x in self.declarations: - yield from x - -class DeclarationNode(Node): - pass - -class ExpressionNode(Node): - pass - -class ClassDeclarationNode(DeclarationNode): - def __init__(self, idx, features, parent=None,row=None,column=None): - super().__init__(row,column) - self.id = idx - self.parent = parent if parent else 'Object' - self.features = features - - def __iter__(self): - yield self - for x in self.features: - yield from x - -class FuncDeclarationNode(DeclarationNode): - def __init__(self, idx, params, return_type, body,row=None,column=None): - super().__init__(row,column) - self.id = idx - self.params = params - self.type = return_type - self.body = body - - def __iter__(self): - yield self - yield from self.params - yield from self.body - -class AttrDeclarationNode(DeclarationNode): - def __init__(self, idx, typex, expr=None,row=None,column=None): - super().__init__(row,column) - self.id = idx - self.type = typex - self.expr = expr - - def __iter__(self): - yield self - if self.expr: - yield from self.expr - -class ParamNode(DeclarationNode): - def __init__(self, idx, typex, row=None,column=None): - super().__init__(row,column) - self.id = idx - self.type = typex - - def __iter__(self): - yield self - -class SpecialNode(ExpressionNode): - def __init__(self, func,row=None,column=None): - super().__init__(row,column) - self.func = func - - def __iter__(self): - yield self - -class VarDeclarationNode(ExpressionNode): - def __init__(self, idx, typex, expr,row=None,column=None): - super().__init__(row,column) - self.id = idx - self.type = typex - self.expr = expr - - def __iter__(self): - yield self - if self.expr: - yield from self.expr - -class AssignNode(ExpressionNode): - def __init__(self, idx, expr,row=None,column=None): - super().__init__(row,column) - self.id = idx - self.expr = expr - - def __iter__(self): - yield self - if self.expr: - yield from self.expr - -class CallNode(ExpressionNode): - def __init__(self, obj, idx, args,at_type,row=None,column=None): - super().__init__(row,column) - self.obj = obj - self.id = idx - self.args = args - self.at = at_type - - def __iter__(self): - yield from self.obj - for x in self.args: - yield from x - yield self - -class BlockNode(ExpressionNode): - def __init__(self, expr_list,row=None,column=None): - super().__init__(row,column) - self.expr_list = expr_list - - def __iter__(self): - yield self - for x in self.expr_list: - yield from x - -class ConditionalNode(ExpressionNode): - def __init__(self, condition,then_expr,else_expr,row=None,column=None): - super().__init__(row,column) - self.condition = condition - self.then_expr = then_expr - self.else_expr = else_expr - - def get_return_type(self,current_type): - else_type = self.else_expr.type - then_type = self.then_expr.type - return else_type.join(then_type,current_type) - - def __iter__(self): - yield self - for x in [self.condition,self.then_expr,self.else_expr]: - yield from x - -class CheckNode(ExpressionNode): - def __init__(self, idx, typex, expr,row=None,column=None): - super().__init__(row,column) - self.id = idx - self.type = typex - self.expr = expr - - def __iter__(self): - yield self - yield from self.expr - -class LetNode(ExpressionNode): - def __init__(self, dec_var_list, expr,row=None,column=None): - super().__init__(row,column) - self.params = dec_var_list - self.expr = expr - - def __iter__(self): - yield self - for x in self.params: - yield from x - - -class CaseNode(ExpressionNode): - def __init__(self, expr, check_list,row=None,column=None): - super().__init__(row,column) - self.expr = expr - self.params = check_list - - def __iter__(self): - yield self - yield from self.expr - for x in self.params: - yield from x - -class WhileNode(ExpressionNode): - def __init__(self, condition, expr,row=None,column=None): - super().__init__(row,column) - self.condition = condition - self.expr = expr - - def __iter__(self): - yield self - yield self.condition - yield self.expr - -class AtomicNode(ExpressionNode): - def __init__(self, lex,row=None,column=None): - super().__init__(row,column) - self.lex = lex - - def __iter__(self): - yield self - -class UnaryNode(ExpressionNode): - def __init__(self, member,row=None,column=None): - super().__init__(row,column) - self.member = member - - def __iter__(self): - yield self - yield self.member - -class BinaryNode(ExpressionNode): - def __init__(self, left, right,row=None,column=None): - super().__init__(row,column) - self.left = left - self.right = right - - def __iter__(self): - yield self - yield self.left - yield self.right - -class StringNode(AtomicNode): - pass - -class BoolNode(AtomicNode): - pass - -class ConstantNumNode(AtomicNode): - pass - -class VoidNode(AtomicNode): - pass - -class VariableNode(AtomicNode): - pass - -class InstantiateNode(AtomicNode): - pass - -class NotNode(UnaryNode): - pass - -class RoofNode(UnaryNode): - pass - -class IsVoidNode(UnaryNode): - pass - -class PlusNode(BinaryNode): - pass - -class MinusNode(BinaryNode): - pass - -class StarNode(BinaryNode): - pass - -class DivNode(BinaryNode): - pass - -class EqualNode(BinaryNode): - pass - -class GreaterNode(BinaryNode): - pass - -class GreaterEqualNode(BinaryNode): - pass - -class LesserNode(BinaryNode): - pass - -class LesserEqualNode(BinaryNode): +""" +Cool AST nodes +""" +from cool_cmp.shared.ast import Node + +class ProgramNode(Node): + def __init__(self, declarations,row=None,column=None): + super().__init__(row,column) + self.declarations = declarations + + def __iter__(self): + for x in self.declarations: + yield from x + +class DeclarationNode(Node): + pass + +class ExpressionNode(Node): + pass + +class ClassDeclarationNode(DeclarationNode): + def __init__(self, idx, features, parent=None,row=None,column=None): + super().__init__(row,column) + self.id = idx + self.parent = parent if parent else 'Object' + self.features = features + + def __iter__(self): + yield self + for x in self.features: + yield from x + +class FuncDeclarationNode(DeclarationNode): + def __init__(self, idx, params, return_type, body,row=None,column=None): + super().__init__(row,column) + self.id = idx + self.params = params + self.type = return_type + self.body = body + + def __iter__(self): + yield self + yield from self.params + yield from self.body + +class AttrDeclarationNode(DeclarationNode): + def __init__(self, idx, typex, expr=None,row=None,column=None): + super().__init__(row,column) + self.id = idx + self.type = typex + self.expr = expr + + def __iter__(self): + yield self + if self.expr: + yield from self.expr + +class ParamNode(DeclarationNode): + def __init__(self, idx, typex, row=None,column=None): + super().__init__(row,column) + self.id = idx + self.type = typex + + def __iter__(self): + yield self + +class SpecialNode(ExpressionNode): + def __init__(self, func,row=None,column=None): + super().__init__(row,column) + self.func = func + + def __iter__(self): + yield self + +class VarDeclarationNode(ExpressionNode): + def __init__(self, idx, typex, expr,row=None,column=None): + super().__init__(row,column) + self.id = idx + self.type = typex + self.expr = expr + + def __iter__(self): + yield self + if self.expr: + yield from self.expr + +class AssignNode(ExpressionNode): + def __init__(self, idx, expr,row=None,column=None): + super().__init__(row,column) + self.id = idx + self.expr = expr + + def __iter__(self): + yield self + if self.expr: + yield from self.expr + +class CallNode(ExpressionNode): + def __init__(self, obj, idx, args,at_type,row=None,column=None): + super().__init__(row,column) + self.obj = obj + self.id = idx + self.args = args + self.at = at_type + + def __iter__(self): + yield from self.obj + for x in self.args: + yield from x + yield self + +class BlockNode(ExpressionNode): + def __init__(self, expr_list,row=None,column=None): + super().__init__(row,column) + self.expr_list = expr_list + + def __iter__(self): + yield self + for x in self.expr_list: + yield from x + +class ConditionalNode(ExpressionNode): + def __init__(self, condition,then_expr,else_expr,row=None,column=None): + super().__init__(row,column) + self.condition = condition + self.then_expr = then_expr + self.else_expr = else_expr + + def get_return_type(self,current_type): + else_type = self.else_expr.type + then_type = self.then_expr.type + return else_type.join(then_type,current_type) + + def __iter__(self): + yield self + for x in [self.condition,self.then_expr,self.else_expr]: + yield from x + +class CheckNode(ExpressionNode): + def __init__(self, idx, typex, expr,row=None,column=None): + super().__init__(row,column) + self.id = idx + self.type = typex + self.expr = expr + + def __iter__(self): + yield self + yield from self.expr + +class LetNode(ExpressionNode): + def __init__(self, dec_var_list, expr,row=None,column=None): + super().__init__(row,column) + self.params = dec_var_list + self.expr = expr + + def __iter__(self): + yield self + for x in self.params: + yield from x + + +class CaseNode(ExpressionNode): + def __init__(self, expr, check_list,row=None,column=None): + super().__init__(row,column) + self.expr = expr + self.params = check_list + + def __iter__(self): + yield self + yield from self.expr + for x in self.params: + yield from x + +class WhileNode(ExpressionNode): + def __init__(self, condition, expr,row=None,column=None): + super().__init__(row,column) + self.condition = condition + self.expr = expr + + def __iter__(self): + yield self + yield self.condition + yield self.expr + +class AtomicNode(ExpressionNode): + def __init__(self, lex,row=None,column=None): + super().__init__(row,column) + self.lex = lex + + def __iter__(self): + yield self + +class UnaryNode(ExpressionNode): + def __init__(self, member,row=None,column=None): + super().__init__(row,column) + self.member = member + + def __iter__(self): + yield self + yield self.member + +class BinaryNode(ExpressionNode): + def __init__(self, left, right,row=None,column=None): + super().__init__(row,column) + self.left = left + self.right = right + + def __iter__(self): + yield self + yield self.left + yield self.right + +class StringNode(AtomicNode): + pass + +class BoolNode(AtomicNode): + pass + +class ConstantNumNode(AtomicNode): + pass + +class VoidNode(AtomicNode): + pass + +class VariableNode(AtomicNode): + pass + +class InstantiateNode(AtomicNode): + pass + +class NotNode(UnaryNode): + pass + +class RoofNode(UnaryNode): + pass + +class IsVoidNode(UnaryNode): + pass + +class PlusNode(BinaryNode): + pass + +class MinusNode(BinaryNode): + pass + +class StarNode(BinaryNode): + pass + +class DivNode(BinaryNode): + pass + +class EqualNode(BinaryNode): + pass + +class GreaterNode(BinaryNode): + pass + +class GreaterEqualNode(BinaryNode): + pass + +class LesserNode(BinaryNode): + pass + +class LesserEqualNode(BinaryNode): pass \ No newline at end of file diff --git a/src/cool_cmp/shared/ast/mips.py b/src/cool_cmp/shared/ast/mips.py index ccac1af73..00e6ad3dc 100644 --- a/src/cool_cmp/shared/ast/mips.py +++ b/src/cool_cmp/shared/ast/mips.py @@ -1,4 +1,4 @@ -""" -Mips AST Nodes -""" -from cool_cmp.shared.ast import Node +""" +Mips AST Nodes +""" +from cool_cmp.shared.ast import Node diff --git a/src/cool_cmp/shared/errors.py b/src/cool_cmp/shared/errors.py index e26853663..2358d9ec7 100644 --- a/src/cool_cmp/shared/errors.py +++ b/src/cool_cmp/shared/errors.py @@ -1,42 +1,46 @@ -from typing import List -class CoolError: - """ - Base Cool Error - """ - - def __init__(self, msg:str): - self.error = msg - - def print_error(self): - print(self.error) - - def __str__(self): - return f"Error: {self.error}" - - def __repr__(self): - return str(self) - -class IErrorTraceable: - """ - Interface for error tracking system - """ - - def add_error(self, error:CoolError): - raise NotImplementedError() - - def get_errors(self)->List[CoolError]: - raise NotImplementedError() - -class ErrorTracker(IErrorTraceable): - """ - Basic Error tracking system - """ - - def __init__(self): - self.__errors = [] - - def add_error(self, error:CoolError): - self.__errors.append(error) - - def get_errors(self)->List[CoolError]: +from typing import List +class CoolError: + """ + Base Cool Error + """ + + ERROR_TYPE = "CoolError" + + FORMAT = "{}: {}" + + def __init__(self, msg:str): + self.error = msg + + def print_error(self): + print(str(self)) + + def __str__(self): + return self.FORMAT.format(self.ERROR_TYPE, self.error) + + def __repr__(self): + return str(self) + +class IErrorTraceable: + """ + Interface for error tracking system + """ + + def add_error(self, error:CoolError): + raise NotImplementedError() + + def get_errors(self)->List[CoolError]: + raise NotImplementedError() + +class ErrorTracker(IErrorTraceable): + """ + Basic Error tracking system + """ + + def __init__(self): + self.__errors = [] + + def add_error(self, error:CoolError): + self.__errors.append(error) + + def get_errors(self)->List[CoolError]: return self.__errors \ No newline at end of file diff --git a/src/cool_cmp/shared/pipeline/__init__.py b/src/cool_cmp/shared/pipeline/__init__.py index e292662eb..a2bd104f2 100644 --- a/src/cool_cmp/shared/pipeline/__init__.py +++ b/src/cool_cmp/shared/pipeline/__init__.py @@ -1,15 +1,15 @@ -""" -Cool pipes package -""" - -from typing import List -from cool_cmp.shared.pipeline.pipes import * - -class IPipeable: - - @property - def name(self)->str: - raise NotImplementedError() - - def __call__(self, arg): - raise NotImplementedError() +""" +Cool pipes package +""" + +from typing import List +from cool_cmp.shared.pipeline.pipes import * + +class IPipeable: + + @property + def name(self)->str: + raise NotImplementedError() + + def __call__(self, arg): + raise NotImplementedError() diff --git a/src/cool_cmp/shared/pipeline/pipes.py b/src/cool_cmp/shared/pipeline/pipes.py index c72ec43b9..bcc63b7c2 100644 --- a/src/cool_cmp/shared/pipeline/pipes.py +++ b/src/cool_cmp/shared/pipeline/pipes.py @@ -1,39 +1,39 @@ -from typing import Callable - -class Pipe: - def __init__(self, pipe_func:Callable[[object],object], pipe_check:Callable[[object],bool]=None): - """ - pipe_func: callable that recieve an argument and return a value - pipe_check: callable that recieve an argument and return if the pipe can be executed - """ - self.func = pipe_func - if pipe_check: - self.check = pipe_check - else: - self.check = lambda x: True - - def __call__(self, *args, **kwargs): - return self.func(*args, **kwargs) - - def can_execute(self, dictionary): - return self.check(dictionary) - -class Pipeline(Pipe): - - def __init__(self, *pipes): - super().__init__(self.__call__) - self.pipes = [pipe for pipe in pipes] - - def __call__(self, *args, **kwargs): - start_pipe = self.pipes[0] - - result = start_pipe(*args, **kwargs) - - for pipe in self.pipes[1:]: - if pipe.can_execute(result): - result = pipe(result) - else: - break - - return result - +from typing import Callable + +class Pipe: + def __init__(self, pipe_func:Callable[[object],object], pipe_check:Callable[[object],bool]=None): + """ + pipe_func: callable that recieve an argument and return a value + pipe_check: callable that recieve an argument and return if the pipe can be executed + """ + self.func = pipe_func + if pipe_check: + self.check = pipe_check + else: + self.check = lambda x: True + + def __call__(self, *args, **kwargs): + return self.func(*args, **kwargs) + + def can_execute(self, dictionary): + return self.check(dictionary) + +class Pipeline(Pipe): + + def __init__(self, *pipes): + super().__init__(self.__call__) + self.pipes = [pipe for pipe in pipes] + + def __call__(self, *args, **kwargs): + start_pipe = self.pipes[0] + + result = start_pipe(*args, **kwargs) + + for pipe in self.pipes[1:]: + if pipe.can_execute(result): + result = pipe(result) + else: + break + + return result + diff --git a/src/cool_cmp/shared/token.py b/src/cool_cmp/shared/token.py index 879df7de9..5f9170675 100644 --- a/src/cool_cmp/shared/token.py +++ b/src/cool_cmp/shared/token.py @@ -1,30 +1,30 @@ -from typing import Tuple - -class ICoolToken: - """ - Cool Token Interface - """ - - def set_lex(self, lex:str): - raise NotImplementedError() - - def get_lex(self)->str: - raise NotImplementedError() - - def set_type(self, typex:str): - raise NotImplementedError() - - def get_type(self)->str: - raise NotImplementedError() - - def set_position(self, line:int, column:int): - raise NotImplementedError - - def get_position(self)->Tuple[int,int]: - raise NotImplementedError - - def __str__(self): - return f"{self.get_lex()}:{self.get_type()} Line:{self.get_position()[0]} Column:{self.get_position()[1]}" - - def __repr__(self): +from typing import Tuple + +class ICoolToken: + """ + Cool Token Interface + """ + + def set_lex(self, lex:str): + raise NotImplementedError() + + def get_lex(self)->str: + raise NotImplementedError() + + def set_type(self, typex:str): + raise NotImplementedError() + + def get_type(self)->str: + raise NotImplementedError() + + def set_position(self, line:int, column:int): + raise NotImplementedError + + def get_position(self)->Tuple[int,int]: + raise NotImplementedError + + def __str__(self): + return f"{self.get_lex()}:{self.get_type()} Line:{self.get_position()[0]} Column:{self.get_position()[1]}" + + def __repr__(self): return str(self) \ No newline at end of file diff --git a/src/coolc.sh b/src/coolc.sh index 3088de4f9..ea81da9b5 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 "WLD CoolCompiler v0.0" +echo "Copyright (c) 2019: Luis Ernesto, Damián, Luis Enrique" # Llamar al compilador -echo "Compiling $INPUT_FILE into $OUTPUT_FILE" +# echo "Compiling $INPUT_FILE into $OUTPUT_FILE" + +python main.py $INPUT_FILE $OUTPUT_FILE diff --git a/src/main.py b/src/main.py index 227a92dd8..38b2f6095 100644 --- a/src/main.py +++ b/src/main.py @@ -1,249 +1,264 @@ -from cool_cmp.lexer.interface import ILexer -from cool_cmp.lexer import LexerPipeline -from cool_cmp.parser.interface import IParser -from cool_cmp.parser import ParserPipeline -from cool_cmp.semantic.interface import ISemantic -from cool_cmp.semantic import SemanticPipeline -from cool_cmp.cil.interface import ICil -from cool_cmp.cil import CilPipeline -from cool_cmp.mips.interface import IMips -from cool_cmp.mips import MipsPipeline -from cool_cmp.shared.token import ICoolToken -from cool_cmp.shared.ast import BaseAST -from cool_cmp.shared.ast.cool import ClassDeclarationNode, ProgramNode -from cool_cmp.lexer.lexer import PlyLexer -from cool_cmp.shared.errors import CoolError -from typing import List, Tuple - -# Mock implementations - -class MockLexer(ILexer): - """ - Mock implementation of lexer - """ - program = "class ` \n\tCoolClass\n{}" - class MockToken(ICoolToken): - def set_lex(self, lex:str): - self.lex = lex - - def set_type(self, typex:str): - self.typex = typex - - def set_position(self, line:int, column:int): - self.line = line - self.column = column - - def get_lex(self)->str: - return self.lex - - def get_type(self)->str: - return self.typex - - def get_position(self)->Tuple[int,int]: - return (self.line, self.column) - - @property - def mock_tokens(self)->List[ICoolToken]: - info = [ - ("class", "CLASS", 0, 0), - ("CoolClass", "ID", 1, 0), - ("{", "OCUR", 2, 0), - ("}", "CCUR", 2, 1), - ] - tokens = [] - for lex, typex, line, col in info: - token = MockLexer.MockToken() - token.set_lex(lex); token.set_type(typex); token.set_position(line, col) - tokens.append(token) - return tokens - - - @property - def name(self)->str: - return "lexer" - - errors = [] - - def add_error(self, error:CoolError): - self.errors.append(error) - - def get_errors(self)->List[CoolError]: - return self.errors - - def __call__(self, program_string:str) -> List[ICoolToken]: - print("Tokenizing program:\n", program_string) - return self.mock_tokens - -class MockParser(IParser): - """ - Mock implementation of parser - """ - - @property - def name(self)->str: - return "parser" - - def __call__(self, tokens:List[ICoolToken]) -> BaseAST: - print("Building AST from tokens",tokens) - class_token = MockLexer().mock_tokens - class_token = class_token[0] - return BaseAST(ProgramNode([ClassDeclarationNode(class_token.lex, [], None, class_token.line, class_token.column)])) - - errors = [] - - def add_error(self, error:CoolError): - self.errors.append(error) - - def get_errors(self)->List[CoolError]: - return self.errors - -class MockSemantic1(ISemantic): - """ - Mock Semantic interface 1 - """ - - @property - def name(self)->str: - return "semantic1" - - def __call__(self, ast:BaseAST) -> BaseAST: - print("Doing some crazy shit with ast, apply visitor, etc") - print("Collecting Types...") - print("Building Types...") - print("Type Checking...") - print("Setted mock1 property to true as a way of communicate between pipes") - ast.mock1 = True - return ast - - errors = [] - - def add_error(self, error:CoolError): - self.errors.append(error) - - def get_errors(self)->List[CoolError]: - return self.errors - -class MockSemantic2(ISemantic): - """ - Mock Semantic interface 2 - """ - - @property - def name(self)->str: - return "semantic2" - - def __call__(self, ast:BaseAST) -> BaseAST: - print("Doing another crazy shit with previously returned ast") - print("Verifying that ast has mock1 property", hasattr(ast, "mock1")) - print("Converting COOL AST into CIL AST...") - return ast - - errors = [] - - def add_error(self, error:CoolError): - self.errors.append(error) - - def get_errors(self)->List[CoolError]: - return self.errors - -class MockCil1(ICil): - """ - Mock Cil interface 1 - """ - - @property - def name(self)->str: - return "cil1" - - def __call__(self, ast:BaseAST) -> BaseAST: - print("Optimizing CIL AST...") - return ast - - errors = [] - - def add_error(self, error:CoolError): - self.errors.append(error) - - def get_errors(self)->List[CoolError]: - return self.errors - -class MockCil2(ICil): - """ - Mock Cil interface 2 - """ - - @property - def name(self)->str: - print("Converting CIL AST into MIPS AST...") - return "cil2" - - def __call__(self, ast:BaseAST) -> BaseAST: - return ast - - errors = [] - - def add_error(self, error:CoolError): - self.errors.append(error) - - def get_errors(self)->List[CoolError]: - return self.errors - -class MockMips1(IMips): - """ - Mock MIPS interface 1 - """ - - @property - def name(self)->str: - return "mips1" - - def __call__(self, ast:BaseAST) -> BaseAST: - print("Doing MIPS things...") - return ast - - errors = [] - - def add_error(self, error:CoolError): - self.errors.append(error) - - def get_errors(self)->List[CoolError]: - return self.errors - -class MockMips2(IMips): - """ - Mock MIPS interface 2 - """ - - @property - def name(self)->str: - return "mips2" - - def __call__(self, ast:BaseAST) -> BaseAST: - print("Generating MIPS code...") - return ast - - errors = [] - - def add_error(self, error:CoolError): - self.errors.append(error) - - def get_errors(self)->List[CoolError]: - return self.errors - -# Testing pipeline - -pipe = LexerPipeline(MockLexer()) - -# pipe = LexerPipeline(PlyLexer()) - -pipe = ParserPipeline(pipe, MockParser()) - -pipe = SemanticPipeline(pipe, MockSemantic1(), MockSemantic2()) - -pipe = CilPipeline(pipe, MockCil1(), MockCil2()) - -pipe = MipsPipeline(pipe, MockMips1(), MockMips2()) - -result = pipe(MockLexer.program) - -print(result.get_errors()) \ No newline at end of file +from cool_cmp.lexer.interface import ILexer +from cool_cmp.lexer import LexerPipeline +from cool_cmp.parser.interface import IParser +from cool_cmp.parser import ParserPipeline +from cool_cmp.semantic.interface import ISemantic +from cool_cmp.semantic import SemanticPipeline +from cool_cmp.cil.interface import ICil +from cool_cmp.cil import CilPipeline +from cool_cmp.mips.interface import IMips +from cool_cmp.mips import MipsPipeline +from cool_cmp.shared.token import ICoolToken +from cool_cmp.shared.ast import BaseAST +from cool_cmp.shared.ast.cool import ClassDeclarationNode, ProgramNode +from cool_cmp.lexer.lexer import PlyLexer +from cool_cmp.shared.errors import CoolError +from typing import List, Tuple + +# Mock implementations + +class MockLexer(ILexer): + """ + Mock implementation of lexer + """ + program = "class ` \n\tCoolClass\n{}" + class MockToken(ICoolToken): + def set_lex(self, lex:str): + self.lex = lex + + def set_type(self, typex:str): + self.typex = typex + + def set_position(self, line:int, column:int): + self.line = line + self.column = column + + def get_lex(self)->str: + return self.lex + + def get_type(self)->str: + return self.typex + + def get_position(self)->Tuple[int,int]: + return (self.line, self.column) + + @property + def mock_tokens(self)->List[ICoolToken]: + info = [ + ("class", "CLASS", 0, 0), + ("CoolClass", "ID", 1, 0), + ("{", "OCUR", 2, 0), + ("}", "CCUR", 2, 1), + ] + tokens = [] + for lex, typex, line, col in info: + token = MockLexer.MockToken() + token.set_lex(lex); token.set_type(typex); token.set_position(line, col) + tokens.append(token) + return tokens + + + @property + def name(self)->str: + return "lexer" + + errors = [] + + def add_error(self, error:CoolError): + self.errors.append(error) + + def get_errors(self)->List[CoolError]: + return self.errors + + def __call__(self, program_string:str) -> List[ICoolToken]: + print("Tokenizing program:\n", program_string) + return self.mock_tokens + +class MockParser(IParser): + """ + Mock implementation of parser + """ + + @property + def name(self)->str: + return "parser" + + def __call__(self, tokens:List[ICoolToken]) -> BaseAST: + print("Building AST from tokens",tokens) + class_token = MockLexer().mock_tokens + class_token = class_token[0] + return BaseAST(ProgramNode([ClassDeclarationNode(class_token.lex, [], None, class_token.line, class_token.column)])) + + errors = [] + + def add_error(self, error:CoolError): + self.errors.append(error) + + def get_errors(self)->List[CoolError]: + return self.errors + +class MockSemantic1(ISemantic): + """ + Mock Semantic interface 1 + """ + + @property + def name(self)->str: + return "semantic1" + + def __call__(self, ast:BaseAST) -> BaseAST: + print("Doing some crazy shit with ast, apply visitor, etc") + print("Collecting Types...") + print("Building Types...") + print("Type Checking...") + print("Setted mock1 property to true as a way of communicate between pipes") + ast.mock1 = True + return ast + + errors = [] + + def add_error(self, error:CoolError): + self.errors.append(error) + + def get_errors(self)->List[CoolError]: + return self.errors + +class MockSemantic2(ISemantic): + """ + Mock Semantic interface 2 + """ + + @property + def name(self)->str: + return "semantic2" + + def __call__(self, ast:BaseAST) -> BaseAST: + print("Doing another crazy shit with previously returned ast") + print("Verifying that ast has mock1 property", hasattr(ast, "mock1")) + print("Converting COOL AST into CIL AST...") + return ast + + errors = [] + + def add_error(self, error:CoolError): + self.errors.append(error) + + def get_errors(self)->List[CoolError]: + return self.errors + +class MockCil1(ICil): + """ + Mock Cil interface 1 + """ + + @property + def name(self)->str: + return "cil1" + + def __call__(self, ast:BaseAST) -> BaseAST: + print("Optimizing CIL AST...") + return ast + + errors = [] + + def add_error(self, error:CoolError): + self.errors.append(error) + + def get_errors(self)->List[CoolError]: + return self.errors + +class MockCil2(ICil): + """ + Mock Cil interface 2 + """ + + @property + def name(self)->str: + print("Converting CIL AST into MIPS AST...") + return "cil2" + + def __call__(self, ast:BaseAST) -> BaseAST: + return ast + + errors = [] + + def add_error(self, error:CoolError): + self.errors.append(error) + + def get_errors(self)->List[CoolError]: + return self.errors + +class MockMips1(IMips): + """ + Mock MIPS interface 1 + """ + + @property + def name(self)->str: + return "mips1" + + def __call__(self, ast:BaseAST) -> BaseAST: + print("Doing MIPS things...") + return ast + + errors = [] + + def add_error(self, error:CoolError): + self.errors.append(error) + + def get_errors(self)->List[CoolError]: + return self.errors + +class MockMips2(IMips): + """ + Mock MIPS interface 2 + """ + + @property + def name(self)->str: + return "mips2" + + def __call__(self, ast:BaseAST) -> BaseAST: + print("Generating MIPS code...") + return ast + + errors = [] + + def add_error(self, error:CoolError): + self.errors.append(error) + + def get_errors(self)->List[CoolError]: + return self.errors + +# Testing pipeline + +# pipe = LexerPipeline(MockLexer()) + +# # pipe = LexerPipeline(PlyLexer()) + +# pipe = ParserPipeline(pipe, MockParser()) + +# pipe = SemanticPipeline(pipe, MockSemantic1(), MockSemantic2()) + +# pipe = CilPipeline(pipe, MockCil1(), MockCil2()) + +# pipe = MipsPipeline(pipe, MockMips1(), MockMips2()) + +# result = pipe(MockLexer.program) + +# print(result.get_errors()) + +if __name__ == "__main__": + import sys + with open(sys.argv[1]) as file: + program = file.read() + pipe = LexerPipeline(PlyLexer()) + result = pipe(program) + for err in result.get_errors(): + err.print_error() + if result.get_errors(): + exit(1) + with open(sys.argv[2], 'w') as file: + file.write("GENERATED MIPS") + + exit(0) \ No newline at end of file diff --git a/src/makefile b/src/makefile index 30df993f5..cd83fb352 100644 --- a/src/makefile +++ b/src/makefile @@ -1,12 +1,12 @@ -.PHONY: clean - -main: - # Compiling the compiler :) - -clean: - rm -rf build/* - rm -rf ../tests/*/*.mips - -test: - pytest ../tests -v --tb=short -m=${TAG} - +.PHONY: clean + +main: + # Compiling the compiler :) + +clean: + rm -rf build/* + rm -rf ../tests/*/*.mips + +test: + pytest ../tests -v --tb=short -m=${TAG} + diff --git a/tests/codegen/arith.cl b/tests/codegen/arith.cl index af5951cf7..0d9f5dd33 100755 --- a/tests/codegen/arith.cl +++ b/tests/codegen/arith.cl @@ -1,430 +1,430 @@ -(* - * A contribution from Anne Sheets (sheets@cory) - * - * Tests the arithmetic operations and various other things - *) - -class A { - - var : Int <- 0; - - value() : Int { var }; - - set_var(num : Int) : A{ - { - var <- num; - self; - } - }; - - method1(num : Int) : A { -- same - self - }; - - method2(num1 : Int, num2 : Int) : A { -- plus - (let x : Int in - { - x <- num1 + num2; - (new B).set_var(x); - } - ) - }; - - method3(num : Int) : A { -- negate - (let x : Int in - { - x <- ~num; - (new C).set_var(x); - } - ) - }; - - method4(num1 : Int, num2 : Int) : A { -- diff - if num2 < num1 then - (let x : Int in - { - x <- num1 - num2; - (new D).set_var(x); - } - ) - else - (let x : Int in - { - x <- num2 - num1; - (new D).set_var(x); - } - ) - fi - }; - - method5(num : Int) : A { -- factorial - (let x : Int <- 1 in - { - (let y : Int <- 1 in - while y <= num loop - { - x <- x * y; - y <- y + 1; - } - pool - ); - (new E).set_var(x); - } - ) - }; - -}; - -class B inherits A { -- B is a number squared - - method5(num : Int) : A { -- square - (let x : Int in - { - x <- num * num; - (new E).set_var(x); - } - ) - }; - -}; - -class C inherits B { - - method6(num : Int) : A { -- negate - (let x : Int in - { - x <- ~num; - (new A).set_var(x); - } - ) - }; - - method5(num : Int) : A { -- cube - (let x : Int in - { - x <- num * num * num; - (new E).set_var(x); - } - ) - }; - -}; - -class D inherits B { - - method7(num : Int) : Bool { -- divisible by 3 - (let x : Int <- num in - if x < 0 then method7(~x) else - if 0 = x then true else - if 1 = x then false else - if 2 = x then false else - method7(x - 3) - fi fi fi fi - ) - }; - -}; - -class E inherits D { - - method6(num : Int) : A { -- division - (let x : Int in - { - x <- num / 8; - (new A).set_var(x); - } - ) - }; - -}; - -(* The following code is from atoi.cl in ~cs164/examples *) - -(* - The class A2I provides integer-to-string and string-to-integer -conversion routines. To use these routines, either inherit them -in the class where needed, have a dummy variable bound to -something of type A2I, or simpl write (new A2I).method(argument). -*) - - -(* - c2i Converts a 1-character string to an integer. Aborts - if the string is not "0" through "9" -*) -class A2I { - - c2i(char : String) : Int { - if char = "0" then 0 else - if char = "1" then 1 else - if char = "2" then 2 else - if char = "3" then 3 else - if char = "4" then 4 else - if char = "5" then 5 else - if char = "6" then 6 else - if char = "7" then 7 else - if char = "8" then 8 else - if char = "9" then 9 else - { abort(); 0; } (* the 0 is needed to satisfy the - typchecker *) - fi fi fi fi fi fi fi fi fi fi - }; - -(* - i2c is the inverse of c2i. -*) - i2c(i : Int) : String { - if i = 0 then "0" else - if i = 1 then "1" else - if i = 2 then "2" else - if i = 3 then "3" else - if i = 4 then "4" else - if i = 5 then "5" else - if i = 6 then "6" else - if i = 7 then "7" else - if i = 8 then "8" else - if i = 9 then "9" else - { abort(); ""; } -- the "" is needed to satisfy the typchecker - fi fi fi fi fi fi fi fi fi fi - }; - -(* - a2i converts an ASCII string into an integer. The empty string -is converted to 0. Signed and unsigned strings are handled. The -method aborts if the string does not represent an integer. Very -long strings of digits produce strange answers because of arithmetic -overflow. - -*) - a2i(s : String) : Int { - if s.length() = 0 then 0 else - if s.substr(0,1) = "-" then ~a2i_aux(s.substr(1,s.length()-1)) else - if s.substr(0,1) = "+" then a2i_aux(s.substr(1,s.length()-1)) else - a2i_aux(s) - fi fi fi - }; - -(* a2i_aux converts the usigned portion of the string. As a - programming example, this method is written iteratively. *) - - - a2i_aux(s : String) : Int { - (let int : Int <- 0 in - { - (let j : Int <- s.length() in - (let i : Int <- 0 in - while i < j loop - { - int <- int * 10 + c2i(s.substr(i,1)); - i <- i + 1; - } - pool - ) - ); - int; - } - ) - }; - -(* i2a converts an integer to a string. Positive and negative - numbers are handled correctly. *) - - i2a(i : Int) : String { - if i = 0 then "0" else - if 0 < i then i2a_aux(i) else - "-".concat(i2a_aux(i * ~1)) - fi fi - }; - -(* i2a_aux is an example using recursion. *) - - i2a_aux(i : Int) : String { - if i = 0 then "" else - (let next : Int <- i / 10 in - i2a_aux(next).concat(i2c(i - next * 10)) - ) - fi - }; - -}; - -class Main inherits IO { - - char : String; - avar : A; - a_var : A; - flag : Bool <- true; - - - menu() : String { - { - out_string("\n\tTo add a number to "); - print(avar); - out_string("...enter a:\n"); - out_string("\tTo negate "); - print(avar); - out_string("...enter b:\n"); - out_string("\tTo find the difference between "); - print(avar); - out_string("and another number...enter c:\n"); - out_string("\tTo find the factorial of "); - print(avar); - out_string("...enter d:\n"); - out_string("\tTo square "); - print(avar); - out_string("...enter e:\n"); - out_string("\tTo cube "); - print(avar); - out_string("...enter f:\n"); - out_string("\tTo find out if "); - print(avar); - out_string("is a multiple of 3...enter g:\n"); - out_string("\tTo divide "); - print(avar); - out_string("by 8...enter h:\n"); - out_string("\tTo get a new number...enter j:\n"); - out_string("\tTo quit...enter q:\n\n"); - in_string(); - } - }; - - prompt() : String { - { - out_string("\n"); - out_string("Please enter a number... "); - in_string(); - } - }; - - get_int() : Int { - { - (let z : A2I <- new A2I in - (let s : String <- prompt() in - z.a2i(s) - ) - ); - } - }; - - is_even(num : Int) : Bool { - (let x : Int <- num in - if x < 0 then is_even(~x) else - if 0 = x then true else - if 1 = x then false else - is_even(x - 2) - fi fi fi - ) - }; - - class_type(var : A) : IO { - case var of - a : A => out_string("Class type is now A\n"); - b : B => out_string("Class type is now B\n"); - c : C => out_string("Class type is now C\n"); - d : D => out_string("Class type is now D\n"); - e : E => out_string("Class type is now E\n"); - o : Object => out_string("Oooops\n"); - esac - }; - - print(var : A) : IO { - (let z : A2I <- new A2I in - { - out_string(z.i2a(var.value())); - out_string(" "); - } - ) - }; - - main() : Object { - { - avar <- (new A); - while flag loop - { - -- avar <- (new A).set_var(get_int()); - out_string("number "); - print(avar); - if is_even(avar.value()) then - out_string("is even!\n") - else - out_string("is odd!\n") - fi; - -- print(avar); -- prints out answer - class_type(avar); - char <- menu(); - if char = "a" then -- add - { - a_var <- (new A).set_var(get_int()); - avar <- (new B).method2(avar.value(), a_var.value()); - } else - if char = "b" then -- negate - case avar of - c : C => avar <- c.method6(c.value()); - a : A => avar <- a.method3(a.value()); - o : Object => { - out_string("Oooops\n"); - abort(); 0; - }; - esac else - if char = "c" then -- diff - { - a_var <- (new A).set_var(get_int()); - avar <- (new D).method4(avar.value(), a_var.value()); - } else - if char = "d" then avar <- (new C)@A.method5(avar.value()) else - -- factorial - if char = "e" then avar <- (new C)@B.method5(avar.value()) else - -- square - if char = "f" then avar <- (new C)@C.method5(avar.value()) else - -- cube - if char = "g" then -- multiple of 3? - if ((new D).method7(avar.value())) - then -- avar <- (new A).method1(avar.value()) - { - out_string("number "); - print(avar); - out_string("is divisible by 3.\n"); - } - else -- avar <- (new A).set_var(0) - { - out_string("number "); - print(avar); - out_string("is not divisible by 3.\n"); - } - fi else - if char = "h" then - (let x : A in - { - x <- (new E).method6(avar.value()); - (let r : Int <- (avar.value() - (x.value() * 8)) in - { - out_string("number "); - print(avar); - out_string("is equal to "); - print(x); - out_string("times 8 with a remainder of "); - (let a : A2I <- new A2I in - { - out_string(a.i2a(r)); - out_string("\n"); - } - ); -- end let a: - } - ); -- end let r: - avar <- x; - } - ) -- end let x: - else - if char = "j" then avar <- (new A) - else - if char = "q" then flag <- false - else - avar <- (new A).method1(avar.value()) -- divide/8 - fi fi fi fi fi fi fi fi fi fi; - } - pool; - } - }; - -}; - +(* + * A contribution from Anne Sheets (sheets@cory) + * + * Tests the arithmetic operations and various other things + *) + +class A { + + var : Int <- 0; + + value() : Int { var }; + + set_var(num : Int) : A{ + { + var <- num; + self; + } + }; + + method1(num : Int) : A { -- same + self + }; + + method2(num1 : Int, num2 : Int) : A { -- plus + (let x : Int in + { + x <- num1 + num2; + (new B).set_var(x); + } + ) + }; + + method3(num : Int) : A { -- negate + (let x : Int in + { + x <- ~num; + (new C).set_var(x); + } + ) + }; + + method4(num1 : Int, num2 : Int) : A { -- diff + if num2 < num1 then + (let x : Int in + { + x <- num1 - num2; + (new D).set_var(x); + } + ) + else + (let x : Int in + { + x <- num2 - num1; + (new D).set_var(x); + } + ) + fi + }; + + method5(num : Int) : A { -- factorial + (let x : Int <- 1 in + { + (let y : Int <- 1 in + while y <= num loop + { + x <- x * y; + y <- y + 1; + } + pool + ); + (new E).set_var(x); + } + ) + }; + +}; + +class B inherits A { -- B is a number squared + + method5(num : Int) : A { -- square + (let x : Int in + { + x <- num * num; + (new E).set_var(x); + } + ) + }; + +}; + +class C inherits B { + + method6(num : Int) : A { -- negate + (let x : Int in + { + x <- ~num; + (new A).set_var(x); + } + ) + }; + + method5(num : Int) : A { -- cube + (let x : Int in + { + x <- num * num * num; + (new E).set_var(x); + } + ) + }; + +}; + +class D inherits B { + + method7(num : Int) : Bool { -- divisible by 3 + (let x : Int <- num in + if x < 0 then method7(~x) else + if 0 = x then true else + if 1 = x then false else + if 2 = x then false else + method7(x - 3) + fi fi fi fi + ) + }; + +}; + +class E inherits D { + + method6(num : Int) : A { -- division + (let x : Int in + { + x <- num / 8; + (new A).set_var(x); + } + ) + }; + +}; + +(* The following code is from atoi.cl in ~cs164/examples *) + +(* + The class A2I provides integer-to-string and string-to-integer +conversion routines. To use these routines, either inherit them +in the class where needed, have a dummy variable bound to +something of type A2I, or simpl write (new A2I).method(argument). +*) + + +(* + c2i Converts a 1-character string to an integer. Aborts + if the string is not "0" through "9" +*) +class A2I { + + c2i(char : String) : Int { + if char = "0" then 0 else + if char = "1" then 1 else + if char = "2" then 2 else + if char = "3" then 3 else + if char = "4" then 4 else + if char = "5" then 5 else + if char = "6" then 6 else + if char = "7" then 7 else + if char = "8" then 8 else + if char = "9" then 9 else + { abort(); 0; } (* the 0 is needed to satisfy the + typchecker *) + fi fi fi fi fi fi fi fi fi fi + }; + +(* + i2c is the inverse of c2i. +*) + i2c(i : Int) : String { + if i = 0 then "0" else + if i = 1 then "1" else + if i = 2 then "2" else + if i = 3 then "3" else + if i = 4 then "4" else + if i = 5 then "5" else + if i = 6 then "6" else + if i = 7 then "7" else + if i = 8 then "8" else + if i = 9 then "9" else + { abort(); ""; } -- the "" is needed to satisfy the typchecker + fi fi fi fi fi fi fi fi fi fi + }; + +(* + a2i converts an ASCII string into an integer. The empty string +is converted to 0. Signed and unsigned strings are handled. The +method aborts if the string does not represent an integer. Very +long strings of digits produce strange answers because of arithmetic +overflow. + +*) + a2i(s : String) : Int { + if s.length() = 0 then 0 else + if s.substr(0,1) = "-" then ~a2i_aux(s.substr(1,s.length()-1)) else + if s.substr(0,1) = "+" then a2i_aux(s.substr(1,s.length()-1)) else + a2i_aux(s) + fi fi fi + }; + +(* a2i_aux converts the usigned portion of the string. As a + programming example, this method is written iteratively. *) + + + a2i_aux(s : String) : Int { + (let int : Int <- 0 in + { + (let j : Int <- s.length() in + (let i : Int <- 0 in + while i < j loop + { + int <- int * 10 + c2i(s.substr(i,1)); + i <- i + 1; + } + pool + ) + ); + int; + } + ) + }; + +(* i2a converts an integer to a string. Positive and negative + numbers are handled correctly. *) + + i2a(i : Int) : String { + if i = 0 then "0" else + if 0 < i then i2a_aux(i) else + "-".concat(i2a_aux(i * ~1)) + fi fi + }; + +(* i2a_aux is an example using recursion. *) + + i2a_aux(i : Int) : String { + if i = 0 then "" else + (let next : Int <- i / 10 in + i2a_aux(next).concat(i2c(i - next * 10)) + ) + fi + }; + +}; + +class Main inherits IO { + + char : String; + avar : A; + a_var : A; + flag : Bool <- true; + + + menu() : String { + { + out_string("\n\tTo add a number to "); + print(avar); + out_string("...enter a:\n"); + out_string("\tTo negate "); + print(avar); + out_string("...enter b:\n"); + out_string("\tTo find the difference between "); + print(avar); + out_string("and another number...enter c:\n"); + out_string("\tTo find the factorial of "); + print(avar); + out_string("...enter d:\n"); + out_string("\tTo square "); + print(avar); + out_string("...enter e:\n"); + out_string("\tTo cube "); + print(avar); + out_string("...enter f:\n"); + out_string("\tTo find out if "); + print(avar); + out_string("is a multiple of 3...enter g:\n"); + out_string("\tTo divide "); + print(avar); + out_string("by 8...enter h:\n"); + out_string("\tTo get a new number...enter j:\n"); + out_string("\tTo quit...enter q:\n\n"); + in_string(); + } + }; + + prompt() : String { + { + out_string("\n"); + out_string("Please enter a number... "); + in_string(); + } + }; + + get_int() : Int { + { + (let z : A2I <- new A2I in + (let s : String <- prompt() in + z.a2i(s) + ) + ); + } + }; + + is_even(num : Int) : Bool { + (let x : Int <- num in + if x < 0 then is_even(~x) else + if 0 = x then true else + if 1 = x then false else + is_even(x - 2) + fi fi fi + ) + }; + + class_type(var : A) : IO { + case var of + a : A => out_string("Class type is now A\n"); + b : B => out_string("Class type is now B\n"); + c : C => out_string("Class type is now C\n"); + d : D => out_string("Class type is now D\n"); + e : E => out_string("Class type is now E\n"); + o : Object => out_string("Oooops\n"); + esac + }; + + print(var : A) : IO { + (let z : A2I <- new A2I in + { + out_string(z.i2a(var.value())); + out_string(" "); + } + ) + }; + + main() : Object { + { + avar <- (new A); + while flag loop + { + -- avar <- (new A).set_var(get_int()); + out_string("number "); + print(avar); + if is_even(avar.value()) then + out_string("is even!\n") + else + out_string("is odd!\n") + fi; + -- print(avar); -- prints out answer + class_type(avar); + char <- menu(); + if char = "a" then -- add + { + a_var <- (new A).set_var(get_int()); + avar <- (new B).method2(avar.value(), a_var.value()); + } else + if char = "b" then -- negate + case avar of + c : C => avar <- c.method6(c.value()); + a : A => avar <- a.method3(a.value()); + o : Object => { + out_string("Oooops\n"); + abort(); 0; + }; + esac else + if char = "c" then -- diff + { + a_var <- (new A).set_var(get_int()); + avar <- (new D).method4(avar.value(), a_var.value()); + } else + if char = "d" then avar <- (new C)@A.method5(avar.value()) else + -- factorial + if char = "e" then avar <- (new C)@B.method5(avar.value()) else + -- square + if char = "f" then avar <- (new C)@C.method5(avar.value()) else + -- cube + if char = "g" then -- multiple of 3? + if ((new D).method7(avar.value())) + then -- avar <- (new A).method1(avar.value()) + { + out_string("number "); + print(avar); + out_string("is divisible by 3.\n"); + } + else -- avar <- (new A).set_var(0) + { + out_string("number "); + print(avar); + out_string("is not divisible by 3.\n"); + } + fi else + if char = "h" then + (let x : A in + { + x <- (new E).method6(avar.value()); + (let r : Int <- (avar.value() - (x.value() * 8)) in + { + out_string("number "); + print(avar); + out_string("is equal to "); + print(x); + out_string("times 8 with a remainder of "); + (let a : A2I <- new A2I in + { + out_string(a.i2a(r)); + out_string("\n"); + } + ); -- end let a: + } + ); -- end let r: + avar <- x; + } + ) -- end let x: + else + if char = "j" then avar <- (new A) + else + if char = "q" then flag <- false + else + avar <- (new A).method1(avar.value()) -- divide/8 + fi fi fi fi fi fi fi fi fi fi; + } + pool; + } + }; + +}; + diff --git a/tests/codegen/arith_input.txt b/tests/codegen/arith_input.txt index 83e05b1ea..c431a225b 100644 --- a/tests/codegen/arith_input.txt +++ b/tests/codegen/arith_input.txt @@ -1,13 +1,13 @@ -a -1 -b -c -0 -d -e -f -g -h -j -5 -q +a +1 +b +c +0 +d +e +f +g +h +j +5 +q diff --git a/tests/codegen/arith_output.txt b/tests/codegen/arith_output.txt index 44b4ce73e..476cb3bad 100644 --- a/tests/codegen/arith_output.txt +++ b/tests/codegen/arith_output.txt @@ -1,158 +1,158 @@ -number 0 is even! -Class type is now A - - To add a number to 0 ...enter a: - To negate 0 ...enter b: - To find the difference between 0 and another number...enter c: - To find the factorial of 0 ...enter d: - To square 0 ...enter e: - To cube 0 ...enter f: - To find out if 0 is a multiple of 3...enter g: - To divide 0 by 8...enter h: - To get a new number...enter j: - To quit...enter q: - - -Please enter a number... number 1 is odd! -Class type is now B - - To add a number to 1 ...enter a: - To negate 1 ...enter b: - To find the difference between 1 and another number...enter c: - To find the factorial of 1 ...enter d: - To square 1 ...enter e: - To cube 1 ...enter f: - To find out if 1 is a multiple of 3...enter g: - To divide 1 by 8...enter h: - To get a new number...enter j: - To quit...enter q: - -number -1 is odd! -Class type is now C - - To add a number to -1 ...enter a: - To negate -1 ...enter b: - To find the difference between -1 and another number...enter c: - To find the factorial of -1 ...enter d: - To square -1 ...enter e: - To cube -1 ...enter f: - To find out if -1 is a multiple of 3...enter g: - To divide -1 by 8...enter h: - To get a new number...enter j: - To quit...enter q: - - -Please enter a number... number 1 is odd! -Class type is now D - - To add a number to 1 ...enter a: - To negate 1 ...enter b: - To find the difference between 1 and another number...enter c: - To find the factorial of 1 ...enter d: - To square 1 ...enter e: - To cube 1 ...enter f: - To find out if 1 is a multiple of 3...enter g: - To divide 1 by 8...enter h: - To get a new number...enter j: - To quit...enter q: - -number 1 is odd! -Class type is now E - - To add a number to 1 ...enter a: - To negate 1 ...enter b: - To find the difference between 1 and another number...enter c: - To find the factorial of 1 ...enter d: - To square 1 ...enter e: - To cube 1 ...enter f: - To find out if 1 is a multiple of 3...enter g: - To divide 1 by 8...enter h: - To get a new number...enter j: - To quit...enter q: - -number 1 is odd! -Class type is now E - - To add a number to 1 ...enter a: - To negate 1 ...enter b: - To find the difference between 1 and another number...enter c: - To find the factorial of 1 ...enter d: - To square 1 ...enter e: - To cube 1 ...enter f: - To find out if 1 is a multiple of 3...enter g: - To divide 1 by 8...enter h: - To get a new number...enter j: - To quit...enter q: - -number 1 is odd! -Class type is now E - - To add a number to 1 ...enter a: - To negate 1 ...enter b: - To find the difference between 1 and another number...enter c: - To find the factorial of 1 ...enter d: - To square 1 ...enter e: - To cube 1 ...enter f: - To find out if 1 is a multiple of 3...enter g: - To divide 1 by 8...enter h: - To get a new number...enter j: - To quit...enter q: - -number 1 is not divisible by 3. -number 1 is odd! -Class type is now E - - To add a number to 1 ...enter a: - To negate 1 ...enter b: - To find the difference between 1 and another number...enter c: - To find the factorial of 1 ...enter d: - To square 1 ...enter e: - To cube 1 ...enter f: - To find out if 1 is a multiple of 3...enter g: - To divide 1 by 8...enter h: - To get a new number...enter j: - To quit...enter q: - -number 1 is equal to 0 times 8 with a remainder of 1 -number 0 is even! -Class type is now A - - To add a number to 0 ...enter a: - To negate 0 ...enter b: - To find the difference between 0 and another number...enter c: - To find the factorial of 0 ...enter d: - To square 0 ...enter e: - To cube 0 ...enter f: - To find out if 0 is a multiple of 3...enter g: - To divide 0 by 8...enter h: - To get a new number...enter j: - To quit...enter q: - -number 0 is even! -Class type is now A - - To add a number to 0 ...enter a: - To negate 0 ...enter b: - To find the difference between 0 and another number...enter c: - To find the factorial of 0 ...enter d: - To square 0 ...enter e: - To cube 0 ...enter f: - To find out if 0 is a multiple of 3...enter g: - To divide 0 by 8...enter h: - To get a new number...enter j: - To quit...enter q: - -number 0 is even! -Class type is now A - - To add a number to 0 ...enter a: - To negate 0 ...enter b: - To find the difference between 0 and another number...enter c: - To find the factorial of 0 ...enter d: - To square 0 ...enter e: - To cube 0 ...enter f: - To find out if 0 is a multiple of 3...enter g: - To divide 0 by 8...enter h: - To get a new number...enter j: - To quit...enter q: - +number 0 is even! +Class type is now A + + To add a number to 0 ...enter a: + To negate 0 ...enter b: + To find the difference between 0 and another number...enter c: + To find the factorial of 0 ...enter d: + To square 0 ...enter e: + To cube 0 ...enter f: + To find out if 0 is a multiple of 3...enter g: + To divide 0 by 8...enter h: + To get a new number...enter j: + To quit...enter q: + + +Please enter a number... number 1 is odd! +Class type is now B + + To add a number to 1 ...enter a: + To negate 1 ...enter b: + To find the difference between 1 and another number...enter c: + To find the factorial of 1 ...enter d: + To square 1 ...enter e: + To cube 1 ...enter f: + To find out if 1 is a multiple of 3...enter g: + To divide 1 by 8...enter h: + To get a new number...enter j: + To quit...enter q: + +number -1 is odd! +Class type is now C + + To add a number to -1 ...enter a: + To negate -1 ...enter b: + To find the difference between -1 and another number...enter c: + To find the factorial of -1 ...enter d: + To square -1 ...enter e: + To cube -1 ...enter f: + To find out if -1 is a multiple of 3...enter g: + To divide -1 by 8...enter h: + To get a new number...enter j: + To quit...enter q: + + +Please enter a number... number 1 is odd! +Class type is now D + + To add a number to 1 ...enter a: + To negate 1 ...enter b: + To find the difference between 1 and another number...enter c: + To find the factorial of 1 ...enter d: + To square 1 ...enter e: + To cube 1 ...enter f: + To find out if 1 is a multiple of 3...enter g: + To divide 1 by 8...enter h: + To get a new number...enter j: + To quit...enter q: + +number 1 is odd! +Class type is now E + + To add a number to 1 ...enter a: + To negate 1 ...enter b: + To find the difference between 1 and another number...enter c: + To find the factorial of 1 ...enter d: + To square 1 ...enter e: + To cube 1 ...enter f: + To find out if 1 is a multiple of 3...enter g: + To divide 1 by 8...enter h: + To get a new number...enter j: + To quit...enter q: + +number 1 is odd! +Class type is now E + + To add a number to 1 ...enter a: + To negate 1 ...enter b: + To find the difference between 1 and another number...enter c: + To find the factorial of 1 ...enter d: + To square 1 ...enter e: + To cube 1 ...enter f: + To find out if 1 is a multiple of 3...enter g: + To divide 1 by 8...enter h: + To get a new number...enter j: + To quit...enter q: + +number 1 is odd! +Class type is now E + + To add a number to 1 ...enter a: + To negate 1 ...enter b: + To find the difference between 1 and another number...enter c: + To find the factorial of 1 ...enter d: + To square 1 ...enter e: + To cube 1 ...enter f: + To find out if 1 is a multiple of 3...enter g: + To divide 1 by 8...enter h: + To get a new number...enter j: + To quit...enter q: + +number 1 is not divisible by 3. +number 1 is odd! +Class type is now E + + To add a number to 1 ...enter a: + To negate 1 ...enter b: + To find the difference between 1 and another number...enter c: + To find the factorial of 1 ...enter d: + To square 1 ...enter e: + To cube 1 ...enter f: + To find out if 1 is a multiple of 3...enter g: + To divide 1 by 8...enter h: + To get a new number...enter j: + To quit...enter q: + +number 1 is equal to 0 times 8 with a remainder of 1 +number 0 is even! +Class type is now A + + To add a number to 0 ...enter a: + To negate 0 ...enter b: + To find the difference between 0 and another number...enter c: + To find the factorial of 0 ...enter d: + To square 0 ...enter e: + To cube 0 ...enter f: + To find out if 0 is a multiple of 3...enter g: + To divide 0 by 8...enter h: + To get a new number...enter j: + To quit...enter q: + +number 0 is even! +Class type is now A + + To add a number to 0 ...enter a: + To negate 0 ...enter b: + To find the difference between 0 and another number...enter c: + To find the factorial of 0 ...enter d: + To square 0 ...enter e: + To cube 0 ...enter f: + To find out if 0 is a multiple of 3...enter g: + To divide 0 by 8...enter h: + To get a new number...enter j: + To quit...enter q: + +number 0 is even! +Class type is now A + + To add a number to 0 ...enter a: + To negate 0 ...enter b: + To find the difference between 0 and another number...enter c: + To find the factorial of 0 ...enter d: + To square 0 ...enter e: + To cube 0 ...enter f: + To find out if 0 is a multiple of 3...enter g: + To divide 0 by 8...enter h: + To get a new number...enter j: + To quit...enter q: + diff --git a/tests/codegen/atoi_output.txt b/tests/codegen/atoi_output.txt index 51b815a48..c51d7bad6 100644 --- a/tests/codegen/atoi_output.txt +++ b/tests/codegen/atoi_output.txt @@ -1 +1 @@ -678987 == 678987 +678987 == 678987 diff --git a/tests/codegen/book_list.cl b/tests/codegen/book_list.cl index 025ea1695..d39f86bbe 100755 --- a/tests/codegen/book_list.cl +++ b/tests/codegen/book_list.cl @@ -1,132 +1,132 @@ --- example of static and dynamic type differing for a dispatch - -Class Book inherits IO { - title : String; - author : String; - - initBook(title_p : String, author_p : String) : Book { - { - title <- title_p; - author <- author_p; - self; - } - }; - - print() : Book { - { - out_string("title: ").out_string(title).out_string("\n"); - out_string("author: ").out_string(author).out_string("\n"); - self; - } - }; -}; - -Class Article inherits Book { - per_title : String; - - initArticle(title_p : String, author_p : String, - per_title_p : String) : Article { - { - initBook(title_p, author_p); - per_title <- per_title_p; - self; - } - }; - - print() : Book { - { - self@Book.print(); - out_string("periodical: ").out_string(per_title).out_string("\n"); - self; - } - }; -}; - -Class BookList inherits IO { - (* Since abort "returns" type Object, we have to add - an expression of type Bool here to satisfy the typechecker. - This code is unreachable, since abort() halts the program. - *) - isNil() : Bool { { abort(); true; } }; - - cons(hd : Book) : Cons { - (let new_cell : Cons <- new Cons in - new_cell.init(hd,self) - ) - }; - - (* Since abort "returns" type Object, we have to add - an expression of type Book here to satisfy the typechecker. - This code is unreachable, since abort() halts the program. - *) - car() : Book { { abort(); new Book; } }; - - (* Since abort "returns" type Object, we have to add - an expression of type BookList here to satisfy the typechecker. - This code is unreachable, since abort() halts the program. - *) - cdr() : BookList { { abort(); new BookList; } }; - - print_list() : Object { abort() }; -}; - -Class Cons inherits BookList { - xcar : Book; -- We keep the car and cdr in attributes. - xcdr : BookList; -- Because methods and features must have different names, - -- we use xcar and xcdr for the attributes and reserve - -- car and cdr for the features. - - isNil() : Bool { false }; - - init(hd : Book, tl : BookList) : Cons { - { - xcar <- hd; - xcdr <- tl; - self; - } - }; - - car() : Book { xcar }; - - cdr() : BookList { xcdr }; - - print_list() : Object { - { - case xcar.print() of - dummy : Book => out_string("- dynamic type was Book -\n"); - dummy : Article => out_string("- dynamic type was Article -\n"); - esac; - xcdr.print_list(); - } - }; -}; - -Class Nil inherits BookList { - isNil() : Bool { true }; - - print_list() : Object { true }; -}; - - -Class Main { - - books : BookList; - - main() : Object { - (let a_book : Book <- - (new Book).initBook("Compilers, Principles, Techniques, and Tools", - "Aho, Sethi, and Ullman") - in - (let an_article : Article <- - (new Article).initArticle("The Top 100 CD_ROMs", - "Ulanoff", - "PC Magazine") - in - { - books <- (new Nil).cons(a_book).cons(an_article); - books.print_list(); - } - ) -- end let an_article - ) -- end let a_book - }; -}; +-- example of static and dynamic type differing for a dispatch + +Class Book inherits IO { + title : String; + author : String; + + initBook(title_p : String, author_p : String) : Book { + { + title <- title_p; + author <- author_p; + self; + } + }; + + print() : Book { + { + out_string("title: ").out_string(title).out_string("\n"); + out_string("author: ").out_string(author).out_string("\n"); + self; + } + }; +}; + +Class Article inherits Book { + per_title : String; + + initArticle(title_p : String, author_p : String, + per_title_p : String) : Article { + { + initBook(title_p, author_p); + per_title <- per_title_p; + self; + } + }; + + print() : Book { + { + self@Book.print(); + out_string("periodical: ").out_string(per_title).out_string("\n"); + self; + } + }; +}; + +Class BookList inherits IO { + (* Since abort "returns" type Object, we have to add + an expression of type Bool here to satisfy the typechecker. + This code is unreachable, since abort() halts the program. + *) + isNil() : Bool { { abort(); true; } }; + + cons(hd : Book) : Cons { + (let new_cell : Cons <- new Cons in + new_cell.init(hd,self) + ) + }; + + (* Since abort "returns" type Object, we have to add + an expression of type Book here to satisfy the typechecker. + This code is unreachable, since abort() halts the program. + *) + car() : Book { { abort(); new Book; } }; + + (* Since abort "returns" type Object, we have to add + an expression of type BookList here to satisfy the typechecker. + This code is unreachable, since abort() halts the program. + *) + cdr() : BookList { { abort(); new BookList; } }; + + print_list() : Object { abort() }; +}; + +Class Cons inherits BookList { + xcar : Book; -- We keep the car and cdr in attributes. + xcdr : BookList; -- Because methods and features must have different names, + -- we use xcar and xcdr for the attributes and reserve + -- car and cdr for the features. + + isNil() : Bool { false }; + + init(hd : Book, tl : BookList) : Cons { + { + xcar <- hd; + xcdr <- tl; + self; + } + }; + + car() : Book { xcar }; + + cdr() : BookList { xcdr }; + + print_list() : Object { + { + case xcar.print() of + dummy : Book => out_string("- dynamic type was Book -\n"); + dummy : Article => out_string("- dynamic type was Article -\n"); + esac; + xcdr.print_list(); + } + }; +}; + +Class Nil inherits BookList { + isNil() : Bool { true }; + + print_list() : Object { true }; +}; + + +Class Main { + + books : BookList; + + main() : Object { + (let a_book : Book <- + (new Book).initBook("Compilers, Principles, Techniques, and Tools", + "Aho, Sethi, and Ullman") + in + (let an_article : Article <- + (new Article).initArticle("The Top 100 CD_ROMs", + "Ulanoff", + "PC Magazine") + in + { + books <- (new Nil).cons(a_book).cons(an_article); + books.print_list(); + } + ) -- end let an_article + ) -- end let a_book + }; +}; diff --git a/tests/codegen/book_list_output.txt b/tests/codegen/book_list_output.txt index 3408320b2..ced587a4f 100644 --- a/tests/codegen/book_list_output.txt +++ b/tests/codegen/book_list_output.txt @@ -1,7 +1,7 @@ -title: The Top 100 CD_ROMs -author: Ulanoff -periodical: PC Magazine -- dynamic type was Article - -title: Compilers, Principles, Techniques, and Tools -author: Aho, Sethi, and Ullman -- dynamic type was Book - +title: The Top 100 CD_ROMs +author: Ulanoff +periodical: PC Magazine +- dynamic type was Article - +title: Compilers, Principles, Techniques, and Tools +author: Aho, Sethi, and Ullman +- dynamic type was Book - diff --git a/tests/codegen/cells.cl b/tests/codegen/cells.cl index 9fd6741bb..bcd891498 100755 --- a/tests/codegen/cells.cl +++ b/tests/codegen/cells.cl @@ -1,97 +1,97 @@ -(* models one-dimensional cellular automaton on a circle of finite radius - arrays are faked as Strings, - X's respresent live cells, dots represent dead cells, - no error checking is done *) -class CellularAutomaton inherits IO { - population_map : String; - - init(map : String) : CellularAutomaton { - { - population_map <- map; - self; - } - }; - - print() : CellularAutomaton { - { - out_string(population_map.concat("\n")); - self; - } - }; - - num_cells() : Int { - population_map.length() - }; - - cell(position : Int) : String { - population_map.substr(position, 1) - }; - - cell_left_neighbor(position : Int) : String { - if position = 0 then - cell(num_cells() - 1) - else - cell(position - 1) - fi - }; - - cell_right_neighbor(position : Int) : String { - if position = num_cells() - 1 then - cell(0) - else - cell(position + 1) - fi - }; - - (* a cell will live if exactly 1 of itself and it's immediate - neighbors are alive *) - cell_at_next_evolution(position : Int) : String { - if (if cell(position) = "X" then 1 else 0 fi - + if cell_left_neighbor(position) = "X" then 1 else 0 fi - + if cell_right_neighbor(position) = "X" then 1 else 0 fi - = 1) - then - "X" - else - "." - fi - }; - - evolve() : CellularAutomaton { - (let position : Int in - (let num : Int <- num_cells() in - (let temp : String in - { - while position < num loop - { - temp <- temp.concat(cell_at_next_evolution(position)); - position <- position + 1; - } - pool; - population_map <- temp; - self; - } - ) ) ) - }; -}; - -class Main { - cells : CellularAutomaton; - - main() : Main { - { - cells <- (new CellularAutomaton).init(" X "); - cells.print(); - (let countdown : Int <- 20 in - while 0 < countdown loop - { - cells.evolve(); - cells.print(); - countdown <- countdown - 1; - } - pool - ); - self; - } - }; -}; +(* models one-dimensional cellular automaton on a circle of finite radius + arrays are faked as Strings, + X's respresent live cells, dots represent dead cells, + no error checking is done *) +class CellularAutomaton inherits IO { + population_map : String; + + init(map : String) : CellularAutomaton { + { + population_map <- map; + self; + } + }; + + print() : CellularAutomaton { + { + out_string(population_map.concat("\n")); + self; + } + }; + + num_cells() : Int { + population_map.length() + }; + + cell(position : Int) : String { + population_map.substr(position, 1) + }; + + cell_left_neighbor(position : Int) : String { + if position = 0 then + cell(num_cells() - 1) + else + cell(position - 1) + fi + }; + + cell_right_neighbor(position : Int) : String { + if position = num_cells() - 1 then + cell(0) + else + cell(position + 1) + fi + }; + + (* a cell will live if exactly 1 of itself and it's immediate + neighbors are alive *) + cell_at_next_evolution(position : Int) : String { + if (if cell(position) = "X" then 1 else 0 fi + + if cell_left_neighbor(position) = "X" then 1 else 0 fi + + if cell_right_neighbor(position) = "X" then 1 else 0 fi + = 1) + then + "X" + else + "." + fi + }; + + evolve() : CellularAutomaton { + (let position : Int in + (let num : Int <- num_cells() in + (let temp : String in + { + while position < num loop + { + temp <- temp.concat(cell_at_next_evolution(position)); + position <- position + 1; + } + pool; + population_map <- temp; + self; + } + ) ) ) + }; +}; + +class Main { + cells : CellularAutomaton; + + main() : Main { + { + cells <- (new CellularAutomaton).init(" X "); + cells.print(); + (let countdown : Int <- 20 in + while 0 < countdown loop + { + cells.evolve(); + cells.print(); + countdown <- countdown - 1; + } + pool + ); + self; + } + }; +}; diff --git a/tests/codegen/cells_output.txt b/tests/codegen/cells_output.txt index 6304902cc..9d06c27bc 100644 --- a/tests/codegen/cells_output.txt +++ b/tests/codegen/cells_output.txt @@ -1,21 +1,21 @@ - X -........XXX........ -.......X...X....... -......XXX.XXX...... -.....X.......X..... -....XXX.....XXX.... -...X...X...X...X... -..XXX.XXX.XXX.XXX.. -.X...............X. -XXX.............XXX -...X...........X... -..XXX.........XXX.. -.X...X.......X...X. -XXX.XXX.....XXX.XXX -.......X...X....... -......XXX.XXX...... -.....X.......X..... -....XXX.....XXX.... -...X...X...X...X... -..XXX.XXX.XXX.XXX.. -.X...............X. + X +........XXX........ +.......X...X....... +......XXX.XXX...... +.....X.......X..... +....XXX.....XXX.... +...X...X...X...X... +..XXX.XXX.XXX.XXX.. +.X...............X. +XXX.............XXX +...X...........X... +..XXX.........XXX.. +.X...X.......X...X. +XXX.XXX.....XXX.XXX +.......X...X....... +......XXX.XXX...... +.....X.......X..... +....XXX.....XXX.... +...X...X...X...X... +..XXX.XXX.XXX.XXX.. +.X...............X. diff --git a/tests/codegen/complex.cl b/tests/codegen/complex.cl index 0b7aa44e9..9edb6151d 100755 --- a/tests/codegen/complex.cl +++ b/tests/codegen/complex.cl @@ -1,52 +1,52 @@ -class Main inherits IO { - main() : IO { - (let c : Complex <- (new Complex).init(1, 1) in - if c.reflect_X().reflect_Y() = c.reflect_0() - then out_string("=)\n") - else out_string("=(\n") - fi - ) - }; -}; - -class Complex inherits IO { - x : Int; - y : Int; - - init(a : Int, b : Int) : Complex { - { - x = a; - y = b; - self; - } - }; - - print() : Object { - if y = 0 - then out_int(x) - else out_int(x).out_string("+").out_int(y).out_string("I") - fi - }; - - reflect_0() : Complex { - { - x = ~x; - y = ~y; - self; - } - }; - - reflect_X() : Complex { - { - y = ~y; - self; - } - }; - - reflect_Y() : Complex { - { - x = ~x; - self; - } - }; -}; +class Main inherits IO { + main() : IO { + (let c : Complex <- (new Complex).init(1, 1) in + if c.reflect_X().reflect_Y() = c.reflect_0() + then out_string("=)\n") + else out_string("=(\n") + fi + ) + }; +}; + +class Complex inherits IO { + x : Int; + y : Int; + + init(a : Int, b : Int) : Complex { + { + x = a; + y = b; + self; + } + }; + + print() : Object { + if y = 0 + then out_int(x) + else out_int(x).out_string("+").out_int(y).out_string("I") + fi + }; + + reflect_0() : Complex { + { + x = ~x; + y = ~y; + self; + } + }; + + reflect_X() : Complex { + { + y = ~y; + self; + } + }; + + reflect_Y() : Complex { + { + x = ~x; + self; + } + }; +}; diff --git a/tests/codegen/complex_output.txt b/tests/codegen/complex_output.txt index 18b77c1fc..7d6173685 100644 --- a/tests/codegen/complex_output.txt +++ b/tests/codegen/complex_output.txt @@ -1 +1 @@ -=) +=) diff --git a/tests/codegen/fib.cl b/tests/codegen/fib.cl index 08ceaede8..ced8cee48 100644 --- a/tests/codegen/fib.cl +++ b/tests/codegen/fib.cl @@ -1,29 +1,29 @@ -class Main inherits IO { - -- the class has features. Only methods in this case. - main(): Object { - { - out_string("Enter n to find nth fibonacci number!\n"); - out_int(fib(in_int())); - out_string("\n"); - } - }; - - fib(i : Int) : Int { -- list of formals. And the return type of the method. - let a : Int <- 1, - b : Int <- 0, - c : Int <- 0 - in - { - while (not (i = 0)) loop -- expressions are nested. - { - c <- a + b; - i <- i - 1; - b <- a; - a <- c; - } - pool; - c; - } - }; - -}; +class Main inherits IO { + -- the class has features. Only methods in this case. + main(): Object { + { + out_string("Enter n to find nth fibonacci number!\n"); + out_int(fib(in_int())); + out_string("\n"); + } + }; + + fib(i : Int) : Int { -- list of formals. And the return type of the method. + let a : Int <- 1, + b : Int <- 0, + c : Int <- 0 + in + { + while (not (i = 0)) loop -- expressions are nested. + { + c <- a + b; + i <- i - 1; + b <- a; + a <- c; + } + pool; + c; + } + }; + +}; diff --git a/tests/codegen/fib_input.txt b/tests/codegen/fib_input.txt index f599e28b8..d43401489 100644 --- a/tests/codegen/fib_input.txt +++ b/tests/codegen/fib_input.txt @@ -1 +1 @@ -10 +10 diff --git a/tests/codegen/fib_output.txt b/tests/codegen/fib_output.txt index d402da6af..2552f4479 100644 --- a/tests/codegen/fib_output.txt +++ b/tests/codegen/fib_output.txt @@ -1,2 +1,2 @@ -Enter n to find nth fibonacci number! -89 +Enter n to find nth fibonacci number! +89 diff --git a/tests/codegen/graph.cl b/tests/codegen/graph.cl index 8e511358c..59e29bbf4 100755 --- a/tests/codegen/graph.cl +++ b/tests/codegen/graph.cl @@ -1,381 +1,381 @@ -(* - * Cool program reading descriptions of weighted directed graphs - * from stdin. It builds up a graph objects with a list of vertices - * and a list of edges. Every vertice has a list of outgoing edges. - * - * INPUT FORMAT - * Every line has the form vertice successor* - * Where vertice is an int, and successor is vertice,weight - * - * An empty line or EOF terminates the input. - * - * The list of vertices and the edge list is printed out by the Main - * class. - * - * TEST - * Once compiled, the file g1.graph can be fed to the program. - * The output should look like this: - -nautilus.CS.Berkeley.EDU 53# spim -file graph.s (new Bar); - n : Foo => (new Razz); - n : Bar => n; - esac; - - b : Int <- a.doh() + g.doh() + doh() + printh(); - - doh() : Int { (let i : Int <- h in { h <- h + 2; i; } ) }; - -}; - -class Bar inherits Razz { - - c : Int <- doh(); - - d : Object <- printh(); -}; - - -class Razz inherits Foo { - - e : Bar <- case self of - n : Razz => (new Bar); - n : Bar => n; - esac; - - f : Int <- a@Bazz.doh() + g.doh() + e.doh() + doh() + printh(); - -}; - -class Bazz inherits IO { - - h : Int <- 1; - - g : Foo <- case self of - n : Bazz => (new Foo); - n : Razz => (new Bar); - n : Foo => (new Razz); - n : Bar => n; - esac; - - i : Object <- printh(); - - printh() : Int { { out_int(h); 0; } }; - - doh() : Int { (let i: Int <- h in { h <- h + 1; i; } ) }; -}; - -(* scary . . . *) -class Main { - a : Bazz <- new Bazz; - b : Foo <- new Foo; - c : Razz <- new Razz; - d : Bar <- new Bar; - - main(): String { "do nothing" }; - -}; - - - - - +(* hairy . . .*) + +class Foo inherits Bazz { + a : Razz <- case self of + n : Razz => (new Bar); + n : Foo => (new Razz); + n : Bar => n; + esac; + + b : Int <- a.doh() + g.doh() + doh() + printh(); + + doh() : Int { (let i : Int <- h in { h <- h + 2; i; } ) }; + +}; + +class Bar inherits Razz { + + c : Int <- doh(); + + d : Object <- printh(); +}; + + +class Razz inherits Foo { + + e : Bar <- case self of + n : Razz => (new Bar); + n : Bar => n; + esac; + + f : Int <- a@Bazz.doh() + g.doh() + e.doh() + doh() + printh(); + +}; + +class Bazz inherits IO { + + h : Int <- 1; + + g : Foo <- case self of + n : Bazz => (new Foo); + n : Razz => (new Bar); + n : Foo => (new Razz); + n : Bar => n; + esac; + + i : Object <- printh(); + + printh() : Int { { out_int(h); 0; } }; + + doh() : Int { (let i: Int <- h in { h <- h + 1; i; } ) }; +}; + +(* scary . . . *) +class Main { + a : Bazz <- new Bazz; + b : Foo <- new Foo; + c : Razz <- new Razz; + d : Bar <- new Bar; + + main(): String { "do nothing" }; + +}; + + + + + diff --git a/tests/codegen/hello_world.cl b/tests/codegen/hello_world.cl index 0c818f908..b0a180a2e 100755 --- a/tests/codegen/hello_world.cl +++ b/tests/codegen/hello_world.cl @@ -1,5 +1,5 @@ -class Main inherits IO { - main(): IO { - out_string("Hello, World.\n") - }; -}; +class Main inherits IO { + main(): IO { + out_string("Hello, World.\n") + }; +}; diff --git a/tests/codegen/hello_world_output.txt b/tests/codegen/hello_world_output.txt index 349db2bfe..9c1c7a002 100644 --- a/tests/codegen/hello_world_output.txt +++ b/tests/codegen/hello_world_output.txt @@ -1 +1 @@ -Hello, World. +Hello, World. diff --git a/tests/codegen/io.cl b/tests/codegen/io.cl index 7f0de869e..42ee6854e 100755 --- a/tests/codegen/io.cl +++ b/tests/codegen/io.cl @@ -1,103 +1,103 @@ -(* - * The IO class is predefined and has 4 methods: - * - * out_string(s : String) : SELF_TYPE - * out_int(i : Int) : SELF_TYPE - * in_string() : String - * in_int() : Int - * - * The out operations print their argument to the terminal. The - * in_string method reads an entire line from the terminal and returns a - * string not containing the new line. The in_int method also reads - * an entire line from the terminal and returns the integer - * corresponding to the first non blank word on the line. If that - * word is not an integer, it returns 0. - * - * - * Because our language is object oriented, we need an object of type - * IO in order to call any of these methods. - * - * There are basically two ways of getting access to IO in a class C. - * - * 1) Define C to Inherit from IO. This way the IO methods become - * methods of C, and they can be called using the abbreviated - * dispatch, i.e. - * - * class C inherits IO is - * ... - * out_string("Hello world\n") - * ... - * end; - * - * 2) If your class C does not directly or indirectly inherit from - * IO, the best way to access IO is through an initialized - * attribute of type IO. - * - * class C inherits Foo is - * io : IO <- new IO; - * ... - * io.out_string("Hello world\n"); - * ... - * end; - * - * Approach 1) is most often used, in particular when you need IO - * functions in the Main class. - * - *) - - -class A { - - -- Let's assume that we don't want A to not inherit from IO. - - io : IO <- new IO; - - out_a() : Object { io.out_string("A: Hello world\n") }; - -}; - - -class B inherits A { - - -- B does not have to an extra attribute, since it inherits io from A. - - out_b() : Object { io.out_string("B: Hello world\n") }; - -}; - - -class C inherits IO { - - -- Now the IO methods are part of C. - - out_c() : Object { out_string("C: Hello world\n") }; - - -- Note that out_string(...) is just a shorthand for self.out_string(...) - -}; - - -class D inherits C { - - -- Inherits IO methods from C. - - out_d() : Object { out_string("D: Hello world\n") }; - -}; - - -class Main inherits IO { - - -- Same case as class C. - - main() : Object { - { - (new A).out_a(); - (new B).out_b(); - (new C).out_c(); - (new D).out_d(); - out_string("Done.\n"); - } - }; - -}; +(* + * The IO class is predefined and has 4 methods: + * + * out_string(s : String) : SELF_TYPE + * out_int(i : Int) : SELF_TYPE + * in_string() : String + * in_int() : Int + * + * The out operations print their argument to the terminal. The + * in_string method reads an entire line from the terminal and returns a + * string not containing the new line. The in_int method also reads + * an entire line from the terminal and returns the integer + * corresponding to the first non blank word on the line. If that + * word is not an integer, it returns 0. + * + * + * Because our language is object oriented, we need an object of type + * IO in order to call any of these methods. + * + * There are basically two ways of getting access to IO in a class C. + * + * 1) Define C to Inherit from IO. This way the IO methods become + * methods of C, and they can be called using the abbreviated + * dispatch, i.e. + * + * class C inherits IO is + * ... + * out_string("Hello world\n") + * ... + * end; + * + * 2) If your class C does not directly or indirectly inherit from + * IO, the best way to access IO is through an initialized + * attribute of type IO. + * + * class C inherits Foo is + * io : IO <- new IO; + * ... + * io.out_string("Hello world\n"); + * ... + * end; + * + * Approach 1) is most often used, in particular when you need IO + * functions in the Main class. + * + *) + + +class A { + + -- Let's assume that we don't want A to not inherit from IO. + + io : IO <- new IO; + + out_a() : Object { io.out_string("A: Hello world\n") }; + +}; + + +class B inherits A { + + -- B does not have to an extra attribute, since it inherits io from A. + + out_b() : Object { io.out_string("B: Hello world\n") }; + +}; + + +class C inherits IO { + + -- Now the IO methods are part of C. + + out_c() : Object { out_string("C: Hello world\n") }; + + -- Note that out_string(...) is just a shorthand for self.out_string(...) + +}; + + +class D inherits C { + + -- Inherits IO methods from C. + + out_d() : Object { out_string("D: Hello world\n") }; + +}; + + +class Main inherits IO { + + -- Same case as class C. + + main() : Object { + { + (new A).out_a(); + (new B).out_b(); + (new C).out_c(); + (new D).out_d(); + out_string("Done.\n"); + } + }; + +}; diff --git a/tests/codegen/io_output.txt b/tests/codegen/io_output.txt index f846f596d..870cffce6 100644 --- a/tests/codegen/io_output.txt +++ b/tests/codegen/io_output.txt @@ -1,5 +1,5 @@ -A: Hello world -B: Hello world -C: Hello world -D: Hello world -Done. +A: Hello world +B: Hello world +C: Hello world +D: Hello world +Done. diff --git a/tests/codegen/life.cl b/tests/codegen/life.cl index b83d97957..7d7a41fdb 100755 --- a/tests/codegen/life.cl +++ b/tests/codegen/life.cl @@ -1,436 +1,436 @@ -(* The Game of Life - Tendo Kayiira, Summer '95 - With code taken from /private/cool/class/examples/cells.cl - - This introduction was taken off the internet. It gives a brief - description of the Game Of Life. It also gives the rules by which - this particular game follows. - - Introduction - - John Conway's Game of Life is a mathematical amusement, but it - is also much more: an insight into how a system of simple - cellualar automata can create complex, odd, and often aesthetically - pleasing patterns. It is played on a cartesian grid of cells - which are either 'on' or 'off' The game gets it's name from the - similarity between the behaviour of these cells and the behaviour - of living organisms. - - The Rules - - The playfield is a cartesian grid of arbitrary size. Each cell in - this grid can be in an 'on' state or an 'off' state. On each 'turn' - (called a generation,) the state of each cell changes simultaneously - depending on it's state and the state of all cells adjacent to it. - - For 'on' cells, - If the cell has 0 or 1 neighbours which are 'on', the cell turns - 'off'. ('dies of loneliness') - If the cell has 2 or 3 neighbours which are 'on', the cell stays - 'on'. (nothing happens to that cell) - If the cell has 4, 5, 6, 7, 8, or 9 neighbours which are 'on', - the cell turns 'off'. ('dies of overcrowding') - - For 'off' cells, - If the cell has 0, 1, 2, 4, 5, 6, 7, 8, or 9 neighbours which - are 'on', the cell stays 'off'. (nothing happens to that cell) - If the cell has 3 neighbours which are 'on', the cell turns - 'on'. (3 neighbouring 'alive' cells 'give birth' to a fourth.) - - Repeat for as many generations as desired. - - *) - - -class Board inherits IO { - - rows : Int; - columns : Int; - board_size : Int; - - size_of_board(initial : String) : Int { - initial.length() - }; - - board_init(start : String) : Board { - (let size :Int <- size_of_board(start) in - { - if size = 15 then - { - rows <- 3; - columns <- 5; - board_size <- size; - } - else if size = 16 then - { - rows <- 4; - columns <- 4; - board_size <- size; - } - else if size = 20 then - { - rows <- 4; - columns <- 5; - board_size <- size; - } - else if size = 21 then - { - rows <- 3; - columns <- 7; - board_size <- size; - } - else if size = 25 then - { - rows <- 5; - columns <- 5; - board_size <- size; - } - else if size = 28 then - { - rows <- 7; - columns <- 4; - board_size <- size; - } - else -- If none of the above fit, then just give - { -- the configuration of the most common board - rows <- 5; - columns <- 5; - board_size <- size; - } - fi fi fi fi fi fi; - self; - } - ) - }; - -}; - - - -class CellularAutomaton inherits Board { - population_map : String; - - init(map : String) : CellularAutomaton { - { - population_map <- map; - board_init(map); - self; - } - }; - - - - - print() : CellularAutomaton { - - (let i : Int <- 0 in - (let num : Int <- board_size in - { - out_string("\n"); - while i < num loop - { - out_string(population_map.substr(i,columns)); - out_string("\n"); - i <- i + columns; - } - pool; - out_string("\n"); - self; - } - ) ) - }; - - num_cells() : Int { - population_map.length() - }; - - cell(position : Int) : String { - if board_size - 1 < position then - " " - else - population_map.substr(position, 1) - fi - }; - - north(position : Int): String { - if (position - columns) < 0 then - " " - else - cell(position - columns) - fi - }; - - south(position : Int): String { - if board_size < (position + columns) then - " " - else - cell(position + columns) - fi - }; - - east(position : Int): String { - if (((position + 1) /columns ) * columns) = (position + 1) then - " " - else - cell(position + 1) - fi - }; - - west(position : Int): String { - if position = 0 then - " " - else - if ((position / columns) * columns) = position then - " " - else - cell(position - 1) - fi fi - }; - - northwest(position : Int): String { - if (position - columns) < 0 then - " " - else if ((position / columns) * columns) = position then - " " - else - north(position - 1) - fi fi - }; - - northeast(position : Int): String { - if (position - columns) < 0 then - " " - else if (((position + 1) /columns ) * columns) = (position + 1) then - " " - else - north(position + 1) - fi fi - }; - - southeast(position : Int): String { - if board_size < (position + columns) then - " " - else if (((position + 1) /columns ) * columns) = (position + 1) then - " " - else - south(position + 1) - fi fi - }; - - southwest(position : Int): String { - if board_size < (position + columns) then - " " - else if ((position / columns) * columns) = position then - " " - else - south(position - 1) - fi fi - }; - - neighbors(position: Int): Int { - { - if north(position) = "X" then 1 else 0 fi - + if south(position) = "X" then 1 else 0 fi - + if east(position) = "X" then 1 else 0 fi - + if west(position) = "X" then 1 else 0 fi - + if northeast(position) = "X" then 1 else 0 fi - + if northwest(position) = "X" then 1 else 0 fi - + if southeast(position) = "X" then 1 else 0 fi - + if southwest(position) = "X" then 1 else 0 fi; - } - }; - - -(* A cell will live if 2 or 3 of it's neighbors are alive. It dies - otherwise. A cell is born if only 3 of it's neighbors are alive. *) - - cell_at_next_evolution(position : Int) : String { - - if neighbors(position) = 3 then - "X" - else - if neighbors(position) = 2 then - if cell(position) = "X" then - "X" - else - "-" - fi - else - "-" - fi fi - }; - - - evolve() : CellularAutomaton { - (let position : Int <- 0 in - (let num : Int <- num_cells() in - (let temp : String in - { - while position < num loop - { - temp <- temp.concat(cell_at_next_evolution(position)); - position <- position + 1; - } - pool; - population_map <- temp; - self; - } - ) ) ) - }; - -(* This is where the background pattern is detremined by the user. More - patterns can be added as long as whoever adds keeps the board either - 3x5, 4x5, 5x5, 3x7, 7x4, 4x4 with the row first then column. *) - option(): String { - { - (let num : Int in - { - out_string("\nPlease chose a number:\n"); - out_string("\t1: A cross\n"); - out_string("\t2: A slash from the upper left to lower right\n"); - out_string("\t3: A slash from the upper right to lower left\n"); - out_string("\t4: An X\n"); - out_string("\t5: A greater than sign \n"); - out_string("\t6: A less than sign\n"); - out_string("\t7: Two greater than signs\n"); - out_string("\t8: Two less than signs\n"); - out_string("\t9: A 'V'\n"); - out_string("\t10: An inverse 'V'\n"); - out_string("\t11: Numbers 9 and 10 combined\n"); - out_string("\t12: A full grid\n"); - out_string("\t13: A 'T'\n"); - out_string("\t14: A plus '+'\n"); - out_string("\t15: A 'W'\n"); - out_string("\t16: An 'M'\n"); - out_string("\t17: An 'E'\n"); - out_string("\t18: A '3'\n"); - out_string("\t19: An 'O'\n"); - out_string("\t20: An '8'\n"); - out_string("\t21: An 'S'\n"); - out_string("Your choice => "); - num <- in_int(); - out_string("\n"); - if num = 1 then - " XX XXXX XXXX XX " - else if num = 2 then - " X X X X X " - else if num = 3 then - "X X X X X" - else if num = 4 then - "X X X X X X X X X" - else if num = 5 then - "X X X X X " - else if num = 6 then - " X X X X X" - else if num = 7 then - "X X X XX X " - else if num = 8 then - " X XX X X X " - else if num = 9 then - "X X X X X " - else if num = 10 then - " X X X X X" - else if num = 11 then - "X X X X X X X X" - else if num = 12 then - "XXXXXXXXXXXXXXXXXXXXXXXXX" - else if num = 13 then - "XXXXX X X X X " - else if num = 14 then - " X X XXXXX X X " - else if num = 15 then - "X X X X X X X " - else if num = 16 then - " X X X X X X X" - else if num = 17 then - "XXXXX X XXXXX X XXXX" - else if num = 18 then - "XXX X X X X XXXX " - else if num = 19 then - " XX X XX X XX " - else if num = 20 then - " XX X XX X XX X XX X XX " - else if num = 21 then - " XXXX X XX X XXXX " - else - " " - fi fi fi fi fi fi fi fi fi fi fi fi fi fi fi fi fi fi fi fi fi; - } - ); - } - }; - - - - - prompt() : Bool { - { - (let ans : String in - { - out_string("Would you like to continue with the next generation? \n"); - out_string("Please use lowercase y or n for your answer [y]: "); - ans <- in_string(); - out_string("\n"); - if ans = "n" then - false - else - true - fi; - } - ); - } - }; - - - prompt2() : Bool { - (let ans : String in - { - out_string("\n\n"); - out_string("Would you like to choose a background pattern? \n"); - out_string("Please use lowercase y or n for your answer [n]: "); - ans <- in_string(); - if ans = "y" then - true - else - false - fi; - } - ) - }; - - -}; - -class Main inherits CellularAutomaton { - cells : CellularAutomaton; - - main() : Main { - { - (let continue : Bool in - (let choice : String in - { - out_string("Welcome to the Game of Life.\n"); - out_string("There are many initial states to choose from. \n"); - while prompt2() loop - { - continue <- true; - choice <- option(); - cells <- (new CellularAutomaton).init(choice); - cells.print(); - while continue loop - if prompt() then - { - cells.evolve(); - cells.print(); - } - else - continue <- false - fi - pool; - } - pool; - self; - } ) ); } - }; -}; - +(* The Game of Life + Tendo Kayiira, Summer '95 + With code taken from /private/cool/class/examples/cells.cl + + This introduction was taken off the internet. It gives a brief + description of the Game Of Life. It also gives the rules by which + this particular game follows. + + Introduction + + John Conway's Game of Life is a mathematical amusement, but it + is also much more: an insight into how a system of simple + cellualar automata can create complex, odd, and often aesthetically + pleasing patterns. It is played on a cartesian grid of cells + which are either 'on' or 'off' The game gets it's name from the + similarity between the behaviour of these cells and the behaviour + of living organisms. + + The Rules + + The playfield is a cartesian grid of arbitrary size. Each cell in + this grid can be in an 'on' state or an 'off' state. On each 'turn' + (called a generation,) the state of each cell changes simultaneously + depending on it's state and the state of all cells adjacent to it. + + For 'on' cells, + If the cell has 0 or 1 neighbours which are 'on', the cell turns + 'off'. ('dies of loneliness') + If the cell has 2 or 3 neighbours which are 'on', the cell stays + 'on'. (nothing happens to that cell) + If the cell has 4, 5, 6, 7, 8, or 9 neighbours which are 'on', + the cell turns 'off'. ('dies of overcrowding') + + For 'off' cells, + If the cell has 0, 1, 2, 4, 5, 6, 7, 8, or 9 neighbours which + are 'on', the cell stays 'off'. (nothing happens to that cell) + If the cell has 3 neighbours which are 'on', the cell turns + 'on'. (3 neighbouring 'alive' cells 'give birth' to a fourth.) + + Repeat for as many generations as desired. + + *) + + +class Board inherits IO { + + rows : Int; + columns : Int; + board_size : Int; + + size_of_board(initial : String) : Int { + initial.length() + }; + + board_init(start : String) : Board { + (let size :Int <- size_of_board(start) in + { + if size = 15 then + { + rows <- 3; + columns <- 5; + board_size <- size; + } + else if size = 16 then + { + rows <- 4; + columns <- 4; + board_size <- size; + } + else if size = 20 then + { + rows <- 4; + columns <- 5; + board_size <- size; + } + else if size = 21 then + { + rows <- 3; + columns <- 7; + board_size <- size; + } + else if size = 25 then + { + rows <- 5; + columns <- 5; + board_size <- size; + } + else if size = 28 then + { + rows <- 7; + columns <- 4; + board_size <- size; + } + else -- If none of the above fit, then just give + { -- the configuration of the most common board + rows <- 5; + columns <- 5; + board_size <- size; + } + fi fi fi fi fi fi; + self; + } + ) + }; + +}; + + + +class CellularAutomaton inherits Board { + population_map : String; + + init(map : String) : CellularAutomaton { + { + population_map <- map; + board_init(map); + self; + } + }; + + + + + print() : CellularAutomaton { + + (let i : Int <- 0 in + (let num : Int <- board_size in + { + out_string("\n"); + while i < num loop + { + out_string(population_map.substr(i,columns)); + out_string("\n"); + i <- i + columns; + } + pool; + out_string("\n"); + self; + } + ) ) + }; + + num_cells() : Int { + population_map.length() + }; + + cell(position : Int) : String { + if board_size - 1 < position then + " " + else + population_map.substr(position, 1) + fi + }; + + north(position : Int): String { + if (position - columns) < 0 then + " " + else + cell(position - columns) + fi + }; + + south(position : Int): String { + if board_size < (position + columns) then + " " + else + cell(position + columns) + fi + }; + + east(position : Int): String { + if (((position + 1) /columns ) * columns) = (position + 1) then + " " + else + cell(position + 1) + fi + }; + + west(position : Int): String { + if position = 0 then + " " + else + if ((position / columns) * columns) = position then + " " + else + cell(position - 1) + fi fi + }; + + northwest(position : Int): String { + if (position - columns) < 0 then + " " + else if ((position / columns) * columns) = position then + " " + else + north(position - 1) + fi fi + }; + + northeast(position : Int): String { + if (position - columns) < 0 then + " " + else if (((position + 1) /columns ) * columns) = (position + 1) then + " " + else + north(position + 1) + fi fi + }; + + southeast(position : Int): String { + if board_size < (position + columns) then + " " + else if (((position + 1) /columns ) * columns) = (position + 1) then + " " + else + south(position + 1) + fi fi + }; + + southwest(position : Int): String { + if board_size < (position + columns) then + " " + else if ((position / columns) * columns) = position then + " " + else + south(position - 1) + fi fi + }; + + neighbors(position: Int): Int { + { + if north(position) = "X" then 1 else 0 fi + + if south(position) = "X" then 1 else 0 fi + + if east(position) = "X" then 1 else 0 fi + + if west(position) = "X" then 1 else 0 fi + + if northeast(position) = "X" then 1 else 0 fi + + if northwest(position) = "X" then 1 else 0 fi + + if southeast(position) = "X" then 1 else 0 fi + + if southwest(position) = "X" then 1 else 0 fi; + } + }; + + +(* A cell will live if 2 or 3 of it's neighbors are alive. It dies + otherwise. A cell is born if only 3 of it's neighbors are alive. *) + + cell_at_next_evolution(position : Int) : String { + + if neighbors(position) = 3 then + "X" + else + if neighbors(position) = 2 then + if cell(position) = "X" then + "X" + else + "-" + fi + else + "-" + fi fi + }; + + + evolve() : CellularAutomaton { + (let position : Int <- 0 in + (let num : Int <- num_cells() in + (let temp : String in + { + while position < num loop + { + temp <- temp.concat(cell_at_next_evolution(position)); + position <- position + 1; + } + pool; + population_map <- temp; + self; + } + ) ) ) + }; + +(* This is where the background pattern is detremined by the user. More + patterns can be added as long as whoever adds keeps the board either + 3x5, 4x5, 5x5, 3x7, 7x4, 4x4 with the row first then column. *) + option(): String { + { + (let num : Int in + { + out_string("\nPlease chose a number:\n"); + out_string("\t1: A cross\n"); + out_string("\t2: A slash from the upper left to lower right\n"); + out_string("\t3: A slash from the upper right to lower left\n"); + out_string("\t4: An X\n"); + out_string("\t5: A greater than sign \n"); + out_string("\t6: A less than sign\n"); + out_string("\t7: Two greater than signs\n"); + out_string("\t8: Two less than signs\n"); + out_string("\t9: A 'V'\n"); + out_string("\t10: An inverse 'V'\n"); + out_string("\t11: Numbers 9 and 10 combined\n"); + out_string("\t12: A full grid\n"); + out_string("\t13: A 'T'\n"); + out_string("\t14: A plus '+'\n"); + out_string("\t15: A 'W'\n"); + out_string("\t16: An 'M'\n"); + out_string("\t17: An 'E'\n"); + out_string("\t18: A '3'\n"); + out_string("\t19: An 'O'\n"); + out_string("\t20: An '8'\n"); + out_string("\t21: An 'S'\n"); + out_string("Your choice => "); + num <- in_int(); + out_string("\n"); + if num = 1 then + " XX XXXX XXXX XX " + else if num = 2 then + " X X X X X " + else if num = 3 then + "X X X X X" + else if num = 4 then + "X X X X X X X X X" + else if num = 5 then + "X X X X X " + else if num = 6 then + " X X X X X" + else if num = 7 then + "X X X XX X " + else if num = 8 then + " X XX X X X " + else if num = 9 then + "X X X X X " + else if num = 10 then + " X X X X X" + else if num = 11 then + "X X X X X X X X" + else if num = 12 then + "XXXXXXXXXXXXXXXXXXXXXXXXX" + else if num = 13 then + "XXXXX X X X X " + else if num = 14 then + " X X XXXXX X X " + else if num = 15 then + "X X X X X X X " + else if num = 16 then + " X X X X X X X" + else if num = 17 then + "XXXXX X XXXXX X XXXX" + else if num = 18 then + "XXX X X X X XXXX " + else if num = 19 then + " XX X XX X XX " + else if num = 20 then + " XX X XX X XX X XX X XX " + else if num = 21 then + " XXXX X XX X XXXX " + else + " " + fi fi fi fi fi fi fi fi fi fi fi fi fi fi fi fi fi fi fi fi fi; + } + ); + } + }; + + + + + prompt() : Bool { + { + (let ans : String in + { + out_string("Would you like to continue with the next generation? \n"); + out_string("Please use lowercase y or n for your answer [y]: "); + ans <- in_string(); + out_string("\n"); + if ans = "n" then + false + else + true + fi; + } + ); + } + }; + + + prompt2() : Bool { + (let ans : String in + { + out_string("\n\n"); + out_string("Would you like to choose a background pattern? \n"); + out_string("Please use lowercase y or n for your answer [n]: "); + ans <- in_string(); + if ans = "y" then + true + else + false + fi; + } + ) + }; + + +}; + +class Main inherits CellularAutomaton { + cells : CellularAutomaton; + + main() : Main { + { + (let continue : Bool in + (let choice : String in + { + out_string("Welcome to the Game of Life.\n"); + out_string("There are many initial states to choose from. \n"); + while prompt2() loop + { + continue <- true; + choice <- option(); + cells <- (new CellularAutomaton).init(choice); + cells.print(); + while continue loop + if prompt() then + { + cells.evolve(); + cells.print(); + } + else + continue <- false + fi + pool; + } + pool; + self; + } ) ); } + }; +}; + diff --git a/tests/codegen/life_input.txt b/tests/codegen/life_input.txt index 07e016726..1dfbde620 100644 --- a/tests/codegen/life_input.txt +++ b/tests/codegen/life_input.txt @@ -1,66 +1,66 @@ -y -1 -n -y -2 -n -y -3 -n -y -4 -n -y -5 -n -y -6 -n -y -7 -n -y -8 -n -y -9 -n -y -10 -n -y -11 -n -y -12 -n -y -13 -n -y -14 -n -y -15 -n -y -16 -n -y -17 -n -y -18 -n -y -19 -n -y -20 -n -y -21 -y -y -n +y +1 +n +y +2 +n +y +3 +n +y +4 +n +y +5 +n +y +6 +n +y +7 +n +y +8 +n +y +9 +n +y +10 +n +y +11 +n +y +12 +n +y +13 +n +y +14 +n +y +15 +n +y +16 +n +y +17 +n +y +18 +n +y +19 +n +y +20 +n +y +21 +y +y +n n \ No newline at end of file diff --git a/tests/codegen/life_output.txt b/tests/codegen/life_output.txt index 5a9b9f73d..e804b2382 100644 --- a/tests/codegen/life_output.txt +++ b/tests/codegen/life_output.txt @@ -1,778 +1,778 @@ -Welcome to the Game of Life. -There are many initial states to choose from. - - -Would you like to choose a background pattern? -Please use lowercase y or n for your answer [n]: -Please chose a number: - 1: A cross - 2: A slash from the upper left to lower right - 3: A slash from the upper right to lower left - 4: An X - 5: A greater than sign - 6: A less than sign - 7: Two greater than signs - 8: Two less than signs - 9: A 'V' - 10: An inverse 'V' - 11: Numbers 9 and 10 combined - 12: A full grid - 13: A 'T' - 14: A plus '+' - 15: A 'W' - 16: An 'M' - 17: An 'E' - 18: A '3' - 19: An 'O' - 20: An '8' - 21: An 'S' -Your choice => - - XX -XXXX -XXXX - XX - -Would you like to continue with the next generation? -Please use lowercase y or n for your answer [y]: - - -Would you like to choose a background pattern? -Please use lowercase y or n for your answer [n]: -Please chose a number: - 1: A cross - 2: A slash from the upper left to lower right - 3: A slash from the upper right to lower left - 4: An X - 5: A greater than sign - 6: A less than sign - 7: Two greater than signs - 8: Two less than signs - 9: A 'V' - 10: An inverse 'V' - 11: Numbers 9 and 10 combined - 12: A full grid - 13: A 'T' - 14: A plus '+' - 15: A 'W' - 16: An 'M' - 17: An 'E' - 18: A '3' - 19: An 'O' - 20: An '8' - 21: An 'S' -Your choice => - - X - X - X - X -X - -Would you like to continue with the next generation? -Please use lowercase y or n for your answer [y]: - - -Would you like to choose a background pattern? -Please use lowercase y or n for your answer [n]: -Please chose a number: - 1: A cross - 2: A slash from the upper left to lower right - 3: A slash from the upper right to lower left - 4: An X - 5: A greater than sign - 6: A less than sign - 7: Two greater than signs - 8: Two less than signs - 9: A 'V' - 10: An inverse 'V' - 11: Numbers 9 and 10 combined - 12: A full grid - 13: A 'T' - 14: A plus '+' - 15: A 'W' - 16: An 'M' - 17: An 'E' - 18: A '3' - 19: An 'O' - 20: An '8' - 21: An 'S' -Your choice => - -X - X - X - X - X - -Would you like to continue with the next generation? -Please use lowercase y or n for your answer [y]: - - -Would you like to choose a background pattern? -Please use lowercase y or n for your answer [n]: -Please chose a number: - 1: A cross - 2: A slash from the upper left to lower right - 3: A slash from the upper right to lower left - 4: An X - 5: A greater than sign - 6: A less than sign - 7: Two greater than signs - 8: Two less than signs - 9: A 'V' - 10: An inverse 'V' - 11: Numbers 9 and 10 combined - 12: A full grid - 13: A 'T' - 14: A plus '+' - 15: A 'W' - 16: An 'M' - 17: An 'E' - 18: A '3' - 19: An 'O' - 20: An '8' - 21: An 'S' -Your choice => - -X X - X X - X - X X -X X - -Would you like to continue with the next generation? -Please use lowercase y or n for your answer [y]: - - -Would you like to choose a background pattern? -Please use lowercase y or n for your answer [n]: -Please chose a number: - 1: A cross - 2: A slash from the upper left to lower right - 3: A slash from the upper right to lower left - 4: An X - 5: A greater than sign - 6: A less than sign - 7: Two greater than signs - 8: Two less than signs - 9: A 'V' - 10: An inverse 'V' - 11: Numbers 9 and 10 combined - 12: A full grid - 13: A 'T' - 14: A plus '+' - 15: A 'W' - 16: An 'M' - 17: An 'E' - 18: A '3' - 19: An 'O' - 20: An '8' - 21: An 'S' -Your choice => - -X - X - X - X -X - -Would you like to continue with the next generation? -Please use lowercase y or n for your answer [y]: - - -Would you like to choose a background pattern? -Please use lowercase y or n for your answer [n]: -Please chose a number: - 1: A cross - 2: A slash from the upper left to lower right - 3: A slash from the upper right to lower left - 4: An X - 5: A greater than sign - 6: A less than sign - 7: Two greater than signs - 8: Two less than signs - 9: A 'V' - 10: An inverse 'V' - 11: Numbers 9 and 10 combined - 12: A full grid - 13: A 'T' - 14: A plus '+' - 15: A 'W' - 16: An 'M' - 17: An 'E' - 18: A '3' - 19: An 'O' - 20: An '8' - 21: An 'S' -Your choice => - - X - X - X - X - X - -Would you like to continue with the next generation? -Please use lowercase y or n for your answer [y]: - - -Would you like to choose a background pattern? -Please use lowercase y or n for your answer [n]: -Please chose a number: - 1: A cross - 2: A slash from the upper left to lower right - 3: A slash from the upper right to lower left - 4: An X - 5: A greater than sign - 6: A less than sign - 7: Two greater than signs - 8: Two less than signs - 9: A 'V' - 10: An inverse 'V' - 11: Numbers 9 and 10 combined - 12: A full grid - 13: A 'T' - 14: A plus '+' - 15: A 'W' - 16: An 'M' - 17: An 'E' - 18: A '3' - 19: An 'O' - 20: An '8' - 21: An 'S' -Your choice => - -X X - X X -X X - - -Would you like to continue with the next generation? -Please use lowercase y or n for your answer [y]: - - -Would you like to choose a background pattern? -Please use lowercase y or n for your answer [n]: -Please chose a number: - 1: A cross - 2: A slash from the upper left to lower right - 3: A slash from the upper right to lower left - 4: An X - 5: A greater than sign - 6: A less than sign - 7: Two greater than signs - 8: Two less than signs - 9: A 'V' - 10: An inverse 'V' - 11: Numbers 9 and 10 combined - 12: A full grid - 13: A 'T' - 14: A plus '+' - 15: A 'W' - 16: An 'M' - 17: An 'E' - 18: A '3' - 19: An 'O' - 20: An '8' - 21: An 'S' -Your choice => - - X X -X X - X X - - -Would you like to continue with the next generation? -Please use lowercase y or n for your answer [y]: - - -Would you like to choose a background pattern? -Please use lowercase y or n for your answer [n]: -Please chose a number: - 1: A cross - 2: A slash from the upper left to lower right - 3: A slash from the upper right to lower left - 4: An X - 5: A greater than sign - 6: A less than sign - 7: Two greater than signs - 8: Two less than signs - 9: A 'V' - 10: An inverse 'V' - 11: Numbers 9 and 10 combined - 12: A full grid - 13: A 'T' - 14: A plus '+' - 15: A 'W' - 16: An 'M' - 17: An 'E' - 18: A '3' - 19: An 'O' - 20: An '8' - 21: An 'S' -Your choice => - -X X - X X - X - -Would you like to continue with the next generation? -Please use lowercase y or n for your answer [y]: - - -Would you like to choose a background pattern? -Please use lowercase y or n for your answer [n]: -Please chose a number: - 1: A cross - 2: A slash from the upper left to lower right - 3: A slash from the upper right to lower left - 4: An X - 5: A greater than sign - 6: A less than sign - 7: Two greater than signs - 8: Two less than signs - 9: A 'V' - 10: An inverse 'V' - 11: Numbers 9 and 10 combined - 12: A full grid - 13: A 'T' - 14: A plus '+' - 15: A 'W' - 16: An 'M' - 17: An 'E' - 18: A '3' - 19: An 'O' - 20: An '8' - 21: An 'S' -Your choice => - - X - X X -X X - -Would you like to continue with the next generation? -Please use lowercase y or n for your answer [y]: - - -Would you like to choose a background pattern? -Please use lowercase y or n for your answer [n]: -Please chose a number: - 1: A cross - 2: A slash from the upper left to lower right - 3: A slash from the upper right to lower left - 4: An X - 5: A greater than sign - 6: A less than sign - 7: Two greater than signs - 8: Two less than signs - 9: A 'V' - 10: An inverse 'V' - 11: Numbers 9 and 10 combined - 12: A full grid - 13: A 'T' - 14: A plus '+' - 15: A 'W' - 16: An 'M' - 17: An 'E' - 18: A '3' - 19: An 'O' - 20: An '8' - 21: An 'S' -Your choice => - -X X X - X X -X X X - -Would you like to continue with the next generation? -Please use lowercase y or n for your answer [y]: - - -Would you like to choose a background pattern? -Please use lowercase y or n for your answer [n]: -Please chose a number: - 1: A cross - 2: A slash from the upper left to lower right - 3: A slash from the upper right to lower left - 4: An X - 5: A greater than sign - 6: A less than sign - 7: Two greater than signs - 8: Two less than signs - 9: A 'V' - 10: An inverse 'V' - 11: Numbers 9 and 10 combined - 12: A full grid - 13: A 'T' - 14: A plus '+' - 15: A 'W' - 16: An 'M' - 17: An 'E' - 18: A '3' - 19: An 'O' - 20: An '8' - 21: An 'S' -Your choice => - -XXXXX -XXXXX -XXXXX -XXXXX -XXXXX - -Would you like to continue with the next generation? -Please use lowercase y or n for your answer [y]: - - -Would you like to choose a background pattern? -Please use lowercase y or n for your answer [n]: -Please chose a number: - 1: A cross - 2: A slash from the upper left to lower right - 3: A slash from the upper right to lower left - 4: An X - 5: A greater than sign - 6: A less than sign - 7: Two greater than signs - 8: Two less than signs - 9: A 'V' - 10: An inverse 'V' - 11: Numbers 9 and 10 combined - 12: A full grid - 13: A 'T' - 14: A plus '+' - 15: A 'W' - 16: An 'M' - 17: An 'E' - 18: A '3' - 19: An 'O' - 20: An '8' - 21: An 'S' -Your choice => - -XXXXX - X - X - X - X - -Would you like to continue with the next generation? -Please use lowercase y or n for your answer [y]: - - -Would you like to choose a background pattern? -Please use lowercase y or n for your answer [n]: -Please chose a number: - 1: A cross - 2: A slash from the upper left to lower right - 3: A slash from the upper right to lower left - 4: An X - 5: A greater than sign - 6: A less than sign - 7: Two greater than signs - 8: Two less than signs - 9: A 'V' - 10: An inverse 'V' - 11: Numbers 9 and 10 combined - 12: A full grid - 13: A 'T' - 14: A plus '+' - 15: A 'W' - 16: An 'M' - 17: An 'E' - 18: A '3' - 19: An 'O' - 20: An '8' - 21: An 'S' -Your choice => - - X - X -XXXXX - X - X - -Would you like to continue with the next generation? -Please use lowercase y or n for your answer [y]: - - -Would you like to choose a background pattern? -Please use lowercase y or n for your answer [n]: -Please chose a number: - 1: A cross - 2: A slash from the upper left to lower right - 3: A slash from the upper right to lower left - 4: An X - 5: A greater than sign - 6: A less than sign - 7: Two greater than signs - 8: Two less than signs - 9: A 'V' - 10: An inverse 'V' - 11: Numbers 9 and 10 combined - 12: A full grid - 13: A 'T' - 14: A plus '+' - 15: A 'W' - 16: An 'M' - 17: An 'E' - 18: A '3' - 19: An 'O' - 20: An '8' - 21: An 'S' -Your choice => - -X X - X X X - X X - -Would you like to continue with the next generation? -Please use lowercase y or n for your answer [y]: - - -Would you like to choose a background pattern? -Please use lowercase y or n for your answer [n]: -Please chose a number: - 1: A cross - 2: A slash from the upper left to lower right - 3: A slash from the upper right to lower left - 4: An X - 5: A greater than sign - 6: A less than sign - 7: Two greater than signs - 8: Two less than signs - 9: A 'V' - 10: An inverse 'V' - 11: Numbers 9 and 10 combined - 12: A full grid - 13: A 'T' - 14: A plus '+' - 15: A 'W' - 16: An 'M' - 17: An 'E' - 18: A '3' - 19: An 'O' - 20: An '8' - 21: An 'S' -Your choice => - - X X - X X X -X X - -Would you like to continue with the next generation? -Please use lowercase y or n for your answer [y]: - - -Would you like to choose a background pattern? -Please use lowercase y or n for your answer [n]: -Please chose a number: - 1: A cross - 2: A slash from the upper left to lower right - 3: A slash from the upper right to lower left - 4: An X - 5: A greater than sign - 6: A less than sign - 7: Two greater than signs - 8: Two less than signs - 9: A 'V' - 10: An inverse 'V' - 11: Numbers 9 and 10 combined - 12: A full grid - 13: A 'T' - 14: A plus '+' - 15: A 'W' - 16: An 'M' - 17: An 'E' - 18: A '3' - 19: An 'O' - 20: An '8' - 21: An 'S' -Your choice => - -XXXX -X -X -XXXX -X -X -XXXX - -Would you like to continue with the next generation? -Please use lowercase y or n for your answer [y]: - - -Would you like to choose a background pattern? -Please use lowercase y or n for your answer [n]: -Please chose a number: - 1: A cross - 2: A slash from the upper left to lower right - 3: A slash from the upper right to lower left - 4: An X - 5: A greater than sign - 6: A less than sign - 7: Two greater than signs - 8: Two less than signs - 9: A 'V' - 10: An inverse 'V' - 11: Numbers 9 and 10 combined - 12: A full grid - 13: A 'T' - 14: A plus '+' - 15: A 'W' - 16: An 'M' - 17: An 'E' - 18: A '3' - 19: An 'O' - 20: An '8' - 21: An 'S' -Your choice => - -XXX - X - X - X - X - X -XXX - -Would you like to continue with the next generation? -Please use lowercase y or n for your answer [y]: - - -Would you like to choose a background pattern? -Please use lowercase y or n for your answer [n]: -Please chose a number: - 1: A cross - 2: A slash from the upper left to lower right - 3: A slash from the upper right to lower left - 4: An X - 5: A greater than sign - 6: A less than sign - 7: Two greater than signs - 8: Two less than signs - 9: A 'V' - 10: An inverse 'V' - 11: Numbers 9 and 10 combined - 12: A full grid - 13: A 'T' - 14: A plus '+' - 15: A 'W' - 16: An 'M' - 17: An 'E' - 18: A '3' - 19: An 'O' - 20: An '8' - 21: An 'S' -Your choice => - - XX -X X -X X - XX - -Would you like to continue with the next generation? -Please use lowercase y or n for your answer [y]: - - -Would you like to choose a background pattern? -Please use lowercase y or n for your answer [n]: -Please chose a number: - 1: A cross - 2: A slash from the upper left to lower right - 3: A slash from the upper right to lower left - 4: An X - 5: A greater than sign - 6: A less than sign - 7: Two greater than signs - 8: Two less than signs - 9: A 'V' - 10: An inverse 'V' - 11: Numbers 9 and 10 combined - 12: A full grid - 13: A 'T' - 14: A plus '+' - 15: A 'W' - 16: An 'M' - 17: An 'E' - 18: A '3' - 19: An 'O' - 20: An '8' - 21: An 'S' -Your choice => - - XX -X X -X X - XX -X X -X X - XX - -Would you like to continue with the next generation? -Please use lowercase y or n for your answer [y]: - - -Would you like to choose a background pattern? -Please use lowercase y or n for your answer [n]: -Please chose a number: - 1: A cross - 2: A slash from the upper left to lower right - 3: A slash from the upper right to lower left - 4: An X - 5: A greater than sign - 6: A less than sign - 7: Two greater than signs - 8: Two less than signs - 9: A 'V' - 10: An inverse 'V' - 11: Numbers 9 and 10 combined - 12: A full grid - 13: A 'T' - 14: A plus '+' - 15: A 'W' - 16: An 'M' - 17: An 'E' - 18: A '3' - 19: An 'O' - 20: An '8' - 21: An 'S' -Your choice => - - XXX -X -X - XX - X - X -XXX - -Would you like to continue with the next generation? -Please use lowercase y or n for your answer [y]: - --XX- -X-X- -X--- --XX- ----X --X-X --XX- - -Would you like to continue with the next generation? -Please use lowercase y or n for your answer [y]: - --XX- -X-X- -X-X- --XX- --X-X --X-X --XX- - -Would you like to continue with the next generation? -Please use lowercase y or n for your answer [y]: - - -Would you like to choose a background pattern? +Welcome to the Game of Life. +There are many initial states to choose from. + + +Would you like to choose a background pattern? +Please use lowercase y or n for your answer [n]: +Please chose a number: + 1: A cross + 2: A slash from the upper left to lower right + 3: A slash from the upper right to lower left + 4: An X + 5: A greater than sign + 6: A less than sign + 7: Two greater than signs + 8: Two less than signs + 9: A 'V' + 10: An inverse 'V' + 11: Numbers 9 and 10 combined + 12: A full grid + 13: A 'T' + 14: A plus '+' + 15: A 'W' + 16: An 'M' + 17: An 'E' + 18: A '3' + 19: An 'O' + 20: An '8' + 21: An 'S' +Your choice => + + XX +XXXX +XXXX + XX + +Would you like to continue with the next generation? +Please use lowercase y or n for your answer [y]: + + +Would you like to choose a background pattern? +Please use lowercase y or n for your answer [n]: +Please chose a number: + 1: A cross + 2: A slash from the upper left to lower right + 3: A slash from the upper right to lower left + 4: An X + 5: A greater than sign + 6: A less than sign + 7: Two greater than signs + 8: Two less than signs + 9: A 'V' + 10: An inverse 'V' + 11: Numbers 9 and 10 combined + 12: A full grid + 13: A 'T' + 14: A plus '+' + 15: A 'W' + 16: An 'M' + 17: An 'E' + 18: A '3' + 19: An 'O' + 20: An '8' + 21: An 'S' +Your choice => + + X + X + X + X +X + +Would you like to continue with the next generation? +Please use lowercase y or n for your answer [y]: + + +Would you like to choose a background pattern? +Please use lowercase y or n for your answer [n]: +Please chose a number: + 1: A cross + 2: A slash from the upper left to lower right + 3: A slash from the upper right to lower left + 4: An X + 5: A greater than sign + 6: A less than sign + 7: Two greater than signs + 8: Two less than signs + 9: A 'V' + 10: An inverse 'V' + 11: Numbers 9 and 10 combined + 12: A full grid + 13: A 'T' + 14: A plus '+' + 15: A 'W' + 16: An 'M' + 17: An 'E' + 18: A '3' + 19: An 'O' + 20: An '8' + 21: An 'S' +Your choice => + +X + X + X + X + X + +Would you like to continue with the next generation? +Please use lowercase y or n for your answer [y]: + + +Would you like to choose a background pattern? +Please use lowercase y or n for your answer [n]: +Please chose a number: + 1: A cross + 2: A slash from the upper left to lower right + 3: A slash from the upper right to lower left + 4: An X + 5: A greater than sign + 6: A less than sign + 7: Two greater than signs + 8: Two less than signs + 9: A 'V' + 10: An inverse 'V' + 11: Numbers 9 and 10 combined + 12: A full grid + 13: A 'T' + 14: A plus '+' + 15: A 'W' + 16: An 'M' + 17: An 'E' + 18: A '3' + 19: An 'O' + 20: An '8' + 21: An 'S' +Your choice => + +X X + X X + X + X X +X X + +Would you like to continue with the next generation? +Please use lowercase y or n for your answer [y]: + + +Would you like to choose a background pattern? +Please use lowercase y or n for your answer [n]: +Please chose a number: + 1: A cross + 2: A slash from the upper left to lower right + 3: A slash from the upper right to lower left + 4: An X + 5: A greater than sign + 6: A less than sign + 7: Two greater than signs + 8: Two less than signs + 9: A 'V' + 10: An inverse 'V' + 11: Numbers 9 and 10 combined + 12: A full grid + 13: A 'T' + 14: A plus '+' + 15: A 'W' + 16: An 'M' + 17: An 'E' + 18: A '3' + 19: An 'O' + 20: An '8' + 21: An 'S' +Your choice => + +X + X + X + X +X + +Would you like to continue with the next generation? +Please use lowercase y or n for your answer [y]: + + +Would you like to choose a background pattern? +Please use lowercase y or n for your answer [n]: +Please chose a number: + 1: A cross + 2: A slash from the upper left to lower right + 3: A slash from the upper right to lower left + 4: An X + 5: A greater than sign + 6: A less than sign + 7: Two greater than signs + 8: Two less than signs + 9: A 'V' + 10: An inverse 'V' + 11: Numbers 9 and 10 combined + 12: A full grid + 13: A 'T' + 14: A plus '+' + 15: A 'W' + 16: An 'M' + 17: An 'E' + 18: A '3' + 19: An 'O' + 20: An '8' + 21: An 'S' +Your choice => + + X + X + X + X + X + +Would you like to continue with the next generation? +Please use lowercase y or n for your answer [y]: + + +Would you like to choose a background pattern? +Please use lowercase y or n for your answer [n]: +Please chose a number: + 1: A cross + 2: A slash from the upper left to lower right + 3: A slash from the upper right to lower left + 4: An X + 5: A greater than sign + 6: A less than sign + 7: Two greater than signs + 8: Two less than signs + 9: A 'V' + 10: An inverse 'V' + 11: Numbers 9 and 10 combined + 12: A full grid + 13: A 'T' + 14: A plus '+' + 15: A 'W' + 16: An 'M' + 17: An 'E' + 18: A '3' + 19: An 'O' + 20: An '8' + 21: An 'S' +Your choice => + +X X + X X +X X + + +Would you like to continue with the next generation? +Please use lowercase y or n for your answer [y]: + + +Would you like to choose a background pattern? +Please use lowercase y or n for your answer [n]: +Please chose a number: + 1: A cross + 2: A slash from the upper left to lower right + 3: A slash from the upper right to lower left + 4: An X + 5: A greater than sign + 6: A less than sign + 7: Two greater than signs + 8: Two less than signs + 9: A 'V' + 10: An inverse 'V' + 11: Numbers 9 and 10 combined + 12: A full grid + 13: A 'T' + 14: A plus '+' + 15: A 'W' + 16: An 'M' + 17: An 'E' + 18: A '3' + 19: An 'O' + 20: An '8' + 21: An 'S' +Your choice => + + X X +X X + X X + + +Would you like to continue with the next generation? +Please use lowercase y or n for your answer [y]: + + +Would you like to choose a background pattern? +Please use lowercase y or n for your answer [n]: +Please chose a number: + 1: A cross + 2: A slash from the upper left to lower right + 3: A slash from the upper right to lower left + 4: An X + 5: A greater than sign + 6: A less than sign + 7: Two greater than signs + 8: Two less than signs + 9: A 'V' + 10: An inverse 'V' + 11: Numbers 9 and 10 combined + 12: A full grid + 13: A 'T' + 14: A plus '+' + 15: A 'W' + 16: An 'M' + 17: An 'E' + 18: A '3' + 19: An 'O' + 20: An '8' + 21: An 'S' +Your choice => + +X X + X X + X + +Would you like to continue with the next generation? +Please use lowercase y or n for your answer [y]: + + +Would you like to choose a background pattern? +Please use lowercase y or n for your answer [n]: +Please chose a number: + 1: A cross + 2: A slash from the upper left to lower right + 3: A slash from the upper right to lower left + 4: An X + 5: A greater than sign + 6: A less than sign + 7: Two greater than signs + 8: Two less than signs + 9: A 'V' + 10: An inverse 'V' + 11: Numbers 9 and 10 combined + 12: A full grid + 13: A 'T' + 14: A plus '+' + 15: A 'W' + 16: An 'M' + 17: An 'E' + 18: A '3' + 19: An 'O' + 20: An '8' + 21: An 'S' +Your choice => + + X + X X +X X + +Would you like to continue with the next generation? +Please use lowercase y or n for your answer [y]: + + +Would you like to choose a background pattern? +Please use lowercase y or n for your answer [n]: +Please chose a number: + 1: A cross + 2: A slash from the upper left to lower right + 3: A slash from the upper right to lower left + 4: An X + 5: A greater than sign + 6: A less than sign + 7: Two greater than signs + 8: Two less than signs + 9: A 'V' + 10: An inverse 'V' + 11: Numbers 9 and 10 combined + 12: A full grid + 13: A 'T' + 14: A plus '+' + 15: A 'W' + 16: An 'M' + 17: An 'E' + 18: A '3' + 19: An 'O' + 20: An '8' + 21: An 'S' +Your choice => + +X X X + X X +X X X + +Would you like to continue with the next generation? +Please use lowercase y or n for your answer [y]: + + +Would you like to choose a background pattern? +Please use lowercase y or n for your answer [n]: +Please chose a number: + 1: A cross + 2: A slash from the upper left to lower right + 3: A slash from the upper right to lower left + 4: An X + 5: A greater than sign + 6: A less than sign + 7: Two greater than signs + 8: Two less than signs + 9: A 'V' + 10: An inverse 'V' + 11: Numbers 9 and 10 combined + 12: A full grid + 13: A 'T' + 14: A plus '+' + 15: A 'W' + 16: An 'M' + 17: An 'E' + 18: A '3' + 19: An 'O' + 20: An '8' + 21: An 'S' +Your choice => + +XXXXX +XXXXX +XXXXX +XXXXX +XXXXX + +Would you like to continue with the next generation? +Please use lowercase y or n for your answer [y]: + + +Would you like to choose a background pattern? +Please use lowercase y or n for your answer [n]: +Please chose a number: + 1: A cross + 2: A slash from the upper left to lower right + 3: A slash from the upper right to lower left + 4: An X + 5: A greater than sign + 6: A less than sign + 7: Two greater than signs + 8: Two less than signs + 9: A 'V' + 10: An inverse 'V' + 11: Numbers 9 and 10 combined + 12: A full grid + 13: A 'T' + 14: A plus '+' + 15: A 'W' + 16: An 'M' + 17: An 'E' + 18: A '3' + 19: An 'O' + 20: An '8' + 21: An 'S' +Your choice => + +XXXXX + X + X + X + X + +Would you like to continue with the next generation? +Please use lowercase y or n for your answer [y]: + + +Would you like to choose a background pattern? +Please use lowercase y or n for your answer [n]: +Please chose a number: + 1: A cross + 2: A slash from the upper left to lower right + 3: A slash from the upper right to lower left + 4: An X + 5: A greater than sign + 6: A less than sign + 7: Two greater than signs + 8: Two less than signs + 9: A 'V' + 10: An inverse 'V' + 11: Numbers 9 and 10 combined + 12: A full grid + 13: A 'T' + 14: A plus '+' + 15: A 'W' + 16: An 'M' + 17: An 'E' + 18: A '3' + 19: An 'O' + 20: An '8' + 21: An 'S' +Your choice => + + X + X +XXXXX + X + X + +Would you like to continue with the next generation? +Please use lowercase y or n for your answer [y]: + + +Would you like to choose a background pattern? +Please use lowercase y or n for your answer [n]: +Please chose a number: + 1: A cross + 2: A slash from the upper left to lower right + 3: A slash from the upper right to lower left + 4: An X + 5: A greater than sign + 6: A less than sign + 7: Two greater than signs + 8: Two less than signs + 9: A 'V' + 10: An inverse 'V' + 11: Numbers 9 and 10 combined + 12: A full grid + 13: A 'T' + 14: A plus '+' + 15: A 'W' + 16: An 'M' + 17: An 'E' + 18: A '3' + 19: An 'O' + 20: An '8' + 21: An 'S' +Your choice => + +X X + X X X + X X + +Would you like to continue with the next generation? +Please use lowercase y or n for your answer [y]: + + +Would you like to choose a background pattern? +Please use lowercase y or n for your answer [n]: +Please chose a number: + 1: A cross + 2: A slash from the upper left to lower right + 3: A slash from the upper right to lower left + 4: An X + 5: A greater than sign + 6: A less than sign + 7: Two greater than signs + 8: Two less than signs + 9: A 'V' + 10: An inverse 'V' + 11: Numbers 9 and 10 combined + 12: A full grid + 13: A 'T' + 14: A plus '+' + 15: A 'W' + 16: An 'M' + 17: An 'E' + 18: A '3' + 19: An 'O' + 20: An '8' + 21: An 'S' +Your choice => + + X X + X X X +X X + +Would you like to continue with the next generation? +Please use lowercase y or n for your answer [y]: + + +Would you like to choose a background pattern? +Please use lowercase y or n for your answer [n]: +Please chose a number: + 1: A cross + 2: A slash from the upper left to lower right + 3: A slash from the upper right to lower left + 4: An X + 5: A greater than sign + 6: A less than sign + 7: Two greater than signs + 8: Two less than signs + 9: A 'V' + 10: An inverse 'V' + 11: Numbers 9 and 10 combined + 12: A full grid + 13: A 'T' + 14: A plus '+' + 15: A 'W' + 16: An 'M' + 17: An 'E' + 18: A '3' + 19: An 'O' + 20: An '8' + 21: An 'S' +Your choice => + +XXXX +X +X +XXXX +X +X +XXXX + +Would you like to continue with the next generation? +Please use lowercase y or n for your answer [y]: + + +Would you like to choose a background pattern? +Please use lowercase y or n for your answer [n]: +Please chose a number: + 1: A cross + 2: A slash from the upper left to lower right + 3: A slash from the upper right to lower left + 4: An X + 5: A greater than sign + 6: A less than sign + 7: Two greater than signs + 8: Two less than signs + 9: A 'V' + 10: An inverse 'V' + 11: Numbers 9 and 10 combined + 12: A full grid + 13: A 'T' + 14: A plus '+' + 15: A 'W' + 16: An 'M' + 17: An 'E' + 18: A '3' + 19: An 'O' + 20: An '8' + 21: An 'S' +Your choice => + +XXX + X + X + X + X + X +XXX + +Would you like to continue with the next generation? +Please use lowercase y or n for your answer [y]: + + +Would you like to choose a background pattern? +Please use lowercase y or n for your answer [n]: +Please chose a number: + 1: A cross + 2: A slash from the upper left to lower right + 3: A slash from the upper right to lower left + 4: An X + 5: A greater than sign + 6: A less than sign + 7: Two greater than signs + 8: Two less than signs + 9: A 'V' + 10: An inverse 'V' + 11: Numbers 9 and 10 combined + 12: A full grid + 13: A 'T' + 14: A plus '+' + 15: A 'W' + 16: An 'M' + 17: An 'E' + 18: A '3' + 19: An 'O' + 20: An '8' + 21: An 'S' +Your choice => + + XX +X X +X X + XX + +Would you like to continue with the next generation? +Please use lowercase y or n for your answer [y]: + + +Would you like to choose a background pattern? +Please use lowercase y or n for your answer [n]: +Please chose a number: + 1: A cross + 2: A slash from the upper left to lower right + 3: A slash from the upper right to lower left + 4: An X + 5: A greater than sign + 6: A less than sign + 7: Two greater than signs + 8: Two less than signs + 9: A 'V' + 10: An inverse 'V' + 11: Numbers 9 and 10 combined + 12: A full grid + 13: A 'T' + 14: A plus '+' + 15: A 'W' + 16: An 'M' + 17: An 'E' + 18: A '3' + 19: An 'O' + 20: An '8' + 21: An 'S' +Your choice => + + XX +X X +X X + XX +X X +X X + XX + +Would you like to continue with the next generation? +Please use lowercase y or n for your answer [y]: + + +Would you like to choose a background pattern? +Please use lowercase y or n for your answer [n]: +Please chose a number: + 1: A cross + 2: A slash from the upper left to lower right + 3: A slash from the upper right to lower left + 4: An X + 5: A greater than sign + 6: A less than sign + 7: Two greater than signs + 8: Two less than signs + 9: A 'V' + 10: An inverse 'V' + 11: Numbers 9 and 10 combined + 12: A full grid + 13: A 'T' + 14: A plus '+' + 15: A 'W' + 16: An 'M' + 17: An 'E' + 18: A '3' + 19: An 'O' + 20: An '8' + 21: An 'S' +Your choice => + + XXX +X +X + XX + X + X +XXX + +Would you like to continue with the next generation? +Please use lowercase y or n for your answer [y]: + +-XX- +X-X- +X--- +-XX- +---X +-X-X +-XX- + +Would you like to continue with the next generation? +Please use lowercase y or n for your answer [y]: + +-XX- +X-X- +X-X- +-XX- +-X-X +-X-X +-XX- + +Would you like to continue with the next generation? +Please use lowercase y or n for your answer [y]: + + +Would you like to choose a background pattern? Please use lowercase y or n for your answer [n]: \ No newline at end of file diff --git a/tests/codegen/list_output.txt b/tests/codegen/list_output.txt index fca724903..00d68ed23 100644 --- a/tests/codegen/list_output.txt +++ b/tests/codegen/list_output.txt @@ -1,5 +1,5 @@ -5 4 3 2 1 -4 3 2 1 -3 2 1 -2 1 -1 +5 4 3 2 1 +4 3 2 1 +3 2 1 +2 1 +1 diff --git a/tests/codegen/new_complex.cl b/tests/codegen/new_complex.cl index a4fe714ce..ad7035b56 100755 --- a/tests/codegen/new_complex.cl +++ b/tests/codegen/new_complex.cl @@ -1,79 +1,79 @@ -class Main inherits IO { - main() : IO { - (let c : Complex <- (new Complex).init(1, 1) in - { - -- trivially equal (see CoolAid) - if c.reflect_X() = c.reflect_0() - then out_string("=)\n") - else out_string("=(\n") - fi; - -- equal - if c.reflect_X().reflect_Y().equal(c.reflect_0()) - then out_string("=)\n") - else out_string("=(\n") - fi; - } - ) - }; -}; - -class Complex inherits IO { - x : Int; - y : Int; - - init(a : Int, b : Int) : Complex { - { - x = a; - y = b; - self; - } - }; - - print() : Object { - if y = 0 - then out_int(x) - else out_int(x).out_string("+").out_int(y).out_string("I") - fi - }; - - reflect_0() : Complex { - { - x = ~x; - y = ~y; - self; - } - }; - - reflect_X() : Complex { - { - y = ~y; - self; - } - }; - - reflect_Y() : Complex { - { - x = ~x; - self; - } - }; - - equal(d : Complex) : Bool { - if x = d.x_value() - then - if y = d.y_value() - then true - else false - fi - else false - fi - }; - - x_value() : Int { - x - }; - - y_value() : Int { - y - }; -}; +class Main inherits IO { + main() : IO { + (let c : Complex <- (new Complex).init(1, 1) in + { + -- trivially equal (see CoolAid) + if c.reflect_X() = c.reflect_0() + then out_string("=)\n") + else out_string("=(\n") + fi; + -- equal + if c.reflect_X().reflect_Y().equal(c.reflect_0()) + then out_string("=)\n") + else out_string("=(\n") + fi; + } + ) + }; +}; + +class Complex inherits IO { + x : Int; + y : Int; + + init(a : Int, b : Int) : Complex { + { + x = a; + y = b; + self; + } + }; + + print() : Object { + if y = 0 + then out_int(x) + else out_int(x).out_string("+").out_int(y).out_string("I") + fi + }; + + reflect_0() : Complex { + { + x = ~x; + y = ~y; + self; + } + }; + + reflect_X() : Complex { + { + y = ~y; + self; + } + }; + + reflect_Y() : Complex { + { + x = ~x; + self; + } + }; + + equal(d : Complex) : Bool { + if x = d.x_value() + then + if y = d.y_value() + then true + else false + fi + else false + fi + }; + + x_value() : Int { + x + }; + + y_value() : Int { + y + }; +}; diff --git a/tests/codegen/new_complex_output.txt b/tests/codegen/new_complex_output.txt index 0e8da112c..831c23af4 100644 --- a/tests/codegen/new_complex_output.txt +++ b/tests/codegen/new_complex_output.txt @@ -1,2 +1,2 @@ -=) -=) +=) +=) diff --git a/tests/codegen/palindrome.cl b/tests/codegen/palindrome.cl index 7f24789f9..6acbeb731 100755 --- a/tests/codegen/palindrome.cl +++ b/tests/codegen/palindrome.cl @@ -1,25 +1,25 @@ -class Main inherits IO { - pal(s : String) : Bool { - if s.length() = 0 - then true - else if s.length() = 1 - then true - else if s.substr(0, 1) = s.substr(s.length() - 1, 1) - then pal(s.substr(1, s.length() -2)) - else false - fi fi fi - }; - - i : Int; - - main() : IO { - { - i <- ~1; - out_string("enter a string\n"); - if pal(in_string()) - then out_string("that was a palindrome\n") - else out_string("that was not a palindrome\n") - fi; - } - }; -}; +class Main inherits IO { + pal(s : String) : Bool { + if s.length() = 0 + then true + else if s.length() = 1 + then true + else if s.substr(0, 1) = s.substr(s.length() - 1, 1) + then pal(s.substr(1, s.length() -2)) + else false + fi fi fi + }; + + i : Int; + + main() : IO { + { + i <- ~1; + out_string("enter a string\n"); + if pal(in_string()) + then out_string("that was a palindrome\n") + else out_string("that was not a palindrome\n") + fi; + } + }; +}; diff --git a/tests/codegen/palindrome_input.txt b/tests/codegen/palindrome_input.txt index 8e1b64142..c49a0114c 100644 --- a/tests/codegen/palindrome_input.txt +++ b/tests/codegen/palindrome_input.txt @@ -1 +1 @@ -anitalabalatina +anitalabalatina diff --git a/tests/codegen/palindrome_output.txt b/tests/codegen/palindrome_output.txt index 7a0095959..e93d36585 100644 --- a/tests/codegen/palindrome_output.txt +++ b/tests/codegen/palindrome_output.txt @@ -1,2 +1,2 @@ -enter a string -that was a palindrome +enter a string +that was a palindrome diff --git a/tests/codegen/primes_output.txt b/tests/codegen/primes_output.txt index a4d0fcb3f..cf5d78d49 100644 --- a/tests/codegen/primes_output.txt +++ b/tests/codegen/primes_output.txt @@ -1,96 +1,96 @@ -2 is trivially prime. -3 is prime. -5 is prime. -7 is prime. -11 is prime. -13 is prime. -17 is prime. -19 is prime. -23 is prime. -29 is prime. -31 is prime. -37 is prime. -41 is prime. -43 is prime. -47 is prime. -53 is prime. -59 is prime. -61 is prime. -67 is prime. -71 is prime. -73 is prime. -79 is prime. -83 is prime. -89 is prime. -97 is prime. -101 is prime. -103 is prime. -107 is prime. -109 is prime. -113 is prime. -127 is prime. -131 is prime. -137 is prime. -139 is prime. -149 is prime. -151 is prime. -157 is prime. -163 is prime. -167 is prime. -173 is prime. -179 is prime. -181 is prime. -191 is prime. -193 is prime. -197 is prime. -199 is prime. -211 is prime. -223 is prime. -227 is prime. -229 is prime. -233 is prime. -239 is prime. -241 is prime. -251 is prime. -257 is prime. -263 is prime. -269 is prime. -271 is prime. -277 is prime. -281 is prime. -283 is prime. -293 is prime. -307 is prime. -311 is prime. -313 is prime. -317 is prime. -331 is prime. -337 is prime. -347 is prime. -349 is prime. -353 is prime. -359 is prime. -367 is prime. -373 is prime. -379 is prime. -383 is prime. -389 is prime. -397 is prime. -401 is prime. -409 is prime. -419 is prime. -421 is prime. -431 is prime. -433 is prime. -439 is prime. -443 is prime. -449 is prime. -457 is prime. -461 is prime. -463 is prime. -467 is prime. -479 is prime. -487 is prime. -491 is prime. -499 is prime. -Abort called from class String +2 is trivially prime. +3 is prime. +5 is prime. +7 is prime. +11 is prime. +13 is prime. +17 is prime. +19 is prime. +23 is prime. +29 is prime. +31 is prime. +37 is prime. +41 is prime. +43 is prime. +47 is prime. +53 is prime. +59 is prime. +61 is prime. +67 is prime. +71 is prime. +73 is prime. +79 is prime. +83 is prime. +89 is prime. +97 is prime. +101 is prime. +103 is prime. +107 is prime. +109 is prime. +113 is prime. +127 is prime. +131 is prime. +137 is prime. +139 is prime. +149 is prime. +151 is prime. +157 is prime. +163 is prime. +167 is prime. +173 is prime. +179 is prime. +181 is prime. +191 is prime. +193 is prime. +197 is prime. +199 is prime. +211 is prime. +223 is prime. +227 is prime. +229 is prime. +233 is prime. +239 is prime. +241 is prime. +251 is prime. +257 is prime. +263 is prime. +269 is prime. +271 is prime. +277 is prime. +281 is prime. +283 is prime. +293 is prime. +307 is prime. +311 is prime. +313 is prime. +317 is prime. +331 is prime. +337 is prime. +347 is prime. +349 is prime. +353 is prime. +359 is prime. +367 is prime. +373 is prime. +379 is prime. +383 is prime. +389 is prime. +397 is prime. +401 is prime. +409 is prime. +419 is prime. +421 is prime. +431 is prime. +433 is prime. +439 is prime. +443 is prime. +449 is prime. +457 is prime. +461 is prime. +463 is prime. +467 is prime. +479 is prime. +487 is prime. +491 is prime. +499 is prime. +Abort called from class String diff --git a/tests/codegen/print-cool_output.txt b/tests/codegen/print-cool_output.txt index 2b58f8985..3c8cd620c 100644 --- a/tests/codegen/print-cool_output.txt +++ b/tests/codegen/print-cool_output.txt @@ -1 +1 @@ -cool +cool diff --git a/tests/codegen/sort-list_input.txt b/tests/codegen/sort-list_input.txt index f599e28b8..d43401489 100644 --- a/tests/codegen/sort-list_input.txt +++ b/tests/codegen/sort-list_input.txt @@ -1 +1 @@ -10 +10 diff --git a/tests/codegen/sort-list_output.txt b/tests/codegen/sort-list_output.txt index 9878d57ea..7b1d40452 100644 --- a/tests/codegen/sort-list_output.txt +++ b/tests/codegen/sort-list_output.txt @@ -1,10 +1,10 @@ -How many numbers to sort? 0 -1 -2 -3 -4 -5 -6 -7 -8 -9 +How many numbers to sort? 0 +1 +2 +3 +4 +5 +6 +7 +8 +9 diff --git a/tests/codegen_test.py b/tests/codegen_test.py index 48df768ff..e748bd2d1 100644 --- a/tests/codegen_test.py +++ b/tests/codegen_test.py @@ -1,17 +1,17 @@ -import pytest -import os -from utils import compare_outputs - -tests_dir = __file__.rpartition('/')[0] + '/codegen/' -tests = [(file) for file in os.listdir(tests_dir) if file.endswith('.cl')] - -# @pytest.mark.lexer -# @pytest.mark.parser -# @pytest.mark.semantic -@pytest.mark.codegen -@pytest.mark.ok -@pytest.mark.run(order=4) -@pytest.mark.parametrize("cool_file", tests) -def test_codegen(compiler_path, cool_file): - compare_outputs(compiler_path, tests_dir + cool_file, tests_dir + cool_file[:-3] + '_input.txt',\ +import pytest +import os +from utils import compare_outputs + +tests_dir = __file__.rpartition('/')[0] + '/codegen/' +tests = [(file) for file in os.listdir(tests_dir) if file.endswith('.cl')] + +# @pytest.mark.lexer +# @pytest.mark.parser +# @pytest.mark.semantic +@pytest.mark.codegen +@pytest.mark.ok +@pytest.mark.run(order=4) +@pytest.mark.parametrize("cool_file", tests) +def test_codegen(compiler_path, cool_file): + compare_outputs(compiler_path, tests_dir + cool_file, tests_dir + cool_file[:-3] + '_input.txt',\ tests_dir + cool_file[:-3] + '_output.txt') \ No newline at end of file diff --git a/tests/conftest.py b/tests/conftest.py index 1f44eeb72..561d8bafc 100644 --- a/tests/conftest.py +++ b/tests/conftest.py @@ -1,6 +1,6 @@ -import pytest -import os - -@pytest.fixture -def compiler_path(): +import pytest +import os + +@pytest.fixture +def compiler_path(): return os.path.abspath('./coolc.sh') \ No newline at end of file diff --git a/tests/lexer/comment1.cl b/tests/lexer/comment1.cl index 69533f23c..1b63af3c7 100644 --- a/tests/lexer/comment1.cl +++ b/tests/lexer/comment1.cl @@ -1,55 +1,55 @@ ---Any characters between two dashes “--” and the next newline ---(or EOF, if there is no next newline) are treated as comments - -(*(*(* -Comments may also be written by enclosing -text in (∗ . . . ∗). The latter form of comment may be nested. -Comments cannot cross file boundaries. -*)*)*) - -class Error() { - - (* There was once a comment, - that was quite long. - But, the reader soon discovered that - the comment was indeed longer than - previously assumed. Now, the reader - was in a real dilemma; is the comment - ever gonna end? If I stop reading, will - it end? - He started imagining all sorts of things. - He thought about heisenberg's cat and how - how that relates to the end of the sentence. - He thought to himself "I'm gonna stop reading". - "If I keep reading this comment, I'm gonna know - the fate of this sentence; That will be disastorous." - He knew that such a comment was gonna extend to - another file. It was too awesome to be contained in - a single file. And he would have kept reading too... - if only... - cool wasn't a super-duper-fab-awesomest language; - but cool is that language; - "This comment shall go not cross this file" said cool. - Alas! The reader could read no more. - There was once a comment, - that was quite long. - But, the reader soon discovered that - the comment was indeed longer than - previously assumed. Now, the reader - was in a real dilemma; is the comment - ever gonna end? If I stop reading, will - it end? - He started imagining all sorts of things. - He thought about heisenberg's cat and how - how that relates to the end of the sentence. - He thought to himself "I'm gonna stop reading". - "If I keep reading this comment, I'm gonna know - the fate of this sentence; That will be disastorous." - He knew that such a comment was gonna extend to - another file. It was too awesome to be contained in - a single file. And he would have kept reading too... - if only... - cool wasn't a super-duper-fab-awesomest language; - but cool is that language; - "This comment shall go not cross this file" said cool. +--Any characters between two dashes “--” and the next newline +--(or EOF, if there is no next newline) are treated as comments + +(*(*(* +Comments may also be written by enclosing +text in (∗ . . . ∗). The latter form of comment may be nested. +Comments cannot cross file boundaries. +*)*)*) + +class Error() { + + (* There was once a comment, + that was quite long. + But, the reader soon discovered that + the comment was indeed longer than + previously assumed. Now, the reader + was in a real dilemma; is the comment + ever gonna end? If I stop reading, will + it end? + He started imagining all sorts of things. + He thought about heisenberg's cat and how + how that relates to the end of the sentence. + He thought to himself "I'm gonna stop reading". + "If I keep reading this comment, I'm gonna know + the fate of this sentence; That will be disastorous." + He knew that such a comment was gonna extend to + another file. It was too awesome to be contained in + a single file. And he would have kept reading too... + if only... + cool wasn't a super-duper-fab-awesomest language; + but cool is that language; + "This comment shall go not cross this file" said cool. + Alas! The reader could read no more. + There was once a comment, + that was quite long. + But, the reader soon discovered that + the comment was indeed longer than + previously assumed. Now, the reader + was in a real dilemma; is the comment + ever gonna end? If I stop reading, will + it end? + He started imagining all sorts of things. + He thought about heisenberg's cat and how + how that relates to the end of the sentence. + He thought to himself "I'm gonna stop reading". + "If I keep reading this comment, I'm gonna know + the fate of this sentence; That will be disastorous." + He knew that such a comment was gonna extend to + another file. It was too awesome to be contained in + a single file. And he would have kept reading too... + if only... + cool wasn't a super-duper-fab-awesomest language; + but cool is that language; + "This comment shall go not cross this file" said cool. Alas! The reader could read no more. \ No newline at end of file diff --git a/tests/lexer/comment1.mips b/tests/lexer/comment1.mips new file mode 100644 index 000000000..3592fad3f --- /dev/null +++ b/tests/lexer/comment1.mips @@ -0,0 +1 @@ +GENERATED MIPS \ No newline at end of file diff --git a/tests/lexer/comment1_error.txt b/tests/lexer/comment1_error.txt index 9fd7b8d67..710483ee9 100644 --- a/tests/lexer/comment1_error.txt +++ b/tests/lexer/comment1_error.txt @@ -1 +1 @@ -(55, 46) - LexicographicError: EOF in comment +(55, 46) - LexicographicError: EOF in comment diff --git a/tests/lexer/iis1.cl b/tests/lexer/iis1.cl index 12cb52beb..ca6b68ac3 100644 --- a/tests/lexer/iis1.cl +++ b/tests/lexer/iis1.cl @@ -1,111 +1,111 @@ -(* Integers, Identifiers, and Special Notation *) - -0007 123 +1 -1 +90 -09 +11113 -4r *a *self* c++ -5! = 120, 2 + 2 = 5 or E = mc2; p + 1 @ p = 1: for x in range(len(b)) -new / <- <<==> {( Int: Objet, Bool; String.string SELF_TYPE isvoid }) -class Class if then else fi testing Testing ~007agent_bond james_007B0N3SS___ -loop pool while tRuE or noT faLsE let in case of ESAC - -(* -#3 INT_CONST 0007 -#3 INT_CONST 123 -#3 '+' -#3 INT_CONST 1 -#3 '-' -#3 INT_CONST 1 -#3 '+' -#3 INT_CONST 90 -#3 '-' -#3 INT_CONST 09 -#3 '+' -#3 INT_CONST 11113 -#3 '-' -#3 INT_CONST 4 -#3 OBJECTID r -#3 '*' -#3 OBJECTID a -#3 '*' -#3 OBJECTID self -#3 '*' -#3 OBJECTID c -#3 '+' -#3 '+' -#4 INT_CONST 5 -#4 ERROR "!" -#4 '=' -#4 INT_CONST 120 -#4 ',' -#4 INT_CONST 2 -#4 '+' -#4 INT_CONST 2 -#4 '=' -#4 INT_CONST 5 -#4 OBJECTID or -#4 TYPEID E -#4 '=' -#4 OBJECTID mc2 -#4 ';' -#4 OBJECTID p -#4 '+' -#4 INT_CONST 1 -#4 '@' -#4 OBJECTID p -#4 '=' -#4 INT_CONST 1 -#4 ':' -#4 OBJECTID for -#4 OBJECTID x -#4 IN -#4 OBJECTID range -#4 '(' -#4 OBJECTID len -#4 '(' -#4 OBJECTID b -#4 ')' -#4 ')' -#5 NEW -#5 '/' -#5 ASSIGN -#5 '<' -#5 LE -#5 DARROW -#5 '{' -#5 '(' -#5 TYPEID Int -#5 ':' -#5 TYPEID Objet -#5 ',' -#5 TYPEID Bool -#5 ';' -#5 TYPEID String -#5 '.' -#5 OBJECTID string -#5 TYPEID SELF_TYPE -#5 ISVOID -#5 '}' -#5 ')' -#6 CLASS -#6 CLASS -#6 IF -#6 THEN -#6 ELSE -#6 FI -#6 OBJECTID testing -#6 TYPEID Testing -#6 '~' -#6 INT_CONST 007 -#6 OBJECTID agent_bond -#6 OBJECTID james_007B0N3SS___ -#7 LOOP -#7 POOL -#7 WHILE -#7 BOOL_CONST true -#7 OBJECTID or -#7 NOT -#7 BOOL_CONST false -#7 LET -#7 IN -#7 CASE -#7 OF -#7 ESAC +(* Integers, Identifiers, and Special Notation *) + +0007 123 +1 -1 +90 -09 +11113 -4r *a *self* c++ +5! = 120, 2 + 2 = 5 or E = mc2; p + 1 @ p = 1: for x in range(len(b)) +new / <- <<==> {( Int: Objet, Bool; String.string SELF_TYPE isvoid }) +class Class if then else fi testing Testing ~007agent_bond james_007B0N3SS___ +loop pool while tRuE or noT faLsE let in case of ESAC + +(* +#3 INT_CONST 0007 +#3 INT_CONST 123 +#3 '+' +#3 INT_CONST 1 +#3 '-' +#3 INT_CONST 1 +#3 '+' +#3 INT_CONST 90 +#3 '-' +#3 INT_CONST 09 +#3 '+' +#3 INT_CONST 11113 +#3 '-' +#3 INT_CONST 4 +#3 OBJECTID r +#3 '*' +#3 OBJECTID a +#3 '*' +#3 OBJECTID self +#3 '*' +#3 OBJECTID c +#3 '+' +#3 '+' +#4 INT_CONST 5 +#4 ERROR "!" +#4 '=' +#4 INT_CONST 120 +#4 ',' +#4 INT_CONST 2 +#4 '+' +#4 INT_CONST 2 +#4 '=' +#4 INT_CONST 5 +#4 OBJECTID or +#4 TYPEID E +#4 '=' +#4 OBJECTID mc2 +#4 ';' +#4 OBJECTID p +#4 '+' +#4 INT_CONST 1 +#4 '@' +#4 OBJECTID p +#4 '=' +#4 INT_CONST 1 +#4 ':' +#4 OBJECTID for +#4 OBJECTID x +#4 IN +#4 OBJECTID range +#4 '(' +#4 OBJECTID len +#4 '(' +#4 OBJECTID b +#4 ')' +#4 ')' +#5 NEW +#5 '/' +#5 ASSIGN +#5 '<' +#5 LE +#5 DARROW +#5 '{' +#5 '(' +#5 TYPEID Int +#5 ':' +#5 TYPEID Objet +#5 ',' +#5 TYPEID Bool +#5 ';' +#5 TYPEID String +#5 '.' +#5 OBJECTID string +#5 TYPEID SELF_TYPE +#5 ISVOID +#5 '}' +#5 ')' +#6 CLASS +#6 CLASS +#6 IF +#6 THEN +#6 ELSE +#6 FI +#6 OBJECTID testing +#6 TYPEID Testing +#6 '~' +#6 INT_CONST 007 +#6 OBJECTID agent_bond +#6 OBJECTID james_007B0N3SS___ +#7 LOOP +#7 POOL +#7 WHILE +#7 BOOL_CONST true +#7 OBJECTID or +#7 NOT +#7 BOOL_CONST false +#7 LET +#7 IN +#7 CASE +#7 OF +#7 ESAC *) \ No newline at end of file diff --git a/tests/lexer/iis1.mips b/tests/lexer/iis1.mips new file mode 100644 index 000000000..3592fad3f --- /dev/null +++ b/tests/lexer/iis1.mips @@ -0,0 +1 @@ +GENERATED MIPS \ No newline at end of file diff --git a/tests/lexer/iis1_error.txt b/tests/lexer/iis1_error.txt index 9e6d66cac..12f62f1ba 100644 --- a/tests/lexer/iis1_error.txt +++ b/tests/lexer/iis1_error.txt @@ -1 +1 @@ -(4, 2) - LexicographicError: ERROR "!" +(4, 2) - LexicographicError: ERROR "!" diff --git a/tests/lexer/iis2.cl b/tests/lexer/iis2.cl index 9b25715d4..d42552494 100644 --- a/tests/lexer/iis2.cl +++ b/tests/lexer/iis2.cl @@ -1,120 +1,120 @@ -(* Integers, Identifiers, and Special Notation *) - -0007 123 +1 -1 +90 -09 +11113 -4r *a *self* c++ -class Class if then else fi testing Testing ~007agent_bond james_007bones___ - - -new / <- <<==> {( Int: Objet, Bool; String.string SELF_TYPE isvoid }) -loop pool while tRuE or noT faLsE let in case of ESAC - -factorial(5) = 120, 2 + 2 = 5? or E = mc2; p + 1 resto p = 1: (@ for x in range(len(b))) - -(* -#3 INT_CONST 0007 -#3 INT_CONST 123 -#3 '+' -#3 INT_CONST 1 -#3 '-' -#3 INT_CONST 1 -#3 '+' -#3 INT_CONST 90 -#3 '-' -#3 INT_CONST 09 -#3 '+' -#3 INT_CONST 11113 -#3 '-' -#3 INT_CONST 4 -#3 OBJECTID r -#3 '*' -#3 OBJECTID a -#3 '*' -#3 OBJECTID self -#3 '*' -#3 OBJECTID c -#3 '+' -#3 '+' -#4 CLASS -#4 CLASS -#4 IF -#4 THEN -#4 ELSE -#4 FI -#4 OBJECTID testing -#4 TYPEID Testing -#4 '~' -#4 INT_CONST 007 -#4 OBJECTID agent_bond -#4 OBJECTID james_007bones___ -#7 NEW -#7 '/' -#7 ASSIGN -#7 '<' -#7 LE -#7 DARROW -#7 '{' -#7 '(' -#7 TYPEID Int -#7 ':' -#7 TYPEID Objet -#7 ',' -#7 TYPEID Bool -#7 ';' -#7 TYPEID String -#7 '.' -#7 OBJECTID string -#7 TYPEID SELF_TYPE -#7 ISVOID -#7 '}' -#7 ')' -#8 LOOP -#8 POOL -#8 WHILE -#8 BOOL_CONST true -#8 OBJECTID or -#8 NOT -#8 BOOL_CONST false -#8 LET -#8 IN -#8 CASE -#8 OF -#8 ESAC -#10 OBJECTID factorial -#10 '(' -#10 INT_CONST 5 -#10 ')' -#10 '=' -#10 INT_CONST 120 -#10 ',' -#10 INT_CONST 2 -#10 '+' -#10 INT_CONST 2 -#10 '=' -#10 INT_CONST 5 -#10 ERROR "?" -#10 OBJECTID or -#10 TYPEID E -#10 '=' -#10 OBJECTID mc2 -#10 ';' -#10 OBJECTID p -#10 '+' -#10 INT_CONST 1 -#10 OBJECTID resto -#10 OBJECTID p -#10 '=' -#10 INT_CONST 1 -#10 ':' -#10 '(' -#10 '@' -#10 OBJECTID for -#10 OBJECTID x -#10 IN -#10 OBJECTID range -#10 '(' -#10 OBJECTID len -#10 '(' -#10 OBJECTID b -#10 ')' -#10 ')' -#10 ')' +(* Integers, Identifiers, and Special Notation *) + +0007 123 +1 -1 +90 -09 +11113 -4r *a *self* c++ +class Class if then else fi testing Testing ~007agent_bond james_007bones___ + + +new / <- <<==> {( Int: Objet, Bool; String.string SELF_TYPE isvoid }) +loop pool while tRuE or noT faLsE let in case of ESAC + +factorial(5) = 120, 2 + 2 = 5? or E = mc2; p + 1 resto p = 1: (@ for x in range(len(b))) + +(* +#3 INT_CONST 0007 +#3 INT_CONST 123 +#3 '+' +#3 INT_CONST 1 +#3 '-' +#3 INT_CONST 1 +#3 '+' +#3 INT_CONST 90 +#3 '-' +#3 INT_CONST 09 +#3 '+' +#3 INT_CONST 11113 +#3 '-' +#3 INT_CONST 4 +#3 OBJECTID r +#3 '*' +#3 OBJECTID a +#3 '*' +#3 OBJECTID self +#3 '*' +#3 OBJECTID c +#3 '+' +#3 '+' +#4 CLASS +#4 CLASS +#4 IF +#4 THEN +#4 ELSE +#4 FI +#4 OBJECTID testing +#4 TYPEID Testing +#4 '~' +#4 INT_CONST 007 +#4 OBJECTID agent_bond +#4 OBJECTID james_007bones___ +#7 NEW +#7 '/' +#7 ASSIGN +#7 '<' +#7 LE +#7 DARROW +#7 '{' +#7 '(' +#7 TYPEID Int +#7 ':' +#7 TYPEID Objet +#7 ',' +#7 TYPEID Bool +#7 ';' +#7 TYPEID String +#7 '.' +#7 OBJECTID string +#7 TYPEID SELF_TYPE +#7 ISVOID +#7 '}' +#7 ')' +#8 LOOP +#8 POOL +#8 WHILE +#8 BOOL_CONST true +#8 OBJECTID or +#8 NOT +#8 BOOL_CONST false +#8 LET +#8 IN +#8 CASE +#8 OF +#8 ESAC +#10 OBJECTID factorial +#10 '(' +#10 INT_CONST 5 +#10 ')' +#10 '=' +#10 INT_CONST 120 +#10 ',' +#10 INT_CONST 2 +#10 '+' +#10 INT_CONST 2 +#10 '=' +#10 INT_CONST 5 +#10 ERROR "?" +#10 OBJECTID or +#10 TYPEID E +#10 '=' +#10 OBJECTID mc2 +#10 ';' +#10 OBJECTID p +#10 '+' +#10 INT_CONST 1 +#10 OBJECTID resto +#10 OBJECTID p +#10 '=' +#10 INT_CONST 1 +#10 ':' +#10 '(' +#10 '@' +#10 OBJECTID for +#10 OBJECTID x +#10 IN +#10 OBJECTID range +#10 '(' +#10 OBJECTID len +#10 '(' +#10 OBJECTID b +#10 ')' +#10 ')' +#10 ')' *) \ No newline at end of file diff --git a/tests/lexer/iis2.mips b/tests/lexer/iis2.mips new file mode 100644 index 000000000..3592fad3f --- /dev/null +++ b/tests/lexer/iis2.mips @@ -0,0 +1 @@ +GENERATED MIPS \ No newline at end of file diff --git a/tests/lexer/iis2_error.txt b/tests/lexer/iis2_error.txt index 922391a9d..988d0286e 100644 --- a/tests/lexer/iis2_error.txt +++ b/tests/lexer/iis2_error.txt @@ -1 +1 @@ -(10, 30) - LexicographicError: ERROR "?" +(10, 30) - LexicographicError: ERROR "?" diff --git a/tests/lexer/iis3.cl b/tests/lexer/iis3.cl index 0b965ddea..dcd85f960 100644 --- a/tests/lexer/iis3.cl +++ b/tests/lexer/iis3.cl @@ -1,121 +1,121 @@ -(* Integers, Identifiers, and Special Notation *) - -factorial(5) = 120, 2 + 2 = 5 or E = mc^2; p + 1 @ p = 1: z for x in range(len(b))) - -loop pool while tRuE or noT faLsE let in case of ESAC - -new / <- <<==> {( Int: Objet, Bool; String.string SELF_TYPE isvoid }) - - -0007 123 +1 -1 +90 -09 +11113 -4r *a *self* c++ -class Class if then else fi testing Testing ~007agent_bond james_007bones___ - -(* -#3 OBJECTID factorial -#3 '(' -#3 INT_CONST 5 -#3 ')' -#3 '=' -#3 INT_CONST 120 -#3 ',' -#3 INT_CONST 2 -#3 '+' -#3 INT_CONST 2 -#3 '=' -#3 INT_CONST 5 -#3 OBJECTID or -#3 TYPEID E -#3 '=' -#3 OBJECTID mc -#3 ERROR "^" -#3 INT_CONST 2 -#3 ';' -#3 OBJECTID p -#3 '+' -#3 INT_CONST 1 -#3 '@' -#3 OBJECTID p -#3 '=' -#3 INT_CONST 1 -#3 ':' -#3 OBJECTID z -#3 OBJECTID for -#3 OBJECTID x -#3 IN -#3 OBJECTID range -#3 '(' -#3 OBJECTID len -#3 '(' -#3 OBJECTID b -#3 ')' -#3 ')' -#3 ')' -#5 LOOP -#5 POOL -#5 WHILE -#5 BOOL_CONST true -#5 OBJECTID or -#5 NOT -#5 BOOL_CONST false -#5 LET -#5 IN -#5 CASE -#5 OF -#5 ESAC -#7 NEW -#7 '/' -#7 ASSIGN -#7 '<' -#7 LE -#7 DARROW -#7 '{' -#7 '(' -#7 TYPEID Int -#7 ':' -#7 TYPEID Objet -#7 ',' -#7 TYPEID Bool -#7 ';' -#7 TYPEID String -#7 '.' -#7 OBJECTID string -#7 TYPEID SELF_TYPE -#7 ISVOID -#7 '}' -#7 ')' -#10 INT_CONST 0007 -#10 INT_CONST 123 -#10 '+' -#10 INT_CONST 1 -#10 '-' -#10 INT_CONST 1 -#10 '+' -#10 INT_CONST 90 -#10 '-' -#10 INT_CONST 09 -#10 '+' -#10 INT_CONST 11113 -#10 '-' -#10 INT_CONST 4 -#10 OBJECTID r -#10 '*' -#10 OBJECTID a -#10 '*' -#10 OBJECTID self -#10 '*' -#10 OBJECTID c -#10 '+' -#10 '+' -#11 CLASS -#11 CLASS -#11 IF -#11 THEN -#11 ELSE -#11 FI -#11 OBJECTID testing -#11 TYPEID Testing -#11 '~' -#11 INT_CONST 007 -#11 OBJECTID agent_bond -#11 OBJECTID james_007bones___ +(* Integers, Identifiers, and Special Notation *) + +factorial(5) = 120, 2 + 2 = 5 or E = mc^2; p + 1 @ p = 1: z for x in range(len(b))) + +loop pool while tRuE or noT faLsE let in case of ESAC + +new / <- <<==> {( Int: Objet, Bool; String.string SELF_TYPE isvoid }) + + +0007 123 +1 -1 +90 -09 +11113 -4r *a *self* c++ +class Class if then else fi testing Testing ~007agent_bond james_007bones___ + +(* +#3 OBJECTID factorial +#3 '(' +#3 INT_CONST 5 +#3 ')' +#3 '=' +#3 INT_CONST 120 +#3 ',' +#3 INT_CONST 2 +#3 '+' +#3 INT_CONST 2 +#3 '=' +#3 INT_CONST 5 +#3 OBJECTID or +#3 TYPEID E +#3 '=' +#3 OBJECTID mc +#3 ERROR "^" +#3 INT_CONST 2 +#3 ';' +#3 OBJECTID p +#3 '+' +#3 INT_CONST 1 +#3 '@' +#3 OBJECTID p +#3 '=' +#3 INT_CONST 1 +#3 ':' +#3 OBJECTID z +#3 OBJECTID for +#3 OBJECTID x +#3 IN +#3 OBJECTID range +#3 '(' +#3 OBJECTID len +#3 '(' +#3 OBJECTID b +#3 ')' +#3 ')' +#3 ')' +#5 LOOP +#5 POOL +#5 WHILE +#5 BOOL_CONST true +#5 OBJECTID or +#5 NOT +#5 BOOL_CONST false +#5 LET +#5 IN +#5 CASE +#5 OF +#5 ESAC +#7 NEW +#7 '/' +#7 ASSIGN +#7 '<' +#7 LE +#7 DARROW +#7 '{' +#7 '(' +#7 TYPEID Int +#7 ':' +#7 TYPEID Objet +#7 ',' +#7 TYPEID Bool +#7 ';' +#7 TYPEID String +#7 '.' +#7 OBJECTID string +#7 TYPEID SELF_TYPE +#7 ISVOID +#7 '}' +#7 ')' +#10 INT_CONST 0007 +#10 INT_CONST 123 +#10 '+' +#10 INT_CONST 1 +#10 '-' +#10 INT_CONST 1 +#10 '+' +#10 INT_CONST 90 +#10 '-' +#10 INT_CONST 09 +#10 '+' +#10 INT_CONST 11113 +#10 '-' +#10 INT_CONST 4 +#10 OBJECTID r +#10 '*' +#10 OBJECTID a +#10 '*' +#10 OBJECTID self +#10 '*' +#10 OBJECTID c +#10 '+' +#10 '+' +#11 CLASS +#11 CLASS +#11 IF +#11 THEN +#11 ELSE +#11 FI +#11 OBJECTID testing +#11 TYPEID Testing +#11 '~' +#11 INT_CONST 007 +#11 OBJECTID agent_bond +#11 OBJECTID james_007bones___ *) \ No newline at end of file diff --git a/tests/lexer/iis3.mips b/tests/lexer/iis3.mips new file mode 100644 index 000000000..3592fad3f --- /dev/null +++ b/tests/lexer/iis3.mips @@ -0,0 +1 @@ +GENERATED MIPS \ No newline at end of file diff --git a/tests/lexer/iis3_error.txt b/tests/lexer/iis3_error.txt index b001b6a71..3abc2b556 100644 --- a/tests/lexer/iis3_error.txt +++ b/tests/lexer/iis3_error.txt @@ -1 +1 @@ -(3, 40) - LexicographicError: ERROR "^" +(3, 40) - LexicographicError: ERROR "^" diff --git a/tests/lexer/iis4.cl b/tests/lexer/iis4.cl index 9e7a9cb62..5357ab734 100644 --- a/tests/lexer/iis4.cl +++ b/tests/lexer/iis4.cl @@ -1,120 +1,120 @@ -(* Integers, Identifiers, and Special Notation *) - -new / <- <<==> {( Int: Objet, Bool; String.string SELF_TYPE isvoid }) -0007 123 +1 -1 +90 -09 +11113 -4r *a *self* c++ - -factorial(5) = 120, 2 + 2 = 5 or E = mc2; p + 1 % p = 1: @.@ for x in range(len(b))~ - - -loop pool while tRuE or noT faLsE let in case of ESAC -class Class if then else fi testing Testing ~007agent_bond james_007bones___ - -(* -#3 NEW -#3 '/' -#3 ASSIGN -#3 '<' -#3 LE -#3 DARROW -#3 '{' -#3 '(' -#3 TYPEID Int -#3 ':' -#3 TYPEID Objet -#3 ',' -#3 TYPEID Bool -#3 ';' -#3 TYPEID String -#3 '.' -#3 OBJECTID string -#3 TYPEID SELF_TYPE -#3 ISVOID -#3 '}' -#3 ')' -#4 INT_CONST 0007 -#4 INT_CONST 123 -#4 '+' -#4 INT_CONST 1 -#4 '-' -#4 INT_CONST 1 -#4 '+' -#4 INT_CONST 90 -#4 '-' -#4 INT_CONST 09 -#4 '+' -#4 INT_CONST 11113 -#4 '-' -#4 INT_CONST 4 -#4 OBJECTID r -#4 '*' -#4 OBJECTID a -#4 '*' -#4 OBJECTID self -#4 '*' -#4 OBJECTID c -#4 '+' -#4 '+' -#6 OBJECTID factorial -#6 '(' -#6 INT_CONST 5 -#6 ')' -#6 '=' -#6 INT_CONST 120 -#6 ',' -#6 INT_CONST 2 -#6 '+' -#6 INT_CONST 2 -#6 '=' -#6 INT_CONST 5 -#6 OBJECTID or -#6 TYPEID E -#6 '=' -#6 OBJECTID mc2 -#6 ';' -#6 OBJECTID p -#6 '+' -#6 INT_CONST 1 -#6 ERROR "%" -#6 OBJECTID p -#6 '=' -#6 INT_CONST 1 -#6 ':' -#6 '@' -#6 '.' -#6 '@' -#6 OBJECTID for -#6 OBJECTID x -#6 IN -#6 OBJECTID range -#6 '(' -#6 OBJECTID len -#6 '(' -#6 OBJECTID b -#6 ')' -#6 ')' -#6 '~' -#9 LOOP -#9 POOL -#9 WHILE -#9 BOOL_CONST true -#9 OBJECTID or -#9 NOT -#9 BOOL_CONST false -#9 LET -#9 IN -#9 CASE -#9 OF -#9 ESAC -#10 CLASS -#10 CLASS -#10 IF -#10 THEN -#10 ELSE -#10 FI -#10 OBJECTID testing -#10 TYPEID Testing -#10 '~' -#10 INT_CONST 007 -#10 OBJECTID agent_bond -#10 OBJECTID james_007bones___ +(* Integers, Identifiers, and Special Notation *) + +new / <- <<==> {( Int: Objet, Bool; String.string SELF_TYPE isvoid }) +0007 123 +1 -1 +90 -09 +11113 -4r *a *self* c++ + +factorial(5) = 120, 2 + 2 = 5 or E = mc2; p + 1 % p = 1: @.@ for x in range(len(b))~ + + +loop pool while tRuE or noT faLsE let in case of ESAC +class Class if then else fi testing Testing ~007agent_bond james_007bones___ + +(* +#3 NEW +#3 '/' +#3 ASSIGN +#3 '<' +#3 LE +#3 DARROW +#3 '{' +#3 '(' +#3 TYPEID Int +#3 ':' +#3 TYPEID Objet +#3 ',' +#3 TYPEID Bool +#3 ';' +#3 TYPEID String +#3 '.' +#3 OBJECTID string +#3 TYPEID SELF_TYPE +#3 ISVOID +#3 '}' +#3 ')' +#4 INT_CONST 0007 +#4 INT_CONST 123 +#4 '+' +#4 INT_CONST 1 +#4 '-' +#4 INT_CONST 1 +#4 '+' +#4 INT_CONST 90 +#4 '-' +#4 INT_CONST 09 +#4 '+' +#4 INT_CONST 11113 +#4 '-' +#4 INT_CONST 4 +#4 OBJECTID r +#4 '*' +#4 OBJECTID a +#4 '*' +#4 OBJECTID self +#4 '*' +#4 OBJECTID c +#4 '+' +#4 '+' +#6 OBJECTID factorial +#6 '(' +#6 INT_CONST 5 +#6 ')' +#6 '=' +#6 INT_CONST 120 +#6 ',' +#6 INT_CONST 2 +#6 '+' +#6 INT_CONST 2 +#6 '=' +#6 INT_CONST 5 +#6 OBJECTID or +#6 TYPEID E +#6 '=' +#6 OBJECTID mc2 +#6 ';' +#6 OBJECTID p +#6 '+' +#6 INT_CONST 1 +#6 ERROR "%" +#6 OBJECTID p +#6 '=' +#6 INT_CONST 1 +#6 ':' +#6 '@' +#6 '.' +#6 '@' +#6 OBJECTID for +#6 OBJECTID x +#6 IN +#6 OBJECTID range +#6 '(' +#6 OBJECTID len +#6 '(' +#6 OBJECTID b +#6 ')' +#6 ')' +#6 '~' +#9 LOOP +#9 POOL +#9 WHILE +#9 BOOL_CONST true +#9 OBJECTID or +#9 NOT +#9 BOOL_CONST false +#9 LET +#9 IN +#9 CASE +#9 OF +#9 ESAC +#10 CLASS +#10 CLASS +#10 IF +#10 THEN +#10 ELSE +#10 FI +#10 OBJECTID testing +#10 TYPEID Testing +#10 '~' +#10 INT_CONST 007 +#10 OBJECTID agent_bond +#10 OBJECTID james_007bones___ *) \ No newline at end of file diff --git a/tests/lexer/iis4.mips b/tests/lexer/iis4.mips new file mode 100644 index 000000000..3592fad3f --- /dev/null +++ b/tests/lexer/iis4.mips @@ -0,0 +1 @@ +GENERATED MIPS \ No newline at end of file diff --git a/tests/lexer/iis4_error.txt b/tests/lexer/iis4_error.txt index f24076a8c..aab8f39c1 100644 --- a/tests/lexer/iis4_error.txt +++ b/tests/lexer/iis4_error.txt @@ -1 +1 @@ -(6, 49) - LexicographicError: ERROR "!" +(6, 49) - LexicographicError: ERROR "!" diff --git a/tests/lexer/iis5.cl b/tests/lexer/iis5.cl index d146c0547..f602488b9 100644 --- a/tests/lexer/iis5.cl +++ b/tests/lexer/iis5.cl @@ -1,121 +1,121 @@ -(* Integers, Identifiers, and Special Notation *) - - -loop pool while tRuE or noT faLsE let in case of ESAC -new / <- <<==> {( Int: Objet, Bool; String.string SELF_TYPE isvoid }) -class Class if then else fi testing Testing ~007agent_bond james_007bones___ - -factorial(5) = 120, 2 + 2 = 5 or E = mc2; p + 1 resto p = 1: [@.@ for x in range(len(b))] - -0007 123 +1 -1 +90 -09 +11113 -4r *a *self* c++ - -(* -#4 LOOP -#4 POOL -#4 WHILE -#4 BOOL_CONST true -#4 OBJECTID or -#4 NOT -#4 BOOL_CONST false -#4 LET -#4 IN -#4 CASE -#4 OF -#4 ESAC -#5 NEW -#5 '/' -#5 ASSIGN -#5 '<' -#5 LE -#5 DARROW -#5 '{' -#5 '(' -#5 TYPEID Int -#5 ':' -#5 TYPEID Objet -#5 ',' -#5 TYPEID Bool -#5 ';' -#5 TYPEID String -#5 '.' -#5 OBJECTID string -#5 TYPEID SELF_TYPE -#5 ISVOID -#5 '}' -#5 ')' -#6 CLASS -#6 CLASS -#6 IF -#6 THEN -#6 ELSE -#6 FI -#6 OBJECTID testing -#6 TYPEID Testing -#6 '~' -#6 INT_CONST 007 -#6 OBJECTID agent_bond -#6 OBJECTID james_007bones___ -#8 OBJECTID factorial -#8 '(' -#8 INT_CONST 5 -#8 ')' -#8 '=' -#8 INT_CONST 120 -#8 ',' -#8 INT_CONST 2 -#8 '+' -#8 INT_CONST 2 -#8 '=' -#8 INT_CONST 5 -#8 OBJECTID or -#8 TYPEID E -#8 '=' -#8 OBJECTID mc2 -#8 ';' -#8 OBJECTID p -#8 '+' -#8 INT_CONST 1 -#8 OBJECTID resto -#8 OBJECTID p -#8 '=' -#8 INT_CONST 1 -#8 ':' -#8 ERROR "[" -#8 '@' -#8 '.' -#8 '@' -#8 OBJECTID for -#8 OBJECTID x -#8 IN -#8 OBJECTID range -#8 '(' -#8 OBJECTID len -#8 '(' -#8 OBJECTID b -#8 ')' -#8 ')' -#8 ERROR "]" -#10 INT_CONST 0007 -#10 INT_CONST 123 -#10 '+' -#10 INT_CONST 1 -#10 '-' -#10 INT_CONST 1 -#10 '+' -#10 INT_CONST 90 -#10 '-' -#10 INT_CONST 09 -#10 '+' -#10 INT_CONST 11113 -#10 '-' -#10 INT_CONST 4 -#10 OBJECTID r -#10 '*' -#10 OBJECTID a -#10 '*' -#10 OBJECTID self -#10 '*' -#10 OBJECTID c -#10 '+' -#10 '+' -*) +(* Integers, Identifiers, and Special Notation *) + + +loop pool while tRuE or noT faLsE let in case of ESAC +new / <- <<==> {( Int: Objet, Bool; String.string SELF_TYPE isvoid }) +class Class if then else fi testing Testing ~007agent_bond james_007bones___ + +factorial(5) = 120, 2 + 2 = 5 or E = mc2; p + 1 resto p = 1: [@.@ for x in range(len(b))] + +0007 123 +1 -1 +90 -09 +11113 -4r *a *self* c++ + +(* +#4 LOOP +#4 POOL +#4 WHILE +#4 BOOL_CONST true +#4 OBJECTID or +#4 NOT +#4 BOOL_CONST false +#4 LET +#4 IN +#4 CASE +#4 OF +#4 ESAC +#5 NEW +#5 '/' +#5 ASSIGN +#5 '<' +#5 LE +#5 DARROW +#5 '{' +#5 '(' +#5 TYPEID Int +#5 ':' +#5 TYPEID Objet +#5 ',' +#5 TYPEID Bool +#5 ';' +#5 TYPEID String +#5 '.' +#5 OBJECTID string +#5 TYPEID SELF_TYPE +#5 ISVOID +#5 '}' +#5 ')' +#6 CLASS +#6 CLASS +#6 IF +#6 THEN +#6 ELSE +#6 FI +#6 OBJECTID testing +#6 TYPEID Testing +#6 '~' +#6 INT_CONST 007 +#6 OBJECTID agent_bond +#6 OBJECTID james_007bones___ +#8 OBJECTID factorial +#8 '(' +#8 INT_CONST 5 +#8 ')' +#8 '=' +#8 INT_CONST 120 +#8 ',' +#8 INT_CONST 2 +#8 '+' +#8 INT_CONST 2 +#8 '=' +#8 INT_CONST 5 +#8 OBJECTID or +#8 TYPEID E +#8 '=' +#8 OBJECTID mc2 +#8 ';' +#8 OBJECTID p +#8 '+' +#8 INT_CONST 1 +#8 OBJECTID resto +#8 OBJECTID p +#8 '=' +#8 INT_CONST 1 +#8 ':' +#8 ERROR "[" +#8 '@' +#8 '.' +#8 '@' +#8 OBJECTID for +#8 OBJECTID x +#8 IN +#8 OBJECTID range +#8 '(' +#8 OBJECTID len +#8 '(' +#8 OBJECTID b +#8 ')' +#8 ')' +#8 ERROR "]" +#10 INT_CONST 0007 +#10 INT_CONST 123 +#10 '+' +#10 INT_CONST 1 +#10 '-' +#10 INT_CONST 1 +#10 '+' +#10 INT_CONST 90 +#10 '-' +#10 INT_CONST 09 +#10 '+' +#10 INT_CONST 11113 +#10 '-' +#10 INT_CONST 4 +#10 OBJECTID r +#10 '*' +#10 OBJECTID a +#10 '*' +#10 OBJECTID self +#10 '*' +#10 OBJECTID c +#10 '+' +#10 '+' +*) diff --git a/tests/lexer/iis5.mips b/tests/lexer/iis5.mips new file mode 100644 index 000000000..3592fad3f --- /dev/null +++ b/tests/lexer/iis5.mips @@ -0,0 +1 @@ +GENERATED MIPS \ No newline at end of file diff --git a/tests/lexer/iis5_error.txt b/tests/lexer/iis5_error.txt index b3dbadcb6..9d6e1a738 100644 --- a/tests/lexer/iis5_error.txt +++ b/tests/lexer/iis5_error.txt @@ -1,2 +1,2 @@ -(8, 62) - LexicographicError: ERROR "[" -(8, 89) - LexicographicError: ERROR "]" +(8, 62) - LexicographicError: ERROR "[" +(8, 89) - LexicographicError: ERROR "]" diff --git a/tests/lexer/iis6.cl b/tests/lexer/iis6.cl index 1042f132b..ba93b19d9 100644 --- a/tests/lexer/iis6.cl +++ b/tests/lexer/iis6.cl @@ -1,125 +1,125 @@ -(* Integers, Identifiers, and Special Notation *) - -factorial(5) = 120, 2 + 2 = 5 or E = mc2; p + 1 resto p = 1: {@.@ for x in range(len(b))} - -new / <- <<==> {( Int: Objet, Bool; String.string SELF_TYPE isvoid }) - - -class Class if then else fi testing Testing ~007agent_bond _james_007bones___ - - - -0007 123 +1 -1 +90 -09 +11113 -4r *a *self* c++ -loop pool while tRuE or noT faLsE let in case of ESAC - -(* -#3 OBJECTID factorial -#3 '(' -#3 INT_CONST 5 -#3 ')' -#3 '=' -#3 INT_CONST 120 -#3 ',' -#3 INT_CONST 2 -#3 '+' -#3 INT_CONST 2 -#3 '=' -#3 INT_CONST 5 -#3 OBJECTID or -#3 TYPEID E -#3 '=' -#3 OBJECTID mc2 -#3 ';' -#3 OBJECTID p -#3 '+' -#3 INT_CONST 1 -#3 OBJECTID resto -#3 OBJECTID p -#3 '=' -#3 INT_CONST 1 -#3 ':' -#3 '{' -#3 '@' -#3 '.' -#3 '@' -#3 OBJECTID for -#3 OBJECTID x -#3 IN -#3 OBJECTID range -#3 '(' -#3 OBJECTID len -#3 '(' -#3 OBJECTID b -#3 ')' -#3 ')' -#3 '}' -#5 NEW -#5 '/' -#5 ASSIGN -#5 '<' -#5 LE -#5 DARROW -#5 '{' -#5 '(' -#5 TYPEID Int -#5 ':' -#5 TYPEID Objet -#5 ',' -#5 TYPEID Bool -#5 ';' -#5 TYPEID String -#5 '.' -#5 OBJECTID string -#5 TYPEID SELF_TYPE -#5 ISVOID -#5 '}' -#5 ')' -#8 CLASS -#8 CLASS -#8 IF -#8 THEN -#8 ELSE -#8 FI -#8 OBJECTID testing -#8 TYPEID Testing -#8 '~' -#8 INT_CONST 007 -#8 OBJECTID agent_bond -#8 ERROR "_" -#8 OBJECTID james_007bones___ -#12 INT_CONST 0007 -#12 INT_CONST 123 -#12 '+' -#12 INT_CONST 1 -#12 '-' -#12 INT_CONST 1 -#12 '+' -#12 INT_CONST 90 -#12 '-' -#12 INT_CONST 09 -#12 '+' -#12 INT_CONST 11113 -#12 '-' -#12 INT_CONST 4 -#12 OBJECTID r -#12 '*' -#12 OBJECTID a -#12 '*' -#12 OBJECTID self -#12 '*' -#12 OBJECTID c -#12 '+' -#12 '+' -#13 LOOP -#13 POOL -#13 WHILE -#13 BOOL_CONST true -#13 OBJECTID or -#13 NOT -#13 BOOL_CONST false -#13 LET -#13 IN -#13 CASE -#13 OF -#13 ESAC +(* Integers, Identifiers, and Special Notation *) + +factorial(5) = 120, 2 + 2 = 5 or E = mc2; p + 1 resto p = 1: {@.@ for x in range(len(b))} + +new / <- <<==> {( Int: Objet, Bool; String.string SELF_TYPE isvoid }) + + +class Class if then else fi testing Testing ~007agent_bond _james_007bones___ + + + +0007 123 +1 -1 +90 -09 +11113 -4r *a *self* c++ +loop pool while tRuE or noT faLsE let in case of ESAC + +(* +#3 OBJECTID factorial +#3 '(' +#3 INT_CONST 5 +#3 ')' +#3 '=' +#3 INT_CONST 120 +#3 ',' +#3 INT_CONST 2 +#3 '+' +#3 INT_CONST 2 +#3 '=' +#3 INT_CONST 5 +#3 OBJECTID or +#3 TYPEID E +#3 '=' +#3 OBJECTID mc2 +#3 ';' +#3 OBJECTID p +#3 '+' +#3 INT_CONST 1 +#3 OBJECTID resto +#3 OBJECTID p +#3 '=' +#3 INT_CONST 1 +#3 ':' +#3 '{' +#3 '@' +#3 '.' +#3 '@' +#3 OBJECTID for +#3 OBJECTID x +#3 IN +#3 OBJECTID range +#3 '(' +#3 OBJECTID len +#3 '(' +#3 OBJECTID b +#3 ')' +#3 ')' +#3 '}' +#5 NEW +#5 '/' +#5 ASSIGN +#5 '<' +#5 LE +#5 DARROW +#5 '{' +#5 '(' +#5 TYPEID Int +#5 ':' +#5 TYPEID Objet +#5 ',' +#5 TYPEID Bool +#5 ';' +#5 TYPEID String +#5 '.' +#5 OBJECTID string +#5 TYPEID SELF_TYPE +#5 ISVOID +#5 '}' +#5 ')' +#8 CLASS +#8 CLASS +#8 IF +#8 THEN +#8 ELSE +#8 FI +#8 OBJECTID testing +#8 TYPEID Testing +#8 '~' +#8 INT_CONST 007 +#8 OBJECTID agent_bond +#8 ERROR "_" +#8 OBJECTID james_007bones___ +#12 INT_CONST 0007 +#12 INT_CONST 123 +#12 '+' +#12 INT_CONST 1 +#12 '-' +#12 INT_CONST 1 +#12 '+' +#12 INT_CONST 90 +#12 '-' +#12 INT_CONST 09 +#12 '+' +#12 INT_CONST 11113 +#12 '-' +#12 INT_CONST 4 +#12 OBJECTID r +#12 '*' +#12 OBJECTID a +#12 '*' +#12 OBJECTID self +#12 '*' +#12 OBJECTID c +#12 '+' +#12 '+' +#13 LOOP +#13 POOL +#13 WHILE +#13 BOOL_CONST true +#13 OBJECTID or +#13 NOT +#13 BOOL_CONST false +#13 LET +#13 IN +#13 CASE +#13 OF +#13 ESAC *) \ No newline at end of file diff --git a/tests/lexer/iis6.mips b/tests/lexer/iis6.mips new file mode 100644 index 000000000..3592fad3f --- /dev/null +++ b/tests/lexer/iis6.mips @@ -0,0 +1 @@ +GENERATED MIPS \ No newline at end of file diff --git a/tests/lexer/iis6_error.txt b/tests/lexer/iis6_error.txt index d7fad9c79..79a9d5aee 100644 --- a/tests/lexer/iis6_error.txt +++ b/tests/lexer/iis6_error.txt @@ -1 +1 @@ -(8, 60) - LexicographicError: ERROR "_" +(8, 60) - LexicographicError: ERROR "_" diff --git a/tests/lexer/mixed1.cl b/tests/lexer/mixed1.cl index 803d58ef5..d3e520a10 100644 --- a/tests/lexer/mixed1.cl +++ b/tests/lexer/mixed1.cl @@ -1,14 +1,14 @@ -"lkjdsafkljdsalfj\u0000dsafdsaf\u0000djafslkjdsalf\nsdajf\" lkjfdsasdkjfl"123 -adsfasklj# -LKldsajf iNhERITS -"lkdsajf" - -(* -#1 STR_CONST "lkjdsafkljdsalfju0000dsafdsafu0000djafslkjdsalf\nsdajf\" lkjfdsasdkjfl" -#1 INT_CONST 123 -#2 OBJECTID adsfasklj -#2 ERROR "#" -#3 TYPEID LKldsajf -#3 INHERITS -#4 STR_CONST "lkdsajf" +"lkjdsafkljdsalfj\u0000dsafdsaf\u0000djafslkjdsalf\nsdajf\" lkjfdsasdkjfl"123 +adsfasklj# +LKldsajf iNhERITS +"lkdsajf" + +(* +#1 STR_CONST "lkjdsafkljdsalfju0000dsafdsafu0000djafslkjdsalf\nsdajf\" lkjfdsasdkjfl" +#1 INT_CONST 123 +#2 OBJECTID adsfasklj +#2 ERROR "#" +#3 TYPEID LKldsajf +#3 INHERITS +#4 STR_CONST "lkdsajf" *) \ No newline at end of file diff --git a/tests/lexer/mixed1.mips b/tests/lexer/mixed1.mips new file mode 100644 index 000000000..3592fad3f --- /dev/null +++ b/tests/lexer/mixed1.mips @@ -0,0 +1 @@ +GENERATED MIPS \ No newline at end of file diff --git a/tests/lexer/mixed1_error.txt b/tests/lexer/mixed1_error.txt index 99af5fbdc..a142c2edd 100644 --- a/tests/lexer/mixed1_error.txt +++ b/tests/lexer/mixed1_error.txt @@ -1 +1 @@ -(2, 10) - LexicographicError: ERROR "#" +(2, 10) - LexicographicError: ERROR "#" diff --git a/tests/lexer/mixed2.cl b/tests/lexer/mixed2.cl index 12039e123..759bf9523 100644 --- a/tests/lexer/mixed2.cl +++ b/tests/lexer/mixed2.cl @@ -1,20 +1,20 @@ -"kjas\"lnnsdj\nfljrdsaf" -@.$.@ -@*%*@ -"alkjfldajf""dasfadsf - -(* -#1 STR_CONST "kjas\"lnnsdj\nfljrdsaf" -#2 '@' -#2 '.' -#2 ERROR "$" -#2 '.' -#2 '@' -#3 '@' -#3 '*' -#3 ERROR "%" -#3 '*' -#3 '@' -#4 STR_CONST "alkjfldajf" -#4 ERROR "Unterminated string constant" +"kjas\"lnnsdj\nfljrdsaf" +@.$.@ +@*%*@ +"alkjfldajf""dasfadsf + +(* +#1 STR_CONST "kjas\"lnnsdj\nfljrdsaf" +#2 '@' +#2 '.' +#2 ERROR "$" +#2 '.' +#2 '@' +#3 '@' +#3 '*' +#3 ERROR "%" +#3 '*' +#3 '@' +#4 STR_CONST "alkjfldajf" +#4 ERROR "Unterminated string constant" *) \ No newline at end of file diff --git a/tests/lexer/mixed2.mips b/tests/lexer/mixed2.mips new file mode 100644 index 000000000..3592fad3f --- /dev/null +++ b/tests/lexer/mixed2.mips @@ -0,0 +1 @@ +GENERATED MIPS \ No newline at end of file diff --git a/tests/lexer/mixed2_error.txt b/tests/lexer/mixed2_error.txt index 097dc2a07..37cb73ac2 100644 --- a/tests/lexer/mixed2_error.txt +++ b/tests/lexer/mixed2_error.txt @@ -1,3 +1,3 @@ -(2, 3) - LexicographicError: ERROR "$" -(3, 3) - LexicographicError: ERROR "%" -(4, 22) - LexicographicError: Unterminated string constant +(2, 3) - LexicographicError: ERROR "$" +(3, 3) - LexicographicError: ERROR "%" +(4, 22) - LexicographicError: Unterminated string constant diff --git a/tests/lexer/string1.cl b/tests/lexer/string1.cl index 6c3c00833..f0a5bd873 100644 --- a/tests/lexer/string1.cl +++ b/tests/lexer/string1.cl @@ -1,6 +1,6 @@ -(* A non-escaped newline character may not appear in a string *) - -"This \ -is OK" -"This is not +(* A non-escaped newline character may not appear in a string *) + +"This \ +is OK" +"This is not OK" \ No newline at end of file diff --git a/tests/lexer/string1.mips b/tests/lexer/string1.mips new file mode 100644 index 000000000..3592fad3f --- /dev/null +++ b/tests/lexer/string1.mips @@ -0,0 +1 @@ +GENERATED MIPS \ No newline at end of file diff --git a/tests/lexer/string1_error.txt b/tests/lexer/string1_error.txt index 078c12bbb..1dd4d70d9 100644 --- a/tests/lexer/string1_error.txt +++ b/tests/lexer/string1_error.txt @@ -1,2 +1,2 @@ -(5, 13) - LexicographicError: Unterminated string constant +(5, 13) - LexicographicError: Unterminated string constant (6, 4) - LexicographicError: EOF in string constant \ No newline at end of file diff --git a/tests/lexer/string2.cl b/tests/lexer/string2.cl index 3704b6ae7..cb3024180 100644 --- a/tests/lexer/string2.cl +++ b/tests/lexer/string2.cl @@ -1,19 +1,19 @@ -(* A string may not contain EOF *) - -" May the Triforce \ - 0 \ - 0v0 \ - 0vvv0 \ - 0vvvvv0 \ - 0vvvvvvv0 \ - 0vvvvvvvvv0 \ - 0vvvvvvvvvvv0 \ - 000000000000000 \ - 0v0 0v0 \ - 0vvv0 0vvv0 \ - 0vvvvv0 0vvvvv0 \ - 0vvvvvvv0 0vvvvvvv0 \ - 0vvvvvvvvv0 0vvvvvvvvv0 \ - 0vvvvvvvvvvv0 0vvvvvvvvvvv0 \ - 00000000000000000000000000000 \ +(* A string may not contain EOF *) + +" May the Triforce \ + 0 \ + 0v0 \ + 0vvv0 \ + 0vvvvv0 \ + 0vvvvvvv0 \ + 0vvvvvvvvv0 \ + 0vvvvvvvvvvv0 \ + 000000000000000 \ + 0v0 0v0 \ + 0vvv0 0vvv0 \ + 0vvvvv0 0vvvvv0 \ + 0vvvvvvv0 0vvvvvvv0 \ + 0vvvvvvvvv0 0vvvvvvvvv0 \ + 0vvvvvvvvvvv0 0vvvvvvvvvvv0 \ + 00000000000000000000000000000 \ be with you! \ No newline at end of file diff --git a/tests/lexer/string2.mips b/tests/lexer/string2.mips new file mode 100644 index 000000000..3592fad3f --- /dev/null +++ b/tests/lexer/string2.mips @@ -0,0 +1 @@ +GENERATED MIPS \ No newline at end of file diff --git a/tests/lexer/string3.mips b/tests/lexer/string3.mips new file mode 100644 index 000000000..3592fad3f --- /dev/null +++ b/tests/lexer/string3.mips @@ -0,0 +1 @@ +GENERATED MIPS \ No newline at end of file diff --git a/tests/lexer/string4.cl b/tests/lexer/string4.cl index f4d39c027..7ca4eb42b 100644 --- a/tests/lexer/string4.cl +++ b/tests/lexer/string4.cl @@ -1,38 +1,38 @@ -class Main { - str <- "The big brown fox - jumped over the fence"; - main() : Object { - { - out_string("Yay! This is the newest shites ); - } - }; -}; - -(* -#1 CLASS -#1 TYPEID Main -#1 '{' -#2 OBJECTID str -#2 ASSIGN -#3 ERROR "Unterminated string constant" -#3 OBJECTID jumped -#3 OBJECTID over -#3 OBJECTID the -#3 OBJECTID fence -#4 ERROR "Unterminated string constant" -#4 OBJECTID main -#4 '(' -#4 ')' -#4 ':' -#4 TYPEID Object -#4 '{' -#5 '{' -#6 OBJECTID out_string -#6 '(' -#7 ERROR "Unterminated string constant" -#7 '}' -#8 '}' -#8 ';' -#9 '}' -#9 ';' +class Main { + str <- "The big brown fox + jumped over the fence"; + main() : Object { + { + out_string("Yay! This is the newest shites ); + } + }; +}; + +(* +#1 CLASS +#1 TYPEID Main +#1 '{' +#2 OBJECTID str +#2 ASSIGN +#3 ERROR "Unterminated string constant" +#3 OBJECTID jumped +#3 OBJECTID over +#3 OBJECTID the +#3 OBJECTID fence +#4 ERROR "Unterminated string constant" +#4 OBJECTID main +#4 '(' +#4 ')' +#4 ':' +#4 TYPEID Object +#4 '{' +#5 '{' +#6 OBJECTID out_string +#6 '(' +#7 ERROR "Unterminated string constant" +#7 '}' +#8 '}' +#8 ';' +#9 '}' +#9 ';' *) \ No newline at end of file diff --git a/tests/lexer/string4.mips b/tests/lexer/string4.mips new file mode 100644 index 000000000..3592fad3f --- /dev/null +++ b/tests/lexer/string4.mips @@ -0,0 +1 @@ +GENERATED MIPS \ No newline at end of file diff --git a/tests/lexer/string4_error.txt b/tests/lexer/string4_error.txt index 5ab0ea847..bf420217c 100644 --- a/tests/lexer/string4_error.txt +++ b/tests/lexer/string4_error.txt @@ -1,3 +1,3 @@ -(2, 30) - LexicographicError: Unterminated string constant -(3, 36) - LexicographicError: Unterminated string constant +(2, 30) - LexicographicError: Unterminated string constant +(3, 36) - LexicographicError: Unterminated string constant (6, 58) - LexicographicError: Unterminated string constant \ No newline at end of file diff --git a/tests/lexer_test.py b/tests/lexer_test.py index 2a27223d3..a21fd880a 100644 --- a/tests/lexer_test.py +++ b/tests/lexer_test.py @@ -1,13 +1,13 @@ -import pytest -import os -from utils import compare_errors - -tests_dir = __file__.rpartition('/')[0] + '/lexer/' -tests = [(file) for file in os.listdir(tests_dir) if file.endswith('.cl')] - -@pytest.mark.lexer -@pytest.mark.error -@pytest.mark.run(order=1) -@pytest.mark.parametrize("cool_file", tests) -def test_lexer_errors(compiler_path, cool_file): +import pytest +import os +from utils import compare_errors + +tests_dir = __file__.rpartition('/')[0] + '/lexer/' +tests = [(file) for file in os.listdir(tests_dir) if file.endswith('.cl')] + +@pytest.mark.lexer +@pytest.mark.error +@pytest.mark.run(order=1) +@pytest.mark.parametrize("cool_file", tests) +def test_lexer_errors(compiler_path, cool_file): compare_errors(compiler_path, tests_dir + cool_file, tests_dir + cool_file[:-3] + '_error.txt') \ No newline at end of file diff --git a/tests/parser/assignment1.cl b/tests/parser/assignment1.cl index 75b4c5bbd..e89ade368 100644 --- a/tests/parser/assignment1.cl +++ b/tests/parser/assignment1.cl @@ -1,37 +1,37 @@ -(* An assignment has the form <- *) - -class Main { - main(): Object { - (new Alpha).print() - }; -}; - -class Test { - test1: Object; - - testing1(): Int { - 2 + 2 - }; - - test2: Int <- 1; - - test3: String <- "1"; - - testing2(a: Alpha, b: Int): Int { - 2 + 2 - }; - - testing3(): String { - "2 + 2" - }; - - testing4(): String { - Test1 <- "Hello World" -- Identifiers begin with a lower case letter - }; -}; - -class Alpha inherits IO { - print() : Object { - out_string("reached!!\n") - }; +(* An assignment has the form <- *) + +class Main { + main(): Object { + (new Alpha).print() + }; +}; + +class Test { + test1: Object; + + testing1(): Int { + 2 + 2 + }; + + test2: Int <- 1; + + test3: String <- "1"; + + testing2(a: Alpha, b: Int): Int { + 2 + 2 + }; + + testing3(): String { + "2 + 2" + }; + + testing4(): String { + Test1 <- "Hello World" -- Identifiers begin with a lower case letter + }; +}; + +class Alpha inherits IO { + print() : Object { + out_string("reached!!\n") + }; }; \ No newline at end of file diff --git a/tests/parser/assignment2.cl b/tests/parser/assignment2.cl index 4efb96487..7b8ed54ba 100644 --- a/tests/parser/assignment2.cl +++ b/tests/parser/assignment2.cl @@ -1,37 +1,37 @@ -(* An assignment has the form <- *) - -class Main { - main(): Object { - (new Alpha).print() - }; -}; - -class Test { - test1: Object; - - testing1(): Int { - 2 - 2 - }; - - test2: Int <- 1; - - test3: String <- "1"; - - testing2(a: Alpha, b: Int): Int { - 2 + 2 - }; - - testing3(): String { - "2 + 2" - }; - - testing4(): Int { - test1 <-- ~(1 + 2 + 3 + 4 + 5) -- The left side must be an expression - }; -}; - -class Alpha inherits IO { - print() : Object { - out_string("reached!!\n") - }; +(* An assignment has the form <- *) + +class Main { + main(): Object { + (new Alpha).print() + }; +}; + +class Test { + test1: Object; + + testing1(): Int { + 2 - 2 + }; + + test2: Int <- 1; + + test3: String <- "1"; + + testing2(a: Alpha, b: Int): Int { + 2 + 2 + }; + + testing3(): String { + "2 + 2" + }; + + testing4(): Int { + test1 <-- ~(1 + 2 + 3 + 4 + 5) -- The left side must be an expression + }; +}; + +class Alpha inherits IO { + print() : Object { + out_string("reached!!\n") + }; }; \ No newline at end of file diff --git a/tests/parser/assignment3.cl b/tests/parser/assignment3.cl index ff633f331..f54305e27 100644 --- a/tests/parser/assignment3.cl +++ b/tests/parser/assignment3.cl @@ -1,37 +1,37 @@ -(* An assignment has the form <- *) - -class Main { - main(): Object { - (new Alpha).print() - }; -}; - -class Test { - test1: Object; - - testing1(): Int { - 2 + 2 - }; - - test2: Int <- 1; - - test3: String <- "1"; - - testing2(a: Alpha, b: Int): Int { - 2 + 2 - }; - - testing3(): String { - "2 + 2" - }; - - testing4(): Bool { - test1 <- true++ -- The left side must be an expression - }; -}; - -class Alpha inherits IO { - print() : Object { - out_string("reached!!\n") - }; +(* An assignment has the form <- *) + +class Main { + main(): Object { + (new Alpha).print() + }; +}; + +class Test { + test1: Object; + + testing1(): Int { + 2 + 2 + }; + + test2: Int <- 1; + + test3: String <- "1"; + + testing2(a: Alpha, b: Int): Int { + 2 + 2 + }; + + testing3(): String { + "2 + 2" + }; + + testing4(): Bool { + test1 <- true++ -- The left side must be an expression + }; +}; + +class Alpha inherits IO { + print() : Object { + out_string("reached!!\n") + }; }; \ No newline at end of file diff --git a/tests/parser/attribute1.cl b/tests/parser/attribute1.cl index 063a02c02..59740336d 100644 --- a/tests/parser/attribute1.cl +++ b/tests/parser/attribute1.cl @@ -1,34 +1,34 @@ -(* An attribute of class A specifies a variable that is part of the state of objects of class A *) - -class Main { - main(): Object { - (new Alpha).print() - }; -}; - -class Test { - test1: Object; - - testing1(): Int { - 2 + 2 - }; - - -- Attributes names must begin with lowercase letters - Test2: Int <- 1; - - test3: String <- "1"; - - testing2(a: Alpha, b: Int): Int { - 2 + 2 - }; - - testing3(): String { - "2 + 2" - }; -}; - -class Alpha inherits IO { - print() : Object { - out_string("reached!!\n") - }; +(* An attribute of class A specifies a variable that is part of the state of objects of class A *) + +class Main { + main(): Object { + (new Alpha).print() + }; +}; + +class Test { + test1: Object; + + testing1(): Int { + 2 + 2 + }; + + -- Attributes names must begin with lowercase letters + Test2: Int <- 1; + + test3: String <- "1"; + + testing2(a: Alpha, b: Int): Int { + 2 + 2 + }; + + testing3(): String { + "2 + 2" + }; +}; + +class Alpha inherits IO { + print() : Object { + out_string("reached!!\n") + }; }; \ No newline at end of file diff --git a/tests/parser/attribute2.cl b/tests/parser/attribute2.cl index c05211483..337696859 100644 --- a/tests/parser/attribute2.cl +++ b/tests/parser/attribute2.cl @@ -1,34 +1,34 @@ -(* An attribute of class A specifies a variable that is part of the state of objects of class A *) - -class Main { - main(): Object { - (new Alpha).print() - }; -}; - -class Test { - test1: Object; - - testing1(): Int { - 2 + 2 - }; - - test2: Int <- 1; - - -- Type names must begin with uppercase letters - test3: string <- "1"; - - testing2(a: Alpha, b: Int): Int { - 2 + 2 - }; - - testing3(): String { - "2 + 2" - }; -}; - -class Alpha inherits IO { - print() : Object { - out_string("reached!!\n") - }; +(* An attribute of class A specifies a variable that is part of the state of objects of class A *) + +class Main { + main(): Object { + (new Alpha).print() + }; +}; + +class Test { + test1: Object; + + testing1(): Int { + 2 + 2 + }; + + test2: Int <- 1; + + -- Type names must begin with uppercase letters + test3: string <- "1"; + + testing2(a: Alpha, b: Int): Int { + 2 + 2 + }; + + testing3(): String { + "2 + 2" + }; +}; + +class Alpha inherits IO { + print() : Object { + out_string("reached!!\n") + }; }; \ No newline at end of file diff --git a/tests/parser/attribute3.cl b/tests/parser/attribute3.cl index d858ae47c..b29b4b26d 100644 --- a/tests/parser/attribute3.cl +++ b/tests/parser/attribute3.cl @@ -1,34 +1,34 @@ -(* An attribute of class A specifies a variable that is part of the state of objects of class A *) - -class Main { - main(): Object { - (new Alpha).print() - }; -}; - -class Test { - test1: Object; - - testing1(): Int { - 2 + 2 - }; - - test2: Int <- 1; - - -- Expected '<-' not '<=' - test3: String <= "1"; - - testing2(a: Alpha, b: Int): Int { - 2 + 2 - }; - - testing3(): String { - "2 + 2" - }; -}; - -class Alpha inherits IO { - print() : Object { - out_string("reached!!\n") - }; +(* An attribute of class A specifies a variable that is part of the state of objects of class A *) + +class Main { + main(): Object { + (new Alpha).print() + }; +}; + +class Test { + test1: Object; + + testing1(): Int { + 2 + 2 + }; + + test2: Int <- 1; + + -- Expected '<-' not '<=' + test3: String <= "1"; + + testing2(a: Alpha, b: Int): Int { + 2 + 2 + }; + + testing3(): String { + "2 + 2" + }; +}; + +class Alpha inherits IO { + print() : Object { + out_string("reached!!\n") + }; }; \ No newline at end of file diff --git a/tests/parser/block1.cl b/tests/parser/block1.cl index 3613d9268..f15872812 100644 --- a/tests/parser/block1.cl +++ b/tests/parser/block1.cl @@ -1,87 +1,87 @@ -(* A block has the form { ; ... ; } *) - -class Main { - main(): Object { - (new Alpha).print() - }; -}; - -class Test { - test1: Object; - - testing1(): Int { - 2 + 2 - }; - - test2: Int <- 1; - - test3: String <- "1"; - - testing2(a: Alpha, b: Int): Int { - 2 + 2 - }; - - testing3(): String { - "2 + 2" - }; - - testing4(x: Int, y: Int): Test { - self - }; - - testing5(a: String, b: String): IO { - If a.length() < b.length() THeN - new IO.out_string("La cadena \"".concat(b).concat("\" es mas larga que la cadena \"").concat(a).concat("\".")) - eLSe - if a.length() = b.length() THeN - new IO.out_string("La cadena \"".concat(a).concat("\" mide igual que la cadena \"").concat(b).concat("\".")) - ElsE - new IO.out_string("La cadena \"".concat(a).concat("\" es mas larga que la cadena \"").concat(b).concat("\".")) - fI - Fi - }; - - pow: Int <- 1; - count: Int <- 0; - - testing6(a: Int): IO { - { - count <- 0; - pow <- 1; - while pow < a - loop - { - count <- count + 1; - pow <- pow * 2 -- Missing ";" - } - pool; - new IO.out_string("El logaritmo en base 2 de ").out_int(a).out_string(" es ").out_int(count); - } - }; -}; - -class Test2 { - test1: Test <- new Test; - - testing1(): Test { - test1.testing4(1 + 1, 1 + 2).testing4(2 + 3, 3 + 5).testing4(5 + 8, 8 + 13) - }; - - testing2(x: Int, y: Int): Test2 { - self - }; - - testing3(): Test2 { - testing2(1 + 1, 1 + 2).testing2(2 + 3, 3 + 5).testing2(5 + 8, true + fALSE) - }; - - testing4(): Object { - test1@Object.copy() - }; -}; - -class Alpha inherits IO { - print() : Object { - out_string("reached!!\n") - }; +(* A block has the form { ; ... ; } *) + +class Main { + main(): Object { + (new Alpha).print() + }; +}; + +class Test { + test1: Object; + + testing1(): Int { + 2 + 2 + }; + + test2: Int <- 1; + + test3: String <- "1"; + + testing2(a: Alpha, b: Int): Int { + 2 + 2 + }; + + testing3(): String { + "2 + 2" + }; + + testing4(x: Int, y: Int): Test { + self + }; + + testing5(a: String, b: String): IO { + If a.length() < b.length() THeN + new IO.out_string("La cadena \"".concat(b).concat("\" es mas larga que la cadena \"").concat(a).concat("\".")) + eLSe + if a.length() = b.length() THeN + new IO.out_string("La cadena \"".concat(a).concat("\" mide igual que la cadena \"").concat(b).concat("\".")) + ElsE + new IO.out_string("La cadena \"".concat(a).concat("\" es mas larga que la cadena \"").concat(b).concat("\".")) + fI + Fi + }; + + pow: Int <- 1; + count: Int <- 0; + + testing6(a: Int): IO { + { + count <- 0; + pow <- 1; + while pow < a + loop + { + count <- count + 1; + pow <- pow * 2 -- Missing ";" + } + pool; + new IO.out_string("El logaritmo en base 2 de ").out_int(a).out_string(" es ").out_int(count); + } + }; +}; + +class Test2 { + test1: Test <- new Test; + + testing1(): Test { + test1.testing4(1 + 1, 1 + 2).testing4(2 + 3, 3 + 5).testing4(5 + 8, 8 + 13) + }; + + testing2(x: Int, y: Int): Test2 { + self + }; + + testing3(): Test2 { + testing2(1 + 1, 1 + 2).testing2(2 + 3, 3 + 5).testing2(5 + 8, true + fALSE) + }; + + testing4(): Object { + test1@Object.copy() + }; +}; + +class Alpha inherits IO { + print() : Object { + out_string("reached!!\n") + }; }; \ No newline at end of file diff --git a/tests/parser/block2.cl b/tests/parser/block2.cl index f485dd0b1..1f45fca4c 100644 --- a/tests/parser/block2.cl +++ b/tests/parser/block2.cl @@ -1,87 +1,87 @@ -(* A block has the form { ; ... ; } *) - -class Main { - main(): Object { - (new Alpha).print() - }; -}; - -class Test { - test1: Object; - - testing1(): Int { - 2 + 2 - }; - - test2: Int <- 1; - - test3: String <- "1"; - - testing2(a: Alpha, b: Int): Int { - 2 + 2 - }; - - testing3(): String { - "2 + 2" - }; - - testing4(x: Int, y: Int): Test { - self - }; - - testing5(a: String, b: String): IO { - If a.length() < b.length() THeN - new IO.out_string("La cadena \"".concat(b).concat("\" es mas larga que la cadena \"").concat(a).concat("\".")) - eLSe - if a.length() = b.length() THeN - new IO.out_string("La cadena \"".concat(a).concat("\" mide igual que la cadena \"").concat(b).concat("\".")) - ElsE - new IO.out_string("La cadena \"".concat(a).concat("\" es mas larga que la cadena \"").concat(b).concat("\".")) - fI - Fi - }; - - pow: Int <- 1; - count: Int <- 0; - - testing6(a: Int): IO { - -- Missing "{" - count <- 0; - pow <- 1; - while pow < a - loop - { - count <- count + 1; - pow <- pow * 2; - } - pool; - new IO.out_string("El logaritmo en base 2 de ").out_int(a).out_string(" es ").out_int(count); - } - }; -}; - -class Test2 { - test1: Test <- new Test; - - testing1(): Test { - test1.testing4(1 + 1, 1 + 2).testing4(2 + 3, 3 + 5).testing4(5 + 8, 8 + 13) - }; - - testing2(x: Int, y: Int): Test2 { - self - }; - - testing3(): Test2 { - testing2(1 + 1, 1 + 2).testing2(2 + 3, 3 + 5).testing2(5 + 8, true + fALSE) - }; - - testing4(): Object { - test1@Object.copy() - }; -}; - -class Alpha inherits IO { - print() : Object { - out_string("reached!!\n") - }; +(* A block has the form { ; ... ; } *) + +class Main { + main(): Object { + (new Alpha).print() + }; +}; + +class Test { + test1: Object; + + testing1(): Int { + 2 + 2 + }; + + test2: Int <- 1; + + test3: String <- "1"; + + testing2(a: Alpha, b: Int): Int { + 2 + 2 + }; + + testing3(): String { + "2 + 2" + }; + + testing4(x: Int, y: Int): Test { + self + }; + + testing5(a: String, b: String): IO { + If a.length() < b.length() THeN + new IO.out_string("La cadena \"".concat(b).concat("\" es mas larga que la cadena \"").concat(a).concat("\".")) + eLSe + if a.length() = b.length() THeN + new IO.out_string("La cadena \"".concat(a).concat("\" mide igual que la cadena \"").concat(b).concat("\".")) + ElsE + new IO.out_string("La cadena \"".concat(a).concat("\" es mas larga que la cadena \"").concat(b).concat("\".")) + fI + Fi + }; + + pow: Int <- 1; + count: Int <- 0; + + testing6(a: Int): IO { + -- Missing "{" + count <- 0; + pow <- 1; + while pow < a + loop + { + count <- count + 1; + pow <- pow * 2; + } + pool; + new IO.out_string("El logaritmo en base 2 de ").out_int(a).out_string(" es ").out_int(count); + } + }; +}; + +class Test2 { + test1: Test <- new Test; + + testing1(): Test { + test1.testing4(1 + 1, 1 + 2).testing4(2 + 3, 3 + 5).testing4(5 + 8, 8 + 13) + }; + + testing2(x: Int, y: Int): Test2 { + self + }; + + testing3(): Test2 { + testing2(1 + 1, 1 + 2).testing2(2 + 3, 3 + 5).testing2(5 + 8, true + fALSE) + }; + + testing4(): Object { + test1@Object.copy() + }; +}; + +class Alpha inherits IO { + print() : Object { + out_string("reached!!\n") + }; }; \ No newline at end of file diff --git a/tests/parser/block3.cl b/tests/parser/block3.cl index ae1598c3b..9b63b0015 100644 --- a/tests/parser/block3.cl +++ b/tests/parser/block3.cl @@ -1,87 +1,87 @@ -(* A block has the form { ; ... ; } *) - -class Main { - main(): Object { - (new Alpha).print() - }; -}; - -class Test { - test1: Object; - - testing1(): Int { - 2 + 2 - }; - - test2: Int <- 1; - - test3: String <- "1"; - - testing2(a: Alpha, b: Int): Int { - 2 + 2 - }; - - testing3(): String { - "2 + 2" - }; - - testing4(x: Int, y: Int): Test { - self - }; - - testing5(a: String, b: String): IO { - If a.length() < b.length() THeN - new IO.out_string("La cadena \"".concat(b).concat("\" es mas larga que la cadena \"").concat(a).concat("\".")) - eLSe - if a.length() = b.length() THeN - new IO.out_string("La cadena \"".concat(a).concat("\" mide igual que la cadena \"").concat(b).concat("\".")) - ElsE - new IO.out_string("La cadena \"".concat(a).concat("\" es mas larga que la cadena \"").concat(b).concat("\".")) - fI - Fi - }; - - pow: Int <- 1; - count: Int <- 0; - - testing6(a: Int): IO { - { - count <- 0; - pow <- 1; - while pow < a - loop - { - count <- count + 1; - pow <- pow * 2; - -- Missing "}" - pool; - new IO.out_string("El logaritmo en base 2 de ").out_int(a).out_string(" es ").out_int(count); - } - }; -}; - -class Test2 { - test1: Test <- new Test; - - testing1(): Test { - test1.testing4(1 + 1, 1 + 2).testing4(2 + 3, 3 + 5).testing4(5 + 8, 8 + 13) - }; - - testing2(x: Int, y: Int): Test2 { - self - }; - - testing3(): Test2 { - testing2(1 + 1, 1 + 2).testing2(2 + 3, 3 + 5).testing2(5 + 8, true + fALSE) - }; - - testing4(): Object { - test1@Object.copy() - }; -}; - -class Alpha inherits IO { - print() : Object { - out_string("reached!!\n") - }; +(* A block has the form { ; ... ; } *) + +class Main { + main(): Object { + (new Alpha).print() + }; +}; + +class Test { + test1: Object; + + testing1(): Int { + 2 + 2 + }; + + test2: Int <- 1; + + test3: String <- "1"; + + testing2(a: Alpha, b: Int): Int { + 2 + 2 + }; + + testing3(): String { + "2 + 2" + }; + + testing4(x: Int, y: Int): Test { + self + }; + + testing5(a: String, b: String): IO { + If a.length() < b.length() THeN + new IO.out_string("La cadena \"".concat(b).concat("\" es mas larga que la cadena \"").concat(a).concat("\".")) + eLSe + if a.length() = b.length() THeN + new IO.out_string("La cadena \"".concat(a).concat("\" mide igual que la cadena \"").concat(b).concat("\".")) + ElsE + new IO.out_string("La cadena \"".concat(a).concat("\" es mas larga que la cadena \"").concat(b).concat("\".")) + fI + Fi + }; + + pow: Int <- 1; + count: Int <- 0; + + testing6(a: Int): IO { + { + count <- 0; + pow <- 1; + while pow < a + loop + { + count <- count + 1; + pow <- pow * 2; + -- Missing "}" + pool; + new IO.out_string("El logaritmo en base 2 de ").out_int(a).out_string(" es ").out_int(count); + } + }; +}; + +class Test2 { + test1: Test <- new Test; + + testing1(): Test { + test1.testing4(1 + 1, 1 + 2).testing4(2 + 3, 3 + 5).testing4(5 + 8, 8 + 13) + }; + + testing2(x: Int, y: Int): Test2 { + self + }; + + testing3(): Test2 { + testing2(1 + 1, 1 + 2).testing2(2 + 3, 3 + 5).testing2(5 + 8, true + fALSE) + }; + + testing4(): Object { + test1@Object.copy() + }; +}; + +class Alpha inherits IO { + print() : Object { + out_string("reached!!\n") + }; }; \ No newline at end of file diff --git a/tests/parser/block4.cl b/tests/parser/block4.cl index 8fd883d02..c144dae48 100644 --- a/tests/parser/block4.cl +++ b/tests/parser/block4.cl @@ -1,88 +1,88 @@ -(* A block has the form { ; ... ; } *) - -class Main { - main(): Object { - (new Alpha).print() - }; -}; - -class Test { - test1: Object; - - testing1(): Int { - 2 + 2 - }; - - test2: Int <- 1; - - test3: String <- "1"; - - testing2(a: Alpha, b: Int): Int { - 2 + 2 - }; - - testing3(): String { - "2 + 2" - }; - - testing4(x: Int, y: Int): Test { - self - }; - - testing5(a: String, b: String): IO { - If a.length() < b.length() THeN - new IO.out_string("La cadena \"".concat(b).concat("\" es mas larga que la cadena \"").concat(a).concat("\".")) - eLSe - if a.length() = b.length() THeN - new IO.out_string("La cadena \"".concat(a).concat("\" mide igual que la cadena \"").concat(b).concat("\".")) - ElsE - new IO.out_string("La cadena \"".concat(a).concat("\" es mas larga que la cadena \"").concat(b).concat("\".")) - fI - Fi - }; - - pow: Int <- 1; - count: Int <- 0; - - testing6(a: Int): IO { - { - count <- 0; - pow <- 1; - while pow < a - loop - { - count <- count + 1; - pow <- pow * 2; - true++; -- Only expressions - } - pool; - new IO.out_string("El logaritmo en base 2 de ").out_int(a).out_string(" es ").out_int(count); - } - }; -}; - -class Test2 { - test1: Test <- new Test; - - testing1(): Test { - test1.testing4(1 + 1, 1 + 2).testing4(2 + 3, 3 + 5).testing4(5 + 8, 8 + 13) - }; - - testing2(x: Int, y: Int): Test2 { - self - }; - - testing3(): Test2 { - testing2(1 + 1, 1 + 2).testing2(2 + 3, 3 + 5).testing2(5 + 8, true + fALSE) - }; - - testing4(): Object { - test1@Object.copy() - }; -}; - -class Alpha inherits IO { - print() : Object { - out_string("reached!!\n") - }; +(* A block has the form { ; ... ; } *) + +class Main { + main(): Object { + (new Alpha).print() + }; +}; + +class Test { + test1: Object; + + testing1(): Int { + 2 + 2 + }; + + test2: Int <- 1; + + test3: String <- "1"; + + testing2(a: Alpha, b: Int): Int { + 2 + 2 + }; + + testing3(): String { + "2 + 2" + }; + + testing4(x: Int, y: Int): Test { + self + }; + + testing5(a: String, b: String): IO { + If a.length() < b.length() THeN + new IO.out_string("La cadena \"".concat(b).concat("\" es mas larga que la cadena \"").concat(a).concat("\".")) + eLSe + if a.length() = b.length() THeN + new IO.out_string("La cadena \"".concat(a).concat("\" mide igual que la cadena \"").concat(b).concat("\".")) + ElsE + new IO.out_string("La cadena \"".concat(a).concat("\" es mas larga que la cadena \"").concat(b).concat("\".")) + fI + Fi + }; + + pow: Int <- 1; + count: Int <- 0; + + testing6(a: Int): IO { + { + count <- 0; + pow <- 1; + while pow < a + loop + { + count <- count + 1; + pow <- pow * 2; + true++; -- Only expressions + } + pool; + new IO.out_string("El logaritmo en base 2 de ").out_int(a).out_string(" es ").out_int(count); + } + }; +}; + +class Test2 { + test1: Test <- new Test; + + testing1(): Test { + test1.testing4(1 + 1, 1 + 2).testing4(2 + 3, 3 + 5).testing4(5 + 8, 8 + 13) + }; + + testing2(x: Int, y: Int): Test2 { + self + }; + + testing3(): Test2 { + testing2(1 + 1, 1 + 2).testing2(2 + 3, 3 + 5).testing2(5 + 8, true + fALSE) + }; + + testing4(): Object { + test1@Object.copy() + }; +}; + +class Alpha inherits IO { + print() : Object { + out_string("reached!!\n") + }; }; \ No newline at end of file diff --git a/tests/parser/case1.cl b/tests/parser/case1.cl index c2f508809..35fe5af53 100644 --- a/tests/parser/case1.cl +++ b/tests/parser/case1.cl @@ -1,91 +1,91 @@ -(* Case expressions provide runtime type tests on objects *) - -class Main { - main(): Object { - (new Alpha).print() - }; -}; - -class Test { - test1: Object; - - testing1(): Int { - 2 + 2 - }; - - test2: Int <- 1; - - test3: String <- "1"; - - testing2(a: Alpha, b: Int): Int { - 2 + 2 - }; - - testing3(): String { - "2 + 2" - }; - - testing4(x: Int, y: Int): Test { - self - }; - - testing5(a: String, b: String): IO { - If a.length() < b.length() THeN - new IO.out_string("La cadena \"".concat(b).concat("\" es mas larga que la cadena \"").concat(a).concat("\".")) - eLSe - if a.length() = b.length() THeN - new IO.out_string("La cadena \"".concat(a).concat("\" mide igual que la cadena \"").concat(b).concat("\".")) - ElsE - new IO.out_string("La cadena \"".concat(a).concat("\" es mas larga que la cadena \"").concat(b).concat("\".")) - fI - Fi - }; - - testing6(a: Int): IO { - let count: Int <- 0, pow: Int - in { - -- count <- 0; - pow <- 1; - while pow < a - loop - { - count <- count + 1; - pow <- pow * 2; - } - pool; - new IO.out_string("El logaritmo en base 2 de ").out_int(a).out_string(" es ").out_int(count); - } - }; - - testing7(): Object { - case 2 + 2 of - -- Every case expression must have at least one branch - esac - }; -}; - -class Test2 { - test1: Test <- new Test; - - testing1(): Test { - test1.testing4(1 + 1, 1 + 2).testing4(2 + 3, 3 + 5).testing4(5 + 8, 8 + 13) - }; - - testing2(x: Int, y: Int): Test2 { - self - }; - - testing3(): Test2 { - testing2(1 + 1, 1 + 2).testing2(2 + 3, 3 + 5).testing2(5 + 8, true + fALSE) - }; - - testing4(): Object { - test1@Object.copy() - }; -}; - -class Alpha inherits IO { - print() : Object { - out_string("reached!!\n") - }; +(* Case expressions provide runtime type tests on objects *) + +class Main { + main(): Object { + (new Alpha).print() + }; +}; + +class Test { + test1: Object; + + testing1(): Int { + 2 + 2 + }; + + test2: Int <- 1; + + test3: String <- "1"; + + testing2(a: Alpha, b: Int): Int { + 2 + 2 + }; + + testing3(): String { + "2 + 2" + }; + + testing4(x: Int, y: Int): Test { + self + }; + + testing5(a: String, b: String): IO { + If a.length() < b.length() THeN + new IO.out_string("La cadena \"".concat(b).concat("\" es mas larga que la cadena \"").concat(a).concat("\".")) + eLSe + if a.length() = b.length() THeN + new IO.out_string("La cadena \"".concat(a).concat("\" mide igual que la cadena \"").concat(b).concat("\".")) + ElsE + new IO.out_string("La cadena \"".concat(a).concat("\" es mas larga que la cadena \"").concat(b).concat("\".")) + fI + Fi + }; + + testing6(a: Int): IO { + let count: Int <- 0, pow: Int + in { + -- count <- 0; + pow <- 1; + while pow < a + loop + { + count <- count + 1; + pow <- pow * 2; + } + pool; + new IO.out_string("El logaritmo en base 2 de ").out_int(a).out_string(" es ").out_int(count); + } + }; + + testing7(): Object { + case 2 + 2 of + -- Every case expression must have at least one branch + esac + }; +}; + +class Test2 { + test1: Test <- new Test; + + testing1(): Test { + test1.testing4(1 + 1, 1 + 2).testing4(2 + 3, 3 + 5).testing4(5 + 8, 8 + 13) + }; + + testing2(x: Int, y: Int): Test2 { + self + }; + + testing3(): Test2 { + testing2(1 + 1, 1 + 2).testing2(2 + 3, 3 + 5).testing2(5 + 8, true + fALSE) + }; + + testing4(): Object { + test1@Object.copy() + }; +}; + +class Alpha inherits IO { + print() : Object { + out_string("reached!!\n") + }; }; \ No newline at end of file diff --git a/tests/parser/case2.cl b/tests/parser/case2.cl index f9162e49f..12cbb87b5 100644 --- a/tests/parser/case2.cl +++ b/tests/parser/case2.cl @@ -1,93 +1,93 @@ -(* Case expressions provide runtime type tests on objects *) - -class Main { - main(): Object { - (new Alpha).print() - }; -}; - -class Test { - test1: Object; - - testing1(): Int { - 2 + 2 - }; - - test2: Int <- 1; - - test3: String <- "1"; - - testing2(a: Alpha, b: Int): Int { - 2 + 2 - }; - - testing3(): String { - "2 + 2" - }; - - testing4(x: Int, y: Int): Test { - self - }; - - testing5(a: String, b: String): IO { - If a.length() < b.length() THeN - new IO.out_string("La cadena \"".concat(b).concat("\" es mas larga que la cadena \"").concat(a).concat("\".")) - eLSe - if a.length() = b.length() THeN - new IO.out_string("La cadena \"".concat(a).concat("\" mide igual que la cadena \"").concat(b).concat("\".")) - ElsE - new IO.out_string("La cadena \"".concat(a).concat("\" es mas larga que la cadena \"").concat(b).concat("\".")) - fI - Fi - }; - - testing6(a: Int): IO { - let count: Int <- 0, pow: Int - in { - -- count <- 0; - pow <- 1; - while pow < a - loop - { - count <- count + 1; - pow <- pow * 2; - } - pool; - new IO.out_string("El logaritmo en base 2 de ").out_int(a).out_string(" es ").out_int(count); - } - }; - - testing7(): Object { - case "2 + 2" of - x: Int => new IO.out_string("Es un entero!") -- Missing ";" - y: String => new IO.out_string("Es una cadena!"); - z: Bool => new IO.out_string("Es un booleano!"); - esac - }; -}; - -class Test2 { - test1: Test <- new Test; - - testing1(): Test { - test1.testing4(1 + 1, 1 + 2).testing4(2 + 3, 3 + 5).testing4(5 + 8, 8 + 13) - }; - - testing2(x: Int, y: Int): Test2 { - self - }; - - testing3(): Test2 { - testing2(1 + 1, 1 + 2).testing2(2 + 3, 3 + 5).testing2(5 + 8, true + fALSE) - }; - - testing4(): Object { - test1@Object.copy() - }; -}; - -class Alpha inherits IO { - print() : Object { - out_string("reached!!\n") - }; +(* Case expressions provide runtime type tests on objects *) + +class Main { + main(): Object { + (new Alpha).print() + }; +}; + +class Test { + test1: Object; + + testing1(): Int { + 2 + 2 + }; + + test2: Int <- 1; + + test3: String <- "1"; + + testing2(a: Alpha, b: Int): Int { + 2 + 2 + }; + + testing3(): String { + "2 + 2" + }; + + testing4(x: Int, y: Int): Test { + self + }; + + testing5(a: String, b: String): IO { + If a.length() < b.length() THeN + new IO.out_string("La cadena \"".concat(b).concat("\" es mas larga que la cadena \"").concat(a).concat("\".")) + eLSe + if a.length() = b.length() THeN + new IO.out_string("La cadena \"".concat(a).concat("\" mide igual que la cadena \"").concat(b).concat("\".")) + ElsE + new IO.out_string("La cadena \"".concat(a).concat("\" es mas larga que la cadena \"").concat(b).concat("\".")) + fI + Fi + }; + + testing6(a: Int): IO { + let count: Int <- 0, pow: Int + in { + -- count <- 0; + pow <- 1; + while pow < a + loop + { + count <- count + 1; + pow <- pow * 2; + } + pool; + new IO.out_string("El logaritmo en base 2 de ").out_int(a).out_string(" es ").out_int(count); + } + }; + + testing7(): Object { + case "2 + 2" of + x: Int => new IO.out_string("Es un entero!") -- Missing ";" + y: String => new IO.out_string("Es una cadena!"); + z: Bool => new IO.out_string("Es un booleano!"); + esac + }; +}; + +class Test2 { + test1: Test <- new Test; + + testing1(): Test { + test1.testing4(1 + 1, 1 + 2).testing4(2 + 3, 3 + 5).testing4(5 + 8, 8 + 13) + }; + + testing2(x: Int, y: Int): Test2 { + self + }; + + testing3(): Test2 { + testing2(1 + 1, 1 + 2).testing2(2 + 3, 3 + 5).testing2(5 + 8, true + fALSE) + }; + + testing4(): Object { + test1@Object.copy() + }; +}; + +class Alpha inherits IO { + print() : Object { + out_string("reached!!\n") + }; }; \ No newline at end of file diff --git a/tests/parser/case3.cl b/tests/parser/case3.cl index a7eedc18b..8fd851836 100644 --- a/tests/parser/case3.cl +++ b/tests/parser/case3.cl @@ -1,93 +1,93 @@ -(* Case expressions provide runtime type tests on objects *) - -class Main { - main(): Object { - (new Alpha).print() - }; -}; - -class Test { - test1: Object; - - testing1(): Int { - 2 + 2 - }; - - test2: Int <- 1; - - test3: String <- "1"; - - testing2(a: Alpha, b: Int): Int { - 2 + 2 - }; - - testing3(): String { - "2 + 2" - }; - - testing4(x: Int, y: Int): Test { - self - }; - - testing5(a: String, b: String): IO { - If a.length() < b.length() THeN - new IO.out_string("La cadena \"".concat(b).concat("\" es mas larga que la cadena \"").concat(a).concat("\".")) - eLSe - if a.length() = b.length() THeN - new IO.out_string("La cadena \"".concat(a).concat("\" mide igual que la cadena \"").concat(b).concat("\".")) - ElsE - new IO.out_string("La cadena \"".concat(a).concat("\" es mas larga que la cadena \"").concat(b).concat("\".")) - fI - Fi - }; - - testing6(a: Int): IO { - let count: Int <- 0, pow: Int - in { - -- count <- 0; - pow <- 1; - while pow < a - loop - { - count <- count + 1; - pow <- pow * 2; - } - pool; - new IO.out_string("El logaritmo en base 2 de ").out_int(a).out_string(" es ").out_int(count); - } - }; - - testing7(): Object { - case 2 + false of - x: Int => new IO.out_string("Es un entero!"); - y: string => new IO.out_string("Es una cadena!"); -- Type identifiers starts with a uppercase letter - z: Bool => new IO.out_string("Es un booleano!"); - esac - }; -}; - -class Test2 { - test1: Test <- new Test; - - testing1(): Test { - test1.testing4(1 + 1, 1 + 2).testing4(2 + 3, 3 + 5).testing4(5 + 8, 8 + 13) - }; - - testing2(x: Int, y: Int): Test2 { - self - }; - - testing3(): Test2 { - testing2(1 + 1, 1 + 2).testing2(2 + 3, 3 + 5).testing2(5 + 8, true + fALSE) - }; - - testing4(): Object { - test1@Object.copy() - }; -}; - -class Alpha inherits IO { - print() : Object { - out_string("reached!!\n") - }; +(* Case expressions provide runtime type tests on objects *) + +class Main { + main(): Object { + (new Alpha).print() + }; +}; + +class Test { + test1: Object; + + testing1(): Int { + 2 + 2 + }; + + test2: Int <- 1; + + test3: String <- "1"; + + testing2(a: Alpha, b: Int): Int { + 2 + 2 + }; + + testing3(): String { + "2 + 2" + }; + + testing4(x: Int, y: Int): Test { + self + }; + + testing5(a: String, b: String): IO { + If a.length() < b.length() THeN + new IO.out_string("La cadena \"".concat(b).concat("\" es mas larga que la cadena \"").concat(a).concat("\".")) + eLSe + if a.length() = b.length() THeN + new IO.out_string("La cadena \"".concat(a).concat("\" mide igual que la cadena \"").concat(b).concat("\".")) + ElsE + new IO.out_string("La cadena \"".concat(a).concat("\" es mas larga que la cadena \"").concat(b).concat("\".")) + fI + Fi + }; + + testing6(a: Int): IO { + let count: Int <- 0, pow: Int + in { + -- count <- 0; + pow <- 1; + while pow < a + loop + { + count <- count + 1; + pow <- pow * 2; + } + pool; + new IO.out_string("El logaritmo en base 2 de ").out_int(a).out_string(" es ").out_int(count); + } + }; + + testing7(): Object { + case 2 + false of + x: Int => new IO.out_string("Es un entero!"); + y: string => new IO.out_string("Es una cadena!"); -- Type identifiers starts with a uppercase letter + z: Bool => new IO.out_string("Es un booleano!"); + esac + }; +}; + +class Test2 { + test1: Test <- new Test; + + testing1(): Test { + test1.testing4(1 + 1, 1 + 2).testing4(2 + 3, 3 + 5).testing4(5 + 8, 8 + 13) + }; + + testing2(x: Int, y: Int): Test2 { + self + }; + + testing3(): Test2 { + testing2(1 + 1, 1 + 2).testing2(2 + 3, 3 + 5).testing2(5 + 8, true + fALSE) + }; + + testing4(): Object { + test1@Object.copy() + }; +}; + +class Alpha inherits IO { + print() : Object { + out_string("reached!!\n") + }; }; \ No newline at end of file diff --git a/tests/parser/case4.cl b/tests/parser/case4.cl index 25ca3858f..eb8a3d3a2 100644 --- a/tests/parser/case4.cl +++ b/tests/parser/case4.cl @@ -1,93 +1,93 @@ -(* Case expressions provide runtime type tests on objects *) - -class Main { - main(): Object { - (new Alpha).print() - }; -}; - -class Test { - test1: Object; - - testing1(): Int { - 2 + 2 - }; - - test2: Int <- 1; - - test3: String <- "1"; - - testing2(a: Alpha, b: Int): Int { - 2 + 2 - }; - - testing3(): String { - "2 + 2" - }; - - testing4(x: Int, y: Int): Test { - self - }; - - testing5(a: String, b: String): IO { - If a.length() < b.length() THeN - new IO.out_string("La cadena \"".concat(b).concat("\" es mas larga que la cadena \"").concat(a).concat("\".")) - eLSe - if a.length() = b.length() THeN - new IO.out_string("La cadena \"".concat(a).concat("\" mide igual que la cadena \"").concat(b).concat("\".")) - ElsE - new IO.out_string("La cadena \"".concat(a).concat("\" es mas larga que la cadena \"").concat(b).concat("\".")) - fI - Fi - }; - - testing6(a: Int): IO { - let count: Int <- 0, pow: Int - in { - -- count <- 0; - pow <- 1; - while pow < a - loop - { - count <- count + 1; - pow <- pow * 2; - } - pool; - new IO.out_string("El logaritmo en base 2 de ").out_int(a).out_string(" es ").out_int(count); - } - }; - - testing7(): Object { - case true of - x: Int => new IO.out_string("Es un entero!"); - y: String => new IO.out_string("Es una cadena!"); - Mazinger_Z: Bool => new IO.out_string("Es un booleano!"); -- Identifiers starts with a lowercase letter - esac - }; -}; - -class Test2 { - test1: Test <- new Test; - - testing1(): Test { - test1.testing4(1 + 1, 1 + 2).testing4(2 + 3, 3 + 5).testing4(5 + 8, 8 + 13) - }; - - testing2(x: Int, y: Int): Test2 { - self - }; - - testing3(): Test2 { - testing2(1 + 1, 1 + 2).testing2(2 + 3, 3 + 5).testing2(5 + 8, true + fALSE) - }; - - testing4(): Object { - test1@Object.copy() - }; -}; - -class Alpha inherits IO { - print() : Object { - out_string("reached!!\n") - }; +(* Case expressions provide runtime type tests on objects *) + +class Main { + main(): Object { + (new Alpha).print() + }; +}; + +class Test { + test1: Object; + + testing1(): Int { + 2 + 2 + }; + + test2: Int <- 1; + + test3: String <- "1"; + + testing2(a: Alpha, b: Int): Int { + 2 + 2 + }; + + testing3(): String { + "2 + 2" + }; + + testing4(x: Int, y: Int): Test { + self + }; + + testing5(a: String, b: String): IO { + If a.length() < b.length() THeN + new IO.out_string("La cadena \"".concat(b).concat("\" es mas larga que la cadena \"").concat(a).concat("\".")) + eLSe + if a.length() = b.length() THeN + new IO.out_string("La cadena \"".concat(a).concat("\" mide igual que la cadena \"").concat(b).concat("\".")) + ElsE + new IO.out_string("La cadena \"".concat(a).concat("\" es mas larga que la cadena \"").concat(b).concat("\".")) + fI + Fi + }; + + testing6(a: Int): IO { + let count: Int <- 0, pow: Int + in { + -- count <- 0; + pow <- 1; + while pow < a + loop + { + count <- count + 1; + pow <- pow * 2; + } + pool; + new IO.out_string("El logaritmo en base 2 de ").out_int(a).out_string(" es ").out_int(count); + } + }; + + testing7(): Object { + case true of + x: Int => new IO.out_string("Es un entero!"); + y: String => new IO.out_string("Es una cadena!"); + Mazinger_Z: Bool => new IO.out_string("Es un booleano!"); -- Identifiers starts with a lowercase letter + esac + }; +}; + +class Test2 { + test1: Test <- new Test; + + testing1(): Test { + test1.testing4(1 + 1, 1 + 2).testing4(2 + 3, 3 + 5).testing4(5 + 8, 8 + 13) + }; + + testing2(x: Int, y: Int): Test2 { + self + }; + + testing3(): Test2 { + testing2(1 + 1, 1 + 2).testing2(2 + 3, 3 + 5).testing2(5 + 8, true + fALSE) + }; + + testing4(): Object { + test1@Object.copy() + }; +}; + +class Alpha inherits IO { + print() : Object { + out_string("reached!!\n") + }; }; \ No newline at end of file diff --git a/tests/parser/case5.cl b/tests/parser/case5.cl index b36c663e1..f0483f1f5 100644 --- a/tests/parser/case5.cl +++ b/tests/parser/case5.cl @@ -1,93 +1,93 @@ -(* Case expressions provide runtime type tests on objects *) - -class Main { - main(): Object { - (new Alpha).print() - }; -}; - -class Test { - test1: Object; - - testing1(): Int { - 2 + 2 - }; - - test2: Int <- 1; - - test3: String <- "1"; - - testing2(a: Alpha, b: Int): Int { - 2 + 2 - }; - - testing3(): String { - "2 + 2" - }; - - testing4(x: Int, y: Int): Test { - self - }; - - testing5(a: String, b: String): IO { - If a.length() < b.length() THeN - new IO.out_string("La cadena \"".concat(b).concat("\" es mas larga que la cadena \"").concat(a).concat("\".")) - eLSe - if a.length() = b.length() THeN - new IO.out_string("La cadena \"".concat(a).concat("\" mide igual que la cadena \"").concat(b).concat("\".")) - ElsE - new IO.out_string("La cadena \"".concat(a).concat("\" es mas larga que la cadena \"").concat(b).concat("\".")) - fI - Fi - }; - - testing6(a: Int): IO { - let count: Int <- 0, pow: Int - in { - -- count <- 0; - pow <- 1; - while pow < a - loop - { - count <- count + 1; - pow <- pow * 2; - } - pool; - new IO.out_string("El logaritmo en base 2 de ").out_int(a).out_string(" es ").out_int(count); - } - }; - - testing7(): Object { - case test2 of - x: Int <- new IO.out_string("Es un entero!"); -- Must be '=>' not '<-'; - y: String => new IO.out_string("Es una cadena!"); - z: Bool => new IO.out_string("Es un booleano!"); - esac - }; -}; - -class Test2 { - test1: Test <- new Test; - - testing1(): Test { - test1.testing4(1 + 1, 1 + 2).testing4(2 + 3, 3 + 5).testing4(5 + 8, 8 + 13) - }; - - testing2(x: Int, y: Int): Test2 { - self - }; - - testing3(): Test2 { - testing2(1 + 1, 1 + 2).testing2(2 + 3, 3 + 5).testing2(5 + 8, true + fALSE) - }; - - testing4(): Object { - test1@Object.copy() - }; -}; - -class Alpha inherits IO { - print() : Object { - out_string("reached!!\n") - }; +(* Case expressions provide runtime type tests on objects *) + +class Main { + main(): Object { + (new Alpha).print() + }; +}; + +class Test { + test1: Object; + + testing1(): Int { + 2 + 2 + }; + + test2: Int <- 1; + + test3: String <- "1"; + + testing2(a: Alpha, b: Int): Int { + 2 + 2 + }; + + testing3(): String { + "2 + 2" + }; + + testing4(x: Int, y: Int): Test { + self + }; + + testing5(a: String, b: String): IO { + If a.length() < b.length() THeN + new IO.out_string("La cadena \"".concat(b).concat("\" es mas larga que la cadena \"").concat(a).concat("\".")) + eLSe + if a.length() = b.length() THeN + new IO.out_string("La cadena \"".concat(a).concat("\" mide igual que la cadena \"").concat(b).concat("\".")) + ElsE + new IO.out_string("La cadena \"".concat(a).concat("\" es mas larga que la cadena \"").concat(b).concat("\".")) + fI + Fi + }; + + testing6(a: Int): IO { + let count: Int <- 0, pow: Int + in { + -- count <- 0; + pow <- 1; + while pow < a + loop + { + count <- count + 1; + pow <- pow * 2; + } + pool; + new IO.out_string("El logaritmo en base 2 de ").out_int(a).out_string(" es ").out_int(count); + } + }; + + testing7(): Object { + case test2 of + x: Int <- new IO.out_string("Es un entero!"); -- Must be '=>' not '<-'; + y: String => new IO.out_string("Es una cadena!"); + z: Bool => new IO.out_string("Es un booleano!"); + esac + }; +}; + +class Test2 { + test1: Test <- new Test; + + testing1(): Test { + test1.testing4(1 + 1, 1 + 2).testing4(2 + 3, 3 + 5).testing4(5 + 8, 8 + 13) + }; + + testing2(x: Int, y: Int): Test2 { + self + }; + + testing3(): Test2 { + testing2(1 + 1, 1 + 2).testing2(2 + 3, 3 + 5).testing2(5 + 8, true + fALSE) + }; + + testing4(): Object { + test1@Object.copy() + }; +}; + +class Alpha inherits IO { + print() : Object { + out_string("reached!!\n") + }; }; \ No newline at end of file diff --git a/tests/parser/case6.cl b/tests/parser/case6.cl index 66e7df2ab..c49467a89 100644 --- a/tests/parser/case6.cl +++ b/tests/parser/case6.cl @@ -1,93 +1,93 @@ -(* Case expressions provide runtime type tests on objects *) - -class Main { - main(): Object { - (new Alpha).print() - }; -}; - -class Test { - test1: Object; - - testing1(): Int { - 2 + 2 - }; - - test2: Int <- 1; - - test3: String <- "1"; - - testing2(a: Alpha, b: Int): Int { - 2 + 2 - }; - - testing3(): String { - "2 + 2" - }; - - testing4(x: Int, y: Int): Test { - self - }; - - testing5(a: String, b: String): IO { - If a.length() < b.length() THeN - new IO.out_string("La cadena \"".concat(b).concat("\" es mas larga que la cadena \"").concat(a).concat("\".")) - eLSe - if a.length() = b.length() THeN - new IO.out_string("La cadena \"".concat(a).concat("\" mide igual que la cadena \"").concat(b).concat("\".")) - ElsE - new IO.out_string("La cadena \"".concat(a).concat("\" es mas larga que la cadena \"").concat(b).concat("\".")) - fI - Fi - }; - - testing6(a: Int): IO { - let count: Int <- 0, pow: Int - in { - -- count <- 0; - pow <- 1; - while pow < a - loop - { - count <- count + 1; - pow <- pow * 2; - } - pool; - new IO.out_string("El logaritmo en base 2 de ").out_int(a).out_string(" es ").out_int(count); - } - }; - - testing7(): Object { - case 2 + 2 -- Missing "of" - x: Int => new IO.out_string("Es un entero!"); - y: String => new IO.out_string("Es una cadena!"); - z: Bool => new IO.out_string("Es un booleano!"); - esac - }; -}; - -class Test2 { - test1: Test <- new Test; - - testing1(): Test { - test1.testing4(1 + 1, 1 + 2).testing4(2 + 3, 3 + 5).testing4(5 + 8, 8 + 13) - }; - - testing2(x: Int, y: Int): Test2 { - self - }; - - testing3(): Test2 { - testing2(1 + 1, 1 + 2).testing2(2 + 3, 3 + 5).testing2(5 + 8, true + fALSE) - }; - - testing4(): Object { - test1@Object.copy() - }; -}; - -class Alpha inherits IO { - print() : Object { - out_string("reached!!\n") - }; +(* Case expressions provide runtime type tests on objects *) + +class Main { + main(): Object { + (new Alpha).print() + }; +}; + +class Test { + test1: Object; + + testing1(): Int { + 2 + 2 + }; + + test2: Int <- 1; + + test3: String <- "1"; + + testing2(a: Alpha, b: Int): Int { + 2 + 2 + }; + + testing3(): String { + "2 + 2" + }; + + testing4(x: Int, y: Int): Test { + self + }; + + testing5(a: String, b: String): IO { + If a.length() < b.length() THeN + new IO.out_string("La cadena \"".concat(b).concat("\" es mas larga que la cadena \"").concat(a).concat("\".")) + eLSe + if a.length() = b.length() THeN + new IO.out_string("La cadena \"".concat(a).concat("\" mide igual que la cadena \"").concat(b).concat("\".")) + ElsE + new IO.out_string("La cadena \"".concat(a).concat("\" es mas larga que la cadena \"").concat(b).concat("\".")) + fI + Fi + }; + + testing6(a: Int): IO { + let count: Int <- 0, pow: Int + in { + -- count <- 0; + pow <- 1; + while pow < a + loop + { + count <- count + 1; + pow <- pow * 2; + } + pool; + new IO.out_string("El logaritmo en base 2 de ").out_int(a).out_string(" es ").out_int(count); + } + }; + + testing7(): Object { + case 2 + 2 -- Missing "of" + x: Int => new IO.out_string("Es un entero!"); + y: String => new IO.out_string("Es una cadena!"); + z: Bool => new IO.out_string("Es un booleano!"); + esac + }; +}; + +class Test2 { + test1: Test <- new Test; + + testing1(): Test { + test1.testing4(1 + 1, 1 + 2).testing4(2 + 3, 3 + 5).testing4(5 + 8, 8 + 13) + }; + + testing2(x: Int, y: Int): Test2 { + self + }; + + testing3(): Test2 { + testing2(1 + 1, 1 + 2).testing2(2 + 3, 3 + 5).testing2(5 + 8, true + fALSE) + }; + + testing4(): Object { + test1@Object.copy() + }; +}; + +class Alpha inherits IO { + print() : Object { + out_string("reached!!\n") + }; }; \ No newline at end of file diff --git a/tests/parser/class1.cl b/tests/parser/class1.cl index f4815e3f4..1c0641a94 100644 --- a/tests/parser/class1.cl +++ b/tests/parser/class1.cl @@ -1,20 +1,20 @@ -(* A class is a list of features *) - -class Main { - main(): Object { - (new Alpha).print() - }; -}; - -class Test { - testing(): Int { - 2 + 2 - }; -}; - --- Class names must begin with uppercase letters -class alpha inherits IO { - print() : Object { - out_string("reached!!\n") - }; -}; +(* A class is a list of features *) + +class Main { + main(): Object { + (new Alpha).print() + }; +}; + +class Test { + testing(): Int { + 2 + 2 + }; +}; + +-- Class names must begin with uppercase letters +class alpha inherits IO { + print() : Object { + out_string("reached!!\n") + }; +}; diff --git a/tests/parser/class2.cl b/tests/parser/class2.cl index f363b032a..baf290822 100644 --- a/tests/parser/class2.cl +++ b/tests/parser/class2.cl @@ -1,20 +1,20 @@ -(* A class is a list of features *) - -CLaSS Main { - main(): Object { - (new Alpha).print() - }; -}; - -CLaSS Test { - testing(): Int { - 2 + 2 - }; -}; - --- Type names must begin with uppercase letters -CLaSS Alpha iNHeRiTS iO { - print() : Object { - out_string("reached!!\n") - }; -}; +(* A class is a list of features *) + +CLaSS Main { + main(): Object { + (new Alpha).print() + }; +}; + +CLaSS Test { + testing(): Int { + 2 + 2 + }; +}; + +-- Type names must begin with uppercase letters +CLaSS Alpha iNHeRiTS iO { + print() : Object { + out_string("reached!!\n") + }; +}; diff --git a/tests/parser/class3.cl b/tests/parser/class3.cl index 0c801372a..5c89c5eb8 100644 --- a/tests/parser/class3.cl +++ b/tests/parser/class3.cl @@ -1,34 +1,34 @@ -(* A class is a list of features *) - -class Main { - main(): Object { - (new Alpha).print() - }; -}; - -class Test { - test1: Object; - - testing1(): Int { - 2 + 2 - }; - - test2: Int <- 1; - - test3: String <- "1"; - - -- Missing semicolon - testing2(a: Alpha, b: Int): Int { - 2 + 2 - } - - testing3(): String { - "2 + 2" - }; -}; - -class Alpha inherits IO { - print() : Object { - out_string("reached!!\n") - }; -}; +(* A class is a list of features *) + +class Main { + main(): Object { + (new Alpha).print() + }; +}; + +class Test { + test1: Object; + + testing1(): Int { + 2 + 2 + }; + + test2: Int <- 1; + + test3: String <- "1"; + + -- Missing semicolon + testing2(a: Alpha, b: Int): Int { + 2 + 2 + } + + testing3(): String { + "2 + 2" + }; +}; + +class Alpha inherits IO { + print() : Object { + out_string("reached!!\n") + }; +}; diff --git a/tests/parser/class4.cl b/tests/parser/class4.cl index 5c286b5e6..cdfbc313c 100644 --- a/tests/parser/class4.cl +++ b/tests/parser/class4.cl @@ -1,36 +1,36 @@ -(* A class is a list of features *) - -CLaSS Main { - main(): Object { - (new Alpha).print() - }; -}; - -CLaSS Test { - test1: Object; - - testing1(): Int { - 2 + 2 - }; - - test2: Int <- 1; - - test3: String <- "1"; - - testing2(a: Alpha, b: Int): Int { - 2 + 2 - }; - - -- Only features - 2 + 2; - - testing3(): String { - "2 + 2" - }; -}; - -CLaSS Alpha iNHeRiTS IO { - print() : Object { - out_string("reached!!\n") - }; -}; +(* A class is a list of features *) + +CLaSS Main { + main(): Object { + (new Alpha).print() + }; +}; + +CLaSS Test { + test1: Object; + + testing1(): Int { + 2 + 2 + }; + + test2: Int <- 1; + + test3: String <- "1"; + + testing2(a: Alpha, b: Int): Int { + 2 + 2 + }; + + -- Only features + 2 + 2; + + testing3(): String { + "2 + 2" + }; +}; + +CLaSS Alpha iNHeRiTS IO { + print() : Object { + out_string("reached!!\n") + }; +}; diff --git a/tests/parser/class5.cl b/tests/parser/class5.cl index 3f40c36eb..d6b5c5fda 100644 --- a/tests/parser/class5.cl +++ b/tests/parser/class5.cl @@ -1,34 +1,34 @@ -(* A class is a list of features *) - -class Main { - main(): Object { - (new Alpha).print() - }; -}; - --- Missing '{' -class Test - test1: Object; - - testing1(): Int { - 2 + 2 - }; - - test2: Int <- 1; - - test3: String <- "1"; - - testing2(a: Alpha, b: Int): Int { - 2 + 2 - }; - - testing3(): String { - "2 + 2" - }; -}; - -class Alpha inherits IO { - print() : Object { - out_string("reached!!\n") - }; -}; +(* A class is a list of features *) + +class Main { + main(): Object { + (new Alpha).print() + }; +}; + +-- Missing '{' +class Test + test1: Object; + + testing1(): Int { + 2 + 2 + }; + + test2: Int <- 1; + + test3: String <- "1"; + + testing2(a: Alpha, b: Int): Int { + 2 + 2 + }; + + testing3(): String { + "2 + 2" + }; +}; + +class Alpha inherits IO { + print() : Object { + out_string("reached!!\n") + }; +}; diff --git a/tests/parser/class6.cl b/tests/parser/class6.cl index 8501d2593..af9ecbf15 100644 --- a/tests/parser/class6.cl +++ b/tests/parser/class6.cl @@ -1,34 +1,34 @@ -(* A class is a list of features *) - -CLaSS Main { - main(): Object { - (new Alpha).print() - }; -}; - --- Missing '}' -CLaSS Test { - test1: Object; - - testing1(): Int { - 2 + 2 - }; - - test2: Int <- 1; - - test3: String <- "1"; - - testing2(a: Alpha, b: Int): Int { - 2 + 2 - }; - - testing3(): String { - "2 + 2" - }; -; - -CLaSS Alpha iNHeRiTS IO { - print() : Object { - out_string("reached!!\n") - }; -}; +(* A class is a list of features *) + +CLaSS Main { + main(): Object { + (new Alpha).print() + }; +}; + +-- Missing '}' +CLaSS Test { + test1: Object; + + testing1(): Int { + 2 + 2 + }; + + test2: Int <- 1; + + test3: String <- "1"; + + testing2(a: Alpha, b: Int): Int { + 2 + 2 + }; + + testing3(): String { + "2 + 2" + }; +; + +CLaSS Alpha iNHeRiTS IO { + print() : Object { + out_string("reached!!\n") + }; +}; diff --git a/tests/parser/conditional1.cl b/tests/parser/conditional1.cl index 4d546fc44..f03b9c4e6 100644 --- a/tests/parser/conditional1.cl +++ b/tests/parser/conditional1.cl @@ -1,69 +1,69 @@ -(* A conditional has the form if then else fi *) - -class Main { - main(): Object { - (new Alpha).print() - }; -}; - -class Test { - test1: Object; - - testing1(): Int { - 2 + 2 - }; - - test2: Int <- 1; - - test3: String <- "1"; - - testing2(a: Alpha, b: Int): Int { - 2 + 2 - }; - - testing3(): String { - "2 + 2" - }; - - testing4(x: Int, y: Int): Test { - self - }; - - testing5(a: String, b: String): IO { - if a.length() < b.length() -- Mising "then" - new IO.out_string("La cadena \"".concat(b).concat("\" es mas larga que la cadena \"").concat(a).concat("\".")) - else - if a.length() = b.length() then - new IO.out_string("La cadena \"".concat(a).concat("\" mide igual que la cadena \"").concat(b).concat("\".")) - else - new IO.out_string("La cadena \"".concat(a).concat("\" es mas larga que la cadena \"").concat(b).concat("\".")) - fi - fi - }; -}; - -class Test2 { - test1: Test <- new Test; - - testing1(): Test { - test1.testing4(1 + 1, 1 + 2).testing4(2 + 3, 3 + 5).testing4(5 + 8, 8 + 13) - }; - - testing2(x: Int, y: Int): Test2 { - self - }; - - testing3(): Test2 { - testing2(1 + 1, 1 + 2).testing2(2 + 3, 3 + 5).testing2(5 + 8, true + fALSE) - }; - - testing4(): Object { - test1@Object.copy() - }; -}; - -class Alpha inherits IO { - print() : Object { - out_string("reached!!\n") - }; +(* A conditional has the form if then else fi *) + +class Main { + main(): Object { + (new Alpha).print() + }; +}; + +class Test { + test1: Object; + + testing1(): Int { + 2 + 2 + }; + + test2: Int <- 1; + + test3: String <- "1"; + + testing2(a: Alpha, b: Int): Int { + 2 + 2 + }; + + testing3(): String { + "2 + 2" + }; + + testing4(x: Int, y: Int): Test { + self + }; + + testing5(a: String, b: String): IO { + if a.length() < b.length() -- Mising "then" + new IO.out_string("La cadena \"".concat(b).concat("\" es mas larga que la cadena \"").concat(a).concat("\".")) + else + if a.length() = b.length() then + new IO.out_string("La cadena \"".concat(a).concat("\" mide igual que la cadena \"").concat(b).concat("\".")) + else + new IO.out_string("La cadena \"".concat(a).concat("\" es mas larga que la cadena \"").concat(b).concat("\".")) + fi + fi + }; +}; + +class Test2 { + test1: Test <- new Test; + + testing1(): Test { + test1.testing4(1 + 1, 1 + 2).testing4(2 + 3, 3 + 5).testing4(5 + 8, 8 + 13) + }; + + testing2(x: Int, y: Int): Test2 { + self + }; + + testing3(): Test2 { + testing2(1 + 1, 1 + 2).testing2(2 + 3, 3 + 5).testing2(5 + 8, true + fALSE) + }; + + testing4(): Object { + test1@Object.copy() + }; +}; + +class Alpha inherits IO { + print() : Object { + out_string("reached!!\n") + }; }; \ No newline at end of file diff --git a/tests/parser/conditional2.cl b/tests/parser/conditional2.cl index 4f10c2957..9ebd7fe84 100644 --- a/tests/parser/conditional2.cl +++ b/tests/parser/conditional2.cl @@ -1,69 +1,69 @@ -(* A conditional has the form if then else fi *) - -class Main { - main(): Object { - (new Alpha).print() - }; -}; - -class Test { - test1: Object; - - testing1(): Int { - 2 + 2 - }; - - test2: Int <- 1; - - test3: String <- "1"; - - testing2(a: Alpha, b: Int): Int { - 2 + 2 - }; - - testing3(): String { - "2 + 2" - }; - - testing4(x: Int, y: Int): Test { - self - }; - - testing5(a: String, b: String): IO { - if a.length() < b.length() then - new IO.out_string("La cadena \"".concat(b).concat("\" es mas larga que la cadena \"").concat(a).concat("\".")) - else - if a.length() = b.length() then - new IO.out_string("La cadena \"".concat(a).concat("\" mide igual que la cadena \"").concat(b).concat("\".")) - else - new IO.out_string("La cadena \"".concat(a).concat("\" es mas larga que la cadena \"").concat(b).concat("\".")) - -- Missing "fi" - fi - }; -}; - -class Test2 { - test1: Test <- new Test; - - testing1(): Test { - test1.testing4(1 + 1, 1 + 2).testing4(2 + 3, 3 + 5).testing4(5 + 8, 8 + 13) - }; - - testing2(x: Int, y: Int): Test2 { - self - }; - - testing3(): Test2 { - testing2(1 + 1, 1 + 2).testing2(2 + 3, 3 + 5).testing2(5 + 8, true + fALSE) - }; - - testing4(): Object { - test1@Object.copy() - }; -}; - -class Alpha inherits IO { - print() : Object { - out_string("reached!!\n") - }; +(* A conditional has the form if then else fi *) + +class Main { + main(): Object { + (new Alpha).print() + }; +}; + +class Test { + test1: Object; + + testing1(): Int { + 2 + 2 + }; + + test2: Int <- 1; + + test3: String <- "1"; + + testing2(a: Alpha, b: Int): Int { + 2 + 2 + }; + + testing3(): String { + "2 + 2" + }; + + testing4(x: Int, y: Int): Test { + self + }; + + testing5(a: String, b: String): IO { + if a.length() < b.length() then + new IO.out_string("La cadena \"".concat(b).concat("\" es mas larga que la cadena \"").concat(a).concat("\".")) + else + if a.length() = b.length() then + new IO.out_string("La cadena \"".concat(a).concat("\" mide igual que la cadena \"").concat(b).concat("\".")) + else + new IO.out_string("La cadena \"".concat(a).concat("\" es mas larga que la cadena \"").concat(b).concat("\".")) + -- Missing "fi" + fi + }; +}; + +class Test2 { + test1: Test <- new Test; + + testing1(): Test { + test1.testing4(1 + 1, 1 + 2).testing4(2 + 3, 3 + 5).testing4(5 + 8, 8 + 13) + }; + + testing2(x: Int, y: Int): Test2 { + self + }; + + testing3(): Test2 { + testing2(1 + 1, 1 + 2).testing2(2 + 3, 3 + 5).testing2(5 + 8, true + fALSE) + }; + + testing4(): Object { + test1@Object.copy() + }; +}; + +class Alpha inherits IO { + print() : Object { + out_string("reached!!\n") + }; }; \ No newline at end of file diff --git a/tests/parser/conditional3.cl b/tests/parser/conditional3.cl index 67e991ade..ac143ad42 100644 --- a/tests/parser/conditional3.cl +++ b/tests/parser/conditional3.cl @@ -1,69 +1,69 @@ -(* A conditional has the form if then else fi *) - -class Main { - main(): Object { - (new Alpha).print() - }; -}; - -class Test { - test1: Object; - - testing1(): Int { - 2 + 2 - }; - - test2: Int <- 1; - - test3: String <- "1"; - - testing2(a: Alpha, b: Int): Int { - 2 + 2 - }; - - testing3(): String { - "2 + 2" - }; - - testing4(x: Int, y: Int): Test { - self - }; - - testing5(a: String, b: String): IO { - iF a.length() < b.length() tHen - new IO.out_string("La cadena \"".concat(b).concat("\" es mas larga que la cadena \"").concat(a).concat("\".")) - elsE - if a.length() = b.length() then - new IO.out_string("La cadena \"".concat(a).concat("\" mide igual que la cadena \"").concat(b).concat("\".")) - eLseif -- elseif isn't a keyword - new IO.out_string("La cadena \"".concat(a).concat("\" es mas larga que la cadena \"").concat(b).concat("\".")) - fI - Fi - }; -}; - -class Test2 { - test1: Test <- new Test; - - testing1(): Test { - test1.testing4(1 + 1, 1 + 2).testing4(2 + 3, 3 + 5).testing4(5 + 8, 8 + 13) - }; - - testing2(x: Int, y: Int): Test2 { - self - }; - - testing3(): Test2 { - testing2(1 + 1, 1 + 2).testing2(2 + 3, 3 + 5).testing2(5 + 8, true + fALSE) - }; - - testing4(): Object { - test1@Object.copy() - }; -}; - -class Alpha inherits IO { - print() : Object { - out_string("reached!!\n") - }; +(* A conditional has the form if then else fi *) + +class Main { + main(): Object { + (new Alpha).print() + }; +}; + +class Test { + test1: Object; + + testing1(): Int { + 2 + 2 + }; + + test2: Int <- 1; + + test3: String <- "1"; + + testing2(a: Alpha, b: Int): Int { + 2 + 2 + }; + + testing3(): String { + "2 + 2" + }; + + testing4(x: Int, y: Int): Test { + self + }; + + testing5(a: String, b: String): IO { + iF a.length() < b.length() tHen + new IO.out_string("La cadena \"".concat(b).concat("\" es mas larga que la cadena \"").concat(a).concat("\".")) + elsE + if a.length() = b.length() then + new IO.out_string("La cadena \"".concat(a).concat("\" mide igual que la cadena \"").concat(b).concat("\".")) + eLseif -- elseif isn't a keyword + new IO.out_string("La cadena \"".concat(a).concat("\" es mas larga que la cadena \"").concat(b).concat("\".")) + fI + Fi + }; +}; + +class Test2 { + test1: Test <- new Test; + + testing1(): Test { + test1.testing4(1 + 1, 1 + 2).testing4(2 + 3, 3 + 5).testing4(5 + 8, 8 + 13) + }; + + testing2(x: Int, y: Int): Test2 { + self + }; + + testing3(): Test2 { + testing2(1 + 1, 1 + 2).testing2(2 + 3, 3 + 5).testing2(5 + 8, true + fALSE) + }; + + testing4(): Object { + test1@Object.copy() + }; +}; + +class Alpha inherits IO { + print() : Object { + out_string("reached!!\n") + }; }; \ No newline at end of file diff --git a/tests/parser/conditional4.cl b/tests/parser/conditional4.cl index 0792fdc85..51337f874 100644 --- a/tests/parser/conditional4.cl +++ b/tests/parser/conditional4.cl @@ -1,73 +1,73 @@ -(* A conditional has the form if then else fi *) - -class Main { - main(): Object { - (new Alpha).print() - }; -}; - -class Test { - test1: Object; - - testing1(): Int { - 2 + 2 - }; - - test2: Int <- 1; - - test3: String <- "1"; - - testing2(a: Alpha, b: Int): Int { - 2 + 2 - }; - - testing3(): String { - "2 + 2" - }; - - testing4(x: Int, y: Int): Test { - self - }; - - testing5(a: String, b: String): IO { - If a.length() < b.length() THeN - new IO.out_string("La cadena \"".concat(b).concat("\" es mas larga que la cadena \"").concat(a).concat("\".")) - eLSe - if a.length() = b.length() THeN - new IO.out_string("La cadena \"".concat(a).concat("\" mide igual que la cadena \"").concat(b).concat("\".")) - ElsE - new IO.out_string("La cadena \"".concat(a).concat("\" es mas larga que la cadena \"").concat(b).concat("\".")) - fI - Fi - }; - - testing6(): Int { - if true++ then 1 else 0 -- Condition must be an expression - }; -}; - -class Test2 { - test1: Test <- new Test; - - testing1(): Test { - test1.testing4(1 + 1, 1 + 2).testing4(2 + 3, 3 + 5).testing4(5 + 8, 8 + 13) - }; - - testing2(x: Int, y: Int): Test2 { - self - }; - - testing3(): Test2 { - testing2(1 + 1, 1 + 2).testing2(2 + 3, 3 + 5).testing2(5 + 8, true + fALSE) - }; - - testing4(): Object { - test1@Object.copy() - }; -}; - -class Alpha inherits IO { - print() : Object { - out_string("reached!!\n") - }; +(* A conditional has the form if then else fi *) + +class Main { + main(): Object { + (new Alpha).print() + }; +}; + +class Test { + test1: Object; + + testing1(): Int { + 2 + 2 + }; + + test2: Int <- 1; + + test3: String <- "1"; + + testing2(a: Alpha, b: Int): Int { + 2 + 2 + }; + + testing3(): String { + "2 + 2" + }; + + testing4(x: Int, y: Int): Test { + self + }; + + testing5(a: String, b: String): IO { + If a.length() < b.length() THeN + new IO.out_string("La cadena \"".concat(b).concat("\" es mas larga que la cadena \"").concat(a).concat("\".")) + eLSe + if a.length() = b.length() THeN + new IO.out_string("La cadena \"".concat(a).concat("\" mide igual que la cadena \"").concat(b).concat("\".")) + ElsE + new IO.out_string("La cadena \"".concat(a).concat("\" es mas larga que la cadena \"").concat(b).concat("\".")) + fI + Fi + }; + + testing6(): Int { + if true++ then 1 else 0 -- Condition must be an expression + }; +}; + +class Test2 { + test1: Test <- new Test; + + testing1(): Test { + test1.testing4(1 + 1, 1 + 2).testing4(2 + 3, 3 + 5).testing4(5 + 8, 8 + 13) + }; + + testing2(x: Int, y: Int): Test2 { + self + }; + + testing3(): Test2 { + testing2(1 + 1, 1 + 2).testing2(2 + 3, 3 + 5).testing2(5 + 8, true + fALSE) + }; + + testing4(): Object { + test1@Object.copy() + }; +}; + +class Alpha inherits IO { + print() : Object { + out_string("reached!!\n") + }; }; \ No newline at end of file diff --git a/tests/parser/conditional5.cl b/tests/parser/conditional5.cl index 0c1e1aad0..399515701 100644 --- a/tests/parser/conditional5.cl +++ b/tests/parser/conditional5.cl @@ -1,73 +1,73 @@ -(* A conditional has the form if then else fi *) - -class Main { - main(): Object { - (new Alpha).print() - }; -}; - -class Test { - test1: Object; - - testing1(): Int { - 2 + 2 - }; - - test2: Int <- 1; - - test3: String <- "1"; - - testing2(a: Alpha, b: Int): Int { - 2 + 2 - }; - - testing3(): String { - "2 + 2" - }; - - testing4(x: Int, y: Int): Test { - self - }; - - testing5(a: String, b: String): IO { - If a.length() < b.length() THeN - new IO.out_string("La cadena \"".concat(b).concat("\" es mas larga que la cadena \"").concat(a).concat("\".")) - eLSe - if a.length() = b.length() THeN - new IO.out_string("La cadena \"".concat(a).concat("\" mide igual que la cadena \"").concat(b).concat("\".")) - ElsE - new IO.out_string("La cadena \"".concat(a).concat("\" es mas larga que la cadena \"").concat(b).concat("\".")) - fI - Fi - }; - - testing6(): Int { - if true then true++ else 0 -- If branch must be an expression - }; -}; - -class Test2 { - test1: Test <- new Test; - - testing1(): Test { - test1.testing4(1 + 1, 1 + 2).testing4(2 + 3, 3 + 5).testing4(5 + 8, 8 + 13) - }; - - testing2(x: Int, y: Int): Test2 { - self - }; - - testing3(): Test2 { - testing2(1 + 1, 1 + 2).testing2(2 + 3, 3 + 5).testing2(5 + 8, true + fALSE) - }; - - testing4(): Object { - test1@Object.copy() - }; -}; - -class Alpha inherits IO { - print() : Object { - out_string("reached!!\n") - }; +(* A conditional has the form if then else fi *) + +class Main { + main(): Object { + (new Alpha).print() + }; +}; + +class Test { + test1: Object; + + testing1(): Int { + 2 + 2 + }; + + test2: Int <- 1; + + test3: String <- "1"; + + testing2(a: Alpha, b: Int): Int { + 2 + 2 + }; + + testing3(): String { + "2 + 2" + }; + + testing4(x: Int, y: Int): Test { + self + }; + + testing5(a: String, b: String): IO { + If a.length() < b.length() THeN + new IO.out_string("La cadena \"".concat(b).concat("\" es mas larga que la cadena \"").concat(a).concat("\".")) + eLSe + if a.length() = b.length() THeN + new IO.out_string("La cadena \"".concat(a).concat("\" mide igual que la cadena \"").concat(b).concat("\".")) + ElsE + new IO.out_string("La cadena \"".concat(a).concat("\" es mas larga que la cadena \"").concat(b).concat("\".")) + fI + Fi + }; + + testing6(): Int { + if true then true++ else 0 -- If branch must be an expression + }; +}; + +class Test2 { + test1: Test <- new Test; + + testing1(): Test { + test1.testing4(1 + 1, 1 + 2).testing4(2 + 3, 3 + 5).testing4(5 + 8, 8 + 13) + }; + + testing2(x: Int, y: Int): Test2 { + self + }; + + testing3(): Test2 { + testing2(1 + 1, 1 + 2).testing2(2 + 3, 3 + 5).testing2(5 + 8, true + fALSE) + }; + + testing4(): Object { + test1@Object.copy() + }; +}; + +class Alpha inherits IO { + print() : Object { + out_string("reached!!\n") + }; }; \ No newline at end of file diff --git a/tests/parser/conditional6.cl b/tests/parser/conditional6.cl index 02310404a..8daa35d7f 100644 --- a/tests/parser/conditional6.cl +++ b/tests/parser/conditional6.cl @@ -1,73 +1,73 @@ -(* A conditional has the form if then else fi *) - -class Main { - main(): Object { - (new Alpha).print() - }; -}; - -class Test { - test1: Object; - - testing1(): Int { - 2 + 2 - }; - - test2: Int <- 1; - - test3: String <- "1"; - - testing2(a: Alpha, b: Int): Int { - 2 + 2 - }; - - testing3(): String { - "2 + 2" - }; - - testing4(x: Int, y: Int): Test { - self - }; - - testing5(a: String, b: String): IO { - If a.length() < b.length() THeN - new IO.out_string("La cadena \"".concat(b).concat("\" es mas larga que la cadena \"").concat(a).concat("\".")) - eLSe - if a.length() = b.length() THeN - new IO.out_string("La cadena \"".concat(a).concat("\" mide igual que la cadena \"").concat(b).concat("\".")) - ElsE - new IO.out_string("La cadena \"".concat(a).concat("\" es mas larga que la cadena \"").concat(b).concat("\".")) - fI - Fi - }; - - testing6(): Int { - if true then 1 else false++ -- Else branch must be an expression - }; -}; - -class Test2 { - test1: Test <- new Test; - - testing1(): Test { - test1.testing4(1 + 1, 1 + 2).testing4(2 + 3, 3 + 5).testing4(5 + 8, 8 + 13) - }; - - testing2(x: Int, y: Int): Test2 { - self - }; - - testing3(): Test2 { - testing2(1 + 1, 1 + 2).testing2(2 + 3, 3 + 5).testing2(5 + 8, true + fALSE) - }; - - testing4(): Object { - test1@Object.copy() - }; -}; - -class Alpha inherits IO { - print() : Object { - out_string("reached!!\n") - }; +(* A conditional has the form if then else fi *) + +class Main { + main(): Object { + (new Alpha).print() + }; +}; + +class Test { + test1: Object; + + testing1(): Int { + 2 + 2 + }; + + test2: Int <- 1; + + test3: String <- "1"; + + testing2(a: Alpha, b: Int): Int { + 2 + 2 + }; + + testing3(): String { + "2 + 2" + }; + + testing4(x: Int, y: Int): Test { + self + }; + + testing5(a: String, b: String): IO { + If a.length() < b.length() THeN + new IO.out_string("La cadena \"".concat(b).concat("\" es mas larga que la cadena \"").concat(a).concat("\".")) + eLSe + if a.length() = b.length() THeN + new IO.out_string("La cadena \"".concat(a).concat("\" mide igual que la cadena \"").concat(b).concat("\".")) + ElsE + new IO.out_string("La cadena \"".concat(a).concat("\" es mas larga que la cadena \"").concat(b).concat("\".")) + fI + Fi + }; + + testing6(): Int { + if true then 1 else false++ -- Else branch must be an expression + }; +}; + +class Test2 { + test1: Test <- new Test; + + testing1(): Test { + test1.testing4(1 + 1, 1 + 2).testing4(2 + 3, 3 + 5).testing4(5 + 8, 8 + 13) + }; + + testing2(x: Int, y: Int): Test2 { + self + }; + + testing3(): Test2 { + testing2(1 + 1, 1 + 2).testing2(2 + 3, 3 + 5).testing2(5 + 8, true + fALSE) + }; + + testing4(): Object { + test1@Object.copy() + }; +}; + +class Alpha inherits IO { + print() : Object { + out_string("reached!!\n") + }; }; \ No newline at end of file diff --git a/tests/parser/dispatch1.cl b/tests/parser/dispatch1.cl index 2ca394716..2eba9db03 100644 --- a/tests/parser/dispatch1.cl +++ b/tests/parser/dispatch1.cl @@ -1,45 +1,45 @@ -(* There are three forms of dispatch (i.e. method call) in Cool. The three forms differ only in how the called method is selected *) - -class Main { - main(): Object { - (new Alpha).print() - }; -}; - -class Test { - test1: Object; - - testing1(): Int { - 2 + 2 - }; - - test2: Int <- 1; - - test3: String <- "1"; - - testing2(a: Alpha, b: Int): Int { - 2 + 2 - }; - - testing3(): String { - "2 + 2" - }; - - testing4(x: Int, y: Int): Test { - self - }; -}; - -class Test2 { - test1: Test <- new Test; - - testing1(): Test { - Test1.testing4(1, 2).testing4(3, 4).testing4(5, 6) -- Objet identifiers begin with a lower case letter - }; -} - -class Alpha inherits IO { - print() : Object { - out_string("reached!!\n") - }; +(* There are three forms of dispatch (i.e. method call) in Cool. The three forms differ only in how the called method is selected *) + +class Main { + main(): Object { + (new Alpha).print() + }; +}; + +class Test { + test1: Object; + + testing1(): Int { + 2 + 2 + }; + + test2: Int <- 1; + + test3: String <- "1"; + + testing2(a: Alpha, b: Int): Int { + 2 + 2 + }; + + testing3(): String { + "2 + 2" + }; + + testing4(x: Int, y: Int): Test { + self + }; +}; + +class Test2 { + test1: Test <- new Test; + + testing1(): Test { + Test1.testing4(1, 2).testing4(3, 4).testing4(5, 6) -- Objet identifiers begin with a lower case letter + }; +} + +class Alpha inherits IO { + print() : Object { + out_string("reached!!\n") + }; }; \ No newline at end of file diff --git a/tests/parser/dispatch2.cl b/tests/parser/dispatch2.cl index 0b57467a1..139eba918 100644 --- a/tests/parser/dispatch2.cl +++ b/tests/parser/dispatch2.cl @@ -1,45 +1,45 @@ -(* There are three forms of dispatch (i.e. method call) in Cool. The three forms differ only in how the called method is selected *) - -class Main { - main(): Object { - (new Alpha).print() - }; -}; - -class Test { - test1: Object; - - testing1(): Int { - 2 + 2 - }; - - test2: Int <- 1; - - test3: String <- "1"; - - testing2(a: Alpha, b: Int): Int { - 2 + 2 - }; - - testing3(): String { - "2 + 2" - }; - - testing4(x: Int, y: Int): Test { - self - }; -}; - -class Test2 { - test1: Test <- new Test; - - testing1(): Test { - test1.testing4(1 + 1, 1 + 2).testing4(2 + 3, 3 + 5).testing4(5 + 8, 8 + 13,) -- Extra comma - }; -} - -class Alpha inherits IO { - print() : Object { - out_string("reached!!\n") - }; +(* There are three forms of dispatch (i.e. method call) in Cool. The three forms differ only in how the called method is selected *) + +class Main { + main(): Object { + (new Alpha).print() + }; +}; + +class Test { + test1: Object; + + testing1(): Int { + 2 + 2 + }; + + test2: Int <- 1; + + test3: String <- "1"; + + testing2(a: Alpha, b: Int): Int { + 2 + 2 + }; + + testing3(): String { + "2 + 2" + }; + + testing4(x: Int, y: Int): Test { + self + }; +}; + +class Test2 { + test1: Test <- new Test; + + testing1(): Test { + test1.testing4(1 + 1, 1 + 2).testing4(2 + 3, 3 + 5).testing4(5 + 8, 8 + 13,) -- Extra comma + }; +} + +class Alpha inherits IO { + print() : Object { + out_string("reached!!\n") + }; }; \ No newline at end of file diff --git a/tests/parser/dispatch3.cl b/tests/parser/dispatch3.cl index 9f1a5afff..0d88f5c23 100644 --- a/tests/parser/dispatch3.cl +++ b/tests/parser/dispatch3.cl @@ -1,45 +1,45 @@ -(* There are three forms of dispatch (i.e. method call) in Cool. The three forms differ only in how the called method is selected *) - -class Main { - main(): Object { - (new Alpha).print() - }; -}; - -class Test { - test1: Object; - - testing1(): Int { - 2 + 2 - }; - - test2: Int <- 1; - - test3: String <- "1"; - - testing2(a: Alpha, b: Int): Int { - 2 + 2 - }; - - testing3(): String { - "2 + 2" - }; - - testing4(x: Int, y: Int): Test { - self - }; -}; - -class Test2 { - test1: Test <- new Test; - - testing1(): Test { - test1.testing4(1 + 1, 1 + 2).Testing4(2 + 3, 3 + 5).testing4(5 + 8, 8 + 13) -- Identifiers begin with a lower case letter - }; -} - -class Alpha inherits IO { - print() : Object { - out_string("reached!!\n") - }; +(* There are three forms of dispatch (i.e. method call) in Cool. The three forms differ only in how the called method is selected *) + +class Main { + main(): Object { + (new Alpha).print() + }; +}; + +class Test { + test1: Object; + + testing1(): Int { + 2 + 2 + }; + + test2: Int <- 1; + + test3: String <- "1"; + + testing2(a: Alpha, b: Int): Int { + 2 + 2 + }; + + testing3(): String { + "2 + 2" + }; + + testing4(x: Int, y: Int): Test { + self + }; +}; + +class Test2 { + test1: Test <- new Test; + + testing1(): Test { + test1.testing4(1 + 1, 1 + 2).Testing4(2 + 3, 3 + 5).testing4(5 + 8, 8 + 13) -- Identifiers begin with a lower case letter + }; +} + +class Alpha inherits IO { + print() : Object { + out_string("reached!!\n") + }; }; \ No newline at end of file diff --git a/tests/parser/dispatch4.cl b/tests/parser/dispatch4.cl index d1efc469d..dc13fd762 100644 --- a/tests/parser/dispatch4.cl +++ b/tests/parser/dispatch4.cl @@ -1,53 +1,53 @@ -(* There are three forms of dispatch (i.e. method call) in Cool. The three forms differ only in how the called method is selected *) - -class Main { - main(): Object { - (new Alpha).print() - }; -}; - -class Test { - test1: Object; - - testing1(): Int { - 2 + 2 - }; - - test2: Int <- 1; - - test3: String <- "1"; - - testing2(a: Alpha, b: Int): Int { - 2 + 2 - }; - - testing3(): String { - "2 + 2" - }; - - testing4(x: Int, y: Int): Test { - self - }; -}; - -class Test2 { - test1: Test <- new Test; - - testing1(): Test { - test1.testing4(1 + 1, 1 + 2).testing4(2 + 3, 3 + 5).testing4(5 + 8, 8 + 13) - }; - - testing2(x: Int, y: Int): Test2 { - self - }; - - testing3(): Test2 { - self.testing2(1 + 1, 1 + 2).testing2(2 + 3, 3 + 5).testing2(5 + 8, true++) -- Arguments must be expressions - }; -} - -class Alpha inherits IO { - print() : Object { - out_string("reached!!\n") - }; +(* There are three forms of dispatch (i.e. method call) in Cool. The three forms differ only in how the called method is selected *) + +class Main { + main(): Object { + (new Alpha).print() + }; +}; + +class Test { + test1: Object; + + testing1(): Int { + 2 + 2 + }; + + test2: Int <- 1; + + test3: String <- "1"; + + testing2(a: Alpha, b: Int): Int { + 2 + 2 + }; + + testing3(): String { + "2 + 2" + }; + + testing4(x: Int, y: Int): Test { + self + }; +}; + +class Test2 { + test1: Test <- new Test; + + testing1(): Test { + test1.testing4(1 + 1, 1 + 2).testing4(2 + 3, 3 + 5).testing4(5 + 8, 8 + 13) + }; + + testing2(x: Int, y: Int): Test2 { + self + }; + + testing3(): Test2 { + self.testing2(1 + 1, 1 + 2).testing2(2 + 3, 3 + 5).testing2(5 + 8, true++) -- Arguments must be expressions + }; +} + +class Alpha inherits IO { + print() : Object { + out_string("reached!!\n") + }; }; \ No newline at end of file diff --git a/tests/parser/dispatch5.cl b/tests/parser/dispatch5.cl index 63a5afa79..b7bae25e1 100644 --- a/tests/parser/dispatch5.cl +++ b/tests/parser/dispatch5.cl @@ -1,53 +1,53 @@ -(* There are three forms of dispatch (i.e. method call) in Cool. The three forms differ only in how the called method is selected *) - -class Main { - main(): Object { - (new Alpha).print() - }; -}; - -class Test { - test1: Object; - - testing1(): Int { - 2 + 2 - }; - - test2: Int <- 1; - - test3: String <- "1"; - - testing2(a: Alpha, b: Int): Int { - 2 + 2 - }; - - testing3(): String { - "2 + 2" - }; - - testing4(x: Int, y: Int): Test { - self - }; -}; - -class Test2 { - test1: Test <- new Test; - - testing1(): Test { - test1.testing4(1 + 1, 1 + 2).testing4(2 + 3, 3 + 5).testing4(5 + 8, 8 + 13) - }; - - testing2(x: Int, y: Int): Test2 { - self - }; - - testing3(): Test2 { - testing2(1 + 1, 1 + 2).testing2(2 + 3, 3 + 5).testing2(5 + 8, ,true + fALSE) -- Extra comma - }; -} - -class Alpha inherits IO { - print() : Object { - out_string("reached!!\n") - }; +(* There are three forms of dispatch (i.e. method call) in Cool. The three forms differ only in how the called method is selected *) + +class Main { + main(): Object { + (new Alpha).print() + }; +}; + +class Test { + test1: Object; + + testing1(): Int { + 2 + 2 + }; + + test2: Int <- 1; + + test3: String <- "1"; + + testing2(a: Alpha, b: Int): Int { + 2 + 2 + }; + + testing3(): String { + "2 + 2" + }; + + testing4(x: Int, y: Int): Test { + self + }; +}; + +class Test2 { + test1: Test <- new Test; + + testing1(): Test { + test1.testing4(1 + 1, 1 + 2).testing4(2 + 3, 3 + 5).testing4(5 + 8, 8 + 13) + }; + + testing2(x: Int, y: Int): Test2 { + self + }; + + testing3(): Test2 { + testing2(1 + 1, 1 + 2).testing2(2 + 3, 3 + 5).testing2(5 + 8, ,true + fALSE) -- Extra comma + }; +} + +class Alpha inherits IO { + print() : Object { + out_string("reached!!\n") + }; }; \ No newline at end of file diff --git a/tests/parser/dispatch6.cl b/tests/parser/dispatch6.cl index 0a953e2e6..6887c60cd 100644 --- a/tests/parser/dispatch6.cl +++ b/tests/parser/dispatch6.cl @@ -1,57 +1,57 @@ -(* There are three forms of dispatch (i.e. method call) in Cool. The three forms differ only in how the called method is selected *) - -class Main { - main(): Object { - (new Alpha).print() - }; -}; - -class Test { - test1: Object; - - testing1(): Int { - 2 + 2 - }; - - test2: Int <- 1; - - test3: String <- "1"; - - testing2(a: Alpha, b: Int): Int { - 2 + 2 - }; - - testing3(): String { - "2 + 2" - }; - - testing4(x: Int, y: Int): Test { - self - }; -}; - -class Test2 { - test1: Test <- new Test; - - testing1(): Test { - test1.testing4(1 + 1, 1 + 2).testing4(2 + 3, 3 + 5).testing4(5 + 8, 8 + 13) - }; - - testing2(x: Int, y: Int): Test2 { - self - }; - - testing3(): Test2 { - testing2(1 + 1, 1 + 2).testing2(2 + 3, 3 + 5).testing2(5 + 8, true + fALSE) - }; - - testing4(): Object { - test1@object.copy() -- Type identifiers begin with a upper case letter - }; -} - -class Alpha inherits IO { - print() : Object { - out_string("reached!!\n") - }; +(* There are three forms of dispatch (i.e. method call) in Cool. The three forms differ only in how the called method is selected *) + +class Main { + main(): Object { + (new Alpha).print() + }; +}; + +class Test { + test1: Object; + + testing1(): Int { + 2 + 2 + }; + + test2: Int <- 1; + + test3: String <- "1"; + + testing2(a: Alpha, b: Int): Int { + 2 + 2 + }; + + testing3(): String { + "2 + 2" + }; + + testing4(x: Int, y: Int): Test { + self + }; +}; + +class Test2 { + test1: Test <- new Test; + + testing1(): Test { + test1.testing4(1 + 1, 1 + 2).testing4(2 + 3, 3 + 5).testing4(5 + 8, 8 + 13) + }; + + testing2(x: Int, y: Int): Test2 { + self + }; + + testing3(): Test2 { + testing2(1 + 1, 1 + 2).testing2(2 + 3, 3 + 5).testing2(5 + 8, true + fALSE) + }; + + testing4(): Object { + test1@object.copy() -- Type identifiers begin with a upper case letter + }; +} + +class Alpha inherits IO { + print() : Object { + out_string("reached!!\n") + }; }; \ No newline at end of file diff --git a/tests/parser/dispatch7.cl b/tests/parser/dispatch7.cl index 3ecac4d0f..a9ff1b67a 100644 --- a/tests/parser/dispatch7.cl +++ b/tests/parser/dispatch7.cl @@ -1,57 +1,57 @@ -(* There are three forms of dispatch (i.e. method call) in Cool. The three forms differ only in how the called method is selected *) - -class Main { - main(): Object { - (new Alpha).print() - }; -}; - -class Test { - test1: Object; - - testing1(): Int { - 2 + 2 - }; - - test2: Int <- 1; - - test3: String <- "1"; - - testing2(a: Alpha, b: Int): Int { - 2 + 2 - }; - - testing3(): String { - "2 + 2" - }; - - testing4(x: Int, y: Int): Test { - self - }; -}; - -class Test2 { - test1: Test <- new Test; - - testing1(): Test { - test1.testing4(1 + 1, 1 + 2).testing4(2 + 3, 3 + 5).testing4(5 + 8, 8 + 13) - }; - - testing2(x: Int, y: Int): Test2 { - self - }; - - testing3(): Test2 { - testing2(1 + 1, 1 + 2).testing2(2 + 3, 3 + 5).testing2(5 + 8, true + fALSE) - }; - - testing4(): Object { - test1@Object.Copy() -- Identifiers begin with a lower case letter - }; -} - -class Alpha inherits IO { - print() : Object { - out_string("reached!!\n") - }; +(* There are three forms of dispatch (i.e. method call) in Cool. The three forms differ only in how the called method is selected *) + +class Main { + main(): Object { + (new Alpha).print() + }; +}; + +class Test { + test1: Object; + + testing1(): Int { + 2 + 2 + }; + + test2: Int <- 1; + + test3: String <- "1"; + + testing2(a: Alpha, b: Int): Int { + 2 + 2 + }; + + testing3(): String { + "2 + 2" + }; + + testing4(x: Int, y: Int): Test { + self + }; +}; + +class Test2 { + test1: Test <- new Test; + + testing1(): Test { + test1.testing4(1 + 1, 1 + 2).testing4(2 + 3, 3 + 5).testing4(5 + 8, 8 + 13) + }; + + testing2(x: Int, y: Int): Test2 { + self + }; + + testing3(): Test2 { + testing2(1 + 1, 1 + 2).testing2(2 + 3, 3 + 5).testing2(5 + 8, true + fALSE) + }; + + testing4(): Object { + test1@Object.Copy() -- Identifiers begin with a lower case letter + }; +} + +class Alpha inherits IO { + print() : Object { + out_string("reached!!\n") + }; }; \ No newline at end of file diff --git a/tests/parser/dispatch8.cl b/tests/parser/dispatch8.cl index eef0455ef..c1bb2abba 100644 --- a/tests/parser/dispatch8.cl +++ b/tests/parser/dispatch8.cl @@ -1,57 +1,57 @@ -(* There are three forms of dispatch (i.e. method call) in Cool. The three forms differ only in how the called method is selected *) - -class Main { - main(): Object { - (new Alpha).print() - }; -}; - -class Test { - test1: Object; - - testing1(): Int { - 2 + 2 - }; - - test2: Int <- 1; - - test3: String <- "1"; - - testing2(a: Alpha, b: Int): Int { - 2 + 2 - }; - - testing3(): String { - "2 + 2" - }; - - testing4(x: Int, y: Int): Test { - self - }; -}; - -class Test2 { - test1: Test <- new Test; - - testing1(): Test { - test1.testing4(1 + 1, 1 + 2).testing4(2 + 3, 3 + 5).testing4(5 + 8, 8 + 13) - }; - - testing2(x: Int, y: Int): Test2 { - self - }; - - testing3(): Test2 { - testing2(1 + 1, 1 + 2).testing2(2 + 3, 3 + 5).testing2(5 + 8, true + fALSE) - }; - - testing4(): Object { - test1@Object.copy(,) -- Extra comma - }; -} - -class Alpha inherits IO { - print() : Object { - out_string("reached!!\n") - }; +(* There are three forms of dispatch (i.e. method call) in Cool. The three forms differ only in how the called method is selected *) + +class Main { + main(): Object { + (new Alpha).print() + }; +}; + +class Test { + test1: Object; + + testing1(): Int { + 2 + 2 + }; + + test2: Int <- 1; + + test3: String <- "1"; + + testing2(a: Alpha, b: Int): Int { + 2 + 2 + }; + + testing3(): String { + "2 + 2" + }; + + testing4(x: Int, y: Int): Test { + self + }; +}; + +class Test2 { + test1: Test <- new Test; + + testing1(): Test { + test1.testing4(1 + 1, 1 + 2).testing4(2 + 3, 3 + 5).testing4(5 + 8, 8 + 13) + }; + + testing2(x: Int, y: Int): Test2 { + self + }; + + testing3(): Test2 { + testing2(1 + 1, 1 + 2).testing2(2 + 3, 3 + 5).testing2(5 + 8, true + fALSE) + }; + + testing4(): Object { + test1@Object.copy(,) -- Extra comma + }; +} + +class Alpha inherits IO { + print() : Object { + out_string("reached!!\n") + }; }; \ No newline at end of file diff --git a/tests/parser/dispatch9.cl b/tests/parser/dispatch9.cl index 5fdef22d6..8914a18c2 100644 --- a/tests/parser/dispatch9.cl +++ b/tests/parser/dispatch9.cl @@ -1,61 +1,61 @@ -(* There are three forms of dispatch (i.e. method call) in Cool. The three forms differ only in how the called method is selected *) - -class Main { - main(): Object { - (new Alpha).print() - }; -}; - -class Test { - test1: Object; - - testing1(): Int { - 2 + 2 - }; - - test2: Int <- 1; - - test3: String <- "1"; - - testing2(a: Alpha, b: Int): Int { - 2 + 2 - }; - - testing3(): String { - "2 + 2" - }; - - testing4(x: Int, y: Int): Test { - self - }; -}; - -class Test2 { - test1: Test <- new Test; - - testing1(): Test { - test1.testing4(1 + 1, 1 + 2).testing4(2 + 3, 3 + 5).testing4(5 + 8, 8 + 13) - }; - - testing2(x: Int, y: Int): Test2 { - self - }; - - testing3(): Test2 { - testing2(1 + 1, 1 + 2).testing2(2 + 3, 3 + 5).testing2(5 + 8, true + fALSE) - }; - - testing4(): Object { - test1@Object.copy() - }; - - testing5(): Object { - test1:Object.copy() -- Must be '@' not ':' - }; -} - -class Alpha inherits IO { - print() : Object { - out_string("reached!!\n") - }; +(* There are three forms of dispatch (i.e. method call) in Cool. The three forms differ only in how the called method is selected *) + +class Main { + main(): Object { + (new Alpha).print() + }; +}; + +class Test { + test1: Object; + + testing1(): Int { + 2 + 2 + }; + + test2: Int <- 1; + + test3: String <- "1"; + + testing2(a: Alpha, b: Int): Int { + 2 + 2 + }; + + testing3(): String { + "2 + 2" + }; + + testing4(x: Int, y: Int): Test { + self + }; +}; + +class Test2 { + test1: Test <- new Test; + + testing1(): Test { + test1.testing4(1 + 1, 1 + 2).testing4(2 + 3, 3 + 5).testing4(5 + 8, 8 + 13) + }; + + testing2(x: Int, y: Int): Test2 { + self + }; + + testing3(): Test2 { + testing2(1 + 1, 1 + 2).testing2(2 + 3, 3 + 5).testing2(5 + 8, true + fALSE) + }; + + testing4(): Object { + test1@Object.copy() + }; + + testing5(): Object { + test1:Object.copy() -- Must be '@' not ':' + }; +} + +class Alpha inherits IO { + print() : Object { + out_string("reached!!\n") + }; }; \ No newline at end of file diff --git a/tests/parser/let1.cl b/tests/parser/let1.cl index 576ae383c..0e5fc6d17 100644 --- a/tests/parser/let1.cl +++ b/tests/parser/let1.cl @@ -1,85 +1,85 @@ -(* A let expression has the form let : [ <- ], ..., : [ <- ] in *) - -class Main { - main(): Object { - (new Alpha).print() - }; -}; - -class Test { - test1: Object; - - testing1(): Int { - 2 + 2 - }; - - test2: Int <- 1; - - test3: String <- "1"; - - testing2(a: Alpha, b: Int): Int { - 2 + 2 - }; - - testing3(): String { - "2 + 2" - }; - - testing4(x: Int, y: Int): Test { - self - }; - - testing5(a: String, b: String): IO { - If a.length() < b.length() THeN - new IO.out_string("La cadena \"".concat(b).concat("\" es mas larga que la cadena \"").concat(a).concat("\".")) - eLSe - if a.length() = b.length() THeN - new IO.out_string("La cadena \"".concat(a).concat("\" mide igual que la cadena \"").concat(b).concat("\".")) - ElsE - new IO.out_string("La cadena \"".concat(a).concat("\" es mas larga que la cadena \"").concat(b).concat("\".")) - fI - Fi - }; - - testing6(a: Int): IO { - let Count: Int <- 0, pow: Int <- 1 -- Object identifiers starts with a lowercase letter - in { - -- count <- 0; - -- pow <- 1; - while pow < a - loop - { - count <- count + 1; - pow <- pow * 2; - } - pool; - new IO.out_string("El logaritmo en base 2 de ").out_int(a).out_string(" es ").out_int(count); - } - }; -}; - -class Test2 { - test1: Test <- new Test; - - testing1(): Test { - test1.testing4(1 + 1, 1 + 2).testing4(2 + 3, 3 + 5).testing4(5 + 8, 8 + 13) - }; - - testing2(x: Int, y: Int): Test2 { - self - }; - - testing3(): Test2 { - testing2(1 + 1, 1 + 2).testing2(2 + 3, 3 + 5).testing2(5 + 8, true + fALSE) - }; - - testing4(): Object { - test1@Object.copy() - }; -}; - -class Alpha inherits IO { - print() : Object { - out_string("reached!!\n") - }; +(* A let expression has the form let : [ <- ], ..., : [ <- ] in *) + +class Main { + main(): Object { + (new Alpha).print() + }; +}; + +class Test { + test1: Object; + + testing1(): Int { + 2 + 2 + }; + + test2: Int <- 1; + + test3: String <- "1"; + + testing2(a: Alpha, b: Int): Int { + 2 + 2 + }; + + testing3(): String { + "2 + 2" + }; + + testing4(x: Int, y: Int): Test { + self + }; + + testing5(a: String, b: String): IO { + If a.length() < b.length() THeN + new IO.out_string("La cadena \"".concat(b).concat("\" es mas larga que la cadena \"").concat(a).concat("\".")) + eLSe + if a.length() = b.length() THeN + new IO.out_string("La cadena \"".concat(a).concat("\" mide igual que la cadena \"").concat(b).concat("\".")) + ElsE + new IO.out_string("La cadena \"".concat(a).concat("\" es mas larga que la cadena \"").concat(b).concat("\".")) + fI + Fi + }; + + testing6(a: Int): IO { + let Count: Int <- 0, pow: Int <- 1 -- Object identifiers starts with a lowercase letter + in { + -- count <- 0; + -- pow <- 1; + while pow < a + loop + { + count <- count + 1; + pow <- pow * 2; + } + pool; + new IO.out_string("El logaritmo en base 2 de ").out_int(a).out_string(" es ").out_int(count); + } + }; +}; + +class Test2 { + test1: Test <- new Test; + + testing1(): Test { + test1.testing4(1 + 1, 1 + 2).testing4(2 + 3, 3 + 5).testing4(5 + 8, 8 + 13) + }; + + testing2(x: Int, y: Int): Test2 { + self + }; + + testing3(): Test2 { + testing2(1 + 1, 1 + 2).testing2(2 + 3, 3 + 5).testing2(5 + 8, true + fALSE) + }; + + testing4(): Object { + test1@Object.copy() + }; +}; + +class Alpha inherits IO { + print() : Object { + out_string("reached!!\n") + }; }; \ No newline at end of file diff --git a/tests/parser/let2.cl b/tests/parser/let2.cl index 4cfaef0f8..01d055dac 100644 --- a/tests/parser/let2.cl +++ b/tests/parser/let2.cl @@ -1,85 +1,85 @@ -(* A let expression has the form let : [ <- ], ..., : [ <- ] in *) - -class Main { - main(): Object { - (new Alpha).print() - }; -}; - -class Test { - test1: Object; - - testing1(): Int { - 2 + 2 - }; - - test2: Int <- 1; - - test3: String <- "1"; - - testing2(a: Alpha, b: Int): Int { - 2 + 2 - }; - - testing3(): String { - "2 + 2" - }; - - testing4(x: Int, y: Int): Test { - self - }; - - testing5(a: String, b: String): IO { - If a.length() < b.length() THeN - new IO.out_string("La cadena \"".concat(b).concat("\" es mas larga que la cadena \"").concat(a).concat("\".")) - eLSe - if a.length() = b.length() THeN - new IO.out_string("La cadena \"".concat(a).concat("\" mide igual que la cadena \"").concat(b).concat("\".")) - ElsE - new IO.out_string("La cadena \"".concat(a).concat("\" es mas larga que la cadena \"").concat(b).concat("\".")) - fI - Fi - }; - - testing6(a: Int): IO { - let count: Int, pow: int <- 1 -- Type identifiers starts with a uppercase letter - in { - count <- 0; - -- pow <- 1; - while pow < a - loop - { - count <- count + 1; - pow <- pow * 2; - } - pool; - new IO.out_string("El logaritmo en base 2 de ").out_int(a).out_string(" es ").out_int(count); - } - }; -}; - -class Test2 { - test1: Test <- new Test; - - testing1(): Test { - test1.testing4(1 + 1, 1 + 2).testing4(2 + 3, 3 + 5).testing4(5 + 8, 8 + 13) - }; - - testing2(x: Int, y: Int): Test2 { - self - }; - - testing3(): Test2 { - testing2(1 + 1, 1 + 2).testing2(2 + 3, 3 + 5).testing2(5 + 8, true + fALSE) - }; - - testing4(): Object { - test1@Object.copy() - }; -}; - -class Alpha inherits IO { - print() : Object { - out_string("reached!!\n") - }; +(* A let expression has the form let : [ <- ], ..., : [ <- ] in *) + +class Main { + main(): Object { + (new Alpha).print() + }; +}; + +class Test { + test1: Object; + + testing1(): Int { + 2 + 2 + }; + + test2: Int <- 1; + + test3: String <- "1"; + + testing2(a: Alpha, b: Int): Int { + 2 + 2 + }; + + testing3(): String { + "2 + 2" + }; + + testing4(x: Int, y: Int): Test { + self + }; + + testing5(a: String, b: String): IO { + If a.length() < b.length() THeN + new IO.out_string("La cadena \"".concat(b).concat("\" es mas larga que la cadena \"").concat(a).concat("\".")) + eLSe + if a.length() = b.length() THeN + new IO.out_string("La cadena \"".concat(a).concat("\" mide igual que la cadena \"").concat(b).concat("\".")) + ElsE + new IO.out_string("La cadena \"".concat(a).concat("\" es mas larga que la cadena \"").concat(b).concat("\".")) + fI + Fi + }; + + testing6(a: Int): IO { + let count: Int, pow: int <- 1 -- Type identifiers starts with a uppercase letter + in { + count <- 0; + -- pow <- 1; + while pow < a + loop + { + count <- count + 1; + pow <- pow * 2; + } + pool; + new IO.out_string("El logaritmo en base 2 de ").out_int(a).out_string(" es ").out_int(count); + } + }; +}; + +class Test2 { + test1: Test <- new Test; + + testing1(): Test { + test1.testing4(1 + 1, 1 + 2).testing4(2 + 3, 3 + 5).testing4(5 + 8, 8 + 13) + }; + + testing2(x: Int, y: Int): Test2 { + self + }; + + testing3(): Test2 { + testing2(1 + 1, 1 + 2).testing2(2 + 3, 3 + 5).testing2(5 + 8, true + fALSE) + }; + + testing4(): Object { + test1@Object.copy() + }; +}; + +class Alpha inherits IO { + print() : Object { + out_string("reached!!\n") + }; }; \ No newline at end of file diff --git a/tests/parser/let3.cl b/tests/parser/let3.cl index 91e567fd8..b2b2fb005 100644 --- a/tests/parser/let3.cl +++ b/tests/parser/let3.cl @@ -1,85 +1,85 @@ -(* A let expression has the form let : [ <- ], ..., : [ <- ] in *) - -class Main { - main(): Object { - (new Alpha).print() - }; -}; - -class Test { - test1: Object; - - testing1(): Int { - 2 + 2 - }; - - test2: Int <- 1; - - test3: String <- "1"; - - testing2(a: Alpha, b: Int): Int { - 2 + 2 - }; - - testing3(): String { - "2 + 2" - }; - - testing4(x: Int, y: Int): Test { - self - }; - - testing5(a: String, b: String): IO { - If a.length() < b.length() THeN - new IO.out_string("La cadena \"".concat(b).concat("\" es mas larga que la cadena \"").concat(a).concat("\".")) - eLSe - if a.length() = b.length() THeN - new IO.out_string("La cadena \"".concat(a).concat("\" mide igual que la cadena \"").concat(b).concat("\".")) - ElsE - new IO.out_string("La cadena \"".concat(a).concat("\" es mas larga que la cadena \"").concat(b).concat("\".")) - fI - Fi - }; - - testing6(a: Int): IO { - let count: Int, pow: Int, -- Extra comma - in { - count <- 0; - pow <- 1; - while pow < a - loop - { - count <- count + 1; - pow <- pow * 2; - } - pool; - new IO.out_string("El logaritmo en base 2 de ").out_int(a).out_string(" es ").out_int(count); - } - }; -}; - -class Test2 { - test1: Test <- new Test; - - testing1(): Test { - test1.testing4(1 + 1, 1 + 2).testing4(2 + 3, 3 + 5).testing4(5 + 8, 8 + 13) - }; - - testing2(x: Int, y: Int): Test2 { - self - }; - - testing3(): Test2 { - testing2(1 + 1, 1 + 2).testing2(2 + 3, 3 + 5).testing2(5 + 8, true + fALSE) - }; - - testing4(): Object { - test1@Object.copy() - }; -}; - -class Alpha inherits IO { - print() : Object { - out_string("reached!!\n") - }; +(* A let expression has the form let : [ <- ], ..., : [ <- ] in *) + +class Main { + main(): Object { + (new Alpha).print() + }; +}; + +class Test { + test1: Object; + + testing1(): Int { + 2 + 2 + }; + + test2: Int <- 1; + + test3: String <- "1"; + + testing2(a: Alpha, b: Int): Int { + 2 + 2 + }; + + testing3(): String { + "2 + 2" + }; + + testing4(x: Int, y: Int): Test { + self + }; + + testing5(a: String, b: String): IO { + If a.length() < b.length() THeN + new IO.out_string("La cadena \"".concat(b).concat("\" es mas larga que la cadena \"").concat(a).concat("\".")) + eLSe + if a.length() = b.length() THeN + new IO.out_string("La cadena \"".concat(a).concat("\" mide igual que la cadena \"").concat(b).concat("\".")) + ElsE + new IO.out_string("La cadena \"".concat(a).concat("\" es mas larga que la cadena \"").concat(b).concat("\".")) + fI + Fi + }; + + testing6(a: Int): IO { + let count: Int, pow: Int, -- Extra comma + in { + count <- 0; + pow <- 1; + while pow < a + loop + { + count <- count + 1; + pow <- pow * 2; + } + pool; + new IO.out_string("El logaritmo en base 2 de ").out_int(a).out_string(" es ").out_int(count); + } + }; +}; + +class Test2 { + test1: Test <- new Test; + + testing1(): Test { + test1.testing4(1 + 1, 1 + 2).testing4(2 + 3, 3 + 5).testing4(5 + 8, 8 + 13) + }; + + testing2(x: Int, y: Int): Test2 { + self + }; + + testing3(): Test2 { + testing2(1 + 1, 1 + 2).testing2(2 + 3, 3 + 5).testing2(5 + 8, true + fALSE) + }; + + testing4(): Object { + test1@Object.copy() + }; +}; + +class Alpha inherits IO { + print() : Object { + out_string("reached!!\n") + }; }; \ No newline at end of file diff --git a/tests/parser/let4.cl b/tests/parser/let4.cl index a716c332d..c7d0ea8e3 100644 --- a/tests/parser/let4.cl +++ b/tests/parser/let4.cl @@ -1,85 +1,85 @@ -(* A let expression has the form let : [ <- ], ..., : [ <- ] in *) - -class Main { - main(): Object { - (new Alpha).print() - }; -}; - -class Test { - test1: Object; - - testing1(): Int { - 2 + 2 - }; - - test2: Int <- 1; - - test3: String <- "1"; - - testing2(a: Alpha, b: Int): Int { - 2 + 2 - }; - - testing3(): String { - "2 + 2" - }; - - testing4(x: Int, y: Int): Test { - self - }; - - testing5(a: String, b: String): IO { - If a.length() < b.length() THeN - new IO.out_string("La cadena \"".concat(b).concat("\" es mas larga que la cadena \"").concat(a).concat("\".")) - eLSe - if a.length() = b.length() THeN - new IO.out_string("La cadena \"".concat(a).concat("\" mide igual que la cadena \"").concat(b).concat("\".")) - ElsE - new IO.out_string("La cadena \"".concat(a).concat("\" es mas larga que la cadena \"").concat(b).concat("\".")) - fI - Fi - }; - - testing6(a: Int): IO { - let count: Int <- true++, pow: Int <- 1 -- Initialization must be an expression - in { - count <- 0; - pow <- 1; - while pow < a - loop - { - count <- count + 1; - pow <- pow * 2; - } - pool; - new IO.out_string("El logaritmo en base 2 de ").out_int(a).out_string(" es ").out_int(count); - } - }; -}; - -class Test2 { - test1: Test <- new Test; - - testing1(): Test { - test1.testing4(1 + 1, 1 + 2).testing4(2 + 3, 3 + 5).testing4(5 + 8, 8 + 13) - }; - - testing2(x: Int, y: Int): Test2 { - self - }; - - testing3(): Test2 { - testing2(1 + 1, 1 + 2).testing2(2 + 3, 3 + 5).testing2(5 + 8, true + fALSE) - }; - - testing4(): Object { - test1@Object.copy() - }; -}; - -class Alpha inherits IO { - print() : Object { - out_string("reached!!\n") - }; +(* A let expression has the form let : [ <- ], ..., : [ <- ] in *) + +class Main { + main(): Object { + (new Alpha).print() + }; +}; + +class Test { + test1: Object; + + testing1(): Int { + 2 + 2 + }; + + test2: Int <- 1; + + test3: String <- "1"; + + testing2(a: Alpha, b: Int): Int { + 2 + 2 + }; + + testing3(): String { + "2 + 2" + }; + + testing4(x: Int, y: Int): Test { + self + }; + + testing5(a: String, b: String): IO { + If a.length() < b.length() THeN + new IO.out_string("La cadena \"".concat(b).concat("\" es mas larga que la cadena \"").concat(a).concat("\".")) + eLSe + if a.length() = b.length() THeN + new IO.out_string("La cadena \"".concat(a).concat("\" mide igual que la cadena \"").concat(b).concat("\".")) + ElsE + new IO.out_string("La cadena \"".concat(a).concat("\" es mas larga que la cadena \"").concat(b).concat("\".")) + fI + Fi + }; + + testing6(a: Int): IO { + let count: Int <- true++, pow: Int <- 1 -- Initialization must be an expression + in { + count <- 0; + pow <- 1; + while pow < a + loop + { + count <- count + 1; + pow <- pow * 2; + } + pool; + new IO.out_string("El logaritmo en base 2 de ").out_int(a).out_string(" es ").out_int(count); + } + }; +}; + +class Test2 { + test1: Test <- new Test; + + testing1(): Test { + test1.testing4(1 + 1, 1 + 2).testing4(2 + 3, 3 + 5).testing4(5 + 8, 8 + 13) + }; + + testing2(x: Int, y: Int): Test2 { + self + }; + + testing3(): Test2 { + testing2(1 + 1, 1 + 2).testing2(2 + 3, 3 + 5).testing2(5 + 8, true + fALSE) + }; + + testing4(): Object { + test1@Object.copy() + }; +}; + +class Alpha inherits IO { + print() : Object { + out_string("reached!!\n") + }; }; \ No newline at end of file diff --git a/tests/parser/let5.cl b/tests/parser/let5.cl index d974cc138..cc4ed7a99 100644 --- a/tests/parser/let5.cl +++ b/tests/parser/let5.cl @@ -1,85 +1,85 @@ -(* A let expression has the form let : [ <- ], ..., : [ <- ] in *) - -class Main { - main(): Object { - (new Alpha).print() - }; -}; - -class Test { - test1: Object; - - testing1(): Int { - 2 + 2 - }; - - test2: Int <- 1; - - test3: String <- "1"; - - testing2(a: Alpha, b: Int): Int { - 2 + 2 - }; - - testing3(): String { - "2 + 2" - }; - - testing4(x: Int, y: Int): Test { - self - }; - - testing5(a: String, b: String): IO { - If a.length() < b.length() THeN - new IO.out_string("La cadena \"".concat(b).concat("\" es mas larga que la cadena \"").concat(a).concat("\".")) - eLSe - if a.length() = b.length() THeN - new IO.out_string("La cadena \"".concat(a).concat("\" mide igual que la cadena \"").concat(b).concat("\".")) - ElsE - new IO.out_string("La cadena \"".concat(a).concat("\" es mas larga que la cadena \"").concat(b).concat("\".")) - fI - Fi - }; - - testing6(a: Int): IO { - let count: Int = 0, pow: Int -- Must be '<-' not '=' - in { - -- count <- 0; - pow <- 1; - while pow < a - loop - { - count <- count + 1; - pow <- pow * 2; - } - pool; - new IO.out_string("El logaritmo en base 2 de ").out_int(a).out_string(" es ").out_int(count); - } - }; -}; - -class Test2 { - test1: Test <- new Test; - - testing1(): Test { - test1.testing4(1 + 1, 1 + 2).testing4(2 + 3, 3 + 5).testing4(5 + 8, 8 + 13) - }; - - testing2(x: Int, y: Int): Test2 { - self - }; - - testing3(): Test2 { - testing2(1 + 1, 1 + 2).testing2(2 + 3, 3 + 5).testing2(5 + 8, true + fALSE) - }; - - testing4(): Object { - test1@Object.copy() - }; -}; - -class Alpha inherits IO { - print() : Object { - out_string("reached!!\n") - }; +(* A let expression has the form let : [ <- ], ..., : [ <- ] in *) + +class Main { + main(): Object { + (new Alpha).print() + }; +}; + +class Test { + test1: Object; + + testing1(): Int { + 2 + 2 + }; + + test2: Int <- 1; + + test3: String <- "1"; + + testing2(a: Alpha, b: Int): Int { + 2 + 2 + }; + + testing3(): String { + "2 + 2" + }; + + testing4(x: Int, y: Int): Test { + self + }; + + testing5(a: String, b: String): IO { + If a.length() < b.length() THeN + new IO.out_string("La cadena \"".concat(b).concat("\" es mas larga que la cadena \"").concat(a).concat("\".")) + eLSe + if a.length() = b.length() THeN + new IO.out_string("La cadena \"".concat(a).concat("\" mide igual que la cadena \"").concat(b).concat("\".")) + ElsE + new IO.out_string("La cadena \"".concat(a).concat("\" es mas larga que la cadena \"").concat(b).concat("\".")) + fI + Fi + }; + + testing6(a: Int): IO { + let count: Int = 0, pow: Int -- Must be '<-' not '=' + in { + -- count <- 0; + pow <- 1; + while pow < a + loop + { + count <- count + 1; + pow <- pow * 2; + } + pool; + new IO.out_string("El logaritmo en base 2 de ").out_int(a).out_string(" es ").out_int(count); + } + }; +}; + +class Test2 { + test1: Test <- new Test; + + testing1(): Test { + test1.testing4(1 + 1, 1 + 2).testing4(2 + 3, 3 + 5).testing4(5 + 8, 8 + 13) + }; + + testing2(x: Int, y: Int): Test2 { + self + }; + + testing3(): Test2 { + testing2(1 + 1, 1 + 2).testing2(2 + 3, 3 + 5).testing2(5 + 8, true + fALSE) + }; + + testing4(): Object { + test1@Object.copy() + }; +}; + +class Alpha inherits IO { + print() : Object { + out_string("reached!!\n") + }; }; \ No newline at end of file diff --git a/tests/parser/let6.cl b/tests/parser/let6.cl index b6e51d7e1..8bc8c4883 100644 --- a/tests/parser/let6.cl +++ b/tests/parser/let6.cl @@ -1,74 +1,74 @@ -(* A let expression has the form let : [ <- ], ..., : [ <- ] in *) - -class Main { - main(): Object { - (new Alpha).print() - }; -}; - -class Test { - test1: Object; - - testing1(): Int { - 2 + 2 - }; - - test2: Int <- 1; - - test3: String <- "1"; - - testing2(a: Alpha, b: Int): Int { - 2 + 2 - }; - - testing3(): String { - "2 + 2" - }; - - testing4(x: Int, y: Int): Test { - self - }; - - testing5(a: String, b: String): IO { - If a.length() < b.length() THeN - new IO.out_string("La cadena \"".concat(b).concat("\" es mas larga que la cadena \"").concat(a).concat("\".")) - eLSe - if a.length() = b.length() THeN - new IO.out_string("La cadena \"".concat(a).concat("\" mide igual que la cadena \"").concat(b).concat("\".")) - ElsE - new IO.out_string("La cadena \"".concat(a).concat("\" es mas larga que la cadena \"").concat(b).concat("\".")) - fI - Fi - }; - - testing6(a: Int): IO { - let count: Int <- 0, pow: Int <- 1 - in false++ -- Let body must be an expression - }; -}; - -class Test2 { - test1: Test <- new Test; - - testing1(): Test { - test1.testing4(1 + 1, 1 + 2).testing4(2 + 3, 3 + 5).testing4(5 + 8, 8 + 13) - }; - - testing2(x: Int, y: Int): Test2 { - self - }; - - testing3(): Test2 { - testing2(1 + 1, 1 + 2).testing2(2 + 3, 3 + 5).testing2(5 + 8, true + fALSE) - }; - - testing4(): Object { - test1@Object.copy() - }; -}; - -class Alpha inherits IO { - print() : Object { - out_string("reached!!\n") - }; +(* A let expression has the form let : [ <- ], ..., : [ <- ] in *) + +class Main { + main(): Object { + (new Alpha).print() + }; +}; + +class Test { + test1: Object; + + testing1(): Int { + 2 + 2 + }; + + test2: Int <- 1; + + test3: String <- "1"; + + testing2(a: Alpha, b: Int): Int { + 2 + 2 + }; + + testing3(): String { + "2 + 2" + }; + + testing4(x: Int, y: Int): Test { + self + }; + + testing5(a: String, b: String): IO { + If a.length() < b.length() THeN + new IO.out_string("La cadena \"".concat(b).concat("\" es mas larga que la cadena \"").concat(a).concat("\".")) + eLSe + if a.length() = b.length() THeN + new IO.out_string("La cadena \"".concat(a).concat("\" mide igual que la cadena \"").concat(b).concat("\".")) + ElsE + new IO.out_string("La cadena \"".concat(a).concat("\" es mas larga que la cadena \"").concat(b).concat("\".")) + fI + Fi + }; + + testing6(a: Int): IO { + let count: Int <- 0, pow: Int <- 1 + in false++ -- Let body must be an expression + }; +}; + +class Test2 { + test1: Test <- new Test; + + testing1(): Test { + test1.testing4(1 + 1, 1 + 2).testing4(2 + 3, 3 + 5).testing4(5 + 8, 8 + 13) + }; + + testing2(x: Int, y: Int): Test2 { + self + }; + + testing3(): Test2 { + testing2(1 + 1, 1 + 2).testing2(2 + 3, 3 + 5).testing2(5 + 8, true + fALSE) + }; + + testing4(): Object { + test1@Object.copy() + }; +}; + +class Alpha inherits IO { + print() : Object { + out_string("reached!!\n") + }; }; \ No newline at end of file diff --git a/tests/parser/let7.cl b/tests/parser/let7.cl index 6fd63e6a7..816c59845 100644 --- a/tests/parser/let7.cl +++ b/tests/parser/let7.cl @@ -1,85 +1,85 @@ -(* A let expression has the form let : [ <- ], ..., : [ <- ] in *) - -class Main { - main(): Object { - (new Alpha).print() - }; -}; - -class Test { - test1: Object; - - testing1(): Int { - 2 + 2 - }; - - test2: Int <- 1; - - test3: String <- "1"; - - testing2(a: Alpha, b: Int): Int { - 2 + 2 - }; - - testing3(): String { - "2 + 2" - }; - - testing4(x: Int, y: Int): Test { - self - }; - - testing5(a: String, b: String): IO { - If a.length() < b.length() THeN - new IO.out_string("La cadena \"".concat(b).concat("\" es mas larga que la cadena \"").concat(a).concat("\".")) - eLSe - if a.length() = b.length() THeN - new IO.out_string("La cadena \"".concat(a).concat("\" mide igual que la cadena \"").concat(b).concat("\".")) - ElsE - new IO.out_string("La cadena \"".concat(a).concat("\" es mas larga que la cadena \"").concat(b).concat("\".")) - fI - Fi - }; - - testing6(a: Int): IO { - let count: Int <- 0, pow: Int - (* Missing "in" *) { - -- count <- 0; - pow <- 1; - while pow < a - loop - { - count <- count + 1; - pow <- pow * 2; - } - pool; - new IO.out_string("El logaritmo en base 2 de ").out_int(a).out_string(" es ").out_int(count); - } - }; -}; - -class Test2 { - test1: Test <- new Test; - - testing1(): Test { - test1.testing4(1 + 1, 1 + 2).testing4(2 + 3, 3 + 5).testing4(5 + 8, 8 + 13) - }; - - testing2(x: Int, y: Int): Test2 { - self - }; - - testing3(): Test2 { - testing2(1 + 1, 1 + 2).testing2(2 + 3, 3 + 5).testing2(5 + 8, true + fALSE) - }; - - testing4(): Object { - test1@Object.copy() - }; -}; - -class Alpha inherits IO { - print() : Object { - out_string("reached!!\n") - }; +(* A let expression has the form let : [ <- ], ..., : [ <- ] in *) + +class Main { + main(): Object { + (new Alpha).print() + }; +}; + +class Test { + test1: Object; + + testing1(): Int { + 2 + 2 + }; + + test2: Int <- 1; + + test3: String <- "1"; + + testing2(a: Alpha, b: Int): Int { + 2 + 2 + }; + + testing3(): String { + "2 + 2" + }; + + testing4(x: Int, y: Int): Test { + self + }; + + testing5(a: String, b: String): IO { + If a.length() < b.length() THeN + new IO.out_string("La cadena \"".concat(b).concat("\" es mas larga que la cadena \"").concat(a).concat("\".")) + eLSe + if a.length() = b.length() THeN + new IO.out_string("La cadena \"".concat(a).concat("\" mide igual que la cadena \"").concat(b).concat("\".")) + ElsE + new IO.out_string("La cadena \"".concat(a).concat("\" es mas larga que la cadena \"").concat(b).concat("\".")) + fI + Fi + }; + + testing6(a: Int): IO { + let count: Int <- 0, pow: Int + (* Missing "in" *) { + -- count <- 0; + pow <- 1; + while pow < a + loop + { + count <- count + 1; + pow <- pow * 2; + } + pool; + new IO.out_string("El logaritmo en base 2 de ").out_int(a).out_string(" es ").out_int(count); + } + }; +}; + +class Test2 { + test1: Test <- new Test; + + testing1(): Test { + test1.testing4(1 + 1, 1 + 2).testing4(2 + 3, 3 + 5).testing4(5 + 8, 8 + 13) + }; + + testing2(x: Int, y: Int): Test2 { + self + }; + + testing3(): Test2 { + testing2(1 + 1, 1 + 2).testing2(2 + 3, 3 + 5).testing2(5 + 8, true + fALSE) + }; + + testing4(): Object { + test1@Object.copy() + }; +}; + +class Alpha inherits IO { + print() : Object { + out_string("reached!!\n") + }; }; \ No newline at end of file diff --git a/tests/parser/loop1.cl b/tests/parser/loop1.cl index 7d0d7688f..2065de506 100644 --- a/tests/parser/loop1.cl +++ b/tests/parser/loop1.cl @@ -1,78 +1,78 @@ -(* A loop has the form while loop pool *) - -class Main { - main(): Object { - (new Alpha).print() - }; -}; - -class Test { - test1: Object; - - testing1(): Int { - 2 + 2 - }; - - test2: Int <- 1; - - test3: String <- "1"; - - testing2(a: Alpha, b: Int): Int { - 2 + 2 - }; - - testing3(): String { - "2 + 2" - }; - - testing4(x: Int, y: Int): Test { - self - }; - - testing5(a: String, b: String): IO { - If a.length() < b.length() THeN - new IO.out_string("La cadena \"".concat(b).concat("\" es mas larga que la cadena \"").concat(a).concat("\".")) - eLSe - if a.length() = b.length() THeN - new IO.out_string("La cadena \"".concat(a).concat("\" mide igual que la cadena \"").concat(b).concat("\".")) - ElsE - new IO.out_string("La cadena \"".concat(a).concat("\" es mas larga que la cadena \"").concat(b).concat("\".")) - fI - Fi - }; - - count: Int <- 1; - - testing6(): Object { - while count < 1024*1024 - -- Missing "loop" - count <- count * 2 - pool - }; -}; - -class Test2 { - test1: Test <- new Test; - - testing1(): Test { - test1.testing4(1 + 1, 1 + 2).testing4(2 + 3, 3 + 5).testing4(5 + 8, 8 + 13) - }; - - testing2(x: Int, y: Int): Test2 { - self - }; - - testing3(): Test2 { - testing2(1 + 1, 1 + 2).testing2(2 + 3, 3 + 5).testing2(5 + 8, true + fALSE) - }; - - testing4(): Object { - test1@Object.copy() - }; -}; - -class Alpha inherits IO { - print() : Object { - out_string("reached!!\n") - }; +(* A loop has the form while loop pool *) + +class Main { + main(): Object { + (new Alpha).print() + }; +}; + +class Test { + test1: Object; + + testing1(): Int { + 2 + 2 + }; + + test2: Int <- 1; + + test3: String <- "1"; + + testing2(a: Alpha, b: Int): Int { + 2 + 2 + }; + + testing3(): String { + "2 + 2" + }; + + testing4(x: Int, y: Int): Test { + self + }; + + testing5(a: String, b: String): IO { + If a.length() < b.length() THeN + new IO.out_string("La cadena \"".concat(b).concat("\" es mas larga que la cadena \"").concat(a).concat("\".")) + eLSe + if a.length() = b.length() THeN + new IO.out_string("La cadena \"".concat(a).concat("\" mide igual que la cadena \"").concat(b).concat("\".")) + ElsE + new IO.out_string("La cadena \"".concat(a).concat("\" es mas larga que la cadena \"").concat(b).concat("\".")) + fI + Fi + }; + + count: Int <- 1; + + testing6(): Object { + while count < 1024*1024 + -- Missing "loop" + count <- count * 2 + pool + }; +}; + +class Test2 { + test1: Test <- new Test; + + testing1(): Test { + test1.testing4(1 + 1, 1 + 2).testing4(2 + 3, 3 + 5).testing4(5 + 8, 8 + 13) + }; + + testing2(x: Int, y: Int): Test2 { + self + }; + + testing3(): Test2 { + testing2(1 + 1, 1 + 2).testing2(2 + 3, 3 + 5).testing2(5 + 8, true + fALSE) + }; + + testing4(): Object { + test1@Object.copy() + }; +}; + +class Alpha inherits IO { + print() : Object { + out_string("reached!!\n") + }; }; \ No newline at end of file diff --git a/tests/parser/loop2.cl b/tests/parser/loop2.cl index a9613c487..70f8cd910 100644 --- a/tests/parser/loop2.cl +++ b/tests/parser/loop2.cl @@ -1,78 +1,78 @@ -(* A loop has the form while loop pool *) - -class Main { - main(): Object { - (new Alpha).print() - }; -}; - -class Test { - test1: Object; - - testing1(): Int { - 2 + 2 - }; - - test2: Int <- 1; - - test3: String <- "1"; - - testing2(a: Alpha, b: Int): Int { - 2 + 2 - }; - - testing3(): String { - "2 + 2" - }; - - testing4(x: Int, y: Int): Test { - self - }; - - testing5(a: String, b: String): IO { - If a.length() < b.length() THeN - new IO.out_string("La cadena \"".concat(b).concat("\" es mas larga que la cadena \"").concat(a).concat("\".")) - eLSe - if a.length() = b.length() THeN - new IO.out_string("La cadena \"".concat(a).concat("\" mide igual que la cadena \"").concat(b).concat("\".")) - ElsE - new IO.out_string("La cadena \"".concat(a).concat("\" es mas larga que la cadena \"").concat(b).concat("\".")) - fI - Fi - }; - - count: Int <- 1; - - testing6(): Object { - while count < 1024*1024 - loop - count <- count * 2 - -- Missing "pool" - }; -}; - -class Test2 { - test1: Test <- new Test; - - testing1(): Test { - test1.testing4(1 + 1, 1 + 2).testing4(2 + 3, 3 + 5).testing4(5 + 8, 8 + 13) - }; - - testing2(x: Int, y: Int): Test2 { - self - }; - - testing3(): Test2 { - testing2(1 + 1, 1 + 2).testing2(2 + 3, 3 + 5).testing2(5 + 8, true + fALSE) - }; - - testing4(): Object { - test1@Object.copy() - }; -}; - -class Alpha inherits IO { - print() : Object { - out_string("reached!!\n") - }; +(* A loop has the form while loop pool *) + +class Main { + main(): Object { + (new Alpha).print() + }; +}; + +class Test { + test1: Object; + + testing1(): Int { + 2 + 2 + }; + + test2: Int <- 1; + + test3: String <- "1"; + + testing2(a: Alpha, b: Int): Int { + 2 + 2 + }; + + testing3(): String { + "2 + 2" + }; + + testing4(x: Int, y: Int): Test { + self + }; + + testing5(a: String, b: String): IO { + If a.length() < b.length() THeN + new IO.out_string("La cadena \"".concat(b).concat("\" es mas larga que la cadena \"").concat(a).concat("\".")) + eLSe + if a.length() = b.length() THeN + new IO.out_string("La cadena \"".concat(a).concat("\" mide igual que la cadena \"").concat(b).concat("\".")) + ElsE + new IO.out_string("La cadena \"".concat(a).concat("\" es mas larga que la cadena \"").concat(b).concat("\".")) + fI + Fi + }; + + count: Int <- 1; + + testing6(): Object { + while count < 1024*1024 + loop + count <- count * 2 + -- Missing "pool" + }; +}; + +class Test2 { + test1: Test <- new Test; + + testing1(): Test { + test1.testing4(1 + 1, 1 + 2).testing4(2 + 3, 3 + 5).testing4(5 + 8, 8 + 13) + }; + + testing2(x: Int, y: Int): Test2 { + self + }; + + testing3(): Test2 { + testing2(1 + 1, 1 + 2).testing2(2 + 3, 3 + 5).testing2(5 + 8, true + fALSE) + }; + + testing4(): Object { + test1@Object.copy() + }; +}; + +class Alpha inherits IO { + print() : Object { + out_string("reached!!\n") + }; }; \ No newline at end of file diff --git a/tests/parser/loop3.cl b/tests/parser/loop3.cl index 860adb4d1..fbb3a56b4 100644 --- a/tests/parser/loop3.cl +++ b/tests/parser/loop3.cl @@ -1,78 +1,78 @@ -(* A loop has the form while loop pool *) - -class Main { - main(): Object { - (new Alpha).print() - }; -}; - -class Test { - test1: Object; - - testing1(): Int { - 2 + 2 - }; - - test2: Int <- 1; - - test3: String <- "1"; - - testing2(a: Alpha, b: Int): Int { - 2 + 2 - }; - - testing3(): String { - "2 + 2" - }; - - testing4(x: Int, y: Int): Test { - self - }; - - testing5(a: String, b: String): IO { - If a.length() < b.length() THeN - new IO.out_string("La cadena \"".concat(b).concat("\" es mas larga que la cadena \"").concat(a).concat("\".")) - eLSe - if a.length() = b.length() THeN - new IO.out_string("La cadena \"".concat(a).concat("\" mide igual que la cadena \"").concat(b).concat("\".")) - ElsE - new IO.out_string("La cadena \"".concat(a).concat("\" es mas larga que la cadena \"").concat(b).concat("\".")) - fI - Fi - }; - - count: Int <- 1; - - testing6(): Object { - while count => 1024*1024 -- Condition must be an expression - loop - count <- count * 2 - pool - }; -}; - -class Test2 { - test1: Test <- new Test; - - testing1(): Test { - test1.testing4(1 + 1, 1 + 2).testing4(2 + 3, 3 + 5).testing4(5 + 8, 8 + 13) - }; - - testing2(x: Int, y: Int): Test2 { - self - }; - - testing3(): Test2 { - testing2(1 + 1, 1 + 2).testing2(2 + 3, 3 + 5).testing2(5 + 8, true + fALSE) - }; - - testing4(): Object { - test1@Object.copy() - }; -}; - -class Alpha inherits IO { - print() : Object { - out_string("reached!!\n") - }; +(* A loop has the form while loop pool *) + +class Main { + main(): Object { + (new Alpha).print() + }; +}; + +class Test { + test1: Object; + + testing1(): Int { + 2 + 2 + }; + + test2: Int <- 1; + + test3: String <- "1"; + + testing2(a: Alpha, b: Int): Int { + 2 + 2 + }; + + testing3(): String { + "2 + 2" + }; + + testing4(x: Int, y: Int): Test { + self + }; + + testing5(a: String, b: String): IO { + If a.length() < b.length() THeN + new IO.out_string("La cadena \"".concat(b).concat("\" es mas larga que la cadena \"").concat(a).concat("\".")) + eLSe + if a.length() = b.length() THeN + new IO.out_string("La cadena \"".concat(a).concat("\" mide igual que la cadena \"").concat(b).concat("\".")) + ElsE + new IO.out_string("La cadena \"".concat(a).concat("\" es mas larga que la cadena \"").concat(b).concat("\".")) + fI + Fi + }; + + count: Int <- 1; + + testing6(): Object { + while count => 1024*1024 -- Condition must be an expression + loop + count <- count * 2 + pool + }; +}; + +class Test2 { + test1: Test <- new Test; + + testing1(): Test { + test1.testing4(1 + 1, 1 + 2).testing4(2 + 3, 3 + 5).testing4(5 + 8, 8 + 13) + }; + + testing2(x: Int, y: Int): Test2 { + self + }; + + testing3(): Test2 { + testing2(1 + 1, 1 + 2).testing2(2 + 3, 3 + 5).testing2(5 + 8, true + fALSE) + }; + + testing4(): Object { + test1@Object.copy() + }; +}; + +class Alpha inherits IO { + print() : Object { + out_string("reached!!\n") + }; }; \ No newline at end of file diff --git a/tests/parser/loop4.cl b/tests/parser/loop4.cl index 0a1194e82..47b0e5f4a 100644 --- a/tests/parser/loop4.cl +++ b/tests/parser/loop4.cl @@ -1,78 +1,78 @@ -(* A loop has the form while loop pool *) - -class Main { - main(): Object { - (new Alpha).print() - }; -}; - -class Test { - test1: Object; - - testing1(): Int { - 2 + 2 - }; - - test2: Int <- 1; - - test3: String <- "1"; - - testing2(a: Alpha, b: Int): Int { - 2 + 2 - }; - - testing3(): String { - "2 + 2" - }; - - testing4(x: Int, y: Int): Test { - self - }; - - testing5(a: String, b: String): IO { - If a.length() < b.length() THeN - new IO.out_string("La cadena \"".concat(b).concat("\" es mas larga que la cadena \"").concat(a).concat("\".")) - eLSe - if a.length() = b.length() THeN - new IO.out_string("La cadena \"".concat(a).concat("\" mide igual que la cadena \"").concat(b).concat("\".")) - ElsE - new IO.out_string("La cadena \"".concat(a).concat("\" es mas larga que la cadena \"").concat(b).concat("\".")) - fI - Fi - }; - - count: Int <- 1; - - testing6(): Object { - while count < 1024*1024 - loop - count <- true++ -- While body must be an expression - pool - }; -}; - -class Test2 { - test1: Test <- new Test; - - testing1(): Test { - test1.testing4(1 + 1, 1 + 2).testing4(2 + 3, 3 + 5).testing4(5 + 8, 8 + 13) - }; - - testing2(x: Int, y: Int): Test2 { - self - }; - - testing3(): Test2 { - testing2(1 + 1, 1 + 2).testing2(2 + 3, 3 + 5).testing2(5 + 8, true + fALSE) - }; - - testing4(): Object { - test1@Object.copy() - }; -}; - -class Alpha inherits IO { - print() : Object { - out_string("reached!!\n") - }; +(* A loop has the form while loop pool *) + +class Main { + main(): Object { + (new Alpha).print() + }; +}; + +class Test { + test1: Object; + + testing1(): Int { + 2 + 2 + }; + + test2: Int <- 1; + + test3: String <- "1"; + + testing2(a: Alpha, b: Int): Int { + 2 + 2 + }; + + testing3(): String { + "2 + 2" + }; + + testing4(x: Int, y: Int): Test { + self + }; + + testing5(a: String, b: String): IO { + If a.length() < b.length() THeN + new IO.out_string("La cadena \"".concat(b).concat("\" es mas larga que la cadena \"").concat(a).concat("\".")) + eLSe + if a.length() = b.length() THeN + new IO.out_string("La cadena \"".concat(a).concat("\" mide igual que la cadena \"").concat(b).concat("\".")) + ElsE + new IO.out_string("La cadena \"".concat(a).concat("\" es mas larga que la cadena \"").concat(b).concat("\".")) + fI + Fi + }; + + count: Int <- 1; + + testing6(): Object { + while count < 1024*1024 + loop + count <- true++ -- While body must be an expression + pool + }; +}; + +class Test2 { + test1: Test <- new Test; + + testing1(): Test { + test1.testing4(1 + 1, 1 + 2).testing4(2 + 3, 3 + 5).testing4(5 + 8, 8 + 13) + }; + + testing2(x: Int, y: Int): Test2 { + self + }; + + testing3(): Test2 { + testing2(1 + 1, 1 + 2).testing2(2 + 3, 3 + 5).testing2(5 + 8, true + fALSE) + }; + + testing4(): Object { + test1@Object.copy() + }; +}; + +class Alpha inherits IO { + print() : Object { + out_string("reached!!\n") + }; }; \ No newline at end of file diff --git a/tests/parser/method1.cl b/tests/parser/method1.cl index fcfbbcd30..d86661430 100644 --- a/tests/parser/method1.cl +++ b/tests/parser/method1.cl @@ -1,34 +1,34 @@ -(* A method of class A is a procedure that may manipulate the variables and objects of class A *) - -class Main { - main(): Object { - (new Alpha).print() - }; -}; - -class Test { - test1: Object; - - testing1(): Int { - 2 + 2 - }; - - test2: Int <- 1; - - test3: String <- "1"; - - -- Method names must begin with lowercase letters - Testing2(a: Alpha, b: Int): Int { - 2 + 2 - }; - - testing3(): String { - "2 + 2" - }; -}; - -class Alpha inherits IO { - print() : Object { - out_string("reached!!\n") - }; +(* A method of class A is a procedure that may manipulate the variables and objects of class A *) + +class Main { + main(): Object { + (new Alpha).print() + }; +}; + +class Test { + test1: Object; + + testing1(): Int { + 2 + 2 + }; + + test2: Int <- 1; + + test3: String <- "1"; + + -- Method names must begin with lowercase letters + Testing2(a: Alpha, b: Int): Int { + 2 + 2 + }; + + testing3(): String { + "2 + 2" + }; +}; + +class Alpha inherits IO { + print() : Object { + out_string("reached!!\n") + }; }; \ No newline at end of file diff --git a/tests/parser/method2.cl b/tests/parser/method2.cl index d5bdfd85c..83648f50d 100644 --- a/tests/parser/method2.cl +++ b/tests/parser/method2.cl @@ -1,34 +1,34 @@ -(* A method of class A is a procedure that may manipulate the variables and objects of class A *) - -class Main { - main(): Object { - (new Alpha).print() - }; -}; - -class Test { - test1: Object; - - testing1(): Int { - 2 + 2 - }; - - test2: Int <- 1; - - test3: String <- "1"; - - -- Parameter names must begin with lowercase letters - testing2(a: Alpha, B: Int): Int { - 2 + 2 - }; - - testing3(): String { - "2 + 2" - }; -}; - -class Alpha inherits IO { - print() : Object { - out_string("reached!!\n") - }; +(* A method of class A is a procedure that may manipulate the variables and objects of class A *) + +class Main { + main(): Object { + (new Alpha).print() + }; +}; + +class Test { + test1: Object; + + testing1(): Int { + 2 + 2 + }; + + test2: Int <- 1; + + test3: String <- "1"; + + -- Parameter names must begin with lowercase letters + testing2(a: Alpha, B: Int): Int { + 2 + 2 + }; + + testing3(): String { + "2 + 2" + }; +}; + +class Alpha inherits IO { + print() : Object { + out_string("reached!!\n") + }; }; \ No newline at end of file diff --git a/tests/parser/method3.cl b/tests/parser/method3.cl index 1e5c9eb53..428b25fec 100644 --- a/tests/parser/method3.cl +++ b/tests/parser/method3.cl @@ -1,34 +1,34 @@ -(* A method of class A is a procedure that may manipulate the variables and objects of class A *) - -class Main { - main(): Object { - (new Alpha).print() - }; -}; - -class Test { - test1: Object; - - testing1(): Int { - 2 + 2 - }; - - test2: Int <- 1; - - test3: String <- "1"; - - -- Type names must begin with uppercase letters - testing2(a: Alpha, b: int): Int { - 2 + 2 - }; - - testing3(): String { - "2 + 2" - }; -}; - -class Alpha inherits IO { - print() : Object { - out_string("reached!!\n") - }; +(* A method of class A is a procedure that may manipulate the variables and objects of class A *) + +class Main { + main(): Object { + (new Alpha).print() + }; +}; + +class Test { + test1: Object; + + testing1(): Int { + 2 + 2 + }; + + test2: Int <- 1; + + test3: String <- "1"; + + -- Type names must begin with uppercase letters + testing2(a: Alpha, b: int): Int { + 2 + 2 + }; + + testing3(): String { + "2 + 2" + }; +}; + +class Alpha inherits IO { + print() : Object { + out_string("reached!!\n") + }; }; \ No newline at end of file diff --git a/tests/parser/method4.cl b/tests/parser/method4.cl index 019ada276..52ec07bce 100644 --- a/tests/parser/method4.cl +++ b/tests/parser/method4.cl @@ -1,34 +1,34 @@ -(* A method of class A is a procedure that may manipulate the variables and objects of class A *) - -class Main { - main(): Object { - (new Alpha).print() - }; -}; - -class Test { - test1: Object; - - testing1(): Int { - 2 + 2 - }; - - test2: Int <- 1; - - test3: String <- "1"; - - testing2(a: Alpha, b: Int): Int { - 2 + 2 - }; - - -- Missing paremeter - testing3(x: Int,): String { - "2 + 2" - }; -}; - -class Alpha inherits IO { - print() : Object { - out_string("reached!!\n") - }; +(* A method of class A is a procedure that may manipulate the variables and objects of class A *) + +class Main { + main(): Object { + (new Alpha).print() + }; +}; + +class Test { + test1: Object; + + testing1(): Int { + 2 + 2 + }; + + test2: Int <- 1; + + test3: String <- "1"; + + testing2(a: Alpha, b: Int): Int { + 2 + 2 + }; + + -- Missing paremeter + testing3(x: Int,): String { + "2 + 2" + }; +}; + +class Alpha inherits IO { + print() : Object { + out_string("reached!!\n") + }; }; \ No newline at end of file diff --git a/tests/parser/method5.cl b/tests/parser/method5.cl index 13127f664..706a3145c 100644 --- a/tests/parser/method5.cl +++ b/tests/parser/method5.cl @@ -1,34 +1,34 @@ -(* A method of class A is a procedure that may manipulate the variables and objects of class A *) - -class Main { - main(): Object { - (new Alpha).print() - }; -}; - -class Test { - test1: Object; - - testing1(): Int { - 2 + 2 - }; - - test2: Int <- 1; - - test3: String <- "1"; - - testing2(a: Alpha, b: Int): Int { - 2 + 2 - }; - - -- Type names must begin with uppercase letters - testing3(): string { - "2 + 2" - }; -}; - -class Alpha inherits IO { - print() : Object { - out_string("reached!!\n") - }; +(* A method of class A is a procedure that may manipulate the variables and objects of class A *) + +class Main { + main(): Object { + (new Alpha).print() + }; +}; + +class Test { + test1: Object; + + testing1(): Int { + 2 + 2 + }; + + test2: Int <- 1; + + test3: String <- "1"; + + testing2(a: Alpha, b: Int): Int { + 2 + 2 + }; + + -- Type names must begin with uppercase letters + testing3(): string { + "2 + 2" + }; +}; + +class Alpha inherits IO { + print() : Object { + out_string("reached!!\n") + }; }; \ No newline at end of file diff --git a/tests/parser/method6.cl b/tests/parser/method6.cl index d48cd1293..26ce7f19f 100644 --- a/tests/parser/method6.cl +++ b/tests/parser/method6.cl @@ -1,33 +1,33 @@ -(* A method of class A is a procedure that may manipulate the variables and objects of class A *) - -class Main { - main(): Object { - (new Alpha).print() - }; -}; - -class Test { - test1: Object; - - testing1(): Int { - 2 + 2 - }; - - test2: Int <- 1; - - test3: String <- "1"; - - -- Body can't be empty - testing2(a: Alpha, b: Int): Int { - }; - - testing3(): String { - "2 + 2" - }; -}; - -class Alpha inherits IO { - print() : Object { - out_string("reached!!\n") - }; +(* A method of class A is a procedure that may manipulate the variables and objects of class A *) + +class Main { + main(): Object { + (new Alpha).print() + }; +}; + +class Test { + test1: Object; + + testing1(): Int { + 2 + 2 + }; + + test2: Int <- 1; + + test3: String <- "1"; + + -- Body can't be empty + testing2(a: Alpha, b: Int): Int { + }; + + testing3(): String { + "2 + 2" + }; +}; + +class Alpha inherits IO { + print() : Object { + out_string("reached!!\n") + }; }; \ No newline at end of file diff --git a/tests/parser/mixed1.cl b/tests/parser/mixed1.cl index a27879513..b13e679da 100644 --- a/tests/parser/mixed1.cl +++ b/tests/parser/mixed1.cl @@ -1,100 +1,100 @@ -(* Cool has four binary arithmetic operations: +, -, *, /. *) - -class Main { - main(): Object { - (new Alpha).print() - }; -}; - -class Test { - test1: Object; - - testing1(): Int { - 2 + 2 - }; - - test2: Int <- 1; - - test3: String <- "1"; - - testing2(a: Alpha, b: Int): Int { - 2 + 2 - }; - - testing3(): String { - "2 + 2" - }; - - testing4(x: Int, y: Int): Test { - self - }; - - testing5(a: String, b: String): IO { - If a.length() < b.length() THeN - new IO.out_string("La cadena \"".concat(b).concat("\" es mas larga que la cadena \"").concat(a).concat("\".")) - eLSe - if a.length() = b.length() THeN - new IO.out_string("La cadena \"".concat(a).concat("\" mide igual que la cadena \"").concat(b).concat("\".")) - ElsE - new IO.out_string("La cadena \"".concat(a).concat("\" es mas larga que la cadena \"").concat(b).concat("\".")) - fI - Fi - }; - - testing6(a: Int): IO { - let count: Int <- 0, pow: Int - in { - -- count <- 0; - pow <- 1; - while pow < a - loop - { - count <- count + 1; - pow <- pow * 2; - } - pool; - new IO.out_string("El logaritmo en base 2 de ").out_int(a).out_string(" es ").out_int(count); - } - }; - - testing7(): Object { - case 2 + 2 of - x: Int => new IO.out_string("Es un entero!"); - y: String => new IO.out_string("Es una cadena!"); - z: Bool => new IO.out_string("Es un booleano!"); - esac - }; - - a: Int <- 1; - - testing8(x: Int, y: Int): Bool { - let z: Int <- 3, w: Int <- 4 - in isvoid (3 + a * (x / w + new Int) - y - (((if tRue = not faLSe then ~z else 3 <= 4 + "hey".length() fi + a))/(0)*(((4 * 4))))) - }; -}-- Mising ";" - -class Test2 { - test1: Test <- new Test; - - testing1(): Test { - test1.testing4(1 + 1, 1 + 2).testing4(2 + 3, 3 + 5).testing4(5 + 8, 8 + 13) - }; - - testing2(x: Int, y: Int): Test2 { - self - }; - - testing3(): Test2 { - testing2(1 + 1, 1 + 2).testing2(2 + 3, 3 + 5).testing2(5 + 8, true + fALSE) - }; - - testing4(): Object { - test1@Object.copy() - }; -}; - -class Alpha inherits IO { - print() : Object { - out_string("reached!!\n") - }; +(* Cool has four binary arithmetic operations: +, -, *, /. *) + +class Main { + main(): Object { + (new Alpha).print() + }; +}; + +class Test { + test1: Object; + + testing1(): Int { + 2 + 2 + }; + + test2: Int <- 1; + + test3: String <- "1"; + + testing2(a: Alpha, b: Int): Int { + 2 + 2 + }; + + testing3(): String { + "2 + 2" + }; + + testing4(x: Int, y: Int): Test { + self + }; + + testing5(a: String, b: String): IO { + If a.length() < b.length() THeN + new IO.out_string("La cadena \"".concat(b).concat("\" es mas larga que la cadena \"").concat(a).concat("\".")) + eLSe + if a.length() = b.length() THeN + new IO.out_string("La cadena \"".concat(a).concat("\" mide igual que la cadena \"").concat(b).concat("\".")) + ElsE + new IO.out_string("La cadena \"".concat(a).concat("\" es mas larga que la cadena \"").concat(b).concat("\".")) + fI + Fi + }; + + testing6(a: Int): IO { + let count: Int <- 0, pow: Int + in { + -- count <- 0; + pow <- 1; + while pow < a + loop + { + count <- count + 1; + pow <- pow * 2; + } + pool; + new IO.out_string("El logaritmo en base 2 de ").out_int(a).out_string(" es ").out_int(count); + } + }; + + testing7(): Object { + case 2 + 2 of + x: Int => new IO.out_string("Es un entero!"); + y: String => new IO.out_string("Es una cadena!"); + z: Bool => new IO.out_string("Es un booleano!"); + esac + }; + + a: Int <- 1; + + testing8(x: Int, y: Int): Bool { + let z: Int <- 3, w: Int <- 4 + in isvoid (3 + a * (x / w + new Int) - y - (((if tRue = not faLSe then ~z else 3 <= 4 + "hey".length() fi + a))/(0)*(((4 * 4))))) + }; +}-- Mising ";" + +class Test2 { + test1: Test <- new Test; + + testing1(): Test { + test1.testing4(1 + 1, 1 + 2).testing4(2 + 3, 3 + 5).testing4(5 + 8, 8 + 13) + }; + + testing2(x: Int, y: Int): Test2 { + self + }; + + testing3(): Test2 { + testing2(1 + 1, 1 + 2).testing2(2 + 3, 3 + 5).testing2(5 + 8, true + fALSE) + }; + + testing4(): Object { + test1@Object.copy() + }; +}; + +class Alpha inherits IO { + print() : Object { + out_string("reached!!\n") + }; }; \ No newline at end of file diff --git a/tests/parser/mixed2.cl b/tests/parser/mixed2.cl index f5477e0bb..6776fcc12 100644 --- a/tests/parser/mixed2.cl +++ b/tests/parser/mixed2.cl @@ -1,14 +1,14 @@ -class Main { - main(): Object { - (new Alpha).print() - }; - -}; - -(* Class names must begin with uppercase letters *) -class alpha inherits IO { - print() : Object { - out_string("reached!!\n"); - }; -}; - +class Main { + main(): Object { + (new Alpha).print() + }; + +}; + +(* Class names must begin with uppercase letters *) +class alpha inherits IO { + print() : Object { + out_string("reached!!\n"); + }; +}; + diff --git a/tests/parser/mixed3.cl b/tests/parser/mixed3.cl index 1bdcad743..f5271eb3e 100644 --- a/tests/parser/mixed3.cl +++ b/tests/parser/mixed3.cl @@ -1,40 +1,40 @@ -class Main inherits IO { - main() : Object { - { - out_string("Enter a number to check if number is prime\n"); - let i : Int <- in_int() in { - if(i <= 1) then { - out_string("Invalid Input\n"); - abort(); - } else { - if (isPrime(i) = 1) then - out_string("Number is prime\n") - else - out_string("Number is composite\n") - fi; - } - fi; - }; - } - }; - - mod(i : Int, ) : Int { -- Formal list must be comma separated. A comma does not terminate a list of formals. - i - (i/k)*k - }; - - isPrime(i : Int) : Int { - { - let x : Int <- 2, - c : Int <- 1 in - { - while (not (x = i)) loop - if (mod(i, x) = 0) then { - c <- 0; - x <- i; - } else x <- x + 1 fi - pool; - c; - }; - } - }; -}; +class Main inherits IO { + main() : Object { + { + out_string("Enter a number to check if number is prime\n"); + let i : Int <- in_int() in { + if(i <= 1) then { + out_string("Invalid Input\n"); + abort(); + } else { + if (isPrime(i) = 1) then + out_string("Number is prime\n") + else + out_string("Number is composite\n") + fi; + } + fi; + }; + } + }; + + mod(i : Int, ) : Int { -- Formal list must be comma separated. A comma does not terminate a list of formals. + i - (i/k)*k + }; + + isPrime(i : Int) : Int { + { + let x : Int <- 2, + c : Int <- 1 in + { + while (not (x = i)) loop + if (mod(i, x) = 0) then { + c <- 0; + x <- i; + } else x <- x + 1 fi + pool; + c; + }; + } + }; +}; diff --git a/tests/parser/mixed4.cl b/tests/parser/mixed4.cl index e752253be..47e6ea5e9 100644 --- a/tests/parser/mixed4.cl +++ b/tests/parser/mixed4.cl @@ -1,21 +1,21 @@ -class Main inherits IO { - main() : Object { - { - out_string("Enter number of numbers to multiply\n"); - out_int(prod(in_int())); - out_string("\n"); - } - }; - - prod(i : Int) : Int { - let y : Int <- 1 in { - while (not (i = 0) ) loop { - out_string("Enter Number: "); - y <- y * in_int(Main : Int); -- the parser correctly catches the error here - i <- i - 1; - } - pool; - y; - } - }; -}; +class Main inherits IO { + main() : Object { + { + out_string("Enter number of numbers to multiply\n"); + out_int(prod(in_int())); + out_string("\n"); + } + }; + + prod(i : Int) : Int { + let y : Int <- 1 in { + while (not (i = 0) ) loop { + out_string("Enter Number: "); + y <- y * in_int(Main : Int); -- the parser correctly catches the error here + i <- i - 1; + } + pool; + y; + } + }; +}; diff --git a/tests/parser/mixed5.cl b/tests/parser/mixed5.cl index c9176a890..d4ca68f44 100644 --- a/tests/parser/mixed5.cl +++ b/tests/parser/mixed5.cl @@ -1,20 +1,20 @@ -class Main inherits IO { - str <- "Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum."; - main() : Object { - { - out_string("Enter number of numbers to multiply\n"); - out_int(prod(in_int())); - out_string("\n"); - } - }; - prod(i : Int) : Int { - let y : Int <- 1 in { - while (not (i = 0) ) loop { - out_string("Enter Number: "); - y <- y * in_int(); - i <- i - 1; - } - y; - } - }; -} +class Main inherits IO { + str <- "Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum."; + main() : Object { + { + out_string("Enter number of numbers to multiply\n"); + out_int(prod(in_int())); + out_string("\n"); + } + }; + prod(i : Int) : Int { + let y : Int <- 1 in { + while (not (i = 0) ) loop { + out_string("Enter Number: "); + y <- y * in_int(); + i <- i - 1; + } + y; + } + }; +} diff --git a/tests/parser/mixed6.cl b/tests/parser/mixed6.cl index 5da80da31..0a51656c9 100644 --- a/tests/parser/mixed6.cl +++ b/tests/parser/mixed6.cl @@ -1,5 +1,5 @@ -classs Doom { - i : Int <- 0; - main() : Object { - if i = 0 then out_string("This is da real *h*t") - +classs Doom { + i : Int <- 0; + main() : Object { + if i = 0 then out_string("This is da real *h*t") + diff --git a/tests/parser/operation1.cl b/tests/parser/operation1.cl index d38eb72d0..d892ec8a6 100644 --- a/tests/parser/operation1.cl +++ b/tests/parser/operation1.cl @@ -1,101 +1,101 @@ -(* Cool has four binary arithmetic operations: +, -, *, /. *) - -class Main { - main(): Object { - (new Alpha).print() - }; -}; - -class Test { - test1: Object; - - testing1(): Int { - 2 + 2 - }; - - test2: Int <- 1; - - test3: String <- "1"; - - testing2(a: Alpha, b: Int): Int { - 2 + 2 - }; - - testing3(): String { - "2 + 2" - }; - - testing4(x: Int, y: Int): Test { - self - }; - - testing5(a: String, b: String): IO { - If a.length() < b.length() THeN - new IO.out_string("La cadena \"".concat(b).concat("\" es mas larga que la cadena \"").concat(a).concat("\".")) - eLSe - if a.length() = b.length() THeN - new IO.out_string("La cadena \"".concat(a).concat("\" mide igual que la cadena \"").concat(b).concat("\".")) - ElsE - new IO.out_string("La cadena \"".concat(a).concat("\" es mas larga que la cadena \"").concat(b).concat("\".")) - fI - Fi - }; - - testing6(a: Int): IO { - let count: Int <- 0, pow: Int - in { - -- count <- 0; - pow <- 1; - while pow < a - loop - { - count <- count + 1; - pow <- pow * 2; - } - pool; - new IO.out_string("El logaritmo en base 2 de ").out_int(a).out_string(" es ").out_int(count); - } - }; - - testing7(): Object { - case 2 + 2 of - x: Int => new IO.out_string("Es un entero!"); - y: String => new IO.out_string("Es una cadena!"); - z: Bool => new IO.out_string("Es un booleano!"); - esac - }; - - a: Int <- 1; - - testing8(x: Int, y: Int): Bool { - let z: Int <- 3, w: Int <- 4 - -- Missing ')' - in isvoid (3 + a * (x / w + new Int) - y - (((if tRue = not faLSe then ~z else 3 <= 4 + "hey".length() fi + a)/(0)*(((4 * 4))))) - }; -}; - -class Test2 { - test1: Test <- new Test; - - testing1(): Test { - test1.testing4(1 + 1, 1 + 2).testing4(2 + 3, 3 + 5).testing4(5 + 8, 8 + 13) - }; - - testing2(x: Int, y: Int): Test2 { - self - }; - - testing3(): Test2 { - testing2(1 + 1, 1 + 2).testing2(2 + 3, 3 + 5).testing2(5 + 8, true + fALSE) - }; - - testing4(): Object { - test1@Object.copy() - }; -}; - -class Alpha inherits IO { - print() : Object { - out_string("reached!!\n") - }; +(* Cool has four binary arithmetic operations: +, -, *, /. *) + +class Main { + main(): Object { + (new Alpha).print() + }; +}; + +class Test { + test1: Object; + + testing1(): Int { + 2 + 2 + }; + + test2: Int <- 1; + + test3: String <- "1"; + + testing2(a: Alpha, b: Int): Int { + 2 + 2 + }; + + testing3(): String { + "2 + 2" + }; + + testing4(x: Int, y: Int): Test { + self + }; + + testing5(a: String, b: String): IO { + If a.length() < b.length() THeN + new IO.out_string("La cadena \"".concat(b).concat("\" es mas larga que la cadena \"").concat(a).concat("\".")) + eLSe + if a.length() = b.length() THeN + new IO.out_string("La cadena \"".concat(a).concat("\" mide igual que la cadena \"").concat(b).concat("\".")) + ElsE + new IO.out_string("La cadena \"".concat(a).concat("\" es mas larga que la cadena \"").concat(b).concat("\".")) + fI + Fi + }; + + testing6(a: Int): IO { + let count: Int <- 0, pow: Int + in { + -- count <- 0; + pow <- 1; + while pow < a + loop + { + count <- count + 1; + pow <- pow * 2; + } + pool; + new IO.out_string("El logaritmo en base 2 de ").out_int(a).out_string(" es ").out_int(count); + } + }; + + testing7(): Object { + case 2 + 2 of + x: Int => new IO.out_string("Es un entero!"); + y: String => new IO.out_string("Es una cadena!"); + z: Bool => new IO.out_string("Es un booleano!"); + esac + }; + + a: Int <- 1; + + testing8(x: Int, y: Int): Bool { + let z: Int <- 3, w: Int <- 4 + -- Missing ')' + in isvoid (3 + a * (x / w + new Int) - y - (((if tRue = not faLSe then ~z else 3 <= 4 + "hey".length() fi + a)/(0)*(((4 * 4))))) + }; +}; + +class Test2 { + test1: Test <- new Test; + + testing1(): Test { + test1.testing4(1 + 1, 1 + 2).testing4(2 + 3, 3 + 5).testing4(5 + 8, 8 + 13) + }; + + testing2(x: Int, y: Int): Test2 { + self + }; + + testing3(): Test2 { + testing2(1 + 1, 1 + 2).testing2(2 + 3, 3 + 5).testing2(5 + 8, true + fALSE) + }; + + testing4(): Object { + test1@Object.copy() + }; +}; + +class Alpha inherits IO { + print() : Object { + out_string("reached!!\n") + }; }; \ No newline at end of file diff --git a/tests/parser/operation2.cl b/tests/parser/operation2.cl index 2dc628359..1f167409a 100644 --- a/tests/parser/operation2.cl +++ b/tests/parser/operation2.cl @@ -1,101 +1,101 @@ -(* Cool has four binary arithmetic operations: +, -, *, /. *) - -class Main { - main(): Object { - (new Alpha).print() - }; -}; - -class Test { - test1: Object; - - testing1(): Int { - 2 + 2 - }; - - test2: Int <- 1; - - test3: String <- "1"; - - testing2(a: Alpha, b: Int): Int { - 2 + 2 - }; - - testing3(): String { - "2 + 2" - }; - - testing4(x: Int, y: Int): Test { - self - }; - - testing5(a: String, b: String): IO { - If a.length() < b.length() THeN - new IO.out_string("La cadena \"".concat(b).concat("\" es mas larga que la cadena \"").concat(a).concat("\".")) - eLSe - if a.length() = b.length() THeN - new IO.out_string("La cadena \"".concat(a).concat("\" mide igual que la cadena \"").concat(b).concat("\".")) - ElsE - new IO.out_string("La cadena \"".concat(a).concat("\" es mas larga que la cadena \"").concat(b).concat("\".")) - fI - Fi - }; - - testing6(a: Int): IO { - let count: Int <- 0, pow: Int - in { - -- count <- 0; - pow <- 1; - while pow < a - loop - { - count <- count + 1; - pow <- pow * 2; - } - pool; - new IO.out_string("El logaritmo en base 2 de ").out_int(a).out_string(" es ").out_int(count); - } - }; - - testing7(): Object { - case 2 + 2 of - x: Int => new IO.out_string("Es un entero!"); - y: String => new IO.out_string("Es una cadena!"); - z: Bool => new IO.out_string("Es un booleano!"); - esac - }; - - a: Int <- 1; - - testing8(x: Int, y: Int): Bool { - let z: Int <- 3, w: Int <- 4 - -- Type identifiers starts with a uppercase letter - in isvoid (3 + a * (x / w + new int) - y - (((if tRue = not faLSe then ~z else 3 <= 4 + "hey".length() fi + a))/(0)*(((4 * 4))))) - }; -}; - -class Test2 { - test1: Test <- new Test; - - testing1(): Test { - test1.testing4(1 + 1, 1 + 2).testing4(2 + 3, 3 + 5).testing4(5 + 8, 8 + 13) - }; - - testing2(x: Int, y: Int): Test2 { - self - }; - - testing3(): Test2 { - testing2(1 + 1, 1 + 2).testing2(2 + 3, 3 + 5).testing2(5 + 8, true + fALSE) - }; - - testing4(): Object { - test1@Object.copy() - }; -}; - -class Alpha inherits IO { - print() : Object { - out_string("reached!!\n") - }; +(* Cool has four binary arithmetic operations: +, -, *, /. *) + +class Main { + main(): Object { + (new Alpha).print() + }; +}; + +class Test { + test1: Object; + + testing1(): Int { + 2 + 2 + }; + + test2: Int <- 1; + + test3: String <- "1"; + + testing2(a: Alpha, b: Int): Int { + 2 + 2 + }; + + testing3(): String { + "2 + 2" + }; + + testing4(x: Int, y: Int): Test { + self + }; + + testing5(a: String, b: String): IO { + If a.length() < b.length() THeN + new IO.out_string("La cadena \"".concat(b).concat("\" es mas larga que la cadena \"").concat(a).concat("\".")) + eLSe + if a.length() = b.length() THeN + new IO.out_string("La cadena \"".concat(a).concat("\" mide igual que la cadena \"").concat(b).concat("\".")) + ElsE + new IO.out_string("La cadena \"".concat(a).concat("\" es mas larga que la cadena \"").concat(b).concat("\".")) + fI + Fi + }; + + testing6(a: Int): IO { + let count: Int <- 0, pow: Int + in { + -- count <- 0; + pow <- 1; + while pow < a + loop + { + count <- count + 1; + pow <- pow * 2; + } + pool; + new IO.out_string("El logaritmo en base 2 de ").out_int(a).out_string(" es ").out_int(count); + } + }; + + testing7(): Object { + case 2 + 2 of + x: Int => new IO.out_string("Es un entero!"); + y: String => new IO.out_string("Es una cadena!"); + z: Bool => new IO.out_string("Es un booleano!"); + esac + }; + + a: Int <- 1; + + testing8(x: Int, y: Int): Bool { + let z: Int <- 3, w: Int <- 4 + -- Type identifiers starts with a uppercase letter + in isvoid (3 + a * (x / w + new int) - y - (((if tRue = not faLSe then ~z else 3 <= 4 + "hey".length() fi + a))/(0)*(((4 * 4))))) + }; +}; + +class Test2 { + test1: Test <- new Test; + + testing1(): Test { + test1.testing4(1 + 1, 1 + 2).testing4(2 + 3, 3 + 5).testing4(5 + 8, 8 + 13) + }; + + testing2(x: Int, y: Int): Test2 { + self + }; + + testing3(): Test2 { + testing2(1 + 1, 1 + 2).testing2(2 + 3, 3 + 5).testing2(5 + 8, true + fALSE) + }; + + testing4(): Object { + test1@Object.copy() + }; +}; + +class Alpha inherits IO { + print() : Object { + out_string("reached!!\n") + }; }; \ No newline at end of file diff --git a/tests/parser/operation3.cl b/tests/parser/operation3.cl index 61d6cbfa8..ef125927a 100644 --- a/tests/parser/operation3.cl +++ b/tests/parser/operation3.cl @@ -1,101 +1,101 @@ -(* Cool has four binary arithmetic operations: +, -, *, /. *) - -class Main { - main(): Object { - (new Alpha).print() - }; -}; - -class Test { - test1: Object; - - testing1(): Int { - 2 + 2 - }; - - test2: Int <- 1; - - test3: String <- "1"; - - testing2(a: Alpha, b: Int): Int { - 2 + 2 - }; - - testing3(): String { - "2 + 2" - }; - - testing4(x: Int, y: Int): Test { - self - }; - - testing5(a: String, b: String): IO { - If a.length() < b.length() THeN - new IO.out_string("La cadena \"".concat(b).concat("\" es mas larga que la cadena \"").concat(a).concat("\".")) - eLSe - if a.length() = b.length() THeN - new IO.out_string("La cadena \"".concat(a).concat("\" mide igual que la cadena \"").concat(b).concat("\".")) - ElsE - new IO.out_string("La cadena \"".concat(a).concat("\" es mas larga que la cadena \"").concat(b).concat("\".")) - fI - Fi - }; - - testing6(a: Int): IO { - let count: Int <- 0, pow: Int - in { - -- count <- 0; - pow <- 1; - while pow < a - loop - { - count <- count + 1; - pow <- pow * 2; - } - pool; - new IO.out_string("El logaritmo en base 2 de ").out_int(a).out_string(" es ").out_int(count); - } - }; - - testing7(): Object { - case 2 + 2 of - x: Int => new IO.out_string("Es un entero!"); - y: String => new IO.out_string("Es una cadena!"); - z: Bool => new IO.out_string("Es un booleano!"); - esac - }; - - a: Int <- 1; - - testing8(x: Int, y: Int): Bool { - let z: Int <- 3, w: Int <- 4 - -- Object identifiers starts with a lowercase letter - in isvoid (3 + a * (x / w + new Int) - y - (((if tRue = not faLSe then ~Mazinger_Z else 3 <= 4 + "hey".length() fi + a))/(0)*(((4 * 4))))) - }; -}; - -class Test2 { - test1: Test <- new Test; - - testing1(): Test { - test1.testing4(1 + 1, 1 + 2).testing4(2 + 3, 3 + 5).testing4(5 + 8, 8 + 13) - }; - - testing2(x: Int, y: Int): Test2 { - self - }; - - testing3(): Test2 { - testing2(1 + 1, 1 + 2).testing2(2 + 3, 3 + 5).testing2(5 + 8, true + fALSE) - }; - - testing4(): Object { - test1@Object.copy() - }; -}; - -class Alpha inherits IO { - print() : Object { - out_string("reached!!\n") - }; +(* Cool has four binary arithmetic operations: +, -, *, /. *) + +class Main { + main(): Object { + (new Alpha).print() + }; +}; + +class Test { + test1: Object; + + testing1(): Int { + 2 + 2 + }; + + test2: Int <- 1; + + test3: String <- "1"; + + testing2(a: Alpha, b: Int): Int { + 2 + 2 + }; + + testing3(): String { + "2 + 2" + }; + + testing4(x: Int, y: Int): Test { + self + }; + + testing5(a: String, b: String): IO { + If a.length() < b.length() THeN + new IO.out_string("La cadena \"".concat(b).concat("\" es mas larga que la cadena \"").concat(a).concat("\".")) + eLSe + if a.length() = b.length() THeN + new IO.out_string("La cadena \"".concat(a).concat("\" mide igual que la cadena \"").concat(b).concat("\".")) + ElsE + new IO.out_string("La cadena \"".concat(a).concat("\" es mas larga que la cadena \"").concat(b).concat("\".")) + fI + Fi + }; + + testing6(a: Int): IO { + let count: Int <- 0, pow: Int + in { + -- count <- 0; + pow <- 1; + while pow < a + loop + { + count <- count + 1; + pow <- pow * 2; + } + pool; + new IO.out_string("El logaritmo en base 2 de ").out_int(a).out_string(" es ").out_int(count); + } + }; + + testing7(): Object { + case 2 + 2 of + x: Int => new IO.out_string("Es un entero!"); + y: String => new IO.out_string("Es una cadena!"); + z: Bool => new IO.out_string("Es un booleano!"); + esac + }; + + a: Int <- 1; + + testing8(x: Int, y: Int): Bool { + let z: Int <- 3, w: Int <- 4 + -- Object identifiers starts with a lowercase letter + in isvoid (3 + a * (x / w + new Int) - y - (((if tRue = not faLSe then ~Mazinger_Z else 3 <= 4 + "hey".length() fi + a))/(0)*(((4 * 4))))) + }; +}; + +class Test2 { + test1: Test <- new Test; + + testing1(): Test { + test1.testing4(1 + 1, 1 + 2).testing4(2 + 3, 3 + 5).testing4(5 + 8, 8 + 13) + }; + + testing2(x: Int, y: Int): Test2 { + self + }; + + testing3(): Test2 { + testing2(1 + 1, 1 + 2).testing2(2 + 3, 3 + 5).testing2(5 + 8, true + fALSE) + }; + + testing4(): Object { + test1@Object.copy() + }; +}; + +class Alpha inherits IO { + print() : Object { + out_string("reached!!\n") + }; }; \ No newline at end of file diff --git a/tests/parser/operation4.cl b/tests/parser/operation4.cl index bae7de5b5..2212740e7 100644 --- a/tests/parser/operation4.cl +++ b/tests/parser/operation4.cl @@ -1,101 +1,101 @@ -(* Cool has four binary arithmetic operations: +, -, *, /. *) - -class Main { - main(): Object { - (new Alpha).print() - }; -}; - -class Test { - test1: Object; - - testing1(): Int { - 2 + 2 - }; - - test2: Int <- 1; - - test3: String <- "1"; - - testing2(a: Alpha, b: Int): Int { - 2 + 2 - }; - - testing3(): String { - "2 + 2" - }; - - testing4(x: Int, y: Int): Test { - self - }; - - testing5(a: String, b: String): IO { - If a.length() < b.length() THeN - new IO.out_string("La cadena \"".concat(b).concat("\" es mas larga que la cadena \"").concat(a).concat("\".")) - eLSe - if a.length() = b.length() THeN - new IO.out_string("La cadena \"".concat(a).concat("\" mide igual que la cadena \"").concat(b).concat("\".")) - ElsE - new IO.out_string("La cadena \"".concat(a).concat("\" es mas larga que la cadena \"").concat(b).concat("\".")) - fI - Fi - }; - - testing6(a: Int): IO { - let count: Int <- 0, pow: Int - in { - -- count <- 0; - pow <- 1; - while pow < a - loop - { - count <- count + 1; - pow <- pow * 2; - } - pool; - new IO.out_string("El logaritmo en base 2 de ").out_int(a).out_string(" es ").out_int(count); - } - }; - - testing7(): Object { - case 2 + 2 of - x: Int => new IO.out_string("Es un entero!"); - y: String => new IO.out_string("Es una cadena!"); - z: Bool => new IO.out_string("Es un booleano!"); - esac - }; - - a: Int <- 1; - - testing8(x: Int, y: Int): Bool { - let z: Int <- 3, w: Int <- 4 - -- Double "+" - in isvoid (3 + a * (x / w++ new Int) - y - (((if tRue = not faLSe then ~z else 3 <= 4 + "hey".length() fi + a))/(0)*(((4 * 4))))) - }; -}; - -class Test2 { - test1: Test <- new Test; - - testing1(): Test { - test1.testing4(1 + 1, 1 + 2).testing4(2 + 3, 3 + 5).testing4(5 + 8, 8 + 13) - }; - - testing2(x: Int, y: Int): Test2 { - self - }; - - testing3(): Test2 { - testing2(1 + 1, 1 + 2).testing2(2 + 3, 3 + 5).testing2(5 + 8, true + fALSE) - }; - - testing4(): Object { - test1@Object.copy() - }; -}; - -class Alpha inherits IO { - print() : Object { - out_string("reached!!\n") - }; +(* Cool has four binary arithmetic operations: +, -, *, /. *) + +class Main { + main(): Object { + (new Alpha).print() + }; +}; + +class Test { + test1: Object; + + testing1(): Int { + 2 + 2 + }; + + test2: Int <- 1; + + test3: String <- "1"; + + testing2(a: Alpha, b: Int): Int { + 2 + 2 + }; + + testing3(): String { + "2 + 2" + }; + + testing4(x: Int, y: Int): Test { + self + }; + + testing5(a: String, b: String): IO { + If a.length() < b.length() THeN + new IO.out_string("La cadena \"".concat(b).concat("\" es mas larga que la cadena \"").concat(a).concat("\".")) + eLSe + if a.length() = b.length() THeN + new IO.out_string("La cadena \"".concat(a).concat("\" mide igual que la cadena \"").concat(b).concat("\".")) + ElsE + new IO.out_string("La cadena \"".concat(a).concat("\" es mas larga que la cadena \"").concat(b).concat("\".")) + fI + Fi + }; + + testing6(a: Int): IO { + let count: Int <- 0, pow: Int + in { + -- count <- 0; + pow <- 1; + while pow < a + loop + { + count <- count + 1; + pow <- pow * 2; + } + pool; + new IO.out_string("El logaritmo en base 2 de ").out_int(a).out_string(" es ").out_int(count); + } + }; + + testing7(): Object { + case 2 + 2 of + x: Int => new IO.out_string("Es un entero!"); + y: String => new IO.out_string("Es una cadena!"); + z: Bool => new IO.out_string("Es un booleano!"); + esac + }; + + a: Int <- 1; + + testing8(x: Int, y: Int): Bool { + let z: Int <- 3, w: Int <- 4 + -- Double "+" + in isvoid (3 + a * (x / w++ new Int) - y - (((if tRue = not faLSe then ~z else 3 <= 4 + "hey".length() fi + a))/(0)*(((4 * 4))))) + }; +}; + +class Test2 { + test1: Test <- new Test; + + testing1(): Test { + test1.testing4(1 + 1, 1 + 2).testing4(2 + 3, 3 + 5).testing4(5 + 8, 8 + 13) + }; + + testing2(x: Int, y: Int): Test2 { + self + }; + + testing3(): Test2 { + testing2(1 + 1, 1 + 2).testing2(2 + 3, 3 + 5).testing2(5 + 8, true + fALSE) + }; + + testing4(): Object { + test1@Object.copy() + }; +}; + +class Alpha inherits IO { + print() : Object { + out_string("reached!!\n") + }; }; \ No newline at end of file diff --git a/tests/parser/program2.cl b/tests/parser/program2.cl index f8b16779c..a281c6c14 100644 --- a/tests/parser/program2.cl +++ b/tests/parser/program2.cl @@ -1,20 +1,20 @@ -(* Cool programs are sets of classes *) - -class Main { - main(): Object { - (new Alpha).print() - }; -}; - --- Missing semicolon -class Test { - testing(): Int { - 2 + 2 - }; -} - -class Alpha inherits IO { - print() : Object { - out_string("reached!!\n") - }; -}; +(* Cool programs are sets of classes *) + +class Main { + main(): Object { + (new Alpha).print() + }; +}; + +-- Missing semicolon +class Test { + testing(): Int { + 2 + 2 + }; +} + +class Alpha inherits IO { + print() : Object { + out_string("reached!!\n") + }; +}; diff --git a/tests/parser/program3.cl b/tests/parser/program3.cl index e27889c57..10d2dc71e 100644 --- a/tests/parser/program3.cl +++ b/tests/parser/program3.cl @@ -1,24 +1,24 @@ -(* Cool programs are sets of classes *) - -class Main { - main(): Object { - (new Alpha).print() - }; -}; - -class Test { - testing(): Int { - 2 + 2 - }; -}; - --- Only classes -suma(a: Int, b: Int) int { - a + b -}; - -class Alpha inherits IO { - print() : Object { - out_string("reached!!\n") - }; -}; +(* Cool programs are sets of classes *) + +class Main { + main(): Object { + (new Alpha).print() + }; +}; + +class Test { + testing(): Int { + 2 + 2 + }; +}; + +-- Only classes +suma(a: Int, b: Int) int { + a + b +}; + +class Alpha inherits IO { + print() : Object { + out_string("reached!!\n") + }; +}; diff --git a/tests/parser_test.py b/tests/parser_test.py index 129c0f20a..166de45de 100644 --- a/tests/parser_test.py +++ b/tests/parser_test.py @@ -1,13 +1,13 @@ -import pytest -import os -from utils import compare_errors - -tests_dir = __file__.rpartition('/')[0] + '/parser/' -tests = [(file) for file in os.listdir(tests_dir) if file.endswith('.cl')] - -@pytest.mark.parser -@pytest.mark.error -@pytest.mark.run(order=2) -@pytest.mark.parametrize("cool_file", tests) -def test_parser_errors(compiler_path, cool_file): +import pytest +import os +from utils import compare_errors + +tests_dir = __file__.rpartition('/')[0] + '/parser/' +tests = [(file) for file in os.listdir(tests_dir) if file.endswith('.cl')] + +@pytest.mark.parser +@pytest.mark.error +@pytest.mark.run(order=2) +@pytest.mark.parametrize("cool_file", tests) +def test_parser_errors(compiler_path, cool_file): compare_errors(compiler_path, tests_dir + cool_file, tests_dir + cool_file[:-3] + '_error.txt') \ No newline at end of file diff --git a/tests/semantic/arithmetic1.cl b/tests/semantic/arithmetic1.cl index bf94eb194..65719c064 100644 --- a/tests/semantic/arithmetic1.cl +++ b/tests/semantic/arithmetic1.cl @@ -1,11 +1,11 @@ ---The static types of the two sub-expressions must be Int. - -class A { }; -class B inherits A { }; -class C inherits B { }; - -class Main inherits IO { - main(): IO { out_string("Hello World!")}; - test: Int <- let x: Int <- 1 * 2 / 3 - 4 + new A.type_name().concat(new B.type_name().concat(new C.type_name())).length() - in x <- x + new A.type_name().concat(new B.type_name().concat(new C.type_name())); +--The static types of the two sub-expressions must be Int. + +class A { }; +class B inherits A { }; +class C inherits B { }; + +class Main inherits IO { + main(): IO { out_string("Hello World!")}; + test: Int <- let x: Int <- 1 * 2 / 3 - 4 + new A.type_name().concat(new B.type_name().concat(new C.type_name())).length() + in x <- x + new A.type_name().concat(new B.type_name().concat(new C.type_name())); }; \ No newline at end of file diff --git a/tests/semantic/arithmetic10.cl b/tests/semantic/arithmetic10.cl index bbfe6cdb3..b2488ad7f 100644 --- a/tests/semantic/arithmetic10.cl +++ b/tests/semantic/arithmetic10.cl @@ -1,15 +1,15 @@ -(* -The expression ~ is the integer -complement of . The expression must have static type Int and the entire expression -has static type Int. -*) - -class A { }; -class B inherits A { }; -class C inherits B { }; - -class Main inherits IO { - main(): IO { out_string("Hello World!")}; - test: Bool <- let x: Bool <- 1 / 2 - 3 + 4 < new A.type_name().concat(new B.type_name().concat(new C.type_name())).length() - in ~new A.type_name().concat(new B.type_name().concat(new C.type_name())).length(); +(* +The expression ~ is the integer +complement of . The expression must have static type Int and the entire expression +has static type Int. +*) + +class A { }; +class B inherits A { }; +class C inherits B { }; + +class Main inherits IO { + main(): IO { out_string("Hello World!")}; + test: Bool <- let x: Bool <- 1 / 2 - 3 + 4 < new A.type_name().concat(new B.type_name().concat(new C.type_name())).length() + in ~new A.type_name().concat(new B.type_name().concat(new C.type_name())).length(); }; \ No newline at end of file diff --git a/tests/semantic/arithmetic11.cl b/tests/semantic/arithmetic11.cl index fc067dc1a..05cec0465 100644 --- a/tests/semantic/arithmetic11.cl +++ b/tests/semantic/arithmetic11.cl @@ -1,14 +1,14 @@ -(* -The expression not is the boolean complement of . The expression - must have static type Bool and the entire expression has static type Bool. -*) - -class A { }; -class B inherits A { }; -class C inherits B { }; - -class Main inherits IO { - main(): IO { out_string("Hello World!")}; - test: Bool <- let x: Bool <- 1 / 2 - 3 + 4 < new A.type_name().concat(new B.type_name().concat(new C.type_name())).length() - in not 1 + new A.type_name().concat(new B.type_name().concat(new C.type_name())).length(); +(* +The expression not is the boolean complement of . The expression + must have static type Bool and the entire expression has static type Bool. +*) + +class A { }; +class B inherits A { }; +class C inherits B { }; + +class Main inherits IO { + main(): IO { out_string("Hello World!")}; + test: Bool <- let x: Bool <- 1 / 2 - 3 + 4 < new A.type_name().concat(new B.type_name().concat(new C.type_name())).length() + in not 1 + new A.type_name().concat(new B.type_name().concat(new C.type_name())).length(); }; \ No newline at end of file diff --git a/tests/semantic/arithmetic12.cl b/tests/semantic/arithmetic12.cl index 2e012fc41..05a2da918 100644 --- a/tests/semantic/arithmetic12.cl +++ b/tests/semantic/arithmetic12.cl @@ -1,14 +1,14 @@ -(* -The expression not is the boolean complement of . The expression - must have static type Bool and the entire expression has static type Bool. -*) - -class A { }; -class B inherits A { }; -class C inherits B { }; - -class Main inherits IO { - main(): IO { out_string("Hello World!")}; - test: Int <- let x: Bool <- 1 / 2 - 3 + 4 < new A.type_name().concat(new B.type_name().concat(new C.type_name())).length() - in not 1 < new A.type_name().concat(new B.type_name().concat(new C.type_name())).length(); +(* +The expression not is the boolean complement of . The expression + must have static type Bool and the entire expression has static type Bool. +*) + +class A { }; +class B inherits A { }; +class C inherits B { }; + +class Main inherits IO { + main(): IO { out_string("Hello World!")}; + test: Int <- let x: Bool <- 1 / 2 - 3 + 4 < new A.type_name().concat(new B.type_name().concat(new C.type_name())).length() + in not 1 < new A.type_name().concat(new B.type_name().concat(new C.type_name())).length(); }; \ No newline at end of file diff --git a/tests/semantic/arithmetic1_error.txt b/tests/semantic/arithmetic1_error.txt index a74ebf3da..59213724d 100644 --- a/tests/semantic/arithmetic1_error.txt +++ b/tests/semantic/arithmetic1_error.txt @@ -1 +1 @@ -(10, 27) - TypeError: non-Int arguments: Int + String +(10, 27) - TypeError: non-Int arguments: Int + String diff --git a/tests/semantic/arithmetic2.cl b/tests/semantic/arithmetic2.cl index 59532573d..f1f0935e2 100644 --- a/tests/semantic/arithmetic2.cl +++ b/tests/semantic/arithmetic2.cl @@ -1,11 +1,11 @@ ---The static types of the two sub-expressions must be Int. - -class A { }; -class B inherits A { }; -class C inherits B { }; - -class Main inherits IO { - main(): IO { out_string("Hello World!")}; - test: Int <- let x: Int <- 1 + 2 * 3 / 4 - new A.type_name().concat(new B.type_name().concat(new C.type_name())).length() - in x <- x - new A.type_name().concat(new B.type_name().concat(new C.type_name())); +--The static types of the two sub-expressions must be Int. + +class A { }; +class B inherits A { }; +class C inherits B { }; + +class Main inherits IO { + main(): IO { out_string("Hello World!")}; + test: Int <- let x: Int <- 1 + 2 * 3 / 4 - new A.type_name().concat(new B.type_name().concat(new C.type_name())).length() + in x <- x - new A.type_name().concat(new B.type_name().concat(new C.type_name())); }; \ No newline at end of file diff --git a/tests/semantic/arithmetic2_error.txt b/tests/semantic/arithmetic2_error.txt index 2c7952af8..aebc7aab9 100644 --- a/tests/semantic/arithmetic2_error.txt +++ b/tests/semantic/arithmetic2_error.txt @@ -1 +1 @@ -(10, 27) - TypeError: non-Int arguments: Int - String +(10, 27) - TypeError: non-Int arguments: Int - String diff --git a/tests/semantic/arithmetic3.cl b/tests/semantic/arithmetic3.cl index b208957f5..df64d8000 100644 --- a/tests/semantic/arithmetic3.cl +++ b/tests/semantic/arithmetic3.cl @@ -1,11 +1,11 @@ ---The static types of the two sub-expressions must be Int. - -class A { }; -class B inherits A { }; -class C inherits B { }; - -class Main inherits IO { - main(): IO { out_string("Hello World!")}; - test: Int <- let x: Int <- 1 - 2 + 3 * 4 / new A.type_name().concat(new B.type_name().concat(new C.type_name())).length() - in x <- x / new A.type_name().concat(new B.type_name().concat(new C.type_name())); +--The static types of the two sub-expressions must be Int. + +class A { }; +class B inherits A { }; +class C inherits B { }; + +class Main inherits IO { + main(): IO { out_string("Hello World!")}; + test: Int <- let x: Int <- 1 - 2 + 3 * 4 / new A.type_name().concat(new B.type_name().concat(new C.type_name())).length() + in x <- x / new A.type_name().concat(new B.type_name().concat(new C.type_name())); }; \ No newline at end of file diff --git a/tests/semantic/arithmetic3_error.txt b/tests/semantic/arithmetic3_error.txt index 81d88331a..d0af01bb5 100644 --- a/tests/semantic/arithmetic3_error.txt +++ b/tests/semantic/arithmetic3_error.txt @@ -1 +1 @@ -(10, 27) - TypeError: non-Int arguments: Int / String +(10, 27) - TypeError: non-Int arguments: Int / String diff --git a/tests/semantic/arithmetic4.cl b/tests/semantic/arithmetic4.cl index 2c7dd4fc9..68512ca44 100644 --- a/tests/semantic/arithmetic4.cl +++ b/tests/semantic/arithmetic4.cl @@ -1,11 +1,11 @@ ---The static types of the two sub-expressions must be Int. - -class A { }; -class B inherits A { }; -class C inherits B { }; - -class Main inherits IO { - main(): IO { out_string("Hello World!")}; - test: Int <- let x: Int <- 1 / 2 - 3 + 4 * new A.type_name().concat(new B.type_name().concat(new C.type_name())).length() - in x <- x * new A.type_name().concat(new B.type_name().concat(new C.type_name())); +--The static types of the two sub-expressions must be Int. + +class A { }; +class B inherits A { }; +class C inherits B { }; + +class Main inherits IO { + main(): IO { out_string("Hello World!")}; + test: Int <- let x: Int <- 1 / 2 - 3 + 4 * new A.type_name().concat(new B.type_name().concat(new C.type_name())).length() + in x <- x * new A.type_name().concat(new B.type_name().concat(new C.type_name())); }; \ No newline at end of file diff --git a/tests/semantic/arithmetic5.cl b/tests/semantic/arithmetic5.cl index bc08c6e82..fd77c7971 100644 --- a/tests/semantic/arithmetic5.cl +++ b/tests/semantic/arithmetic5.cl @@ -1,11 +1,11 @@ ---The static type of the expression is Int. - -class A { }; -class B inherits A { }; -class C inherits B { }; - -class Main inherits IO { - main(): IO { out_string("Hello World!")}; - test: Bool <- let x: Int <- 1 / 2 - 3 + 4 * new A.type_name().concat(new B.type_name().concat(new C.type_name())).length() - in x <- x * new A.type_name().concat(new B.type_name().concat(new C.type_name())).length(); +--The static type of the expression is Int. + +class A { }; +class B inherits A { }; +class C inherits B { }; + +class Main inherits IO { + main(): IO { out_string("Hello World!")}; + test: Bool <- let x: Int <- 1 / 2 - 3 + 4 * new A.type_name().concat(new B.type_name().concat(new C.type_name())).length() + in x <- x * new A.type_name().concat(new B.type_name().concat(new C.type_name())).length(); }; \ No newline at end of file diff --git a/tests/semantic/arithmetic5_error.txt b/tests/semantic/arithmetic5_error.txt index dd5346844..8c67c2f53 100644 --- a/tests/semantic/arithmetic5_error.txt +++ b/tests/semantic/arithmetic5_error.txt @@ -1 +1 @@ -(9, 19) - TypeError: Inferred type Int of initialization of attribute test does not conform to declared type Bool. +(9, 19) - TypeError: Inferred type Int of initialization of attribute test does not conform to declared type Bool. diff --git a/tests/semantic/arithmetic6.cl b/tests/semantic/arithmetic6.cl index a0c3d03ff..d4da66a73 100644 --- a/tests/semantic/arithmetic6.cl +++ b/tests/semantic/arithmetic6.cl @@ -1,11 +1,11 @@ - --The static types of the two sub-expressions must be Int. - -class A { }; -class B inherits A { }; -class C inherits B { }; - -class Main inherits IO { - main(): IO { out_string("Hello World!")}; - test: Bool <- let x: Bool <- 1 / 2 - 3 + 4 <= new A.type_name().concat(new B.type_name().concat(new C.type_name())).length() - in 1 <= new A.type_name().concat(new B.type_name().concat(new C.type_name())); -}; + --The static types of the two sub-expressions must be Int. + +class A { }; +class B inherits A { }; +class C inherits B { }; + +class Main inherits IO { + main(): IO { out_string("Hello World!")}; + test: Bool <- let x: Bool <- 1 / 2 - 3 + 4 <= new A.type_name().concat(new B.type_name().concat(new C.type_name())).length() + in 1 <= new A.type_name().concat(new B.type_name().concat(new C.type_name())); +}; diff --git a/tests/semantic/arithmetic6_error.txt b/tests/semantic/arithmetic6_error.txt index 2e43dfc17..a0d67cb48 100644 --- a/tests/semantic/arithmetic6_error.txt +++ b/tests/semantic/arithmetic6_error.txt @@ -1 +1 @@ -(10, 22) - TypeError: non-Int arguments: Int <= String +(10, 22) - TypeError: non-Int arguments: Int <= String diff --git a/tests/semantic/arithmetic7.cl b/tests/semantic/arithmetic7.cl index c00c75cde..b98a4b0e2 100644 --- a/tests/semantic/arithmetic7.cl +++ b/tests/semantic/arithmetic7.cl @@ -1,12 +1,12 @@ - --The static types of the two sub-expressions must be Int. - -class A { }; -class B inherits A { }; -class C inherits B { }; - -class Main inherits IO { - main(): IO { out_string("Hello World!")}; - test: Bool <- let x: Bool <- 1 / 2 - 3 + 4 < new A.type_name().concat(new B.type_name().concat(new C.type_name())).length() - in 1 < new A.type_name().concat(new B.type_name().concat(new C.type_name())); -}; - + --The static types of the two sub-expressions must be Int. + +class A { }; +class B inherits A { }; +class C inherits B { }; + +class Main inherits IO { + main(): IO { out_string("Hello World!")}; + test: Bool <- let x: Bool <- 1 / 2 - 3 + 4 < new A.type_name().concat(new B.type_name().concat(new C.type_name())).length() + in 1 < new A.type_name().concat(new B.type_name().concat(new C.type_name())); +}; + diff --git a/tests/semantic/arithmetic7_error.txt b/tests/semantic/arithmetic7_error.txt index 6f3537117..166bcc8ef 100644 --- a/tests/semantic/arithmetic7_error.txt +++ b/tests/semantic/arithmetic7_error.txt @@ -1 +1 @@ -(10, 22) - TypeError: non-Int arguments: Int < String +(10, 22) - TypeError: non-Int arguments: Int < String diff --git a/tests/semantic/arithmetic8.cl b/tests/semantic/arithmetic8.cl index 3210bdb8a..f3ad37ec4 100644 --- a/tests/semantic/arithmetic8.cl +++ b/tests/semantic/arithmetic8.cl @@ -1,13 +1,13 @@ - --The rules are exactly the same as for the binary arithmetic operations, except that the result is a Bool. - -class A { }; -class B inherits A { }; -class C inherits B { }; - -class Main inherits IO { - main(): IO { out_string("Hello World!")}; - test: Int <- let x: Bool <- 1 / 2 - 3 + 4 < new A.type_name().concat(new B.type_name().concat(new C.type_name())).length() - in 1 < new A.type_name().concat(new B.type_name().concat(new C.type_name())).length(); -}; - - + --The rules are exactly the same as for the binary arithmetic operations, except that the result is a Bool. + +class A { }; +class B inherits A { }; +class C inherits B { }; + +class Main inherits IO { + main(): IO { out_string("Hello World!")}; + test: Int <- let x: Bool <- 1 / 2 - 3 + 4 < new A.type_name().concat(new B.type_name().concat(new C.type_name())).length() + in 1 < new A.type_name().concat(new B.type_name().concat(new C.type_name())).length(); +}; + + diff --git a/tests/semantic/arithmetic8_error.txt b/tests/semantic/arithmetic8_error.txt index ebcaa3797..5a8814e1a 100644 --- a/tests/semantic/arithmetic8_error.txt +++ b/tests/semantic/arithmetic8_error.txt @@ -1 +1 @@ -(9, 18) - TypeError: Inferred type Bool of initialization of attribute test does not conform to declared type Int. +(9, 18) - TypeError: Inferred type Bool of initialization of attribute test does not conform to declared type Int. diff --git a/tests/semantic/arithmetic9.cl b/tests/semantic/arithmetic9.cl index 95579e134..b7b4d3645 100644 --- a/tests/semantic/arithmetic9.cl +++ b/tests/semantic/arithmetic9.cl @@ -1,15 +1,15 @@ -(* -The expression ~ is the integer -complement of . The expression must have static type Int and the entire expression -has static type Int. -*) - -class A { }; -class B inherits A { }; -class C inherits B { }; - -class Main inherits IO { - main(): IO { out_string("Hello World!")}; - test: Int <- let x: Bool <- 1 / 2 - 3 + 4 < new A.type_name().concat(new B.type_name().concat(new C.type_name())).length() - in 1 + ~new A.type_name().concat(new B.type_name().concat(new C.type_name())); +(* +The expression ~ is the integer +complement of . The expression must have static type Int and the entire expression +has static type Int. +*) + +class A { }; +class B inherits A { }; +class C inherits B { }; + +class Main inherits IO { + main(): IO { out_string("Hello World!")}; + test: Int <- let x: Bool <- 1 / 2 - 3 + 4 < new A.type_name().concat(new B.type_name().concat(new C.type_name())).length() + in 1 + ~new A.type_name().concat(new B.type_name().concat(new C.type_name())); }; \ No newline at end of file diff --git a/tests/semantic/assignment1.cl b/tests/semantic/assignment1.cl index 19ab70219..174f93e2b 100644 --- a/tests/semantic/assignment1.cl +++ b/tests/semantic/assignment1.cl @@ -1,7 +1,7 @@ ---The static type of the expression must conform to the declared type of the identifier - -class Main inherits IO { - main(): IO { out_string("Hello World!")}; - - test: Int <- "String"; -}; +--The static type of the expression must conform to the declared type of the identifier + +class Main inherits IO { + main(): IO { out_string("Hello World!")}; + + test: Int <- "String"; +}; diff --git a/tests/semantic/assignment1_error.txt b/tests/semantic/assignment1_error.txt index 6eb883012..9d05707aa 100644 --- a/tests/semantic/assignment1_error.txt +++ b/tests/semantic/assignment1_error.txt @@ -1 +1 @@ -(6, 18) - TypeError: Inferred type String of initialization of attribute test does not conform to declared type Int. +(6, 18) - TypeError: Inferred type String of initialization of attribute test does not conform to declared type Int. diff --git a/tests/semantic/assignment2.cl b/tests/semantic/assignment2.cl index cace221ae..c7f3d7873 100644 --- a/tests/semantic/assignment2.cl +++ b/tests/semantic/assignment2.cl @@ -1,13 +1,13 @@ ---The static type of an assignment is the static type of . - -class A { }; -class B inherits A { }; -class C inherits B { }; -class D inherits B { }; - -class Main inherits IO { - main(): IO { out_string("Hello World!")}; - - test(a: A): B { a <- new C }; - test2(a: A): D { a <- new C }; -}; +--The static type of an assignment is the static type of . + +class A { }; +class B inherits A { }; +class C inherits B { }; +class D inherits B { }; + +class Main inherits IO { + main(): IO { out_string("Hello World!")}; + + test(a: A): B { a <- new C }; + test2(a: A): D { a <- new C }; +}; diff --git a/tests/semantic/assignment2_error.txt b/tests/semantic/assignment2_error.txt index ed10b7f38..55f5aa214 100644 --- a/tests/semantic/assignment2_error.txt +++ b/tests/semantic/assignment2_error.txt @@ -1 +1 @@ -(12, 22) - TypeError: Inferred return type C of method test2 does not conform to declared return type D. +(12, 22) - TypeError: Inferred return type C of method test2 does not conform to declared return type D. diff --git a/tests/semantic/assignment3.cl b/tests/semantic/assignment3.cl index eba0d69e2..9d60a4b6c 100644 --- a/tests/semantic/assignment3.cl +++ b/tests/semantic/assignment3.cl @@ -1,14 +1,14 @@ ---The static type of an assignment is the static type of . - -class A { }; -class B inherits A { }; -class C inherits B { }; -class D inherits B { }; - -class Main inherits IO { - main(): IO { out_string("Hello World!")}; - - a: A; - b: B <- a <- new C; - d: D <- a <- new C; -}; +--The static type of an assignment is the static type of . + +class A { }; +class B inherits A { }; +class C inherits B { }; +class D inherits B { }; + +class Main inherits IO { + main(): IO { out_string("Hello World!")}; + + a: A; + b: B <- a <- new C; + d: D <- a <- new C; +}; diff --git a/tests/semantic/attributes1.cl b/tests/semantic/attributes1.cl index 3fa0440e4..d11ea7cdb 100644 --- a/tests/semantic/attributes1.cl +++ b/tests/semantic/attributes1.cl @@ -1,13 +1,13 @@ ---The static type of the expression must conform to the declared type of the attribute. - -class A { }; -class B inherits A { }; -class C inherits B { }; -class D inherits B { }; - -class Main inherits IO { - test1: IO <- new Main; - test2: B <- new A; - - main(): IO { out_string("Hello World!")}; +--The static type of the expression must conform to the declared type of the attribute. + +class A { }; +class B inherits A { }; +class C inherits B { }; +class D inherits B { }; + +class Main inherits IO { + test1: IO <- new Main; + test2: B <- new A; + + main(): IO { out_string("Hello World!")}; }; \ No newline at end of file diff --git a/tests/semantic/attributes1_error.txt b/tests/semantic/attributes1_error.txt index 9cb8460c9..22e45b837 100644 --- a/tests/semantic/attributes1_error.txt +++ b/tests/semantic/attributes1_error.txt @@ -1 +1 @@ -(10, 17) - TypeError: Inferred type A of initialization of attribute test2 does not conform to declared type B. +(10, 17) - TypeError: Inferred type A of initialization of attribute test2 does not conform to declared type B. diff --git a/tests/semantic/attributes2.cl b/tests/semantic/attributes2.cl index 7937c2cc8..85c791b5e 100644 --- a/tests/semantic/attributes2.cl +++ b/tests/semantic/attributes2.cl @@ -1,13 +1,13 @@ ---The static type of the expression must conform to the declared type of the attribute. - -class A { }; -class B inherits A { }; -class C inherits B { }; -class D inherits B { }; - -class Main inherits IO { - test1: IO <- new Main; - test2: C <- new D; - - main(): IO { out_string("Hello World!")}; +--The static type of the expression must conform to the declared type of the attribute. + +class A { }; +class B inherits A { }; +class C inherits B { }; +class D inherits B { }; + +class Main inherits IO { + test1: IO <- new Main; + test2: C <- new D; + + main(): IO { out_string("Hello World!")}; }; \ No newline at end of file diff --git a/tests/semantic/attributes2_error.txt b/tests/semantic/attributes2_error.txt index 6d601b7cd..d7694b0ad 100644 --- a/tests/semantic/attributes2_error.txt +++ b/tests/semantic/attributes2_error.txt @@ -1 +1 @@ -(10, 17) - TypeError: Inferred type D of initialization of attribute test2 does not conform to declared type C. +(10, 17) - TypeError: Inferred type D of initialization of attribute test2 does not conform to declared type C. diff --git a/tests/semantic/attributes3.cl b/tests/semantic/attributes3.cl index 8a67decd1..0c8294fa2 100644 --- a/tests/semantic/attributes3.cl +++ b/tests/semantic/attributes3.cl @@ -1,25 +1,25 @@ ---Attributes are local to the class in which they are defined or inherited. - -class A { - a: Int <- 5; - test(x1: Int, y1: Int): Int { - let x: Int <- x1, y: Int <-y1 in { - x <- x + a; - y <- y + a; - if b then x + y else x - y fi; - } - }; -}; -class B inherits A { - b: Bool <- true; -}; -class C inherits B { - c: String <- "C"; -}; -class D inherits B { - d: IO <- new Main.main(); -}; - -class Main inherits IO { - main(): IO { out_string("Hello World!") }; +--Attributes are local to the class in which they are defined or inherited. + +class A { + a: Int <- 5; + test(x1: Int, y1: Int): Int { + let x: Int <- x1, y: Int <-y1 in { + x <- x + a; + y <- y + a; + if b then x + y else x - y fi; + } + }; +}; +class B inherits A { + b: Bool <- true; +}; +class C inherits B { + c: String <- "C"; +}; +class D inherits B { + d: IO <- new Main.main(); +}; + +class Main inherits IO { + main(): IO { out_string("Hello World!") }; }; \ No newline at end of file diff --git a/tests/semantic/attributes3_error.txt b/tests/semantic/attributes3_error.txt index 6195c816c..68a2ba571 100644 --- a/tests/semantic/attributes3_error.txt +++ b/tests/semantic/attributes3_error.txt @@ -1 +1 @@ -(9, 16) - NameError: Undeclared identifier b. +(9, 16) - NameError: Undeclared identifier b. diff --git a/tests/semantic/attributes4.cl b/tests/semantic/attributes4.cl index a7f63adbd..307b185fd 100644 --- a/tests/semantic/attributes4.cl +++ b/tests/semantic/attributes4.cl @@ -1,39 +1,39 @@ ---Attributes are local to the class in which they are defined or inherited. - -class A { - a: Int <- 5; -}; -class B inherits A { - b: Bool <- true; - test(x1: Int, y1: Int): Int { - let x: Int <- x1, y: Int <-y1 in { - x <- x + a; - y <- y + a; - if b then x + y else x - y fi; - } - }; -}; -class D inherits B { - d: IO <- new Main.main(); - test3(x1: Int, y1: Int): IO { - let x: Int <- x1, y: Int <-y1, c: String <- "C" in { - x <- x + a; - y <- y + a; - if b then new IO.out_string(c) else d fi; - } - }; -}; -class C inherits B { - c: String <- "C"; - test2(x1: Int, y1: Int): IO { - let x: Int <- x1, y: Int <-y1 in { - x <- x + a; - y <- y + a; - if b then new IO.out_string(c) else d fi; - } - }; -}; - -class Main inherits IO { - main(): IO { out_string("Hello World!") }; +--Attributes are local to the class in which they are defined or inherited. + +class A { + a: Int <- 5; +}; +class B inherits A { + b: Bool <- true; + test(x1: Int, y1: Int): Int { + let x: Int <- x1, y: Int <-y1 in { + x <- x + a; + y <- y + a; + if b then x + y else x - y fi; + } + }; +}; +class D inherits B { + d: IO <- new Main.main(); + test3(x1: Int, y1: Int): IO { + let x: Int <- x1, y: Int <-y1, c: String <- "C" in { + x <- x + a; + y <- y + a; + if b then new IO.out_string(c) else d fi; + } + }; +}; +class C inherits B { + c: String <- "C"; + test2(x1: Int, y1: Int): IO { + let x: Int <- x1, y: Int <-y1 in { + x <- x + a; + y <- y + a; + if b then new IO.out_string(c) else d fi; + } + }; +}; + +class Main inherits IO { + main(): IO { out_string("Hello World!") }; }; \ No newline at end of file diff --git a/tests/semantic/basics1.cl b/tests/semantic/basics1.cl index 32ae16564..af84ca0c9 100644 --- a/tests/semantic/basics1.cl +++ b/tests/semantic/basics1.cl @@ -1,10 +1,10 @@ --- It is an error to redefine the IO class. - -class IO { - scan(): String { ":)" }; - print(s: String): IO { new IO }; -}; - -class Main inherits IO { - main(): IO { out_string("Hello World!")}; +-- It is an error to redefine the IO class. + +class IO { + scan(): String { ":)" }; + print(s: String): IO { new IO }; +}; + +class Main inherits IO { + main(): IO { out_string("Hello World!")}; }; \ No newline at end of file diff --git a/tests/semantic/basics1_error.txt b/tests/semantic/basics1_error.txt index 676f5049c..0db23a051 100644 --- a/tests/semantic/basics1_error.txt +++ b/tests/semantic/basics1_error.txt @@ -1 +1 @@ -(3, 7) - SemanticError: Redefinition of basic class IO. +(3, 7) - SemanticError: Redefinition of basic class IO. diff --git a/tests/semantic/basics2.cl b/tests/semantic/basics2.cl index cf2b1cd2f..61399a989 100644 --- a/tests/semantic/basics2.cl +++ b/tests/semantic/basics2.cl @@ -1,9 +1,9 @@ --- It is an error to inherit from or redefine Int. - -class Main inherits IO { - main(): IO { out_string("Hello World!")}; -}; - -class A inherits Int { - is_prime(): Bool { false }; -}; +-- It is an error to inherit from or redefine Int. + +class Main inherits IO { + main(): IO { out_string("Hello World!")}; +}; + +class A inherits Int { + is_prime(): Bool { false }; +}; diff --git a/tests/semantic/basics2_error.txt b/tests/semantic/basics2_error.txt index 69a3b6814..e2810833a 100644 --- a/tests/semantic/basics2_error.txt +++ b/tests/semantic/basics2_error.txt @@ -1 +1 @@ -(7, 18) - SemanticError: Class A cannot inherit class Int. +(7, 18) - SemanticError: Class A cannot inherit class Int. diff --git a/tests/semantic/basics3.cl b/tests/semantic/basics3.cl index fef017a8d..8c28b31e1 100644 --- a/tests/semantic/basics3.cl +++ b/tests/semantic/basics3.cl @@ -1,9 +1,9 @@ --- It is an error to inherit from or redefine Int. - -class Main inherits IO { - main(): IO { out_string("Hello World!")}; -}; - -class Int { - is_prime(): Bool { false }; +-- It is an error to inherit from or redefine Int. + +class Main inherits IO { + main(): IO { out_string("Hello World!")}; +}; + +class Int { + is_prime(): Bool { false }; }; \ No newline at end of file diff --git a/tests/semantic/basics3_error.txt b/tests/semantic/basics3_error.txt index d8f80cb12..ed382c8eb 100644 --- a/tests/semantic/basics3_error.txt +++ b/tests/semantic/basics3_error.txt @@ -1 +1 @@ -(7, 7) - SemanticError: Redefinition of basic class Int. +(7, 7) - SemanticError: Redefinition of basic class Int. diff --git a/tests/semantic/basics4.cl b/tests/semantic/basics4.cl index 9266ec79b..4475bc08f 100644 --- a/tests/semantic/basics4.cl +++ b/tests/semantic/basics4.cl @@ -1,9 +1,9 @@ --- It is an error to inherit from or redefine String. - -class Main inherits IO { - main(): IO { out_string("Hello World!")}; -}; - -class A inherits String { - is_palindrome(): Bool { false }; +-- It is an error to inherit from or redefine String. + +class Main inherits IO { + main(): IO { out_string("Hello World!")}; +}; + +class A inherits String { + is_palindrome(): Bool { false }; }; \ No newline at end of file diff --git a/tests/semantic/basics4_error.txt b/tests/semantic/basics4_error.txt index d5cd4c3db..bfe08a9a6 100644 --- a/tests/semantic/basics4_error.txt +++ b/tests/semantic/basics4_error.txt @@ -1 +1 @@ -(7, 18) - SemanticError: Class A cannot inherit class String. +(7, 18) - SemanticError: Class A cannot inherit class String. diff --git a/tests/semantic/basics5.cl b/tests/semantic/basics5.cl index bad5eff13..f0d4dafb3 100644 --- a/tests/semantic/basics5.cl +++ b/tests/semantic/basics5.cl @@ -1,9 +1,9 @@ --- It is an error to inherit from or redefine String. - -class Main inherits IO { - main(): IO { out_string("Hello World!")}; -}; - -class String { - is_palindrome(): Bool { false }; +-- It is an error to inherit from or redefine String. + +class Main inherits IO { + main(): IO { out_string("Hello World!")}; +}; + +class String { + is_palindrome(): Bool { false }; }; \ No newline at end of file diff --git a/tests/semantic/basics5_error.txt b/tests/semantic/basics5_error.txt index 8437accf7..47b247faa 100644 --- a/tests/semantic/basics5_error.txt +++ b/tests/semantic/basics5_error.txt @@ -1 +1 @@ -(7, 7) - SemanticError: Redefinition of basic class String. +(7, 7) - SemanticError: Redefinition of basic class String. diff --git a/tests/semantic/basics6.cl b/tests/semantic/basics6.cl index 47266ebed..c16572a31 100644 --- a/tests/semantic/basics6.cl +++ b/tests/semantic/basics6.cl @@ -1,9 +1,9 @@ --- It is an error to inherit from or redefine Bool. - -class Main inherits IO { - main(): IO { out_string("Hello World!")}; -}; - -class A inherits Bool { - xor(b: Bool): Bool { false }; +-- It is an error to inherit from or redefine Bool. + +class Main inherits IO { + main(): IO { out_string("Hello World!")}; +}; + +class A inherits Bool { + xor(b: Bool): Bool { false }; }; \ No newline at end of file diff --git a/tests/semantic/basics6_error.txt b/tests/semantic/basics6_error.txt index b4d22da13..9adf1d488 100644 --- a/tests/semantic/basics6_error.txt +++ b/tests/semantic/basics6_error.txt @@ -1 +1 @@ -(7, 18) - SemanticError: Class A cannot inherit class Bool. +(7, 18) - SemanticError: Class A cannot inherit class Bool. diff --git a/tests/semantic/basics7.cl b/tests/semantic/basics7.cl index 0f30aaec3..38f789245 100644 --- a/tests/semantic/basics7.cl +++ b/tests/semantic/basics7.cl @@ -1,9 +1,9 @@ --- It is an error to inherit from or redefine Bool. - -class Main inherits IO { - main(): IO { out_string("Hello World!")}; -}; - -class Bool { - xor(b: Bool): Bool { false }; +-- It is an error to inherit from or redefine Bool. + +class Main inherits IO { + main(): IO { out_string("Hello World!")}; +}; + +class Bool { + xor(b: Bool): Bool { false }; }; \ No newline at end of file diff --git a/tests/semantic/basics7_error.txt b/tests/semantic/basics7_error.txt index 92660ab9f..9f1347200 100644 --- a/tests/semantic/basics7_error.txt +++ b/tests/semantic/basics7_error.txt @@ -1 +1 @@ -(7, 7) - SemanticError: Redefinition of basic class Bool. +(7, 7) - SemanticError: Redefinition of basic class Bool. diff --git a/tests/semantic/basics8.cl b/tests/semantic/basics8.cl index 3b9697d4f..d45cd941d 100644 --- a/tests/semantic/basics8.cl +++ b/tests/semantic/basics8.cl @@ -1,9 +1,9 @@ --- It is an error redefine Object. - -class Main inherits IO { - main(): IO { out_string("Hello World!")}; -}; - -class Object { - xor(b: Bool): Bool { false }; +-- It is an error redefine Object. + +class Main inherits IO { + main(): IO { out_string("Hello World!")}; +}; + +class Object { + xor(b: Bool): Bool { false }; }; \ No newline at end of file diff --git a/tests/semantic/basics8_error.txt b/tests/semantic/basics8_error.txt index 652f47b30..45767c9c5 100644 --- a/tests/semantic/basics8_error.txt +++ b/tests/semantic/basics8_error.txt @@ -1 +1 @@ -(7, 7) - SemanticError: Redefinition of basic class Object. +(7, 7) - SemanticError: Redefinition of basic class Object. diff --git a/tests/semantic/blocks1.cl b/tests/semantic/blocks1.cl index 1e928908b..bad9093d7 100644 --- a/tests/semantic/blocks1.cl +++ b/tests/semantic/blocks1.cl @@ -1,31 +1,31 @@ ---The static type of a block is the static type of the last expression. - -class A { }; -class B inherits A { }; -class C inherits B { }; -class D inherits B { }; -class E inherits B { }; -class F inherits A { }; - -class Main inherits IO { - main(): IO { out_string("Hello World!")}; - - test: B <- { - new A; - { - new B; - { - new C; - { - new D; - { - new E; - { - new F; - }; - }; - }; - }; - }; - }; +--The static type of a block is the static type of the last expression. + +class A { }; +class B inherits A { }; +class C inherits B { }; +class D inherits B { }; +class E inherits B { }; +class F inherits A { }; + +class Main inherits IO { + main(): IO { out_string("Hello World!")}; + + test: B <- { + new A; + { + new B; + { + new C; + { + new D; + { + new E; + { + new F; + }; + }; + }; + }; + }; + }; }; \ No newline at end of file diff --git a/tests/semantic/blocks1_error.txt b/tests/semantic/blocks1_error.txt index 2f0e2caf3..6bd9d6118 100644 --- a/tests/semantic/blocks1_error.txt +++ b/tests/semantic/blocks1_error.txt @@ -1 +1 @@ -(13, 16) - TypeError: Inferred type F of initialization of attribute test does not conform to declared type B. +(13, 16) - TypeError: Inferred type F of initialization of attribute test does not conform to declared type B. diff --git a/tests/semantic/case1.cl b/tests/semantic/case1.cl index 82c6a4d61..af452f11c 100644 --- a/tests/semantic/case1.cl +++ b/tests/semantic/case1.cl @@ -1,23 +1,23 @@ ---For each branch, let Ti be the static type of . The static type of a case expression is Join 1≤i≤n Ti. - -class A { }; -class B inherits A { }; -class C inherits B { }; -class D inherits B { }; -class E inherits B { }; -class F inherits A { }; - -class Main inherits IO { - main(): IO { out_string("Hello World!")}; - - b: B <- case "true" of - i: Int => New C; - b: Bool => New D; - s: String => New E; - esac; - - test: B <- case 0 of - b: Bool => new F; - i: Int => new E; - esac; -}; +--For each branch, let Ti be the static type of . The static type of a case expression is Join 1≤i≤n Ti. + +class A { }; +class B inherits A { }; +class C inherits B { }; +class D inherits B { }; +class E inherits B { }; +class F inherits A { }; + +class Main inherits IO { + main(): IO { out_string("Hello World!")}; + + b: B <- case "true" of + i: Int => New C; + b: Bool => New D; + s: String => New E; + esac; + + test: B <- case 0 of + b: Bool => new F; + i: Int => new E; + esac; +}; diff --git a/tests/semantic/case1_error.txt b/tests/semantic/case1_error.txt index f05ce31b9..70c7d16ca 100644 --- a/tests/semantic/case1_error.txt +++ b/tests/semantic/case1_error.txt @@ -1 +1 @@ -(19, 16) - TypeError: Inferred type A of initialization of attribute test does not conform to declared type B. +(19, 16) - TypeError: Inferred type A of initialization of attribute test does not conform to declared type B. diff --git a/tests/semantic/case2.cl b/tests/semantic/case2.cl index ae97b41da..dbbe4148c 100644 --- a/tests/semantic/case2.cl +++ b/tests/semantic/case2.cl @@ -1,23 +1,23 @@ --- The variables declared on each branch of a case must all have distinct types. - -class A { }; -class B inherits A { }; -class C inherits B { }; -class D inherits B { }; -class E inherits B { }; -class F inherits A { }; - -class Main inherits IO { - main(): IO { out_string("Hello World!")}; - - b: B <- case "true" of - i: Int => New C; - b: Bool => New D; - s: String => New E; - esac; - - test: A <- case 0 of - b: Bool => new F; - i: Bool => new E; - esac; +-- The variables declared on each branch of a case must all have distinct types. + +class A { }; +class B inherits A { }; +class C inherits B { }; +class D inherits B { }; +class E inherits B { }; +class F inherits A { }; + +class Main inherits IO { + main(): IO { out_string("Hello World!")}; + + b: B <- case "true" of + i: Int => New C; + b: Bool => New D; + s: String => New E; + esac; + + test: A <- case 0 of + b: Bool => new F; + i: Bool => new E; + esac; }; \ No newline at end of file diff --git a/tests/semantic/case3.cl b/tests/semantic/case3.cl index da79bbfe6..9ff06336a 100644 --- a/tests/semantic/case3.cl +++ b/tests/semantic/case3.cl @@ -1,23 +1,23 @@ --- Missing type - -class A { }; -class B inherits A { }; -class C inherits B { }; -class D inherits B { }; -class E inherits B { }; -class F inherits A { }; - -class Main inherits IO { - main(): IO { out_string("Hello World!")}; - - b: B <- case "true" of - i: Int => New C; - b: Bool => New D; - s: String => New E; - esac; - - test: A <- case 0 of - b: Bool => new F; - i: Ball => new E; - esac; +-- Missing type + +class A { }; +class B inherits A { }; +class C inherits B { }; +class D inherits B { }; +class E inherits B { }; +class F inherits A { }; + +class Main inherits IO { + main(): IO { out_string("Hello World!")}; + + b: B <- case "true" of + i: Int => New C; + b: Bool => New D; + s: String => New E; + esac; + + test: A <- case 0 of + b: Bool => new F; + i: Ball => new E; + esac; }; \ No newline at end of file diff --git a/tests/semantic/class1.cl b/tests/semantic/class1.cl index ed83da9d1..576a3d0eb 100644 --- a/tests/semantic/class1.cl +++ b/tests/semantic/class1.cl @@ -1,9 +1,9 @@ --- Classes may not be redefined. - -class Repeat { - sum(a: Int, b: Int): Int { a + b }; -}; - -class Repeat { - mult(a: Int, b: Int): Int { a * b }; +-- Classes may not be redefined. + +class Repeat { + sum(a: Int, b: Int): Int { a + b }; +}; + +class Repeat { + mult(a: Int, b: Int): Int { a * b }; }; \ No newline at end of file diff --git a/tests/semantic/class1_error.txt b/tests/semantic/class1_error.txt index 19c507672..6337828d0 100644 --- a/tests/semantic/class1_error.txt +++ b/tests/semantic/class1_error.txt @@ -1,2 +1,2 @@ -(7, 5) - SemanticError: Classes may not be redefined - +(7, 5) - SemanticError: Classes may not be redefined + diff --git a/tests/semantic/conditionals1.cl b/tests/semantic/conditionals1.cl index 3446a8b0f..46af8cc73 100644 --- a/tests/semantic/conditionals1.cl +++ b/tests/semantic/conditionals1.cl @@ -1,14 +1,14 @@ ---The predicate must have static type Bool. - -class A { }; -class B inherits A { }; -class C inherits B { }; -class D inherits B { }; -class E inherits B { }; -class F inherits A { }; - -class Main inherits IO { - main(): IO { out_string("Hello World!")}; - - a: A <- if new F then new D else new C fi; +--The predicate must have static type Bool. + +class A { }; +class B inherits A { }; +class C inherits B { }; +class D inherits B { }; +class E inherits B { }; +class F inherits A { }; + +class Main inherits IO { + main(): IO { out_string("Hello World!")}; + + a: A <- if new F then new D else new C fi; }; \ No newline at end of file diff --git a/tests/semantic/conditionals1_error.txt b/tests/semantic/conditionals1_error.txt index b86345359..52db300b9 100644 --- a/tests/semantic/conditionals1_error.txt +++ b/tests/semantic/conditionals1_error.txt @@ -1 +1 @@ -(13, 16) - TypeError: Predicate of 'if' does not have type Bool. +(13, 16) - TypeError: Predicate of 'if' does not have type Bool. diff --git a/tests/semantic/conditionals2.cl b/tests/semantic/conditionals2.cl index 9d6313d75..8814177fc 100644 --- a/tests/semantic/conditionals2.cl +++ b/tests/semantic/conditionals2.cl @@ -1,24 +1,24 @@ -(* -Let T and F be the static types of the branches of the conditional. Then the static type of the -conditional is T t F. (think: Walk towards Object from each of T and F until the paths meet.) -*) - -class A { }; -class B inherits A { }; -class C inherits B { }; -class D inherits B { }; -class E inherits B { }; -class F inherits A { }; - -class Main inherits IO { - main(): IO { out_string("Hello World!")}; - - b: B <- if true then - new C - else - if false then new D - else new E fi - fi; - - test: B <- if not true then new F else new E fi; -}; +(* +Let T and F be the static types of the branches of the conditional. Then the static type of the +conditional is T t F. (think: Walk towards Object from each of T and F until the paths meet.) +*) + +class A { }; +class B inherits A { }; +class C inherits B { }; +class D inherits B { }; +class E inherits B { }; +class F inherits A { }; + +class Main inherits IO { + main(): IO { out_string("Hello World!")}; + + b: B <- if true then + new C + else + if false then new D + else new E fi + fi; + + test: B <- if not true then new F else new E fi; +}; diff --git a/tests/semantic/conditionals2_error.txt b/tests/semantic/conditionals2_error.txt index d6f5fc307..8f54d195e 100644 --- a/tests/semantic/conditionals2_error.txt +++ b/tests/semantic/conditionals2_error.txt @@ -1,2 +1,2 @@ -(23, 16) - TypeError: Inferred type A of initialization of attribute test does not conform to declared type B. - +(23, 16) - TypeError: Inferred type A of initialization of attribute test does not conform to declared type B. + diff --git a/tests/semantic/dispatch1.cl b/tests/semantic/dispatch1.cl index 1c0457fa3..bfd90f075 100644 --- a/tests/semantic/dispatch1.cl +++ b/tests/semantic/dispatch1.cl @@ -1,33 +1,33 @@ -(* -e0 .f(e1, . . . , en ) -Assume e0 has static type A. -Class A must have a method f -*) - -class A inherits IO { - f(x: Int, y: Int): Int { x + y }; - g(x: Int): Int { x + x }; -}; -class B inherits A { - f(a: Int, b: Int): Int { a - b }; -}; -class C inherits B { - ident(m: Int): Int { m }; - f(m: Int, n: Int): Int { m * n }; -}; -class D inherits B { - ident(v: String): IO { new IO.out_string(v) }; - f(v: Int, w: Int): Int { v / w }; - g(v: Int): Int { v + v + v }; - - back(s: String): B { { - out_string(s); - self; - } }; -}; - -class Main inherits IO { - main(): IO { out_string("Hello World!")}; - - test: B <- new D.back("Hello ").back("World!"); +(* +e0 .f(e1, . . . , en ) +Assume e0 has static type A. +Class A must have a method f +*) + +class A inherits IO { + f(x: Int, y: Int): Int { x + y }; + g(x: Int): Int { x + x }; +}; +class B inherits A { + f(a: Int, b: Int): Int { a - b }; +}; +class C inherits B { + ident(m: Int): Int { m }; + f(m: Int, n: Int): Int { m * n }; +}; +class D inherits B { + ident(v: String): IO { new IO.out_string(v) }; + f(v: Int, w: Int): Int { v / w }; + g(v: Int): Int { v + v + v }; + + back(s: String): B { { + out_string(s); + self; + } }; +}; + +class Main inherits IO { + main(): IO { out_string("Hello World!")}; + + test: B <- new D.back("Hello ").back("World!"); }; \ No newline at end of file diff --git a/tests/semantic/dispatch1_error.txt b/tests/semantic/dispatch1_error.txt index 7fb22edce..89fa22b77 100644 --- a/tests/semantic/dispatch1_error.txt +++ b/tests/semantic/dispatch1_error.txt @@ -1 +1 @@ -(32, 37) - AttributeError: Dispatch to undefined method back. +(32, 37) - AttributeError: Dispatch to undefined method back. diff --git a/tests/semantic/dispatch2.cl b/tests/semantic/dispatch2.cl index 5182912b8..ebca718ac 100644 --- a/tests/semantic/dispatch2.cl +++ b/tests/semantic/dispatch2.cl @@ -1,34 +1,34 @@ -(* -e0 .f(e1, . . . , en ) -Assume e0 has static type A. -Class A must have a method f -the dispatch and the definition of f must have the same number of arguments -*) - -class A inherits IO { - f(x: Int, y: Int): Int { x + y }; - g(x: Int): Int { x + x }; -}; -class B inherits A { - f(a: Int, b: Int): Int { a - b }; -}; -class C inherits B { - ident(m: Int): Int { m }; - f(m: Int, n: Int): Int { m * n }; -}; -class D inherits B { - ident(v: String): IO { new IO.out_string(v) }; - f(v: Int, w: Int): Int { v / w }; - g(v: Int): Int { v + v + v }; - - back(s: String): B { { - out_string(s); - self; - } }; -}; - -class Main inherits IO { - main(): IO { out_string("Hello World!")}; - - test: Int <- new D.back("Hello ").g(2, 2); +(* +e0 .f(e1, . . . , en ) +Assume e0 has static type A. +Class A must have a method f +the dispatch and the definition of f must have the same number of arguments +*) + +class A inherits IO { + f(x: Int, y: Int): Int { x + y }; + g(x: Int): Int { x + x }; +}; +class B inherits A { + f(a: Int, b: Int): Int { a - b }; +}; +class C inherits B { + ident(m: Int): Int { m }; + f(m: Int, n: Int): Int { m * n }; +}; +class D inherits B { + ident(v: String): IO { new IO.out_string(v) }; + f(v: Int, w: Int): Int { v / w }; + g(v: Int): Int { v + v + v }; + + back(s: String): B { { + out_string(s); + self; + } }; +}; + +class Main inherits IO { + main(): IO { out_string("Hello World!")}; + + test: Int <- new D.back("Hello ").g(2, 2); }; \ No newline at end of file diff --git a/tests/semantic/dispatch2_error.txt b/tests/semantic/dispatch2_error.txt index a86c35340..1530fb82c 100644 --- a/tests/semantic/dispatch2_error.txt +++ b/tests/semantic/dispatch2_error.txt @@ -1 +1 @@ -(33, 39) - SemanticError: Method g called with wrong number of arguments. +(33, 39) - SemanticError: Method g called with wrong number of arguments. diff --git a/tests/semantic/dispatch3.cl b/tests/semantic/dispatch3.cl index ecb1535db..98c19da77 100644 --- a/tests/semantic/dispatch3.cl +++ b/tests/semantic/dispatch3.cl @@ -1,36 +1,36 @@ -(* -e0 .f(e1, . . . , en ) -Assume e0 has static type A. -Class A must have a method f -the static type of the ith actual parameter must conform to the declared type of the ith formal parameter. -*) - -class A inherits IO { - f(x: Int, y: Int): Int { x + y }; - g(x: Int): Int { x + x }; -}; -class B inherits A { - f(a: Int, b: Int): Int { a - b }; -}; -class C inherits B { - ident(m: Int): Int { m }; - f(m: Int, n: Int): Int { m * n }; -}; -class D inherits B { - ident(v: String): IO { new IO.out_string(v) }; - f(v: Int, w: Int): Int { v / w }; - g(v: Int): Int { v + v + v }; - - back(s: String): B { { - out_string(s); - self; - } }; - - alphabet(a: A, b: B, c: C): D { self }; -}; - -class Main inherits IO { - main(): IO { out_string("Hello World!")}; - - test: B <- new D.alphabet(new D, new D, new D.back("Hello ")).back("World!"); +(* +e0 .f(e1, . . . , en ) +Assume e0 has static type A. +Class A must have a method f +the static type of the ith actual parameter must conform to the declared type of the ith formal parameter. +*) + +class A inherits IO { + f(x: Int, y: Int): Int { x + y }; + g(x: Int): Int { x + x }; +}; +class B inherits A { + f(a: Int, b: Int): Int { a - b }; +}; +class C inherits B { + ident(m: Int): Int { m }; + f(m: Int, n: Int): Int { m * n }; +}; +class D inherits B { + ident(v: String): IO { new IO.out_string(v) }; + f(v: Int, w: Int): Int { v / w }; + g(v: Int): Int { v + v + v }; + + back(s: String): B { { + out_string(s); + self; + } }; + + alphabet(a: A, b: B, c: C): D { self }; +}; + +class Main inherits IO { + main(): IO { out_string("Hello World!")}; + + test: B <- new D.alphabet(new D, new D, new D.back("Hello ")).back("World!"); }; \ No newline at end of file diff --git a/tests/semantic/dispatch3_error.txt b/tests/semantic/dispatch3_error.txt index 0def5cf03..77e81db72 100644 --- a/tests/semantic/dispatch3_error.txt +++ b/tests/semantic/dispatch3_error.txt @@ -1 +1 @@ -(35, 45) - TypeError: In call of method alphabet, type B of parameter c does not conform to declared type C. +(35, 45) - TypeError: In call of method alphabet, type B of parameter c does not conform to declared type C. diff --git a/tests/semantic/dispatch4.cl b/tests/semantic/dispatch4.cl index 9cadd8332..604e462a2 100644 --- a/tests/semantic/dispatch4.cl +++ b/tests/semantic/dispatch4.cl @@ -1,36 +1,36 @@ -(* -e0 .f(e1, . . . , en ) -Assume e0 has static type A. -Class A must have a method f -If f has return type B and B is a class name, then the static type of the dispatch is B. -*) - -class A inherits IO { - f(x: Int, y: Int): Int { x + y }; - g(x: Int): Int { x + x }; -}; -class B inherits A { - f(a: Int, b: Int): Int { a - b }; -}; -class C inherits B { - ident(m: Int): Int { m }; - f(m: Int, n: Int): Int { m * n }; -}; -class D inherits B { - ident(v: String): IO { new IO.out_string(v) }; - f(v: Int, w: Int): Int { v / w }; - g(v: Int): Int { v + v + v }; - - back(s: String): B { { - out_string(s); - self; - } }; - - alphabet(a: A, b: B, c: C): D { self }; -}; - -class Main inherits IO { - main(): IO { out_string("Hello World!")}; - - test: D <- new D.alphabet(new D, new D.back("Hello "), new C).back("World!"); +(* +e0 .f(e1, . . . , en ) +Assume e0 has static type A. +Class A must have a method f +If f has return type B and B is a class name, then the static type of the dispatch is B. +*) + +class A inherits IO { + f(x: Int, y: Int): Int { x + y }; + g(x: Int): Int { x + x }; +}; +class B inherits A { + f(a: Int, b: Int): Int { a - b }; +}; +class C inherits B { + ident(m: Int): Int { m }; + f(m: Int, n: Int): Int { m * n }; +}; +class D inherits B { + ident(v: String): IO { new IO.out_string(v) }; + f(v: Int, w: Int): Int { v / w }; + g(v: Int): Int { v + v + v }; + + back(s: String): B { { + out_string(s); + self; + } }; + + alphabet(a: A, b: B, c: C): D { self }; +}; + +class Main inherits IO { + main(): IO { out_string("Hello World!")}; + + test: D <- new D.alphabet(new D, new D.back("Hello "), new C).back("World!"); }; \ No newline at end of file diff --git a/tests/semantic/dispatch5.cl b/tests/semantic/dispatch5.cl index b4437b1b4..9820e5ee1 100644 --- a/tests/semantic/dispatch5.cl +++ b/tests/semantic/dispatch5.cl @@ -1,31 +1,31 @@ -(* -(,...,) is shorthand for self.(,...,). -*) - -class A inherits IO { - f(x: Int, y: Int): Int { x + y }; - g(x: Int): Int { x + x }; -}; -class B inherits A { - f(a: Int, b: Int): Int { a - b }; -}; -class C inherits B { - ident(m: Int): Int { m }; - f(m: Int, n: Int): Int { m * n }; - sum(m: Int, n: Int, p: Int): Int { m + n + p }; -}; -class D inherits B { - ident(v: String): IO { new IO.out_string(v) }; - f(v: Int, w: Int): Int { v / w }; - - back(s: String): B { { - out_string(s); - g(2); - sum(1, 2, 3); - self; - } }; -}; - -class Main inherits IO { - main(): IO { out_string("Hello World!")}; +(* +(,...,) is shorthand for self.(,...,). +*) + +class A inherits IO { + f(x: Int, y: Int): Int { x + y }; + g(x: Int): Int { x + x }; +}; +class B inherits A { + f(a: Int, b: Int): Int { a - b }; +}; +class C inherits B { + ident(m: Int): Int { m }; + f(m: Int, n: Int): Int { m * n }; + sum(m: Int, n: Int, p: Int): Int { m + n + p }; +}; +class D inherits B { + ident(v: String): IO { new IO.out_string(v) }; + f(v: Int, w: Int): Int { v / w }; + + back(s: String): B { { + out_string(s); + g(2); + sum(1, 2, 3); + self; + } }; +}; + +class Main inherits IO { + main(): IO { out_string("Hello World!")}; }; \ No newline at end of file diff --git a/tests/semantic/dispatch5_error.txt b/tests/semantic/dispatch5_error.txt index d26bf34a1..6a6922f32 100644 --- a/tests/semantic/dispatch5_error.txt +++ b/tests/semantic/dispatch5_error.txt @@ -1 +1 @@ -(24, 9) - AttributeError: Dispatch to undefined method sum. +(24, 9) - AttributeError: Dispatch to undefined method sum. diff --git a/tests/semantic/dispatch6.cl b/tests/semantic/dispatch6.cl index fcc033f2c..bbe58fbb1 100644 --- a/tests/semantic/dispatch6.cl +++ b/tests/semantic/dispatch6.cl @@ -1,32 +1,32 @@ -(* -e@B.f() invokes the method -f in class B on the object that is the value of e. For this form of dispatch, the static type to the left of -“@”must conform to the type specified to the right of “@”. -*) - -class A { - f(x: Int, y: Int): Int { x + y }; - g(x: Int): Int { x + x }; -}; -class B inherits A { - f(a: Int, b: Int): Int { a - b }; - sum(m: Int, n: Int, p: Int): Int { m + n + p }; -}; -class C inherits B { - ident(m: Int): Int { m }; - f(m: Int, n: Int): Int { m * n }; -}; -class D inherits B { - ident(v: String): IO { new IO.out_string(v) }; - f(v: Int, w: Int): Int { v / w }; - g(v: Int): Int { v + v + v }; - sum(v: Int, w: Int, z: Int): Int { v - w - z }; -}; - -class Main inherits IO { - main(): IO { out_string("Hello World!")}; - - a: A <- new D; - b: Int <- new D@B.sum(1, 2, 3); - test: Int <- a@B.sum(1, 2, 3); -}; +(* +e@B.f() invokes the method +f in class B on the object that is the value of e. For this form of dispatch, the static type to the left of +“@”must conform to the type specified to the right of “@”. +*) + +class A { + f(x: Int, y: Int): Int { x + y }; + g(x: Int): Int { x + x }; +}; +class B inherits A { + f(a: Int, b: Int): Int { a - b }; + sum(m: Int, n: Int, p: Int): Int { m + n + p }; +}; +class C inherits B { + ident(m: Int): Int { m }; + f(m: Int, n: Int): Int { m * n }; +}; +class D inherits B { + ident(v: String): IO { new IO.out_string(v) }; + f(v: Int, w: Int): Int { v / w }; + g(v: Int): Int { v + v + v }; + sum(v: Int, w: Int, z: Int): Int { v - w - z }; +}; + +class Main inherits IO { + main(): IO { out_string("Hello World!")}; + + a: A <- new D; + b: Int <- new D@B.sum(1, 2, 3); + test: Int <- a@B.sum(1, 2, 3); +}; diff --git a/tests/semantic/dispatch6_error.txt b/tests/semantic/dispatch6_error.txt index ae9184b2f..7d5ec3780 100644 --- a/tests/semantic/dispatch6_error.txt +++ b/tests/semantic/dispatch6_error.txt @@ -1 +1 @@ -(31, 18) - TypeError: Expression type A does not conform to declared static dispatch type B. +(31, 18) - TypeError: Expression type A does not conform to declared static dispatch type B. diff --git a/tests/semantic/eq1.cl b/tests/semantic/eq1.cl index 88f2a7ffe..dc8a0cd43 100644 --- a/tests/semantic/eq1.cl +++ b/tests/semantic/eq1.cl @@ -1,17 +1,17 @@ -(* -The comparison = is a special -case. If either or has static type Int, Bool, or String, then the other must have the -same static type. Any other types, including SELF TYPE, may be freely compared. -*) - -class A { }; -class B inherits A { }; - -class Main inherits IO { - main(): IO { out_string("Hello World!")}; - - x: Bool <- 1 = 2; - test: Bool <- 1 = new A; - y: Bool <- "1" = "2"; - z: Bool <- true = not false; +(* +The comparison = is a special +case. If either or has static type Int, Bool, or String, then the other must have the +same static type. Any other types, including SELF TYPE, may be freely compared. +*) + +class A { }; +class B inherits A { }; + +class Main inherits IO { + main(): IO { out_string("Hello World!")}; + + x: Bool <- 1 = 2; + test: Bool <- 1 = new A; + y: Bool <- "1" = "2"; + z: Bool <- true = not false; }; \ No newline at end of file diff --git a/tests/semantic/eq1_error.txt b/tests/semantic/eq1_error.txt index f81425683..0b85d2fa0 100644 --- a/tests/semantic/eq1_error.txt +++ b/tests/semantic/eq1_error.txt @@ -1 +1 @@ -(14, 21) - TypeError: Illegal comparison with a basic type. +(14, 21) - TypeError: Illegal comparison with a basic type. diff --git a/tests/semantic/eq2.cl b/tests/semantic/eq2.cl index d76852780..f4b2ffac7 100644 --- a/tests/semantic/eq2.cl +++ b/tests/semantic/eq2.cl @@ -1,17 +1,17 @@ -(* -The comparison = is a special -case. If either or has static type Int, Bool, or String, then the other must have the -same static type. Any other types, including SELF TYPE, may be freely compared. -*) - -class A { }; -class B inherits A { }; - -class Main inherits IO { - main(): IO { out_string("Hello World!")}; - - x: Bool <- 1 = 2; - y: Bool <- "1" = "2"; - test: Bool <- "1" = new B; - z: Bool <- true = not false; -}; +(* +The comparison = is a special +case. If either or has static type Int, Bool, or String, then the other must have the +same static type. Any other types, including SELF TYPE, may be freely compared. +*) + +class A { }; +class B inherits A { }; + +class Main inherits IO { + main(): IO { out_string("Hello World!")}; + + x: Bool <- 1 = 2; + y: Bool <- "1" = "2"; + test: Bool <- "1" = new B; + z: Bool <- true = not false; +}; diff --git a/tests/semantic/eq2_error.txt b/tests/semantic/eq2_error.txt index a44ab0d51..1bb29ca32 100644 --- a/tests/semantic/eq2_error.txt +++ b/tests/semantic/eq2_error.txt @@ -1 +1 @@ -(15, 23) - TypeError: Illegal comparison with a basic type. +(15, 23) - TypeError: Illegal comparison with a basic type. diff --git a/tests/semantic/eq3.cl b/tests/semantic/eq3.cl index 4dad693ee..b7ee462c5 100644 --- a/tests/semantic/eq3.cl +++ b/tests/semantic/eq3.cl @@ -1,17 +1,17 @@ -(* -The comparison = is a special -case. If either or has static type Int, Bool, or String, then the other must have the -same static type. Any other types, including SELF TYPE, may be freely compared. -*) - -class A { }; -class B inherits A { }; - -class Main inherits IO { - main(): IO { out_string("Hello World!")}; - - x: Bool <- 1 = 2; - y: Bool <- "1" = "2"; - z: Bool <- true = not false; - test: Bool <- "true" = not false; -}; +(* +The comparison = is a special +case. If either or has static type Int, Bool, or String, then the other must have the +same static type. Any other types, including SELF TYPE, may be freely compared. +*) + +class A { }; +class B inherits A { }; + +class Main inherits IO { + main(): IO { out_string("Hello World!")}; + + x: Bool <- 1 = 2; + y: Bool <- "1" = "2"; + z: Bool <- true = not false; + test: Bool <- "true" = not false; +}; diff --git a/tests/semantic/eq3_error.txt b/tests/semantic/eq3_error.txt index c4e27eb8a..d57841b95 100644 --- a/tests/semantic/eq3_error.txt +++ b/tests/semantic/eq3_error.txt @@ -1 +1 @@ -(16, 26) - TypeError: Illegal comparison with a basic type. +(16, 26) - TypeError: Illegal comparison with a basic type. diff --git a/tests/semantic/eq4.cl b/tests/semantic/eq4.cl index 11afc119f..63c1067f0 100644 --- a/tests/semantic/eq4.cl +++ b/tests/semantic/eq4.cl @@ -1,17 +1,17 @@ -(* -The comparison = is a special -case. If either or has static type Int, Bool, or String, then the other must have the -same static type. Any other types, including SELF TYPE, may be freely compared. The result is a Bool. -*) - -class A { }; -class B { }; - -class Main inherits IO { - main(): IO { out_string("Hello World!")}; - - x: Bool <- 1 = 2; - y: Bool <- "1" = "2"; - z: Bool <- new A = new B; - test: Int <- new A = new B; -}; +(* +The comparison = is a special +case. If either or has static type Int, Bool, or String, then the other must have the +same static type. Any other types, including SELF TYPE, may be freely compared. The result is a Bool. +*) + +class A { }; +class B { }; + +class Main inherits IO { + main(): IO { out_string("Hello World!")}; + + x: Bool <- 1 = 2; + y: Bool <- "1" = "2"; + z: Bool <- new A = new B; + test: Int <- new A = new B; +}; diff --git a/tests/semantic/eq4_error.txt b/tests/semantic/eq4_error.txt index 3ead21d0e..f075fecbe 100644 --- a/tests/semantic/eq4_error.txt +++ b/tests/semantic/eq4_error.txt @@ -1 +1 @@ -(16, 18) - TypeError: Inferred type Bool of initialization of attribute test does not conform to declared type Int. +(16, 18) - TypeError: Inferred type Bool of initialization of attribute test does not conform to declared type Int. diff --git a/tests/semantic/isvoid1.cl b/tests/semantic/isvoid1.cl index 072720d85..7c3a83a77 100644 --- a/tests/semantic/isvoid1.cl +++ b/tests/semantic/isvoid1.cl @@ -1,26 +1,26 @@ ---evaluates to true if expr is void and evaluates to false if expr is not void. - -class A { }; -class B inherits A { }; -class C inherits B { }; -class D inherits B { }; -class E inherits B { }; -class F inherits A { }; - -class Main inherits IO { - main(): IO { out_string("Hello World!")}; - - b: B <- if isvoid new F then - new C - else - if false then new D - else new E fi - fi; - - test: B <- isvoid if isvoid new F then - new C - else - if false then new D - else new E fi - fi; +--evaluates to true if expr is void and evaluates to false if expr is not void. + +class A { }; +class B inherits A { }; +class C inherits B { }; +class D inherits B { }; +class E inherits B { }; +class F inherits A { }; + +class Main inherits IO { + main(): IO { out_string("Hello World!")}; + + b: B <- if isvoid new F then + new C + else + if false then new D + else new E fi + fi; + + test: B <- isvoid if isvoid new F then + new C + else + if false then new D + else new E fi + fi; }; \ No newline at end of file diff --git a/tests/semantic/isvoid1_error.txt b/tests/semantic/isvoid1_error.txt index 0922de909..3fd0157b4 100644 --- a/tests/semantic/isvoid1_error.txt +++ b/tests/semantic/isvoid1_error.txt @@ -1 +1 @@ -(20, 16) - TypeError: Inferred type Bool of initialization of attribute test does not conform to declared type B. +(20, 16) - TypeError: Inferred type Bool of initialization of attribute test does not conform to declared type B. diff --git a/tests/semantic/let1.cl b/tests/semantic/let1.cl index 26ef63026..9220d3dbc 100644 --- a/tests/semantic/let1.cl +++ b/tests/semantic/let1.cl @@ -1,15 +1,15 @@ ---The type of an initialization expression must conform to the declared type of the identifier. - -class A { }; -class B inherits A { }; -class C inherits B { }; -class D inherits B { }; -class E inherits B { }; -class F inherits A { }; - -class Main inherits IO { - main(): IO { out_string("Hello World!")}; - - b: B <- let a: Bool, a: Int <- 5, a: String, a: A <- new F, b: B <- new E in b; - test: B <- let a: Bool, a: Int <- 5, a: String, a: A <- new F, b: C <- new E in b; +--The type of an initialization expression must conform to the declared type of the identifier. + +class A { }; +class B inherits A { }; +class C inherits B { }; +class D inherits B { }; +class E inherits B { }; +class F inherits A { }; + +class Main inherits IO { + main(): IO { out_string("Hello World!")}; + + b: B <- let a: Bool, a: Int <- 5, a: String, a: A <- new F, b: B <- new E in b; + test: B <- let a: Bool, a: Int <- 5, a: String, a: A <- new F, b: C <- new E in b; }; \ No newline at end of file diff --git a/tests/semantic/let1_error.txt b/tests/semantic/let1_error.txt index 16ecf780c..56547dae5 100644 --- a/tests/semantic/let1_error.txt +++ b/tests/semantic/let1_error.txt @@ -1 +1 @@ -(14, 76) - TypeError: Inferred type E of initialization of b does not conform to identifier's declared type C. +(14, 76) - TypeError: Inferred type E of initialization of b does not conform to identifier's declared type C. diff --git a/tests/semantic/let2.cl b/tests/semantic/let2.cl index c5956ead3..2949fb94d 100644 --- a/tests/semantic/let2.cl +++ b/tests/semantic/let2.cl @@ -1,15 +1,15 @@ ---The type of let is the type of the body. - -class A { }; -class B inherits A { }; -class C inherits B { }; -class D inherits B { }; -class E inherits B { }; -class F inherits A { }; - -class Main inherits IO { - main(): IO { out_string("Hello World!")}; - - b: B <- let a: Bool, a: Int <- 5, a: String, a: A <- new F, b: B <- new E in b; - test: B <- let a: Bool, a: Int <- 5, a: String, a: A <- new F, b: A <- new E in b; +--The type of let is the type of the body. + +class A { }; +class B inherits A { }; +class C inherits B { }; +class D inherits B { }; +class E inherits B { }; +class F inherits A { }; + +class Main inherits IO { + main(): IO { out_string("Hello World!")}; + + b: B <- let a: Bool, a: Int <- 5, a: String, a: A <- new F, b: B <- new E in b; + test: B <- let a: Bool, a: Int <- 5, a: String, a: A <- new F, b: A <- new E in b; }; \ No newline at end of file diff --git a/tests/semantic/let2_error.txt b/tests/semantic/let2_error.txt index b1e8a365d..3b7c669a3 100644 --- a/tests/semantic/let2_error.txt +++ b/tests/semantic/let2_error.txt @@ -1 +1 @@ -(14, 16) - TypeError: Inferred type A of initialization of attribute test does not conform to declared type B. +(14, 16) - TypeError: Inferred type A of initialization of attribute test does not conform to declared type B. diff --git a/tests/semantic/let3.cl b/tests/semantic/let3.cl index 8c0670ab8..0a4a9ceaf 100644 --- a/tests/semantic/let3.cl +++ b/tests/semantic/let3.cl @@ -1,15 +1,15 @@ --- Missing type - -class A { }; -class B inherits A { }; -class C inherits B { }; -class D inherits B { }; -class E inherits B { }; -class F inherits A { }; - -class Main inherits IO { - main(): IO { out_string("Hello World!")}; - - b: B <- let a: Bool, a: Int <- 5, a: String, a: A <- new F, b: B <- new E in b; - test: B <- let a: Bool, a: Int <- 5, a: String, a: A <- new F, b: Cadena in new B; +-- Missing type + +class A { }; +class B inherits A { }; +class C inherits B { }; +class D inherits B { }; +class E inherits B { }; +class F inherits A { }; + +class Main inherits IO { + main(): IO { out_string("Hello World!")}; + + b: B <- let a: Bool, a: Int <- 5, a: String, a: A <- new F, b: B <- new E in b; + test: B <- let a: Bool, a: Int <- 5, a: String, a: A <- new F, b: Cadena in new B; }; \ No newline at end of file diff --git a/tests/semantic/loops1.cl b/tests/semantic/loops1.cl index de3a624d2..6a0a81818 100644 --- a/tests/semantic/loops1.cl +++ b/tests/semantic/loops1.cl @@ -1,8 +1,8 @@ ---The predicate must have static type Bool. - -class Main inherits IO { - main(): IO { out_string("Hello World!")}; - - i: Int <- 1; - test: Object <- while "true" loop i <- i + 1 pool; +--The predicate must have static type Bool. + +class Main inherits IO { + main(): IO { out_string("Hello World!")}; + + i: Int <- 1; + test: Object <- while "true" loop i <- i + 1 pool; }; \ No newline at end of file diff --git a/tests/semantic/loops2.cl b/tests/semantic/loops2.cl index dea69fa14..d52169da7 100644 --- a/tests/semantic/loops2.cl +++ b/tests/semantic/loops2.cl @@ -1,9 +1,9 @@ ---The static type of a loop expression is Object. - -class Main inherits IO { - main(): IO { out_string("Hello World!")}; - - i: Int <- 1; - test: Object <- while not false loop i <- i + 1 pool; - test2: Int <- while not false loop i <- i + 1 pool; -}; +--The static type of a loop expression is Object. + +class Main inherits IO { + main(): IO { out_string("Hello World!")}; + + i: Int <- 1; + test: Object <- while not false loop i <- i + 1 pool; + test2: Int <- while not false loop i <- i + 1 pool; +}; diff --git a/tests/semantic/loops2_error.txt b/tests/semantic/loops2_error.txt index 9711cdf6a..ab79de8da 100644 --- a/tests/semantic/loops2_error.txt +++ b/tests/semantic/loops2_error.txt @@ -1 +1 @@ -(8, 19) - TypeError: Inferred type Object of initialization of attribute test2 does not conform to declared type Int. +(8, 19) - TypeError: Inferred type Object of initialization of attribute test2 does not conform to declared type Int. diff --git a/tests/semantic/methods1.cl b/tests/semantic/methods1.cl index d12031970..f4ff07bb0 100644 --- a/tests/semantic/methods1.cl +++ b/tests/semantic/methods1.cl @@ -1,12 +1,12 @@ ---The identifiers used in the formal parameter list must be distinct - -class A { }; -class B inherits A { }; -class C inherits B { }; -class D inherits B { }; - -class Main inherits IO { - main(): IO { out_string("Hello World!")}; - - test(a: A, a: B): Int { 4 }; +--The identifiers used in the formal parameter list must be distinct + +class A { }; +class B inherits A { }; +class C inherits B { }; +class D inherits B { }; + +class Main inherits IO { + main(): IO { out_string("Hello World!")}; + + test(a: A, a: B): Int { 4 }; }; \ No newline at end of file diff --git a/tests/semantic/methods1_error.txt b/tests/semantic/methods1_error.txt index 06ab88a92..809036803 100644 --- a/tests/semantic/methods1_error.txt +++ b/tests/semantic/methods1_error.txt @@ -1 +1 @@ -(11, 16) - SemanticError: Formal parameter a is multiply defined. +(11, 16) - SemanticError: Formal parameter a is multiply defined. diff --git a/tests/semantic/methods2.cl b/tests/semantic/methods2.cl index 3865f0e13..c010df01e 100644 --- a/tests/semantic/methods2.cl +++ b/tests/semantic/methods2.cl @@ -1,12 +1,12 @@ ---The type of the method body must conform to the declared return type. - -class A { }; -class B inherits A { }; -class C inherits B { }; -class D inherits B { }; - -class Main inherits IO { - main(): IO { out_string("Hello World!")}; - - test(a: A, b: B): C { new D }; +--The type of the method body must conform to the declared return type. + +class A { }; +class B inherits A { }; +class C inherits B { }; +class D inherits B { }; + +class Main inherits IO { + main(): IO { out_string("Hello World!")}; + + test(a: A, b: B): C { new D }; }; \ No newline at end of file diff --git a/tests/semantic/methods2_error.txt b/tests/semantic/methods2_error.txt index f7e4a330d..1a4baea17 100644 --- a/tests/semantic/methods2_error.txt +++ b/tests/semantic/methods2_error.txt @@ -1 +1 @@ -(11, 27) - TypeError: Inferred return type D of method test does not conform to declared return type C. +(11, 27) - TypeError: Inferred return type D of method test does not conform to declared return type C. diff --git a/tests/semantic/methods3.cl b/tests/semantic/methods3.cl index b92faeb97..9aff4d167 100644 --- a/tests/semantic/methods3.cl +++ b/tests/semantic/methods3.cl @@ -1,14 +1,14 @@ ---A formal parameter hides any definition of an attribute of the same name. - -class A { }; -class B inherits A { }; -class C inherits B { }; -class D inherits B { }; - -class Main inherits IO { - main(): IO { out_string("Hello World!")}; - - a: C <- new C; - test(a: D): D { a }; - test2(a: B): C { a }; +--A formal parameter hides any definition of an attribute of the same name. + +class A { }; +class B inherits A { }; +class C inherits B { }; +class D inherits B { }; + +class Main inherits IO { + main(): IO { out_string("Hello World!")}; + + a: C <- new C; + test(a: D): D { a }; + test2(a: B): C { a }; }; \ No newline at end of file diff --git a/tests/semantic/methods3_error.txt b/tests/semantic/methods3_error.txt index 1165b7595..a0f6d9db2 100644 --- a/tests/semantic/methods3_error.txt +++ b/tests/semantic/methods3_error.txt @@ -1 +1 @@ -(13, 22) - TypeError: Inferred return type B of method test2 does not conform to declared return type C. +(13, 22) - TypeError: Inferred return type B of method test2 does not conform to declared return type C. diff --git a/tests/semantic/methods4.cl b/tests/semantic/methods4.cl index be8fa36ef..e093bac1c 100644 --- a/tests/semantic/methods4.cl +++ b/tests/semantic/methods4.cl @@ -1,19 +1,19 @@ -(* -The rule is -simple: If a class C inherits a method f from an ancestor class P, then C may override the inherited -definition of f provided the number of arguments, the types of the formal parameters, and the return -type are exactly the same in both definitions. -*) - -class A { - f(x: Int, y: Int): Int { x + y }; -}; -class B inherits A { - f(x: Int, y: Object): Int { x }; -}; -class C inherits B { }; -class D inherits B { }; - -class Main inherits IO { - main(): IO { out_string("Hello World!")}; +(* +The rule is +simple: If a class C inherits a method f from an ancestor class P, then C may override the inherited +definition of f provided the number of arguments, the types of the formal parameters, and the return +type are exactly the same in both definitions. +*) + +class A { + f(x: Int, y: Int): Int { x + y }; +}; +class B inherits A { + f(x: Int, y: Object): Int { x }; +}; +class C inherits B { }; +class D inherits B { }; + +class Main inherits IO { + main(): IO { out_string("Hello World!")}; }; \ No newline at end of file diff --git a/tests/semantic/methods4_error.txt b/tests/semantic/methods4_error.txt index 9f1486dec..c9fc2d12a 100644 --- a/tests/semantic/methods4_error.txt +++ b/tests/semantic/methods4_error.txt @@ -1 +1 @@ -(12, 15) - SemanticError: In redefined method f, parameter type Object is different from original type Int. +(12, 15) - SemanticError: In redefined method f, parameter type Object is different from original type Int. diff --git a/tests/semantic/methods5.cl b/tests/semantic/methods5.cl index 3905dfdd6..732e4d408 100644 --- a/tests/semantic/methods5.cl +++ b/tests/semantic/methods5.cl @@ -1,20 +1,20 @@ -(* -The rule is -simple: If a class C inherits a method f from an ancestor class P, then C may override the inherited -definition of f provided the number of arguments, the types of the formal parameters, and the return -type are exactly the same in both definitions. -*) - -class A { - f(x: Int, y: Int): Int { x + y }; -}; -class B inherits A { - f(a: Int, b: Int): Object { a - b }; -}; -class C inherits B { }; -class D inherits B { }; - -class Main inherits IO { - main(): IO { out_string("Hello World!")}; -}; - +(* +The rule is +simple: If a class C inherits a method f from an ancestor class P, then C may override the inherited +definition of f provided the number of arguments, the types of the formal parameters, and the return +type are exactly the same in both definitions. +*) + +class A { + f(x: Int, y: Int): Int { x + y }; +}; +class B inherits A { + f(a: Int, b: Int): Object { a - b }; +}; +class C inherits B { }; +class D inherits B { }; + +class Main inherits IO { + main(): IO { out_string("Hello World!")}; +}; + diff --git a/tests/semantic/methods5_error.txt b/tests/semantic/methods5_error.txt index 8b6bdf36e..073eee8c5 100644 --- a/tests/semantic/methods5_error.txt +++ b/tests/semantic/methods5_error.txt @@ -1 +1 @@ -(12, 24) - SemanticError: In redefined method f, return type Object is different from original return type Int. +(12, 24) - SemanticError: In redefined method f, return type Object is different from original return type Int. diff --git a/tests/semantic/methods6.cl b/tests/semantic/methods6.cl index dd2b73da6..61a62b4b0 100644 --- a/tests/semantic/methods6.cl +++ b/tests/semantic/methods6.cl @@ -1,27 +1,27 @@ -(* -The rule is -simple: If a class C inherits a method f from an ancestor class P, then C may override the inherited -definition of f provided the number of arguments, the types of the formal parameters, and the return -type are exactly the same in both definitions. -*) - -class A { - f(x: Int, y: Int): Int { x + y }; - g(x: Int): Int { x + x }; -}; -class B inherits A { - f(a: Int, b: Int): Int { a - b }; -}; -class C inherits B { - ident(m: Int): Int { m }; - f(m: Int, n: Int): Int { m * n }; -}; -class D inherits B { - ident(v: String): IO { new IO.out_string(v) }; - f(v: Int, w: Int): Int { v / w }; - g(v: Int, w: Int, z: Int): Int { v + w + z }; -}; - -class Main inherits IO { - main(): IO { out_string("Hello World!")}; +(* +The rule is +simple: If a class C inherits a method f from an ancestor class P, then C may override the inherited +definition of f provided the number of arguments, the types of the formal parameters, and the return +type are exactly the same in both definitions. +*) + +class A { + f(x: Int, y: Int): Int { x + y }; + g(x: Int): Int { x + x }; +}; +class B inherits A { + f(a: Int, b: Int): Int { a - b }; +}; +class C inherits B { + ident(m: Int): Int { m }; + f(m: Int, n: Int): Int { m * n }; +}; +class D inherits B { + ident(v: String): IO { new IO.out_string(v) }; + f(v: Int, w: Int): Int { v / w }; + g(v: Int, w: Int, z: Int): Int { v + w + z }; +}; + +class Main inherits IO { + main(): IO { out_string("Hello World!")}; }; \ No newline at end of file diff --git a/tests/semantic/methods6_error.txt b/tests/semantic/methods6_error.txt index 8e32663b9..70ad02e32 100644 --- a/tests/semantic/methods6_error.txt +++ b/tests/semantic/methods6_error.txt @@ -1 +1 @@ -(22, 5) - SemanticError: Incompatible number of formal parameters in redefined method g. +(22, 5) - SemanticError: Incompatible number of formal parameters in redefined method g. diff --git a/tests/semantic/methods7.cl b/tests/semantic/methods7.cl index e5a01f682..21e8ca275 100644 --- a/tests/semantic/methods7.cl +++ b/tests/semantic/methods7.cl @@ -1,12 +1,12 @@ --- Missing type - -class A { }; -class B inherits A { }; -class C inherits B { }; -class D inherits B { }; - -class Main inherits IO { - main(): IO { out_string("Hello World!")}; - - test(a: A, b: Ball): Int { 4 }; +-- Missing type + +class A { }; +class B inherits A { }; +class C inherits B { }; +class D inherits B { }; + +class Main inherits IO { + main(): IO { out_string("Hello World!")}; + + test(a: A, b: Ball): Int { 4 }; }; \ No newline at end of file diff --git a/tests/semantic/methods8.cl b/tests/semantic/methods8.cl index 3fccab54c..121210748 100644 --- a/tests/semantic/methods8.cl +++ b/tests/semantic/methods8.cl @@ -1,12 +1,12 @@ --- Missing type - -class A { }; -class B inherits A { }; -class C inherits B { }; -class D inherits B { }; - -class Main inherits IO { - main(): IO { out_string("Hello World!")}; - - test(a: A, b: B): Integrer { 4 }; +-- Missing type + +class A { }; +class B inherits A { }; +class C inherits B { }; +class D inherits B { }; + +class Main inherits IO { + main(): IO { out_string("Hello World!")}; + + test(a: A, b: B): Integrer { 4 }; }; \ No newline at end of file diff --git a/tests/semantic/new1.cl b/tests/semantic/new1.cl index d007fc03d..40a8b9e9b 100644 --- a/tests/semantic/new1.cl +++ b/tests/semantic/new1.cl @@ -1,31 +1,31 @@ --- Missing type - -class A { }; -class B inherits A { }; -class C inherits B { }; -class D inherits B { }; -class E inherits B { }; -class F inherits A { }; - -class Main inherits IO { - main(): IO { out_string("Hello World!")}; - - test: F <- { - new A; - { - new Ball; - { - new C; - { - new D; - { - new E; - { - new F; - }; - }; - }; - }; - }; - }; +-- Missing type + +class A { }; +class B inherits A { }; +class C inherits B { }; +class D inherits B { }; +class E inherits B { }; +class F inherits A { }; + +class Main inherits IO { + main(): IO { out_string("Hello World!")}; + + test: F <- { + new A; + { + new Ball; + { + new C; + { + new D; + { + new E; + { + new F; + }; + }; + }; + }; + }; + }; }; \ No newline at end of file diff --git a/tests/semantic/self1.cl b/tests/semantic/self1.cl index 3387fd263..399f6ff06 100644 --- a/tests/semantic/self1.cl +++ b/tests/semantic/self1.cl @@ -1,11 +1,11 @@ -(* -But it is an error to assign to self or to bind self in a let, a -case, or as a formal parameter. It is also illegal to have attributes named self. -*) - - -class Main inherits IO { - main(): IO { out_string("Hello World!")}; - - test(a: Main): IO { self <- a }; -}; +(* +But it is an error to assign to self or to bind self in a let, a +case, or as a formal parameter. It is also illegal to have attributes named self. +*) + + +class Main inherits IO { + main(): IO { out_string("Hello World!")}; + + test(a: Main): IO { self <- a }; +}; diff --git a/tests/semantic/self1_error.txt b/tests/semantic/self1_error.txt index 6beb3cda2..f9f51b9b3 100644 --- a/tests/semantic/self1_error.txt +++ b/tests/semantic/self1_error.txt @@ -1 +1 @@ -(10, 30) - SemanticError: Cannot assign to 'self'. +(10, 30) - SemanticError: Cannot assign to 'self'. diff --git a/tests/semantic/self2.cl b/tests/semantic/self2.cl index 2e6921a92..6ef75e368 100644 --- a/tests/semantic/self2.cl +++ b/tests/semantic/self2.cl @@ -1,10 +1,10 @@ -(* -But it is an error to assign to self or to bind self in a let, a -case, or as a formal parameter. It is also illegal to have attributes named self. -*) - -class Main inherits IO { - main(): IO { out_string("Hello World!")}; - - test(): IO { let self: Main <- new Main in self }; -}; +(* +But it is an error to assign to self or to bind self in a let, a +case, or as a formal parameter. It is also illegal to have attributes named self. +*) + +class Main inherits IO { + main(): IO { out_string("Hello World!")}; + + test(): IO { let self: Main <- new Main in self }; +}; diff --git a/tests/semantic/self2_error.txt b/tests/semantic/self2_error.txt index 20c883c91..2e53bb210 100644 --- a/tests/semantic/self2_error.txt +++ b/tests/semantic/self2_error.txt @@ -1 +1 @@ -(9, 22) - SemanticError: 'self' cannot be bound in a 'let' expression. +(9, 22) - SemanticError: 'self' cannot be bound in a 'let' expression. diff --git a/tests/semantic/self3.cl b/tests/semantic/self3.cl index 81709b4b5..d314798a9 100644 --- a/tests/semantic/self3.cl +++ b/tests/semantic/self3.cl @@ -1,10 +1,10 @@ -(* -But it is an error to assign to self or to bind self in a let, a -case, or as a formal parameter. It is also illegal to have attributes named self. -*) - -class Main inherits IO { - main(): IO { out_string("Hello World!")}; - - test(self: IO): IO { self }; -}; +(* +But it is an error to assign to self or to bind self in a let, a +case, or as a formal parameter. It is also illegal to have attributes named self. +*) + +class Main inherits IO { + main(): IO { out_string("Hello World!")}; + + test(self: IO): IO { self }; +}; diff --git a/tests/semantic/self3_error.txt b/tests/semantic/self3_error.txt index 0ae382007..0015bbe0a 100644 --- a/tests/semantic/self3_error.txt +++ b/tests/semantic/self3_error.txt @@ -1 +1 @@ -(9, 10) - SemanticError: 'self' cannot be the name of a formal parameter. +(9, 10) - SemanticError: 'self' cannot be the name of a formal parameter. diff --git a/tests/semantic/self4.cl b/tests/semantic/self4.cl index 7c2b960cb..9185c8760 100644 --- a/tests/semantic/self4.cl +++ b/tests/semantic/self4.cl @@ -1,10 +1,10 @@ -(* -But it is an error to assign to self or to bind self in a let, a -case, or as a formal parameter. It is also illegal to have attributes named self. -*) - -class Main inherits IO { - main(): IO { out_string("Hello World!")}; - - self: IO <- self; +(* +But it is an error to assign to self or to bind self in a let, a +case, or as a formal parameter. It is also illegal to have attributes named self. +*) + +class Main inherits IO { + main(): IO { out_string("Hello World!")}; + + self: IO <- self; }; \ No newline at end of file diff --git a/tests/semantic/self4_error.txt b/tests/semantic/self4_error.txt index c19ca400f..bf8740604 100644 --- a/tests/semantic/self4_error.txt +++ b/tests/semantic/self4_error.txt @@ -1 +1 @@ -(9, 5) - SemanticError: 'self' cannot be the name of an attribute. +(9, 5) - SemanticError: 'self' cannot be the name of an attribute. diff --git a/tests/semantic_test.py b/tests/semantic_test.py index cac9cd78b..46f07439d 100644 --- a/tests/semantic_test.py +++ b/tests/semantic_test.py @@ -1,14 +1,14 @@ -import pytest -import os -from utils import compare_errors, first_error_only_line - -tests_dir = __file__.rpartition('/')[0] + '/semantic/' -tests = [(file) for file in os.listdir(tests_dir) if file.endswith('.cl')] - -@pytest.mark.semantic -@pytest.mark.error -@pytest.mark.run(order=3) -@pytest.mark.parametrize("cool_file", tests) -def test_semantic_errors(compiler_path, cool_file): - compare_errors(compiler_path, tests_dir + cool_file, tests_dir + cool_file[:-3] + '_error.txt', \ +import pytest +import os +from utils import compare_errors, first_error_only_line + +tests_dir = __file__.rpartition('/')[0] + '/semantic/' +tests = [(file) for file in os.listdir(tests_dir) if file.endswith('.cl')] + +@pytest.mark.semantic +@pytest.mark.error +@pytest.mark.run(order=3) +@pytest.mark.parametrize("cool_file", tests) +def test_semantic_errors(compiler_path, cool_file): + compare_errors(compiler_path, tests_dir + cool_file, tests_dir + cool_file[:-3] + '_error.txt', \ cmp=first_error_only_line) \ No newline at end of file diff --git a/tests/utils/utils.py b/tests/utils/utils.py index 961cf7cbc..f98d19dd0 100644 --- a/tests/utils/utils.py +++ b/tests/utils/utils.py @@ -1,91 +1,91 @@ -import subprocess -import re - - -COMPILER_TIMEOUT = 'El compilador tarda mucho en responder.' -SPIM_TIMEOUT = 'El spim tarda mucho en responder.' -TEST_MUST_FAIL = 'El test %s debe fallar al compilar' -TEST_MUST_COMPILE = 'El test %s debe compilar' -BAD_ERROR_FORMAT = '''El error no esta en formato: (,) - : - o no se encuentra en la 3ra linea\n\n%s''' -UNEXPECTED_ERROR = 'Se esperaba un %s en (%d, %d). Su error fue un %s en (%d, %d)' -UNEXPECTED_OUTPUT = 'La salida de %s no es la esperada:\n%s\nEsperada:\n%s' - -ERROR_FORMAT = r'^\s*\(\s*(\d+)\s*,\s*(\d+)\s*\)\s*-\s*(\w+)\s*:(.*)$' - -def parse_error(error: str): - merror = re.fullmatch(ERROR_FORMAT, error) - assert merror, BAD_ERROR_FORMAT % error - - return (t(x) for t, x in zip([int, int, str, str], merror.groups())) - - -def first_error(compiler_output: list, errors: list): - line, column, error_type, _ = parse_error(errors[0]) - - oline, ocolumn, oerror_type, _ = parse_error(compiler_output[0]) - - assert line == oline and column == ocolumn and error_type == oerror_type,\ - UNEXPECTED_ERROR % (error_type, line, column, oerror_type, oline, ocolumn) - -def first_error_only_line(compiler_output: list, errors: list): - line, column, error_type, _ = parse_error(errors[0]) - - oline, ocolumn, oerror_type, _ = parse_error(compiler_output[0]) - - assert line == oline and error_type == oerror_type,\ - UNEXPECTED_ERROR % (error_type, line, column, oerror_type, oline, ocolumn) - - -def get_file_name(path: str): - try: - return path[path.rindex('/') + 1:] - except ValueError: - return path - -def compare_errors(compiler_path: str, cool_file_path: str, error_file_path: str, cmp=first_error, timeout=100): - try: - sp = subprocess.run(['bash', compiler_path, cool_file_path], capture_output=True, timeout=timeout) - return_code, output = sp.returncode, sp.stdout.decode() - except subprocess.TimeoutExpired: - assert False, COMPILER_TIMEOUT - - assert return_code == 1, TEST_MUST_FAIL % get_file_name(cool_file_path) - - fd = open(error_file_path, 'r') - errors = fd.read().split('\n') - fd.close() - - # checking the errors of compiler - compiler_output = output.split('\n') - cmp(compiler_output[2:], errors) - -SPIM_HEADER = r'''^SPIM Version .+ of .+ -Copyright .+\, James R\. Larus\. -All Rights Reserved\. -See the file README for a full copyright notice\. -(?:Loaded: .+\n)*''' -def compare_outputs(compiler_path: str, cool_file_path: str, input_file_path: str, output_file_path: str, timeout=100): - try: - sp = subprocess.run(['bash', compiler_path, cool_file_path], capture_output=True, timeout=timeout) - assert sp.returncode == 0, TEST_MUST_COMPILE % get_file_name(cool_file_path) - except subprocess.TimeoutExpired: - assert False, COMPILER_TIMEOUT - - spim_file = cool_file_path[:-2] + 'mips' - - try: - fd = open(input_file_path, 'rb') - sp = subprocess.run(['spim', '-file', spim_file], input=fd.read(), capture_output=True, timeout=timeout) - fd.close() - mo = re.match(SPIM_HEADER, sp.stdout.decode()) - if mo: - output = mo.string[mo.end():] - except subprocess.TimeoutExpired: - assert False, SPIM_TIMEOUT - - fd = open(output_file_path, 'r') - eoutput = fd.read() - fd.close() - - assert output == eoutput, UNEXPECTED_OUTPUT % (spim_file, repr(output), repr(eoutput)) +import subprocess +import re + + +COMPILER_TIMEOUT = 'El compilador tarda mucho en responder.' +SPIM_TIMEOUT = 'El spim tarda mucho en responder.' +TEST_MUST_FAIL = 'El test %s debe fallar al compilar' +TEST_MUST_COMPILE = 'El test %s debe compilar' +BAD_ERROR_FORMAT = '''El error no esta en formato: (,) - : + o no se encuentra en la 3ra linea\n\n%s''' +UNEXPECTED_ERROR = 'Se esperaba un %s en (%d, %d). Su error fue un %s en (%d, %d)' +UNEXPECTED_OUTPUT = 'La salida de %s no es la esperada:\n%s\nEsperada:\n%s' + +ERROR_FORMAT = r'^\s*\(\s*(\d+)\s*,\s*(\d+)\s*\)\s*-\s*(\w+)\s*:(.*)$' + +def parse_error(error: str): + merror = re.fullmatch(ERROR_FORMAT, error) + assert merror, BAD_ERROR_FORMAT % error + + return (t(x) for t, x in zip([int, int, str, str], merror.groups())) + + +def first_error(compiler_output: list, errors: list): + line, column, error_type, _ = parse_error(errors[0]) + + oline, ocolumn, oerror_type, _ = parse_error(compiler_output[0]) + + assert line == oline and column == ocolumn and error_type == oerror_type,\ + UNEXPECTED_ERROR % (error_type, line, column, oerror_type, oline, ocolumn) + +def first_error_only_line(compiler_output: list, errors: list): + line, column, error_type, _ = parse_error(errors[0]) + + oline, ocolumn, oerror_type, _ = parse_error(compiler_output[0]) + + assert line == oline and error_type == oerror_type,\ + UNEXPECTED_ERROR % (error_type, line, column, oerror_type, oline, ocolumn) + + +def get_file_name(path: str): + try: + return path[path.rindex('/') + 1:] + except ValueError: + return path + +def compare_errors(compiler_path: str, cool_file_path: str, error_file_path: str, cmp=first_error, timeout=100): + try: + sp = subprocess.run(['bash', compiler_path, cool_file_path], capture_output=True, timeout=timeout) + return_code, output = sp.returncode, sp.stdout.decode() + except subprocess.TimeoutExpired: + assert False, COMPILER_TIMEOUT + + assert return_code == 1, TEST_MUST_FAIL % get_file_name(cool_file_path) + + fd = open(error_file_path, 'r') + errors = fd.read().split('\n') + fd.close() + + # checking the errors of compiler + compiler_output = output.split('\n') + cmp(compiler_output[2:], errors) + +SPIM_HEADER = r'''^SPIM Version .+ of .+ +Copyright .+\, James R\. Larus\. +All Rights Reserved\. +See the file README for a full copyright notice\. +(?:Loaded: .+\n)*''' +def compare_outputs(compiler_path: str, cool_file_path: str, input_file_path: str, output_file_path: str, timeout=100): + try: + sp = subprocess.run(['bash', compiler_path, cool_file_path], capture_output=True, timeout=timeout) + assert sp.returncode == 0, TEST_MUST_COMPILE % get_file_name(cool_file_path) + except subprocess.TimeoutExpired: + assert False, COMPILER_TIMEOUT + + spim_file = cool_file_path[:-2] + 'mips' + + try: + fd = open(input_file_path, 'rb') + sp = subprocess.run(['spim', '-file', spim_file], input=fd.read(), capture_output=True, timeout=timeout) + fd.close() + mo = re.match(SPIM_HEADER, sp.stdout.decode()) + if mo: + output = mo.string[mo.end():] + except subprocess.TimeoutExpired: + assert False, SPIM_TIMEOUT + + fd = open(output_file_path, 'r') + eoutput = fd.read() + fd.close() + + assert output == eoutput, UNEXPECTED_OUTPUT % (spim_file, repr(output), repr(eoutput)) From 1f73e347e5762b6594b4f7109d4d15fa5b23614c Mon Sep 17 00:00:00 2001 From: Luiso Date: Sun, 14 Mar 2021 15:54:11 -0400 Subject: [PATCH 006/143] Basic Semantic Infrastructure --- src/cool_cmp/semantic/errors.py | 20 +- src/cool_cmp/semantic/implementations.py | 236 ++++++++++++++++++ src/cool_cmp/semantic/interface.py | 118 +++++++++ src/cool_cmp/semantic/types.py | 45 ++++ .../semantic/visitors/type_collector.py | 24 ++ src/cool_cmp/shared/ast/cool.py | 76 +++--- src/cool_cmp/shared/errors.py | 2 +- src/cool_cmp/shared/visitor.py | 80 ++++++ 8 files changed, 560 insertions(+), 41 deletions(-) create mode 100644 src/cool_cmp/semantic/implementations.py create mode 100644 src/cool_cmp/semantic/types.py create mode 100644 src/cool_cmp/semantic/visitors/type_collector.py create mode 100644 src/cool_cmp/shared/visitor.py diff --git a/src/cool_cmp/semantic/errors.py b/src/cool_cmp/semantic/errors.py index d9c36c46f..4f70938e6 100644 --- a/src/cool_cmp/semantic/errors.py +++ b/src/cool_cmp/semantic/errors.py @@ -2,4 +2,22 @@ Semantic errors """ -from cool_cmp.shared.errors import CoolError \ No newline at end of file +from cool_cmp.shared.errors import CoolError + +VARIABLE_ALREADY_DEFINED = lambda variable_name: f"Variable {variable_name} already defined" +VARIABLE_NOT_DEFINED = lambda variable_name: f"Variable {variable_name} is not defined" +METHOD_ALREADY_DEFINED = lambda method_name, param_count, type_name: f"Method {method_name} with {param_count} params already defined in {type_name}" +METHOD_REDEFINITION_INVALID = lambda method_name, old_signature: f"Method {method_name} already defined with {old_signature} in a parent class" +METHOD_NOT_DEFINED = lambda method_name, params_count, type_name: f"Method {method_name} with {params_count} params is not defined in {type_name}" +ATTRIBUTE_ALREADY_DEFINED = lambda attribute_name, type_name: f"Attribute {attribute_name} already defined in {type_name}" +ATTRIBUTE_NOT_DEFINED = lambda attr_name, type_name: f"Attribute {attr_name} not defined in {type_name}" +TYPE_NOT_DEFINED = lambda type_name: f"Type {type_name} not defined" +TYPE_ALREADY_DEFINED = lambda type_name: f"Type {type_name} already defined" +TYPE_NOT_INHERITABLE = lambda type_name: f"Can't inherit from {type_name}" + +class SemanticError(CoolError): + """ + Base Semantic Error + """ + pass + diff --git a/src/cool_cmp/semantic/implementations.py b/src/cool_cmp/semantic/implementations.py new file mode 100644 index 000000000..44dd7bb4a --- /dev/null +++ b/src/cool_cmp/semantic/implementations.py @@ -0,0 +1,236 @@ +from cool_cmp.semantic.interface import IContext, IVariableInfo, IAttributeInfo, IMethodInfo, IType, IScope +from cool_cmp.semantic.errors import SemanticError, VARIABLE_ALREADY_DEFINED, ATTRIBUTE_ALREADY_DEFINED, \ + METHOD_ALREADY_DEFINED, METHOD_REDEFINITION_INVALID, METHOD_NOT_DEFINED, ATTRIBUTE_NOT_DEFINED, \ + TYPE_NOT_DEFINED, TYPE_ALREADY_DEFINED, VARIABLE_NOT_DEFINED, TYPE_NOT_INHERITABLE +from typing import List + +class VariableInfo(IVariableInfo): + """ + Base VariableInfo + """ + + def __init__(self, name:str, variable_type:IType): + self._name = name + self._type = variable_type + + @property + def name(self)->str: + return self._name + + @property + def type(self)->IType: + return self._type + +class AttributeInfo(IAttributeInfo): + """ + Base AttributeInfo + """ + + def __init__(self, name:str, variable_type:IType): + self._name = name + self._type = variable_type + + @property + def name(self)->str: + return self._name + + @property + def type(self)->IType: + return self._type + +class MethodInfo(IMethodInfo): + """ + Base MethodInfo + """ + + def __init__(self, name:str, parameters:List[IVariableInfo], return_type:IType): + self._name = name + self._parameters = parameters + self._return_type = return_type + + @property + def name(self)->str: + return self._name + + @property + def parameters(self)->List[IVariableInfo]: + return self._parameters + + @property + def return_type(self)->IType: + return self._return_type + +class CoolType(IType): + """ + Base Type + """ + + ATTRIBUTE_INFO_TYPE = AttributeInfo + METHOD_INFO_TYPE = MethodInfo + + def __init__(self, name:str, father:IType, inheritable:bool=True): + self._name = name + self._father = father + self._inheritable = inheritable + self._attributes = [] + self._methods = [] + + + @property + def name(self)->str: + return self._name + + @property + def father(self)->IType: + return self._father + + @property + def is_inheritable(self)->bool: + return self._inheritable + + def add_attribute(self, name:str, attr_type:IType)->IAttributeInfo: + if self.is_attribute_defined(name): + raise SemanticError(ATTRIBUTE_ALREADY_DEFINED(name, self.name)) + attribute = self.ATTRIBUTE_INFO_TYPE(name, attr_type) + self._attributes.append(attribute) + return attribute + + def is_attribute_defined(self, name:str)->bool: + try: + attr = self.get_attribute(name) + return bool(attr) + except SemanticError: + return False + + def add_method(self, name:str, params:List[IVariableInfo], return_type:IType)->IMethodInfo: + try: + # Checking if a local method already exist with the given signature + local_method = next(x for x in self._methods if x.name == name and len(x.parameters) == len(params)) + raise SemanticError(METHOD_ALREADY_DEFINED(name, len(params), self.name)) + except StopIteration: + # Checking if the method already exist in parents types and can be redefined + if self.father: + try: + parent_method = self.father.get_method(name, len(params)) + if not all([parent_method.return_type == return_type, *[x.type == y.type for x,y in zip(params, parent_method.parameters)]]): + # The params types or the return type of the new and the old method doesn't match + raise SemanticError(METHOD_REDEFINITION_INVALID(name, parent_method.parameters + [parent_method.return_type])) + except SemanticError: + # No method exist + pass + + # The method can be defined in this type + method = self.METHOD_INFO_TYPE(name, params, return_type) + self._methods.append(method) + return method + + def is_method_defined(self, name:str, params_count:int)->bool: + try: + method = self.get_method(name, params_count) + return bool(method) + except SemanticError: + return False + + def get_method(self, name:str, params_count:int)->IMethodInfo: + try: + method = next(x for x in self.methods if x.name == name and len(x.parameters) == len(params)) + return method + except StopIteration: + raise SemanticError(METHOD_NOT_DEFINED(name, params_count, self.name)) + + def get_attribute(self, name:str)->IAttributeInfo: + try: + attr = next(x for x in self.attributes if x.name == name) + except StopIteration: + raise SemanticError(ATTRIBUTE_NOT_DEFINED(name, self.name)) + + @property + def methods(self)->List[IMethodInfo]: + father_methods = [] + if self.father: + father_methods = self.father.methods + return self._methods + father_methods + + @property + def attributes(self)->List[IAttributeInfo]: + father_attrs = [] + if self.father: + father_attrs = self.father.attributes + return self._attributes + father_attrs + +class Context(IContext): + """ + Base Context + """ + + TYPE_TYPE = CoolType + + def __init__(self, basic_types:List[IType]=[]): + self._types = basic_types + + def add_type(self, name:str, father:IType)->IType: + if not father.is_inheritable: + raise SemanticError(TYPE_NOT_INHERITABLE(father.name)) + try: + typex = self.get_type(name) + except SemanticError: + typex = self.TYPE_TYPE(name, father) + self._types.append(typex) + return typex + raise SemanticError(TYPE_ALREADY_DEFINED(name)) + + def get_type(self, name:str)->IType: + try: + typex = next(x for x in self._types if x.name == name) + return typex + except StopIteration: + raise SemanticError(TYPE_NOT_DEFINED(name)) + +class Scope(IScope): + """ + Base Scope + """ + + # Implementation type for IVariableInfo used by this scope + VARIABLE_INFO_TYPE = VariableInfo + + def __init__(self, father=None, children:List[IScope]=[], locals:List[IVariableInfo]=[]): + self._locals = locals + self._father = father + self.children = children + self.index = 0 if father is None else len(father) + + def __len__(self): + return len(self._locals) + + @property + def father(self)->IScope: + return self._father + + def find_variable(self, name: str, index=None)->IVariableInfo: + index = index if not index is None else len(self.local_variables) + for var in self.local_variables[:index]: + if var.name == name: + return var + if self.father: + return self.father.find_variable(name, self.index) + raise SemanticError(VARIABLE_NOT_DEFINED(name)) + + @property + def local_variables(self)->List[IVariableInfo]: + return self._locals + + def is_local_variable_defined(self, name: str)->bool: + return len([v for v in self.local_variables if v.name == name]) == 1 + + def add_variable(self, name: str, variable_type: IType)->IVariableInfo: + if self.is_local_variable_defined(name): + raise SemanticError(VARIABLE_ALREADY_DEFINED(name)) + variable = self.VARIABLE_INFO_TYPE(name, variable_type) + self._locals.append(variable) + return variable + + def create_child(self)->IScope: + child = type(self)(self) # In case of been inherited the Scope class the child created will have the same type of its father + self.children.append(child) + return child \ No newline at end of file diff --git a/src/cool_cmp/semantic/interface.py b/src/cool_cmp/semantic/interface.py index 7bc31f869..909a9cb33 100644 --- a/src/cool_cmp/semantic/interface.py +++ b/src/cool_cmp/semantic/interface.py @@ -1,5 +1,6 @@ from cool_cmp.shared.ast import BaseAST from cool_cmp.shared import ICoolService +from typing import List class ISemantic(ICoolService): """ @@ -9,3 +10,120 @@ class ISemantic(ICoolService): def __call__(self, ast:BaseAST) -> BaseAST: raise NotImplementedError() + +class IType: + """ + Type interface to implement + """ + + @property + def name(self)->str: + raise NotImplementedError() + + @property + def father(self)->'IType': + raise NotImplementedError() + + @property + def is_inheritable(self)->bool: + raise NotImplementedError() + + def add_attribute(self, name:str, attr_type:'IType')->'IAttributeInfo': + raise NotImplementedError() + + def is_attribute_defined(self, name:str)->bool: + raise NotImplementedError() + + def add_method(self, name:str, params:List['IVariableInfo'], return_type:'IType')->'IMethodInfo': + raise NotImplementedError() + + def is_method_defined(self, name:str, params_count:int)->bool: + raise NotImplementedError() + + def get_method(self, name:str, params_count:int)->'IMethodInfo': + raise NotImplementedError() + + def get_attribute(self, name:str)->'IAttributeInfo': + raise NotImplementedError() + + @property + def methods(self)->List['IMethodInfo']: + raise NotImplementedError() + + @property + def attributes(self)->List['IAttributeInfo']: + raise NotImplementedError() + + +class IVariableInfo: + """ + Variable info interface to implement + """ + + @property + def name(self)->str: + raise NotImplementedError() + + @property + def type(self)->IType: + raise NotImplementedError() + +class IMethodInfo: + """ + Method info interface to implement + """ + + @property + def name(self)->str: + raise NotImplementedError() + + @property + def parameters(self)->List[IVariableInfo]: + raise NotImplementedError() + + @property + def return_type(self)->IType: + raise NotImplementedError() + +class IAttributeInfo(IVariableInfo): + """ + Attribute info interface to implement + """ + pass + +class IContext: + """ + Context interface to implement + """ + + def add_type(self, name:str)->IType: + raise NotImplementedError() + + def get_type(self, name:str)->IType: + raise NotImplementedError() + + +class IScope: + """ + Scope interface to implement + """ + + @property + def father(self)->'IScope': + raise NotImplementedError() + + def find_variable(self, name: str)->IVariableInfo: + raise NotImplementedError() + + @property + def local_variables(self)->List[IVariableInfo]: + raise NotImplementedError() + + def is_local_variable_defined(self, name: str)->bool: + raise NotImplementedError() + + def add_variable(self, name: str, variable_type: IType)->IVariableInfo: + raise NotImplementedError() + + def create_child(self)->'IScope': + raise NotImplementedError() \ No newline at end of file diff --git a/src/cool_cmp/semantic/types.py b/src/cool_cmp/semantic/types.py new file mode 100644 index 000000000..46015522a --- /dev/null +++ b/src/cool_cmp/semantic/types.py @@ -0,0 +1,45 @@ +from cool_cmp.semantic.interface import IContext, IVariableInfo, IAttributeInfo, IMethodInfo, IType, IScope +from cool_cmp.semantic.implementations import CoolType +from typing import List + +class ObjectType(CoolType): + """ + Cool Object Type + """ + + def __init__(self): + super().__init__("Object", None) + +class IntType(CoolType): + """ + Cool Int Type + """ + + def __init__(self, father:ObjectType = ObjectType()): + super().__init__("Int", father, False) + +class BoolType(CoolType): + """ + Cool Bool Type + """ + + def __init__(self, father:ObjectType = ObjectType()): + super().__init__("Bool", father, False) + +class StringType(CoolType): + """ + Cool String Type + """ + + def __init__(self, father:ObjectType = ObjectType()): + super().__init__("String", father, False) + +class IOType(CoolType): + """ + Cool IO Type + """ + + def __init__(self, father:ObjectType = ObjectType()): + super().__init__("IO", father) + + \ No newline at end of file diff --git a/src/cool_cmp/semantic/visitors/type_collector.py b/src/cool_cmp/semantic/visitors/type_collector.py new file mode 100644 index 000000000..09370491f --- /dev/null +++ b/src/cool_cmp/semantic/visitors/type_collector.py @@ -0,0 +1,24 @@ +import cool_cmp.shared.visitor as visitor +from cool_cmp.semantic.interface import IContext +from cool_cmp.shared.errors import ErrorTracker +from cool_cmp.shared.ast.cool import * + +class TypeCollectorVisitor: + """ + Collects the types by saving them in a Context + """ + + def __init__(self, error_tracker:ErrorTracker, context:IContext=None): + self.errors = error_tracker + self.context = context + + @visitor.on('node') + def visit(self, node): + pass + + @visitor.when(ProgramNode) + def visit(self, node:ProgramNode): + pass # TODO + + + diff --git a/src/cool_cmp/shared/ast/cool.py b/src/cool_cmp/shared/ast/cool.py index 61a837d91..6aa141833 100644 --- a/src/cool_cmp/shared/ast/cool.py +++ b/src/cool_cmp/shared/ast/cool.py @@ -2,15 +2,8 @@ Cool AST nodes """ from cool_cmp.shared.ast import Node +from typing import List -class ProgramNode(Node): - def __init__(self, declarations,row=None,column=None): - super().__init__(row,column) - self.declarations = declarations - - def __iter__(self): - for x in self.declarations: - yield from x class DeclarationNode(Node): pass @@ -19,7 +12,7 @@ class ExpressionNode(Node): pass class ClassDeclarationNode(DeclarationNode): - def __init__(self, idx, features, parent=None,row=None,column=None): + def __init__(self, idx:str, features:List[DeclarationNode], parent=None,row=None,column=None): super().__init__(row,column) self.id = idx self.parent = parent if parent else 'Object' @@ -30,8 +23,27 @@ def __iter__(self): for x in self.features: yield from x +class ProgramNode(Node): + def __init__(self, declarations:List[ClassDeclarationNode],row:int=None,column:int=None): + super().__init__(row,column) + self.declarations = declarations + + def __iter__(self): + for x in self.declarations: + yield from x + + +class ParamNode(DeclarationNode): + def __init__(self, idx:str, typex:str, row:int=None,column:int=None): + super().__init__(row,column) + self.id = idx + self.type = typex + + def __iter__(self): + yield self + class FuncDeclarationNode(DeclarationNode): - def __init__(self, idx, params, return_type, body,row=None,column=None): + def __init__(self, idx:int, params:List[ParamNode], return_type:str, body:ExpressionNode, row:int=None,column:int=None): super().__init__(row,column) self.id = idx self.params = params @@ -44,7 +56,7 @@ def __iter__(self): yield from self.body class AttrDeclarationNode(DeclarationNode): - def __init__(self, idx, typex, expr=None,row=None,column=None): + def __init__(self, idx:str, typex:str, expr:ExpressionNode=None,row:int=None,column:int=None): super().__init__(row,column) self.id = idx self.type = typex @@ -55,15 +67,6 @@ def __iter__(self): if self.expr: yield from self.expr -class ParamNode(DeclarationNode): - def __init__(self, idx, typex, row=None,column=None): - super().__init__(row,column) - self.id = idx - self.type = typex - - def __iter__(self): - yield self - class SpecialNode(ExpressionNode): def __init__(self, func,row=None,column=None): super().__init__(row,column) @@ -73,8 +76,8 @@ def __iter__(self): yield self class VarDeclarationNode(ExpressionNode): - def __init__(self, idx, typex, expr,row=None,column=None): - super().__init__(row,column) + def __init__(self, idx:str, typex:str, expr:ExpressionNode, row=None, column=None): + super().__init__(row, column) self.id = idx self.type = typex self.expr = expr @@ -85,7 +88,7 @@ def __iter__(self): yield from self.expr class AssignNode(ExpressionNode): - def __init__(self, idx, expr,row=None,column=None): + def __init__(self, idx:str, expr:ExpressionNode, row=None,column=None): super().__init__(row,column) self.id = idx self.expr = expr @@ -96,7 +99,7 @@ def __iter__(self): yield from self.expr class CallNode(ExpressionNode): - def __init__(self, obj, idx, args,at_type,row=None,column=None): + def __init__(self, obj:ExpressionNode, idx:str, args:List[ExpressionNode], at_type:str, row:int = None, column:int = None): super().__init__(row,column) self.obj = obj self.id = idx @@ -110,7 +113,7 @@ def __iter__(self): yield self class BlockNode(ExpressionNode): - def __init__(self, expr_list,row=None,column=None): + def __init__(self, expr_list:List[ExpressionNode],row=None,column=None): super().__init__(row,column) self.expr_list = expr_list @@ -120,24 +123,19 @@ def __iter__(self): yield from x class ConditionalNode(ExpressionNode): - def __init__(self, condition,then_expr,else_expr,row=None,column=None): + def __init__(self, condition:ExpressionNode, then_expr:ExpressionNode, else_expr:ExpressionNode, row:int=None,column:int=None): super().__init__(row,column) self.condition = condition self.then_expr = then_expr self.else_expr = else_expr - def get_return_type(self,current_type): - else_type = self.else_expr.type - then_type = self.then_expr.type - return else_type.join(then_type,current_type) - def __iter__(self): yield self - for x in [self.condition,self.then_expr,self.else_expr]: + for x in [self.condition, self.then_expr, self.else_expr]: yield from x class CheckNode(ExpressionNode): - def __init__(self, idx, typex, expr,row=None,column=None): + def __init__(self, idx:str, typex:str, expr:ExpressionNode, row:int=None, column:int=None): super().__init__(row,column) self.id = idx self.type = typex @@ -148,7 +146,7 @@ def __iter__(self): yield from self.expr class LetNode(ExpressionNode): - def __init__(self, dec_var_list, expr,row=None,column=None): + def __init__(self, dec_var_list:List[VarDeclarationNode], expr:ExpressionNode, row:int=None,column:int=None): super().__init__(row,column) self.params = dec_var_list self.expr = expr @@ -160,7 +158,7 @@ def __iter__(self): class CaseNode(ExpressionNode): - def __init__(self, expr, check_list,row=None,column=None): + def __init__(self, expr:ExpressionNode, check_list:List[CheckNode], row:int=None,column:int=None): super().__init__(row,column) self.expr = expr self.params = check_list @@ -172,7 +170,7 @@ def __iter__(self): yield from x class WhileNode(ExpressionNode): - def __init__(self, condition, expr,row=None,column=None): + def __init__(self, condition:ExpressionNode, expr:ExpressionNode, row:int=None, column:int=None): super().__init__(row,column) self.condition = condition self.expr = expr @@ -183,7 +181,7 @@ def __iter__(self): yield self.expr class AtomicNode(ExpressionNode): - def __init__(self, lex,row=None,column=None): + def __init__(self, lex:str, row:int=None,column:int=None): super().__init__(row,column) self.lex = lex @@ -191,7 +189,7 @@ def __iter__(self): yield self class UnaryNode(ExpressionNode): - def __init__(self, member,row=None,column=None): + def __init__(self, member:ExpressionNode, row:int=None,column:int=None): super().__init__(row,column) self.member = member @@ -200,7 +198,7 @@ def __iter__(self): yield self.member class BinaryNode(ExpressionNode): - def __init__(self, left, right,row=None,column=None): + def __init__(self, left:ExpressionNode, right:ExpressionNode, row:int=None,column:int=None): super().__init__(row,column) self.left = left self.right = right diff --git a/src/cool_cmp/shared/errors.py b/src/cool_cmp/shared/errors.py index 2358d9ec7..1eb6b3c9a 100644 --- a/src/cool_cmp/shared/errors.py +++ b/src/cool_cmp/shared/errors.py @@ -1,5 +1,5 @@ from typing import List -class CoolError: +class CoolError(Exception): """ Base Cool Error """ diff --git a/src/cool_cmp/shared/visitor.py b/src/cool_cmp/shared/visitor.py new file mode 100644 index 000000000..94946b9df --- /dev/null +++ b/src/cool_cmp/shared/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) From 4fd4530a4f72deba10233c8d60fb281b33a06802 Mon Sep 17 00:00:00 2001 From: Luiso Date: Sun, 14 Mar 2021 16:50:16 -0400 Subject: [PATCH 007/143] Testing integration with 2nd compilation project --- .vscode/settings.json | 3 + src/a.cl | 1 + src/cool2/Informe.pdf | Bin 0 -> 993464 bytes src/cool2/__init__.py | 5 + src/cool2/cmp/__init__.py | 0 src/cool2/cmp/ast.py | 78 ++ src/cool2/cmp/automata.py | 210 ++++ src/cool2/cmp/cil.py | 231 ++++ src/cool2/cmp/evaluation.py | 33 + src/cool2/cmp/languages.py | 228 ++++ src/cool2/cmp/nbpackage.py | 87 ++ src/cool2/cmp/pycompiler.py | 532 +++++++++ src/cool2/cmp/semantic.py | 220 ++++ src/cool2/cmp/tools/__init__.py | 0 src/cool2/cmp/tools/automata.py | 3 + src/cool2/cmp/tools/evaluation.py | 3 + src/cool2/cmp/tools/parsing.py | 16 + src/cool2/cmp/tools/regex.py | 3 + src/cool2/cmp/utils.py | 219 ++++ src/cool2/cmp/visitor.py | 80 ++ src/cool2/cool/__init__.py | 0 src/cool2/cool/ast/ast.py | 267 +++++ src/cool2/cool/error/errors.py | 47 + src/cool2/cool/grammar/comment_grammar.py | 18 + src/cool2/cool/grammar/cool_grammar.py | 141 +++ src/cool2/cool/lexer/comment_lexer.obj | Bin 0 -> 6215 bytes src/cool2/cool/lexer/comment_lexer.py | 22 + src/cool2/cool/lexer/cool_lexer.obj | Bin 0 -> 139064 bytes src/cool2/cool/lexer/cool_lexer.py | 46 + src/cool2/cool/lexer/utils.py | 19 + src/cool2/cool/libs.py | 29 + src/cool2/cool/parser/comment_parser.obj | Bin 0 -> 2591 bytes src/cool2/cool/parser/comment_parser.py | 7 + src/cool2/cool/parser/cool_parser.obj | Bin 0 -> 150856 bytes src/cool2/cool/parser/cool_parser.py | 7 + src/cool2/cool/parser/utils.py | 25 + src/cool2/cool/pipeline.py | 33 + src/cool2/cool/pipes/pipeline.py | 38 + src/cool2/cool/pipes/pipes.py | 297 +++++ src/cool2/cool/pipes/utils.py | 19 + src/cool2/cool/semantic/atomic.py | 50 + src/cool2/cool/semantic/context.py | 31 + src/cool2/cool/semantic/operations.py | 184 +++ src/cool2/cool/semantic/scope.py | 96 ++ src/cool2/cool/semantic/type.py | 388 ++++++ src/cool2/cool/visitors/utils.py | 83 ++ src/cool2/cool/visitors/visitors.py | 1058 +++++++++++++++++ src/cool2/cool_programs/program1.cl | 35 + src/cool2/cool_programs/program1.cl.infer.cl | 65 + src/cool2/main.py | 72 ++ src/cool2/main_st.py | 91 ++ src/cool2/requirements.txt | 5 + src/cool2/test/__init__.py | 0 src/cool2/test/test_class.py | 54 + .../test/test_data/test_SELF_TYPE.meta_test | 37 + src/cool2/test/test_data/test_at.meta_test | 28 + .../test/test_data/test_attr_dep_1.meta_test | 19 + .../test/test_data/test_attr_dep_2.meta_test | 18 + .../test/test_data/test_auto_1.meta_test | 11 + .../test/test_data/test_auto_2.meta_test | 8 + .../test/test_data/test_auto_3.meta_test | 9 + .../test/test_data/test_auto_4.meta_test | 15 + .../test/test_data/test_auto_5.meta_test | 19 + .../test_data/test_auto_conditional.meta_test | 104 ++ .../test_data/test_auto_defaults.meta_test | 11 + .../test_data/test_auto_undecidable.meta_test | 23 + src/cool2/test/test_data/test_case.meta_test | 14 + .../test_data/test_case_no_type.meta_test | 12 + .../test_case_repeated_type.meta_test | 15 + .../test_data/test_case_void_eval.meta_test | 14 + .../test/test_data/test_comments.meta_test | 12 + .../test/test_data/test_conditional.meta_test | 24 + .../test/test_data/test_default.meta_test | 33 + .../test_data/test_entry_point_1.meta_test | 12 + .../test_data/test_entry_point_2.meta_test | 13 + .../test_data/test_entry_point_3.meta_test | 12 + .../test/test_data/test_equality.meta_test | 28 + .../test/test_data/test_escape_line.meta_test | 12 + .../test/test_data/test_in_out.meta_test | 23 + .../test/test_data/test_instances.meta_test | 21 + .../test/test_data/test_isvoid.meta_test | 15 + src/cool2/test/test_data/test_let.meta_test | 19 + src/cool2/test/test_data/test_other.meta_test | 21 + .../test/test_data/test_recursive.meta_test | 11 + .../test/test_data/test_substr_1.meta_test | 11 + .../test/test_data/test_substr_2.meta_test | 11 + .../test/test_data/test_substr_3.meta_test | 11 + .../test_data/test_void_dispatch.meta_test | 12 + src/cool2/test/test_data/test_while.meta_test | 13 + .../test_data/test_zero_division.meta_test | 10 + .../test_reconstruction/test_SELF_TYPE.cl | 73 ++ src/cool2/test/test_reconstruction/test_at.cl | 46 + .../test_reconstruction/test_attr_dep_1.cl | 19 + .../test_reconstruction/test_attr_dep_2.cl | 19 + .../test/test_reconstruction/test_auto_1.cl | 23 + .../test/test_reconstruction/test_auto_2.cl | 16 + .../test/test_reconstruction/test_auto_3.cl | 16 + .../test/test_reconstruction/test_auto_4.cl | 41 + .../test/test_reconstruction/test_auto_5.cl | 41 + .../test_auto_conditional.cl | 172 +++ .../test_reconstruction/test_auto_defaults.cl | 14 + .../test_auto_undecidable.cl | 33 + .../test/test_reconstruction/test_case.cl | 26 + .../test_reconstruction/test_case_no_type.cl | 24 + .../test_case_repeated_type.cl | 34 + .../test_case_void_eval.cl | 30 + .../test/test_reconstruction/test_comments.cl | 9 + .../test_reconstruction/test_conditional.cl | 93 ++ .../test/test_reconstruction/test_default.cl | 45 + .../test_reconstruction/test_entry_point_1.cl | 11 + .../test_reconstruction/test_entry_point_2.cl | 16 + .../test_reconstruction/test_entry_point_3.cl | 11 + .../test/test_reconstruction/test_equality.cl | 33 + .../test_reconstruction/test_escape_line.cl | 11 + .../test_reconstruction/test_instances.cl | 35 + .../test/test_reconstruction/test_isvoid.cl | 20 + .../test/test_reconstruction/test_let.cl | 50 + .../test/test_reconstruction/test_other.cl | 28 + .../test_reconstruction/test_recursive.cl | 30 + .../test/test_reconstruction/test_substr_1.cl | 31 + .../test/test_reconstruction/test_substr_2.cl | 17 + .../test/test_reconstruction/test_substr_3.cl | 17 + .../test_reconstruction/test_void_dispatch.cl | 12 + .../test/test_reconstruction/test_while.cl | 20 + .../test_reconstruction/test_zero_division.cl | 11 + src/cool2/test/test_run/01_program.cl | 34 + src/cool2/test/test_run/02_program.cl | 24 + src/cool2/test/test_run/03_program.cl | 22 + src/cool2/test/test_run/04_program.cl | 47 + src/cool2/test/test_run/05_program.cl | 23 + src/cool2/test/tests.py | 35 + src/main.py | 6 +- 132 files changed, 7177 insertions(+), 1 deletion(-) create mode 100644 .vscode/settings.json create mode 100644 src/a.cl create mode 100644 src/cool2/Informe.pdf create mode 100644 src/cool2/__init__.py create mode 100644 src/cool2/cmp/__init__.py create mode 100644 src/cool2/cmp/ast.py create mode 100644 src/cool2/cmp/automata.py create mode 100644 src/cool2/cmp/cil.py create mode 100644 src/cool2/cmp/evaluation.py create mode 100644 src/cool2/cmp/languages.py create mode 100644 src/cool2/cmp/nbpackage.py create mode 100644 src/cool2/cmp/pycompiler.py create mode 100644 src/cool2/cmp/semantic.py create mode 100644 src/cool2/cmp/tools/__init__.py create mode 100644 src/cool2/cmp/tools/automata.py create mode 100644 src/cool2/cmp/tools/evaluation.py create mode 100644 src/cool2/cmp/tools/parsing.py create mode 100644 src/cool2/cmp/tools/regex.py create mode 100644 src/cool2/cmp/utils.py create mode 100644 src/cool2/cmp/visitor.py create mode 100644 src/cool2/cool/__init__.py create mode 100644 src/cool2/cool/ast/ast.py create mode 100644 src/cool2/cool/error/errors.py create mode 100644 src/cool2/cool/grammar/comment_grammar.py create mode 100644 src/cool2/cool/grammar/cool_grammar.py create mode 100644 src/cool2/cool/lexer/comment_lexer.obj create mode 100644 src/cool2/cool/lexer/comment_lexer.py create mode 100644 src/cool2/cool/lexer/cool_lexer.obj create mode 100644 src/cool2/cool/lexer/cool_lexer.py create mode 100644 src/cool2/cool/lexer/utils.py create mode 100644 src/cool2/cool/libs.py create mode 100644 src/cool2/cool/parser/comment_parser.obj create mode 100644 src/cool2/cool/parser/comment_parser.py create mode 100644 src/cool2/cool/parser/cool_parser.obj create mode 100644 src/cool2/cool/parser/cool_parser.py create mode 100644 src/cool2/cool/parser/utils.py create mode 100644 src/cool2/cool/pipeline.py create mode 100644 src/cool2/cool/pipes/pipeline.py create mode 100644 src/cool2/cool/pipes/pipes.py create mode 100644 src/cool2/cool/pipes/utils.py create mode 100644 src/cool2/cool/semantic/atomic.py create mode 100644 src/cool2/cool/semantic/context.py create mode 100644 src/cool2/cool/semantic/operations.py create mode 100644 src/cool2/cool/semantic/scope.py create mode 100644 src/cool2/cool/semantic/type.py create mode 100644 src/cool2/cool/visitors/utils.py create mode 100644 src/cool2/cool/visitors/visitors.py create mode 100644 src/cool2/cool_programs/program1.cl create mode 100644 src/cool2/cool_programs/program1.cl.infer.cl create mode 100644 src/cool2/main.py create mode 100644 src/cool2/main_st.py create mode 100644 src/cool2/requirements.txt create mode 100644 src/cool2/test/__init__.py create mode 100644 src/cool2/test/test_class.py create mode 100644 src/cool2/test/test_data/test_SELF_TYPE.meta_test create mode 100644 src/cool2/test/test_data/test_at.meta_test create mode 100644 src/cool2/test/test_data/test_attr_dep_1.meta_test create mode 100644 src/cool2/test/test_data/test_attr_dep_2.meta_test create mode 100644 src/cool2/test/test_data/test_auto_1.meta_test create mode 100644 src/cool2/test/test_data/test_auto_2.meta_test create mode 100644 src/cool2/test/test_data/test_auto_3.meta_test create mode 100644 src/cool2/test/test_data/test_auto_4.meta_test create mode 100644 src/cool2/test/test_data/test_auto_5.meta_test create mode 100644 src/cool2/test/test_data/test_auto_conditional.meta_test create mode 100644 src/cool2/test/test_data/test_auto_defaults.meta_test create mode 100644 src/cool2/test/test_data/test_auto_undecidable.meta_test create mode 100644 src/cool2/test/test_data/test_case.meta_test create mode 100644 src/cool2/test/test_data/test_case_no_type.meta_test create mode 100644 src/cool2/test/test_data/test_case_repeated_type.meta_test create mode 100644 src/cool2/test/test_data/test_case_void_eval.meta_test create mode 100644 src/cool2/test/test_data/test_comments.meta_test create mode 100644 src/cool2/test/test_data/test_conditional.meta_test create mode 100644 src/cool2/test/test_data/test_default.meta_test create mode 100644 src/cool2/test/test_data/test_entry_point_1.meta_test create mode 100644 src/cool2/test/test_data/test_entry_point_2.meta_test create mode 100644 src/cool2/test/test_data/test_entry_point_3.meta_test create mode 100644 src/cool2/test/test_data/test_equality.meta_test create mode 100644 src/cool2/test/test_data/test_escape_line.meta_test create mode 100644 src/cool2/test/test_data/test_in_out.meta_test create mode 100644 src/cool2/test/test_data/test_instances.meta_test create mode 100644 src/cool2/test/test_data/test_isvoid.meta_test create mode 100644 src/cool2/test/test_data/test_let.meta_test create mode 100644 src/cool2/test/test_data/test_other.meta_test create mode 100644 src/cool2/test/test_data/test_recursive.meta_test create mode 100644 src/cool2/test/test_data/test_substr_1.meta_test create mode 100644 src/cool2/test/test_data/test_substr_2.meta_test create mode 100644 src/cool2/test/test_data/test_substr_3.meta_test create mode 100644 src/cool2/test/test_data/test_void_dispatch.meta_test create mode 100644 src/cool2/test/test_data/test_while.meta_test create mode 100644 src/cool2/test/test_data/test_zero_division.meta_test create mode 100644 src/cool2/test/test_reconstruction/test_SELF_TYPE.cl create mode 100644 src/cool2/test/test_reconstruction/test_at.cl create mode 100644 src/cool2/test/test_reconstruction/test_attr_dep_1.cl create mode 100644 src/cool2/test/test_reconstruction/test_attr_dep_2.cl create mode 100644 src/cool2/test/test_reconstruction/test_auto_1.cl create mode 100644 src/cool2/test/test_reconstruction/test_auto_2.cl create mode 100644 src/cool2/test/test_reconstruction/test_auto_3.cl create mode 100644 src/cool2/test/test_reconstruction/test_auto_4.cl create mode 100644 src/cool2/test/test_reconstruction/test_auto_5.cl create mode 100644 src/cool2/test/test_reconstruction/test_auto_conditional.cl create mode 100644 src/cool2/test/test_reconstruction/test_auto_defaults.cl create mode 100644 src/cool2/test/test_reconstruction/test_auto_undecidable.cl create mode 100644 src/cool2/test/test_reconstruction/test_case.cl create mode 100644 src/cool2/test/test_reconstruction/test_case_no_type.cl create mode 100644 src/cool2/test/test_reconstruction/test_case_repeated_type.cl create mode 100644 src/cool2/test/test_reconstruction/test_case_void_eval.cl create mode 100644 src/cool2/test/test_reconstruction/test_comments.cl create mode 100644 src/cool2/test/test_reconstruction/test_conditional.cl create mode 100644 src/cool2/test/test_reconstruction/test_default.cl create mode 100644 src/cool2/test/test_reconstruction/test_entry_point_1.cl create mode 100644 src/cool2/test/test_reconstruction/test_entry_point_2.cl create mode 100644 src/cool2/test/test_reconstruction/test_entry_point_3.cl create mode 100644 src/cool2/test/test_reconstruction/test_equality.cl create mode 100644 src/cool2/test/test_reconstruction/test_escape_line.cl create mode 100644 src/cool2/test/test_reconstruction/test_instances.cl create mode 100644 src/cool2/test/test_reconstruction/test_isvoid.cl create mode 100644 src/cool2/test/test_reconstruction/test_let.cl create mode 100644 src/cool2/test/test_reconstruction/test_other.cl create mode 100644 src/cool2/test/test_reconstruction/test_recursive.cl create mode 100644 src/cool2/test/test_reconstruction/test_substr_1.cl create mode 100644 src/cool2/test/test_reconstruction/test_substr_2.cl create mode 100644 src/cool2/test/test_reconstruction/test_substr_3.cl create mode 100644 src/cool2/test/test_reconstruction/test_void_dispatch.cl create mode 100644 src/cool2/test/test_reconstruction/test_while.cl create mode 100644 src/cool2/test/test_reconstruction/test_zero_division.cl create mode 100644 src/cool2/test/test_run/01_program.cl create mode 100644 src/cool2/test/test_run/02_program.cl create mode 100644 src/cool2/test/test_run/03_program.cl create mode 100644 src/cool2/test/test_run/04_program.cl create mode 100644 src/cool2/test/test_run/05_program.cl create mode 100644 src/cool2/test/tests.py diff --git a/.vscode/settings.json b/.vscode/settings.json new file mode 100644 index 000000000..d3c016a79 --- /dev/null +++ b/.vscode/settings.json @@ -0,0 +1,3 @@ +{ + "python.pythonPath": "/home/luiso/anaconda3/bin/python" +} \ No newline at end of file diff --git a/src/a.cl b/src/a.cl new file mode 100644 index 000000000..196991234 --- /dev/null +++ b/src/a.cl @@ -0,0 +1 @@ +class `A {} \ No newline at end of file diff --git a/src/cool2/Informe.pdf b/src/cool2/Informe.pdf new file mode 100644 index 0000000000000000000000000000000000000000..4a97ce6900e649489b4cbeca33d7d1ff4e67f1b7 GIT binary patch literal 993464 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

l+ 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%0DA5Jo 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

Zp-B9!;_r)RgT&Nska9Y8`6zs&D+d;8iM)9 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

lxbLp|R{iTtD{ZI*?DW5ZnzTIYyo-R{)n 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⁣ryB5Ywi_a)RTpBaM*}_)Qq{ zJmrilUsn$QU(PsqauHV?Ri~XzginIBuRzWhv0vvPlKdj*TS0j*C|g1K4^Xxt3XMDX z)MXpukb78y(}fcxJn5VFfG)pZClLj`8+YGA4Eb*G-~DeDm=|2Je|BSkepzXuZ=l9TO5j6Zg?$*g&=GRV2goE-U7`-MgHT*hY{4?X^hdEj+ zN3_hu72cb_s#^)05=Brs|ip)+2eETXn>*IG?C=ytLy_I>s0Qze;-cLAW znxBWZF34FAttLRLV;K3*$S(!h%huwFwFytGYr)@RG0*c>HlNU9E@tA{rssP zqm0Y%0;v!18fV=!`Mv%q^n4oDaum8{VMg#Y=fKmPfu}iRqE*Ge%2^4<<(v(AZey!3 zX7d|sR;~F4 z;|a#O)K8#yYtg$CDt0Qv2s7nXMUgS*zI+#Sd3yZ-R+%66SzuY{1(n7hxpX`)IJ7F@ zsKD`QF3D3-Uv&6S^S@CJ=Sz6fe-j?>OR`&GUFTw5FF_188Y4Lw zJ4>&tlffpxfus>xa;5oGu#WpiBd$9F*zu zOC@}49OVX-v$5VE#|XRv$^n$$R=;d+Cj2z|^-=a?wDaa*M`rK$EO?%QKfi%m`!U)% z(0o5UyicP&PFsZU#17Lh>3`9$>o<&G;NAeA`HwOh`QH;Tb{o$cFYq@?jaQ6c7{4@5 z7^jTi8-L(0j~Z|AS4B;V!c2$hG=2PSQ2rXIIhw!uZx)%w<|Okz=hMFL;omB%Qc$g+ zrXL@r&DSdEUNzBHXsfif+InrXRW2`?;&*$a#Oc-4dls~_F;535KLLK(It44^yEjm`N1`ez zakBnyi5>VYPWc^DegUKXkSWc;Uxbb0G>bgeziR5&PMXOuQD`Ix*WF~l+H%d(LPU*g zIW)R=Yh$$cX%n>~tynA3?$47HtVmeVFdi{d$%j zr1==3{}av0JM?$zqxG5m?;Yv$^v~+^^>V#Je?+g+zo36fU#kBfeTDv*UaNmqU#YLw z*XWPy-_Yyy_4-!*zx2KOb9$@3Pd}vpL~qky(qGnprnftvc8zk~&EIKvjdk7QDsYW= zO>`Bz?&I$ly5_qca(#~9Yo`B}(?B?NpB~oZq{~P>UmvGW(2J;?tWVWv=(7QHNuvt7 zSEX0$HKfZbeJ!=VS+Ca{^d^0m-h%eq^me^dKc#m8&g(t2R<7!OhGDpjfMMTqC5>ET zlrhFA=)Ve$Vxz>EmT}EA<``v0IZ-QbxfT%=8%vC3MlJnKU#pEeVdr)Z)*Go8kOc{ zYlp@sa>S{f;ZmKd1rOV=& z>a_V4_LC;RpD>GYqt*&iS;eKf*zAHd;?$0Espm;8P&&D^E^@9+Q8`8qKAd&!us|EW<9te*PLLVaHr(%jf4lrf8jvYiGD5 ze#Vb7zoQ6pav}2|>W(w{6n&Jrg}sc~06ll|?@*|_2jxVR=amkOm!j@Aa~balpckuh zCr8>!LIaNywA#zKeioYew3ucXnlV;0G(E67uwK*t@%^~JoA1Zb4ij9WX~AU#wFIjP z>J)7w!4`sT1dU9yLc!(1mBBT)0p?j3+!Wjz+#cK!+!@?60GQSqJQzGea4dKna58u% zc!z@NGf-GjCU!X^7E{`N(U+dpZllvXp6KmzMh#ia_Vc*Fz0mov;V<%&0V&`HPV!g2|vFq7dwlmw89nOyP_+m_H zEO`vGN6JD0_1D)a)6&@AU7pnRBFMLT-9bQF{UF z#Cv1*?CiO$TlW0yiu62#pXLs8Px~_OdEB?`s_bf}W!KOizEuJ5*R2EKr0mPQUtvGy zJvhA=I~9=qpl<{B*!cq4D?;@Q*kjZCs)FoQp-T+uJra=K8#DIGTlUEGKB*vkE$xH6 z7xLam`(gI_a5)3&@IF^5(C@PdvNvbf<8MQDQ}(Xx7SPFmWbey9l--uyp52*!D!VKD zJnWd(Fz+8&x3rFUJ!SV~U&_9k-N(KtZp2+!7wL77_KRd&EQkGEJV3rltr4Ct_FJg0 zeBhw9HE>-i|B}`pGRSYn?Rb*sJf0gL)nA|aX}!hA#0%nuarm8hNqky-W_%9&aQG$a zL%b|r9@3=d~-|_Z;UsCMm{UPJH9u5 zAbvOHmJK`ter{Tva?ueg_cO#x4pBBFuzZ}07Po-mmgq85HKg0gdaRd3fL@*KK zJvxz-$V-e)j7^MBOiWBlOi4^n%u19d<|P&+7A6)amL`@bRw5o#{*vn=HdApEVjdNv zK$eoh_Y!Lo>)2niUj1=hIu1+S^ud6145MNXmc{W{VpC!($2*Dbi5=IO z&Z530b|&_K#&KgB(s5!Y42%WSv0o;raUB%brGaCyGz^UC(lOc)v00*3;I^?D$6lH7 z7sXutaaTr+#k!&{@ubIO(AuK)mpGU>GH_ibjwOz#W30r<#F@mo#0B<6X}&~n;tJb! z;(AhJd+PUN%)@Jr?2rE@ok?FZoQwk>ksO)KPmW{ATyMz<$s+a<$;rv7Y@f*)$=QSA z7=q;7|J@WEK8aCu`Dckk>?VMRHYgZE}78`lEG~+?=fE^_FZ%HYIl@ zTax>dhj{Nv1NWDov3Q)g&&jrAd$Kcmie@HLEW*^Z4@Dn&HQ&|NPBn{5_U) zob93i+s(Z>SNMy~Y5U7|f^)baU2|SaSVpxnDmf+yu2>y+rd=q!SDLC$q#@*3q{WECyGlD~taw$Ps zjJu1`?mX0*$GG#?7>8d&!+dCxFF14?2Ffr{rURcYIPQKNl#fHs8<6t`ylx&w}k4#hBA#$b1B2H%8{)Ff#uJ z`Vr8Nh%{Kphf(Xpn2VE`i<96y1kOXC_ki95`u9QqzVNdIBM0(xKta2@w2NLF((482 z#R9b3fEF8o7XmK?UIe@d{1_Q0M#eY>ImgiYJJ9+&(AJx1>rL?N0?#hs=%ax?8V7(M zKwVg!39ECo!rEIwhb5b0$=?P3UEuEq{%+v#PA0sQV-xCb0^SC^4f=0~{@a0f0`ElK zD_Co_TE-~%n0aq$>WqV5XRT>(wr1^iu*gAp?^ zVvYbf0~iggO#^Gw7zKP3e1#i+!wt#j(aL$qc^Yz_2K_^ze+V)m$02KcGU$_m2Y{n3 z*VEjt3w2%SqYHg>nwVJ=ay|k%AAt_fL5Jsn+rVw`4;O!qF+2vD--fhrgA;y!82r3- z0D=hI9z2NUfTfYST zm%tl=H-ZPdkBQyK{4?-BLw*6;Er342oxmN?%>mu4_e0M6QTIXAeGoV-!GtB4>w&Ka zjy{^`qqzn67K~&EMzRBz_DR^rC(#z77YCvj2gcKZ@pQfdeO^Ib%*Zgzh!b_4sB57u zOWJ~uHQ{5;dx768o*!fQ1Sn6S73^OQiJd+G2_FFdLEs+*{t4ipfP~|ydmJP7Dn{&8 z;C}`FSMaX}|7vLXIcWGf(EkPWe?hyjRR?UwsgO8d#?WX3fB?SxX^j zDR7K{g%L2Jrzv`3_D#&b36E-uN8Jy6Kk7b;x{m_Ky0);c%>;N7(Ekh2{|mseTAf&} z!@x5PJQl3Tf)zQIp)bq8kBHHUh|z+#x8Ut9?3EVwN)!8+iT%rwg}Pbb34yUpPbVLp&A_r?ZN3?}n)g8aF^E0ho z>$v5;oeSD)+UvKxy<_#iv6I{XuFhu%y`%HhLGR`~-~VP#yS3K(hV>n5i`8m9Z@p-> zSr@F=tiM=S`PonFre1by7pcu_)Sjh#sI{1$qvugMS|6*AmrJ_{n5a+Er>JYXK1(mv z=jjXdh5BOBWVyalU!$+nH<1q8^&R?7eUIL%A5_<3z!CkJeq3EA^)vc8{es@BU(v4{ zn&C8jMwqa;F~S&WGDnuzg~;?i(yrh=ms0i4%SesiVd-M6K>5Gwy_m6@dYeZ3 z3*NYycJqB1?K<9|vGF#|PLvh=mW|%X(v0Ks4vx^lxf!?t3B4$nqHHqPVg5Dfu#VsQ z!P_gd`TZKB4R5}T2R`0>jwOKq2HtszsJCyZ)c(Lcjsi%y#-%w6bxZJm%|xc?5v!iZ z0kvXyUuHCTwyF1PcJ{BnWm+w-KI1)97SQSo)5^P>R$T#B9k03cN?WKsN~>!Ft*jli zs-DHF;x(0CNv~-=w0eTHZf0V|%*TpZj1`k!C(r4v7=tR3fAj6e!S6S=-13g&;I|x& zTi$fc#oLZtj?oLKEYyqj623G&webeO-8J(@2S1AdFXb69jx(;`4}31}mH>YN_$hEM z2krts5qE!zTGv7U570+~{-2;v0G^M#PT>Cv{Ocltas4;o?7`g{!ErY%x`A>o#qTqX z0Z$kb#-S|7-IG${Zjy2Jwqu`RU?$)DkN2INe=qMl(OPolTOn8eplh6Kf~&|ix&NBV z|CTkb*@LdRuKD~gSL3R3Rnt{Nl!4a@*DBXqy4KUR8CN~Z2L5-a)wRpjGQ_nH^;=wr z)YYc0c2_4|rwF=+xX!zJT$lQ?m6x`Xs--cxx13eMO;H&ONNj>#=Q*n$D~KrU8}Cu?mD_Q63iUp z+T!YTZ%bcPtFixTmTT}xxp%wwl1>Nuufy)6xH>XMB7L23pC(&KU#!!t_UCaMMx4KF_OVgJc{WAA7wr|gJSnhn!O3xa$G0!?43C||B56@P@wxhS*o*kZ@ z&}b*5izKoX&mPQCp{LbzkTK5@9(~U-jPym%aaub??haZvGdw3uJVzyV|@(-Vv?}?_}>(lErHlJp!k!6`tKTSG9MBC(pgamG7NR^iJZNOslTbJD0E` z&vx&8*9>okx5``Xt?{n#uEJWbmdm@=%d2(0ceA&it_E)tt=0xvvEE(Y7PLk!dH0bv zSeM>5l3ed?_jY+4A)IlD=HuC>ptLPaf&i#*;rC&m}u>+se2PLQpR^-Q8Wr%C&>Y^PMt^L6_!`Y!vfc~|*T z?pnX)S?u@lzcSB#^9TJgUmf*xv1cjw&!6MA{dxY;G%BO~V_lQUN|U7VIail|yniD3 znLhs{@)KjcSN&7`)BUsDNq?!Y*+0*}fY-IBmq*LL(7)Kf)aUXq_pc-iCC@=3kbKdpvvm$NeYC&n*&P=Re~=$E|To{tGlBz3k1X ztn>H!ulTPAw16||oeiv_6HHvgBB-zd6>npRyWEEr;x;nFp-D9#! zXf0K^yR)WIoozIW$M*WFu2Ny@4WknXe$LEUP?g4vj-&R%Odr|J%3uGIBv8?rJkf`f)62v+@o>U-)Z2rsr~$vz!WnJPb&ANp5mtv;Ke9Y^b}4{ zV~`W%Qoo0Jj7yBuvpn-`fwbS_ZX-vtj!y9FxZA+3mJ#LX)*<-6PL%EYnedyN6M3h31XGr-A=8^jQc>tF(){G0=~If*H|IVoav0 z788*F6z4EW-=R-8IOEXp|E1_DRy)Y0nG2rbH~1W7CAX{n1$r)mw9~5ZOj!&$XTa0V zrCAL84CveO+_4L-tUxR0Wfo9Q0RJ+kn|+|nMjywe`jAuC$K!@_Eth%^%Cn&4fTtUH z81z<9UI%3c%88&K=F|er|lS{~2u$v>;WS% z6`D^+Fa7`>&horFVU5Nn?yq%`OB$d3=*xbr_otvC^QRzTteVsPkkEi%2zmY5n2`r? zcOuI3u$)fFJg!De#({M%K`+i?lwzR!QI>+UO10PrYwUwnUxtMUCy&?&#>snJh96*_ zc_`O|@+oj8(N+?i9jMg?o~_Ep`0h1WQ7xBxO8L43=vcFu{UTZ8;^o+uY%A2OLUCO%pD?F4$dA3bH6Ef_1C zac3Szbtzh@hi>(dFiWj{mQw}`9Eoy1bhxN|IG3f6-!8r}wFNUZl3wLwT-^u7{7s_=g`yjalvp)%C9%d??&lcu}ikAhYO z2NfJqe~%FyCpbxPW(YV(aN!Q1m*5J)b%79#Xvldhpl<^pw0xA?#5metH)DWytuu8#N1?$z{%>?xX4FpX? zz%GK8JAi#k#vui51nmQ$a|k#!0J{DMaGs!N2)IOWbqMGi0QGkUVS~V>*2fUQ_rd`J zTj9w&gj|AALx4-c7=nU3ghGPi3@FKfG|x2kcP7Ccg0hUZGND}kt-J$RM6hH){bd87 zb_n3Qs|o4|Hmbi{-bSCdgKY$jLqIdZ?jd0B06380yEFZ5+E*$+7Cs#8_b=h2!CU=D z+D0051@+u{4+vjp7)7sW^L{t~_{ zbna958bM0@Ys3n|PewchLD>T$G3DF2zhv)`oM69yj^q)Hmi|V@$~Z*E2jMSSCPQQ* z!6bqy1k(v-5tI_7$5)Lf*Tp`W-osim#+l_T5E*VU_muQJrDSi6 zELFNK&!~Sv`hx#oDtjLKwo=g+XTb6yVBo&IbqLszvA<^Sv1vG&0R#8f^nUrj*(c+{ zTld0|!JB(v#(vlDvj`$<)L)+SO^QzbBf{fIF-C;?6xpfhwX%O;-5!wj6rq?R(ki|v za!{>{^g2lUMMLDk&y{4Xk(Br?)K@-m(ApZfuG0P`y|z+H|08P7k7d-al=T*&xF&K^ z^p4P2N6sl9UaR^*wh`%7^={{X2Kw=#c+xr#<&V=)mH}_q*6sXh-2fQqCo_FzbMU5* z%=CvYRku<3CLROw(GiN_BG&~tw&D5an8wx=jeM3(HfpnNsrbbv8?@ufk5SxVvkf+h zKeNdW?fjcDfjv(7vr{T=AU|hMkUiR_7|W(M?WqLZCbvax*xUxiLw1$&pJ{)|esV(a zX56&urXPhYB?CHED}UKEgdZN@gZpC`<%d}o$76O)5b=(^BBOqb>d!bCFT`2YH+z+$ z6=lHGAz)xESTO{saUB%brJ*4Mng&2RM*H8yX6J*q#$T6$H{-6%7^_0n?a3IE0c(rb zpS^bQy0X{bjInHrQ|)^3MQJ{}LG0RY5_{_RV_RiivHjsM#jtjZ!YNL*4+&(hH@i)I zgx#*}vokn2jv=s5DVQpNwMTx-?oxlx-&})O6Vz|}QpWnDb!A_b^=8xfM9CjUU9$J2 z0ewCvopqRc-{_CE&nmtuk_ z#eh+c0lED$u~Xg)qZIo^X&j=9WW1sj+eNA0{G1T2m2rx$7XB#pF-oypbW0F1Ap8Hc zZ;n!bqm2rfW{Yh{n`PXiG!D_dVrS6~^pp4bARArtKgl^&FuwFWr3);MRHOk8%6R^Bm+fqPbBL^@=YYiM6ydH zuS7CRR4$3E8jOHsj!15ZWQC}F5M+X=91y&>Z>U@kv@02!c<)-}cSvT3^{K$0<+`5-)jKr znf(Q3XMx#MV0IIjeFSC)f!RCY?;5~<0kc!U>=E#H2Vh@-zazlx1u(k+(3_c+|7Oj< zzuMod^EW*J^v$g5H|zP$N`AA3->lyE*X?7)zFDhpR_Ti@v53D$sDzLrZXLG%E`;?D z>!^^{m)5_9ZF!Og3h78!1Z54GB{HR!wM3ST${Z1rx$=CWb>U5I+pv78)e32n`M0C9V#Q3f(8J4~+{wC~gc*2t6Wh z3Ox~eLv#zP+5iMB{2*g8BrF2$~YKBxsX%ZBNjVpbJ4a zf?fpu2nG@iB`6>mLokkD0>PsMQwe4e%qEymu-J#?1gi;(2}%ey5xh^ZonRNiUV{C8 zo}ZlHdf2}n^}~N9gd>f=6C?=vynW(dsSHkpAeW$?@lRbF5HvQt@+ygBnVnVyZ3#LM zbTa8U`6fT72SFc#{=WYpg5dBuBgkse!AQ=G(nDE-{kMsId*j~AZRFFQX|!9HC7d=NoopUx|*ftsYPm; zTB%-B>(oZIMQu|%)gJYcI-m}zBkD`_r7djN&a%UH%&u$Kw;S0_?Ur^MyFH+z-39mE z$n~=O5e&44+6DF)dz^aBo}jkbkJ?l18GzaLe0#CI++J-L+a>lU`+a-6y^HSm+WYNK z?8Ej^`zuE}K_}!yoLr}#)4*x$vJk z9rpsaq1yz|!fh?AL~mGQ?!~s06E#3*g7&R6gKkc}gOzB(w*Se$Km4Cg+Hg+fIWL3m z2-?JL209;ce?+(m{9CXMY)XgY<{|w)c5}u~Z`h+KBs#)Y-V+h$9>OaTW-jMj3cB=7 z@K6N*J3$+p(#=7)Fx>jM5cn>Hp)^bXXkSctpI@Sj@w7L%GWMu1L%9Zn^Fd6jU7&+_ zDiG@h_zXkb>)}%q^c9GEFH)-sPOboU5T+%<3?$qXbW_lIc5C<#0o@WQz#q6dp621{ zrS|2FojbuvYlLLFIqv|XFh&lOk=k0M))Dkvgk=09>YF)?!NwMMSA-dW^w%JLmP~Ix zCo0j4&yGvrl-fil=w_gsfo{&HQ6*aN*>vPT68=j-_vPIA%od{G0euMcL8b#S#4Toh zfPV`5yHyp`@VJiAXL{Cvk2A zUo#H0L+P3T{|)7xfTyd01Mt5TA=@LbgYe&iH1ptNG{N|MiKo30{!!$(1ycGF_(9}; zB|<`n101p`=%%3a+|BU+0`$d5$M`n@Cp|!41o{@F^M4oz1E8Vv!62UYhR-jM+Uu+# zNa0eX&0fq4dsQd4qX$v zF4QS>eW-KjhERU!#!%PLO`&d~?x7x`n?pTAw}g6ydWZUi`iA<2eirH<8XOuDx-(P| zx;s=Dx<52F^gyU6^l)fxXnpA5Sxot2USjA&7D3pDm=AT+Xa<_)S&e*}!!`BkmOixc zfkthMW_%Wng9*+=Z9y%fob!D& zYCeC8e7Ik3Uiv2UV|)@10@njZETqt^bR$9{ZYkPN>9e5sBh9x#?*x4aPtiA({ttZq zf^?`)5swO9jNHx&S+9{RA-CDuDW|Eu>Y(j9?bJba(CJ9?k&ey)db21bSLAk32i>mZ zdbz!vc1}Bv#aE}mgr%pwDE?lKYaID<8tewpdu%d(*HkK0Q=*;fu6n9lRBzQ+6{x3m zH+`EPpa>{eT{)AJRqoVLd@VqJOR@>0jta^<(;%dWxQ^ zr|Bp4Q+kGeR?pFM^*sH8UZR)kW%@e3=YpOG{5;ZJ4*!>cR{*a<`mcd5Mmlc*uLXV+cpdNtPG4At3%-hmqc7Nvjg4)5vHkM}ul*`yeC5|Kfgp%#HZ!{R;amBgdcO zn;@0pc6oQvn1Ymh%yqU}Vy5FxD@+>CjKC?Pf?X>MNH0 z8#6ugk`hregaTA?m|EZMHSHo1vfFpT0~5+w9YWi#3y|P5C^!Gq^Xv zh`EUL5slKJoBsPhDFH?A0eC#LTpnfn}i3eO(+Wua%% zfTUPh$<*`zapP}B>mewh8}=5A-tlp*-+Hgz{tK*GH!|(4iA)!{MrBbkcFr7uF?t;ZkB*mH<+%!oE_jdA zn+4(tBb8LJ`Ra!m;qFt1M@BUm>zf@Bs@BQ{=kKGhXvbULy}T$}o2Hwl(gEFAqss6P z29E|uF0MHW{I5NazIvbA@5C5clF>0^*C^a`M_+H9_mX;*-CJ=0#CuyoKvv6*n89KFa&$!Blx*Pi!Lm)xj9H*La45a*_&!d=ZoqOR}r@3+^5 zS2lOWZm+JM4f6rAZn2n;@<&Czqj}JciVGcYmE#3>_dS6R)&;+qp$i*}Q?-cKVn&Ic z>!`X~LTXyUA)cWo-vrImn7j4|_N2_%dNWX^-m}TA2g`~;-u&A;txEvEzy9hI`8^qS zg!?bvj+@>Xw@y91Sz$M-)WxICBa%=(@$T;39+l+4(~QmZRO_@`S^hnN1{T4br4jgI zL*PXLNE2AQtEt?LcBO8d9q=l&DnFV>CKo=vdAnJyCJ?Z;-SYeMFZ&9G7y5f=0s7G_ zu88C5v7@?wc!bOrob(PhS=f<|)cVi`%Q;;>cSF>xg+Tt} z@zghZiJ1*8<&A?B&tLFbKlF)!a^OXnKVKz4N>J=%1~*~mErHbLB-)3hROpfi0N-=2 zG)h?PB2kgsOU4&|D6Jw`M@Z>VK4&+&sCZ0Cm1pgPbMRCWkiwN!SFZY9@ud9t1R+I% zJ_V^)l&{!Q_2k$-T1N6x_U?{5MdydgI7KBrIiZhlkz0ByB&&2tb=2oSA9m-D7z5GV z-YP@#sW0^s<(j4YGjyO;tOYYxTPw;nj8 zB0jA}Fn+NuX}pHNw!*t$Qhcz{mO{H={_OL6U z;2MY|_a9ADMB39t1vfYD^ZU3nu~rs1i_>REm2|Q9CNf#( z_(3(HN$wE=+P!jjGCe78+80N{7hog&upPD|{17h4LKnm%{M+OF*3^|ylj8KdWwbll z3eeIw40B`5cMSaDV=QT7eV3E-&y(032{=*Nknz?C^^`i%ZIJk4l*x=fQ36H1Kgl8} zYlTPGyvwsEVB&kMZcrakutF{dK8>`N1?@f5WIqi!qZ<%`+_E~tU!mM7u6Bvq;HQYyr!c8lM$p#2=v zARaWW`vRU_QgTVk$F*JZ^~f?t({9*16dVA$`y3sr7s-U95I0GDB)uilxCIT^8q8lB zN}BO%s$Udf%IE5m|HR#kIp!1*)kM{WPoICXE66J)R#n6588C-)W)bsua<0T zuKi z68_-jTg1PTU-q!Zdd~eIvYVLO^VfhlW5pKK`T+gCV`bCkvcYrh^>}pnLHM;9O<(od z&zKT#ZPcwLb#K9$!3l>)J{Pm9a(>nNNN>;W&@Hb{LGeh#GAMT!!C8W*IIqzqM|EG$ zrHpGnPkV~bGP#+Ky!Xb1j&gecpY}<2*$~%R%8fe*kFHsAGd;DWWqI@P^9c7g9?3-l zmtk2x<-wVu{!XVuIK28V-hyQ$0tWm5av z;Q8T$%WLWWg<#A9{>oUA=)bne_Vfw&Pi|Z@Sh{m?VO!v#NWJETS1PJ-1#7dOtskuQ)X(jT-y1ArX9$!F`YEo3}c@2{d#ODM=uEprAH3kL!O0osHNtTb$R4@6N5K(q{D9d`0)fkKr0mEq zcJC+xyg`m&5&7GAX5*m;aep^py&>KL5!c~0!@F)?4}?OSCQ!EeT^wHbqXN72tg8lyYw{9luqO#-F= z^m>^A2L!zd*pIZu-1P59DTmAV?Dt_Mw4BPd_ltrU_jhLeS%)RV!;{6dpPq}SxPuPD z&HsMtojIcSqJ%r1wztd%oS}GCPI47!=>54{iLC;m=XL-JlzAEhgPgHEWIH@}ZLgE6JbBqVP7*dY z{n(Y2P?i;CW97H2F`0l9Jt3m%jSx1!9GzfAL^gK|fHZ%gW&>|5GF&r>S1dc9K3`Quc* z*`2{3!44I;C$aa5Zmb(3hIMPFv~Io|lAnNSTA2E=0(&YS#~gW;DjNYshDlXwQMi9< zC>`7%$7=ktvrwz6lS_-fv?yIodvi)wF7Q4_C&?+(Cr!{HvHZX(w@zOq4PIdP#(Jg{``cY6w2fRjkjHOwx@X@845|uZsT6 zyDldeR!L+HHv*xr9!5GDlEy8??mgEz_J$TuUp{+@VoPZG18Z7O6dc zT+3%La~lCro^7B#TJ{N^q<~Wc?DxmNhx!s&6387AH+t2?vDtIg7gm7eG{}EMz$DO`&^c_6M_XYzn%f=j6z$)L^=XUS z>W>!6s^a-@r~k=e<_1qGl?NFigyTsHpyf)!m67ZtShlei7%?l4j}OB0R_B?7pzb$} zdT*^LG{jc)Oa&aF9W}Tv2yFWu$dp&&x`{%fD9lf~pLHO@;-IBza>%!*u@2eG&h($} zA9nGt3pfOZ2#X5ys+@B?)tMK z-;h|5xDk3`d4UB9_h{5trgsX%0>?Ing|yE#2uUScsIVO(BpN~?)(YF=K>@jnqK9UM ze+*(nbVIyDaBH=MqyG4R4TQicZ2;ez{`udB`tP|aQkuvi# zQ_Fd$K-GA$CsQ5o^Ja^8Woy}-5E!U5fIn{FGsb481qN#KtrhJshZ#%)()oy4vF0_Kn zE2wd+-N-b4Jyn}bz;Rgk!vb^0>0#lrXlq8ACZb^u*&u2tnJrhP7P>-xG`YsbP2Lj` z5jC|YO%$*iiFKbXjdJOY6i}44+DRwIXeZk2{8}@T^8b#~AM#eIIzEitzvVG_I1vq-e5po}LZn@PYcNOR2{6KM3z&5O>c3_OH? zS@HxM7TG3M6Qm*`I-vu9V9S2=duhosokJ&e0Ef)gBPm{s;e|hX(OB`e6;OH|tjLP2 z<$7Qck)-v@vN+@F3yc}V%S*V^$PoKN_;pdRHh%d`?~WLfxa$UqUmit_2F-3D>;~>hd3yfs`^w_R#w9qX&Mp9eX_E_uR4q zJ3i;i{XUJaG-(b#p#8_S#Vw`)I{E66&+f8=-;U8~tjR3IG=?4@5q*wb++S_p7uo*w zo|6ixU1LK>7_HsAOE<2JHFNb#!>5g`UOS7~qIg$#J;nc(L`_ z9~1YwQ;bXJKRb2R5WU#5m9!?=jP2Ox!kj;4Yx%fMvTzIx=xw-0U7PYX)na`VT=MK5 z_o_PEm?5aw9@Y4fC+A)O*B{3!$F9t6G50`LH!CQgm^@Ne`D?71@1;q^C={mt_UD@gKt{b@PVFj_>cmpNiTG0v5!rGehD%M6E$PXyL*6|DfM@5)|93}Fy}s-zMgLLSI5Dv(Ni%UcR7`m z{fE37g)+^8D8-rxKIr-16ekXEo^w2-UQ2y8Z+RAZb5>TawsO+vUHR24EUt3ZE!j`! zb1!x38Zz^9<`4RY8ru52R7g#wW*>&D-&Iaw#AiKSUSG5YdisZ$Y}1OgCMrdu^Rn1i zWol4tuj~7{zV|9mR7;RExjTW@MmzwaOKOQ|T@`BTl|7ce^!ypr+5+BFX;VXjLcCXm zGj=hJx;4DzW94%)!*?AT0?m8wltAmLU#}#Ml7L)Qay7NUu6diQV%IY0@rNP~Hh0e| z^8D%pH^2Cey|Wth*J6Z2BAPfMLLr&&+&Wz!SKG``5?>1VkJXyDkJ81Z+%1xtw?nPWW zu3?{0s3Sc`BO5FHe`Ad}K7?J;Rhr&3*|jS(-SL&_clsT`^8=cY`wdfi+v;U!1%TYZ z7~JKZlH2*o8jtrj!hWW}*-IUUX1NRZ2UipIJ>yD8(fN@JZ~l3GQQO})8oJraRelw+ z$MBCpk#39UwUHC%=cy30*#1koq9KjqPo?p3f4eF?jaK^b_OHV^W7Yh;^+C5lE3&qD zQB!5C5MeZGBJJJ`Vnyq4RHLaDM8rGlw<7wG{C+^hy}n8-IV!?!pa4i67vkrZp8~k< zi*$(9CE4!lbOaB436~xqb?9EC5{`%7us%aQ$=+n{rwUO3?B$iRWUBEFmH#PYRYX+5 zZM1*ug{*3|fdTq&{$sCVg%~`i39X{^DyVQ!hgkS=g#`R3b*LM!VVdO$T?Qs@gPI{O zuRI^_XgH>ot}?`fq#&X?1U_$B=xmc}UL&dxyNVcxKlaQqt5lEm^?9Pa5A;~Gg3f=0HT)$nQ5-X*=t2HYcv5G15H7H zgJ{jCo^*nf(6&QWmRq-xtED(V^FPl7YzP|~@u9lJIBG~L&kLOy()kc@FmudY`NHY( zwlGoXWU<%uhkVk_r;2@1jg1bZzIyP5{LPqhwDY@wAQU35$WVGp2p>5e0kta!P7M|H zvZ6`@k-43~N&^gqF^XirOJZX_`N~aFV_N@SOXFW=1w7t%nIcPO;?q(QKB?td{CoG# zRG`5lJyx9q&AQh>+eP7jEEEitN{4rH0;(0|6;E#UW>Ny~(e~i23J^d?O(dN?8vkl| zaB^15#2v`5S&#g5aqd<0gmX}C#;G*zsU$7W>@8ov?;JLuS8$`xMvwg1q}mQP#1gB`3AZ4@1ym}wvPP6eIBl^G8JRe+mpa%h1H{Zq9e+x7Ea4lrf(`W z=#b1PL*bV{+;Kxo(655FJy|9R@^jeN9u8aB6CuhXR~kOBbDG}6dU z<+nr8u4IgziNJhyO09U|?kGzek~O~bdLIJ1=Yk83$7|6w`cbQA5|$Se8?j97hWN-7 z0(kW1I>#%AbZ+8J=*1B^$kxH^x6OVjzQyUHkJnWr%QcHR-e0arZ2s5^EZIggZE_-U z{8C0r<21aZT&I4wGbk?j=*T`qXeXKZ3B~6+gyS;;sh}j+$DvzKhtYr~v2LU-E*>a=%FOB` zl}q#eq_z!yS=Lp;Toop4JVxJDW6O#&GC~G8;)fd--?s^GHN0CmVVU7D_G*$3K>?F`|UGG~HNhhwT`%=kdikV30 zH>E9k#jBj#>Hpnz;6z`F9%e7L#I~F@g<$$vE7sd#={c?>Gq!2<1KUwwfmeQU8FsEV zvCX;puo;~WhkBXM0@mo4`A>TXrzK zd(&Pgm`wD*cogdrcSJEQ@)W!Lu~!sY>=^@7j4W>WL%eZ!+1JF{Pu(xgixtTIQKyBq zg})V#c7gjn|C2qKU4VS-y<217!nfHUh;Y@>@37x3g=Q?y7;8XbmY|vQlz1L}j-v6F z{zM-ZIqVHhgi#Z z#{cFV@t${ie+?5CzdW&o`VIqzalrF1|7cGAcE-F3!Q6pA0B=0kd)F(iLUuu0+ zj>>+ldCnTGj_DjC8p3>TGjsVEPLWI}=}ao&0uA2(k%AkjKt8-wg2cd}4~i#R+Bz%v zDVs2#FP$*{$A?7R!o&3x4ASRYJi+$sF&Kjoa*`yyf0Y$s9%de6=MaaIZ!aRf|4*G4 z*o#(bfq39bhM6JBIw*Z518sxiby%!@5*Tvqv2q>-WO@uasiqc;ej~ea zO)3a|LqFh*ze)vEi+0TY{^`rxCJkxYOMzs!Yt-ztcZo`f>gTk@k>xXpd*To9kr?>1 zz-0XIRJ9s;r@uAg2Z2Vczr+HL^i#0T9&BPgdp^@+6y|+YJ3J*H6JOZN;IExu17(}e zT(ghifz;qT!+#V}^HK7JzvNOVcD#4I!vfJZX{X>niT$(|3Ikd=(oWw%w{V}59{dAz zntA@N4@yOZQ~4rKS)k{z42yd7Um*3BL9yP99!)dz!2EZ`)9Buj4;eFY@MJm^Awexx z7uOQX*&b3sH0K9nc}ydEvH!s&RiHLec8g#93U}t)D&0BUVdRo<+Xmju^&qs1A1uIOJw-T5dl}`S^Hp z^EWZ@#l67dar=)`j#YjYj~CTP*BqbCx))iv`QO>!Z2k1lIHsT7inVgHysLmc)XR4b zhuv?PCMIy<+5dseo#)Df_8~o`2)mW(inc?*&4~siKf)XkoP4I<0do7pO;2gdQeRQ2 zG(RBQv}4Y$3RZ3uT=le4{s<_0u=`~*UHhH_yZB*xE*OK;`f2d$I@hqfoI@hF`oOW{ zX>$2v6K_qw)?Cll?(+pkPPGV?dFdsFj~Vv$J+3E*vqmWGy4-ZkFn2i9ra{AAzwzjv?$(?mWvK6H!P zhuhKKx2`|Sn0WViqd$l~z@9IoFERR`40mBhJ{_-nV~rg6I5Q3r=3=g&Y~0%xzhiw? zF|QDkqaqoq)kjfC6*HzACdchR;9~U}Rynsb6+O%ML$>OD^AHTGe2$NLGOx{Tr z+rnTV`t15s90jE0%Rct~?>)5JKOz!@f0o|2Bo=JDNqL=o5q82BOQe_;rM?qJx`8hY zUmIbBpdS;C9TWTC+j!$FwBB(YS}%V<*GO0y&-H*8@uXGhx@6}Ur~kijH(HmfPIrqy zy|pmGI{~?!_{|~oGVVBR?@7N%3n4a|S4CSKL=IP4!W^wf@5j=U5-QmO4`9GJGxbwt z>2T?TumR#|^uAx!+;fN`D=VKdkdl${)6(zr@p~|W>gQcnVr2I{j8&kZAzniqx+qkza4*TO^O!WtfO)#$K1i<$BlK% z1Cs@3)(-U!brJNWbD2uut=JP>QF(Yd$~)Pl59sRd%I(HG=1_p;&F#s$$QzpM&nHH{ z_#NwxWz^R)PWij$9n0u^w0zOV!-%64%TXUFl}Is{vK3;NCZ)%iGMkF8`yup#hUPp<#&1Jo<{lkJ1}@u!Jli2ULd^|Lj3tx&`q zaa>q7QmE|-?C<=c97g++uAr^#3oKBVbbjKEm%!NGGgabIG2j1R>Av&IjYeJPq|Xn! znNvTZ0e}iZ4djc}PnPEDjV<)#48f<%nzuVzT58!hV(N_smC+~RxT{hw!|+=sXTJ(F z9z=KSJk%S{5ziGLCE2`0~wXPWdA9L@OZtzh%=CT=mItVTw42=(h!V6mN z3c>0MA#)p>sh`y-#Ig@^D^NKfO!fb^{ZGTZA%S9`H{bigwLkWO_H>}3gm=^UZT^ji zN$dDCLjfh%67yh9~$N)FsrFpjY`-z}$nR+VVD$SQr`N6qS;4B`$DgJuhd3u64``v!yb?Fe3fey7)Z6hcN@;Okdq|AA9q@G)bOa9ljQ_hFHxTU?$2o<2scz&Z5Rdj`*)#2H zcmu3OF(SM*j07j}sV)!6KQ+P7x%^$#t~LmVA>yGLQ+Ld`_V%Rv z{-F6nUqh$7p7gfsn$^_QbEG!N;?9b^MWM|4OKbvQO|$fiao+3JVgj7p!oYE}qGX~y zCp;z;NUx%Ii+QUbW4UPq)IG7-v_MjFU~}Yzq5dj4o-~(Cmn^brITOg4q=1}BmRr#= z-bokN5SLib{;m>)2Nx)mgPtbA%m>}#driv@T$%_vH6Q9<+lDIM6+K%bWse${?`fM| zK9c%(8jess7>d99TRtI4msaY! zj%)QFFOQ|SSPRt3Zuc*@f)obU3-G*T%8a4pIIs0>r`Rm1nKxxAuGv&E(CGCC4L`A$ z?wJ13kl)dd_BL;_`pb5DzUZF-vm{x^SrW}{)|)S6FUBuuFRCwWFAUdfa&ATPPsREE zob4WXKO0QQdPy@LWaS@}_QW0eINo;paC&k2@OO(`;`g^Fj^-F=!5O}H>{vD0>bA*Y@{n|46q>!9@(J8ciEy2^XEv$)0im8Nyj;%04oo5E{j_7L(=MN5VOy3LjJKUAov_m74v@K? z30LAdB(v!o1#i_uod!En(uD(&6yw}C%c8UPPDKAP#w-{`&swy z1518pD(;5+4El?Y{^|Bn??7@rV;3KMdx5uWa?ptW&COYU%(4XdVWA7T^`7Y*6TzJk zdHLbTXgK<3J(e2t&`>0K<;(38eQa#ljKyTkX!_*j%_!Ml2t#z;!;+M~8alOIy+?oV z8yt)p?B_wXINnlFs%nFYfUQQEAl)k_zjYO=_dCt-m_gOu_E`Fs<%<2;p?uA9+ly@I z3n9?r-S~p~`y+sie|Y0_i02(QCFB7&h5Wo%v4g=&RE}8?ZQ+fYyU}F5PU}$iE3pAEPJgUb6v%>n{jkP(x$14O6(d={67#zL`Wd0Y=QGJ?bDaBDr1$Ti zyA1ec8|Ss*naanfUSB?z^!o*oziO9cGe4+1cWE>{uSC;r+sg0^^ICfM^NWZYMdTKv z%)V=N9(?0y^Y{CmJuG}T(OvL`Lp9t^zrrbDpB~0rpl4UXVNUSm?09;{tLPk=qHe%e zIckaj^3iem29Wc}g^}f**a$-RLCN>h;Cxs{Ws^YzSkNZAO7RlCn(ydoTMR<1d|6Ns zmp+#{SFO8sxQC9((4`J)Gk_}%NO_4aDr+lk>%SE|s;Rl<7b!1&P#Is!S6tT`V~4`*7UTxtvR%E8nOH%D&5uDxS-qs~1%ssZ0|u z0Sgy(G5(z$-Ty&)cevbGu6^}=Z=~Hi+=zJh4dN7H?tb*TRNmbbYEcY;^LpP}0hX&O z7UKujrajx418U>k?aWV)Dhu{DX1#Uy>}*>^b@KEVZ+)q7wVB&BYkbMS(45mN7Cw||}_!Tj1km|YM zUe-GYEXtGhuZNk6+^NLBQRt=ltWj9Iu~_w{@8bM=_@QVTzFtv3<;aE?8Yyk|zOh2X z@ejfCy#(+2i@nvrB7ut?;py zD>!_APcKTSvj4chUCS6vZic8xQuK^}Kp4vM9%xt`>=KEpB8br_epp1J3*T`QX*{Wj zv}gOhytM5#nFIvhZiIC_rC&(s0j5vw+4Y+rkl4X#Dw#QjZw%%c#@=LELZh0tTrT(} zr|?v9pxJjr>Ttib>2b+in`j+4U z*E7$vCFfgb{wQIuhWZpDmx;f7alkZtE+eH47pGCgb==kV@hW*|I&As7lkoKo?cUV# z>oX1Qes`%CIO!L<`g;`>9~B=HAB8?KAvz&ZAxJoE!?le6f6^)dZQR>Bid+ zsxW)f0h1D};g`QJp~x|5O#VtWY^y(MW8-7u~Cp@Johk+G?+jVfVYgC3u!<8u5C?7F{E^!nHJ&UNzj?6q^Bk`XvlRyXPk_Vvnu>ELz80{u74 zKOS5IrBa4Ch6ILCKnh^+iS2GqQX4xkZy5>%g#_u`zk}qb`KI+I7WsQy?MKXqTQ~Oa zA76r%WNbvpZ*C-TT+wy={6YEGdPR~&oO|yzkpI$~JOi)yc zau602RU=1W5NLZS3}$|C4G3DvT9Z$me?!Wa~MaW&2nGg!fj z%yLp>qgn>t3YqSj6fswlO;M(mFkG9lSyN=EFw7uQp?8@v-}wi9j0%sYm{BW1BgiCx z4}~PkYcL5=b7Snd8DTQPWESW_`m-yg>Sfi)*TI|y(f5t*a~M%JMD7F}BY{b-&D*99 z2dDPM#udo65p@GpRP}?njLOyi!UtUlC5KfS%sfItx0F{`_J8#r*B+^AasO~BMYpxw z(5o{v6E8cKAJ}gr2ODNL+2Dgd8Y9F8;#TTbjOBzo#D+DQi9yePiriPuQlxK@!KQuD z3zeMl&Xx0dMcnShX%N}+lM`qT6#e+KIr|G7$=AO+3mxCY362f3YPHG7sX@}GratiOj+2JuFZJ1fNTg>5|*yt08 zfSDwP#SM}u+_Ynv&ODloJ@l4^6Ty!Hr`VNh)~jC25&5NVcTfBh`q&PgoS{{UFdn2l zbDz;!+X!AgVAnuXBfYO$dxBsI=2{~?Gu@8aL#?LEz9>_OsfDbj>%N#?NWT`8*N53+ zsi<@ru>GR}#vQLUL6%_$7Z!?O$a7$XdSwjFKbn-SBq zQuQ(cQXFK&YVo-<0wv^*RhgkRYu5GHH&(J1O9;t?l(Bx;IcdHZNC?jm$+hBPdCc3 z88^M-&eG{^egm*L6RtErq^(!4Uf%G=@8w(?D3=O&DN36su)0qK!>_9+5IG-xMCFiF z?qfd^NC?l=m~!{!#JTV@4S#=%+YLzQC_5iC0Ok z3)~mEW=|vot_rcP)Ze@$pgOrES)Kbls#u|@u-dXNvWnyw3mKV+wnP3Kr(S)M&?m*g z&V6Bh4lZRZtfZ{GtXNv#=#YLJQ8(aeVO+8IK^vhpU@Q|XpO1ki@ka2bzOu9BHlvvEbbD^U)n&>@A*PS zA%a|yJ0;hl+fnpp!rBVJriy{F*CdM0xUd_sK4nKf8%6O3oVkw9m=E7gi-W`s8kdI8 z2zmGK6RS6gu2Pd7=fX+x5}=A$}}YBC4;48G4_!zrB~)0gkj5tP*=VD>Tomi~Me zKgs5c(a7V7y2|uVf9~1SZEeA3U>W7Ggai%lS1c@jCF;_I{AdosYNVxe1V zl1;|%x|*_uH%;lOf4w((&z4zl@x^^7nNe9lPFol^wGz&TrZPG;Ff%tB1Z6|A;D>T^ z+wp55jC#;^rf_tNU?T~4$fFKr<%MYttvG@{&dj=)m3>exEt^5^9l{%+B2h)WCi0$G zfi2G+oZU5gNx4PFqNj-HtH>+-z6W5X>K#x^yf;vsG_l!~_~5?5rScy?)0&dq$E#lt zU3iQ3qjp6ESR(gM2t32SK0`h1hl0=^{7S-Zia~!cr6a@V@XN@ z@YIjYt!X!C3w**K(qq0A9r14v0CkdP%8E~B_J1|zKgXww1K18s0g{!Bg$Hsr0YUi& z&!OTmXU9B%un4GZ=W=O!Y6>tc_50?MeP>X?@rq+FK-bYkr7bV!0E1PaDAgk#SZ;Oa zsA3ueXjFBK)D{v1!%)@IH2h=OqnLoc~*)+bP zER4&Yk`@xV~s*(L&sBb_tU4hO)?HH)?z}jzFl-_Tm zRT$@|*7HGl^93Bp*_VBk%Shd_ftYr9b*jT8EmREv4_0dj-l0b++#PbmQIeH!^y!z3Mkt;=eQJ(C^GH#{e_#k^+Lpvzjw9)m5Ki~D?^ zt}O*d_;Ee+-4H5|;a-8A{Uj?A(giLL`c5x_>i<)ImPAH#c6u+^YVkw4mx$oMs@Rt83Ad{Ew-cnh3 zC`g5G^T65~w0EYmDLL99Hr{L!-QLG2)|QYOkg6~Hp0J5aKRU98<^r|GmudgOrMnz% z??C)XL_Ve#%bu&%g3nSQnjX;z8@NVjSc`e%FqxFcu{7}RLA*5^Q3CCF+Xx^;vDTYz z9?tW_1HlWi=;m8y}LNT@uMWqdWYP0i_nR}Ea%6J zz=RzI$bDFrT^OHQR-E)NFs59JSyx_xr9V^dBzvj|3^`}?Tt6&w6&#};{mH2K1k+WQ z3COHG3Oh3_JJc2!cN5qUzhQdD(jJgHG&eMVbt>DCeOFzOqjV&@9GxR5KZMG%UUk0f z*yK^P$bQh*_T0IYvN`jZl?)hprt4i1s2<%a?BaN2-f1cjy}i^kH%#Iup3oQB9F#f` zAf+r&ioY{>>e4YxHot> zHE*KY5EStVJxeF+MOz;9KyYW>$O0s(1OLnlGO)5fQrkRzjEQTN$xOaJFFw(DYmU7G zY^_}r{MbqzKe-ORPpPMoC#JBlPyJc9^j7fAs+RarIA~pc6q8^wkRd;sbE?gLV6_&^ zBw)wW!EJpoaL%^{Wy6?88Zd+9-O$bsKO|-2Bnm1aK93#DVB_N;}_IgS~r>I zPL6rI+k3$rsI)14Pz=d5!XauJmLBp0AhI5ZTl_*w^A7Bu=^op=!fjc;dsef9U}o3x zKDd{{3tqmh-TIED0#vD$MUG{6FtvXfbAQs=zhm19Qa7bpPQfm@?5jVNcRQE8@&w3nMEbV-ws$Vm-d?B^K735(lnU%jJ^*VzmU$0*sy@Otr((XP22~1sk&9fPEbZgF z=bhBI6#NUGJ~3kbr0EjRTYR=yb^R}5EJm$|w&D4!=>cn79|CA4sDOW|tNr{yd0$4+ zeB4V9ED8XDylKAzM*IH>81wA)-rYYg-u$0oZWH%8zr_oweq_Dfe(eV2(=*9AlPTq zX8wVnt<9H9oPGVaa}0S(JQA%F@}u*A$AeLT*1IBcLb8H;Dn0&{`P~g{H|TtA@Vm)I zaFG^cp`+cmf&M6DsEA>SAqkkDSbd3#QSu4u;AkeSK`VXh%iw>pAY@I)gVlw9Q?wDc z!7byg5v*aX;qw0A#p6xiLgz&}C*;N8#kYlKfHY>P8@vrNTaTuP(SZ8WcwbtkFtY8I ztl{1y-JC}@-s0Uv{`r#^j}ROoHZdxL6b~W}hGM8(Nf7bH`a0{H=E^(dY2fL{U#J%3 zx%9sogVjd5;eM!;VwC3d4p6(**oKiTRb+;Kqzze}K1MJ%mlD5`xnS{xwTm2sfM5^t z+wy(b`}LA~La#*Ue5bZwapaBk8_5h654A1`_4{r$dH#&DMDe~TF3xPkkQkdWxI>X{ z(GO$h8JOdOpJM)ScjVD?V%SeqMs`KuFDe7=kMk7dWh5YI1QS0VC5b!$y+?E35t9I+ zIe>YKq!6bHjJGQX%J>lKj11Qm|+!N3*H-c zla#NPs-Lt7zKI&PHuBhvuuQQR-B}{P2EM#VUX>ik-w=Kvgq(8`Fv4aHb3+kZcu9V3 zwq=GDrlL_OizJ1lfF!>#$XN^m>vJ+Hx!OP8pV|Mt4|y{>DiiVLrt8TORw8b!6W3Ju zl-T|ZiVRkNbr-}r%pgYCft~$5t%zj@AwM+xc8Vn;J*179eG@=O<~_0y7cp!`GiY|A z3*QfG&sTZaBpu@zmHnEyuR+Yc8*k^Q5g!wCWB7}8^u3U5_jf;nmu;aY_&fSA$IEhh z;SZW+4dQ~o{%Wjv6duwwsb-8rOTXf1MlKO8nTSU@N+ zuJ%~Akg-1#5S$1;RP-tT7Yf)@zyW8f4ZX&)pZerh826uh+d?l*`~=3ROo2jJym@@- zd|y73n@{ckynR8MI@1zJHADNjphI zpy4}O`v_(Ml5bph)T6|MbsZ~=H#4AgQg|p4(#mKVmaTNEWNNtd94mEL4(fe| zbj9uz!Bu#f?nHBy>_KU14d%6;^QFFhE6@U+2~5+gs$eDl|CuBGR(11binhToZG=0x zfrR6&Xonw4A_g|;V8tN~q(dG>)!!(R71_&knuEB(4s&8BYzm>Z;1;saf_YqHI}n$# zdF?L19)=||BYzOYPqQI|4fiWTa}gNzmQbSKdik~1$2vlCVR73HjiT&WbH z8&{U9V5K$0liFiGMA+Jd$aUie|sh8*ESo# zZ3nq8^H}6HC}@}0TAYQXHM8?hE377Lq!}as^$mJ#@m+fd1wZ@<`vXEb_i01{teJwY zAH;lP_$TwNHCJvy;SZ?3Sv>;=_Vvx2t2yUB5xhUPz<_I;mV_;d%ZMJuU(-+4OR@3W zer{E{=P5b6qCw0L;-H(YgI4+UkV-OFWxU&MRU1d7vziRsG!E01G?{+~*_H<5Ra570Z< z<~!`Bgu*kzzmj{O*87BO|IIs~!@?u?GIfv0*3U8Zk0SllTp@2LLcF0kCNjK}w0{pIah&dVu=T1N0esXn0Gm{z`7i)iM5Ex!jK{HNpM3cr+(w z#oze>@t-MsbMH{v?#=yMY~CF!YqQ^L^KOQ$>`5iKDOT2s4-%SJ+HPPKB`f|O|Aw@e z+++nlS4B!z&&W8cF~63`S>7ng-PR-7Z?TqJMe5=)EzQ zyp5J?#ZtMAKHA%|Pbh8v-OAQ~VXJPY>_PcarOo53WFJ-9e78#WA-0o#V)|=u%dS$| z{kVE*`<5;2$tSaeIuB6Ynveh8(LK&r;$R}+fA5A%d@_x!YC`;RK?kfE|J9>*N z=`F7Gy~6YvSGM{kzm4(+O8SH8ORntT6|SULxRPGs%FF&>aNY*ePh6S%%vE=Sm2xN8 z@eU4_4=TY8Z@cgOKb6bAbKgIGf%24cweLKsT=t#&H8N&@BjM2zj@61x0XMk+)T}fIF}C6lB?l+`y~U!B+&$p*G(n zdO*hdHvJEjGP~CDes-z%CezVM9m+WdPxy4MK?uNij<+?h8Z*CCXRZhXWr`K zETpLXd(I6-;h&TCA%Eh$V_g0Pj;=}%#s4$t{#j0Zoa5!GL*wN{7pe}6N4%P+JKrxd zBRuneR9US-##(>mWwd#+{oMr~B`F?RXAJH-244)X_I z36_O7S^hA;@7ntBAJLCjJj2!fIO3nDA3J;Aw)y$GI?6lOUo+pBcD^<=A~ZTw5t>&Q)#?Uy zi@HPIs~%Dt)Kls?^}&<&oOR#>m#l&d9#V;mGkw zQ=~a^N!RtHUZ5B1z4ZZli9SLftykz%^cngbeWAWoU#YLtH|g8--TDFjsNSfb)>{n4 z$TMuCo6*zgYYZ}m86%Cc#zdpim}Sf}78^CjYGZ@3#n@r&H4Ygk71c-@j?vZV0j3*K zb=k1{*_-;l@ZM z?3V@IP-8ICr54k0(Kf%lu+ay)V(PjfT}td{3^azI?IVnGXc!t}jPZDmDaJI^Hd~*o zFEA#8O+ycCG{%BW(x>XvjnQD^&@;=85n$!|7=1j>q^e%*w+Bx&OdqNb$LUiwN~C_H zzoep9kzf8g41KWFj@16>ZR7{*i`?DidD~hBHVC;(jWRGiqm&IMt@QIs8vDxc18EgT zMf$QF49{tl8KZ)JmR|Aud7#nnbtR8B#(@RT4mQb{it(%fn~X6XY0LndX3R9IjCo*l zjQPkfsdJ45811ECON?dcQ}l*WjnSWF%mG_!EXN!v09$6Pkd}ef7%PohqY!MBu|{vv zFM+K#))^a_y56Y9{AvPQgWMaKy3SZ=tk#=Za;;IL?_=KO#$tUZ*b0nQxqg@>R~V&C zm3jv=bs5^#Pv07}s#xFXr(#~u_gjTIKD)hD>sjvx870^8_uUAHi_0BM68*^Fj zY9o)S$f+OH`voQM(TjuU#Z!TyR4JV$ufm#G%91x?l`Q1*)&+gF!PtbJmVVx7Yz{_2 z^7^BIRt4j`4&@GGKl`aA?WqIWQ+o%gb?j#;xi3dyBhq^Y{alCHp)+-jVF&%Z5i7fy zskQB+FcK^NILBxhR{vrDd5!h#XX&eL>}ScliT#X{Wv%%2V&|y!pGejUSRM9+srr(j z%@a5Z>-0+GmQh%*S0T5oyjl8mj1fjbU!X7cM?qhvukc4fuR*ybdMR?u)7Kaab(z`o z^>xM~j=}PFP-$c)u%QEwEbtkE}M&%;KJ_O8~G_=d)8*Vh}p@SUp~ z6ZQYi-nW2PQC<7*$IK-1h%htfoH+pz5F%hiL_{7M5fN#mh=_oQm|}#85fLfmB1S|? zDe}jNF{Lyj0;LrBkRpO2B8`-b6p>3Q<G{5+H3agnV-m1WLETFhNYZjyw7ECUrtBv5^5_L9uOydT6e zPO*GKI4FdWEChwQn=m3Fi+Covl>i+vVH`%4Klf?8A{-RWUZe+w5f{vTjAiS%brKY2 zb|6(~cF$z_JRKv_mq-|`RvAfPgyoWC1vLg~k66b!?F4JTb%=BBW9_zzx!g2so3+!I z8&(nax5QPp^_I1P(}FK6*XPPwZx#7mS*xtKe6C2YfSQpo+v;yYcLhfoR&VS8xE#>vP`Wk^fGLuV#{eLXia~6KTI9&C|5kK`W3A_@ji!5q}PNkunRv!I!H+W5;9E zH=~GYn%IpcPSb0JyreDVYz;J?MV@z;V{c@%CCjey$MQzR8`0{j@;7OZMej0LCE^pf zR~oOvX9LC2pIO-98H38(BmP8#tTNO6v}YT#n?k!ruJ3BT@gk9Gs;7~4N{?h4)lPoD zAe-t1e{Mr^jrv9xxVQ5u>xOzT+e==iogbYlH{WOfZn`=94X{k|iLY&a4^>>13eH(oNXE0el*1 z?B>n-)TxN$d6U z_n4l<^pl+P4$c|7-h@Q593)&{K9}jsnMNJ0KhcSFE06L7ozFD&_(tQ8lv_}iV&1@~ zIkmhk@9fV|-*IassgFT_Mpg>FgGQj(seZ*h@<-ZRm;Ib92TZr3QEjv$>!z`XPG}oP zDHJsM^kI8I+cMhOchMhlk30hZCB{+Ee`T)TGk*&Dh4LRWSJSu!z=QP$opH6skzK?n zrSq}Yb99Q;D5Vi%{)A4wT1j;3)p&?bFDFdkp8ABwxt>7p(TF9I&d?SRtv;uHw*P*O z`hqwSeJNBwLcMI%Cx7Eby^uyb(<_M+GfFf@!gX-(AQ(I<==-WaPp<%lu?eiE0QxpJ`53VOc_L2%luOLOnubk}Q8}rgbIT@;Ogx zN%cFj?7}5_5x;HJmGd0Ud49~#a~|RNzoGNSdI067zsv6|4B)ql*3fBxy@u(o^gKs@ zR7!7fj50{t9N@IKQR|75+%nfwi-`lA|5B=>n8WmDs-wtddOFwfW2&QA#y;z*b}F0e zbMg=QK2e?k8I{3?}v#8Z5R_7^noL?+ER$+JTi@tNcyG|Q0c z51d;A_MgQ5PqP2JoZB&~9nCoP7n*UPFK3!0c0ZbNvV`&kozFB`l?2T=Ig$LqA=#Bb zp}AJJklL-hkjD9$)l{yygXUWKU^OJ-q%{6fc zg@T4p`J3FT1vJ;r9HJHw1vJjl0`&YLN;6L0##}X{87EHDj4SoXPKH|3+hjJMD*ppN zg~;R+=_NGdL?+ERnabzPi};rKTYtz z(MnI`42k`rP#(4}LU>nrWF3=YT;nl~A19kyrQ3rav&Zt6G@c0V5uR+nWxs8&v)}RW z5_Ys>I40k5O81%uckc3?!a2^r@twjiIR$*D@Xwv4e5dgH&L-zK&Nk;m=P%9?=daEe zPKonhu5z_&xC!p9?qGMQd!IYjo#;O6{?J|MzUHpwdxkg0?okhJ8~!}hBy@SGd8lQm zD}Uo-R4B{;&POOa^k`^o=<(3^Lz6;J((S)=^Ll7`b}<0A~hB`Q-r%fQ7(?z@@+yfj^bC8n~{~@5U;)xeD6q z{C17;zAeC=0qzNq^4S-(Prslp2ZP@rFZ3ZjI}2_M<6FXRD9;eVmxsj-Wn^1s4FEEN zp4XJHHjq#LioYu@(>Z1_Yof)RwwV0~@_WStIn_YYoMbxF{d||>e4pK;?B=n5o=;Q$ z((&_Q_Zi>iIL~nCb6nOI_HmfcV)nn5-ffoGmeKR~BA>T%e$RpZujG8LDYNA`nIm)MG&xh|$pTp@7s{n_g0`O{v0VCSIe=X%J(D@f>zrG;j;6&vy*O21f9e0@8|GpH7)STV4pU$!y+H^G>3lV z@1bZ0SSI^yB-g0VZd-B_IS0$1XJtHBKH;ZToWD$>d0_JWy3*t)Z2r7A=xLH!>o3&;PAeRgmtufoP2cKOz4vkTKp*nKZ3Wfy7M>{H4< zETy1hQ7zW^HHGWn3fuwVGlN+9G>7fRwrfXh&yLzDUPG^u=XkCc^5_i6Rn@KI@wGa( z7JtF3ufO)vx=U+rl@&KMG<08RSm^%HaGw2ASdbQ1c&)tFUW#{Rz!~*aQNS5?YNC~G z+aWvI?m*vyum{^i?0fB@_I>s+`+j@4{eYcef6pG_C3%g#u$Stkc{h9gygR-A-d)}R z?{06PcaJy78|)47=-VFGRI8DYJ_23jYpIccr!(o)`zzI{w$}bft}m?xZG@ z)uX&wSPP!_=7cK7B~S2 ziK(YD$|~^;J);(!eSz?=0`rB5Uz6#1asR6INrh%JkYOlvx{+}@8ycHW=Kym9dKz$M zK<8DV3j(?jxGGkr4|p)}Kf?aU;HFiSt9w>q!AU z1vovRX94E~^jzTlfL;t-7SJn!YxH`ii-21K|LyF*E9TF?d;R!K?+@Y}3jRN;Pv}zt zU1k_YJ)(?8Mi_2Wrdt|q;I^+QH%=oTqm$7sRuqk zmFPhnXD+)xWXoyCm@QQ=XFCF~D|%n~2W$b{!u|7M@J}XJ(~S=w=|}iP&E@R=jeOK1qRA!*cOUJm)l96!EhvBSIy7SNMtgXK z4kn8goo2M2BiGEK6IRxhx|BF4*O*7GVQiyzFt4U|fKLM1U&vmZs3_0GCR9hzsAFO$ zUiCLq9nC{j$Aq<1NAnPsW%j1{W^bxRLN?VgaU|8zIK#ERnyqYC6AgdVF>x-{G4W|~ z%?GHa$n$b`|3*GkQ{>#7U9KtB?vmTNlpKnQ-YOstslKL5wvxn=RMUiRRMW&JRL4XG zm)7Op{1t}I!5UT&eA8l(5d0mBR$dV_`_9oV(GNuT=-%jFaTD(a^1aoN|7#)n>2#oq z4vap`YvNR_iT%k^P$|E^<7VW)gOe^|h@i82eJ-+QqrBP3FApnQn_BWW#CT{|o%yDAYcoZP%;7k(Z%n++yEe&RWD&F?D;JpjwV|w2yyn0ZU@EYqP^t@KT`US$j3hWOY6wt%s{)xgyucV=6M~f*K3(L{kCje#QgfxKl=s!tc#V!`b z=#paE+cJ&)t)S;!n;G^1_c3*lb6Cl+7g{F$9-`fqmH^`lp{`TB`M|}%Wx$mej4#!i z8qugF?5jW%XvP0uANQ}yV?D^)PLPIU#cYh8t>Q37cPotQL2?*y1l%k+Mvj-0yB`frj?ph7H%<1E>6N(>JAJ;y&NwK}W0+ViM~KyM5iUycJD&dI&|U~Fki9Th z3b?d=_#H(5A(Xi2FLLmI4qTMWzg@^hD3zZkh9d1yxZs(8lgYi{yo=2>npverd*~0> zk@jU25*=6so`s~5DplXdw#3FnPQHEKX5BGfOX2m3ch&X00guW(t9a#^oXS+r~6IGtDmw8VPcMoy*O zCnUfdkN`CIzsqx!J4`r}-d z(Q{B)!Zth7b`nIYoEd0HZwtZwemDRBp?6G(Xi78%V`Q)p=EkoLRh{B(25tlH1nv>q z+{YLTH>$)p>8&cdjk88Peu40>0#6DHzZTP_asR4yy8sQkg8fKo*W? z`}lTwRMk^$RBx53rm6*MgDO_1wWmAlfqItev%k7sI&n&S^m*O;3a(FWo8*8G0l_i5jP| zHXF^6){4V%)HIbS@Ma+tHB^UVKyi&!qI73g6SiBi4gDf-%xo+kI|Lb;bf+i2rFqEs8X7opSiOu0S|r(_vIL8*Fj zJwkJIE;O0|C@w;YK1xrPbPhC_IH=L!I+y9~atcC6Lc@py*C>anTxcg0+N%Ood#W?j zbD&`;Jy51pfNF#~w_L%0OD2%4B+#(Bm&ZK;GjXM+4 zoaXbnu&3!gG2;Z$f#&p?%EpS~lFdjh=2K?J_{7Jh9NK`aU1mo4U34~z-(|`W*yx@q!Q(3?+i8`85Q>(qe^_4Fsl}vbAj-$0_O@7 zzb4c3EBPb5YlEv~QWMMniOAS{!Y7SPV z&1yg7o2%RFK6-?n3|YTYZ`B8Nnc-nQ?Tc|b#h7QTHntmwO=UJUJDdH@(dIOBzWJ89 z(>w~zZ;!dpJZK&P95+w$F9Sjjn1{_{<_87@q?MYR&3*x_h*SL#vcz0&b_yUrPGO~_bMj_# z0E^>PGD7lDZxKL2oDv9`YR)hY`Czt;QN|&Jj5iJ%+k7w+W0ZlF!pty78moLTvtyL8 z3L!(x;n3?C%mFdVn2V5fqrk{Da?Sq0K}7k0keKS3Pfe{8|vdaMdkolE##kmzULT2jTF)(r~Xsq?A#sr<}16nXnBgC!%)HF^DrA}3V(V>DyIi2{a z;6Am_=%$X)uMY@GQN_?9H>piVN66hxF+gs6F$0$ca41eKL&!nokr%-6I0a1sy67=A zA%NX+YC@3PumBFlsbQ$k8syhAfZRCMle9n81N##nGUHSmgv?e&Dk*?1aViNRlhis{ z;)7Zdqp(hrUUq`D!{xwLzRpO1kO6X!+$cAy#lX2l`2eoRsXlUvT%vM-xkUMZkkP8W zoF!+evA|5Cd_ZpXK*e@in52TUlco#%a%R+FMzJJ0tT*dot*zpl;0VyKFxn zNBWxD&GB=oboB=5XzFv0(}TXdpu3Ug1>WdJ>0)y=)rCqyK4f`*iEqz;m#lVb5oufM zH>@M?=Cldy{}=vdMglz{Rf|dI(usVVK{mHZ3wl!pZIwsuKxG+0+pF3#7V| z+xc^9Red+Lq0yLIaz3@BDx-R-&$&h`_^f_3?KEMq>iiO;wOH|+u^TO_}m&(F@x245TMI>obr9{?6?mgC@oO^OGwxrtJ}pIWks&6DeC!anhy$WjhGhqtE;D5=_K&ONHhD-1m5iNy zKb3{ue4$#ScBmuT(9LugJplbUT`zz8wRJnwYxFL?mn{QiZ`r1IlJ$k@ zgL)lVU-S+=6|h?uW2HXK^f6t+x$W0kfc5&2K8oCkMqU~^74qoEun)MJse^i>?#H=x zV%QCwz*I5#BkRK!oy@QuxQnU1C?T0G9{j)>ya_#$DdG`2mEMZ=3!rxZ7cjL%FIUj1 z^m?_8Ko6us!0mva!ZDp!J_r#7}>8UE6K+gagOwHDfR64JV9SHOkxCKm2*GURG z6It$`mg>o}RE=P2wAw47Q|UCFB@f60dW1Zn3Yi+CmP+VU z`bOPTZjoDbAGt+MWon?BE$L+G3b{h}0QQzER2Ki&SB;m@sdN{auhW33GGFzR`Jg(g zp|p3??c^lg0f=AKNlt>hL-oL#rgT%8p<4nQ$qbb&GvKaKDH1xBPJ)yNXcz8srjq4b zbW&DZ{LCs_vKP5n zzt`gzYqr=F1KEyTwMH%!q*JXE%VHoKk*hY!8G>}GRTcIZHJjahHIJ4&VBpZ&ngX}h6&ZgC+tN~eU%HQOZNyBL`FI$quMml7xL7dy@tsM&W-#RM)Gso=C zah?~e>D&z8qnAe(o3r=x+c~e3tp;mjf4b9$)@Id|EHmn6;vacVq;hGc@!v^O{m53M zMv<)s{`}U?+0A5;pj99BxrOPMnWh_>QfRG3zJlI2V*5;rZQ4&|aH&;E)m4?@uT zCmph8oLx$~McF$Pr~LnsHRh~MwG)}NF3MDjQ0C=aEH6iX)nlzv#&R9r-!^7P7#g z?!@yfs;Rt<`B}(51+?~`{et)x1!Q$W8DT6DM%# z+aA>WbaJVXN6?Sy9-sKxn@h1kHnweXiS61UQQwZ($)cfsncYG(wy&^TA!dr5B0T$Q z`&tpTud}Zg&FmZOE~16q-M&#=VW-(Qi`H~XS6pe|VfPhn?SA&1;wpQ9JwjX!4dE$~ z<~~3d1>4a8)zGY}noYR_52|eRIBL;Xs@O~if zX6<7jTahM3Poad#-_|-xn&aI-6$wxFkJr+ysg_)u0pFV1C=vOF_@LfRc zHUC@IUbS82Jse;0bfV`N2`T@r=}LEA{}&sH`*KTO~jmd7Bhlwdo@wUW8UN554{Oh*}M!o*Im3m}c%UhH;3Sbw;KD)x?W*Rad2B|nSp2=VtzcHg0W8{hA9 zM;XzhWe?iD(QOFe)rL#=cT(NxjJx(;@?Q29b1q4!M-p}k$?-jCwEP)%3)%Hou8d&i z>LtSQcp9SF4Iu{zR^}LI2 z?+5JX?L7Mhdyf60U0~0(7w{X85vPfBnbXX<+-dIo&e`q!r}GErl=G!?+BxGkahtlA zxtF^w+$-ExZfiHiZR56e+qqY{?JM_}`=mSB&2{tKIqr+@kKMWMJMMb-U3Y`~p8KVH z)-4Zr58oKRDV!F*IovbcE8I7HXZWu0fbgL3;PAcS`@;`}%OfHpBRXP65+aF_OCt3m z4I&LANs&t0mOaWI zh2DSMeq2Z{PjPuJm)Dxhqr1dSccPo){+m0=`!E>s8I{U?`J(&2p}pV!#6DnuY9F$X z*vFhVoYl^o&RXX!=j{uRyK`pS{I7JxYv(qe=ese_?dS)X;a5~U&%TLLtIn`j{twQu znp$SaIpco8MfXN^`Bu!Tb8~<7E6Tn0IkfOAo~PsHpG?R}sj z4JyyO&@-Wh;tHO9xAE+|liy(@%TUN`;9ct3Uc~dfW?l=gz1PvZ-s|jj^=|Zfc(-`H zyxYA#-cawpq=;F$x*6mxVb$@ozeJJh6>>O^DCiZN5 zHfC z5K%YZEfC4>Ja?YB%zfE?MKp6yxu--6_q1CoTDoO!nP?pnp-V(csD7xS=n%Rzbg8&5 z)HoCsokG`zI*Xe^{~hikZi&7Z-6)=o7De9|KZyQ5x<~vlS{ywneiWBc{VK_*fBrfv zarm5>y5yp3{Etvi^o#qj`Od)=L(UP(qYCO;WsXgCb zXfLu~w^!P4*lSUf0@UCY@O;KCb<4o73>hIaln_b;*9}4q!FA)19SViQAukjK-TAni#Fev#f`tQ0&p<5?>ZyRH^gM(Lxs3 zi|wD;OYEQ9OYPU}H-k0okbTTPVSizl*ry!fjBp-wvYdyV(as~zqs|!T31__XeP@F6 zinG9Z)mh{$c7DdI-8;_uK!f?%+3$QBXfbDt5sD;C6Aly4~EHSc@5j zu{GKq11;uh)?rA4`HA~ecY*t=`?|Z5beK@RP|`VCjIYDQG??u0qv0{($HHU7PlTTe zPYF*A|1dlwJTp8SnoUfjX&7-LO(IPrmqnUIT12jlTodUKxi-=9$Ze6{ zk?%zAj0}zpiQF3*8o596VC0cVcI45>n8@R3^#rVuJ<{;#>B6Lq^ zuu!3Uv41gn|6;L(2(wHg%S*3symZO8Wl_!jufMzoH<9debTgCJ$ZJfuFTqdQ*Lt*f zHe&Zwa$2$~B?Y!B1Ki#R9h2HE?5^JRY%^@i_A6@pj^MYc z=*IEsOLMX@jSmYP-HUXMz>S|W#lC^dc{oIOK`D$yhdJ`NM1OT(LFsL>?pbsjrnl4k zt+&hjU2qpyE9R>rruNGS($L!qI}ul$m+R~C7$)V4cD+2*oZqwj4`Fz_y+2?y@Av*J zF0H1V*Q+TE5`Px15 zAnQc*?H{uD&8ZUm|C^=!JRS|y)0N+vTp| zRtbAH|2FX=+&gEnX51g1U1^T<7Zt@?o~@11*Y|r3y)9)Y|D3v^1qlt(5Lp)%Pd!?%xB=X{)JsQMH_6 zpAn)`(;$;b9-S+TNY;&HY4>YQ;!HGEf> zN(H59XtT{CL#rw4*EN=v!1HGh%^%F3J1;7&ff<4Mz;muDpVg&O)GoF1x%q!3&8p_| z?={Z%s?wsm`qrE}p|Y$9YTbqBX)NuQYSNxxW^A=kqVok)S1r>|8@t=N7e5g>H;ocj zH9GT$-hS*H4tNJdvUkusESd=Vph?qeu{pHas`r89h}>%Kd|n4Rvsr8tJH;NcPaG6S z#Bp&_l!6Dk0ZSotW87T_Q2s4E{66_N<@#?R(UbkUyVM&6@o&rUH)MiulJFNug71y+ zmqvoGi}>FX;jf4U-wffeg#_OT;V*>v-v;r&2NHYeBc!P(g~E zsWV@(+PAKA8Cmi8Rkgh8OUMdJxUL$VtL747T4B{%d|2yF@_#>ICP_^w=O9{a<%5yo|UWZy#?ahyB8!uc+)p1;1 ziVB@pI=5Y9DK+g3cGOt=OJcjf_>I?oYn+ft2yMi69EE-QH*-#lZUas8621E3R`8i5 zZYOJ#_>SjzVR45S^)3^4dY606#Xzs6*HR4f+Im-u!QM6AwPKieo!3b`;N9SL5#RH= zdELc>bXT}|$m{9dDn@y?dA-HM-gmrv#UpH6m>exjt|y*JZjkJV*<=rk-w65?wEw5Q z7lp^C!^pnnMF&Rji4KYmj@}!+FFGuGe{@9j!RSNLkRv!i38W228p$3-Vb zbNFs=vZ0iS64^-jd+vX=VIsF3wHdXQ-$o)BVKhSjS)ug z>(lz^+2s1Q_Qm<<91R}d7|T5KvqVmO=6`+s)y6~Rak2Ajm@vj1Oo zPZ9{%Ff&PdD%?l7!w^mp5ake&Bb;)H$bAza2;u=|5fM=lS!9s~RumCYq97=8$t41^ z7%{S}0xBRXMu`UqB=dju?Ve;v0s~<}*8k7$Pra#rb-k+kR@JNOo+=U|)z*yVi3u|H zZg;d|>?s7+1OjUVfw4fK2MD|!2y6=kwg&<`0D+x=z^*{xJwRY@Ag~V*cpnfr5C|L$ z1P%oPhXa8lfxyu~;8-AV8W1=G2z>7UKpXo9E5P$Dvxvo-_th4s7IkN38p{oNp^ z=QCT_?DTuy>-T#whwn!^MktAmE{fw9|7V*7I ztPrc2w~Wo(;$5*t>=b+1woDvkek$C)V7_@i+7{mlX=003TytoKSgjGezN?k<-RwU} ztD@Cp>d_i$&9pnjGg>>GZsEb%76Uj%6?&*PTK!*}ITJVrWt*Zss?A`x*=(PyY#e(Z zZ2{XCYcH|uO6^VYg|<$c!u*ZeHs+So7O>yN+G3V-HPd%Fw%y_ar*MJee1;`0$MWvi zQrLc&%~9+>!%u8a5ozjN3-Px0JBPZY&aGg1Sw5|iUQ*1`UD_1%Z!&o$;_29BDW;7yqs$4et@f%N+k<<;v8Irwr_G!~Os$ zzB$qG{uTeR#XoJ?u7Q70NodBV{^`tNZ-eU$a7M$9FZk6yCP1v>FNYof`&N5-FnvZ* zTPUaiE%7~4H1pCD^i9z1px-0xbS1Nu>w!vIRX!XFrJ>=! zT{^z)*BgN1-%skl$=-K#wayr)^5Xp#Cyo;K{Uhx&<*H6tr8sRRT*JU04?mMJs*Ys3 z9bdTAxys1h+R?_*_J+pqg3$}=a6XC`fMJbK7zGetIhdL>mJ-=arRl6=PUU+J&118W zmN5TiepY)P%%fylkN*>G=T>-t4q*rV<8+Eco?(CK%vXC3ooAn!_*NS4r*oVXjkc_I zrDxg%RH3&z{{LRe+)@r6hzYccg4x3HE`$7rbZN_@uANaf{W&ZYYAVv?VI7VD&mR{73hs_uDQzRJFYOW5SBLJ6DpO(^B*C}*F4 zRo>HaUV+I;=d$+s&-Cjf*Z6wM%x}yuCCvUzX~oQ$#J>J(NW}c&;ef=f#*ZKOn%uCvLh-bKpf%&<=Bv2>n z3Wtk)ZgV%-@vAYQ z48E}`gAj)UrclNTBdier2@pa@+-4Y4sv`lVs!rM8EByBg{x}Y4OpnE+Dd4)J5a8Z^)&>TJqcU-6{{ti#-4ehOHNxx@S%upY}A z>oRwkUjo)SG#hju68-AA){p(SzcC@m1GrJRmRBLGFHaP`m(WXBAd!)vbnrXwvw%7TiH&w zmv_q!vZL%IJIi?4MRt|lWN+D5-X{mhfpUl(AxBH~WomVfHJbH-GLn0Kj=enToI*vW zm1HHdaY|Jvgj1_Zp`2n2g>kC2na0Xk=Euo6_ETThry?vxV=Bs$G-21KvMGhLM9rue zOVylx-X?FO;w)V&D!~%ArjjgWTPnqpw&S!|TC%ge9oXJccBIlQcPDbN?46ktFXPF{ zdFaCNbd_D1(@l0G7w4xp`|KX~hay@#`W%?e`jgK4n$q<1gRJIT9|?=yUV;(f`K z%Db1%QBM04_HopE)JlIZKY!v~hp&f&Bm>8~%aS3*N+re2v3H;Kbd2RV<_qH8qDq9_ z8U;rL?*X$f@ctg0_-;Uw6z?f3&rH1oRNW)LiCI4OW!ZLesT@9s%#$feggY3?!T9NH}h{YRWz|01POBY%hz&62PgIH%W8Y7MRIPv zV=V4b?@N}4V6}j&$uPkCD8~9^lzSA{!bYGZYZT)0H}P`J`xLjI&1MY+E&auT{oIU; zTl?3ADuC*vK0#YNoYN@_4+VVXvt0yJ0#jQ{1_sr5@tV@(ctl9%-_72kS^jpS#hl?< zdx_0@tJnA>h>}eAKErf{>Zw?>^K#v6gbX#ae5|+nsFN(s#$dJyQX1XhtkPf#TrB3c zxdA9an^9;{<&q`Oa>%lQS_MZ0F83x^b-zk!=f-e8Wf?Z}ycM{Qi}(t}wYdxUpVG`qz@5JmzLW zlpqHt3-40ebm8IAr_)}e?=A%>$ry|e$!2eu`3v`gpQA7IWckQ$7kjuLRqGnB1TFQE zx%)|P)_mS^k3chV#TYcR4rQXsP_IR-kv_n3F z2d7;m5)AYO&6!+0RWr&-U}h0fr-Ij}I9-@3TLvCf<5-H3Jeojlhn z={?{*<6Z7e=6U8$@7D^M7%l2jUFHYvUFa3A3Xtc)0#4;xWSCl*B3R=^e%G1{e!RGb zD|qkJybZFfb+6*BfG(>p67qDrNn`gF^{AV8S$)*%LX{yI^ozoj!MsdV6z-L89VR%b zFDrZ*uJy9s79RC}V~+EM@#e^UPmZsO3hj$1ONLb6EcE7=1HPhJx;lb!Gnp(12=C)AP*UkZ|}-1TVa%gnQ}_uz0XDZ1@gtjb)@fmgIV*c-U?a@tZRFB zUB1HY9a+fVs@?lP@a@5r>wWKEFjrph+;8%wkxM=-rxD4=GcWx6-IdUSQ_&uQPwX+KOtJ|Z6>O+G3gC0$Nq zX>2UbV-zBjWD7OOLoGa&2Y5AOdjvR6x=f}zUna@7|F8|IsUmzDygnU81K#_8xTu4#!ADml* zbNeFacClPcQvOr^lcMDk&VL!s|1!@1zql02aVfmarSOV;g(}Duas^eCugX`cl3XcQ zQf2v?e2uEeRdN+om9NX!DMqfAtErlNL%u=P<(u+Nsv+0NHB?h3%Ver0-;!@pZTYr* zo9f85axKNmb#fimmG8)R$RpRw^;A!8kQ*pYzANA5nBSA{QGK~lZlnhCefd5$l$+!x zY9u$y&D2Lv(;H{YQn zYbs0un+EI?24qOz-X}mHN@1Kbz61^EO%jBM)M)9hQWSP@{+vK8_@O-*Fa2YeS5AGw z3>xgZ2q*^>@(z>%Ir2{bm|}ZUeF3r(Vi+|;>iAw1MsQ}tcbU(`CCu`QkW}SxNdq9C zfBGdH3rHI06+y|Pf+sCNNYQ{3vH8Vnd>6OgE<}t|AQ%aQWH#`N!i|ao-ldyX1~2XA z7w++01b+q@o^`>1)oTQ1tv}5SL|Pe&!oV*a_q&L&9w}6s83gMB2(E!&$WYlc#nsox z&xMzkk8FjPnvZmamrXpARLU@Bh!9P2y<@sK!8pu;8e*a1q4Q@&Z{@nMo(ARJU!6T^ zpEpGAWbb5^7kqMG`4kV(mxUuj(2dVQo43SAZx0GQa^^oDD^2W%!yN4RdVyH!XtB`00=}0`y zRg1qkcXs~v+M{H$q_s78?X*Qg6G!v2y@sxBrA=frTOC_-b5nEk)k&`9dspBp*K)_I zNBW-Li7lQLp80;p-m&@DI$k}`S7qh@%3 zA>$T|mY*%H)d{026fLgSnpWJ^37~8M%neI!v#-rk(iU556R*Z$o-+S=U-xaYO^@iW`1{3p^N2Q-Z?k)tO1FzA{eC-ee=|So8sdz!3nPzmhPyKno z@=*np1lPV#tIqtn1c)ef4Wes6Uv5EL!^pCpHVSHzPBC5*fgG4FPX{1^9KaZOs?AC_ zN02k81iWLI@hGmtzo!a_`}?Ba1?Pq7@<4eiJWLB@nP{8{R`wwol*rn3gHvZP#|EAF z3UmsZB>8P3H341(*y*6M4C>^9-*xqU;qKt`!Y>{9AD{)-0RDFtPAbqgDnQl3RB!J zq;Kl{z5@Eh@sJ*cDokx|5^YwO`A`L(TPuXR(AEOg1jYHiI%E|EOR$qHsbN^WqWny8 zh>^dn@W$DC5t%ADTf@tnG^I;l4NB7bdnpft(uVCFPYp=6lu({g|1b~N{MPxL0Jy`H zP8{@*HBPLJt;@H$0ndvR%+8~jk}dSo1rg<5@)rWWQJPLw9B)yLVS!UEhB>eO4HUyum$y|<15L>po%HP2iE@-6?^v*i%@P2H1gYj?klU|dCVvcUBFopt{3+NEz^QN!Ab zO=WYr?$6&Yj+txT?wLw96&pWVJA5%w^QGU{mVIS>{Mj|v>;Oi$`Btec*mz3jG4*2pU8Mm79B_nr=FhQ5xeF!W`-`!SDBu6D#dlr zh6*NB&X|Y!!Ymy*m-fh?!S8%mQ^|pC7%?(Ug1rU+pMe;>V_y0 zlfWm8!a)coQ63tTuoVM(yj*X8^{m^M1a<07^po#fR4x9!nx|OQzw0<@WWz27r+>TJ zOU^fuYDi8(`r^wbYCzJ7RYsfo_UkusR!gdCy#1-PqluE^%Z=g#c&3<>Q>I@7MC23zDUk-Xn<<1hG#5yQ#&KAESHnIX)?3YH?zU_mtL zR;#+i(%4aCl&fT{Hv27FA0PKEn0N-XK$Nnd&L}if*-iNy$dafA5QbI)425bd&q6mA zY$sPB6qycWG63*qa2`+#St{9J*4BHZknIwJ^l28iRb53!w?74X1-7U{)Z69xHZH>x zP<++rXx?~LH&s3zRQD*8OawZ@$OLIuGpH%3`ok{g!beG^SYcNWxXeIFygDxXP6(@& z>6&%)y+eBWUQ_nFO65GT*MM)mWk|j1nbCkf#eM&>%5$WiR!MRjPp`@}fCB^(vzVtD zP4OwyoyFbpUB;cpUB{ir9n_uX8Qp2?%0=Bh@RdQijw+)IQB`U^GTZpwpj`M)IA!g9 ziqHTqm}lzVw>}P1=f?Y2UA*`jZ_G)pCN-Ptx5mSxbwyK$PBl7L3XM}COU&+|i=(h^ z@5L`2u+xPq{B>~GM~epDs*!_HYJobn`nC{d~Y;+7Y2;au0rw>43V- z^P9A+(l3CjE(~yQ=h?7MV6#>^rd2W<)%AYSQI_f-`PXi+Z6Bt>XO3^1AiaHbI||G9X2R%ZLjR^r z#}{#?aX@H#+-q9o6potjrV83)LA@?qY2{ovwwX{J%Q*C>=%kkSkBlIjnza1Td1+Z) zc_ial*u(TD^KCgF1Dpi}wWL|h)?jQbQMXnHo6|sEec*9&zuy~wY z^2HqBgX^lp5(b{gE>(j^0!5oROrJoq5n})ipyjx%(2g?d(0xvhbf!(Zcn|QBOA+J? z{x&VTNWRyIB6)f(Rundlxu;#M{Lnx(AT2lc_# zri62`50|(4`>jz|y0WVIICpiDHj$fI#kHqO@iLc^V{co+sITUx_$ zCKH2u$RJz@OxSA;XCixJQZ2SNHT%4#+Y+h{Z!&}6GUSF*pV7zMc3_*iG=JNPnw}Gu z&fdhQy}Xl4v(>A^EaL_5eAJoBB0;a?@i3OxbfHdvjW1VAXp`k=(j|`1cz;`3RWHhM zE8EyRoHM=utfaKn{1lL|t+#2_y)uwy*n_b2XK`ds#}DAx8g1} zo9h;8X}&i61)${RofzZp5|f;gc@1?VclSo4)IGd_O+<1dxUYPDcem?l==ZGgQuZuB zvO)IVVXym@cekpsFJ;9Qy7LX|!2aNJ+*{;mZaO9B>n`?hY|#wEC_}g@weCRDtm^EI z^5n+SMoMSbGY5hK!W!izNnBJEqS<5$QK#5L870yX(NBfqWMU#w1?DKRqFrK=Lt=(R z-RC+dI$tw4JlBb4G4E0CQL>)2{WS>`Sa7SR6v^LDz@`qU&$0Kh&#{BiJShQ$+fE&J z6J!9JXrW|0SQmmpr!KCas*fc3UQ8;ecD1HuERH|#$&WP&@#qSTTGR@*wMy0R;8{b5 zdsYh)${@<+W+9ofd^7*x?*zCl1{D8@~o4l6V_zcZ2Q-d8>A&7;M7gBq&`Zk$a-J%0$TyJ3W$jishE9q zut-!awp>lvLYVU1weSFv^t5PJVcp2@_`!7cXse+nMo66 z#{5R2IfE+$&C&V6xp?u$>&7c}AMbZwzp>8H_E7sWlyj6*Q{d&sd8TuwQ^zh(p69H? zoDn|Cc1nfE0KrbiG90oM5l`11%WLa8rr93RUoUX2S1NdPPYg}(!FSt*-gq-_m4}0@ zS5NZU`KG5+R`Dy0hQ>o<6Std2w$K+tpLiR!_df$4N9EW>amZ1>#mx1Ij_%pz+cg01Zg{R#kL(#LhrA7B8@CIVu^v4PO!2vb%ldw>johSQzSptIRpAn@O@a< zhB=2Cyt54PpCz}konsWDBS zp$xAd6k^KT?@;bgp2JPyno>_Z3_b)$VE5wmDzw>8af*vZ^GESZu+ZaTt;mJM3zCSH z#E;%mt>ePFhH(c)wD+%zj_f14h9;iV@6(I@I!8T6-E~TJNF@zjJ9n|0`4wX) z&d5JTAnMZki@j~LaKkuFX;V8#>pbFIN_k9sP&-&b;iq;osrqj>V*M}E#@5Q@uGYdk zE2G6VH$PbR-;y^$%i2X$EK9h(Xj}z9D^J=TIgC2AJES*2F@G^YPn|K4rF+ph_7hIq z974i~F{LXbn6!Bs?(hKZp3%?oqcc}NE zsl2i1f*Gj1`D@G*@S(jYJ#w&(VMk1sD@6P{zTxk29xuCbPUc8)Bp)}IqN>4!PCZ%H zvNz+=`9%5)cOMrD(Gah+&tgc<=9ZtBo1b=S!;7|OazwfMhqY0=x2)yE8;@M;51t10 zaTHnL6r@wXJlRBNWE|VBrVyxEwKKwxdXDK z5j84=(gYn}oF1oZA@^q;S1Pg4d-JSikBwhu1@{CP=Ni*9gQHzE>tI<&9^Su{)5&*f zT`zXYTpvYKhF%qXES{enQ+i!YQhXZVV&`o1U?8?~U%ypjHdz^Tll9^IIH~`+yw=-3nMVaNx$>LtZ9Bum9@~0PBfssgg zSS#7$pRV7bVU9ehjHD-*S_SPHydp6X_;`=fyNx`dZjlFw)bZkEzD(z2w>cL2IVF&j zR0EpTa}|w2rOWJZDn5@4{0iIyw`z|YAa8ehFUqlXEU^trFN5xvDo9Karhx~0%-a$4 zVDuiC(^ak57U`kBhjmr$6{uB7rL*Pa?Nx~`SY-@DLaODp%qo#{vO!Ig5xuBv<77TR za2p~zwQ|HpPa<2LCEQfCRLVpN?f*#sNN5tu5o_F3e;juwHIn^~Dq$xbhMm)kIurSv z{5^W0)`r$cli8z^^nt-3hwV#;Cz9?DV`kal>2YIz)@mNDCLJA5LVC3+wCTaHNTK>J~ za4(cs`<3%Y*H(|C?kV25OFnHin^wx*l8v;JFncR1D23R~1^}tKY$@G>&QGN%lCIY` z?O&YP$M7)9Iu%CCM0lfd#Rxiv%PbcPeZ;&AK{rfU*)wKd#gukbaR(mTqC>Dyj{+^SZwnMZIVQk}b^Wlg*d7$2LuZRL@Ti1}xv41n+ z&g5ORt(yQnxsM~d2+MtSL!Rdv+>hFF?AW?^#2-ps;`O-;7-{YOGwo^uywd_V`ibJu z!+5s8@sx1Ufc;N*_T3C9mxy>8VX4w~cD@tWG;87%d?pN*3kvnvrM9H9X(n@hfxj7z zMnp>i_*T7_D`YfAw}>y3dK5bBp+3ElIY>~eGlX}JM6{$mg|3r{hSW;8sX0Q~@6IZD z$-MoTPnz4lHCJS^v=#PSzg|_p7_M!K-W5}H5fPC>*P)ND&xcP%x+j)T8ax?2pAn<{k#&T2% zbc@qk_iC07%WVEm7B?vC8l`UW-J0{7A7tTAs{4v|QBP>$RH!cxB9dvxtO*woUiK^r zmOkW@zu)Zn=n9OR0#qBn3EIwgMg?X!r5*`N85;rb%yP;Yj?{fr+rT=sRP7WJwbzev zfK$>1)7{cYQ!M+kcQ&|?4NzMVqz&x&^6c`Ej==x?%as42&hwzZw4t+$&<@=>u!Gq* z*jE&`9Qh?tZPIG=x3JteKB9(ZbI2zzr9jS6ZsY~w_JaYpo}0QY#}Oh1qc44GvLtN?lIwa-j|_umU{@hT#vwW&!(WtF%DwC=dQ{& zEUn2m9Bp{a=wsBjpA5+=TcVn!LrLC05|!^BXA0O%TQc2if?(!TJ|}1Ys5;6(zKw8? z2nkuBDYs+};<*nES%Nb*CM&O}uo~}h?{Kqtwjb+Kw(-E}8Z#HmHYb)TvS-Z*ktfZ> zJib%DiJaKisNuhQGTm6Z?NawS3D8{X;wt+(O%wa&E3=1Nf)zK9|J#DA>ha(nx}8P@PsHNy%nE=JA4uQ{}-;k}8sD*01|@e*1^8`hL{m=xWpZ zB;^T4Q}rd1z~Lx7?lv8O9L^xDtC$l$lUZL|_)Z9gsu`P&P+uGDJ!Q-`G6wHna_2c~ z!$xN;HcOFg>%j9cxpJ7Tb7!fRa=1q{Ppcd5lI41xWdD=*Lw)#G;X!>fBK#yg&MNY-OVS9bG82Kk6=6s=gM?vO1!uEM9H zwOqR@trrb&>+ijrW}SRp1IKE7bh9m!M{HlmUYP59UDu0m;ZI&(?+?cseJ&dA*03&) z(r4|rz9MXKyTAA7)6-SlK2nmMYpGJceA8&W2)90i_FXU-8Sw*4U2y1sccxJRSB;Jf_F z7<)e&U_+(-$K$(`btdwg;dmsHmX;>1yLf+dG0#SF=v7NhPA9J`U8m!>%=GFIN(u_r zYE9RfpxJsuuUm9(v-PwN*g`QXn~fmY9Vj{6B+hypRkjKZkEMsE!50<-64uwCAw*qUBhlv&7E93Ble+k+~_Y zp2s3Dbp88kE9~bhbab&B4FxX4l$N!p8>IAJyKi4#3#{OIvz59Abv<6pex9|Ih)y|k zkt`AttXW8u?o1l4kuRhr&AE#lj8I!5@htqNyg!by?FLZSSSH6|?GxI#?mC@BI$p${ zZoKcbTHHQ7&(*y0E$^3OrlhRquLfGWW?6341$FLRd35$XD64vOXv{*<;Ss6N)l8g9 zHfp|J+eM$BsIn8DX*8IQ9U`2o$Vq?Obi2f4x|m3%E8S3A-Sqa6$R(R!RP9}>FA|(n za;`)&v*PELCX~u}*fO-oiv6kkCNmy_!NGQp$Kkc!)88x``3%i_-&duy^`2E#{kWsm z`*oObZFYOsS-U5S-nZl=9X_W>{>R3$tIx*r)I|;t|84!lMc3hw%TuZb)A}fqi>U^_5FQ~S9+WEC9W%!=VSM^w9F*JI-=i4~h>phch%PuzYINM(KYnB3b zV|c@3Kbl>X*VVfYxU4aM#D(W}p(ENmA>c8wbTju|KYp0@Mcu}@zj3FPNUo^UwAAa* zI#Rh3b8gS&kl(jSj$XC5Nq+{b4Yr-?cH0u*+OX#LkqEI~JU*M{4x21H;%AwvyZ!?^ zcl2k7*~PqPTwjnZ-5OlOqe8JR06z~hhkd_lD_6X*(8^APJrO%!%r*S(k9t>IPY)Gd zxwZE)ITafr4nO7I$*HF_qI{;eymOATDvT!4jOW2lFgB7L(@+>8l#Gvai=%r+rGs<7 zn9bzj?Y_9~^6gq;Sz=?k2OM-Ym~EG+h)AF*87lBLqPQ3PRNsmiUGEh-&XQF$MJj6*Wb8N*qRF za-3KsU|cF+i(tl#T2if)Up+4+_Bx4V$F~g1thB)xV!B=-R^{cOQi<`!TlXgmN#Rqhz;^c zoh50r@r#z;6s=}Wl{Juy`1IRy&G$Lr6g&wYEo4yOThF(raO~Pc|gyF zhSdJ?0+JKgap)F{@lR=q@nO~n7#+R5;=+sap+IjycGk7)U7JALK|E2iD>kmhLoexErqTMXvVUJeE?o zJ7u@jybl#o+E2d}{ixhQGD2&K@87#~uh5Yb>KL=^su6J$f8NR%-x?t_IZIu0@(EBV z%HqOP@F~3;>Sxg|-*?YXk;7pn@N7%rh;7N+&Kx z#9avCoCuCz`*AKq^C;}|xYw6!5g1j`PcQI-Qg^I4NDi4C12`$t0TCCk(d+xEJO$(5MS~W zEYzy_CbAm3`3v<*Cc*D+8SF;U+8y z=l!(OO?nBVIVF*zB#mSv=mRu~4vXnArUCUK-)uq&aH_+BCwV)1O3V9Vh@!*Yge%0` zM&lhSKYqA~>@Cs*OQluY+%+A}{QLmyVryLo_N_)=5%VYS=Q4lvHUeWk6KNJn(vDy0 zX)q5Wwb>xdy;&E-YEyQJsa%`?y~k z1$XFZLok0fZ8Ry9b$Rio^C?DxM4U2K|wqaKvH2wA%5}A{lQ@8L~Cr;z&d_i=6U#ER3wuV%ad*-3~ zRj@ndTr~YOK70?W^@5UxE8I0~Zq{QLnUUk-b4d5Yxraj91*5Y+8auQU(*00_l8N(w za=i7UZ4=c-2N@D3bTIb!?$#dK05}OT>-XUX>$NGO{475rSNV%Gh+Q{is(yyKOwEdg z1I|!O=Qts0pByQV-H@dO2Z%QL4od61<8hf*Mb6^tt}33scfA$ES1Xdba2y|4PXADu zy2*HOqyr^jBcW$soB>~mYT^xyGAD}RvumzGN6UTlVyc)Q@uHY#Sb1*AvbhkqdZkN| z7th~Y|3o5rrKdVf2^Y$Q--h9qX(p3uvA1v#?08RcWdkLla?+afTYnoVfQYTfYfMTw zxxS^LL3+BHSJRmL(>O-TAVk|#8RK$&i>bY%bLCXT#A4UdG5Q&SKVI0~{gayzSw&t|-FE=8DJ=MGj*erS{ zt_Paw6L~X!;FJ)ndi<8UC0|gy~lZ18HXzv!x)Ax!f2CmXJH|cbWDciBSl;ttkNan(g-zkCG=~HBtad z-EKP*w@F<9V(^D(THUUWzY$!_2>O!|G`8a)t?tXwN}MZ{m(8!kTXLeB&DPy9RRH-Z zo29W^WrkKtvhwq<1+_ud9kFi@nvuIgTw1lO84%sjZL-F z9c#ByCTYs@k?g5G^r+OZ#Wdc@9tAt(v@2DXGoMz|as|CQ(&p+a6|2Ww@*pKq)lYlw z&>LL_LcYJ~?EJ#}cCf}brAPt^kaaR(h<$D0V-l*borcP+rDN^BP(UPoP-y788VU-- z;0?`b*Rv^m#|t4-S#<%AjI2;2)5$I8L~}lDHh45%OIHt+SX2pk2p<)3uSbO)U!)nV zJxom|hugy_mTWliWbPN?Kg`tKOZaV>a&6zEcXi3NksZhrFSo@{9v<;-)H`Rb`ul#S<)A1CNNGV;HcSe#f-q)xyy3gdVMV{*CHS zLRA+`Gv>uk`ekD=%N$INR%q1E&5&QGp}1)@Cy2KhN7CachbHb#mftFVPF2XUvph8yi-=NgFUW>wmw{zWm#d<6XPk=de+XiW&V2 z$Y>J811JS>3U6d;?+|<4II+FUDA+U#x1eO{Z_8Txc*l@(RpffN2sQ(sl>_m+@w2JU z-pyyBqcj;Ue&}t;NS%Vhda`;zJGyu>nts9td-7x~y}D<-*R3G}2{J{q%}Hd%M+%|@ z|E1;%4J!#2Bn~3A)fp(5{`rk&(&5gT3xO{kdguqy$*wEz5uqPw%$W`dlmwxeV}r00 z2|Y4L82A950n(qfZz^|woPu?FN37%^Rg^W1hTmI}+=6zE-c?BVTX2(e?}pIFBfJ2} z+e6u)avS{nNl7&c9yQQk6PFJSV$oH!Z0~&i!yld`%tJbadvqEPhcbralz3Z}`8F3n zGyhrtj3C?yno-oqTg7E%DXv0~eA9L&5)gFklBJxrMOg@f#JjoQn zzJ{2InDp&c$tNwuP6^*zMh68)W~ks9%hgCKrY@l_wYUWhIyp2}MdR3L;5mfW5|@-L z^J>Fcah%WVvr;iwTRDvj)rgavMKDsT8VVfV-N)ej)6(YI=&GNyEhJfzrhB3w_ zOy1I6tS7f6=E+-xvQXZv_t6bggeV()ZJLbE&C!p}mX!4jc`f6d z-VZLk4RTq?xb8eS+ri%BAecdE-j5*L)(hCa3A!J)6^ZHlk@mgfm3vP5VHov~?nTqt zY!Th}2DOAsBJL3Ry6X*0X7r7zzIs+ExM)(~Dw9PLKUX9kzX<sfqR*z2o!U+p>|~mlk3zZ|Jh^ti z+QRO}G)bjjB(px~Q@`;jxZz3(vY*KXHXPY4WLVLh4UAf*%S?|{7VVi}=I|3`bO z_N|?t#P48fr>~m`aK<)9j!q87`qqC*TLTLOI2I-*Vn*V>q$V+=CNUc$hc+=WizYEM zGdnRG2LRY%;sml;Sb!8Wt0pl!6Wa#`?~B#tfv`nSp#} z#t#Z8!O8Za#lZ&DXa6|L36x`F`WOoEF(~VYbpX&h&=QbhVgxc-IX@0FF@B86`k}+b z1XO2Z|I20mu>MzoneD@Yzm77qf2jRKaxej9nc09Y{B@iYC=cuctuS-41BCz|t}}Cf zsQyC&fLs>F4*?dI4*`ykXjoW*tSEFbZ(umNrVi)8;xe)!D7{@4A#7T7)2L77GiI_(%lnM~YYgAF7;eA5~`ihzM96 zAj!`8Q95FFz+V=}M?yH+KaPGRkA?9s`BANZGRFB)E*3_Xk1tRNSh5dy89Dy*t4*v; z{GU)`{x7I~z>D#}ZU3_Mmj`s~W5mDy0G$Bx{)grN(ERZGKP>@4`ymeu83@%6kp3H< zK<-C;AF@DTegqAqfN=Z(B9H=t@UJ8gRv&=-K-xdD!2W;P2L=!90YUb+G(dFyvj=j4 z==p%n-#)PTKmY)M_Simv@NWbFj{)-oEa|_>`cco1LjLWu{`Ku&rTVDRe~kKH!{ z^qq`FjDhR%7&Ay4+n73;5d#1Kc8-5_n24E~*q8zAe0&IS|30z1Wu1D#dg1+SzeooZ zPOge|?$%ew4<%Shwpfj}bdB7Jq_lEIo^c9}(HK&4`jgB4uET^uhDk#=^WSd3c-lc( z-E2Y2V{Hm9NoAe6&+z=}``wz_Tc&l5t-IZtgnHoWc}l|Nly&{?uI1wX{P|JBaercE z|M_jpp~ta{7>>j!I7-mKLdEv}V#~`H;@1pwkW}u4th37s;f`}Z!!A}-f3FLV+$o8AkWeD8n{h!HjsjDriT1E#Zc336`ijq zh@eA*aUULLM~Z!W9y!cr3ulq;1@QN(aX3cUjCD?%M*V9d2mHx4eA zXl5ZCCV7JPQLmAY4CM>;LMqY{s)7_8&c0-GMYhNT zRwY<0s`Qe8Z>55xHWeAL%Euzu^+I>ClhD(wt7lLe4_ zM?HIaR_I83+YHCabvLSc5f))t?8;^lHVa!n3dLIO{!QNG{Z;@V6lZ-SV(ATL;&^|h z){*wD3=*GRyB>E!>?TX)$h#%t+V{BGe=&VJIh1*FJY?L6kbQrpdVQyzB~gB*be0pa zs!PM1H_(udJgSv#v#}=tQszu_6ckZSj>gk3*JZu*vrsq70go4=v0mz1gH8XE2FjHD z&(2%sXN|X4#-J*gMAYcGgr{h&>x?d%p zcMMtQ?0{Zy0>76UNVd7KCBm{b0;H|Q)rJb}t4_K1OFf6fF30S zuS<*+#8@eeK=at|J%f|Yrg~Az>+yGPoZ)vrTCp;=JDt*ql9zqT?(H5kbYO2wN1dJa zhoj}gad~k@pyw4=P7Ns>sY!K3%=VW>jcW2JR>-*2#HEB5$t3O#VOJztS)wI`V9QA% zg6iZGOodUx8{T9KzMvXwB9b$BLE^Yn{6WQbim^fC5^n`7C|rS0%NZp*{zdsX`lE>& zN;LhD@2J`T2E|$-bf&%?h9*wSKL*`AU+gQ5Uc?`=q?+gi_@%67 zO@%uY^qt(iHTwEze!=whOx2T=7uEXGDgUV49*g-SvQ7;k>)YWTevovd@NSHQrzX9D zye%}$Y!s0xtBMO?ml1#RJHVz)|Fub-E|fsnaa8+G9ZJcat?7Qn32^sI%>i*ZX!ejm zl-h=vqvVEk!;@ZYr;`*xO-IYDita9Kup8(MbvNJITf6Gu@Ul~V=S{3C@|*^r1AY81 z;jRZdHhao0bk(BYXL7*qb9Fw+TnTNwD4_c|Wlil6@1OT-#ulZMd5(|9#n6R}9SU}* z&9k@@uSLRa=g^QC$H@b?a3C2H0)K;+xh;e~GfxsOe1(88%IwDx(O)i|UooRkha;xZ zP%b@`n9A*xtlR@0Vd0-k&Qjr(z&79!BfM5$VYj8%!7@0y{?$pm~!G+Aqs? zoAyocIbu_W87K4a`je2E;AhZy#OxfSZ%}6Q{;t0t=zm+p1cYvrx5TG?L2lwgU}qvd zsm=1Qt_m0h7_f2foUC_LfMA-L2)c(M;ZrU2krN`-mv+WR96Gqg-60fRPxEwSd18jg zBabkB)rgsFSyoL<@ouDZ>~91cnOtc-o<6+y+Q?yrHR+)y-C^k5R5)5jzI3ZmP~^ZA zu|1M7jG<35LrhdufYJIUmRR-OS2J!ouINyLXa^N1ht0pTFi%b{OdZ*6 z!4`h2D2zx{RkZ*W)tK@Y=B46vv%U}o_W(WoB5MMZ)tXN;cu>#jbgoN~NEPI+&C56O z8!^7mR~K8z+Orcuwcj&>9qt0Nv4H6<(De@e9H?iz(sra^h~O7Oi%*9gX$vHCRZC%X$zFc z)2gGpt1;*s>p$a1G=(!P=mpo+3$}bok98DAxU*Kt6vR)WFXzpa0VoQD1&R-e5 zp57p_c{7|Ao$PL$+~%{{t)U!;iB1rQIG*w+BXvHgXgBxhs13-{>0OBKA!b!$ZsEEY5O$mP-! z6T(HIOF8EJSXM9(fI=>UM1CqGzboZWz{#92fmt5OQ4^T4%14lFnsEs#NaiY+Sk6jn z5=vNTO?P}dw3?eV%$A*@Z_GYH_dkh4ZC5stD#-KPBdVVNmVc8ax3pluyklONS z`~Jg12#2uAjr!(=6!)erTga#x~ zOyFB6WIN1HJ^W{8MG6s}j7=$uZTGNHk02Atqv`4L%S0?u=0p<`8U{+KT;*ME0UO-B zXilN_kU(*_Zxl!gQqW1UFf*(Pd!W`((N$o^MlZXX1ACV2{S{LW9OG&zYGxwt@N>tJ zkAE6Loa>&e_Uo7z0}OBS^4 z26#rAEkZ5s6Jyqdo!)Boma(r-h^Yr$ZOXM=I>p1foA=D#0tG&A@HFIn(m%a~UV?7I z4RiJQ?(Dh9`2@_xvdC&<^n65Ytpb{N+HG2EkEn{#IAj#^A9fb@ypGm{hi= zpI8m0$zU~`TUd|b;>9cj-#bR7LO#T`%Jb%zPpZcXRUYBUlJDrt+T*%-8S08~b}*Gy zxb`jpDI8)qa)|M1FQ9(HdV50*CTkEhxTI`!B*Q6#v|6@Y@r1E(h9qK<`cIjrYIprU*1EXYAf|jtDl|^uv!3-NQq1@HJ^4jO`P)4-)Pq9a;!ONhqQc$@CBE8EFrpiY{u<#u zXzRb@2G(J7>|nson@{G%sj_6RwMDx@Wdl35OAa(ZrV9*JF7CGb@I+48ZG*OI%Z}(}*xY&RM_6H#k+f8j? z8%cQ@Ik(rK#qvq$E)xErxHB8GVdy~3q4)rSN(JYCOV7#xMOom7+pJx=BT_}YB|~0uujlgFfEsbmv>$o9d>C~#ziHR0(`B0 zTC(}?;W>~IYK1KTuFP^`6)1HckhxcSt}vGaAL8*e5(!3Est|cdxI$dz&=AqcpB9%i z$x!Hs!4{unO=YsNL1|IjanTJ8ovM^I{pA^r>cy%_=weh<*cJvzDJNx68Viq%Aoyg+ zh2s$@BYowikL5BlNckTql@h6;keb<)y|sZasO-OW_W5C%ry}GnEc``7Y^~~2?>l>D zKgEI)MQpg1la@JB3inHvy(FHl()pYbnya%@bW<|+IGX;Zty20$;glThpUX$F;{%sY z26ZAkQ3DyFt-NL)4HwIA&gNB>b6bn6DtoZdG*U_2IbO{7O8dW%S{O~Y-l6sIyb@5q z_(Ps-&uG9HbE%e!mW~+=N>w&1j|!IEIYlrZ-*l8Jc|>KMk)TBY$jEE`H#iuL90n{b zIcFb;T!JUf@zYvZLFWD2#38c`!0oJD<>vv>BFCiSe#Oto#8{8&4z}czL3U(*cTG*AWwptPtz^S@;S|v|CW}$(DyzS{ z^wLafUf50p;70RiM?6WbrY*KCmMS^n<+F>yunoeYgR4hx`*;2Vh#0gw#?KC4^pw1~ zjF<~c(zJw_g%H0LN63xqp(TyKjeTQQUiVKP19Jfdr*nJtspG?_BSn0-~Mbg)0r1R=CyBFA=^L0aG+E;>WkdoPkbe4zIyg)*9kpt7StrI(vYXUyq ziVv)k};^iPLB!T;jx9e{I*ns(77JJ_*yY&$!)ZQHh;ys>TDwr$(ij&1Yi z`_DP|{I|}%x9X{EY(dH>&h?k zd{dh#nmBGg?8p2R=o8j+iOThOhQAAuH#pAAM@QWx@4{5!4a^dinMjrrjqNs>28wLX zVn&xFRl^D?6(}t-VYDtXr@svy*VXji-Tr9RM*e*tJ~i8pK#KM9Nho84Q*Sbqx2tn( zw=;N}_m%oXf7fI=t zcH+~ji>onwMh9)iwAcCYSM~bYodcR_v$^x!l$SMU{zG(;c2?Q3naF0MwJ|oy90Fo6AM&nA8|;2r{?NrxgO48q}M(%0U0GTQ*8j{xoH< zoq@#TVA}(yF9+|nVUdOC%@|z8R6=Kj`DtHO_!SN z?7Z)}Gz#eUw8E#3$+Lg6*;A6sM^|Oh=@sKi?v*z+z!eG)@L-jDG9lB|R7FnUoXd)d znl+AW5p;r#Cc{6*JV1{wPc585Kg9K0GB&D71Fob}WKY~!l*0?eiYXAE5zz!4IW;J%EG*Bu-nzOf|>mWI~lBWq#+XbODg; zLSsuX8X0J;=iR5}*agO9dc@_n7*K!Ism)ga4F>B{QL9^2PT8i-Wahq&0z(H3{+v)n z9pT0l{X_s}P(0|nb;e*_uPD;7fm^p@Y09*E(C@O6XcjMR_ZB$WSKWysle1bYsna@z z@JH7!hb&(&4zysnXQhGxb&UeV?910`bZ}DHKPQ!eJZ(!qWs6MB9sesczq!QZFdeDR z)M<;8EE(^#szbe@v&67B>uj~z`(1KH+ubQQ&f!VxNPQc{DmnzYhhuYd(vn zdw4kfiF4Vet)^ntJ6i2frgwR!GirRU4x|~fYdJbr_q?&Kk~U%~s7Z`EcquPa^w5f3 z7mn)Je&1oXVC0rrp3|xUa#rvFMb-*yc8X&xxK-HKT8gVUybKfu3ymElbm7`5Mt6tT&N!;idk9!iQ`I_^bKwJU zwUtGgg$dwR&e;1)jR(^h*(j_6uQ)@B5yO`4^*yZBS=pvTCnR71AtZait4_y33S0pI zszR|0BJA;}x-|@1mb-u@*Mbr*ny47kP$TY2L|tAsfH$r;*dD->N#pk0BV6=mKuXZ= z>Qq8HyFYj8!^ELq!xY!k)S*q_YFZzN`el=&n{*+!W*GurxA%dzp~#~g z?{W681}1(L!G(5`EUgrqmrN+;k)%22dbavp6>WLv|%2HJ&$9RUXa5m6OlQd-NlUFABs>3_8-m+~mD`T>s*2{Stj z+8J4$lI<*Ld8F(<4)?p`CNc4dEzZho2@(ZVzJ=1lz;NFf>UP|nQ{YS|?uCeRz&L>x0t9M>;c)F>}2vzGNbw9#m(9^IVO zSV=rC8VM_V!^`aEs6iSgPAYA58=-*`X70~((9%Zs8vwRN^I{ORpi!{)d!T9KZ;)VW zmqdnRSHY-;arK0Hx!s1LOl$)y%!@MAU3aICT0;x0m*4$|@Ql&Arf<9{fV;d)LvHRk zBwB34c=3VYFk5?j)wT5*-3G^FWL_(o);mlAUaE6<1Q7maP%LHy0UVh!Ef;n*pg3AC z8GKIkGk=n4MVnj=VeX1q;Dfe#Fr-{>gU}(I#FaXKc#wHCqMXI*4V9NI>Y*9mWR^y0 zD~m=>>|qN@WCqqIdXWM(ZS3Q5Li^(Ner67#tg?}RW^(UI=};H@>4A&Szl ze0EmR2KA-K&-Zhs*r;itd`DMxyobgvuJ9S}uYh-Vt9V1;-Ne^50U1?H|Ka4vwr5<+ z#*;=D)fR;^{HYwB--B~-l#*Re6ilUc%`-Nu7}+UXH|8&P?e{qrzi}1 z3_6qNzN&v{kg2$TyIU4S&ZwFeOw`Wn46i;64^VVn*^Ng$Eak<|#}80o(~`Ul-XY@N z@LEQq*#XByK;D;2j;|WH4u)n=BeNIxyez0htzj)t{_VS|Haz6{Dch3ZBY`|k;_+!J ziubyb{v>(-%Sxbst_5aZX&hjV2j}rRK!Ig)%Q&cA`U>l>{g`d1XI$P{*<{xp!MR4ID(5-17ywtN$cxOmmaAcZV3P^mUcqS1_7AVx> zByh-FuTr$gEYfXv6UgUWv<=8v$Mch~%D)3Udk^kSdr;v~&(6y+_;Y#TRB+0f4;H}_ zMh+d$@WfSCmV~I0t6oN|k)fj2bjEA)I|rRA1lZCBH8h3m)4s~f>N}Dhj=OE)7n9au zC6IJGqbzUGQ2rJe#7gy`FPcx78DlJ4SbF05;yua9lt2I+^6x8E7!&5R7tSeHp|t3y zzT$4=R&!8faC`~wGquDPl{$8T(iu$9}yn?Guo7>|O3*zyomW$<*rj<(f3_K-w#0UaNa zp?(Zm6Eo$YpV;c5&<$R#Mbsu0>BOx7GmHY(q@K!y7m_K;6fa})u?sMB$bYXKVoIpb zY*4SrT59!JcZWpHy4?9arO7?E-JDFBiVX_kC@^uKZu(<8Eqbr1tlNA%H$~st1FeF= ztPC-FD73$RD%0dtTdQ7||AW=-Y--v*zjaT?>W@Zptt)7qw4tE=t3b~48`aWUrC6FV zOK(DAQ6(e)F?joFE93cVA@C=0%H~$xClRxNTE)JVryHOlI9Xw595W;@Hyy9iR%%hdMLi1-5 zR`Ye)Xt!NNes{MqbrRt6%?l(2y-zp0@l+U3J%=5-I1$XrKK z<6qf%2Bg9m({XmBKGVJ4hEG-(`6%JJ^sXX;Dn@+_rdZ`_{18g7hfuun0IyszU8`eN zrc;h@W1*s5|2H4(%Gu%P>I+R(rIMq7)+EeoBpesff%W3el+vG)63aFw8jCH?PSqE` zMZLAJ>e=Zi?Uwp8a_Mq$rP%_Dbk=$DGvRPR`_NE27zSpeom zb0e@xy?DGMQ>g`uh1Y{JC3`{gWV1WxDH+&1{6*`0+!I-z8ZYrVVM*CYgS@p=H3@-& zo6x~>1(Dx#<#LX#0wd)5cMe$tK0rA6S5=Z(5_-Kljm}l4j_yD1zm!RN5=wcCjyl7p zjA^~|4#f>gJaZxR)30|dqAqJ-8;lc@del7~0T+#(j()3peQ`k--n2aRv9i>qu?%x+ zI|kKJ4=%1-%}?c;L9pX))Tj;1%MY}GkL_wNYwwSdVjV z6&>tRCfjw3CEi~jnZwKv6vn&`1;F&7m6SKiM+Q~nMhs|%No$1bkF{2-G%i*-s5IAY z7~mh%OpM0@P#XzcmJA;d(TFz`Pf^HCiJqQx01v)nS@;7tIUcSThx7TqmsyXdX6pi; z?Gsnex{rmY8B(dU*b*Aw9Ls8FxWboVOsE$9ZrhWDfom70)rx?m-wEFXqSE_{atZ{@ z<%p7Ikk9+nxDcgFB1w(1VTeKI8bjo@NMang_Ckogo8AV3pf2-&P6EJa!_#G^oTPe% z=N^d_qUF zBZo(mN|gDFt31B+;jk~ZwzxNhRs09 zn>Ow3k1thhMosi*@OZcAp-OI@sRRhx^m*N@AC#bQ|1;r~0cn!-nJlz@{ulOebHw(~|zViZmE<9v>o0R>P%2)UuB6iJCI zbngv7rG=`XDX*mfi5$h-DOFzAExmfCKawAYf3K(DVaBA8zUKNw=c z?|eFO(lO$MGPRKz|$pucDc^y7rB_!*#Sykl~hqFa?#sj1`7tF=$^yS-M?anwJf;E8$@3u3ks$35% zS1GR6KH?QuEm{eig329{^5B~%P|uy#t5NEGjd`wng}fxXvbw5xLW$PQe70K_?gFA2 z?ONwB*J1hPEr24N#2AJmNpHttK!q8G>ye5I2RPzYm1njDqS$QwF?bzBBwt`6JS=;{_#+9T^ zC157#RP6{Y!1fo2GZGDH4V4y=)H86OA5QkVpFS$)rMb1XklINzbyr>(W=zGfCX2HF zhFSRiT~hq=^E3``y!nR6qY!u+i#QOL@J`HR4U$#_l;eHPpweiM{^V=oTNbd8uA7=K zBaytAx&ZqYJanXuo{NUZ!DDe09&=GlCu8<0e9ilgb`TfHda#WreYK?plb$F3UOA+q zrNMnmsUJ&B_R1&}PyheGmoD0UO-TT?Rz?D@5Gl^6(_O2)q)9x~K2vql9*4G;=^h8( zA#pPR+uHq_{^>bMfJjvbFsY$2YCq=V2EwY{D4)FCBby@^YHa(kScp}r#_Wv zMT6t0VAJ|Ek$9EJ+Gu_z+xQgf)a-AQlZeJfidwq_$Et3FQ^$VQ<&LQuo?%+6Y4urPf|jo?QLEW z6>;?tuB7SU++J!?1#$CGOmcPHxrRkDuAl~U$K<>&#>Yv$u>X3z9c)+ zbfq;m9WqGT%6N<%-tVxLuTpbnw40kMvR+!96=z9rH8zxGxUe`cOc(#WBP>e)9|u_* zI{nUzvqBKz$4L42$OvL3`v)PbQbF-`7Td((Bpl>Nd>ll~uK@3mAPl}mM@%L3awhp&maFwVoz1Q8{I>2#57{7KY)w=TrLYfwWL>T z2ETK}E2za0@;!}bxNC%_6JNA*AF;==g=ZX;rsgV`gV83nYU;PbwKJkEy=w@-Kj)9Y z1hPi7^Yo-E)|F4R+e)Ov$Dg!{fA{2Q_x~)hDbI))v+)G_~Lqx{)6zgh&% zO9^Y%r^n+NNo*h8N3lJKV82t&_}Z(#m{q%mr9nvxDVe@LckSbRRJxhEcQkox5MGA5(%lp zU>Ij%B*0*J$bc7Xx^r8@#EQXCuWO|BZzx|etY=|@TM6GPp;Z3oM?Yft(8duYthze| zR?#CMep|0G_vLecXfG3dh^Os*d0x%aiAX^-MvIV(F(*}h>Vj;hK0@=sz=JQ&xg1j@ z%Ia2q+Vrpy!D{4rCq^gV+93hO5gMzL`*+pUg=Kb_&);-3O-9zwQ7Rd|18mSU9{M2a zo$$#m<4fT|RZrNv7>t7cy#v&xuD;7ZwFYLTw9{#AJ48?UJOaWOMkd5DtA;&~V?!5N zGu_0LAZ>%@;h8Ih6A4_-7~@1Tp$p?9Xm83EM6b+UamZ|P7hrg%W&tOTDJa5(EN4+) z)g)ej-8wY4Heh$GKO#E@r2+_3+Jj0!hjhNrXS&??+SEM388kC;gh2XbaH+;&kQpCY zYlr2iptMt|7=^X&p{J}YMXCkt@4ja|G56+*zL4ejyOvipyQ&MRxln84nqW|b2$&-WbBR$;T%wm!MJHnyQvN)9Xhc2;N-aSn#~Tc}3%Cmh>7*{5rFP9NW)v)sLXq8o_un*4G{(1tmyYZ;Xe z`soN{+c*=?urX0_WbGg(GOr-b(4kuEiMBxfy(gn&VQC-$TJGLC6I2$nJQribg549y z4<4RZvE;*v+--6JHc}%I!qRqtiOf8^+3N|(J|Qlh7RDXChs1X;{m#wYHg<|_h@6r( zP*-^%#{n*3_#hjyaeC&}VU%fCh^&8}Xh&E_e_ta8+?>#YHn$-?)zeQB7rp9yRHaZ;5AQXoRePy*tfWZtGRf}<< z+!Pzws-X{Cvm==7p8s({{Zp{s=zb8ihp;mn%Q%vwO|247s{`GBAKq1++b}pKTJ|jJ zTK4D=MwWT)#6Cv~?`R*=1{|){8QCGyJaRc9;;t^Aykj|aKec`z9FLKgP2x4{P0u zcB~o}ORS652@JS)hfU#XJ%SvPo#i*NShNQ;2siuY9`fBg_g#j8j~g|fs64y8(Xz=0 z(9db9#QruY!PkFw_+5bK`1tt6d@wSQXJt-;bmjJ7JgTvAv! z9)a*L{bkd0Uj|k>7_JJF$d1QDo1vjB)wP^MY5S01oz?Y!s1)?@=iYZcZ@cAyzu`m@wRi-^hnD#+{NM+k-O&d-C_b`AfmSI&6D~f$8L9em*{-$? z+zdW1K6x^Or?V>%B8&c$_^72q40m_?H>ZX+H!EI*JVQ<7zSZ`_`v#RK&@EEL}fVaxh1P zLvB;&OpKfd4Z$2TuCQnxI#9y0kXKNbtV_tqyz7DCLA6jduZQ##><#?X`2?#-dX`nv zmT!l!3+&VS6z9Yzsae2BbshZEb{;UhlfvWsmDh;#COQvyf+v~7_pS5>^Cn}C_OLaN zd*YMwB=D{E2J)6NpECO_{+{bY;sxf_;{^1^>Vz3*Mp8x0kR?+hn8R<%mqVc|P0TKR zC2rJ~(h2dlbBK8|l4KfI$W`*eXH=w87W74gC1%7T21pcJ=dvb>Ym$bW9Jwv~Wys!b zG5tNLTKC139sgK6z!CX{RLQR&qMF+SWhu9+|Lb8z9~O$!D)#vZULw2zFEO6~ zSJem2!@!;XiRb~*Y|OZ59$rEW|F_H++}rcr^Q>iSkp9N8-jeY+!_O9hq~pB5s>y=7 zd6mB13hIzvN%c@~7RJmEXo+aKHS)`RKJxB}ZyLrl5686~xcL{VPX!;Ahn6Q0iN9uh-U%jGkmkknD5dSFKc;LhyjwHQ?8lGTEqq|{61wU|`pwdh;twisLI=?>EdY)3Z* zc(Zq4djOVk-%u`~PGpV(vqzEg$m)ns#;7qoAeMpOk}f1paMVRlved{2o zUG=qdU5&LNzfpuJW;^O^W);;bAFe7nA8;!nRP31tFpSKpVM(~vWly@){+_JVTIN2R zbmcwkd*wbGd*$g)R0m~G&*$lmR0nLwJO+4kci_IkUVxu`s{1@}`NHyzMO}y_!WmUXYB+SmKXr?WpLwNeWn3p`p&Kwiv?=!Mf-biw`W@x9fnA=4o`?uPkEz z#2cS5TJX8GMG8Wfi1uX$Au^gd1zZjTA#ia__YFES@hNO~=x^7tNmQskKxGl*;4_<~ zsyHK(?J1_Yj7D6bfafgX&q_{G>p1K@VXs|CD<$mQ!d`oX_b7H{@R7Vn!g}aY#XgI; zIzLKDTS-kxY=5Y!tJMW{g~|SstXgo;Ij}ZGNNj87^6L?0`|t8tE$NqQ=TzoV?^`N+qJ2ytz?GBSIFQGj>fz>=^PptZ355A)IIM8g@Si@8wgy#+|oAxG@g& z51vkWsI;3%```!oOz7y<{K?xU!Q2e8MZv~Q8lLv| z3DMl+OgEDDx0bTgCsswldCO!(^>?kH)92Ij++)rTns!f(q0{GF!QA7>lkiFoCz|%R zr4zTb9THX)7m=#=dgkf@_>6TOuR_4_L|hRz7?*z;w}GJn#IVWW(J;7y>B#b6b^uHy zk-fIvOcWN3$gp%&Mfe7`D~~syr_BduV~-k0Y$V4RN|!b{b8@Do5_tMgr~v8;nBg%C zA@HP8VXCxHd0-|hm?NVxY|859gaON=ohsnXoLY%Sa zgNK8@uz`dTVu*OWK4%OKU22_CFGO)2%OoL450;>3FM0TU6Ii^Ao~+KK7KWgieZpG< z9XNVcNsj4mO1q`BgFfzN78c$~PK{*yQ~RSc=dd($rYda>1q}&}>&8Cwfdz(zWIkE( zjo2rdb$~giNAp$MlB4>}qnUHvLoo4@c=*Y_0EP|Z+hrSuS4NjEc^CaQIK7F`_Wt}i zbENG!gXFTKxMZ72b*-TLVEW^eB(^sV%HBzjx7-xowEc@?u!G}8m1XeCmgr<_fma~s z@pc~40*2**fqj6|#L8rzQAaVzZpQzU1);;zd9+|16%IdpTB z^UFsK}I@V7I3 z7{ur>GH|po*-$cYF)%UkHL$jDGPE-AH8k6*TH2mY{jSIsk}{#~O|v~zT}@1C96b+N z^Or>-lJF^U3|*emubhWX@QpdC>~%m4mV4BR@$;|9$I|Pn-A8$TW^G>eEe~`xX`Mf{ z9V4Yq=}4RRQ`6Nh3C;hzT*T*IILg~;zT42 zL|a^Xqe=WVzY4<-%MC21T`Q-I zj7Bt~{UnnsSl6^UJB`B;bv@EOaCvEKCA20?I~uJp$Ta3LriU`N@gt7zEeJ1_Q4~(h-KhCyqOh^gM=P`W7a}LgWfyOD z4vOKBR=DXx$&_1kbH1lrR!N4t;jcFfvh|f$z>Kk*gusOlyfb-D6Pej7phnEA^1xQ2GnYpjeyHJbD~+GZk+ zx`dud87&SWVyMe?;+ZK?p$)pJ=lE3lU0*g@!}SbBQFGY8lc`3>>PVR4KYvNskY10@ zzBE}A;cg>5wO3kFCV*Nn)!Eh%#|sFvvXuB6=lCob;b#;om1!O(D^}BXFW=KcI54g@ zAS^YO)A2N?!I@=hgKR&Hi245O`h7*@{eqKa1ty7?SjrQ4#f+c?62m_azDI~6K1wr_l?r0 z>G8pXFb<-7GR++=N6X7q2{=!a``DBu9Ln=e>I z=>d&HK$A;kUfh_tEi5WIGheqzL{k#fDCZG`DxdQ}2|MfifQDJfZ~hB*f#KAdBaVl> zOT1(j>eQMeWICyN-u=Pe97~%IO$=|gT4a7@+UHV%}H|c2} z)+ykDc}uKC4B}+t0q0b+J*k@tC(ozgT5{w>^ns`>_$t4naNh}jizq7>aC$s1-X3$6 zAhRI&Q0o+oTQpLLXAWBx=bcY>0(olc5u6?8o!2wBQ{=c9TlSDqIZjDZPkXKupD{*5 zk%W8zT0T~F*vuqiLq;aw;aTKK5|zX`rfr^?Nm(+#eAnc_VNJ9{({W;4uS#32I!<+% z)Y#D}xGfAA9Z8Zj!y{3_kQ^c3PZW6T+*2Gs^)PN{$m$*guj7B9VF`@NwQY#AV%nM> zt_PgbvShcN;2xO>JN<>Ob9jTz7$u!Etn{DCXSRX^q-Pt?)E^(9KXCzcm)GE2_^zhm z8gdT&F0eVtOzU@)#Jd%hm4v%Rmy|NNRGKPi4(u%|*Hv9AR?0pP)X!kg_}rU464D8H zi?=Jc33;od<*s9MXNX#lt$+ytNUQy)S)+SK`XSlZSh#aaYql7J6uy7iTN`$ZM4L zlm#Qz$VX9DF&8%}t()0vWX{W57Lkx=H7;vt9#URHRdvMYKh9OFR5*nw~q6cSH-1QEClgKipl-o%d)hH7DuXdeOx2m$jGG!0J@9fM#f$)!z*r6R_qL zDOGD7CvXrPppfD;jC#VdHJp4h^?+(shFdYxaQpz;1*=)zvQnd0@kq=aRyEggwzV4J z0pA6oSyFP!y(Z~_*98wyzpi;UtvO*mW35+x7fykWgBSddnqUoIVjyHkq!5 zl&7rPHpD1su8VGrlv-aJUaDJg0;0F7NH^{;UeA)E1EK>g$VTW#o-IsA{zmIAR!3!j7E<*lrqfrETNax6&E?F=oMk;&la73 zvGrqqLW=4k6Bl9Lu1K~axxBqX?9_~Mtw4O|B~8iQf@|D}I^G{vuh8aNzpS)$EnAv^ zTeL}{rGLs7HZ@PI6UKxc_678++5IK&)lBHqHwiJ|ah=Wc8>#RAqZ!l@fwVWQ%bfj0sE+qU8XXb@UV$r_lp=->PYNEp+)a zuxZMEeJ{#xk!>0M$!}N$klp8lYPoJ&(->GfY@Q#Wh5(Ng$#|eJS=dyU)7aNVV`hp? zpe?t8Qs`@gk5U+L&c|!V-HN;kCgRu8XGRCU`D@r8(C4LxJvz+xqc;bB+|dy=T%e3N zfyiqx;a-Rje4r}`egqJGxyWW@K1gyf<5Wv_Hy|lcMfzo!o8K=)sbF8ACIRevw5cQC z*%+n$@3OvRQO-udYM|SEuc$YutKeVY+rRL9;rm=}5LbbDfOGt3dSz;%FM@r%0szcx zzoY%Gd!Nxuz%zl!`Y7x$Gl6-1HExL5puItO4QYFP9d}i4u*SVdKDn;IpR`$IBw-r; zRvLrgbxbM~*8QG*0SW{+{)CIKzIdR$pZLE8@L|IHK;`_OCHOJOA>e((cKPWbsfW~L z15A4xcG+uTxZslmQ1nRDz*GXzfIf6NfNMA*r+x7vyOh9T%ytzr&n@{mPX122F;BZS z&G)a?G=kRQ!wm+g-~($xX<&&avgFd7=x3_f^m2ao0of6u{|5K#(+jPWX@h)**%j^# z;QlH9>G$6#k!~F|01~LXDACWsu#N%8u(xDYdRN5`3teA{@r5Vgx}k3N8f-uE)2h=x z5G4+sNDtpIXZp6yAjXz< zJ3zZ37luH(MmNIED{KQi5W2p9`*?0pwzuHB!6ABCPf9ScK|^|5fYJg95^4)}(M$W# zk7svkc3JluC4fC;lzqXidTo>{rXrm!u$=~xnwYZ@1(gO79vNoghzmNA`MBh3`W$U% z(Kw#Oa01`;ZXixt_IZ%J2wX}-aVQN!&VY4*0OdTp5Wsf4zy0aR!D}m^I?9WHnw9l6 zAA#u*xqj=JJ`&JjNC7AP^fQoEUey)Ol*1dsKJpvs^}J!@^OQrxTO{iPsYSRKjFdwQ zDCu1QGUfBab)Lwp>Bae~>Hl6r%c~jsoY^H=?DButwg1L)=pPw2ctpOzc*r7u2YmGK0 zfohYUO6@glA&=0PIF?HXcAQ=^qVKD0hweM{uwJnEE*20w9ukH*J`?Mvvu%_%;(lGQ zX+Grvnf66@HS7R006X~O9QQ{%>Wj+M?gY3Z92cDm(X${1UsU^{>-}=s1)Az3$ptfm z+~cE))w>XJxx?pNBIKkIcF_=|Wh^EX1RFaXMw-X<(gDuIMQ2jo#8 z!6=mRsbPo1|EaZ&-rshd=#W=HY50+o0kRyULQA7UxJG%H#!$W86euFF0q_hj_!IcI zqe7AxACY}C=>MTrGrHv)d@A?YB#XEt|4CzNeVfE%f38?~E+9ZJRFpIfz_(I4;=f@+ zpcFd5zz}@ktM^-$DcG;3WjS;BOT8g=HmKme%NJiY!?n- z8Gk%Fk^ej$=H${TF;i!JbVyW?OhadSHz+RHAE&!Z;`>N&zKH*WeWi+={V$Ris0&t= z(vUtxyG3$U**qA-GSl0SggSu*KE?tK%VSije#J!dV73XGZ_xYeae_7VVo+qv-SG^YNX{J*(=7w3NH%K;Y$DC;r10XtjT z`5^vJtey^ri7-%z~3ToCg#Cn}XuQYbHo!*x62s4$K#oNW-;vI@40X=gP6B zxu>sS{R6dQ@v>>eYi`@x=sD+hOLUv6Gd48eJS??iD+sW8tE1|*z?N>wxv%kGSf@|X zH#%lzsJ<^ydX*md=L!KvwC;F7yUyb{?Q>2`rPbD-M5WU+fvyU#`g9vG2B;LcdaUXR zqwbEn4(z5^q0p$InsM0fdJ-zYS?dus)b1E*3Oc(MSr|8k9dHH@d|5hh3f zm;K#7+ydaw0X#m0#M!EO4uvysdI>zjwJ=mndJCH>+YtwfpV|<>;7ALL3`NKz2*GC-MCR>pGnTo z8t4CW%aq;MaHZMr8135jZNmls{$vLKv?u)YzxHBaBHNNzr>}y|H-Ie=VQE+dW54F# zbuF&$2=~_Z(}NxZ3o}7!Hw)Q9q&LsOSbCfgPZ=da=z|^S@9V? zzJFXQ%sjtu8ppCD;tA8Vqv8pY*$m#$?@D-q;Zij?XuIm`qPq@bemfubdLisS(9sQc zm*w(>s9Wxuo7QONv(c1~g_aC?{$$Y|fxL7Zrqko4>=u~K6)U$P zNKSsoj9+u<0CsM3b{rG)-v^>kfGY-mE{>urZ&TfQ`~v13COR8HL>91WMW7?~0y!aVWrw@E z=?laCyOnkm^Rwz+cj?J;yl23}6KAJa@P)Mgr;h72hc`MF_BS`n9$=LH+3S#({2P(C zm-GYS``{zy#&E>_qhUX1w+*Z)*hkT3Xf*t=6)}`l zaG@f)-4+uay~j)PrTG6xoXgI2{9Z4(F4gB3S}RFD>Z`^OU7YEt8wHyc*e>39I?WMV zGuLqB@WGy3xb6GOV!JeRh4RO1;<#w*<meuS>jC!YofwV9SI_FZ&e;7ljO?_3Y3$i) z`)O_CfEA<}>f1@4Z1r-?HNOLm5oi_ohK7BHqX-HD*^zDe&v%OYK(F>@JoNK|m=lt`@jXOs2K51pI z#Ac*sV;f420GZ9eW$Uk;WmmMTLG71FM^~Z%fL%>DZ^cLEbqiF^bC>Eqbo2^&hdo|* z=-;-liQNDPc^TUA&l1;4UD&O#8vO(9bC3N;XU|{o?lPQ_Ke|=e+vx6CO~{de)FBsr zBw?|*=RRosaThEZ4_*mW0-6paHp3;eklwT@#uYJP(d9??OI+RSZD)X6_R9XV*tlbc zJ1v#$36&b>D=M{+p8MClS)DHWO^r|Po0YSiJ=6c;*9=)3!Fu{@`a~;Q*k`^EMkXz< z)hl#=w~WW7(O<_zV+!gT@}RFDPVl_e+fE8k&qa2sV=}6`*Uj9|V%IyEslZK*=dIm! ztSrxRA31o<&7i+w2=b~#!8%xwVj2c9jX{~mqs$ksCZG@VTpUY46qw(-F;VCQ1#-V( zI4s3#F%zqD6R5(DDHHW5!rm?rcykQOhERHQI0fdwCHrher}#u*cuXM=KE$Tt{^1Qc zWHVR#$!|jQVEq*``5pV})BnM%|DR^=D<0D~(e*~i_pVjCC z-0PcQ=Nodz$0(+0IowJ6wdF0c1XEoS22idqOMgKT%-vVp0o$=r%L&`D@E;ne-41!= zsa(8&bthmfUuzxC#327Zeyl~pzMK%kGV+9 z>gl=*HarNQn#(XqS?$sZpP2Pm(bCzTGpVvBRo{>*w}AF3ZU3(s)+<2W*JK9oL(Z!? z))4i~Igtv!BM^s4m7yUTH`^ zZny{e+5c{sHO8Q~+_C=c$qSv1DiR=gVnvTI{2#u;&$R&6nGNmuACl*vls$&VT$Gf3 zD6KFSEo0SxtH`M3BGEO&%xb8bwYKMx}5YhGObJ6#Q|C3DH&Pq=~0j0|G~E7@a` z>NgsVsavqmu)JV3a3y!eoEW(kdcAzlef+;zdkd&Knl5cLK!D&0?j*RoYY3L$2X}Xu z;1(dbg`fwQ;O_2zaCeu3yX!r?@ArNG+*vdKotZmp?e+Aor>pkf)tqj+s=BHM^`>F< zHK&8dgXeGGF?Ne3`cn?|^d#ccQV=6~p!3Db8N8DlAm{>l>1(onxF(MQaTyR&t`(ks zKewQluX`V6mwLe!C@`MxRh6dp+h8(3P+R>F#g#0wm&FxhCEVT>X$6rkXzsS0%Q>Zi zPw)~iHClK4LOfsZXDx0fFGYhin(*pecQnsvzHii? zuU29XseHONe_B%4iM~#(A+DOiSlmup1_^*#K>CMB0%vu>w>JS!&V2{ssW|RK7E$WU z1}b%l(+nm~VgG@uJe;zI=t+NAa^9>gb@uK1oB&go!;+QLu3O=8r)mAI(E5j@+NQ6M zyoWOUw3;v2ZIJIIMlM*KV4ZwA(+5wAA(wO-B`dh^>}@?3#g+U-W4F!G^;*t((;;!; z{Y^-Xi^qnsVofzq0Gowkk#xidAY+GwSDTbQp2irWVBD7C0G)_#C5o!Ki_;y> zMsqN2aK2wKEx9>UL-(C!jk&5z>Pg#FgIqhJLN(U!L2fU8Z{2G0=ZJFh>(qSO!_de$ zi52u*aoZ|O0upabA{^rINHn&ry2Z@Y3~y-J?r*N}vb~}yev0<^l=D|Iy&U%%fB*2a zP9VR0<9pr6AULXX;tc`-3yc)R3;ETTnTpchTnbLdop$r=!;F zN5N+ntB@BZb$Q}5&?_O}&Qd^AIl<^A>|uvab8rH4xR)!>VWGQa zBH)TMybm_zvCE+KL)F;_t>7?bc$vhoFcvf-wZLt)54oX7$K#G)oWENp^4>dC2jFFw zA$nz(AvKBnpcAdZWH-gSblT`$;>20jJcOPXmEJX;YYO^ks=pJ+uG^Hmb$P^tSvYL> zS!4PImOxuCIL#iHgY5jVwrdKQ=ySXWJ7v2V&Xuked9GRFo+V2h(R+tk3ks=R#|aa& z_KU!|eND4PK93L<1Z-gV^JfJ~AfE@@5VQ1?xZieV+#X>2EN_%)K01+nu=F>umM<>B zusrD2h*F~qsrju&5K?7h&q+Pv-aVf&M@vzn7c(045X1CSc|c%&eQTwh=guH8)stq5 zJV->*VarVi^&sYtNFQxR?78=4-cgJ1n2o&oA`~4{8 z8|>z3aGg1uPV#0ok6`$>7VRdC6d^#<>*J7cCy&y?MG`lGSGj32HYU|4Pf)h6sX9H& zyU=??{>A1grNBF>5kDp8+3M;9@S<}yIo&quHDce&xTOfTa3mI7^Oz3WE5OtD-1pa4 z+!wj$UUfn#1@0}VgOE_Ygp6lXE%>XMob>y17#)@g*#o34Y<6X}5AWSdZ8=RYB1S1P z$(#zD6ZVQ=OBS?Cm$4SK3$&B%H`5C&FDu?SZxo2_;}q#;J!?E+c8yGJx$T z{Y?R#j&lLauEb1w|EGC67o~bi>-u^BBkHH0seA8Og6l5(I_2*-^_B1M$KrJGiDi9A zFLQ0*RZVgQ4(!PV##gIJGoc+!a+z0=ZcWnFH1G_`t!6#X$R`|sU$(9%a^Zg2D`#KE z>diSpmdHC%SUGA$KK4y@xUbUJzjxIC;vRQDO2Yf=`AdJ!Y<(@kO@Q=$SSH_74zs}1 zHnX6YPyRQdU2jY62drYG)V;PVXNF#fi2rwra`G5tVD|b%qI}O%+Vi1>Pca z3IZq`^~}WuHi!p>i=89;MdeFpGW86_6{ZIvEr58?pu9R>pI*BUUY{JDWu`cv(lNV$7lh}Q|Lp+^ngF_%4PQ61K9k>AkE{@y$sw0lu(64~}3iSWf*686!9R3DD)TKV( z2W~qOcpGs05g~V%FA`yW`zy%X*Xy9pYTDP!5SFHPaoE=bv4ReE?A#zL4%*#?c3JP( zCDWnS0lR7-X9!C}y0+<1X9w5aAzVmTjbl9}DpyRG^@d#wVQE5_lYKoW>TLHq@s8aQ zp$$gR!8U|v>{@T+)*=Y9+_1yoUg~=FA~3Yh=~zEZSQ_(M~$ zOT%8>K}`Gp%d|1K7EX}mrkxC-4Rnx4;;U=HzXIWTH5u_5I3zv%>I&oUe8)VfH+F^5 zY}&i?f887Adp<>)aag&gw=$OGZdGl;PO0iY@31MIEyXWsl7riWR zImjv*+eYBky`{ax4cEeh5&5(b^O5N_tG*Na=N8aS!!hC0G>@R_vfJ&CP4_FdSQ4d| zPgh_*n|5iiDUHyfXZ8gSZ)-n?BlGN8&pwz2T+}A|loQFMIO{hku!=RJThP34XPKh; zLo5=f$>!Ts;b@34)jY#fiBJvEvb zk4}_Ujy!uCJWkd7(kH#+&XLqMwYXaImlLXAD$j+%v%EK0k0qOB7B4mVEcfgjDa0gq zLLOOm*B>O!a(|jg=I8ws{~HoT5ge&Af#^vznQ7?wG?}$okkgPn?viGIAOOkuZBUzT;nOj>FLu?hnfA~HM~Y8IosiJm4VVh#6XKU>1i6pntml1R;4`T8p*)@*i1y$ zsV{gIpG`mm(y6SIw7>#I6MQB%ETh~2)=5ra0XbWoQEu_j)F(El0cj%E$#K@nAK|ev z(Y(fglvx$>!r67i@qdLlumbX=t0gJ-V?%leMPY$vnbPbZVVpppi;Oa4@$x^yFvR-u z8begGIOHi{V><_l%547ems$2b$I=JgV(+y3E@0lr3;H;>+5vtFU>^B}k#QO$Fi&b{ zFW)E)3Xh6v^ZNjIkx|NsL+_m{^#D5s(1d*O`v5CZ<&Xj}N7ZCtfWF8mUKVJaJ5(uS zLNh>50i>X-`rc2k%_Q zW=ejL+oB*lUdLjua)w4;8osAme#qT2m413kewe$kAUjF$AO-ax1$)0C-Mw;3QAA)f zKQGdKM-x0$Jp%kDR;AcSxDf3=nR%o%^+k+BiG7vVG}mcF+9F9(rb=N>*HI-iS zEU>>?h0F50o^?*9(Lj9;>Ys zq7Um$0{#Z#vD3kr80Vnjo)j>VcFt9UfC) zn_^TP;*^@Cvo$~FQj{@h(zGhWSv=^u{=znEvtyFjfX{}5vAAr&M^{^AVqc(cmB3<> zJg^s6$Nmq_sX*O2fyX3y{*}8)UZ$)_#aL8_1U;$E`ZZ=gjj`4-4tWC7V>kG*Y8Vpa zQ{tSC@lK!e%7jsmb39@rE$~kW`aZRO*4zl7s}rP|bY%cp>+(1&Qg+`gD-wSnSJh1_ z25V7Wa<9-Rmh$zs42gTK(N)}>jvNm|tfB^hKnC{K!y9JItn{Md!69 zNCkzdg(mXKD9SzE*Sr;}5KD-VuP~1)hplLnWsNI9n!o?yuu-{} z9|lS*cZ@)Us_K*9m1o$KX&`=ppyS3s);@qq8mlS*Vm&hZZ?9bP zI|}OH&tkykSYW|n=}F!YFDT_JkORxu`86+F?7aHCc3IvK{wwPQVHHhsE1O!=Sr4ck zNsVXZO+r|kUhOASS4FWVQ#;ZcGyVYu6|g#XFXRQ#LaHf9c5=*HP#Ht3nGXRf`EA@n zLR~kPgiH{!GGlSR1FLdmHt4PLP64Y(Txy*56=X$>Oi+lDx+=h!OWB0B&?+0mTS$mg zQdI1FoZH(}Q1;m?AoTuxYF60GI<9V>Vqa6j%`0|R6`+%LzCcr!^JqiWff2)9+{i!e zB(JTxUE&0pd#9wD(TMz-CYZL)ZA2{`RMhr`u(L8wc!dWUjhuzaU!9T*O8xga0oo+f zR{-tYbJ@JqeT{q@(&_Dj($7w?g$KF+v;f&IgMS(;AHpsxC1gUZS!OqeDAfV?d0#o$ zLOS_n%2%tkpwN5o!ogRke|bL2ZXABJqny5y|I2w@>G@2f|Na7P;XzL0Na+53e(6cD z3-6KAtb&{6vdG_t8bO)|5C?Qc$D2yd0;qJ1a`s1F&mdXGH4~ z@48OxIRRQEJy!s&+;if*)C~=48`7R_n$pkqu%QRJ^^Bnh8TGZGLJv`UFU(n&ZjTh_ zP|Jd5hU3h7=pHO;8=@YDoO;wy%dp=S*;(dfFVG^765|h>Ic6RCidk$iH=?6ak0f;C zb7U5y^s|Q{xfCMo3~`;B_N*%bRbxL@=)-e!5)8z?o7|&rx74E@WSNiOoo#ky-M&o; zh0UaF#hu+$DMnT8CP7%-)I4RnB_b9PO6!)=_i*r0i}i#p3luA$#T*{`&sxfzHl$bR zffZ;>hW{GK)e|L~Q~y(j6gELb{gZBDu?uUupuB}Q0)ZPPh8Yhsgp-0%`!f93Xyluq z&gfYc;Feronuk zt!ztikKg8!9`U?SZdo2F_!E~xNUwv`r#QkIO#rjSQxaXhQ_5xu+NE86r~er7`N1Iv z@}`{jHCFqp8&6*@rt{*1F z1EeSY)0K=d_G;zgtus5-6;a=+vS2t$emxkoH^z_ze15k4dLn3Njqi9RB1ATek5Ab>qF? zmDx|++|zQltZ#cRo5lH@Pb;T-?z}5Oj;G#JS*YNRPCM0m(jj4tZrgiR?hZPyQ0+{b zD{7sG>I))-Uu7K86b~>yqun;kdD7!A;OBW9b9vPFlw|)_*2TXccncg?Bc9g1o^=;= z5*!8YUC_IW2Yi8!D^dA(``USr!wyU zs8>E6A`blIeg6|+Pt5D!N7*rFmh*SRADYxlgS$}S=c75W=l9h=%r4z55!!N^xAEX9 zV|>pMW-nlaMReXcRH3~y$LMGURuYp{-umAH#`07otEd&xK||fB17nAt7iOChj1qaz z6YFiWhL4Yc@B9jpk!}W6lG0CVDg5LvHt<7ywG_`IRz0%^!zekZaB zj3g3M{Ra14J7%lewVw%Ze-h~K!i-MA8Bi2;&>@%mFyQ7mW`eo^Ds?J!=x$g!!W+Sy zcFL@&+?X@#sbH}&{g|byszT%vvTGct+-ETA?Qi-LPPL*Ia#$l#%!?gztWp#iLF#$+ zp(xVW2WveHR1_Jo-6oR!L092Uv)XyvY2Ej>FP5aQU(%@H9HV~xPL7rjq{Gju5C)=) zk-PIGuyIM}>x3D&`I%bQbbQjqidqpa!r%Uhcmts=R-xRSN1>IhD6Q z9md%9Krt z&+rdqxrYy{4=KKQxaeQAYGYQr<{bsE`!q(qXBNQHCj9x~@Ns7uti(#a{vcO*JT%%( zVPgmhRvcE6Zs^*6gYfYKw=jC*8?APf-@=HA?=&J>cbRONe8@9=UR4x zddhoAeericEQG@UBRSOQ=D|4_wJrJbpz`5h8h;<%q+G#|-sA=yG?+L)UyM+y=Rwh* z+3d&QmMB0rYML9%#&-@Rx*Ll0H2KU^JQ{R##oR)iTi0-#qM&YOLTWK7Udd!#LQgn>&6heEMk@p5#L&M!SOR zz0{z$N&Vy;ctqV;A6%G0slbG#Q%Er4oxMhNjUs+%fE1Lh4sUwQNaCxjxJ$%NJ4p zll$qwk*Os^t@`hB1RuzBUr{QR{Hs)^oeZ-%qckn9Qsrv4ja-&N(BSn8WlJ}J$$mNES^y;FN!N40Yol)Xvncq>8m@!6Jg2 zd+*H;({hFY?^Y1i+$*88NtWJ7>(MwlAV4;Q@CQuGz&&L&A$ zRfH4Tb#02~qMt5t`EfWk=UFN`Q=l*nHvk=z#kN>U^M6R5*1lbdZ(l@r)r7W{TC+xI zX7H_}_^*-5ZIT{9HEPCb7d*!C=lyP7zEi-vm+$+0?`992M5$VLyKz#}`#~>gYf%|c zj|h)Ax3;I4wM}7aJXHx%|b}k84vk0;WD%Mc3A5fXO3NcD$eVmqGBS50))C`Nbq%&LDwfa9K zCAp_lNQDrAQo@%h3TdbI)sxph6*grTFR`|m}Zwj@hOMjwPWI=rEXqJ znPTE@lfg);0;}yo)g)8%G^S55( zIOUx~U3tUz`Lp0h^omH;DjQ&J#)MV69iE3#UIot24v%}AY~K6W%Cb$;yPt+oGupmR zPuJRE86$^STnRA=rSFlfz3+#$A`3W%QPx)5Ba*Vcix2u5|EHjGFAR|x!M{*Qct+?| zQMvCx@cAU-if;uy`opxtF$2L>n9U4o)F0<|1yBzp$`3qn_F5CJy%H$n*>Bn1yv zrmMIOJXNYOxE^7d#oWQ%*Ts9?l%GhPBdEaW{3Wy`AkzO0Ny!fE3=g<+B%6DN{9fUCQD>JdX@b3zeXjRwBV1Q;$?Kv)i3P1V9dl$R zko7p-#LE&beqLcrT!cTxqS+mOq~nkI8viCmFDX&-fv9@51Cd!qsG>!}nHxK#Ad-+J zx%K;40_y#QurBYe6uj471{2X`j{iT%PZN3y?mlcgvSp#43q>bv^MkzZ-(QxqGJ0@R!R^vQM6}0?Cg1riKEp^+D<^T`Daa{KaW3*K5nf&5Qm4 z9_4mu*vB260&4Z}NH==x#Pmxdo;5-o`ZK?L9zT@wQ+N#HtWDa0KH*KIhbOj=6FX*G zvFpYxm$p^vybzrFyBKBj2|I@wU3}t+s9G4J+DfnYPQ8PB*YC;J@43RDKxm*BMW(C+ z=2o{MXL6TJu@3B>=Z#kzRUye57Mi%W*|`E=DGuSQTW}6yTUgF`1RQ1s@=d=yClI*R z0hSC7aktM8U`3lEshF(ECd0S<6uz8htY?D;VA)2B=2mIt5*#_m#4Temj($gx4C=hc5{D?9HXBvjI?AXq;2E8&0vzJo$!x~Pr z(({J)4(0}BO*7UOb`{QLP#(lb%3;j)&ex?w$L_OM;(6v$^aPq9G&KP9V~NGy4_W3r zRIdOF9-30y^RBEF)qpR2!-$2n9C5g)h7mj|ICZ5 z)s~IUxZy6inV>CU^G5uF1tjMubn$Fnn>^!6ROJQx-7ocxG4twd^J7(4 ztOwz@b76@j&-X%H8(_}as&kv!3q7_IU84uq#ga-wpSF;;)$Y}Z+a?sBy7f&?Pp!zP z!83avL7VQrh|;H6p|u4x;|34E#C>O945RjcUOJo~ZXZ((k>*UwTa=FNbx~A6@XN%t zmh&rQB+_^C(lUGSB)VHS#9~&cxr5=Nk6f>q81v(UN1qfu|i*-;VG1{f1yeBx;`!Yq*mH$vh7{ zHJ($%#x+r#lwH3`uVk(LRw7UM;_bQRLptai6qgktCe(1z4Ss|S2J!t{nn6D!}17P}BqX`X`I`74R# ziu+)~%AZM#+k*3BMU+6FXt&?TIh3E0y0@*gXw}v`wmYq%%6Ib`BS_nFjWNM2vl2n3 z#_rh)>}6jaX~bC2*cv_Ho}|B(gCX(sLa>6;6o%K+hQ0Z6Dk~xFitT6kDF%y<9|>qg zciB4062NT-y@LYZsgqpo-iMoS&)R+X?|36bvCS*nJ@|20;=wp zQ;N5Lf$A4KinkCTe=GFml=Lmt7dz6oIA82OZ3${RH$obK?KrM4Vs;;H(e+x6TK$e( zI&MBkA4-2(P0aVUHbTVSYo}VKHeuVJ3rBa(_FNXN*O@8?ra6yIqQ(~D#aW1_HUctj zF!Pi1C#012u+nn^-#E+km0r!UV zTP0sYexZzc#1YE^B@I`wCTdr@M25_6!+4J!`*8OF-SGQwoiC$TFC!oErtrf@N>}4V`=59siO)fMK8bGc zO_231kl6y6AJHaHUg3t>3r*27u{CJV<1fIq30yaUfcYnBB>i3t6Mp?(^uRC(n9%esCk%JbQ-HFr`8w8N61mMbIZ zE9Uw%vvCpf>|m=TO(*-{1LA&dzM*%&1ZVwO_l3kub>m?aEY z7zGWc;n+H$T-$2$fl;b}Y z)M-wk-g9g7rZy{hbWEd(?Ay@hy{1`38n&w9D8rOv7+J)V>40Yb0k(#=Oi|G?T@Sy8 zj?7@uvLy|U{UI@n+-RS8bHnfJvS{zVD+jI?4Vs&C#bm* zBW)^mr;zvFUB`REhHyKIdhzEkv(KfQ?&~{qk`^s+Hademvvf{&OQ>xsgWBP1&Uer` zvxJsgf+NjDl&ikaB|AeL=U$IKO`fXKc1wuqZNsh5&EJy`?L32smpkTE2YE*>0Gk%Y z<>GA3Ka}HZUdpCi=}ea+NQm z&Ccfx=yyak^q+haLzDigmYeA<&>+SUnD4BEn#Oi)SBrlUlN+-C2i;I)!b{wG;z`ec zZsVEd?#wnEb`c{Uv%zaH>^_&W(cd$V5z0=b?Dh) z2OO$(D@z;zVzS)`2QUy~A)A^ZPPviJo4>2O;2Hm=M7s3tX1w#sAn<{!^JwKAanDVZ zuQ;CMNPx#T*-W1firDXM_a(~nJ>!g#&J^H6Cvue~Kdd^v4u+IAgs>b0c#nfQ&At_0 zFIlJ)L|4^z9#Em1LDDV7Y|#l^Z5xj3smG51>OVmN_k2Hmq?#zQe(7LO4zeEpiXu9% z5D^q$%;kbd8cPV4OV!l@@wHYZVLIh_1db!+rF%sv%k$OX6X`YPGLnCDm%hriP!8X@3d_Fz3s^ zB#l*FZYXSenr!kjlW4u0=n&JZ*yU=4en#tms|%~X##)Y9jOiFb;2Y$478@;hx46b> z$9Icb4^s^1;z#nsY0c*JgmiI^f;QZx-KHXDy7R%i&!mD)KcHXg7Ooyq3&o*B(hj9w<&tVF;q86&XCd@miB9}eZI;;v?Mpti z41^~uA@rwjRbo05>T85*%&7oX=6ytQ?T&mpTqoGp6|Pc0j9^k zrS#qGkUnJqMrFxiPls17%!9O&a)FQ?vQB8Li=&Pr0bW5w;;D>p>44Q3I=k?Q+yFm>Kq$27#_g=afGrLrr;} z2^T5XV23kc`A45s%7N!$DGyw0FxG1nV^Glf2bw8W7=n}=4cb;No9hC4Oz-?}3?z`@ z>DDsNtza#55>LgrYjlQW4sqNH#`wBtOu5w6iQiQ=I5|btkwMG3(->T{rgCa6|K_!S-Bo6a`uoZ^H|!DCHrYbc&>Q_7XN~YJJy_r*#oRNU54@KNDau zv3|T7vkLhRtBa~n4kEJ$Tf8ds@K<#TD5VwLIz4B!_MxeoH4BLH5h!(fT?}(}VMJJU zb3F0Mgo6?I)md;(`NFA0ZGohJMvA@@E~3YLP%D)GLHo#T^XM?HK*TT&(1fYs5H**i zQ)S=PwdRX%jUOLny;m0(;u;^7xpbV&P>`7cG&xNrTJ%gp-~Z;3&J4VF_&p<9tp8KA z<6T((Zb`A8DKL6m!d$-@fV1fK#Y&0}u7)|wS7ad}?07rNqhVKvvGFg}rQz&S_F&Nl zz!`X_x3`!j95EPCWG&JdU*r^VF;=a`&VVB`7?EvWVmMJft>s}RFi0fUr_wuJak~&l zz-D@J$deqz0V)^Ao1yN)pI7_j@v}A+mxE*c+-o}%4S92T6 z36A9nI8=u}Jq$c}`Y6k<%OFS)V;X*3V$rNjG{Et_Tm3zOn0Wr)SEsTu$u!BZc`_w> z*gt*+8sq0#m<+Z*jU#c;vP~q%y%A7s1}~U3YpX_(4{6W$bC9tBE$GwBsjwNOlT7=q^anb$mp01DR{!-w&?kb zcWOZ+gZ(3eY8O&;t+8T3f4v?phoZ3Q?wyWOo5tAyd&E4(uwd0-;;?6J|xs1^As@JNYILDgV|TpcU++FHLVXk? zy4MM6#psC$3t2aJ zEbu@O3>DGaWA?;1&>)U_X#PWhZ~X4AJWq9baH3?GDrqRM=F2pG$xxl9`WHH>Lls2x zoL?$ClORtGpd8(WIjq2+S}j9_npwaDr$xHR5nm6qm+Cij4Ss>e;_Qy4V4Zo(yN+fK z3uDb(c5`M!6`mfW%2JH)@i;|105`@Y!_HO)4XLb4zwkDV0}-p-77EFAHZ09Ry5^CH zl0%QoiLiWiHA<7UaUHAqFfNZX6Q-3zj(}*9%hrTlFQ)Mao#w?b7mtdYmbnKVQ`!q3 zmJ=T&B&l!+19#X0MNrn$YguuoVbzTNP%1ORYNEf1Qr*foZwQ&1??Ub~$&DeXu4R$2 z4X0uA3ffrzz%aFq~;~Ur&G4FqO{QJ}|rE<<}0h23av`qxa-| zNtTi-?}Bm&M09r~YPmyl*7GiZNw&K~c2^X(ozfn(a95PERg1u_+8w5MXA4_VnoPEE zXA4>x@^x&uq>Oq^@BeWc%0p*Oyvo1xDu41mHRNvamG0eGrFZ8HOB>EKb=*}iO&uS~ zG}Zjh8DmM08$CYs$8>t!5#@w@tm^L4N>Ct~gzW~|<5`=!l?3RKUD+ml7J!$rlW3d} z&9+`FvK&xH=7RshBao;hX!Qh-L*szYegC)xBdWJ4rtNc?elg#j(By{-E(?K5j6g24 zz#^xR#wH<4`D6nDZg{@o4q3-6ep~=b4gl7lxVl-@j7~y7R&cn(*)ebtwaL26#;NOw z5`9>Akkc{dnNTs=63123{3+myR5%!x2Z&&#sv72Vmf%V0{q1;j)%qMDi6~T=5tU1# z>>>6j9aop<1Ga3A~zGsl!;iYD=PcRE81{jUD++A+knvdR$*dX!>wowrQ<@$`R+Y zzg;U4Nwmdyx)QCN*UPVAZZ_KY!rKgMoZdnNnIN20C^>V*${k5L?eysxHfvGEBkO^! z5oOA$Vz!rXId6W-G%n~Gl?g?=Y!rnf9J zx9y(<=~G$d(kL!AOAs-s=!FVr+l7(N*}be1h{x;MA7i{wz-foR`ZASZjAOBz@|&Rk z-5^Kx);yD$_1QNL7kUFl?+-4>JPLSgf^>Pd)HA7p`X*p!@& z>t1k9i{mQ#b499WIMpiMntLt z6DwIa{iz(Lbr_yCU<3+FBHq^e==(gV-r>?XP&F#ButW82&Oj|_j&E2==Ko#Wc2QF0me&PM)tmzr%!%Tgt%5zyD5-(xc&{>f|=A^K6(~YVuKy^8=50viAqJE2?bx0O88T&=;K4*xlGGZs3_RbGwL~U>hi2b z&?c4tJi!aHU{0&h;SW9?=evQHK+{t9^o_|c9J4@w@PeC3Yg*Be_OO2hSy^a7rj{vW z8To#JK)&oqj!1`bK$`&=!)@w$Jd#MJMdGrMpbKvt{D1{<_*1Svb+Meg$#}VWuuVSY&aJ8!12zGn4uQJ#Q!|Dr8 z*~hT2ukN1BcUZ+4yrGc9z=u6w-v5pmIG)l*X5#C{4L^El2@1NdINV{V ze@CA&W@FyAgQJ2$;-WG!fQ+yYat2OXV(^SY1|Y%iP(4i17`8KDR?f@AWfH# zUJMC2`EgnV&OtMNg2rP%F*n=DS%j=ZJ$2m|;~;%Z#qr6S)=jJyCKWk?%%P7}wM;UjOq*tr3Pb=I0x27CTz=>|S;{DHbwFAzBpeTbWwjdu;QGplB z>MPgmCHklPy|MKZ%-`AjVD@1VPjl244cD^viBw)Mk4&0J zjh94~Ci8B);0zI-^cj$N;+?|f`1+_S>ZJ2m2|vbCr2L09l?N+Lqrp#QmXXK9b0?ki zEWFOG#U!dx1SPl`ZsW!Y1bv!=+zVkkbg~$x2VU`LCL}u()tO6xgY(HRJ{I~*0_8#o zygL^qL+jKIS)~he7I9H(7bUnL;zZX+^?(hvO+ZT2S^EqtIC^HaoTxLBaM=dhZIXGcG0Df0+d zJV71)ML6_kQ15oH4}KH@7am`DT{vI3Kp=aoX_Cor`*ffcB>`F@#&Ae4oMR==yz5ws zfh-GXcynkoJZNZhcrzlMhtYwa+56a)YaM?R2Vs`Vb5>fCTDpEP1z0YaIh7LUYQB-P zV;%ww0q*RDsD}&=v7uLCR1G_#mq|B+__@xKx-Ey@Lb%9R^mGzk6*sg^+&31wn}&@; zdO}|Gb)wZ$)eN!8ZKNODo5?DKDpsvz`4e5otiBQ2Azrwd`muE^9ZEYi4&kH@j2KOV zQ?^o!YI%e<`IoI+()Lnpuem2v_jWu+n0RoK9#UVVmR;H1!k_3`bOFS{R!3jCB{q0Dul0OrsN&DihD92hb(g0Vz>bm*|lp;g|C3HA74{R#f@ z@z+z@1v*P8?D)aW%gLkj`HsM(vx`X@c)r-nw2V>Llw%ouFJUJyKs_ZPgd*3D!q?lD^p5 zuBYfJ)@OQ}o@RZnXY0Av7kZ&yXzehUo7vV*GuOu z+s$^{@t69`Y~?Tax3zu#4*m{yw7;9byB*{2=|9Vk^Y`=jvy=P-`~&TpB*i|=QJb2O zgJh9Ha!4b^lMhLg%gOtDydNom6hsPfilS;rG58q;e>74oev3if>PSg^HZY#zkdpB; zo=U+x6R3=8Af;0xQaMT`QFE$^)DoqVsTQRm)qxtz!l<&n|Mg9|RDe;1sSs_{hgYMe za*U=KwMV}ls3XSG2`LJztuy-f)>{nLTQBtAn+BoQU?fdL=v?$Llt!TbNE(HajHZbg z$whPt#&9XtWi6UUm!bC=NU>P2Gg0?)q)eKP6s9>yb!jeACe1_2rYn$g=$}ZrbR|*& zU4@iMS0m-pHAwYnK2jzvplfk;Azg=d{)LoF*CREg8<2`=5mF=iH&Q0uNHq6d&NX*E3t|KqdU%TTt?C`Vgb~2q_zT(RPgLQ|wU1yhG*l4%LWu(|0I;h$7Tj z5O%8)>{j?xgc6E`&_ZK2`Gk*b;R=`Hgb{|6@C!frL;(Am!M;|Ff+9*_-iT=Ianac0 zFjGXVh(&oMN)+(|Ge#tc1ga@&h#Hh45=A1ViX@RlX`-g6NlitvNTy~YMWj#*kt$Lt zL!^l`sx8t*4n~qIy#IxgCkio=`l10^D-vb!G!@M;hEAe0#?VD{!5F%Vt{4OMR^&ZJ zAB^NIF$Cq$5$B-%xndZ`aGp3HV;CjIU<~8LB#hx=F&Vv25!2D{Wnw1!oh4?W?&acg z)V)nC#e1?$ETbH8ySRf2#By;bWr`KzF3J^mi+iY^xL4fg-P2rrOdZ50Vmq}GpNh|5 z?-skMqu3+%P-n4M?4?fP8}SWw5&N)8JBn|`xA5;5`{6$z4#0mG4Liz?rgS^T zPJms*PNG_NO}j2-+F5o1W!qu95O#gLF@^0CyPOK`W_ELUTG(x2x3k+*W4nXhi-g_V z9zY6Mbsojp!|l^`VP8Rc z9;%V$p&G>l)jA;W=yalh)7j|^yNlC>wA0n;O0I`?R1UQ3fvY{8o)qQua(ct=mpYSCW~wt4El+c%q2=k$bX=X~ z%%WK5a%VPb&2i?U);#A5wE0ixN|e9KxtfxlYn=ILd4aP4ulic&T3lV|EW}8zbFPEu zU(UbK=Jn3?XyFFu2DGrqxe0Y|c5bFx&MnR@@Go{2ljAILmQajyt8*(}=~8DY>MnDZ zq2=41+fn8Y=ML0e;jBQ9cR6>V?%mGa@ZaOyhm!X@_oIcC&Vy+AA?Fd4eAIaq@9<;J zV|W#hJI`QL&pPYyN}qFHL@zHn>*3kpyoy)%n)4=VZE`l_eSXV%2kmcl-ltOM1Lq@* z|_M`m+ z&H?mz&^d@Q-#Oo*7cdnHdrSp+g;PP9PQ-~&*aJh#@mLGh18eb7u5y(NKe!7u^0*6? zfV;#XudZToHBQA*fr?l0l&KO_0!r3U$*@yY3hY#shO6l+ow8Mis)h2kRc*9fM`hw_ zmdb)BTVZ=AQS)>|LLshIA!2`C0GG(d^wVJA?u$!rt zDAP){qQ)L`g56zpNBJJA2Rvu1vr(qM8VLU&HHhk}!D=Yl8Ky>{7w{^)-qC6_dLN_y zfsz-h3Aj2@O~N=YR+C{*Q8VG6rRJf9E7VnJ`D%476|05ndb}q$sGCscX0-%;-KuV* zZfdDo3VWHl10!6nmZR>S>Q0ngq3(kJZgn?2_o#a)U)`(jr96+%!Lv%OLjHhy0R663 zt5Nqs^&rYWq#i=Q|55)z{)l>nq{sFs&SQJXpHOF`jV9TO6?${ zcB-9d^J}#WW%j7OsQZoj2L1!;0D3v7zC-y#>M+V5RmV`KLPe-1D3zczY_5_d*rbCT zQ;KS9JVL6w4kPg9LO;;mZN9ibv)<~2J4q6>`V|6TAi_>u^6R+b@ zH&G{|mn5Bpx@kHM{&ZanC2Q;2XrYeIM$0)mA0-QP0r_-Thsl7yHlb>|RF|Xumbwj9 z*KKusl(Lm`7(EufjML-L@&)<=w0xny5WSDr<56pZo`8PAnu)n3h6*cs?_P3^De;a=rYUFS0Z%ZW}7e|?H z{%$C9rvFUJ^mq4nN8O(Oo>b4@%ijy0-u^x)bC&-slmSym3j_QEsGEPFe;^p3ltY=X zH)W^l?-=IXG0e8hnQb>?ww=Iiy9KlDR?N2BFx#%lY&(_Nc2j2C7PIXb zaP2}cY>#KhfM0h+`@O-TW0*acGkb2v>^Xtib1P=gHJLr9GH>=XZw@nW&I4=4eo=)h zhnXwqfhiwn$@5`*{5T){cp^y?BISZ*zl0IKjFbzmy&nDzNO@q~8{vNiJUfrxK*|H# zeiNQev>EU5TS)m}-fyFqcaXxgmEMQ_0a$n#Ec|0!-HwzGF8(QA`)Bkyc=;FL@`F?p__a$Je8ouZNhgM>Ag!F<-CFd_9i&dOY*> z=FHbyGGA}ad_9TzdJ6b@IyicU$UwifL^kZ46AWI0!RMjnd{Ka{GYmE#4K`mNTA~3s zy#c2$B3CpN4bgA0Xac(wEI$O6Urqth3|v22v=A-8^;?RT@V64J!1!B>*09@%4zN3l zPO!U(Gm&?P2Jndn8@n9t3R=4Q+5Pt_~GLQF0h` zLI^tHJiPYdVg$vCkq`s0&X&^kDtak@Z6l)(<^dKlEn( z(2Mm$AJz{&SwHk<{m_f`Lm$=;Jy<_&m*w!!k^XvP_mC?<@OKcR3WhUUxZ64kJg7kRygu#ZZp zFH2jRGOUmlqB>Tz6-{SY@m4(SR4WyEwv~;mxmGT0PfeX=HMg2mx;4X^L1or#YcBFD ztog{FvYtZjX|1NvRM^*9P1S^&stN6s3{6!CO_c>Z8_KE>$|_9t>_X_PT*#B3COrIn0`;6q*Nfm^Du!Yn~>mwQ7wr zZB!ey)>gH}t2je-K$(uJBNeGmsuOB;QC;9UQ=M6ob0VgIO+3RxrNK_gv=*D_v>M>`Xsm+C<;O@`l7O8HPqb5L@w`X}rw)s?t<6;x9` zRMRyW&wRB23hG)Ys3KNS#ZXY-HtI&`sK%_LN?1qrWgS(*I;xa)R37W7V(6$9@ZSYB z)r8el7;5TXjL_3jVYO1NM4464QF*MRN?AvhsE5_V7{jCLQ3|Oi)Dsj91@$B}%roj4 z*z43f&=s;|)I4k)VtE2|)s)z|Rsg1!nsU+qCVp1umGeQF9G)A?5x5m};!J0<5@#thnN_0b8(bZBvL9S4tJd6=KB|V8xYGMR6V1T5(nM zRY=#+HQ@L3RZ%(rK8(=&|x)^q2 z-57QWbXSOVR{*-JD{N13RcFN&$BHYS6<2dsTrF8~wPwYY#EL6LkI*Af%hO$Pth-vW z?n;yBpjMus6G#sg}Fd zeV5v~+uhGlX1BWsSNFU7;Xmjegy%c=JCr=^9;RBxXI#oMK@+47CfY<(o=G!l@MM`B zHGWt#@32v-}LVt7hTDe|tSE7dXGO?NuO^f&z}!wfO!P+K#}Tnx`tGZmi8%w;Gu z)69hJsmHLH!@BWGb0w?CYmj^Du@MyGBIN%zH&VX2nRVk*vlOmp*^8l5a#|$(A z)Z^30*P7>$KW|=wZhYCiOgUzw*+^w(v)RnLaU1fTW+&R*WA;K-eq+9&Qr3`-SVQLd zoBEqV?|F)`h!tZoYsF&Lig~OR!~tb zzdt3y(IZc@)%XwB9q^Pc{`>i9@SMi7L zIp87Gsl8J_$FftzqhEf+@_l|c|5EMWZ}Io==vQj}i8E`b`#64{imlaEqtL5F$*%xs z`PEPCbWh|*rv2o%Q;wGWvyaH)NQ)}JO^E#LMB;z*EX0rJQutnhzZ+l9cOd(XJExw^ zH-4qu)+%@MAsG=ifh>5vVNL z3#<3WpPvJY6dfP$@%uPc@wejpqa5Tt`DXCud}hvle!%od%~Krz?%yx-haSC7rO*Gu zjLkc>Vv(Z1w%bVH6w3T$#{Gf&GyUn$=BMX*euZ`Y{A{#$dhKtx~nrEDj-!t_u`JV)*`uO3VPL*e0fAcKaf2IG=IscXW@?f9*Mb^yUJFNIK zcwTw-|#} zKMlWsb0R14rATq*&tE+8_Q!PSNo^ne;k5p5eD8St)8yUz&)(VTq_*Gv|8EcZ;k%{3 zt-n>3&%e9hcPgL%2j9c;_hHBPXGs@uoD0b`nNC91mbJ;^Xct@N%3N|}p3JX|eDTSK zvY1@iM3#|XwvlZpB)iBiR84l1-6%@-lsze$<6>fDf7zd^%fWIO#mW(KBqhnwax^7# zbWDmIFUM1=oG2$!n!H$EOzCp6oJ<*Vs+>l(mPcuXtdJ2JZ3)Yw zv6iwF{lhYrp$n~mRgK15(N+vivf`|Gy2PqsCDCLn*-EBqR+^PY)2$4v7F}j#SvfS* zs%O=s*;av7Ky&mO{S3|3YxO$1Qg7Cq>1w@2Z=q}SNBSe0uRqbB&;tFb{(`R6dwdpM z?+f~3=^kIXuM0isyUMqW*7+Xt9in$#>AE7_ZR~at+3sj}f@tnO;yx^;n+kH8^H0#4%$@^7hJkzZ`4ePR#yf%>KKPdr@TZSOfPVzaJ~12CsxDUI`&y z2{Bj+PoT{wu^!@iJ%o8Z)Zq1y&T(b6dOss16JKe`3n56%2%njd`-Rq+lxBO zl$+!xsw+3k&9J=)v<$gLZh^;(L(Aegv`mgeE9BLcj@9)6*7G*G4fcofL)ah5k5T3m zth#hwbs4uu26*ThfwL-NJ)XdBs-e6`Oz+8=!y{w1O1m__su>8rE!>c`qSG&Wj-N9=A6!~XZ^SQj{C9ioKYrYLBUt3>Wa((Tv{{6iE z8({rEPC33Od{0tC-x}XWD&|;-BHwoG3&PtM$ilwRj2ySQ+k$*=BPp zviqp}D8+j_1w~<}c#>k>HSQWpbDwgbg}v5Yi!$rnb-4P1`vQeHjv|TUC<@#S?kl+Z zhWiHcH{CZ;ezW@yY%iuF&wbDRABu6`cR!#S?lyNDu72o#h?cjzpTqNo`xS-V9qta4 z-|6l|`(L|X!`|iYg6&0O6u5icz3^afLOVy?BUHyd>K>&`H{wR9KHrEr+X!P*ZR}Pq zWf(lDE=On-a)d@*6Jx4VEfZ^EQ6BpjrDOj}L7r;T;7>Ohlxb?2TDV%<)JAKWrY^2# znOyk2h>k2%U<%;z;yMaVL(>p;Gt-P}n&zfC#hDhS1qDn?(~_!T2W(B%O&imOQcYXa z9(D)Q0VO+{j=0*zbfF+egCue^NWSS|df{qc(-(O^(+}mbSHkw?$IbNp@|$M5EIY_9tgRPgXXSHS~a$ySPF5%yd!TGuDbWjXegR6Qks)O0-aNj2sL5IA&ZR{{h=W4kaf5IV?twT5^h<0>r9{9GA%%mH43; zKWfR@ayH6%D3ZyT5yuh6K}L%xMvEv$ix@_WY(|SLxk9c0{&_eN!_meuj15s7Z5-ri z;}}MTY(|ABMulXKIZkG5NM>xvWjsg(9=rzRcmt@A$f%GERM-sL!-HhTgG9!I6vl&O z#)BN-!F#|S4-b;$$H0I@`KkOA`DgMoN?;^NW+bS|7?3FU$UW%ALxMy`f&@l_6vlu= z#(-pwX-;7bs3VVI_Yd*zAL89V*0Qnhhd9$L4(Y+D5hc0m(lNZA=c~OidF^nZy9FrI0ZUmzE8B5|gJ}>CL?Y<3< zhbWnhB5@q67sdFI&G_MGw8-MPy&%W!MRD9-49D#S-S6G+DbcNPE0BB0kqgX_*oAE% zN0M=jhV8}nB{O~`GJfQkkO4C=)l3w{Gk)ZnI1>j?yosk8Cc)HzjfAUS6ksx=NG{_? zB1Z%!Gk&Bne#D!wDTLocksOW=Og1H^1no32P2l$;1(QvgDT7^Z%Hj7gCdB9xGObK2 z?qE70?`%3_$M+B=nh~Y0=?O$Jrnl*hs~)a|7*}G=+2(B6Ui4wW3@`&> z4+7E{Mw&WiD3B(^NE2($H=|&WHe=DUhcwk0X$+8NBK#h@)a97PfSG2d!86@VM@cV! zF`DBS102s6#qoSm9M6}+@qEb~&zJ16d#V=11G$GciNUtPwiF^kt-LrtN~Y;_EiIzO zw2GdkP4p=p6l26BF;&bL*L$ap#0s$*`8x3itoOv%Vg-KN2RspIeR6PWa9VJBa7J)u za8_`3a8B^5;5EU8!5f0N2A2kx2k#872;LREH+Wz0!Qi98$AV7%n>Wc|~MY@Sm`+jI0h`g=<&i z=QZ%p$8!Om*W!5{%KQu07Qy~E>>Kg(W;_>1ItQ1a?yd0MhToT=%iMs z%P$01VT>ZU0I$*;`#g+&6vpno`g#1i2c7W$@R}B*=6os(K1}nfzLv^&z^eM{xMaJZ zmpnF>OA7dhp~M18gH<0j=Tm+BR-bq*@2)?_8V>XGh5C1gjNgDO?0DHqPyrpt;Jw5nA-CF8QO`F zVkDh`c{_&MWA09(4q}#=O&!Hbv68wl7wIZn$QIO%8OfQ1JT$LApgA zvTRxc4OElvwNk8fde*9K)u!jIOe>RK09NJFi$JC@t+z|;5_;8cVYi^y?BVusdfgsp zkE1v2OYO<@rajf3Mw{&!_6*u$&$j2$+kA%OJwCnhKOkuqz0ar7w<)9i^bsg#G=0Kn z%)j9C7GJ75st$dn8mq>%gU?CqROPCizE*8iJKCk%t1h%xbyGd)pz5u9)Awq=nomd6 z{c0s0Rjbujs_>%tMV1%EFS5M|eUalu<%?V|9$(b+BJo9@+OPJDeD6fFDA2Z6BJ9P~ ziv~KRQ$>+R5^Z#M-CeZRWAsI$oxWIKEY8$Z^i*M!O>YrH^*eg27_Hya?}@Scef_=|r$5x6i3@|33f)+^?w?T`fi>0QHsUwz|Y?Cc+2WEuD z9j3l163a~^(^uSueSEOk0A+Blc*6`c!^E3rq!}qTnK5RJ*zBnR@fLKz05PHnUtDGIyD~M1{H6+$SPtrCBY7dDuKG9rK8JTq^UVStE^i)>sD2 zOXejRG_RUnGGz9ceKKtJo9|_j7aJ~1{muOCWSPIczrAeZ@96I;+j`O8vZKGZzn|>n z@9!Th&-9<;A0~VIhx>=ie*TgE^X1vt@!QG)!A`+WV9|oeeo+p#U$kGbH`!b5ZT6@3 z4tuYC&^`*%?{h*oa>Bq#yJz6 z$<7RCu5-0>opYmetFzp>*IDg6;;g}Qo%6Eun)8prVMJSC{rs$omxqHyM{_oZJ$pyZCC5mM$xM_tF3Cg+NpM{ zJ*rtIs}|KpE6gJ#(Q4XF9alZ7kEBkCMsUModAJtu^M|H36*Qcqss8y+4 zXZ4`Z#c4AChxy5Via*9rCoO37GyRGFWNM3MKZp9I%%ASh6cv7fKgXX>?NsC!`-`cb z>qNg_>M!@p{Z)RIzm_C&{0(BSUqyX*TGaWQ{4G?zT&ho+zs=v_?}Cn=_nU`vd0msXqfJkU>QHS0d_y;Z))svMLx!Z~NF_TrfVEBxeRw zXl*wmnB{i_v+3PlU^If(VUEa?=jfeY8mti6qz@yEM$k%YHCBW6sHQ^mjmH z>0JLX>E*#-9m#AAHV0dS?ZHlZtM}+Uc`|4YT7tHqGdL0)r~Y0Y^aOpuDN!Aq3C@R1 zl!V%RohN$3xY`5~VdoM?J9GpvjXqB$ZhnjaO1HBpW{81_et>1wGw z87+^>#fWHCR28j_Hbk4~@0Ms=v?JOTHAVZP12hJi(dZOLq7gI((Fi2x^G<5fW26n$ zA|4%7j@%M;Bz|Mw^?q(BJAw4Sfd0@>@fy;<09^}eP(d!oT{KS{x*c@BvxWFBWFEl1 z`;aaL-2$1DNLS%1_{^X$To;o3QSko^{x4_+;7eK~22dl^=)93~eca8U9IiD~-fAf6 z(k*Bc(=R^C|7}p)``^b^)Q+DA%>hNf%b$R*0_PdDz<)r#1N;$4TOF9@yh$?u4*t(T zi*eTu&;!n`M1Kj%cc3}ngqAKq$zO#g{1)k94%uPCw(1_#&ZQT}W#>`5KZ3r4t22@A zNAEul$sTY{JLyC>LFOAK?Y1MG1RA>giQaGc-h0UTMes-A-iOitl_=+%pf|XJILDEm z1!{Gp-0;2EQ1W)r7f@5k$P!QwoHEEm&%KSHe*^y-q%Q-V0O}a3*SM#tE*p@;8gTG7 z$;Tn{chI{*e{bFqFB7yCRDjzGfp1?d#X9Doe8 z*R6+U=Ao6okM{Wt(q93!?YtVT_b$>5>4Qk4r{pKV`3*QJ$lT=X#o?A#)UqU{1WL^jQ2DD-}sC^Uvfb>$Np%pZ?n6g~} z=XM{*<7p{H{{K=5t z2mXuTTnP?Zo!7AWQ{wgGq9Hx75P&q4AA@P7&_L9cf@>FFL?2CXWFBmE~_y$5NGQQ}T; z(1z?R%JT$hHTaNW{{eYB8axIH8Sg33>7YA7vq8TDdI0%f2D%sd^nw2-azk45f?7UC zP7h^fR=zV)YcnokB=A1?7;oRV)IyccV#<|(acRuNSgN{#O zx6m|aAswG~7SoaLETiKyPKC3|8RzbBcQ~J8DeP(|lU>6mJ9%sho8ru5ceDGPo7k_| zPN$GHuqT}*tdTW3rL2iHIp1T?u-`gM*|TiF^8-%isdEP(&WAf?*ze*;{3rY_r(8@E zi=8{gabd_%tByd>Y0Z#r%AE%}!7vOFbEIqmWT`GNC_dPX%n9qM`YqH`GD>$h~ePIr#O zZ~X`OlDqs$ztVXdzT|Gd(eH5H@jLz3ok9Pof7Io)3P0uwzsv8UsjbKFag~WGy4wGf z|2NlG$ zY(1-CwXBZSQ(I#p+X>&&_$yTyUiy#l&BKXGp^-8;vd zPh~3dioL~NskhuKr(+eJRdnB4Zv&Nw>O*(sduvH*6P2Nc?p{xKZlPnFw}bNDwQNVt&Lcrrd5lnRU2vTAF)>6N4eC&BLSZTByy?sS}9d1 z^w8R$KW*0M?(!hlWlrLu|Jx}c^e05?Zd$FRt-KwX{_v6hqIA5Ik>A7rM=tvaNWUM z!O1XB6o=5NKTnk5IB_szh`Hc=!IY5gaY{(uT1%LbcsZWf% zg03DmEh#h5!f%>1Tjfk4I(Kk2a;P&AXOja@Me9vB9(`Ito7#POFq!CnJa^DtN9idh z9|7H9SWSClOi4r*?!5x}dO{Ste(ip{mWy<_)8nD%ne6jb@&r`3f_YmxEC^i$=(7%t-k|^AVM=g3fBMoJx~R z_pYLusgd%gxyjpPXCO2Gd^q>in);a8$Lk)NbGXB$c`A?S$(de(w}THiwc*qjG($u* z>rhLW`Nou&=9@}yfHm+b#G7i$OQZP=K8w$0`^}8wb=diabY&%9O>I?;|1qz4i!rn8 zr}}5n6AJ0h?R+P-56v-DD$aLPYZmc6bTqRXDp?EVIh+lWRGT-QR(owUw+zs1Gu)gs zD-}}i|KQ;!8W)<{W(Pa%%@HH0u8YMeuhjUwy>r&;&(&i)(8R+exHcvSNM`^q07b^*X-_ z6m|v2Oyd0v^c?QB7CU@B{92@IkhVUcMo?Il94mF}3*)wS-v)(+$>CeE-WCqKg~R6O zFI=<`tW9D)bYd7dKLKYFs1KS7zV*9pLmGCYm<3u0{?|dz;HvfVzNEqUvA0C&j z0tfz94i6ef@9?p>3jZL->WLqLq_vH2#8p_298ct*1_ypUnlro2s!E#Z*6nVCyW4GY z_qxxyzjI%7Uv^)0UvuAZyJ(E9@+NpkX=FR@bpQN$lFZ{3kZ~TG~Vz?L~lEp|dN~DOM+NmB_ z4eANCOFgM}tKXWpuocfaUBgEtHL_GfI zh{tb0JpNw9Q>p_QY-O#J6~i8>%A=8@%d5iyZ3x`@+D_wX$y(8C5fr>frWgc;%=|~(1pQY`=VJ4GSt^%PUPnclET{aciAPVDt3;iw67_Pe zm?t;LO`=_Hk=uA1<+6hxk-KD*&~l%}O~@QXW;^9!s_!V`8w^D^Jw1V=P>zx+;^#@a zgRYXiK~nHGHCl}o`D&aggBmXusY#;5AS%=hDnXfjKGkTRnx$rob~V=^E9h9FmZ}v7 zQ6Z|;8grfMP3=Z^m67aR`oB)Cpmx^OdV8s*=NY60a29lk2^MWZU5p1r;?Vat-8Tn zC8>Fu`c{{TTD_P4TVxR!bnhX$&!8~MbgynSC=AkRtq!WI*JO(^R6?CS9j$`tm$e_}-(6iS1WMz@m z27eQ-PL_wM)Cc@6{!D}M5Qm@{Q~h0{jP#_;Z&GvpeQ3c$bhP^iNHbOd`OzUNpg(0K zxq!5&OXpJi)lu0G8*B#q=+mCQxl_+wQ1wju4!tcv3=DUe?YZqvTwsQdN-^5T|?F<_OBd@ z6c!hkfb6{_tRGKL!0UXhQ;la0{aOx4??J80nsmSQ}^p+i(!H9`q&9Z(S0HMeH>Z zLEZvd3%U?dyaJ?w)8L5zavQ_F4Rk*^h>Ci^W+YG+7IX5ips<6PjiOqF0IYUvuP35! z)>7;M-2u7`)Y{7E9d0>g7vuZh@k?UOXF+WQA8}8PI`Se=M2&5PRKQY9c$Q)DTicxj zwPK@q{Mbd?j>AIb-%mW<&`*Ig73l_0i>CMiDC|GB4?ffP;Av$@KZKH4Yy%Jl9B2oN zNa2f+uDK-Y4YY%Yn5?k42*jW*(!=6gJVf3(;?bUs*seqw7F=TE9!Y3Lv6G033W4(5 z9Yk3c0PMjfp0=Io#3JPpZTBmFT@yai$+ zWVVCaeBQ=WtS;HuG~(I}8pROBW{Bx~XV`ZggMqmP+H2@^T(yx|c&B&^>TV;j@YZ;+ z@)KAo_$`=?ICo#P@)Q1~R}$mQEf^~d1^UGsbeB0po(9A%622CT8R!BZ9x@IekM}h^ z1yLLiJ`7&uK2K?j&A1aSgIF-PxP{Y5Z$`QeX**)9M%p5&Zb#a_@7AkiBih!Z1OEiG z@97p$_|Lo;=;uK#b_Fp28*Q?&9E;XS;4AKhep)<0DrDe~;TR`v#E3(?1bk^8d{Jy2 z(&KOy{vOYw0_=!tkxq9Y4TM|5$AhSkjY$aW$AY)Uvu`wza@^uTEVk_ss2w+KBy2jU zoizez{x^_D^e7ScLX6FN<2*DCAYw7b`HAy0=abIQonPR$f$nua=iKLPb|yIw(=o-_ z={)XS>+GZB%gzf)z$Ac1qkG?qg0VA}C7{L0QYD zu&K^EmdkE*?qfHxeCGjHz~(p)!h=!I?q=(q{cHof&uKw?D#5VD;yUN6M+|}ZgIPLya42ZwDRpPJWg1bgCDcyUdmVsL%BN=z^C#z;?|j8g>WA-J`;Bvts5u151Zd~aJQS^ad00Yd*>#1hxs*Ncc+{!zwS23Tji~8 zqx`1)ru&p!BA2*L#*5-UEgzLT+&$*E58S=-3EAxKll$chZo7O@zTzH|ugbUFH{nq^ zVSdHUeNUd21MVN;MLB7Hq0N0?#ns2%Q|fXx+C2;Z$sk!OCGG`Ps=mjV`o8)h+vg=Gti%mB%Eq1+$X|Wl`uflFHeifEy{3`55<5yubO{|OEWMW-xmWg$-oBgN! zr&+%F@{fJhAg0-D{{_F5-DvWp zv6aEFU^u%oxFWcMtuh`Sc9-$+u+_%H!+vT!JghSKLNJN_EXWF`uwR({EZ92py&c;S zTo>fAd(D@2?Ec{9AfIgx3W5SwORMfe_JH}uj%_twAoif~0JsqqM?qU0adxMRvIrvpj%k~Fbf(Kb^P#-+T+Jc7QY1R=u z6FkR`2G0iv*xT^`yc4__yv%xnSArw#z2InYj(reL4ySk{!t8LGcUgE{INiH4oDt6Q zt}?&b=B1h6Y4biEn-;syOONHnZlrYw8}cQ1pOZy$kt{L41|lnDrL3mZdOB)kt*n#v zvVqc#axZCRt8ABt0G3n%TRjyW%4z5)jNHT@wHqo(#B)8GIgJgH9Cbf_F#p(c^=4es}RR>as z)iKpg=@XPbsRqG7g9;-U^R6T?0NOe-(M7w0Q zo@MH&XY0ALRxi*C^%DATsobkq=#}!2Uai;2BE60@d!62_r|7MEyV|FB>fNeI@6pYw zK)2{N-Kmf0(i(78GT;w^qH^yxIe-lh0+)MsdT2PgMNlTf&R}lrPsq%x1U92 z>`?=Lwye{UpX=w@@|nZW_Y3`b^tXsgS8dBCOZ-KC3B6He#IK-ts#>4-jcg5_wRF}| z|24{Ge!br6H~5Wup}$uJRHjbdsmA#G{Z_wSclw89wSQC<_+8W+$$qcj@1Ity{ImX` z`T8{o=-o&T%(tuQx--ZOCaQ`4pnHsQ!H!^8&=l;Wdkz41K#0GPmhGor zFQtAx7aR;ag2TbFpqpe*1SjPpETghv^UxxwW^a*ebjF9SHZL8wSv4jajPY0y8zxIk-vsCM3b{U(9G;J*#KzE@ zv@@0-W$2bzW^AH96`QOkn$y>@oY?f(%veEePHaB)^?`7=IsI%^94nTsvBj}cszDLy z{2F@4GU=_!v;Pgf7t|{Y{YCVo<#d*tUrUXxid9j1t?99-Rrba5b*rw=gY)fog zY=?PsV!OgfcgC93oY+2pzj-74vr(l!9y>ra>(wJ;2g5b7j@V(fA#95Ur0E^8iLqm` z?%0Xg$=E>bTy&ILa;!Pyj%g)5DIon1ie)9W{_wbr&c>toaDRdx?PteFs_yvc_}KWk z`1tsw_!Qj|pGv*4C_aO9dS=)bw$Yo|8b;KrCGlDD*(x*K7@r$o5MLNy5?>l$5nmZA zj<1fdiLdihb!U8Id~*QN=wQ}nvj&0lubQapgWUt zlk$RtN%={IN%NA5^o*oMNhMK5xFBg6mCy0JNTSR?lvELJR4GZ7N!3Z~X~rl^s!_|6 zYLn`c>M7@jq(;3nX|I}`w4eH7gep#IO==(V^9V%U8L+Z8s%*c8V6feojpSQgKr=Y@ zs|Q5mMFp+`Th45}cREl|w_=5FW4wp}Tdc3NU&J8lo3Lvd!1?+`Ye)cv&VWZ?h+|8j z3^?${?3WkqZVoiE^%}7c2Imn6_H_b92fqqKTv`=eVqIEGWE$k}9xNC_0eY~Nxc!O& zR=l6?N9&BWbz)v7wP#$T~9M(p*?~X;? z!$%>2L9l3r1m50CE>GxKsX=6z+A(+)ECSzRA}mTpu7kYA4g%pKtWN;CYQIPGz6iec zzgh2`^|<0*>$~&51Q~eeEZWlgOvnxmNCvtjfEBX%d~XY$Zn3a`1>Yh~tcMVGy}(l} zZbiny$M;hSpHBtkfs{$0Y;9>LqQ|XI06r6DJ&iyN3D5*`2k)A9kI?}Q^oq(GEOQc` zJ^Sq#4}cwze1WV#9jYv`vMei9JAh}*O9i70w8o4MhRwn1b$TOM@o2N=b<&O0L2)h;1^0@ zuP}OBbQpUQ?Fp}}#lcCd`Og9yZAW`}qWIr%FFe&O6FJXB3!gV77Z?NiMuVMYpT|{{ zP+C82_l0Lk26G>`sOT<~2Yxw=`Lbv`0ezDgc?7;ul6V({ZJ9T1nUO}%@>8Jpd&6~* zNB`Pyb0qXu+R@4S11(C+enA5dp1?fIG0$3GAIG@L?U!`uSpmPRL~Bbs=3^caETIV_ zh_l|d1~?~8S{_5&;vKZuEAbM>fveCyn1xv`?!w5-p12_CE;~P>Z0rGCMIF6i;6M{> zjDS}nf4m(6v#mvwSuC~qF|IZsjnR^!By2ix?bb_dv24~~3VpCK5&Lx^wV3g@+t%J= z(()2dGRN%9?JN)9tk>dPjXCRT!?`ew(spdI9%IaC9P^#Th^?Tj0$+Nvo6siqi>gl{ z9T}=H+I!Ft_BHU~BV{?L9sJuCC1-t}&Nye{B|cBjeZbv97q_|(In3SeKI%y02XGeR z44h;q#TiTYOmMQC9A~;S%b88rXF3IrNB5@gh=}ck@o)`B8^^h-g)WypYIdHdlKlUxJczK1_8x8Eq0#wiENU{ z6?y*~*eAPJ%s=iFQ+UZq2D79Vh?0xQCMrRNs8_40 z1&TzgTBFv9t^|VV!<}M=_K8u)sovCX7NMlhsPm#;vms;>jmfP#RUX!9qFrZD>wLH; zj6o)uzS4yvRnId!!JsGmbP4rYrz|xnC0$|4P1k`=;%5v#$zYSHPc?BHbfey@_mga^ znr+IY+r?SCD~#C@Ml92(sb{K9tLn3+M=c7;A7)xrv|0p`==an8bTv1zJIr2D@8_7E zVW=$@7^D&PO(C^H4av{4Z7yG56cW9RwQQ@y8EeNQ+_ELI-=vU3yC#I2i z+C5_YCV4KgPs~1{(ojEMUk)N5^aw;0~ypGX`hd7s%WM&^(X7O7@)%5A^`l_yX@ zqvgb*9bclVrIw_!7PISN@xV>M6f^(7&MUCKZYb$$P=VT-Sl2`QVIHk zm304Vl3Ek2!)`HCgRTBHg9B1~M1z`Z_Ki_X%&swNh0zRy2%^3u%`khvm|bMl=AcIo z3;KdSQ5iI=mBA@dp*EZSa&)hsPMS2AaycWqELuqS521y0zh+??y~TKs!wlIGP7o8Q zr;pejWz2puVPQBgETZwCBwQAj@$+FtSSbet8`?WYq=vPAM_A_{4C~F_F*3*O9uv0e ze6wpzcvPfe&lv0&BM*p-a4_QG-YAgA!mgn`W1@65+@POytsIEB**PYf94^wecGnoQ zXG}CdDxm+1gYD7cs5DwG@`+b&c8lSiI?e1D<2OZ{?2a*)>=R?qPo(`+Khgv0pOd5! zJ<%?D3s*-?AATze8`|1c)?;M9zL4+?CB9L?nux>GV88uHM9Z!1dxdjsXe}@Box1f@ zSsT}S9hkLd_{DX%#CJ8`2X2mIcCDZs_*W87oC_ZSQLh*2?Vw}8IfisD=-Y;}AAsHl z&JxHxWvF-voaaFy!;?YpFjxaV3z8M!d=c~k&@UJ&*CCw*>O!&`dB~U>0BYM7)(nRSf?4c<-Fd}&t+3ute7|H73Kmmi=Nxd!Vzps(ok9 zH#6V-|K|IXh?r6uBSwr!BO;A7Qkv2fQ;LXbikMQwh&)V@BGQOyY`J+@u~Ldy5h+rN zm8MA3mQtEhq$#D8MnpuK(i#!DNK>SgnuXYb!{pH&D~BK5ep!I-F>}b-fOPEdg?nafM9Np0y(>H}3%X%p ziwOqJ1b&Cg-_@^Yit$0B)es*K>v^UqXmBmrf&DGs3vyiMvS>u0K}k%)+&s)EEgHKG z{kkz>6DiRZY4Q9z3BJ=5tN>Fm(>a`jo{^q!;ymlx+wJG9YZ1zt z<3e&4p^XE-4cc3jRyhUcpyFcOtvh#B9Vx z4x!siN*l!{aHqb9;xkYatN_Md)ck!&c|StwXUI9tZjS!|c`z;p=8GZHg8cL>4lxt0 zdl9aau|WETh>JxP%?CW3(v{mx7fu#UqihTcIH~po5QXO5z&^>_iLt21_)lBG00G z6D{oJnB$^-6kVEV_azRwGDHUkO`#d`xM;pMi_#5aE5$#eAJTuH=r$fTHXDx_TZ|`+ zHseWSoAH#f!+6?gH=dzyh-Zym#&gDQ<9Yg8dco*0UNkz5myAQkpXuA>RpXTLhVeJ! zobi^?YbH$Ah7319WsXg5)s*(5|00h5Ds3oqDOYL_YQLbnS(j1;tlQPvP8#b8(5QR{ zShq>Qx_uVfmCr%DQVs3Owa~8AXn)pT)vkkfWh%5QU(gf!g<382D&K@&Q3Uj@Y5^+3FRk+mrL4B+0r#99=6W@3M0zdjr2x32>IRuA;sjX=N6 z0s8H0K)*3By+OapSZFNP=d%w3{WhT79$+5^dJC{_4+87<3t-(^*@uDtF#9mjH?t1| z{V`zO+Rc+zPJaeCw&TFDy~>&vy&Gt@*MMd_VO?TIPuZnv+i_|D=|%hD7}WXtT{gn>_}!*%qM99tYZNE6`?70BzO= zwAqtDn{5Ny>?!uFptiGT1@#gzWPbpL>@YB7e*}i?2zypge{x=Pv+Agu=e{oyu#P2> zV;xH(k991GeAclf-orYU!~pkVw@fO4hCwAY3o)-ld*`hCY2Ya!irg7?+pgOy5~BztMM$ZqDnv{S~rY2nbk!=Pc0t8SxR zDvX)G^QJn|-AcdWO|=gGR986#?r7>4>L2FFQ2(s6%iZJDKNBf!Dl=xNf2P~@?m24n z5Kq|+p6!Kp&RG+CS{>q*c%{7ir5(8DRk$nq_rOlRUF}V`JNtLSPQ*K5+9itz)z#iQ zZWHf;sZERQW^c1Qlv?C-i@Y|s(r$1kcstxWZ^t$YlcYLbS7?w*N zX@r|;H|*LC@9dLhZ|sk;XUfjlZgPg%<7rRLJLDPuEJ~l_82)^(f_KMG!Q0+cu`{Nc zm(v(Veyekbzr|VXo@39d^t0RF>mTqB`G@Ti|Cnp}YiNvV#%&EOcR=6=nP5OLC@7+yZf8bLV9GI#$2_314`v5*Y5XePxlSRqdH#wpWKs!e!wycXoI;joHL-b+9vB7jB>#QYY*tmetg0 zr#-daE=`?v3~JAYNQ<8>!)Q@7);$;99xb(-ow8^JNnTmBD%hB6iPrKu;!cR} zkJi%9#^{k~YqUMZ>(5zdQnb_UiS|T$f<_v{DtESjAlgR~Gni$7euucPLI8ZZ%*m`|DW)rH zGV57tGcB3c%$CeHcR{9|Mmoyu&g}J;ImerJewXq2`ib2wXy3*++u9Y>_+7KkF{a zZl#~?*`3)v*?pOo^kA<%7@a*xGGFM_W?#u3VJQ!HL_1SuB;^}}Rbgj(RG`uImhAC> zd#*KmGIPS2Pf|bKE2nqY8GrZN9)!drC-#5hZ%6zE#Y{rcWr|-QXr#m|p56zP)y41Z zr)A$3>jQxPi7`DJfbAuA64#>yK@oir&&94? zU5fNu5zav!ym$7a>RF`Uhj1f8vHe327yCyN^qa6Nr{S&<@S3&>A@-O0?{P2iE-_|h z=XrZ&!K}O=dB))`Xa|K$CP(F(e{a66VDRHm)e4MiHFwLk)B0qqY(=7rUL20 z9FtwIaI0?W>!7%JNxlW;2wF}w$`f$cRS0Frd?Dh{U&b6Q!R?7RZSi7={j`3LF&yF@ zP|$vYS&aP=O+@%L7~!gTg%UqVqG=d| z^fd^l;3=Y;hlVL}4N^n{E%;8{n<&G09mZ97l8t$}A3}*YFo>~j0i!8? z9L4LDaQp<98FK|?4hmjJP=ksqBIhFi!X6Z?;c>2ELNLVQp$o{x7?mhq zp~Qn3P)+(?#8;wB@rWhZMA6TQuPxz+3)fLJeBzD03E^zWhKBfE@qA%_nWD!9Dl7J) zCYAx%aANeZ_=6I!#F$NntZecuSx;ncVkI=B6=I8sHCuT4ER1pkS^@8jVg(Y!oA|0s z3`M$Vw(dq;81G-^5|sFK6qcrF)HWe5*d}4ozel7F&qBWmp4j*k?iCc^&(Qy({kjeL zWt7E>H_%|>6Wkh!dq0PJQKq0pW6Y~ybH%^&JejA6M~JubJWafX@EL@&5k8Eof?7U- zo)Mkm*N`qXgf3PXp)rHE2v3)F`y*%_W~DJ5anaswNBk9(1B;m{{Bv2`FGl(gQ3vr| z+Rgo-?nO!y%Y%A|L;W{suXvyP3hug@^QebVzA(D4=M?=dPSItphQ3p+LR^sUf{DZ` zq@Z_Hf*ZR8d4O&d^m!sg`E3XfpicxXC+oSe!NtBOYb@gG5Trt6_**i{2-`S4J{RF@ zKs{c8GG)~8wzb9}XNa74A>4;NpGQB77rrZz0*`iy_d;e8sQE|YlDXf$Cw+^Wd&vJi zgyJO^=-~w38cGnoz|kgTPRVLH4fzvjiy-wOZ!u4PBKllP!?%Qj_W|{Dq+`CQ-N-L~ z6F!D^i8fjg*prb*Yz2b#`~&h2NBaGE`WQ&U-;jP8Mql2@SnJdocz*57b-5=yH>ya{0~O2*fn+KYHQ@(7Y!PCfYq(jg%U zAe)87VF;HScC*+w<88>Z5~0-R`v`xGP`-u!h|=Cfig*H)cs-<4z8+;gzZUHkJ1217 zD%D%bGZDQcp{xK>PuO;pASp2mR5?n(Dylw#@CMu^-nCZ z51;bJ&v5TrqzfWk{F~q_G4WGemDO@PO3ug1(1+H^D9CDXrL1qrkMR;e`*IdYd=7!| z=jEGRd@9Pbgrz?SPc$KE;w!od>D5TT4lVttd{rX;Lk`7Wq0Q5W{+HV;bp0NkC4yE7 zz2v{$Z=e-uh1yVUxHeKN*D8RVouWPIoZ2whq|Z$@G`8wKHODE$8GMjIA9-fBUhuSMpd{u(KM+z+b@kk%f1-Ex2*< zpn|uYAZ!i1bCRQQFW$+vd>uDKM)0jF7+pEVK((kHIK|*s#;=Xv7{4`sN45E?cALgC zk}X05VG$~TMd(6Uga*SRbP+53@m#P|VI;=pA>S_8SSONbA_ppUWdz39a+T(2D(YCUMM|*-TJlZzkWcLCmdlWd? zZ-n*k>++eWnohnfJp!Qw00W2VtKnhHrsS!MDH|_!hW~?KApVco?|cdcZoUR{#$? z7kJp4fQOw2JnYwjhn){R>^FdiT>w1n&A`Jp01x{u;9(nqhy6bAuuIuuqu-g_o&1x& z8EDphK(oFGG;1f&tSQ<`j3G}KZ30*>f>x(Q6t&9qT1NHqMid{^i?26U*j_!%+vq)t4^3l%;V-s zv&TGVDa*D(D`yR)aIiJRDzQqfQC7KCVU4#YTGiH6aN?(1_2v<4o_W%0uo~#DCb}a> zd7J5;C2uq2$Acq3(b`P+mC*I`nezQy`3{N+W4^=crJjv>@^tN}aOJHX$q`mZvdrpC zj^S~!x|8GRw~ErD@hOTwTEc&;3^ybO8ne;1@3zKV->#ba}h3Z>Me}zTg4(9v; zt`jrosr+H|%wzO~&g2QMQSvm^shetbHkr>{`eY%q>CGeke0uYoJ=`8?k7gFVJ;5Gr zl~Vbm%#-$Hiy7)OlMBJ1ud`><-)Z(-x@&=5WHs51)HBq>_9D8ciE`a;FSVxHD=5vj zs_i;!hrJ4QI%IV+!`?c|41224QK=8LJRfZPLhdJfXUw_pXWbvNkCWU*+_!YsQTngI zK1uy`l*YD{Mxul6nn)$|&?nHrCK@67;Zb#Lz9x))XRt$Z;FMTxPN_A~ z8D&j(%9)w(Or+5-0Vm(4k{g`qW{)$IB;y=OL5Z;PnUhbgt#_7~=Nx9`mpiNJ{&mg< zX5%|;%)qB-Hqg^UYrNB89&z?lETlU+oi1VGJ7?@w%*1CY=;z{#1TiDu9cFd9BWSd$ z+%k6z*!VPF$$V~SvY2`JZmo6Hondvmvq@MDGJALiI8&LCPd}&Kvz}(%sv)iDE~+4o7q>ZY<5(b#6}(cbM=33Kovi(~Dv-YRb`$>eCtaX-r<^YN{j z-d2`DEA+N|+bP!~&Os>?yq(rOZ;w@PFSU!jrIc?UtpYvXLGK{vVYa?|%%&%K$AzhH zuk{s4<3`{1L(I_vKgaUOJpE*$KZMileSV2wLVe6qdlWyXk|X?5w~FFvn6>Xsw#NIr{0@JIzn^k+f}`JU)%&OD_e?)GKgf5F`8*4AL4j2o6w*&| zFpS%6_5>q>GWr=4jAJQ`Ir%j~Z7_pb_*NToutc87D{#?;mW z!*_|gMT<|WG7HsRtO4yCtM#AKu^6X@T!B1tRwJ|s>KEEkE%xxOpy`Qq)je-jXfdCT zcKO{}tYK2m@@ZKyYw}5)4zdAHuRtEb-Hz$Iv>%zIZtXh^eZyHs3-vALFLy;H2p3G< z3UutFebuZz#hwxxN7})6Qn;S)H5%#5SXUi-3a#V#a|nN}U%)y5J|j=<)w8Ti;B<3y z?^_ys<>EZs=&MRs%Mjnm@x*YBCzc{*HR3mG@8xvt6swW`kTwWZ^W}jL6Te=@SngHh722`Tez$KF^9?y}e7Ub(V|F~%*0`zfIc^>IfKiKm zVLjSag)?bpbGwWzQqF;?`V+MATPR^OpH`-1EM!-DFY0zrUy;TP*?s6Yk`M)an|Uob zs?gvX9b6N&9p{~iezB16GE%s!mA-~7@NaZ=M&Fls7v6Uv?}qyhQu=(B zd1~X2d0$IA4&4xa`FB{Z5+l_m+#8VFm-WeXRq(IEu5HJuaL==J#rx3_I7{(yv{#tp zIJePowFPulsUch+Z9dBY`sy;CYq3|u2T&8}nba2%K8v3D7w)kHEHK7Bd>W(bMf&aB z=ZQ%Oi`C7T#XJhLaPLEX8!7*TsGInZ9D!ba33dA0xo$kW6G!#+tYd&wzsh~0S|Dj1TsN}?tI#z%ts91{ zKi@_@Z^O#)GM*UWT%zydDfePN9(-#D>Q7P>a~>|n8vYeK*p6~8 z#`^gqv}jVF&Z`!WyLugc{$k&UDBjqYNAbsbToPS;l2~Jf4_uFGg5uiC}#@3 zJ|IqqZsIdIGfde1L%A)9gFFX~&5)4e zkfMi?rv_mI!l@i4o<#3ms8O#P7wKFp_11EhQXUa?5cS!L*6l&RiC2W1cnuUg63rBD z@9jK;M027QX=U%yob>Cv*fz1`UBCY~`lSCnSGzdYCk>lklx%KF;!D zLThhdi@31W#qJJtbW*&tT+Kb2e3e6LF`qcLDM2fumN1j1Z>Il=M*b%n`JZUy|1Z(V z^t!hT`hWkAQNI5k#{2%<9z=^lyB9)gY%7?7P_OA?h%~t?(;Cd z=iz$=+iQ2d!;=+cuTv%1UVHF)=-%FcMEBYy%z6d3cQQQSAB4~QM)rD7*WmY_`^Skq zoBC%1qw@MuzV>u4XU}xcQ4e+5&EUDNW?ZkG6J&2cuhpp$W~<`@(VO!cye7AV;`y>*P}rKfBbR;xD=Z5ruKr8cc{$9f%ZrBmhY=N5UL_F%7zN;v8^(%s$eQbzRB zQ+pYw>brJHKaQ8i1}I;@SWv!RwLi>WEy!NM^?F_Xh+cn=Uqho@;m;SotAB@AN_Q@| zyZTYRPQKsjZ*i*pZT33G^3n)a(HOcknv4Ct&aHy%^^XC`yV5@!Xo1D3UCIHJZ!jPj z;1n~0*O_G32SX{XkV-r34hn|bU5wy$hX-T5P4>~C(jG`H9cvE`CeY{=xsCoxyWCyc zkL+b6Z$D~RFuOFmH0D6=ZVVow@fjR!bqa#*!OoyE*dusdM(YMA{cS-{KTxH?>C&%^&47@zCs)6?2QnlIrl_n_Ssc7*$B)-lSr(;gpo(F|J`9;I1SZP$n0 z;nDC^xP$6a>aL<0WVrjnGfout(kv=q%&t?Gio9kI2|JtR#h z?Otzxw9lQu*jsmnpl_p|=p2px9!BO1+BTgF8q)()?T!&?>A_Bv9^&MuOPoT+*rvMXo*5jmSEp*y_0IhCJVyDl41@zc>V12r;AqoJ(#yQ5jB527 z?B4X3*W!!8g zVzg|gFjJfv=5^WizL6P`Da(w>jLTH{g_%j=(M(OIHZ#MWotZ@|?mGVfV_<=OU7WeY zuE;FUtjw&TT=O&Q?ag6HSZ~kFv^X`HR(mxgTr=&AZOt42rZqovjP5_dvo&*?F|ApP zO5A2QWc_R=J3vsZd}<01tfSpUPBCLw8Mm69LV2cT>uA+pNj*8wnUtOFtjW%$)jG;9 z$TnsdF)r2X%&th6WLIU^1`FJ}U?ihbvyU(eHK=r_q;0{XW)DWwXzg9W7}V@>yWZWG zJ(=xsD=FW(%zVmqJg40INs<=WgLAaR=0bOA=7cvmm&*;z4bBb8mE=mJ;ki+{a+-Zz zxe6ygH{RdkDQ6o=aiKdrH!)XD?~Iz<)ZFyk%v^nLUale6rU6_PUX(zdMRf|p5fHy zxqK}z%FE9y$Sb7(i}Qx%jj-3{mF11e8<#4`tFoIiRnE#_LEfb3m8dSS#$WHW=G8i7 zc}Ct0dmW|E%A1onKW|~)t^P_dQ;Mi2WqFI8;=DU@&3Vh+B5z3E%Dgps>+@RjTJyH# zwYWuj+q{mvc6ygadAsxWdIMdbt{lialy}(456*w2!Op2ZK?huunK}X!I=aW^ks6Yqxeu044&~-c1kN@_DMJgXKM!;SupGiD75@tQ50DN{ssaM)-Ci6L490kAq z8t`MnqLz`BlR7c(mSFh=qbNQT1qTjfj`-G0j1&Yf>ISWr4jC|p;kj~_jCg{(6m1s| zgL`oo(89(ikrPNc@lzT5ypuC>Fs6cj6b7&HXQh=$BeN>1ZS+0stI$+Pv_f(+-Ats(GsN(EPjP!PF-CKV)` zpuS}XFXyxiY6Kb?@x&UR&GR_-Oj2eyY#t`C`X=zV;)T-$#@@gv3r;&J_|K!9Vq)E} zfIv1dBa&hl5g7wI%iM&v=y(=nCW%onMW6cu${B@(aQlJ_*Dx zWPB2__YSHp@h^}N@y#H(bZ{k1d3p=34nhCJLM9$lOyS21Ctkb;V-70RDk*+`v6jkN zz{XWTBz7SV9AJz?5(E;k)^f&-oNMd=3ug<~)J8dw>LluH0cB@kwMnA<dd^8%!N2RVC?2Y6uPM#RM*DA35_BP6i`?LvJLK=2x}{)4?DNNhoV zCdJb+keMdX_%W{pcwG}KPs}Fx0(zr}Q_L%1qZ-1YCH;mzPka$|77U)C@z(=0+=+*nE(bNdTC<@Z?qRtVY<;Zo#3-%p90ZY&;| zh07x+Ys*R{Z&Ju}0tjRUjIjmVs#%KkKcJ<8(0&lTE#5`JxiEmd6=s1AjHnIdq&Ww( z$w#;l>jTzm3pS^ua6=4~CaB|kz+Z^au8rqm$d9A{UE~D%T^FP)MpiuG>VlLOr1wtb zybfVL>OTb`{N4&D$dEJ5#V3S}IDAqotU~AeJBg8~n~?|4)mLL2e~AtXe_fPYQ` zkDZ(*^2S(Y3vmnIX%@c9EX?;L=6+IqF(=VWzyn*aqs+&U@(9)qa4Re@M=Y!W$qdrx z$++XLPb2>Xgs{E~%SrsDnsT)hAuzGVVU%zR&w3HzpOKDPVBu@Z5_U-kWCje2q9LK$aqXuT!T8$I~_V4A0qebE&#+0u_StqloxrRI=IJ658|0!xR5qY`=(~eQ- zMO_0=WfD74hp*#aVEC;v#4%HhN`&Y6&bCD6#lC_u zO5mMtu0cAU9{a!EhLjtSazTu&=5PU0#0L_dYr$%0HQ}z|2*m^SH7G&e#nLnR*{~7x8Kkv6@4Z;tjobx=^2(#>e@3%%jf;vk-oaeVD_GtVmT*aGFyw@ap z&FzJzoiA+YG`HH6?YB;>nVSZNCJ6A05NVJ}*TRab08+F)(i z-`R-zEn-Z#TJ-ks|0`s51^s19*tFOZHVu}r+Sn3S3rpD4f3$?jp3lN}fiaas2ko;>4v{;VC>4c*jRRDrrc4N@1X!RjJasNS!N z)CbfM^+8pvKBR`K533UO5j9MGR1H@bt5WqbH9~z{jZ~jt`;{84%GIaTW$JQug&L=> zQdR16>RR=EwN%}ymZ`hca`gkXLj6$vNUc;qR;$!c)M|B)TBCld)~b8eI(45~uYRWP zS3g%9)B~zTJ*YOSP3j@lsvcIG)fV-HYEw_Dr_^@!OSMC_t7p~k)SuLG)vaDvZzlE| zu8}bo7>&j)#RuQJD*pEj$^tIY}KHReR~Gv*}ov*u*; zb7r-9tyyDUXHGFcZ%#G8VAh(~o72o2%<1MA%^BvG%sTVS=1lV|<}7o*c{5wL%v;PR z^V{b4%{$Fy=3V9w%paSp%%7O6&3nu>=1!DT)jVtd%{*uJTAHO>hGklo!a$EAL z*oOw>ObGNhEdCb}3Jnn3D zo^aZnC!KB1Q_gnhm(C97X{X(J#@WfIwK>l@yPfBqJiX?$0cQRl(NCQq%*ou7wLg|h#sy->e0GHPmp7KG+CGG>3XJ~ zqvz=wNwv`CK`+q{%k|p4I=w=!xmMyN2YR*FDF5%!+B>M%=?&f~y-9EJ%Jeq9Q}5CH z^&x#!AJ-@JX?<2dt1sxw`l?aJ_sUGtn_==yU$5HqH-pSjQ!IJiD9_hu?vkgf@D7?W zW}J6Q-hwI8;-^VVpCzrj%G8=gW~o_bs^nRuEX+!?#;ljNyjjlNYPOqQX0JJ54x3|o zhH_b&OaNm{>pGss&2-}(Xe+5Ew_Y_lHr_Sw9H z^t1QcIz8>Xtv)Ad*UB+`>*ZO_`HfOe)&35Dw>{V2CpCD)EAtO}tNbHUTP^aG^|I}d zaGjK7o4F!)Z<3NO^qWy91NCVCl$6!+&UNeB{Pyl%_}SI!P=`bG5bJ5LyRBS@_@bWc z*Gc_X$em=@>!d9;c6#A&lKQz|RInx3W{&mr!4FQGN^ghB^ZNy7%^+)2HpRhZua0Zj z*;JRIb;ORK0eQ=~@E`)R*S>L$-y5Dt};-97WCh2>_D^~ING zDA7e>mHZwLYwenLMup34uB{J#xITQu`rC)wgC*gva4(Jyz`K4|*1JBtqFv0@MRrBI zsC7%bj^#^ec&wkfD>mSd(+#_jpKeMw{0C`;sSl=Q)|lL!~0Y;jF8u}WR%^wfA!cg0myx@)cUqqzI?bypqmb>yly-85KS z*(B0naebv0gl|TJbk=9)>biR9vB*sfJ(m6#JoN`rgVbQH z#q*Mh0lIKqf-)Hc{I12|IL>;vMgKvh#}y=p_Bvw`UySrsz|SLoAHqyC31PG)_cg#p z7GrtVgVC=+ZXtzGPZMckK8m}H1meDKCGhWoyyM-t{`2%4(ad13O#NwG0XzyC>7TEk zxPczxx@C6_sPW~hQqR{tP-6<+gAeV(4fG9U@8E3O=V=r)`jZ#ZC>Xx29Vm0ug?kUM zKb~k6p8Z{jLG8E?AZ9bdmw;yxgC548fU`ybk<$MZ@Jql0i2ne>A47O95O2E1ef0n! z*Z0|i@LO@0g*fYb2>(uTHi6AE+A1X!k&8z5kWjj0Gi$h5(PO_g`D`BKvw4wxHV^XI z+(88LGZgEYa^s8notBGRlu z8usgZEJ_({vo$IGh@+-BPdHCGPdoqW9CQvlN1Z0;8Rvv^%6pUdWA7*4PraXcKlgs& z{nGoD_iNptx9F|9QO0cB^$r=g?b3Vo8QmfywpQIHqqg7Z|IokHS9CkaZrRc`eN2IQ zg}KeV(%f!dWnOLWFt0JMHLo+TH+Pyhm=R{Axyy_)qh%a7*1RM6OtLw7B6%`-DtY>4 z$?;{$@ny;JPbxV|FK#uaH@&a9#q@uz`+AYEUrf5Tvi0sx>AESBue+q{#Ufp=6)CSl zJIx}Iz$H5`>nc)IK}4!#_SwuiTefy)yMEYQv(M%=afZ9di&hTL?>@&}aeXXm=F)kRWstE>!a_WJdv(n{7Mqi;lB=lKo#GUmZ^ zrTE=?h<`*+<}7&qY*z9{9gfr^{dU0#k_SlPW}@GS+Qb%Q{8M&jGsnWHZ`5)%(2qxQFlBa3&T7ptD zE<2arEJaZE~Qlpp5Gk(6+B?R-VG%gZ)R^ne3*qS?LhWZ<&#wOe67nwhj`mMxo zOCs1N8>D`D3X%KqL{0Lpe21phO}ClNl3 zFviGUJCNh(28`C|*+ZWpGDDGRh&~zQp2G;8+>V-aN$DL@-X#HW^<6SIQTM<#ZzDFV zBe>(Yfa`%6Lwer_Rs%Usp9X}qL`xEN7110iuI-rtLY|9!ceoNSEq@nr)>I&UcepwU zB(2ES&Q%h?=@Qw_k)C{!)R#mnXq-Ua{t-y4=SGB|L=M3P8S!6+!tGQ168J+PB({iIX+NHhY^3CxBy-bKXiJYF{i6u~Kgc~NBMIA{ z1uc%`xl8eKJYUF6498D4_)RXfTsKs(L zM$&oE3E1?*C50yv>`0^>fRz1k#tpuiF5Y8*99ku`B;gsvKriM#jTq<&40Ndm`qKbs znL80i-{22NN`$3f&w&<<+JZc3>2o~GlV;aJepx2Jb(y?NBES5yO#ZS=-b*Hzx7sV~ zX2Gp^0V%T~k~0s=an_1kCL{R@8TVT`r%q(ewIXY-^*Th-l$0WCZqyl(4trW~N7`Ah zkTXjqJYDvg@-O8}Yr$P8a%F{Fd$CBCbs~GLusIOA{m(zYLQtD=}A#1@E8S+3R zH=dN(GjiTZ*;=uk6zOpWS<$>U-!&J>jAp8={WdE#+Oo~EquFQk_W5PrMqg^8xAoSt zqDflIiw0>>o~6~_C*@RO8OXf$NFt2u6>_wka-qncmn;*Rx5ICdx~wo)tX!xIgSeD= zk+nKoJFQ4qotD}_jVYAdT*+ib8? zT1rK*$KEa2Z>qe8E-P(tR%#|0JnPRj2Z9TxmXey4(yUe1Z#Pw8f73r4WQxO~)&lD- zrVZBClAS|sZLwxbSWaFvk<_Iw<;fOHT$R@%rCTAjP$gHHB64~eEwtgXaHY2(T;u0k z`>Z(}Zq_B%Mr+xnEDM@RhKKb)Frf9a&<5-5k+srhSdCPRX;;mi7F_zV)6bdn3%Cj*Jk!W2t&dxD;1BFHA%^lId`b;uJKD*tKCII! z#+fh02!p>@!-p*(Cp;v!$cq@9I|fn?t;LazmeK=P2>haDpzvuMgHNM|&4%)sJVmD^ zgO)-EqgMx`V2#nJ&I7_O>(EXHTd(sAj8*>~xUowr)!Qiom&mPw~ z)Lf+=-h+nz(j1S&GHU3B2v`g&aop4)>xD0p)da1+xwO*fM|^ zu6;n?NHAnYY#;^T8NqnZK`V0LJ4*iBU;uJUHW1o?$)!x&Jbei0YsxuC^y|j#kJutu zbGM=G;YqpvY5{x(Vy+KAwL;4Oj|f)9;FKpnT&d-pZnhdlh zXG537M!T=mh7oU@<#nPZHGr3F2+3YUyP&@Y+=ld&lHUNV1C~Xt+NNi_!gCpN>EnIT zw?n&dA?i=;S=98o!z1&5ayRhKW@=g7O3;h-)7XzEI!_r0{<$nf%W}1ay zFs5v0B{o-0gDhAJ8P8gLDccQ4$^~Gt=_fMYj82R1hmc>iEKU3MaFa}VcLM9Me?EX@!Fy(WIcuYw5sl^LynX&bbFP!!IO|W@=}l=l zQe~80XvgXPd5+ZSN9i94O8w?wy5ADa^xN%dU5;ub?p`nn_!FF>r&{;J+$0YaugSeoaL1EJJ8OJ8YOKtnHOvdsq%(M07pO4ps&%_r7ljBReGZ;&%74c2+E#Y>{ zVI1EhE%AuoY`4CBQLR#6tU}pTPw3Ft9O8EY$ zc#)Na6G?L=k>|VNv_#+7T-llv{iPP|n=>{s$nqE`?usq99LCZv7WpIOU7YAowM@pb zvfy~4oLg`qK2S<=mE|=~RD$bzvD9}nx1h#v3pZyOjuXrLFtO6I8q0Makt=SN76gCZ z*m83&v6ZaG!Ev@l*(wryrA}ANSqEf098Q#dnm;O>BKN75cDzW=UzFIKIF@)up68_H zHBOuh*MQSFFL5pDCc|J!G97MC=7-xYmvM4%a#%1kIl?j(+x94L>V%*qDg9@BTXIrz zYI3UIo-7OINNLRppGnRTS-UX4M4qS3Z?g=>;l*TiazS!&%(eW*vB7fHRmrucHCb=l zPjX|THQ697vDtTHYZHf)jmbuT1elEXB@bE#V`(GB$>vZcPnnCB#n>O4JfFNI@8x*Q zW1PH{((>+4BAcN_z;OLB9}2(j4g+8ztm)ZR3eH0O1pTBErYS;FZNx_VC*kQ zZAfkMt1XAIlv`zLr_{Fecc#_y8HdHO-Lb~hA&EcgZ%iGRS|1kH#>%9Z7z1wOPFCZz z3YW=g$<&5mOIT|;jT5`1PYlUx9ITEPrHj)^%WfPSYx#{Wv$53FAZcInhL~&V^3Fba zgzb;hvuw@E(~YvNFkLA%)hfMSTCP*&PfgcKx=ZOrovZC}H89R{rsa?P7L2FV&e_0_ znaF`0%`@YAS^K)i-O;*FkYYV}|3<41(aK2cDS0e79}es1MDiv5x4X^UjkE>iMut_G zE5bz#1YC^@p3hu=iK~;jT%(WcM{%AUxQpfP7AFClf#mYN2e=w|A>t^u_&8#|1DpWq z=RF8N3H&D@Qp%IwiBh@~D<&1MQJ(LwWJix2yQ7WZ%ldJYYKBk9iv|1e{V9s1T zf;@Zz*b4kK^7#Sa`+*t6Lnfo=HrGFlRyd@_f~!l>Z-Dn@KI!vJ$(wR5^*Z$3p0m&Aa}Z4iH`3M z{U_)%&$6O#JTyR@&&t^fk#2)}2uhRm*M;?$JlI@;$^+*|wDuir?Ob=!g)C04au+q| z(tgiXfT+0vA3(G&6ZKfHKsk^DyaD(bJT*$yJ%E3I1N;X5{Vi}C`KW>WfqQ_9fe)gl z_+ES#VTJg5g!2$S3_K1b>v%bE3J~1O-a+8EySSQN+{Gm~Zz;lP1MWkJIS2d*a5#{B z9^|}%{=nnBWYnVz>pNL(qg4cHm8WJ4bN3_P8rEqs5OY=zX+jL7NqM$zTOD>gNm@P3T+0+NVXI?=>}@)P!-ZRQe4-8)Vo^A3!`y`|H3t z$jL7deiq?0!dDSSY3nZle};H!jMyuJF<#>ty-NtA%)N!cm5A8_WDRac7%f5kVHJk`|5Qb!2A zjyDSky^hD3*?ixrwcP~78|`6cjmy$e;RT=dC&}zy@qR$YR;%8mjiuJ zxLzoIdg>6yTOavJa2-x~AVq6qW<8B8k_C!iOw9d>=5~`~pQ~uXqeW9A$ns%-!u*3@ zir!sZ2h8RCEcOh?fn0SEWge|53N5n1H;7EmTpclDz@iQkK1^KqjDAR5J&)QF)!#Bs zcWO(rIwSk;)nWhhu8}o5nkg_EGX=_`HL}V&^+tI6xki@3yY)hAWRWe4Yh)RWXades zg8sL2MJ#Le-zHyrN1pV|%M8aaGaSFnaQqj^a4heCnExkaHZGIYZQYoStHX{LVK$EU z;5P2fY@Cs8i=447wllUz{x6B`m;I2OMTX-dIjf81ct{t|aqL38Z)~TOU?mvvK@PVsQLeVpvaZW6NwDyO5X*X5+<)I?HM-=dMlEOUy>H8h3IUHzk@ArxGpp znG$V@^Om(Z-Z#;a)bT;FA@+%q@%WfzCblzK7@ubOi{n+vA<5x3mo}ftk?}K~%*B)A z7sIg$H(8oUTV`X+VjOOac#Jz4jFan<8|3-MND9kr3>IU{VVvAAXC4YAZ^@&{<2`tb zuO?bjDzP!;$4V`CajI{sf7p^5lp2~UwhYCoaf$lW#MG43wB&KiMI4($He&l!mJe6X zw4B71v4N>IsbYy+Z=W^wNIaS4CQj{@C+!;_lR6-EpyJcQ_SE5cRhFYTY$QvuWhhRx zrCmueMarjtI!ve2`RRV?0r4^E!LcG~kE>$w^f0N_a?4Mg9!p;0RAsu1oWz!qSpHia zD@xae{gNYNr@?Fte&TShWhf3ur5n7-Ys$a(g$Ow(}QiDr;mW4xFLNiT$^r5 zx24agFQwbl?eQz=j&M|tw#VU&oOn(qwaoGmhih{N%G)-;vLag^V#{nSZ`~xzMl5gD z*u>bJlAH~gx19L2HKmZdmn z3mJ-2TVn$wmg3T!<2fg~@e?o1xf+XGhT_yja1`fd`eypaR%E$}GhpU9TBhX;wg?#$?cb0mU$$1Mp7qrZaT9xw<1#+FVB?c zl-hnIcW&Izt(I*8d5Y!#Rkr`hU7J&qTW@)aa~pEQaBXg*4|EK5O}l6Fd4Gj{BCq zIOjraXQqFjlX3?5i&M3IF2;8Dxe^=R=UTp-A4>h#=BM-XbH~Q7gcX*tIH~gorQD5mcuwznJgrKalX9aWH8Qe3I~I~m<+~k`RDU5g)R9Da!yzt z<4k#en6o;!x$?0G-F0%g>3@jKT`|yI1vW+e%Nw*hFNc1b1Tu>_8 z^u)0ENjYa$!OVg=>E>if7ngCmE_pgrUa+EIwe0H(HdvtdOLv*a<(t0}lzsM6ze`(-(d3;P!iDjaGVi*rf~ z$KejUHHGWL@qLP8 zI|~LDK9ZFQIOl*e4E-+1{lz&MluoG&pcer-#GE$EIc&hSMXx6x+5tnZMd(Ah0=Q{J zN@`sx&vUK}HK*{v@iAv2nmtbK8#HVBryVooU#=OJCSxb!C)jxM-@2c)VVAxx(Du^j z#06)U9)^FZv87Z=wqfuGYOa|MdD!_5IJ^GSrq|G5y3iv>Y`V}uQMwMG%{0&=x$i~# z&vle$byd`0x-1z=iI`Pmz`IDjp)(t2L9W&d@hn^q=`lRx|3#i>03n&X(6v#|MHW^A zIoACs^2Qa5ud)Q}S>97XuDVo$Ff}HRbxEz{=yU0p1^Lebcb}oY8}pWY`r-Ujmt1YI zPQ58c3_M{X%{6CVk->yMJk*JERVPaLUqEew^}_|Pa-`9_j1u?|9hyHkgC7Ucj* z-jIzGTFZ#n8ho%3e=lcZ(SHx6?W4s;46)QJksX&>Fz6!585_-`;*2iD)0@j@4bqo} z`eJCT=ra>}(QrNrYwCyOFGb8y)Xr9vKWE`USLLE)G~X%CQ=yKM>+C71F1Uj5g3T1f2In&1?xuk_;?0Xo35|WFF0w z0E^06%tv$5uKHE7MaJW*Ffepq$UuMW~NIxrTkn3>YFQX)LP^z1d zrUkhr%L!NbgZA8~zKOH?$hpHZ=iEfDMuzv!`63wqsP`}LI3QtK3#bKKgLCoDMj9?y z7XorKk_!g!Dg716v2W?=;8XSOUEa_O5)gJ0EcPv)cb5*H`Q9ROk(qcj8Ae2u)Jtj59 zoD=y`^g{GRooTNVS^M~f0%tz;q?`q4j$0;J!>9NtZ|c9-k$;rc!;1hegXO*@1;6jyr}^7A{a1^9WaKneC;JqrKQ-y83X z&)L#Hva8s7V1ah=#u?iDPJrJNKCy=LKB4#b*}viYPMtefE^z60iZ2o80UNYcgVHlT z-yP(HR>1F~W#Vme`4z*rlDt75P+T^C*gE1p$z zz3rQ*Yp$TN23Ka^#cv8NUYv)`)ppTG8FF;fp7#XqOG^@4@{_m=#u#J|_VJ~N=29<> z+N5m{8Kh_5MGH5^TKZ$S0_REJX4|v#95{{13`KuqjC{yd=SR%&NWTgA7QClqJjNI- zvdVy?ine^`7ijG@$bZ}QD-u41(!gkeRxzKm12{+jRp^o4hV)Z_92Y&z_JB8tjNcf& zJ7Al2p8!`g`cD^Q4EM9R4(;@40}lI4*3?@3%lC-3Pt3#iRwHj*OB?bCIm0>2gKKkW z+D$qC9^(YJzx`@B-$qYDsfsflI74B#8l&vIiN;R*i7LpaxQwsm=XtS!Gp{aF%{xXp|6ZP3Gtso`d6Ti(EjPQ?#2*CP4)D00BGHq2LWm*lE-z*a{WI@``bSapPX_o|hdVmpSoa0~4Dd9-`12_v@^#OY5 zIQhRJaYo#OS&P~hi7#=hCH$U0+x&3X_ylbxO7t`E8S2YeZg+J`j)%)O5@>%J8Vwxi z43x9v`c8>lr9?J+PLrHd@4={SM56H*RR=?j<2`d_1v#tu`60! z@33Ew>(5>PiJUdrv5evq>|7-^-mc-UO6(fts?6CgX-47SsdklZ^{~5AaFJUjIL&FW zv*hhM&Z~IZ{p+obK!qTqn{UebSiuAAauhjs3N&lz1L;qgJ`L8ja z(du=k*c7YRo41>{t6^rG8K>?H_6B>gR`H+bx-u?v`bWEr)SRK+j*FvxQ1ol#6S)DXwJlL$2Z#THK#=T(CB)-(`4^8E!xYwg^PRrU*4IYH>l0gwpF(6vh4!yRR`2z z*)}V?PW4F%ZI}NKM{%3g8Km2V_*U7^$<~T(mu$!64Ez7FY@1Zt`gpJ%N=?KO542-s z!IOx|&$dq$!W+a_{Urt~TwQOpn!@W_I>ria>KGunp+mlL*Y|hq7TjS+4c9k!3>Dni zQ7E|0ejBf^|L+Tek8~sj>;JnQEv;3)6#KkN3D0wOIJ=xZ&OYax&bOWKI{)T;-}#~Q zBd5h(?ym4gc&EJ6-WjjOJL|Q2ZQirqIsKUaivFs8Tz^gfvwlK9sh`$A(LdEc(?8e0 z(7)8@^#y%V|Cj!qeokLEjxi?Bx24WL-57mOTm|eM}x%orTiC|~& zWUwoED%c%-BiIu>o&0|C2gx5M|2_Gm}#Xr`fl;v(`~%L zxi{MFbsAnGjlGA+G}h0lNBr(+x8Z%=@>1U|Kg<7s{busL?WgW$c^SPeor-G8HL=+0!Mhv=V)T@%4?qh5U9-a%wxjyZi?8+d0vh+xa!+mz7`B zna&BpW$wDpFEPKq{F+ue9fFJ93c)J3NN}zj3f78@oBi(2bFGZ9);%RyC-vm0a_6An zI+5EPwb_&2Q*HC?Y{_HZHTKcY##p)1_trj8t@_RWcx{y$eShtJA61*G?p;tVUj{1) ztLxXnQqxc5fZ^(`>YZwenxX!edRTqN_VIU@jvF9ygH2`i0lr8gebg(}9rEqDOUh@w zdY>v&A6B!~N7RGrA(2!*rB+6%9TkWikuQ?PAdxu!M!ik_t;ioAR5N9?Fc0%WKcSYX z&vxb_U%oA&%25UCcJ&(dM!D-)kzd}grmK6@ed?p?W9skKQuS%IN);;a-BYJdR1-?y z_uc_&*0j?13{Y!vjRKKfVk#pk`ilfJRE#NJ&-T9r~nNuKoe((rg<&1#&Qq~5DOpgshtsv3+&|68q4Yt%o^9x;EmHy2m~ zTm-BGt^lr?eQ(tQuO7G=xDB`)cmQ~G_Pz7(^O}LDfGxl_;CbLBVEgRZ57c-a7IhwQ zFmN<*3a|oLGpG8&2lT_h6~NWNb-)e4O~5U{ZI!k6&el7D`+-M+CxB;x7l2nSnq<{S z?!VXc1r7iX0Tx$PKlFeZ1sn?;51a&?3M>Q809I5zIJ?Ts1y%zW0+#?EmL#=i1#mTR z9dHA16L1S~8*t}?_L_Tu`+a+lfXP+ zUtoXWp!rgR{!m~s@GjsO;5gvK`5&pS^rrx)0n352fR(^1VD0<|X4m+OfDZ##0oMaJ z0UPJfFMgxH3%Cz>2zU(G3_LA3!fyqh16~AP0ba8hxPosALSP2i7dQYo1Xw)(p^Evz zDBxJ&c;F=9RA3o!#{7qB<_8tPxxi}R0^nj`9dP*qDYsx1a4oPNxDnU@Yy|EA%J*FQ z6w9-9^S_?-gLkMwe^AI#shh;`c`RXn`ZyEC@hFg^p8q>4=ax`QZ%z)gKQ5fj#f|@U zrSBj7BEOF6dyDAdY!BZV2R`WzzK+=uLHZW)6}O15ejXOyBEI$}F#jgeldt#fs{bv8 zEJxj|P>Ms;>u%xKQA1x6UCIBjTll^CC$+20-7FT|0*;oE%W}0|ZBe^Lf`?Ft8>}aZoWIn9qEpDr@3?71+I(%-3E7;+vHyJhI*sD zNnW`(&s*%R^fq{n-d^vhNO7jbGo}|n5JO#U!3fXpLeAnYx`@N1cy^aUI z$g#W^x>yBH_Sl!MqoU`rKdD=KISz*5*uJ+MX(2W_F#6rj-_&USrWf`+JEyVdai+Lu zZu0Yb=JvLQJ&*h4-@>tfgX6(H@7Hf!uk)AnI^Nau_?36{I^NUsxM*;%<8?jnH*i?D zW9g3u?(B7ZzUTRO^t#^A{=JR|^g8ZU+pl+VY&AD|EP>an=y%19>T%ex==TzT@7%-R z;mdE7`VAAJ-zCxS&gl1I&!;XP+_M&nt9s`0jgwyDcyX`eCB2T9KL6P1eq7h%6MCtx)1SmYpAtFaGhVa&lc)SxLr@=kELd-KIkxmge%|5ibaq*75svSJ z#p(Zp;msBK$+*9B+ui5ftM2dRf8!o<54+!SpK*Vv>^CWdWHnA?p)!%?DwVa{gwD69 zt+MxsJsR6A-;K%O{Rui#Efo3iVaeMfk(#U&q(#Zu0RsCLM&)l2S`-#u!- zIwZdb)e+Sc+os&uW3hi0Y=~_a{ECe2JmAt< zalsA2cI8Rl)(2n5Z+#$lgJ$aq{MH9MqyL|b{@)e-|5Wt<7jeZe;fi0z6(2>+V~F{R zjfrhRKEH~5ZngQ0J&u@0#C*-hU_9dd4|`t%W<_!BU#F_Ns=Mzx!`eKQ{j2iuFJTWP_*&}EVt<6UBY3Sc|1@JrE zeu=xL9OrXcyMJTE5-*i^NX7qJ0`C?v>Ww@2GsVYPhnOX=h1qC2nj@};QW@3vA^D8o z!*acTju`)Y#pp1e@tZ53@q5&W^ot%dV*R4W;TiZDdIDC%^Jp==2rr_g@DjX)o`#p< zWwZ>o!8Y^^?1EirIqZQw=vgR(GPDB9#g+auH~QfR=PNoegd1O1cu-(p7X7 zl+e|5HQYwm&^6GGuBB_CJzYoFK?k~?u7{3v1Kj|f=tjB`I@2w53v{9T>3+DK9-s%H zD?LaL!X31NRzNp;h#rFO^e82c^uPtX(4i&oM~=uJ=3lW-UPf_?#g z=$G_M=u5w%UqL^5fnI?A^dh|o1L!4s2?iSO7z|Rdf?=@26b3^SP0`?P#iCeXsA5;_ zFideOP8hCKQ>wuT#jUtuq~cY)aF0@;6u>AYpafvF5>i4iMu{j97^}pT7>rXAN&@ax z3Y9__ucVbU+^1xfEKE?UE7jqC9^Y=nKgM0zd+a^j zjlIv_$KBZn>;v3`on>e7o$PD&HSWpB^YOSBpTH;L-bQ3(+?P+|({O)2gU`SNj2Ovy zAb*9I;lcbZ{uaK^c+Uo&z~AHV;rsdf{Czx;ALU2!1N=+%ZahgFrY*ZCgb%d^e4##eW|{bJfJVrmytTC72 zWV-&M{$He2e@TCdJg9Hcw~$%-HvKj7kP+dS%+vSk`^bF#jDChZsei41O%_>hvD`|Y zvb46eCQB^sEgi{EjabKInGrpREVuNr^dZk$23iJ^6_&x4!Q^Mw*Q{@nmDU5+1LS$@ zLF+-X)>>h$AiuO8vK}JqtcR_K$qUva)+1!S^&{(-}}XjN}PGh(gJ{vSm%mr=}r zGnGQ!WS~`9yw5bRE)f+5o(EosEzPOlBk{dq)|uI6J>-?(i_!9eb7L3 zqwuNjMz^40XskHmUct*+qNmYw!h2bb)}lLwU-Bza?q#$M4MDG>*U<>{CMrYY&_3}Q zFLx8&hu%g9g8Aja7$Q zXboHs*P|EU2B?qLLj!1pHbWC=jkZAvv_r2$2k3zILTBiW-h@8T2fYLRU@)qHyI~kQ z0wdsF^dZ~_)6q#>gp1JE_!iMS=W%!3A6>wc@Js-_7%vtK>;?P+Snx*t5?JxC@UOs* zU&dR&fw$xBkcao-eUOho#vem9d=d-Rg+Irig9m?s&wv-7!{?xYC`18236dZLNQ}fG zNRlK8VUi+gh>+@}I>f|y&>&7NG!GimYV;;(L2srFVHj;p2f`RSn2vy_=sk2iEK`Ol z_rfaWK4k*Ds7zEIfd5jaC{y4irBs;*o0R#=e0WV+s4Rrt$|7Y6ysoTPUV$=YoAM+%kG3-82-;dlAt{Bish-^q93bG(v&iqDG?PVfa)QPaetX4O8V zrrJ+^k_=LxQZJGv>Lo2iE^863FO6#bwXw9ncCYpz9j85{Jw+eXexfa>i?p}3x9KwN zkoF#ZMtfg-pFXR7pnXVJXdh`G)918Lv{Q7oc3L}4*Xb_ZMPJZ^dWf#qBYK2x&|`Xn z{+C{;r|Bj=tJkD2>$UaT^i{pP-kt8y`{;e?PQAb0pT4FK)Cbeu`rZ04`i4G2A4ALZ zar!vAUtC>o!6#l67u`nTRbas@UPe@Ki)~_x+bO&XRd9?wNEbY#3|R%&C`Wd|Hx3}D z;2aewUwCkbkxTB}^2nWAUg6KpLIrp>o{jv1liYv;WizT4RnGY;<^|LV_+hb!W>ut zOT-+$1~$MJ;s2BgU*|9!hm&vy&Wj^=z+Tu7hv1mVISUs>wJLUEACBS-t}U*kMz|I3 zfV<&7cnBVaC*Wy9@40vZ3gX^)5FUxg<0&F%9$ti(;Z=AY-XyM~J-8fK;G?(_pTQUK z6~c&}ct}`WBRNuA9C?{gVkItOTs1XFUDALwBPFB@=}88V;ba_{L}rq?WFc8fR+6=3 zqkNmiUQ&*NWC>Y8){qTki^wS>2LzKlPEL_?It z+b7elmbYZO%~CGY?Uvuj^i|8-BHb>^9gyi(%Wq}6&2mtt+b!?N^i_+P$Ax^wa!96I zE$_*6o8_=fw_Dzq>8qCA2#a6KgxqEkV^RE4A>^x;52XIDSdNB7iR)7n*GUuCXC|&wCa%v-Twj>D&X~BqG;y6ZaeZat`r5>G-o*8diR*%i z>su4o5rN_rOQpnhTH-nWkpr}W`!zzR$PV8N~-W# zX%#-JQiadTs&H9(6)vk*h0Cf};j&t)a9OQYxU9A+TvkUFE~~Q&mo={nmo>i%m$jOS z>!QSU#ppGwYT&Zk4O~`t6)vl%3YXPeh0E%z!euR}!etGZxPm6Gkclg7;)Ip8*2Goa#8tz@b)AW;rirUo6)vmGz-9GITv4OftT~D6 zdJ|V26W0wUuDT|!8%YKQ3F>&2$;wmmykua$|fwTZ8diLb4RueQWhPvUAIaWye|O}>#U3!lO%_yR7$@8Jsk5fiLn zj&*FqcASUvu^W5v95IItCnLy6at|3rMw2mQteD5{CF99`WCFRLOe7D8S#2_zLZ&Ly ztj5Y_7AoO0I1Lx!cW@d0fH9_+VGUcb6+6TXTn)SM!{ipyjdUkH$em(t>_vK$yGS3> zm-Hk3#Y{Pn3?hTc5OOyeN`@&@tyAUQgf50%5a*Roe2MRwKl%Rs-wkH#lPt1|+1_BR zuVW-jl*wp+SGjN8EKyhdhOxYLOqP+=a^3Q3SwOm6V>k9}Ve)J8mf*-vbJq|SytF1+ zgNV2=mm4WrEz)1e?<!6;OXMZNeT_G$#Yt1roU|mZg@tKHI+D&m)_OnIPqvn} z*0$Sh?QI=xosAYCO@x(dL0XZvLz|~yzL@Ux-^u%qX9ijqVXT$`WsYn&t zw?;vse|r?RbrR=}nr9bB8OIn$80G)kE6J$Os7=;ooV|$=RnWj8&MuIMvkNTZ?A^#P zadx4rIJ?kRoZZk@oL!0HM}Ywc6*SPn0#>kr9US0=xnE+zQ3e z5E?;a!PS~VGiVMipe3||)<%Rm;pf~2?V!EzbUF$i*BQFN?a&qOfNs!Tu(~^;C-j2e zaF_6Y`U;NM9|pic7zBf1i12}i!Y~*vyr7YA4~&A*g8z+$ad0n;7e3JhxL>fr2VfFR zhAA)=ronW<4QIei_@!WdU%`3!1`ogk1*01*c;4N3C?1A~;}L@2-6L4vXgmgw#pCe3 zg6-WWIN<$wB7Q)4RFehwn<^OLbXQ`%U%`JAW)}!1MDRRCus=>zq7j`~ z1naX2F6baml1K7MHNgShf*E>=j}#C;2?$0Q5uV58`0I);v=Ky~cL4*V!KS2HVTtWM%BvY#)1zm9yWl{p@Xafc=&oWbd#F_AWalK8MBU zi1-|3AF^ZYBX*p9%ucXRSS9RjS;eAoAa3#+`;-B-={0n|Y{a8Jrexg>YpQ6%5eYBtTTIW(u1r{!zaG`Hr_yqZrd(EM6J z3u+-Pt!1>VmeZ6`^+vsie68$#4o!(yWpm)?e>7Dg1`t5pG{SLjG@Kt;0cj`U$UV3l+ zF2iTl`w5?Qfbduc>4Wtl!ei3h(t^eY}33K0&`nz(bUGb0x&Eaa_c~xFTgg_#>vFZL%vcM6 z_f(sjhBNB*Y9Fl0HLshkA6Y-gldWG^zr?fUn%5(?Hnui+uH+z0a1di3b6r$~ni)(+ zXb{IqTm#p_25&L;U}S{0RZoUC5Z{V~w_G1JLM`OocR(fT7xK=uOUhb8{#+BhkFVCQ z7xEWcU76od$S1VMQVI^pmugLgT&6V>a=F%4mg#25Yg!ra>8UHu*hKQH&)`{D3C|g; zr3f4Q_Jy>7MM}Gv7A-P0zU7(U^8N=kwKk7!{Uh3Cm^JtSXCj_^-58U;PCe_2(AKA#BrkR_W@D z|IcQfzOz8Xg-y--zLgT3;AP1M3?^XkfY;e>KB9^T@OuOcC};b*%isXq&E65CaEKUv zBa9Ju)t2E2KeS`oFm1RtLK~^wqm9x=Yh$#r+GK5tHdULZP1j1b8QM(kLG3YZzV^7b zKzl-4s6DAI(w_QJ>-WX~!v4Ab3JX|EtF2ux?O@#>wS*0YEo`hc(VA+_eqa;Z{y(i^ zx4)BJ{GnCs_V=)hY&#$M7i{Avwo2Qlwv)EcY^QAB+L0aX*iMAKY+Uu!51NbiAQkOJ zWvD6IhYkuGRDn*S_ULm^P(Nu+C*xajF`9xK;pS)>ZizdfS-2DKg689{xEFc?--Y|4 zpW^mOQV;=(imj7NbfP-p8Fn|x#ZfYN>REMfl;Y)Rfx(qAo z3iUM{Q{PZO#7(tJ`mMO9K1-j4ms?6KCHPtES?eXd!g|?y1^?34%+?IAw;i^9h+ni_ zuwB5vvR$~e+T5%h+c)A|QFdr@EdFr7mmp>ye@bRK<-&Zm#l1@sBJ zkUmKl(WmHQ`V+c@{**4IPt#@e8M>T4OIN^yFbf`n+3+yTfk$92JPPyRF_;gJ!vc5$ z7Q&OT2%dt)@Do@9KZT|6G%O>Jl6hnrnNCW{3^J2ENM@0T$ZYcP|0-_|d4$XrIG7oA z-FP#fU#^DK6+Z7&R8Ovk^p>k3edO*&dDp;vgC8;XpIu%3xzT7D!KjSL8P$w8p(7+? zmSe+4B5K%2#Q0f$4ypV+zlbcVU3DO*ny0!@zFMp{L>{%V+6?*BmTFrR6th$%iitVu zG)fw?5Xy>KNJG_)nFG}lGe2PrXy8Y?U_iEy6I9ko762|>gG~Cs!wR;SHmbE^^8hA<7#`gBTA{A)jLpD zYFkrkd%e`Qp47HT%$GLQK+Kjd)KF^PTx#A@%xE1^TQQ$?L+#9avGuRrWxvVj6UmT~ z;3G3p%;3GKiQ&tjj>f%S)LH#lTaL;_IiEmwliAW#B4YyCRK#U_TO=|Y-^q6(yYPf6 zk%NE9FCizttP;`2Hi0RrM%6T`Cz$jds95c;_CW)MjrbCc5Z2%#nka053zdo)K7^hW z^Lk_Sv}pBiXtS7o`=WATfySZ=VRarvM}?(%3RPZP^Q&+6!@oKj14R@0J_Nss$Jj+o z{>4#U#na?kFa0~9Leh)7dL0!RPb*cSkvL)!fw4DleTI$JZsj<Assx4BqP4H>@xgL`Pc`OY36^HHSZ#I#D`HLmWOvV< ztS9Tmh8w#L*gb3%8!foRICd`^&+cOr*!^rGo5NO`dw{0#8FF8bv6E+u+`)5g*N){D zOMAIfXCN}3ADoR&(J6xC+|7Q$@8^^G!}@djYJHpjy7ZuB%j06)9Cwl5HPH^5!97HK z3+^G>TW}9yCC-Y`8W1cvBrH{TF*dIg%zUh{0F%YoY$zD`Q>axHf1uOpOxY*Xpw!GM zr4L@q`O|Y&m6$9kres@`(wP5YPw;#N!#(&nmj92yhZnxCc zobT^d0&O*YTX1Oe{Q=kB9#Aa!TN_&kTUT38TR+@&#Xl=!D#E=H-#$P1T{Qcuc=blK`uyJ4;?(~jO1%-G-iS}1zgF-;_l(@;~wN5 z?jGZw;GW{1>7MIe;9l%r=3ePu<6iIHAFtXQ*eCXS`>Ur_?jsGtaZov(&S~v)Z%Hv(dA~v%|B; zv(IzDbI5blbHa1VbJlagbJ>f%s@LxIjJ*SJCQ%nHIXPA0Z(+jhR#b~3ST z+qUhz{Qtf8`BlB@Q>%BMuHJompQ^6L+H1S@ZzVMsH)l7mtWT^Dt@o}kuFtPe^9=CJ z@l5f|@=WrK^DMXbw6C;Jw9mBO(XrA(2@*Q`cbnX@KZyrCm zdkN{zsr9Q3s~ylhvEBc>b-r=QZ! zJ&GDrm$59jQO~8fBOn+|8af4|l3HD}Hc!v7_bGr6><|nMOdN(9W(qZvq2)l_=cxJZ z{*&K#CFkMzD`pSV3FCT|i&JT_E5>M^ImEWt0qutTy#H9cH>DVb_Oh5&2SLO9fj5odrb*59Xogx#ZF3W#{?i z5#`0_+2@7lndfQbwdR544dxl=spL85Md#7w;pOq=dFO%TiR2aJq2wv$Ip#&?(dc6@ z^}p)ta^lK`IPR|PdhCMllJBbS!tdJb^6wJu#_!tihVPp1uI{?;2Je#XqV2NnKJLoy zR_$uNjMofX*n4*vT{?j5w?-G5x0@I z5w(%Ek+e~?F?bMqP5PML2uzL`BaD^CJOyD~Xe$<)KqE#SOpwL914W|#T4R8;% z4S5W`4TcPe4Gj&F4;2rZ3?2=k51k?C3_cBw8A2F_8V(zl7#tg< z?r~NR-|v69v1Ugshb@O=3`Xux?pg2Q?P>4x?SJlx?2+v6@1g9^?=|c>?!)Xw?vL+P z?49pr?5*#6?j7Dnyv({8f5?0Ye>ebBECliGO1+@W{-#zSQJ_#rqLgEmZj}7FJlO}C z2WeQzD48f}85tSr38@KLS_xV?TIoueO36xjD=9143#kj)Bno(Pc#0S@YLpU`V`K~z zwFK5OiY!Ta=CmjZmeKUlw$Zmyu~GEV1fvq8W~1u^zk`{B(u0?SLL+kr7Y7{&eFsSg zcLx;*YX=brM+Z*_*LvDk0gZUx1m0|30B>SPT!-Qa^kr~%pPhbrt%-hT-(DYjFj;UG zbUWG;J?EhNEyb36JK5ckKDyv`=y7NSRD60~Rdo%wwi~V8 z!9E(Q^f{%eBnmW{K_!6lr1GZnr*eUEq_SkWQn_ZitA(J2nT4{2mxW?ED;vc>!hfXy zi2srQBl;1wruoOAlFgmQoynb2E|VpfC9N%^Ejizq?NZEls>hu@RcKj!UW8MaQK(t8 zUesRbS@>QEQ50G-Tu4z^QfOLyTzFi9QIuM&R;X68T*Oh_T4_zTP$BF zUo>6JP{?3vCpNjlQXM6LGFiyX#`4HK2tWt80IC6703pCGfC&HvpaK8@T!7bjFn}n) z7@z`h2FL;~0epZv8POw&Bex@?Bbg(wBSm!4VPlP@>_?;iRplm=CY>hEbz;qg%cRSc z%PhP&ymUM?o;19itc0xOtW@nJ?G)`~?bOYr&GgOW%?!F}{0aOi{BczidDCfQ29uft zGr4Il$80S|jA>&BHb-a2N5^OfItQ{=xmKIjtyZAcydS0K+g8cexs8jBj*Y&JgpIq6 z@{P5Pkd32_r;RHujUUNoVpmdEURP#Ua#w!WGVb!Ja?Oh4Z&Qd@UzfQ5tiE^Q9iSPc zUK5fSq<<)Pe2OWO8uW)iByupgamFl)%t~FfhwM}cFr$at<&~Pr#>yIEZ1>L+8sx^U zNka;4v<2h1n-i~pBqAr8$JQ$A*((4I|I15}eWh$l-8eSx9cAbTkNj5_1>0yM+rj9E zmxgcG2b(T_27~Wp>f`bF+r9DU>mMQ`28}uEF>6KaM>nGw@ui*kSA4*AvajdiHorT4 z4((|onfIO6e@xy~`Rs9uzngJGSf6*4-MnKAv3KKlLI8rK*Z%?$043oVZj>DWbY8SU zd1ylAD)riyWGnTWl4KjXg|3+L*_woq?r`tR@w}<8KsLUfC?S$S8fNqd3Ca``@s9#1 zX=zx1eiS6ELINal3=lZRZ#6J5a8T4g1F#Y1Xc#{lg%A_;M{K6r&a&I5u2bntozGui zEz_S*yzeeQyp{5^Gww}iaBzRPp2!~!dP{f@$C-@4EnKrn|E2$c7rl`~?`X24+rO90 zu$L`p>#CV0=uy>|mH%2&L)34%u1Lgfn2y@8+z5vEJ0kc)NB%holdmU}!z*47MJcJrOdLVHpRG=k@*wDQUpxuy^foNDpMse}I5wNO8wZuPLc!mK3s!(Fd zddfc_*oFF`cq84*0M-qg$*j&m!em5)EQ#F=e%S>aLi#Yi5Zev(nCa0n%EiRVfM=15+mYrP zYY|0DkKPYc5%M5K4^-d@VsVn4R0SL$Rg*+Rj7D*Wj(C}quEqHAgMV=m_a6$~q3qhx zg~O(b8?}tsSCgL_-?kw5r}}pT)d1Ik*zQfd1;@9+b%1n0c=>(ppQxF{ohT0ZRKiA& z&6NHZCd`xrE3wV2!5Kh&LH4wIIjy7SQ)2gTC3(#@gyW)ePd{_R;X%Nm2#%2uOi|=B zb}t}gjiN9+qWE&$GQs4~G1~c_p=&|h;M_pk-{{58&~IFKw0FE82t<7015;}qIsChT zFMjEOAV+gW4XKXMgJwcbpgSx-aS@FkasoI!~bw; zcEEzoMpyy#H?>!~ho%R9hm8=cWPE@GxglzRmV{|sVuBf&8y1W715|=sklBBi)t{W1 z99E}KPFOWA)It!h6pl4FUYZ)A?JTZ&9q`2Ph0aTI=Y#)_T#jN%NPx`>NnMB%BaF%E zRXikYJ*tVYA(M?iGnTt3SmD0}UIq6Tp%LE5;(0zLU*FPT2W~4eA2yM8Hy2)kS)c7h5u5* zeB44n2`TW;@CpC695BVDGl$|5VbvR+np9SMl!iZuGLN{WppgF0Qfs-~fRQ4M>VGD_-RpzYf@yd-7 zqc1ByX;9g+F4h(j5!DZk9Zy;u8BGnqS z*R_bS1j_gc4bWB5+IGy>y0p>EK?nQ`y52Cdum|L*5s)m35+VSE@90J|lQqm5H#xb( zcPg`l7{&;as<}}!ao=w>A`j?U5w)E#67OODX<`-5Q<{K7VhN01ajZM#5Wv_@WYn!_ zh}LdxM4bq_p%GMLlAjK|2bWSTN*ipl8#B|U4?80r_HU9wl<)DOrv|DK?QE;xNODj zLp(sE;@BMV_fV`qwYJRQX4-O{h0X()Agq5B%Nw~_X%fs5|6*^Qj5z{q4z*fs$?uxs z62`5_7H(F|SAQC+mr{JUu3rss7c{j6T0Q$g7I4GS_CZKhYYY&Cc#*L-U28DWbK({Q8|$|7%x^* zEZKde9ioGX4Ga-?v4-}1sndvfv6+B~cx%|TH233{ux9AHCjAHGlcs0&dm_~dUB@|tAitO+vH{))+eLakj2dk z$Q@=xQ*aBo;opy1e^mVsoHX~S!LZ#H0qg{^-jzL0P@&O z%%ZXW8ip*T{U7E{4>Z1JuOW?_6p3e3)T64!Z9!K5RS8-RtO^q8H`sFsIx5%O>n`g! zg1d3+$}XWf)_5q>o?s4>)|6nb#$?SlkIrPxG*4fm2|tX|<6zPd?6|9%nzi1nBdxsmL3fAju&1IZQm4{-fT z{|{gT=Mo{_`Nu(l<3-i=V&cGV2LQJIhhy*-^cHw^P+D;0?SNfPMlTehZpb&$8HNBK zq=(2{UC>YB6AYmY$hF@y5-iSoqcIlpE2!~gdku-^kW z^8X)@R)yJsCeoaCt1|IIZQ_N-(iMTFI~Y}CFr>!hbx7GQI@J09Y{BxgX(Fsgh|yfX z``^vn2;i;@c3oLNVuHPB4-AP2BDsbQlxf8Vf8b%*z>1c|g)8m_XMBPs@rOtuz?)B4qe~noMMYy&YHFkC?!1`x_p3pW)tX&KJDlk>y{@bL{&o zbT_>4j^X`Udk46cy0}XWSm+7*L_d(ngVmP!1K0_9L|}JRagrceA$8`M8Q|A*Y5y~> z|CzAt!rnwyH)4Nws7A?LSzv2qvqZrR&^6K-^8eWtpe~)pmcVF9s=p-BR~a9qK?2nv zhHMf|I)NRmAKh0grmtSWSgC-yL zyC{llh#X?xfz|`7MN|jVg3}62H^5R#Ok%o(F0RsB6E3bYSX-2#X-zH8E|C7`=ics^%j zoAUc^SsC?~L4343UgT<_%ZnhyJMb~haDb_wBiDL_BbUoSj+G(>Wmxj4aCW+%zi8d* z8-bz7bz7$~#g-wmTX(GZ$6O@PW#865SYO@N>HF!*w`K64|F)wN_BT}(!MXqJ9x zww&9pCh(p1_klT7pEUzQMK%J|Ewo;CymqukSPiNVK1#79A5qfn)a=&me~$^g*!mpj z40;_0e28~}^&t(D{bq7HHg`z>h&<1NARne!Np~-z7+y_(B^Q^eW5dBtPq;Cpck=mi zb%+}6eyF11(AScXjJ`)xv8w;9*R5zs=0sC8q?%0c*KYtTPm0JFW`Ls~{^KtiOnv7` zRqt+91`|420a+~er%Bg7s$7OS>SQN@CJt>VeMrh^u#Q-r>f!ryR21M_6jh?x;PY?& z3x62fsof{(05%$%v}w7#Dh?Y|NpZ_ZYD`;K$W|UVCg!IUxM1HyBG47VvS`mPNYb0B z?J9JmuDM+zPee&+KNcG8OkU$ibUnPU%t_-(T2N`y0H_d!R3KxrQ&Ir&z->5`fj^Y- zZ%!|~PQSF9g8}pKcq$W}@KMR|(L5@q2S~@Uqz6Z5*sh1zsPNcY7&)kDd1jm|wm9Zl zHH?Si4N46E$cBoBea^R~#|lkq$8+C77PbiaHx=g=5Rgt#zEYbzo&_{bzNbQRtdJrm}5`Pc~=r)CvJ1DGAJKYKaW+euAi^iJMeted@m#MhORy@w4WWe9Hd<@?9}xfeqSDRJ=B(5 zqqf66o$ymQoCewJZ_Q^aMO}5{8P@pzsVgj;ZSvqBf4QjrI&=(u&1eE#s_P#iH!Nu>Ii@(EvN9Kn7zau8Xa+x#P)d}&+$ccedjC$oG=8IzX4 zW~-MI7WeNELHr7z?azI>lL5ppO4ta(`T- zZ-G$3;OI7p^XHrK`4V5kj5uXvr_`8g9!b6+);tm0(vI`d>6BWnSMI;&`8`*d$fXnK z9Ol#h(wJQdB17qHphYf`<+;GSS3fa9%+1XOm=WVY7+6dGa1U(T@FoOANmbvuPLre>NhX|U z7TMH?WV`b4J*9D*8BZo~G*Hy!)Kt8EH1&mGdk$`U@^5~VuY5FKmiO?_ji7uswYL&1 zL|^>#di~eM);T{U|6cT-Ph&2C?ZWC@u=HEZ~=dXjZ7ZZ zJmJB&{C-$N;0v|t9shWL{s6Le{n>`rQ0)QUwbByVt;(*_VSoIT#CLrp(@=Uo!dvD< zwA&oh^`7-ZCs?5e6ldKx-G~e#g0233&s6)YR(^PDRJ`S6{xY@y>L~SgHW8J!h`dB83!R*r*lEPrwesn=Pj%Nqs9~BT8YTc- zy~H7HHwpOW3!f*?8fq*r;zymn+*qmU#11X=UMn?|N4hk=Bt!S3%AuhvbF^t#{Vl}X zL2((a8^3)-M-h9smc(5^XXCRXX7f!BbdEf=%ZOY}h5OfLeC7K}J@zHnc3XiXR>lyD zXYK7tB7-N?1^(Kn7id-#`rIBF(yY<3}T%@fw*#Iz~h zgf1e`3rY;0F*F{%6E&V4%7|)}t~DTtPfZYC>wxdR-_S*3xXbzt0=Jx~=j|l`n!{QR zpnAa)uIYr7siZ52@+uvjN1Ns~ZGwtS&H9h99XZ8Q3hjMvvgX$2(k^av zJnT^Df+x}mca{n}=j;XJ)$~f1ugv1;j3NHfZ4H#sxl-)4s?V{kaH-7xB{?MT&3bmU zHClcxd$radp41mI!gqk*AW8eJt*!Z?K=zqKt&`%l8e8_(HMYaq!ZVldMK;PE)mgan z*f|)gyrs~>EYVb=;p8&teQ8G^@pFc&fu4t&u4s2?xg%IqOpeVcK(bd(`9pb?-4_5 zsOavZH+QVV4E@-`n)I41cvIX170-#YJB6%g{@1E!nubg0YFaoH zTFqy2&$3q$Rs&unsUXDX*Nc=D!udh6N2Y)Im>ru@Sbd6ZW7HC@$i7lbw;$7c+R&J| zo=y}sD9i-UeTS6YacP{gUXX3GS9Mus2!)3~Y)O{b$Qa4EnMnn$_ADfw3>>5-In_5S z(I=shp>lG=P;q$Jx}xr=O2n^!YlD4WH$5jO+iO&FHtzwA)1XgTKRm|Qks>D}kKASB>#a(7NK38?Ik1itS<54lwu`TmhbLkC&7mqDHyhVquje_k_7A;?kZm<(YB* zvC~)Atg04L1OHi5QqrjORy>s)NK?)MmqqSTR%l2d1eZzx{guSPR@zr#p!ekW-9h`c zktL%^s>uZ(6t9a1>4WOs=Iu<_@}(L7qy7XO(TWq)~*_7)Jyxjh5``lzL)$YaX z{ri_^r1#ju?X3kjPRuv8M*Vn~cKg}!ORcscr+5h96qe!Dx_CI6$(LEnqwaA&mA}$}CEJa`tLF3)?#Zw{G)8>7NpNjD zNm-}nTjfMj)~l!fx<~bK(5IbjW31ppN5Xm)DvLSO>Xx_aha(jdV;c(%E|1ajETzil zBry6pFYlFA@&1|@nUlR;^sTqj*Gf0lNnT~)HN&X6s5FP4Y4w%H-C-&=5Oizh-Bmqw zD&00%^Y)2MRo?8_a{p9vQ9vTLO(#zswtm9i_!;{!^a#$<@4;V2eR*1#W`enNWP@x= z?OncR(S97H_IcTiU~1dl>R}ymU#(B4ME9>p}zPO<9!!Bo_6Tqj4 z`VU{8!_E6?C#dJ~%_ zdxFQx=gMDFZ~bogYgwm(1ldu=lpuoZ!L#oZd(~cOS{|EjpF#`r*L6!;jC4ow$ZTksQ1Smge2loYAbP3l41wb%smSoyPZ9`1n_;>1uYR zt7x1qpVa&cqG{jf4HLG+ZLMP5g|6JU1!v!IgH5951}km6RHv}sxjVeU)}dMH+tMdrfKkow#Lpa zn>8pcR#(3Z_5mK%?8ESG*{qzpS{<8Bnn73ynJmZWCdE?N&WQ;^(uZ`KpBNATz6I+8 zEJq@55O^#6CVptor)7YBys5uiGU=sUeH2w+aOWQVVlk`-cl@1io!aZ-aIO2QW$x2+ z-MxCL&y_;W>FJ54I?`Q+&i`gh3J*tp3YbzG(D`6*N-WrEyWZ?R#@3#@7e%+s5{#wr zvpiW3Vtl8u@y^-U8VZ!b7F?<{xu991%)k zt<7tm93-f`vTjU503)iVdk1}opCG~a^ox_r*Xm3c#tPbj@}gR|rt z**vM$Uu(RzdoG_nT@IOp*>GHiuXk*kPBePuv3SbrNG;{3inlp>Sv@hFG@3qggZ-Nm zpY;`6$535?tpyO@A#kXKOJb^NInkUSc9Ai7c{q1mdpgwICkEMM*e*Z0Io&m5VE-dq zs^9ws=el@2)_N?Hfd^;9YhA~~?=#5m%kd@j^%a=GzP;sfTfg;z6!^P3M#&R%YV)ps zI0Js2--KyUbNEP%f^_LZ#{B8eAFckTx+?~}TolBzwi^DO#YZy7Xyiu|gmQq-dOx{= z%KboY?S;wy%~t(IMXFAR`&O%id+hmexiJNSJS>5mV~3)KobMp(pi+dUqO7wo9NgyD z!&q;1hi(nL)ncvB2GZ`$pC|FdpnRB0$7Fub;jTE93fS9o{*8r?cySzFoR6 zf^7RfGTUp<3nW`JgK9O!L!rm*In)Gvw6#ozQKE;ibmI2_dGNbtShwbwhb#zu)aqpuWLtv_!n1nlahZQU8h8tgRyU&;T z9tHvIvJqZEb^`Knl@{QCHNIEEp0|f`D!D@5n-$_3K3F3tGnyHW^Lo>CLq(&WS6Mnu zLT*Bax~?eBQXh+mY`ta{M!~WnY&5?RRGB}+;t4i?&&4KUS(1L{#)r~J9EHv)v(j|d zdxk6u@UN20Cn6Ulh$QCEPj>NSxGS=cujr0nEy<{TqZD0sDw4{3SZ^x3v{s95=55$W zj>LP%h8C9_j}CEFb^ct&nCw*b_1ZnY^YzD6@w)uebEY9C?|JE}`8(K<1&(x%8-k5O zE9=|Z!n|#kH-&Ed`B#&L-CW%>o@VSd_O8d!LpGM?YYChyOSZBO_laX;Lzd;AtcMr( z@PlZdVcMl?`AByd69l++*XJSeoI$0GRL|6!YqVP0sdC&K)TCj8<9cf)!YvmRV0l7$)vgSNfdo#F9pb(y@Dm8W_206M|>9K)=Ge&grbgEL4Snh{r1WTdjB`Sgfa9 z^(SGyVg*i#i7xl2u+XtTjR{1H4PuP$%ZXEeuQ;2(_Z350K|a98TP#&Hy_05mE!)`z zHfr*6dEanf-A5+Ke%NMNu%>~>s||O(fke80HAu(r8^U)R#~cpY*v81w$-!9P`oE^F zfdw2i3lke5BjJBdUS0+@KeJA7J#)h^=#tbsXHl|Kygv`ur{QPjx|LbtK zOzpT{i+)DfkZT`kymk@$Hfc{t|0=^ck@k8kgq;;B6Lq5k&d`oeBL*i%f4IAE-@WFC zR&VoBiWv_0rhd>sieb*sU^?O00t)BS9`}r<>J1lW1#|y)XF1M)te`5(x!a5XkM5qY@-F&l-N)di z;&6LWww6&vCY1RFP>F2+{C@E+y+{?4e{tVF~-NQrM7;uGdXhwP-hIGb8^RYi!&c zWB2VILyufQAD5z2a{G3?hJ_6A_kQ#K>VEgycD-s{=Q_^zJlfig7W~x%BLtkjkq@=c zT|8k9KC<}>(aTKHe)dHp$Kk!!j%taUwTP1IpVM$G^sl*?S1c9>id5E=?KvPqyx8aG zx4&6jyVXH*R-Ob)fU+E0v^{ujEpWvBfWrhSN0m!B61m%7MBVy6j>%I7A;0YUZdDC@ z%gfV?rR=!vI0W;d#}QQ27UD+QvMH=1vj^|vBk z!_NMm2h`qfaQwn;I<90R@4Tv-4>biT^zm~}w{(V?$gGJPi+%I|V_AEqdM%Iu`zOX( zP^qMXt&#uj*IBwwz+OwbEP4ed&`*9oTa|4!N1CaXrx7cB@TEJ$7)WnA}Ed_ zbNNUzS4p%t*-{FrGDoE&J$_1q3!#ztG#|H8X5P`m&7%65Vw#a`>X(eM#|1 zf?*a@Ho5;)}k_M=eD9(ERAad_I>pM)Dn&!1lw0^bZq4B-QaFVgzNv6plj)a03!1LRI($#01-i*iVG3 z0B3Q_5#|RuAQ2`UWq=7cyH|qNmCA9jn#kR1vtAt)0>IzTERCSy_x6zvAD zNC?J+rie68D9a0T8W;2M2Q%b>aYS%Jj}te9#6-CG1t!W5DU3v))BxQmT%g~X5!xO# zN(>k7?=8g)@fYlBTr?;65aE#^{GB5a1{1;%aZIrN9u_9VKGHBt<}VaPioV|iUL)d} zpoxeSBK|KVs6D6yKdHO+)G{c(e&(<+d5Cq85r~m_nD*Qgi1PigE%>({ltQ%riUW!v zEc(SSd~L_H1FMWUx8N;Ew_<4|J`j$%uywFbVcY|@dAMWoOM&=xz|?Vueo_NcrNWf~ z7PmO1;3WA-N5oU2ykPfw_y%wbgiFF%ZpddnL5)xoVJ0n5TpU7t27Dqg>WEc(qE>#d zdr)Zz(P7cGzwCiniP(Du5Wj(725a<*`ZIP-1~hk62Q+ut2iEoCSAZh*nDz|J#H>Qd z_S9;Fb`{klT3}O1=tI%MX!OYTq}0iLpek~~>cDKoJMeBfD?=8;EcM{_{_8Er7_U?zp_OlOI z-vqrt@dB+QHTT2c8m<7z6MvHK4d0;hM!&GGAX?9m`+!>4qS#Yh2ycK*6Y_(!4}SO9 zxS`)_tOT||tRb!IGwkWSq`ug`VDN^%;PS@4@Lr(A_VK#`-GZ&eW`5fJIt95Q-iy3J z-jlZb^MMH8M>HUE!@M_s6Y&Dn0bE1e1zkgQ%;tu7y1WZFz_BAcz_H7?r|$-HOXr4j zYt2dAg>cQt3w%x71#=yK-RpV7zxRFvv@B(>D@`8D5 z`0|?Ftw!1RITBQ(We zhki?&iL&kIXor7#e}gmE-3KpQgp|NmZAZujZT~5?h_~v#Rg7?lI=V@+=m5@BecI_L_jFM2^-;&!?6MCKlnL7DgbCM9x8P zb4#^v(+mX$lbt}|AICT<0S+YBxGyy(8(*qsUzhFfUjwik@-I7~xP)kjC42D9b_Y9} z2R}hNk56}+Fc+iz-h-9{dz{=!%K^OgOc$dmzwiB@bx!wCIDE{LFGsxZ{WM3=YR`ngrn%^T}uE@0b2Gxb14QH`8af&4=D+*T=@4 z>yscdC^P@;$P)~K1eRS>@|-eGC0~*MqN%fr(=2UkaDdsS|#1Lm@zV@T42 zvf&h{rc&k%QYk(kFXG7x=aHF5gQQhZqv%V`Ve+Ip3*qhbzm*#9qOw(;#Z4vT3Q!tx z$!UZKWnk-?trmG%6Pc<359wOYWqLf5C!&#V! zuI8A7bBY%r>kjdT<9~f((#3kBKo{;ocw?bLf3F#}XO!Wv}>fFLWPN4jakY3PlniV=z)V_*iw>6`(T0_f41Aa>(C2uU_ zy$G>(baX|eukM5rk;f`fca}QDcv4UK6TW~lLt+)6BPc1gP&#i19kM%k&M3azQBd-Xg0Cy&aI=w)e+wN$ zT>E!+pkoAcjKOPOIMn@mtJOaZvK^2~2&LJkwmGCxv#(l4?%w8R%RF3`CeeAR3wNNg zK$ie^uC9flH)(WrAyK?cBuw?ykewP*Rhqp2nySkO;-T1D{qpiW&SJ z6!(RkTyI3clR=oyfD~C&@7%!~`Y`7!#N#0nN}WGrOW{cX=olG*f3rCIv(d`x9H+~;1n-^{+7 zZLA!eHXs?h4fNWmoXn=73 z4r=a=4Mz9O(EPNSEg*v52sr{-tb#M*A(+5PFNTsc4U(OKKWCf)Tjb6f;vr6#eq{=y zY&{&=RJ9ym#47tWPM3FRo1K@xTbmwzjd}0h`mU@&Y7J6DGeQfe8JSK;YfkR>*kAPD zfhRhIzqOjkM{a59z_wotKE*v_?nZDk))A7p7{46uVz0CAO4#`j@(O`s74in!r19J2 z<+FO5G_6PenJ=~qYd-b)L}1MASvlL_&`!|Mn`@S`9b8Q;nH-ln zw-$Nk`6V?ER$a1elwa*W3FA_I8^&H1X1ZXBi_@yFPls8W+XLa*)BUH_32OF;O|Z;C zW&WKj_G2n)Sy^?&IIbLMrG$Z|!P~E3jx|7GVV?M2%Jr4>5*KHGldsj#1AIoizx0Ljpc z<>o)otISD=55$*b_w($X2I<;^&-p87H@*s3n>TI)Ho%Rba>#HTc?B4dAZX7a6QBg` zdNZhW3~2z2^&*JID;U!Y%1cVtC$W)G9pEuwKJuLmJB%CAL78N5RHnQf2$Bxldu*A?e&bj1H9EbILns1E5`ICMre0?=$ zAyg_>jom`&m#Q16C4O+^091`hRAK>_cWUT~xEgf!zkP+S(?2lHeNyUyf31QhZ&)*l z3nbaxBR_gt9U@yk=gRJY-TmjkrVs}0_uSu>5QFj>m-~4)6t<(4Ge;mXdROiUR%CKul%jDXM?t800sx=If zI2CCzmcy#tvV?I$XPNy9A_!vILJ&WXXyHq0#vSHL`2z7)iFd_&M|rtGUfN?>Q7^d5t()ZB9GwXy$S= zQE1mYx|0_M`%m2Ae<~w<5lp+FT;w?cbIy12hFO|=sO~M}AWf3et<20po@p{?T63=i zK8Y;=P&-{!Z)!1GKNC#z$xSj##jV(D0R;f{lk z%0c!6QFUd|v8gIPs?Zohak#l99#N$?WJ5|AH=$8{ zf?Ayi4^1lsQ}-GL;i0A`CJ{r@ zlj3l)1Su&I5X!Bo%`$jbBlOYK@zIh?M$)Ypg##BC1BF(KgTn}qqX1TOMZt~1K*`lr zNy)!-E8BD_nZ0fd!_M8ZJm#FR$3xQPWS4}p$|h74RHihRNgOoBxqpyb6BWr_69NME zwMnPkAX7kTjJKqUqU?AHDoG`f;~;1eL4AO=Elz*RxxSc4(_@tN~>+_|;Zt>p` z?3fEnFLamehx}e_ikR|V%$ioQJ;uM(k`lXrA5GuRiI+R8Kj9I%DDhcET42QDpCgZv z?&5d%xw|t8Z|5GM;^kGwLq^2UE`<+=M>t*}im>}9g|~-NjlMvnuzTimJH_V{NBcrS zM$wFtsCs^UeR+I*9qBFh{z`m$9tkejZ(f!s^38az>}lj7 zSL|lw!Z7^`Pdy@J15u2(~tTOxAt70Vn@B#AI6s2PsjqJDZ?B5@E2$%l0$7Jd zMfpbRfsMnYRT=ivBJW2rlHm`Jkm{K6m2_Dw&#Q^S!P?s$wfh-3IlkXr@7EPtZk`Tq zgaQ|WaXqvtF+mCzs{Q5_yr7Ey0&Oxcw0=Tzn0n2H;cmc zQckfW@A#ns3LNu&^i57 zPfI@j&<9n&z6uu$&GqGZtq$sa4To^^`$*E{WQ`Y>Z;JWmt+^p`ND(W>=G{VvxT&V1 z2n)84c5(D*)gF3xgVShWv(47OFEn2SW=j>>uRQB!Z)I)n2aR{vlxxSF8+m`0pi8i9 z-h%&)v9k=SD~Q%C!JQCXgX_iJgA?4{-JReb+}+*X-66Qkh2VOzi@P(asd_W-P0g#B ze_gfDukKT)`gHHLJ~x*{(flev|M2}NGd|NE60fT5;rzZg4Btgk5txfmpwsAGiE;gd zAMq|u_Q6Il`^`bubg@NOcPdYJ_MKrR?NbM5-Lcr^*1Oj&2W4|xWl^AXK-lG}qiIU` z7MI7I&+=y-uku^8?s@*T3p^*cCCmk=joXrX1MSjzPMWMA4s{Jrd0Y$b9MVcRIrtZ4 zSi3UZIqc6w!mwHJnWva&b!IP^qLV0v^Y-tJi_p5o zyl~TjS*}&VhXsnf!@@FNz=WW_J1aN_sFXW3xS9Z(lRlzmjOc`4yq1(@DKWq1W)utB zr-i$Il+o3i{@L{v>Qrq(aC$ip&` zYIw`k*=bgjro5t3-=fz3bea{uaPM8TYDRTahvkO27>x@X3=`P3Q^R9h=j&ccCzde0k0DqPO4&ap&rcIKQj zT#hq^;^t)&tfx{{F=1H4xr7uWVbhX7By4Ss?4@p6iD~Vo8EUu$wuX*ssGQl7ODUL9 zj3ab`%A7L(T8agRiGl-eUGo{pPz_?z!g4egRfJpOBLNo^$E{zyd`r;W~ z)>OZ1GtE&>{Lhw$@K?H?d0h#E-H_5G$FRZJPR|#HEt0Zg&6*=lO5WbqR<_dZFDrug z&)U}IWr`K;lem{e8wQuNO$wz85*>DY>v$VOIWKdKdiHdsAHlwN6a`C50c#uxDCG?; z7Nv@=zCFSg;$RJ2hC_Tk6-bqNT?f}Ga<#8moCK#fG3xZcG`9XU*9=(I75N$kivPgi z4VUH>f%IL_P8()f$)dx*TZRtgrEQhVx9f!aVZxzKa1ZREu=yh#n^KHn1W=HARM+XFQKp53NKPm z#|K->c1HFtFRsdk*hieEr~7B(S0BPwDH`QlFYp3G@m&m}s1R1&}94TTgp*B%y=Bg@6h2W`nU=t;}iKz|^ z$i}zL`>o2j5B_jgSiKt+eFBKdW3{#hg@BNs*bO(I!?FC29iXk(e)Fc0$F&j$f4ckt<5ooPa5iO>K4eGQ9eV(6Q{8O&Uu9Lcu?#zl^eJy;0zuWbs;_6d zdYSeJfH4+UT1%^vA=a4cKF^-h6GG?pmO3-~F+uN#)e=q&LX&xYrdPc-9CmImR#qFW zm)bl1ToJb_bM15|J&JwcSS`g_L!AK?kLgeIdmE=}Q!)DGd6sd@8Az8GsiY;9$4jDM zFTZSyp~HhX+tpp7FIsJ}v<))Nv^6Zj(JNayV; zbv&-)y?6G##>D&?(N?raU|<)>p13wDcZ_!=jdGR?96KwvsA>_);^1ZHXlyYE-dAN7 zcx@7}QyD79t^OXR0Ch{zrS}5fWE?VRIR|agdo9-s1Zk~K>BVxrTg?f)d*oFtO8`=x8E+FnFFL#vG3mnzm0F?tNgZOg`Mlj2=tX3&FA(~0HEQ#SC}(rXvs!f% z%)&?u7bVf|n~8~JvT>8GDi>#z{yZMWyD=OqB)y|P^pF$I($UQ3V4jRCOrxsSu=sX~ z({yQfp8hceW0Fz2Q?`6idA~b-+sgh*z&@QAoN4a&ik(YNc$5-KcCqCoa~|obMEsy} z{L4nyyO>frxhf%ITp?|&yZuC*Rv#ZNQ^|w2ZbhroiOr4!SW?bzpG^Chh=Wh(*rlED ztq@OyN17>bvg=b{aygk;8|9ng1}i|iz1Eea9?uN%(6aZkQT z7&CTR^`lJo<0o<7tUIZQ_!@t`I^{LA63z_>CgBYKSd&JkHmJD*}T-I zxDn*Rdwm|Q|2DZ)*BUed+iN{84U}JhZZUr}#Wq&HC(dZ}>M#C_N;!3*R!*(z&A_>u zzC!C;!`n%6KB`DNviI>Qd43RG28vS2Tj6frNz7kA95GGg5_*A2XV+F-T08k2Z5P(f zN?fMJKT1Q$V|!bh?H386`1b9UyInNrp9Xik>^H7gg9Pxd$uazNeKzBxd29UIb}!<5 zh$n?U-ZH$4JCA}MJMH;jA7bUOq9(R|j_z-(bdn=%9#j2a8{3VX4qDyQp7Lh7hLg@0 z!Lc9CGbdiLqi%PKY)&FkQvdp!77V!}>i&5DylNA9VY>CdZmWIuxLI=$A?~<7G@A+w z0)F$4xg>LNdvW-V70W@dV8b-i*G|9n%kh}eU5=&ZD_utQ_=@abR(qMP8^X~JX#;~K z)pY@dGefgni{cH_Q9NMc23w3P_9{i829*((T!d^Ok_uf4#y=TAMmgSg&Wht?lh^l+ z5AJ}14+XDQ>+bUIgl{-8ja@SOPQyV56=`Gc?|xQ;vQK{VS-^9q9+vZzMVv*8O-GLr zs?CUTm~93<&i9y|5|Qhz(oL+#u=8-8H4@kZ`{qABFN-I>DKaBlPJTK)j_eG)Dywtt zyfa*?INrNELGaxE2Mx_oua`Z(v;28^d)8}Y(+&;`qd=xUadgFwi>A@8P#+KZm#39W z+b1{B@aX6WtVfTtw4ghfug)BrP)GCo^+nG}=DfaJN?nb7*R9#g%F5Y07BbVJ5006R zTdC;-nzw6a*gcc)UK5AaaaK&lA~pc8^GS6-)qRFPD_On8>)-~b-ItnrQ^<2W+-V-I zf>MQYK9mhfvd3reNq-#*!C-uOIVy>YCCpU0#ksRjgC|0oIomDm z#fN9={maNMPkrV7YhMGu`UTB%M=(%r3hdoMM{H~rm`Y`L3G9gnlD(CIHepKXEhzh#gVYLP!4Ob0}D3{-J? z8V(=3-Lqpip6<}samRykh-LGP;+i_l@@2rQ?qOPK>U&cQfhmUk@P3e80s# zI$!GcJUKZ*>Kq%{sfaYoUx*gyIbys2dxT1f?cm32D*Q`&e^)LqPPfs%Dt4XdZ0*;{ ziX7kg`tI}a9?Z!=8uZb{k0v69oAi+!Mt&v{u?W?n{UVKn6R!HX*>FR51m8BV4hc#Z zXCKAauMe#)=GU;^rrH~oTHWReZR@dztmo0k_Xd()dXap!fENp^(IK6CH1G%bVzt`Z zYr!4-harxiXI$A?{UXJ5PPQfN4??C>W;X+mPGedev5(XlZS5$!6~JV-zFmWxunw)rM>x(gXfWF}dE4O4n~xUnq7Ku!M_cmOC&=xR@BlDBz zV6b6CSt1vb8H4hbUGHHu6OK9^9^bFGJ`#Lfj%ITBK-sr^Aiv-@hpF8e-}1AZdxuPs z!KPA~t~DchpPQ%2kS_PP5n{Mw>u+j9-AtJs$}GtO_6X9J0Vs<&e>&FKaRdEi+E)vUCYx7*sVCK@I;AYrLTBb2h7shlzx_ANF2HEil zjH|x;jo>g+pZ;8JIwpPOhzv3+%o*hMySIXxOjNhkOiL~Y$(<>yz?V4B)%Q}*>g3nW zZ*Ofxhld0uz&>WPybm!w5=Gk-?78SD&A)es-trKSGlEzx!h~QA9hzX*;Oj6=5o8iy z8dmQnoI0HgG-$s^a-fzk&Di!JRCudto-zRQfo=(L`u~oTaeCULN-OE2|kJ1l2$h+r5GQh>&xt=Djgu?4e1cPzE9td}1U4pyAJGsa6L3_`L1kkz(!nep&=6 z9ZEnrKINJqg$FGT^sc4}v&Z?1TMYHf7S6I+!={jB4QM85NW6;7eS8S<}< zCH9E>EKE>_t`!5nxX=$mu?>brmZ{gl{tbm^n_tt>f&0&JiUwuOaPN!cWwb8|`zt{U zvA^@-U^t`pESRfOf4SDRr9yYDP@$nH*YULOt^5%h=C@d@%uj(UNf0PVe#CvCzrE9t zrnWIWVJesCvC?00>V9VT#KN(O&o{_+HfbF(%GY>#vsn*=+I~Wp_aGba&ZYT`rSAOR zoFgFC?9dU8c2!($;o1?3;Li7<_t}W#yuXw&P!$ufCI4syKOo0I@fY-cJB1?PuT>&* zI2i>5MC{j=FU1h+`wt6>C;N$xG^8(%0c(GywM8i@DH0sLf3_zR1-x-xYEDnvON5!i z`(}x2XSU6>G|$3i-yCgSE7Y`fg{?56!0(B~+XSg%Kzm8%D)IR%{=i2had$HGg@A*F zAdMFY2v+gu8Q>y&3#0A+Qt7K5{ACX9v?qgh#$6JeRuStD-C4gZpJjD zGaK7;ag;FBE3>mzKhs_*I&5kRg#TA^#XRVV&fyB9ke@^2?@QLeb&HG zIjt{^b3k`mtuQu!-4eJ*9i$>!XhBYe&^gT=%N!U|+-W*kwWXV7V(jzd)VSKvi3ZIe zs}X_2Tn>g92=(LIgAv=jN3Kj7vDR0*>^aN+YLtt_{Em@KQrF8}@0E$Cffs+#?$u4x zW}M|W!eA+PqtIk%k7WI_ie`tO(9oB%AiyPi7!i5_J!BVj~sD5L4>{9y5#ba+ww3;2nY8o`_>&V3^0FFT-d^?wiB%X z3oEAr_K()UgdcvSvxGA6oz&}uSetX(m?F?ia6~<316vz)jSqp4dK=2)!Pg?`zmw{@_cO$2IQ5G$e=bS$L5rPlgesV+np7eKtE}r!m#X-J zYvaCssfdCXL(HU$;fmuf=Tb6%^^WKIV%hiuLeBV>K`_4~c71Z~Z7e99{u4td3L-#Y zpYF}QyZKTx0K*u80x~{vHHsqkl5@9T{`UJQHvXF=Nq~C*1QkSJzKdDadaf&BJ)cM0 zx+SmNIGoL6wC7Fos(3bzteZVoJlQJADcT@!Ik$l{pZnrItwzE*Ua^`0E4f&-Bd7rP z6ryXgU|fAvm(B&)gfpYFN7Z;|L-U81E^qT&4Ln7iPp0*P#jr%<&JJJP97aA)9z9<% zo1%B)7UVVp`2#Iq-$xa=vzjOLx#u+aXR%&KR!*|NQ!fL9tJL+#VU-i>Df(>yncFVU zX{rxwU#e+Gib9>3+(|n-N(dsP0QHF(7|3uvIY}3hFdJkYRJfWqt}&`LHTut)tTPmz z)!pj{sZDCMp~mzH!=fq!(9l!K_>DXa`W@vv$0~b}uy+~n)P%I?A1~*91ixKkuq{OL z?++nRn`mihgwnv?8gzXq*?_nS#=#W1DVxmZ5&Wu~qdG94;XJt;!;!X_9PfpCI>FvI z{53ALTi+rlVUT4HNF8XtNA6R{53qDod$;BiR*?f4vRhZDRJ3OJ{{3Mh4=dW$U$*sU z&5`wK3EYZ0+y8U6!vsSU7+st43-w=zwT%NiFfg(4DT2$~GbPVJokzB!tSj?reS z0x69d9xLrPcj&Bs%e5ph+`!8^*)H}TqreJaViKj(0&lh7oLZxmE7^8I;( z$73H563o9NlOT)HZoaGrsQ!@tB({AgRptxlQ78xC`sslL$s?ARjK@WvFnV!+_Pe6F z@X@NB`%L@9Sl)lqA&AZC1TB9{cFPl^`2iX7<7{x|2IA}vg6Ibr;w<@7|Lper%q5E0 z!MQF!7X=wgxD+hex5w|_qSJS%ArZGrp)gov-mrAeH|%=g-G2=L*Vy`(O)MB9>GRwD zA(=_WBX$H4dAoFOx5f*LepO+6KR*R*BIZ3_-q$%`{ltBo&zoOyV(6s#=ZAzKu!X-o zAIlQY@bZ}8q!s1ifX4aZ)-z2EgORj-cIC5&|6C$&lv{WS{cu;41;!b%ZRl1Ox59o% zs}1{8d5XJJkr?D;i%$sdqZvI#QGY*tlg>q^%I2US)o0grUt-cS_Zp~uEY9{P+2$3M zx^}x9v$^!gf~bmYHYl+Hbc>6mwD|bF@&hOh#DxE>YHp@Ry#-}id7XmC9%~T5m4*9x z3do!?HSO0LQ>uuJXuY~+6;ofOWmbw4igfuf*(QL2hB^A@L4urGH(y1k#~FqbXeSt=@Q9%#dEm9Co_Yh;>C zdPxu@t$Elrwryyw)$yI5MzzXN^pYaK`?aC}CbuRT?MX+>9;sx^aztF@cDA(yvGi zYs?yr(Tw=k6*<;w7t=Q!MerIf9}FaQHLE~ig~n`DAg|&PIw490`O?4-W?j?MPzTTuEQ{-D^SyHqk~o ztCl^}7b;#^H`arzy+Uc7>GZy4O{^zxu7wSGNQZkf+z>}&5adgh`W1wFOf@s2Jx9Ep z?k9Ht38=%EfBw4W^>m_`Q+3ZFxN>(cSwSpq*Lm<8WrGCOB}6(cvp%}zs?BgidE_sc zIjo&f^(=eX!6S4lz{+SnZYfb5pdEHZ2`AMP$6`q}Uk(Yvru#~y^SY*)6>3~~U{=W7 zB@KFOLxBgO z$)cxoaAGHLU!tp{XPf!jKshAM{r{XBiF2La*HMHonH@1op9Tg8<5& zmYV8?FsF=&tY`EXTl~S4cy*gqXw_=#W01mMYhAD8CuWuL)iv#ftX)Mr2V>=m*qE4N z=tjI=jyOum?MAsF3A&K8>k!AQc}vgNJd~SffpvaKfqewh@m2+GKTzYMnGKsdO0~`W zqkv5@6MYJ14Kn8<=h93A&!Hc4UZsalrFCHZdO1TUmEfMbY!>kW)PD9MGs0xS5Sh)gt&xp>&#MR$^vj1`VvfUAz`5=Qq5pQM$GKh9xowsg zO#8*#2TZj?KgxeY*l_&^VWUIBtV6=d%&bR3!ovD*$jMm_f~-AYqStMoABLcJ79 zyC7A|%KcowEUF}9v*s+>O*(zs{oI1V{09qP&ZEHP_r8JI4?%F}if>Ji1#cKMy7-=P zBE8$$mC?%fMyEa88s#^p!5jnbo_v0KV_UdFeUR9f zBkY+B(9=VqMjh1zZ3tgavCqWae9y?6Y;t=l&DjPm3>Ii9C7VWpZb4F(HcR8?50SS` z1eI5~#65t4k>=${Zhm;|lk9h%0b*Qy^qj$tgy(Vg!bwxDZd2psqnLzyVJ@B-8xm5f ze-c;xF?w>{(BTP;Myh+{IXl)h7g?`~*L8J}e%!;f=yTt1?~Ul7lVO8aqUft5#qBvY z5F5QY@H5I@+;b-3da{m&QK;_LrYl^x++Y-iVx2Yoc&KU_Th`ibcNR0qe8}R+=(R(6 zyT|Ovjye^%iO$S`YxiTo^s9e7Gy8X_il>IZO>@(sa0RXF3`Q2=7 zb;7^!Chfdl>b|epCB;8iSwP>^Bk?BboDgbZfW&b-k0$COi#T#X+Dzvio&hpxmZWag z!ee&vzVCol&0pEgCwp?g<|;~Mi^5I~Lx9Lx);oVQ^N)YUPk!G^EKQ=q1oPk4m&I(= zvl+?5d9uSO{$DR)g=Q%9Lm~YNzq2E`At<+YZy(X+-WeRFG5$SZskqJhTO6Zr&B|CT zpm?n@GJr?Ynn4LW5_t9W><{M`yp2yX%5fM7uRhk!5efafr|>V-qjXBEYRWsG`Yr^I zUB`_o<@voA zaP)wObdur`qDRA4{XU~+;j~AjEp3DPKr?p#t;Z6M=>nuTwyR$qw?#VPryY|*2b}#` zsf(nx#YOXpXg2AYRJb?EaiX5IQpdg6mRh6nteV-|Qr1Uj!qs%0=?ZHyShZ}ldEpG0 zGji;&dv_)H7if7$tZ_g08g47vWj&N3x5lK9A{Xbd>bDYP%#1pAKVYSOI~Lg|^$g~@ zOF7NcZMxp-9>1&M>SPqE^pOR*w=}}hrI^_FTFca=NRM(@~d`*reOTYwaJq3bMQzO2~U<-|kZZl8isS)pE{vBl( znVNz%`~byk(HfVCR+Rg8ndEF z=hvDa)WEsUex*TO{aCs<*9G|Bd-{h-HV&T2W(%BacqL;}V(4Z`t&!zGJ>5pMHUV;07EvkyLtL}uGu@N5|Q`yW%D5qNMu5PEc# zrr8f%*s1_sAbVw0EjTZ&-M%fU`p?KJ)o!?q(sT1ol`vng$jTtB%$t2l zuPuZ>id$CD``|fbvMG$eXzX65*2Xy|{s`5Rc-!h_W;XoDcmBYw8~TBZs*gJUdHUd2 zK1Z)oXNZDua&-YU=CmJ^B1aTIfugO0&vOX z@*}3xk~bNce{qxFli~7QW0uV-O{E9J8h`W?Z=w!WJ*TBlur1HJX5O<^JX5|>@4l%I%=-S1d1CC}$$xT&J$ z@IAsw+C_fR!TF*^_53E27iyPyi(&e1-#;?P@11P)9u{POvAHA&6#QB0kgH$4b}QI2 zn^W~dKBQpw;Y>tStD5UHqkMrC?c;}ja~9Op=<{iKPJd!v^eD2NQQz@QGU^hY$EQMi zuH^TOnQgRwY{CA_So}Q>?b4#u?c!$QC{*6B`BAiNW!6E>!b8aaz`GVqH6v0(tO|DVLC;$&n1>eT+`)Jd{)Vh{?!W579$A7KE;%wE(pGa=<3fhEI(a2 zDZNBy7>NiW4_YT6{C|l0g{k}!y z_k4T=p>;@HDa6k7Ogg$agqZOO^t;pWEq@h`eFV#Hzgl#(7H9O{i^pDSbsJdX_x5>+ zwG4Qv^@=S%3hK0uI%D;feOL0^iMEbW)1taZKJyJNxCM3*;W?y_zt&ePgr2r}Plvj& zoU4Npyob%PEP7Y#^}W!_AM_4yo3?;*Dq}t3z1yc%xS$o{$BJG*-TU#QQ^6`#p3-u! zv!jpX*|r0v*$$81Z{={l&~tUmucDrQquH4Ia)M~El@^8WDVsS3d9r>6K`f+XST{Ui zwWPd~A*>sLs}4lI_)7u3(w02!BaY{3O|F^Q36Rg{f>Cl$iINDx4(aNAkAyj*D%~}+VTXu3%pccd;%5^o~3)k8sxS z4J_E$HF^`K9ZNx$wK=m^p@$PrPHgP7VY+SAEi!RGd1fOq|259^ez(-bxNA?@#+kN? zc1ukH8-#k^XbIV^@Kg~0X@Y|< zLGl-{zQ9X8v5geb1DN*=>t2%$+@KFkdjV26|IEarE~^;os6l;oCtBX>B$93zT`o93 ztq)t?MfTc*`1i@|vDe4oT3+d-Z)fz!eI%Vz0evcBJW2$#v|YFuJl8lL_(Q&$Zo&!? z*tikR5WCTHpCkOG-ct26s;Rr>`_wrZsc;1=?D~BfyC!-Zb?P}4$oY7eRC%Nwf8!c) z;1bgDha-J_mbXaVdJSy+Jbu0M#{kR5^h~}k=SjIdu^UM$B(=uX<$TJ%a@_rF6UulH z3V37G&$X+*b^1Mit7$d#SY?CdoRj7q`*ES&n>S~puy|Pt!q=_t*M0CC7-LIMsr#r^ zKcd`6xd9pVNRsE}rATzXZ&xLHzHHO`l(37W{3Kfu^8A%}X>b+2_pZDtG<^4tEt6`~ zRB4lf=-q$$x;lz={=TPL@h}HI6g3JOT!uzVWKxNATxuRh?#=z4Sg(s!43Zn!b!=_;FE#3%3a-T)#|ws&2=< zkdFp#yw-9(<7vifbhLqeRE|?J_e`_PTa|6_vSge?_xZE+<8`v+>h(%*_2wBb zVu5m5c*Tilz_20xun=}t^*0#B5HBXw2G3WVhy813EH)`1Cz5s(Tr#X)E!#N}X&|=f zBv)9o1l5Ksbz)5ED|ug-%o+9JN()lPicwvmdy7e=gB7;Wx}f`A5-cwD&aE4#A0z0r!|zZB;wTa-N&yWO=m zOR47y8I;r)-OUZp&cp7p6nZn)o@UQ@(doqY9GjKZmTFQ;#A_aSEp!h9t>p5?=M*M< z3i66b{vm#r?2F`3rOrj4RSd$S51hd!7q7&nT=)}WT#w5*5-v0Bs_P=Iq3b;$gw9eL zF6<~CCBFwir@u*HOr2?UuBYw1x{Mo^Xg6G4PNKE{ElJ!#15qGmB$W`LO}X&3Srs3p z2)*1J`^AZ0F8`-$z*d&DizTu34ULx#Uk{}h0kKJy(z=`F%C4TI+w@r`5V+~)v(gd^ zFR3G0xvxDcGD=prLMMsKGdIHfSTN?xt@nFll;c2ON~Juymf!3GkM2=MKB4RC1K?nI zH@Z#uZ}aJ2*mX?h_oHViuPwfESg>KG3m2@I@%BnrcfDN@g@)y;eRYzQ*ZS66@}k#H zV0R~U-4Nkp5}i(S^T!qubCjWPU(P=YwXMLf!+!2WXfO>-8~iM9njADKk2UnyJOTld zHvC}r1oUJd(Mulv25&8ASI%(uD<8`3Zqgh5Qv+helKX&PS9WFbTcV9A=GgtsXI|iEm^n#@GtO*f z``*n>23u*GT$io&Npj*|;dm02{1`!A*8U7J7pINv;c--3Y^UWx_%7bi!lH6mDMeRe zC~~jk91IlPolu{FNZnKJrisanQ!WvZrO{2Ssvf(5bU--pOX>92CAR5|+{>#!hu%|~ z77`NOu6#&4N;^jhmrnikJx+vi6CKDo!q5Wq^04Gx>7hJkFLc$M7h2d z@{%=UMEjw>DwS-byFAvbMS%6_7D6LOF-h_e@){GX$|3R_BCFgxo{|)+=ZdkqPVr;K zqq)7gr(9K{HAr(L&D@Kb8|!)B@OtDT?==`8EKJz=x|c1B6^wZ96w5`ks<*3aEJ#wc zrkg)dR6tl=KenQAs(DIj)5RSW(}K>lkSBrUM+8$1>btRsjlyTJLU^0G_w^*+gSu0-@Kk2 zZL+aWJMUfJSvSGu$MCik2M|&vkaiN8MT4Nwt~XQiEu0?Lg2%HxNFI;7SZDHZVbxv z;$IXROI0ZeI{q~dH6J6%lI3L|BF@2>&RfJBubsgBx{vTMNe@YkVz&3nuEQS7fV4D} zpq_oI(Jzq!TMk*dA+IMd+8WZyq6|HGF*PULiaVwIgf}*Fli2fjez!n2Y+dVY%E=hH zgLSjfzOkm}gNSFD$62;^{nU;_dW6H-=?UVOCk_6Kl%E`5Wqc{t?1{K zBatksEY1=$^DmEq#xHg+|6at2M0Is+wIl`!RD181`sjdyNoW3|edp}laidNIsYazdf3?}`IRR%)@22E5H~X~QNB&f z=!>+%iKA556fIJ$A$_lJSCnNSmbErFJ=)k(=pV+Wf64}|H_}79rWz|SZ9b%E$oBH>=k#FfwQQ8P|%xO zO5iabtPe8GJXa!NfTWDCzv#;VST0|TrTbt|{AfC&57W>uMzLlefFyh?Jy@gSMs2m> z31mXJX+$zCmGwi#Jdp?C-Be}GtU{8t)Jw!gIG?{Ixx?Z|9Z`iNpme-Ma)xPhv1c$( z@+jVu{t79+z!@=yKS1U%r0q`lP^X$tk#eoV%nD{rEhvHSV_hUKo4!v+jGRqCb=WIN z5w>;(q@#y($tI#*8s)--z%&y6i|w-tHV(x(G%P(uMmKAxhUWQQPWY94Q<~g@^)J;zxJS3kJuujiiEzF=mUaEM3p~$mSeSIjE zlvU;;*5c%pT{5^Zfi8bWD-jJX`JJFtp}B}gazrG7t)``VRceqaaihIvik=+SDov5` zVoG{8d1Ph*)}j-7bSPclr}Br|fp%1bbH=7hXhKEN7_pNoFIhGYH)vRzUp6sL-Z~u{ z_XF3>c>zG<+D>g!Pd;?_HgPBIX~_UkoG!gM$UtAAk(ojY90-m!b>l~3C3@zG^&wOr z#&vgXCTc=Ir5P9pYLT=B$<`{E*SdMomM4p5JO1;&dk6h9HDhA(6*ZK393cLwVVbn8 zv=V)okhUlp&1^dfk3KU2<&wjEPV8sH4Bsf~kUgWC=`1bm$^GR58pu zYQ9iS${3aKH!6(9iOMwB+Nzx3YDQ3cK>k8wYrRVJu~l=Hbk@|ya?jjPK2in)K=@G%{|KQ^FVOQV&#o>Wn!dHWMTZPYUw73wPuJyWCerdLkm z_|C4SK8{3+Ugb`v>SLKyyrs+m-j$NDE=8ijqWO+WcC&x0`gq5W+TXeu-!%_4f;!86GrR8M7@U^tAkN&C5q-O}`4N{j z4|RfBOOrOv+~pBQJN}Xw>Xrr-0>0)3J%W}s4-tYmn|&5L+r<%eyCusaZ31FD+xd~A zwSPyylFirkoWu(w5_bIMF>uXJeViAIBLZvfIs|HKfAeG9)-U67O z`m9WPIcw)ffNSYxG4w4g8U!xQEHVUGYwaQgY%MIJ1dA<7r7>1(?Xmol1p6%sMJR2YjKC4fwXx+9ZaY&elW0!X78Y#+ z92=8lP6Cyr|mAHJu9PKwEwsNrCSe?wo0^MZdA08hAh2`#^8!jqa#j9l;{w zu`Xrcu?|fji>6g*l!C8>t^D4sg&*zb1L5!v%=e!w?2^X|O{Iow7 z7Sv`@PS++)Nh6V)Cd05`1NFD`v#F=~RZx-mpk)K8XtG84CZ4f*sH0i<-Ba_~(o?}I z?e+L-t+t7ToK)4_xkLs_O{oIr#v-82y-lf+quA^P%ol@*xoW1{+v5J|3T>djNXgr? zTo254`-Lfw0w^rCafjC;>jvBd+yK;?esw3`;GSbZ27tcCw?XG@kVR^q^%+9Ef8*Pj zb2JDp^-`{H~0H((7{u};D z{Zs+KYyI$BnI;7Q?oFxjj?77lX?IfP4%W~X$QlOL+QmSI=*Hpt)lGGom5ZFn>BgXac*v_5>iC&6u@&Vfe4m$=r8dpf% zsD7|Qf&M|?2UxD4o``+mVuKt8xOQx=u%5p8d=Z3`?6=rqe!?V#gd3pVF>!?Offj`R z9`HCI)bG8ccO~zkzO=r>a7E<-)s3nT^*SJUMdtz2jjRvJ6ok`%YsmB$Di>-t$RfZ7 zXs??G+tshK^Rph~3}RiF!2^;Xl^ANhUtJNZa)?UOvmd)2j~|gUP;!v9h&WUbnllJ) z0BXn2@q0VY2Gj+tnjw@X+Hzpa4x^$Cz%AvsxMYnL51TcICd>si{Q%LO%utib#VvxLq$Y^nF5#L%NFNLersp0yZkqq1raDX_a_wPe; z{YM$f&u|?}-Hov|5{G3e1;;~=IL*@tJo>s?mX6&165@#&qRU$By+cCq`WFS8=a4v% zzR#lE5{FrF8CcUjrlzveMtVkS7SgIqH}yP)lT=zhdmUKG?<{-(PJknyhOrY@`(1v2 zv4uZ>+wEEoJ_|2zTAb90DAS1m?Gg;O{a$bVEktC`PxPVvM7_(Y@f4qY*B+_WL~lMi ztfs2^*F{Ws_)H56tBK~~^F#8j4e3hgR(Mf^21Ab(sppHya`mcH6A6kiGu@I!s_S%P zHmkEZq5pgG8HcUM2y+00nD`*lsW8zK(LcWjj2}#2!2XWK6~+_w6FMQ3P{8GY`wq<$ zwhvSL&$X&sD z5Ou>o1pDr&T_JklbwlezGX)$C=-0#X!{x%v25jzF)g$r4i3dJNCXh_`tZ3@QY6tn3E zOgrQhG+V!`B5Y~kWWTB+VrjrPMdZjIr0K|rKLpdEse%wl(4ygZ`cW8R(gJLmQB4C` zNHC&dae|c$p>a_cgb}4+8RMa0gM|%Aa3NlTZ-s^Ca43QV^SQXuC<63V@3u2KJ}VSv z{JS9j9jVpmH0>XtVFXXuzJs$qtN~HdzV&@RzJqk`N}4}nXtetodX&;ZoGxL2^Jl>XFm!r;#s;}{VF*+S#yA+!AG zi2_gdK;|?{I^DD)0duWB=I+Sa(r5PILy=Dj|1xn`fqx0=kb;C>3=F?7zkwgOxEAD= znfP(Y4vn7}LcYIlKIr+8jniacu+@J`n>LkcuTs_XQ@2t0EYp7o_JYq1c#)(P0X z-8X;n_2$|XeJmK|S{OREc({G$7s;|jQXBdtp0r@=3GNeBRp?tpc*%`5%Kjb6S;!_a zX~=?DCCQSaU6UN9o+aF5MYUEnNH^Nj48f7?mgUeg(4w9-{;;deSPWK00<^64uRykG zfeb>F$DC_^5Sc!AoIPs$>GmbrP>WtVzzeCpUs{FVezgV{;IB!qA3J$La%@L;uJqsI zp1~=-hN(vJM}9@q4%rtpAKp5~X$2L}X|DR$B|h_D5Texs<%;f)-&&rd%jLG6v#Qw3jB;h0wc|K-s5yBP5z?- zG^|%87)NX63We24{ubPgOE^Mp6hu+fn^t=k$GNwYQ>?CuZxtqAtw%%zcMjTx?a-jM zLjF8Sh(uR}X@sqci0IyFb?3l4ZON@1c;epH-CQY#Qc~T=b^CES+Gb$FQIT-q(7T5s zjy>OcV8WrjZy)M?p54_hB5YL|(su5&x~#J;@Xj)%?=CZNg_L!733Fu`a_N{9*Li>3 z{-n75amXj`yxuvYb#!Zs$Z*3MSB zzpppGjL%N)w*JJT_>4q*QR0{>b>Ae$sCaGKI68)-5kk>od1$d9SXHvMTx~*aW{4RO zY={s+j-bGh(BN7T?&@OLY$4r40+$;b41CO>z~q1cQwS3opqUaAT3i2;5>dekZ3YfV zOo%wzM|4U^OzhloU!VC)_UsW#(P}lJhLvf%GGrQfQf?g^XV+$6VnSRWG+MON7HxBi z#I`n@nfL7Yx`TP!Ma@rn2M&m?^@juOC+;?O)}_h+wjj^@gY9rY9Ce{`S*NYxUCwGR z-}MG`#USXgZFIN@Z!O<)gr?9=(H7ApVN$XcyTgvCsMa zZHL!ws{f|`y{uLLxPJWFjA5&C^X@1eu>XZ|YlihsmjC6S%6+ruHrkzWL(8;&MfMSpvOH8xv@ zv^`uFk{lfQMOm;l2$QU#7a49kqgzKi$Yu?>)`ypald_JzVp3AzI&qAjUG5WWT?ibxU+@`0m90<1WhOZf4de6*3 z=ap=j*|ulT9uf6Fi=92jPny{C`uFY}lY8%xO8180=AdqIBbMx{xN_HZ>1I%9pr~3= zSJd{b$k1{3AHU~rZ}o(zv*qaCgx23-wSI=I^<(1y(efqWZB=KR_uQjP9WxjK+Bs&5AJ~PiV;OJ^O=iYC9zxSN`NlKMW!H=SSVU>zk zeWq;S7%)C4=!ZY#{rc>f{$mo#VJ)vzeI~-^EJLhL^zeshW+4z+vUX_(`Y3uDYon)8 z;pKm#LfOtIp15p!>M6MHkt_eiH^Ox-Oq&#k2>mjwG@8{`)88$1Dq>`{?sGntM#QKm zJ|uB?)MEO(XsJ_^5l2Neulro&6G=m{Fa~znrB*8>N+}o+-{>FQvogE0rQTlqI~!Jp zb)#4QYdA@#WaTofkgIuKWi)8ib%LNmh*1$-jKNrd0+}sXLH-LEj3`(G&}bj?2MI*l z^|2?!OAhYG&u9PM2?G)?VmWrZu|fXP;F3dl?7<& z!6Q=_&^LdG9=rSrq_lb8KJ*2#;YP6GSD>?oSxR(1gcO{hIu6TpsI`#TI$l$U1uQ7! zb$kP2KrMeL6bJ^ldbfD(A>=*pJ?}RJY4Yf#kxXJJNE{+49ve)hMc7~f*g+|=ReN=#!}8L72$f`K!#SLW>=`qir2k8iH- z8hQhcTWXHE+Bf(0+}7@Kw{7a}+0@}-pLp#G^UKZ!uY5PWAHkD_FHih({ag37cHVz{ z)rPn3h2!fWH>`N@1+b}>u`!W2ZvXb6PGOc4u+%8*=FfOuVLA^04Hiv+ObF-AfSV$sk=4<7RH7ajs;O5R+t8GEnEn4myN%CB<|h z%PJppCOMOYv`z zfRZP^K0zc`A|4QezE8|8VIz^RG()D3Zaoy;ieH!Ab!n^YE`o?RLj9S9bK96&DKZ&| zOber-k!iQpsp^b>lOi)jOAPg^K;Gw!e~ZUuDDF=ZmS8~|hya2CJXv$=%j-71w5E6@ zwSHCWgS$r1JL0RG8^X6de*<23`PsG&?K#gsjt3a>ojH;v{ASiem@&hn5FzEvPLS_y z9A%u0m)Sux+hS3obaGc&laW1&{t(tFed<3VCzFXXGab%O%P0a^GaD{CA_Wt&E~!Y4 zha^rXmC3R5L7=}x5lyE0k0Nr?iDo)eE1DgKi=rbUE0_>rdaO`}D2bz0Jv*L$Wr08` z_&Jl8m)2sEh!z8$&O;zeS+cGd7xixH$U6V;fBwlWbAK@}qjUaJ|AI$uD0}tey2aJ0 z#+-0b^>FX5&gO!)l{HVkJg3?+m*IzbS1 zD&s{_6-lKt#8m8<6mi!zTo9CHO0Ok@R;u4CjSsbc8ND*+?$>W>zo)itS98P0`6UN_ zHhX7nmLmzV7dHOpz7}iJzRdK%lKc9O9CkVh7bO0KwT_v|U>4G7CPnqN5k)eX|6tg_ zWGXbAeNx}0>J+-uzvUt{^k}NaeMiX`jMCdDMQI$D{;dcjX(-a<1j$V?nIRlqF_)D~ zavO*i{Ebc953lnrDtz^6{<@cz_1@K2FtTQ8{iB`vD+3Qci_YKi&b_nM8nkb6^P8&& zo9`OkeB|J*8_)+3BqSOXifX-l0u+q@Qf7`!xHLQyi7DR4pAm7DZ99<|ggRWIfE3<_8 zNJ0rMx(`%uLkEr)GpYhyTm=O4Kv>h~F7C@M?&~Yg#TmBa=#NYzQ;psWdor%qo1#t4 zO23iEss)&Y^ti z64Dj&4FL%xt+6Gm2f_tH+{UHh={0Owc@d63N`aMOL6RE)1hTWV*c2$Ds*~xA7f*1?ne+OuC1S2uS-4ALVo1DB z(h_MXp|~L@l*u3`U}?4BclP&nx@zj%1N-;$xg8x{1y4NBZr`xp9Z+tT#8CnQw6i z1l3>j$3rxPP7<%{V?|6UlPf1gd8b*LA)0X*GvA6ZgY0$a-_M_3S4gW0Q>?(pQw~i; z&!LY{{^h@*IW?*NEQ=+}pBg*jg%4l;#qQl4WAPNHruaP;i^rdmTI{jJS3jq~)=Fvh zbBtoo2J7Iv1!4I35}VvB8;Hne6^G_rIJlNbVfcm5>Bw?u5IGcP0S3PswrMm)UVb7kVw%W|q-HsiI~<*wxW6G+q~t_F@7F=_`xyy| zZF&5Jm>0=3O^8=dvd3phyfwoR%L@_(GoA))Qaq4Hg8_?joy}m*%=S8;mFw&Vqchu= zv3IA!QCw2x?(DG_m4@?v#h&t3yR`PQ{<^8n?3?9{9<8}#e$&)u{KZ?rNU?48I+82V zkEgQYaSeLM80-PoO5)nas7PF^ZFZKib*eh`m*f$qgYYOKvJ}^ndRl3KTz^TVLkOiJ zV@QRGi*A?^;CqMV1zix#!_V%Y*XOLMZ}aWPUp!YbE6;Frbn0ey`>i)i0ZyWNAut{R zWSg-rV9-G*jcHH`$-3a8F5O(OZ6de_O+yzutPw+i=|e>m7^kU*xSB+%;#>1M~gA5yK$qHhvSr7iRlb4Z{llsjDCBp}D;Tl)+siT< zuj?%Gtqu(BWfgb-en-8ajxOZxeqlv-^IgX_b?jJNYANoljwYFHZxL1kTD?s@kr`tp z3W2Z^b&6SHB`%H9oiwTI66Pu+&NB&CVlZCKN??>OX;PHNan3U{Sc$w zF}kVe?nQn=OicFQNV{4x1=XWhigx31bb#(99SG}{CNpbLC>T?x`VtopcVHF4s{&pZ z8^ftDiCiM$=`bngi5IMLS6Mvb-+h+r|F?Nh|D1mfcSl^aTY7x2;V+J@S@MT>c5F;4 z@2#2I%+Q^eFY$4Ztd!X*VO{|`csSXe>=qzu4j(iKWiIkXSND})ht13^cc7$%q7nLQy&6IF8U1yN4p$sZ&W;1J(B zgn)|e(?Bd`PnUGd^fs6+4ddhP+O9=Qy@M}z&D-C*^`|EPss)Yfdwki8_x`l%;r6aa z%j){e?UvwRSO2Eg3}h_rsVhp;8qIr?9F3vm{Or7BbD(K)IB$7xP@^9C&n2Xo10zJO!YF>sPrgfnCt za!lHXw8%C3??iPNhT51*q79obOtbia<0P#Ql*fBY>Aqe$-I9!P_;;&rulv>O(Z8dl z{ph#Rn)NsNep0K|Xj?xBX0KfAzU)UMxEF6Aj z|9)pp0{|(7(Icbm!&A5a9Z8HzD%p``)g7(IJ=f9T>|x5h4~M za!7y38}J77C;af&(lzf8i+BO+0O;ux0UljlNT0t;JEq^dOEK2ZRFF{|;*~Noiat?t z*N!4%6pUONL!T(&(bdI#?@TI2E?tj45meWXqOfqS`1ZK)@F#7FA@~cBKUwbIqhCkA zIgfTmhdx0HgwI8Hp>5Ia=mPpCdONx^svv%=iY}pkvorTlziG@SJL5DefrlqKETdQc z+hO42#!lrZ!eO1Dxq^H$#P^!aW_!$wflx45A+7C$6)|tx#~|V*cOIeB1v=j#3cTiu zC__x*4Aj8^3Ji34Pr(7vFqFUnq7YHX%Tl8+mEL^!-er!wBx^~kuX|3TGrI0XG*J1! z@E4bFzxTwpYLRORQ80c}^7YYe{o#$x4w( z!XVar4S7cIWet=x|Fo|-`U=Y~=+EoQJI1I%Ym-uG63iL}7QRz=Fc(ew2|T?Fk10uwPR6JOK++Bqb5S&5{ltvT5J{P= z?+cGVzE)PnwSvv?OF9#fcCn5zUNkk5#e+!?9JcE9C8bVcTuWY9Z)?tT*e+O26OM@d zB9AA8uJ}#8aXobn5bzUUpP+s@kVmJbi?MlOtL=hlbpRK4M0oi{5#VD&6lQGcP}k5h zNTrW&AQ)o%SQ5js;A@=54lS6U*XfLDI)8p%x68AnboOoYiWgt&x(HGEG06mI4SDgX z23~CGXU|>h!i2}7V3+06T{vQouoVpA%%C>VGr~1yprt=y1>(4*gwk~M_w|-?{9kJQ z<68c>Rt!R=pu;DynlmwTs17C-p^RFESp_lI%}koKiHM7BV$lV&o_=g%CLz?cGeYf} zNDCNZdp-D*swd|xer&L8WW$!!##xPN8P=qt>z`OD<1cIP-6hwU)Ny;#IJ?@OIvv>a zV$7aB|Jt4xr|sGE|J$Bybj{Ml3(#RP;3ipNBugvRLuRvpj6@Bs-7Yw6DR%Q|#9K*S zP=%FH7x;+d&rHYkJ}(8+F_P8sU0#*lYCbKJNdldvm8trvT+owVyi&E4=)MBDWCW)CVt(KBmSa}~flH_3JNuBCZq&}iCUXe!-q2rPQBt(LG;)=s$jD`#f z3!+ADydu(pggH@hrx&3>SS>|(nvoaGU%p|st*W}h>};wl*N4S{?rcKKOWTU=oKmCM zuQO};%$n}vscl5cr4Tp7=04_8dOudB;tSn8F}m8E4F4DV-F%*|kQmQc#N2$21;->8946C?==>D1hzaLKl2g7Dlhf@JVtPcSz9_16 zQ1Em!fGXtp_(Y#i}9)%A4staG`V(Hvt_M9m^bP~i=VM4g zzFZ3zI!P`Bd>SbZh={REyU|by@#3kM*k1If=b0;4kb>=kGfc2@1bNKd#jxlV(BKf@ zBZhTOVwd9+c&S8_BPLchiaZCkT}(HL|Mx*1TG$S4brlw9nl`)}*716ZsJ~GCzFfwRqWi*1 zj{5+M{0HRMKgOxYrCta%<~|Uy$nYPC@KItPDBf|>6g8po=rJ@qIw-Gy;NpkGzfQP< zX2um*PCklW3M*x@4|oxOfUdm)FZ)2`MT9>P(ab9l77Ds(;0m*%$Kfi25Ab;pfcN{O zf5c6^7Bsqx8HCqPc&(wYcMuI3CORk^j81^HGAYhr7Jo}GDw*pr!r#WO6uXXW@X{Wb zG59cKA!6}scscc(oT-r_NrwIbFEIKCIFV5RvF=7>oITsT^;HRj{g5srhk^LlB)Q z2Ng?|kPnUnXt~s@LT-T53kS}ReJtIL<@j+ChAYVU!y-%|8RQrdVT#nelIRu!t8&>{ zq?_Zj%kQB`^qnx;y9c?S!>_;e*k3MfCvrUxEAVq%AydXYDWOmrdL*pz`fXmf&Fi(f zu@Z1#cR1CcEce^rF_gb*LpEDpdDhqNjIZ*RDZWvvzv6C4z_lTEQGCH&-ZN zW%zCOcSJ)uneWc}T6E`Sd?n^BQ!2g@m7Mx35zdnUYlC!mbmoR#YzdWU38c44X5K3` z^YS!|&ow=8W~hBjS?ztzOYfhTJg}^z z7!CD4d2^khcv_`))i16pi1?Iq>zL@r6_VsO=b2D*S`)WYMPuY!l9rqwBXNSOd?SLRg zb_HI{Jpu1LMBV}GeG2HkjtzrXrOrL#)5pRl%p*DG56Kk7O-q#cLnuJRn=~{5UjiSW zh&ApRIsT9sABXUVB8;R?Ox)w=2;Y+vF9z_sso^jGQi&?=fA`#ObXvUY>8VWwNsU*M zpw2ZzCa#bB@X4?W64B=I*t}Q;K71=|GN?SR(E`8yAb6nQdX;vR8vyT}AXg&3JAu5{ zc+To6aE*$PdIv>m3a1?vVG?l|nMK@{@YZy@*-USd-SJqOS!3Q>?7#kzC8?Dq1^Uc& z)eA(kx4p8fwYWaVpmmn#l&vkaSB2`*9viB!^3)XAY!!}U0K)V!RPT)+|J3(a2~{*f@^#%>tZ;G zryOHA^yOhz26Y@b(HQcMQPMSn=&EXr(ByK2ve7A#-8;2JWHa!m>!$uR^{2JyZQ@G< zMBQ922l&Z+>1+bpF``ymM$wKiYb3NQ38%YoD&`7)_fSU*m7peRa=`@Q3IgOd!C z>t7aQ(&RNe+sPqkH-r(ZIW3Yd4wH~wxNOO&xxH~G7V1QN0}69zx$9@o04KB-%saKlHgh+ ztkwXBLmEL~*irUw+Ik2=lH32@ad=8r5+kH(ncXFIGqbx6i;2&ri{8F$na}m&FCur&FE$t!nkl!N8l z!1APjc~+eL`$jYx(RP{C)SQmHTxi3-Vd`2laDftfUj zS>>??WP(y}|ymCl{p)5BfvMdsY zL^Lg9yxD}3N$G!}djsg_(ch!|=vh=Dv#`TmC%XPh{Hl_vaUOq+$(8Qyj4%v%)qB`v zv6xH>@bA$K^N_`&VeoNw7Yy>ScOEiohHNR4j{&3MUrfM&H%Pkfsepn7>_+B!NFw>&LhSY&5$U~1&;%2!@nQtVlD+pyta!uBeyS5xu7;9IoMyTO0wIOk4Nn#Wo3@G z&3#2>19!Cf`*ZmUlhxfgP+2`t?_y;f`^QN1Wu;!=@_V*+cip+bt2cnobHNJ+>VgtuF?z(#0J@Z zK+zY`8!x^XZG4em)ZHCbb(79O)FD%|XFv*zbVr8tGke22qfu=(tBK9kx(8Hz+{`pI z>9uKaY9#ZjdEEn|G>Fjn`X1lyE=R2<`{@xXDI{LB+88)Bu13`nk~j9&(t#{w@93pqlbN}}^uqiPam zQ=>#v4V-fxoYTl`r}kE96l%4K^fDu16crs-8L-jF5YrnVAhANB<9W5pU{I@(O1dTu zeS%qj1HIuw904$H0Q3tGf6%s1?e}NeubxW%g0tn=l}#Q@0h_J7Jb>q|(DIo#Hm-27 zn&s~8%aZLYcgiei@3RY|6IWL3i>_dYFU_5MiD=>nO-ex%O?)RogNhCzwORrBTNGCD zYBh!+f_UO%fg?%hjioR3opALa`}C-ocqS4zCL1md;L`ighVkIN@3rDVBGJz1Yi9wJ zZ+!~g@GHPJy-WbR9;J5-=1gOo*_?J9`JmDy0U+yit|5*_(N*wvKu3`HsHoF%E{I_% zf~EoPKkY`q^L$-m^>u4zyS!a%YOY(;?7GQRFss;IcS*Kr%cRwRiAQI#_YAN3s#*eTi#N*Zhq&o`F)GH z|DGEO<@|4U*jn6PRZ|z8Yyb%dK*FUUA?Z>clsXI2NHVCPQX2pp!+NX3p|U!M6bd12 zNTq^kjzudKG)OBn3DuaidaJ?-)kGnv(uTzNa2ldi;Gk5FV=c*nYg>{n)b>knTdPh< z%d+}coorZAn`+Lj@}%cm{jH5ld)$?|HksvAQo2bI{dacQQ54QeFE7ea2)-O75BN`M zQ*uZQ=YUqVl#A5G5Fe4$6J+EoP#l;XCqR!V5JU8%i06o@@ifj+aY0f*I$ZnV>JM2f zzi4WdZKmkv1-?)LSFy#bt_^udtX4I(pH-zYz7F+-i^XLi5toPHBhE-_jYvPGK^geb z7Uc?8!?IA*(wdaj`mCzFNXu$jw7fr*>8$PvpkGH@Je3|(WDYt6zBIGH#noJxNo8`F zU6MqpsQRFW$;li=wTDbfl#}{8yNyXFyopik)lM~5t7ZCO+Z*f_CX2m?k+FBgzfH7g zJVg3o#>UT#jg3>Xnv;4QCadXmr3e#k`eD)bCQOyl$s(Pe_=ZtP+8e#P9eL(TjdUaP zG19}1v1MX)d57PA=g{Mim#q%wZLF(W-kiH*b5D!cSK435zTfRHNcA^lEfh1?+N>LN zcDui|Bsve-YXYvq{6ez-q6Ph%cpqS}OX|2kpc2S07s-=>2-O^6)X6EBv(~9VRxL;> zfHq2YBj|Gv_+d-DWyS_tgVN)KWgekZwAMN;)owbTQW`RxHU2dGZTqI40zJZ)pV_djp!25I zn+A;TQa55$g0;9SuIB((3p~}uY?FGF8S+C-sV>=ZR0~k)V($qXbvPZfU}G z=bIp~9mJKxQ9r}eH&g26oua+h*fq-Db6AA&c4wVHRU~utbe2fx&HNG>ZTGd#X!rF@ zcl*+HIUYc4v=R?opAxDmHy3TG{pGWH9XB;Kh%Gtya_zm>b;@O#wj6C|A1|o*bsF6Z zkJqj53*=n4%HP#JzlFgl8J)zBa5YRh)6DcR-$-$-MO7o&dDk6BHB2FlsR^q(vI{#p z3bS!WxsCjGRoGnthl3e;Dh2t?owQbW<8H&c)@`j1wc=K?@VLW(7=zy6G(-$C1Nn^_ zWpY|`tD)6U)kP!3&y+)kmN9-%z6xHau-l2j&Ww|iOuk*Q=;r)IdRQfolQbD8%Pgt= zS3geQC7Z-~s#bb+jp#Ns?iT6U{ax_Rh*(xmIb)fh4; zkdvO`NwFtq&t4L=`83}|&2#6LHs$K;>i1Y28ZNW3wxPDMrm7X&TLTGx>!vi&J?VRY8f*MO8-zV3=UZty9t;#k>dbW(z}d zGL_wO(C#!_34obtW^QAqn?YgOe&)=gasAZq;13*9r)6?sKo&iPNKcZ=NU8lpC$djB zh|-0=p(N2C>iMDeP*^)-l&>*clMI`!R+ZVFX4$-&(>pUgwvw6<897C2b1*_qszB}QW!7RjfFxa47xetvZyW;|5nCDTh!Qh9Sb*0=0_ zDz|+@%ftU=S8unsF5A%C<%36=ym@1<_vd?>Z=LJi`^?IfnUyWMtH>irnu|{38$ePs z;L%T}CAA1?8YKZ(OK@#uV zM!`Xhtw5SIz^2xG;#SZwg!zkdD;zMMZsntu-fEkOTEKjID)!kC2lu?QqS_MYhVF>- zDkE?Pc?%M}Sk;9NDeMWjqnVzrrXI;2@Jz*&sbQYzPr|{_1K{oDfJ1f22U}wyK`cC8 zZ9AY(LchHh9flNKWLcEvY0jP<1C`9LYygrXMLOt zArd$pv@wNam;>dw!nVrnt%qZY@{D*1L7CZ-ri zPA-4mGEV=}DCbOjyL66yggeXBH5zeBv%%fc(ONV&vLu-Dnajy4Irwa%4*Yw=tSUI zsAqgx7ke$#QKhZE5lc@E+ajxnk1hB0G;-v*S(TR)y$bw?%Rq3Fga%Vhd2Gdayzi4V zFK%7|;8YuMI*@1$5{p5-yLQnPUoz~yNu7=(>Tbb}Ytx)g28J>QG(8q5@G!PffOlFo ziP@;E^b1jQdOkUzw;s@?@nO|Q0r>OIUF8#E9X6~jCHR;5No2%PuqM;dRFD%gifB^c z?=iC2*4a$MH$s8#V><(1z|SC>K!&O(9on$IE*@gg>10DlAJ)Y)&~H!Hm_gbKVu+Ht zZhr&iyhX2h z$KIwMCJ@ZPykiqXr%ums3vRsX$rBnn(1{kphrjn?Ur#D++b5OYfpf~@bAuciWh2Xc z{W&QJJHw3+oHBQ0n7^HXmBc0Jg*aFl*t(g=47TLC(RGtG7z_u0M@7>;XGf-kH8z8a zA!y>>kxan&$lRo*TdxTmWBewjaXp0Z`&0~Y^?lb+a`MhP!Mj3G50_j+gZUXCKK{me_8`OhIGIcAM zk5O<{n@r5=)rq~bQ3?mJplaOp%Z%I2-4)xi4u{%DW_cS6404C!Vute~1txhOuIyMx z19#J?WFjRnJJd>zbnYWxm}eb4)PHDy51(69>OfReK7bbopeZ!Zw%p9DBM``}dQ>-? zXyke{l&b{Mtx^z!p=O~93g1tK9bm;6V_uLglF7Ci?mT${J1EV|W01dv7#|Vt7fmGc zbj_)@Ue>xgg9O!9e#WpptFkl!l-J*GPJB_W1aUp?mzFcG!%h1Hj zf$!qZ<+u|&b?i;!GpJ;mfu?P8$CHMMXxqPtt$1@erEggAvd zTi@Q&+yF33TBl-Ddw-REen0dv za=4CkmX*C}P7dBF0g2Bb=)r+LUKB%nb445O*ztIQZCV;?0|P)iTBO$y8PbDH+Q@`D zHPlHd+E5*EglhEVtt0-*c3dr#PeSPem6U-rak#lKyj&5n#+>Ek#IoRUh`+@u;$mZQ z4G!jJ4i0AK4ydmU0&U>X@8^LG+6ZKj(isX;7cyCwVXQMOkznF#qi<`bW9-5)aD|Cd1d%001+}Mzi zkVfDnacqMTlNTYn2D~tyq7BucldobUsy8n1UDQ{=WEj%hXi%>O$Zs_7 zc}(b!x$D=D0KZ6uJ#4}}?T6w+KpbOYZ|vA_2JO?ds8p1R%^-*o)z#IAJu669cd>5w@MNp^ zRVGVP(^C#ScneNchN}xdcPPnbptDn?3p3BvgB8m)^fb2ea?mH5g^EHfXy)g2_H z?w_M;PjGc+hh>KtJ2TUAiIJJSC>Q2%AGhEnh6^()l}K^V?B}FTW_+~WBgwH28tRvl zR&t9%8;aU&6tDzcrG9>ebb%pIb=1{qs3}0u7pA}y`1jgE-`9HJs9|^{k()e>kc)G4 zq_eFZjKm~Hx(AXY3U;mn(26A zFF~o4@aJ#fkW??=;6B*;#o7&z=cI8weASGd9ofDdV>&-6&^*r0A;HwgnQX$h=LXv8 z5}NEht+eR_G&IauG&)01gJNPOVCxHhpqJ!oH*_c*qu5zY2C|2-xMTrz90KU8L-ba>`Zf@Th#-ZphLqdY;GP5?wkWP* znpGB_D<&}%>y!G!@d_B7$$-1V!QE8IbhA2fHuyliKcHp^nW4`YaMhd1qWiws_%GkFz$;Td|1BnNK`GuEYz?ZVrh&b!mCo7XamLJ1fMxeEQGhuN55VDQW_&C7 zlcF^)0giznXs!}q0)XN0Cio5f23G{21@{j34sVHHfWJ=&Bg`VqB2oZ&5Xbz>Q2S2+ zNfSUJ>6BWeTBN#<`V{qND3G1VtH`e?@sw>;b80^I6)l!Fmv(`6L8DmX);|fF=Kne@ z`UW0om3#xgD?!`54^;MnyGqdUR>7gKL0A2M4E_Ly>56qX{%tr9;FYf8dqC&W)9E>M z=@0n-45k9;_-}xr1z^Q@;2GnY-ng%z7Ql=@h0}Vc^;7>8gaC$r0YCjEZ2bYB!9e5x z3`hVRG}JL11mLvcX(I!p3;=aTPmMi{8;$Q8-!(}BP-AlR2mF8^@B@Cp{~a)m?S;Ke zPbHW+V9;*XZcb6bDD&ei3k;I~by)tt1n*F=i0p;tAMgWyzz_HVKi~)afFJN(cwzCv z60saxnWQYaZ zscX06>LxqzeZBkFWRLGGuOoh`RJ<=2vt^w~OHK}k-$W1*9 zvTn5QqB@oI7zyjrB zC~*NU(+5jcv#aU}T60yTrNJ5Y-GfC5@ajC~5hzZ|2Q1!@+6qYTOeWoXOjm0VEz zeV%f#^J2hNPwh-_ECA<1(2^MAfD0vpV-Bd5k5T@Xj+d%;eR)g4I4@H5k_EW~ItYMh zKqnKj!|K?B@+^$EaEzW5<5doz5YXfSD8YaWIQqIxu8LD0z{@bH=3o*i1Qg0ZX(lMo z#@a@u`6bn!@@!B)1Cx4QFTL+csR)x$A?TSH(*&!R0;;`%m;_mv+{?hR7}J_k?n?di z)JDssn2b@~qBbCcoG=-n^$Ib`m0=~Q^wD+-G47Q2W~!)&ROd@js-eHtCBpO~ zRedUZrNoOde<{Z9M7izJW)B^-W)V ztUxT619BNr4DAk3ABq&q3ybAIqi8qL zI=Q0!!Y?HXtW8p;s*WlI5Crnc6=#S6&)mOb1DFNyvP9X!;(R$*SX5Z5%10r^D`+~F z<$e7JgBlb7bV%*fC-kegAQ%IzE^{$BGL#jWjYt*qClcAACZguG2CRl_s$y1yq6!=g z^Qj1#M0C`|v1hrX(Z*aq3fIy2Px)6!O6!4miCsX%Jzq?}q|n z(T5m;aSXx9)^3+TcEG|TiEF>wE6`B<52eM#Ie_YNQ^x?)qJU` z=KbmKha0Ciy|^b|wF34#(VDZV$?|qy+WwO%wx_2OpZwJA@Y03YkEQsyGirE0V|v@l zy>H44S9PwN@g^g0dn36vKqn|~Zbs>gq=m_Xe62su?c4Q4T{v`X_mvmaD(1HBip~W? zHaECgrc_$&JL)o1(c&DuzSbx-&QyQ2Ufb@%%k#sbjw=yWCp;2fEf{<5_M8dPc`iw_ z#zdd7JRe=|vTeaUFKbuvk&Fv12d@NQ-YwnndK1~KU(o$~6)#7twM<;0H|E+Zs2O^E z#cXp)0o}!;BmBZ{qR{8fS^f*Ht?UCgGs5te?;*yf9g??M7cMc}U9OGlkHZ5$T3Q8@ zfkcQ16Nr?lNzf-S=ImcE@cHiD=TH9ZAp3Y}jO>+<|82~Mm~4U}qF=#ayS({5F|3f% z9q@kS`!>f79jsDh-D)PZ9XiE1!Rh(`J$5k2P!uEyt2BM#PpNcI<$

?+*P1{>C_NR&k| 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 z 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

sAfnsvj|k1KnGggNr%_{ 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

Br+q``b5!(D9D*v__?~q&{ z|Ic?E5W2xdn9WK{U6N>j+!%>~}KL6jW)2*o${&Cs$@?(0td`12y?WC|1jcqvI4aCM|+O>~3Q& zhEXI8Xii-tb4`-cd#G!VI>_y&!Lk?C-VnsDm)rhI9V6;85_93QnZx8`kerS;OA< z>pmZpV*i^EY&pqmmRWKe#ixR$0xPDEG25H(0;N%($eMO2x(dUi6~>@!S|hV<39`uj zyd_k`%;rCaM+38$Zkr+8^RV6Fa3*f|Z9iAc#s$ra>ctd(P-H(g0Y_Pr2BPaJ(%1#4 z+7j78hNR1gT#g$JJ>A1kzDo*+yn?jD|E@2(eDydQU)ipOA65n#9}0t+DH{#z^1AZqqB9s7pk%<4d2}8t=B@&AIZupaJG)c@vKJ}ztgdBK zt^&FyoG}!&tohO^-tn!Pd_Tp8c+ytwRYHrK!b#D)<#!pSpaf`K}NNP*JSdq>d%UkAUeY-xMzdvAQE4U481Y7 z+Gv&Tq@*SoaI8&Fj?weNbTKNmxJ}O22NdmgwuTP=tSr&jt$+8JlDG-(Y{f{ev>Y1 zv_d5iySwvLY)RQk_U=3-DQwyXVV8~Qy{qanD0^Z_WQ&yg6z+Q1tUPF5pfb4ms6V}S z2qq{ibkcTrQJdMLGzMCTt%PuGA37&r?Ig0I zit)Y=$*fUl=c(0qvYLQgWsF#4H{yyXP@nBG*i~$5bs?(p`Gnv-4$SnzJJ6(#wrbFt zblgT}6FyTCE{Z6cdBV3y6!>99-W8^;CNnqfE1v6;cjiuM18;x_C0GwWX~c30R1d-~&$XqoDo z0m`YSgPnQYiaeiKW~YZ)T;#V|zVBc52^P$C$TkOSW{bvEe_S$epK|AMd!CJ26Amf1 z(b#H7JduSOu0poAZ=>UHlFTs%YN=RWEIC)n*P)z?xo3Tst7Di*aXB4l9BoIZN%%OM z^e1Vwh-=>nbk97|I#J@x**89BKudf2^~s@<)4B_}VPceUs;_OvtAy!Xc%*GG#)P)B zVNJLWv#)4=P7ZE^R%?bg0?8vqO7#?21=VlLv?F7;>tE_CqCHJuw4q*DVP5Fv?56fz zb3=_8Onx__dhxyVw;b{^sn0ZvQHd1Ays9I&_M3;Ty)VOd&iaTCCN!!aA-@tI#6qmN zslAZRt4XhohUiRaJTtxt79KYYPRU|r8C+wzvqlRlwWZ2EczpF(C)=!A+(S$2f&W;C zH{gxQ!u{#9rUw1_d|w#8zI%LYov#@`>p)Xr|@<{i0p(5K8lGy zlJ#J~p9m}hr~W>Qyd3E>m-YRhW*6Q=ASs~pjnrSy`;E4YZ&Q<39)ww&zMgdT~Gr|+cqUJYAU7~ZoAzzo&r^ax1e9sG>SwGCG zR6xg9(l*vCSt8{55qAthg@=tvce=giNd-=7if3Q_d&9W+Q(`Yj$32>CfcU>f!wEaVEL)k!vEKTe36X zUOC(gTTWE7$FBZ!yH)J!woCPc{_O!`&l0+TKd(u4>K~;&N-^#Yy8LEM)>avB6^ZT- zvA}UStO4h^pRg{CMcU!4+X1?oSmv_2Q!^1i=DyMVQQ(hh*tI|m@$rF)7F81Kr5%-ae@cJQ_&4<;`v(hhSHa9^PxsEOf;=zeK{ z`$D*v*|r$KpC~fT*Rvb#0z-TZEn#^bEDU0K#&rLP3#|D=Dv{Q+w6dXV5V*aV3c~@V z*{<)iSDx#wWKNVvu$%dMSD2Gz0n~OSu^G4K#I2JTkPE~l-pzf1_|o@DtN=i_;AU!x zzYX_o^UOLvBf(8r0cc{vnYaRuZ^X~SQ?$dI9;7H2e^4bYosHH=*Qn4?9nSpl!QYjH z0!arsQfot^a;H?267z!C2_z~Ao;l|=1Xcak$hT>Gfa5HGchz@|yh8t}eI^1!CJ|c$ z3=3^Z;mqcv`yF;Q&Ipna&%J(N@p1pl?4I5NYgAvQYZ*QnQ@#|bLU)B?0n>Qy2s(8d zgL3Sg$b13#�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 {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/cool2/cmp/automata.py b/src/cool2/cmp/automata.py new file mode 100644 index 000000000..502d6d67b --- /dev/null +++ b/src/cool2/cmp/automata.py @@ -0,0 +1,210 @@ +try: + import pydot +except: + pass + +class State: + def __init__(self, state, final=False, formatter=None, 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=None): + 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): + try: + return f'{self.idx}\n' + self.formatter(self.state) + except AttributeError: + 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,rankdir_conf='LR'): + G = pydot.Dot(rankdir=rankdir_conf, 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) for item in state) + except TypeError: + return str(state) \ No newline at end of file diff --git a/src/cool2/cmp/cil.py b/src/cool2/cmp/cil.py new file mode 100644 index 000000000..de6782c16 --- /dev/null +++ b/src/cool2/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/cool2/cmp/evaluation.py b/src/cool2/cmp/evaluation.py new file mode 100644 index 000000000..55ff38e9d --- /dev/null +++ b/src/cool2/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/cool2/cmp/languages.py b/src/cool2/cmp/languages.py new file mode 100644 index 000000000..e2c0c33da --- /dev/null +++ b/src/cool2/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/cool2/cmp/nbpackage.py b/src/cool2/cmp/nbpackage.py new file mode 100644 index 000000000..e89c62ad3 --- /dev/null +++ b/src/cool2/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/cool2/cmp/pycompiler.py b/src/cool2/cmp/pycompiler.py new file mode 100644 index 000000000..48fdbbeb9 --- /dev/null +++ b/src/cool2/cmp/pycompiler.py @@ -0,0 +1,532 @@ +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 + + def __hash__(self): + return hash(self.Name) + + def __eq__(self,value): + return isinstance(value,Symbol) and value.Name == self.Name + +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 SentenceFromIter(Sentence): + def __init__(self,args): + self._symbols = tuple(x for x in args if not x.IsEpsilon) + self.hash = hash(self._symbols) + +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' + + if self.nonTerminals: + nonterminals = mul * (len(self.nonTerminals)-1) + '%s\n' + + ans += nonterminals % tuple(self.nonTerminals) + else: + ans += '\n' + + ans += 'Terminals:\n\t' + + if self.terminals: + terminals = mul * (len(self.terminals)-1) + '%s\n' + + ans += terminals % tuple(self.terminals) + else: + ans += '\n' + + 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)) + + def __getitem__(self,pos): + return self.production.Right[pos] + + @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/cool2/cmp/semantic.py b/src/cool2/cmp/semantic.py new file mode 100644 index 000000000..780b4ca83 --- /dev/null +++ b/src/cool2/cmp/semantic.py @@ -0,0 +1,220 @@ +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_methods(self, clean=True): + plain = OrderedDict() if self.parent is None else self.parent.all_methods(False) + for method in self.methods: + plain[method.name] = (method, self) + return plain.values() if clean else plain + + def conforms_to(self, other): + return other.bypass() or self == other or self.parent is not None and self.parent.conforms_to(other) + + def bypass(self): + return False + + def __str__(self): + output = f'type {self.name}' + parent = '' if self.parent is None else f' : {self.parent.name}' + output += parent + output += ' {' + output += '\n\t' if self.attributes or self.methods else '' + output += '\n\t'.join(str(x) for x in self.attributes) + output += '\n\t' if self.attributes else '' + output += '\n\t'.join(str(x) for x in self.methods) + output += '\n' if self.methods else '' + output += '}\n' + return output + + def __repr__(self): + return str(self) + +class ErrorType(Type): + def __init__(self): + Type.__init__(self, '') + + def conforms_to(self, other): + return True + + def bypass(self): + return True + + def __eq__(self, other): + return isinstance(other, Type) + +class VoidType(Type): + def __init__(self): + Type.__init__(self, '') + + def conforms_to(self, other): + raise Exception('Invalid type: void type.') + + def bypass(self): + return True + + def __eq__(self, other): + return isinstance(other, VoidType) + +class 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 + + def __str__(self): + return str(self.name) + " -> " + str(self.type) + + def __repr__(self): + return str(self) + +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 = type(self)(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 not 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/cool2/cmp/tools/__init__.py b/src/cool2/cmp/tools/__init__.py new file mode 100644 index 000000000..e69de29bb diff --git a/src/cool2/cmp/tools/automata.py b/src/cool2/cmp/tools/automata.py new file mode 100644 index 000000000..59643cc5b --- /dev/null +++ b/src/cool2/cmp/tools/automata.py @@ -0,0 +1,3 @@ +import zlib, base64 +exec(zlib.decompress(base64.b64decode('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'))) +# Created by pyminifier (https://github.com/liftoff/pyminifier) diff --git a/src/cool2/cmp/tools/evaluation.py b/src/cool2/cmp/tools/evaluation.py new file mode 100644 index 000000000..d49df0ce5 --- /dev/null +++ b/src/cool2/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/cool2/cmp/tools/parsing.py b/src/cool2/cmp/tools/parsing.py new file mode 100644 index 000000000..d0275cbcb --- /dev/null +++ b/src/cool2/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/cool2/cmp/tools/regex.py b/src/cool2/cmp/tools/regex.py new file mode 100644 index 000000000..85d74325a --- /dev/null +++ b/src/cool2/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/cool2/cmp/utils.py b/src/cool2/cmp/utils.py new file mode 100644 index 000000000..b4080149f --- /dev/null +++ b/src/cool2/cmp/utils.py @@ -0,0 +1,219 @@ +from .pycompiler import Production, Sentence, Symbol, EOF, Epsilon + +class ContainerSet: + def __init__(self, *values, contains_epsilon=False): + self.set = set(values) + self.contains_epsilon = contains_epsilon + + def add(self, value): + n = len(self.set) + self.set.add(value) + return n != len(self.set) + + def extend(self, values): + change = False + for value in values: + change |= self.add(value) + return change + + def set_epsilon(self, value=True): + last = self.contains_epsilon + self.contains_epsilon = value + return last != self.contains_epsilon + + def update(self, other): + n = len(self.set) + self.set.update(other.set) + return n != len(self.set) + + def epsilon_update(self, other): + return self.set_epsilon(self.contains_epsilon | other.contains_epsilon) + + def hard_update(self, other): + return self.update(other) | self.epsilon_update(other) + + def find_match(self, match): + for item in self.set: + if item == match: + return item + return None + + def __len__(self): + return len(self.set) + int(self.contains_epsilon) + + def __str__(self): + return '%s-%s' % (str(self.set), self.contains_epsilon) + + def __repr__(self): + return str(self) + + def __iter__(self): + return iter(self.set) + + def __nonzero__(self): + return len(self) > 0 + + def __eq__(self, other): + if isinstance(other, set): + return self.set == other + return isinstance(other, ContainerSet) and self.set == other.set and self.contains_epsilon == other.contains_epsilon + + +def inspect(item, grammar_name='G', mapper=None): + try: + return mapper[item] + except (TypeError, KeyError ): + if isinstance(item, dict): + items = ',\n '.join(f'{inspect(key, grammar_name, mapper)}: {inspect(value, grammar_name, mapper)}' for key, value in item.items() ) + return f'{{\n {items} \n}}' + elif isinstance(item, ContainerSet): + args = f'{ ", ".join(inspect(x, grammar_name, mapper) for x in item.set) } ,' if item.set else '' + return f'ContainerSet({args} contains_epsilon={item.contains_epsilon})' + elif isinstance(item, EOF): + return f'{grammar_name}.EOF' + elif isinstance(item, Epsilon): + return f'{grammar_name}.Epsilon' + elif isinstance(item, Symbol): + return f"G['{item.Name}']" + elif isinstance(item, Sentence): + items = ', '.join(inspect(s, grammar_name, mapper) for s in item._symbols) + return f'Sentence({items})' + elif isinstance(item, Production): + left = inspect(item.Left, grammar_name, mapper) + right = inspect(item.Right, grammar_name, mapper) + return f'Production({left}, {right})' + elif isinstance(item, tuple) or isinstance(item, list): + ctor = ('(', ')') if isinstance(item, tuple) else ('[',']') + return f'{ctor[0]} {("%s, " * len(item)) % tuple(inspect(x, grammar_name, mapper) for x in item)}{ctor[1]}' + else: + raise ValueError(f'Invalid: {item}') + +def pprint(item, header=""): + if header: + print(header) + + if isinstance(item, dict): + for key, value in item.items(): + print(f'{key} ---> {value}') + elif isinstance(item, list): + print('[') + for x in item: + print(f' {repr(x)}') + print(']') + else: + print(item) + +class Token: + """ + Basic token class. + + Parameters + ---------- + lex : str + Token's lexeme. + token_type : Enum + Token's type. + """ + + def __init__(self, lex, token_type): + 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/cool2/cmp/visitor.py b/src/cool2/cmp/visitor.py new file mode 100644 index 000000000..964842836 --- /dev/null +++ b/src/cool2/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/cool2/cool/__init__.py b/src/cool2/cool/__init__.py new file mode 100644 index 000000000..e69de29bb diff --git a/src/cool2/cool/ast/ast.py b/src/cool2/cool/ast/ast.py new file mode 100644 index 000000000..d10dc349c --- /dev/null +++ b/src/cool2/cool/ast/ast.py @@ -0,0 +1,267 @@ + +class Node: + def __init__(self,row=None,column=None): + self.row = row + self.column = column + +class ProgramNode(Node): + def __init__(self, declarations,row=None,column=None): + super().__init__(row,column) + self.declarations = declarations + + def __iter__(self): + for x in self.declarations: + yield from x + +class DeclarationNode(Node): + pass + +class ExpressionNode(Node): + pass + +class ClassDeclarationNode(DeclarationNode): + def __init__(self, idx, features, parent=None,row=None,column=None): + super().__init__(row,column) + self.id = idx + self.parent = parent if parent else 'Object' + self.features = features + + def __iter__(self): + yield self + for x in self.features: + yield from x + +class FuncDeclarationNode(DeclarationNode): + def __init__(self, idx, params, return_type, body,row=None,column=None): + super().__init__(row,column) + self.id = idx + self.params = params + self.type = return_type + self.body = body + + def __iter__(self): + yield self + yield from self.params + yield from self.body + +class AttrDeclarationNode(DeclarationNode): + def __init__(self, idx, typex, expr=None,row=None,column=None): + super().__init__(row,column) + self.id = idx + self.type = typex + self.expr = expr + + def __iter__(self): + yield self + if self.expr: + yield from self.expr + +class ParamNode(DeclarationNode): + def __init__(self, idx, typex, row=None,column=None): + super().__init__(row,column) + self.id = idx + self.type = typex + + def __iter__(self): + yield self + +class SpecialNode(ExpressionNode): + def __init__(self, func,row=None,column=None): + super().__init__(row,column) + self.func = func + + def __iter__(self): + yield self + +class VarDeclarationNode(ExpressionNode): + def __init__(self, idx, typex, expr,row=None,column=None): + super().__init__(row,column) + self.id = idx + self.type = typex + self.expr = expr + + def __iter__(self): + yield self + if self.expr: + yield from self.expr + +class AssignNode(ExpressionNode): + def __init__(self, idx, expr,row=None,column=None): + super().__init__(row,column) + self.id = idx + self.expr = expr + + def __iter__(self): + yield self + if self.expr: + yield from self.expr + +class CallNode(ExpressionNode): + def __init__(self, obj, idx, args,at_type,row=None,column=None): + super().__init__(row,column) + self.obj = obj + self.id = idx + self.args = args + self.at = at_type + + def __iter__(self): + yield from self.obj + for x in self.args: + yield from x + yield self + +class BlockNode(ExpressionNode): + def __init__(self, expr_list,row=None,column=None): + super().__init__(row,column) + self.expr_list = expr_list + + def __iter__(self): + yield self + for x in self.expr_list: + yield from x + +class ConditionalNode(ExpressionNode): + def __init__(self, condition,then_expr,else_expr,row=None,column=None): + super().__init__(row,column) + self.condition = condition + self.then_expr = then_expr + self.else_expr = else_expr + + def get_return_type(self,current_type): + else_type = self.else_expr.type + then_type = self.then_expr.type + return else_type.join(then_type,current_type) + + def __iter__(self): + yield self + for x in [self.condition,self.then_expr,self.else_expr]: + yield from x + +class CheckNode(ExpressionNode): + def __init__(self, idx, typex, expr,row=None,column=None): + super().__init__(row,column) + self.id = idx + self.type = typex + self.expr = expr + + def __iter__(self): + yield self + yield from self.expr + +class LetNode(ExpressionNode): + def __init__(self, dec_var_list, expr,row=None,column=None): + super().__init__(row,column) + self.params = dec_var_list + self.expr = expr + + def __iter__(self): + yield self + for x in self.params: + yield from x + + +class CaseNode(ExpressionNode): + def __init__(self, expr, check_list,row=None,column=None): + super().__init__(row,column) + self.expr = expr + self.params = check_list + + def __iter__(self): + yield self + yield from expr + for x in self.params: + yield from x + +class WhileNode(ExpressionNode): + def __init__(self, condition, expr,row=None,column=None): + super().__init__(row,column) + self.condition = condition + self.expr = expr + + def __iter__(self): + yield self + yield from expr + for x in self.params: + yield from x + +class AtomicNode(ExpressionNode): + def __init__(self, lex,row=None,column=None): + super().__init__(row,column) + self.lex = lex + + def __iter__(self): + yield self + +class UnaryNode(ExpressionNode): + def __init__(self, member,row=None,column=None): + super().__init__(row,column) + self.member = member + + def __iter__(self): + yield self + yield self.member + +class BinaryNode(ExpressionNode): + def __init__(self, left, right,row=None,column=None): + super().__init__(row,column) + self.left = left + self.right = right + + def __iter__(self): + yield self + yield self.left + yield self.right + +class StringNode(AtomicNode): + pass + +class BoolNode(AtomicNode): + pass + +class ConstantNumNode(AtomicNode): + pass + +class VoidNode(AtomicNode): + pass + +class VariableNode(AtomicNode): + pass + +class InstantiateNode(AtomicNode): + pass + +class NotNode(UnaryNode): + pass + +class RoofNode(UnaryNode): + pass + +class IsVoidNode(UnaryNode): + pass + +class PlusNode(BinaryNode): + pass + +class MinusNode(BinaryNode): + pass + +class StarNode(BinaryNode): + pass + +class DivNode(BinaryNode): + pass + +class EqualNode(BinaryNode): + pass + +class GreaterNode(BinaryNode): + pass + +class GreaterEqualNode(BinaryNode): + pass + +class LesserNode(BinaryNode): + pass + +class LesserEqualNode(BinaryNode): + pass \ No newline at end of file diff --git a/src/cool2/cool/error/errors.py b/src/cool2/cool/error/errors.py new file mode 100644 index 000000000..e5920dc80 --- /dev/null +++ b/src/cool2/cool/error/errors.py @@ -0,0 +1,47 @@ +WRONG_SIGNATURE = 'Method "{0}" already defined in "{1}" with a different signature.' +SELF_IS_READONLY = 'Variable "self" is read-only.' +ATTRIBUTE_NOT_DEFINED = 'Attribute "{0}" is not defined in class "{1}".' +ATTRIBUTE_ALREADY_DEFINED = 'Attribute "{0}" is already defined in class "{1}".' +LOCAL_ALREADY_DEFINED = 'Variable "{0}" is already defined in method "{1}".' +INCOMPATIBLE_TYPES = 'Cannot convert "{0}" into "{1}".' +VARIABLE_NOT_DEFINED = 'Variable "{0}" is not defined in "{1}".' +INVALID_UNARY_OPERATION = 'Operation {0} is not defined with "{1}".' +INVALID_BINARY_OPERATION = 'Operation {0} is not defined between "{1}" and "{2}".' +NOT_BOOLEAN_CONDITION = 'A conditional must be a Bool type instead of {0} type.' +CIRCULAR_DEPENDENCY = 'Circular Dependency with types "{0}" and "{1}".' +VOID_TYPE_CONFORMS = "Void is not a valid type." +NO_OPERATION_DEFINDED = "No operations defined with operator {0} and types {1}." +MULTIPLE_OPERATION_DEFINED = "Multiple operations defined with operator {0} and types {1}." +CASE_NO_BRANCH_SELECTED = "No branch can be selected. Type {0} is not conformed by any of the branches." +NO_BOOL_CONDITION = "The condition value type is not Bool." +DISPATCH_VOID = "Can't dispatch a Void value." +METHOD_NOT_DEFINED = "Method '{0}' is not defined{1} in '{2}' with {3} params." +METHOD_ALREADY_DEFINED = "Method '{0}' with {1} params already defined in {2}" +NO_ENTRY_POINT = "The program must define a 'main' method with no parameters in the 'Main' class" +NO_MAIN_TYPE = "The program must define a 'Main' type" +CASE_TYPES_REPEATED = "Type {0} already present in case expression." +ZERO_DIVISION = "Divison by 0 not defined." +SUBSTR_OUT_RANGE = "Index out of range in substr, word:'{0}' substr begin:{1} substr length:{2}." +ATTRIBUTE_CANT_INFER = "No attribute '{0}' in inferred types." +METHOD_CANT_INFER = "No method '{0}' with {1} params in inferred types." +TYPE_NOT_DEFINED = "Type '{0}' is not defined." +TYPE_ALREADY_DEFINED = "Type with the same name ({0}) already in context." +TYPE_CANT_INFER = "Cant infer type in given context." + +class CoolError(Exception): + @property + def text(self): + return self.args[0] + + +class SemanticError(CoolError): + pass + +class InferError(SemanticError): + pass + +class RunError(CoolError): + pass + +class CoolTypeError(CoolError): + pass diff --git a/src/cool2/cool/grammar/comment_grammar.py b/src/cool2/cool/grammar/comment_grammar.py new file mode 100644 index 000000000..902185aee --- /dev/null +++ b/src/cool2/cool/grammar/comment_grammar.py @@ -0,0 +1,18 @@ +from cmp.pycompiler import Grammar, NonTerminal, Terminal +from cool.ast.ast import * + +C = Grammar() + +# non-terminals +text = C.NonTerminal('', startSymbol=True) +chunk = C.NonTerminal('') + +# Terminals +delimiter_open,delimiter_close, plain_text = C.Terminals('(* *) text') + +# productions +text %= chunk + delimiter_open + text + delimiter_close + text, lambda h,s: s[1] + s[5] +text %= chunk, lambda h,s: s[1] +chunk %= plain_text + chunk, lambda h,s: s[1][0] + s[2] +chunk %= C.Epsilon, lambda h,s: '' + diff --git a/src/cool2/cool/grammar/cool_grammar.py b/src/cool2/cool/grammar/cool_grammar.py new file mode 100644 index 000000000..5d574cc41 --- /dev/null +++ b/src/cool2/cool/grammar/cool_grammar.py @@ -0,0 +1,141 @@ +from cmp.pycompiler import Grammar, NonTerminal, Terminal +from cool.ast.ast import * + +G = Grammar() + +# non-terminals +program = G.NonTerminal('', startSymbol=True) +class_list, def_class = G.NonTerminals(' ') +feature_list, def_attr, def_func, feature = G.NonTerminals(' ') +param_list, param, expr_list = G.NonTerminals(' ') +expr, boolean, compare, arith, term, factor, negate, atom = \ + G.NonTerminals(' ') +func_call, arg_list, dispatch = G.NonTerminals(' ') +def_var, def_var_list = G.NonTerminals(' ') +case_check, case_check_list = G.NonTerminals(' ') + +# Terminals + +ifx,then,elsex,if_r,whilex,loop,loop_r = G.Terminals('if then else fi while loop pool') +ocur,ccur,colon,semi,comma, dot = G.Terminals('{ } : ; , .') +opar,cpar,plus,minus,div,star,notx,roof = G.Terminals('( ) + - / * not ~') +less,less_eq,greater,greater_eq,equal = G.Terminals('< <= > >= =') +let,inx,case,of,case_r,arrow,assign = G.Terminals('let in case of esac => <-') +true,false,num,string = G.Terminals('true false num string') +classx, inherits, new, isvoid = G.Terminals('class inherits new isvoid') +idx,typex,at = G.Terminals('id type @') # 'at' is @ + +# productions +program %= class_list, lambda h,s: ProgramNode(s[1],row=0,column=0) + +# ??? +class_list %= def_class, lambda h,s: [s[1]] +class_list %= def_class + class_list, lambda h,s: [s[1]] + s[2] + +# ??? +def_class %= classx + typex + ocur + feature_list + ccur, lambda h,s: ClassDeclarationNode(s[2][0],s[4],row=s[1][1] ,column=s[1][2]) +def_class %= classx + typex + inherits + typex + ocur + feature_list + ccur, lambda h,s: ClassDeclarationNode(s[2][0],s[6],s[4][0],row=s[1][1] ,column=s[1][2]) + +def_class %= classx + typex + ocur + ccur, lambda h,s: ClassDeclarationNode(s[2][0],[],row=s[1][1] ,column=s[1][2]) +def_class %= classx + typex + inherits + typex + ocur + ccur, lambda h,s: ClassDeclarationNode(s[2][0],[],s[4][0],row=s[1][1] ,column=s[1][2]) + +# ??? +feature_list %= feature + semi, lambda h,s: [s[1]] +feature_list %= feature + semi + feature_list, lambda h,s: [s[1]] + s[3] + +# ??? +feature %= def_attr, lambda h,s: s[1] +feature %= def_func, lambda h,s: s[1] + +# ??? +def_attr %= idx + colon + typex, lambda h,s: AttrDeclarationNode(s[1][0],s[3][0],row=s[1][1] ,column=s[1][2]) +def_attr %= idx + colon + typex + assign + expr, lambda h,s: AttrDeclarationNode(s[1][0],s[3][0],s[5],row=s[1][1] ,column=s[1][2]) + +# ??? +def_func %= idx + opar + param_list + cpar + colon + typex + ocur + expr + ccur, lambda h,s: FuncDeclarationNode(s[1][0],s[3],s[6][0],s[8],row=s[1][1] ,column=s[1][2]) + +param_list %= G.Epsilon, lambda h,s: [ ] +param_list %= param, lambda h,s: [ s[1] ] +param_list %= param + comma + param_list, lambda h,s: [ s[1] ] + s[3] + +# ??? +param %= idx + colon + typex, lambda h,s: ParamNode(s[1][0],s[3][0],row=s[1][1],column = s[1][2]) + +def_var %= idx + colon + typex + assign + expr, lambda h,s: VarDeclarationNode(s[1][0],s[3][0],s[5],row=s[1][1] ,column=s[1][2]) +def_var %= idx + colon + typex, lambda h,s: VarDeclarationNode(s[1][0],s[3][0],None,row=s[1][1] ,column=s[1][2]) + +def_var_list %= def_var, lambda h,s: [ s[1] ] +def_var_list %= def_var + comma + def_var_list, lambda h,s: [ s[1] ] + s[3] + +case_check %= idx + colon + typex + arrow + expr + semi, lambda h,s: CheckNode(s[1][0],s[3][0],s[5],row=s[1][1] ,column=s[1][2]) + +case_check_list %= case_check, lambda h,s: [ s[1] ] +case_check_list %= case_check + case_check_list, lambda h,s: [ s[1] ] + s[2] + + +# ??? +expr %= idx + assign + expr, lambda h,s: AssignNode(s[1][0],s[3],row=s[1][1] ,column=s[1][2]) +expr %= idx + colon + typex + assign + expr, lambda h,s: VarDeclarationNode(s[1][0],s[3][0],s[5],row=s[1][1] ,column=s[1][2]) +expr %= boolean, lambda h,s: s[1] +expr %= ocur + expr_list + ccur, lambda h,s: BlockNode(s[2],row=s[1][1] ,column=s[1][2]) +expr %= ifx + expr + then + expr + elsex + expr + if_r, lambda h,s: ConditionalNode(s[2],s[4],s[6],row=s[1][1] ,column=s[1][2]) +expr %= let + def_var_list + inx + expr, lambda h,s: LetNode(s[2],s[4],row=s[1][1] ,column=s[1][2]) +expr %= case + expr + of + case_check_list + case_r, lambda h,s: CaseNode(s[2],s[4],row=s[1][1] ,column=s[1][2]) +expr %= whilex + expr + loop + expr + loop_r, lambda h,s: WhileNode(s[2],s[4],row=s[1][1] ,column=s[1][2]) + +# ??? +expr_list %= expr + semi, lambda h,s: [ s[1] ] +expr_list %= expr + semi + expr_list, lambda h,s: [ s[1] ] + s[3] + +# ??? +boolean %= notx + boolean, lambda h,s: NotNode(s[2],s[1][1],s[1][2]) +boolean %= compare, lambda h,s: s[1] + +# ??? +compare %= arith + equal + arith , lambda h,s: EqualNode(s[1],s[3],s[2][1],s[2][2]) +compare %= arith + less + arith , lambda h,s: LesserNode(s[1],s[3],s[2][1],s[2][2]) +compare %= arith + less_eq + arith , lambda h,s: LesserEqualNode(s[1],s[3],s[2][1],s[2][2]) +compare %= arith + greater + arith , lambda h,s: GreaterNode(s[1],s[3],s[2][1],s[2][2]) +compare %= arith + greater_eq + arith, lambda h,s: GreaterEqualNode(s[1],s[3],s[2][1],s[2][2]) +compare %= arith, lambda h,s: s[1] + +# ??? +arith %= arith + plus + term, lambda h,s: PlusNode(s[1],s[3],row=s[2][1] ,column=s[2][2]) +arith %= arith + minus + term, lambda h,s: MinusNode(s[1],s[3],row=s[2][1] ,column=s[2][2]) +arith %= term, lambda h,s: s[1] + +# ??? +term %= term + star + factor, lambda h,s: StarNode(s[1],s[3],row=s[2][1] ,column=s[2][2]) +term %= term + div + factor, lambda h,s: DivNode(s[1],s[3],row=s[2][1] ,column=s[2][2]) +term %= factor, lambda h,s: s[1] + +# ??? +factor %= isvoid + factor, lambda h,s: IsVoidNode(s[2],row=s[1][1] ,column=s[1][2]) +factor %= negate, lambda h,s: s[1] + +# +negate %= roof + negate, lambda h,s: RoofNode(s[2],row=s[1][1] ,column=s[1][2]) +negate %= dispatch, lambda h,s: s[1] + +# +dispatch %= dispatch + at + typex + dot + func_call, lambda h,s: CallNode(s[1],s[5][1],s[5][2],s[3][0],row=s[5][3] ,column=s[5][4]) +dispatch %= dispatch + dot + func_call, lambda h,s: CallNode(s[1],s[3][1],s[3][2],None,row=s[3][3] ,column=s[3][4]) +dispatch %= atom, lambda h,s: s[1] + +# ??? +atom %= num, lambda h,s: ConstantNumNode(s[1][0],row=s[1][1] ,column=s[1][2]) +atom %= string, lambda h,s: StringNode(s[1][0],row=s[1][1] ,column=s[1][2]) +atom %= true, lambda h,s: BoolNode(s[1][0],row=s[1][1] ,column=s[1][2]) +atom %= false, lambda h,s: BoolNode(s[1][0],row=s[1][1] ,column=s[1][2]) +atom %= idx, lambda h,s: VariableNode(s[1][0],row=s[1][1] ,column=s[1][2]) +atom %= new + typex, lambda h,s: InstantiateNode(s[2][0],row=s[1][1] ,column=s[1][2]) +atom %= func_call, lambda h,s: CallNode(s[1][0],s[1][1],s[1][2],None,row=s[1][3] ,column=s[1][4]) +atom %= at + typex + dot + func_call, lambda h,s: CallNode(s[4][0],s[4][1],s[4][2],s[2][0],row=s[4][3] ,column=s[4][4]) +atom %= opar + expr + cpar, lambda h,s: s[2] + +# ??? +func_call %= idx + opar + arg_list + cpar, lambda h,s: (VariableNode('self',s[1][1],s[1][2]),s[1][0],s[3],s[1][1],s[1][2]) + +arg_list %= G.Epsilon, lambda h,s: [ ] +arg_list %= expr, lambda h,s: [ s[1] ] +arg_list %= expr + comma + arg_list, lambda h,s: [ s[1] ] + s[3] diff --git a/src/cool2/cool/lexer/comment_lexer.obj b/src/cool2/cool/lexer/comment_lexer.obj new file mode 100644 index 0000000000000000000000000000000000000000..f54cc410648fb4d72204cc16e0a4d6f55b76927a GIT binary patch literal 6215 zcmeI$duS9#90%~kyuGASRIF*OZ*!MuG`=5EqcOe`<0J8zi*fJVWNvdW?{-aMjIBs~ zjO7fAVWw|Qul@SAy8Wj=_h>G0BDT>~o>meJ zd%OWZbx(ZLG}$_4d{yIJvgT^l*{H4Q2R8TKT6^*OZR=okx9lv34WqBq$)1Z{$&Nmp z>!8D;ucF15-;H_pdz;i|cCI(eW20rB>5tq2xOv$uN9Gx-bywh65i4c|GPi|QDWq1I zS5!AHeXz1ADC?{n8_UMCG?u~BjmtgF<5rE~#WGn2`-){Vhv@HAR>Vp~Gb31mW^{n3 zhU7ZAF>F>W;F-o4lr%NqlT=CPDHW=uM%~90@yViGn#@yb&B3yn+tiTc4|`zLnV(jz zvkL2~EGkW5i#M|+c)UvDiDSgrWCkh`Rfsu=nTV+f;{urPk`O}?D-kxt5QKslhzKE6 zLJBhUkM>fe0W*AVwnQB3clm5z7(vh;@j0h|Y+7#9~B$ z#8Sj?L=mDdB8YGy#v(k3eu!C!Vni)s9AY|R6=F6b6EO<01hE=18Ig|A5akFNu?DdW z(TEt2@F5Bj3lLsJ17a{@En*R30%97X2H`{uLbwrG2tT3;k%6c~3`0yp0s8XF?n!pOggqcwr2#H5sOfz~pPYE+g%p*M2?FqTPvaY!#R%1SP zOoDJj$Pjf99*BAfA4C9R6+{?9gJ^(gf@p?lf%pbuJ;VlxR)|dyTOhVUY=_tZu@hn! z#BPW^5PKo^LF|V(0C5oF5X51KBM?U+jzJuUI011I;uOSbh%*ppA6Ya)*iA;=% zI58@V6B81ub!DjtF)KP^t0h_0lxnC|W7G;OeMRihmHr~&PAUTh%auXmRml-A;@)W; zs>RMjH!nb3gt!E88R81WRfua4*CB2|+=RFVaU0?eL>t6ihMK|Kt{bM zZg)Ge;(w;Q8qcuyX<{cE>4jZ6CT>!sEGBhhv#8@mhD&MHs#JavmA^NqkvNx(!Fl7& zQF}PLNUTcikfY8he~8A+6~mGwW;HnGCds5al801J@{t0hRirRUBQ=nkNX?`c(l?~_ zqz$B2(k9Xt(l*j|(hky2(k{|&(jL-Y(mv9D(gD&z(jn4e(h<^8(lOF;(h1T@(kaqu z(izfO(mB$3(go5*(k0Sm(iPHG(lyd`(hbs0(k;?$(j8J8=`QIW=|1TJ=^^P6=`raE z=_%SafmMJN0SYmTLY(R3|Ea;49zT((;iGlj z+`lR(#1gQPR)zSLBC7^&eFL4um9chxk)=y9_PSIVH-(x}x823Q-LYtGn4&)IRLrME zZ8_o%<(S^I`(f-8o=0P@gt1wN$$J90mMD1VD?I{Fu8yvL;d_W#uth^MHMOZ;QQ_;+LhZ>?I@ zAcxc@%@YVp;gz*@OsV&-X>_l4`F-;jmn^7Ux^ULqc~dH;O`kDyc4^tNCB=)Tmd~jv z7(Qa;sL^A_jvLlFD?KyA)^FX4oW6s{Pbe&!IH`}b%2_>hh%+zWIoav#KVabULC&?! tE!DYetL)C)JiF8G$aRQUA%~FDktcqB7V%`KIIFW_fn29U7yp>e{|g7uM9u&J literal 0 HcmV?d00001 diff --git a/src/cool2/cool/lexer/comment_lexer.py b/src/cool2/cool/lexer/comment_lexer.py new file mode 100644 index 000000000..7581de18d --- /dev/null +++ b/src/cool2/cool/lexer/comment_lexer.py @@ -0,0 +1,22 @@ +from cool.lexer.cool_lexer import Lexer, save_lexer, load_lexer, lower_case,upper_case,numbers +from cool.grammar.comment_grammar import delimiter_close,delimiter_open,plain_text,C + +text = f'[{lower_case}{upper_case}_{numbers}\n\r\b\f\t\v"~`\'!,:;<@=> \\[\\]\\*/-\\+.\\?\\(\\)\\\\#$%^&\\|'+'{}'+']*' +tex = text.replace('\\(\\)','').replace('\\*','') + +tex3 = f'[(\\*+(\\()*)(\\(+(\\))*)\\)+]' # ( \\(\\* | \\*\\) )^C +tex3 = tex + f'|{tex3}' +tex1 = '\\(\\*' +tex2 = '\\*\\)' + +comment_tokens_def = [ + (plain_text, tex3), + (delimiter_open, tex1), + (delimiter_close, tex2), + ] + +# comment_lexer = Lexer(comment_tokens_def,C.EOF) + +comment_lexer_path = 'cool2/cool/lexer/comment_lexer.obj' +# comment_lexer = save_lexer(comment_tokens_def,comment_lexer_path,C) +comment_lexer = load_lexer(comment_lexer_path) \ No newline at end of file diff --git a/src/cool2/cool/lexer/cool_lexer.obj b/src/cool2/cool/lexer/cool_lexer.obj new file mode 100644 index 0000000000000000000000000000000000000000..a14c5e1045ff9c09fdbd1feda349f39801f8c269 GIT binary patch literal 139064 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'),(greater_eq, '>='), + (equal, '='),(assign, '<-'),(arrow, '=>'),(at, '@'), + ] + +signs = [ + (opar, '\\('),(cpar, '\\)'),(ocur, '{'),(ccur ,'}'), + (colon, ':'),(semi, ';'),(comma,','), + ] + +lower_case = 'qwertyuiopasdfghjklzxcvbnm' +upper_case = 'QWERTYUIOPASDFGHJKLZXCVBNM' +numbers = '1234567890' +text = f'[{lower_case}{upper_case}_{numbers}~`\'!,:;<@=> \\[\\]\\*/-\\+.\\?\\(\\)\\\\#$%^&\\|'+'{}'+']*' + +ignore = [('space',f'[ \n\r\b\f\t\v]+|(--({text}|")*\n)')] + +other = [ + (idx, f'[{lower_case}][{lower_case}{upper_case}_{numbers}]*'), + (typex, f'[{upper_case}][{lower_case}{upper_case}_{numbers}]*'), + (num, f'[123456789][{numbers}]*(.[{numbers}]+)?|0'), + (string, f'"{text}"') + ] +cool_tokens_def = keywords+operators+signs+other+ignore +# cool_lexer = Lexer(cool_token_def,G.EOF) + +cool_lexer_path = 'cool2/cool/lexer/cool_lexer.obj' +# cool_lexer = save_lexer(cool_tokens_def,cool_lexer_path,G) +cool_lexer = load_lexer(cool_lexer_path) \ No newline at end of file diff --git a/src/cool2/cool/lexer/utils.py b/src/cool2/cool/lexer/utils.py new file mode 100644 index 000000000..d2e9c2c8d --- /dev/null +++ b/src/cool2/cool/lexer/utils.py @@ -0,0 +1,19 @@ +from lib.lexer.lexer import DetailLexer as Lexer +import pickle + +def save_lexer(table,path,G): + with open(path,'wb') as f: + attributes = [] + for prod in G.Productions: + attributes.append(prod.attributes) + prod.__delattr__('attributes') + lexer = Lexer(table,G.EOF) + pickle.dump(lexer,f,pickle.HIGHEST_PROTOCOL) + for prod,attr in zip(G.Productions,attributes): + prod.attributes = attr + return lexer + +def load_lexer(path): + with open(path,'rb') as f: + lexer = pickle.load(f) + return lexer diff --git a/src/cool2/cool/libs.py b/src/cool2/cool/libs.py new file mode 100644 index 000000000..935712eee --- /dev/null +++ b/src/cool2/cool/libs.py @@ -0,0 +1,29 @@ +import os +from cool.pipes.pipes import start_pipe, change_escaped_lines, remove_comments_pipe, parse_text_pipe, ast_pipe, type_collector_pipe, build_types_pipe, check_types_pipe +from cool.pipes.pipeline import Pipeline, Pipe + +def get_std(): + std_dir = os.path.join(os.path.dirname(__file__),"lib","std.cool") + with open(std_dir, "r") as f: + std = f.read() + return std + +def get_std_context(): + std_pipeline = Pipeline(start_pipe, + change_escaped_lines, + remove_comments_pipe, + parse_text_pipe, + ast_pipe, + type_collector_pipe, + build_types_pipe, + check_types_pipe) + + std_result = std_pipeline(get_std()) + return std_result['context'] + +def add_std_pipe(result:dict): + std_context = get_std_context() + result['context'] = std_context + return result + +add_std_pipe = Pipe(add_std_pipe) diff --git a/src/cool2/cool/parser/comment_parser.obj b/src/cool2/cool/parser/comment_parser.obj new file mode 100644 index 0000000000000000000000000000000000000000..43689ab6ac74622686cc81c67f360ebb7e32ccc7 GIT binary patch literal 2591 zcmaJ?ZEGA=6wbcQOtPUNWNk|j(GcRMhNX)LLfWO(yqNB?2}$}TsLSl;CTHmE&N6dH z5~!e*gjz0?&|3r%)Q=jXKS9A?A=;`x!0&=zJonDsnVZdS_RHLRUY~Q$bI*R3eO?$F zkv~>&oo1 zP++bqRcxH!@V(uzMIEmb@Q-*A3^sL_Qr~HYlq9+&dbURPC`Yq=-Ro=;MacoJa4=z%<-2`3Cg4hh6(*s;a+}UR$dJ|+=}^*XktCkg2oBp#&kZlJ|NXF`ggUGu%)nc5IqBk! z9+ZMEzA+z*5i3S?j@~tnoXM61Hw|-8CTzTuj3Js}0cHprrD`;hY!NE3Kv+JYwokWY zN4aQycl)qQ#5Bjw%^Kc@cUWe5eFar;uSXWFHZ`jtF-y#4$!Sr+IjBPou0a82v9Q~) z2zM}F2^PYz$;?lP-}C~)zhH(fi6%U%BT&P(8=K2Z_upUUP(J1;T0G`(U!50Fo!k0g zZDpHRQ>aZrCDx{dv$9NSUMWB?*pc>WWj}|$X=NXsqb%Z_ks#9RRbIziRz}*X=Hxlb zHZ9LlHgV2rC~{inoR-;;Hak@l?M=Z#5lVXIke zR{_5nY>c?45t_Ig_>J^t{1tL5WCrnl&liMXD^ zv6$~r)u)&qrdU(HliE^9Z@nqFX(?4+Y=sGsuLMpByeIDXG~kTDk~l|C0#@J*R$)yr z9_}HybtLC7I>S$JhLjc?2TpA~=$obxz53UWP7fZ9z5G|c(IIus#dSr!IKd|G$vyg* z<*9FX0w-F?0!s;#eW{B2s;IS1L`jLZRR;95+)FV?8Rw6cCmeo$48Qd9{c6MPWogsr z(#FngN-e{NUc^(*GSuGZbvC-^`N%O`O&p%Hsdng6$8|cSk;tE6D*~`_DKVqv_$?O? z8E5wZ2_81>5Po~g;rBZH0WaZC_zV87rceymhJiip^+-}H26{0}MuZt!vtVei)dM)H ztCAeIhk+8&dL8VGrj4!tm1sljSHInT$)Fa)X_+ aC?3G?5%Cx5`VA`ns9DanUB8S2Dg6&gPYFZ- literal 0 HcmV?d00001 diff --git a/src/cool2/cool/parser/comment_parser.py b/src/cool2/cool/parser/comment_parser.py new file mode 100644 index 000000000..9a72372f3 --- /dev/null +++ b/src/cool2/cool/parser/comment_parser.py @@ -0,0 +1,7 @@ +from cool.grammar.comment_grammar import C +from cool.parser.utils import load_parser, save_parser + +comment_parser_path = 'cool2/cool/parser/comment_parser.obj' +# comment_parser = save_parser(comment_parser_path,C) +# print('Errors',comment_parser.errors) +comment_parser = load_parser(comment_parser_path,C) \ No newline at end of file diff --git a/src/cool2/cool/parser/cool_parser.obj b/src/cool2/cool/parser/cool_parser.obj new file mode 100644 index 0000000000000000000000000000000000000000..7610d1f5a4751a05df9b811ae1fea728748e76a5 GIT binary patch literal 150856 zcmeHw3zVHzb#UHfUg422U}hr7b9e<3AqinZAS5A~A;cj-fH-C*$%JpdNhWb}0|9vq zG(sRF0U3-+TPWqH713H${#N|?)w(FlWvm5SSxT$b%ECvB3ReGG+yCtI+~@3bzI(nq z_ud(0Sc^ON?(DPAew}^x+2?%c8=Ugcgb5QTz<)!tw)JkByR&;>SI>a_vvJ$N@KD`l z7hiV8y!UYU@Posr-7$RYaLdqP*AH}W-`>ssea7bPJLm4aY4eWlJA1eFFu-A#%74Lhp|LUBjKjEy=W@nHLud1HGGe7kbRU8R}Cm>)BcuW@vSn@94j>XJC78 zfA==@&zE;^?-?GdY27)n1NgXbI5{k-8LDGHQ8&rq-McppO}(P``h?S(zOJXg(9^%U z#}Jynao0`TH|^NAYdD!H1WOu+>RUH&>)y5NtZlu!3W9QSEHFJoVjP(qpUh60<*(yV z%evMrJzLM>t^EyKa6?eTbRryMU@E7;W!k+dnWq_>Qn+bn4+T&BfPxtrX0&eY=`QRZ z=*f`c0?Z7OqDHm#%Wz4cSuGd7o0-+$uH9;9q?**bMe%Qd6BjGkd3O&FO-Z_UCByel zU;V(V&vvctKXLaarWa!li*ie{F1b2652sgAJQP8y3o;^DQoNlr>U6*W3Hr+j( zTzBvE*DhQ#@U3^Y{HfEwg|GT&jhNT5Mi>6qv;D7}@bxKKjV$eN+OcC>Pj|mxiv^1} zqp->SJ6LyY6fn&c?&2LE({XvSefv}AJ005;j7Dl&VF-5*^!Pj8Yjtdz9oE`C&|66S zjfSj7L&zu-&rsYNQqx)sY^eJw9keJN#FS2_l%}`#_gvpy2#E5SMa$@LTJNqKb{KDb zOsKJK<1Mq7&-}-JM~yZSBgFL9t=*doI|8otbfD{rw<#f>7QJq`|MuU0{^_e{I=$NM zO|NY5_3ql)UDzBj#8+^?Ey-7~{w`8o<3TfVu7a9Q>RPw%?%#aY=I(9VjFHed{HEQ> zI^0PMC0ENl=lYEJ^c_ZS+R$MucJAukwxeGMZ5AL8>ya?w@K!K@Rwg!Y`XC<)r!4+x z%PmJ;akHaM2N&~ursDTIV(4G+KgYiR)z$a@5-9XoQF-EN5>*WMb88=*`_4Px`Vk%+%cq&s01H0A4 zCOA3n?8YnSe|7dJ;%Q9X+P!U;P2}76tUl$@js;i zA3Lu|tK^krQ}U+psE@z4@?Vabdd4%*g!!-N{T)+2_T{ra^UA&*% zN@E`X{1<0D`-MxtAu$Ebmur4C^!{(GoME$(WQ6Lom9`!e6!si<=A)nf(4l3S6dn`I zHa+sf2mkTMJ;z%t{PBW68u;u_|Bwk~1%ZrtN9O_u1qGak=d*L>$1@?~I@6^TYk%{9@8c4qNT0#Pg)S3&uWPvJ^{20LhE?K_ z5DGr?m;bu<$(j!wB^4}yUz<624hmL5QtWdWIa!T;?#TM1=2cVl&^a9oL}v`WZ`nVcMpm5Afw9R!YyuF^{z+S)-3yHDGT>TMKGFsduaLAI$fZLekUk{f8bCEg4l4(%g7v@v=d7Q zVF0|=&|&a>fBV3O|M}_P-)Ac$bvYM=-{5dTZQ|jNJ-YsHpIA%nKB?E(M`-+|!n0>= ze(!?Q?M}j`pR&1IPkgp1cW9q5sywn4(9hbO^la|SFve?+O3_GS>tjv9*uzAJ^xvJf z${fhamgqpUi4PGW`PTa%{q^VfKG_M9v)oAx|BGf{_}@Do-t%Ew9jVJ8@0CNv5WRNK zLoXlrn{Q2{8lTd$t9!HM<0sy6!6oxIcAQ{$5!U$B8xvOQP+pEl&y;ODcI@>2!%nuU zvpSu9#vfhx=f}UrQyk`CLIZfQsJ-qdmo7>_d*>A|JM3jI#35DpTIbkFc0vf&dBrj> zTLgA*&+v`o1%)5rDL0islX@GMJFJyc^m22$!%b)iYIk_#)W;7VeR2EawlYZfDJT2} zhl?TjtH&OB@A@MzR{Bofh6nF@_Ah|A?(wk~?w|kA|2ifG zAhQYgSK(uC{mtsz`fq;aQ#LP&U0AtLyz7OjxBq+F zr4BKtWHjF@ZA|a&PkPw$o7JyG#62%rlw5#_zV&CrAN|Thr~OrNqKil8H}`t4h7=iT@49ne_2@QWY%QTI7df5pjjokChma=g&%q?vQ}@9({V z+gOu0yzP~V_a5`3Cpc3lrB^}EXQs>8pR^4=)&gK@+6sRzO=rQMwaJFG8UF1g_&5bV z&V`S;@bLn(dU^;xz62jH!N)J)3;bAIDC8_K3;{7DL~;t`1UaS zNcJRy$-Z<8{N4s11^BoHKK8PYI2k@phmZO2u@F95*+=qn^4;XM;Gb{B)-EB~0&YnC550zjF1jNG?fFfL{f!X9PW|jMxT|UMv^Ek83=a_XS0~TMxznFY8`6p(kuQEeD4^T*Y(&yj<(vS3K z@BuS#`Z|2T^q4*gAKYS}f^XbxpMh`OcAtfB+<>*f5Vv9jeB-7(0={v3E@l>e3~(Q1 zAIUY$upfh8kl7|bPu@uWj#>EI%*MZDR{jXH^R@7=+}Q2#ja&Rrn9XlvR{tWidkRo) z`~mpJJ>X{e#@*mf_{M!<6MW+i@hp7fUh!@C#$Dt4@QwS&58)ekl1acb_mp1)jJr!W zbC@Rh#l7Z4_{LqQ4!&{!nF-&x6P?T4Xcf?au$GS{hVevJB*uc@E!nM1&{ z6{MCo(Ke{#2;f=@;TR49vn$9<-bAaep8c#zEIwS~GpYl)(2i85=6jcRSP*4l1R zM?|Z$Wv{bEt0O(u**;K5n5=VqbO%TsQCFRaQ<)Brv}~t=*meqNY^NZW?G(`1 zPC+c&X&|uNrB;Rf%i=9T2 z#y6TgA8HPbBm-|WvP0dY(N6mt?Uly-h=D&$(pvmwI8fYR)Ya%*nvZZda{h^7EL0<2J5>^wj zOA~RJCSuJd;zCWtg_sCOpZUqGERzR?B1qAC>K(KBF1nZVVjBW)2)~$eG-HIk!x8kSPt-!~+6%edj z0l~Tz5Ug8(oNQ_Gw;Y1j8i{~l-ExT0tw6xK6$p^(KoLn&z^WEau&MYhFOG<^ggF);!<%=Rl-< z14R1QKy3mIv7$YkBmjaY+AD|^?KKdY!w^IkF+gM#14MQ)Kx7&X)Yj1uE81&*kc~8^ ziS`OjTT0_tiv^9Q>}Al9$qW!#%>a?%6!27&W}MEHe5w_op5qYMd{;w2K#&m=4$&4A zKe72F1dkd)1er_Jp z;vj`f2-3Ff1;!@Npjwz>**P}#6EdjN@E}Tl%^_$6NeI?F$1$20O>p=U2s(TT1Z)Ta z!G;hJtWp8Nh7b^JECIo$;Sgg8fq)GmAlMM1i4I@lr?!j0$A%CH+EM}m+eJXIT?7Q% z1t6!JME4B#^9+;dp2>cmX`AUR%S_KRgtM$6BOusJ9AeBQ5U`m9f;N*tz-AH^VR0+N!{Tx(6C(D-R5A}=zW;Y6R+X#=a7O3T^&=PN1_ z!s794Gak=jKhH6RmwD{xJTqav6Mm9~@H~4lK9AG{=8^UIJTu1Ua+>Y|n|Y)(F^??a z=aD7+JhLcAjc38?E?}4$0E@L zk6#Ya{vey+@hh6(cqJfcfXG(J=Q-rv7XLdL|L?Z=7ZA)phZz0^0_I;pF#n>7jz0nc z^DiKne*wY#3kc>PAm>{A4{``jmIMUz&mo3?fq?lJ5X`@5qWKpHn12Dm{0j)?UqCSb z069;^E1r|A+ja zyTL@_8z9nv0m`X|i@XX1;WGvQxq!n)g5ZPS=wdpm)8r>^IxH#X+pfnKqfN750zyNUurJE!7Fqng8t*U`0gVJr0 z+h{a?BwHmj3R63eMl(T4;aC}z1|l!gZLNGsV} z&tlto7TeCV*mj=9#Ca~j&XYXMJm&)JITC{0MnJUBNCX@%1Q8rA1O&N?Y=!(pLXeY4 z2=WjKLGB?S+BYNuatsN<@q{y@T|!dO@kFqSi&{z!{jVZq#Vq2*ohtQQBOxt>1&#(~(o6sGYm1+lzK1JOV>1hIE%Ozd46 zh#g4-u_I|98n&h*>^2J1xQ&Krw^0zwZ8Q+OjRs=3(Ln4r61bGuXsK=GrM3?&C01T) zyTMZ8MN5g}EwvqQsqLssi6>vg&B_xP$a^p1&qOJ=fwjOzmfLI*ZgY_plr#`4UjdEs z6~uBI4MZW7Kcfg;n`u|0VT))Wb~z2iE~kOm4 z90fE3M?owNXdsqC1F;kuh=+p);(?=qSY8dp14jd~?G(`1PDAv-Q4nk3XdpI|2I7IE zfq39Z;4LCBWob`C=J9OrGeN_6wrXvKl-A&8)V8t9s7 z6*1E)+f1u$Gp!vvuA^wgB3QR@=xC$2MDyZT8+o z8`+6yBc^X7nr|aJ8g0Y@+KdChjz$}?Z=3A^ZMH|X**g*KmMOO~g6)|{3dL&b0BJWk0*O*>j5Q1D|oi>-iYc1fH1m?B8E!Wj`OfAiKlfEZ|pRjz!2ypzSl`SypxQhPBL&hZU5{f4%}%ElTLf6 zcG?5ElZ^EDS@eovhvOl?cZUN}NYp^%Y`QIWHVwqirhvu`6vT2i4aCl-fhbOD zW|X1%aFCcAHHXP-Op1-hf4bPuE?awD#714jH@b*ryG$4GI7yf7U0t>>brH|$B5u${ z+@OniU>7lEm+gUF#8tX%U+S`bqs#W3ZX?uBm{9C0Q#EC5WS4nzks~;xvIPVyTR^a~1q4;iB{NvH0tc&~LyWQo0#>$wU}cLYTD#(>&aDJKRnK@`NYrUL31Wx7D_YJgZe)mEj?d2&zJC*1o z^L!tf!~5*ippRr0ePj;rv*++_mb?!OdABS7n957Cj#~pZu@Pe_1nz% z6M6fIR{L$O_7ffU6Akp+=I*z}?%WE(5mp2Z*-}*xoWg++)BtMhu87YN%0o5=#ve}O2cU~8{H^j{zfFW4q45Stc=!gs3)f-C0+ zrkoqB!Sc8WI5#LA@)4wgc(5p-87vB-y0F@sIY^`7!J>h9uxKD2EEt+%@ng{T z|3TtugT$W)$sige18vYACWB-g-JxbfuE{$YCwF3N^4o|R@DGgs;Z|Y}f#A$r?PWjr z8U$(ay+%N=_y-LFTxrZ8cUfKE%^-KfO`&4#3s2tMjjsqz0J0DBmR!mp_h1OWM418a zRY1<(eg@gE`rPn6n}%>*G(fC|68NA54ozCyK72XD8pc@3;SBN+r^GpeyImi$ z5Kdx{k6;MDqnfR{ho)S=qp(BX4?BxtJ;?5fg-6yNVty3 z1?EYH=gBS?NKy+VsRaggfkuH|7q%~u)ECJni}>&J+ZV~Ei%io+vgu-px>%wtHYkgE z>kHZ!OVlM2b%{h>Vo;Z86kz1S_9c>$rCjWb+L!9zLOn|*silU*Qb~2GB(;n;y`+7a z)aWuvVwq{OOg332sV$RDm-D78+Lz0w%VpE$rs;Cobh&K0TsB?Fn_k+!QZ`*Fo31oX zSIVX~dv$hwP|Bw(gLvJ50wNlKl?JK!+sQAsOl5 z9bLgliEwkJp?;<6^Q!ji%-Fw9MT8A(3rh&mDZB5KG&&_)owBb^*>|U5tCM&8K1M=J zVV#nlF4?q;|NF-FF4Lq-Qt8sDqW3Ny+Pc}6hmfF4a?>SAcS$b#BQzcL8ssJ-Y*#% zkWB_8i2+G+z@QFDE(Un7yV#mqOwR+7`hetWK$0v-5(P=Jz}xL+%S~a1g6y&&+Z7}o z_PPk>aObucOxFe3^&b8*1NN_f(7uPi;edU;g=OEO!JzDD&@>#BGzTRwgR;*-&dVLF zso-T$@-Qen+N-F4uzj!TbH7ZR_RF+sKTn&6SbnP0rUMe?fJ8aKQ66YNU{D^C$=E|Y z8T-iYp=mt>13L!bg%?Bh{Od;cB8fuJE|$U#)pYM>?^3)&*1V~BnCGoSlXvdeHQYX7sFuAzsINQe>E5!7HJs1W$p_fG z)6z2r%w)Qqpso>Uz=* zOn0D|a=s}gUBF3q3etQ<%!^*UFu^Mfd)zQ{B=)}$Sm|>%y9RI$Ki<=PGOQ+oWe`0{B$2+ z)5TW)R{MQJb%lZM{$0KB5*e6cT9R{_a!+B}lJWjDt~hrBFj|->RGY;Jyc|m~5`hf$ zkXU48HcIvZ_Qog1_`dWS_Q_suxiCT~MQu_Zi6W4Vt|E|)_!QX@swG(*Bh^yd2cTr^ zo735A%dlhdSmVW88;2*}eHKSjj=sso6>oG|9GSg;EvkE7A+%9LO5#OH8x>+Q=*cw> ze`{5QeZ>3YIHOaD{0`BfneyG^8}Xn$Fg!H5(0%>z7!$Z?H_YQ1kFOwXa+`DBDdyf-)@nCXQ1k;Ih0y*%) z<}v3=cVri|VMLCAlVsKiRJwLt*};94?;IX_SqdFr+DjMoR?2! zj%vFuoHF6ZtCC0f0g>boKljhxqSnR!CHWc`!Mq>O8x5YxCW#3~kP|O3jFKmLuB<&L zdu(!QSi4If_DaSm_QY!xE!1+TyDzf$y~Nldfy7H6%gv~GS!CI%-V!esJgjRg3l14^ zKhNou?CqGDf(UYlat)h@DM>a62a9kB68J`;cwJ{Elk7E#@+Fw@V|&nMGUP$*4Wm}q zrRrI{=F}n;>XKhYX7@@*SAKzCE;~tn)j&4Yyx#akdC6_H$%Qnok|~*^1ZOw?kV7QN zPuqB|1#>Efzv0%VSh6R=$swCmP%qEhczz*r1qXgc(54#dUA*fzYRIrx1V`bC_Y6m& zCcm~xg6y6s#?lHBbApi!F4Cb?ah8(TE#R_UN)hv!k@p1ogjcH+qZ$lAL8@?=mQtnC zXZn+qNVf1d@x~;R@R+{cyMF+|S)w9oVbrJ?Hl9(OB){X><_?cFck1+z_RgHKX+i{p z3yJzLgK)%Ox~_*eKJ|MU+(M`VD_+!`V<$o+cn4cp=Ov2dMxvlR`$_LXR2r~@sINv- zWv@%O;$R@Y@#rSX?Qj~Ok5;dJ;MO8ak&bS${z4SNxqTGlGB40Xk>Tnj0YTlJ94kRC z7l)&VLN99ZKLh4pIDyO$@+WVKEetpUnY_VISc$bHmkbUu`jq3D$bxdgB@4D?f~baB zRa6e4CWDbAk0!mBA#nI6XXR?82Fr#@FzQ+J2Q?bC63aKO#7?>SsQS}`k?NnW>Vukc z$`nC7r=SqTd&2hRfP=cBFc`R;YD=)&Jmn=IQY6l0EbvMl6UEDFrOf)Y&=FKoo>f$Y zit?1zD7jGXxpu~c3EgQe3*a${Xr3_%lY&}b@aZ??b7}4|>5}%%GD)O|YFgPIn&xpQ zO_P;sGeu9FQ4S4#!o)10b*DwTjC9LLaw#1UAtBrfZ4fVGGVufh&8SqiSAYJsW=7bL zq6!yx+2J=Rlaq1;@tpV@!V8qI@Zwo5$F(HFFM>;4I){ZiPofu*y6<8G!Bb)k_Fk;2 zsXNUc2obeeGeKyY#RQ36nrMRAR!N)%Ku{@>X@JvhC?vZp!C<%f=55`3)=-7}G2#YQ zM6tO93-SjeBN@n(ce}!eD+_s+9r0A@QYvIsq=B-o`(BNU^lG-0F z1ixn7cBD{t9@%-$rK6^rHQe$P$6SJ87v2)oQdA;(2(r?sQ#5%t+Tk&D+a8ZHh&Nxz zlkIgeB22@u6_D+Qu_-AKj3DP(pNF<1V#iZt&!ey>ZvuI|x}D@Yq#XW^65B6=sCfc@ zG@}uF@+FAx$;^XdMa0Kg*T_iTy=7_OsmQR&Q|&e6f{AVkwR1ytkg@BF7;a55r0TfJ zaPj^;33I5+Txn9Y(4tm0ZqBGckX=SCaAA8vi6xC4GsR-Eqt1Thh+C`l7vum zW`(YKf}oxhpVHeC944Mp4aE=5z__oH%Eqd)eWT@_sv}3Y%kn$ad%?gPCTSuJUcVrlYymtPF9u;WnGpV_Je60Z{NV{>w8j-jiyp}d@J^y^058X$Yor;RiNN2l zZxvCpPRMH3-)Ej$S*)yf%=Gu`o4={7!?Me4U!oI_zt7y~S!{v5!-gz}16?m9Q%;~_ zz)p?EjqK=JZ6v*jQ^|U*5=+vATa{Ju69)XO!Fr|<7AoN4GPTC$n_E1baOJ=;18#Zd z7(Bkf^-f$rP_9tN5+DllMAB=Y9LxP!p>BZ=arWRhd2|{b*{`$766n<4W$+^fa)6SD zpRuyayhiNX_5wd3hW~I+sba*PIcH}QBJEXV^V3Y2IY^S79KUAVrD$n0wCAX)XTcV& z58wWYkDiEr*@XjIAPXpBFlL~}?Y~R27h#OnTcVU-a!UCkPcCnZNiNA$#UD!_@tH3g zVXGYD-L%P^WJ6Pob9#+m*lkQ{O)zirXZGW!7rIYudgrrMN$2R?qOPG?af#dlu<5#V zaXb%ZRoO*6h2XLXMvKPP7L4YV)#iYtL*)r8G>{sLWsCy77)BXmjsfi^0aaF}J4Z%| z%#$rX%Yg7Dhca3`ueR{^WL*|FaxYtpIUSGjvnwLePl+>y2NOLb(o zvQW*gouXD1yY`$TjOtyhyTo|u6!NR>X|(Kaq%J*SaSutAg{HHdsq1jQYeq z%5?_*#xJWhF3P|>FGFFld>eBac>`n%Ux2(JZDKiUOzL2cNvI%RVFz-!PakJDy&V`% zpJ0D3PydX4rk{Y1C*k9h@bMJ;Xg{3Y?UtSxDU*o`V_K#oK&HA#nM{T=re!)3WSSW% zlbJxrv`j~VOy@+(WWwR-$P{(iAKFhw2eHQdATtfuojg!1)nL99S%zW!R zTvVDvdJYT*+#=8A0MTCQ+zk*iVM@0^9dX(5SSyr;9*lA7*})EJjW}fGNVg&7`C!*3 z1u3JMX>~LtPY$n*yM01CL-xRO_a{p=u&l33-37HwSKe45PUo@F-Pr;v14XAkQ3i_k z-8@14kRwt-%$Q^Fabq*%^cgXTHSBzWTFp;o97Jo}DJM8DTigZbWcQ3irgbfnK7mnU z?2{Q!bvefPR(oa&O%CMrSI}L^AsL0~Kn2qL??7CdOd zU|~xWCrY!6Vwrim;NRpOQ3?C*9aMqo80g}Y(d=eNx$?2;LdQP3?txg%EGBA`ERv=8 z9{qN?6a=aCAuc^9Ry0ZUIGIvrHiz_pXWS4`@Bhf~XirWMb`#%ZuBCF8u`xvmlDP-F z_LTH9?4H9+rig1zu&z{WRW5G3cbq?DezFJ(OGa**&9dzp7(Dpt9FeZL?B;QQRCz3` z3Ovs(hW*g{EHFrgBT&c)p$OzYQe)#Ef`qvU*YK>`N+w0} z`kbUbfqCScGQ7ug#<*Ll~EBlPzt>}fVD`o;(o$oK>5iS$g*y@LN~!FPN1-E|Kj-H+Ph{T6lHC8~Kjb07$vpj)r|atZ z4|y!@X!wyl6_XWHUxFSn-ej-D!a@41_&${e^^NtL@F0}p%AQc#t3;LfpC?%osf zs?7miZd98CdQ7Ft9JnP;vkNP>h{S8V4nkV+N%-J38$L1X#kXAaKQwg$Lty{$iG!b4 z;kST43eCFU112+HX67a_rSp)xYtMFnwGH4@+N#{WCw;Fn2kr&5RkXfa=BQ@ZGS{U` zk-X!ssL#9k8+yc+JSsD?yldi2$iS{*8Q7^j7yEilE@tLIrHcwXprenthXo9mRXVEc za`|`pbSY0uRQfS2lGj!@nmI~pi8_3vR_vqXZg6q$d>9_F4|Ti?9_n~8pZV20zN?=4&INltop%X!p&&5b0%ZbYG$DAZ5 zF-g+NEJ=(JMjjzj!i-5rKN(CDHF8bme@qK>3JCPo81p=5l#>094>qZ+sp&cF#)FnhFJBy&i_Eyr$}!l^WVo@3 z{uw!NPtU=_sX1`xFx;qB6snw6X{sZhz17Y!%p?yRa_o9-col$k_p)8a^p+y!Me$0c zve1;%ouXOc+1BytmZ(QU)PV>U>PwN^={PQaOMR>OU5A!1>He90-cfvxynP99#1o-8UeTTj7K@hx=+;_n#H_|A5(-li4Ozk36h>uwDG;` z09|VD?i2J;UF%*;oEBszqoxJZ=Lr0Q9;&W($fIY^%OV)L1fo2{lOFRf2e(Xb|Q9Q1swysAjM$%`us~x53G3BzD)Opgv(5$?N zI*<5QuxqD4Q>kYX%XpC694LC7@7KVt#R~pyOUx#ujAg2rY*ZUR_#K@^R*2_A74?P~ z7G(%mwZZDK_-z6ci4lei<{l9V@0v5Jz-2n>4GDs450vIW|0 zyyv>os8UH;Y04+MDa{J1$2?U-yDeQk=sC@@k~MX|k&t!5P)qWWm|^jDf6PVg4v$v4 zWKQ*r76%Jq3(R_Uy~+vOD6c+8@o@Ruy)-`tp6?m;g}*8$({0tt&5xy5?seW$>S}wG zEBB*VW^gX?HEDc?oy)WbH|ll%@uCF1SfSdp?C2E{s{1sNSp0u1BKcKekY?Q#ACfBS z(?cy8UF+am;-Ybd&h)AvvAY~e+ga}sMi~P;SI%AFRUW7)Da@4IId_Ru?m}v6<~cU?D2|U!w^^UZ-)<>_m$yIO4d4yvuthvVcNg6cyS7C*c;Rm_a znz3Ezvu1CzjLw#)=p{U+KI*y@8r#1ZF~?GqxK#1 z>>Mh8phLWAUEe0}y|_s}i9#o7pdenj_9lA+BisqaUel*(Io*0mJBK^@<`+o8*yD9r z)5Ea5bt-QU&dJsC_u$$1t&AN!M}W&Ojj94EV^^RDXP>@8%A)1n%5>yb63eeJG-oQS z7*WgsDRBsT_8$!O^)RI6$R24J@w|@CbTo1?=xWkXFRK`I(O995%;Rk|RRVpLcnzQP zg7^wCPs=(L>WDrFR+*06N?nC|ajHS(k0$P2FqM%5WtS>um0uLkk>~PAGZvN)F%M^} z)9^HbH}O>EO*wDtM3YVybv5barEX#yP5J?~~MXA5OvrfF0Ti325T((_Y>1EqeShfu?31YUq z%ro}ooYpHA3*D8wTQ@C#Sa(k`m%oRqE!o^+XL+V4Vv>jjda2VuwU$w6zEKH-QI?s_ zN}O+UtZG#cR&+WD(McCje3cB(K{CTXii{(!#^kF4lqyZN7CG({GZoSq-Day$M^#&m zIM3QuNF4LzqZ*VM!MIdktJ+dA>tb>fVoHpZ?qRkJMX4TghM4NCKs`s&E8|?{FPyn) z<7k|;i&?gz+ek;U`U#~0G&3KubEDZgi@J&cMOK5vP9s~kU80t4Pc_1>4jj*`)c(ve zNc3-xO!Vm$Q570U72o3cJ<7<@U1a54evK#kd`%y*n}|Y*$|=mSFo7yEl#m$c9B0ay zg;ftZXW{Wg-@K?i&n9wpwjIV3{gSMcuUud1$Qe)cp^Q+xmN1^^7m3LvfrJ-7zj!!g ze^q$xy0|DQCeg3t{)~B5M>*FJXm%cZiT;Y}CHl)D(Z6#%(T9Kq=~>y%V^yv6jxMeq zPxMc_WBAtLmUJ$A@u%ma7L$LMwNP!Y_#RL6*$_(MX)y2B0eudl0?U^1ihgOC@pwg_ zMgM%KdOfd%tWby1eB{Kq-s$IbELzGE@M%17 zo?!q`6L!z^d#g3h#-#|p`sogqDMUTm;kqVzBcs1cT-sBPX=PqH?>YQY?dw864=Xk6 zwR6T=d?D~Ae-OXO5j@un?m6sgOGS?cRGWkSmC>tw!)sZ6`_;;@>O6XC+zKtNDij3h zQORnXpVmmL&A}AdBP#pA_UyM-S+Jx6uf0dd6zIsxaMzsI$B-x~^C;S9>5645sE0(OC0d>S$mW{I?hu}xDn0>p9A1^is zjc;SHbw|Lznj^>03JTBh;^X(lj~kBya!umL&!ItjqWJNj{70SmF`2iCtzN#)U(95x zms=P!MTOPCI1Guu2lZ3Y$b3m zFr81ssacqhWEyfDGyS3PkYe^ly^qAi?wTk41?%2U117+M;C0@kj6e1wdr^NAM0FI< zl3c~Sqp0|XPgOxTd^!}9Yw{6P)(jI|n2(?`P>^8y>qw*1Z0aCu=~ehtqHumd3ohM} z`9g9?ZKK0=JnE_nomKd#x~m+QBqk_|N15b2Q$|Q0@AKnGn4CQ)&iW>w&FeU*Vx&l%8iP^D9UP%N{frjieE4zWwI9^8941uu_qO1C)f z3L-r1kR%v4Y&_4YvO&mmSiX7A-Z%<^<1)t z(IR_DUbph&w_2`2pVB?1Q19rP0wr?@RUIZNhL_Tv!yrkg>;$rVq7$3HNS*+e@#P7g zGPhr&(NP&Crc>mrEKE8@0wk>@xZ)~y&- ztgjuv6)WKt?|>qbR7K5f`0;vfz2GG+u;ug=S=qUY86~!$awQlrmQj;zsMv05q4App zu27RiQLktz!St>|NjZ9t3lQsbjESprsDDikx|cEBF;TEu}D zBQMD4TgCMVHdR;-q8j0gQZ+tHxpl-B6U&Qy4?>!uhfgfeMcTwDT9r1H1Wr$p6UzqX z5U~@>bvYdET5z;jq0E9wnWY@lz_%k+y1{ie{-lF=Ez)}SZ){xwZ(Kqr@_6-V55PeA z_&lz48Wh%S`l@x;ZQW&Wm3Kf>x=yP$2efWztX?_2d+Mdt7MyM+7{TLnF4v(v3x7j4 zL!W%ftDV_C&=Bv=qkmxAEzcO?N1#($NGc0iS{*A3S(8qyIjbCG^B$Jq(XmWkMV}8f z5>?!%-EwUTqG{sz5Du$}(c;RzkG}qelkHAz0#^ zmnaKO=M+R)Xgb@fhGyj_bm!=a>ALjE)jgMEXjWbvXBWNNeoW6fRhxrbDl<{%d_b>^ z39ozBs)F9+)Qf)q_bttQQqHZ8xXp(5v2xg$DD-#csNubr;l|bmkAZsMm^+F6n#kg1 z`8s*iw=bCd#DnAvj;N}&`*6SiECIWaj}^0dX^<}!A$a)MqR;CmZ)6LgiqBpt>5;Dx zIZq|Wl?0BsvFif(1Se!+$K0}|F@sOB8Is1yd!cW7B!)8}^{Dzz0-8*|Ypvz{O7xTk zU8#k^=pu%a6;}Kk(_tXQssNi;19qgMH$Uz`{uvT}OJ^Gx|Xt2W- zMXfB4P{#HvL#y1?35+~a#0m08mu_GT#}@*TDd-Dyq3c@M2z|&2IYHNc#tJ#&3K!r& zmDhRbWUu##8hvgZvQ#gyS3f~5)14uzWup>15{9(w?smBp1wFW9SE)n|q9Dbnqmw|- zHCm9Oy?_qthiu;jG3m}?sit$DRQ0RRC{KigprFpW(WM46oG^$=Rf-uS{_u5d`;DdKR6%J*ea zQA|pXh9eJ8bi=CdtE|tD!(*bcOQo(HOR$a5!)Pf=h6z=Dt#EP2F+uXn7+Xpf)q=jd zEJk|C<{b9kRFX$E=IG&Q=)pX0`?(k`%WQ@o&MC3P$+Dh0c0!cDs6#A;RpfS@AX39y zm`v$pmR>Po_VFjSUu54N;NNl&&ly0hNf0a7W9057m@uM?9gbDcNm8w&Vzs5ogN$kW z<>{rVvj<#c^d25>fbM@sp7lq!A1OOkHwHz8NyO#o1oOyod_A*#>@yo%!AmMIGv#U zIxqbwgLsR(4+l}*kRAI36oSUi02jE(UV(EwXQbrpDp^?LW=1R)BIzbrkm~pa z34sZTY)l4Kh_V_r6oPY?b8ByCK-Y(Z8L@%sNEK^GxHDuiEK5n_PO)Ww8Hdm-{o_p2XmtzH$0{dfO zsa^++#NRCW&?##s@c+i#!6tNoLCu!sSUfjGKDM8cbf~vPk-N1rQ(z>I8Zo8FL*gP` z6a!h`5-oUmt3-h^mS-M{W>U&lQ^xIMBcMu7!*E(YEoE(_md-$MRqi1vlKo6AIj3F1 z!fYT(K1{G2A0M)b=n|FERFV{QG$$br;d!#$ASyXQ6P7t+HSW=NtY2mv9P&_&QRqTq z8@&+mP>kjCaY8kyQ*vN2s8iqB7QU=2ol)ck!w+bn;VinR9B+C;@vY+NS3B zYg?Z(rxiir6q56upVlpnhce)x1_U;s{LwKI$T@ckoTD1}kp$7N; zxFt){y!%=qlU)E4C6V;3Lqh_gV@67L!T=gMk;#?k#foL1>Rn-hV0hgUF}B^;`Gj;6 z+=)`gMv95(7RAT17R=}ZIVg~Gn@JGw`M{$v9>HThB5Mf>e3azq!73D^21Ii@M%D?1 z5s02uzmcLNPl{D2T|tD*=~=J_Q8PBtOV@yV8x_O?Wua1*Fo9aO)MW`2-F13MJ?=@L z5MI{vxFb-bvsw=JP7hZFQ3I<%z|T8)G75^y*vQRe0%lf09ynxV5yjYnLpK!BDa zkDJs4g^4`K3GHKq$=Kd1T^P?5X*z!_ooaBu&_gmpbZGlyvDHwz#yzLaf=bWWHY%L~cRxDRyPG(M@Zuk~j*c3%Xc6o@W9?PCLJp>PjTNQ`axne=vBGo>W90Yo#pD~roTyd?>Lk4r_#z+Hy>YBqYkCeBJ1ht8 zG!PI@3m4tnKM77|;dCQ#)74yp7q|p3J%wQUZ%1p^cxsze8I>&I&ef=L%JtrS68R2r zBZ<0OQA*h0f#n%o)*P>b~Q59Dx}n{(ja#BkFSm{DRp%-C6N z_b5BN3Xi!qB{k$!tu90^>ILqGT#?WcXFx+vG78V{5R##_Rq(W{m1KKK zhPd>+i%9P3Knq@$&yYRDsAqWvLi!d%SsT}-i{-BSgT2$$Y7o_56~ZPp&!qy9INzho z{}2ON=TC4EN%x&yu5=5{!EW!jrdM%7q?GY+JvnfvGTdZI#QHutQzTHbl99|$dzf>>GS9I`Y*kUVinmrsT(oO5X%|CHb8&ZAJ3jvUj7==tdexbP$<3?L>{iBvJ+P>zhALux2~3qq$F9p8f=+*^N?C!+3W z#A0tYy_ey}M%2j+H(eDuaF^-fqb3&526?3$Z<3SzJQ6&aj2EYv zw&@9`II`+qg+*l^U}NE?ojt=tQ@3?*8<@wuZtnjFy~d)o literal 0 HcmV?d00001 diff --git a/src/cool2/cool/parser/cool_parser.py b/src/cool2/cool/parser/cool_parser.py new file mode 100644 index 000000000..7898028cd --- /dev/null +++ b/src/cool2/cool/parser/cool_parser.py @@ -0,0 +1,7 @@ +from cool.grammar.cool_grammar import G +from cool.parser.utils import load_parser, save_parser + +cool_parser_path = 'cool2/cool/parser/cool_parser.obj' +# cool_parser = save_parser(cool_parser_path,G) +# print('Errors',cool_parser.errors) +cool_parser = load_parser(cool_parser_path,G) \ No newline at end of file diff --git a/src/cool2/cool/parser/utils.py b/src/cool2/cool/parser/utils.py new file mode 100644 index 000000000..2231c8981 --- /dev/null +++ b/src/cool2/cool/parser/utils.py @@ -0,0 +1,25 @@ +from cmp.pycompiler import Production +from lib.lang.language_lr import LanguageLR,LR1Parser,LALR1Parser +import pickle + +def save_parser(path,G): + with open(path,'wb') as f: + pType = G.pType + G.pType = Production + attributes = [] + for prod in G.Productions: + attributes.append(prod.attributes) + prod.__delattr__('attributes') + parser = LALR1Parser(G) + pickle.dump(parser,f,pickle.HIGHEST_PROTOCOL) + for prod,attr in zip(parser.grammar.Productions,attributes): + prod.attributes = attr + G.pType = pType + return parser + +def load_parser(path,G): + with open(path,'rb') as f: + parser = pickle.load(f) + for prod,prod2 in zip(parser.grammar.Productions,G.Productions): + prod.attributes = prod2.attributes + return parser diff --git a/src/cool2/cool/pipeline.py b/src/cool2/cool/pipeline.py new file mode 100644 index 000000000..74eda3802 --- /dev/null +++ b/src/cool2/cool/pipeline.py @@ -0,0 +1,33 @@ +from cool.pipes.pipes import start_pipe, change_escaped_lines, remove_comments_pipe,\ + parse_text_pipe, ast_pipe, type_collector_pipe, build_types_pipe, \ + check_types_pipe, run_program_pipe, reconstruct_pipe, void_as_type_pipe, \ + auto_resolver_pipe, string_escape_pipe +from cool.libs import add_std_pipe +from cool.pipes.pipeline import Pipeline + +lexer_syntax_pipeline = Pipeline(start_pipe, + change_escaped_lines, + remove_comments_pipe, + parse_text_pipe, + string_escape_pipe + ) + +semantic_pipeline = Pipeline(add_std_pipe, + ast_pipe, + void_as_type_pipe, + type_collector_pipe, + build_types_pipe, + check_types_pipe, + auto_resolver_pipe, + ) + +execution_pipeline = Pipeline(run_program_pipe) + +cool_pipeline = Pipeline(lexer_syntax_pipeline, + semantic_pipeline, + execution_pipeline) + +reconstr_pipeline = Pipeline(lexer_syntax_pipeline, + semantic_pipeline, + reconstruct_pipe, + execution_pipeline) diff --git a/src/cool2/cool/pipes/pipeline.py b/src/cool2/cool/pipes/pipeline.py new file mode 100644 index 000000000..78e25ee01 --- /dev/null +++ b/src/cool2/cool/pipes/pipeline.py @@ -0,0 +1,38 @@ +class Pipe: + def __init__(self, pipe_func, pipe_check=None): + """ + pipe_func: callable that recieve a dictionary and return a dictionary + pipe_check: callable that recieve a dictionary and return if the pipe can be executed + """ + self.func = pipe_func + if pipe_check: + self.check = pipe_check + else: + self.check = lambda x: True + + def __call__(self, *args, **kwargs): + return self.func(*args, **kwargs) + + def can_execute(self, dictionary): + return self.check(dictionary) + +class Pipeline(Pipe): + + def __init__(self, *pipes): + super().__init__(self.__call__) + self.pipes = [pipe for pipe in pipes] + + def __call__(self, *args, **kwargs): + start_pipe = self.pipes[0] + + result = start_pipe(*args, **kwargs) + + for pipe in self.pipes[1:]: + if pipe.can_execute(result): + result = pipe(result) + else: + break + + return result + + \ No newline at end of file diff --git a/src/cool2/cool/pipes/pipes.py b/src/cool2/cool/pipes/pipes.py new file mode 100644 index 000000000..704e3e6a5 --- /dev/null +++ b/src/cool2/cool/pipes/pipes.py @@ -0,0 +1,297 @@ + +from cool.lexer.cool_lexer import cool_lexer +from cool.lexer.comment_lexer import comment_lexer +from lib.lang.language_lr import LanguageLR +from cool.parser.cool_parser import cool_parser +from cool.parser.comment_parser import comment_parser +from cool.visitors.visitors import * +from cool.grammar.cool_grammar import G +from cool.grammar.comment_grammar import C +from cool.semantic.scope import Scope +from cool.pipes.utils import pprint_tokens, print_errors +from cool.pipes.pipeline import Pipe + +def start_pipe(text, verbose = False): + """ + Start the pipeline + """ + result = { + "text": text, + "verbose": verbose, + "errors": [], + } + return result + +start_pipe = Pipe(start_pipe) + +def change_escaped_lines(result:dict, escape_regex="\\\ *?\n"): + import re + text, _ = re.subn(escape_regex, "", result['text']) + result["text"] = text + return result + +change_escaped_lines = Pipe(change_escaped_lines) + +def remove_comments_pipe(result:dict, comment_grammar=C, comment_lexer=comment_lexer, comment_parser=comment_parser): + """ + Remove the commented lines from the text + """ + text = result["text"] + + lang = LanguageLR(C, comment_lexer, comment_parser) + errors = [] + parse, tokens = lang(text, errors) + if not errors: + text = comment_parser.evaluate(tokens, errors, True) + + if result.get("verbose",False): + if errors: + print_errors("Removing Comments Errors", errors) + if len(text) != len(result["text"]): + print("=========== Text Comments Removed ===============") + print(text) + else: + print("=========== No Comments Removed ===============") + + result["errors"].extend(errors) + result["text"] = text + + return result + +remove_comments_pipe = Pipe(remove_comments_pipe) + +def parse_text_pipe(result:dict, language_grammar=G, language_lexer=cool_lexer, language_parser=cool_parser): + """ + Parse the text + """ + text = result['text'] + + lang = LanguageLR(language_grammar, language_lexer, language_parser) + + + errors = [] + parse, tokens = lang(text,errors) + + result.update({ + "language": lang, + "parser": language_parser, + "lexer": language_lexer, + "text_parse": parse, + "text_tokens": tokens, + }) + + if result.get("verbose",False): + if errors: + print_errors("Parsing Text Errors", errors) + print('================== TOKENS =====================') + pprint_tokens(tokens) + print('=================== PARSE =====================') + print('\n'.join(repr(x) for x in parse)) + + result["errors"].extend(errors) + + return result + +parse_text_pipe = Pipe(parse_text_pipe) + +def ast_pipe(result:dict): + """ + Add the initial ast + """ + parser = result["parser"] + tokens = result["text_tokens"] + + errors = [] + ast = parser.evaluate(tokens,errors,True) + + result["ast"] = ast + + if result.get("verbose", False): + if errors: + print_errors("Building AST Errors", errors) + print('==================== AST ======================') + formatter = FormatVisitor() + tree = formatter.visit(ast) + print(tree) + + result["errors"].extend(errors) + + return result + +ast_pipe = Pipe(ast_pipe, lambda x: not x["errors"]) + +def type_collector_pipe(result:dict, collector=TypeCollector): + """ + Collects the types in the program. + """ + ast = result.get("ast", None) + if not ast: + return result + + errors = [] + collector = collector(errors, result.get("context", None)) + collector.visit(ast) + context = collector.context + + result["context"] = context + + if result.get("verbose", False): + if errors: + print_errors("Collecting Types Errors", errors) + print('============== COLLECTED TYPES ===============') + print(context) + + result["errors"].extend(errors) + + return result + +type_collector_pipe = Pipe(type_collector_pipe) + +def build_types_pipe(result:dict, builder=TypeBuilder): + """ + Build the types in context + """ + ast = result.get("ast", None) + context = result.get("context",None) + if not ast or not context: + return result + + errors = [] + builder = builder(context, errors) + builder.visit(ast) + + if result.get("verbose", False): + if errors: + print_errors("Building Types Errors", errors) + print('=============== BUILT TYPES ================') + print(context) + + result["errors"].extend(errors) + + return result + +build_types_pipe = Pipe(build_types_pipe) + +def check_types_pipe(result:dict, checker=TypeChecker): + """ + Build the scope and check for types to be ok + """ + + ast = result.get("ast", None) + context = result.get("context",None) + if not ast or not context: + return result + + errors = [] + checker = checker(context, errors) + scope, operator = checker.visit(ast, Scope()) + + result["scope"] = scope + result["operator"] = operator + + if result.get("verbose", False): + if errors: + print_errors("Checking Types Errors", errors) + print("=========== Checked Types Info =============") + print("Scope:") + print(scope) + print("Operator:") + print(operator) + + result["errors"].extend(errors) + + return result + +check_types_pipe = Pipe(check_types_pipe) + +def run_program_pipe(result:dict, runner=RunVisitor): + """ + Run ast and store the result + """ + ast = result.get("ast",None) + context = result.get("context",None) + scope = result.get("scope",None) + operator = result.get("operator",None) + errors = result.get("errors", None) + if any(x == None for x in [ast, context, scope, operator]) or errors: + return result + + errors = [] + runner = runner(context,scope,operator,errors) + value = runner.visit(ast) + + result["value"] = value + + if result.get("verbose", False): + if errors: + print_errors("Running Program Errors", errors) + print('=============== PROGRAM RAN ===============') + print('Returned Value:') + print(value) + + result["errors"].extend(errors) + + return result + +run_program_pipe = Pipe(run_program_pipe, lambda x: not x["errors"]) + +def reconstruct_pipe(result:dict, reconstructer=ReconstructVisitor): + ast = result.get("ast",None) + context = result.get("context",None) + operation = result.get("operator",None) + if any(x == None for x in [ast, context, operation]): + return result + + reconstructer = reconstructer(context, operation) + reconstructed_text = reconstructer.visit(ast) + if result.get("verbose", False): + print("============== Reconstructed Text ===============") + print(reconstructed_text) + result['reconstructed_text'] = reconstructed_text + return result + +reconstruct_pipe = Pipe(reconstruct_pipe) + +def void_as_type_pipe(result:dict): + + tokens = result.get("text_tokens",None) + if not tokens: + return result + + result["errors"].extend([f"Void cant appear explicitly. Line:{x.lex[1]} Column:{x.lex[2]}" for x in tokens if x.token_type.Name == "type" and x.lex[0] == "Void"]) + return result + +void_as_type_pipe = Pipe(void_as_type_pipe) + +def auto_resolver_pipe(result:dict, auto_resolver=AutoResolver): + ast = result.get("ast",None) + context = result.get("context",None) + if any(x == None for x in [ast, context]): + return result + + errors = [] + resolver = auto_resolver(context, errors) + resolver.visit(ast) + + if result.get("verbose", False): + if errors: + print_errors("Auto Resolver Errors", errors) + + result["errors"].extend(errors) + + return result + +auto_resolver_pipe = Pipe(auto_resolver_pipe) + +def string_escape_pipe(result:dict): + tokens = result["text_tokens"] + import re + for tok_str in [x for x in tokens if x.token_type.Name == "string"]: + text = re.sub(r"\\n","\n",tok_str.lex[0]) + text = re.sub(r"\\b","\b",text) + text = re.sub(r"\\t","\t",text) + text = re.sub(r"\\f","\f",text) + tok_str.lex = (text, tok_str.lex[1], tok_str.lex[2]) + return result + +string_escape_pipe = Pipe(string_escape_pipe) \ No newline at end of file diff --git a/src/cool2/cool/pipes/utils.py b/src/cool2/cool/pipes/utils.py new file mode 100644 index 000000000..d3bc80e0d --- /dev/null +++ b/src/cool2/cool/pipes/utils.py @@ -0,0 +1,19 @@ +from cool.lexer.cool_lexer import ocur, ccur, semi + +def pprint_tokens(tokens): + indent = 0 + pending = [] + for token in tokens: + pending.append(token) + if token.token_type in { ocur, ccur, semi }: + if token.token_type == ccur: + indent -= 1 + print(' '*indent + ' '.join(str(t.token_type) for t in pending)) + pending.clear() + if token.token_type == ocur: + indent += 1 + print(' '.join([str(t.token_type) for t in pending])) + +def print_errors(header:str, errors): + print(f"=========== {header} ===============") + print(*[f"- {error}\n" for error in errors]) \ No newline at end of file diff --git a/src/cool2/cool/semantic/atomic.py b/src/cool2/cool/semantic/atomic.py new file mode 100644 index 000000000..5298126a5 --- /dev/null +++ b/src/cool2/cool/semantic/atomic.py @@ -0,0 +1,50 @@ +class ClassInstance: + def __init__(self,typex, context, operator, errors,**kwargs): + self.type = typex + self.context = context + self.operator = operator + self.errors = errors + self.scope = typex.class_node.scope.instance_copy(typex,context,operator,errors) + self.scope.set_variable_value('self',self) + if 'value' in kwargs: + self.value = kwargs['value'] + + def __eq__(self, other): + if self.type.name == "Void" and other.type.name == "Void": + return True + else: + return super().__eq__(other) + + def __hash__(self): + if self.type.name == "Void": + return 0 + else: + return super().__hash__() + + def copy(self): + if hasattr(self,'value'): + instance = ClassInstance(self.type,self.context,self.operator,self.errors,value=self.value) + else: + instance = ClassInstance(self.type,self.context,self.operator,self.errors) + instance.scope = self.scope.copy() + return instance + + def shallow_copy(self): + if hasattr(self,'value'): + instance = ClassInstance(self.type,self.context,self.operator,self.errors,value=self.value) + else: + instance = ClassInstance(self.type,self.context,self.operator,self.errors) + instance.scope.locals = [] + for var_info in self.scope.locals: + var = instance.scope.define_variable(var_info.name,var_info.type) + if hasattr(var_info,'value'): + if hasattr(var_info.value,'value'): + var.value = ClassInstance(var_info.value.type,var_info.value.context,var_info.value.operator,var_info.value.errors,value=var_info.value.value) + else: + var.value = var_info.value + return instance + + def __str__(self): + if hasattr(self,'value'): + return f'{self.value}: {self.type.name}' + return f'{self.type.name} instance' \ No newline at end of file diff --git a/src/cool2/cool/semantic/context.py b/src/cool2/cool/semantic/context.py new file mode 100644 index 000000000..7cbde0b69 --- /dev/null +++ b/src/cool2/cool/semantic/context.py @@ -0,0 +1,31 @@ +from cmp.semantic import Context as DeprecatedContext +from cool.semantic.type import Type, SelfType, AutoType +from cool.error.errors import SemanticError, TYPE_NOT_DEFINED, TYPE_ALREADY_DEFINED + +class Context(DeprecatedContext): + + def __init__(self, special_types:dict): + super().__init__() + self.special_types = special_types + for name in self.special_types: + self.types[name] = self.special_types[name]() + + def get_type(self, name:str,self_type=None): + if name == 'SELF_TYPE': + if self_type: + name = self_type.name + else: + return SelfType() + # raise TypeError('Wrong argument combination: name is "SELF_TYPE" and no self_type given') + elif name == 'AUTO_TYPE': + return AutoType(self) + try: + return self.types[name] + except KeyError: + raise SemanticError(TYPE_NOT_DEFINED.format(name)) + + def create_type(self, name:str): + if name in self.types: + raise SemanticError(TYPE_ALREADY_DEFINED.format(name)) + typex = self.types[name] = Type(name) + return typex \ No newline at end of file diff --git a/src/cool2/cool/semantic/operations.py b/src/cool2/cool/semantic/operations.py new file mode 100644 index 000000000..d6a9d0a2b --- /dev/null +++ b/src/cool2/cool/semantic/operations.py @@ -0,0 +1,184 @@ +from cool.error.errors import SemanticError,RunError, NO_OPERATION_DEFINDED, MULTIPLE_OPERATION_DEFINED, ZERO_DIVISION +from cool.ast.ast import * +from cool.semantic.type import * +from cool.semantic.atomic import * + +class Operator: + + def __init__(self,context,errors): + object_type = context.get_type('Object') + int_type = context.get_type('Int') + string_type = context.get_type('String') + bool_type = context.get_type('Bool') + void_type = context.get_type('Void') + self.operations = OperationDict({ + ('~',(int_type,)):(lambda x: ClassInstance(int_type,context,self,errors,value=-x.value),int_type), + ('-',(int_type,int_type)):(lambda x,y: ClassInstance(int_type,context,self,errors,value=x.value - y.value),int_type), + ('+',(int_type,int_type)):(lambda x,y: ClassInstance(int_type,context,self,errors,value=x.value + y.value),int_type), + ('*',(int_type,int_type)):(lambda x,y: ClassInstance(int_type,context,self,errors,value=x.value * y.value),int_type), + ('/',(int_type,int_type)):(lambda x,y: ClassInstance(int_type,context,self,errors,value=x.value // y.value),int_type), + ('<' ,(int_type,int_type)):(lambda x,y: ClassInstance(bool_type,context,self,errors,value=x.value < y.value),bool_type), + ('>' ,(int_type,int_type)):(lambda x,y: ClassInstance(bool_type,context,self,errors,value=x.value > y.value),bool_type), + ('<=',(int_type,int_type)):(lambda x,y: ClassInstance(bool_type,context,self,errors,value=x.value <= y.value),bool_type), + ('>=',(int_type,int_type)):(lambda x,y: ClassInstance(bool_type,context,self,errors,value=x.value >= y.value),bool_type), + ( '=',(int_type,int_type)):(lambda x,y: ClassInstance(bool_type,context,self,errors,value=x.value == y.value),bool_type), + + ('<' ,(string_type,string_type)):(lambda x,y: ClassInstance(bool_type,context,self,errors,value=x.value < y.value),bool_type), + ('>' ,(string_type,string_type)):(lambda x,y: ClassInstance(bool_type,context,self,errors,value=x.value > y.value),bool_type), + ('<=',(string_type,string_type)):(lambda x,y: ClassInstance(bool_type,context,self,errors,value=x.value <= y.value),bool_type), + ('>=',(string_type,string_type)):(lambda x,y: ClassInstance(bool_type,context,self,errors,value=x.value >= y.value),bool_type), + ( '=',(string_type,string_type)):(lambda x,y: ClassInstance(bool_type,context,self,errors,value=x.value == y.value),bool_type), + + ('not',(bool_type,)):(lambda x: ClassInstance(bool_type,context,self,errors,value=not x.value),bool_type), + ( '=',(bool_type,bool_type)):(lambda x,y: ClassInstance(bool_type,context,self,errors,value=x.value == y.value),bool_type), + + ('isvoid',(object_type,)):(lambda x: ClassInstance(bool_type,context,self,errors,value=x.type == void_type),bool_type), + + ( '=',(object_type,object_type)):(lambda x,y: ClassInstance(bool_type,context,self,errors,value=x.scope.get_variable_value('self') == y.scope.get_variable_value('self')),bool_type), + }) + self.operator = { + PlusNode:'+', + MinusNode:'-', + DivNode:'/', + StarNode:'*', + NotNode:'not', + GreaterEqualNode:'>=', + GreaterNode:'>', + LesserEqualNode:'<=', + LesserNode:'<', + EqualNode:'=', + RoofNode:'~', + IsVoidNode:'isvoid', + } + + def register_operation(self,operator,function,return_type,*types): + self.operations[operator,types] = (function,return_type) + + def get_operator(self,node): + return self.operator.get(type(node),None) + + def operation_defined(self,node, *types): + operator = self.get_operator(node) + if operator: + return (operator,types) in self.operations + return False + + def type_of_operation(self,node,*types): + operator = self.get_operator(node) + if operator: + try: + value = self.operations[(operator,types)] + except KeyError: + return ErrorType() + return value[1] + return ErrorType() + + def operate(self,operator,*values): + types = tuple(value.type for value in values) + try: + return self.operations[operator,types][0](*values) + except KeyError as exc: + raise RunError(exc.args[0]) + except ZeroDivisionError as exc: + raise RunError(ZERO_DIVISION) + + def __str__(self): + return str(self.operations) + +class OperationDict: + def __init__(self, default:"(operator,*types)->(func_operator,return_type)"={}): + self.operations = default + + def get(self,key,default=None): + operator, types = key + types = list(types) + auto_type_index = [i for i,x in enumerate(types) if isinstance(x, AutoType)] + if not auto_type_index: + try: + return self[key] + except KeyError: + return default + + possible_operations_types = [oper_types for oper,oper_types in self.operations if oper == operator and len(oper_types) == len(types)] + for i in [i for i in range(len(types)) if i not in auto_type_index]: + possible_operations_types = [x for x in possible_operations_types if x[i] == types[i]] + + combinations = dict() + for i, possibles in [(i, types[i].possibles) for i in auto_type_index]: + no_possibles = [] + for possible in possibles: + if any(x for x in possible_operations_types if x[i] == possible): + value = combinations.get(i,[]) + value.append(possible) + combinations[i] = value + else: + no_possibles.append(possible) + for to_remove in no_possibles: + types[i].remove_possible(to_remove) + + if len(combinations) != len(auto_type_index): + raise KeyError("No operation can satisfy the current types") + + results = [] + import cool.visitors.utils as ut + for possible_types in ut.set_permutation(*[combinations[i] for i in auto_type_index]): + type_key = [x for x in types] + for j,i in enumerate(auto_type_index): + type_key[i] = possible_types[j] + type_key = tuple(type_key) + try: + result = self[operator,type_key] + results.append(result) + except KeyError: + for j,i in enumerate(auto_type_index): + types[i].remove_possible(possible_types[j]) + + if len(results) != 1: + raise KeyError("Result must have a cardinality of 1") + return results[0] + + def get_covariant(self,key): + operator,types = key + possible_op = [] + + for op,op_types in self.operations: + if operator == op and len(types) == len(op_types): + for op_type,key_type in zip(op_types,types): + if not (op_type in key_type.get_parents() or op_type == key_type): + break + else: + possible_op.append((op,op_types)) + + if len(possible_op) > 1: + raise KeyError(MULTIPLE_OPERATION_DEFINED.format(operator,types)) + if len(possible_op) == 0: + raise KeyError(NO_OPERATION_DEFINDED.format(operator, types)) + + return self.operations[possible_op[0]] + + def __getitem__(self,key): + operator,types = key + try: + return self.operations[key] + except KeyError: + if any(x for x in key[1] if isinstance(x,AutoType)): + result = self.get(key) + return result + raise KeyError(NO_OPERATION_DEFINDED.format(operator, types)) + return self.get_covariant(key) + except TypeError: # Unhashable error + raise KeyError(NO_OPERATION_DEFINDED.format(operator, types)) + + def __contains__(self,key): + try: + self[key] + return True + except KeyError: + return False + + def __setitem__(self,key,value): + op,types = key + self.operations[key] = value + + def __str__(self): + return "\n".join([f"{op} : {', '.join([typex.name for typex in types])} --> {ret_type.name}" for (op, types),(func, ret_type) in self.operations.items()]) \ No newline at end of file diff --git a/src/cool2/cool/semantic/scope.py b/src/cool2/cool/semantic/scope.py new file mode 100644 index 000000000..0da81c5c8 --- /dev/null +++ b/src/cool2/cool/semantic/scope.py @@ -0,0 +1,96 @@ +from cmp.semantic import Scope as DeprecatedScope +from cool.error.errors import SemanticError +from cool.visitors.visitors import RunVisitor +class Scope(DeprecatedScope): + + def __init__(self, parent=None, typex=None): + super().__init__(parent) + self.type = typex + + def set_parent(self, parent): + self.parent = parent + self.index = len(parent.locals) + + def set_variable_value(self,name:str,value): + variable = self.find_variable(name) + variable.value = value + + def find_variable(self, vname, index=None): + var = super().find_variable(vname, index) + if var: return var + try: + if self.type: + attribute = self.type.get_attribute(vname) + return attribute + return None + except SemanticError: + return None + + def create_child(self, typex=None): + child = super().create_child() + child.type = self.type if not typex else typex + return child + + def get_variable_value(self,name:str): + variable = self.find_variable(name) + if hasattr(variable,'value'): + return variable.value + else: + raise TypeError(f"Value of variable {name} is not assigned") + + def instance_copy(self,typex,context,operator,errors): + scope = Scope(self.parent, self.type) + scope.index = self.index + scope.define_variable('self',typex) + attributes = typex.all_attributes() + run = RunVisitor(context,scope,operator,errors) + for var,attr_typex in attributes: + if var.type.name == 'SELF_TYPE': + scope.define_variable(var.name,typex) + else: + scope.define_variable(var.name,var.type) + for var,attr_typex in attributes: + default_node = var.node.expr + value = run.visit(default_node,scope) + scope.set_variable_value(var.name,value) + scope.children = self.children.copy() + return scope + + def copy(self): + copy_self = Scope(self.parent, self.type) + copy_self.index = self.index + copy_self.children = self.children.copy() + + for var_info in self.locals: + var = copy_self.define_variable(var_info.name,var_info.type) + if hasattr(var_info,'value'): + var.value = var_info.value.copy() + + return copy_self + + def _depth(self) -> int: + if self.parent == None: + return 0 + else: + return self.parent._depth() + 1 + + def __str__(self): + tab = " " + base = tab*self._depth() + value = base + "Scope: Index->%d\n" % self.index + + base += tab + + if self.locals: + value += base + "Local Variables \n" + for x in self.locals: + value += base + tab + x.name + ":" + x.type.name + "\n" + + if self.children: + value += base + "Childrens \n" + + for x in self.children: + value += str(x) + + return value + \ No newline at end of file diff --git a/src/cool2/cool/semantic/type.py b/src/cool2/cool/semantic/type.py new file mode 100644 index 000000000..de8ce3cc6 --- /dev/null +++ b/src/cool2/cool/semantic/type.py @@ -0,0 +1,388 @@ +from cmp.semantic import Attribute, Method +from cmp.semantic import Type as DeprecatedType +from cmp.semantic import SemanticError as DeprecatedSemanticError +from cool.ast.ast import VoidNode,ConstantNumNode,StringNode,BoolNode,SpecialNode,InstantiateNode +from cool.semantic.atomic import ClassInstance +from cool.error.errors import RunError, SemanticError, CoolTypeError, InferError, \ + VOID_TYPE_CONFORMS, METHOD_NOT_DEFINED, METHOD_ALREADY_DEFINED, \ + SUBSTR_OUT_RANGE, ATTRIBUTE_NOT_DEFINED, ATTRIBUTE_ALREADY_DEFINED, \ + ATTRIBUTE_CANT_INFER, METHOD_CANT_INFER, TYPE_CANT_INFER +import cool.visitors.utils as ut + +class Type(DeprecatedType): + + def add_special_method(self,func,method_name,method_args): + f = self.get_method(method_name,method_args) + old_type = f.node.body.type + f.node.body = SpecialNode(func,f.node.row,f.node.column) + f.node.body.type = old_type + + def set_parent(self,parent): + DeprecatedType.set_parent(self,parent) + if not parent.can_have_children: + self.parent = None + raise SemanticError(f"Type {parent.name} cant be inherited") + + def __hash__(self): + return hash(self.name) + + def __eq__(self,other): + return isinstance(other, type(self)) and other.name==self.name + + def get_method(self,name:str,args:int,current_type = None, only_local = False): + self = self if not isinstance(self,SelfType) or not current_type else current_type + try: + return next(method for method in self.methods if method.name == name and len(method.param_names)==args) + except StopIteration: + if self.parent is None: + raise SemanticError(METHOD_NOT_DEFINED.format(name, "", self.name, args)) + try: + if not only_local: + return self.parent.get_method(name,args) + raise SemanticError() + except SemanticError: + raise SemanticError(METHOD_NOT_DEFINED.format(name, "" if not only_local else " locally", self.name, args)) + + def define_method(self, name:str, param_names:list, param_types:list, return_type): + if (name,len(param_names)) in ((method.name,len(method.param_names)) for method in self.methods): + raise SemanticError(METHOD_ALREADY_DEFINED.format(name, len(param_names), self.name)) + method = Method(name, param_names, param_types, return_type) + self.methods.append(method) + return method + + def get_parents(self): + if self.parent: + parents = self.parent.get_parents() + return [self.parent] + parents + else: + return [] + + 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(ATTRIBUTE_NOT_DEFINED.format(name, self.name)) + try: + return self.parent.get_attribute(name) + except SemanticError: + raise SemanticError(ATTRIBUTE_NOT_DEFINED.format(name, 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(ATTRIBUTE_ALREADY_DEFINED.format(name, self.name)) + + def conforms_to(self,other,current_type): + self_type = self if not isinstance(self,SelfType) else current_type + other_type = other if not isinstance(other,SelfType) else current_type + if isinstance(other,AutoType): + new_types = [] + for x in self_type.get_parents() + [self_type]: + if x in other_type.possibles: + new_types.append(x) + other.update_possibles(new_types) + return bool(new_types) + return self_type.__conforms_to(other_type) + + def __conforms_to(self, other): + return other.bypass() or self == other or self.parent is not None and self.parent.__conforms_to(other) + + def join(self,other,current_type): + self = self if not isinstance(self,SelfType) else current_type + other = other if not isinstance(other,SelfType) else current_type + + self = self if not isinstance(self,AutoType) else self.get_lower(current_type) + other = other if not isinstance(other,AutoType) else other.get_lower(current_type) + + self_parents = self.get_parents() + other_parents = other.get_parents() + other_types = [other] + other_parents + self_types = [self] + self_parents + for common in self_types: + if common in other_types: + return common + raise CoolTypeError(f"No common types between {self.name} and {other.name}") + + @property + def can_have_children(self): + return True + + @property + def default(self): + return InstantiateNode("Void") + +class ObjectType(Type): + def __init__(self): + Type.__init__(self, 'Object') + self.parent = None + + def complete(self): + self.add_special_method(self.abort,'abort',0) + self.add_special_method(self.copy,'copy',0) + self.add_special_method(self.type_name,'type_name',0) + + def set_parent(self,parent): + pass + + @staticmethod + def abort(scope,context,operator,errors,**kwargs): + raise RunError('Cool Program Aborted') + + @staticmethod + def type_name(scope,context,operator,errors,**kwargs): + this_type = scope.get_variable_value('self').type + string = context.get_type('String') + return ClassInstance(string,context,operator,errors,value=this_type.name) + + @staticmethod + def copy(scope,context,operator,errors,**kwargs): + value = scope.get_variable_value('self') + return value.shallow_copy() + +class SelfType(Type): + + def __init__(self): + Type.__init__(self, 'SELF_TYPE') + + @property + def can_have_children(self): + return False + +class IntType(Type): + def __init__(self): + Type.__init__(self, 'Int') + + @property + def can_have_children(self): + return False + + @property + def default(self): + return ConstantNumNode('0') + +class BoolType(Type): + def __init__(self): + Type.__init__(self, 'Bool') + + @property + def can_have_children(self): + return False + + @property + def default(self): + return BoolNode('false') + +class StringType(Type): + def __init__(self): + Type.__init__(self, 'String') + + def complete(self): + self.add_special_method(self.length,'length',0) + self.add_special_method(self.concat,'concat',1) + self.add_special_method(self.substr,'substr',2) + + @staticmethod + def length(scope,context,operator,errors,**kwargs): + value = scope.get_variable_value('self').value + int_type = context.get_type('Int') + return ClassInstance(int_type,context,operator,errors,value=len(value)) + + @staticmethod + def concat(scope,context,operator,errors,**kwargs): + value = scope.get_variable_value('self').value + concat = scope.get_variable_value('s').value + typex = context.get_type('String') + return ClassInstance(typex,context,operator,errors,value=value + concat) + + @staticmethod + def substr(scope,context,operator,errors,**kwargs): + value = scope.get_variable_value('self').value + i = scope.get_variable_value('i').value + l = scope.get_variable_value('l').value + typex = context.get_type('String') + value_sliced = value[i:i+l] + if len(value) < l-i or l < 0: + raise RunError(SUBSTR_OUT_RANGE.format(value,i,l)) + return ClassInstance(typex,context,operator,errors,value=value_sliced) + + @property + def can_have_children(self): + return False + + @property + def default(self): + return StringNode('""') + +class VoidType(Type): + + def __init__(self): + Type.__init__(self, 'Void') + + def conforms_to(self, other, current_type): + return True + + def bypass(self): + return True + +class ErrorType(Type): + def __init__(self): + Type.__init__(self, 'Error') + + def conforms_to(self, other, current_type): + return True + + def bypass(self): + return True + + def __eq__(self, other): + return isinstance(other, Type) + + def __hash__(self): + return super().__hash__() + +class IOType(Type): + def __init__(self): + Type.__init__(self, 'IO') + + def complete(self): + self.add_special_method(self.out_string,'out_string',1) + self.add_special_method(self.out_int,'out_int',1) + self.add_special_method(self.in_string,'in_string',0) + self.add_special_method(self.in_int,'in_int',0) + + @staticmethod + def out_string(scope,context,operator,errors,**kwargs): + out = scope.get_variable_value('x').value + print(out, end="") + typex = context.get_type('IO') + return scope.get_variable_value('self') + + @staticmethod + def out_int(scope,context,operator,errors,**kwargs): + out = scope.get_variable_value('x').value + print(out, end="") + typex = context.get_type('IO') + return scope.get_variable_value('self') + + @staticmethod + def in_string(scope,context,operator,errors,**kwargs): + value = input() + typex = context.get_type('String') + return ClassInstance(typex,context,operator,errors,value=value) + + @staticmethod + def in_int(scope,context,operator,errors,**kwargs): + value = input() + import re + m = re.search(" *([1234567890]+)[^1234567890\n]*\n?", value) + typex = context.get_type('Int') + try: + if m: + return ClassInstance(typex,context,operator,errors,value=int(m.group(1))) + else: + raise ValueError() + except ValueError: + raise RunError(f'Invalid type: {value} is not an Int') + +class AutoType(Type): + def __init__(self,context): + Type.__init__(self, 'AUTO_TYPE') + self.parent = None + self.context = context + self.possibles = [ x for x in context.types.values() if not isinstance(x, ErrorType) ] + self.equals = [self,] + + def update_possibles(self, new_possibles): + for x in self.equals: + x.possibles = new_possibles + + def get_attribute(self,name): + self.update_possibles([x for x in self.possibles if any(attr for attr,typex in x.all_attributes() if attr.name==name)]) + if not self.possibles: + raise InferError(ATTRIBUTE_CANT_INFER.format(name)) + return self.possibles[-1].get_attribute(name) + + def get_method(self,name,args,current_type = None): + self.update_possibles([x for x in self.possibles if any(meth for meth,typex in x.all_methods() if (meth.name==name and len(meth.param_names) == args))]) + if not self.possibles: + raise InferError(METHOD_CANT_INFER.format(name, args)) + return self.possibles[-1].get_method(name,args) + + def conforms_to(self,other,current_type): + self_type = self if not isinstance(self,SelfType) else current_type + other_type = other if not isinstance(other,SelfType) else current_type + if isinstance(other,AutoType): + new_possibles = [] + for p_type in self.possibles: + if p_type in other_type.possibles: + new_possibles.append(p_type) + + self.add_to_equals(other) + + self.update_possibles(new_possibles) + + return bool(new_possibles) + else: + self.update_possibles([x for x in self.possibles if other_type.conforms_to(x,current_type)]) + return bool(self.possibles) + + def is_valid(self,current_type): + """ + Return True if the graph of the possible types is unilaterally connected + """ + if not self.possibles: return False + graph = ut.reverse_graph(ut.build_graph_list(self.possibles)) + _, topo_sort = ut.any_cycles(graph, True) + current = topo_sort[0] + d, f = ut.dfs(graph, current) + if len(d) == len(graph): + return True + return False + + def add_to_equals(self,other): + if not id(other) in [id(x) for x in self.equals]: + for x in self.possibles: + if not x in other.possibles: + self.possibles.remove(x) + for eq in other.equals: + self.equals.append(eq) + eq.equals = self.equals + eq.possibles = self.possibles + + def remove_possible(self, typex): + self.possibles.remove(typex) + self.update_possibles(self.possibles) + + def get_higher(self, current_type): + valid = self.is_valid(current_type) + if not valid: raise InferError(TYPE_CANT_INFER) + graph = ut.reverse_graph(ut.build_graph_list(self.possibles)) + _, topo_sort = ut.any_cycles(graph, True) + + current = topo_sort[0] + while len(graph[current]) == 1: + current = graph[current][0] + + return current + + def get_lower(self,current_type): + ## Lower implementation using graphs + valid = self.is_valid(current_type) + if not valid: raise InferError(TYPE_CANT_INFER) + graph = ut.reverse_graph(ut.build_graph_list(self.possibles)) + _, topo_sort = ut.any_cycles(graph, True) + + current = topo_sort[0] + return current + ## Lower implementation using join operator + # possibles = [x for x in self.possibles if not isinstance(x, ErrorType)] + # lesser = possibles[0] + # for x in possibles: + # lesser = x.join(lesser,current_type) + # return lesser \ No newline at end of file diff --git a/src/cool2/cool/visitors/utils.py b/src/cool2/cool/visitors/utils.py new file mode 100644 index 000000000..1623b129b --- /dev/null +++ b/src/cool2/cool/visitors/utils.py @@ -0,0 +1,83 @@ +def build_graph_list(type_list): + graph = { x:[x.parent,] for x in type_list } + for x in graph: + if graph[x][0] is None or graph[x][0] not in type_list: + graph[x] = [] + return graph + +def reverse_graph(graph): + graph = { x:y.copy() for x,y in graph.items() } + reverse = { x:[] for x in graph } + for x,y in graph.items(): + for z in y: + reverse_list = reverse.get(z, []) + if x not in reverse_list: + reverse_list.append(x) + reverse[z] = reverse_list + return reverse + +def build_graph_dict(context_types): + graph = { y:[y.parent,] for x,y in context_types.items() } + for x in graph: + if graph[x][0] is None: + graph[x] = [] + return graph + + +def dfs(graph, node=None): + d = dict() + f = dict() + time = [0] + if node: + d[node] = time[0]; time[0]+=1 + _dfs(graph, node, d, f, time) + f[node] = time[0]; time[0]+=1 + else: + for node in graph: + if node not in d: + d[node] = time[0]; time[0]+=1 + _dfs(graph, node, d, f, time) + f[node] = time[0]; time[0]+=1 + return d,f + +def _dfs(graph, node, d, f, time): + for adj in graph[node]: + if adj not in d: + d[adj] = time[0]; time[0]+=1 + _dfs(graph,adj,d,f,time) + f[adj] = time[0]; time[0]+=1 + +def any_cycles(graph, return_sort=False): + d,f,time,stack,cycles = { x:0 for x in graph },{ x:0 for x in graph },[1], [], [] + for x in graph: + if not d[x]: + d[x] = time[0]; time[0]+=1 + topological_order(graph,x,d,f,time,stack,cycles) + f[x] = time[0]; time[0]+=1 + stack.append(x) + if return_sort: + stack.reverse() + return cycles, stack + return cycles + + +def topological_order(graph,node,d,f,time,stack,cycles): + + for adj in graph[node]: + if not d[adj]: + d[adj] = time[0]; time[0]+=1 + topological_order(graph,adj,d,f,time,stack,cycles) + f[adj] = time[0]; time[0]+=1 + stack.append(adj) + elif not f[adj]: + cycles.append((node,adj)) + +def set_permutation(*iterables): + if len(iterables) == 1: + for x in iterables[0]: + yield [x] + else: + permutations = [x for x in set_permutation(*iterables[1:])] + for value in iterables[0]: + for permutation in permutations: + yield [value] + permutation \ No newline at end of file diff --git a/src/cool2/cool/visitors/visitors.py b/src/cool2/cool/visitors/visitors.py new file mode 100644 index 000000000..4e51d99ba --- /dev/null +++ b/src/cool2/cool/visitors/visitors.py @@ -0,0 +1,1058 @@ +import cmp.visitor as visitor +from cool.ast.ast import * +from cool.semantic.type import Type,ErrorType,StringType,IntType,IOType,BoolType,ObjectType,SelfType,VoidType,AutoType +from cool.semantic.context import Context +from cool.semantic.atomic import ClassInstance +from cool.error.errors import * +import cool.visitors.utils as ut +from cool.semantic.operations import Operator + +class ReconstructVisitor: + def __init__(self, context, operator): + self.context = context + self.operator = operator + self.tab = " " + + def add_line(self, line, depth): + return self.tab*depth + line + "\n" + + def preappend_depth_if_needed(self, text, depth): + if text[-1] != "\n": + return self.tab*depth + text + "\n" + return text + + @visitor.on('node') + def visit(self, node): + pass + + @visitor.when(ProgramNode) + def visit(self, node, depth=0): + program = "" + for decl in node.declarations: + program += self.visit(decl, depth) + program += self.add_line("\n", depth) + + return program + + @visitor.when(ClassDeclarationNode) + def visit(self, node, depth=0): + father = f" inherits {node.parent}" if node.parent != "Object" else "" + class_decl = self.add_line(f"class {node.id}" + father, depth) + class_decl += self.add_line("{", depth) + for feature in node.features: + class_decl += self.visit(feature, depth + 1) + class_decl += self.add_line("}", depth) + return class_decl + + @visitor.when(AttrDeclarationNode) + def visit(self, node, depth=0): + attr_decl = f"{node.id}:{node.type.name}" + if node.expr and node.expr.type.name != "Void" and node.expr.column != None: + attr_decl += " <- " + attr_decl += self.visit(node.expr, depth + 1) + attr_decl += ";" + return self.add_line(attr_decl, depth) + + @visitor.when(FuncDeclarationNode) + def visit(self, node, depth=0): + func_decl = f"{node.id}(" + func_decl += ", ".join([self.visit(x, depth + 1) for x in node.params]) + func_decl += f"): {node.type.name}" + func_decl = self.add_line(func_decl, depth) + func_decl += self.add_line("{", depth) + func_decl += self.preappend_depth_if_needed(self.visit(node.body, depth+1), depth + 1) + func_decl += self.add_line("};", depth) + return func_decl + + @visitor.when(ParamNode) + def visit(self, node, depth=0): + return f"{node.id}:{node.type.name}" + + @visitor.when(VarDeclarationNode) + def visit(self, node, depth=0): + decl = self.add_line(f"{node.id}:{node.type.name} <- ", depth) + decl += self.preappend_depth_if_needed(self.visit(node.expr, depth + 1), depth + 1) + return decl + + @visitor.when(AssignNode) + def visit(self, node, depth=0): + assign = self.add_line(f"{node.id} <- ", depth) + assign += self.preappend_depth_if_needed(self.visit(node.expr, depth + 1), depth + 1) + return assign + + @visitor.when(CallNode) + def visit(self, node, depth=0): + obj = self.visit(node.obj, depth) + obj = self.preappend_depth_if_needed(obj, depth) + if node.at: + obj += self.add_line(f"@{node.at.name}.{node.id}(", depth) # remove the \n + else: + obj += self.add_line(f".{node.id}(", depth) # remove the \n + + for arg in [self.visit(x, depth + 1) for x in node.args]: + if arg[-1] == "\n": + arg = arg[:len(arg)-1] + ",\n" + else: + arg = self.add_line(arg + ",", depth + 1) + obj += arg + if node.args: + obj = obj[:len(obj)-2] + "\n" + obj += self.add_line(")", depth) + return obj + + @visitor.when(BlockNode) + def visit(self, node, depth=0): + block = self.add_line("{", depth) + + for expr in node.expr_list: + expr = self.visit(expr, depth + 1) + if expr[-1] == "\n": + expr = expr[:len(expr)-1] + ";\n" + else: + expr = self.add_line(expr + ";", depth + 1) + block += expr + + block += self.add_line("}", depth) + return block + + @visitor.when(ConditionalNode) + def visit(self, node, depth=0): + condition = self.add_line("if", depth) + condition += self.preappend_depth_if_needed(self.visit(node.condition,depth + 1), depth + 1) + condition += self.add_line("then", depth) + condition += self.preappend_depth_if_needed(self.visit(node.then_expr, depth + 1), depth + 1) + condition += self.add_line("else", depth) + condition += self.preappend_depth_if_needed(self.visit(node.else_expr, depth + 1), depth + 1) + condition += self.add_line("fi", depth) + return condition + + @visitor.when(LetNode) + def visit(self, node, depth=0): + let = self.add_line("let", depth) + for arg in [self.visit(arg, depth + 1) for arg in node.params]: + if arg[-1] == "\n": + arg = arg[:len(arg)-1] + ",\n" + else: + arg = self.preappend_depth_if_needed(arg + ",", depth + 1) + let += arg + if node.params: + let = let[:len(let)-2] + "\n" + + + let += self.add_line("in", depth) + expr = self.preappend_depth_if_needed(self.visit(node.expr, depth + 1), depth + 1) + return let + expr + + @visitor.when(CaseNode) + def visit(self, node, depth=0): + case = self.add_line("case", depth) + case += self.preappend_depth_if_needed(self.visit(node.expr, depth + 1), depth + 1) + case += self.add_line("of", depth) + for check in [self.visit(x, depth + 1) for x in node.params]: + check = self.preappend_depth_if_needed(check, depth + 1) + case += check + case += self.add_line("esac", depth) + return case + + @visitor.when(CheckNode) + def visit(self, node, depth=0): + check = self.add_line(f"{node.id}:{node.type.name} =>", depth) + expr = self.visit(node.expr, depth + 1) + expr = self.preappend_depth_if_needed(expr, depth + 1) + check += expr[:len(expr)-1] + ";\n" + return check + + @visitor.when(WhileNode) + def visit(self, node, depth=0): + code = self.add_line("while", depth) + code += self.preappend_depth_if_needed(self.visit(node.condition, depth + 1), depth + 1) + code += self.add_line("loop", depth) + code += self.preappend_depth_if_needed(self.visit(node.expr, depth + 1), depth + 1) + code += self.add_line("pool", depth) + return code + + @visitor.when(UnaryNode) + def visit(self, node, depth=0): + operator = self.operator.get_operator(node) + " " + operator += self.visit(node.member, depth + 1) + return operator + + @visitor.when(BinaryNode) + def visit(self, node, depth=0): + operator = "(" + operator += self.visit(node.left, depth + 1) + operator += " " + self.operator.get_operator(node) + " " + operator += self.visit(node.right, depth + 1) + operator += ")" + return operator + + @visitor.when(VariableNode) + def visit(self, node, depth=0): + return node.lex + + @visitor.when(ConstantNumNode) + def visit(self, node, depth=0): + return node.lex + + @visitor.when(BoolNode) + def visit(self, node, depth=0): + return node.lex + + @visitor.when(StringNode) + def visit(self, node, depth=0): + return node.lex + + @visitor.when(VoidNode) + def visit(self, node, depth=0): + return "void" + + @visitor.when(InstantiateNode) + def visit(self, node, depth=0): + return "new " + node.lex + +class FormatVisitor(object): + + @visitor.on('node') + def visit(self, node, tabs): + pass + + def get_type_name(self, node): + try: + return node.type.name + except AttributeError: + return node.type + + @visitor.when(ProgramNode) + def visit(self, node, tabs=0): + ans = '\t' * tabs + f'\\__ProgramNode [ ... ]' + statements = '\n'.join(self.visit(child, tabs + 1) for child in node.declarations) + return f'{ans}\n{statements}' + + @visitor.when(ClassDeclarationNode) + def visit(self, node, tabs=0): + parent = '' if node.parent is None else f": {node.parent}" + ans = '\t' * tabs + f'\\__ClassDeclarationNode: class {node.id} {parent} {{ ... }}' + features = '\n'.join(self.visit(child, tabs + 1) for child in node.features) + return f'{ans}\n{features}' + + @visitor.when(AttrDeclarationNode) + def visit(self, node, tabs=0): + ans = '\t' * tabs + f'\\__AttrDeclarationNode: {node.id} : {self.get_type_name(node)}' + return f'{ans}' + + @visitor.when(VarDeclarationNode) + def visit(self, node, tabs=0): + ans = '\t' * tabs + f'\\__VarDeclarationNode: let {node.id} : {self.get_type_name(node)} = ' + expr = self.visit(node.expr, tabs + 1) + return f'{ans}\n{expr}' + + @visitor.when(AssignNode) + def visit(self, node, tabs=0): + ans = '\t' * tabs + f'\\__AssignNode: let {node.id} <- ' + expr = self.visit(node.expr, tabs + 1) + return f'{ans}\n{expr}' + + @visitor.when(FuncDeclarationNode) + def visit(self, node, tabs=0): + params = ', '.join([self.visit(x) for x in node.params]) + ans = '\t' * tabs + f'\\__FuncDeclarationNode: {node.id}({params}) : {self.get_type_name(node)} {{ }};' + body = self.visit(node.body, tabs + 1) + return f'{ans}\n{body}' + + @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(UnaryNode) + def visit(self, node, tabs=0): + ans = '\t' * tabs + f'\\__{node.__class__.__name__} ' + member = self.visit(node.member, tabs + 1) + return f'{ans}\n{member}' + + @visitor.when(AtomicNode) + def visit(self, node, tabs=0): + return '\t' * tabs + f'\\__{node.__class__.__name__}: {node.lex}' + + @visitor.when(BlockNode) + def visit(self,node,tabs=0): + ans = '\t' * tabs + f'\\__ BlockNode: {{ }}' + expr_list = [ self.visit(x,tabs+1) for x in node.expr_list ] + return ans + "\n" + '\n'.join(expr_list) + + @visitor.when(CallNode) + def visit(self, node, tabs=0): + obj = self.visit(node.obj, tabs + 1) + ans = '\t' * tabs + f'\\__CallNode: .{node.id}(, ..., )' + args = '\n'.join(self.visit(arg, tabs + 1) for arg in node.args) + return f'{ans}\n{obj}\n{args}' + + @visitor.when(InstantiateNode) + def visit(self, node, tabs=0): + return '\t' * tabs + f'\\__ InstantiateNode: new {node.lex}' + + @visitor.when(ParamNode) + def visit(self, node, tabs=0): + return '\t' * tabs + f"{node.id} : {self.get_type_name(node)}" + + @visitor.when(CaseNode) + def visit(self, node, tabs=0): + ans = '\t' * tabs + f"\\__CaseNode: case of esac" + expr = self.visit(node.expr, tabs + 1) + checks = "\n".join(self.visit(check, tabs + 1) for check in node.params) + return f"{ans}\n{expr}\n{checks}" + + @visitor.when(CheckNode) + def visit(self, node, tabs=0): + ans = "\t" * tabs + f"\\__CheckNode: {node.id}:{self.get_type_name(node)} => " + expr = self.visit(node.expr, tabs + 1) + return f"{ans}\n{expr}" + + @visitor.when(LetNode) + def visit(self, node, tabs=0): + ans = '\t' * tabs + f"\\__LetNode: let in " + decls = "\n".join(self.visit(decl, tabs + 1) for decl in node.params) + expr = self.visit(node.expr, tabs + 1) + return f"{ans}\n{decls}\n{expr}" + + @visitor.when(ConditionalNode) + def visit(self, node, tabs=0): + ans = '\t' * tabs + f"\\__ConditionalNode: if then else fi" + cond = self.visit(node.condition, tabs + 1) + then = self.visit(node.then_expr, tabs + 1) + elsex = self.visit(node.else_expr, tabs + 1) + return f"{ans}\n{cond}\n{then}\n{elsex}" + + @visitor.when(WhileNode) + def visit(self, node, tabs=0): + ans = '\t' * tabs + f"\\__WhileNode: while loop pool" + cond = self.visit(node.condition, tabs + 1) + body = self.visit(node.expr, tabs + 1) + return f"{ans}\n{cond}\n{body}" + + +class TypeCollector(object): + def __init__(self, errors=[], context=None): + self.context = context + self.errors = errorsTrue + + @visitor.on('node') + def visit(self, node): + pass + + @visitor.when(ProgramNode) + def visit(self, node): + if not self.context: + self.context = Context({ + 'Object':ObjectType, + 'String':StringType, + 'Bool':BoolType, + 'Int':IntType, + 'IO':IOType, + 'Void':VoidType, + 'Error':ErrorType, + }) + + for class_decl in node.declarations: + try: + self.context.create_type(class_decl.id) + except SemanticError as er: + self.errors.append(er.text + f' Line:{class_decl.row} Column:{class_decl.column}') + + for class_decl in node.declarations: + try: + curr_type = self.context.get_type(class_decl.id) + except SemanticError as er: + self.errors.append(er.text+ f' Line:{class_decl.row} Column:{class_decl.column}') + if class_decl.parent: + try: + curr_type.set_parent(self.context.get_type(class_decl.parent)) + except SemanticError as er: + self.errors.append(er.text+ f' Line:{class_decl.row} Column:{class_decl.column}') + + cycles = ut.any_cycles(ut.build_graph_dict(self.context.types)) + + for type1,type2 in cycles: + self.errors.append(CIRCULAR_DEPENDENCY.format(type1, type2)) + +class TypeBuilder: + def __init__(self, context, errors=[]): + self.context = context + self.current_type = None + self.errors = errors + + @visitor.on('node') + def visit(self, node): + pass + + @visitor.when(ProgramNode) + def visit(self, node): + for decl in node.declarations: + self.visit(decl) + + @visitor.when(ClassDeclarationNode) + def visit(self, node): + self.current_type = self.context.get_type(node.id) + self.current_type.class_node = node + for child in node.features: + self.visit(child) + + @visitor.when(AttrDeclarationNode) + def visit(self, node): + try: + node.type = self.context.get_type(node.type) + except SemanticError as er: + node.type = ErrorType() + self.errors.append(er.text + f' Line:{node.row} Column:{node.column}') + try: + attribute = self.current_type.define_attribute(node.id,node.type) + attribute.node = node + except SemanticError as er: + self.errors.append(er.text + f' Line:{node.row} Column:{node.column}') + + @visitor.when(FuncDeclarationNode) + def visit(self, node): + for x in node.params: + try: + x.type = self.context.get_type(x.type) + except SemanticError as er: + x.type = ErrorType() + self.errors.append(er.text + f' Line:{x.row} Column:{x.column}') + try: + node.type = self.context.get_type(node.type) + except SemanticError as er: + self.errors.append(er.text + f' Line:{node.row} Column:{node.column}') + try: + method = self.current_type.define_method(node.id,[x.id for x in node.params],[x.type for x in node.params],node.type) + method.node = node + except SemanticError as er: + self.errors.append(er.text + f' Line:{node.row} Column:{node.column}') + +class TypeChecker: + """ + Checks if the operations between types are correct. + Build the program scope. + Nodes are tagged with their type. + """ + def __init__(self, context:Context, errors=[]): + self.context = context + self.current_type = None + self.current_method = None + self.errors = errors + self.operator = Operator(context,errors) + + def complete_initial_types(self): + complete_types = ['Object','String','IO'] + for cmp_type in complete_types: + self.context.get_type(cmp_type).complete() + + @visitor.on('node') + def visit(self, node, scope): + pass + + @visitor.when(ProgramNode) + def visit(self, node, scope=None): + scope = scope + + for declaration in node.declarations: + self.visit(declaration, scope) + + if 'Main' in self.context.types: + MainType = self.context.get_type('Main') + try: + main_method = MainType.get_method('main',0, only_local = True) + except SemanticError as cexc: + self.errors.append(cexc.text + " " + NO_ENTRY_POINT) + else: + self.errors.append(NO_MAIN_TYPE) + + self.complete_initial_types() + + return scope,self.operator + + @visitor.when(ClassDeclarationNode) + def visit(self, node, scope): + self.current_type = self.context.get_type(node.id) + node.scope = scope.create_child(self.current_type) + node.scope.define_variable('self',self.current_type) + node.features.sort(key=lambda x: x.__class__.__name__) # Attributes first, Stable sort + for feature in node.features: + self.visit(feature,node.scope) + + @visitor.when(AttrDeclarationNode) + def visit(self, node, scope): + if not node.expr: + if node.type.name != "AUTO_TYPE": + node.expr = node.type.default + node.expr.type = node.type # if type is void then is not assigned in the visit else the type is overridden + else: + return # No default expr can be assing at this moment + self.visit(node.expr,scope) + + if not node.expr.type.conforms_to(node.type,self.current_type): + self.errors.append(INCOMPATIBLE_TYPES.format(node.expr.type.name,node.type.name) + f' Line:{node.row} Column:{node.column}') + + @visitor.when(FuncDeclarationNode) + def visit(self, node, scope): + methods = [] + current_method = self.current_type.get_method(node.id,len(node.params)) + if self.current_type.parent: + methods = self.current_type.parent.all_methods() + methods = [x for x,typex in methods if x.name == node.id and len(x.param_names) == len(node.params)] + methods = [x for x in methods if x != current_method] + if methods: + self.errors.append(WRONG_SIGNATURE.format(methods[0].name,self.current_type.parent.name) + f' Line:{node.row} Column:{node.column}') + + f_scope = scope.create_child() + node.scope = f_scope + + for param in node.params: + self.visit(param,f_scope) + + self.current_method = self.current_type.get_method(node.id,len(node.params)) + self.visit(node.body,f_scope) + + if not isinstance(node.type,VoidType) and not node.body.type.conforms_to(node.type,self.current_type): + self.errors.append(INCOMPATIBLE_TYPES.format(node.body.type.name,node.type.name) + f' Line:{node.row} Column:{node.column}') + + @visitor.when(ParamNode) + def visit(self, node, scope): + scope.define_variable(node.id,node.type) + + @visitor.when(VarDeclarationNode) + def visit(self, node, scope): + if node.id == 'self': + self.errors.append(SELF_IS_READONLY + f' Line:{node.row} Column:{node.column}') + + try: + node.type = self.context.get_type(node.type) + except SemanticError as er: + node.type = ErrorType() + self.errors.append(er.text + f' Line:{node.row} Column:{node.column}') + if not node.expr: + if node.type.name != "AUTO_TYPE": + node.expr = node.type.default + node.expr.type = node.type # if type is void then is not assigned in the visit else the type is overridden + else: + if scope.is_local(node.id): + self.errors.append(LOCAL_ALREADY_DEFINED.format(node.id,self.current_method.name) + f' Line:{node.row} Column:{node.column}') + else: + scope.define_variable(node.id,node.type) + return # No default expr can be assing at this moment + + self.visit(node.expr,scope) + + if scope.is_local(node.id): + self.errors.append(LOCAL_ALREADY_DEFINED.format(node.id,self.current_method.name) + f' Line:{node.row} Column:{node.column}') + else: + scope.define_variable(node.id,node.type) + + if not node.expr.type.conforms_to(node.type,self.current_type): + self.errors.append(INCOMPATIBLE_TYPES.format(node.expr.type.name,node.type.name) + f' Line:{node.row} Column:{node.column}') + + @visitor.when(AssignNode) + def visit(self, node, scope): + if node.id == 'self': + self.errors.append(SELF_IS_READONLY + f' Line:{node.row} Column:{node.column}') + + self.visit(node.expr,scope) + + if not scope.is_defined(node.id): + self.errors.append(VARIABLE_NOT_DEFINED.format(node.id,self.current_method.name) + f' Line:{node.row} Column:{node.column}') + node.type = node.expr.type + else: + var_info = scope.find_variable(node.id) + node.type = var_info.type + + if not node.expr.type.conforms_to(node.type,self.current_type): + self.errors.append(INCOMPATIBLE_TYPES.format(node.expr.type.name,node.type.name) + f' Line:{node.row} Column:{node.column}') + + @visitor.when(CallNode) + def visit(self, node, scope): + # obj,id,args + self.visit(node.obj,scope) + for arg in node.args: + self.visit(arg,scope) + + try: + if node.at: + node.at = self.context.get_type(node.at) + if not node.obj.type.conforms_to(node.at,self.current_type): + self.errors.append(INCOMPATIBLE_TYPES.format(node.obj.type.name,node.at.name) + f' Line:{node.row} Column:{node.column}') + method = node.at.get_method(node.id,len(node.args),self.current_type) + else: + method = node.obj.type.get_method(node.id,len(node.args),self.current_type) + not_conform = [(x,y.type) for x,y in zip(method.param_types,node.args) if not y.type.conforms_to(x,self.current_type)] + for x,y in not_conform: + self.errors.append(INCOMPATIBLE_TYPES.format(y.name,x.name) + f' Line:{node.row} Column:{node.column}') + node.type = method.return_type if method.return_type.name != "SELF_TYPE" else node.obj.type + + except SemanticError as er: + node.type = ErrorType() + self.errors.append(er.text + f' Line:{node.row} Column:{node.column}') + + @visitor.when(BlockNode) + def visit(self, node:BlockNode, scope): + + node.scope = scope.create_child() + + for expr in node.expr_list: + self.visit(expr,node.scope) + + node.type = node.expr_list[-1].type + + @visitor.when(ConditionalNode) + def visit(self, node:ConditionalNode, scope): + self.visit(node.condition,scope) + self.visit(node.then_expr,scope) + self.visit(node.else_expr,scope) + + if node.condition.type != self.context.get_type('Bool') and node.condition.type != ErrorType(): + self.errors.append(NOT_BOOLEAN_CONDITION.format(node.condition.type.name)+ f' Line:{node.row} Column:{node.column}') + try: + node.type = node.get_return_type(self.current_type) + except InferError as cexc: + node.type = self.context.get_type("Error") + self.errors.append(cexc.text + f" Line:{node.row} Column:{node.column}") + + @visitor.when(LetNode) + def visit(self, node: LetNode, scope): + let_scope = scope.create_child() + curr_scope = let_scope + for var_node in node.params: + attr_scope = curr_scope.create_child() + self.visit(var_node,attr_scope) + curr_scope = attr_scope + body_scope = curr_scope.create_child() + self.visit(node.expr,body_scope) + node.type = node.expr.type + + @visitor.when(WhileNode) + def visit(self, node:WhileNode, scope): + node.type = self.context.get_type('Object') + self.visit(node.condition,scope) + if node.condition.type != self.context.get_type('Bool'): + self.errors.append(NOT_BOOLEAN_CONDITION.format(node.condition.type.name)+ f' Line:{node.row} Column:{node.column}') + self.visit(node.expr,scope) + + @visitor.when(CheckNode) + def visit(self, node:CheckNode, scope): + if node.id == 'self': + self.errors.append(SELF_IS_READONLY + f' Line:{node.row} Column:{node.column}') + + node.type = self.context.get_type(node.type) + node.scope = scope.create_child() + node.scope.define_variable(node.id,node.type) + + self.visit(node.expr,node.scope) + + # if not node.expr.type.conforms_to(node.type,self.current_type): + # self.errors.append(INCOMPATIBLE_TYPES.format(node.expr.type.name,node.type.name) + f' Line:{node.row} Column:{node.column}') + + @visitor.when(CaseNode) + def visit(self, node:CaseNode, scope): + self.visit(node.expr,scope) + + types = [] + for i,param in enumerate(node.params): + self.visit(param,scope) + # if not (node.expr.type.conforms_to(param.type,self.current_type) or param.type.conforms_to(node.expr.type,self.current_type)): + # self.errors.append(f"Incompatible types {param.type.name} with {node.expr.type.name}" + f' Line:{node.row} Column:{node.column}') + if any(x for x in types if x[1] == param.type): + self.errors.append(CASE_TYPES_REPEATED.format(param.expr.type.name) + f" Line:{param.row} Column:{param.column}") + types.append((i,param.expr.type)) + + static_type = types[0][1] + for i,join in types[1:]: + static_type = static_type.join(join,self.current_type) + node.type = static_type + + @visitor.when(UnaryNode) + def visit(self, node, scope): + self.visit(node.member,scope) + + if not self.operator.operation_defined(node,node.member.type): + node.type = ErrorType() + self.errors.append(INVALID_UNARY_OPERATION.format(self.operator.get_operator(node),node.member.type.name) + f' Line:{node.row} Column:{node.column}') + else: + node.type = self.operator.type_of_operation(node,node.member.type) + + @visitor.when(BinaryNode) + def visit(self, node, scope): + self.visit(node.left,scope) + self.visit(node.right,scope) + + if not self.operator.operation_defined(node,node.left.type,node.right.type): + node.type = ErrorType() + self.errors.append(INVALID_BINARY_OPERATION.format(self.operator.get_operator(node),node.left.type.name,node.right.type.name) + f' Line:{node.row} Column:{node.column}') + else: + node.type = self.operator.type_of_operation(node,node.left.type,node.right.type) + + @visitor.when(ConstantNumNode) + def visit(self, node, scope): + node.type = self.context.get_type("Int") + + @visitor.when(BoolNode) + def visit(self, node, scope): + node.type = self.context.get_type("Bool") + + @visitor.when(StringNode) + def visit(self, node:StringNode, scope): + node.type = self.context.get_type("String") + if len(node.lex) > 1024: + self.errors.append(f"String to long. Max length is 1024 chars, have {len(node.lex)} chars" + f' Line:{node.row} Column:{node.column}') + + @visitor.when(VoidNode) + def visit(self, node, scope): + pass + + @visitor.when(VariableNode) + def visit(self, node, scope): + if scope.is_defined(node.lex): + var = scope.find_variable(node.lex) + node.type = var.type + else: + node.type = ErrorType() + self.errors.append(VARIABLE_NOT_DEFINED.format(node.lex,self.current_method.name) + f' Line:{node.row} Column:{node.column}') + + @visitor.when(InstantiateNode) + def visit(self, node, scope): + try: + node.type = self.context.get_type(node.lex) + + except SemanticError as er: + node.type = ErrorType() + self.errors.append(er.text + f' Line:{node.row} Column:{node.column}') + +class AutoResolver: + + def __init__(self, context:Context, errors=[]): + self.context = context + self.errors = errors + + def change_auto_to_concrete_type(self, node, node_type_name="type", less_concrete=False): + typex = getattr(node, node_type_name) + if typex.name == "AUTO_TYPE": + try: + if less_concrete: + setattr(node, node_type_name, typex.get_lower(self.current_type)) + else: + setattr(node, node_type_name, typex.get_higher(self.current_type)) + except InferError as cexc: + self.errors.append(cexc.text + f" Line:{node.row} Column:{node.column}") + + @visitor.on('node') + def visit(self, node): + pass + + @visitor.when(ProgramNode) + def visit(self, node): + for declaration in node.declarations: + self.visit(declaration) + + @visitor.when(ClassDeclarationNode) + def visit(self, node): + self.current_type = self.context.get_type(node.id) + for feature in node.features: + self.visit(feature) + + @visitor.when(AttrDeclarationNode) + def visit(self, node): + self.change_auto_to_concrete_type(node) + if not node.expr: + node.expr = node.type.default + node.expr.type = node.type + self.visit(node.expr) + + @visitor.when(FuncDeclarationNode) + def visit(self, node): + self.change_auto_to_concrete_type(node) + for param in node.params: + self.visit(param) + + self.visit(node.body) + + @visitor.when(ParamNode) + def visit(self, node): + self.change_auto_to_concrete_type(node, less_concrete=True) + + @visitor.when(VarDeclarationNode) + def visit(self, node): + self.change_auto_to_concrete_type(node) + if not node.expr: + node.expr = node.type.default + node.expr.type = node.type + self.visit(node.expr) + + @visitor.when(AssignNode) + def visit(self, node): + self.visit(node.expr) + + @visitor.when(CallNode) + def visit(self, node): + self.visit(node.obj) + + for arg in node.args: + self.visit(arg) + + if node.at: + pass + + @visitor.when(BlockNode) + def visit(self, node:BlockNode): + for expr in node.expr_list: + self.visit(expr) + + @visitor.when(ConditionalNode) + def visit(self, node:ConditionalNode): + self.visit(node.condition) + self.visit(node.then_expr) + self.visit(node.else_expr) + + @visitor.when(LetNode) + def visit(self, node: LetNode): + for var_node in node.params: + self.visit(var_node) + self.visit(node.expr) + + @visitor.when(WhileNode) + def visit(self, node:WhileNode): + self.visit(node.condition) + self.visit(node.expr) + + @visitor.when(CheckNode) + def visit(self, node:CheckNode): + self.visit(node.expr) + + @visitor.when(CaseNode) + def visit(self, node:CaseNode): + self.visit(node.expr) + + for i,param in enumerate(node.params): + self.visit(param) + + @visitor.when(UnaryNode) + def visit(self, node): + self.visit(node.member) + + @visitor.when(BinaryNode) + def visit(self, node): + self.visit(node.left) + self.visit(node.right) + + @visitor.when(ConstantNumNode) + def visit(self, node): + pass + + @visitor.when(BoolNode) + def visit(self, node): + pass + + @visitor.when(StringNode) + def visit(self, node:StringNode): + pass + + @visitor.when(VoidNode) + def visit(self, node): + pass + + @visitor.when(VariableNode) + def visit(self, node): + pass + + @visitor.when(InstantiateNode) + def visit(self, node): + pass + + +class RunVisitor: + + def __init__(self, context:Context, scope, operator, errors=[]): + self.context = context + self.scope = scope + self.errors = errors + self.operator = operator + + @visitor.on('node') + def visit(self, node, scope): + pass + + @visitor.when(ProgramNode) + def visit(self, node:ProgramNode, scope=None): + for decl in node.declarations: + if decl.id == 'Main': + for method in [x.node for x,y in self.context.get_type('Main').all_methods()]: + if method.id == 'main': + main_type = self.context.get_type(decl.id) + main_instance = ClassInstance(main_type,self.context,self.operator,self.errors) + # method_scope = method.scope.copy() + # method_scope.set_variable_value('self',main_instance) + # method_scope.set_parent(main_instance.scope) + try: + value = self.visit(method.body,main_instance.scope) + except RunError as err: + self.errors.append(err.text) + return None + return value + + @visitor.when(VarDeclarationNode) + def visit(self, node:VarDeclarationNode, scope): + current_value = self.visit(node.expr,scope) + scope.define_variable(node.id,node.type) + scope.set_variable_value(node.id,current_value) + return current_value + + @visitor.when(AssignNode) + def visit(self, node:AssignNode, scope): + value = self.visit(node.expr,scope) + scope.set_variable_value(node.id,value) + return value + + @visitor.when(CallNode) + def visit(self, node:CallNode, scope): + value = self.visit(node.obj,scope) + if value.type.name == "Void": + raise RunError(DISPATCH_VOID + f" Line:{node.row} Column:{node.column}") + args = [self.visit(x,scope) for x in node.args] + if node.at: + method = node.at.get_method(node.id,len(node.args)) + else: + method = value.type.get_method(node.id,len(node.args)) + func_scope = method.node.scope.copy() + func_scope.set_parent(value.scope) + for arg,name in zip(args,method.param_names): + func_scope.set_variable_value(name,arg) + # func_scope.set_variable_value('self',value) + value = self.visit(method.node.body,func_scope) + return value + + @visitor.when(SpecialNode) + def visit(self, node:SpecialNode, scope): + try: + return node.func(scope,self.context,self.operator,self.errors) + except RunError as cexc: + raise RunError(cexc.text + f" Line:{node.row} Column:{node.column}") + + @visitor.when(BlockNode) + def visit(self, node:BlockNode, scope): + body_scope = scope.create_child() + values = [self.visit(x,body_scope) for x in node.expr_list] + return values[-1] + + @visitor.when(ConditionalNode) + def visit(self, node:ConditionalNode, scope): + condition = self.visit(node.condition,scope) + if condition.value is True: + return self.visit(node.then_expr,scope) + if condition.value is False: + return self.visit(node.else_expr,scope) + raise RunError(NO_BOOL_CONDITION + f" Line:{node.row} Column:{node.column}") # This connot happend BUT you never know + + @visitor.when(LetNode) + def visit(self, node:LetNode, scope): + let_scope = scope.create_child() + for var_decl in node.params: + value = self.visit(var_decl.expr,let_scope) + if not let_scope.is_local(var_decl.id): + let_scope.define_variable(var_decl.id,var_decl.type) + let_scope.set_variable_value(var_decl.id,value) + value = self.visit(node.expr,let_scope) + return value + + @visitor.when(WhileNode) + def visit(self, node:WhileNode, scope): + while self.visit(node.condition,scope).value == True: + self.visit(node.expr,scope) + typex = self.context.get_type('Void') + return ClassInstance(typex,self.context,self.operator,self.errors,value=None) + + @visitor.when(CheckNode) + def visit(self, node:CheckNode, scope,**kwargs): + node_scope = scope.create_child() + value = kwargs['value'] + node_scope.define_variable(node.id,value.type) + node_scope.set_variable_value(node.id,value) + return self.visit(node.expr,node_scope) + + @visitor.when(CaseNode) + def visit(self, node:CaseNode, scope): + value = self.visit(node.expr,scope) + + if value.type.name == "Void": + raise RunError(VOID_TYPE_CONFORMS + f" Line: {node.row} Column: {node.column}") + + try: + greaters = (x for x in [(i,x.type) for i,x in enumerate(node.params)] if value.type.conforms_to(x[1],value.type)) + least_pos,least = next(greaters) + for i,other in greaters: + least_pos,least = (least_pos,least) if least.conforms_to(other,least) else (i,other) + except StopIteration: + raise RunError(CASE_NO_BRANCH_SELECTED.format(value.type.name) + f" Line: {node.row} Column: {node.column}") + + return self.visit(node.params[least_pos],scope,value=value) + + @visitor.when(UnaryNode) + def visit(self, node, scope): + operator = self.operator.get_operator(node) + value = None + member_value = self.visit(node.member,scope) + try: + value = self.operator.operate(operator,member_value) + except RunError as err: + raise RunError(err.text + f' Line:{node.row} Column:{node.column}') + return value + + @visitor.when(BinaryNode) + def visit(self, node, scope): + operator = self.operator.get_operator(node) + value = None + left_value = self.visit(node.left,scope) + right_value = self.visit(node.right,scope) + try: + value = self.operator.operate(operator,left_value,right_value) + except RunError as err: + raise RunError(err.text + f' Line:{node.row} Column:{node.column}') + return value + + @visitor.when(ConstantNumNode) + def visit(self, node:ConstantNumNode, scope): + typex = self.context.get_type('Int') + return ClassInstance(typex,self.context,self.operator,self.errors,value=int(node.lex)) + + @visitor.when(BoolNode) + def visit(self, node, scope): + typex = self.context.get_type("Bool") + if node.lex[0]=='t': # true + return ClassInstance(typex,self.context,self.operator,self.errors,value=True) + return ClassInstance(typex,self.context,self.operator,self.errors,value=False) + + @visitor.when(StringNode) + def visit(self, node, scope): + typex = self.context.get_type('String') + string_without_delimitators = node.lex[1:len(node.lex)-1] + return ClassInstance(typex,self.context,self.operator,self.errors,value=string_without_delimitators) + + @visitor.when(VoidNode) + def visit(self, node, scope): + typex = self.context.get_type('Void') + return ClassInstance(typex,self.context,self.operator,self.errors,value=None) + + @visitor.when(VariableNode) + def visit(self, node:VariableNode, scope): + try: + value = scope.get_variable_value(node.lex) + except TypeError as exc: # Variable not assign yet searching in Attr + try: + attr = [x.type for x in scope.locals if x.name == 'self'][0].get_attribute(node.lex) + value = self.visit(attr.node.expr, scope) + except SemanticError: # Attr not found + self.errors.append(exc.args[0] + f' Line:{node.row} Column:{node.column}') + return value + + @visitor.when(InstantiateNode) + def visit(self, node:InstantiateNode, scope): + typex = self.context.get_type(node.lex,scope.find_variable('self').type) + return ClassInstance(typex,self.context,self.operator,self.errors) + \ No newline at end of file diff --git a/src/cool2/cool_programs/program1.cl b/src/cool2/cool_programs/program1.cl new file mode 100644 index 000000000..32ea12c21 --- /dev/null +++ b/src/cool2/cool_programs/program1.cl @@ -0,0 +1,35 @@ +class B {} + +class Main { + main() : AUTO_TYPE + { + { + a:Int <- 20; + b:Int <- 30; + c:B <- new B; + d:String <- "hello"; + e:String <- "olleh"; + f:Bool <- false; + g:Bool <- true; + + r1:AUTO_TYPE <- a + b; + r2:AUTO_TYPE <- a + c; + r3:AUTO_TYPE <- a + d; + r4:AUTO_TYPE <- e + d; + r5:AUTO_TYPE <- ~a; + r6:AUTO_TYPE <- ~c; + r7:AUTO_TYPE <- ~d; + r8:AUTO_TYPE <- not a; + r9:AUTO_TYPE <- not c; + r10:AUTO_TYPE <- not d; + r11:AUTO_TYPE <- a = b; + r12:AUTO_TYPE <- c = b; + r13:AUTO_TYPE <- d = b; + r14:AUTO_TYPE <- d = e; + r15:AUTO_TYPE <- f + a; + r16:AUTO_TYPE <- f + d; + r17:AUTO_TYPE <- ~f; + r18:AUTO_TYPE <- not f; + } + }; +} \ No newline at end of file diff --git a/src/cool2/cool_programs/program1.cl.infer.cl b/src/cool2/cool_programs/program1.cl.infer.cl new file mode 100644 index 000000000..3f68abcc8 --- /dev/null +++ b/src/cool2/cool_programs/program1.cl.infer.cl @@ -0,0 +1,65 @@ +class B +{ +} + + +class Main +{ + main(): Bool + { + { + a:Int <- + 20; + b:Int <- + 30; + c:B <- + new B; + d:String <- + "hello"; + e:String <- + "olleh"; + f:Bool <- + false; + g:Bool <- + true; + r1:Int <- + (a + b); + r2:Object <- + (a + c); + r3:Object <- + (a + d); + r4:Object <- + (e + d); + r5:Int <- + ~ a; + r6:Object <- + ~ c; + r7:Object <- + ~ d; + r8:Object <- + not a; + r9:Object <- + not c; + r10:Object <- + not d; + r11:Bool <- + (a = b); + r12:Bool <- + (c = b); + r13:Bool <- + (d = b); + r14:Bool <- + (d = e); + r15:Object <- + (f + a); + r16:Object <- + (f + d); + r17:Object <- + ~ f; + r18:Bool <- + not f; + } + }; +} + + diff --git a/src/cool2/main.py b/src/cool2/main.py new file mode 100644 index 000000000..9af449f95 --- /dev/null +++ b/src/cool2/main.py @@ -0,0 +1,72 @@ +from cool.pipeline import cool_pipeline, reconstr_pipeline +from cool.grammar.cool_grammar import G +from cool.parser.cool_parser import save_parser,cool_parser_path, cool_parser +from cool.lexer.cool_lexer import save_lexer,cool_lexer_path, cool_tokens_def +from cool.grammar.comment_grammar import C +from cool.parser.comment_parser import comment_parser_path, comment_parser +from cool.lexer.comment_lexer import comment_lexer_path, comment_tokens_def +import plac + +# plac annotation (description, type of arg [option, flag, positional], abrev, type, choices) +def main( + program_dir:("Path to cool program", "positional"), + out_infer:("Creates a file containing the inferred types", 'flag', 'i'), + verbose:("Print more info", 'flag', 'v'), + update_cool_parser:("Update pickled cool parser", 'flag', 'ucp'), + update_cool_lexer:("Update pickled cool lexer", 'flag', 'ucl'), + update_comment_parser:("Update pickled comment parser", 'flag', 'ump'), + update_comment_lexer:("Update pickled comment lexer", 'flag', 'uml')): + """ + Manage cool interpreter and run programs + """ + change = False + if update_cool_parser: + obj = save_parser(cool_parser_path,G) + print('Errors',obj.errors) + print("Parser updated") + change |= True + + if update_cool_lexer: + obj = save_lexer(cool_tokens_def,cool_lexer_path,G) + print("Lexer updated") + change |= True + + if update_comment_parser: + obj = save_parser(comment_parser_path,C) + print('Errors',obj.errors) + print("Comment Parser updated") + change |= True + + if update_comment_lexer: + obj = save_lexer(comment_tokens_def,comment_lexer_path,C) + print("Comment Lexer updated") + change |= True + + if change: + print("Parsers and Lexers updated") + exit() + + if program_dir == None: + print("If no update flag is provided 'program_dir' is required") + exit() + + with open(program_dir) as file: + file_content = file.read() + + if out_infer: + result = reconstr_pipeline(file_content, verbose=verbose) + else: + result = cool_pipeline(file_content,verbose=verbose) + ast, g_errors, parse, tokens, context, scope, operator, value, reconstr = [result.get(x, None) for x in ["ast", "errors", "text_parse", "text_tokens", "context", "scope", "operator", "value", "reconstructed_text"]] + + if reconstr and out_infer: + with open(program_dir + ".infer.cl", "w") as file: + file.write(reconstr) + + if g_errors: + print("Errors\n", *[f"- {x}\n" for x in g_errors]) + if hasattr(value, "value"): + print("Value:",value.value) + +if __name__ == "__main__": + plac.call(main) \ No newline at end of file diff --git a/src/cool2/main_st.py b/src/cool2/main_st.py new file mode 100644 index 000000000..429ba1962 --- /dev/null +++ b/src/cool2/main_st.py @@ -0,0 +1,91 @@ +import streamlit as st +import time +from cool.parser.cool_parser import save_parser,cool_parser_path, cool_parser +from cool.lexer.cool_lexer import save_lexer,cool_lexer_path, cool_tokens_def +from cool.grammar.cool_grammar import G +from cool.parser.comment_parser import comment_parser_path, comment_parser +from cool.lexer.comment_lexer import comment_lexer_path, comment_tokens_def +from cool.grammar.comment_grammar import C +from cool.visitors.visitors import FormatVisitor +from cool.pipeline import reconstr_pipeline + +run_mode = "Correr código" +option_mode = "Opciones" + +st.title("Proyecto II de Compilación") + +mode = st.sidebar.selectbox("Modos",[run_mode, option_mode]) + +if mode == run_mode: + code = st.text_area("Código") + + if st.button("Correr"): + result = reconstr_pipeline(code, verbose=True) + ast, g_errors, parse, tokens, context, scope, operator, value, reconstr_text = [result[x] for x in ["ast", "errors", "text_parse", "text_tokens", "context", "scope", "operator", "value", "reconstructed_text"]] + if g_errors: + st.json(g_errors) + else: + st.subheader("Código con AUTO_TYPE inferidos") + st.text(reconstr_text) + + st.subheader("AST") + format_visitor = FormatVisitor() + text = format_visitor.visit(ast) + st.text(text) + + parse.reverse() + parse_str = "" + for y in [str(x) + "\n" for x in parse]: + parse_str += y + st.subheader("Parse") + st.text(parse_str) + + token_str = "" + for y in [str(x) + "\n" for x in tokens]: + token_str += y + st.subheader("Tokens") + st.text(token_str) + st.subheader("Contexto") + st.text(context) + st.subheader("Scope") + st.text(scope) + st.subheader("Operadores") + st.text(operator) + st.subheader("Resultado del programa") + st.text(value) + + + +if mode == option_mode: + if st.checkbox("Ver opciones para actualizar los parsers y lexers"): + loading = st.empty() + if st.button("Recrear Parsers"): + loading.text("Creando el parser de comentarios...") + obj = save_parser(comment_parser_path,C) + if obj.errors: + st.subheader("Errores creando el parser de comentarios") + st.json(obj.errors) + + loading.text("Creando el parser de cool...") + obj = save_parser(cool_parser_path,G) + if obj.errors: + st.subheader("Errores creando el parser de cool") + st.json(obj.errors) + + loading.empty() + + + if st.button("Recrear Lexers"): + loading.text("Creando el lexer de comentarios...") + + obj = save_lexer(comment_tokens_def,comment_lexer_path,C) + if not obj: + st.error("Fallo crear el lexer de comentarios") + + loading.text("Creando el lexer de cool...") + obj = save_lexer(cool_tokens_def,cool_lexer_path,G) + if not obj: + st.error("Fallo crear el lexer de cool") + + loading.empty() + \ No newline at end of file diff --git a/src/cool2/requirements.txt b/src/cool2/requirements.txt new file mode 100644 index 000000000..4b76ab6cb --- /dev/null +++ b/src/cool2/requirements.txt @@ -0,0 +1,5 @@ +nbformat==4.4.0 +plac==1.1.3 +streamlit==0.51.0 +pydot==1.4.1 +pytest==6.1.2 diff --git a/src/cool2/test/__init__.py b/src/cool2/test/__init__.py new file mode 100644 index 000000000..e69de29bb diff --git a/src/cool2/test/test_class.py b/src/cool2/test/test_class.py new file mode 100644 index 000000000..f3fa23af1 --- /dev/null +++ b/src/cool2/test/test_class.py @@ -0,0 +1,54 @@ +import sys +import pytest +import os +base_dir = os.path.dirname(__file__) +sys.path.append(os.path.join(base_dir, "..")) +from cool.pipeline import cool_pipeline, reconstr_pipeline +from cool.error.errors import CoolError +from .tests import tests, tests_run + +class TestPrograms: + + @pytest.mark.parametrize("name,progr,exp_result,exp_errors",[x for x in tests if "in_out" not in x[0]]) + def test_general(self, name, progr, exp_result, exp_errors): + result = cool_pipeline(progr) + ast, errors, parse, tokens, context, scope, operator, out = [result.get(x,None) for x in ["ast", "errors", "text_parse", "text_tokens", "context", "scope", "operator", "value"]] + if hasattr(out, "value"): + assert out.value == exp_result + assert errors == exp_errors + + @pytest.mark.parametrize("name,program", [(x[0],x[1]) for x in tests if "in_out" not in x[0]]) + def test_reconstruction(self, name, program): + result = reconstr_pipeline(program) + ast, errors, context, scope, operator, out, reconstr_text = [result.get(x,None) for x in ["ast", "errors", "context", "scope", "operator", "value", "reconstructed_text"]] + with open(os.path.join(base_dir, "test_reconstruction", name + ".cl"), "w") as f: + f.write(reconstr_text) + result = reconstr_pipeline(reconstr_text) + ast2, errors2, context2, scope2, operator2, out2, reconstr_text2 = [result.get(x,None) for x in ["ast", "errors", "context", "scope", "operator", "value", "reconstructed_text"]] + assert len(errors) == len(errors2) + assert reconstr_text == reconstr_text2 + if hasattr(out, "value"): + assert out.value == out2.value + + @pytest.mark.parametrize("progr",tests_run) + def test_run(self, progr): + result = cool_pipeline(progr) + errors, out = [result.get(x,None) for x in ["errors", "value"]] + assert len(errors) == 0 + + + def test_input(self, monkeypatch): + with monkeypatch.context() as m: + import builtins # Mocking input + def input(): + return "20" + monkeypatch.setattr(builtins, "input", input) + name, progr, exp_result, exp_errors = [x for x in tests if "in_out" in x[0]][0] + + result = cool_pipeline(progr) + ast, errors, parse, tokens, context, scope, operator, out = [result[x] for x in ["ast", "errors", "text_parse", "text_tokens", "context", "scope", "operator", "value"]] + + if hasattr(out, "value"): + assert out.value == "20" + assert errors == exp_errors + diff --git a/src/cool2/test/test_data/test_SELF_TYPE.meta_test b/src/cool2/test/test_data/test_SELF_TYPE.meta_test new file mode 100644 index 000000000..b3ea61549 --- /dev/null +++ b/src/cool2/test/test_data/test_SELF_TYPE.meta_test @@ -0,0 +1,37 @@ +"{\"name\": \"test_SELF_TYPE\", \"result\": \"BBBB\", \"errors\": []}" + + +class A{ + a:SELF_TYPE; + get():SELF_TYPE { + self + }; + get2():SELF_TYPE { + new SELF_TYPE + }; + get3():SELF_TYPE { + { + a <- new SELF_TYPE; + a; + } + }; +} + +class B inherits A { + get4(): SELF_TYPE { + get3() + }; + } + +class Main inherits IO { + main() : String{ + { + a:A <- new A; + b:B <- new B; + b.get().type_name().concat( + b.get2().type_name().concat( + b.get3().type_name()).concat( + b.get4().type_name())); + } + }; +} diff --git a/src/cool2/test/test_data/test_at.meta_test b/src/cool2/test/test_data/test_at.meta_test new file mode 100644 index 000000000..983bee452 --- /dev/null +++ b/src/cool2/test/test_data/test_at.meta_test @@ -0,0 +1,28 @@ +"{\"name\": \"test_at\", \"result\": \"class A class B\", \"errors\": []}" + +class A { + name():String { + "class A ".concat(name2()) + }; + name2():String { + "class A" + }; +} + +class B inherits A { + name():String { + "class B" + }; + name2():String { + "class B" + }; +} + +class Main inherits IO { + main():String { + { + b:B <- new B; + b@A.name(); + } + }; +} diff --git a/src/cool2/test/test_data/test_attr_dep_1.meta_test b/src/cool2/test/test_data/test_attr_dep_1.meta_test new file mode 100644 index 000000000..b2705643d --- /dev/null +++ b/src/cool2/test/test_data/test_attr_dep_1.meta_test @@ -0,0 +1,19 @@ +"{\"name\": \"test_attr_dep_1\", \"result\": 30, \"errors\": []}" + + +class Test +{ + test:Int <- 10; + test2:Int <- 20; +} + + +class Main inherits Test +{ + test3:Int <- test + test2; + main():Int { + { + test3; + } + }; +} diff --git a/src/cool2/test/test_data/test_attr_dep_2.meta_test b/src/cool2/test/test_data/test_attr_dep_2.meta_test new file mode 100644 index 000000000..0625ae2c2 --- /dev/null +++ b/src/cool2/test/test_data/test_attr_dep_2.meta_test @@ -0,0 +1,18 @@ +"{\"name\": \"test_attr_dep_2\", \"result\": 30, \"errors\": []}" + +class Test +{ + test3:Int <- test + test2; + test2:Int <- 2*test; + test:Int <- 10; +} + + +class Main inherits Test +{ + main():Int { + { + test3; + } + }; +} \ No newline at end of file diff --git a/src/cool2/test/test_data/test_auto_1.meta_test b/src/cool2/test/test_data/test_auto_1.meta_test new file mode 100644 index 000000000..ab577727c --- /dev/null +++ b/src/cool2/test/test_data/test_auto_1.meta_test @@ -0,0 +1,11 @@ +"{\"name\": \"test_auto_1\", \"result\": 0, \"errors\": []}" + +class Main inherits IO { + main() : AUTO_TYPE { + let x : AUTO_TYPE <- 3 + 2 in { + case x of + y : Int => out_string("Ok"); + esac; + } + }; +} diff --git a/src/cool2/test/test_data/test_auto_2.meta_test b/src/cool2/test/test_data/test_auto_2.meta_test new file mode 100644 index 000000000..841b0e5c0 --- /dev/null +++ b/src/cool2/test/test_data/test_auto_2.meta_test @@ -0,0 +1,8 @@ +"{\"name\": \"test_auto_2\", \"result\": 11, \"errors\": []}" + +class Main inherits IO { + main():AUTO_TYPE { + succ(10) + }; + succ(n : Int) : AUTO_TYPE { n + 1 }; +} diff --git a/src/cool2/test/test_data/test_auto_3.meta_test b/src/cool2/test/test_data/test_auto_3.meta_test new file mode 100644 index 000000000..b5f63fa41 --- /dev/null +++ b/src/cool2/test/test_data/test_auto_3.meta_test @@ -0,0 +1,9 @@ +"{\"name\": \"test_auto_3\", \"result\": 11, \"errors\": []}" + +class Main inherits IO { + succ(n : AUTO_TYPE) : AUTO_TYPE { n + 1 }; + + main():AUTO_TYPE { + succ(10) + }; +} diff --git a/src/cool2/test/test_data/test_auto_4.meta_test b/src/cool2/test/test_data/test_auto_4.meta_test new file mode 100644 index 000000000..ea5e80d42 --- /dev/null +++ b/src/cool2/test/test_data/test_auto_4.meta_test @@ -0,0 +1,15 @@ +"{\"name\": \"test_auto_4\", \"result\": 4, \"errors\": []}" + +class Main inherits IO { + main():AUTO_TYPE { + ackermann(0,3) + }; + + ackermann(m : AUTO_TYPE, n: AUTO_TYPE) : AUTO_TYPE { + if (m=0) then n+1 else + if (n=0) then ackermann(m-1, 1) else + ackermann(m-1, ackermann(m, n-1)) + fi + fi + }; +} diff --git a/src/cool2/test/test_data/test_auto_5.meta_test b/src/cool2/test/test_data/test_auto_5.meta_test new file mode 100644 index 000000000..5ebee911b --- /dev/null +++ b/src/cool2/test/test_data/test_auto_5.meta_test @@ -0,0 +1,19 @@ +"{\"name\": \"test_auto_5\", \"result\": 1, \"errors\": []}" + +class Main inherits Object { + main():AUTO_TYPE { + f(1,1) + }; + + f(a: AUTO_TYPE, b: AUTO_TYPE) : AUTO_TYPE { + if (a=1) then b else + g(a + 1, b/2) + fi + }; + + g(a: AUTO_TYPE, b: AUTO_TYPE) : AUTO_TYPE { + if (b=1) then a else + f(a/2, b+1) + fi + }; +} diff --git a/src/cool2/test/test_data/test_auto_conditional.meta_test b/src/cool2/test/test_data/test_auto_conditional.meta_test new file mode 100644 index 000000000..2a0432b51 --- /dev/null +++ b/src/cool2/test/test_data/test_auto_conditional.meta_test @@ -0,0 +1,104 @@ +"{\"name\": \"test_auto_conditional\", \"result\": 1, \"errors\": []}" + +class Base { + base():AUTO_TYPE { + self + }; +} + +class OtraBase { + otraBase():AUTO_TYPE { + self + }; +} + +class ConcBase1 inherits Base { + +} + +class ConcBase2 inherits OtraBase { + +} + +class Main { + a:AUTO_TYPE; + main() : AUTO_TYPE + { + { + a:AUTO_TYPE <- + if true then + new OtraBase + else + new ConcBase2 + fi; + b:AUTO_TYPE <- + if true then + new ConcBase2 + else + new ConcBase2 + fi; + c:AUTO_TYPE <- + if true then + new ConcBase1 + else + new ConcBase2 + fi; + d:AUTO_TYPE <- + if true then + ret1() + else + ret2() + fi; + e:AUTO_TYPE <- + if true then + ret1(new ConcBase1) + else + ret2(new ConcBase2) + fi; + 1; + } + }; + + ret1():AUTO_TYPE + { + c:AUTO_TYPE <- + if true then + new ConcBase1 + else + new ConcBase2 + fi + }; + + ret2():AUTO_TYPE + { + c:AUTO_TYPE <- + if true then + new OtraBase + else + new ConcBase2 + fi + }; + + ret1(param:AUTO_TYPE):AUTO_TYPE + { + { + c:AUTO_TYPE <- param.base(); + a:AUTO_TYPE <- case param of + b:Base => if true then b else param fi; + b:OtraBase => if false then b else param fi; + esac; + } + }; + + ret2(param:AUTO_TYPE):AUTO_TYPE + { + { + c:AUTO_TYPE <- param.otraBase(); + a:AUTO_TYPE <- case param of + b:Base => if true then b else b fi; + b:ConcBase1 => if true then b else b fi; + esac; + } + }; + +} \ No newline at end of file diff --git a/src/cool2/test/test_data/test_auto_defaults.meta_test b/src/cool2/test/test_data/test_auto_defaults.meta_test new file mode 100644 index 000000000..30d27848b --- /dev/null +++ b/src/cool2/test/test_data/test_auto_defaults.meta_test @@ -0,0 +1,11 @@ +"{\"name\": \"test_auto_defaults\", \"result\": 10, \"errors\": []}" + +class Main { + + a:AUTO_TYPE; + + main() : Int + { + let b:AUTO_TYPE in a + b + 10 + }; +} \ No newline at end of file diff --git a/src/cool2/test/test_data/test_auto_undecidable.meta_test b/src/cool2/test/test_data/test_auto_undecidable.meta_test new file mode 100644 index 000000000..b394f1cce --- /dev/null +++ b/src/cool2/test/test_data/test_auto_undecidable.meta_test @@ -0,0 +1,23 @@ +"{\"name\": \"test_auto_undecidable\", \"result\": 10, \"errors\": [\"Cant infer type in given context. Line:18 Column:7\"]}" + +class A{ + m1():Int{ + 10 + }; +} + +class B{ + m1():Int{ + 20 + }; +} + +class Main inherits IO { + main() : Int + { + 10 + }; + m(a:AUTO_TYPE):Int{ + a.m1() + }; +} \ No newline at end of file diff --git a/src/cool2/test/test_data/test_case.meta_test b/src/cool2/test/test_data/test_case.meta_test new file mode 100644 index 000000000..4d0d6390b --- /dev/null +++ b/src/cool2/test/test_data/test_case.meta_test @@ -0,0 +1,14 @@ +"{\"name\": \"test_case\", \"result\": 50, \"errors\": []}" + +class Main inherits Object { + main():Object { + { + a:Object <- case 20+40 of + -- a:Bool => if a then true else false fi; + b:Int => if b = 60 then 50 else 0 fi; + c:Object => c; + esac; + a; + } + }; +} diff --git a/src/cool2/test/test_data/test_case_no_type.meta_test b/src/cool2/test/test_data/test_case_no_type.meta_test new file mode 100644 index 000000000..d52ecd831 --- /dev/null +++ b/src/cool2/test/test_data/test_case_no_type.meta_test @@ -0,0 +1,12 @@ +"{\"name\": \"test_case_no_type\", \"result\": 50, \"errors\": [\"No branch can be selected. Type Int is not conformed by any of the branches. Line: 4 Column: 28\"]}" + +class Main inherits Object { + main():Object { + { + a:Object <- case 20+40 of + a:Bool => if a then true else false fi; + esac; + a; + } + }; +} diff --git a/src/cool2/test/test_data/test_case_repeated_type.meta_test b/src/cool2/test/test_data/test_case_repeated_type.meta_test new file mode 100644 index 000000000..af02fcf29 --- /dev/null +++ b/src/cool2/test/test_data/test_case_repeated_type.meta_test @@ -0,0 +1,15 @@ +"{\"name\": \"test_case_repeated_type\", \"result\": 50, \"errors\": [\"Type Int already present in case expression. Line:7 Column:12\"]}" + +class Main inherits Object { + main():Object { + { + a:Object <- case 20+40 of + -- a:Bool => if a then true else false fi; + b:Int => if b = 60 then 50 else 0 fi; + b:Int => if b = 60 then 40 else 0 fi; + c:Object => c; + esac; + a; + } + }; +} diff --git a/src/cool2/test/test_data/test_case_void_eval.meta_test b/src/cool2/test/test_data/test_case_void_eval.meta_test new file mode 100644 index 000000000..9b6fd945d --- /dev/null +++ b/src/cool2/test/test_data/test_case_void_eval.meta_test @@ -0,0 +1,14 @@ +"{\"name\": \"test_case_void_eval\", \"result\": 50, \"errors\": [\"Void is not a valid type. Line: 4 Column: 27\"]}" + +class Main inherits Object { + main():Object { + { + a:Object <- case while false loop 1 pool of + -- a:Bool => if a then true else false fi; + b:Int => if b = 60 then 50 else 0 fi; + c:Object => c; + esac; + a; + } + }; +} diff --git a/src/cool2/test/test_data/test_comments.meta_test b/src/cool2/test/test_data/test_comments.meta_test new file mode 100644 index 000000000..ce1749df3 --- /dev/null +++ b/src/cool2/test/test_data/test_comments.meta_test @@ -0,0 +1,12 @@ +"{\"name\": \"test_comments\", \"result\": 0, \"errors\": []}" + +class Main inherits Object { + (* this is a comment *) + -- This is also a comment + (* This is a nested (* very nested comment *) *) + (* -- Double comment? *) + -- Double (* comment again *) + main():Object { + 0 + }; +} diff --git a/src/cool2/test/test_data/test_conditional.meta_test b/src/cool2/test/test_data/test_conditional.meta_test new file mode 100644 index 000000000..521a7b061 --- /dev/null +++ b/src/cool2/test/test_data/test_conditional.meta_test @@ -0,0 +1,24 @@ +"{\"name\": \"test_conditional\", \"result\": 3, \"errors\": []}" + + + class A { } + class B inherits A { } + class C inherits B { } + class D inherits B { } + class E inherits A { } + + class Main inherits IO { + + main() : Int { + { + d:Object <- if true then new E else new C fi; + e:B <- if true then new C else new D fi; + a:Int <- if true then 1 else 0 fi; + b:Int <- if false then 0 else 1 fi; + c:Int <- if if 1 > 2 then 2 > 1 else 2 > 1 fi then 1 else 0 fi; + out_string(d.type_name()).out_string(e.type_name()); + a+b+c; + + } + }; + } diff --git a/src/cool2/test/test_data/test_default.meta_test b/src/cool2/test/test_data/test_default.meta_test new file mode 100644 index 000000000..f4735ece0 --- /dev/null +++ b/src/cool2/test/test_data/test_default.meta_test @@ -0,0 +1,33 @@ +"{\"name\": \"test_default\", \"result\": true, \"errors\": []}" + +class B {} + +class Main inherits IO { + a:Int; + s:String; + b:Bool; + o:Object; + o2:B; + + main():Bool { + { + if a = 0 then + if s = "" then + if not b then + if o = o2 then + true + else + false + fi + else + false + fi + else + false + fi + else + false + fi; + } + }; +} diff --git a/src/cool2/test/test_data/test_entry_point_1.meta_test b/src/cool2/test/test_data/test_entry_point_1.meta_test new file mode 100644 index 000000000..6479fc095 --- /dev/null +++ b/src/cool2/test/test_data/test_entry_point_1.meta_test @@ -0,0 +1,12 @@ +"{\"name\": \"test_entry_point_1\", \"result\": 30, \"errors\": [\"Method 'main' is not defined locally in 'Main' with 0 params. The program must define a 'main' method with no parameters in the 'Main' class\"]}" + + +class Main{ + + main(a:Int) : Int { + { + 30; + } + }; + +} \ No newline at end of file diff --git a/src/cool2/test/test_data/test_entry_point_2.meta_test b/src/cool2/test/test_data/test_entry_point_2.meta_test new file mode 100644 index 000000000..c1eb4b197 --- /dev/null +++ b/src/cool2/test/test_data/test_entry_point_2.meta_test @@ -0,0 +1,13 @@ +"{\"name\": \"test_entry_point_2\", \"result\": 30, \"errors\": [\"Method 'main' is not defined locally in 'Main' with 0 params. The program must define a 'main' method with no parameters in the 'Main' class\"]}" + +class M{ + main() : Int { + { + 30; + } + }; +} + +class Main inherits M{ + +} \ No newline at end of file diff --git a/src/cool2/test/test_data/test_entry_point_3.meta_test b/src/cool2/test/test_data/test_entry_point_3.meta_test new file mode 100644 index 000000000..7ec2a8731 --- /dev/null +++ b/src/cool2/test/test_data/test_entry_point_3.meta_test @@ -0,0 +1,12 @@ +"{\"name\": \"test_entry_point_3\", \"result\": 30, \"errors\": [\"The program must define a 'Main' type\"]}" + + +class NoMain{ + + main() : Int { + { + 30; + } + }; + +} \ No newline at end of file diff --git a/src/cool2/test/test_data/test_equality.meta_test b/src/cool2/test/test_data/test_equality.meta_test new file mode 100644 index 000000000..b191a6a22 --- /dev/null +++ b/src/cool2/test/test_data/test_equality.meta_test @@ -0,0 +1,28 @@ +"{\"name\": \"test_equality\", \"result\": true, \"errors\": []}" + +class A +{ + +} +class B +{ + +} + +class Main +{ + cClass:A; + dClass:B; + + main():Bool { + { + aClass:A <- new A; + bClass:A <- new A; + a:Bool <- aClass = aClass; + b:Bool <- not aClass = bClass; + c:Bool <- cClass = dClass; + + (c = (a = b)) = true; + } + }; +} diff --git a/src/cool2/test/test_data/test_escape_line.meta_test b/src/cool2/test/test_data/test_escape_line.meta_test new file mode 100644 index 000000000..c6541ca09 --- /dev/null +++ b/src/cool2/test/test_data/test_escape_line.meta_test @@ -0,0 +1,12 @@ +"{\"name\": \"test_escape_line\", \"result\": \"Hello People\", \"errors\": []}" + + +class Main\ +{ + main():String { + { + "Hello \ + People"; + } + }; +} diff --git a/src/cool2/test/test_data/test_in_out.meta_test b/src/cool2/test/test_data/test_in_out.meta_test new file mode 100644 index 000000000..5933bc05c --- /dev/null +++ b/src/cool2/test/test_data/test_in_out.meta_test @@ -0,0 +1,23 @@ +"{\"name\": \"test_in_out\", \"result\": \"hello\", \"errors\": []}" + +class Main inherits IO { + a:Int; + + set(v:Int):Int { + a <- v + }; + + get():Int { + a + }; + main():String { + { + out_string("Write a number"); + a:Int <- in_int(); + out_string("Write a string"); + b:String <- in_string(); + out_int(a).out_string(b); + b; + } + }; +} diff --git a/src/cool2/test/test_data/test_instances.meta_test b/src/cool2/test/test_data/test_instances.meta_test new file mode 100644 index 000000000..51783d31c --- /dev/null +++ b/src/cool2/test/test_data/test_instances.meta_test @@ -0,0 +1,21 @@ +"{\"name\": \"test_instances\", \"result\": 30, \"errors\": []}" + +class Main { + a:Int; + + set(v:Int):Int { + a <- v + }; + + get():Int { + a + }; + main():Int { + { + a <- 10; + b:Main <- copy(); + a <- 20; + b.get() + get(); + } + }; +} diff --git a/src/cool2/test/test_data/test_isvoid.meta_test b/src/cool2/test/test_data/test_isvoid.meta_test new file mode 100644 index 000000000..83ad115b5 --- /dev/null +++ b/src/cool2/test/test_data/test_isvoid.meta_test @@ -0,0 +1,15 @@ +"{\"name\": \"test_isvoid\", \"result\": true, \"errors\": []}" + +class Main inherits IO { + a:Int; + s:String; + o:Object; + main():Bool { + { + a:Int <- 10; + b:Bool <- isvoid a; + c:Bool <- isvoid o; + c; + } + }; +} diff --git a/src/cool2/test/test_data/test_let.meta_test b/src/cool2/test/test_data/test_let.meta_test new file mode 100644 index 000000000..3a2061d3e --- /dev/null +++ b/src/cool2/test/test_data/test_let.meta_test @@ -0,0 +1,19 @@ +"{\"name\": \"test_let\", \"result\": 20, \"errors\": []}" + +class Main +{ + b:Bool; + main() : Int { + { + a:Int <- let a:Bool <- true, a:Int <- if a then 10 else 20 fi in a; + c:Int <- let a:SELF_TYPE <- change(), b:Int <- if b then 10 else 20 fi in b; + a + c; + } + }; + change():SELF_TYPE { + { + b <- not b; + self; + } + }; +} diff --git a/src/cool2/test/test_data/test_other.meta_test b/src/cool2/test/test_data/test_other.meta_test new file mode 100644 index 000000000..90fd76326 --- /dev/null +++ b/src/cool2/test/test_data/test_other.meta_test @@ -0,0 +1,21 @@ +"{\"name\": \"test_other\", \"result\": null, \"errors\": [\"Method \\\"a\\\" already defined in \\\"A\\\" with a different signature. Line:10 Column:5\"]}" + +class A { + a:Int; + a(a:Object):Object { + a + }; +} + +class B inherits A { + -- a:Int; + a(self:Int):Object { + a + }; +} + +class Main inherits IO { + main():B { + new B + }; +} diff --git a/src/cool2/test/test_data/test_recursive.meta_test b/src/cool2/test/test_data/test_recursive.meta_test new file mode 100644 index 000000000..bc6c50dd1 --- /dev/null +++ b/src/cool2/test/test_data/test_recursive.meta_test @@ -0,0 +1,11 @@ +"{\"name\": \"test_recursive\", \"result\": 21, \"errors\": []}" + +class Main inherits Object { + main():Int { + fibo(7) + }; + + fibo(n:Int):Int { + if n > 1 then fibo(n-1) + fibo(n-2) else 1 fi + }; +} diff --git a/src/cool2/test/test_data/test_substr_1.meta_test b/src/cool2/test/test_data/test_substr_1.meta_test new file mode 100644 index 000000000..c5166d770 --- /dev/null +++ b/src/cool2/test/test_data/test_substr_1.meta_test @@ -0,0 +1,11 @@ +"{\"name\": \"test_substr_1\", \"result\": \"01234567890123456789\", \"errors\": []}" + +class Main +{ + main():String { + { + s:String <- "0123456789"; + s.substr(0,10).concat(s.substr(0,5)).concat(s.substr(5,5)); + } + }; +} diff --git a/src/cool2/test/test_data/test_substr_2.meta_test b/src/cool2/test/test_data/test_substr_2.meta_test new file mode 100644 index 000000000..ac12dd27d --- /dev/null +++ b/src/cool2/test/test_data/test_substr_2.meta_test @@ -0,0 +1,11 @@ +"{\"name\": \"test_substr_2\", \"result\": \"\", \"errors\": [\"Index out of range in substr, word:'0123456789' substr begin:0 substr length:-1. Line:20 Column:10\"]}" + +class Main +{ + main():String { + { + s:String <- "0123456789"; + s.substr(0,~1); + } + }; +} diff --git a/src/cool2/test/test_data/test_substr_3.meta_test b/src/cool2/test/test_data/test_substr_3.meta_test new file mode 100644 index 000000000..2e4aa0f16 --- /dev/null +++ b/src/cool2/test/test_data/test_substr_3.meta_test @@ -0,0 +1,11 @@ +"{\"name\": \"test_substr_3\", \"result\": \"\", \"errors\": [\"Index out of range in substr, word:'0123456789' substr begin:0 substr length:11. Line:20 Column:10\"]}" + +class Main +{ + main():String { + { + s:String <- "0123456789"; + s.substr(0,11); + } + }; +} diff --git a/src/cool2/test/test_data/test_void_dispatch.meta_test b/src/cool2/test/test_data/test_void_dispatch.meta_test new file mode 100644 index 000000000..da5fe3fc7 --- /dev/null +++ b/src/cool2/test/test_data/test_void_dispatch.meta_test @@ -0,0 +1,12 @@ +"{\"name\": \"test_void_dispatch\", \"result\": 0, \"errors\": [\"Can't dispatch a Void value. Line:8 Column:19\"]}" + + +class Main +{ + b:Object; + + main():String + { + b.type_name() + }; +} \ No newline at end of file diff --git a/src/cool2/test/test_data/test_while.meta_test b/src/cool2/test/test_data/test_while.meta_test new file mode 100644 index 000000000..34441e8e8 --- /dev/null +++ b/src/cool2/test/test_data/test_while.meta_test @@ -0,0 +1,13 @@ +"{\"name\": \"test_while\", \"result\": 10, \"errors\": []}" + +class Main inherits IO { + main():Int { + { + counter:Int <- 0; + a:Object <- while counter < 10 loop + counter <- counter + 1 + pool; + counter; + } + }; +} diff --git a/src/cool2/test/test_data/test_zero_division.meta_test b/src/cool2/test/test_data/test_zero_division.meta_test new file mode 100644 index 000000000..f37582965 --- /dev/null +++ b/src/cool2/test/test_data/test_zero_division.meta_test @@ -0,0 +1,10 @@ +"{\"name\": \"test_zero_division\", \"result\": 0, \"errors\": [\"Divison by 0 not defined. Line:5 Column:15\"]}" + +class Main +{ + main():Int { + { + 10/0; + } + }; +} diff --git a/src/cool2/test/test_reconstruction/test_SELF_TYPE.cl b/src/cool2/test/test_reconstruction/test_SELF_TYPE.cl new file mode 100644 index 000000000..609a3f8f2 --- /dev/null +++ b/src/cool2/test/test_reconstruction/test_SELF_TYPE.cl @@ -0,0 +1,73 @@ +class A +{ + a:SELF_TYPE; + get(): SELF_TYPE + { + self + }; + get2(): SELF_TYPE + { + new SELF_TYPE + }; + get3(): SELF_TYPE + { + { + a <- + new SELF_TYPE; + a; + } + }; +} + + +class B inherits A +{ + get4(): SELF_TYPE + { + self + .get3( + ) + }; +} + + +class Main inherits IO +{ + main(): String + { + { + a:A <- + new A; + b:B <- + new B; + b + .get( + ) + .type_name( + ) + .concat( + b + .get2( + ) + .type_name( + ) + .concat( + b + .get3( + ) + .type_name( + ) + ) + .concat( + b + .get4( + ) + .type_name( + ) + ) + ); + } + }; +} + + diff --git a/src/cool2/test/test_reconstruction/test_at.cl b/src/cool2/test/test_reconstruction/test_at.cl new file mode 100644 index 000000000..cfef6f95f --- /dev/null +++ b/src/cool2/test/test_reconstruction/test_at.cl @@ -0,0 +1,46 @@ +class A +{ + name(): String + { + "class A " + .concat( + self + .name2( + ) + ) + }; + name2(): String + { + "class A" + }; +} + + +class B inherits A +{ + name(): String + { + "class B" + }; + name2(): String + { + "class B" + }; +} + + +class Main inherits IO +{ + main(): String + { + { + b:B <- + new B; + b + @A.name( + ); + } + }; +} + + diff --git a/src/cool2/test/test_reconstruction/test_attr_dep_1.cl b/src/cool2/test/test_reconstruction/test_attr_dep_1.cl new file mode 100644 index 000000000..d9b99bb88 --- /dev/null +++ b/src/cool2/test/test_reconstruction/test_attr_dep_1.cl @@ -0,0 +1,19 @@ +class Test +{ + test:Int <- 10; + test2:Int <- 20; +} + + +class Main inherits Test +{ + test3:Int <- (test + test2); + main(): Int + { + { + test3; + } + }; +} + + diff --git a/src/cool2/test/test_reconstruction/test_attr_dep_2.cl b/src/cool2/test/test_reconstruction/test_attr_dep_2.cl new file mode 100644 index 000000000..78dea4533 --- /dev/null +++ b/src/cool2/test/test_reconstruction/test_attr_dep_2.cl @@ -0,0 +1,19 @@ +class Test +{ + test3:Int <- (test + test2); + test2:Int <- (2 * test); + test:Int <- 10; +} + + +class Main inherits Test +{ + main(): Int + { + { + test3; + } + }; +} + + diff --git a/src/cool2/test/test_reconstruction/test_auto_1.cl b/src/cool2/test/test_reconstruction/test_auto_1.cl new file mode 100644 index 000000000..0446f20db --- /dev/null +++ b/src/cool2/test/test_reconstruction/test_auto_1.cl @@ -0,0 +1,23 @@ +class Main inherits IO +{ + main(): Main + { + let + x:Int <- + (3 + 2) + in + { + case + x + of + y:Int => + self + .out_string( + "Ok" + ); + esac; + } + }; +} + + diff --git a/src/cool2/test/test_reconstruction/test_auto_2.cl b/src/cool2/test/test_reconstruction/test_auto_2.cl new file mode 100644 index 000000000..87f525ed9 --- /dev/null +++ b/src/cool2/test/test_reconstruction/test_auto_2.cl @@ -0,0 +1,16 @@ +class Main inherits IO +{ + main(): Int + { + self + .succ( + 10 + ) + }; + succ(n:Int): Int + { + (n + 1) + }; +} + + diff --git a/src/cool2/test/test_reconstruction/test_auto_3.cl b/src/cool2/test/test_reconstruction/test_auto_3.cl new file mode 100644 index 000000000..aab18e038 --- /dev/null +++ b/src/cool2/test/test_reconstruction/test_auto_3.cl @@ -0,0 +1,16 @@ +class Main inherits IO +{ + succ(n:Int): Int + { + (n + 1) + }; + main(): Int + { + self + .succ( + 10 + ) + }; +} + + diff --git a/src/cool2/test/test_reconstruction/test_auto_4.cl b/src/cool2/test/test_reconstruction/test_auto_4.cl new file mode 100644 index 000000000..d0b0c3d43 --- /dev/null +++ b/src/cool2/test/test_reconstruction/test_auto_4.cl @@ -0,0 +1,41 @@ +class Main inherits IO +{ + main(): Int + { + self + .ackermann( + 0, + 3 + ) + }; + ackermann(m:Int, n:Int): Int + { + if + (m = 0) + then + (n + 1) + else + if + (n = 0) + then + self + .ackermann( + (m - 1), + 1 + ) + else + self + .ackermann( + (m - 1), + self + .ackermann( + m, + (n - 1) + ) + ) + fi + fi + }; +} + + diff --git a/src/cool2/test/test_reconstruction/test_auto_5.cl b/src/cool2/test/test_reconstruction/test_auto_5.cl new file mode 100644 index 000000000..ba5179fea --- /dev/null +++ b/src/cool2/test/test_reconstruction/test_auto_5.cl @@ -0,0 +1,41 @@ +class Main +{ + main(): Object + { + self + .f( + 1, + 1 + ) + }; + f(a:Int, b:Int): Object + { + if + (a = 1) + then + b + else + self + .g( + (a + 1), + (b / 2) + ) + fi + }; + g(a:Int, b:Int): Object + { + if + (b = 1) + then + a + else + self + .f( + (a / 2), + (b + 1) + ) + fi + }; +} + + diff --git a/src/cool2/test/test_reconstruction/test_auto_conditional.cl b/src/cool2/test/test_reconstruction/test_auto_conditional.cl new file mode 100644 index 000000000..768dd49d5 --- /dev/null +++ b/src/cool2/test/test_reconstruction/test_auto_conditional.cl @@ -0,0 +1,172 @@ +class Base +{ + base(): Base + { + self + }; +} + + +class OtraBase +{ + otraBase(): OtraBase + { + self + }; +} + + +class ConcBase1 inherits Base +{ +} + + +class ConcBase2 inherits OtraBase +{ +} + + +class Main +{ + a:Object; + main(): Int + { + { + a:OtraBase <- + if + true + then + new OtraBase + else + new ConcBase2 + fi; + b:ConcBase2 <- + if + true + then + new ConcBase2 + else + new ConcBase2 + fi; + c:Object <- + if + true + then + new ConcBase1 + else + new ConcBase2 + fi; + d:Object <- + if + true + then + self + .ret1( + ) + else + self + .ret2( + ) + fi; + e:Object <- + if + true + then + self + .ret1( + new ConcBase1 + ) + else + self + .ret2( + new ConcBase2 + ) + fi; + 1; + } + }; + ret1(): Object + { + c:Object <- + if + true + then + new ConcBase1 + else + new ConcBase2 + fi + }; + ret2(): OtraBase + { + c:OtraBase <- + if + true + then + new OtraBase + else + new ConcBase2 + fi + }; + ret1(param:Base): Object + { + { + c:Base <- + param + .base( + ); + a:Object <- + case + param + of + b:Base => + if + true + then + b + else + param + fi; + b:OtraBase => + if + false + then + b + else + param + fi; + esac; + } + }; + ret2(param:OtraBase): Base + { + { + c:OtraBase <- + param + .otraBase( + ); + a:Base <- + case + param + of + b:Base => + if + true + then + b + else + b + fi; + b:ConcBase1 => + if + true + then + b + else + b + fi; + esac; + } + }; +} + + diff --git a/src/cool2/test/test_reconstruction/test_auto_defaults.cl b/src/cool2/test/test_reconstruction/test_auto_defaults.cl new file mode 100644 index 000000000..b0dfda219 --- /dev/null +++ b/src/cool2/test/test_reconstruction/test_auto_defaults.cl @@ -0,0 +1,14 @@ +class Main +{ + a:Int; + main(): Int + { + let + b:Int <- + 0 + in + ((a + b) + 10) + }; +} + + diff --git a/src/cool2/test/test_reconstruction/test_auto_undecidable.cl b/src/cool2/test/test_reconstruction/test_auto_undecidable.cl new file mode 100644 index 000000000..3d0d950e9 --- /dev/null +++ b/src/cool2/test/test_reconstruction/test_auto_undecidable.cl @@ -0,0 +1,33 @@ +class A +{ + m1(): Int + { + 10 + }; +} + + +class B +{ + m1(): Int + { + 20 + }; +} + + +class Main inherits IO +{ + main(): Int + { + 10 + }; + m(a:AUTO_TYPE): Int + { + a + .m1( + ) + }; +} + + diff --git a/src/cool2/test/test_reconstruction/test_case.cl b/src/cool2/test/test_reconstruction/test_case.cl new file mode 100644 index 000000000..d4e22a245 --- /dev/null +++ b/src/cool2/test/test_reconstruction/test_case.cl @@ -0,0 +1,26 @@ +class Main +{ + main(): Object + { + { + a:Object <- + case + (20 + 40) + of + b:Int => + if + (b = 60) + then + 50 + else + 0 + fi; + c:Object => + c; + esac; + a; + } + }; +} + + diff --git a/src/cool2/test/test_reconstruction/test_case_no_type.cl b/src/cool2/test/test_reconstruction/test_case_no_type.cl new file mode 100644 index 000000000..2970e7d95 --- /dev/null +++ b/src/cool2/test/test_reconstruction/test_case_no_type.cl @@ -0,0 +1,24 @@ +class Main +{ + main(): Object + { + { + a:Object <- + case + (20 + 40) + of + a:Bool => + if + a + then + true + else + false + fi; + esac; + a; + } + }; +} + + diff --git a/src/cool2/test/test_reconstruction/test_case_repeated_type.cl b/src/cool2/test/test_reconstruction/test_case_repeated_type.cl new file mode 100644 index 000000000..5ae3dc3c0 --- /dev/null +++ b/src/cool2/test/test_reconstruction/test_case_repeated_type.cl @@ -0,0 +1,34 @@ +class Main +{ + main(): Object + { + { + a:Object <- + case + (20 + 40) + of + b:Int => + if + (b = 60) + then + 50 + else + 0 + fi; + b:Int => + if + (b = 60) + then + 40 + else + 0 + fi; + c:Object => + c; + esac; + a; + } + }; +} + + diff --git a/src/cool2/test/test_reconstruction/test_case_void_eval.cl b/src/cool2/test/test_reconstruction/test_case_void_eval.cl new file mode 100644 index 000000000..c5797b263 --- /dev/null +++ b/src/cool2/test/test_reconstruction/test_case_void_eval.cl @@ -0,0 +1,30 @@ +class Main +{ + main(): Object + { + { + a:Object <- + case + while + false + loop + 1 + pool + of + b:Int => + if + (b = 60) + then + 50 + else + 0 + fi; + c:Object => + c; + esac; + a; + } + }; +} + + diff --git a/src/cool2/test/test_reconstruction/test_comments.cl b/src/cool2/test/test_reconstruction/test_comments.cl new file mode 100644 index 000000000..098ac7a18 --- /dev/null +++ b/src/cool2/test/test_reconstruction/test_comments.cl @@ -0,0 +1,9 @@ +class Main +{ + main(): Object + { + 0 + }; +} + + diff --git a/src/cool2/test/test_reconstruction/test_conditional.cl b/src/cool2/test/test_reconstruction/test_conditional.cl new file mode 100644 index 000000000..a82e2e576 --- /dev/null +++ b/src/cool2/test/test_reconstruction/test_conditional.cl @@ -0,0 +1,93 @@ +class A +{ +} + + +class B inherits A +{ +} + + +class C inherits B +{ +} + + +class D inherits B +{ +} + + +class E inherits A +{ +} + + +class Main inherits IO +{ + main(): Int + { + { + d:Object <- + if + true + then + new E + else + new C + fi; + e:B <- + if + true + then + new C + else + new D + fi; + a:Int <- + if + true + then + 1 + else + 0 + fi; + b:Int <- + if + false + then + 0 + else + 1 + fi; + c:Int <- + if + if + (1 > 2) + then + (2 > 1) + else + (2 > 1) + fi + then + 1 + else + 0 + fi; + self + .out_string( + d + .type_name( + ) + ) + .out_string( + e + .type_name( + ) + ); + ((a + b) + c); + } + }; +} + + diff --git a/src/cool2/test/test_reconstruction/test_default.cl b/src/cool2/test/test_reconstruction/test_default.cl new file mode 100644 index 000000000..d8a5b1f0a --- /dev/null +++ b/src/cool2/test/test_reconstruction/test_default.cl @@ -0,0 +1,45 @@ +class B +{ +} + + +class Main inherits IO +{ + a:Int; + s:String; + b:Bool; + o:Object; + o2:B; + main(): Bool + { + { + if + (a = 0) + then + if + (s = "") + then + if + not b + then + if + (o = o2) + then + true + else + false + fi + else + false + fi + else + false + fi + else + false + fi; + } + }; +} + + diff --git a/src/cool2/test/test_reconstruction/test_entry_point_1.cl b/src/cool2/test/test_reconstruction/test_entry_point_1.cl new file mode 100644 index 000000000..e4d564db5 --- /dev/null +++ b/src/cool2/test/test_reconstruction/test_entry_point_1.cl @@ -0,0 +1,11 @@ +class Main +{ + main(a:Int): Int + { + { + 30; + } + }; +} + + diff --git a/src/cool2/test/test_reconstruction/test_entry_point_2.cl b/src/cool2/test/test_reconstruction/test_entry_point_2.cl new file mode 100644 index 000000000..3ca834180 --- /dev/null +++ b/src/cool2/test/test_reconstruction/test_entry_point_2.cl @@ -0,0 +1,16 @@ +class M +{ + main(): Int + { + { + 30; + } + }; +} + + +class Main inherits M +{ +} + + diff --git a/src/cool2/test/test_reconstruction/test_entry_point_3.cl b/src/cool2/test/test_reconstruction/test_entry_point_3.cl new file mode 100644 index 000000000..0a1f6c54e --- /dev/null +++ b/src/cool2/test/test_reconstruction/test_entry_point_3.cl @@ -0,0 +1,11 @@ +class NoMain +{ + main(): Int + { + { + 30; + } + }; +} + + diff --git a/src/cool2/test/test_reconstruction/test_equality.cl b/src/cool2/test/test_reconstruction/test_equality.cl new file mode 100644 index 000000000..a8f1bcde0 --- /dev/null +++ b/src/cool2/test/test_reconstruction/test_equality.cl @@ -0,0 +1,33 @@ +class A +{ +} + + +class B +{ +} + + +class Main +{ + cClass:A; + dClass:B; + main(): Bool + { + { + aClass:A <- + new A; + bClass:A <- + new A; + a:Bool <- + (aClass = aClass); + b:Bool <- + not (aClass = bClass); + c:Bool <- + (cClass = dClass); + ((c = (a = b)) = true); + } + }; +} + + diff --git a/src/cool2/test/test_reconstruction/test_escape_line.cl b/src/cool2/test/test_reconstruction/test_escape_line.cl new file mode 100644 index 000000000..1e952ba9f --- /dev/null +++ b/src/cool2/test/test_reconstruction/test_escape_line.cl @@ -0,0 +1,11 @@ +class Main +{ + main(): String + { + { + "Hello People"; + } + }; +} + + diff --git a/src/cool2/test/test_reconstruction/test_instances.cl b/src/cool2/test/test_reconstruction/test_instances.cl new file mode 100644 index 000000000..b228613c0 --- /dev/null +++ b/src/cool2/test/test_reconstruction/test_instances.cl @@ -0,0 +1,35 @@ +class Main +{ + a:Int; + set(v:Int): Int + { + a <- + v + }; + get(): Int + { + a + }; + main(): Int + { + { + a <- + 10; + b:Main <- + self + .copy( + ); + a <- + 20; + ( b + .get( + ) + + self + .get( + ) +); + } + }; +} + + diff --git a/src/cool2/test/test_reconstruction/test_isvoid.cl b/src/cool2/test/test_reconstruction/test_isvoid.cl new file mode 100644 index 000000000..52758c9e0 --- /dev/null +++ b/src/cool2/test/test_reconstruction/test_isvoid.cl @@ -0,0 +1,20 @@ +class Main inherits IO +{ + a:Int; + s:String; + o:Object; + main(): Bool + { + { + a:Int <- + 10; + b:Bool <- + isvoid a; + c:Bool <- + isvoid o; + c; + } + }; +} + + diff --git a/src/cool2/test/test_reconstruction/test_let.cl b/src/cool2/test/test_reconstruction/test_let.cl new file mode 100644 index 000000000..22c92ecc6 --- /dev/null +++ b/src/cool2/test/test_reconstruction/test_let.cl @@ -0,0 +1,50 @@ +class Main +{ + b:Bool; + main(): Int + { + { + a:Int <- + let + a:Bool <- + true, + a:Int <- + if + a + then + 10 + else + 20 + fi + in + a; + c:Int <- + let + a:SELF_TYPE <- + self + .change( + ), + b:Int <- + if + b + then + 10 + else + 20 + fi + in + b; + (a + c); + } + }; + change(): SELF_TYPE + { + { + b <- + not b; + self; + } + }; +} + + diff --git a/src/cool2/test/test_reconstruction/test_other.cl b/src/cool2/test/test_reconstruction/test_other.cl new file mode 100644 index 000000000..eeca2e9b8 --- /dev/null +++ b/src/cool2/test/test_reconstruction/test_other.cl @@ -0,0 +1,28 @@ +class A +{ + a:Int; + a(a:Object): Object + { + a + }; +} + + +class B inherits A +{ + a(self:Int): Object + { + a + }; +} + + +class Main inherits IO +{ + main(): B + { + new B + }; +} + + diff --git a/src/cool2/test/test_reconstruction/test_recursive.cl b/src/cool2/test/test_reconstruction/test_recursive.cl new file mode 100644 index 000000000..531d729bc --- /dev/null +++ b/src/cool2/test/test_reconstruction/test_recursive.cl @@ -0,0 +1,30 @@ +class Main +{ + main(): Int + { + self + .fibo( + 7 + ) + }; + fibo(n:Int): Int + { + if + (n > 1) + then + ( self + .fibo( + (n - 1) + ) + + self + .fibo( + (n - 2) + ) +) + else + 1 + fi + }; +} + + diff --git a/src/cool2/test/test_reconstruction/test_substr_1.cl b/src/cool2/test/test_reconstruction/test_substr_1.cl new file mode 100644 index 000000000..ccd6f75e2 --- /dev/null +++ b/src/cool2/test/test_reconstruction/test_substr_1.cl @@ -0,0 +1,31 @@ +class Main +{ + main(): String + { + { + s:String <- + "0123456789"; + s + .substr( + 0, + 10 + ) + .concat( + s + .substr( + 0, + 5 + ) + ) + .concat( + s + .substr( + 5, + 5 + ) + ); + } + }; +} + + diff --git a/src/cool2/test/test_reconstruction/test_substr_2.cl b/src/cool2/test/test_reconstruction/test_substr_2.cl new file mode 100644 index 000000000..3f7e50d6b --- /dev/null +++ b/src/cool2/test/test_reconstruction/test_substr_2.cl @@ -0,0 +1,17 @@ +class Main +{ + main(): String + { + { + s:String <- + "0123456789"; + s + .substr( + 0, + ~ 1 + ); + } + }; +} + + diff --git a/src/cool2/test/test_reconstruction/test_substr_3.cl b/src/cool2/test/test_reconstruction/test_substr_3.cl new file mode 100644 index 000000000..59b4fcb22 --- /dev/null +++ b/src/cool2/test/test_reconstruction/test_substr_3.cl @@ -0,0 +1,17 @@ +class Main +{ + main(): String + { + { + s:String <- + "0123456789"; + s + .substr( + 0, + 11 + ); + } + }; +} + + diff --git a/src/cool2/test/test_reconstruction/test_void_dispatch.cl b/src/cool2/test/test_reconstruction/test_void_dispatch.cl new file mode 100644 index 000000000..12023ab3a --- /dev/null +++ b/src/cool2/test/test_reconstruction/test_void_dispatch.cl @@ -0,0 +1,12 @@ +class Main +{ + b:Object; + main(): String + { + b + .type_name( + ) + }; +} + + diff --git a/src/cool2/test/test_reconstruction/test_while.cl b/src/cool2/test/test_reconstruction/test_while.cl new file mode 100644 index 000000000..dbc37dd2c --- /dev/null +++ b/src/cool2/test/test_reconstruction/test_while.cl @@ -0,0 +1,20 @@ +class Main inherits IO +{ + main(): Int + { + { + counter:Int <- + 0; + a:Object <- + while + (counter < 10) + loop + counter <- + (counter + 1) + pool; + counter; + } + }; +} + + diff --git a/src/cool2/test/test_reconstruction/test_zero_division.cl b/src/cool2/test/test_reconstruction/test_zero_division.cl new file mode 100644 index 000000000..d17986555 --- /dev/null +++ b/src/cool2/test/test_reconstruction/test_zero_division.cl @@ -0,0 +1,11 @@ +class Main +{ + main(): Int + { + { + (10 / 0); + } + }; +} + + diff --git a/src/cool2/test/test_run/01_program.cl b/src/cool2/test/test_run/01_program.cl new file mode 100644 index 000000000..cf8b86a51 --- /dev/null +++ b/src/cool2/test/test_run/01_program.cl @@ -0,0 +1,34 @@ +class Main inherits IO { + number: Int <- 5; + + main () : Object { + testing_fibonacci(number) + }; + + testing_fibonacci(n: Int) : IO {{ + out_string("Iterative Fibonacci : "); + out_int(iterative_fibonacci(5)); + out_string("\n"); + + out_string("Recursive Fibonacci : "); + out_int(recursive_fibonacci(5)); + out_string("\n"); + }}; + + recursive_fibonacci (n: AUTO_TYPE) : AUTO_TYPE { + if n <= 2 then 1 else recursive_fibonacci(n - 1) + recursive_fibonacci(n - 2) fi + }; + + iterative_fibonacci(n: AUTO_TYPE) : AUTO_TYPE { + let i: Int <- 2, n1: Int <- 1, n2: Int <- 1, temp: Int in { + while i < n loop + let temp: Int <- n2 in { + n2 <- n2 + n1; + n1 <- temp; + i <- i + 1; + } + pool; + n2; + } + }; +} \ No newline at end of file diff --git a/src/cool2/test/test_run/02_program.cl b/src/cool2/test/test_run/02_program.cl new file mode 100644 index 000000000..5c80aa2c7 --- /dev/null +++ b/src/cool2/test/test_run/02_program.cl @@ -0,0 +1,24 @@ +class Main inherits IO { + main (): Object { + { + out_int((new Ackermann).ackermann(3, 2)); + out_string("\n"); + } + }; +} + +class Ackermann { + ackermann (m: Int, n: Int): Int { + if m = 0 + then + n + 1 + else + if n = 0 + then + self.ackermann(m - 1, 1) + else + self.ackermann(m - 1, self.ackermann(m, n - 1)) + fi + fi + }; +} \ No newline at end of file diff --git a/src/cool2/test/test_run/03_program.cl b/src/cool2/test/test_run/03_program.cl new file mode 100644 index 000000000..d061381d3 --- /dev/null +++ b/src/cool2/test/test_run/03_program.cl @@ -0,0 +1,22 @@ +class A { } + +class B inherits A { } + +class C inherits A { } + +class D inherits C { } + +class Main inherits IO { + main () : Object { + testing_case() + }; + + testing_case() : IO { + let a: A <- new D in + case a of + x: B => out_string("Is type B.\n"); + x: C => out_string("Is type C.\n"); + x: D => out_string("Is type D.\n"); + esac + }; +} \ No newline at end of file diff --git a/src/cool2/test/test_run/04_program.cl b/src/cool2/test/test_run/04_program.cl new file mode 100644 index 000000000..1615934ff --- /dev/null +++ b/src/cool2/test/test_run/04_program.cl @@ -0,0 +1,47 @@ +class Main inherits IO { + main (): IO { + let vector: Vector2 <- (new Vector2).init(0, 0) in + vector.print_vector() + }; +} + +class Vector2 { + x: Int; + + y: Int; + + init (x_: Int, y_: Int): Vector2 { + { + x <- x_; + y <- y_; + self; + } + }; + + get_x (): Int { + x + }; + + get_y (): Int { + y + }; + + add (v: Vector2): Vector2 { + (new Vector2).init(x + v.get_x(), y + v.get_y()) + }; + + print_vector (): IO { + let io: IO <- (new IO) in + { + io.out_string("("); + io.out_int(self.get_x()); + io.out_string("; "); + io.out_int(self.get_y()); + io.out_string(")\n"); + } + }; + + clone_vector (): Vector2 { + (new Vector2).init(x, y) + }; +} \ No newline at end of file diff --git a/src/cool2/test/test_run/05_program.cl b/src/cool2/test/test_run/05_program.cl new file mode 100644 index 000000000..2bd4b5309 --- /dev/null +++ b/src/cool2/test/test_run/05_program.cl @@ -0,0 +1,23 @@ +class Main { + main (): Object { + let total: Int <- 10, + i: Int <- 1, + io: IO <- (new IO) in + while i <= total loop + { + io.out_int(self.fibonacci(i)); + io.out_string("\n"); + i <- i + 1; + } + pool + }; + + fibonacci (n: Int): Int { + if n <= 2 + then + 1 + else + self.fibonacci(n - 1) + self.fibonacci(n - 2) + fi + }; +} \ No newline at end of file diff --git a/src/cool2/test/tests.py b/src/cool2/test/tests.py new file mode 100644 index 000000000..f1e84d080 --- /dev/null +++ b/src/cool2/test/tests.py @@ -0,0 +1,35 @@ +import json +import os + +base_dir = os.path.dirname(__file__) +test_dir = os.path.join(base_dir, "test_data") + +def dump_meta_test(name, code, result, errors): + meta = json.dumps({"name":name, "result":result, "errors":errors}) + dump_dir = os.path.join(test_dir, name+".meta_test") + with open(dump_dir, "w") as f: + json.dump(meta,f) + f.write("\n") + f.write(code) + +def load_meta_test(path): + with open(path, "r") as f: + meta = f.readline() + meta = json.loads(meta) + meta = json.loads(meta) + name, result, errors = [meta[x] for x in ["name", "result", "errors"]] + code = f.read() + return name, code, result, errors + +tests = [] + +for meta_file in os.listdir(test_dir): + tests.append(load_meta_test(os.path.join(test_dir, meta_file))) + +run_dir = os.path.join(base_dir, "test_run") + +tests_run = [] +for program in os.listdir(run_dir): + with open(os.path.join(run_dir, program)) as f: + program = f.read() + tests_run.append(program) \ No newline at end of file diff --git a/src/main.py b/src/main.py index a02443d9f..a6c713063 100644 --- a/src/main.py +++ b/src/main.py @@ -148,4 +148,8 @@ def __call__(self, ast:BaseAST) -> BaseAST: pipe = MipsPipeline(pipe, MockMips1(), MockMips2()) -print(pipe(MockLexer.program)) \ No newline at end of file +# print(pipe(MockLexer.program)) + +from cool2.main import main + +main('a.cl', None,False,False,False,False,False) \ No newline at end of file From 5d32cb26b91f7a5e4f3f7179222834ca34142924 Mon Sep 17 00:00:00 2001 From: Luiso Date: Sun, 14 Mar 2021 20:32:42 -0400 Subject: [PATCH 008/143] TypeCollector added TODO: - Complete semantic tests --- src/cool_cmp/semantic/errors.py | 20 +++- src/cool_cmp/semantic/implementations.py | 15 ++- src/cool_cmp/semantic/interface.py | 6 +- src/cool_cmp/semantic/tests/__init__.py | 0 .../semantic/tests/collector_visitor_test.py | 0 src/cool_cmp/semantic/tests/context_test.py | 0 src/cool_cmp/semantic/tests/scope_test.py | 0 src/cool_cmp/semantic/tests/type_test.py | 44 +++++++ .../semantic/visitors/type_collector.py | 113 ++++++++++++++++-- src/cool_cmp/semantic/visitors/utils.py | 47 ++++++++ 10 files changed, 233 insertions(+), 12 deletions(-) create mode 100644 src/cool_cmp/semantic/tests/__init__.py create mode 100644 src/cool_cmp/semantic/tests/collector_visitor_test.py create mode 100644 src/cool_cmp/semantic/tests/context_test.py create mode 100644 src/cool_cmp/semantic/tests/scope_test.py create mode 100644 src/cool_cmp/semantic/tests/type_test.py create mode 100644 src/cool_cmp/semantic/visitors/utils.py diff --git a/src/cool_cmp/semantic/errors.py b/src/cool_cmp/semantic/errors.py index 4f70938e6..cdacd76cf 100644 --- a/src/cool_cmp/semantic/errors.py +++ b/src/cool_cmp/semantic/errors.py @@ -6,6 +6,7 @@ VARIABLE_ALREADY_DEFINED = lambda variable_name: f"Variable {variable_name} already defined" VARIABLE_NOT_DEFINED = lambda variable_name: f"Variable {variable_name} is not defined" +METHOD_PARAMS_NAMES_EQUALS = lambda method_name: f"Params with equal names present in {method_name}" METHOD_ALREADY_DEFINED = lambda method_name, param_count, type_name: f"Method {method_name} with {param_count} params already defined in {type_name}" METHOD_REDEFINITION_INVALID = lambda method_name, old_signature: f"Method {method_name} already defined with {old_signature} in a parent class" METHOD_NOT_DEFINED = lambda method_name, params_count, type_name: f"Method {method_name} with {params_count} params is not defined in {type_name}" @@ -14,10 +15,27 @@ TYPE_NOT_DEFINED = lambda type_name: f"Type {type_name} not defined" TYPE_ALREADY_DEFINED = lambda type_name: f"Type {type_name} already defined" TYPE_NOT_INHERITABLE = lambda type_name: f"Can't inherit from {type_name}" +TYPE_CYCLIC_DEPENDENCY = lambda cycle: f"Types {', '.join(cycle)} form a cyclic dependency" class SemanticError(CoolError): """ Base Semantic Error """ - pass + + ERROR_TYPE = "SemanticError" + + FORMAT = "({}, {}) - {}: {}" + + def set_position(self, line:int, col:int): + """ + Set the line and column of this error + """ + self.line = line + self.column = col + + def __str__(self): + if hasattr(self, 'line') and hasattr(self, 'column'): + return self.FORMAT.format(self.line, self.column, self.ERROR_TYPE, self.error) + return self.ERROR_TYPE + " - " + self.error + diff --git a/src/cool_cmp/semantic/implementations.py b/src/cool_cmp/semantic/implementations.py index 44dd7bb4a..90c3468db 100644 --- a/src/cool_cmp/semantic/implementations.py +++ b/src/cool_cmp/semantic/implementations.py @@ -1,7 +1,7 @@ from cool_cmp.semantic.interface import IContext, IVariableInfo, IAttributeInfo, IMethodInfo, IType, IScope from cool_cmp.semantic.errors import SemanticError, VARIABLE_ALREADY_DEFINED, ATTRIBUTE_ALREADY_DEFINED, \ METHOD_ALREADY_DEFINED, METHOD_REDEFINITION_INVALID, METHOD_NOT_DEFINED, ATTRIBUTE_NOT_DEFINED, \ - TYPE_NOT_DEFINED, TYPE_ALREADY_DEFINED, VARIABLE_NOT_DEFINED, TYPE_NOT_INHERITABLE + TYPE_NOT_DEFINED, TYPE_ALREADY_DEFINED, VARIABLE_NOT_DEFINED, TYPE_NOT_INHERITABLE, METHOD_PARAMS_NAMES_EQUALS from typing import List class VariableInfo(IVariableInfo): @@ -103,6 +103,11 @@ def is_attribute_defined(self, name:str)->bool: return False def add_method(self, name:str, params:List[IVariableInfo], return_type:IType)->IMethodInfo: + names = { x.name for x in params } + + if len(names) != len(params): + raise SemanticError(METHOD_PARAMS_NAMES_EQUALS(name)) + try: # Checking if a local method already exist with the given signature local_method = next(x for x in self._methods if x.name == name and len(x.parameters) == len(params)) @@ -133,7 +138,7 @@ def is_method_defined(self, name:str, params_count:int)->bool: def get_method(self, name:str, params_count:int)->IMethodInfo: try: - method = next(x for x in self.methods if x.name == name and len(x.parameters) == len(params)) + method = next(x for x in self.methods if x.name == name and len(x.parameters) == params_count) return method except StopIteration: raise SemanticError(METHOD_NOT_DEFINED(name, params_count, self.name)) @@ -141,6 +146,7 @@ def get_method(self, name:str, params_count:int)->IMethodInfo: def get_attribute(self, name:str)->IAttributeInfo: try: attr = next(x for x in self.attributes if x.name == name) + return attr except StopIteration: raise SemanticError(ATTRIBUTE_NOT_DEFINED(name, self.name)) @@ -185,6 +191,11 @@ def get_type(self, name:str)->IType: return typex except StopIteration: raise SemanticError(TYPE_NOT_DEFINED(name)) + + @property + def types(self)->List[IType]: + return self._types + class Scope(IScope): """ diff --git a/src/cool_cmp/semantic/interface.py b/src/cool_cmp/semantic/interface.py index 909a9cb33..99b0c3cce 100644 --- a/src/cool_cmp/semantic/interface.py +++ b/src/cool_cmp/semantic/interface.py @@ -99,7 +99,11 @@ class IContext: def add_type(self, name:str)->IType: raise NotImplementedError() - def get_type(self, name:str)->IType: + def get_type(self, name:str, father:IType)->IType: + raise NotImplementedError() + + @property + def types(self)->List[IType]: raise NotImplementedError() diff --git a/src/cool_cmp/semantic/tests/__init__.py b/src/cool_cmp/semantic/tests/__init__.py new file mode 100644 index 000000000..e69de29bb diff --git a/src/cool_cmp/semantic/tests/collector_visitor_test.py b/src/cool_cmp/semantic/tests/collector_visitor_test.py new file mode 100644 index 000000000..e69de29bb diff --git a/src/cool_cmp/semantic/tests/context_test.py b/src/cool_cmp/semantic/tests/context_test.py new file mode 100644 index 000000000..e69de29bb diff --git a/src/cool_cmp/semantic/tests/scope_test.py b/src/cool_cmp/semantic/tests/scope_test.py new file mode 100644 index 000000000..e69de29bb diff --git a/src/cool_cmp/semantic/tests/type_test.py b/src/cool_cmp/semantic/tests/type_test.py new file mode 100644 index 000000000..02e459423 --- /dev/null +++ b/src/cool_cmp/semantic/tests/type_test.py @@ -0,0 +1,44 @@ +from cool_cmp.semantic.implementations import CoolType, SemanticError, VariableInfo +import pytest + +# To execute the test $ pytest src/cool_cmp/semantic/tests/ + +def test_type_attribute(): + """ + Attribute Redefinition in same type + """ + obj = CoolType("Object", None) + attr = obj.add_attribute("a", obj) + with pytest.raises(SemanticError): + obj.add_attribute("a", obj) + + +def test_type_attribute_2(): + """ + Attribute Redefinition in child type + """ + obj = CoolType("Object", None) + attr = obj.add_attribute("a", obj) + obj2 = CoolType("Object2", obj) + with pytest.raises(SemanticError): + obj2.add_attribute("a", obj2) + + +def test_type_method(): + """ + Method Definition with same signature + """ + obj = CoolType("Object", None) + attr = obj.add_method("a", [VariableInfo("a", obj)], obj) + with pytest.raises(SemanticError): + obj.add_method("a", [VariableInfo("b", obj)], obj) + + +def test_type_method_2(): + """ + Method Definition with same params names + """ + obj = CoolType("Object", None) + with pytest.raises(SemanticError): + attr = obj.add_method("a", [VariableInfo("a", obj), VariableInfo("a", obj)], obj) + diff --git a/src/cool_cmp/semantic/visitors/type_collector.py b/src/cool_cmp/semantic/visitors/type_collector.py index 09370491f..4a0f3b678 100644 --- a/src/cool_cmp/semantic/visitors/type_collector.py +++ b/src/cool_cmp/semantic/visitors/type_collector.py @@ -1,16 +1,25 @@ import cool_cmp.shared.visitor as visitor -from cool_cmp.semantic.interface import IContext -from cool_cmp.shared.errors import ErrorTracker +from cool_cmp.shared.ast import BaseAST +from cool_cmp.semantic.interface import IContext, ISemantic +from cool_cmp.shared.errors import ErrorTracker, IErrorTraceable, CoolError from cool_cmp.shared.ast.cool import * +from cool_cmp.semantic.implementations import Context, CoolType +from cool_cmp.semantic.types import * +from cool_cmp.semantic.errors import SemanticError, TYPE_ALREADY_DEFINED, TYPE_NOT_DEFINED, \ + TYPE_CYCLIC_DEPENDENCY -class TypeCollectorVisitor: +class TypeCollectorVisitor(IErrorTraceable): """ - Collects the types by saving them in a Context + Collects the types by saving them to an IContext """ - def __init__(self, error_tracker:ErrorTracker, context:IContext=None): - self.errors = error_tracker - self.context = context + def __init__(self): + self.errors = ErrorTracker() + self.context = Context() + + def add_semantic_error(self, error:SemanticError, line:int, pos:int): + error.set_position(line, pos) + self.add_error(error) @visitor.on('node') def visit(self, node): @@ -18,7 +27,95 @@ def visit(self, node): @visitor.when(ProgramNode) def visit(self, node:ProgramNode): - pass # TODO + + if not self.context: + obj = ObjectType() + int_ = IntType(obj) + bool_ = BoolType(obj) + string = StringType(obj) + io = IOType(obj) + + self.context = Context([ + obj, + int_, + bool_, + string, + io, + ]) + + default_names = [x.name for x in self.context.types] + type_names = [x.name for x in default_names] + + for class_decl in node.declarations: # Collecting defined classes + if class_decl.id in type_names: + error = SemanticError(TYPE_ALREADY_DEFINED(class_decl.id)) + self.add_semantic_error(error, class_decl.row, class_decl.column) + else: + type_names.append(class_decl.id) + + for class_decl in node.declarations: # Verify that parent classes exist + if class_decl.parent not in type_names: + error = SemanticError(TYPE_NOT_DEFINED(class_decl.parent)) + self.add_semantic_error(error, class_decl.row, class_decl.column) # TODO row and column should point to 'parent' token position + + + # Search for cyclic dependencies + import cool_cmp.semantic.visitors.utils as ut + graph = ut.build_graph_list([(x.id, x.parent) for x in node.declarations]) + cycles, sort = ut.any_cycles(graph) + + for cycle in cycles: # Adding cyclic dependency errors + error = SemanticError(TYPE_CYCLIC_DEPENDENCY(cycle)) + self.add_error(error) + + for class_name in sort: # Defining types in topological order + if class_name in default_names: # Class already in context + continue + + class_decl = [x for x in node.declarations if x.id == class_name] + if len(class_decl) != 1: # Error already captured. Multiple definitions or no definition + continue + class_decl = class_decl[0] + + # At this point shold work whitout the try except + try: + father = self.context.get_type(class_decl.parent) + except SemanticError as exc: + self.add_semantic_error(exc, class_decl.row, class_decl.column) + + try: + self.context.add_type(class_decl, father) + except SemanticError as exc: + self.add_semantic_error(exc, class_decl.row, class_decl.column) + + return self.context + + def add_error(self, error:CoolError): + self.errors.add_error(error) + + def get_errors(self)->List[CoolError]: + self.errors.get_errors() + +class CollectorService(ISemantic): + + + def __init__(self): + self._errors = ErrorTracker() + + @property + def name(self)->str: + return 'context' + + def __call__(self, ast:BaseAST) -> BaseAST: + collector = TypeCollectorVisitor() + context = collector.visit(ast.node) + for error in collector.get_errors(): + self.add_error(error) + return context + def add_error(self, error:CoolError): + self._errors.add_error(error) + def get_errors(self)->List[CoolError]: + return self._errors.get_errors() \ No newline at end of file diff --git a/src/cool_cmp/semantic/visitors/utils.py b/src/cool_cmp/semantic/visitors/utils.py new file mode 100644 index 000000000..a65697292 --- /dev/null +++ b/src/cool_cmp/semantic/visitors/utils.py @@ -0,0 +1,47 @@ +from typing import List, Tuple, Dict + +Graph = Dict[str,List[str]] + +def build_graph_list(type_list:List[Tuple[str,str]])->Graph: + graph = { x:[] for x,_ in type_list } + + for x,y in type_list: + graph[x].append(y) + + add_keys = [y for x,y in type_list if y not in graph] + + for key in add_keys: + graph[key] = [] + + return graph + +def any_cycles(graph:Graph)->Tuple[Tuple[str,str],List[str]]: + d,f,time,stack,cycles = { x:0 for x in graph },{ x:0 for x in graph },[1], [], [] + for x in graph: + if not d[x]: + d[x] = time[0]; time[0]+=1 + topological_order(graph,x,d,f,time,stack,cycles) + f[x] = time[0]; time[0]+=1 + stack.append(x) + stack.reverse() + return cycles, stack + + +def topological_order(graph, node, d, f, time, stack, cycles): + + for adj in graph[node]: + if not d[adj]: + d[adj] = time[0]; time[0]+=1 + topological_order(graph,adj,d,f,time,stack,cycles) + f[adj] = time[0]; time[0]+=1 + stack.append(adj) + elif not f[adj]: + cycles.append((node,adj)) + +gr = build_graph_list([('a','b'), ('b','c')]) # a->b->c +gr = build_graph_list([('b','a'), ('c','b')]) # a<-b<-c +gr = build_graph_list([('b','a'), ('b','b')]) # a<-b<-c + +cycles, sort = any_cycles(gr) + +print(gr,cycles,sort) \ No newline at end of file From 2e1dafc76502e06eb6bba2a552c580a511553091 Mon Sep 17 00:00:00 2001 From: Luiso Date: Sat, 17 Apr 2021 06:40:52 -0400 Subject: [PATCH 009/143] Project running with cool2 package --- .gitignore | 4 + .vscode/settings.json | 2 +- src/a.cl | 35 +- src/cool2/cool/lexer/comment_lexer.py | 2 +- src/cool2/cool/lexer/cool_lexer.py | 2 +- src/cool2/cool/lib/std.cool | 35 ++ src/cool2/cool/parser/comment_parser.py | 2 +- src/cool2/cool/parser/cool_parser.py | 2 +- src/cool2/cool/visitors/visitors.py | 2 +- src/cool2/lib/__init__.py | 0 src/cool2/lib/grammar/grammar_fixer.py | 401 ++++++++++++++ src/cool2/lib/grammar/grammar_parser.py | 174 ++++++ src/cool2/lib/grammar/grammar_tokens.py | 141 +++++ src/cool2/lib/grammar/grammar_util.py | 21 + src/cool2/lib/lang/language.py | 59 ++ src/cool2/lib/lang/language_ll1.py | 62 +++ src/cool2/lib/lang/language_lr.py | 53 ++ src/cool2/lib/lang/language_regular.py | 397 ++++++++++++++ src/cool2/lib/lexer/lexer.py | 116 ++++ src/cool2/lib/lexer/regex.py | 147 +++++ src/cool2/lib/parser/__init__.py | 0 src/cool2/lib/parser/parser.py | 44 ++ src/cool2/lib/parser/parser_ll1.py | 332 ++++++++++++ src/cool2/lib/parser/parser_lr.py | 684 ++++++++++++++++++++++++ src/cool2/lib/shortcut.py | 12 + src/cool2/lib/utils/algorithms.py | 21 + src/cool2/lib/utils/automaton.py | 463 ++++++++++++++++ src/cool2/lib/utils/first_follow.py | 108 ++++ src/cool2/lib/utils/trie.py | 63 +++ src/main.py | 2 +- 30 files changed, 3378 insertions(+), 8 deletions(-) create mode 100644 src/cool2/cool/lib/std.cool create mode 100644 src/cool2/lib/__init__.py create mode 100644 src/cool2/lib/grammar/grammar_fixer.py create mode 100644 src/cool2/lib/grammar/grammar_parser.py create mode 100644 src/cool2/lib/grammar/grammar_tokens.py create mode 100644 src/cool2/lib/grammar/grammar_util.py create mode 100644 src/cool2/lib/lang/language.py create mode 100644 src/cool2/lib/lang/language_ll1.py create mode 100644 src/cool2/lib/lang/language_lr.py create mode 100644 src/cool2/lib/lang/language_regular.py create mode 100644 src/cool2/lib/lexer/lexer.py create mode 100644 src/cool2/lib/lexer/regex.py create mode 100644 src/cool2/lib/parser/__init__.py create mode 100644 src/cool2/lib/parser/parser.py create mode 100644 src/cool2/lib/parser/parser_ll1.py create mode 100644 src/cool2/lib/parser/parser_lr.py create mode 100644 src/cool2/lib/shortcut.py create mode 100644 src/cool2/lib/utils/algorithms.py create mode 100644 src/cool2/lib/utils/automaton.py create mode 100644 src/cool2/lib/utils/first_follow.py create mode 100644 src/cool2/lib/utils/trie.py diff --git a/.gitignore b/.gitignore index 4acafde18..3559caca9 100644 --- a/.gitignore +++ b/.gitignore @@ -318,6 +318,10 @@ share/python-wheels/ *.egg MANIFEST +# lib named project folders +!src/cool2/lib +!src/cool2/cool/lib + # PyInstaller # Usually these files are written by a python script from a template # before PyInstaller builds the exe, so as to inject date/other infos into it. diff --git a/.vscode/settings.json b/.vscode/settings.json index d3c016a79..615aafb03 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -1,3 +1,3 @@ { - "python.pythonPath": "/home/luiso/anaconda3/bin/python" + "python.pythonPath": "/usr/bin/python3" } \ No newline at end of file diff --git a/src/a.cl b/src/a.cl index 196991234..cf8b86a51 100644 --- a/src/a.cl +++ b/src/a.cl @@ -1 +1,34 @@ -class `A {} \ No newline at end of file +class Main inherits IO { + number: Int <- 5; + + main () : Object { + testing_fibonacci(number) + }; + + testing_fibonacci(n: Int) : IO {{ + out_string("Iterative Fibonacci : "); + out_int(iterative_fibonacci(5)); + out_string("\n"); + + out_string("Recursive Fibonacci : "); + out_int(recursive_fibonacci(5)); + out_string("\n"); + }}; + + recursive_fibonacci (n: AUTO_TYPE) : AUTO_TYPE { + if n <= 2 then 1 else recursive_fibonacci(n - 1) + recursive_fibonacci(n - 2) fi + }; + + iterative_fibonacci(n: AUTO_TYPE) : AUTO_TYPE { + let i: Int <- 2, n1: Int <- 1, n2: Int <- 1, temp: Int in { + while i < n loop + let temp: Int <- n2 in { + n2 <- n2 + n1; + n1 <- temp; + i <- i + 1; + } + pool; + n2; + } + }; +} \ No newline at end of file diff --git a/src/cool2/cool/lexer/comment_lexer.py b/src/cool2/cool/lexer/comment_lexer.py index 7581de18d..810d0bd6f 100644 --- a/src/cool2/cool/lexer/comment_lexer.py +++ b/src/cool2/cool/lexer/comment_lexer.py @@ -17,6 +17,6 @@ # comment_lexer = Lexer(comment_tokens_def,C.EOF) -comment_lexer_path = 'cool2/cool/lexer/comment_lexer.obj' +comment_lexer_path = 'src/cool2/cool/lexer/comment_lexer.obj' # comment_lexer = save_lexer(comment_tokens_def,comment_lexer_path,C) comment_lexer = load_lexer(comment_lexer_path) \ No newline at end of file diff --git a/src/cool2/cool/lexer/cool_lexer.py b/src/cool2/cool/lexer/cool_lexer.py index 0ea0855dd..9338e1f34 100644 --- a/src/cool2/cool/lexer/cool_lexer.py +++ b/src/cool2/cool/lexer/cool_lexer.py @@ -41,6 +41,6 @@ cool_tokens_def = keywords+operators+signs+other+ignore # cool_lexer = Lexer(cool_token_def,G.EOF) -cool_lexer_path = 'cool2/cool/lexer/cool_lexer.obj' +cool_lexer_path = 'src/cool2/cool/lexer/cool_lexer.obj' # cool_lexer = save_lexer(cool_tokens_def,cool_lexer_path,G) cool_lexer = load_lexer(cool_lexer_path) \ No newline at end of file diff --git a/src/cool2/cool/lib/std.cool b/src/cool2/cool/lib/std.cool new file mode 100644 index 000000000..91de5a293 --- /dev/null +++ b/src/cool2/cool/lib/std.cool @@ -0,0 +1,35 @@ +-- Standar Cool Library +-- Some methods changed when building the ast to provide a python implementation for it +class Object { + abort() : Object{ + self + }; + + type_name() : String { + "Object" + }; + + copy() : SELF_TYPE { + self + }; +} + +class String inherits Object { + + length() : Int { 0 }; + concat(s : String) : String { s }; + substr(i : Int, l : Int) : String { "string" }; +} + +class IO inherits Object { + out_string(x : String) : SELF_TYPE { self }; + out_int(x : Int) : SELF_TYPE { self }; + in_string() : String { "string" }; + in_int() : Int { 0 }; +} + +class Int inherits Object { } + +class Bool inherits Object { } + +class Void { } \ No newline at end of file diff --git a/src/cool2/cool/parser/comment_parser.py b/src/cool2/cool/parser/comment_parser.py index 9a72372f3..62d2c98a9 100644 --- a/src/cool2/cool/parser/comment_parser.py +++ b/src/cool2/cool/parser/comment_parser.py @@ -1,7 +1,7 @@ from cool.grammar.comment_grammar import C from cool.parser.utils import load_parser, save_parser -comment_parser_path = 'cool2/cool/parser/comment_parser.obj' +comment_parser_path = 'src/cool2/cool/parser/comment_parser.obj' # comment_parser = save_parser(comment_parser_path,C) # print('Errors',comment_parser.errors) comment_parser = load_parser(comment_parser_path,C) \ No newline at end of file diff --git a/src/cool2/cool/parser/cool_parser.py b/src/cool2/cool/parser/cool_parser.py index 7898028cd..7e86080a0 100644 --- a/src/cool2/cool/parser/cool_parser.py +++ b/src/cool2/cool/parser/cool_parser.py @@ -1,7 +1,7 @@ from cool.grammar.cool_grammar import G from cool.parser.utils import load_parser, save_parser -cool_parser_path = 'cool2/cool/parser/cool_parser.obj' +cool_parser_path = 'src/cool2/cool/parser/cool_parser.obj' # cool_parser = save_parser(cool_parser_path,G) # print('Errors',cool_parser.errors) cool_parser = load_parser(cool_parser_path,G) \ No newline at end of file diff --git a/src/cool2/cool/visitors/visitors.py b/src/cool2/cool/visitors/visitors.py index 4e51d99ba..0ac7b0754 100644 --- a/src/cool2/cool/visitors/visitors.py +++ b/src/cool2/cool/visitors/visitors.py @@ -336,7 +336,7 @@ def visit(self, node, tabs=0): class TypeCollector(object): def __init__(self, errors=[], context=None): self.context = context - self.errors = errorsTrue + self.errors = errors @visitor.on('node') def visit(self, node): diff --git a/src/cool2/lib/__init__.py b/src/cool2/lib/__init__.py new file mode 100644 index 000000000..e69de29bb diff --git a/src/cool2/lib/grammar/grammar_fixer.py b/src/cool2/lib/grammar/grammar_fixer.py new file mode 100644 index 000000000..39e250a2e --- /dev/null +++ b/src/cool2/lib/grammar/grammar_fixer.py @@ -0,0 +1,401 @@ +from cmp.pycompiler import (EOF, Epsilon, Grammar, NonTerminal, Production, + Sentence, SentenceList, Symbol, Terminal, SentenceFromIter) +from lib.utils.trie import TrieNode, Trie +from lib.utils.algorithms import permutation # FIX + +# GRAMMAR FIXER UTILS +def change_grammar_from_productions(gramm:Grammar,new_productions): + """ + Empty all non terminal and grammar productions\n + and add all productions in new_productions to gramm + """ + for x in gramm.nonTerminals: + x.productions = [] + + gramm.Productions = [] + + for x in new_productions: + gramm.Add_Production(x) + + return gramm + +def generate_name(G:Grammar,idx,prefix = 'N'): + new_key = prefix+'_' + cur_idx = idx + while f'{new_key}{cur_idx}' in G.symbDict.keys(): + cur_idx+=1 + return f'{new_key}{cur_idx}',cur_idx+1 + +def remove_unnecessary_symbols(gramm:Grammar,unnecesary): + new_prod = [] + + for prod in gramm.Productions: + if not prod.Left in unnecesary: + r_unne = [x for x in prod.Right if x in unnecesary] + if not r_unne: + new_prod.append(prod) + + for x in unnecesary: + try: + gramm.nonTerminals.remove(x) + except ValueError: + gramm.terminals.remove(x) + gramm.symbDict.__delitem__(x.Name) + + change_grammar_from_productions(gramm,new_prod) + +def build_reach(gramm:Grammar, symbol): + """ + Return a set with the symbols that reach symbol + """ + reach = set() + reach.add(symbol) + last_len = 0 + while last_len != len(reach): + last_len = len(reach) + for x in gramm.Productions: + if any([y for y in x.Right if y in reach]): + reach.add(x.Left) + return reach + +def fix_grammar(G,errors): + """ + Check if G is LL1 and fix it if possible\n + Removes the immediate left recursion and the common prefixes\n + return the new grammar fixed if possible\n + else return None + """ + if not G: + return None + # Your code goes here + new_errors = [] + + G = fix_common_prefix(G) + G = fix_left_recursion(G,new_errors) + G = fix_unnecesary_prod(G) + + errors.extend(new_errors) + if new_errors: + return None + return G + + +# LEFT RECURSION +# Esta implementado solamente la inmediata una sola vez Probar algo como E -> EE o algo por el estilo +def fix_left_recursion(grammar:Grammar, errors): + ''' + Fix immediate left recursion of grammar\n + return a fixed copy of grammar + ''' + new_grammar = grammar.copy() + new_grammar.Productions = [] + + for n_ter in grammar.nonTerminals: + for prod in n_ter.productions: + if not prod.Right.IsEpsilon and prod.Right[0] == prod.Left: + fix_non_terminal_left_recursion(n_ter,new_grammar, errors) + break + else: + new_grammar.Productions.extend(n_ter.productions) + + return new_grammar + +def fix_non_terminal_left_recursion(non_terminal:NonTerminal, grammar, errors): + ''' + Fix immediate left recursion non_terminal in grammar\n + ''' + new_name,idx = generate_name(grammar,0,non_terminal.Name) + new_non_terminal = grammar.NonTerminal(new_name) + + left_prod = non_terminal.productions + non_terminal.productions = [] + new_prod_new_n_ter = set() + new_prod_old_n_ter = set() + + for prod in left_prod: + if not prod.Right.IsEpsilon and prod.Right[0] == non_terminal: + if len(prod.Right) > 1: + sentence = [x for x in prod.Right[1:]] + sentence.append(new_non_terminal) + new_prod_new_n_ter.add(Production(new_non_terminal,SentenceFromIter(sentence))) + else: + sentence = [x for x in prod.Right] + sentence.append(new_non_terminal) + new_prod_new_n_ter.add(Production(new_non_terminal,grammar.Epsilon)) + new_prod_old_n_ter.add(Production(non_terminal,SentenceFromIter(sentence))) + + for prod in new_prod_new_n_ter: + grammar.Add_Production(prod) + + if not new_prod_old_n_ter: + errors.append(f'All productions of {non_terminal} begins with {non_terminal}, no string can be parsed by a left parse') + + for prod in new_prod_old_n_ter: + grammar.Add_Production(prod) + + +# CLEANING GRAMMAR +def fix_unnecesary_prod(grammar:Grammar): + grammar = fix_e_productions(grammar) + grammar = fix_useless_symbols(grammar) + return grammar + + +# COMMON PREFIX +def fix_common_prefix(grammar:Grammar): + """ + returns a copy of grammar without common prefixes + """ + G = grammar.copy() + G.Productions = [] + + for non_terminal in grammar.nonTerminals: + trie = Trie() + epsilon = False + for x in non_terminal.productions: + if not x.Right.IsEpsilon: + trie.add(x.Right) + else: + epsilon = True + non_terminal.productions = [] + if epsilon: + G.Add_Production(Production(x.Left,G.Epsilon)) + for node in trie.top.sons: + execute_node(trie.top.sons[node],non_terminal,[],G,0) + return G + +def execute_node(node:TrieNode,left,right:list,grammar:Grammar,idx): + """ + Fills productions with the new grammar productions\n + left: left part of the production\n + right: right part of the production in a list\n + productions: all the productions of the grammar without common prefixes\n + idx: index of the generated name + """ + right.append(node.value) + if len(node.sons) > 1 or (len(node.sons) == 1 and node.terminal): + name,idx = generate_name(grammar,idx,left.Name) + new_prod = grammar.NonTerminal(name) + right.append(new_prod) + grammar.Add_Production(Production(left,SentenceFromIter(right))) + left = new_prod + if node.terminal: + grammar.Add_Production(Production(left,grammar.Epsilon)) + for x in node.sons: + right = [] + execute_node(node.sons[x],left,right,grammar,idx) + elif len(node.sons) == 1: + for key in node.sons: + execute_node(node.sons[key],left,right,grammar,idx) + break + else: + grammar.Add_Production(Production(left,SentenceFromIter(right))) + + +# e-PRODUCTIONS +def remove_e_productions(G): + """ + returns a grammar with all epsilon productions eliminated\n + and a set with the Non Terminal that reached epsilon productions\n + return G,n_t_epsilon + """ + + n_t_epsilon = set() + new_productions = [] + # Removes all epsilon transitions from the non terminals + for x in G.Productions: + if x.Right.IsEpsilon: + n_t_epsilon.add(x.Left) + x.Left.productions.remove(x) + else: + new_productions.append(x) + + change = 0 + while change != len(n_t_epsilon): + change = len(n_t_epsilon) + for prod in G.Productions: + if len([x for x in prod.Right if x in n_t_epsilon]) == len(prod.Right): # All x in right is on n_t_epsilon + n_t_epsilon.add(prod.Left) + + G.Productions = new_productions + + return G,n_t_epsilon + +def add_new_productions(prod,new_productions,n_t_epsilon): + """ + Add to new_productions the corresponding productions from\n + permutate prod with the non_terminals in n_t_epsilon \n + Ex: A->BCB,n_t_eps={B}\n + add to new_productions {A->BCB, A->CB, A->BC, A->C} + """ + had_epsilon = [False]*len(prod.Right) + cant_epsilon = 0 + + for i,x in enumerate(prod.Right): + if x in n_t_epsilon: + had_epsilon[i] = True + cant_epsilon += 1 + + for perm in permutation(cant_epsilon,2): + new_prod_right = [] + perm_idx = 0 + for i,x in enumerate(had_epsilon): + if not x: + new_prod_right.append(prod.Right[i]) + elif perm[perm_idx]: + new_prod_right.append(prod.Right[i]) + perm_idx = perm_idx+1 if x else perm_idx + if new_prod_right: + new_productions.add(Production(prod.Left,SentenceFromIter(new_prod_right))) + if cant_epsilon == 0: + new_productions.add(Production(prod.Left,prod.Right)) + +def fix_e_productions(gramm:Grammar): + """ + Returns a grammar without epsilon transitions.\n + if gramm recognize epsilon then an augmented grammar\n + is return with an epsilon transition on the start symbol + """ + G = gramm + + G,n_t_epsilon = remove_e_productions(G) + + if G.startSymbol in n_t_epsilon: + G = G.AugmentedGrammar(force=True) + G.Add_Production(Production(G.startSymbol,G.Epsilon)) + else: + G = G.copy() + + new_productions = set() + + for prod in gramm.Productions: + add_new_productions(prod,new_productions,n_t_epsilon) + + for x in gramm.nonTerminals: + x.productions = [] + + for prod in new_productions: + G.Add_Production(prod) + + return G + + +# NON DERIVE TERMINAL +def update_derivation(production,derivation,left_derivation = True): + symbol,sentence = production.Left,production.Right + derive = [] + derive.append(production) + if not symbol in derivation: + if sentence.IsEpsilon: + derive.extend(derivation[symbol.Grammar.Epsilon]) + elif not left_derivation: + sentence = [ sentence[i] for i in range(len(sentence)-1,-1,-1) ] # sentence.reverse() + for x in sentence: + derive.extend(derivation[x]) + derivation[symbol] = derive + +def fix_non_derive_terminal(gramm:Grammar,return_derivations = False,left_derivation = True): + """ + Remove from gramm the non terminals A that dont satisfy:\n + A->*w where w in {G.T}* + return grammar + return grammar,derivation + """ + gramm = gramm.copy() + + derivation = { x:[Production(x,Sentence(x)),] for x in gramm.terminals } + derivation[gramm.Epsilon] = [Production(gramm.Epsilon,Sentence(gramm.Epsilon)),] + derive_something = set(gramm.terminals) + productions = set(gramm.Productions) + + change = -1 + while change != len(derive_something): + change = len(derive_something) + to_remove = [] + for x in productions: + if not any([y for y in x.Right if not y in derive_something]): # if y ->* w with w in T* + derive_something.add(x.Left) + update_derivation(x,derivation,left_derivation) + to_remove.append(x) + for x in to_remove: productions.remove(x) + + remove_unnecessary_symbols(gramm,[x for x in gramm.nonTerminals if x not in derive_something]) + + if return_derivations: + return gramm,derivation + return gramm + + +# UNREACHABLE SYMBOLS +def fix_unreachable_symbols(gramm:Grammar): + gramm = gramm.copy() + + pending = [gramm.startSymbol] + reachable = set(pending) + while pending: + symb = pending.pop() + for prod in symb.productions: + for r in prod.Right: + if not r in reachable: + reachable.add(r) + if isinstance(r,NonTerminal): + pending.append(r) + + remove_unnecessary_symbols(gramm,[x for x in gramm.nonTerminals + gramm.terminals if x not in reachable]) + + return gramm + + +# USELESS SYMBOLS +def fix_useless_symbols(gramm:Grammar): + """ + fix the non derive on terminal, non_terminals\n + and the unreachable symbols from the start symbol + """ + gramm = fix_unit_productions(gramm) + + gramm = fix_non_derive_terminal(gramm) + + gramm = fix_unreachable_symbols(gramm) + + return gramm + + +# UNIT PRODUCTIONS +def get_unit_tuples(unit_productions): + change = -1 + tuples = set([(x.Left,x.Right[0]) for x in unit_productions]) + while change != len(tuples): + change = len(tuples) + to_add = set() + for t in tuples: + for q in [x for x in tuples if t[1] == x[0]]: + to_add.add((t[0],q[1])) + tuples.update(to_add) + return tuples + + +def fix_unit_productions(gramm:Grammar): + """ + returns an equivalent grammar without productions of the form:\n + A -> B + """ + gramm = gramm.copy() + + unit_productions = {x for x in gramm.Productions if len(x.Right) == 1 and x.Right[0].IsNonTerminal} + + new_productions = set() + + for x in gramm.Productions: + if not x in unit_productions: + new_productions.add(x) + + pending = get_unit_tuples(unit_productions) + + while pending: + l,r = pending.pop() + for prod in r.productions: + if not prod in unit_productions: + new_productions.add(Production(l,prod.Right)) + + return change_grammar_from_productions(gramm,new_productions) diff --git a/src/cool2/lib/grammar/grammar_parser.py b/src/cool2/lib/grammar/grammar_parser.py new file mode 100644 index 000000000..9f8aa6f5f --- /dev/null +++ b/src/cool2/lib/grammar/grammar_parser.py @@ -0,0 +1,174 @@ +from cmp.pycompiler import (EOF, Epsilon, Grammar, NonTerminal, Production, + Sentence, SentenceList, Symbol, Terminal) +from cmp.ast import Node, AtomicNode, BinaryNode, TernaryNode +from cmp.utils import Token + +G_parser = Grammar() +Q = G_parser.NonTerminal('Q', True) +non_terminals = G_parser.NonTerminals('L S X Z E K T N M') +L, S, X, Z, E, K, T, N, M = non_terminals +non_terminals = { x.Name:x for x in non_terminals } +non_terminals[Q.Name] = Q + +terminals = G_parser.Terminals('pipe sym der end comma') +pipe, sym, der, end, comma = terminals +terminals = { x.Name:x for x in terminals } +terminals['eof'] = G_parser.EOF + +symbols = non_terminals.copy() +symbols.update(terminals) + +class GramConcatLines(BinaryNode): + @staticmethod + def operate(lvalue, rvalue): + return lvalue + '\n' + rvalue + +class GramNewProdNode(TernaryNode): + @staticmethod + def operate(lvalue, mvalue, rvalue): + return lvalue + ' > ' + mvalue + rvalue + +class GramConcatProdNode(BinaryNode): + @staticmethod + def operate(lvalue, rvalue): + return ' | ' + lvalue + rvalue + +class GramConcatNode(BinaryNode): + @staticmethod + def operate(lvalue, rvalue): + if rvalue == '': + return lvalue + return lvalue + ' ' + rvalue + +class SymbolNode(AtomicNode): + def evaluate(self): + return self.lex + + +############################ BEGIN PRODUCTIONS ############################ +# ======================================================================= # +Q %= E + end + T, lambda h,s: GramConcatLines(s[1],s[3]) +T %= E + end + T, lambda h,s: GramConcatLines(s[1],s[3]) +T %= G_parser.Epsilon, lambda h,s: SymbolNode('') +E %= sym + der + L, lambda h,s: s[3], None, None, lambda h,s: SymbolNode(s[1]) +L %= S + X, lambda h,s: GramNewProdNode(h[0],s[1],s[2]) +X %= pipe + S + X, lambda h,s: GramConcatProdNode(s[2],s[3]), None, None, lambda h,s: h[0] +X %= G_parser.Epsilon, lambda h,s: SymbolNode('') +S %= sym + K, lambda h,s: GramConcatNode(SymbolNode(s[1]),s[2]) +K %= sym + K, lambda h,s: GramConcatNode(SymbolNode(s[1]),s[2]) +K %= G_parser.Epsilon, lambda h,s: SymbolNode('') +# ======================================================================= # +############################# END PRODUCTIONS ############################# + +from lib.parser.parser_ll1 import ParserLL1 +from lib.grammar.grammar_tokens import get_lexer_from_text,nonzero_digits,digits,min_letters,cap_letters,separator,regex_char_separator + +# Grammar of the parser of Grammars +gram_parser = ParserLL1(G_parser) + +expand = '~' + +# Lexer of Grammar of the parser of Grammars +terms = f'sym{separator}[{min_letters}{cap_letters}_][_{min_letters}{cap_letters}{digits}]*{regex_char_separator}pipe{separator}\\|{regex_char_separator}end{separator};{regex_char_separator}der@{expand}{regex_char_separator}space{separator} *{regex_char_separator}comma{separator},{regex_char_separator}' +gram_lexer = None #get_lexer_from_text(terms,[]) ############################## + +def fix_gram_text(gram_text,errors): + gram_text = gram_text.replace('\n','') + + new_errors = [] + tokens = get_grammar_tokens(gram_text,new_errors) + if new_errors: + errors.append('Error getting the grammar tokens') + errors.extend(new_errors) + + new_errors = [] + gramm = gram_parser.evaluate(tokens,new_errors) + if new_errors: + errors.append('Error evaluating the grammar definition') + errors.extend(new_errors) + + return gramm + +def get_grammar_from_text(gramm_text, errors): + """ + returns a Grammar form gramm_text\n + gramm_text: is the grammar written by the user\n + NonTerminal_i ~ Production1_1 | ... | Production1_N;\n + ... + NonTerminal_j ~ ProductionQ_1 | ... | ProductionZ_P;\n + """ + gramm_text = fix_gram_text(gramm_text,errors) + if not gramm_text: + return None + + G = Grammar() + dic_sym = {} + + gramm = gramm_text.split('\n') + index = 0 + + distinguish = '' + symbols = set() + non_terminals = set() + for s in gramm: + if s: + s = s.split(' > ') + if not distinguish: + distinguish = s[0] + non_terminals.add(s[0]) + symbols.add(s[0]) + for ps in s[1].split(' | '): + for q in ps.split(' '): + symbols.add(q) + terminals = symbols.difference(non_terminals) + non_terminals.remove(distinguish) + S = G.NonTerminal(distinguish,True) + dic_sym[distinguish] = S + non_ter = ' '.join(non_terminals) + non_ter = G.NonTerminals(non_ter) + for x in non_ter: + dic_sym[x.Name] = x + ter = ' '.join(terminals) + ter = G.Terminals(ter) + for x in ter: + dic_sym[x.Name] = x + + dic_sym.update({"epsilon":G.Epsilon, "$":G.EOF}) + + s = gramm[index] + index+=1 + while s != "": + s = s.split(" > ") + q = s[1].split(" | ") + for prod in q: + p = prod.split(" ") + try: + temp = dic_sym[p[0]] + except KeyError: + errors.append(f'{p[0]} is not defined in the terminals or in the non_terminals') + break + for ter in p[1:]: + try: + temp += dic_sym[ter] + except KeyError: + errors.append(f'{ter} is not defined in the terminals or in the non_terminals') + break + try: + dic_sym[s[0]] %= temp + except TypeError: + errors.append(f'A Non Terminal cant be left part of a production: {s}') + break + s = gramm[index] + index+=1 + return G + +def get_grammar_tokens(gram_def,errors:list): + tokens = [] + for x in gram_lexer(gram_def): + if x.token_type != 'space': + try: + tok = Token(x.lex,symbols[x.token_type]) + tokens.append(tok) + except KeyError: + errors.append(f'Unknown Token({x.lex},{x.token_type}) in gram_def') + return tokens diff --git a/src/cool2/lib/grammar/grammar_tokens.py b/src/cool2/lib/grammar/grammar_tokens.py new file mode 100644 index 000000000..fdce5046b --- /dev/null +++ b/src/cool2/lib/grammar/grammar_tokens.py @@ -0,0 +1,141 @@ +from cmp.pycompiler import (EOF, Epsilon, Grammar, NonTerminal, Production, + Sentence, SentenceList, Symbol, Terminal) +from cmp.ast import Node, AtomicNode, BinaryNode, TernaryNode +from cmp.utils import Token +from lib.lexer.lexer import Lexer,nonzero_digits,digits,min_letters,cap_letters +from lib.lexer.regex import fixed_tokens, EPSILON, regex_automaton + + +digits_letters = f'{digits}{min_letters}{cap_letters}' +others = f',.^;:<>=-_!^&~/' +separator = '@' +regex_char_separator = '\n' +regex_separator = f'{regex_char_separator}*' + +# Lexer that recognise the token defined by the user +lex_token = Lexer([ #################################################### + # ('term',f'[{digits_letters}]*'), + # ('regex_ex',f'{separator}[ \\\\\\[\\]\\+\\?\\*\\(\\)\\|\\{EPSILON}{digits_letters}{others}]*'), + # ('regex_sep',regex_separator), +],'eof') + +# num@12w09djqdk\nlet@[asdcsac] ==> num - regex(12w09djqdk) let - regex([asdcasac]) + +def get_lexer_from_text(text,errors:list): + """ + Returns a Lexer that recognise tokens of the form @\\n@\\n...@\\n\n + where term is the name of the terminal and regex is the regular expression of term\n + the order in text is the same that the order of the lexer table + """ + tokens = lex_token(text) + lexer_table = [] + tok_iter = iter(tokens) + new_errors = [] + while True: + try: + terminal = next(tok_iter) + except StopIteration: + new_errors.append(f'Expected: token') + break + + if terminal.token_type == 'eof': + break + + if not terminal.token_type == 'term': + new_errors.append(f'Expected: {terminal.lex}.token_type == term, Recieve: {terminal.lex}.token_type == {terminal.token_type}') + break + + try: + regex = next(tok_iter) + except StopIteration: + new_errors.append(f'Expected: token') + break + + if not regex.token_type == 'regex_ex': + new_errors.append(f'Expected: {regex.lex}.token_type == regex_ex, Recieve: {terminal.lex}.token_type == {terminal.token_type}') + break + + try: + sep = next(tok_iter) + except StopIteration: + new_errors.append(f'Expected: token') + break + + if not (sep.token_type == 'regex_sep' or sep.token_type == 'eof'): + new_errors.append(f'Expected: {sep.lex}.token_type == regex_sep or eof, Recieve: {terminal.lex}.token_type == {terminal.token_type}') + break + + lexer_table.append((terminal.lex,regex.lex[1:])) + + if sep.token_type == 'eof': + break + + for x in new_errors: + errors.append(x) + try: + next(tok_iter) + errors.append('tokens left to parse') + return None + except StopIteration: + if new_errors: + return None + return Lexer(lexer_table,'eof') + +def get_lexer_dict_from_text(text,errors:list): + """ + Returns a dictionary that recognise tokens of the form @\\n@\\n...@\\n\n + where term is the name of the terminal and regex is the regular expression of term\n + """ + tokens = lex_token(text) + lexer_table = {} + tok_iter = iter(tokens) + new_errors = [] + while True: + try: + terminal = next(tok_iter) + except StopIteration: + new_errors.append(f'Expected: token') + break + + if terminal.token_type == 'eof': + break + + if not terminal.token_type == 'term': + new_errors.append(f'Expected: {terminal.lex}.token_type == term, Recieve: {terminal.lex}.token_type == {terminal.token_type}') + break + + try: + regex = next(tok_iter) + except StopIteration: + new_errors.append(f'Expected: token') + break + + if not regex.token_type == 'regex_ex': + new_errors.append(f'Expected: {regex.lex}.token_type == regex_ex, Recieve: {terminal.lex}.token_type == {terminal.token_type}') + break + + try: + sep = next(tok_iter) + except StopIteration: + new_errors.append(f'Expected: token') + break + + if not (sep.token_type == 'regex_sep' or sep.token_type == 'eof'): + new_errors.append(f'Expected: {sep.lex}.token_type == regex_sep or eof, Recieve: {terminal.lex}.token_type == {terminal.token_type}') + break + + lexer_table[terminal.lex] = regex_automaton(regex.lex[1:]) + + if sep.token_type == 'eof': + break + + for x in new_errors: + errors.append(x) + try: + next(tok_iter) + errors.append('tokens left to parse') + return None + except StopIteration: + if new_errors: + return None + return lexer_table diff --git a/src/cool2/lib/grammar/grammar_util.py b/src/cool2/lib/grammar/grammar_util.py new file mode 100644 index 000000000..94ed80d0c --- /dev/null +++ b/src/cool2/lib/grammar/grammar_util.py @@ -0,0 +1,21 @@ +from cmp.pycompiler import (EOF, Epsilon, Grammar, NonTerminal, Production, + Sentence, SentenceList, Symbol, Terminal) +from lib.grammar.grammar_parser import expand + + +def grammar_to_text(G:Grammar): + gram = '' + for n in G.nonTerminals: + gram += n.Name + f' {expand} ' + for p in n.productions: + if p.Right.IsEpsilon: + gram += ' epsilon ' + else: + for s in p.Right: + gram += f' {s.Name} ' + gram += ' | ' + gram = gram[:len(gram)-2] + gram += ';\n' + + return gram + \ No newline at end of file diff --git a/src/cool2/lib/lang/language.py b/src/cool2/lib/lang/language.py new file mode 100644 index 000000000..50e763c3a --- /dev/null +++ b/src/cool2/lib/lang/language.py @@ -0,0 +1,59 @@ +from cmp.utils import Token + +class Language(): + + parser = None + + grammar = None + + lexer = None + + def __init__(self, grammar,lexer,token_parse_dict={}): + self.grammar = grammar + self.lexer = lexer + self.token_parse = token_parse_dict + + def _fix_tokens(self,tokens,errors): + """ + If there are a token_type named 'space' this are discarted from the parsing tokens\n + also transform lexer tokens to grammar tokens + """ + fix_tokens = [] + for x in tokens: + if x.token_type != 'space': + try: + if x.token_type in self.token_parse: + tok = Token(self.token_parse[x.token_type](x.lex),x.token_type) + else: + tok = Token(x.lex,x.token_type) + fix_tokens.append(tok) + except KeyError: + errors.append(f'The grammar does not recognize the token {x}') + return fix_tokens + + def __call__(self, text, errors): + """ + returns a tuple of the parse and the tokens of the text + """ + + tokens = self.lexer(text) + tokens = self._fix_tokens(tokens,errors) + + parse_errors = [] + parse = self.parser(tokens,parse_errors) + + for x in parse_errors: + errors.append(x) + + if parse_errors: + return [],[] + + return parse,tokens + + def find_conflict(self): + return self.parser.find_conflict() + + def evaluate(self, text, errors:list,return_ast = False): + # tokens = [ x for x in self.lexer(text) if x.token_type != 'space'] + tokens = self._fix_tokens(self.lexer(text),errors) + return self.parser.evaluate(tokens,errors,return_ast) diff --git a/src/cool2/lib/lang/language_ll1.py b/src/cool2/lib/lang/language_ll1.py new file mode 100644 index 000000000..fc8654b0f --- /dev/null +++ b/src/cool2/lib/lang/language_ll1.py @@ -0,0 +1,62 @@ +from cmp.pycompiler import (EOF, Epsilon, Grammar, NonTerminal, Production, + Sentence, SentenceList, Symbol, Terminal, SentenceFromIter) +from cmp.utils import ContainerSet, inspect, pprint, Token +from lib.grammar.grammar_tokens import get_lexer_from_text +from lib.grammar.grammar_parser import get_grammar_from_text +from lib.lexer.lexer import digits,min_letters,cap_letters +from lib.parser.parser_ll1 import ParserLL1 +from lib.grammar.grammar_fixer import fix_grammar +from lib.lang.language import Language +class LanguageLL1(Language): + + def __init__(self, grammar,lexer): + super().__init__(grammar,lexer) + self.parser = ParserLL1(self.grammar) + + @property + def get_firsts(self): + return self.parser.get_firsts + + @property + def get_follows(self): + return self.parser.get_follows + + @property + def get_predictive_table(self): + return self.parser.get_parser_table + + def get_derivation_tree(self,parse): + return self.parser.get_derivation_tree(parse) + +def build_LL1(tokens_def, grammar_def, errors:list): + """ + token_def:\n + The definition of the non terminal symbols of the grammar\n + (the name must be equal to the non terminals written in grammar_def)\n + token_def --> same format than text in get_lexer function\n + token_def can be an instance of Lexer in case of already have it\n + grammar_def:\n + The definition by the user of the grammar in this format:\n + Production1;Production2;...ProductionN;\n + A production can be: T ~ A bc |r3 | epsilon\n + grammar_def can be an instance of Grammar in case of already have it\n + return None if the grammar is not LL1\n + return the LanguageLL1 defined in grammar_def \n + """ + new_errors = [] + lex_gram = get_lexer_from_text(tokens_def,new_errors) if isinstance(tokens_def,str) else tokens_def + Gramm = get_grammar_from_text(grammar_def,new_errors) if isinstance(grammar_def,str) else grammar_def + # Gramm = fix_grammar(Gramm,errors) + + if not Gramm or not lex_gram: + for x in new_errors: + errors.append(x) + return None + ll1 = LanguageLL1(Gramm,lex_gram) + for x in ll1.parser.errors: + new_errors.append(x) + for x in new_errors: + errors.append(x) + # if new_errors: + # return None + return ll1 diff --git a/src/cool2/lib/lang/language_lr.py b/src/cool2/lib/lang/language_lr.py new file mode 100644 index 000000000..959b87bf6 --- /dev/null +++ b/src/cool2/lib/lang/language_lr.py @@ -0,0 +1,53 @@ +from lib.parser.parser_lr import LR0Parser,LR1Parser,SLR1Parser,LALR1Parser +from lib.grammar.grammar_parser import get_grammar_from_text +from lib.grammar.grammar_tokens import get_lexer_from_text +from lib.lang.language import Language + +def return_lr_parser(G,parser): + parser = parser.lower() + if parser == 'lr0': + lr = LR0Parser(G) + elif parser == 'lalr1': + lr = LALR1Parser(G) + elif parser == 'slr1': + lr = SLR1Parser(G) + elif parser == 'lr1': + lr = LR1Parser(G) + else: + lr = None + return lr + +def build_LR(tok_def,gram_def,lr:str,errors): + """ + gram_def: grammar definition or an instance of Grammar\n + tok_def: tokens deefinition or an instance of Lexer\n + lr: type of lr parser, can be ['lalr1', 'lr1', 'lr0', 'slr1']\n + return a LanguageLR\n + return None in case of errors + """ + new_errors = [] + + lex_gram = get_lexer_from_text(tok_def,new_errors) if isinstance(tok_def,str) else tok_def + Gramm = get_grammar_from_text(gram_def,new_errors) if isinstance(gram_def,str) else gram_def + + for x in new_errors: + errors.append(x) + if lex_gram and Gramm: + lr = return_lr_parser(Gramm,lr) + if not lr: + errors.append(f'{lr} is not a valid LR type of parser, try [lalr1, lr1, lr0, slr1]') + return None + if lr.errors: + errors.extend(lr.errors) + # return None + return LanguageLR(Gramm,lex_gram,lr) + return None + +class LanguageLR(Language): + + def __init__(self, grammar, lexer, parser_lr,token_parse_dict={}): + super().__init__(grammar,lexer,token_parse_dict) + self.parser = parser_lr + + def get_derivation_tree(self,parse): + return self.parser.get_derivation_tree(parse,False) diff --git a/src/cool2/lib/lang/language_regular.py b/src/cool2/lib/lang/language_regular.py new file mode 100644 index 000000000..1e233fabe --- /dev/null +++ b/src/cool2/lib/lang/language_regular.py @@ -0,0 +1,397 @@ +from cmp.pycompiler import (EOF, Epsilon, Grammar, NonTerminal, Production, + Sentence, SentenceFromIter, SentenceList, Symbol, + Terminal) +from cmp.utils import ContainerSet, Token, inspect, pprint +from lib.grammar.grammar_parser import get_grammar_from_text +from lib.grammar.grammar_tokens import get_lexer_dict_from_text,get_lexer_from_text +from lib.lang.language_lr import LanguageLR,return_lr_parser +from lib.lexer.lexer import cap_letters, digits, min_letters +from lib.lexer.regex import EPSILON, regex_automaton +from lib.utils.automaton import * + +def copy_automaton(base_automaton): + """ + Returns a tuple of base_automaton properties\n + start, states, finals, transitions + """ + start = base_automaton.start + dif = base_automaton.states + finals = base_automaton.finals.copy() + + transitions = {} + for x in base_automaton.transitions: + for y in base_automaton.transitions[x]: + transitions[x,y] = base_automaton.transitions[x][y] + + return start,dif,finals,transitions + +def join(raw_automaton,raw_automaton_join,state1,state2): + """ + raw_automaton, raw_automaton_join: tuples of copy_automatons\n + state1, state2: states of raw_automaton\n + insert raw_automaton_join in raw_automaton between state1 and state2\n + ______ ______ + (state1)--[symb in transitions[start of join automaton]]-->raw_automaton_join--e-->(state2) + """ + start, dif, finals, transitions = raw_automaton + j_start, j_dif, j_finals, j_transitions = raw_automaton_join + + for symb in [s for x,s in j_transitions if x == j_start]: + try: + transitions[state1,symb].append(j_transitions[j_start,symb][0] + dif) + except KeyError: + transitions[state1,symb] = [j_transitions[j_start,symb][0] + dif] + + for f in j_finals: + transitions[f+dif,''] = [state2] + + for state,symb in j_transitions: + transitions[state + dif,symb] = [j_transitions[state,symb][0] + dif] + + return start,dif+j_dif,finals,transitions + +def join_automaton(base_automaton,lex_dic): ################ convertir los States de lex_dic en DFA + """ + base_automaton: automaton from grammar, (deterministic) + lex_dic: dictionary that maps the terminals to the automaton that recognize it + """ + + start, dif, finals, transitions = copy_automaton(base_automaton) + fixed_transitions = transitions.copy() + transitions = {} + raw = start, dif, finals, transitions + cached_automaton = {} + + for state,terminal in fixed_transitions: + try: + au = cur_start, cur_states, cur_finals, cur_transitions = cached_automaton[terminal] + except KeyError: + automaton = lex_dic[terminal] + au = cur_start, cur_states, cur_finals, cur_transitions = copy_automaton(automaton) + cached_automaton[terminal] = au + + raw = join(raw,au,state,fixed_transitions[state,terminal][0]) + + nfa = NFA(raw[1],raw[2],raw[3],raw[0]) + dfa = nfa_to_dfa(nfa) + mini = automata_minimization(dfa) + + return mini + +def build_automaton_from_grammar(tok_def,gram_def,errors,lexer=None,grammar=None): + """ + Return the NFA if the grammar is regular + else return None + """ + new_errors = [] + + lex = get_lexer_from_text(tok_def,errors) if not lexer else lexer + G = get_grammar_from_text(gram_def,errors) if not grammar else grammar + + if G == None or lex == None: + errors.append('Invalid grammar or token definition') + return None + + if not check_grammar_regular(G, errors): + errors.append('Invalid Regular Grammar') + return None + + symb_num = {} + transitions = {} + itr1 = iter(range(len(G.nonTerminals))) + for x in G.nonTerminals: + symb_num[x] = next(itr1) + + start = symb_num[G.startSymbol] + finals = [len(G.nonTerminals)] + + distinguish_on_epsilon = any([x for x in G.startSymbol.productions if x.IsEpsilon]) + if distinguish_on_epsilon: finals.append(start) + + for prod in G.Productions: + left = symb_num[prod.Left] + len_right = len(prod.Right) + first_symbol = prod.Right[0] + if len_right == 2: + try: + transitions[left,first_symbol.Name].add(symb_num[prod.Right[1]]) + except KeyError: + transitions[left,first_symbol.Name] = {symb_num[prod.Right[1]]} + elif len_right == 1: + try: + transitions[left,first_symbol.Name].add(finals[0]) + except KeyError: + transitions[left,first_symbol.Name] = {finals[0]} + + nfa = NFA(len(G.nonTerminals)+1,finals,transitions,start) + dfa = nfa_to_dfa(nfa) + mini = automata_minimization(dfa) + + return mini # join_automaton(mini,lex.regexs) ############################################## + +def check_grammar_regular(G:Grammar,errors): + """ + return True if the Grammar G is regular + """ + new_errors = [] + for prod in G.Productions: + left = prod.Left + right = prod.Right + if right.IsEpsilon : + if left != G.startSymbol: + new_errors.append(f'Invalid Production for Regular Grammar: {prod}; the only left part that can have an epsilon on right part is the distiguish state') + continue + if len(right) > 2: + new_errors.append(f'Invalid Production for Regular Grammar: {prod}; the lenght of right part must be at most 2 and is {len(right)}') + else: + if not right[0].IsTerminal: + new_errors.append(f'Invalid Production for Regular Grammar: {prod}; the first symbol on a right part must be a terminal') + if len(right) == 2: + if right[1].IsTerminal: + new_errors.append(f'Invalid Production for Regular Grammar: {prod}; the second symbol on a right part must be a non terminal') + errors.extend(new_errors) + return not bool(new_errors) + +# REGULAR GRAMMAR TO REGEX + +def eliminate_state(tupled_automaton,s): + start,states,finals,transitions = tupled_automaton + + assert s != start,"you cant eliminate the start state" + + start = start if start < s else start - 1 + try: + finals.remove(s) + except: + pass + finals = { x for x in map(lambda x: x if x < s else x-1,finals)} + + exit_s = [] + enter_s = [] + other = [] + new_transitions = {} + cycle = '' + + for x in transitions.items(): # cycles in s dont go anywhere just 'activate' cycle variable + (state,value),regex = x + if state == s: + if value == s: + cycle = f'({regex})*' + continue + exit_s.append(x) + elif value == s: + enter_s.append(x) + else: + other.append(x) + + for (state0, value0),regex0 in enter_s: + for (state1, value1),regex1 in exit_s: + third = first(lambda x: x[0][0] == state0 and x[0][1] == value1,other) + if third: + other.remove(third) + transition = f"{third[1]}|" if third else "" + state = state0 if state0 < s else state0 - 1 + value = value1 if value1 < s else value1 - 1 + new_transitions[state,value] = f"({transition}{regex0}{cycle}{regex1})" + + for (state0, value0),regex0 in other: + state0 = state0 if state0 < s else state0 - 1 + value0 = value0 if value0 < s else value0 - 1 + if [x for (state,value), regex in new_transitions.items() if state == state0 and value == value0]: + print("WTF!!") + new_transitions[state0,value0] = regex0 + + return start,states-1,finals,new_transitions + +def eliminate_all_non_start_non_finals(tupled_automaton): + """ + return a tuple of start,states,finals,transitions of the automaton from eliminate all the \n + non start and the non finals states + """ + start,states,finals,transitions = tupled_automaton + change = True + while change: + change = False + for s in range(states): + if not s in finals and s != start: + change = True + tupled_automaton = start,states,finals,transitions = eliminate_state(tupled_automaton,s) + break + return tupled_automaton + +def eliminate_all_non_start_non_t(tupled_automaton,t): + """ + return a tuple of start,states,finals,transitions of the automaton from eliminate all the \n + non start and the non t states + """ + start,states,finals,transitions = tupled_automaton + change = True + while change: + change = False + for s in range(states): + if s != t and s != start: + change = True + tupled_automaton = start,states,finals,transitions = eliminate_state(tupled_automaton,s) + t = t if s > t else t-1 + break + return tupled_automaton + +def get_key(start,end,transitions): + # key = first(lambda x: x[0][0] == start and x[1] == end,transitions.items()) + try: + key = transitions[start,end] + except KeyError: + key = '' + return key#[0][1] if key else '' + +def get_regex_from_reduced_automaton(tupled_automaton): + """ + tupled_automaton: an automaton with de form:\n + (A)--regex->(A)<-regex_left regex_right->(B)<-regex--(B)\n + or\n + (AB)--regex->(AB)\n + where A is start and B is final\n + """ + start,states,finals,transitions = tupled_automaton + if states == 2: + other = 0 if start == 1 else 1 + A = get_key(start,start,transitions) + B = get_key(start,other,transitions) + C = get_key(other,other,transitions) + D = get_key(other,start,transitions) + if not B: + return '' + if A and C and D: + return f"({A}|{B}{C}*{D})*{B}{C}*" + if A and D: + return f"({A}|{B}{D})*{B}" + if A and C: + return f"{A}*{B}{C}*" + if A: + return f"{A}*{B}" + if C and D: + return f"({B}{C}*{D})*{B}{C}*" + if C: + return f"{B}{C}*" + if D: + return f"({B}{D})*{B}" + return f"{B}" + if states == 1: + assert start in finals, "start is not a final state" + A = get_key(start,start,transitions) + if A: + return f"{A}*" + else: + return '' + assert False, "Wrong number of states in the tupled automaton" + +def regex_from_clean_automaton(tupled_automaton,s): + """ + tupled_automaton: an automaton with only finals states and the start state + s: final state to find regex + return the regex from eliminate all the states except the start state and the s state + """ + tupled_automaton = eliminate_all_non_start_non_t(tupled_automaton,s) + + return get_regex_from_reduced_automaton(tupled_automaton) + +def convert_to_regex_automaton(tupled_automaton): + """ + join al the transitions from state x to state y in one equivalent regex\n + example:\n + (A)-a,b,c->(B) => (A)-a|b|c->(B) + """ + start,states,finals,transitions = tupled_automaton + + temp_dic = {} + for (state,symbol),values in transitions.items(): + for value in values: + try: + regex = temp_dic[state,value] + regex = regex[:len(regex)-1] + f"|{symbol})" + except KeyError: + regex = f'({symbol})' + temp_dic[state,value] = regex + + transitions = temp_dic + + return start,states,finals,transitions + +def build_regex_from_grammar(tok_def,gram_def,errors:list,lexer=None,grammar=None,automaton=None): + """ + return the regex and the automaton from tok_def and gram_def or from lexer and grammar if automaton is not given\n + else return the regex of the automaton given + """ + new_errors = [] + + if not automaton: + automaton = build_automaton_from_grammar(tok_def,gram_def,new_errors,lexer,grammar) + if not automaton: + errors.extend(new_errors) + return None + + tupled_automaton = copy_automaton(automaton) + tupled_automaton = convert_to_regex_automaton(tupled_automaton) + tupled_automaton = start,states,finals,transitions = eliminate_all_non_start_non_finals(tupled_automaton) + iter_finals = finals.copy() + regex = [] + for s in iter_finals: + new_transitions = transitions.copy() + new_finals = finals.copy() + automaton = start,states,new_finals,new_transitions + new_regex = regex_from_clean_automaton(automaton,s) + regex.append(new_regex) + + if new_errors: + errors.extend(new_errors) + + regex = '|'.join(regex) + + tupl = regex_automaton(regex,return_regex=True) + return tupl[1] + + + +def build_Lan_Reg(tok_def,gram_def,errors): + """ + gram_def: grammar definition or an instance of Grammar\n + tok_def: tokens deefinition or an instance of Lexer\n + return a LanguageRegular\n + return None in case of errors + """ + lexer = get_lexer_from_text(tok_def,errors) if isinstance(tok_def,str) else tok_def + grammar = get_grammar_from_text(gram_def,errors) if isinstance(gram_def,str) else gram_def + + if not grammar or not lexer: + errors.append('Invalid grammar or token definition') + return None + + if not check_grammar_regular(grammar,errors): + errors.append('Invalid Regular Grammar') + return None + + return LanguageRegular(grammar,lexer) + +class LanguageRegular(LanguageLR): + + grammar_regex = None + + grammar_automaton = None + + def __init__(self, grammar, lexer): + super().__init__(grammar,lexer,None) + + def grammar_to_regex(self,errors): + self.grammar_regex = self.grammar_regex if self.grammar_regex else build_regex_from_grammar('','',errors,self.lexer,self.grammar,self.grammar_to_automaton(errors)) + return self.grammar_regex + + def grammar_to_automaton(self,errors): + self.grammar_automaton = self.grammar_automaton if self.grammar_automaton else build_automaton_from_grammar('','',errors,self.lexer,self.grammar) + return self.grammar_automaton + + def __call__(self, string,errors): + automaton = self.grammar_to_automaton(errors) + if automaton: + return automaton.recognize(string),None + return None,None + diff --git a/src/cool2/lib/lexer/lexer.py b/src/cool2/lib/lexer/lexer.py new file mode 100644 index 000000000..95f3f8bdb --- /dev/null +++ b/src/cool2/lib/lexer/lexer.py @@ -0,0 +1,116 @@ + +from cmp.utils import Token +from cmp.automata import State +from lib.lexer.regex import regex_automaton + +class DetailToken(Token): + def __init__(self, lex,token_type): + super().__init__(lex,token_type) + self.row = lex[1] + self.column = lex[2] + +class Lexer: + def __init__(self, table, eof): + self.eof = eof + self.regexs = self._build_regexs(table) + self.automaton = self._build_automaton() + self.table = { x:y for x,y in table} + + def _build_regexs(self, table): + regexs = {} + for n, (token_type, regex) in enumerate(table): + # Your code here!!! + # - Remember to tag the final states with the token_type and priority. + # - .tag might be useful for that purpose ;-) + auto = regex_automaton(regex) + auto, states = State.from_nfa(auto,True) + for st in states: + if st.final: + st.tag = (n,token_type) + regexs[token_type] = auto + return regexs + + def _build_automaton(self): + start = State('start') + # Your code here!!! + for x in self.regexs: + start.add_epsilon_transition(self.regexs[x]) + return start.to_deterministic() + + def _walk(self, string): + state = self.automaton + final = state if state.final else None + final_lex = lex = '' + + for symbol in string: + # Your code here!!! + if state.has_transition(symbol): + state = state[symbol][0] + if state.final: + final = state + final_lex = lex + # else: + # final = None + else: + final_lex = lex + break + lex += symbol + else: + final_lex = lex + + return final, final_lex + + def _tokenize(self, text): + + while text: + stop_state, lex = self._walk(text) + + if stop_state and len(lex)>0: + st_min = min([st.tag for st in [ final for final in stop_state.state if final.final]]) + yield lex,st_min[1] + else: + if len(lex) == 0: + lex = text[0] + yield lex,'UNKNOWN' + + text = text[len(lex):] + + yield '$', self.eof + + def __call__(self, text): + return [ Token(lex, ttype) for lex, ttype in self._tokenize(text) ] + +class DetailLexer(Lexer): + + def _tokenize(self, text): + row,column = 0,0 + while text: + stop_state, lex = self._walk(text) + + rows_in_lex = len([x for x in lex if x == '\n']) + row += rows_in_lex + if rows_in_lex: + column = len(lex.split('\n')[-1]) + else: + column += len(lex) + + if stop_state and len(lex)>0: + st_min = min([st.tag for st in [ final for final in stop_state.state if final.final]]) + yield (lex,row,column),st_min[1] + else: + if len(lex) == 0: + lex = text[0] + yield (lex,row,column),'UNKNOWN' + + text = text[len(lex):] + + yield ('$',row,column), self.eof + + def __call__(self, text): + return [ DetailToken(lex, ttype) for lex, ttype in self._tokenize(text) ] + + +nonzero_digits = '[123456789]' +min_letters = '[abcdefghijklmnopqrstuvwxyz]' +cap_letters = '[ABCDEFGHIJKLMNOPQRSTUVWXYZ]' +digits = f'({nonzero_digits}|0)' diff --git a/src/cool2/lib/lexer/regex.py b/src/cool2/lib/lexer/regex.py new file mode 100644 index 000000000..567467c2e --- /dev/null +++ b/src/cool2/lib/lexer/regex.py @@ -0,0 +1,147 @@ +from cmp.ast import Node, AtomicNode, UnaryNode, BinaryNode +from cmp.utils import Token +from cmp.pycompiler import (EOF, Epsilon, Grammar, NonTerminal, Production, + Sentence, SentenceList, Symbol, Terminal) +from lib.utils.automaton import * +from lib.parser.parser_ll1 import ParserLL1 + + +G = Grammar() + +EPSILON = 'ε' + +E = G.NonTerminal('E', True) +T, F, A, X, Y, Z, R, Q = G.NonTerminals('T F A X Y Z R Q') +pipe, star, opar, cpar, symbol, epsilon, plus, qtn, obra, cbra = G.Terminals(f'| * ( ) symbol {EPSILON} + ? [ ]') + +# Your code here!!! +E %= T + X, lambda h,s: s[2], None,lambda h,s: s[1] + +X %= pipe + T + X, lambda h,s: UnionNode(h[0],s[3]), None, None,lambda h,s: s[2] +X %= G.Epsilon, lambda h,s: h[0] + +T %= A + Y, lambda h,s: s[2], None,lambda h,s: s[1] + +Y %= A + Y, lambda h,s: ConcatNode(h[0],s[2]), None,lambda h,s: s[1] +Y %= G.Epsilon, lambda h,s: h[0] + +A %= Z + F, lambda h,s: s[2] ,None ,lambda h,s: s[1] + +F %= star + F, lambda h,s: s[2], None, lambda h,s: ClosureNode(h[0]) +F %= plus + F, lambda h,s: s[2],None,lambda h,s: PlusNode(h[0]) +F %= qtn + F, lambda h,s: s[2], None, lambda h,s: QuestionNode(h[0]) +F %= G.Epsilon, lambda h,s: h[0] + +R %= A + Q, lambda h,s: s[2], None,lambda h,s: s[1] + +Q %= A + Q, lambda h,s: UnionNode(h[0],s[2]), None, lambda h,s: s[1] +Q %= G.Epsilon, lambda h,s: h[0] + +Z %= symbol, lambda h,s: SymbolNode(s[1]), None +Z %= opar + E + cpar, lambda h,s: s[2], None, None, None +Z %= obra + R + cbra, lambda h,s: s[2], None, None, None +Z %= epsilon, lambda h,s: EpsilonNode(s[1]), None + + +regex_parser = ParserLL1(G) + +class EpsilonNode(AtomicNode): + def evaluate(self): + automata = NFA(1,{0},{},0) + return '',automata + +class SymbolNode(AtomicNode): + def evaluate(self): + s = self.lex + # Your code here!!! + automata = NFA(2,{1},{(0,s):[1]},0) + return f'{s}',automata + +class ClosureNode(UnaryNode): + @staticmethod + def operate(value): + # Your code here!!! + return f'({value[0]})*',automata_closure(value[1]) + +class UnionNode(BinaryNode): + @staticmethod + def operate(lvalue, rvalue): + # Your code here!!! + return f'({lvalue[0]}|{rvalue[0]})',automata_union(lvalue[1],rvalue[1]) + +class ConcatNode(BinaryNode): + @staticmethod + def operate(lvalue, rvalue): + # Your code here!!! + return f'{lvalue[0]}{rvalue[0]}',automata_concatenation(lvalue[1],rvalue[1]) + +class PlusNode(UnaryNode): + @staticmethod + def operate(value): + # Your code here!!! + return f'({value[0]})+',automata_concatenation(value[1],automata_closure(value[1])) + +class QuestionNode(UnaryNode): + @staticmethod + def operate(value): + # Your code here!!! + epsilon = NFA(1,{0},{},0) + return f'({value[0]})?',automata_union(value[1],epsilon) + +fixed_tokens = { + '*' : Token('*' , star), + '(' : Token('(' , opar), + ')' : Token(')' , cpar), + '|' : Token('|' , pipe), + '?' : Token('?' , qtn), + '+' : Token('+' , plus), + '[' : Token('[' , obra), + ']' : Token(']' , cbra), + EPSILON : Token(EPSILON , epsilon), + } + +def regex_tokenizer(text, G, skip_whitespaces=True): + tokens = [] + # > fixed_tokens = ??? + # Your code here!!! + + skip = False + for i, char in enumerate(text): + + if skip: + skip = False + continue + + if skip_whitespaces and char.isspace(): + continue + # Your code here!!! + if char == '\\': + try: + tokens.append(Token(text[i+1],symbol)) + except IndexError: + tokens.append(Token('\\',symbol)) + skip = True + continue + + try: + tokens.append(fixed_tokens[char]) + except KeyError: + tokens.append(Token(char,symbol)) + + tokens.append(Token('$', G.EOF)) + return tokens + +def regex_automaton(regex,skip_whitespces=False,return_regex = False): + """ + returns the minimal dfa that recognize regex + """ + tokens = regex_tokenizer(regex,G,skip_whitespces) + errors = [] + regex,nfa = regex_parser.evaluate(tokens,errors) + if nfa: + dfa = nfa_to_dfa(nfa) + mini = automata_minimization(dfa) + if return_regex: + return mini,regex + return mini + return None \ No newline at end of file diff --git a/src/cool2/lib/parser/__init__.py b/src/cool2/lib/parser/__init__.py new file mode 100644 index 000000000..e69de29bb diff --git a/src/cool2/lib/parser/parser.py b/src/cool2/lib/parser/parser.py new file mode 100644 index 000000000..7e395d858 --- /dev/null +++ b/src/cool2/lib/parser/parser.py @@ -0,0 +1,44 @@ +from cmp.automata import State +class Parser(): + + def __call__(self,tokens,errors): + raise NotImplementedError() + + def find_conflit(self): + ''' + return a list of conflicts + ''' + raise NotImplementedError() + + def evaluate(self,tokens,errors,return_ast=False): + raise NotImplementedError() + + def get_derivation_tree(self,parse_list, left_to_right=True): + if not left_to_right: parse_list.reverse() + + node = State(parse_list[0].Left) + self.build_tree(node,[0],parse_list,left_to_right) + return node.graph('TD') + + def build_tree(self,current_node, index, parse_list, left_to_right): + + new_nodes = [] + if parse_list[index[0]].Right: + for x in parse_list[index[0]].Right: + new_nodes.append(State(x)) + else: + new_nodes.append(State(parse_list[index[0]].Right)) + + if not left_to_right: new_nodes.reverse() + + for x in new_nodes: + current_node.add_transition('',x) + if x.state.IsNonTerminal: + index[0] += 1 + self.build_tree(x,index,parse_list,left_to_right) + + if not left_to_right: + try: + current_node.transitions[''].reverse() + except: + pass diff --git a/src/cool2/lib/parser/parser_ll1.py b/src/cool2/lib/parser/parser_ll1.py new file mode 100644 index 000000000..f1467747a --- /dev/null +++ b/src/cool2/lib/parser/parser_ll1.py @@ -0,0 +1,332 @@ +from cmp.pycompiler import (EOF, Epsilon, Grammar, NonTerminal, Production, + Sentence, SentenceList, Symbol, Terminal) +from cmp.utils import ContainerSet, inspect, pprint, Token +from lib.utils.first_follow import compute_firsts, compute_follows, compute_local_first +from lib.parser.parser import Parser +from lib.grammar.grammar_fixer import fix_non_derive_terminal, build_reach + +class ParserLL1(Parser): + + grammar = None + + firsts = None + + follows = None + + parse_table = None + + parser = None + + errors = [] + + eval_errors = [] + + def __init__(self, G:Grammar): + """ + When created check for grammar errors in errors + """ + self.grammar = G + + @property + def get_follows(self): + if self.follows is None: + self.firsts = self.get_firsts + self.follows = compute_follows(self.grammar,self.firsts) + return self.follows + + @property + def get_firsts(self): + if self.firsts is None: + self.firsts = compute_firsts(self.grammar) + return self.firsts + + @property + def get_parser_table(self): + if self.parse_table is None: + self.parse_table = self.build_parsing_table() + return self.parse_table + + def __call__(self,tokens,errors): + if not self.parser: + self.parser = self.metodo_predictivo_no_recursivo() + return self.parser(tokens,errors) + + def build_parsing_table(self): + # init parsing table + M = {} + + G = self.grammar + firsts = self.get_firsts + follows = self.get_follows + + # P: X -> alpha + for production in G.Productions: + X = production.Left + alpha = production.Right + + ################################################### + # working with symbols on First(alpha) ... + ################################################### + # # + for t in firsts[alpha]: + try: + a = M[X,t] + if type(a) == type(list()): + self.errors.append(\ + f'Grammar Ambiguity: M[{X},{t}] = {a}, and also must be M[{X},{t}] = {alpha}') + if not alpha in a: + a.append(alpha) + elif a != alpha: + self.errors.append(\ + f'Grammar Ambiguity: M[{X},{t}] = {a}, and also must be M[{X},{t}] = {alpha}') + M[X,t] = [a,alpha] + except KeyError: + M[X,t] = alpha + ################################################### + + ################################################### + # working with epsilon... + ################################################### + # # + if firsts[alpha].contains_epsilon: + for t in follows[X]: + try: + a = M[X,t] + if a != alpha: + self.errors.append(\ + f'Grammar Ambiguity: M[{X},{t}] = {a}, and also must be M[{X},{t}] = {alpha}') + if type(a) == type(list()): + a.append(alpha) + else: + M[X,t] = [a,alpha] + except KeyError: + M[X,t] = alpha + ################################################### + + # parsing table is ready!!! + return M + + def return_prod(self, left,right): + for i,x in enumerate(left.productions,0): + if x.Right == right: + return left.productions[i]; + return None + + def metodo_predictivo_no_recursivo(self): + + G = self.grammar + M = self.get_parser_table + firsts = self.get_firsts + follows = self.get_follows + + # parser construction... + def parser(tokens, parser_errors:list): + + ################################################### + # tokens ends with $ (G.EOF) + ################################################### + # init: + stack = [G.EOF,G.startSymbol] + + cursor = 0 + output = [] + ################################################### + + # parsing tokens... + # print((top, a)) + + ################################################### + # # + while cursor < len(tokens): + top = stack.pop() + a = tokens[cursor] + prod = None + if top.IsTerminal: + if a.token_type == top: + cursor+=1 + continue + else: + parser_errors.append(f'Expected: {top}, Recieve: {a.token_type}') + break + try: + prod = M[top,a.token_type] + except KeyError: + parser_errors.append(f'M[{top},{a.token_type}] generate nothing: expected {[ x[1] for x in M if x[0] == top ]}') + break + + if a.token_type in firsts[prod]: + rev = prod[::-1] + for t in rev: + stack.append(t) + output.append(self.return_prod(top,prod)) + + elif prod.IsEpsilon and a.token_type in follows[top]: + output.append(self.return_prod(top,prod)) + else: + parser_errors.append(f'{a.token_type} Not in follows[{top}]') + break + + ################################################### + + # left parse is ready!!! + return output + + # parser is ready!!! + return parser + + def _evaluate(self, production, left_parse, tokens, inherited_value=None): + head, body = production + attributes = production.attributes + + # Insert your code here ... + # > synteticed = ... + synteticed = [None for _ in attributes] + # > inherited = ... + inherited = [None]*(len(body)+1) + # Anything to do with inherited_value? + inherited[0] = inherited_value + + for i, symbol in enumerate(body, 1): + if symbol.IsTerminal: + if not inherited[i] is None: + self.eval_errors.append(f'Terminals cant have inherited values. Terminal:{symbol}; Inherited:{inherited[i]}') + break + # Insert your code here ... + try: + synteticed[i] = next(tokens).lex # como es un terminal se sintetiza como el mismo + except StopIteration: + self.eval_errors.append(f'tokens stopped the iteration') + break + else: + try: + next_production = next(left_parse) + except StopIteration: + self.eval_errors.append(f'left_parse stopped the iteration') + break + if not symbol == next_production.Left: + self.eval_errors.append(f'{symbol} doesnt match with {next_production.Left}. Cant expand {symbol}') + break + # Insert your code here ... + if attributes[i]: + inherited[i] = attributes[i](inherited, synteticed) + synteticed[i] = self._evaluate(next_production, left_parse, tokens, inherited[i]) + + # Insert your code here ... + if attributes[0]: + synteticed[0] = attributes[0](inherited, synteticed) + # > return ... + return synteticed[0] + + def evaluate_parse(self, left_parse, tokens): + self.eval_errors.clear() + if not left_parse or not tokens: + self.eval_errors.append('Empty tokens or Empty left_parse') + return None + + left_parse = iter(left_parse) + tokens = iter(tokens) + result = self._evaluate(next(left_parse), left_parse, tokens) + + if not isinstance(next(tokens).token_type, EOF): + self.eval_errors.append('Last parsed token doesnt match with EOF') + return None + return result + + def evaluate(self,tokens,errors:list): + """ + If no errors then returns the evaluated tokens\n + else fills errors with errors returning None + """ + parser = self.metodo_predictivo_no_recursivo() + new_errors = [] + left_parse = parser(tokens,new_errors) + if not new_errors: + ast = self.evaluate_parse(left_parse, tokens) + if self.eval_errors: + for x in self.eval_errors: + errors.append(x) + return None + return ast.evaluate() + else: + for x in new_errors: + errors.append(x) + return None + + def find_conflict(self): + table = self.get_parser_table + conflicts = [] + for x in table: + if type(table[x]) == type([1,2]): + grammar,derive = fix_non_derive_terminal(self.grammar,True) + visited = set([grammar.startSymbol,]) + conflict = self._find_conflict(build_reach(self.grammar,x[0]), grammar.startSymbol,x,derive,visited,[]) + conflicts.append(conflict) + return conflicts + + def _find_conflict(self, reach, current, searching, derive, visited, productions): + """ + si X in reach => X puede alcanzar a searching + derive[X] producciones para hacer X una oracion + searching llave de la tabla que da problemas + productions producciones que va llevando + """ + if current == searching[0]: + return self._generate_conflict_error(searching,productions,derive) + + for x in current.productions: + new_productions = productions.copy() + new_productions.append(x) + for y in x.Right: + if y in reach: + if y not in visited: + visited.add(y) + conflict = self._find_conflict(reach,y,searching,derive,visited,new_productions) + if conflict: + return conflict + visited.remove(y) + new_productions.extend(derive[y]) + + return '' + + def _generate_conflict_error(self,searching,productions,derive): + string = self._generate_conflict_string(productions,derive) + aux = ' | '.join([str(x) for x in self.get_parser_table[searching]]) + conflict = f'The prefix "{string}{searching[1].Name}" generate the parse {productions}, that can be continued by these productions {searching[0]} -> {aux}' + return conflict + + def _generate_conflict_string(self,productions,derive): + + stack = [self.grammar.startSymbol] + cursor = 0 + output = [] + string = [] + + while cursor < len(productions): + top = stack.pop() + + if top.IsTerminal: + if not top.IsEpsilon: + string.append(top) + cursor += 1 + continue + + prod = productions[cursor] + + if prod.Right: + rev = prod.Right[::-1] + for t in rev: + stack.append(t) + else: + stack.append(self.grammar.Epsilon) + cursor += 1 + + stack.reverse() + for x in stack: + if x.IsTerminal: + string.append(x) + + output = '' + for x in string: + output += x.Name + ' ' + + return output diff --git a/src/cool2/lib/parser/parser_lr.py b/src/cool2/lib/parser/parser_lr.py new file mode 100644 index 000000000..f02876bad --- /dev/null +++ b/src/cool2/lib/parser/parser_lr.py @@ -0,0 +1,684 @@ +from cmp.automata import State, lr0_formatter, multiline_formatter +from cmp.pycompiler import (EOF, Epsilon, Grammar, Item, NonTerminal, + Production, Sentence, SentenceFromIter, + SentenceList, Symbol, Terminal) +from lib.utils.first_follow import (compute_firsts, compute_follows, compute_local_first) +from cmp.utils import ContainerSet, DisjointNode, DisjointSet, Token +from lib.parser.parser import Parser +from lib.grammar.grammar_fixer import fix_non_derive_terminal +from lib.utils.automaton import state_transpose + +###################### LR0 and SLR1 ########################### +def get_state(visited,pending,item): + if not item in visited.keys(): + new_state = State(item,True) + pending.append(item) + visited[item] = new_state + return new_state + return visited[item] + +def build_LR0_automaton(G:Grammar): + assert len(G.startSymbol.productions) == 1, 'Grammar must be augmented' + + start_production = G.startSymbol.productions[0] + start_item = Item(start_production, 0) + + automaton = State(start_item, True) + + pending = [ start_item ] + visited = { start_item: automaton } + + while pending: + current_item = pending.pop() + if current_item.IsReduceItem: + continue + + current_state = visited[current_item] + next_symbol = current_item.NextSymbol + next_item = current_item.NextItem() + + #- $(X --> a.cB) ---c---> (X --> ac.B) con c in V_T + #- $(X --> a.YB) ---Y---> (X --> aY.B) con Y in V_N + next_state = get_state(visited,pending,next_item) + current_state.add_transition(next_symbol.Name,next_state) + + if next_symbol in G.nonTerminals: + sym_productions = G.symbDict[next_symbol.Name].productions + #- $(X --> a.YB) ---e---> (Y --> .Z) con Y in V_N + for pr in [Item(x,0) for x in sym_productions]: + trans_state = get_state(visited,pending,pr) + current_state.add_epsilon_transition(trans_state) + + return automaton.to_deterministic(lr0_formatter) + +class ShiftReduceParser(Parser): + SHIFT = 'SHIFT' + REDUCE = 'REDUCE' + OK = 'OK' + lr_type = None + errors = None + automaton = None + grammar = None + error_states = None # self.error_states[key] is the set of posibles values + eval_errors = [] + + + state_dict = {} # state_dic[key] = State with idx==key + + def __init__(self, G, automaton_builder, verbose=False): + self.grammar = G + self.verbose = verbose + self.action = {} + self.goto = {} + self.errors = [] + self.error_states = {} + G = G.AugmentedGrammar(True) + self.automaton = automaton_builder(G) + self._build_parsing_table(G) + + def _build_parsing_table(self, G): + raise NotImplementedError() + + def __call__(self, tokens, errors,finding_conflict = False): + stack = [ 0 ] + cursor = 0 + output = [] + tokens = [x for x in tokens] + while True and cursor < len(tokens): + state = stack[-1] + lookahead = tokens[cursor] + if self.verbose: print(stack, '<---||--->', tokens[cursor:]) + + # Your code here!!! (Detect error) + try: + action = self.action[state, lookahead.token_type] + if isinstance(action,tuple): + action, tag = action + else: + return None if not finding_conflict else (state,lookahead,output) + except KeyError: + # errors.append(f'Invalid transition ({state},{lookahead}) doesnt exist expected {[ x[1] for x in self.action if x[0] == state ]}') + posibles = [x for x in self.action if x[0] == state ] + errors.append(f"Invalid transition near '{lookahead.lex[0]}'. Expected: {', '.join([ str(x[1]) for x in posibles ])}. Line:{lookahead.lex[1] + 1} Column:{lookahead.lex[2] + 1}") + if len(posibles) == 1: + tokens.insert(cursor + 1, Token((str(posibles[0][1]), lookahead.lex[1], lookahead.lex[2]), posibles[0][1])) + cursor += 1 + continue + return None if not finding_conflict else (state,lookahead,output) + + if action == self.SHIFT: + # Your code here!!! (Shift case) + stack.append(lookahead.token_type) + stack.append(tag) + cursor+=1 + elif action == self.REDUCE: + # Your code here!!! (Reduce case) + for i in range(len(tag.Right)): + stack.pop() + top = stack.pop() + if not top == tag.Right[-(i+1)]: + errors.append(f"Productions reduce doesnt match: {top} != {tag.Right[-(i+1)]}") + + index = self.goto[stack[-1],tag.Left] + stack.append(tag.Left) + stack.append(index) + output.append(tag) + elif action == self.OK: + # Your code here!!! (OK case) + return output if not finding_conflict else (state,lookahead,output) + # Your code here!!! (Invalid case) + else: + errors.append(f"Invalid case: {action}") + return None if not finding_conflict else (state,lookahead,output) + + def _register(self, table, key, value): + if key in table: + key_value = table[key] + if type(key_value) == type(set()): + if value not in key_value: + self.errors.append(f'Shift-Reduce or Reduce-Reduce conflict: {key} already on table with values {table[key]} cannot register with value {value}') + key_value.add(value) + if key in self.error_states: + self.error_states[key].add(key) + else: + self.error_states[key] = set([value,key_value]) + elif key_value != value: + self.errors.append(f'Shift-Reduce or Reduce-Reduce conflict: {key} already on table with value {table[key]} cannot register with value {value}') + table[key] = set([value, key_value]) + if key in self.error_states: + self.error_states[key].add(key) + else: + self.error_states[key] = set([value,key_value]) + else: + table[key] = value + + def _evaluate(self, production, right_parse, tokens, inherited_value=None): + head, body = production + attributes = production.attributes + body = list(body) + body.reverse() + # Insert your code here ... + # > synteticed = ... + synteticed = [None for _ in attributes] + # > inherited = ... + inherited = [None]*(len(body)+1) + # Anything to do with inherited_value? + inherited[0] = inherited_value + + for i, symbol in zip(range(len(body),0,-1),body) : + if symbol.IsTerminal: + if not inherited[i] is None: + self.eval_errors.append(f'Terminals cant have inherited values. Terminal:{symbol}; Inherited:{inherited[i]}') + break + # Insert your code here ... + try: + synteticed[i] = next(tokens).lex # como es un terminal se sintetiza como el mismo + except StopIteration: + self.eval_errors.append(f'tokens stopped the iteration') + break + else: + try: + next_production = next(right_parse) + except StopIteration: + self.eval_errors.append(f'right_parse stopped the iteration') + break + if not symbol == next_production.Left: + self.eval_errors.append(f'{symbol} doesnt match with {next_production.Left}. Cant expand {symbol}') + break + # Insert your code here ... + if attributes[i]: + inherited[i] = attributes[i](inherited, synteticed) + synteticed[i] = self._evaluate(next_production, right_parse, tokens, inherited[i]) + + # Insert your code here ... + if attributes[0]: + synteticed[0] = attributes[0](inherited, synteticed) + # > return ... + return synteticed[0] + + def evaluate_parse(self, right_parse, tokens): + self.eval_errors.clear() + if not right_parse or not tokens: + self.eval_errors.append('Empty tokens or Empty right_parse') + return None + + right_parse = iter(right_parse) + end = tokens[-1] + tokens = tokens[:len(tokens)-1] + tokens.reverse() + tokens.append(end) + tokens = iter(tokens) + result = self._evaluate(next(right_parse), right_parse, tokens) + + if not isinstance(next(tokens).token_type, EOF): + self.eval_errors.append('Last parsed token doesnt match with EOF') + return None + return result + + def evaluate(self,tokens,errors:list,return_ast=False): + """ + If no errors then returns the evaluated tokens\n + else fills errors with errors returning None + """ + parser = self.__call__ + new_errors = [] + right_parse = parser(tokens,new_errors) + if not new_errors: + right_parse.reverse() + ast = self.evaluate_parse(right_parse, tokens) + if self.eval_errors: + for x in self.eval_errors: + errors.append(x) + return None + if return_ast: + return ast + return ast.evaluate() + else: + for x in new_errors: + errors.append(x) + return None + + def find_conflict(self): + conflicts = [] + automaton_reverse,state_dict = state_transpose(self.automaton) + for key,value in self.action.items(): + if type(value) == type(set()): + conflict_productions = [x[1] for x in value if isinstance(x[1],Production)] + for production in conflict_productions: + conflict_state = state_dict[key[0]][0] + conflict_item = find_conflict_item(conflict_state,production) + stack = [(conflict_state,conflict_item)] + + sentence = [ y for y in production.Right ] + sentence.reverse() + current_state = conflict_state + current_item = conflict_item + for y in sentence: + current_item = Item(current_item.production,current_item.pos-1,current_item.lookaheads) + current_state = go_back(current_state,y,current_item,state_dict) + stack.append((current_state,current_item)) + stack.reverse() + + initial_item = find_initial_item(self.automaton) + path = [(self.automaton,initial_item,False)] + visited = {(initial_item,self.automaton.idx)} + find_path_to(stack[0][0],stack[0][1],self.automaton,initial_item,path,visited) # una lista de tuplas que es (estado en que estoy,item en el que estoy) + + terminals = items_to_terminals(path[:len(path)-1],stack) + conflicts.append(self._generate_error(value,terminals,[x[1] for x in path] + [x[1] for x in stack[1:]],key[0],key[1])) + break + + return conflicts + + def _generate_error(self,conflict,terminals,items,state,lookahead): + string = ' '.join(x.Name for x in terminals) + return f'The string "{string}" is generated by the items {items}, stopping in state {state}, the conflict is because the automaton can do the following actions {conflict} looking at terminal "{lookahead}"' + +class LR0Parser(ShiftReduceParser): + + def __init__(self,G,verbose=False): + super().__init__(G,build_LR0_automaton,verbose) + self.lr_type = 'lr0' + + def _build_parsing_table(self,G): + assert len(G.startSymbol.productions) == 1, 'Grammar must be augmented' + + automaton = self.automaton + for i, node in enumerate(automaton): + if self.verbose: print(i, '\t', '\n\t '.join(str(x) for x in node.state), '\n') + node.idx = i + + for node in automaton: + idx = node.idx + for state in node.state: + item = state.state + # Your code here!!! + # - Fill `self.Action` and `self.Goto` according to `item`) + # - Feel free to use `self._register(...)`) + item = state.state + if item.IsReduceItem: + if item.production.Left == G.startSymbol: + self._register(self.action,(idx,G.EOF),(self.OK,None)) + else: + for c in G.terminals + [G.EOF]: + self._register(self.action, (idx,c),(self.REDUCE,item.production)) + else: + next_sym = item.NextSymbol + try: + next_state = node[next_sym.Name][0] + if next_sym.IsNonTerminal: + self._register(self.goto,(idx,next_sym),next_state.idx) + else: + self._register(self.action,(idx,next_sym),(self.SHIFT,next_state.idx)) + except KeyError: + self.errors.append(f'Node: {node} without transition with symbol {next_sym}') + return None + +class SLR1Parser(ShiftReduceParser): + + def __init__(self,G,verbose=False): + super().__init__(G,build_LR0_automaton,verbose) + self.lr_type = 'slr1' + + def _build_parsing_table(self,G): + assert len(G.startSymbol.productions) == 1, 'Grammar must be augmented' + + firsts = compute_firsts(G) + follows = compute_follows(G, firsts) + + automaton = self.automaton + for i, node in enumerate(automaton): + if self.verbose: print(i, '\t', '\n\t '.join(str(x) for x in node.state), '\n') + node.idx = i + + for node in automaton: + idx = node.idx + for state in node.state: + item = state.state + # Your code here!!! + # - Fill `self.Action` and `self.Goto` according to `item`) + # - Feel free to use `self._register(...)`) + item = state.state + if item.IsReduceItem: + if item.production.Left == G.startSymbol: + self._register(self.action,(idx,G.EOF),(self.OK,None)) + else: + for c in follows[item.production.Left]: + self._register(self.action, (idx,c),(self.REDUCE,item.production)) + else: + next_sym = item.NextSymbol + try: + next_state = node[next_sym.Name][0] + if next_sym.IsNonTerminal: + self._register(self.goto,(idx,next_sym),next_state.idx) + else: + self._register(self.action,(idx,next_sym),(self.SHIFT,next_state.idx)) + except KeyError: + self.errors.append(f'Node: {node} without transition with symbol {next_sym}') + return None + + +###################### LR1 ########################### +def expand(item, firsts): + next_symbol = item.NextSymbol + if next_symbol is None or not next_symbol.IsNonTerminal: + return [] + + lookaheads = ContainerSet() # lookahead = que yo quiero ver cuando vaya a reducir + # Your code here!!! (Compute lookahead for child items) + + for prev in item.Preview(): + lookaheads.update(compute_local_first(firsts,prev)) + + assert not lookaheads.contains_epsilon + + # Your code here!!! (Build and return child items) + return [ Item(x,0,lookaheads) for x in next_symbol.productions] + +def compress(items): + centers = {} + + for item in items: + center = item.Center() + try: + lookaheads = centers[center] + except KeyError: + centers[center] = lookaheads = set() + lookaheads.update(item.lookaheads) + + return { Item(x.production, x.pos, set(lookahead)) for x, lookahead in centers.items() } + +def closure_lr1(items, firsts): + closure = ContainerSet(*items) + + changed = True + while changed: + changed = False + + new_items = ContainerSet() + # Your code here!!! + for x in closure: + new_items.extend(expand(x,firsts)) + + changed = closure.update(new_items) + + return compress(closure) + +def goto_lr1(items, symbol, firsts=None, just_kernel=False): + assert just_kernel or firsts is not None, '`firsts` must be provided if `just_kernel=False`' + items = frozenset(item.NextItem() for item in items if item.NextSymbol == symbol) + return items if just_kernel else closure_lr1(items, firsts) + +def build_LR1_automaton(G): + assert len(G.startSymbol.productions) == 1, 'Grammar must be augmented' + + firsts = compute_firsts(G) + firsts[G.EOF] = ContainerSet(G.EOF) + + start_production = G.startSymbol.productions[0] + start_item = Item(start_production, 0, lookaheads=(G.EOF,)) + start = frozenset([start_item]) + + closure = closure_lr1(start, firsts) + automaton = State(frozenset(closure), True) + + pending = [ start ] + visited = { start: automaton } + + while pending: + current = pending.pop() + current_state = visited[current] + + for symbol in G.terminals + G.nonTerminals: + # Your code here!!! (Get/Build `next_state`) + next_state_key = goto_lr1(current_state.state,symbol,just_kernel=True) + # next_state_key = frozenset([i.NextItem() for i in current_state.state if i.NextSymbol == symbol]) + if not next_state_key: + continue + try: + next_state = visited[next_state_key] + except KeyError: + next_state_items = goto_lr1(current_state.state,symbol,firsts) + next_state = State(frozenset(next_state_items),True) + pending.append(next_state_key) + visited[next_state_key] = next_state + current_state.add_transition(symbol.Name, next_state) + + + automaton.set_formatter(multiline_formatter) + return automaton + +class LR1Parser(ShiftReduceParser): + + def __init__(self,G,verbose=False): + super().__init__(G,build_LR1_automaton,verbose) + self.lr_type = 'lr1' + + def _build_parsing_table(self,G): + assert len(G.startSymbol.productions) == 1, 'Grammar must be augmented' + + automaton = self.automaton + for i, node in enumerate(automaton): + if self.verbose: print(i, '\t', '\n\t '.join(str(x) for x in node.state), '\n') + node.idx = i + + for node in automaton: + idx = node.idx + for item in node.state: + # Your code here!!! + # - Fill `self.Action` and `self.Goto` according to `item`) + # - Feel free to use `self._register(...)`) + if item.IsReduceItem: + if item.production.Left == G.startSymbol: + self._register(self.action,(idx,G.EOF),(self.OK,None)) + else: + for c in item.lookaheads: + self._register(self.action, (idx,c),(self.REDUCE,item.production)) + else: + next_sym = item.NextSymbol + try: + next_state = node[next_sym.Name][0] + if next_sym.IsNonTerminal: + self._register(self.goto,(idx,next_sym),next_state.idx) + else: + self._register(self.action,(idx,next_sym),(self.SHIFT,next_state.idx)) + except KeyError: + self.errors.append(f'Node: {node} without transition with symbol {next_sym}') + return None + + +###################### LALR1 ########################### +def center_of(items): + return { item.Center() for item in items } + +def build_automaton_from_ds(initial:State, ds:DisjointSet): + for g in ds.groups: + r = g[0].representative.value + old_transitions = r.transitions + r.transitions = {} + for x in old_transitions: + node = None + for y in ds.groups: + if center_of(y[0].representative.value.state) == center_of(old_transitions[x][0].state): + node = y + break + assert node, 'group not found' + r.add_transition(x, node[0].representative.value) + end = [x for x in ds.groups if x[0].representative.value == initial][0] + return end[0].representative.value + +def from_lr1_to_lalr(lr1_automaton:State): + pending = [s for s in lr1_automaton] + ds = DisjointSet(*pending) + while pending: + to_merge = [] + pivot = center_of(pending[0].state) + for i,x in enumerate(pending): + if center_of(x.state) == pivot: + to_merge.append(x) + pending[i] = None + pending = [x for x in pending if x] + ds.merge(to_merge) + + for g in ds.groups: + r = g[0].representative + for x in [x for x in g if r != x]: + for prod in r.value.state: + for prod2 in x.value.state: + if prod.Center() == prod2.Center(): + new_lookahead = {x for x in prod.lookaheads} + new_lookahead.update({x for x in prod2.lookaheads}) + prod.lookaheads = frozenset(new_lookahead) + return build_automaton_from_ds(lr1_automaton,ds) + +def build_LALR1_automaton(G): + return from_lr1_to_lalr(build_LR1_automaton(G)) + +class LALR1Parser(ShiftReduceParser): + + def __init__(self,G,verbose=False): + super().__init__(G,build_LALR1_automaton,verbose) + self.lr_type = 'lalr1' + + def _build_parsing_table(self,G): + assert len(G.startSymbol.productions) == 1, 'Grammar must be augmented' + + automaton = self.automaton + for i, node in enumerate(automaton): + if self.verbose: print(i, '\t', '\n\t '.join(str(x) for x in node.state), '\n') + node.idx = i + + for node in automaton: + idx = node.idx + for item in node.state: + # Your code here!!! + # - Fill `self.Action` and `self.Goto` according to `item`) + # - Feel free to use `self._register(...)`) + if item.IsReduceItem: + if item.production.Left == G.startSymbol: + self._register(self.action,(idx,G.EOF),(self.OK,None)) + else: + for c in item.lookaheads: + self._register(self.action, (idx,c),(self.REDUCE,item.production)) + else: + next_sym = item.NextSymbol + try: + next_state = node[next_sym.Name][0] + if next_sym.IsNonTerminal: + self._register(self.goto,(idx,next_sym),next_state.idx) + else: + self._register(self.action,(idx,next_sym),(self.SHIFT,next_state.idx)) + except KeyError: + self.errors.append(f'Node: {node} without transition with symbol {next_sym}') + return None + + +############## Conflict Utils ####################### +def go_back(state_to_transpose, symbol,expected_item,transpose_state_dic): + """ + return a state that enter to state_to_transpose with symbol `symbol` + """ + candidates = transpose_state_dic[state_to_transpose.idx][1][symbol.Name] + candidates = [x for x in candidates if any([y for y in x.state if y.state == expected_item])] + return transpose_state_dic[candidates[0].idx][0] + +def find_path_to(goal_state,goal_item,current_state,current_item,path,item_checked): + """ + path: a list of tuple (item's state, item) that represent th items taken to reach current state + item_checked: a set of tuple (item, item's state.idx) that saves the visited items + return in path the items needed to reach the goal_item in goal_state from current_item in current_state + """ + if goal_state == current_state and goal_item == current_item: + return True + if current_item.IsReduceItem: + return False + + spanned_items = span_item(current_state,current_item) + + path.append((current_state[current_item[current_item.pos].Name][0],current_item.NextItem(),True if current_item[current_item.pos].IsNonTerminal else False)) + if not (path[-1][1],path[-1][0].idx) in item_checked: + item_checked.add((path[-1][1],path[-1][0].idx)) + if find_path_to(goal_state,goal_item,path[-1][0],path[-1][1],path,item_checked): + return True + path.pop() + + for next_item in spanned_items: + path.append((current_state,next_item,False)) + if not (path[-1][1],path[-1][0].idx) in item_checked: + item_checked.add((next_item,current_state.idx)) + if find_path_to(goal_state,goal_item,path[-1][0],path[-1][1],path,item_checked): + return True + path.pop() + return False + +def items_to_terminals(first_path, second_path): + """ + concat the terminals of first_path an the sentence of second_path + """ + terminals = [] + for i,(state,item,skipped) in enumerate(first_path): + if not item.IsReduceItem: + if i != 0 and skipped: # before skiped: + last_state,last_item,skipped = first_path[i-1] + spanned_items = span_item(last_state,last_item) + for next_item in spanned_items: + next_terminals = item_sentence(last_state,next_item) + if next_terminals: + terminals += next_terminals + break + if item[item.pos].IsTerminal: + terminals.append(item[item.pos]) + + for state,item in second_path: + if not item.IsReduceItem: + if item[item.pos].IsTerminal: + terminals.append(item[item.pos]) + else: + spanned_items = span_item(state,item) + terminals += item_sentence(state,spanned_items[0]) + return terminals + +def find_initial_item(initial_state): + for in_state in initial_state.state: + item = in_state.state + if len(item.production.Right) == 1 and item.production.Right[0].IsNonTerminal \ + and "'" in item.production.Left.Name: + return item + +def find_conflict_item(state,production): + for in_state in state.state: + if isinstance(in_state,Item): + if in_state.production == production: + return in_state + else: + item = in_state.state + if item.production == production: + return item + +def span_item(state,item): + return [x.state for x in state.state if x.state.production.Left == item[item.pos] and x.state.pos == 0] + # Tambien incluir los lookahead en la condicion? + +def item_sentence(state,item): + path = [] + _item_sentence(state,item,set(),path) + return path + +def _item_sentence(state:State,item,visited,path): + + if item.IsReduceItem: + return True + + next_symb = item.NextSymbol + if isinstance(next_symb,NonTerminal): + if not (state.idx,next_symb) in visited: + visited.add((state.idx,next_symb)) + for next_item in span_item(state,item): + if _item_sentence(state,next_item,visited,path): + if _item_sentence(state[next_symb.Name][0],item.NextItem(),visited,path): + return True + else: + path.append(next_symb) + if _item_sentence(state[next_symb.Name][0],item.NextItem(),visited,path): + return True + path.pop() \ No newline at end of file diff --git a/src/cool2/lib/shortcut.py b/src/cool2/lib/shortcut.py new file mode 100644 index 000000000..3d0d365bf --- /dev/null +++ b/src/cool2/lib/shortcut.py @@ -0,0 +1,12 @@ +from lib.lang.language_ll1 import build_LL1 +from lib.lang.language_lr import build_LR +from lib.lang.language_regular import build_Lan_Reg +from lib.grammar.grammar_fixer import fix_common_prefix,fix_e_productions,fix_left_recursion,fix_useless_symbols +from lib.grammar.grammar_util import grammar_to_text +from lib.grammar.grammar_parser import get_grammar_from_text +from lib.utils.first_follow import compute_firsts, compute_follows + +def build_LR_2(lr_type): + def build(tok_def,gram_def,error): + return build_LR(tok_def,gram_def,lr_type,error) + return build \ No newline at end of file diff --git a/src/cool2/lib/utils/algorithms.py b/src/cool2/lib/utils/algorithms.py new file mode 100644 index 000000000..90405befe --- /dev/null +++ b/src/cool2/lib/utils/algorithms.py @@ -0,0 +1,21 @@ + +def permutation(n,k): + """ + returns an iter from all permutations in n places of k elements\n + Ex: n = 2, k = 2\n + returns [0,0],[0,1],[1,1],[1,0] + """ + perm = [None]*n + if n == 0: + return [] + for t in inner_permutation(perm,0,k,n): + yield t + +def inner_permutation(perm,i,k,n): + for p in range(k): + perm[i] = p + if i < n-1: + for t in inner_permutation(perm,i+1,k,n): + yield t + else: + yield tuple(perm) diff --git a/src/cool2/lib/utils/automaton.py b/src/cool2/lib/utils/automaton.py new file mode 100644 index 000000000..2d04aee0d --- /dev/null +++ b/src/cool2/lib/utils/automaton.py @@ -0,0 +1,463 @@ +from cmp.utils import ContainerSet +from cmp.utils import DisjointSet +from cmp.automata import State +import pydot + +def state_transpose(initial_state:State): + """ + returns the state equivalent to initial_state on the transpose graph and a dictionary mapping idx to new states transposed + """ + state_dict = {} # state_dict[key] = (original_state,copy_of_original_state_with_transposed_transitions) + idx = 0 + for x in initial_state: + new_state = State(x.state,x.final,x.formatter,x.shape) + if not hasattr(x,'idx'): + x.idx = idx + idx+=1 + new_state.idx = x.idx + state_dict[x.idx] = (x,new_state) + + for x in initial_state: + for key,states in x.transitions.items(): + for state in states: + state_dict[state.idx][1].add_transition(key,state_dict[x.idx][1]) + + return state_dict[initial_state.idx][1],state_dict + +class NFA: + def __init__(self, states, finals, transitions, start=0): + self.states = states + self.start = start + self.finals = set(finals) + self.map = transitions + self.vocabulary = set() + self.transitions = { state: {} for state in range(states) } + + for (origin, symbol), destinations in transitions.items(): + assert hasattr(destinations, '__iter__'), 'Invalid collection of states' + self.transitions[origin][symbol] = destinations + self.vocabulary.add(symbol) + + self.vocabulary.discard('') + + def epsilon_transitions(self, state): + assert state in self.transitions, 'Invalid state' + try: + return self.transitions[state][''] + except KeyError: + return () + + def graph(self): + G = pydot.Dot(rankdir='LR', margin=0.1) + G.add_node(pydot.Node('start', shape='plaintext', label='', width=0, height=0)) + + for (start, tran), destinations in self.map.items(): + tran = 'ε' if tran == '' else tran + G.add_node(pydot.Node(start, shape='circle', style='bold' if start in self.finals else '')) + for end in destinations: + G.add_node(pydot.Node(end, shape='circle', style='bold' if end in self.finals else '')) + G.add_edge(pydot.Edge(start, end, label=tran, labeldistance=2)) + + G.add_edge(pydot.Edge('start', self.start, label='', style='dashed')) + # G = 'Graph Drawed' + return G + + def _repr_svg_(self): + try: + return self.graph().create_svg().decode('utf8') + except: + pass + +class DFA(NFA): + + def __init__(self, states, finals, transitions, start=0): + assert all(isinstance(value, int) for value in transitions.values()) + assert all(len(symbol) > 0 for origin, symbol in transitions) + + transitions = { key: [value] for key, value in transitions.items() } + NFA.__init__(self, states, finals, transitions, start) + self.current = start + + def _move(self, symbol): + # Your code here + try: + self.current = self.transitions[self.current][symbol][0] + return True + except: # Ver el error q da + return False + + def _reset(self): + self.current = self.start + + def recognize(self, string): + # Your code here + self._reset() + for i in range(0,len(string)): + if not self._move(string[i]): + return False + return self.current in self.finals + +def goto(automaton:NFA, states, symbol): + # Devuelve los estados en los cuales existe una transicion desde alguno de los estados de states mediante + # symbol + moves = set() + for state in states: + # Your code here + try: + for i in automaton.transitions[state][symbol]: + moves.add(i) + except: + pass + return moves + +def epsilon_closure(automaton, states): + pending = [ s for s in states ] # equivalente a list(states) pero me gusta así :p + closure = { s for s in states } # equivalente a set(states) pero me gusta así :p + analiced = [False]*automaton.states + + while pending: + state = pending.pop() + # Your code here + try: + for i in automaton.transitions[state]['']: + if not analiced[i]: + closure.add(i) + pending.append(i) + except KeyError: # Ver el error q da + pass + analiced[state] = True + return ContainerSet(*closure) + +def first(cond, itera): + for x in iter(itera): + if cond(x): + return x + return None + +def nfa_to_dfa(automaton): + transitions = {} + + start = epsilon_closure(automaton, [automaton.start]) + start.id = 0 + start.is_final = any(s in automaton.finals for s in start) + states = [ start ] + pending = [ start ] + ids = 1 + while pending: + state = pending.pop() + + for symbol in automaton.vocabulary: + # Your code here + # ... + new_state = epsilon_closure(automaton,goto(automaton, state, symbol)) + + if len(new_state) == 0: # no genera nada + continue + + try: + transitions[state.id, symbol] + assert False, 'Invalid DFA!!!' + except KeyError: + # Your code here + if new_state in states: + transitions[state.id, symbol] = first(lambda x: x.set == new_state.set,states).id + continue + + new_state.id = ids + ids += 1 + new_state.is_final = any(s in automaton.finals for s in new_state) + transitions[state.id, symbol] = new_state.id + + pending.append(new_state) # agregar id al new_state + states.append(new_state) + + finals = [ state.id for state in states if state.is_final ] + dfa = DFA(len(states), finals, transitions) + return dfa + +def automata_union(a1, a2): + transitions = {} + + start = 0 + d1 = 1 + d2 = a1.states + d1 + final = a2.states + d2 + + for (origin, symbol), destinations in a1.map.items(): + ## Relocate a1 transitions ... + # Your code here + transitions[origin + d1,symbol] = [t for t in map(lambda x: x+d1,destinations)] + + + for (origin, symbol), destinations in a2.map.items(): + ## Relocate a2 transitions ... + # Your code here + transitions[origin + d2,symbol] = [t for t in map(lambda x: x+d2,destinations)] + + ## Add transitions from start state ... + # Your code here + transitions[start,''] = [a1.start + d1, a2.start + d2] # epsilon transitions to initials of a1 and a2 + + ## Add transitions to final state ... + # Your code here + for f1 in a1.finals: + try: + transitions[f1 + d1,''].append(final) + except KeyError: + transitions[f1 + d1,''] = [final] + + for f2 in a2.finals: + try: + transitions[f2 + d2,''].append(final) + except KeyError: + transitions[f2 + d2,''] = [final] + + states = a1.states + a2.states + 2 + finals = { final } + + return NFA(states, finals, transitions, start) + +def automata_concatenation(a1, a2): + transitions = {} + + start = 0 + d1 = 0 + d2 = a1.states + d1 + final = a2.states + d2 + + for (origin, symbol), destinations in a1.map.items(): + ## Relocate a1 transitions ... + # Your code here + transitions[origin + d1, symbol] = [t for t in map(lambda x: x+d1,destinations)] + + for (origin, symbol), destinations in a2.map.items(): + ## Relocate a2 transitions ... + # Your code here + transitions[origin + d2, symbol] = [t for t in map(lambda x: x+d2,destinations)] + + + ## Add transitions to final state ... + # Your code here + for f1 in a1.finals: + try: + transitions[f1 + d1,''].append(a2.start + d2) + except KeyError: + transitions[f1 + d1,''] = [a2.start + d2] + + for f2 in a2.finals: + try: + transitions[f2 + d2,''].append(a2.start + d2) + except KeyError: + transitions[f2 + d2,''] = [final] + states = a1.states + a2.states + 1 + finals = { final } + + return NFA(states, finals, transitions, start) + +def automata_closure(a1): + transitions = {} + + start = 0 + d1 = 1 + final = a1.states + d1 + + for (origin, symbol), destinations in a1.map.items(): + ## Relocate automaton transitions ... + # Your code here + transitions[origin + d1,symbol] = [t for t in map(lambda x: x+d1,destinations)] + + ## Add transitions from start state ... + # Your code here + transitions[start,''] = [a1.start + d1, final] + + ## Add transitions to final state and to start state ... + # Your code here + for f1 in a1.finals: + try: + transitions[f1 + d1,''].append(a1.start + d1) + transitions[f1 + d1,''].append(final) + except: + transitions[f1 + d1,''] = [a1.start + d1] + transitions[f1 + d1,''].append(final) + + + states = a1.states + 2 + finals = { final } + + return NFA(states, finals, transitions, start) + +def distinguish_states(group, automaton, partition): + split = {} + vocabulary = tuple(automaton.vocabulary) + for member1 in group: + # Your code here + # mira a ver a que grupo pertenece + for group_representative in split.keys(): + # chequea el vocabulario + for symbol in vocabulary: + err1, err2 = False, False + try: + m1 = automaton.transitions[partition[group_representative].value][symbol][0] + except KeyError: + err1 = True + try: + m2 = automaton.transitions[member1.value][symbol][0] + except KeyError: + err2 = True + + if err1 and err2: + continue + elif (not err1 and err2) or (err1 and not err2): + break + + if partition[m1].representative != partition[m2].representative: + break + else: + # matcheo todo el vocabulario + split[group_representative].append(member1.value) + break + else: + # no pertenece a ninguno por lo tanto creo uno nuevo + split[member1.value] = [member1.value] + return [group for group in split.values()] + +def state_minimization(automaton): + partition = DisjointSet(*range(automaton.states)) + + ## partition = { NON-FINALS | FINALS } + # Your code here + partition.merge([ x for x in automaton.finals]) + + non_finals = [nf for nf in filter(lambda x: not x in automaton.finals,range(automaton.states))] + partition.merge(non_finals) + + while True: + new_partition = DisjointSet(*range(automaton.states)) + + ## Split each group if needed (use distinguish_states(group, automaton, partition)) + # Your code here + for gr in partition.groups: + for gr in distinguish_states(gr,automaton,partition) : + new_partition.merge(gr) + + if len(new_partition) == len(partition): + break + + partition = new_partition + + return partition + +def automata_minimization(automaton): + partition = state_minimization(automaton) + + states = [s for s in partition.representatives] + transitions = {} + + for i, state in enumerate(states): + # Your code here + origin = state.value + for symbol, destinations in automaton.transitions[origin].items(): + # Your code here + transition = states.index(partition[destinations[0]].representative) + + try: + transitions[i,symbol] + assert False + except KeyError: + # Your code here + transitions[i,symbol] = transition + + # Your code here + finals = set() + for gr in partition.groups: + for i in gr: + if i.value in automaton.finals: + finals.add(states.index(i.representative)) + start = states.index(partition[automaton.start].representative) + + return DFA(len(states), finals, transitions, start) + +def automata_full_determined(a1): + transitions = {} + + if a1.states * len(a1.vocabulary) == len(a1.map.items()): + for (origin, symbol), destinations in a1.map.items(): + # Your code here + transitions[origin,symbol] = [t for t in destinations] + + return NFA(a1.states,a1.finals,transitions,a1.start) + + + start = 0 + # final = a1.states + end = a1.states #final + 1 + + for (origin, symbol), destinations in a1.map.items(): + # Your code here + transitions[origin,symbol] = [t for t in destinations] + + + for i in range(end): + for x in a1.vocabulary: + try: + transitions[i,x] + except KeyError: + transitions[i,x] = [end] + + for v in a1.vocabulary: + try: + transitions[end,v].append(end) + except KeyError: + transitions[end,v] = [end] + + states = a1.states + 1 + finals = set(a1.finals) + + return NFA(states, finals, transitions, start) + +def automata_complement(a1): + a1 = automata_full_determined(a1) + transitions = {} + + start = a1.start + final = a1.states # a1.states estado epsilon + + for (origin, symbol), destinations in a1.map.items(): + # Your code here + transitions[origin,symbol] = [t for t in destinations] + + for x in filter(lambda x: not x in a1.finals, range(a1.states)): + transitions[x,''] = [final] + + states = a1.states + 1 + finals = { final } + + return NFA(states, finals, transitions, start) + +def automata_intersection(a1,a2): + return automata_complement(automata_union(automata_complement(a1),automata_complement(a2))) + +def automata_difference(a1,a2): + return automata_intersection(a1,automata_complement(a2)) + +def automata_reverse(a1): + transitions = {} + + start = a1.states + final = a1.start + + for (origin, symbol), destinations in a1.map.items(): + ## Relocate automaton transitions ... + # Your code here + for x in destinations: + try: + transitions[x,symbol].append(origin) + except KeyError: + transitions[x,symbol] = [origin] + + transitions[start,''] = [x for x in a1.finals] + + states = a1.states + 1 + finals = { final } + + return NFA(states, finals, transitions, start) diff --git a/src/cool2/lib/utils/first_follow.py b/src/cool2/lib/utils/first_follow.py new file mode 100644 index 000000000..dfbaabce9 --- /dev/null +++ b/src/cool2/lib/utils/first_follow.py @@ -0,0 +1,108 @@ +from cmp.utils import ContainerSet + +def compute_local_first(firsts, alpha): + first_alpha = ContainerSet() + try: + alpha_is_epsilon = alpha.IsEpsilon + except: + alpha_is_epsilon = False + ################################################### + # alpha == epsilon ? First(alpha) = { epsilon } + ################################################### + # # + if alpha_is_epsilon: + first_alpha.set_epsilon() + return first_alpha + ################################################### + ################################################### + # alpha = X1 ... XN + # First(X1) subconjunto First(alpha) + # epsilon pertenece a First(X1)...First(Xi) ? First(Xi+1) subconjunto de First(X) y First(alpha) + # epsilon pertenece a First(X1)...First(XN) ? epsilon pertence a First(X) y al First(alpha) + ################################################### + # # + change = False + for i in alpha: + if not firsts[i].contains_epsilon: + # epsilon pertenece a First(X1)...First(Xi) ? First(Xi+1) subconjunto de First(X) y First(alpha) + change = True + for j in firsts[i]: + first_alpha.add(j) + if change: + break + if not change: + # epsilon pertenece a First(X1)...First(XN) ? epsilon pertence a First(X) y al First(alpha) + first_alpha.set_epsilon() + ################################################### + # First(alpha) + return first_alpha + + +def compute_firsts(G): + + firsts = {} + change = True + # init First(Vt) + for terminal in G.terminals: + firsts[terminal] = ContainerSet(terminal) + # init First(Vn) + for nonterminal in G.nonTerminals: + firsts[nonterminal] = ContainerSet() + while change: + change = False + # P: X -> alpha + for production in G.Productions: + X = production.Left + alpha = production.Right + # get current First(X) + first_X = firsts[X] + # init First(alpha) + try: + first_alpha = firsts[alpha] + except KeyError: + first_alpha = firsts[alpha] = ContainerSet() + # CurrentFirst(alpha)??? + local_first = compute_local_first(firsts, alpha) + # update First(X) and First(alpha) from CurrentFirst(alpha) + change |= first_alpha.hard_update(local_first) + change |= first_X.hard_update(local_first) + # First(Vt) + First(Vt) + First(RightSides) + return firsts + +def compute_follows(G,firsts): + + follows = { } + change = True + local_firsts = { } + # init Follow(Vn) + for nonterminal in G.nonTerminals: + follows[nonterminal] = ContainerSet() + follows[G.startSymbol] = ContainerSet(G.EOF) + while change: + change = False + # P: X -> alpha + for production in G.Productions: + X = production.Left + alpha = production.Right + follow_X = follows[X] + ################################################### + # X -> zeta Y beta + # First(beta) - { epsilon } subset of Follow(Y) + # beta ->* epsilon or X -> zeta Y ? Follow(X) subset of Follow(Y) + ################################################### + # # + j = 0 + alpha2 = {} + for i in alpha: + alpha2[j] = i + j+=1 + for i in alpha2.keys(): + y = alpha2[i] + if y.IsNonTerminal: + local_firsts = compute_local_first(firsts,alpha[i+1:]) + change |= follows[y].update(local_firsts) + if local_firsts.contains_epsilon: + change |= follows[y].update(follow_X) + ################################################### + # Follow(Vn) + return follows \ No newline at end of file diff --git a/src/cool2/lib/utils/trie.py b/src/cool2/lib/utils/trie.py new file mode 100644 index 000000000..86ba0e9d0 --- /dev/null +++ b/src/cool2/lib/utils/trie.py @@ -0,0 +1,63 @@ +class TrieNode(): + + sons = None + value = None + terminal = None + + def __init__(self,value,terminal = False,sons = []): + self.value = value + self.sons = {} + self.terminal = terminal + for x in sons: + self.add_son(x) + + def add_son(self,item): + assert isinstance(item,TrieNode), "item must be a TrieNode" + self.sons[item.value] = item + + def find_son(self,son_value): + return self.sons[son_value] + + def __iter__(self): + yield self + for x in self.sons: + for y in self.sons[x]: + yield y + + def __str__(self): + rep = f'{self.value}[' + for x in self.sons: + rep += f'{self.sons[x]},' + rep += ']\n' + return rep + +class Trie: + top = None + + def __init__(self): + self.top = TrieNode('^') + + def LCP(self, item): + current = self.top + idx = 0 + for x in item: + try: + current = current.find_son(x) + idx += 1 + except KeyError: + break + return current,idx + + def add(self,item): + where_to,from_this = self.LCP(item) + for x in item[from_this:]: + new = TrieNode(x) + where_to.add_son(new) + where_to = new + where_to.terminal = True + + def __str__(self): + return str(self.top) + + def __iter__(self): + return self.top.__iter__() \ No newline at end of file diff --git a/src/main.py b/src/main.py index a6c713063..2b39722c8 100644 --- a/src/main.py +++ b/src/main.py @@ -152,4 +152,4 @@ def __call__(self, ast:BaseAST) -> BaseAST: from cool2.main import main -main('a.cl', None,False,False,False,False,False) \ No newline at end of file +main('src/a.cl', None,False,False,False,False,False) \ No newline at end of file From 02cf92cb077a20f3a3bf21ec940c0aa8512023e0 Mon Sep 17 00:00:00 2001 From: Luiso Date: Sat, 17 Apr 2021 10:27:31 -0400 Subject: [PATCH 010/143] Lexing process integrated --- .vscode/launch.json | 16 ++++++++ src/cool2/cool/pipeline.py | 18 ++++++--- src/cool2/cool/pipes/pipes.py | 46 +++++++++++++++++++---- src/cool2/lib/lang/language.py | 19 ++++++++-- src/cool_cmp/lexer/__init__.py | 5 ++- src/cool_cmp/lexer/lexer2.py | 68 ++++++++++++++++++++++++++++++++++ src/main.py | 9 +++-- 7 files changed, 160 insertions(+), 21 deletions(-) create mode 100644 .vscode/launch.json create mode 100644 src/cool_cmp/lexer/lexer2.py diff --git a/.vscode/launch.json b/.vscode/launch.json new file mode 100644 index 000000000..c9c6c4213 --- /dev/null +++ b/.vscode/launch.json @@ -0,0 +1,16 @@ +{ + // Use IntelliSense to learn about possible attributes. + // Hover to view descriptions of existing attributes. + // For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387 + "version": "0.2.0", + "configurations": [ + { + "name": "Python: Archivo actual", + "type": "python", + "request": "launch", + "program": "src/main.py", + "console": "integratedTerminal", + "args": ["src/cas.cl","src/cas.mips"] + } + ] +} \ No newline at end of file diff --git a/src/cool2/cool/pipeline.py b/src/cool2/cool/pipeline.py index 74eda3802..57983a3a3 100644 --- a/src/cool2/cool/pipeline.py +++ b/src/cool2/cool/pipeline.py @@ -1,13 +1,17 @@ from cool.pipes.pipes import start_pipe, change_escaped_lines, remove_comments_pipe,\ parse_text_pipe, ast_pipe, type_collector_pipe, build_types_pipe, \ check_types_pipe, run_program_pipe, reconstruct_pipe, void_as_type_pipe, \ - auto_resolver_pipe, string_escape_pipe + auto_resolver_pipe, string_escape_pipe, tokenize_text_pipe from cool.libs import add_std_pipe from cool.pipes.pipeline import Pipeline -lexer_syntax_pipeline = Pipeline(start_pipe, - change_escaped_lines, - remove_comments_pipe, +lexer_pipeline = Pipeline(start_pipe, + change_escaped_lines, + remove_comments_pipe, + tokenize_text_pipe, + ) + +syntax_pipeline = Pipeline( parse_text_pipe, string_escape_pipe ) @@ -23,11 +27,13 @@ execution_pipeline = Pipeline(run_program_pipe) -cool_pipeline = Pipeline(lexer_syntax_pipeline, +cool_pipeline = Pipeline(lexer_pipeline, + syntax_pipeline, semantic_pipeline, execution_pipeline) -reconstr_pipeline = Pipeline(lexer_syntax_pipeline, +reconstr_pipeline = Pipeline(lexer_pipeline, + syntax_pipeline, semantic_pipeline, reconstruct_pipe, execution_pipeline) diff --git a/src/cool2/cool/pipes/pipes.py b/src/cool2/cool/pipes/pipes.py index 704e3e6a5..558ade6e0 100644 --- a/src/cool2/cool/pipes/pipes.py +++ b/src/cool2/cool/pipes/pipes.py @@ -60,34 +60,64 @@ def remove_comments_pipe(result:dict, comment_grammar=C, comment_lexer=comment_l remove_comments_pipe = Pipe(remove_comments_pipe) -def parse_text_pipe(result:dict, language_grammar=G, language_lexer=cool_lexer, language_parser=cool_parser): +def tokenize_text_pipe(result:dict, language_grammar=G, language_lexer=cool_lexer, language_parser=cool_parser): """ - Parse the text + Tokenize the text """ text = result['text'] lang = LanguageLR(language_grammar, language_lexer, language_parser) - errors = [] - parse, tokens = lang(text,errors) + tokens = lang.get_tokens(text, errors) result.update({ - "language": lang, "parser": language_parser, "lexer": language_lexer, - "text_parse": parse, + "language": lang, "text_tokens": tokens, }) - + if result.get("verbose",False): if errors: - print_errors("Parsing Text Errors", errors) + print_errors("Lexer Errors", errors) print('================== TOKENS =====================') pprint_tokens(tokens) + + result["errors"].extend(errors) + + return result + +tokenize_text_pipe = Pipe(tokenize_text_pipe) + +def parse_text_pipe(result:dict, language_grammar=G, language_lexer=cool_lexer, language_parser=cool_parser): + """ + Parse the text + """ + text = result['text'] + tokens = result.get('text_tokens') + + lang = result.get('language', LanguageLR(language_grammar, language_lexer, language_parser)) + + errors = [] + parse, tokens = lang(text, errors, tokens) + + + if result.get("verbose",False): + if errors: + print_errors("Parsing Text Errors", errors) + if not 'text_tokens' in result: + print('================== TOKENS =====================') + pprint_tokens(tokens) print('=================== PARSE =====================') print('\n'.join(repr(x) for x in parse)) + result.update({ + "text_parse": parse, + "language": lang, + "text_tokens": tokens, + }) + result["errors"].extend(errors) return result diff --git a/src/cool2/lib/lang/language.py b/src/cool2/lib/lang/language.py index 50e763c3a..6a9eac2f8 100644 --- a/src/cool2/lib/lang/language.py +++ b/src/cool2/lib/lang/language.py @@ -31,13 +31,13 @@ def _fix_tokens(self,tokens,errors): errors.append(f'The grammar does not recognize the token {x}') return fix_tokens - def __call__(self, text, errors): + def __call__(self, text, errors, tokens=None): """ returns a tuple of the parse and the tokens of the text """ - tokens = self.lexer(text) - tokens = self._fix_tokens(tokens,errors) + if tokens is None: + tokens = self.get_tokens(text, errors) parse_errors = [] parse = self.parser(tokens,parse_errors) @@ -50,6 +50,19 @@ def __call__(self, text, errors): return parse,tokens + def get_tokens(self, text, errors): + """ + Return the text tokens + """ + tokens = self.lexer(text) + tokens = self._fix_tokens(tokens,errors) + for tok in tokens: + if tok.token_type == "UNKNOWN": + errors.append(f"Unknown Token {tok.lex[0]} at Line: {tok.lex[1]} Column: {tok.lex[2]}") + tokens = [x for x in tokens if x.token_type != "UNKNOWN"] + + return tokens + def find_conflict(self): return self.parser.find_conflict() diff --git a/src/cool_cmp/lexer/__init__.py b/src/cool_cmp/lexer/__init__.py index 53deab303..b2973664d 100644 --- a/src/cool_cmp/lexer/__init__.py +++ b/src/cool_cmp/lexer/__init__.py @@ -6,10 +6,13 @@ from cool_cmp.lexer.interface import ILexer from cool_cmp.shared.pipeline import Pipeline, Pipe from cool_cmp.shared import SymbolTable +from cool_cmp.lexer.lexer2 import CoolLexer2 + +default_lexer = CoolLexer2() class LexerPipeline(Pipeline): - def __init__(self, lexer:ILexer): + def __init__(self, lexer:ILexer=default_lexer): result = SymbolTable() def get_tokens(program:str): diff --git a/src/cool_cmp/lexer/lexer2.py b/src/cool_cmp/lexer/lexer2.py new file mode 100644 index 000000000..f9cfb5f7d --- /dev/null +++ b/src/cool_cmp/lexer/lexer2.py @@ -0,0 +1,68 @@ +""" +Lexer usando ply +""" +from typing import List, Tuple + +from cool_cmp.lexer.interface import ILexer +from cool_cmp.shared.token import ICoolToken +from cool_cmp.shared.errors import ErrorTracker +from cool_cmp.lexer.errors import LexerCoolError +from cool2.cool.lexer.cool_lexer import cool_lexer +from cool2.cool.lexer.comment_lexer import comment_lexer +from cool2.cool.pipeline import lexer_pipeline + +from cool2.lib.lexer.lexer import DetailToken, DetailLexer + +class CoolToken2(DetailToken, ICoolToken): + + def set_lex(self, lex:str): + self.lex = (lex, self.row, self.column) + + def get_lex(self)->str: + return self.lex[0] + + def set_type(self, typex:str): + self.token_type = typex + + def get_type(self)->str: + return self.token_type + + def set_position(self, line:int, column:int): + self.lex = (self.lex[0], line, column) + self.row = line + self.column = column + + def get_position(self)->Tuple[int,int]: + return (self.row,self.column) + + def __str__(self): + return f"{self.get_lex()}:{self.get_type()} Line:{self.get_position()[0]} Column:{self.get_position()[1]}" + + def __repr__(self): + return str(self) + +class CoolLexer2(ILexer): + + @property + def name(self)->str: + return "lexer_name" + + def __init__(self): + self.error_tracker = ErrorTracker() # Error tracker implementation + + def __call__(self, program_string:str) -> List[ICoolToken]: + result = lexer_pipeline(program_string) + tokens = result["text_tokens"] + for err in result["errors"]: + self.add_error(err) + return [self.__DetailToken2CoolToken2(tok) for tok in tokens] + + def add_error(self, error:LexerCoolError): + self.error_tracker.add_error(error) + + def get_errors(self)->List[LexerCoolError]: + errors = self.error_tracker.get_errors() + return errors + + def __DetailToken2CoolToken2(self, detail_token:DetailToken)->CoolToken2: + return CoolToken2(detail_token.lex, detail_token.token_type) \ No newline at end of file diff --git a/src/main.py b/src/main.py index fffab4eda..6d2294627 100644 --- a/src/main.py +++ b/src/main.py @@ -11,7 +11,7 @@ from cool_cmp.shared.token import ICoolToken from cool_cmp.shared.ast import BaseAST from cool_cmp.shared.ast.cool import ClassDeclarationNode, ProgramNode -from cool_cmp.lexer.lexer import PlyLexer +# from cool_cmp.lexer.lexer import PlyLexer from cool_cmp.shared.errors import CoolError from typing import List, Tuple @@ -256,10 +256,13 @@ def get_errors(self)->List[CoolError]: import sys with open(sys.argv[1]) as file: program = file.read() - pipe = LexerPipeline(PlyLexer()) + pipe = LexerPipeline() result = pipe(program) for err in result.get_errors(): - err.print_error() + if isinstance(err, str): + print(err) + else: + err.print_error() if result.get_errors(): exit(1) with open(sys.argv[2], 'w') as file: From 2e25875bf4cfbaf73dbaabb3d5fb8183282a970e Mon Sep 17 00:00:00 2001 From: Luiso Date: Sat, 17 Apr 2021 16:01:06 -0400 Subject: [PATCH 011/143] TODO FIX LEXING ERROR Run tests with make test TAG=lexer --- .vscode/launch.json | 5 +- src/cas.cl | 2 - src/cas.mips | 1 - src/cool2/Informe.pdf | Bin 993464 -> 0 bytes src/cool2/cool/lexer/comment_lexer.py | 4 +- src/cool2/cool/lexer/cool_lexer.py | 4 +- src/cool2/cool/parser/comment_parser.py | 4 +- src/cool2/cool/parser/cool_parser.py | 4 +- src/cool2/lib/lang/language.py | 2 +- src/cool2/main_st.py | 91 ------------------- src/coolc.sh | 5 +- src/{cool2 => }/requirements.txt | 0 src/testing.cl | 111 ++++++++++++++++++++++++ tests/lexer_test.py | 4 +- tests/utils/utils.py | 8 +- 15 files changed, 139 insertions(+), 106 deletions(-) delete mode 100644 src/cas.cl delete mode 100644 src/cas.mips delete mode 100644 src/cool2/Informe.pdf delete mode 100644 src/cool2/main_st.py rename src/{cool2 => }/requirements.txt (100%) create mode 100644 src/testing.cl diff --git a/.vscode/launch.json b/.vscode/launch.json index c9c6c4213..7775f9cdc 100644 --- a/.vscode/launch.json +++ b/.vscode/launch.json @@ -8,9 +8,8 @@ "name": "Python: Archivo actual", "type": "python", "request": "launch", - "program": "src/main.py", - "console": "integratedTerminal", - "args": ["src/cas.cl","src/cas.mips"] + "program": "${file}", + "console": "integratedTerminal" } ] } \ No newline at end of file diff --git a/src/cas.cl b/src/cas.cl deleted file mode 100644 index 408bc5588..000000000 --- a/src/cas.cl +++ /dev/null @@ -1,2 +0,0 @@ -class A -` {} \ No newline at end of file diff --git a/src/cas.mips b/src/cas.mips deleted file mode 100644 index 3592fad3f..000000000 --- a/src/cas.mips +++ /dev/null @@ -1 +0,0 @@ -GENERATED MIPS \ No newline at end of file diff --git a/src/cool2/Informe.pdf b/src/cool2/Informe.pdf deleted file mode 100644 index 4a97ce6900e649489b4cbeca33d7d1ff4e67f1b7..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 993464 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

l+ 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%0DA5Jo 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

Zp-B9!;_r)RgT&Nska9Y8`6zs&D+d;8iM)9 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

lxbLp|R{iTtD{ZI*?DW5ZnzTIYyo-R{)n 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⁣ryB5Ywi_a)RTpBaM*}_)Qq{ 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

?+*P1{>C_NR&k| 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 z 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

sAfnsvj|k1KnGggNr%_{ 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

Br+q``b5!(D9D*v__?~q&{ z|Ic?E5W2xdn9WK{U6N>j+!%>~}KL6jW)2*o${&Cs$@?(0td`12y?WC|1jcqvI4aCM|+O>~3Q& zhEXI8Xii-tb4`-cd#G!VI>_y&!Lk?C-VnsDm)rhI9V6;85_93QnZx8`kerS;OA< z>pmZpV*i^EY&pqmmRWKe#ixR$0xPDEG25H(0;N%($eMO2x(dUi6~>@!S|hV<39`uj zyd_k`%;rCaM+38$Zkr+8^RV6Fa3*f|Z9iAc#s$ra>ctd(P-H(g0Y_Pr2BPaJ(%1#4 z+7j78hNR1gT#g$JJ>A1kzDo*+yn?jD|E@2(eDydQU)ipOA65n#9}0t+DH{#z^1AZqqB9s7pk%<4d2}8t=B@&AIZupaJG)c@vKJ}ztgdBK zt^&FyoG}!&tohO^-tn!Pd_Tp8c+ytwRYHrK!b#D)<#!pSpaf`K}NNP*JSdq>d%UkAUeY-xMzdvAQE4U481Y7 z+Gv&Tq@*SoaI8&Fj?weNbTKNmxJ}O22NdmgwuTP=tSr&jt$+8JlDG-(Y{f{ev>Y1 zv_d5iySwvLY)RQk_U=3-DQwyXVV8~Qy{qanD0^Z_WQ&yg6z+Q1tUPF5pfb4ms6V}S z2qq{ibkcTrQJdMLGzMCTt%PuGA37&r?Ig0I zit)Y=$*fUl=c(0qvYLQgWsF#4H{yyXP@nBG*i~$5bs?(p`Gnv-4$SnzJJ6(#wrbFt zblgT}6FyTCE{Z6cdBV3y6!>99-W8^;CNnqfE1v6;cjiuM18;x_C0GwWX~c30R1d-~&$XqoDo z0m`YSgPnQYiaeiKW~YZ)T;#V|zVBc52^P$C$TkOSW{bvEe_S$epK|AMd!CJ26Amf1 z(b#H7JduSOu0poAZ=>UHlFTs%YN=RWEIC)n*P)z?xo3Tst7Di*aXB4l9BoIZN%%OM z^e1Vwh-=>nbk97|I#J@x**89BKudf2^~s@<)4B_}VPceUs;_OvtAy!Xc%*GG#)P)B zVNJLWv#)4=P7ZE^R%?bg0?8vqO7#?21=VlLv?F7;>tE_CqCHJuw4q*DVP5Fv?56fz zb3=_8Onx__dhxyVw;b{^sn0ZvQHd1Ays9I&_M3;Ty)VOd&iaTCCN!!aA-@tI#6qmN zslAZRt4XhohUiRaJTtxt79KYYPRU|r8C+wzvqlRlwWZ2EczpF(C)=!A+(S$2f&W;C zH{gxQ!u{#9rUw1_d|w#8zI%LYov#@`>p)Xr|@<{i0p(5K8lGy zlJ#J~p9m}hr~W>Qyd3E>m-YRhW*6Q=ASs~pjnrSy`;E4YZ&Q<39)ww&zMgdT~Gr|+cqUJYAU7~ZoAzzo&r^ax1e9sG>SwGCG zR6xg9(l*vCSt8{55qAthg@=tvce=giNd-=7if3Q_d&9W+Q(`Yj$32>CfcU>f!wEaVEL)k!vEKTe36X zUOC(gTTWE7$FBZ!yH)J!woCPc{_O!`&l0+TKd(u4>K~;&N-^#Yy8LEM)>avB6^ZT- zvA}UStO4h^pRg{CMcU!4+X1?oSmv_2Q!^1i=DyMVQQ(hh*tI|m@$rF)7F81Kr5%-ae@cJQ_&4<;`v(hhSHa9^PxsEOf;=zeK{ z`$D*v*|r$KpC~fT*Rvb#0z-TZEn#^bEDU0K#&rLP3#|D=Dv{Q+w6dXV5V*aV3c~@V z*{<)iSDx#wWKNVvu$%dMSD2Gz0n~OSu^G4K#I2JTkPE~l-pzf1_|o@DtN=i_;AU!x zzYX_o^UOLvBf(8r0cc{vnYaRuZ^X~SQ?$dI9;7H2e^4bYosHH=*Qn4?9nSpl!QYjH z0!arsQfot^a;H?267z!C2_z~Ao;l|=1Xcak$hT>Gfa5HGchz@|yh8t}eI^1!CJ|c$ z3=3^Z;mqcv`yF;Q&Ipna&%J(N@p1pl?4I5NYgAvQYZ*QnQ@#|bLU)B?0n>Qy2s(8d zgL3Sg$b13#�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 {( Int: Objet, Bool; String.string SELF_TYPE isvoid }) +class Class if then else fi testing Testing ~007agent_bond james_007B0N3SS___ +loop pool while tRuE or noT faLsE let in case of ESAC + +(* +#3 INT_CONST 0007 +#3 INT_CONST 123 +#3 '+' +#3 INT_CONST 1 +#3 '-' +#3 INT_CONST 1 +#3 '+' +#3 INT_CONST 90 +#3 '-' +#3 INT_CONST 09 +#3 '+' +#3 INT_CONST 11113 +#3 '-' +#3 INT_CONST 4 +#3 OBJECTID r +#3 '*' +#3 OBJECTID a +#3 '*' +#3 OBJECTID self +#3 '*' +#3 OBJECTID c +#3 '+' +#3 '+' +#4 INT_CONST 5 +#4 ERROR "!" +#4 '=' +#4 INT_CONST 120 +#4 ',' +#4 INT_CONST 2 +#4 '+' +#4 INT_CONST 2 +#4 '=' +#4 INT_CONST 5 +#4 OBJECTID or +#4 TYPEID E +#4 '=' +#4 OBJECTID mc2 +#4 ';' +#4 OBJECTID p +#4 '+' +#4 INT_CONST 1 +#4 '@' +#4 OBJECTID p +#4 '=' +#4 INT_CONST 1 +#4 ':' +#4 OBJECTID for +#4 OBJECTID x +#4 IN +#4 OBJECTID range +#4 '(' +#4 OBJECTID len +#4 '(' +#4 OBJECTID b +#4 ')' +#4 ')' +#5 NEW +#5 '/' +#5 ASSIGN +#5 '<' +#5 LE +#5 DARROW +#5 '{' +#5 '(' +#5 TYPEID Int +#5 ':' +#5 TYPEID Objet +#5 ',' +#5 TYPEID Bool +#5 ';' +#5 TYPEID String +#5 '.' +#5 OBJECTID string +#5 TYPEID SELF_TYPE +#5 ISVOID +#5 '}' +#5 ')' +#6 CLASS +#6 CLASS +#6 IF +#6 THEN +#6 ELSE +#6 FI +#6 OBJECTID testing +#6 TYPEID Testing +#6 '~' +#6 INT_CONST 007 +#6 OBJECTID agent_bond +#6 OBJECTID james_007B0N3SS___ +#7 LOOP +#7 POOL +#7 WHILE +#7 BOOL_CONST true +#7 OBJECTID or +#7 NOT +#7 BOOL_CONST false +#7 LET +#7 IN +#7 CASE +#7 OF +#7 ESAC +*) \ No newline at end of file diff --git a/tests/lexer_test.py b/tests/lexer_test.py index a21fd880a..0f42ea4ab 100644 --- a/tests/lexer_test.py +++ b/tests/lexer_test.py @@ -10,4 +10,6 @@ @pytest.mark.run(order=1) @pytest.mark.parametrize("cool_file", tests) def test_lexer_errors(compiler_path, cool_file): - compare_errors(compiler_path, tests_dir + cool_file, tests_dir + cool_file[:-3] + '_error.txt') \ No newline at end of file + compare_errors(compiler_path, tests_dir + cool_file, tests_dir + cool_file[:-3] + '_error.txt') + +test_lexer_errors("/media/luiso/Data/Universidad/Cuarto/Compilación/cool-compiler-2021/src/coolc.sh", tests[1]) \ No newline at end of file diff --git a/tests/utils/utils.py b/tests/utils/utils.py index f98d19dd0..65f7df874 100644 --- a/tests/utils/utils.py +++ b/tests/utils/utils.py @@ -45,11 +45,15 @@ def get_file_name(path: str): def compare_errors(compiler_path: str, cool_file_path: str, error_file_path: str, cmp=first_error, timeout=100): try: - sp = subprocess.run(['bash', compiler_path, cool_file_path], capture_output=True, timeout=timeout) + f = open("test_log.txt",mode="x") + except: + f = open("test_log.txt", mode="a") + try: + sp = subprocess.run(['bash', compiler_path, cool_file_path], stdout=subprocess.PIPE, stderr=subprocess.PIPE, timeout=timeout) return_code, output = sp.returncode, sp.stdout.decode() except subprocess.TimeoutExpired: assert False, COMPILER_TIMEOUT - + f.write(output + "\n") assert return_code == 1, TEST_MUST_FAIL % get_file_name(cool_file_path) fd = open(error_file_path, 'r') From ad01600413de6f1e8ef9e7d9a68806fbcda9804f Mon Sep 17 00:00:00 2001 From: Luiso Date: Sun, 18 Apr 2021 08:28:51 -0400 Subject: [PATCH 012/143] Checkpoint: Move cmp to cool_cmp --- .vscode/launch.json | 1 + src/cool2/cool/pipeline.py | 7 ++--- src/cool_cmp/lexer/__init__.py | 1 + src/cool_cmp/lexer/lexer2.py | 7 ++++- src/cool_cmp/parser/__init__.py | 5 ++- src/cool_cmp/parser/interface.py | 3 +- src/cool_cmp/parser/parser2.py | 32 +++++++++++++++++++ src/cool_cmp/semantic/interface.py | 3 +- src/cool_cmp/shared/__init__.py | 29 +++++++++++++++--- src/cool_cmp/shared/pipeline/__init__.py | 2 +- src/main.py | 1 + src/testing.cl | 39 ++++++++++++++++++++++-- 12 files changed, 115 insertions(+), 15 deletions(-) create mode 100644 src/cool_cmp/parser/parser2.py diff --git a/.vscode/launch.json b/.vscode/launch.json index 7775f9cdc..a5d54f0fd 100644 --- a/.vscode/launch.json +++ b/.vscode/launch.json @@ -9,6 +9,7 @@ "type": "python", "request": "launch", "program": "${file}", + "args": ["src/testing.cl", "src/testing.mips"], "console": "integratedTerminal" } ] diff --git a/src/cool2/cool/pipeline.py b/src/cool2/cool/pipeline.py index 57983a3a3..46c944e8a 100644 --- a/src/cool2/cool/pipeline.py +++ b/src/cool2/cool/pipeline.py @@ -9,12 +9,11 @@ change_escaped_lines, remove_comments_pipe, tokenize_text_pipe, + string_escape_pipe, ) -syntax_pipeline = Pipeline( - parse_text_pipe, - string_escape_pipe - ) +syntax_pipeline = Pipeline(parse_text_pipe, + ) semantic_pipeline = Pipeline(add_std_pipe, ast_pipe, diff --git a/src/cool_cmp/lexer/__init__.py b/src/cool_cmp/lexer/__init__.py index b2973664d..8a71bba02 100644 --- a/src/cool_cmp/lexer/__init__.py +++ b/src/cool_cmp/lexer/__init__.py @@ -19,6 +19,7 @@ def get_tokens(program:str): tokens = lexer(program) result.tokens = tokens errors = lexer.get_errors() + result.context.update(lexer.get_extra_info()) for error in errors: result.add_error(error) return result diff --git a/src/cool_cmp/lexer/lexer2.py b/src/cool_cmp/lexer/lexer2.py index f9cfb5f7d..e1d01dced 100644 --- a/src/cool_cmp/lexer/lexer2.py +++ b/src/cool_cmp/lexer/lexer2.py @@ -55,6 +55,11 @@ def __call__(self, program_string:str) -> List[ICoolToken]: tokens = result["text_tokens"] for err in result["errors"]: self.add_error(err) + + # Adding context + for key, value in result.items(): + self.add_extra_info(key, value) + return [self.__DetailToken2CoolToken2(tok) for tok in tokens] def add_error(self, error:LexerCoolError): @@ -65,4 +70,4 @@ def get_errors(self)->List[LexerCoolError]: return errors def __DetailToken2CoolToken2(self, detail_token:DetailToken)->CoolToken2: - return CoolToken2(detail_token.lex, detail_token.token_type) \ No newline at end of file + return CoolToken2(detail_token.lex, detail_token.token_type) diff --git a/src/cool_cmp/parser/__init__.py b/src/cool_cmp/parser/__init__.py index 7401daef3..19c05f937 100644 --- a/src/cool_cmp/parser/__init__.py +++ b/src/cool_cmp/parser/__init__.py @@ -4,11 +4,14 @@ from cool_cmp.parser.interface import IParser from cool_cmp.lexer import LexerPipeline +from cool_cmp.parser.parser2 import CoolParer2 from cool_cmp.shared import SymbolTable, InterfacePipeline +default_parser = CoolParer2() + class ParserPipeline(InterfacePipeline): - def __init__(self, lexer_pipeline:LexerPipeline, parser:IParser): + def __init__(self, lexer_pipeline:LexerPipeline, parser:IParser=default_parser): super().__init__(lexer_pipeline, *[parser,]) def __call__(self, program:str)->SymbolTable: diff --git a/src/cool_cmp/parser/interface.py b/src/cool_cmp/parser/interface.py index c26b988a1..2d0301df0 100644 --- a/src/cool_cmp/parser/interface.py +++ b/src/cool_cmp/parser/interface.py @@ -2,11 +2,12 @@ from cool_cmp.shared.token import ICoolToken from cool_cmp.shared.ast import BaseAST from cool_cmp.shared import ICoolService +from cool2.cmp.pycompiler import AttributeProduction class IParser(ICoolService): """ Parser interface to implement """ - def __call__(self, tokens:List[ICoolToken]) -> BaseAST: + def __call__(self, tokens:List[ICoolToken], context) -> List[AttributeProduction]: raise NotImplementedError() diff --git a/src/cool_cmp/parser/parser2.py b/src/cool_cmp/parser/parser2.py new file mode 100644 index 000000000..c8ce731b1 --- /dev/null +++ b/src/cool_cmp/parser/parser2.py @@ -0,0 +1,32 @@ +from cool_cmp.parser.interface import IParser +from cool_cmp.lexer.interface import ICoolToken +from cool_cmp.shared.errors import ErrorTracker +from cool_cmp.parser.errors import SyntacticCoolError +from cool2.cool.parser.cool_parser import cool_parser +from cool2.cool.pipeline import syntax_pipeline +from cool_cmp.shared.ast import BaseAST +from typing import List,Dict + +class CoolParer2(IParser): + + @property + def name(self)->str: + return "parser" + + def __init__(self): + self.error_tracker = ErrorTracker() # Error tracker implementation + + def __call__(self, tokens:List[ICoolToken], context:Dict[str,object]) -> BaseAST: + result = syntax_pipeline(context) + new_keys = set(result.keys()).difference(set(context.keys())) + for key in new_keys: + self.add_extra_info(key, result[key]) + return BaseAST(result["ast"]) + + + def add_error(self, error:SyntacticCoolError): + self.error_tracker.add_error(error) + + def get_errors(self)->List[SyntacticCoolError]: + errors = self.error_tracker.get_errors() + return errors diff --git a/src/cool_cmp/semantic/interface.py b/src/cool_cmp/semantic/interface.py index 99b0c3cce..778956361 100644 --- a/src/cool_cmp/semantic/interface.py +++ b/src/cool_cmp/semantic/interface.py @@ -1,5 +1,6 @@ from cool_cmp.shared.ast import BaseAST from cool_cmp.shared import ICoolService +from cool2.cmp.pycompiler import AttributeProduction from typing import List class ISemantic(ICoolService): @@ -7,7 +8,7 @@ class ISemantic(ICoolService): Semantic interface to implement """ - def __call__(self, ast:BaseAST) -> BaseAST: + def __call__(self, parse:List[AttributeProduction]) -> BaseAST: raise NotImplementedError() diff --git a/src/cool_cmp/shared/__init__.py b/src/cool_cmp/shared/__init__.py index 01f9d920c..f33cf5e43 100644 --- a/src/cool_cmp/shared/__init__.py +++ b/src/cool_cmp/shared/__init__.py @@ -4,13 +4,32 @@ from cool_cmp.shared.pipeline import IPipeable, Pipe, Pipeline from cool_cmp.shared.errors import ErrorTracker, IErrorTraceable, CoolError -from typing import List +from typing import List, Dict class ICoolService(IErrorTraceable, IPipeable): """ Interface that should implement all parts of the cool pipeline """ - pass + + def __call__(self, arg:object, context:Dict[str,object]): + raise NotImplementedError() + + def add_extra_info(self, key:str, value:object): + """ + Add extra information that will be pass to all following CoolServices + """ + if not hasattr(self, "_extra"): + self._extra = {} + self._extra[key] = value + + def get_extra_info(self) -> Dict[str,object]: + """ + Get current extra information + """ + if not hasattr(self, "_extra"): + self._extra = {} + return self._extra + class SymbolTable(IErrorTraceable): """ @@ -21,6 +40,7 @@ class SymbolTable(IErrorTraceable): def __init__(self): self.last = None + self.context = {} self.__errors = ErrorTracker() def __setattr__(self, name:str, value): @@ -43,13 +63,14 @@ def __str__(self): class InterfacePipeline(Pipeline): - def __init__(self, pipeline:'InterfacePipeline', *interfaces:List[IPipeable]): + def __init__(self, pipeline:'InterfacePipeline', *interfaces:List[ICoolService]): def make_pipe(result:SymbolTable, interface:ICoolService): - returned = interface(result.last) + returned = interface(result.last, result.context.copy()) for error in interface.get_errors(): result.add_error(error) result.__setattr__(interface.name, returned) + result.context.update(interface.get_extra_info()) return result pipes = [] diff --git a/src/cool_cmp/shared/pipeline/__init__.py b/src/cool_cmp/shared/pipeline/__init__.py index a2bd104f2..af648efe7 100644 --- a/src/cool_cmp/shared/pipeline/__init__.py +++ b/src/cool_cmp/shared/pipeline/__init__.py @@ -11,5 +11,5 @@ class IPipeable: def name(self)->str: raise NotImplementedError() - def __call__(self, arg): + def __call__(self, *args, **kwargs): raise NotImplementedError() diff --git a/src/main.py b/src/main.py index 6d2294627..03d1da4f8 100644 --- a/src/main.py +++ b/src/main.py @@ -257,6 +257,7 @@ def get_errors(self)->List[CoolError]: with open(sys.argv[1]) as file: program = file.read() pipe = LexerPipeline() + pipe = ParserPipeline(pipe) result = pipe(program) for err in result.get_errors(): if isinstance(err, str): diff --git a/src/testing.cl b/src/testing.cl index ca6b68ac3..faf9530d9 100644 --- a/src/testing.cl +++ b/src/testing.cl @@ -1,11 +1,46 @@ -(* Integers, Identifiers, and Special Notation *) +class Main inherits IO { + number: Int <- 5; + + main () : Object { + testing_fibonacci(number) + }; + + testing_fibonacci(n: Int) : IO {{ + out_string("Iterative Fibonacci : "); + out_int(iterative_fibonacci(5)); + out_string("\n"); + + out_string("Recursive Fibonacci : "); + out_int(recursive_fibonacci(5)); + out_string("\n"); + }}; + + recursive_fibonacci (n: AUTO_TYPE) : AUTO_TYPE { + if n <= 2 then 1 else recursive_fibonacci(n - 1) + recursive_fibonacci(n - 2) fi + }; + iterative_fibonacci(n: AUTO_TYPE) : AUTO_TYPE { + let i: Int <- 2, n1: Int <- 1, n2: Int <- 1, temp: Int in { + while i < n loop + let temp: Int <- n2 in { + n2 <- n2 + n1; + n1 <- temp; + i <- i + 1; + } + pool; + n2; + } + }; +} + +(* Integers, Identifiers, and Special Notation *) +(* 0007 123 +1 -1 +90 -09 +11113 -4r *a *self* c++ 5! = 120, 2 + 2 = 5 or E = mc2; p + 1 @ p = 1: for x in range(len(b)) new / <- <<==> {( Int: Objet, Bool; String.string SELF_TYPE isvoid }) class Class if then else fi testing Testing ~007agent_bond james_007B0N3SS___ loop pool while tRuE or noT faLsE let in case of ESAC - +*) (* #3 INT_CONST 0007 #3 INT_CONST 123 From f96a5d0a738bca25a55e8235e48765b234777f16 Mon Sep 17 00:00:00 2001 From: Luiso Date: Sun, 18 Apr 2021 20:44:56 -0400 Subject: [PATCH 013/143] Cool2 Main Project cool_cmp will be deprecated Parser fixed: Missing semicolon at the end of a class definition Errors updates: All string errors were replaced with class errors TODO: Fix cool2 test to match new languages specs Pass github tests --- .gitignore | 3 + src/cool2/cool/error/errors.py | 91 +++++++++++++++++-- src/cool2/cool/grammar/cool_grammar.py | 8 +- src/cool2/cool/lib/std.cool | 12 +-- src/cool2/cool/parser/cool_parser.obj | Bin 150856 -> 151457 bytes src/cool2/cool/pipes/pipes.py | 1 + src/cool2/cool/semantic/context.py | 4 +- src/cool2/cool/semantic/type.py | 29 +++--- src/cool2/cool/visitors/visitors.py | 119 ++++++++++++++++--------- src/cool2/lib/parser/parser_lr.py | 4 +- src/cool2/main.py | 11 ++- src/coolc.sh | 2 +- src/testing.cl | 8 +- tests/utils/utils.py | 1 + 14 files changed, 205 insertions(+), 88 deletions(-) diff --git a/.gitignore b/.gitignore index 3559caca9..1af0ba55d 100644 --- a/.gitignore +++ b/.gitignore @@ -30,6 +30,9 @@ ## Generated if empty string is given at "Please type another file name for output:" .pdf +## User defined files: +*_log.txt + ## Bibliography auxiliary files (bibtex/biblatex/biber): *.bbl *.bcf diff --git a/src/cool2/cool/error/errors.py b/src/cool2/cool/error/errors.py index e5920dc80..99ccd204b 100644 --- a/src/cool2/cool/error/errors.py +++ b/src/cool2/cool/error/errors.py @@ -1,3 +1,7 @@ +STRING_TOO_LONG = "String too long. Max length is 1024 chars, have {0} chars" + +SYNTACTIC_ERROR = 'at or near "{0}"' + WRONG_SIGNATURE = 'Method "{0}" already defined in "{1}" with a different signature.' SELF_IS_READONLY = 'Variable "self" is read-only.' ATTRIBUTE_NOT_DEFINED = 'Attribute "{0}" is not defined in class "{1}".' @@ -27,21 +31,92 @@ TYPE_NOT_DEFINED = "Type '{0}' is not defined." TYPE_ALREADY_DEFINED = "Type with the same name ({0}) already in context." TYPE_CANT_INFER = "Cant infer type in given context." +TYPE_CANT_BE_INHERITED = "Type {0} cant be inherited" +NO_COMMON_TYPE = "No common types between {0} and {1}" +READ_IS_NOT_INT = "Invalid type: {0} is not an Int" class CoolError(Exception): + """ + Base Cool Error + """ + @property def text(self): - return self.args[0] + return str(self) + + ERROR_TYPE = "CoolError" + + FORMAT = "{}: {}" + + def __init__(self, msg:str, *args): + self.error = msg + self.args = args + + def print_error(self): + print(str(self)) + + def __str__(self): + return CoolError.FORMAT.format(self.ERROR_TYPE, self.error.format(*self.args)) + + def __repr__(self): + return str(self) +class PositionError(CoolError): + """ + Error class for positional errors + """ + + FORMAT = "({}, {}) - {}: ERROR {}" -class SemanticError(CoolError): - pass + def __init__(self, error_message:str, *args, **kwargs): + """ + kwargs: + token: The token where the error is + row: Row error + column: Column error + lex: Representation of the error + """ + super().__init__(error_message, *args) + self.row = kwargs.get("row") + self.column = kwargs.get("column") + self.lex = kwargs.get("lex") + if "token" in kwargs: + self.row = kwargs["token"].lex[1] + self.column = kwargs["token"].lex[2] + self.lex = kwargs["token"].lex[0] + + def set_position(self, row:int, column:int): + self.row = row + self.column = column + + def __str__(self): + if self.row == None or self.column == None: + return super().__str__() + return self.FORMAT.format(self.row,self.column, self.ERROR_TYPE, self.error.format(*self.args)) + +class LexerCoolError(PositionError): + """ + Error class for lexical errors + """ + + ERROR_TYPE = "LexicographicError" + +class SyntacticCoolError(PositionError): + """ + Error class for syntactic errors + """ + + ERROR_TYPE = "SyntacticError" + +class SemanticError(PositionError): + FORMAT = "({}, {}) - {}: {}" + ERROR_TYPE = "SemanticError" + +class TypeCoolError(SemanticError): + ERROR_TYPE = "TypeError" class InferError(SemanticError): - pass + ERROR_TYPE = "InferenceError" class RunError(CoolError): - pass - -class CoolTypeError(CoolError): - pass + ERROR_TYPE = "RunError" diff --git a/src/cool2/cool/grammar/cool_grammar.py b/src/cool2/cool/grammar/cool_grammar.py index 5d574cc41..c487ab72a 100644 --- a/src/cool2/cool/grammar/cool_grammar.py +++ b/src/cool2/cool/grammar/cool_grammar.py @@ -33,11 +33,11 @@ class_list %= def_class + class_list, lambda h,s: [s[1]] + s[2] # ??? -def_class %= classx + typex + ocur + feature_list + ccur, lambda h,s: ClassDeclarationNode(s[2][0],s[4],row=s[1][1] ,column=s[1][2]) -def_class %= classx + typex + inherits + typex + ocur + feature_list + ccur, lambda h,s: ClassDeclarationNode(s[2][0],s[6],s[4][0],row=s[1][1] ,column=s[1][2]) +def_class %= classx + typex + ocur + feature_list + ccur + semi, lambda h,s: ClassDeclarationNode(s[2][0],s[4],row=s[1][1] ,column=s[1][2]) +def_class %= classx + typex + inherits + typex + ocur + feature_list + ccur + semi, lambda h,s: ClassDeclarationNode(s[2][0],s[6],s[4][0],row=s[1][1] ,column=s[1][2]) -def_class %= classx + typex + ocur + ccur, lambda h,s: ClassDeclarationNode(s[2][0],[],row=s[1][1] ,column=s[1][2]) -def_class %= classx + typex + inherits + typex + ocur + ccur, lambda h,s: ClassDeclarationNode(s[2][0],[],s[4][0],row=s[1][1] ,column=s[1][2]) +def_class %= classx + typex + ocur + ccur + semi, lambda h,s: ClassDeclarationNode(s[2][0],[],row=s[1][1] ,column=s[1][2]) +def_class %= classx + typex + inherits + typex + ocur + ccur + semi, lambda h,s: ClassDeclarationNode(s[2][0],[],s[4][0],row=s[1][1] ,column=s[1][2]) # ??? feature_list %= feature + semi, lambda h,s: [s[1]] diff --git a/src/cool2/cool/lib/std.cool b/src/cool2/cool/lib/std.cool index 91de5a293..c045dfee5 100644 --- a/src/cool2/cool/lib/std.cool +++ b/src/cool2/cool/lib/std.cool @@ -12,24 +12,24 @@ class Object { copy() : SELF_TYPE { self }; -} +}; class String inherits Object { length() : Int { 0 }; concat(s : String) : String { s }; substr(i : Int, l : Int) : String { "string" }; -} +}; class IO inherits Object { out_string(x : String) : SELF_TYPE { self }; out_int(x : Int) : SELF_TYPE { self }; in_string() : String { "string" }; in_int() : Int { 0 }; -} +}; -class Int inherits Object { } +class Int inherits Object { }; -class Bool inherits Object { } +class Bool inherits Object { }; -class Void { } \ No newline at end of file +class Void { }; \ No newline at end of file diff --git a/src/cool2/cool/parser/cool_parser.obj b/src/cool2/cool/parser/cool_parser.obj index 7610d1f5a4751a05df9b811ae1fea728748e76a5..0d1711928aaacede2d6f49978a3f338b4a1ec09a 100644 GIT binary patch literal 151457 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 literal 150856 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 diff --git a/src/cool2/cool/pipes/pipes.py b/src/cool2/cool/pipes/pipes.py index 558ade6e0..0b707a213 100644 --- a/src/cool2/cool/pipes/pipes.py +++ b/src/cool2/cool/pipes/pipes.py @@ -116,6 +116,7 @@ def parse_text_pipe(result:dict, language_grammar=G, language_lexer=cool_lexer, "text_parse": parse, "language": lang, "text_tokens": tokens, + "parser":language_parser }) result["errors"].extend(errors) diff --git a/src/cool2/cool/semantic/context.py b/src/cool2/cool/semantic/context.py index 7cbde0b69..8256da6d2 100644 --- a/src/cool2/cool/semantic/context.py +++ b/src/cool2/cool/semantic/context.py @@ -22,10 +22,10 @@ def get_type(self, name:str,self_type=None): try: return self.types[name] except KeyError: - raise SemanticError(TYPE_NOT_DEFINED.format(name)) + raise SemanticError(TYPE_NOT_DEFINED, name) def create_type(self, name:str): if name in self.types: - raise SemanticError(TYPE_ALREADY_DEFINED.format(name)) + raise SemanticError(TYPE_ALREADY_DEFINED, name) typex = self.types[name] = Type(name) return typex \ No newline at end of file diff --git a/src/cool2/cool/semantic/type.py b/src/cool2/cool/semantic/type.py index de8ce3cc6..e3d554bcd 100644 --- a/src/cool2/cool/semantic/type.py +++ b/src/cool2/cool/semantic/type.py @@ -3,10 +3,11 @@ from cmp.semantic import SemanticError as DeprecatedSemanticError from cool.ast.ast import VoidNode,ConstantNumNode,StringNode,BoolNode,SpecialNode,InstantiateNode from cool.semantic.atomic import ClassInstance -from cool.error.errors import RunError, SemanticError, CoolTypeError, InferError, \ +from cool.error.errors import RunError, SemanticError, TypeCoolError, InferError, \ VOID_TYPE_CONFORMS, METHOD_NOT_DEFINED, METHOD_ALREADY_DEFINED, \ SUBSTR_OUT_RANGE, ATTRIBUTE_NOT_DEFINED, ATTRIBUTE_ALREADY_DEFINED, \ - ATTRIBUTE_CANT_INFER, METHOD_CANT_INFER, TYPE_CANT_INFER + ATTRIBUTE_CANT_INFER, METHOD_CANT_INFER, TYPE_CANT_INFER, TYPE_CANT_BE_INHERITED, \ + NO_COMMON_TYPE, READ_IS_NOT_INT import cool.visitors.utils as ut class Type(DeprecatedType): @@ -21,7 +22,7 @@ def set_parent(self,parent): DeprecatedType.set_parent(self,parent) if not parent.can_have_children: self.parent = None - raise SemanticError(f"Type {parent.name} cant be inherited") + raise SemanticError(TYPE_CANT_BE_INHERITED, parent.name) def __hash__(self): return hash(self.name) @@ -35,17 +36,17 @@ def get_method(self,name:str,args:int,current_type = None, only_local = False): return next(method for method in self.methods if method.name == name and len(method.param_names)==args) except StopIteration: if self.parent is None: - raise SemanticError(METHOD_NOT_DEFINED.format(name, "", self.name, args)) + raise SemanticError(METHOD_NOT_DEFINED,name, "", self.name, args) try: if not only_local: return self.parent.get_method(name,args) raise SemanticError() except SemanticError: - raise SemanticError(METHOD_NOT_DEFINED.format(name, "" if not only_local else " locally", self.name, args)) + raise SemanticError(METHOD_NOT_DEFINED, name, "" if not only_local else " locally", self.name, args) def define_method(self, name:str, param_names:list, param_types:list, return_type): if (name,len(param_names)) in ((method.name,len(method.param_names)) for method in self.methods): - raise SemanticError(METHOD_ALREADY_DEFINED.format(name, len(param_names), self.name)) + raise SemanticError(METHOD_ALREADY_DEFINED, name, len(param_names), self.name) method = Method(name, param_names, param_types, return_type) self.methods.append(method) return method @@ -62,11 +63,11 @@ def get_attribute(self, name:str): return next(attr for attr in self.attributes if attr.name == name) except StopIteration: if self.parent is None: - raise SemanticError(ATTRIBUTE_NOT_DEFINED.format(name, self.name)) + raise SemanticError(ATTRIBUTE_NOT_DEFINED, name, self.name) try: return self.parent.get_attribute(name) except SemanticError: - raise SemanticError(ATTRIBUTE_NOT_DEFINED.format(name, self.name)) + raise SemanticError(ATTRIBUTE_NOT_DEFINED, name, self.name) def define_attribute(self, name:str, typex): try: @@ -76,7 +77,7 @@ def define_attribute(self, name:str, typex): self.attributes.append(attribute) return attribute else: - raise SemanticError(ATTRIBUTE_ALREADY_DEFINED.format(name, self.name)) + raise SemanticError(ATTRIBUTE_ALREADY_DEFINED, name, self.name) def conforms_to(self,other,current_type): self_type = self if not isinstance(self,SelfType) else current_type @@ -107,7 +108,7 @@ def join(self,other,current_type): for common in self_types: if common in other_types: return common - raise CoolTypeError(f"No common types between {self.name} and {other.name}") + raise TypeCoolError(NO_COMMON_TYPE, self.name, other.name) @property def can_have_children(self): @@ -208,7 +209,7 @@ def substr(scope,context,operator,errors,**kwargs): typex = context.get_type('String') value_sliced = value[i:i+l] if len(value) < l-i or l < 0: - raise RunError(SUBSTR_OUT_RANGE.format(value,i,l)) + raise RunError(SUBSTR_OUT_RANGE,value,i,l) return ClassInstance(typex,context,operator,errors,value=value_sliced) @property @@ -288,7 +289,7 @@ def in_int(scope,context,operator,errors,**kwargs): else: raise ValueError() except ValueError: - raise RunError(f'Invalid type: {value} is not an Int') + raise RunError(READ_IS_NOT_INT, value) class AutoType(Type): def __init__(self,context): @@ -305,13 +306,13 @@ def update_possibles(self, new_possibles): def get_attribute(self,name): self.update_possibles([x for x in self.possibles if any(attr for attr,typex in x.all_attributes() if attr.name==name)]) if not self.possibles: - raise InferError(ATTRIBUTE_CANT_INFER.format(name)) + raise InferError(ATTRIBUTE_CANT_INFER, name) return self.possibles[-1].get_attribute(name) def get_method(self,name,args,current_type = None): self.update_possibles([x for x in self.possibles if any(meth for meth,typex in x.all_methods() if (meth.name==name and len(meth.param_names) == args))]) if not self.possibles: - raise InferError(METHOD_CANT_INFER.format(name, args)) + raise InferError(METHOD_CANT_INFER, name, args) return self.possibles[-1].get_method(name,args) def conforms_to(self,other,current_type): diff --git a/src/cool2/cool/visitors/visitors.py b/src/cool2/cool/visitors/visitors.py index 0ac7b0754..a99ac2600 100644 --- a/src/cool2/cool/visitors/visitors.py +++ b/src/cool2/cool/visitors/visitors.py @@ -338,6 +338,10 @@ def __init__(self, errors=[], context=None): self.context = context self.errors = errors + def add_semantic_error(self, error:SemanticError, row:int, column:int): + error.set_position(row, column) + self.errors.append(error) + @visitor.on('node') def visit(self, node): pass @@ -359,23 +363,24 @@ def visit(self, node): try: self.context.create_type(class_decl.id) except SemanticError as er: - self.errors.append(er.text + f' Line:{class_decl.row} Column:{class_decl.column}') + self.add_semantic_error(er, class_decl.row, class_decl.column) for class_decl in node.declarations: try: curr_type = self.context.get_type(class_decl.id) except SemanticError as er: - self.errors.append(er.text+ f' Line:{class_decl.row} Column:{class_decl.column}') + self.add_semantic_error(er, class_decl.row, class_decl.column) if class_decl.parent: try: curr_type.set_parent(self.context.get_type(class_decl.parent)) except SemanticError as er: - self.errors.append(er.text+ f' Line:{class_decl.row} Column:{class_decl.column}') + self.add_semantic_error(er, class_decl.row, class_decl.column) cycles = ut.any_cycles(ut.build_graph_dict(self.context.types)) for type1,type2 in cycles: - self.errors.append(CIRCULAR_DEPENDENCY.format(type1, type2)) + error = SemanticError(CIRCULAR_DEPENDENCY, type1.name, type2.name) + self.errors.append(error) class TypeBuilder: def __init__(self, context, errors=[]): @@ -383,6 +388,10 @@ def __init__(self, context, errors=[]): self.current_type = None self.errors = errors + def add_semantic_error(self, error:SemanticError, row:int, column:int): + error.set_position(row, column) + self.errors.append(error) + @visitor.on('node') def visit(self, node): pass @@ -405,12 +414,12 @@ def visit(self, node): node.type = self.context.get_type(node.type) except SemanticError as er: node.type = ErrorType() - self.errors.append(er.text + f' Line:{node.row} Column:{node.column}') + self.add_semantic_error(er, node.row, node.column) try: attribute = self.current_type.define_attribute(node.id,node.type) attribute.node = node except SemanticError as er: - self.errors.append(er.text + f' Line:{node.row} Column:{node.column}') + self.add_semantic_error(er, node.row, node.column) @visitor.when(FuncDeclarationNode) def visit(self, node): @@ -419,16 +428,16 @@ def visit(self, node): x.type = self.context.get_type(x.type) except SemanticError as er: x.type = ErrorType() - self.errors.append(er.text + f' Line:{x.row} Column:{x.column}') + self.add_semantic_error(er, node.row, node.column) try: node.type = self.context.get_type(node.type) except SemanticError as er: - self.errors.append(er.text + f' Line:{node.row} Column:{node.column}') + self.add_semantic_error(er, node.row, node.column) try: method = self.current_type.define_method(node.id,[x.id for x in node.params],[x.type for x in node.params],node.type) method.node = node except SemanticError as er: - self.errors.append(er.text + f' Line:{node.row} Column:{node.column}') + self.add_semantic_error(er, node.row, node.column) class TypeChecker: """ @@ -447,7 +456,11 @@ def complete_initial_types(self): complete_types = ['Object','String','IO'] for cmp_type in complete_types: self.context.get_type(cmp_type).complete() - + + def add_semantic_error(self, error:SemanticError, row:int, column:int): + error.set_position(row, column) + self.errors.append(error) + @visitor.on('node') def visit(self, node, scope): pass @@ -463,10 +476,10 @@ def visit(self, node, scope=None): MainType = self.context.get_type('Main') try: main_method = MainType.get_method('main',0, only_local = True) - except SemanticError as cexc: - self.errors.append(cexc.text + " " + NO_ENTRY_POINT) + except SemanticError as er: + self.errors.append(SemanticError(NO_ENTRY_POINT)) else: - self.errors.append(NO_MAIN_TYPE) + self.errors.append(SemanticError(NO_MAIN_TYPE)) self.complete_initial_types() @@ -492,7 +505,7 @@ def visit(self, node, scope): self.visit(node.expr,scope) if not node.expr.type.conforms_to(node.type,self.current_type): - self.errors.append(INCOMPATIBLE_TYPES.format(node.expr.type.name,node.type.name) + f' Line:{node.row} Column:{node.column}') + self.add_semantic_error(TypeCoolError(INCOMPATIBLE_TYPES, node.expr.type.name, node.type.name),node.row,node.column) @visitor.when(FuncDeclarationNode) def visit(self, node, scope): @@ -503,7 +516,8 @@ def visit(self, node, scope): methods = [x for x,typex in methods if x.name == node.id and len(x.param_names) == len(node.params)] methods = [x for x in methods if x != current_method] if methods: - self.errors.append(WRONG_SIGNATURE.format(methods[0].name,self.current_type.parent.name) + f' Line:{node.row} Column:{node.column}') + err = SemanticError(WRONG_SIGNATURE,methods[0].name,self.current_type.parent.name) + self.add_semantic_error(err, node.row, node.column) f_scope = scope.create_child() node.scope = f_scope @@ -515,7 +529,8 @@ def visit(self, node, scope): self.visit(node.body,f_scope) if not isinstance(node.type,VoidType) and not node.body.type.conforms_to(node.type,self.current_type): - self.errors.append(INCOMPATIBLE_TYPES.format(node.body.type.name,node.type.name) + f' Line:{node.row} Column:{node.column}') + err = TypeCoolError(INCOMPATIBLE_TYPES, node.body.type.name,node.type.name) + self.add_semantic_error(err, node.row, node.column) @visitor.when(ParamNode) def visit(self, node, scope): @@ -524,20 +539,22 @@ def visit(self, node, scope): @visitor.when(VarDeclarationNode) def visit(self, node, scope): if node.id == 'self': - self.errors.append(SELF_IS_READONLY + f' Line:{node.row} Column:{node.column}') + err = SemanticError(SELF_IS_READONLY) + self.add_semantic_error(err, node.row, node.column) try: node.type = self.context.get_type(node.type) except SemanticError as er: node.type = ErrorType() - self.errors.append(er.text + f' Line:{node.row} Column:{node.column}') + self.add_semantic_error(er, node.row, node.column) if not node.expr: if node.type.name != "AUTO_TYPE": node.expr = node.type.default node.expr.type = node.type # if type is void then is not assigned in the visit else the type is overridden else: if scope.is_local(node.id): - self.errors.append(LOCAL_ALREADY_DEFINED.format(node.id,self.current_method.name) + f' Line:{node.row} Column:{node.column}') + er = SemanticError(LOCAL_ALREADY_DEFINED, node.id, self.current_method.name) + self.add_semantic_error(er, node.row, node.column) else: scope.define_variable(node.id,node.type) return # No default expr can be assing at this moment @@ -545,29 +562,34 @@ def visit(self, node, scope): self.visit(node.expr,scope) if scope.is_local(node.id): - self.errors.append(LOCAL_ALREADY_DEFINED.format(node.id,self.current_method.name) + f' Line:{node.row} Column:{node.column}') + er = SemanticError(LOCAL_ALREADY_DEFINED, node.id, self.current_method.name) + self.add_semantic_error(er, node.row, node.column) else: scope.define_variable(node.id,node.type) if not node.expr.type.conforms_to(node.type,self.current_type): - self.errors.append(INCOMPATIBLE_TYPES.format(node.expr.type.name,node.type.name) + f' Line:{node.row} Column:{node.column}') + er = TypeCoolError(INCOMPATIBLE_TYPES, node.expr.type.name, node.type.name) + self.add_semantic_error(er, node.row, node.column) @visitor.when(AssignNode) def visit(self, node, scope): if node.id == 'self': - self.errors.append(SELF_IS_READONLY + f' Line:{node.row} Column:{node.column}') + er = SemanticError(SELF_IS_READONLY) + self.add_semantic_error(er, node.row, node.column) self.visit(node.expr,scope) if not scope.is_defined(node.id): - self.errors.append(VARIABLE_NOT_DEFINED.format(node.id,self.current_method.name) + f' Line:{node.row} Column:{node.column}') + er = SemanticError(VARIABLE_NOT_DEFINED, node.id,self.current_method.name) + self.add_semantic_error(er, node.row, node.column) node.type = node.expr.type else: var_info = scope.find_variable(node.id) node.type = var_info.type if not node.expr.type.conforms_to(node.type,self.current_type): - self.errors.append(INCOMPATIBLE_TYPES.format(node.expr.type.name,node.type.name) + f' Line:{node.row} Column:{node.column}') + er = TypeCoolError(INCOMPATIBLE_TYPES, node.expr.type.name, node.type.name) + self.add_semantic_error(er, node.row, node.column) @visitor.when(CallNode) def visit(self, node, scope): @@ -580,18 +602,20 @@ def visit(self, node, scope): if node.at: node.at = self.context.get_type(node.at) if not node.obj.type.conforms_to(node.at,self.current_type): - self.errors.append(INCOMPATIBLE_TYPES.format(node.obj.type.name,node.at.name) + f' Line:{node.row} Column:{node.column}') + er = TypeCoolError(INCOMPATIBLE_TYPES, node.obj.type.name, node.at.name) + self.add_semantic_error(er, node.row, node.column) method = node.at.get_method(node.id,len(node.args),self.current_type) else: method = node.obj.type.get_method(node.id,len(node.args),self.current_type) not_conform = [(x,y.type) for x,y in zip(method.param_types,node.args) if not y.type.conforms_to(x,self.current_type)] for x,y in not_conform: - self.errors.append(INCOMPATIBLE_TYPES.format(y.name,x.name) + f' Line:{node.row} Column:{node.column}') + er = TypeCoolError(INCOMPATIBLE_TYPES, y.name, x.name) + self.add_semantic_error(er, node.row, node.column) node.type = method.return_type if method.return_type.name != "SELF_TYPE" else node.obj.type except SemanticError as er: node.type = ErrorType() - self.errors.append(er.text + f' Line:{node.row} Column:{node.column}') + self.add_semantic_error(er, node.row, node.column) @visitor.when(BlockNode) def visit(self, node:BlockNode, scope): @@ -610,12 +634,13 @@ def visit(self, node:ConditionalNode, scope): self.visit(node.else_expr,scope) if node.condition.type != self.context.get_type('Bool') and node.condition.type != ErrorType(): - self.errors.append(NOT_BOOLEAN_CONDITION.format(node.condition.type.name)+ f' Line:{node.row} Column:{node.column}') + er = TypeCoolError(NOT_BOOLEAN_CONDITION, node.condition.type.name) + self.add_semantic_error(er, node.row, node.column) try: node.type = node.get_return_type(self.current_type) - except InferError as cexc: + except InferError as er: node.type = self.context.get_type("Error") - self.errors.append(cexc.text + f" Line:{node.row} Column:{node.column}") + self.add_semantic_error(er, node.row, node.column) @visitor.when(LetNode) def visit(self, node: LetNode, scope): @@ -634,13 +659,15 @@ def visit(self, node:WhileNode, scope): node.type = self.context.get_type('Object') self.visit(node.condition,scope) if node.condition.type != self.context.get_type('Bool'): - self.errors.append(NOT_BOOLEAN_CONDITION.format(node.condition.type.name)+ f' Line:{node.row} Column:{node.column}') + er = TypeCoolError(NOT_BOOLEAN_CONDITION, node.condition.type.name) + self.add_semantic_error(er, node.row, node.column) self.visit(node.expr,scope) @visitor.when(CheckNode) def visit(self, node:CheckNode, scope): if node.id == 'self': - self.errors.append(SELF_IS_READONLY + f' Line:{node.row} Column:{node.column}') + er = SemanticError(SELF_IS_READONLY) + self.add_semantic_error(er, node.row, node.column) node.type = self.context.get_type(node.type) node.scope = scope.create_child() @@ -661,7 +688,8 @@ def visit(self, node:CaseNode, scope): # if not (node.expr.type.conforms_to(param.type,self.current_type) or param.type.conforms_to(node.expr.type,self.current_type)): # self.errors.append(f"Incompatible types {param.type.name} with {node.expr.type.name}" + f' Line:{node.row} Column:{node.column}') if any(x for x in types if x[1] == param.type): - self.errors.append(CASE_TYPES_REPEATED.format(param.expr.type.name) + f" Line:{param.row} Column:{param.column}") + er = TypeCoolError(CASE_TYPES_REPEATED, param.expr.type.name) + self.add_semantic_error(er, param.row, param.column) types.append((i,param.expr.type)) static_type = types[0][1] @@ -675,7 +703,8 @@ def visit(self, node, scope): if not self.operator.operation_defined(node,node.member.type): node.type = ErrorType() - self.errors.append(INVALID_UNARY_OPERATION.format(self.operator.get_operator(node),node.member.type.name) + f' Line:{node.row} Column:{node.column}') + er = TypeCoolError(INVALID_UNARY_OPERATION, self.operator.get_operator(node), node.member.type.name) + self.add_semantic_error(er, node.row, node.column) else: node.type = self.operator.type_of_operation(node,node.member.type) @@ -686,7 +715,8 @@ def visit(self, node, scope): if not self.operator.operation_defined(node,node.left.type,node.right.type): node.type = ErrorType() - self.errors.append(INVALID_BINARY_OPERATION.format(self.operator.get_operator(node),node.left.type.name,node.right.type.name) + f' Line:{node.row} Column:{node.column}') + er = TypeCoolError(INVALID_BINARY_OPERATION,self.operator.get_operator(node), node.left.type.name,node.right.type.name) + self.add_semantic_error(er, node.row, node.column) else: node.type = self.operator.type_of_operation(node,node.left.type,node.right.type) @@ -702,7 +732,8 @@ def visit(self, node, scope): def visit(self, node:StringNode, scope): node.type = self.context.get_type("String") if len(node.lex) > 1024: - self.errors.append(f"String to long. Max length is 1024 chars, have {len(node.lex)} chars" + f' Line:{node.row} Column:{node.column}') + er = LexerCoolError(STRING_TOO_LONG, len(node.lex)) + self.add_semantic_error(er, node.row, node.column) @visitor.when(VoidNode) def visit(self, node, scope): @@ -715,7 +746,8 @@ def visit(self, node, scope): node.type = var.type else: node.type = ErrorType() - self.errors.append(VARIABLE_NOT_DEFINED.format(node.lex,self.current_method.name) + f' Line:{node.row} Column:{node.column}') + er = SemanticError(VARIABLE_NOT_DEFINED, node.lex, self.current_method.name) + self.add_semantic_error(er, node.row, node.column) @visitor.when(InstantiateNode) def visit(self, node, scope): @@ -724,7 +756,7 @@ def visit(self, node, scope): except SemanticError as er: node.type = ErrorType() - self.errors.append(er.text + f' Line:{node.row} Column:{node.column}') + self.add_semantic_error(er, node.row, node.column) class AutoResolver: @@ -740,8 +772,8 @@ def change_auto_to_concrete_type(self, node, node_type_name="type", less_concret setattr(node, node_type_name, typex.get_lower(self.current_type)) else: setattr(node, node_type_name, typex.get_higher(self.current_type)) - except InferError as cexc: - self.errors.append(cexc.text + f" Line:{node.row} Column:{node.column}") + except InferError as er: + self.add_semantic_error(er, node.row, node.column) @visitor.on('node') def visit(self, node): @@ -893,7 +925,7 @@ def visit(self, node:ProgramNode, scope=None): try: value = self.visit(method.body,main_instance.scope) except RunError as err: - self.errors.append(err.text) + self.errors.append(err) return None return value @@ -1047,8 +1079,9 @@ def visit(self, node:VariableNode, scope): try: attr = [x.type for x in scope.locals if x.name == 'self'][0].get_attribute(node.lex) value = self.visit(attr.node.expr, scope) - except SemanticError: # Attr not found - self.errors.append(exc.args[0] + f' Line:{node.row} Column:{node.column}') + except SemanticError as er: # Attr not found + er.set_position(node.row, node.column) + self.errors.append(er) return value @visitor.when(InstantiateNode) diff --git a/src/cool2/lib/parser/parser_lr.py b/src/cool2/lib/parser/parser_lr.py index f02876bad..49d47415f 100644 --- a/src/cool2/lib/parser/parser_lr.py +++ b/src/cool2/lib/parser/parser_lr.py @@ -7,6 +7,7 @@ from lib.parser.parser import Parser from lib.grammar.grammar_fixer import fix_non_derive_terminal from lib.utils.automaton import state_transpose +from cool.error.errors import SyntacticCoolError, SYNTACTIC_ERROR ###################### LR0 and SLR1 ########################### def get_state(visited,pending,item): @@ -99,7 +100,8 @@ def __call__(self, tokens, errors,finding_conflict = False): except KeyError: # errors.append(f'Invalid transition ({state},{lookahead}) doesnt exist expected {[ x[1] for x in self.action if x[0] == state ]}') posibles = [x for x in self.action if x[0] == state ] - errors.append(f"Invalid transition near '{lookahead.lex[0]}'. Expected: {', '.join([ str(x[1]) for x in posibles ])}. Line:{lookahead.lex[1] + 1} Column:{lookahead.lex[2] + 1}") + errors.append(SyntacticCoolError(SYNTACTIC_ERROR, lookahead.lex[0], token=lookahead)) + # errors.append(f"Invalid transition near '{lookahead.lex[0]}'. Expected: {', '.join([ str(x[1]) for x in posibles ])}. Line:{lookahead.lex[1] + 1} Column:{lookahead.lex[2] + 1}") if len(posibles) == 1: tokens.insert(cursor + 1, Token((str(posibles[0][1]), lookahead.lex[1], lookahead.lex[2]), posibles[0][1])) cursor += 1 diff --git a/src/cool2/main.py b/src/cool2/main.py index 9af449f95..66c163533 100644 --- a/src/cool2/main.py +++ b/src/cool2/main.py @@ -10,6 +10,7 @@ # plac annotation (description, type of arg [option, flag, positional], abrev, type, choices) def main( program_dir:("Path to cool program", "positional"), + output_dir:("Path for the compiled mips", "positional"), out_infer:("Creates a file containing the inferred types", 'flag', 'i'), verbose:("Print more info", 'flag', 'v'), update_cool_parser:("Update pickled cool parser", 'flag', 'ucp'), @@ -48,7 +49,7 @@ def main( if program_dir == None: print("If no update flag is provided 'program_dir' is required") - exit() + exit(1) with open(program_dir) as file: file_content = file.read() @@ -64,9 +65,11 @@ def main( file.write(reconstr) if g_errors: - print("Errors\n", *[f"- {x}\n" for x in g_errors]) - if hasattr(value, "value"): - print("Value:",value.value) + for err in g_errors: + print(err) + exit(1) + # if hasattr(value, "value"): + # print("Value:",value.value) if __name__ == "__main__": plac.call(main) \ No newline at end of file diff --git a/src/coolc.sh b/src/coolc.sh index 433d0d235..223ee34ea 100755 --- a/src/coolc.sh +++ b/src/coolc.sh @@ -3,7 +3,7 @@ INPUT_FILE=$1 OUTPUT_FILE=${INPUT_FILE:0: -2}mips BASEDIR=$(dirname "$0") -PROGRAM=$BASEDIR/main.py +PROGRAM=$BASEDIR/cool2/main.py # Si su compilador no lo hace ya, aquí puede imprimir la información de contacto echo "WLD CoolCompiler v0.0" diff --git a/src/testing.cl b/src/testing.cl index faf9530d9..511873774 100644 --- a/src/testing.cl +++ b/src/testing.cl @@ -31,16 +31,14 @@ class Main inherits IO { n2; } }; -} +}; (* Integers, Identifiers, and Special Notation *) -(* -0007 123 +1 -1 +90 -09 +11113 -4r *a *self* c++ +(* 0007 123 +1 -1 +90 -09 +11113 -4r *a *self* c++ 5! = 120, 2 + 2 = 5 or E = mc2; p + 1 @ p = 1: for x in range(len(b)) new / <- <<==> {( Int: Objet, Bool; String.string SELF_TYPE isvoid }) class Class if then else fi testing Testing ~007agent_bond james_007B0N3SS___ -loop pool while tRuE or noT faLsE let in case of ESAC -*) +loop pool while tRuE or noT faLsE let in case of ESAC *) (* #3 INT_CONST 0007 #3 INT_CONST 123 diff --git a/tests/utils/utils.py b/tests/utils/utils.py index 65f7df874..610243571 100644 --- a/tests/utils/utils.py +++ b/tests/utils/utils.py @@ -53,6 +53,7 @@ def compare_errors(compiler_path: str, cool_file_path: str, error_file_path: str return_code, output = sp.returncode, sp.stdout.decode() except subprocess.TimeoutExpired: assert False, COMPILER_TIMEOUT + f.write(cool_file_path + "\n") f.write(output + "\n") assert return_code == 1, TEST_MUST_FAIL % get_file_name(cool_file_path) From b1aa897b79f6ef79227b5cd53f025f972b3e2daa Mon Sep 17 00:00:00 2001 From: Luiso Date: Fri, 23 Apr 2021 11:08:22 -0400 Subject: [PATCH 014/143] Nothing Changed --- .gitignore | 834 +++---- .vscode/launch.json | 30 +- .vscode/settings.json | 4 +- src/a.cl | 66 +- src/cool2/__init__.py | 8 +- src/cool2/cmp/ast.py | 154 +- src/cool2/cmp/automata.py | 418 ++-- src/cool2/cmp/cil.py | 460 ++-- src/cool2/cmp/evaluation.py | 64 +- src/cool2/cmp/languages.py | 454 ++-- src/cool2/cmp/nbpackage.py | 172 +- src/cool2/cmp/pycompiler.py | 1062 ++++---- src/cool2/cmp/semantic.py | 440 ++-- src/cool2/cmp/tools/automata.py | 6 +- src/cool2/cmp/tools/evaluation.py | 6 +- src/cool2/cmp/tools/parsing.py | 32 +- src/cool2/cmp/tools/regex.py | 6 +- src/cool2/cmp/utils.py | 436 ++-- src/cool2/cmp/visitor.py | 160 +- src/cool2/cool/ast/ast.py | 532 ++-- src/cool2/cool/error/errors.py | 244 +- src/cool2/cool/grammar/comment_grammar.py | 36 +- src/cool2/cool/grammar/cool_grammar.py | 282 +-- src/cool2/cool/lexer/comment_lexer.py | 46 +- src/cool2/cool/lexer/cool_lexer.py | 94 +- src/cool2/cool/lexer/utils.py | 38 +- src/cool2/cool/lib/std.cool | 68 +- src/cool2/cool/libs.py | 58 +- src/cool2/cool/parser/comment_parser.py | 16 +- src/cool2/cool/parser/cool_parser.py | 16 +- src/cool2/cool/parser/utils.py | 50 +- src/cool2/cool/pipeline.py | 76 +- src/cool2/cool/pipes/pipeline.py | 74 +- src/cool2/cool/pipes/pipes.py | 654 ++--- src/cool2/cool/pipes/utils.py | 36 +- src/cool2/cool/semantic/atomic.py | 98 +- src/cool2/cool/semantic/context.py | 60 +- src/cool2/cool/semantic/operations.py | 366 +-- src/cool2/cool/semantic/scope.py | 190 +- src/cool2/cool/semantic/type.py | 776 +++--- src/cool2/cool/visitors/utils.py | 164 +- src/cool2/cool/visitors/visitors.py | 2180 ++++++++--------- src/cool2/cool_programs/program1.cl | 68 +- src/cool2/cool_programs/program1.cl.infer.cl | 130 +- src/cool2/lib/grammar/grammar_fixer.py | 802 +++--- src/cool2/lib/grammar/grammar_parser.py | 348 +-- src/cool2/lib/grammar/grammar_tokens.py | 282 +-- src/cool2/lib/grammar/grammar_util.py | 40 +- src/cool2/lib/lang/language.py | 144 +- src/cool2/lib/lang/language_ll1.py | 124 +- src/cool2/lib/lang/language_lr.py | 106 +- src/cool2/lib/lang/language_regular.py | 794 +++--- src/cool2/lib/lexer/lexer.py | 232 +- src/cool2/lib/lexer/regex.py | 292 +-- src/cool2/lib/parser/parser.py | 88 +- src/cool2/lib/parser/parser_ll1.py | 664 ++--- src/cool2/lib/parser/parser_lr.py | 1370 +++++------ src/cool2/lib/shortcut.py | 22 +- src/cool2/lib/utils/algorithms.py | 42 +- src/cool2/lib/utils/automaton.py | 926 +++---- src/cool2/lib/utils/first_follow.py | 214 +- src/cool2/lib/utils/trie.py | 124 +- src/cool2/main.py | 148 +- src/cool2/test/test_class.py | 108 +- .../test/test_data/test_SELF_TYPE.meta_test | 74 +- src/cool2/test/test_data/test_at.meta_test | 56 +- .../test/test_data/test_attr_dep_1.meta_test | 38 +- .../test/test_data/test_attr_dep_2.meta_test | 34 +- .../test/test_data/test_auto_1.meta_test | 22 +- .../test/test_data/test_auto_2.meta_test | 16 +- .../test/test_data/test_auto_3.meta_test | 18 +- .../test/test_data/test_auto_4.meta_test | 30 +- .../test/test_data/test_auto_5.meta_test | 38 +- .../test_data/test_auto_conditional.meta_test | 206 +- .../test_data/test_auto_defaults.meta_test | 20 +- .../test_data/test_auto_undecidable.meta_test | 44 +- src/cool2/test/test_data/test_case.meta_test | 28 +- .../test_data/test_case_no_type.meta_test | 24 +- .../test_case_repeated_type.meta_test | 30 +- .../test_data/test_case_void_eval.meta_test | 28 +- .../test/test_data/test_comments.meta_test | 24 +- .../test/test_data/test_conditional.meta_test | 48 +- .../test/test_data/test_default.meta_test | 66 +- .../test_data/test_entry_point_1.meta_test | 22 +- .../test_data/test_entry_point_2.meta_test | 24 +- .../test_data/test_entry_point_3.meta_test | 22 +- .../test/test_data/test_equality.meta_test | 56 +- .../test/test_data/test_escape_line.meta_test | 24 +- .../test/test_data/test_in_out.meta_test | 46 +- .../test/test_data/test_instances.meta_test | 42 +- .../test/test_data/test_isvoid.meta_test | 30 +- src/cool2/test/test_data/test_let.meta_test | 38 +- src/cool2/test/test_data/test_other.meta_test | 42 +- .../test/test_data/test_recursive.meta_test | 22 +- .../test/test_data/test_substr_1.meta_test | 22 +- .../test/test_data/test_substr_2.meta_test | 22 +- .../test/test_data/test_substr_3.meta_test | 22 +- .../test_data/test_void_dispatch.meta_test | 22 +- src/cool2/test/test_data/test_while.meta_test | 26 +- .../test_data/test_zero_division.meta_test | 20 +- .../test_reconstruction/test_SELF_TYPE.cl | 146 +- src/cool2/test/test_reconstruction/test_at.cl | 92 +- .../test_reconstruction/test_attr_dep_1.cl | 38 +- .../test_reconstruction/test_attr_dep_2.cl | 38 +- .../test/test_reconstruction/test_auto_1.cl | 46 +- .../test/test_reconstruction/test_auto_2.cl | 32 +- .../test/test_reconstruction/test_auto_3.cl | 32 +- .../test/test_reconstruction/test_auto_4.cl | 82 +- .../test/test_reconstruction/test_auto_5.cl | 82 +- .../test_auto_conditional.cl | 344 +-- .../test_reconstruction/test_auto_defaults.cl | 28 +- .../test_auto_undecidable.cl | 66 +- .../test/test_reconstruction/test_case.cl | 52 +- .../test_reconstruction/test_case_no_type.cl | 48 +- .../test_case_repeated_type.cl | 68 +- .../test_case_void_eval.cl | 60 +- .../test/test_reconstruction/test_comments.cl | 18 +- .../test_reconstruction/test_conditional.cl | 186 +- .../test/test_reconstruction/test_default.cl | 90 +- .../test_reconstruction/test_entry_point_1.cl | 22 +- .../test_reconstruction/test_entry_point_2.cl | 32 +- .../test_reconstruction/test_entry_point_3.cl | 22 +- .../test/test_reconstruction/test_equality.cl | 66 +- .../test_reconstruction/test_escape_line.cl | 22 +- .../test_reconstruction/test_instances.cl | 70 +- .../test/test_reconstruction/test_isvoid.cl | 40 +- .../test/test_reconstruction/test_let.cl | 100 +- .../test/test_reconstruction/test_other.cl | 56 +- .../test_reconstruction/test_recursive.cl | 60 +- .../test/test_reconstruction/test_substr_1.cl | 62 +- .../test/test_reconstruction/test_substr_2.cl | 34 +- .../test/test_reconstruction/test_substr_3.cl | 34 +- .../test_reconstruction/test_void_dispatch.cl | 24 +- .../test/test_reconstruction/test_while.cl | 40 +- .../test_reconstruction/test_zero_division.cl | 22 +- src/cool2/test/test_run/01_program.cl | 66 +- src/cool2/test/test_run/02_program.cl | 46 +- src/cool2/test/test_run/03_program.cl | 42 +- src/cool2/test/test_run/04_program.cl | 92 +- src/cool2/test/test_run/05_program.cl | 44 +- src/cool2/test/tests.py | 68 +- src/cool_cmp/lexer/lexer2.py | 146 +- src/cool_cmp/parser/parser2.py | 64 +- src/cool_cmp/semantic/implementations.py | 492 ++-- src/cool_cmp/semantic/tests/type_test.py | 88 +- src/cool_cmp/semantic/types.py | 88 +- .../semantic/visitors/type_collector.py | 240 +- src/cool_cmp/semantic/visitors/utils.py | 92 +- src/cool_cmp/shared/visitor.py | 160 +- src/coolc.sh | 30 +- src/requirements.txt | 10 +- 151 files changed, 11701 insertions(+), 11701 deletions(-) diff --git a/.gitignore b/.gitignore index 1af0ba55d..fe260740f 100644 --- a/.gitignore +++ b/.gitignore @@ -1,417 +1,417 @@ -# File created using '.gitignore Generator' for Visual Studio Code: https://bit.ly/vscode-gig - -# Created by https://www.gitignore.io/api/visualstudiocode,linux,latex,python -# Edit at https://www.gitignore.io/?templates=visualstudiocode,linux,latex,python - -### LaTeX ### -## Core latex/pdflatex auxiliary files: -*.aux -*.lof -*.log -*.lot -*.fls -*.out -*.toc -*.fmt -*.fot -*.cb -*.cb2 -.*.lb - -## Intermediate documents: -*.dvi -*.xdv -*-converted-to.* -# these rules might exclude image files for figures etc. -# *.ps -# *.eps -# *.pdf - -## Generated if empty string is given at "Please type another file name for output:" -.pdf - -## User defined files: -*_log.txt - -## Bibliography auxiliary files (bibtex/biblatex/biber): -*.bbl -*.bcf -*.blg -*-blx.aux -*-blx.bib -*.run.xml - -## Build tool auxiliary files: -*.fdb_latexmk -*.synctex -*.synctex(busy) -*.synctex.gz -*.synctex.gz(busy) -*.pdfsync - -## Build tool directories for auxiliary files -# latexrun -latex.out/ - -## Auxiliary and intermediate files from other packages: -# algorithms -*.alg -*.loa - -# achemso -acs-*.bib - -# amsthm -*.thm - -# beamer -*.nav -*.pre -*.snm -*.vrb - -# changes -*.soc - -# comment -*.cut - -# cprotect -*.cpt - -# elsarticle (documentclass of Elsevier journals) -*.spl - -# endnotes -*.ent - -# fixme -*.lox - -# feynmf/feynmp -*.mf -*.mp -*.t[1-9] -*.t[1-9][0-9] -*.tfm - -#(r)(e)ledmac/(r)(e)ledpar -*.end -*.?end -*.[1-9] -*.[1-9][0-9] -*.[1-9][0-9][0-9] -*.[1-9]R -*.[1-9][0-9]R -*.[1-9][0-9][0-9]R -*.eledsec[1-9] -*.eledsec[1-9]R -*.eledsec[1-9][0-9] -*.eledsec[1-9][0-9]R -*.eledsec[1-9][0-9][0-9] -*.eledsec[1-9][0-9][0-9]R - -# glossaries -*.acn -*.acr -*.glg -*.glo -*.gls -*.glsdefs - -# uncomment this for glossaries-extra (will ignore makeindex's style files!) -# *.ist - -# gnuplottex -*-gnuplottex-* - -# gregoriotex -*.gaux -*.gtex - -# htlatex -*.4ct -*.4tc -*.idv -*.lg -*.trc -*.xref - -# hyperref -*.brf - -# knitr -*-concordance.tex -# TODO Comment the next line if you want to keep your tikz graphics files -*.tikz -*-tikzDictionary - -# listings -*.lol - -# luatexja-ruby -*.ltjruby - -# makeidx -*.idx -*.ilg -*.ind - -# minitoc -*.maf -*.mlf -*.mlt -*.mtc[0-9]* -*.slf[0-9]* -*.slt[0-9]* -*.stc[0-9]* - -# minted -_minted* -*.pyg - -# morewrites -*.mw - -# nomencl -*.nlg -*.nlo -*.nls - -# pax -*.pax - -# pdfpcnotes -*.pdfpc - -# sagetex -*.sagetex.sage -*.sagetex.py -*.sagetex.scmd - -# scrwfile -*.wrt - -# sympy -*.sout -*.sympy -sympy-plots-for-*.tex/ - -# pdfcomment -*.upa -*.upb - -# pythontex -*.pytxcode -pythontex-files-*/ - -# tcolorbox -*.listing - -# thmtools -*.loe - -# TikZ & PGF -*.dpth -*.md5 -*.auxlock - -# todonotes -*.tdo - -# vhistory -*.hst -*.ver - -# easy-todo -*.lod - -# xcolor -*.xcp - -# xmpincl -*.xmpi - -# xindy -*.xdy - -# xypic precompiled matrices -*.xyc - -# endfloat -*.ttt -*.fff - -# Latexian -TSWLatexianTemp* - -## Editors: -# WinEdt -*.bak -*.sav - -# Texpad -.texpadtmp - -# LyX -*.lyx~ - -# Kile -*.backup - -# KBibTeX -*~[0-9]* - -# auto folder when using emacs and auctex -./auto/* -*.el - -# expex forward references with \gathertags -*-tags.tex - -# standalone packages -*.sta - -### LaTeX Patch ### -# glossaries -*.glstex - -### Linux ### -*~ - -# temporary files which can be created if a process still has a handle open of a deleted file -.fuse_hidden* - -# KDE directory preferences -.directory - -# Linux trash folder which might appear on any partition or disk -.Trash-* - -# .nfs files are created when an open file is removed but is still being accessed -.nfs* - -### Python ### -# Byte-compiled / optimized / DLL files -__pycache__/ -*.py[cod] -*$py.class - -# C extensions -*.so - -# Distribution / packaging -.Python -build/ -develop-eggs/ -dist/ -downloads/ -eggs/ -.eggs/ -lib/ -lib64/ -parts/ -sdist/ -var/ -wheels/ -pip-wheel-metadata/ -share/python-wheels/ -*.egg-info/ -.installed.cfg -*.egg -MANIFEST - -# lib named project folders -!src/cool2/lib -!src/cool2/cool/lib - -# PyInstaller -# Usually these files are written by a python script from a template -# before PyInstaller builds the exe, so as to inject date/other infos into it. -*.manifest -*.spec - -# Installer logs -pip-log.txt -pip-delete-this-directory.txt - -# Unit test / coverage reports -htmlcov/ -.tox/ -.nox/ -.coverage -.coverage.* -.cache -nosetests.xml -coverage.xml -*.cover -.hypothesis/ -.pytest_cache/ - -# Translations -*.mo -*.pot - -# Scrapy stuff: -.scrapy - -# Sphinx documentation -docs/_build/ - -# PyBuilder -target/ - -# pyenv -.python-version - -# pipenv -# According to pypa/pipenv#598, it is recommended to include Pipfile.lock in version control. -# However, in case of collaboration, if having platform-specific dependencies or dependencies -# having no cross-platform support, pipenv may install dependencies that don't work, or not -# install all needed dependencies. -#Pipfile.lock - -# celery beat schedule file -celerybeat-schedule - -# SageMath parsed files -*.sage.py - -# Spyder project settings -.spyderproject -.spyproject - -# Rope project settings -.ropeproject - -# Mr Developer -.mr.developer.cfg -.project -.pydevproject - -# mkdocs documentation -/site - -# mypy -.mypy_cache/ -.dmypy.json -dmypy.json - -# Pyre type checker -.pyre/ - -### VisualStudioCode ### -.vscode/* -!.vscode/settings.json -!.vscode/tasks.json -!.vscode/launch.json -!.vscode/extensions.json - -### VisualStudioCode Patch ### -# Ignore all local history of files -.history - -# End of https://www.gitignore.io/api/visualstudiocode,linux,latex,python - -# Custom rules (everything added below won't be overriden by 'Generate .gitignore File' if you use 'Update' option) - +# File created using '.gitignore Generator' for Visual Studio Code: https://bit.ly/vscode-gig + +# Created by https://www.gitignore.io/api/visualstudiocode,linux,latex,python +# Edit at https://www.gitignore.io/?templates=visualstudiocode,linux,latex,python + +### LaTeX ### +## Core latex/pdflatex auxiliary files: +*.aux +*.lof +*.log +*.lot +*.fls +*.out +*.toc +*.fmt +*.fot +*.cb +*.cb2 +.*.lb + +## Intermediate documents: +*.dvi +*.xdv +*-converted-to.* +# these rules might exclude image files for figures etc. +# *.ps +# *.eps +# *.pdf + +## Generated if empty string is given at "Please type another file name for output:" +.pdf + +## User defined files: +*_log.txt + +## Bibliography auxiliary files (bibtex/biblatex/biber): +*.bbl +*.bcf +*.blg +*-blx.aux +*-blx.bib +*.run.xml + +## Build tool auxiliary files: +*.fdb_latexmk +*.synctex +*.synctex(busy) +*.synctex.gz +*.synctex.gz(busy) +*.pdfsync + +## Build tool directories for auxiliary files +# latexrun +latex.out/ + +## Auxiliary and intermediate files from other packages: +# algorithms +*.alg +*.loa + +# achemso +acs-*.bib + +# amsthm +*.thm + +# beamer +*.nav +*.pre +*.snm +*.vrb + +# changes +*.soc + +# comment +*.cut + +# cprotect +*.cpt + +# elsarticle (documentclass of Elsevier journals) +*.spl + +# endnotes +*.ent + +# fixme +*.lox + +# feynmf/feynmp +*.mf +*.mp +*.t[1-9] +*.t[1-9][0-9] +*.tfm + +#(r)(e)ledmac/(r)(e)ledpar +*.end +*.?end +*.[1-9] +*.[1-9][0-9] +*.[1-9][0-9][0-9] +*.[1-9]R +*.[1-9][0-9]R +*.[1-9][0-9][0-9]R +*.eledsec[1-9] +*.eledsec[1-9]R +*.eledsec[1-9][0-9] +*.eledsec[1-9][0-9]R +*.eledsec[1-9][0-9][0-9] +*.eledsec[1-9][0-9][0-9]R + +# glossaries +*.acn +*.acr +*.glg +*.glo +*.gls +*.glsdefs + +# uncomment this for glossaries-extra (will ignore makeindex's style files!) +# *.ist + +# gnuplottex +*-gnuplottex-* + +# gregoriotex +*.gaux +*.gtex + +# htlatex +*.4ct +*.4tc +*.idv +*.lg +*.trc +*.xref + +# hyperref +*.brf + +# knitr +*-concordance.tex +# TODO Comment the next line if you want to keep your tikz graphics files +*.tikz +*-tikzDictionary + +# listings +*.lol + +# luatexja-ruby +*.ltjruby + +# makeidx +*.idx +*.ilg +*.ind + +# minitoc +*.maf +*.mlf +*.mlt +*.mtc[0-9]* +*.slf[0-9]* +*.slt[0-9]* +*.stc[0-9]* + +# minted +_minted* +*.pyg + +# morewrites +*.mw + +# nomencl +*.nlg +*.nlo +*.nls + +# pax +*.pax + +# pdfpcnotes +*.pdfpc + +# sagetex +*.sagetex.sage +*.sagetex.py +*.sagetex.scmd + +# scrwfile +*.wrt + +# sympy +*.sout +*.sympy +sympy-plots-for-*.tex/ + +# pdfcomment +*.upa +*.upb + +# pythontex +*.pytxcode +pythontex-files-*/ + +# tcolorbox +*.listing + +# thmtools +*.loe + +# TikZ & PGF +*.dpth +*.md5 +*.auxlock + +# todonotes +*.tdo + +# vhistory +*.hst +*.ver + +# easy-todo +*.lod + +# xcolor +*.xcp + +# xmpincl +*.xmpi + +# xindy +*.xdy + +# xypic precompiled matrices +*.xyc + +# endfloat +*.ttt +*.fff + +# Latexian +TSWLatexianTemp* + +## Editors: +# WinEdt +*.bak +*.sav + +# Texpad +.texpadtmp + +# LyX +*.lyx~ + +# Kile +*.backup + +# KBibTeX +*~[0-9]* + +# auto folder when using emacs and auctex +./auto/* +*.el + +# expex forward references with \gathertags +*-tags.tex + +# standalone packages +*.sta + +### LaTeX Patch ### +# glossaries +*.glstex + +### Linux ### +*~ + +# temporary files which can be created if a process still has a handle open of a deleted file +.fuse_hidden* + +# KDE directory preferences +.directory + +# Linux trash folder which might appear on any partition or disk +.Trash-* + +# .nfs files are created when an open file is removed but is still being accessed +.nfs* + +### Python ### +# Byte-compiled / optimized / DLL files +__pycache__/ +*.py[cod] +*$py.class + +# C extensions +*.so + +# Distribution / packaging +.Python +build/ +develop-eggs/ +dist/ +downloads/ +eggs/ +.eggs/ +lib/ +lib64/ +parts/ +sdist/ +var/ +wheels/ +pip-wheel-metadata/ +share/python-wheels/ +*.egg-info/ +.installed.cfg +*.egg +MANIFEST + +# lib named project folders +!src/cool2/lib +!src/cool2/cool/lib + +# PyInstaller +# Usually these files are written by a python script from a template +# before PyInstaller builds the exe, so as to inject date/other infos into it. +*.manifest +*.spec + +# Installer logs +pip-log.txt +pip-delete-this-directory.txt + +# Unit test / coverage reports +htmlcov/ +.tox/ +.nox/ +.coverage +.coverage.* +.cache +nosetests.xml +coverage.xml +*.cover +.hypothesis/ +.pytest_cache/ + +# Translations +*.mo +*.pot + +# Scrapy stuff: +.scrapy + +# Sphinx documentation +docs/_build/ + +# PyBuilder +target/ + +# pyenv +.python-version + +# pipenv +# According to pypa/pipenv#598, it is recommended to include Pipfile.lock in version control. +# However, in case of collaboration, if having platform-specific dependencies or dependencies +# having no cross-platform support, pipenv may install dependencies that don't work, or not +# install all needed dependencies. +#Pipfile.lock + +# celery beat schedule file +celerybeat-schedule + +# SageMath parsed files +*.sage.py + +# Spyder project settings +.spyderproject +.spyproject + +# Rope project settings +.ropeproject + +# Mr Developer +.mr.developer.cfg +.project +.pydevproject + +# mkdocs documentation +/site + +# mypy +.mypy_cache/ +.dmypy.json +dmypy.json + +# Pyre type checker +.pyre/ + +### VisualStudioCode ### +.vscode/* +!.vscode/settings.json +!.vscode/tasks.json +!.vscode/launch.json +!.vscode/extensions.json + +### VisualStudioCode Patch ### +# Ignore all local history of files +.history + +# End of https://www.gitignore.io/api/visualstudiocode,linux,latex,python + +# Custom rules (everything added below won't be overriden by 'Generate .gitignore File' if you use 'Update' option) + diff --git a/.vscode/launch.json b/.vscode/launch.json index a5d54f0fd..e8f224d78 100644 --- a/.vscode/launch.json +++ b/.vscode/launch.json @@ -1,16 +1,16 @@ -{ - // Use IntelliSense to learn about possible attributes. - // Hover to view descriptions of existing attributes. - // For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387 - "version": "0.2.0", - "configurations": [ - { - "name": "Python: Archivo actual", - "type": "python", - "request": "launch", - "program": "${file}", - "args": ["src/testing.cl", "src/testing.mips"], - "console": "integratedTerminal" - } - ] +{ + // Use IntelliSense to learn about possible attributes. + // Hover to view descriptions of existing attributes. + // For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387 + "version": "0.2.0", + "configurations": [ + { + "name": "Python: Archivo actual", + "type": "python", + "request": "launch", + "program": "${file}", + "args": ["src/testing.cl", "src/testing.mips"], + "console": "integratedTerminal" + } + ] } \ No newline at end of file diff --git a/.vscode/settings.json b/.vscode/settings.json index 615aafb03..5c425b56f 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -1,3 +1,3 @@ -{ - "python.pythonPath": "/usr/bin/python3" +{ + "python.pythonPath": "/usr/bin/python3" } \ No newline at end of file diff --git a/src/a.cl b/src/a.cl index cf8b86a51..c87561ca0 100644 --- a/src/a.cl +++ b/src/a.cl @@ -1,34 +1,34 @@ -class Main inherits IO { - number: Int <- 5; - - main () : Object { - testing_fibonacci(number) - }; - - testing_fibonacci(n: Int) : IO {{ - out_string("Iterative Fibonacci : "); - out_int(iterative_fibonacci(5)); - out_string("\n"); - - out_string("Recursive Fibonacci : "); - out_int(recursive_fibonacci(5)); - out_string("\n"); - }}; - - recursive_fibonacci (n: AUTO_TYPE) : AUTO_TYPE { - if n <= 2 then 1 else recursive_fibonacci(n - 1) + recursive_fibonacci(n - 2) fi - }; - - iterative_fibonacci(n: AUTO_TYPE) : AUTO_TYPE { - let i: Int <- 2, n1: Int <- 1, n2: Int <- 1, temp: Int in { - while i < n loop - let temp: Int <- n2 in { - n2 <- n2 + n1; - n1 <- temp; - i <- i + 1; - } - pool; - n2; - } - }; +class Main inherits IO { + number: Int <- 5; + + main () : Object { + testing_fibonacci(number) + }; + + testing_fibonacci(n: Int) : IO {{ + out_string("Iterative Fibonacci : "); + out_int(iterative_fibonacci(5)); + out_string("\n"); + + out_string("Recursive Fibonacci : "); + out_int(recursive_fibonacci(5)); + out_string("\n"); + }}; + + recursive_fibonacci (n: AUTO_TYPE) : AUTO_TYPE { + if n <= 2 then 1 else recursive_fibonacci(n - 1) + recursive_fibonacci(n - 2) fi + }; + + iterative_fibonacci(n: AUTO_TYPE) : AUTO_TYPE { + let i: Int <- 2, n1: Int <- 1, n2: Int <- 1, temp: Int in { + while i < n loop + let temp: Int <- n2 in { + n2 <- n2 + n1; + n1 <- temp; + i <- i + 1; + } + pool; + n2; + } + }; } \ No newline at end of file diff --git a/src/cool2/__init__.py b/src/cool2/__init__.py index 19aa17c0f..912fcd4be 100644 --- a/src/cool2/__init__.py +++ b/src/cool2/__init__.py @@ -1,5 +1,5 @@ -import sys -import os -base_dir = os.path.dirname(__file__) -# sys.path.append(os.path.join(base_dir, "..")) +import sys +import os +base_dir = os.path.dirname(__file__) +# sys.path.append(os.path.join(base_dir, "..")) sys.path.append(base_dir) \ No newline at end of file diff --git a/src/cool2/cmp/ast.py b/src/cool2/cmp/ast.py index 46811e0b9..792908349 100644 --- a/src/cool2/cmp/ast.py +++ b/src/cool2/cmp/ast.py @@ -1,78 +1,78 @@ -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() - -class TernaryNode(Node): - def __init__(self, left, middle, right): - self.left = left - self.right = right - self.middle = middle - - def evaluate(self): - lvalue = self.left.evaluate() - mvalue = self.middle.evaluate() - rvalue = self.right.evaluate() - return self.operate(lvalue, mvalue, rvalue) - - @staticmethod - def operate(lvalue, mvalue, 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() +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() + +class TernaryNode(Node): + def __init__(self, left, middle, right): + self.left = left + self.right = right + self.middle = middle + + def evaluate(self): + lvalue = self.left.evaluate() + mvalue = self.middle.evaluate() + rvalue = self.right.evaluate() + return self.operate(lvalue, mvalue, rvalue) + + @staticmethod + def operate(lvalue, mvalue, 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/cool2/cmp/automata.py b/src/cool2/cmp/automata.py index 502d6d67b..07522ae8a 100644 --- a/src/cool2/cmp/automata.py +++ b/src/cool2/cmp/automata.py @@ -1,210 +1,210 @@ -try: - import pydot -except: - pass - -class State: - def __init__(self, state, final=False, formatter=None, 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=None): - 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): - try: - return f'{self.idx}\n' + self.formatter(self.state) - except AttributeError: - 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,rankdir_conf='LR'): - G = pydot.Dot(rankdir=rankdir_conf, 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) for item in state) - except TypeError: +try: + import pydot +except: + pass + +class State: + def __init__(self, state, final=False, formatter=None, 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=None): + 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): + try: + return f'{self.idx}\n' + self.formatter(self.state) + except AttributeError: + 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,rankdir_conf='LR'): + G = pydot.Dot(rankdir=rankdir_conf, 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) for item in state) + except TypeError: return str(state) \ No newline at end of file diff --git a/src/cool2/cmp/cil.py b/src/cool2/cmp/cil.py index de6782c16..a281a45f0 100644 --- a/src/cool2/cmp/cil.py +++ b/src/cool2/cmp/cil.py @@ -1,231 +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() +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/cool2/cmp/evaluation.py b/src/cool2/cmp/evaluation.py index 55ff38e9d..d49eaae8c 100644 --- a/src/cool2/cmp/evaluation.py +++ b/src/cool2/cmp/evaluation.py @@ -1,33 +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) +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/cool2/cmp/languages.py b/src/cool2/cmp/languages.py index e2c0c33da..a1dfc78e6 100644 --- a/src/cool2/cmp/languages.py +++ b/src/cool2/cmp/languages.py @@ -1,228 +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) +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/cool2/cmp/nbpackage.py b/src/cool2/cmp/nbpackage.py index e89c62ad3..5458e63b9 100644 --- a/src/cool2/cmp/nbpackage.py +++ b/src/cool2/cmp/nbpackage.py @@ -1,87 +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] - +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/cool2/cmp/pycompiler.py b/src/cool2/cmp/pycompiler.py index 48fdbbeb9..fa0a71df9 100644 --- a/src/cool2/cmp/pycompiler.py +++ b/src/cool2/cmp/pycompiler.py @@ -1,532 +1,532 @@ -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 - - def __hash__(self): - return hash(self.Name) - - def __eq__(self,value): - return isinstance(value,Symbol) and value.Name == self.Name - -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 SentenceFromIter(Sentence): - def __init__(self,args): - self._symbols = tuple(x for x in args if not x.IsEpsilon) - self.hash = hash(self._symbols) - -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' - - if self.nonTerminals: - nonterminals = mul * (len(self.nonTerminals)-1) + '%s\n' - - ans += nonterminals % tuple(self.nonTerminals) - else: - ans += '\n' - - ans += 'Terminals:\n\t' - - if self.terminals: - terminals = mul * (len(self.terminals)-1) + '%s\n' - - ans += terminals % tuple(self.terminals) - else: - ans += '\n' - - 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)) - - def __getitem__(self,pos): - return self.production.Right[pos] - - @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): +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 + + def __hash__(self): + return hash(self.Name) + + def __eq__(self,value): + return isinstance(value,Symbol) and value.Name == self.Name + +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 SentenceFromIter(Sentence): + def __init__(self,args): + self._symbols = tuple(x for x in args if not x.IsEpsilon) + self.hash = hash(self._symbols) + +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' + + if self.nonTerminals: + nonterminals = mul * (len(self.nonTerminals)-1) + '%s\n' + + ans += nonterminals % tuple(self.nonTerminals) + else: + ans += '\n' + + ans += 'Terminals:\n\t' + + if self.terminals: + terminals = mul * (len(self.terminals)-1) + '%s\n' + + ans += terminals % tuple(self.terminals) + else: + ans += '\n' + + 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)) + + def __getitem__(self,pos): + return self.production.Right[pos] + + @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/cool2/cmp/semantic.py b/src/cool2/cmp/semantic.py index 780b4ca83..10341b3bc 100644 --- a/src/cool2/cmp/semantic.py +++ b/src/cool2/cmp/semantic.py @@ -1,220 +1,220 @@ -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_methods(self, clean=True): - plain = OrderedDict() if self.parent is None else self.parent.all_methods(False) - for method in self.methods: - plain[method.name] = (method, self) - return plain.values() if clean else plain - - def conforms_to(self, other): - return other.bypass() or self == other or self.parent is not None and self.parent.conforms_to(other) - - def bypass(self): - return False - - def __str__(self): - output = f'type {self.name}' - parent = '' if self.parent is None else f' : {self.parent.name}' - output += parent - output += ' {' - output += '\n\t' if self.attributes or self.methods else '' - output += '\n\t'.join(str(x) for x in self.attributes) - output += '\n\t' if self.attributes else '' - output += '\n\t'.join(str(x) for x in self.methods) - output += '\n' if self.methods else '' - output += '}\n' - return output - - def __repr__(self): - return str(self) - -class ErrorType(Type): - def __init__(self): - Type.__init__(self, '') - - def conforms_to(self, other): - return True - - def bypass(self): - return True - - def __eq__(self, other): - return isinstance(other, Type) - -class VoidType(Type): - def __init__(self): - Type.__init__(self, '') - - def conforms_to(self, other): - raise Exception('Invalid type: void type.') - - def bypass(self): - return True - - def __eq__(self, other): - return isinstance(other, VoidType) - -class 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 - - def __str__(self): - return str(self.name) + " -> " + str(self.type) - - def __repr__(self): - return str(self) - -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 = type(self)(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 not 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) +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_methods(self, clean=True): + plain = OrderedDict() if self.parent is None else self.parent.all_methods(False) + for method in self.methods: + plain[method.name] = (method, self) + return plain.values() if clean else plain + + def conforms_to(self, other): + return other.bypass() or self == other or self.parent is not None and self.parent.conforms_to(other) + + def bypass(self): + return False + + def __str__(self): + output = f'type {self.name}' + parent = '' if self.parent is None else f' : {self.parent.name}' + output += parent + output += ' {' + output += '\n\t' if self.attributes or self.methods else '' + output += '\n\t'.join(str(x) for x in self.attributes) + output += '\n\t' if self.attributes else '' + output += '\n\t'.join(str(x) for x in self.methods) + output += '\n' if self.methods else '' + output += '}\n' + return output + + def __repr__(self): + return str(self) + +class ErrorType(Type): + def __init__(self): + Type.__init__(self, '') + + def conforms_to(self, other): + return True + + def bypass(self): + return True + + def __eq__(self, other): + return isinstance(other, Type) + +class VoidType(Type): + def __init__(self): + Type.__init__(self, '') + + def conforms_to(self, other): + raise Exception('Invalid type: void type.') + + def bypass(self): + return True + + def __eq__(self, other): + return isinstance(other, VoidType) + +class 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 + + def __str__(self): + return str(self.name) + " -> " + str(self.type) + + def __repr__(self): + return str(self) + +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 = type(self)(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 not 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/cool2/cmp/tools/automata.py b/src/cool2/cmp/tools/automata.py index 59643cc5b..2ad7b848a 100644 --- a/src/cool2/cmp/tools/automata.py +++ b/src/cool2/cmp/tools/automata.py @@ -1,3 +1,3 @@ -import zlib, base64 -exec(zlib.decompress(base64.b64decode('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'))) -# Created by pyminifier (https://github.com/liftoff/pyminifier) +import zlib, base64 +exec(zlib.decompress(base64.b64decode('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'))) +# Created by pyminifier (https://github.com/liftoff/pyminifier) diff --git a/src/cool2/cmp/tools/evaluation.py b/src/cool2/cmp/tools/evaluation.py index d49df0ce5..591a58f13 100644 --- a/src/cool2/cmp/tools/evaluation.py +++ b/src/cool2/cmp/tools/evaluation.py @@ -1,3 +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) +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/cool2/cmp/tools/parsing.py b/src/cool2/cmp/tools/parsing.py index d0275cbcb..34d472eb7 100644 --- a/src/cool2/cmp/tools/parsing.py +++ b/src/cool2/cmp/tools/parsing.py @@ -1,16 +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) +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/cool2/cmp/tools/regex.py b/src/cool2/cmp/tools/regex.py index 85d74325a..d272ad86c 100644 --- a/src/cool2/cmp/tools/regex.py +++ b/src/cool2/cmp/tools/regex.py @@ -1,3 +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) +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/cool2/cmp/utils.py b/src/cool2/cmp/utils.py index b4080149f..62fed781a 100644 --- a/src/cool2/cmp/utils.py +++ b/src/cool2/cmp/utils.py @@ -1,219 +1,219 @@ -from .pycompiler import Production, Sentence, Symbol, EOF, Epsilon - -class ContainerSet: - def __init__(self, *values, contains_epsilon=False): - self.set = set(values) - self.contains_epsilon = contains_epsilon - - def add(self, value): - n = len(self.set) - self.set.add(value) - return n != len(self.set) - - def extend(self, values): - change = False - for value in values: - change |= self.add(value) - return change - - def set_epsilon(self, value=True): - last = self.contains_epsilon - self.contains_epsilon = value - return last != self.contains_epsilon - - def update(self, other): - n = len(self.set) - self.set.update(other.set) - return n != len(self.set) - - def epsilon_update(self, other): - return self.set_epsilon(self.contains_epsilon | other.contains_epsilon) - - def hard_update(self, other): - return self.update(other) | self.epsilon_update(other) - - def find_match(self, match): - for item in self.set: - if item == match: - return item - return None - - def __len__(self): - return len(self.set) + int(self.contains_epsilon) - - def __str__(self): - return '%s-%s' % (str(self.set), self.contains_epsilon) - - def __repr__(self): - return str(self) - - def __iter__(self): - return iter(self.set) - - def __nonzero__(self): - return len(self) > 0 - - def __eq__(self, other): - if isinstance(other, set): - return self.set == other - return isinstance(other, ContainerSet) and self.set == other.set and self.contains_epsilon == other.contains_epsilon - - -def inspect(item, grammar_name='G', mapper=None): - try: - return mapper[item] - except (TypeError, KeyError ): - if isinstance(item, dict): - items = ',\n '.join(f'{inspect(key, grammar_name, mapper)}: {inspect(value, grammar_name, mapper)}' for key, value in item.items() ) - return f'{{\n {items} \n}}' - elif isinstance(item, ContainerSet): - args = f'{ ", ".join(inspect(x, grammar_name, mapper) for x in item.set) } ,' if item.set else '' - return f'ContainerSet({args} contains_epsilon={item.contains_epsilon})' - elif isinstance(item, EOF): - return f'{grammar_name}.EOF' - elif isinstance(item, Epsilon): - return f'{grammar_name}.Epsilon' - elif isinstance(item, Symbol): - return f"G['{item.Name}']" - elif isinstance(item, Sentence): - items = ', '.join(inspect(s, grammar_name, mapper) for s in item._symbols) - return f'Sentence({items})' - elif isinstance(item, Production): - left = inspect(item.Left, grammar_name, mapper) - right = inspect(item.Right, grammar_name, mapper) - return f'Production({left}, {right})' - elif isinstance(item, tuple) or isinstance(item, list): - ctor = ('(', ')') if isinstance(item, tuple) else ('[',']') - return f'{ctor[0]} {("%s, " * len(item)) % tuple(inspect(x, grammar_name, mapper) for x in item)}{ctor[1]}' - else: - raise ValueError(f'Invalid: {item}') - -def pprint(item, header=""): - if header: - print(header) - - if isinstance(item, dict): - for key, value in item.items(): - print(f'{key} ---> {value}') - elif isinstance(item, list): - print('[') - for x in item: - print(f' {repr(x)}') - print(']') - else: - print(item) - -class Token: - """ - Basic token class. - - Parameters - ---------- - lex : str - Token's lexeme. - token_type : Enum - Token's type. - """ - - def __init__(self, lex, token_type): - 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): +from .pycompiler import Production, Sentence, Symbol, EOF, Epsilon + +class ContainerSet: + def __init__(self, *values, contains_epsilon=False): + self.set = set(values) + self.contains_epsilon = contains_epsilon + + def add(self, value): + n = len(self.set) + self.set.add(value) + return n != len(self.set) + + def extend(self, values): + change = False + for value in values: + change |= self.add(value) + return change + + def set_epsilon(self, value=True): + last = self.contains_epsilon + self.contains_epsilon = value + return last != self.contains_epsilon + + def update(self, other): + n = len(self.set) + self.set.update(other.set) + return n != len(self.set) + + def epsilon_update(self, other): + return self.set_epsilon(self.contains_epsilon | other.contains_epsilon) + + def hard_update(self, other): + return self.update(other) | self.epsilon_update(other) + + def find_match(self, match): + for item in self.set: + if item == match: + return item + return None + + def __len__(self): + return len(self.set) + int(self.contains_epsilon) + + def __str__(self): + return '%s-%s' % (str(self.set), self.contains_epsilon) + + def __repr__(self): + return str(self) + + def __iter__(self): + return iter(self.set) + + def __nonzero__(self): + return len(self) > 0 + + def __eq__(self, other): + if isinstance(other, set): + return self.set == other + return isinstance(other, ContainerSet) and self.set == other.set and self.contains_epsilon == other.contains_epsilon + + +def inspect(item, grammar_name='G', mapper=None): + try: + return mapper[item] + except (TypeError, KeyError ): + if isinstance(item, dict): + items = ',\n '.join(f'{inspect(key, grammar_name, mapper)}: {inspect(value, grammar_name, mapper)}' for key, value in item.items() ) + return f'{{\n {items} \n}}' + elif isinstance(item, ContainerSet): + args = f'{ ", ".join(inspect(x, grammar_name, mapper) for x in item.set) } ,' if item.set else '' + return f'ContainerSet({args} contains_epsilon={item.contains_epsilon})' + elif isinstance(item, EOF): + return f'{grammar_name}.EOF' + elif isinstance(item, Epsilon): + return f'{grammar_name}.Epsilon' + elif isinstance(item, Symbol): + return f"G['{item.Name}']" + elif isinstance(item, Sentence): + items = ', '.join(inspect(s, grammar_name, mapper) for s in item._symbols) + return f'Sentence({items})' + elif isinstance(item, Production): + left = inspect(item.Left, grammar_name, mapper) + right = inspect(item.Right, grammar_name, mapper) + return f'Production({left}, {right})' + elif isinstance(item, tuple) or isinstance(item, list): + ctor = ('(', ')') if isinstance(item, tuple) else ('[',']') + return f'{ctor[0]} {("%s, " * len(item)) % tuple(inspect(x, grammar_name, mapper) for x in item)}{ctor[1]}' + else: + raise ValueError(f'Invalid: {item}') + +def pprint(item, header=""): + if header: + print(header) + + if isinstance(item, dict): + for key, value in item.items(): + print(f'{key} ---> {value}') + elif isinstance(item, list): + print('[') + for x in item: + print(f' {repr(x)}') + print(']') + else: + print(item) + +class Token: + """ + Basic token class. + + Parameters + ---------- + lex : str + Token's lexeme. + token_type : Enum + Token's type. + """ + + def __init__(self, lex, token_type): + 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/cool2/cmp/visitor.py b/src/cool2/cmp/visitor.py index 964842836..500298bcd 100644 --- a/src/cool2/cmp/visitor.py +++ b/src/cool2/cmp/visitor.py @@ -1,80 +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) +# 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/cool2/cool/ast/ast.py b/src/cool2/cool/ast/ast.py index d10dc349c..97e5675bf 100644 --- a/src/cool2/cool/ast/ast.py +++ b/src/cool2/cool/ast/ast.py @@ -1,267 +1,267 @@ - -class Node: - def __init__(self,row=None,column=None): - self.row = row - self.column = column - -class ProgramNode(Node): - def __init__(self, declarations,row=None,column=None): - super().__init__(row,column) - self.declarations = declarations - - def __iter__(self): - for x in self.declarations: - yield from x - -class DeclarationNode(Node): - pass - -class ExpressionNode(Node): - pass - -class ClassDeclarationNode(DeclarationNode): - def __init__(self, idx, features, parent=None,row=None,column=None): - super().__init__(row,column) - self.id = idx - self.parent = parent if parent else 'Object' - self.features = features - - def __iter__(self): - yield self - for x in self.features: - yield from x - -class FuncDeclarationNode(DeclarationNode): - def __init__(self, idx, params, return_type, body,row=None,column=None): - super().__init__(row,column) - self.id = idx - self.params = params - self.type = return_type - self.body = body - - def __iter__(self): - yield self - yield from self.params - yield from self.body - -class AttrDeclarationNode(DeclarationNode): - def __init__(self, idx, typex, expr=None,row=None,column=None): - super().__init__(row,column) - self.id = idx - self.type = typex - self.expr = expr - - def __iter__(self): - yield self - if self.expr: - yield from self.expr - -class ParamNode(DeclarationNode): - def __init__(self, idx, typex, row=None,column=None): - super().__init__(row,column) - self.id = idx - self.type = typex - - def __iter__(self): - yield self - -class SpecialNode(ExpressionNode): - def __init__(self, func,row=None,column=None): - super().__init__(row,column) - self.func = func - - def __iter__(self): - yield self - -class VarDeclarationNode(ExpressionNode): - def __init__(self, idx, typex, expr,row=None,column=None): - super().__init__(row,column) - self.id = idx - self.type = typex - self.expr = expr - - def __iter__(self): - yield self - if self.expr: - yield from self.expr - -class AssignNode(ExpressionNode): - def __init__(self, idx, expr,row=None,column=None): - super().__init__(row,column) - self.id = idx - self.expr = expr - - def __iter__(self): - yield self - if self.expr: - yield from self.expr - -class CallNode(ExpressionNode): - def __init__(self, obj, idx, args,at_type,row=None,column=None): - super().__init__(row,column) - self.obj = obj - self.id = idx - self.args = args - self.at = at_type - - def __iter__(self): - yield from self.obj - for x in self.args: - yield from x - yield self - -class BlockNode(ExpressionNode): - def __init__(self, expr_list,row=None,column=None): - super().__init__(row,column) - self.expr_list = expr_list - - def __iter__(self): - yield self - for x in self.expr_list: - yield from x - -class ConditionalNode(ExpressionNode): - def __init__(self, condition,then_expr,else_expr,row=None,column=None): - super().__init__(row,column) - self.condition = condition - self.then_expr = then_expr - self.else_expr = else_expr - - def get_return_type(self,current_type): - else_type = self.else_expr.type - then_type = self.then_expr.type - return else_type.join(then_type,current_type) - - def __iter__(self): - yield self - for x in [self.condition,self.then_expr,self.else_expr]: - yield from x - -class CheckNode(ExpressionNode): - def __init__(self, idx, typex, expr,row=None,column=None): - super().__init__(row,column) - self.id = idx - self.type = typex - self.expr = expr - - def __iter__(self): - yield self - yield from self.expr - -class LetNode(ExpressionNode): - def __init__(self, dec_var_list, expr,row=None,column=None): - super().__init__(row,column) - self.params = dec_var_list - self.expr = expr - - def __iter__(self): - yield self - for x in self.params: - yield from x - - -class CaseNode(ExpressionNode): - def __init__(self, expr, check_list,row=None,column=None): - super().__init__(row,column) - self.expr = expr - self.params = check_list - - def __iter__(self): - yield self - yield from expr - for x in self.params: - yield from x - -class WhileNode(ExpressionNode): - def __init__(self, condition, expr,row=None,column=None): - super().__init__(row,column) - self.condition = condition - self.expr = expr - - def __iter__(self): - yield self - yield from expr - for x in self.params: - yield from x - -class AtomicNode(ExpressionNode): - def __init__(self, lex,row=None,column=None): - super().__init__(row,column) - self.lex = lex - - def __iter__(self): - yield self - -class UnaryNode(ExpressionNode): - def __init__(self, member,row=None,column=None): - super().__init__(row,column) - self.member = member - - def __iter__(self): - yield self - yield self.member - -class BinaryNode(ExpressionNode): - def __init__(self, left, right,row=None,column=None): - super().__init__(row,column) - self.left = left - self.right = right - - def __iter__(self): - yield self - yield self.left - yield self.right - -class StringNode(AtomicNode): - pass - -class BoolNode(AtomicNode): - pass - -class ConstantNumNode(AtomicNode): - pass - -class VoidNode(AtomicNode): - pass - -class VariableNode(AtomicNode): - pass - -class InstantiateNode(AtomicNode): - pass - -class NotNode(UnaryNode): - pass - -class RoofNode(UnaryNode): - pass - -class IsVoidNode(UnaryNode): - pass - -class PlusNode(BinaryNode): - pass - -class MinusNode(BinaryNode): - pass - -class StarNode(BinaryNode): - pass - -class DivNode(BinaryNode): - pass - -class EqualNode(BinaryNode): - pass - -class GreaterNode(BinaryNode): - pass - -class GreaterEqualNode(BinaryNode): - pass - -class LesserNode(BinaryNode): - pass - -class LesserEqualNode(BinaryNode): + +class Node: + def __init__(self,row=None,column=None): + self.row = row + self.column = column + +class ProgramNode(Node): + def __init__(self, declarations,row=None,column=None): + super().__init__(row,column) + self.declarations = declarations + + def __iter__(self): + for x in self.declarations: + yield from x + +class DeclarationNode(Node): + pass + +class ExpressionNode(Node): + pass + +class ClassDeclarationNode(DeclarationNode): + def __init__(self, idx, features, parent=None,row=None,column=None): + super().__init__(row,column) + self.id = idx + self.parent = parent if parent else 'Object' + self.features = features + + def __iter__(self): + yield self + for x in self.features: + yield from x + +class FuncDeclarationNode(DeclarationNode): + def __init__(self, idx, params, return_type, body,row=None,column=None): + super().__init__(row,column) + self.id = idx + self.params = params + self.type = return_type + self.body = body + + def __iter__(self): + yield self + yield from self.params + yield from self.body + +class AttrDeclarationNode(DeclarationNode): + def __init__(self, idx, typex, expr=None,row=None,column=None): + super().__init__(row,column) + self.id = idx + self.type = typex + self.expr = expr + + def __iter__(self): + yield self + if self.expr: + yield from self.expr + +class ParamNode(DeclarationNode): + def __init__(self, idx, typex, row=None,column=None): + super().__init__(row,column) + self.id = idx + self.type = typex + + def __iter__(self): + yield self + +class SpecialNode(ExpressionNode): + def __init__(self, func,row=None,column=None): + super().__init__(row,column) + self.func = func + + def __iter__(self): + yield self + +class VarDeclarationNode(ExpressionNode): + def __init__(self, idx, typex, expr,row=None,column=None): + super().__init__(row,column) + self.id = idx + self.type = typex + self.expr = expr + + def __iter__(self): + yield self + if self.expr: + yield from self.expr + +class AssignNode(ExpressionNode): + def __init__(self, idx, expr,row=None,column=None): + super().__init__(row,column) + self.id = idx + self.expr = expr + + def __iter__(self): + yield self + if self.expr: + yield from self.expr + +class CallNode(ExpressionNode): + def __init__(self, obj, idx, args,at_type,row=None,column=None): + super().__init__(row,column) + self.obj = obj + self.id = idx + self.args = args + self.at = at_type + + def __iter__(self): + yield from self.obj + for x in self.args: + yield from x + yield self + +class BlockNode(ExpressionNode): + def __init__(self, expr_list,row=None,column=None): + super().__init__(row,column) + self.expr_list = expr_list + + def __iter__(self): + yield self + for x in self.expr_list: + yield from x + +class ConditionalNode(ExpressionNode): + def __init__(self, condition,then_expr,else_expr,row=None,column=None): + super().__init__(row,column) + self.condition = condition + self.then_expr = then_expr + self.else_expr = else_expr + + def get_return_type(self,current_type): + else_type = self.else_expr.type + then_type = self.then_expr.type + return else_type.join(then_type,current_type) + + def __iter__(self): + yield self + for x in [self.condition,self.then_expr,self.else_expr]: + yield from x + +class CheckNode(ExpressionNode): + def __init__(self, idx, typex, expr,row=None,column=None): + super().__init__(row,column) + self.id = idx + self.type = typex + self.expr = expr + + def __iter__(self): + yield self + yield from self.expr + +class LetNode(ExpressionNode): + def __init__(self, dec_var_list, expr,row=None,column=None): + super().__init__(row,column) + self.params = dec_var_list + self.expr = expr + + def __iter__(self): + yield self + for x in self.params: + yield from x + + +class CaseNode(ExpressionNode): + def __init__(self, expr, check_list,row=None,column=None): + super().__init__(row,column) + self.expr = expr + self.params = check_list + + def __iter__(self): + yield self + yield from expr + for x in self.params: + yield from x + +class WhileNode(ExpressionNode): + def __init__(self, condition, expr,row=None,column=None): + super().__init__(row,column) + self.condition = condition + self.expr = expr + + def __iter__(self): + yield self + yield from expr + for x in self.params: + yield from x + +class AtomicNode(ExpressionNode): + def __init__(self, lex,row=None,column=None): + super().__init__(row,column) + self.lex = lex + + def __iter__(self): + yield self + +class UnaryNode(ExpressionNode): + def __init__(self, member,row=None,column=None): + super().__init__(row,column) + self.member = member + + def __iter__(self): + yield self + yield self.member + +class BinaryNode(ExpressionNode): + def __init__(self, left, right,row=None,column=None): + super().__init__(row,column) + self.left = left + self.right = right + + def __iter__(self): + yield self + yield self.left + yield self.right + +class StringNode(AtomicNode): + pass + +class BoolNode(AtomicNode): + pass + +class ConstantNumNode(AtomicNode): + pass + +class VoidNode(AtomicNode): + pass + +class VariableNode(AtomicNode): + pass + +class InstantiateNode(AtomicNode): + pass + +class NotNode(UnaryNode): + pass + +class RoofNode(UnaryNode): + pass + +class IsVoidNode(UnaryNode): + pass + +class PlusNode(BinaryNode): + pass + +class MinusNode(BinaryNode): + pass + +class StarNode(BinaryNode): + pass + +class DivNode(BinaryNode): + pass + +class EqualNode(BinaryNode): + pass + +class GreaterNode(BinaryNode): + pass + +class GreaterEqualNode(BinaryNode): + pass + +class LesserNode(BinaryNode): + pass + +class LesserEqualNode(BinaryNode): pass \ No newline at end of file diff --git a/src/cool2/cool/error/errors.py b/src/cool2/cool/error/errors.py index 99ccd204b..fbad6e62b 100644 --- a/src/cool2/cool/error/errors.py +++ b/src/cool2/cool/error/errors.py @@ -1,122 +1,122 @@ -STRING_TOO_LONG = "String too long. Max length is 1024 chars, have {0} chars" - -SYNTACTIC_ERROR = 'at or near "{0}"' - -WRONG_SIGNATURE = 'Method "{0}" already defined in "{1}" with a different signature.' -SELF_IS_READONLY = 'Variable "self" is read-only.' -ATTRIBUTE_NOT_DEFINED = 'Attribute "{0}" is not defined in class "{1}".' -ATTRIBUTE_ALREADY_DEFINED = 'Attribute "{0}" is already defined in class "{1}".' -LOCAL_ALREADY_DEFINED = 'Variable "{0}" is already defined in method "{1}".' -INCOMPATIBLE_TYPES = 'Cannot convert "{0}" into "{1}".' -VARIABLE_NOT_DEFINED = 'Variable "{0}" is not defined in "{1}".' -INVALID_UNARY_OPERATION = 'Operation {0} is not defined with "{1}".' -INVALID_BINARY_OPERATION = 'Operation {0} is not defined between "{1}" and "{2}".' -NOT_BOOLEAN_CONDITION = 'A conditional must be a Bool type instead of {0} type.' -CIRCULAR_DEPENDENCY = 'Circular Dependency with types "{0}" and "{1}".' -VOID_TYPE_CONFORMS = "Void is not a valid type." -NO_OPERATION_DEFINDED = "No operations defined with operator {0} and types {1}." -MULTIPLE_OPERATION_DEFINED = "Multiple operations defined with operator {0} and types {1}." -CASE_NO_BRANCH_SELECTED = "No branch can be selected. Type {0} is not conformed by any of the branches." -NO_BOOL_CONDITION = "The condition value type is not Bool." -DISPATCH_VOID = "Can't dispatch a Void value." -METHOD_NOT_DEFINED = "Method '{0}' is not defined{1} in '{2}' with {3} params." -METHOD_ALREADY_DEFINED = "Method '{0}' with {1} params already defined in {2}" -NO_ENTRY_POINT = "The program must define a 'main' method with no parameters in the 'Main' class" -NO_MAIN_TYPE = "The program must define a 'Main' type" -CASE_TYPES_REPEATED = "Type {0} already present in case expression." -ZERO_DIVISION = "Divison by 0 not defined." -SUBSTR_OUT_RANGE = "Index out of range in substr, word:'{0}' substr begin:{1} substr length:{2}." -ATTRIBUTE_CANT_INFER = "No attribute '{0}' in inferred types." -METHOD_CANT_INFER = "No method '{0}' with {1} params in inferred types." -TYPE_NOT_DEFINED = "Type '{0}' is not defined." -TYPE_ALREADY_DEFINED = "Type with the same name ({0}) already in context." -TYPE_CANT_INFER = "Cant infer type in given context." -TYPE_CANT_BE_INHERITED = "Type {0} cant be inherited" -NO_COMMON_TYPE = "No common types between {0} and {1}" - -READ_IS_NOT_INT = "Invalid type: {0} is not an Int" -class CoolError(Exception): - """ - Base Cool Error - """ - - @property - def text(self): - return str(self) - - ERROR_TYPE = "CoolError" - - FORMAT = "{}: {}" - - def __init__(self, msg:str, *args): - self.error = msg - self.args = args - - def print_error(self): - print(str(self)) - - def __str__(self): - return CoolError.FORMAT.format(self.ERROR_TYPE, self.error.format(*self.args)) - - def __repr__(self): - return str(self) - -class PositionError(CoolError): - """ - Error class for positional errors - """ - - FORMAT = "({}, {}) - {}: ERROR {}" - - def __init__(self, error_message:str, *args, **kwargs): - """ - kwargs: - token: The token where the error is - row: Row error - column: Column error - lex: Representation of the error - """ - super().__init__(error_message, *args) - self.row = kwargs.get("row") - self.column = kwargs.get("column") - self.lex = kwargs.get("lex") - if "token" in kwargs: - self.row = kwargs["token"].lex[1] - self.column = kwargs["token"].lex[2] - self.lex = kwargs["token"].lex[0] - - def set_position(self, row:int, column:int): - self.row = row - self.column = column - - def __str__(self): - if self.row == None or self.column == None: - return super().__str__() - return self.FORMAT.format(self.row,self.column, self.ERROR_TYPE, self.error.format(*self.args)) - -class LexerCoolError(PositionError): - """ - Error class for lexical errors - """ - - ERROR_TYPE = "LexicographicError" - -class SyntacticCoolError(PositionError): - """ - Error class for syntactic errors - """ - - ERROR_TYPE = "SyntacticError" - -class SemanticError(PositionError): - FORMAT = "({}, {}) - {}: {}" - ERROR_TYPE = "SemanticError" - -class TypeCoolError(SemanticError): - ERROR_TYPE = "TypeError" - -class InferError(SemanticError): - ERROR_TYPE = "InferenceError" - -class RunError(CoolError): - ERROR_TYPE = "RunError" +STRING_TOO_LONG = "String too long. Max length is 1024 chars, have {0} chars" + +SYNTACTIC_ERROR = 'at or near "{0}"' + +WRONG_SIGNATURE = 'Method "{0}" already defined in "{1}" with a different signature.' +SELF_IS_READONLY = 'Variable "self" is read-only.' +ATTRIBUTE_NOT_DEFINED = 'Attribute "{0}" is not defined in class "{1}".' +ATTRIBUTE_ALREADY_DEFINED = 'Attribute "{0}" is already defined in class "{1}".' +LOCAL_ALREADY_DEFINED = 'Variable "{0}" is already defined in method "{1}".' +INCOMPATIBLE_TYPES = 'Cannot convert "{0}" into "{1}".' +VARIABLE_NOT_DEFINED = 'Variable "{0}" is not defined in "{1}".' +INVALID_UNARY_OPERATION = 'Operation {0} is not defined with "{1}".' +INVALID_BINARY_OPERATION = 'Operation {0} is not defined between "{1}" and "{2}".' +NOT_BOOLEAN_CONDITION = 'A conditional must be a Bool type instead of {0} type.' +CIRCULAR_DEPENDENCY = 'Circular Dependency with types "{0}" and "{1}".' +VOID_TYPE_CONFORMS = "Void is not a valid type." +NO_OPERATION_DEFINDED = "No operations defined with operator {0} and types {1}." +MULTIPLE_OPERATION_DEFINED = "Multiple operations defined with operator {0} and types {1}." +CASE_NO_BRANCH_SELECTED = "No branch can be selected. Type {0} is not conformed by any of the branches." +NO_BOOL_CONDITION = "The condition value type is not Bool." +DISPATCH_VOID = "Can't dispatch a Void value." +METHOD_NOT_DEFINED = "Method '{0}' is not defined{1} in '{2}' with {3} params." +METHOD_ALREADY_DEFINED = "Method '{0}' with {1} params already defined in {2}" +NO_ENTRY_POINT = "The program must define a 'main' method with no parameters in the 'Main' class" +NO_MAIN_TYPE = "The program must define a 'Main' type" +CASE_TYPES_REPEATED = "Type {0} already present in case expression." +ZERO_DIVISION = "Divison by 0 not defined." +SUBSTR_OUT_RANGE = "Index out of range in substr, word:'{0}' substr begin:{1} substr length:{2}." +ATTRIBUTE_CANT_INFER = "No attribute '{0}' in inferred types." +METHOD_CANT_INFER = "No method '{0}' with {1} params in inferred types." +TYPE_NOT_DEFINED = "Type '{0}' is not defined." +TYPE_ALREADY_DEFINED = "Type with the same name ({0}) already in context." +TYPE_CANT_INFER = "Cant infer type in given context." +TYPE_CANT_BE_INHERITED = "Type {0} cant be inherited" +NO_COMMON_TYPE = "No common types between {0} and {1}" + +READ_IS_NOT_INT = "Invalid type: {0} is not an Int" +class CoolError(Exception): + """ + Base Cool Error + """ + + @property + def text(self): + return str(self) + + ERROR_TYPE = "CoolError" + + FORMAT = "{}: {}" + + def __init__(self, msg:str, *args): + self.error = msg + self.args = args + + def print_error(self): + print(str(self)) + + def __str__(self): + return CoolError.FORMAT.format(self.ERROR_TYPE, self.error.format(*self.args)) + + def __repr__(self): + return str(self) + +class PositionError(CoolError): + """ + Error class for positional errors + """ + + FORMAT = "({}, {}) - {}: ERROR {}" + + def __init__(self, error_message:str, *args, **kwargs): + """ + kwargs: + token: The token where the error is + row: Row error + column: Column error + lex: Representation of the error + """ + super().__init__(error_message, *args) + self.row = kwargs.get("row") + self.column = kwargs.get("column") + self.lex = kwargs.get("lex") + if "token" in kwargs: + self.row = kwargs["token"].lex[1] + self.column = kwargs["token"].lex[2] + self.lex = kwargs["token"].lex[0] + + def set_position(self, row:int, column:int): + self.row = row + self.column = column + + def __str__(self): + if self.row == None or self.column == None: + return super().__str__() + return self.FORMAT.format(self.row,self.column, self.ERROR_TYPE, self.error.format(*self.args)) + +class LexerCoolError(PositionError): + """ + Error class for lexical errors + """ + + ERROR_TYPE = "LexicographicError" + +class SyntacticCoolError(PositionError): + """ + Error class for syntactic errors + """ + + ERROR_TYPE = "SyntacticError" + +class SemanticError(PositionError): + FORMAT = "({}, {}) - {}: {}" + ERROR_TYPE = "SemanticError" + +class TypeCoolError(SemanticError): + ERROR_TYPE = "TypeError" + +class InferError(SemanticError): + ERROR_TYPE = "InferenceError" + +class RunError(CoolError): + ERROR_TYPE = "RunError" diff --git a/src/cool2/cool/grammar/comment_grammar.py b/src/cool2/cool/grammar/comment_grammar.py index 902185aee..7bf285ca5 100644 --- a/src/cool2/cool/grammar/comment_grammar.py +++ b/src/cool2/cool/grammar/comment_grammar.py @@ -1,18 +1,18 @@ -from cmp.pycompiler import Grammar, NonTerminal, Terminal -from cool.ast.ast import * - -C = Grammar() - -# non-terminals -text = C.NonTerminal('', startSymbol=True) -chunk = C.NonTerminal('') - -# Terminals -delimiter_open,delimiter_close, plain_text = C.Terminals('(* *) text') - -# productions -text %= chunk + delimiter_open + text + delimiter_close + text, lambda h,s: s[1] + s[5] -text %= chunk, lambda h,s: s[1] -chunk %= plain_text + chunk, lambda h,s: s[1][0] + s[2] -chunk %= C.Epsilon, lambda h,s: '' - +from cmp.pycompiler import Grammar, NonTerminal, Terminal +from cool.ast.ast import * + +C = Grammar() + +# non-terminals +text = C.NonTerminal('', startSymbol=True) +chunk = C.NonTerminal('') + +# Terminals +delimiter_open,delimiter_close, plain_text = C.Terminals('(* *) text') + +# productions +text %= chunk + delimiter_open + text + delimiter_close + text, lambda h,s: s[1] + s[5] +text %= chunk, lambda h,s: s[1] +chunk %= plain_text + chunk, lambda h,s: s[1][0] + s[2] +chunk %= C.Epsilon, lambda h,s: '' + diff --git a/src/cool2/cool/grammar/cool_grammar.py b/src/cool2/cool/grammar/cool_grammar.py index c487ab72a..6ae5bcbe2 100644 --- a/src/cool2/cool/grammar/cool_grammar.py +++ b/src/cool2/cool/grammar/cool_grammar.py @@ -1,141 +1,141 @@ -from cmp.pycompiler import Grammar, NonTerminal, Terminal -from cool.ast.ast import * - -G = Grammar() - -# non-terminals -program = G.NonTerminal('', startSymbol=True) -class_list, def_class = G.NonTerminals(' ') -feature_list, def_attr, def_func, feature = G.NonTerminals(' ') -param_list, param, expr_list = G.NonTerminals(' ') -expr, boolean, compare, arith, term, factor, negate, atom = \ - G.NonTerminals(' ') -func_call, arg_list, dispatch = G.NonTerminals(' ') -def_var, def_var_list = G.NonTerminals(' ') -case_check, case_check_list = G.NonTerminals(' ') - -# Terminals - -ifx,then,elsex,if_r,whilex,loop,loop_r = G.Terminals('if then else fi while loop pool') -ocur,ccur,colon,semi,comma, dot = G.Terminals('{ } : ; , .') -opar,cpar,plus,minus,div,star,notx,roof = G.Terminals('( ) + - / * not ~') -less,less_eq,greater,greater_eq,equal = G.Terminals('< <= > >= =') -let,inx,case,of,case_r,arrow,assign = G.Terminals('let in case of esac => <-') -true,false,num,string = G.Terminals('true false num string') -classx, inherits, new, isvoid = G.Terminals('class inherits new isvoid') -idx,typex,at = G.Terminals('id type @') # 'at' is @ - -# productions -program %= class_list, lambda h,s: ProgramNode(s[1],row=0,column=0) - -# ??? -class_list %= def_class, lambda h,s: [s[1]] -class_list %= def_class + class_list, lambda h,s: [s[1]] + s[2] - -# ??? -def_class %= classx + typex + ocur + feature_list + ccur + semi, lambda h,s: ClassDeclarationNode(s[2][0],s[4],row=s[1][1] ,column=s[1][2]) -def_class %= classx + typex + inherits + typex + ocur + feature_list + ccur + semi, lambda h,s: ClassDeclarationNode(s[2][0],s[6],s[4][0],row=s[1][1] ,column=s[1][2]) - -def_class %= classx + typex + ocur + ccur + semi, lambda h,s: ClassDeclarationNode(s[2][0],[],row=s[1][1] ,column=s[1][2]) -def_class %= classx + typex + inherits + typex + ocur + ccur + semi, lambda h,s: ClassDeclarationNode(s[2][0],[],s[4][0],row=s[1][1] ,column=s[1][2]) - -# ??? -feature_list %= feature + semi, lambda h,s: [s[1]] -feature_list %= feature + semi + feature_list, lambda h,s: [s[1]] + s[3] - -# ??? -feature %= def_attr, lambda h,s: s[1] -feature %= def_func, lambda h,s: s[1] - -# ??? -def_attr %= idx + colon + typex, lambda h,s: AttrDeclarationNode(s[1][0],s[3][0],row=s[1][1] ,column=s[1][2]) -def_attr %= idx + colon + typex + assign + expr, lambda h,s: AttrDeclarationNode(s[1][0],s[3][0],s[5],row=s[1][1] ,column=s[1][2]) - -# ??? -def_func %= idx + opar + param_list + cpar + colon + typex + ocur + expr + ccur, lambda h,s: FuncDeclarationNode(s[1][0],s[3],s[6][0],s[8],row=s[1][1] ,column=s[1][2]) - -param_list %= G.Epsilon, lambda h,s: [ ] -param_list %= param, lambda h,s: [ s[1] ] -param_list %= param + comma + param_list, lambda h,s: [ s[1] ] + s[3] - -# ??? -param %= idx + colon + typex, lambda h,s: ParamNode(s[1][0],s[3][0],row=s[1][1],column = s[1][2]) - -def_var %= idx + colon + typex + assign + expr, lambda h,s: VarDeclarationNode(s[1][0],s[3][0],s[5],row=s[1][1] ,column=s[1][2]) -def_var %= idx + colon + typex, lambda h,s: VarDeclarationNode(s[1][0],s[3][0],None,row=s[1][1] ,column=s[1][2]) - -def_var_list %= def_var, lambda h,s: [ s[1] ] -def_var_list %= def_var + comma + def_var_list, lambda h,s: [ s[1] ] + s[3] - -case_check %= idx + colon + typex + arrow + expr + semi, lambda h,s: CheckNode(s[1][0],s[3][0],s[5],row=s[1][1] ,column=s[1][2]) - -case_check_list %= case_check, lambda h,s: [ s[1] ] -case_check_list %= case_check + case_check_list, lambda h,s: [ s[1] ] + s[2] - - -# ??? -expr %= idx + assign + expr, lambda h,s: AssignNode(s[1][0],s[3],row=s[1][1] ,column=s[1][2]) -expr %= idx + colon + typex + assign + expr, lambda h,s: VarDeclarationNode(s[1][0],s[3][0],s[5],row=s[1][1] ,column=s[1][2]) -expr %= boolean, lambda h,s: s[1] -expr %= ocur + expr_list + ccur, lambda h,s: BlockNode(s[2],row=s[1][1] ,column=s[1][2]) -expr %= ifx + expr + then + expr + elsex + expr + if_r, lambda h,s: ConditionalNode(s[2],s[4],s[6],row=s[1][1] ,column=s[1][2]) -expr %= let + def_var_list + inx + expr, lambda h,s: LetNode(s[2],s[4],row=s[1][1] ,column=s[1][2]) -expr %= case + expr + of + case_check_list + case_r, lambda h,s: CaseNode(s[2],s[4],row=s[1][1] ,column=s[1][2]) -expr %= whilex + expr + loop + expr + loop_r, lambda h,s: WhileNode(s[2],s[4],row=s[1][1] ,column=s[1][2]) - -# ??? -expr_list %= expr + semi, lambda h,s: [ s[1] ] -expr_list %= expr + semi + expr_list, lambda h,s: [ s[1] ] + s[3] - -# ??? -boolean %= notx + boolean, lambda h,s: NotNode(s[2],s[1][1],s[1][2]) -boolean %= compare, lambda h,s: s[1] - -# ??? -compare %= arith + equal + arith , lambda h,s: EqualNode(s[1],s[3],s[2][1],s[2][2]) -compare %= arith + less + arith , lambda h,s: LesserNode(s[1],s[3],s[2][1],s[2][2]) -compare %= arith + less_eq + arith , lambda h,s: LesserEqualNode(s[1],s[3],s[2][1],s[2][2]) -compare %= arith + greater + arith , lambda h,s: GreaterNode(s[1],s[3],s[2][1],s[2][2]) -compare %= arith + greater_eq + arith, lambda h,s: GreaterEqualNode(s[1],s[3],s[2][1],s[2][2]) -compare %= arith, lambda h,s: s[1] - -# ??? -arith %= arith + plus + term, lambda h,s: PlusNode(s[1],s[3],row=s[2][1] ,column=s[2][2]) -arith %= arith + minus + term, lambda h,s: MinusNode(s[1],s[3],row=s[2][1] ,column=s[2][2]) -arith %= term, lambda h,s: s[1] - -# ??? -term %= term + star + factor, lambda h,s: StarNode(s[1],s[3],row=s[2][1] ,column=s[2][2]) -term %= term + div + factor, lambda h,s: DivNode(s[1],s[3],row=s[2][1] ,column=s[2][2]) -term %= factor, lambda h,s: s[1] - -# ??? -factor %= isvoid + factor, lambda h,s: IsVoidNode(s[2],row=s[1][1] ,column=s[1][2]) -factor %= negate, lambda h,s: s[1] - -# -negate %= roof + negate, lambda h,s: RoofNode(s[2],row=s[1][1] ,column=s[1][2]) -negate %= dispatch, lambda h,s: s[1] - -# -dispatch %= dispatch + at + typex + dot + func_call, lambda h,s: CallNode(s[1],s[5][1],s[5][2],s[3][0],row=s[5][3] ,column=s[5][4]) -dispatch %= dispatch + dot + func_call, lambda h,s: CallNode(s[1],s[3][1],s[3][2],None,row=s[3][3] ,column=s[3][4]) -dispatch %= atom, lambda h,s: s[1] - -# ??? -atom %= num, lambda h,s: ConstantNumNode(s[1][0],row=s[1][1] ,column=s[1][2]) -atom %= string, lambda h,s: StringNode(s[1][0],row=s[1][1] ,column=s[1][2]) -atom %= true, lambda h,s: BoolNode(s[1][0],row=s[1][1] ,column=s[1][2]) -atom %= false, lambda h,s: BoolNode(s[1][0],row=s[1][1] ,column=s[1][2]) -atom %= idx, lambda h,s: VariableNode(s[1][0],row=s[1][1] ,column=s[1][2]) -atom %= new + typex, lambda h,s: InstantiateNode(s[2][0],row=s[1][1] ,column=s[1][2]) -atom %= func_call, lambda h,s: CallNode(s[1][0],s[1][1],s[1][2],None,row=s[1][3] ,column=s[1][4]) -atom %= at + typex + dot + func_call, lambda h,s: CallNode(s[4][0],s[4][1],s[4][2],s[2][0],row=s[4][3] ,column=s[4][4]) -atom %= opar + expr + cpar, lambda h,s: s[2] - -# ??? -func_call %= idx + opar + arg_list + cpar, lambda h,s: (VariableNode('self',s[1][1],s[1][2]),s[1][0],s[3],s[1][1],s[1][2]) - -arg_list %= G.Epsilon, lambda h,s: [ ] -arg_list %= expr, lambda h,s: [ s[1] ] -arg_list %= expr + comma + arg_list, lambda h,s: [ s[1] ] + s[3] +from cmp.pycompiler import Grammar, NonTerminal, Terminal +from cool.ast.ast import * + +G = Grammar() + +# non-terminals +program = G.NonTerminal('', startSymbol=True) +class_list, def_class = G.NonTerminals(' ') +feature_list, def_attr, def_func, feature = G.NonTerminals(' ') +param_list, param, expr_list = G.NonTerminals(' ') +expr, boolean, compare, arith, term, factor, negate, atom = \ + G.NonTerminals(' ') +func_call, arg_list, dispatch = G.NonTerminals(' ') +def_var, def_var_list = G.NonTerminals(' ') +case_check, case_check_list = G.NonTerminals(' ') + +# Terminals + +ifx,then,elsex,if_r,whilex,loop,loop_r = G.Terminals('if then else fi while loop pool') +ocur,ccur,colon,semi,comma, dot = G.Terminals('{ } : ; , .') +opar,cpar,plus,minus,div,star,notx,roof = G.Terminals('( ) + - / * not ~') +less,less_eq,greater,greater_eq,equal = G.Terminals('< <= > >= =') +let,inx,case,of,case_r,arrow,assign = G.Terminals('let in case of esac => <-') +true,false,num,string = G.Terminals('true false num string') +classx, inherits, new, isvoid = G.Terminals('class inherits new isvoid') +idx,typex,at = G.Terminals('id type @') # 'at' is @ + +# productions +program %= class_list, lambda h,s: ProgramNode(s[1],row=0,column=0) + +# ??? +class_list %= def_class, lambda h,s: [s[1]] +class_list %= def_class + class_list, lambda h,s: [s[1]] + s[2] + +# ??? +def_class %= classx + typex + ocur + feature_list + ccur + semi, lambda h,s: ClassDeclarationNode(s[2][0],s[4],row=s[1][1] ,column=s[1][2]) +def_class %= classx + typex + inherits + typex + ocur + feature_list + ccur + semi, lambda h,s: ClassDeclarationNode(s[2][0],s[6],s[4][0],row=s[1][1] ,column=s[1][2]) + +def_class %= classx + typex + ocur + ccur + semi, lambda h,s: ClassDeclarationNode(s[2][0],[],row=s[1][1] ,column=s[1][2]) +def_class %= classx + typex + inherits + typex + ocur + ccur + semi, lambda h,s: ClassDeclarationNode(s[2][0],[],s[4][0],row=s[1][1] ,column=s[1][2]) + +# ??? +feature_list %= feature + semi, lambda h,s: [s[1]] +feature_list %= feature + semi + feature_list, lambda h,s: [s[1]] + s[3] + +# ??? +feature %= def_attr, lambda h,s: s[1] +feature %= def_func, lambda h,s: s[1] + +# ??? +def_attr %= idx + colon + typex, lambda h,s: AttrDeclarationNode(s[1][0],s[3][0],row=s[1][1] ,column=s[1][2]) +def_attr %= idx + colon + typex + assign + expr, lambda h,s: AttrDeclarationNode(s[1][0],s[3][0],s[5],row=s[1][1] ,column=s[1][2]) + +# ??? +def_func %= idx + opar + param_list + cpar + colon + typex + ocur + expr + ccur, lambda h,s: FuncDeclarationNode(s[1][0],s[3],s[6][0],s[8],row=s[1][1] ,column=s[1][2]) + +param_list %= G.Epsilon, lambda h,s: [ ] +param_list %= param, lambda h,s: [ s[1] ] +param_list %= param + comma + param_list, lambda h,s: [ s[1] ] + s[3] + +# ??? +param %= idx + colon + typex, lambda h,s: ParamNode(s[1][0],s[3][0],row=s[1][1],column = s[1][2]) + +def_var %= idx + colon + typex + assign + expr, lambda h,s: VarDeclarationNode(s[1][0],s[3][0],s[5],row=s[1][1] ,column=s[1][2]) +def_var %= idx + colon + typex, lambda h,s: VarDeclarationNode(s[1][0],s[3][0],None,row=s[1][1] ,column=s[1][2]) + +def_var_list %= def_var, lambda h,s: [ s[1] ] +def_var_list %= def_var + comma + def_var_list, lambda h,s: [ s[1] ] + s[3] + +case_check %= idx + colon + typex + arrow + expr + semi, lambda h,s: CheckNode(s[1][0],s[3][0],s[5],row=s[1][1] ,column=s[1][2]) + +case_check_list %= case_check, lambda h,s: [ s[1] ] +case_check_list %= case_check + case_check_list, lambda h,s: [ s[1] ] + s[2] + + +# ??? +expr %= idx + assign + expr, lambda h,s: AssignNode(s[1][0],s[3],row=s[1][1] ,column=s[1][2]) +expr %= idx + colon + typex + assign + expr, lambda h,s: VarDeclarationNode(s[1][0],s[3][0],s[5],row=s[1][1] ,column=s[1][2]) +expr %= boolean, lambda h,s: s[1] +expr %= ocur + expr_list + ccur, lambda h,s: BlockNode(s[2],row=s[1][1] ,column=s[1][2]) +expr %= ifx + expr + then + expr + elsex + expr + if_r, lambda h,s: ConditionalNode(s[2],s[4],s[6],row=s[1][1] ,column=s[1][2]) +expr %= let + def_var_list + inx + expr, lambda h,s: LetNode(s[2],s[4],row=s[1][1] ,column=s[1][2]) +expr %= case + expr + of + case_check_list + case_r, lambda h,s: CaseNode(s[2],s[4],row=s[1][1] ,column=s[1][2]) +expr %= whilex + expr + loop + expr + loop_r, lambda h,s: WhileNode(s[2],s[4],row=s[1][1] ,column=s[1][2]) + +# ??? +expr_list %= expr + semi, lambda h,s: [ s[1] ] +expr_list %= expr + semi + expr_list, lambda h,s: [ s[1] ] + s[3] + +# ??? +boolean %= notx + boolean, lambda h,s: NotNode(s[2],s[1][1],s[1][2]) +boolean %= compare, lambda h,s: s[1] + +# ??? +compare %= arith + equal + arith , lambda h,s: EqualNode(s[1],s[3],s[2][1],s[2][2]) +compare %= arith + less + arith , lambda h,s: LesserNode(s[1],s[3],s[2][1],s[2][2]) +compare %= arith + less_eq + arith , lambda h,s: LesserEqualNode(s[1],s[3],s[2][1],s[2][2]) +compare %= arith + greater + arith , lambda h,s: GreaterNode(s[1],s[3],s[2][1],s[2][2]) +compare %= arith + greater_eq + arith, lambda h,s: GreaterEqualNode(s[1],s[3],s[2][1],s[2][2]) +compare %= arith, lambda h,s: s[1] + +# ??? +arith %= arith + plus + term, lambda h,s: PlusNode(s[1],s[3],row=s[2][1] ,column=s[2][2]) +arith %= arith + minus + term, lambda h,s: MinusNode(s[1],s[3],row=s[2][1] ,column=s[2][2]) +arith %= term, lambda h,s: s[1] + +# ??? +term %= term + star + factor, lambda h,s: StarNode(s[1],s[3],row=s[2][1] ,column=s[2][2]) +term %= term + div + factor, lambda h,s: DivNode(s[1],s[3],row=s[2][1] ,column=s[2][2]) +term %= factor, lambda h,s: s[1] + +# ??? +factor %= isvoid + factor, lambda h,s: IsVoidNode(s[2],row=s[1][1] ,column=s[1][2]) +factor %= negate, lambda h,s: s[1] + +# +negate %= roof + negate, lambda h,s: RoofNode(s[2],row=s[1][1] ,column=s[1][2]) +negate %= dispatch, lambda h,s: s[1] + +# +dispatch %= dispatch + at + typex + dot + func_call, lambda h,s: CallNode(s[1],s[5][1],s[5][2],s[3][0],row=s[5][3] ,column=s[5][4]) +dispatch %= dispatch + dot + func_call, lambda h,s: CallNode(s[1],s[3][1],s[3][2],None,row=s[3][3] ,column=s[3][4]) +dispatch %= atom, lambda h,s: s[1] + +# ??? +atom %= num, lambda h,s: ConstantNumNode(s[1][0],row=s[1][1] ,column=s[1][2]) +atom %= string, lambda h,s: StringNode(s[1][0],row=s[1][1] ,column=s[1][2]) +atom %= true, lambda h,s: BoolNode(s[1][0],row=s[1][1] ,column=s[1][2]) +atom %= false, lambda h,s: BoolNode(s[1][0],row=s[1][1] ,column=s[1][2]) +atom %= idx, lambda h,s: VariableNode(s[1][0],row=s[1][1] ,column=s[1][2]) +atom %= new + typex, lambda h,s: InstantiateNode(s[2][0],row=s[1][1] ,column=s[1][2]) +atom %= func_call, lambda h,s: CallNode(s[1][0],s[1][1],s[1][2],None,row=s[1][3] ,column=s[1][4]) +atom %= at + typex + dot + func_call, lambda h,s: CallNode(s[4][0],s[4][1],s[4][2],s[2][0],row=s[4][3] ,column=s[4][4]) +atom %= opar + expr + cpar, lambda h,s: s[2] + +# ??? +func_call %= idx + opar + arg_list + cpar, lambda h,s: (VariableNode('self',s[1][1],s[1][2]),s[1][0],s[3],s[1][1],s[1][2]) + +arg_list %= G.Epsilon, lambda h,s: [ ] +arg_list %= expr, lambda h,s: [ s[1] ] +arg_list %= expr + comma + arg_list, lambda h,s: [ s[1] ] + s[3] diff --git a/src/cool2/cool/lexer/comment_lexer.py b/src/cool2/cool/lexer/comment_lexer.py index dd4855e62..cbf9a7324 100644 --- a/src/cool2/cool/lexer/comment_lexer.py +++ b/src/cool2/cool/lexer/comment_lexer.py @@ -1,24 +1,24 @@ -from cool.lexer.cool_lexer import Lexer, save_lexer, load_lexer, lower_case,upper_case,numbers -from cool.grammar.comment_grammar import delimiter_close,delimiter_open,plain_text,C - -text = f'[{lower_case}{upper_case}_{numbers}\n\r\b\f\t\v"~`\'!,:;<@=> \\[\\]\\*/-\\+.\\?\\(\\)\\\\#$%^&\\|'+'{}'+']*' -tex = text.replace('\\(\\)','').replace('\\*','') - -tex3 = f'[(\\*+(\\()*)(\\(+(\\))*)\\)+]' # ( \\(\\* | \\*\\) )^C -tex3 = tex + f'|{tex3}' -tex1 = '\\(\\*' -tex2 = '\\*\\)' - -comment_tokens_def = [ - (plain_text, tex3), - (delimiter_open, tex1), - (delimiter_close, tex2), - ] - -# comment_lexer = Lexer(comment_tokens_def,C.EOF) - -import os -base_dir = os.path.dirname(__file__) -comment_lexer_path = os.path.join(base_dir, 'comment_lexer.obj') -# comment_lexer = save_lexer(comment_tokens_def,comment_lexer_path,C) +from cool.lexer.cool_lexer import Lexer, save_lexer, load_lexer, lower_case,upper_case,numbers +from cool.grammar.comment_grammar import delimiter_close,delimiter_open,plain_text,C + +text = f'[{lower_case}{upper_case}_{numbers}\n\r\b\f\t\v"~`\'!,:;<@=> \\[\\]\\*/-\\+.\\?\\(\\)\\\\#$%^&\\|'+'{}'+']*' +tex = text.replace('\\(\\)','').replace('\\*','') + +tex3 = f'[(\\*+(\\()*)(\\(+(\\))*)\\)+]' # ( \\(\\* | \\*\\) )^C +tex3 = tex + f'|{tex3}' +tex1 = '\\(\\*' +tex2 = '\\*\\)' + +comment_tokens_def = [ + (plain_text, tex3), + (delimiter_open, tex1), + (delimiter_close, tex2), + ] + +# comment_lexer = Lexer(comment_tokens_def,C.EOF) + +import os +base_dir = os.path.dirname(__file__) +comment_lexer_path = os.path.join(base_dir, 'comment_lexer.obj') +# comment_lexer = save_lexer(comment_tokens_def,comment_lexer_path,C) comment_lexer = load_lexer(comment_lexer_path) \ No newline at end of file diff --git a/src/cool2/cool/lexer/cool_lexer.py b/src/cool2/cool/lexer/cool_lexer.py index e5d6f8d5b..5c134a7ba 100644 --- a/src/cool2/cool/lexer/cool_lexer.py +++ b/src/cool2/cool/lexer/cool_lexer.py @@ -1,48 +1,48 @@ -from cool.grammar.cool_grammar import * -from cool.lexer.utils import * - -# Keyword are case in sensitive except for true and false that the first letter must be lower case -keywords = [ - (ifx, '[iI][fF]'), (if_r, '[fF][iI]'), (then, '[tT][hH][eE][nN]'), - (elsex, '[eE][lL][sS][eE]'), (isvoid, '[iI][sS][vV][oO][iI][dD]'), - (new, '[nN][eE][wW]'), (whilex, '[wW][hH][iI][lL][eE]'), - (loop, '[lL][oO][oO][pP]'), (loop_r, '[pP][oO][oO][lL]'), - (classx, '[cC][lL][aA][sS][sS]'), (let, '[lL][eE][tT]'), - (inx, '[iI][nN]'), (case, '[cC][aA][sS][eE]'), (of, '[oO][fF]'), - (case_r, '[eE][sS][aA][cC]'), (inherits, '[iI][nN][hH][eE][rR][iI][tT][sS]'), - (true, 't[rR][uU][eE]'), (false, 'f[aA][lL][sS][eE]') - ] - -operators = [ - (dot, '.'),(plus, '\\+'),(minus, '-'),(div, '/'), - (star, '\\*'),(notx, 'not'),(roof, '~'),(less, '<'), - (less_eq, '<='),(greater, '>'),(greater_eq, '>='), - (equal, '='),(assign, '<-'),(arrow, '=>'),(at, '@'), - ] - -signs = [ - (opar, '\\('),(cpar, '\\)'),(ocur, '{'),(ccur ,'}'), - (colon, ':'),(semi, ';'),(comma,','), - ] - -lower_case = 'qwertyuiopasdfghjklzxcvbnm' -upper_case = 'QWERTYUIOPASDFGHJKLZXCVBNM' -numbers = '1234567890' -text = f'[{lower_case}{upper_case}_{numbers}~`\'!,:;<@=> \\[\\]\\*/-\\+.\\?\\(\\)\\\\#$%^&\\|'+'{}'+']*' - -ignore = [('space',f'[ \n\r\b\f\t\v]+|(--({text}|")*\n)')] - -other = [ - (idx, f'[{lower_case}][{lower_case}{upper_case}_{numbers}]*'), - (typex, f'[{upper_case}][{lower_case}{upper_case}_{numbers}]*'), - (num, f'[123456789][{numbers}]*(.[{numbers}]+)?|0'), - (string, f'"{text}"') - ] -cool_tokens_def = keywords+operators+signs+other+ignore -# cool_lexer = Lexer(cool_token_def,G.EOF) - -import os -base_dir = os.path.dirname(__file__) -cool_lexer_path = os.path.join(base_dir, 'cool_lexer.obj') -# cool_lexer = save_lexer(cool_tokens_def,cool_lexer_path,G) +from cool.grammar.cool_grammar import * +from cool.lexer.utils import * + +# Keyword are case in sensitive except for true and false that the first letter must be lower case +keywords = [ + (ifx, '[iI][fF]'), (if_r, '[fF][iI]'), (then, '[tT][hH][eE][nN]'), + (elsex, '[eE][lL][sS][eE]'), (isvoid, '[iI][sS][vV][oO][iI][dD]'), + (new, '[nN][eE][wW]'), (whilex, '[wW][hH][iI][lL][eE]'), + (loop, '[lL][oO][oO][pP]'), (loop_r, '[pP][oO][oO][lL]'), + (classx, '[cC][lL][aA][sS][sS]'), (let, '[lL][eE][tT]'), + (inx, '[iI][nN]'), (case, '[cC][aA][sS][eE]'), (of, '[oO][fF]'), + (case_r, '[eE][sS][aA][cC]'), (inherits, '[iI][nN][hH][eE][rR][iI][tT][sS]'), + (true, 't[rR][uU][eE]'), (false, 'f[aA][lL][sS][eE]') + ] + +operators = [ + (dot, '.'),(plus, '\\+'),(minus, '-'),(div, '/'), + (star, '\\*'),(notx, 'not'),(roof, '~'),(less, '<'), + (less_eq, '<='),(greater, '>'),(greater_eq, '>='), + (equal, '='),(assign, '<-'),(arrow, '=>'),(at, '@'), + ] + +signs = [ + (opar, '\\('),(cpar, '\\)'),(ocur, '{'),(ccur ,'}'), + (colon, ':'),(semi, ';'),(comma,','), + ] + +lower_case = 'qwertyuiopasdfghjklzxcvbnm' +upper_case = 'QWERTYUIOPASDFGHJKLZXCVBNM' +numbers = '1234567890' +text = f'[{lower_case}{upper_case}_{numbers}~`\'!,:;<@=> \\[\\]\\*/-\\+.\\?\\(\\)\\\\#$%^&\\|'+'{}'+']*' + +ignore = [('space',f'[ \n\r\b\f\t\v]+|(--({text}|")*\n)')] + +other = [ + (idx, f'[{lower_case}][{lower_case}{upper_case}_{numbers}]*'), + (typex, f'[{upper_case}][{lower_case}{upper_case}_{numbers}]*'), + (num, f'[123456789][{numbers}]*(.[{numbers}]+)?|0'), + (string, f'"{text}"') + ] +cool_tokens_def = keywords+operators+signs+other+ignore +# cool_lexer = Lexer(cool_token_def,G.EOF) + +import os +base_dir = os.path.dirname(__file__) +cool_lexer_path = os.path.join(base_dir, 'cool_lexer.obj') +# cool_lexer = save_lexer(cool_tokens_def,cool_lexer_path,G) cool_lexer = load_lexer(cool_lexer_path) \ No newline at end of file diff --git a/src/cool2/cool/lexer/utils.py b/src/cool2/cool/lexer/utils.py index d2e9c2c8d..6929616ae 100644 --- a/src/cool2/cool/lexer/utils.py +++ b/src/cool2/cool/lexer/utils.py @@ -1,19 +1,19 @@ -from lib.lexer.lexer import DetailLexer as Lexer -import pickle - -def save_lexer(table,path,G): - with open(path,'wb') as f: - attributes = [] - for prod in G.Productions: - attributes.append(prod.attributes) - prod.__delattr__('attributes') - lexer = Lexer(table,G.EOF) - pickle.dump(lexer,f,pickle.HIGHEST_PROTOCOL) - for prod,attr in zip(G.Productions,attributes): - prod.attributes = attr - return lexer - -def load_lexer(path): - with open(path,'rb') as f: - lexer = pickle.load(f) - return lexer +from lib.lexer.lexer import DetailLexer as Lexer +import pickle + +def save_lexer(table,path,G): + with open(path,'wb') as f: + attributes = [] + for prod in G.Productions: + attributes.append(prod.attributes) + prod.__delattr__('attributes') + lexer = Lexer(table,G.EOF) + pickle.dump(lexer,f,pickle.HIGHEST_PROTOCOL) + for prod,attr in zip(G.Productions,attributes): + prod.attributes = attr + return lexer + +def load_lexer(path): + with open(path,'rb') as f: + lexer = pickle.load(f) + return lexer diff --git a/src/cool2/cool/lib/std.cool b/src/cool2/cool/lib/std.cool index c045dfee5..3a3eee3c3 100644 --- a/src/cool2/cool/lib/std.cool +++ b/src/cool2/cool/lib/std.cool @@ -1,35 +1,35 @@ --- Standar Cool Library --- Some methods changed when building the ast to provide a python implementation for it -class Object { - abort() : Object{ - self - }; - - type_name() : String { - "Object" - }; - - copy() : SELF_TYPE { - self - }; -}; - -class String inherits Object { - - length() : Int { 0 }; - concat(s : String) : String { s }; - substr(i : Int, l : Int) : String { "string" }; -}; - -class IO inherits Object { - out_string(x : String) : SELF_TYPE { self }; - out_int(x : Int) : SELF_TYPE { self }; - in_string() : String { "string" }; - in_int() : Int { 0 }; -}; - -class Int inherits Object { }; - -class Bool inherits Object { }; - +-- Standar Cool Library +-- Some methods changed when building the ast to provide a python implementation for it +class Object { + abort() : Object{ + self + }; + + type_name() : String { + "Object" + }; + + copy() : SELF_TYPE { + self + }; +}; + +class String inherits Object { + + length() : Int { 0 }; + concat(s : String) : String { s }; + substr(i : Int, l : Int) : String { "string" }; +}; + +class IO inherits Object { + out_string(x : String) : SELF_TYPE { self }; + out_int(x : Int) : SELF_TYPE { self }; + in_string() : String { "string" }; + in_int() : Int { 0 }; +}; + +class Int inherits Object { }; + +class Bool inherits Object { }; + class Void { }; \ No newline at end of file diff --git a/src/cool2/cool/libs.py b/src/cool2/cool/libs.py index 935712eee..651ec7093 100644 --- a/src/cool2/cool/libs.py +++ b/src/cool2/cool/libs.py @@ -1,29 +1,29 @@ -import os -from cool.pipes.pipes import start_pipe, change_escaped_lines, remove_comments_pipe, parse_text_pipe, ast_pipe, type_collector_pipe, build_types_pipe, check_types_pipe -from cool.pipes.pipeline import Pipeline, Pipe - -def get_std(): - std_dir = os.path.join(os.path.dirname(__file__),"lib","std.cool") - with open(std_dir, "r") as f: - std = f.read() - return std - -def get_std_context(): - std_pipeline = Pipeline(start_pipe, - change_escaped_lines, - remove_comments_pipe, - parse_text_pipe, - ast_pipe, - type_collector_pipe, - build_types_pipe, - check_types_pipe) - - std_result = std_pipeline(get_std()) - return std_result['context'] - -def add_std_pipe(result:dict): - std_context = get_std_context() - result['context'] = std_context - return result - -add_std_pipe = Pipe(add_std_pipe) +import os +from cool.pipes.pipes import start_pipe, change_escaped_lines, remove_comments_pipe, parse_text_pipe, ast_pipe, type_collector_pipe, build_types_pipe, check_types_pipe +from cool.pipes.pipeline import Pipeline, Pipe + +def get_std(): + std_dir = os.path.join(os.path.dirname(__file__),"lib","std.cool") + with open(std_dir, "r") as f: + std = f.read() + return std + +def get_std_context(): + std_pipeline = Pipeline(start_pipe, + change_escaped_lines, + remove_comments_pipe, + parse_text_pipe, + ast_pipe, + type_collector_pipe, + build_types_pipe, + check_types_pipe) + + std_result = std_pipeline(get_std()) + return std_result['context'] + +def add_std_pipe(result:dict): + std_context = get_std_context() + result['context'] = std_context + return result + +add_std_pipe = Pipe(add_std_pipe) diff --git a/src/cool2/cool/parser/comment_parser.py b/src/cool2/cool/parser/comment_parser.py index 01b4a5a22..75d75f2a1 100644 --- a/src/cool2/cool/parser/comment_parser.py +++ b/src/cool2/cool/parser/comment_parser.py @@ -1,9 +1,9 @@ -from cool.grammar.comment_grammar import C -from cool.parser.utils import load_parser, save_parser - -import os -base_dir = os.path.dirname(__file__) -comment_parser_path = os.path.join(base_dir, 'comment_parser.obj') -# comment_parser = save_parser(comment_parser_path,C) -# print('Errors',comment_parser.errors) +from cool.grammar.comment_grammar import C +from cool.parser.utils import load_parser, save_parser + +import os +base_dir = os.path.dirname(__file__) +comment_parser_path = os.path.join(base_dir, 'comment_parser.obj') +# comment_parser = save_parser(comment_parser_path,C) +# print('Errors',comment_parser.errors) comment_parser = load_parser(comment_parser_path,C) \ No newline at end of file diff --git a/src/cool2/cool/parser/cool_parser.py b/src/cool2/cool/parser/cool_parser.py index b5440694b..39bc2b6aa 100644 --- a/src/cool2/cool/parser/cool_parser.py +++ b/src/cool2/cool/parser/cool_parser.py @@ -1,9 +1,9 @@ -from cool.grammar.cool_grammar import G -from cool.parser.utils import load_parser, save_parser - -import os -base_dir = os.path.dirname(__file__) -cool_parser_path = os.path.join(base_dir, 'cool_parser.obj') -# cool_parser = save_parser(cool_parser_path,G) -# print('Errors',cool_parser.errors) +from cool.grammar.cool_grammar import G +from cool.parser.utils import load_parser, save_parser + +import os +base_dir = os.path.dirname(__file__) +cool_parser_path = os.path.join(base_dir, 'cool_parser.obj') +# cool_parser = save_parser(cool_parser_path,G) +# print('Errors',cool_parser.errors) cool_parser = load_parser(cool_parser_path,G) \ No newline at end of file diff --git a/src/cool2/cool/parser/utils.py b/src/cool2/cool/parser/utils.py index 2231c8981..a72b7e77e 100644 --- a/src/cool2/cool/parser/utils.py +++ b/src/cool2/cool/parser/utils.py @@ -1,25 +1,25 @@ -from cmp.pycompiler import Production -from lib.lang.language_lr import LanguageLR,LR1Parser,LALR1Parser -import pickle - -def save_parser(path,G): - with open(path,'wb') as f: - pType = G.pType - G.pType = Production - attributes = [] - for prod in G.Productions: - attributes.append(prod.attributes) - prod.__delattr__('attributes') - parser = LALR1Parser(G) - pickle.dump(parser,f,pickle.HIGHEST_PROTOCOL) - for prod,attr in zip(parser.grammar.Productions,attributes): - prod.attributes = attr - G.pType = pType - return parser - -def load_parser(path,G): - with open(path,'rb') as f: - parser = pickle.load(f) - for prod,prod2 in zip(parser.grammar.Productions,G.Productions): - prod.attributes = prod2.attributes - return parser +from cmp.pycompiler import Production +from lib.lang.language_lr import LanguageLR,LR1Parser,LALR1Parser +import pickle + +def save_parser(path,G): + with open(path,'wb') as f: + pType = G.pType + G.pType = Production + attributes = [] + for prod in G.Productions: + attributes.append(prod.attributes) + prod.__delattr__('attributes') + parser = LALR1Parser(G) + pickle.dump(parser,f,pickle.HIGHEST_PROTOCOL) + for prod,attr in zip(parser.grammar.Productions,attributes): + prod.attributes = attr + G.pType = pType + return parser + +def load_parser(path,G): + with open(path,'rb') as f: + parser = pickle.load(f) + for prod,prod2 in zip(parser.grammar.Productions,G.Productions): + prod.attributes = prod2.attributes + return parser diff --git a/src/cool2/cool/pipeline.py b/src/cool2/cool/pipeline.py index 46c944e8a..fd9efbe35 100644 --- a/src/cool2/cool/pipeline.py +++ b/src/cool2/cool/pipeline.py @@ -1,38 +1,38 @@ -from cool.pipes.pipes import start_pipe, change_escaped_lines, remove_comments_pipe,\ - parse_text_pipe, ast_pipe, type_collector_pipe, build_types_pipe, \ - check_types_pipe, run_program_pipe, reconstruct_pipe, void_as_type_pipe, \ - auto_resolver_pipe, string_escape_pipe, tokenize_text_pipe -from cool.libs import add_std_pipe -from cool.pipes.pipeline import Pipeline - -lexer_pipeline = Pipeline(start_pipe, - change_escaped_lines, - remove_comments_pipe, - tokenize_text_pipe, - string_escape_pipe, - ) - -syntax_pipeline = Pipeline(parse_text_pipe, - ) - -semantic_pipeline = Pipeline(add_std_pipe, - ast_pipe, - void_as_type_pipe, - type_collector_pipe, - build_types_pipe, - check_types_pipe, - auto_resolver_pipe, - ) - -execution_pipeline = Pipeline(run_program_pipe) - -cool_pipeline = Pipeline(lexer_pipeline, - syntax_pipeline, - semantic_pipeline, - execution_pipeline) - -reconstr_pipeline = Pipeline(lexer_pipeline, - syntax_pipeline, - semantic_pipeline, - reconstruct_pipe, - execution_pipeline) +from cool.pipes.pipes import start_pipe, change_escaped_lines, remove_comments_pipe,\ + parse_text_pipe, ast_pipe, type_collector_pipe, build_types_pipe, \ + check_types_pipe, run_program_pipe, reconstruct_pipe, void_as_type_pipe, \ + auto_resolver_pipe, string_escape_pipe, tokenize_text_pipe +from cool.libs import add_std_pipe +from cool.pipes.pipeline import Pipeline + +lexer_pipeline = Pipeline(start_pipe, + change_escaped_lines, + remove_comments_pipe, + tokenize_text_pipe, + string_escape_pipe, + ) + +syntax_pipeline = Pipeline(parse_text_pipe, + ) + +semantic_pipeline = Pipeline(add_std_pipe, + ast_pipe, + void_as_type_pipe, + type_collector_pipe, + build_types_pipe, + check_types_pipe, + auto_resolver_pipe, + ) + +execution_pipeline = Pipeline(run_program_pipe) + +cool_pipeline = Pipeline(lexer_pipeline, + syntax_pipeline, + semantic_pipeline, + execution_pipeline) + +reconstr_pipeline = Pipeline(lexer_pipeline, + syntax_pipeline, + semantic_pipeline, + reconstruct_pipe, + execution_pipeline) diff --git a/src/cool2/cool/pipes/pipeline.py b/src/cool2/cool/pipes/pipeline.py index 78e25ee01..973426399 100644 --- a/src/cool2/cool/pipes/pipeline.py +++ b/src/cool2/cool/pipes/pipeline.py @@ -1,38 +1,38 @@ -class Pipe: - def __init__(self, pipe_func, pipe_check=None): - """ - pipe_func: callable that recieve a dictionary and return a dictionary - pipe_check: callable that recieve a dictionary and return if the pipe can be executed - """ - self.func = pipe_func - if pipe_check: - self.check = pipe_check - else: - self.check = lambda x: True - - def __call__(self, *args, **kwargs): - return self.func(*args, **kwargs) - - def can_execute(self, dictionary): - return self.check(dictionary) - -class Pipeline(Pipe): - - def __init__(self, *pipes): - super().__init__(self.__call__) - self.pipes = [pipe for pipe in pipes] - - def __call__(self, *args, **kwargs): - start_pipe = self.pipes[0] - - result = start_pipe(*args, **kwargs) - - for pipe in self.pipes[1:]: - if pipe.can_execute(result): - result = pipe(result) - else: - break - - return result - +class Pipe: + def __init__(self, pipe_func, pipe_check=None): + """ + pipe_func: callable that recieve a dictionary and return a dictionary + pipe_check: callable that recieve a dictionary and return if the pipe can be executed + """ + self.func = pipe_func + if pipe_check: + self.check = pipe_check + else: + self.check = lambda x: True + + def __call__(self, *args, **kwargs): + return self.func(*args, **kwargs) + + def can_execute(self, dictionary): + return self.check(dictionary) + +class Pipeline(Pipe): + + def __init__(self, *pipes): + super().__init__(self.__call__) + self.pipes = [pipe for pipe in pipes] + + def __call__(self, *args, **kwargs): + start_pipe = self.pipes[0] + + result = start_pipe(*args, **kwargs) + + for pipe in self.pipes[1:]: + if pipe.can_execute(result): + result = pipe(result) + else: + break + + return result + \ No newline at end of file diff --git a/src/cool2/cool/pipes/pipes.py b/src/cool2/cool/pipes/pipes.py index 0b707a213..313231322 100644 --- a/src/cool2/cool/pipes/pipes.py +++ b/src/cool2/cool/pipes/pipes.py @@ -1,328 +1,328 @@ - -from cool.lexer.cool_lexer import cool_lexer -from cool.lexer.comment_lexer import comment_lexer -from lib.lang.language_lr import LanguageLR -from cool.parser.cool_parser import cool_parser -from cool.parser.comment_parser import comment_parser -from cool.visitors.visitors import * -from cool.grammar.cool_grammar import G -from cool.grammar.comment_grammar import C -from cool.semantic.scope import Scope -from cool.pipes.utils import pprint_tokens, print_errors -from cool.pipes.pipeline import Pipe - -def start_pipe(text, verbose = False): - """ - Start the pipeline - """ - result = { - "text": text, - "verbose": verbose, - "errors": [], - } - return result - -start_pipe = Pipe(start_pipe) - -def change_escaped_lines(result:dict, escape_regex="\\\ *?\n"): - import re - text, _ = re.subn(escape_regex, "", result['text']) - result["text"] = text - return result - -change_escaped_lines = Pipe(change_escaped_lines) - -def remove_comments_pipe(result:dict, comment_grammar=C, comment_lexer=comment_lexer, comment_parser=comment_parser): - """ - Remove the commented lines from the text - """ - text = result["text"] - - lang = LanguageLR(C, comment_lexer, comment_parser) - errors = [] - parse, tokens = lang(text, errors) - if not errors: - text = comment_parser.evaluate(tokens, errors, True) - - if result.get("verbose",False): - if errors: - print_errors("Removing Comments Errors", errors) - if len(text) != len(result["text"]): - print("=========== Text Comments Removed ===============") - print(text) - else: - print("=========== No Comments Removed ===============") - - result["errors"].extend(errors) - result["text"] = text - - return result - -remove_comments_pipe = Pipe(remove_comments_pipe) - -def tokenize_text_pipe(result:dict, language_grammar=G, language_lexer=cool_lexer, language_parser=cool_parser): - """ - Tokenize the text - """ - text = result['text'] - - lang = LanguageLR(language_grammar, language_lexer, language_parser) - - errors = [] - tokens = lang.get_tokens(text, errors) - - result.update({ - "parser": language_parser, - "lexer": language_lexer, - "language": lang, - "text_tokens": tokens, - }) - - if result.get("verbose",False): - if errors: - print_errors("Lexer Errors", errors) - print('================== TOKENS =====================') - pprint_tokens(tokens) - - result["errors"].extend(errors) - - return result - -tokenize_text_pipe = Pipe(tokenize_text_pipe) - -def parse_text_pipe(result:dict, language_grammar=G, language_lexer=cool_lexer, language_parser=cool_parser): - """ - Parse the text - """ - text = result['text'] - tokens = result.get('text_tokens') - - lang = result.get('language', LanguageLR(language_grammar, language_lexer, language_parser)) - - errors = [] - parse, tokens = lang(text, errors, tokens) - - - if result.get("verbose",False): - if errors: - print_errors("Parsing Text Errors", errors) - if not 'text_tokens' in result: - print('================== TOKENS =====================') - pprint_tokens(tokens) - print('=================== PARSE =====================') - print('\n'.join(repr(x) for x in parse)) - - result.update({ - "text_parse": parse, - "language": lang, - "text_tokens": tokens, - "parser":language_parser - }) - - result["errors"].extend(errors) - - return result - -parse_text_pipe = Pipe(parse_text_pipe) - -def ast_pipe(result:dict): - """ - Add the initial ast - """ - parser = result["parser"] - tokens = result["text_tokens"] - - errors = [] - ast = parser.evaluate(tokens,errors,True) - - result["ast"] = ast - - if result.get("verbose", False): - if errors: - print_errors("Building AST Errors", errors) - print('==================== AST ======================') - formatter = FormatVisitor() - tree = formatter.visit(ast) - print(tree) - - result["errors"].extend(errors) - - return result - -ast_pipe = Pipe(ast_pipe, lambda x: not x["errors"]) - -def type_collector_pipe(result:dict, collector=TypeCollector): - """ - Collects the types in the program. - """ - ast = result.get("ast", None) - if not ast: - return result - - errors = [] - collector = collector(errors, result.get("context", None)) - collector.visit(ast) - context = collector.context - - result["context"] = context - - if result.get("verbose", False): - if errors: - print_errors("Collecting Types Errors", errors) - print('============== COLLECTED TYPES ===============') - print(context) - - result["errors"].extend(errors) - - return result - -type_collector_pipe = Pipe(type_collector_pipe) - -def build_types_pipe(result:dict, builder=TypeBuilder): - """ - Build the types in context - """ - ast = result.get("ast", None) - context = result.get("context",None) - if not ast or not context: - return result - - errors = [] - builder = builder(context, errors) - builder.visit(ast) - - if result.get("verbose", False): - if errors: - print_errors("Building Types Errors", errors) - print('=============== BUILT TYPES ================') - print(context) - - result["errors"].extend(errors) - - return result - -build_types_pipe = Pipe(build_types_pipe) - -def check_types_pipe(result:dict, checker=TypeChecker): - """ - Build the scope and check for types to be ok - """ - - ast = result.get("ast", None) - context = result.get("context",None) - if not ast or not context: - return result - - errors = [] - checker = checker(context, errors) - scope, operator = checker.visit(ast, Scope()) - - result["scope"] = scope - result["operator"] = operator - - if result.get("verbose", False): - if errors: - print_errors("Checking Types Errors", errors) - print("=========== Checked Types Info =============") - print("Scope:") - print(scope) - print("Operator:") - print(operator) - - result["errors"].extend(errors) - - return result - -check_types_pipe = Pipe(check_types_pipe) - -def run_program_pipe(result:dict, runner=RunVisitor): - """ - Run ast and store the result - """ - ast = result.get("ast",None) - context = result.get("context",None) - scope = result.get("scope",None) - operator = result.get("operator",None) - errors = result.get("errors", None) - if any(x == None for x in [ast, context, scope, operator]) or errors: - return result - - errors = [] - runner = runner(context,scope,operator,errors) - value = runner.visit(ast) - - result["value"] = value - - if result.get("verbose", False): - if errors: - print_errors("Running Program Errors", errors) - print('=============== PROGRAM RAN ===============') - print('Returned Value:') - print(value) - - result["errors"].extend(errors) - - return result - -run_program_pipe = Pipe(run_program_pipe, lambda x: not x["errors"]) - -def reconstruct_pipe(result:dict, reconstructer=ReconstructVisitor): - ast = result.get("ast",None) - context = result.get("context",None) - operation = result.get("operator",None) - if any(x == None for x in [ast, context, operation]): - return result - - reconstructer = reconstructer(context, operation) - reconstructed_text = reconstructer.visit(ast) - if result.get("verbose", False): - print("============== Reconstructed Text ===============") - print(reconstructed_text) - result['reconstructed_text'] = reconstructed_text - return result - -reconstruct_pipe = Pipe(reconstruct_pipe) - -def void_as_type_pipe(result:dict): - - tokens = result.get("text_tokens",None) - if not tokens: - return result - - result["errors"].extend([f"Void cant appear explicitly. Line:{x.lex[1]} Column:{x.lex[2]}" for x in tokens if x.token_type.Name == "type" and x.lex[0] == "Void"]) - return result - -void_as_type_pipe = Pipe(void_as_type_pipe) - -def auto_resolver_pipe(result:dict, auto_resolver=AutoResolver): - ast = result.get("ast",None) - context = result.get("context",None) - if any(x == None for x in [ast, context]): - return result - - errors = [] - resolver = auto_resolver(context, errors) - resolver.visit(ast) - - if result.get("verbose", False): - if errors: - print_errors("Auto Resolver Errors", errors) - - result["errors"].extend(errors) - - return result - -auto_resolver_pipe = Pipe(auto_resolver_pipe) - -def string_escape_pipe(result:dict): - tokens = result["text_tokens"] - import re - for tok_str in [x for x in tokens if x.token_type.Name == "string"]: - text = re.sub(r"\\n","\n",tok_str.lex[0]) - text = re.sub(r"\\b","\b",text) - text = re.sub(r"\\t","\t",text) - text = re.sub(r"\\f","\f",text) - tok_str.lex = (text, tok_str.lex[1], tok_str.lex[2]) - return result - + +from cool.lexer.cool_lexer import cool_lexer +from cool.lexer.comment_lexer import comment_lexer +from lib.lang.language_lr import LanguageLR +from cool.parser.cool_parser import cool_parser +from cool.parser.comment_parser import comment_parser +from cool.visitors.visitors import * +from cool.grammar.cool_grammar import G +from cool.grammar.comment_grammar import C +from cool.semantic.scope import Scope +from cool.pipes.utils import pprint_tokens, print_errors +from cool.pipes.pipeline import Pipe + +def start_pipe(text, verbose = False): + """ + Start the pipeline + """ + result = { + "text": text, + "verbose": verbose, + "errors": [], + } + return result + +start_pipe = Pipe(start_pipe) + +def change_escaped_lines(result:dict, escape_regex="\\\ *?\n"): + import re + text, _ = re.subn(escape_regex, "", result['text']) + result["text"] = text + return result + +change_escaped_lines = Pipe(change_escaped_lines) + +def remove_comments_pipe(result:dict, comment_grammar=C, comment_lexer=comment_lexer, comment_parser=comment_parser): + """ + Remove the commented lines from the text + """ + text = result["text"] + + lang = LanguageLR(C, comment_lexer, comment_parser) + errors = [] + parse, tokens = lang(text, errors) + if not errors: + text = comment_parser.evaluate(tokens, errors, True) + + if result.get("verbose",False): + if errors: + print_errors("Removing Comments Errors", errors) + if len(text) != len(result["text"]): + print("=========== Text Comments Removed ===============") + print(text) + else: + print("=========== No Comments Removed ===============") + + result["errors"].extend(errors) + result["text"] = text + + return result + +remove_comments_pipe = Pipe(remove_comments_pipe) + +def tokenize_text_pipe(result:dict, language_grammar=G, language_lexer=cool_lexer, language_parser=cool_parser): + """ + Tokenize the text + """ + text = result['text'] + + lang = LanguageLR(language_grammar, language_lexer, language_parser) + + errors = [] + tokens = lang.get_tokens(text, errors) + + result.update({ + "parser": language_parser, + "lexer": language_lexer, + "language": lang, + "text_tokens": tokens, + }) + + if result.get("verbose",False): + if errors: + print_errors("Lexer Errors", errors) + print('================== TOKENS =====================') + pprint_tokens(tokens) + + result["errors"].extend(errors) + + return result + +tokenize_text_pipe = Pipe(tokenize_text_pipe) + +def parse_text_pipe(result:dict, language_grammar=G, language_lexer=cool_lexer, language_parser=cool_parser): + """ + Parse the text + """ + text = result['text'] + tokens = result.get('text_tokens') + + lang = result.get('language', LanguageLR(language_grammar, language_lexer, language_parser)) + + errors = [] + parse, tokens = lang(text, errors, tokens) + + + if result.get("verbose",False): + if errors: + print_errors("Parsing Text Errors", errors) + if not 'text_tokens' in result: + print('================== TOKENS =====================') + pprint_tokens(tokens) + print('=================== PARSE =====================') + print('\n'.join(repr(x) for x in parse)) + + result.update({ + "text_parse": parse, + "language": lang, + "text_tokens": tokens, + "parser":language_parser + }) + + result["errors"].extend(errors) + + return result + +parse_text_pipe = Pipe(parse_text_pipe) + +def ast_pipe(result:dict): + """ + Add the initial ast + """ + parser = result["parser"] + tokens = result["text_tokens"] + + errors = [] + ast = parser.evaluate(tokens,errors,True) + + result["ast"] = ast + + if result.get("verbose", False): + if errors: + print_errors("Building AST Errors", errors) + print('==================== AST ======================') + formatter = FormatVisitor() + tree = formatter.visit(ast) + print(tree) + + result["errors"].extend(errors) + + return result + +ast_pipe = Pipe(ast_pipe, lambda x: not x["errors"]) + +def type_collector_pipe(result:dict, collector=TypeCollector): + """ + Collects the types in the program. + """ + ast = result.get("ast", None) + if not ast: + return result + + errors = [] + collector = collector(errors, result.get("context", None)) + collector.visit(ast) + context = collector.context + + result["context"] = context + + if result.get("verbose", False): + if errors: + print_errors("Collecting Types Errors", errors) + print('============== COLLECTED TYPES ===============') + print(context) + + result["errors"].extend(errors) + + return result + +type_collector_pipe = Pipe(type_collector_pipe) + +def build_types_pipe(result:dict, builder=TypeBuilder): + """ + Build the types in context + """ + ast = result.get("ast", None) + context = result.get("context",None) + if not ast or not context: + return result + + errors = [] + builder = builder(context, errors) + builder.visit(ast) + + if result.get("verbose", False): + if errors: + print_errors("Building Types Errors", errors) + print('=============== BUILT TYPES ================') + print(context) + + result["errors"].extend(errors) + + return result + +build_types_pipe = Pipe(build_types_pipe) + +def check_types_pipe(result:dict, checker=TypeChecker): + """ + Build the scope and check for types to be ok + """ + + ast = result.get("ast", None) + context = result.get("context",None) + if not ast or not context: + return result + + errors = [] + checker = checker(context, errors) + scope, operator = checker.visit(ast, Scope()) + + result["scope"] = scope + result["operator"] = operator + + if result.get("verbose", False): + if errors: + print_errors("Checking Types Errors", errors) + print("=========== Checked Types Info =============") + print("Scope:") + print(scope) + print("Operator:") + print(operator) + + result["errors"].extend(errors) + + return result + +check_types_pipe = Pipe(check_types_pipe) + +def run_program_pipe(result:dict, runner=RunVisitor): + """ + Run ast and store the result + """ + ast = result.get("ast",None) + context = result.get("context",None) + scope = result.get("scope",None) + operator = result.get("operator",None) + errors = result.get("errors", None) + if any(x == None for x in [ast, context, scope, operator]) or errors: + return result + + errors = [] + runner = runner(context,scope,operator,errors) + value = runner.visit(ast) + + result["value"] = value + + if result.get("verbose", False): + if errors: + print_errors("Running Program Errors", errors) + print('=============== PROGRAM RAN ===============') + print('Returned Value:') + print(value) + + result["errors"].extend(errors) + + return result + +run_program_pipe = Pipe(run_program_pipe, lambda x: not x["errors"]) + +def reconstruct_pipe(result:dict, reconstructer=ReconstructVisitor): + ast = result.get("ast",None) + context = result.get("context",None) + operation = result.get("operator",None) + if any(x == None for x in [ast, context, operation]): + return result + + reconstructer = reconstructer(context, operation) + reconstructed_text = reconstructer.visit(ast) + if result.get("verbose", False): + print("============== Reconstructed Text ===============") + print(reconstructed_text) + result['reconstructed_text'] = reconstructed_text + return result + +reconstruct_pipe = Pipe(reconstruct_pipe) + +def void_as_type_pipe(result:dict): + + tokens = result.get("text_tokens",None) + if not tokens: + return result + + result["errors"].extend([f"Void cant appear explicitly. Line:{x.lex[1]} Column:{x.lex[2]}" for x in tokens if x.token_type.Name == "type" and x.lex[0] == "Void"]) + return result + +void_as_type_pipe = Pipe(void_as_type_pipe) + +def auto_resolver_pipe(result:dict, auto_resolver=AutoResolver): + ast = result.get("ast",None) + context = result.get("context",None) + if any(x == None for x in [ast, context]): + return result + + errors = [] + resolver = auto_resolver(context, errors) + resolver.visit(ast) + + if result.get("verbose", False): + if errors: + print_errors("Auto Resolver Errors", errors) + + result["errors"].extend(errors) + + return result + +auto_resolver_pipe = Pipe(auto_resolver_pipe) + +def string_escape_pipe(result:dict): + tokens = result["text_tokens"] + import re + for tok_str in [x for x in tokens if x.token_type.Name == "string"]: + text = re.sub(r"\\n","\n",tok_str.lex[0]) + text = re.sub(r"\\b","\b",text) + text = re.sub(r"\\t","\t",text) + text = re.sub(r"\\f","\f",text) + tok_str.lex = (text, tok_str.lex[1], tok_str.lex[2]) + return result + string_escape_pipe = Pipe(string_escape_pipe) \ No newline at end of file diff --git a/src/cool2/cool/pipes/utils.py b/src/cool2/cool/pipes/utils.py index d3bc80e0d..55e75d1d9 100644 --- a/src/cool2/cool/pipes/utils.py +++ b/src/cool2/cool/pipes/utils.py @@ -1,19 +1,19 @@ -from cool.lexer.cool_lexer import ocur, ccur, semi - -def pprint_tokens(tokens): - indent = 0 - pending = [] - for token in tokens: - pending.append(token) - if token.token_type in { ocur, ccur, semi }: - if token.token_type == ccur: - indent -= 1 - print(' '*indent + ' '.join(str(t.token_type) for t in pending)) - pending.clear() - if token.token_type == ocur: - indent += 1 - print(' '.join([str(t.token_type) for t in pending])) - -def print_errors(header:str, errors): - print(f"=========== {header} ===============") +from cool.lexer.cool_lexer import ocur, ccur, semi + +def pprint_tokens(tokens): + indent = 0 + pending = [] + for token in tokens: + pending.append(token) + if token.token_type in { ocur, ccur, semi }: + if token.token_type == ccur: + indent -= 1 + print(' '*indent + ' '.join(str(t.token_type) for t in pending)) + pending.clear() + if token.token_type == ocur: + indent += 1 + print(' '.join([str(t.token_type) for t in pending])) + +def print_errors(header:str, errors): + print(f"=========== {header} ===============") print(*[f"- {error}\n" for error in errors]) \ No newline at end of file diff --git a/src/cool2/cool/semantic/atomic.py b/src/cool2/cool/semantic/atomic.py index 5298126a5..1e3611cfb 100644 --- a/src/cool2/cool/semantic/atomic.py +++ b/src/cool2/cool/semantic/atomic.py @@ -1,50 +1,50 @@ -class ClassInstance: - def __init__(self,typex, context, operator, errors,**kwargs): - self.type = typex - self.context = context - self.operator = operator - self.errors = errors - self.scope = typex.class_node.scope.instance_copy(typex,context,operator,errors) - self.scope.set_variable_value('self',self) - if 'value' in kwargs: - self.value = kwargs['value'] - - def __eq__(self, other): - if self.type.name == "Void" and other.type.name == "Void": - return True - else: - return super().__eq__(other) - - def __hash__(self): - if self.type.name == "Void": - return 0 - else: - return super().__hash__() - - def copy(self): - if hasattr(self,'value'): - instance = ClassInstance(self.type,self.context,self.operator,self.errors,value=self.value) - else: - instance = ClassInstance(self.type,self.context,self.operator,self.errors) - instance.scope = self.scope.copy() - return instance - - def shallow_copy(self): - if hasattr(self,'value'): - instance = ClassInstance(self.type,self.context,self.operator,self.errors,value=self.value) - else: - instance = ClassInstance(self.type,self.context,self.operator,self.errors) - instance.scope.locals = [] - for var_info in self.scope.locals: - var = instance.scope.define_variable(var_info.name,var_info.type) - if hasattr(var_info,'value'): - if hasattr(var_info.value,'value'): - var.value = ClassInstance(var_info.value.type,var_info.value.context,var_info.value.operator,var_info.value.errors,value=var_info.value.value) - else: - var.value = var_info.value - return instance - - def __str__(self): - if hasattr(self,'value'): - return f'{self.value}: {self.type.name}' +class ClassInstance: + def __init__(self,typex, context, operator, errors,**kwargs): + self.type = typex + self.context = context + self.operator = operator + self.errors = errors + self.scope = typex.class_node.scope.instance_copy(typex,context,operator,errors) + self.scope.set_variable_value('self',self) + if 'value' in kwargs: + self.value = kwargs['value'] + + def __eq__(self, other): + if self.type.name == "Void" and other.type.name == "Void": + return True + else: + return super().__eq__(other) + + def __hash__(self): + if self.type.name == "Void": + return 0 + else: + return super().__hash__() + + def copy(self): + if hasattr(self,'value'): + instance = ClassInstance(self.type,self.context,self.operator,self.errors,value=self.value) + else: + instance = ClassInstance(self.type,self.context,self.operator,self.errors) + instance.scope = self.scope.copy() + return instance + + def shallow_copy(self): + if hasattr(self,'value'): + instance = ClassInstance(self.type,self.context,self.operator,self.errors,value=self.value) + else: + instance = ClassInstance(self.type,self.context,self.operator,self.errors) + instance.scope.locals = [] + for var_info in self.scope.locals: + var = instance.scope.define_variable(var_info.name,var_info.type) + if hasattr(var_info,'value'): + if hasattr(var_info.value,'value'): + var.value = ClassInstance(var_info.value.type,var_info.value.context,var_info.value.operator,var_info.value.errors,value=var_info.value.value) + else: + var.value = var_info.value + return instance + + def __str__(self): + if hasattr(self,'value'): + return f'{self.value}: {self.type.name}' return f'{self.type.name} instance' \ No newline at end of file diff --git a/src/cool2/cool/semantic/context.py b/src/cool2/cool/semantic/context.py index 8256da6d2..685b6bf61 100644 --- a/src/cool2/cool/semantic/context.py +++ b/src/cool2/cool/semantic/context.py @@ -1,31 +1,31 @@ -from cmp.semantic import Context as DeprecatedContext -from cool.semantic.type import Type, SelfType, AutoType -from cool.error.errors import SemanticError, TYPE_NOT_DEFINED, TYPE_ALREADY_DEFINED - -class Context(DeprecatedContext): - - def __init__(self, special_types:dict): - super().__init__() - self.special_types = special_types - for name in self.special_types: - self.types[name] = self.special_types[name]() - - def get_type(self, name:str,self_type=None): - if name == 'SELF_TYPE': - if self_type: - name = self_type.name - else: - return SelfType() - # raise TypeError('Wrong argument combination: name is "SELF_TYPE" and no self_type given') - elif name == 'AUTO_TYPE': - return AutoType(self) - try: - return self.types[name] - except KeyError: - raise SemanticError(TYPE_NOT_DEFINED, name) - - def create_type(self, name:str): - if name in self.types: - raise SemanticError(TYPE_ALREADY_DEFINED, name) - typex = self.types[name] = Type(name) +from cmp.semantic import Context as DeprecatedContext +from cool.semantic.type import Type, SelfType, AutoType +from cool.error.errors import SemanticError, TYPE_NOT_DEFINED, TYPE_ALREADY_DEFINED + +class Context(DeprecatedContext): + + def __init__(self, special_types:dict): + super().__init__() + self.special_types = special_types + for name in self.special_types: + self.types[name] = self.special_types[name]() + + def get_type(self, name:str,self_type=None): + if name == 'SELF_TYPE': + if self_type: + name = self_type.name + else: + return SelfType() + # raise TypeError('Wrong argument combination: name is "SELF_TYPE" and no self_type given') + elif name == 'AUTO_TYPE': + return AutoType(self) + try: + return self.types[name] + except KeyError: + raise SemanticError(TYPE_NOT_DEFINED, name) + + def create_type(self, name:str): + if name in self.types: + raise SemanticError(TYPE_ALREADY_DEFINED, name) + typex = self.types[name] = Type(name) return typex \ No newline at end of file diff --git a/src/cool2/cool/semantic/operations.py b/src/cool2/cool/semantic/operations.py index d6a9d0a2b..01bce0b2e 100644 --- a/src/cool2/cool/semantic/operations.py +++ b/src/cool2/cool/semantic/operations.py @@ -1,184 +1,184 @@ -from cool.error.errors import SemanticError,RunError, NO_OPERATION_DEFINDED, MULTIPLE_OPERATION_DEFINED, ZERO_DIVISION -from cool.ast.ast import * -from cool.semantic.type import * -from cool.semantic.atomic import * - -class Operator: - - def __init__(self,context,errors): - object_type = context.get_type('Object') - int_type = context.get_type('Int') - string_type = context.get_type('String') - bool_type = context.get_type('Bool') - void_type = context.get_type('Void') - self.operations = OperationDict({ - ('~',(int_type,)):(lambda x: ClassInstance(int_type,context,self,errors,value=-x.value),int_type), - ('-',(int_type,int_type)):(lambda x,y: ClassInstance(int_type,context,self,errors,value=x.value - y.value),int_type), - ('+',(int_type,int_type)):(lambda x,y: ClassInstance(int_type,context,self,errors,value=x.value + y.value),int_type), - ('*',(int_type,int_type)):(lambda x,y: ClassInstance(int_type,context,self,errors,value=x.value * y.value),int_type), - ('/',(int_type,int_type)):(lambda x,y: ClassInstance(int_type,context,self,errors,value=x.value // y.value),int_type), - ('<' ,(int_type,int_type)):(lambda x,y: ClassInstance(bool_type,context,self,errors,value=x.value < y.value),bool_type), - ('>' ,(int_type,int_type)):(lambda x,y: ClassInstance(bool_type,context,self,errors,value=x.value > y.value),bool_type), - ('<=',(int_type,int_type)):(lambda x,y: ClassInstance(bool_type,context,self,errors,value=x.value <= y.value),bool_type), - ('>=',(int_type,int_type)):(lambda x,y: ClassInstance(bool_type,context,self,errors,value=x.value >= y.value),bool_type), - ( '=',(int_type,int_type)):(lambda x,y: ClassInstance(bool_type,context,self,errors,value=x.value == y.value),bool_type), - - ('<' ,(string_type,string_type)):(lambda x,y: ClassInstance(bool_type,context,self,errors,value=x.value < y.value),bool_type), - ('>' ,(string_type,string_type)):(lambda x,y: ClassInstance(bool_type,context,self,errors,value=x.value > y.value),bool_type), - ('<=',(string_type,string_type)):(lambda x,y: ClassInstance(bool_type,context,self,errors,value=x.value <= y.value),bool_type), - ('>=',(string_type,string_type)):(lambda x,y: ClassInstance(bool_type,context,self,errors,value=x.value >= y.value),bool_type), - ( '=',(string_type,string_type)):(lambda x,y: ClassInstance(bool_type,context,self,errors,value=x.value == y.value),bool_type), - - ('not',(bool_type,)):(lambda x: ClassInstance(bool_type,context,self,errors,value=not x.value),bool_type), - ( '=',(bool_type,bool_type)):(lambda x,y: ClassInstance(bool_type,context,self,errors,value=x.value == y.value),bool_type), - - ('isvoid',(object_type,)):(lambda x: ClassInstance(bool_type,context,self,errors,value=x.type == void_type),bool_type), - - ( '=',(object_type,object_type)):(lambda x,y: ClassInstance(bool_type,context,self,errors,value=x.scope.get_variable_value('self') == y.scope.get_variable_value('self')),bool_type), - }) - self.operator = { - PlusNode:'+', - MinusNode:'-', - DivNode:'/', - StarNode:'*', - NotNode:'not', - GreaterEqualNode:'>=', - GreaterNode:'>', - LesserEqualNode:'<=', - LesserNode:'<', - EqualNode:'=', - RoofNode:'~', - IsVoidNode:'isvoid', - } - - def register_operation(self,operator,function,return_type,*types): - self.operations[operator,types] = (function,return_type) - - def get_operator(self,node): - return self.operator.get(type(node),None) - - def operation_defined(self,node, *types): - operator = self.get_operator(node) - if operator: - return (operator,types) in self.operations - return False - - def type_of_operation(self,node,*types): - operator = self.get_operator(node) - if operator: - try: - value = self.operations[(operator,types)] - except KeyError: - return ErrorType() - return value[1] - return ErrorType() - - def operate(self,operator,*values): - types = tuple(value.type for value in values) - try: - return self.operations[operator,types][0](*values) - except KeyError as exc: - raise RunError(exc.args[0]) - except ZeroDivisionError as exc: - raise RunError(ZERO_DIVISION) - - def __str__(self): - return str(self.operations) - -class OperationDict: - def __init__(self, default:"(operator,*types)->(func_operator,return_type)"={}): - self.operations = default - - def get(self,key,default=None): - operator, types = key - types = list(types) - auto_type_index = [i for i,x in enumerate(types) if isinstance(x, AutoType)] - if not auto_type_index: - try: - return self[key] - except KeyError: - return default - - possible_operations_types = [oper_types for oper,oper_types in self.operations if oper == operator and len(oper_types) == len(types)] - for i in [i for i in range(len(types)) if i not in auto_type_index]: - possible_operations_types = [x for x in possible_operations_types if x[i] == types[i]] - - combinations = dict() - for i, possibles in [(i, types[i].possibles) for i in auto_type_index]: - no_possibles = [] - for possible in possibles: - if any(x for x in possible_operations_types if x[i] == possible): - value = combinations.get(i,[]) - value.append(possible) - combinations[i] = value - else: - no_possibles.append(possible) - for to_remove in no_possibles: - types[i].remove_possible(to_remove) - - if len(combinations) != len(auto_type_index): - raise KeyError("No operation can satisfy the current types") - - results = [] - import cool.visitors.utils as ut - for possible_types in ut.set_permutation(*[combinations[i] for i in auto_type_index]): - type_key = [x for x in types] - for j,i in enumerate(auto_type_index): - type_key[i] = possible_types[j] - type_key = tuple(type_key) - try: - result = self[operator,type_key] - results.append(result) - except KeyError: - for j,i in enumerate(auto_type_index): - types[i].remove_possible(possible_types[j]) - - if len(results) != 1: - raise KeyError("Result must have a cardinality of 1") - return results[0] - - def get_covariant(self,key): - operator,types = key - possible_op = [] - - for op,op_types in self.operations: - if operator == op and len(types) == len(op_types): - for op_type,key_type in zip(op_types,types): - if not (op_type in key_type.get_parents() or op_type == key_type): - break - else: - possible_op.append((op,op_types)) - - if len(possible_op) > 1: - raise KeyError(MULTIPLE_OPERATION_DEFINED.format(operator,types)) - if len(possible_op) == 0: - raise KeyError(NO_OPERATION_DEFINDED.format(operator, types)) - - return self.operations[possible_op[0]] - - def __getitem__(self,key): - operator,types = key - try: - return self.operations[key] - except KeyError: - if any(x for x in key[1] if isinstance(x,AutoType)): - result = self.get(key) - return result - raise KeyError(NO_OPERATION_DEFINDED.format(operator, types)) - return self.get_covariant(key) - except TypeError: # Unhashable error - raise KeyError(NO_OPERATION_DEFINDED.format(operator, types)) - - def __contains__(self,key): - try: - self[key] - return True - except KeyError: - return False - - def __setitem__(self,key,value): - op,types = key - self.operations[key] = value - - def __str__(self): +from cool.error.errors import SemanticError,RunError, NO_OPERATION_DEFINDED, MULTIPLE_OPERATION_DEFINED, ZERO_DIVISION +from cool.ast.ast import * +from cool.semantic.type import * +from cool.semantic.atomic import * + +class Operator: + + def __init__(self,context,errors): + object_type = context.get_type('Object') + int_type = context.get_type('Int') + string_type = context.get_type('String') + bool_type = context.get_type('Bool') + void_type = context.get_type('Void') + self.operations = OperationDict({ + ('~',(int_type,)):(lambda x: ClassInstance(int_type,context,self,errors,value=-x.value),int_type), + ('-',(int_type,int_type)):(lambda x,y: ClassInstance(int_type,context,self,errors,value=x.value - y.value),int_type), + ('+',(int_type,int_type)):(lambda x,y: ClassInstance(int_type,context,self,errors,value=x.value + y.value),int_type), + ('*',(int_type,int_type)):(lambda x,y: ClassInstance(int_type,context,self,errors,value=x.value * y.value),int_type), + ('/',(int_type,int_type)):(lambda x,y: ClassInstance(int_type,context,self,errors,value=x.value // y.value),int_type), + ('<' ,(int_type,int_type)):(lambda x,y: ClassInstance(bool_type,context,self,errors,value=x.value < y.value),bool_type), + ('>' ,(int_type,int_type)):(lambda x,y: ClassInstance(bool_type,context,self,errors,value=x.value > y.value),bool_type), + ('<=',(int_type,int_type)):(lambda x,y: ClassInstance(bool_type,context,self,errors,value=x.value <= y.value),bool_type), + ('>=',(int_type,int_type)):(lambda x,y: ClassInstance(bool_type,context,self,errors,value=x.value >= y.value),bool_type), + ( '=',(int_type,int_type)):(lambda x,y: ClassInstance(bool_type,context,self,errors,value=x.value == y.value),bool_type), + + ('<' ,(string_type,string_type)):(lambda x,y: ClassInstance(bool_type,context,self,errors,value=x.value < y.value),bool_type), + ('>' ,(string_type,string_type)):(lambda x,y: ClassInstance(bool_type,context,self,errors,value=x.value > y.value),bool_type), + ('<=',(string_type,string_type)):(lambda x,y: ClassInstance(bool_type,context,self,errors,value=x.value <= y.value),bool_type), + ('>=',(string_type,string_type)):(lambda x,y: ClassInstance(bool_type,context,self,errors,value=x.value >= y.value),bool_type), + ( '=',(string_type,string_type)):(lambda x,y: ClassInstance(bool_type,context,self,errors,value=x.value == y.value),bool_type), + + ('not',(bool_type,)):(lambda x: ClassInstance(bool_type,context,self,errors,value=not x.value),bool_type), + ( '=',(bool_type,bool_type)):(lambda x,y: ClassInstance(bool_type,context,self,errors,value=x.value == y.value),bool_type), + + ('isvoid',(object_type,)):(lambda x: ClassInstance(bool_type,context,self,errors,value=x.type == void_type),bool_type), + + ( '=',(object_type,object_type)):(lambda x,y: ClassInstance(bool_type,context,self,errors,value=x.scope.get_variable_value('self') == y.scope.get_variable_value('self')),bool_type), + }) + self.operator = { + PlusNode:'+', + MinusNode:'-', + DivNode:'/', + StarNode:'*', + NotNode:'not', + GreaterEqualNode:'>=', + GreaterNode:'>', + LesserEqualNode:'<=', + LesserNode:'<', + EqualNode:'=', + RoofNode:'~', + IsVoidNode:'isvoid', + } + + def register_operation(self,operator,function,return_type,*types): + self.operations[operator,types] = (function,return_type) + + def get_operator(self,node): + return self.operator.get(type(node),None) + + def operation_defined(self,node, *types): + operator = self.get_operator(node) + if operator: + return (operator,types) in self.operations + return False + + def type_of_operation(self,node,*types): + operator = self.get_operator(node) + if operator: + try: + value = self.operations[(operator,types)] + except KeyError: + return ErrorType() + return value[1] + return ErrorType() + + def operate(self,operator,*values): + types = tuple(value.type for value in values) + try: + return self.operations[operator,types][0](*values) + except KeyError as exc: + raise RunError(exc.args[0]) + except ZeroDivisionError as exc: + raise RunError(ZERO_DIVISION) + + def __str__(self): + return str(self.operations) + +class OperationDict: + def __init__(self, default:"(operator,*types)->(func_operator,return_type)"={}): + self.operations = default + + def get(self,key,default=None): + operator, types = key + types = list(types) + auto_type_index = [i for i,x in enumerate(types) if isinstance(x, AutoType)] + if not auto_type_index: + try: + return self[key] + except KeyError: + return default + + possible_operations_types = [oper_types for oper,oper_types in self.operations if oper == operator and len(oper_types) == len(types)] + for i in [i for i in range(len(types)) if i not in auto_type_index]: + possible_operations_types = [x for x in possible_operations_types if x[i] == types[i]] + + combinations = dict() + for i, possibles in [(i, types[i].possibles) for i in auto_type_index]: + no_possibles = [] + for possible in possibles: + if any(x for x in possible_operations_types if x[i] == possible): + value = combinations.get(i,[]) + value.append(possible) + combinations[i] = value + else: + no_possibles.append(possible) + for to_remove in no_possibles: + types[i].remove_possible(to_remove) + + if len(combinations) != len(auto_type_index): + raise KeyError("No operation can satisfy the current types") + + results = [] + import cool.visitors.utils as ut + for possible_types in ut.set_permutation(*[combinations[i] for i in auto_type_index]): + type_key = [x for x in types] + for j,i in enumerate(auto_type_index): + type_key[i] = possible_types[j] + type_key = tuple(type_key) + try: + result = self[operator,type_key] + results.append(result) + except KeyError: + for j,i in enumerate(auto_type_index): + types[i].remove_possible(possible_types[j]) + + if len(results) != 1: + raise KeyError("Result must have a cardinality of 1") + return results[0] + + def get_covariant(self,key): + operator,types = key + possible_op = [] + + for op,op_types in self.operations: + if operator == op and len(types) == len(op_types): + for op_type,key_type in zip(op_types,types): + if not (op_type in key_type.get_parents() or op_type == key_type): + break + else: + possible_op.append((op,op_types)) + + if len(possible_op) > 1: + raise KeyError(MULTIPLE_OPERATION_DEFINED.format(operator,types)) + if len(possible_op) == 0: + raise KeyError(NO_OPERATION_DEFINDED.format(operator, types)) + + return self.operations[possible_op[0]] + + def __getitem__(self,key): + operator,types = key + try: + return self.operations[key] + except KeyError: + if any(x for x in key[1] if isinstance(x,AutoType)): + result = self.get(key) + return result + raise KeyError(NO_OPERATION_DEFINDED.format(operator, types)) + return self.get_covariant(key) + except TypeError: # Unhashable error + raise KeyError(NO_OPERATION_DEFINDED.format(operator, types)) + + def __contains__(self,key): + try: + self[key] + return True + except KeyError: + return False + + def __setitem__(self,key,value): + op,types = key + self.operations[key] = value + + def __str__(self): return "\n".join([f"{op} : {', '.join([typex.name for typex in types])} --> {ret_type.name}" for (op, types),(func, ret_type) in self.operations.items()]) \ No newline at end of file diff --git a/src/cool2/cool/semantic/scope.py b/src/cool2/cool/semantic/scope.py index 0da81c5c8..d1fb5c411 100644 --- a/src/cool2/cool/semantic/scope.py +++ b/src/cool2/cool/semantic/scope.py @@ -1,96 +1,96 @@ -from cmp.semantic import Scope as DeprecatedScope -from cool.error.errors import SemanticError -from cool.visitors.visitors import RunVisitor -class Scope(DeprecatedScope): - - def __init__(self, parent=None, typex=None): - super().__init__(parent) - self.type = typex - - def set_parent(self, parent): - self.parent = parent - self.index = len(parent.locals) - - def set_variable_value(self,name:str,value): - variable = self.find_variable(name) - variable.value = value - - def find_variable(self, vname, index=None): - var = super().find_variable(vname, index) - if var: return var - try: - if self.type: - attribute = self.type.get_attribute(vname) - return attribute - return None - except SemanticError: - return None - - def create_child(self, typex=None): - child = super().create_child() - child.type = self.type if not typex else typex - return child - - def get_variable_value(self,name:str): - variable = self.find_variable(name) - if hasattr(variable,'value'): - return variable.value - else: - raise TypeError(f"Value of variable {name} is not assigned") - - def instance_copy(self,typex,context,operator,errors): - scope = Scope(self.parent, self.type) - scope.index = self.index - scope.define_variable('self',typex) - attributes = typex.all_attributes() - run = RunVisitor(context,scope,operator,errors) - for var,attr_typex in attributes: - if var.type.name == 'SELF_TYPE': - scope.define_variable(var.name,typex) - else: - scope.define_variable(var.name,var.type) - for var,attr_typex in attributes: - default_node = var.node.expr - value = run.visit(default_node,scope) - scope.set_variable_value(var.name,value) - scope.children = self.children.copy() - return scope - - def copy(self): - copy_self = Scope(self.parent, self.type) - copy_self.index = self.index - copy_self.children = self.children.copy() - - for var_info in self.locals: - var = copy_self.define_variable(var_info.name,var_info.type) - if hasattr(var_info,'value'): - var.value = var_info.value.copy() - - return copy_self - - def _depth(self) -> int: - if self.parent == None: - return 0 - else: - return self.parent._depth() + 1 - - def __str__(self): - tab = " " - base = tab*self._depth() - value = base + "Scope: Index->%d\n" % self.index - - base += tab - - if self.locals: - value += base + "Local Variables \n" - for x in self.locals: - value += base + tab + x.name + ":" + x.type.name + "\n" - - if self.children: - value += base + "Childrens \n" - - for x in self.children: - value += str(x) - - return value +from cmp.semantic import Scope as DeprecatedScope +from cool.error.errors import SemanticError +from cool.visitors.visitors import RunVisitor +class Scope(DeprecatedScope): + + def __init__(self, parent=None, typex=None): + super().__init__(parent) + self.type = typex + + def set_parent(self, parent): + self.parent = parent + self.index = len(parent.locals) + + def set_variable_value(self,name:str,value): + variable = self.find_variable(name) + variable.value = value + + def find_variable(self, vname, index=None): + var = super().find_variable(vname, index) + if var: return var + try: + if self.type: + attribute = self.type.get_attribute(vname) + return attribute + return None + except SemanticError: + return None + + def create_child(self, typex=None): + child = super().create_child() + child.type = self.type if not typex else typex + return child + + def get_variable_value(self,name:str): + variable = self.find_variable(name) + if hasattr(variable,'value'): + return variable.value + else: + raise TypeError(f"Value of variable {name} is not assigned") + + def instance_copy(self,typex,context,operator,errors): + scope = Scope(self.parent, self.type) + scope.index = self.index + scope.define_variable('self',typex) + attributes = typex.all_attributes() + run = RunVisitor(context,scope,operator,errors) + for var,attr_typex in attributes: + if var.type.name == 'SELF_TYPE': + scope.define_variable(var.name,typex) + else: + scope.define_variable(var.name,var.type) + for var,attr_typex in attributes: + default_node = var.node.expr + value = run.visit(default_node,scope) + scope.set_variable_value(var.name,value) + scope.children = self.children.copy() + return scope + + def copy(self): + copy_self = Scope(self.parent, self.type) + copy_self.index = self.index + copy_self.children = self.children.copy() + + for var_info in self.locals: + var = copy_self.define_variable(var_info.name,var_info.type) + if hasattr(var_info,'value'): + var.value = var_info.value.copy() + + return copy_self + + def _depth(self) -> int: + if self.parent == None: + return 0 + else: + return self.parent._depth() + 1 + + def __str__(self): + tab = " " + base = tab*self._depth() + value = base + "Scope: Index->%d\n" % self.index + + base += tab + + if self.locals: + value += base + "Local Variables \n" + for x in self.locals: + value += base + tab + x.name + ":" + x.type.name + "\n" + + if self.children: + value += base + "Childrens \n" + + for x in self.children: + value += str(x) + + return value \ No newline at end of file diff --git a/src/cool2/cool/semantic/type.py b/src/cool2/cool/semantic/type.py index e3d554bcd..662e5298a 100644 --- a/src/cool2/cool/semantic/type.py +++ b/src/cool2/cool/semantic/type.py @@ -1,389 +1,389 @@ -from cmp.semantic import Attribute, Method -from cmp.semantic import Type as DeprecatedType -from cmp.semantic import SemanticError as DeprecatedSemanticError -from cool.ast.ast import VoidNode,ConstantNumNode,StringNode,BoolNode,SpecialNode,InstantiateNode -from cool.semantic.atomic import ClassInstance -from cool.error.errors import RunError, SemanticError, TypeCoolError, InferError, \ - VOID_TYPE_CONFORMS, METHOD_NOT_DEFINED, METHOD_ALREADY_DEFINED, \ - SUBSTR_OUT_RANGE, ATTRIBUTE_NOT_DEFINED, ATTRIBUTE_ALREADY_DEFINED, \ - ATTRIBUTE_CANT_INFER, METHOD_CANT_INFER, TYPE_CANT_INFER, TYPE_CANT_BE_INHERITED, \ - NO_COMMON_TYPE, READ_IS_NOT_INT -import cool.visitors.utils as ut - -class Type(DeprecatedType): - - def add_special_method(self,func,method_name,method_args): - f = self.get_method(method_name,method_args) - old_type = f.node.body.type - f.node.body = SpecialNode(func,f.node.row,f.node.column) - f.node.body.type = old_type - - def set_parent(self,parent): - DeprecatedType.set_parent(self,parent) - if not parent.can_have_children: - self.parent = None - raise SemanticError(TYPE_CANT_BE_INHERITED, parent.name) - - def __hash__(self): - return hash(self.name) - - def __eq__(self,other): - return isinstance(other, type(self)) and other.name==self.name - - def get_method(self,name:str,args:int,current_type = None, only_local = False): - self = self if not isinstance(self,SelfType) or not current_type else current_type - try: - return next(method for method in self.methods if method.name == name and len(method.param_names)==args) - except StopIteration: - if self.parent is None: - raise SemanticError(METHOD_NOT_DEFINED,name, "", self.name, args) - try: - if not only_local: - return self.parent.get_method(name,args) - raise SemanticError() - except SemanticError: - raise SemanticError(METHOD_NOT_DEFINED, name, "" if not only_local else " locally", self.name, args) - - def define_method(self, name:str, param_names:list, param_types:list, return_type): - if (name,len(param_names)) in ((method.name,len(method.param_names)) for method in self.methods): - raise SemanticError(METHOD_ALREADY_DEFINED, name, len(param_names), self.name) - method = Method(name, param_names, param_types, return_type) - self.methods.append(method) - return method - - def get_parents(self): - if self.parent: - parents = self.parent.get_parents() - return [self.parent] + parents - else: - return [] - - 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(ATTRIBUTE_NOT_DEFINED, name, self.name) - try: - return self.parent.get_attribute(name) - except SemanticError: - raise SemanticError(ATTRIBUTE_NOT_DEFINED, name, 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(ATTRIBUTE_ALREADY_DEFINED, name, self.name) - - def conforms_to(self,other,current_type): - self_type = self if not isinstance(self,SelfType) else current_type - other_type = other if not isinstance(other,SelfType) else current_type - if isinstance(other,AutoType): - new_types = [] - for x in self_type.get_parents() + [self_type]: - if x in other_type.possibles: - new_types.append(x) - other.update_possibles(new_types) - return bool(new_types) - return self_type.__conforms_to(other_type) - - def __conforms_to(self, other): - return other.bypass() or self == other or self.parent is not None and self.parent.__conforms_to(other) - - def join(self,other,current_type): - self = self if not isinstance(self,SelfType) else current_type - other = other if not isinstance(other,SelfType) else current_type - - self = self if not isinstance(self,AutoType) else self.get_lower(current_type) - other = other if not isinstance(other,AutoType) else other.get_lower(current_type) - - self_parents = self.get_parents() - other_parents = other.get_parents() - other_types = [other] + other_parents - self_types = [self] + self_parents - for common in self_types: - if common in other_types: - return common - raise TypeCoolError(NO_COMMON_TYPE, self.name, other.name) - - @property - def can_have_children(self): - return True - - @property - def default(self): - return InstantiateNode("Void") - -class ObjectType(Type): - def __init__(self): - Type.__init__(self, 'Object') - self.parent = None - - def complete(self): - self.add_special_method(self.abort,'abort',0) - self.add_special_method(self.copy,'copy',0) - self.add_special_method(self.type_name,'type_name',0) - - def set_parent(self,parent): - pass - - @staticmethod - def abort(scope,context,operator,errors,**kwargs): - raise RunError('Cool Program Aborted') - - @staticmethod - def type_name(scope,context,operator,errors,**kwargs): - this_type = scope.get_variable_value('self').type - string = context.get_type('String') - return ClassInstance(string,context,operator,errors,value=this_type.name) - - @staticmethod - def copy(scope,context,operator,errors,**kwargs): - value = scope.get_variable_value('self') - return value.shallow_copy() - -class SelfType(Type): - - def __init__(self): - Type.__init__(self, 'SELF_TYPE') - - @property - def can_have_children(self): - return False - -class IntType(Type): - def __init__(self): - Type.__init__(self, 'Int') - - @property - def can_have_children(self): - return False - - @property - def default(self): - return ConstantNumNode('0') - -class BoolType(Type): - def __init__(self): - Type.__init__(self, 'Bool') - - @property - def can_have_children(self): - return False - - @property - def default(self): - return BoolNode('false') - -class StringType(Type): - def __init__(self): - Type.__init__(self, 'String') - - def complete(self): - self.add_special_method(self.length,'length',0) - self.add_special_method(self.concat,'concat',1) - self.add_special_method(self.substr,'substr',2) - - @staticmethod - def length(scope,context,operator,errors,**kwargs): - value = scope.get_variable_value('self').value - int_type = context.get_type('Int') - return ClassInstance(int_type,context,operator,errors,value=len(value)) - - @staticmethod - def concat(scope,context,operator,errors,**kwargs): - value = scope.get_variable_value('self').value - concat = scope.get_variable_value('s').value - typex = context.get_type('String') - return ClassInstance(typex,context,operator,errors,value=value + concat) - - @staticmethod - def substr(scope,context,operator,errors,**kwargs): - value = scope.get_variable_value('self').value - i = scope.get_variable_value('i').value - l = scope.get_variable_value('l').value - typex = context.get_type('String') - value_sliced = value[i:i+l] - if len(value) < l-i or l < 0: - raise RunError(SUBSTR_OUT_RANGE,value,i,l) - return ClassInstance(typex,context,operator,errors,value=value_sliced) - - @property - def can_have_children(self): - return False - - @property - def default(self): - return StringNode('""') - -class VoidType(Type): - - def __init__(self): - Type.__init__(self, 'Void') - - def conforms_to(self, other, current_type): - return True - - def bypass(self): - return True - -class ErrorType(Type): - def __init__(self): - Type.__init__(self, 'Error') - - def conforms_to(self, other, current_type): - return True - - def bypass(self): - return True - - def __eq__(self, other): - return isinstance(other, Type) - - def __hash__(self): - return super().__hash__() - -class IOType(Type): - def __init__(self): - Type.__init__(self, 'IO') - - def complete(self): - self.add_special_method(self.out_string,'out_string',1) - self.add_special_method(self.out_int,'out_int',1) - self.add_special_method(self.in_string,'in_string',0) - self.add_special_method(self.in_int,'in_int',0) - - @staticmethod - def out_string(scope,context,operator,errors,**kwargs): - out = scope.get_variable_value('x').value - print(out, end="") - typex = context.get_type('IO') - return scope.get_variable_value('self') - - @staticmethod - def out_int(scope,context,operator,errors,**kwargs): - out = scope.get_variable_value('x').value - print(out, end="") - typex = context.get_type('IO') - return scope.get_variable_value('self') - - @staticmethod - def in_string(scope,context,operator,errors,**kwargs): - value = input() - typex = context.get_type('String') - return ClassInstance(typex,context,operator,errors,value=value) - - @staticmethod - def in_int(scope,context,operator,errors,**kwargs): - value = input() - import re - m = re.search(" *([1234567890]+)[^1234567890\n]*\n?", value) - typex = context.get_type('Int') - try: - if m: - return ClassInstance(typex,context,operator,errors,value=int(m.group(1))) - else: - raise ValueError() - except ValueError: - raise RunError(READ_IS_NOT_INT, value) - -class AutoType(Type): - def __init__(self,context): - Type.__init__(self, 'AUTO_TYPE') - self.parent = None - self.context = context - self.possibles = [ x for x in context.types.values() if not isinstance(x, ErrorType) ] - self.equals = [self,] - - def update_possibles(self, new_possibles): - for x in self.equals: - x.possibles = new_possibles - - def get_attribute(self,name): - self.update_possibles([x for x in self.possibles if any(attr for attr,typex in x.all_attributes() if attr.name==name)]) - if not self.possibles: - raise InferError(ATTRIBUTE_CANT_INFER, name) - return self.possibles[-1].get_attribute(name) - - def get_method(self,name,args,current_type = None): - self.update_possibles([x for x in self.possibles if any(meth for meth,typex in x.all_methods() if (meth.name==name and len(meth.param_names) == args))]) - if not self.possibles: - raise InferError(METHOD_CANT_INFER, name, args) - return self.possibles[-1].get_method(name,args) - - def conforms_to(self,other,current_type): - self_type = self if not isinstance(self,SelfType) else current_type - other_type = other if not isinstance(other,SelfType) else current_type - if isinstance(other,AutoType): - new_possibles = [] - for p_type in self.possibles: - if p_type in other_type.possibles: - new_possibles.append(p_type) - - self.add_to_equals(other) - - self.update_possibles(new_possibles) - - return bool(new_possibles) - else: - self.update_possibles([x for x in self.possibles if other_type.conforms_to(x,current_type)]) - return bool(self.possibles) - - def is_valid(self,current_type): - """ - Return True if the graph of the possible types is unilaterally connected - """ - if not self.possibles: return False - graph = ut.reverse_graph(ut.build_graph_list(self.possibles)) - _, topo_sort = ut.any_cycles(graph, True) - current = topo_sort[0] - d, f = ut.dfs(graph, current) - if len(d) == len(graph): - return True - return False - - def add_to_equals(self,other): - if not id(other) in [id(x) for x in self.equals]: - for x in self.possibles: - if not x in other.possibles: - self.possibles.remove(x) - for eq in other.equals: - self.equals.append(eq) - eq.equals = self.equals - eq.possibles = self.possibles - - def remove_possible(self, typex): - self.possibles.remove(typex) - self.update_possibles(self.possibles) - - def get_higher(self, current_type): - valid = self.is_valid(current_type) - if not valid: raise InferError(TYPE_CANT_INFER) - graph = ut.reverse_graph(ut.build_graph_list(self.possibles)) - _, topo_sort = ut.any_cycles(graph, True) - - current = topo_sort[0] - while len(graph[current]) == 1: - current = graph[current][0] - - return current - - def get_lower(self,current_type): - ## Lower implementation using graphs - valid = self.is_valid(current_type) - if not valid: raise InferError(TYPE_CANT_INFER) - graph = ut.reverse_graph(ut.build_graph_list(self.possibles)) - _, topo_sort = ut.any_cycles(graph, True) - - current = topo_sort[0] - return current - ## Lower implementation using join operator - # possibles = [x for x in self.possibles if not isinstance(x, ErrorType)] - # lesser = possibles[0] - # for x in possibles: - # lesser = x.join(lesser,current_type) +from cmp.semantic import Attribute, Method +from cmp.semantic import Type as DeprecatedType +from cmp.semantic import SemanticError as DeprecatedSemanticError +from cool.ast.ast import VoidNode,ConstantNumNode,StringNode,BoolNode,SpecialNode,InstantiateNode +from cool.semantic.atomic import ClassInstance +from cool.error.errors import RunError, SemanticError, TypeCoolError, InferError, \ + VOID_TYPE_CONFORMS, METHOD_NOT_DEFINED, METHOD_ALREADY_DEFINED, \ + SUBSTR_OUT_RANGE, ATTRIBUTE_NOT_DEFINED, ATTRIBUTE_ALREADY_DEFINED, \ + ATTRIBUTE_CANT_INFER, METHOD_CANT_INFER, TYPE_CANT_INFER, TYPE_CANT_BE_INHERITED, \ + NO_COMMON_TYPE, READ_IS_NOT_INT +import cool.visitors.utils as ut + +class Type(DeprecatedType): + + def add_special_method(self,func,method_name,method_args): + f = self.get_method(method_name,method_args) + old_type = f.node.body.type + f.node.body = SpecialNode(func,f.node.row,f.node.column) + f.node.body.type = old_type + + def set_parent(self,parent): + DeprecatedType.set_parent(self,parent) + if not parent.can_have_children: + self.parent = None + raise SemanticError(TYPE_CANT_BE_INHERITED, parent.name) + + def __hash__(self): + return hash(self.name) + + def __eq__(self,other): + return isinstance(other, type(self)) and other.name==self.name + + def get_method(self,name:str,args:int,current_type = None, only_local = False): + self = self if not isinstance(self,SelfType) or not current_type else current_type + try: + return next(method for method in self.methods if method.name == name and len(method.param_names)==args) + except StopIteration: + if self.parent is None: + raise SemanticError(METHOD_NOT_DEFINED,name, "", self.name, args) + try: + if not only_local: + return self.parent.get_method(name,args) + raise SemanticError() + except SemanticError: + raise SemanticError(METHOD_NOT_DEFINED, name, "" if not only_local else " locally", self.name, args) + + def define_method(self, name:str, param_names:list, param_types:list, return_type): + if (name,len(param_names)) in ((method.name,len(method.param_names)) for method in self.methods): + raise SemanticError(METHOD_ALREADY_DEFINED, name, len(param_names), self.name) + method = Method(name, param_names, param_types, return_type) + self.methods.append(method) + return method + + def get_parents(self): + if self.parent: + parents = self.parent.get_parents() + return [self.parent] + parents + else: + return [] + + 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(ATTRIBUTE_NOT_DEFINED, name, self.name) + try: + return self.parent.get_attribute(name) + except SemanticError: + raise SemanticError(ATTRIBUTE_NOT_DEFINED, name, 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(ATTRIBUTE_ALREADY_DEFINED, name, self.name) + + def conforms_to(self,other,current_type): + self_type = self if not isinstance(self,SelfType) else current_type + other_type = other if not isinstance(other,SelfType) else current_type + if isinstance(other,AutoType): + new_types = [] + for x in self_type.get_parents() + [self_type]: + if x in other_type.possibles: + new_types.append(x) + other.update_possibles(new_types) + return bool(new_types) + return self_type.__conforms_to(other_type) + + def __conforms_to(self, other): + return other.bypass() or self == other or self.parent is not None and self.parent.__conforms_to(other) + + def join(self,other,current_type): + self = self if not isinstance(self,SelfType) else current_type + other = other if not isinstance(other,SelfType) else current_type + + self = self if not isinstance(self,AutoType) else self.get_lower(current_type) + other = other if not isinstance(other,AutoType) else other.get_lower(current_type) + + self_parents = self.get_parents() + other_parents = other.get_parents() + other_types = [other] + other_parents + self_types = [self] + self_parents + for common in self_types: + if common in other_types: + return common + raise TypeCoolError(NO_COMMON_TYPE, self.name, other.name) + + @property + def can_have_children(self): + return True + + @property + def default(self): + return InstantiateNode("Void") + +class ObjectType(Type): + def __init__(self): + Type.__init__(self, 'Object') + self.parent = None + + def complete(self): + self.add_special_method(self.abort,'abort',0) + self.add_special_method(self.copy,'copy',0) + self.add_special_method(self.type_name,'type_name',0) + + def set_parent(self,parent): + pass + + @staticmethod + def abort(scope,context,operator,errors,**kwargs): + raise RunError('Cool Program Aborted') + + @staticmethod + def type_name(scope,context,operator,errors,**kwargs): + this_type = scope.get_variable_value('self').type + string = context.get_type('String') + return ClassInstance(string,context,operator,errors,value=this_type.name) + + @staticmethod + def copy(scope,context,operator,errors,**kwargs): + value = scope.get_variable_value('self') + return value.shallow_copy() + +class SelfType(Type): + + def __init__(self): + Type.__init__(self, 'SELF_TYPE') + + @property + def can_have_children(self): + return False + +class IntType(Type): + def __init__(self): + Type.__init__(self, 'Int') + + @property + def can_have_children(self): + return False + + @property + def default(self): + return ConstantNumNode('0') + +class BoolType(Type): + def __init__(self): + Type.__init__(self, 'Bool') + + @property + def can_have_children(self): + return False + + @property + def default(self): + return BoolNode('false') + +class StringType(Type): + def __init__(self): + Type.__init__(self, 'String') + + def complete(self): + self.add_special_method(self.length,'length',0) + self.add_special_method(self.concat,'concat',1) + self.add_special_method(self.substr,'substr',2) + + @staticmethod + def length(scope,context,operator,errors,**kwargs): + value = scope.get_variable_value('self').value + int_type = context.get_type('Int') + return ClassInstance(int_type,context,operator,errors,value=len(value)) + + @staticmethod + def concat(scope,context,operator,errors,**kwargs): + value = scope.get_variable_value('self').value + concat = scope.get_variable_value('s').value + typex = context.get_type('String') + return ClassInstance(typex,context,operator,errors,value=value + concat) + + @staticmethod + def substr(scope,context,operator,errors,**kwargs): + value = scope.get_variable_value('self').value + i = scope.get_variable_value('i').value + l = scope.get_variable_value('l').value + typex = context.get_type('String') + value_sliced = value[i:i+l] + if len(value) < l-i or l < 0: + raise RunError(SUBSTR_OUT_RANGE,value,i,l) + return ClassInstance(typex,context,operator,errors,value=value_sliced) + + @property + def can_have_children(self): + return False + + @property + def default(self): + return StringNode('""') + +class VoidType(Type): + + def __init__(self): + Type.__init__(self, 'Void') + + def conforms_to(self, other, current_type): + return True + + def bypass(self): + return True + +class ErrorType(Type): + def __init__(self): + Type.__init__(self, 'Error') + + def conforms_to(self, other, current_type): + return True + + def bypass(self): + return True + + def __eq__(self, other): + return isinstance(other, Type) + + def __hash__(self): + return super().__hash__() + +class IOType(Type): + def __init__(self): + Type.__init__(self, 'IO') + + def complete(self): + self.add_special_method(self.out_string,'out_string',1) + self.add_special_method(self.out_int,'out_int',1) + self.add_special_method(self.in_string,'in_string',0) + self.add_special_method(self.in_int,'in_int',0) + + @staticmethod + def out_string(scope,context,operator,errors,**kwargs): + out = scope.get_variable_value('x').value + print(out, end="") + typex = context.get_type('IO') + return scope.get_variable_value('self') + + @staticmethod + def out_int(scope,context,operator,errors,**kwargs): + out = scope.get_variable_value('x').value + print(out, end="") + typex = context.get_type('IO') + return scope.get_variable_value('self') + + @staticmethod + def in_string(scope,context,operator,errors,**kwargs): + value = input() + typex = context.get_type('String') + return ClassInstance(typex,context,operator,errors,value=value) + + @staticmethod + def in_int(scope,context,operator,errors,**kwargs): + value = input() + import re + m = re.search(" *([1234567890]+)[^1234567890\n]*\n?", value) + typex = context.get_type('Int') + try: + if m: + return ClassInstance(typex,context,operator,errors,value=int(m.group(1))) + else: + raise ValueError() + except ValueError: + raise RunError(READ_IS_NOT_INT, value) + +class AutoType(Type): + def __init__(self,context): + Type.__init__(self, 'AUTO_TYPE') + self.parent = None + self.context = context + self.possibles = [ x for x in context.types.values() if not isinstance(x, ErrorType) ] + self.equals = [self,] + + def update_possibles(self, new_possibles): + for x in self.equals: + x.possibles = new_possibles + + def get_attribute(self,name): + self.update_possibles([x for x in self.possibles if any(attr for attr,typex in x.all_attributes() if attr.name==name)]) + if not self.possibles: + raise InferError(ATTRIBUTE_CANT_INFER, name) + return self.possibles[-1].get_attribute(name) + + def get_method(self,name,args,current_type = None): + self.update_possibles([x for x in self.possibles if any(meth for meth,typex in x.all_methods() if (meth.name==name and len(meth.param_names) == args))]) + if not self.possibles: + raise InferError(METHOD_CANT_INFER, name, args) + return self.possibles[-1].get_method(name,args) + + def conforms_to(self,other,current_type): + self_type = self if not isinstance(self,SelfType) else current_type + other_type = other if not isinstance(other,SelfType) else current_type + if isinstance(other,AutoType): + new_possibles = [] + for p_type in self.possibles: + if p_type in other_type.possibles: + new_possibles.append(p_type) + + self.add_to_equals(other) + + self.update_possibles(new_possibles) + + return bool(new_possibles) + else: + self.update_possibles([x for x in self.possibles if other_type.conforms_to(x,current_type)]) + return bool(self.possibles) + + def is_valid(self,current_type): + """ + Return True if the graph of the possible types is unilaterally connected + """ + if not self.possibles: return False + graph = ut.reverse_graph(ut.build_graph_list(self.possibles)) + _, topo_sort = ut.any_cycles(graph, True) + current = topo_sort[0] + d, f = ut.dfs(graph, current) + if len(d) == len(graph): + return True + return False + + def add_to_equals(self,other): + if not id(other) in [id(x) for x in self.equals]: + for x in self.possibles: + if not x in other.possibles: + self.possibles.remove(x) + for eq in other.equals: + self.equals.append(eq) + eq.equals = self.equals + eq.possibles = self.possibles + + def remove_possible(self, typex): + self.possibles.remove(typex) + self.update_possibles(self.possibles) + + def get_higher(self, current_type): + valid = self.is_valid(current_type) + if not valid: raise InferError(TYPE_CANT_INFER) + graph = ut.reverse_graph(ut.build_graph_list(self.possibles)) + _, topo_sort = ut.any_cycles(graph, True) + + current = topo_sort[0] + while len(graph[current]) == 1: + current = graph[current][0] + + return current + + def get_lower(self,current_type): + ## Lower implementation using graphs + valid = self.is_valid(current_type) + if not valid: raise InferError(TYPE_CANT_INFER) + graph = ut.reverse_graph(ut.build_graph_list(self.possibles)) + _, topo_sort = ut.any_cycles(graph, True) + + current = topo_sort[0] + return current + ## Lower implementation using join operator + # possibles = [x for x in self.possibles if not isinstance(x, ErrorType)] + # lesser = possibles[0] + # for x in possibles: + # lesser = x.join(lesser,current_type) # return lesser \ No newline at end of file diff --git a/src/cool2/cool/visitors/utils.py b/src/cool2/cool/visitors/utils.py index 1623b129b..27054a1fb 100644 --- a/src/cool2/cool/visitors/utils.py +++ b/src/cool2/cool/visitors/utils.py @@ -1,83 +1,83 @@ -def build_graph_list(type_list): - graph = { x:[x.parent,] for x in type_list } - for x in graph: - if graph[x][0] is None or graph[x][0] not in type_list: - graph[x] = [] - return graph - -def reverse_graph(graph): - graph = { x:y.copy() for x,y in graph.items() } - reverse = { x:[] for x in graph } - for x,y in graph.items(): - for z in y: - reverse_list = reverse.get(z, []) - if x not in reverse_list: - reverse_list.append(x) - reverse[z] = reverse_list - return reverse - -def build_graph_dict(context_types): - graph = { y:[y.parent,] for x,y in context_types.items() } - for x in graph: - if graph[x][0] is None: - graph[x] = [] - return graph - - -def dfs(graph, node=None): - d = dict() - f = dict() - time = [0] - if node: - d[node] = time[0]; time[0]+=1 - _dfs(graph, node, d, f, time) - f[node] = time[0]; time[0]+=1 - else: - for node in graph: - if node not in d: - d[node] = time[0]; time[0]+=1 - _dfs(graph, node, d, f, time) - f[node] = time[0]; time[0]+=1 - return d,f - -def _dfs(graph, node, d, f, time): - for adj in graph[node]: - if adj not in d: - d[adj] = time[0]; time[0]+=1 - _dfs(graph,adj,d,f,time) - f[adj] = time[0]; time[0]+=1 - -def any_cycles(graph, return_sort=False): - d,f,time,stack,cycles = { x:0 for x in graph },{ x:0 for x in graph },[1], [], [] - for x in graph: - if not d[x]: - d[x] = time[0]; time[0]+=1 - topological_order(graph,x,d,f,time,stack,cycles) - f[x] = time[0]; time[0]+=1 - stack.append(x) - if return_sort: - stack.reverse() - return cycles, stack - return cycles - - -def topological_order(graph,node,d,f,time,stack,cycles): - - for adj in graph[node]: - if not d[adj]: - d[adj] = time[0]; time[0]+=1 - topological_order(graph,adj,d,f,time,stack,cycles) - f[adj] = time[0]; time[0]+=1 - stack.append(adj) - elif not f[adj]: - cycles.append((node,adj)) - -def set_permutation(*iterables): - if len(iterables) == 1: - for x in iterables[0]: - yield [x] - else: - permutations = [x for x in set_permutation(*iterables[1:])] - for value in iterables[0]: - for permutation in permutations: +def build_graph_list(type_list): + graph = { x:[x.parent,] for x in type_list } + for x in graph: + if graph[x][0] is None or graph[x][0] not in type_list: + graph[x] = [] + return graph + +def reverse_graph(graph): + graph = { x:y.copy() for x,y in graph.items() } + reverse = { x:[] for x in graph } + for x,y in graph.items(): + for z in y: + reverse_list = reverse.get(z, []) + if x not in reverse_list: + reverse_list.append(x) + reverse[z] = reverse_list + return reverse + +def build_graph_dict(context_types): + graph = { y:[y.parent,] for x,y in context_types.items() } + for x in graph: + if graph[x][0] is None: + graph[x] = [] + return graph + + +def dfs(graph, node=None): + d = dict() + f = dict() + time = [0] + if node: + d[node] = time[0]; time[0]+=1 + _dfs(graph, node, d, f, time) + f[node] = time[0]; time[0]+=1 + else: + for node in graph: + if node not in d: + d[node] = time[0]; time[0]+=1 + _dfs(graph, node, d, f, time) + f[node] = time[0]; time[0]+=1 + return d,f + +def _dfs(graph, node, d, f, time): + for adj in graph[node]: + if adj not in d: + d[adj] = time[0]; time[0]+=1 + _dfs(graph,adj,d,f,time) + f[adj] = time[0]; time[0]+=1 + +def any_cycles(graph, return_sort=False): + d,f,time,stack,cycles = { x:0 for x in graph },{ x:0 for x in graph },[1], [], [] + for x in graph: + if not d[x]: + d[x] = time[0]; time[0]+=1 + topological_order(graph,x,d,f,time,stack,cycles) + f[x] = time[0]; time[0]+=1 + stack.append(x) + if return_sort: + stack.reverse() + return cycles, stack + return cycles + + +def topological_order(graph,node,d,f,time,stack,cycles): + + for adj in graph[node]: + if not d[adj]: + d[adj] = time[0]; time[0]+=1 + topological_order(graph,adj,d,f,time,stack,cycles) + f[adj] = time[0]; time[0]+=1 + stack.append(adj) + elif not f[adj]: + cycles.append((node,adj)) + +def set_permutation(*iterables): + if len(iterables) == 1: + for x in iterables[0]: + yield [x] + else: + permutations = [x for x in set_permutation(*iterables[1:])] + for value in iterables[0]: + for permutation in permutations: yield [value] + permutation \ No newline at end of file diff --git a/src/cool2/cool/visitors/visitors.py b/src/cool2/cool/visitors/visitors.py index a99ac2600..7c7866617 100644 --- a/src/cool2/cool/visitors/visitors.py +++ b/src/cool2/cool/visitors/visitors.py @@ -1,1091 +1,1091 @@ -import cmp.visitor as visitor -from cool.ast.ast import * -from cool.semantic.type import Type,ErrorType,StringType,IntType,IOType,BoolType,ObjectType,SelfType,VoidType,AutoType -from cool.semantic.context import Context -from cool.semantic.atomic import ClassInstance -from cool.error.errors import * -import cool.visitors.utils as ut -from cool.semantic.operations import Operator - -class ReconstructVisitor: - def __init__(self, context, operator): - self.context = context - self.operator = operator - self.tab = " " - - def add_line(self, line, depth): - return self.tab*depth + line + "\n" - - def preappend_depth_if_needed(self, text, depth): - if text[-1] != "\n": - return self.tab*depth + text + "\n" - return text - - @visitor.on('node') - def visit(self, node): - pass - - @visitor.when(ProgramNode) - def visit(self, node, depth=0): - program = "" - for decl in node.declarations: - program += self.visit(decl, depth) - program += self.add_line("\n", depth) - - return program - - @visitor.when(ClassDeclarationNode) - def visit(self, node, depth=0): - father = f" inherits {node.parent}" if node.parent != "Object" else "" - class_decl = self.add_line(f"class {node.id}" + father, depth) - class_decl += self.add_line("{", depth) - for feature in node.features: - class_decl += self.visit(feature, depth + 1) - class_decl += self.add_line("}", depth) - return class_decl - - @visitor.when(AttrDeclarationNode) - def visit(self, node, depth=0): - attr_decl = f"{node.id}:{node.type.name}" - if node.expr and node.expr.type.name != "Void" and node.expr.column != None: - attr_decl += " <- " - attr_decl += self.visit(node.expr, depth + 1) - attr_decl += ";" - return self.add_line(attr_decl, depth) - - @visitor.when(FuncDeclarationNode) - def visit(self, node, depth=0): - func_decl = f"{node.id}(" - func_decl += ", ".join([self.visit(x, depth + 1) for x in node.params]) - func_decl += f"): {node.type.name}" - func_decl = self.add_line(func_decl, depth) - func_decl += self.add_line("{", depth) - func_decl += self.preappend_depth_if_needed(self.visit(node.body, depth+1), depth + 1) - func_decl += self.add_line("};", depth) - return func_decl - - @visitor.when(ParamNode) - def visit(self, node, depth=0): - return f"{node.id}:{node.type.name}" - - @visitor.when(VarDeclarationNode) - def visit(self, node, depth=0): - decl = self.add_line(f"{node.id}:{node.type.name} <- ", depth) - decl += self.preappend_depth_if_needed(self.visit(node.expr, depth + 1), depth + 1) - return decl - - @visitor.when(AssignNode) - def visit(self, node, depth=0): - assign = self.add_line(f"{node.id} <- ", depth) - assign += self.preappend_depth_if_needed(self.visit(node.expr, depth + 1), depth + 1) - return assign - - @visitor.when(CallNode) - def visit(self, node, depth=0): - obj = self.visit(node.obj, depth) - obj = self.preappend_depth_if_needed(obj, depth) - if node.at: - obj += self.add_line(f"@{node.at.name}.{node.id}(", depth) # remove the \n - else: - obj += self.add_line(f".{node.id}(", depth) # remove the \n - - for arg in [self.visit(x, depth + 1) for x in node.args]: - if arg[-1] == "\n": - arg = arg[:len(arg)-1] + ",\n" - else: - arg = self.add_line(arg + ",", depth + 1) - obj += arg - if node.args: - obj = obj[:len(obj)-2] + "\n" - obj += self.add_line(")", depth) - return obj - - @visitor.when(BlockNode) - def visit(self, node, depth=0): - block = self.add_line("{", depth) - - for expr in node.expr_list: - expr = self.visit(expr, depth + 1) - if expr[-1] == "\n": - expr = expr[:len(expr)-1] + ";\n" - else: - expr = self.add_line(expr + ";", depth + 1) - block += expr - - block += self.add_line("}", depth) - return block - - @visitor.when(ConditionalNode) - def visit(self, node, depth=0): - condition = self.add_line("if", depth) - condition += self.preappend_depth_if_needed(self.visit(node.condition,depth + 1), depth + 1) - condition += self.add_line("then", depth) - condition += self.preappend_depth_if_needed(self.visit(node.then_expr, depth + 1), depth + 1) - condition += self.add_line("else", depth) - condition += self.preappend_depth_if_needed(self.visit(node.else_expr, depth + 1), depth + 1) - condition += self.add_line("fi", depth) - return condition - - @visitor.when(LetNode) - def visit(self, node, depth=0): - let = self.add_line("let", depth) - for arg in [self.visit(arg, depth + 1) for arg in node.params]: - if arg[-1] == "\n": - arg = arg[:len(arg)-1] + ",\n" - else: - arg = self.preappend_depth_if_needed(arg + ",", depth + 1) - let += arg - if node.params: - let = let[:len(let)-2] + "\n" - - - let += self.add_line("in", depth) - expr = self.preappend_depth_if_needed(self.visit(node.expr, depth + 1), depth + 1) - return let + expr - - @visitor.when(CaseNode) - def visit(self, node, depth=0): - case = self.add_line("case", depth) - case += self.preappend_depth_if_needed(self.visit(node.expr, depth + 1), depth + 1) - case += self.add_line("of", depth) - for check in [self.visit(x, depth + 1) for x in node.params]: - check = self.preappend_depth_if_needed(check, depth + 1) - case += check - case += self.add_line("esac", depth) - return case - - @visitor.when(CheckNode) - def visit(self, node, depth=0): - check = self.add_line(f"{node.id}:{node.type.name} =>", depth) - expr = self.visit(node.expr, depth + 1) - expr = self.preappend_depth_if_needed(expr, depth + 1) - check += expr[:len(expr)-1] + ";\n" - return check - - @visitor.when(WhileNode) - def visit(self, node, depth=0): - code = self.add_line("while", depth) - code += self.preappend_depth_if_needed(self.visit(node.condition, depth + 1), depth + 1) - code += self.add_line("loop", depth) - code += self.preappend_depth_if_needed(self.visit(node.expr, depth + 1), depth + 1) - code += self.add_line("pool", depth) - return code - - @visitor.when(UnaryNode) - def visit(self, node, depth=0): - operator = self.operator.get_operator(node) + " " - operator += self.visit(node.member, depth + 1) - return operator - - @visitor.when(BinaryNode) - def visit(self, node, depth=0): - operator = "(" - operator += self.visit(node.left, depth + 1) - operator += " " + self.operator.get_operator(node) + " " - operator += self.visit(node.right, depth + 1) - operator += ")" - return operator - - @visitor.when(VariableNode) - def visit(self, node, depth=0): - return node.lex - - @visitor.when(ConstantNumNode) - def visit(self, node, depth=0): - return node.lex - - @visitor.when(BoolNode) - def visit(self, node, depth=0): - return node.lex - - @visitor.when(StringNode) - def visit(self, node, depth=0): - return node.lex - - @visitor.when(VoidNode) - def visit(self, node, depth=0): - return "void" - - @visitor.when(InstantiateNode) - def visit(self, node, depth=0): - return "new " + node.lex - -class FormatVisitor(object): - - @visitor.on('node') - def visit(self, node, tabs): - pass - - def get_type_name(self, node): - try: - return node.type.name - except AttributeError: - return node.type - - @visitor.when(ProgramNode) - def visit(self, node, tabs=0): - ans = '\t' * tabs + f'\\__ProgramNode [ ... ]' - statements = '\n'.join(self.visit(child, tabs + 1) for child in node.declarations) - return f'{ans}\n{statements}' - - @visitor.when(ClassDeclarationNode) - def visit(self, node, tabs=0): - parent = '' if node.parent is None else f": {node.parent}" - ans = '\t' * tabs + f'\\__ClassDeclarationNode: class {node.id} {parent} {{ ... }}' - features = '\n'.join(self.visit(child, tabs + 1) for child in node.features) - return f'{ans}\n{features}' - - @visitor.when(AttrDeclarationNode) - def visit(self, node, tabs=0): - ans = '\t' * tabs + f'\\__AttrDeclarationNode: {node.id} : {self.get_type_name(node)}' - return f'{ans}' - - @visitor.when(VarDeclarationNode) - def visit(self, node, tabs=0): - ans = '\t' * tabs + f'\\__VarDeclarationNode: let {node.id} : {self.get_type_name(node)} = ' - expr = self.visit(node.expr, tabs + 1) - return f'{ans}\n{expr}' - - @visitor.when(AssignNode) - def visit(self, node, tabs=0): - ans = '\t' * tabs + f'\\__AssignNode: let {node.id} <- ' - expr = self.visit(node.expr, tabs + 1) - return f'{ans}\n{expr}' - - @visitor.when(FuncDeclarationNode) - def visit(self, node, tabs=0): - params = ', '.join([self.visit(x) for x in node.params]) - ans = '\t' * tabs + f'\\__FuncDeclarationNode: {node.id}({params}) : {self.get_type_name(node)} {{ }};' - body = self.visit(node.body, tabs + 1) - return f'{ans}\n{body}' - - @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(UnaryNode) - def visit(self, node, tabs=0): - ans = '\t' * tabs + f'\\__{node.__class__.__name__} ' - member = self.visit(node.member, tabs + 1) - return f'{ans}\n{member}' - - @visitor.when(AtomicNode) - def visit(self, node, tabs=0): - return '\t' * tabs + f'\\__{node.__class__.__name__}: {node.lex}' - - @visitor.when(BlockNode) - def visit(self,node,tabs=0): - ans = '\t' * tabs + f'\\__ BlockNode: {{ }}' - expr_list = [ self.visit(x,tabs+1) for x in node.expr_list ] - return ans + "\n" + '\n'.join(expr_list) - - @visitor.when(CallNode) - def visit(self, node, tabs=0): - obj = self.visit(node.obj, tabs + 1) - ans = '\t' * tabs + f'\\__CallNode: .{node.id}(, ..., )' - args = '\n'.join(self.visit(arg, tabs + 1) for arg in node.args) - return f'{ans}\n{obj}\n{args}' - - @visitor.when(InstantiateNode) - def visit(self, node, tabs=0): - return '\t' * tabs + f'\\__ InstantiateNode: new {node.lex}' - - @visitor.when(ParamNode) - def visit(self, node, tabs=0): - return '\t' * tabs + f"{node.id} : {self.get_type_name(node)}" - - @visitor.when(CaseNode) - def visit(self, node, tabs=0): - ans = '\t' * tabs + f"\\__CaseNode: case of esac" - expr = self.visit(node.expr, tabs + 1) - checks = "\n".join(self.visit(check, tabs + 1) for check in node.params) - return f"{ans}\n{expr}\n{checks}" - - @visitor.when(CheckNode) - def visit(self, node, tabs=0): - ans = "\t" * tabs + f"\\__CheckNode: {node.id}:{self.get_type_name(node)} => " - expr = self.visit(node.expr, tabs + 1) - return f"{ans}\n{expr}" - - @visitor.when(LetNode) - def visit(self, node, tabs=0): - ans = '\t' * tabs + f"\\__LetNode: let in " - decls = "\n".join(self.visit(decl, tabs + 1) for decl in node.params) - expr = self.visit(node.expr, tabs + 1) - return f"{ans}\n{decls}\n{expr}" - - @visitor.when(ConditionalNode) - def visit(self, node, tabs=0): - ans = '\t' * tabs + f"\\__ConditionalNode: if then else fi" - cond = self.visit(node.condition, tabs + 1) - then = self.visit(node.then_expr, tabs + 1) - elsex = self.visit(node.else_expr, tabs + 1) - return f"{ans}\n{cond}\n{then}\n{elsex}" - - @visitor.when(WhileNode) - def visit(self, node, tabs=0): - ans = '\t' * tabs + f"\\__WhileNode: while loop pool" - cond = self.visit(node.condition, tabs + 1) - body = self.visit(node.expr, tabs + 1) - return f"{ans}\n{cond}\n{body}" - - -class TypeCollector(object): - def __init__(self, errors=[], context=None): - self.context = context - self.errors = errors - - def add_semantic_error(self, error:SemanticError, row:int, column:int): - error.set_position(row, column) - self.errors.append(error) - - @visitor.on('node') - def visit(self, node): - pass - - @visitor.when(ProgramNode) - def visit(self, node): - if not self.context: - self.context = Context({ - 'Object':ObjectType, - 'String':StringType, - 'Bool':BoolType, - 'Int':IntType, - 'IO':IOType, - 'Void':VoidType, - 'Error':ErrorType, - }) - - for class_decl in node.declarations: - try: - self.context.create_type(class_decl.id) - except SemanticError as er: - self.add_semantic_error(er, class_decl.row, class_decl.column) - - for class_decl in node.declarations: - try: - curr_type = self.context.get_type(class_decl.id) - except SemanticError as er: - self.add_semantic_error(er, class_decl.row, class_decl.column) - if class_decl.parent: - try: - curr_type.set_parent(self.context.get_type(class_decl.parent)) - except SemanticError as er: - self.add_semantic_error(er, class_decl.row, class_decl.column) - - cycles = ut.any_cycles(ut.build_graph_dict(self.context.types)) - - for type1,type2 in cycles: - error = SemanticError(CIRCULAR_DEPENDENCY, type1.name, type2.name) - self.errors.append(error) - -class TypeBuilder: - def __init__(self, context, errors=[]): - self.context = context - self.current_type = None - self.errors = errors - - def add_semantic_error(self, error:SemanticError, row:int, column:int): - error.set_position(row, column) - self.errors.append(error) - - @visitor.on('node') - def visit(self, node): - pass - - @visitor.when(ProgramNode) - def visit(self, node): - for decl in node.declarations: - self.visit(decl) - - @visitor.when(ClassDeclarationNode) - def visit(self, node): - self.current_type = self.context.get_type(node.id) - self.current_type.class_node = node - for child in node.features: - self.visit(child) - - @visitor.when(AttrDeclarationNode) - def visit(self, node): - try: - node.type = self.context.get_type(node.type) - except SemanticError as er: - node.type = ErrorType() - self.add_semantic_error(er, node.row, node.column) - try: - attribute = self.current_type.define_attribute(node.id,node.type) - attribute.node = node - except SemanticError as er: - self.add_semantic_error(er, node.row, node.column) - - @visitor.when(FuncDeclarationNode) - def visit(self, node): - for x in node.params: - try: - x.type = self.context.get_type(x.type) - except SemanticError as er: - x.type = ErrorType() - self.add_semantic_error(er, node.row, node.column) - try: - node.type = self.context.get_type(node.type) - except SemanticError as er: - self.add_semantic_error(er, node.row, node.column) - try: - method = self.current_type.define_method(node.id,[x.id for x in node.params],[x.type for x in node.params],node.type) - method.node = node - except SemanticError as er: - self.add_semantic_error(er, node.row, node.column) - -class TypeChecker: - """ - Checks if the operations between types are correct. - Build the program scope. - Nodes are tagged with their type. - """ - def __init__(self, context:Context, errors=[]): - self.context = context - self.current_type = None - self.current_method = None - self.errors = errors - self.operator = Operator(context,errors) - - def complete_initial_types(self): - complete_types = ['Object','String','IO'] - for cmp_type in complete_types: - self.context.get_type(cmp_type).complete() - - def add_semantic_error(self, error:SemanticError, row:int, column:int): - error.set_position(row, column) - self.errors.append(error) - - @visitor.on('node') - def visit(self, node, scope): - pass - - @visitor.when(ProgramNode) - def visit(self, node, scope=None): - scope = scope - - for declaration in node.declarations: - self.visit(declaration, scope) - - if 'Main' in self.context.types: - MainType = self.context.get_type('Main') - try: - main_method = MainType.get_method('main',0, only_local = True) - except SemanticError as er: - self.errors.append(SemanticError(NO_ENTRY_POINT)) - else: - self.errors.append(SemanticError(NO_MAIN_TYPE)) - - self.complete_initial_types() - - return scope,self.operator - - @visitor.when(ClassDeclarationNode) - def visit(self, node, scope): - self.current_type = self.context.get_type(node.id) - node.scope = scope.create_child(self.current_type) - node.scope.define_variable('self',self.current_type) - node.features.sort(key=lambda x: x.__class__.__name__) # Attributes first, Stable sort - for feature in node.features: - self.visit(feature,node.scope) - - @visitor.when(AttrDeclarationNode) - def visit(self, node, scope): - if not node.expr: - if node.type.name != "AUTO_TYPE": - node.expr = node.type.default - node.expr.type = node.type # if type is void then is not assigned in the visit else the type is overridden - else: - return # No default expr can be assing at this moment - self.visit(node.expr,scope) - - if not node.expr.type.conforms_to(node.type,self.current_type): - self.add_semantic_error(TypeCoolError(INCOMPATIBLE_TYPES, node.expr.type.name, node.type.name),node.row,node.column) - - @visitor.when(FuncDeclarationNode) - def visit(self, node, scope): - methods = [] - current_method = self.current_type.get_method(node.id,len(node.params)) - if self.current_type.parent: - methods = self.current_type.parent.all_methods() - methods = [x for x,typex in methods if x.name == node.id and len(x.param_names) == len(node.params)] - methods = [x for x in methods if x != current_method] - if methods: - err = SemanticError(WRONG_SIGNATURE,methods[0].name,self.current_type.parent.name) - self.add_semantic_error(err, node.row, node.column) - - f_scope = scope.create_child() - node.scope = f_scope - - for param in node.params: - self.visit(param,f_scope) - - self.current_method = self.current_type.get_method(node.id,len(node.params)) - self.visit(node.body,f_scope) - - if not isinstance(node.type,VoidType) and not node.body.type.conforms_to(node.type,self.current_type): - err = TypeCoolError(INCOMPATIBLE_TYPES, node.body.type.name,node.type.name) - self.add_semantic_error(err, node.row, node.column) - - @visitor.when(ParamNode) - def visit(self, node, scope): - scope.define_variable(node.id,node.type) - - @visitor.when(VarDeclarationNode) - def visit(self, node, scope): - if node.id == 'self': - err = SemanticError(SELF_IS_READONLY) - self.add_semantic_error(err, node.row, node.column) - - try: - node.type = self.context.get_type(node.type) - except SemanticError as er: - node.type = ErrorType() - self.add_semantic_error(er, node.row, node.column) - if not node.expr: - if node.type.name != "AUTO_TYPE": - node.expr = node.type.default - node.expr.type = node.type # if type is void then is not assigned in the visit else the type is overridden - else: - if scope.is_local(node.id): - er = SemanticError(LOCAL_ALREADY_DEFINED, node.id, self.current_method.name) - self.add_semantic_error(er, node.row, node.column) - else: - scope.define_variable(node.id,node.type) - return # No default expr can be assing at this moment - - self.visit(node.expr,scope) - - if scope.is_local(node.id): - er = SemanticError(LOCAL_ALREADY_DEFINED, node.id, self.current_method.name) - self.add_semantic_error(er, node.row, node.column) - else: - scope.define_variable(node.id,node.type) - - if not node.expr.type.conforms_to(node.type,self.current_type): - er = TypeCoolError(INCOMPATIBLE_TYPES, node.expr.type.name, node.type.name) - self.add_semantic_error(er, node.row, node.column) - - @visitor.when(AssignNode) - def visit(self, node, scope): - if node.id == 'self': - er = SemanticError(SELF_IS_READONLY) - self.add_semantic_error(er, node.row, node.column) - - self.visit(node.expr,scope) - - if not scope.is_defined(node.id): - er = SemanticError(VARIABLE_NOT_DEFINED, node.id,self.current_method.name) - self.add_semantic_error(er, node.row, node.column) - node.type = node.expr.type - else: - var_info = scope.find_variable(node.id) - node.type = var_info.type - - if not node.expr.type.conforms_to(node.type,self.current_type): - er = TypeCoolError(INCOMPATIBLE_TYPES, node.expr.type.name, node.type.name) - self.add_semantic_error(er, node.row, node.column) - - @visitor.when(CallNode) - def visit(self, node, scope): - # obj,id,args - self.visit(node.obj,scope) - for arg in node.args: - self.visit(arg,scope) - - try: - if node.at: - node.at = self.context.get_type(node.at) - if not node.obj.type.conforms_to(node.at,self.current_type): - er = TypeCoolError(INCOMPATIBLE_TYPES, node.obj.type.name, node.at.name) - self.add_semantic_error(er, node.row, node.column) - method = node.at.get_method(node.id,len(node.args),self.current_type) - else: - method = node.obj.type.get_method(node.id,len(node.args),self.current_type) - not_conform = [(x,y.type) for x,y in zip(method.param_types,node.args) if not y.type.conforms_to(x,self.current_type)] - for x,y in not_conform: - er = TypeCoolError(INCOMPATIBLE_TYPES, y.name, x.name) - self.add_semantic_error(er, node.row, node.column) - node.type = method.return_type if method.return_type.name != "SELF_TYPE" else node.obj.type - - except SemanticError as er: - node.type = ErrorType() - self.add_semantic_error(er, node.row, node.column) - - @visitor.when(BlockNode) - def visit(self, node:BlockNode, scope): - - node.scope = scope.create_child() - - for expr in node.expr_list: - self.visit(expr,node.scope) - - node.type = node.expr_list[-1].type - - @visitor.when(ConditionalNode) - def visit(self, node:ConditionalNode, scope): - self.visit(node.condition,scope) - self.visit(node.then_expr,scope) - self.visit(node.else_expr,scope) - - if node.condition.type != self.context.get_type('Bool') and node.condition.type != ErrorType(): - er = TypeCoolError(NOT_BOOLEAN_CONDITION, node.condition.type.name) - self.add_semantic_error(er, node.row, node.column) - try: - node.type = node.get_return_type(self.current_type) - except InferError as er: - node.type = self.context.get_type("Error") - self.add_semantic_error(er, node.row, node.column) - - @visitor.when(LetNode) - def visit(self, node: LetNode, scope): - let_scope = scope.create_child() - curr_scope = let_scope - for var_node in node.params: - attr_scope = curr_scope.create_child() - self.visit(var_node,attr_scope) - curr_scope = attr_scope - body_scope = curr_scope.create_child() - self.visit(node.expr,body_scope) - node.type = node.expr.type - - @visitor.when(WhileNode) - def visit(self, node:WhileNode, scope): - node.type = self.context.get_type('Object') - self.visit(node.condition,scope) - if node.condition.type != self.context.get_type('Bool'): - er = TypeCoolError(NOT_BOOLEAN_CONDITION, node.condition.type.name) - self.add_semantic_error(er, node.row, node.column) - self.visit(node.expr,scope) - - @visitor.when(CheckNode) - def visit(self, node:CheckNode, scope): - if node.id == 'self': - er = SemanticError(SELF_IS_READONLY) - self.add_semantic_error(er, node.row, node.column) - - node.type = self.context.get_type(node.type) - node.scope = scope.create_child() - node.scope.define_variable(node.id,node.type) - - self.visit(node.expr,node.scope) - - # if not node.expr.type.conforms_to(node.type,self.current_type): - # self.errors.append(INCOMPATIBLE_TYPES.format(node.expr.type.name,node.type.name) + f' Line:{node.row} Column:{node.column}') - - @visitor.when(CaseNode) - def visit(self, node:CaseNode, scope): - self.visit(node.expr,scope) - - types = [] - for i,param in enumerate(node.params): - self.visit(param,scope) - # if not (node.expr.type.conforms_to(param.type,self.current_type) or param.type.conforms_to(node.expr.type,self.current_type)): - # self.errors.append(f"Incompatible types {param.type.name} with {node.expr.type.name}" + f' Line:{node.row} Column:{node.column}') - if any(x for x in types if x[1] == param.type): - er = TypeCoolError(CASE_TYPES_REPEATED, param.expr.type.name) - self.add_semantic_error(er, param.row, param.column) - types.append((i,param.expr.type)) - - static_type = types[0][1] - for i,join in types[1:]: - static_type = static_type.join(join,self.current_type) - node.type = static_type - - @visitor.when(UnaryNode) - def visit(self, node, scope): - self.visit(node.member,scope) - - if not self.operator.operation_defined(node,node.member.type): - node.type = ErrorType() - er = TypeCoolError(INVALID_UNARY_OPERATION, self.operator.get_operator(node), node.member.type.name) - self.add_semantic_error(er, node.row, node.column) - else: - node.type = self.operator.type_of_operation(node,node.member.type) - - @visitor.when(BinaryNode) - def visit(self, node, scope): - self.visit(node.left,scope) - self.visit(node.right,scope) - - if not self.operator.operation_defined(node,node.left.type,node.right.type): - node.type = ErrorType() - er = TypeCoolError(INVALID_BINARY_OPERATION,self.operator.get_operator(node), node.left.type.name,node.right.type.name) - self.add_semantic_error(er, node.row, node.column) - else: - node.type = self.operator.type_of_operation(node,node.left.type,node.right.type) - - @visitor.when(ConstantNumNode) - def visit(self, node, scope): - node.type = self.context.get_type("Int") - - @visitor.when(BoolNode) - def visit(self, node, scope): - node.type = self.context.get_type("Bool") - - @visitor.when(StringNode) - def visit(self, node:StringNode, scope): - node.type = self.context.get_type("String") - if len(node.lex) > 1024: - er = LexerCoolError(STRING_TOO_LONG, len(node.lex)) - self.add_semantic_error(er, node.row, node.column) - - @visitor.when(VoidNode) - def visit(self, node, scope): - pass - - @visitor.when(VariableNode) - def visit(self, node, scope): - if scope.is_defined(node.lex): - var = scope.find_variable(node.lex) - node.type = var.type - else: - node.type = ErrorType() - er = SemanticError(VARIABLE_NOT_DEFINED, node.lex, self.current_method.name) - self.add_semantic_error(er, node.row, node.column) - - @visitor.when(InstantiateNode) - def visit(self, node, scope): - try: - node.type = self.context.get_type(node.lex) - - except SemanticError as er: - node.type = ErrorType() - self.add_semantic_error(er, node.row, node.column) - -class AutoResolver: - - def __init__(self, context:Context, errors=[]): - self.context = context - self.errors = errors - - def change_auto_to_concrete_type(self, node, node_type_name="type", less_concrete=False): - typex = getattr(node, node_type_name) - if typex.name == "AUTO_TYPE": - try: - if less_concrete: - setattr(node, node_type_name, typex.get_lower(self.current_type)) - else: - setattr(node, node_type_name, typex.get_higher(self.current_type)) - except InferError as er: - self.add_semantic_error(er, node.row, node.column) - - @visitor.on('node') - def visit(self, node): - pass - - @visitor.when(ProgramNode) - def visit(self, node): - for declaration in node.declarations: - self.visit(declaration) - - @visitor.when(ClassDeclarationNode) - def visit(self, node): - self.current_type = self.context.get_type(node.id) - for feature in node.features: - self.visit(feature) - - @visitor.when(AttrDeclarationNode) - def visit(self, node): - self.change_auto_to_concrete_type(node) - if not node.expr: - node.expr = node.type.default - node.expr.type = node.type - self.visit(node.expr) - - @visitor.when(FuncDeclarationNode) - def visit(self, node): - self.change_auto_to_concrete_type(node) - for param in node.params: - self.visit(param) - - self.visit(node.body) - - @visitor.when(ParamNode) - def visit(self, node): - self.change_auto_to_concrete_type(node, less_concrete=True) - - @visitor.when(VarDeclarationNode) - def visit(self, node): - self.change_auto_to_concrete_type(node) - if not node.expr: - node.expr = node.type.default - node.expr.type = node.type - self.visit(node.expr) - - @visitor.when(AssignNode) - def visit(self, node): - self.visit(node.expr) - - @visitor.when(CallNode) - def visit(self, node): - self.visit(node.obj) - - for arg in node.args: - self.visit(arg) - - if node.at: - pass - - @visitor.when(BlockNode) - def visit(self, node:BlockNode): - for expr in node.expr_list: - self.visit(expr) - - @visitor.when(ConditionalNode) - def visit(self, node:ConditionalNode): - self.visit(node.condition) - self.visit(node.then_expr) - self.visit(node.else_expr) - - @visitor.when(LetNode) - def visit(self, node: LetNode): - for var_node in node.params: - self.visit(var_node) - self.visit(node.expr) - - @visitor.when(WhileNode) - def visit(self, node:WhileNode): - self.visit(node.condition) - self.visit(node.expr) - - @visitor.when(CheckNode) - def visit(self, node:CheckNode): - self.visit(node.expr) - - @visitor.when(CaseNode) - def visit(self, node:CaseNode): - self.visit(node.expr) - - for i,param in enumerate(node.params): - self.visit(param) - - @visitor.when(UnaryNode) - def visit(self, node): - self.visit(node.member) - - @visitor.when(BinaryNode) - def visit(self, node): - self.visit(node.left) - self.visit(node.right) - - @visitor.when(ConstantNumNode) - def visit(self, node): - pass - - @visitor.when(BoolNode) - def visit(self, node): - pass - - @visitor.when(StringNode) - def visit(self, node:StringNode): - pass - - @visitor.when(VoidNode) - def visit(self, node): - pass - - @visitor.when(VariableNode) - def visit(self, node): - pass - - @visitor.when(InstantiateNode) - def visit(self, node): - pass - - -class RunVisitor: - - def __init__(self, context:Context, scope, operator, errors=[]): - self.context = context - self.scope = scope - self.errors = errors - self.operator = operator - - @visitor.on('node') - def visit(self, node, scope): - pass - - @visitor.when(ProgramNode) - def visit(self, node:ProgramNode, scope=None): - for decl in node.declarations: - if decl.id == 'Main': - for method in [x.node for x,y in self.context.get_type('Main').all_methods()]: - if method.id == 'main': - main_type = self.context.get_type(decl.id) - main_instance = ClassInstance(main_type,self.context,self.operator,self.errors) - # method_scope = method.scope.copy() - # method_scope.set_variable_value('self',main_instance) - # method_scope.set_parent(main_instance.scope) - try: - value = self.visit(method.body,main_instance.scope) - except RunError as err: - self.errors.append(err) - return None - return value - - @visitor.when(VarDeclarationNode) - def visit(self, node:VarDeclarationNode, scope): - current_value = self.visit(node.expr,scope) - scope.define_variable(node.id,node.type) - scope.set_variable_value(node.id,current_value) - return current_value - - @visitor.when(AssignNode) - def visit(self, node:AssignNode, scope): - value = self.visit(node.expr,scope) - scope.set_variable_value(node.id,value) - return value - - @visitor.when(CallNode) - def visit(self, node:CallNode, scope): - value = self.visit(node.obj,scope) - if value.type.name == "Void": - raise RunError(DISPATCH_VOID + f" Line:{node.row} Column:{node.column}") - args = [self.visit(x,scope) for x in node.args] - if node.at: - method = node.at.get_method(node.id,len(node.args)) - else: - method = value.type.get_method(node.id,len(node.args)) - func_scope = method.node.scope.copy() - func_scope.set_parent(value.scope) - for arg,name in zip(args,method.param_names): - func_scope.set_variable_value(name,arg) - # func_scope.set_variable_value('self',value) - value = self.visit(method.node.body,func_scope) - return value - - @visitor.when(SpecialNode) - def visit(self, node:SpecialNode, scope): - try: - return node.func(scope,self.context,self.operator,self.errors) - except RunError as cexc: - raise RunError(cexc.text + f" Line:{node.row} Column:{node.column}") - - @visitor.when(BlockNode) - def visit(self, node:BlockNode, scope): - body_scope = scope.create_child() - values = [self.visit(x,body_scope) for x in node.expr_list] - return values[-1] - - @visitor.when(ConditionalNode) - def visit(self, node:ConditionalNode, scope): - condition = self.visit(node.condition,scope) - if condition.value is True: - return self.visit(node.then_expr,scope) - if condition.value is False: - return self.visit(node.else_expr,scope) - raise RunError(NO_BOOL_CONDITION + f" Line:{node.row} Column:{node.column}") # This connot happend BUT you never know - - @visitor.when(LetNode) - def visit(self, node:LetNode, scope): - let_scope = scope.create_child() - for var_decl in node.params: - value = self.visit(var_decl.expr,let_scope) - if not let_scope.is_local(var_decl.id): - let_scope.define_variable(var_decl.id,var_decl.type) - let_scope.set_variable_value(var_decl.id,value) - value = self.visit(node.expr,let_scope) - return value - - @visitor.when(WhileNode) - def visit(self, node:WhileNode, scope): - while self.visit(node.condition,scope).value == True: - self.visit(node.expr,scope) - typex = self.context.get_type('Void') - return ClassInstance(typex,self.context,self.operator,self.errors,value=None) - - @visitor.when(CheckNode) - def visit(self, node:CheckNode, scope,**kwargs): - node_scope = scope.create_child() - value = kwargs['value'] - node_scope.define_variable(node.id,value.type) - node_scope.set_variable_value(node.id,value) - return self.visit(node.expr,node_scope) - - @visitor.when(CaseNode) - def visit(self, node:CaseNode, scope): - value = self.visit(node.expr,scope) - - if value.type.name == "Void": - raise RunError(VOID_TYPE_CONFORMS + f" Line: {node.row} Column: {node.column}") - - try: - greaters = (x for x in [(i,x.type) for i,x in enumerate(node.params)] if value.type.conforms_to(x[1],value.type)) - least_pos,least = next(greaters) - for i,other in greaters: - least_pos,least = (least_pos,least) if least.conforms_to(other,least) else (i,other) - except StopIteration: - raise RunError(CASE_NO_BRANCH_SELECTED.format(value.type.name) + f" Line: {node.row} Column: {node.column}") - - return self.visit(node.params[least_pos],scope,value=value) - - @visitor.when(UnaryNode) - def visit(self, node, scope): - operator = self.operator.get_operator(node) - value = None - member_value = self.visit(node.member,scope) - try: - value = self.operator.operate(operator,member_value) - except RunError as err: - raise RunError(err.text + f' Line:{node.row} Column:{node.column}') - return value - - @visitor.when(BinaryNode) - def visit(self, node, scope): - operator = self.operator.get_operator(node) - value = None - left_value = self.visit(node.left,scope) - right_value = self.visit(node.right,scope) - try: - value = self.operator.operate(operator,left_value,right_value) - except RunError as err: - raise RunError(err.text + f' Line:{node.row} Column:{node.column}') - return value - - @visitor.when(ConstantNumNode) - def visit(self, node:ConstantNumNode, scope): - typex = self.context.get_type('Int') - return ClassInstance(typex,self.context,self.operator,self.errors,value=int(node.lex)) - - @visitor.when(BoolNode) - def visit(self, node, scope): - typex = self.context.get_type("Bool") - if node.lex[0]=='t': # true - return ClassInstance(typex,self.context,self.operator,self.errors,value=True) - return ClassInstance(typex,self.context,self.operator,self.errors,value=False) - - @visitor.when(StringNode) - def visit(self, node, scope): - typex = self.context.get_type('String') - string_without_delimitators = node.lex[1:len(node.lex)-1] - return ClassInstance(typex,self.context,self.operator,self.errors,value=string_without_delimitators) - - @visitor.when(VoidNode) - def visit(self, node, scope): - typex = self.context.get_type('Void') - return ClassInstance(typex,self.context,self.operator,self.errors,value=None) - - @visitor.when(VariableNode) - def visit(self, node:VariableNode, scope): - try: - value = scope.get_variable_value(node.lex) - except TypeError as exc: # Variable not assign yet searching in Attr - try: - attr = [x.type for x in scope.locals if x.name == 'self'][0].get_attribute(node.lex) - value = self.visit(attr.node.expr, scope) - except SemanticError as er: # Attr not found - er.set_position(node.row, node.column) - self.errors.append(er) - return value - - @visitor.when(InstantiateNode) - def visit(self, node:InstantiateNode, scope): - typex = self.context.get_type(node.lex,scope.find_variable('self').type) - return ClassInstance(typex,self.context,self.operator,self.errors) +import cmp.visitor as visitor +from cool.ast.ast import * +from cool.semantic.type import Type,ErrorType,StringType,IntType,IOType,BoolType,ObjectType,SelfType,VoidType,AutoType +from cool.semantic.context import Context +from cool.semantic.atomic import ClassInstance +from cool.error.errors import * +import cool.visitors.utils as ut +from cool.semantic.operations import Operator + +class ReconstructVisitor: + def __init__(self, context, operator): + self.context = context + self.operator = operator + self.tab = " " + + def add_line(self, line, depth): + return self.tab*depth + line + "\n" + + def preappend_depth_if_needed(self, text, depth): + if text[-1] != "\n": + return self.tab*depth + text + "\n" + return text + + @visitor.on('node') + def visit(self, node): + pass + + @visitor.when(ProgramNode) + def visit(self, node, depth=0): + program = "" + for decl in node.declarations: + program += self.visit(decl, depth) + program += self.add_line("\n", depth) + + return program + + @visitor.when(ClassDeclarationNode) + def visit(self, node, depth=0): + father = f" inherits {node.parent}" if node.parent != "Object" else "" + class_decl = self.add_line(f"class {node.id}" + father, depth) + class_decl += self.add_line("{", depth) + for feature in node.features: + class_decl += self.visit(feature, depth + 1) + class_decl += self.add_line("}", depth) + return class_decl + + @visitor.when(AttrDeclarationNode) + def visit(self, node, depth=0): + attr_decl = f"{node.id}:{node.type.name}" + if node.expr and node.expr.type.name != "Void" and node.expr.column != None: + attr_decl += " <- " + attr_decl += self.visit(node.expr, depth + 1) + attr_decl += ";" + return self.add_line(attr_decl, depth) + + @visitor.when(FuncDeclarationNode) + def visit(self, node, depth=0): + func_decl = f"{node.id}(" + func_decl += ", ".join([self.visit(x, depth + 1) for x in node.params]) + func_decl += f"): {node.type.name}" + func_decl = self.add_line(func_decl, depth) + func_decl += self.add_line("{", depth) + func_decl += self.preappend_depth_if_needed(self.visit(node.body, depth+1), depth + 1) + func_decl += self.add_line("};", depth) + return func_decl + + @visitor.when(ParamNode) + def visit(self, node, depth=0): + return f"{node.id}:{node.type.name}" + + @visitor.when(VarDeclarationNode) + def visit(self, node, depth=0): + decl = self.add_line(f"{node.id}:{node.type.name} <- ", depth) + decl += self.preappend_depth_if_needed(self.visit(node.expr, depth + 1), depth + 1) + return decl + + @visitor.when(AssignNode) + def visit(self, node, depth=0): + assign = self.add_line(f"{node.id} <- ", depth) + assign += self.preappend_depth_if_needed(self.visit(node.expr, depth + 1), depth + 1) + return assign + + @visitor.when(CallNode) + def visit(self, node, depth=0): + obj = self.visit(node.obj, depth) + obj = self.preappend_depth_if_needed(obj, depth) + if node.at: + obj += self.add_line(f"@{node.at.name}.{node.id}(", depth) # remove the \n + else: + obj += self.add_line(f".{node.id}(", depth) # remove the \n + + for arg in [self.visit(x, depth + 1) for x in node.args]: + if arg[-1] == "\n": + arg = arg[:len(arg)-1] + ",\n" + else: + arg = self.add_line(arg + ",", depth + 1) + obj += arg + if node.args: + obj = obj[:len(obj)-2] + "\n" + obj += self.add_line(")", depth) + return obj + + @visitor.when(BlockNode) + def visit(self, node, depth=0): + block = self.add_line("{", depth) + + for expr in node.expr_list: + expr = self.visit(expr, depth + 1) + if expr[-1] == "\n": + expr = expr[:len(expr)-1] + ";\n" + else: + expr = self.add_line(expr + ";", depth + 1) + block += expr + + block += self.add_line("}", depth) + return block + + @visitor.when(ConditionalNode) + def visit(self, node, depth=0): + condition = self.add_line("if", depth) + condition += self.preappend_depth_if_needed(self.visit(node.condition,depth + 1), depth + 1) + condition += self.add_line("then", depth) + condition += self.preappend_depth_if_needed(self.visit(node.then_expr, depth + 1), depth + 1) + condition += self.add_line("else", depth) + condition += self.preappend_depth_if_needed(self.visit(node.else_expr, depth + 1), depth + 1) + condition += self.add_line("fi", depth) + return condition + + @visitor.when(LetNode) + def visit(self, node, depth=0): + let = self.add_line("let", depth) + for arg in [self.visit(arg, depth + 1) for arg in node.params]: + if arg[-1] == "\n": + arg = arg[:len(arg)-1] + ",\n" + else: + arg = self.preappend_depth_if_needed(arg + ",", depth + 1) + let += arg + if node.params: + let = let[:len(let)-2] + "\n" + + + let += self.add_line("in", depth) + expr = self.preappend_depth_if_needed(self.visit(node.expr, depth + 1), depth + 1) + return let + expr + + @visitor.when(CaseNode) + def visit(self, node, depth=0): + case = self.add_line("case", depth) + case += self.preappend_depth_if_needed(self.visit(node.expr, depth + 1), depth + 1) + case += self.add_line("of", depth) + for check in [self.visit(x, depth + 1) for x in node.params]: + check = self.preappend_depth_if_needed(check, depth + 1) + case += check + case += self.add_line("esac", depth) + return case + + @visitor.when(CheckNode) + def visit(self, node, depth=0): + check = self.add_line(f"{node.id}:{node.type.name} =>", depth) + expr = self.visit(node.expr, depth + 1) + expr = self.preappend_depth_if_needed(expr, depth + 1) + check += expr[:len(expr)-1] + ";\n" + return check + + @visitor.when(WhileNode) + def visit(self, node, depth=0): + code = self.add_line("while", depth) + code += self.preappend_depth_if_needed(self.visit(node.condition, depth + 1), depth + 1) + code += self.add_line("loop", depth) + code += self.preappend_depth_if_needed(self.visit(node.expr, depth + 1), depth + 1) + code += self.add_line("pool", depth) + return code + + @visitor.when(UnaryNode) + def visit(self, node, depth=0): + operator = self.operator.get_operator(node) + " " + operator += self.visit(node.member, depth + 1) + return operator + + @visitor.when(BinaryNode) + def visit(self, node, depth=0): + operator = "(" + operator += self.visit(node.left, depth + 1) + operator += " " + self.operator.get_operator(node) + " " + operator += self.visit(node.right, depth + 1) + operator += ")" + return operator + + @visitor.when(VariableNode) + def visit(self, node, depth=0): + return node.lex + + @visitor.when(ConstantNumNode) + def visit(self, node, depth=0): + return node.lex + + @visitor.when(BoolNode) + def visit(self, node, depth=0): + return node.lex + + @visitor.when(StringNode) + def visit(self, node, depth=0): + return node.lex + + @visitor.when(VoidNode) + def visit(self, node, depth=0): + return "void" + + @visitor.when(InstantiateNode) + def visit(self, node, depth=0): + return "new " + node.lex + +class FormatVisitor(object): + + @visitor.on('node') + def visit(self, node, tabs): + pass + + def get_type_name(self, node): + try: + return node.type.name + except AttributeError: + return node.type + + @visitor.when(ProgramNode) + def visit(self, node, tabs=0): + ans = '\t' * tabs + f'\\__ProgramNode [ ... ]' + statements = '\n'.join(self.visit(child, tabs + 1) for child in node.declarations) + return f'{ans}\n{statements}' + + @visitor.when(ClassDeclarationNode) + def visit(self, node, tabs=0): + parent = '' if node.parent is None else f": {node.parent}" + ans = '\t' * tabs + f'\\__ClassDeclarationNode: class {node.id} {parent} {{ ... }}' + features = '\n'.join(self.visit(child, tabs + 1) for child in node.features) + return f'{ans}\n{features}' + + @visitor.when(AttrDeclarationNode) + def visit(self, node, tabs=0): + ans = '\t' * tabs + f'\\__AttrDeclarationNode: {node.id} : {self.get_type_name(node)}' + return f'{ans}' + + @visitor.when(VarDeclarationNode) + def visit(self, node, tabs=0): + ans = '\t' * tabs + f'\\__VarDeclarationNode: let {node.id} : {self.get_type_name(node)} = ' + expr = self.visit(node.expr, tabs + 1) + return f'{ans}\n{expr}' + + @visitor.when(AssignNode) + def visit(self, node, tabs=0): + ans = '\t' * tabs + f'\\__AssignNode: let {node.id} <- ' + expr = self.visit(node.expr, tabs + 1) + return f'{ans}\n{expr}' + + @visitor.when(FuncDeclarationNode) + def visit(self, node, tabs=0): + params = ', '.join([self.visit(x) for x in node.params]) + ans = '\t' * tabs + f'\\__FuncDeclarationNode: {node.id}({params}) : {self.get_type_name(node)} {{ }};' + body = self.visit(node.body, tabs + 1) + return f'{ans}\n{body}' + + @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(UnaryNode) + def visit(self, node, tabs=0): + ans = '\t' * tabs + f'\\__{node.__class__.__name__} ' + member = self.visit(node.member, tabs + 1) + return f'{ans}\n{member}' + + @visitor.when(AtomicNode) + def visit(self, node, tabs=0): + return '\t' * tabs + f'\\__{node.__class__.__name__}: {node.lex}' + + @visitor.when(BlockNode) + def visit(self,node,tabs=0): + ans = '\t' * tabs + f'\\__ BlockNode: {{ }}' + expr_list = [ self.visit(x,tabs+1) for x in node.expr_list ] + return ans + "\n" + '\n'.join(expr_list) + + @visitor.when(CallNode) + def visit(self, node, tabs=0): + obj = self.visit(node.obj, tabs + 1) + ans = '\t' * tabs + f'\\__CallNode: .{node.id}(, ..., )' + args = '\n'.join(self.visit(arg, tabs + 1) for arg in node.args) + return f'{ans}\n{obj}\n{args}' + + @visitor.when(InstantiateNode) + def visit(self, node, tabs=0): + return '\t' * tabs + f'\\__ InstantiateNode: new {node.lex}' + + @visitor.when(ParamNode) + def visit(self, node, tabs=0): + return '\t' * tabs + f"{node.id} : {self.get_type_name(node)}" + + @visitor.when(CaseNode) + def visit(self, node, tabs=0): + ans = '\t' * tabs + f"\\__CaseNode: case of esac" + expr = self.visit(node.expr, tabs + 1) + checks = "\n".join(self.visit(check, tabs + 1) for check in node.params) + return f"{ans}\n{expr}\n{checks}" + + @visitor.when(CheckNode) + def visit(self, node, tabs=0): + ans = "\t" * tabs + f"\\__CheckNode: {node.id}:{self.get_type_name(node)} => " + expr = self.visit(node.expr, tabs + 1) + return f"{ans}\n{expr}" + + @visitor.when(LetNode) + def visit(self, node, tabs=0): + ans = '\t' * tabs + f"\\__LetNode: let in " + decls = "\n".join(self.visit(decl, tabs + 1) for decl in node.params) + expr = self.visit(node.expr, tabs + 1) + return f"{ans}\n{decls}\n{expr}" + + @visitor.when(ConditionalNode) + def visit(self, node, tabs=0): + ans = '\t' * tabs + f"\\__ConditionalNode: if then else fi" + cond = self.visit(node.condition, tabs + 1) + then = self.visit(node.then_expr, tabs + 1) + elsex = self.visit(node.else_expr, tabs + 1) + return f"{ans}\n{cond}\n{then}\n{elsex}" + + @visitor.when(WhileNode) + def visit(self, node, tabs=0): + ans = '\t' * tabs + f"\\__WhileNode: while loop pool" + cond = self.visit(node.condition, tabs + 1) + body = self.visit(node.expr, tabs + 1) + return f"{ans}\n{cond}\n{body}" + + +class TypeCollector(object): + def __init__(self, errors=[], context=None): + self.context = context + self.errors = errors + + def add_semantic_error(self, error:SemanticError, row:int, column:int): + error.set_position(row, column) + self.errors.append(error) + + @visitor.on('node') + def visit(self, node): + pass + + @visitor.when(ProgramNode) + def visit(self, node): + if not self.context: + self.context = Context({ + 'Object':ObjectType, + 'String':StringType, + 'Bool':BoolType, + 'Int':IntType, + 'IO':IOType, + 'Void':VoidType, + 'Error':ErrorType, + }) + + for class_decl in node.declarations: + try: + self.context.create_type(class_decl.id) + except SemanticError as er: + self.add_semantic_error(er, class_decl.row, class_decl.column) + + for class_decl in node.declarations: + try: + curr_type = self.context.get_type(class_decl.id) + except SemanticError as er: + self.add_semantic_error(er, class_decl.row, class_decl.column) + if class_decl.parent: + try: + curr_type.set_parent(self.context.get_type(class_decl.parent)) + except SemanticError as er: + self.add_semantic_error(er, class_decl.row, class_decl.column) + + cycles = ut.any_cycles(ut.build_graph_dict(self.context.types)) + + for type1,type2 in cycles: + error = SemanticError(CIRCULAR_DEPENDENCY, type1.name, type2.name) + self.errors.append(error) + +class TypeBuilder: + def __init__(self, context, errors=[]): + self.context = context + self.current_type = None + self.errors = errors + + def add_semantic_error(self, error:SemanticError, row:int, column:int): + error.set_position(row, column) + self.errors.append(error) + + @visitor.on('node') + def visit(self, node): + pass + + @visitor.when(ProgramNode) + def visit(self, node): + for decl in node.declarations: + self.visit(decl) + + @visitor.when(ClassDeclarationNode) + def visit(self, node): + self.current_type = self.context.get_type(node.id) + self.current_type.class_node = node + for child in node.features: + self.visit(child) + + @visitor.when(AttrDeclarationNode) + def visit(self, node): + try: + node.type = self.context.get_type(node.type) + except SemanticError as er: + node.type = ErrorType() + self.add_semantic_error(er, node.row, node.column) + try: + attribute = self.current_type.define_attribute(node.id,node.type) + attribute.node = node + except SemanticError as er: + self.add_semantic_error(er, node.row, node.column) + + @visitor.when(FuncDeclarationNode) + def visit(self, node): + for x in node.params: + try: + x.type = self.context.get_type(x.type) + except SemanticError as er: + x.type = ErrorType() + self.add_semantic_error(er, node.row, node.column) + try: + node.type = self.context.get_type(node.type) + except SemanticError as er: + self.add_semantic_error(er, node.row, node.column) + try: + method = self.current_type.define_method(node.id,[x.id for x in node.params],[x.type for x in node.params],node.type) + method.node = node + except SemanticError as er: + self.add_semantic_error(er, node.row, node.column) + +class TypeChecker: + """ + Checks if the operations between types are correct. + Build the program scope. + Nodes are tagged with their type. + """ + def __init__(self, context:Context, errors=[]): + self.context = context + self.current_type = None + self.current_method = None + self.errors = errors + self.operator = Operator(context,errors) + + def complete_initial_types(self): + complete_types = ['Object','String','IO'] + for cmp_type in complete_types: + self.context.get_type(cmp_type).complete() + + def add_semantic_error(self, error:SemanticError, row:int, column:int): + error.set_position(row, column) + self.errors.append(error) + + @visitor.on('node') + def visit(self, node, scope): + pass + + @visitor.when(ProgramNode) + def visit(self, node, scope=None): + scope = scope + + for declaration in node.declarations: + self.visit(declaration, scope) + + if 'Main' in self.context.types: + MainType = self.context.get_type('Main') + try: + main_method = MainType.get_method('main',0, only_local = True) + except SemanticError as er: + self.errors.append(SemanticError(NO_ENTRY_POINT)) + else: + self.errors.append(SemanticError(NO_MAIN_TYPE)) + + self.complete_initial_types() + + return scope,self.operator + + @visitor.when(ClassDeclarationNode) + def visit(self, node, scope): + self.current_type = self.context.get_type(node.id) + node.scope = scope.create_child(self.current_type) + node.scope.define_variable('self',self.current_type) + node.features.sort(key=lambda x: x.__class__.__name__) # Attributes first, Stable sort + for feature in node.features: + self.visit(feature,node.scope) + + @visitor.when(AttrDeclarationNode) + def visit(self, node, scope): + if not node.expr: + if node.type.name != "AUTO_TYPE": + node.expr = node.type.default + node.expr.type = node.type # if type is void then is not assigned in the visit else the type is overridden + else: + return # No default expr can be assing at this moment + self.visit(node.expr,scope) + + if not node.expr.type.conforms_to(node.type,self.current_type): + self.add_semantic_error(TypeCoolError(INCOMPATIBLE_TYPES, node.expr.type.name, node.type.name),node.row,node.column) + + @visitor.when(FuncDeclarationNode) + def visit(self, node, scope): + methods = [] + current_method = self.current_type.get_method(node.id,len(node.params)) + if self.current_type.parent: + methods = self.current_type.parent.all_methods() + methods = [x for x,typex in methods if x.name == node.id and len(x.param_names) == len(node.params)] + methods = [x for x in methods if x != current_method] + if methods: + err = SemanticError(WRONG_SIGNATURE,methods[0].name,self.current_type.parent.name) + self.add_semantic_error(err, node.row, node.column) + + f_scope = scope.create_child() + node.scope = f_scope + + for param in node.params: + self.visit(param,f_scope) + + self.current_method = self.current_type.get_method(node.id,len(node.params)) + self.visit(node.body,f_scope) + + if not isinstance(node.type,VoidType) and not node.body.type.conforms_to(node.type,self.current_type): + err = TypeCoolError(INCOMPATIBLE_TYPES, node.body.type.name,node.type.name) + self.add_semantic_error(err, node.row, node.column) + + @visitor.when(ParamNode) + def visit(self, node, scope): + scope.define_variable(node.id,node.type) + + @visitor.when(VarDeclarationNode) + def visit(self, node, scope): + if node.id == 'self': + err = SemanticError(SELF_IS_READONLY) + self.add_semantic_error(err, node.row, node.column) + + try: + node.type = self.context.get_type(node.type) + except SemanticError as er: + node.type = ErrorType() + self.add_semantic_error(er, node.row, node.column) + if not node.expr: + if node.type.name != "AUTO_TYPE": + node.expr = node.type.default + node.expr.type = node.type # if type is void then is not assigned in the visit else the type is overridden + else: + if scope.is_local(node.id): + er = SemanticError(LOCAL_ALREADY_DEFINED, node.id, self.current_method.name) + self.add_semantic_error(er, node.row, node.column) + else: + scope.define_variable(node.id,node.type) + return # No default expr can be assing at this moment + + self.visit(node.expr,scope) + + if scope.is_local(node.id): + er = SemanticError(LOCAL_ALREADY_DEFINED, node.id, self.current_method.name) + self.add_semantic_error(er, node.row, node.column) + else: + scope.define_variable(node.id,node.type) + + if not node.expr.type.conforms_to(node.type,self.current_type): + er = TypeCoolError(INCOMPATIBLE_TYPES, node.expr.type.name, node.type.name) + self.add_semantic_error(er, node.row, node.column) + + @visitor.when(AssignNode) + def visit(self, node, scope): + if node.id == 'self': + er = SemanticError(SELF_IS_READONLY) + self.add_semantic_error(er, node.row, node.column) + + self.visit(node.expr,scope) + + if not scope.is_defined(node.id): + er = SemanticError(VARIABLE_NOT_DEFINED, node.id,self.current_method.name) + self.add_semantic_error(er, node.row, node.column) + node.type = node.expr.type + else: + var_info = scope.find_variable(node.id) + node.type = var_info.type + + if not node.expr.type.conforms_to(node.type,self.current_type): + er = TypeCoolError(INCOMPATIBLE_TYPES, node.expr.type.name, node.type.name) + self.add_semantic_error(er, node.row, node.column) + + @visitor.when(CallNode) + def visit(self, node, scope): + # obj,id,args + self.visit(node.obj,scope) + for arg in node.args: + self.visit(arg,scope) + + try: + if node.at: + node.at = self.context.get_type(node.at) + if not node.obj.type.conforms_to(node.at,self.current_type): + er = TypeCoolError(INCOMPATIBLE_TYPES, node.obj.type.name, node.at.name) + self.add_semantic_error(er, node.row, node.column) + method = node.at.get_method(node.id,len(node.args),self.current_type) + else: + method = node.obj.type.get_method(node.id,len(node.args),self.current_type) + not_conform = [(x,y.type) for x,y in zip(method.param_types,node.args) if not y.type.conforms_to(x,self.current_type)] + for x,y in not_conform: + er = TypeCoolError(INCOMPATIBLE_TYPES, y.name, x.name) + self.add_semantic_error(er, node.row, node.column) + node.type = method.return_type if method.return_type.name != "SELF_TYPE" else node.obj.type + + except SemanticError as er: + node.type = ErrorType() + self.add_semantic_error(er, node.row, node.column) + + @visitor.when(BlockNode) + def visit(self, node:BlockNode, scope): + + node.scope = scope.create_child() + + for expr in node.expr_list: + self.visit(expr,node.scope) + + node.type = node.expr_list[-1].type + + @visitor.when(ConditionalNode) + def visit(self, node:ConditionalNode, scope): + self.visit(node.condition,scope) + self.visit(node.then_expr,scope) + self.visit(node.else_expr,scope) + + if node.condition.type != self.context.get_type('Bool') and node.condition.type != ErrorType(): + er = TypeCoolError(NOT_BOOLEAN_CONDITION, node.condition.type.name) + self.add_semantic_error(er, node.row, node.column) + try: + node.type = node.get_return_type(self.current_type) + except InferError as er: + node.type = self.context.get_type("Error") + self.add_semantic_error(er, node.row, node.column) + + @visitor.when(LetNode) + def visit(self, node: LetNode, scope): + let_scope = scope.create_child() + curr_scope = let_scope + for var_node in node.params: + attr_scope = curr_scope.create_child() + self.visit(var_node,attr_scope) + curr_scope = attr_scope + body_scope = curr_scope.create_child() + self.visit(node.expr,body_scope) + node.type = node.expr.type + + @visitor.when(WhileNode) + def visit(self, node:WhileNode, scope): + node.type = self.context.get_type('Object') + self.visit(node.condition,scope) + if node.condition.type != self.context.get_type('Bool'): + er = TypeCoolError(NOT_BOOLEAN_CONDITION, node.condition.type.name) + self.add_semantic_error(er, node.row, node.column) + self.visit(node.expr,scope) + + @visitor.when(CheckNode) + def visit(self, node:CheckNode, scope): + if node.id == 'self': + er = SemanticError(SELF_IS_READONLY) + self.add_semantic_error(er, node.row, node.column) + + node.type = self.context.get_type(node.type) + node.scope = scope.create_child() + node.scope.define_variable(node.id,node.type) + + self.visit(node.expr,node.scope) + + # if not node.expr.type.conforms_to(node.type,self.current_type): + # self.errors.append(INCOMPATIBLE_TYPES.format(node.expr.type.name,node.type.name) + f' Line:{node.row} Column:{node.column}') + + @visitor.when(CaseNode) + def visit(self, node:CaseNode, scope): + self.visit(node.expr,scope) + + types = [] + for i,param in enumerate(node.params): + self.visit(param,scope) + # if not (node.expr.type.conforms_to(param.type,self.current_type) or param.type.conforms_to(node.expr.type,self.current_type)): + # self.errors.append(f"Incompatible types {param.type.name} with {node.expr.type.name}" + f' Line:{node.row} Column:{node.column}') + if any(x for x in types if x[1] == param.type): + er = TypeCoolError(CASE_TYPES_REPEATED, param.expr.type.name) + self.add_semantic_error(er, param.row, param.column) + types.append((i,param.expr.type)) + + static_type = types[0][1] + for i,join in types[1:]: + static_type = static_type.join(join,self.current_type) + node.type = static_type + + @visitor.when(UnaryNode) + def visit(self, node, scope): + self.visit(node.member,scope) + + if not self.operator.operation_defined(node,node.member.type): + node.type = ErrorType() + er = TypeCoolError(INVALID_UNARY_OPERATION, self.operator.get_operator(node), node.member.type.name) + self.add_semantic_error(er, node.row, node.column) + else: + node.type = self.operator.type_of_operation(node,node.member.type) + + @visitor.when(BinaryNode) + def visit(self, node, scope): + self.visit(node.left,scope) + self.visit(node.right,scope) + + if not self.operator.operation_defined(node,node.left.type,node.right.type): + node.type = ErrorType() + er = TypeCoolError(INVALID_BINARY_OPERATION,self.operator.get_operator(node), node.left.type.name,node.right.type.name) + self.add_semantic_error(er, node.row, node.column) + else: + node.type = self.operator.type_of_operation(node,node.left.type,node.right.type) + + @visitor.when(ConstantNumNode) + def visit(self, node, scope): + node.type = self.context.get_type("Int") + + @visitor.when(BoolNode) + def visit(self, node, scope): + node.type = self.context.get_type("Bool") + + @visitor.when(StringNode) + def visit(self, node:StringNode, scope): + node.type = self.context.get_type("String") + if len(node.lex) > 1024: + er = LexerCoolError(STRING_TOO_LONG, len(node.lex)) + self.add_semantic_error(er, node.row, node.column) + + @visitor.when(VoidNode) + def visit(self, node, scope): + pass + + @visitor.when(VariableNode) + def visit(self, node, scope): + if scope.is_defined(node.lex): + var = scope.find_variable(node.lex) + node.type = var.type + else: + node.type = ErrorType() + er = SemanticError(VARIABLE_NOT_DEFINED, node.lex, self.current_method.name) + self.add_semantic_error(er, node.row, node.column) + + @visitor.when(InstantiateNode) + def visit(self, node, scope): + try: + node.type = self.context.get_type(node.lex) + + except SemanticError as er: + node.type = ErrorType() + self.add_semantic_error(er, node.row, node.column) + +class AutoResolver: + + def __init__(self, context:Context, errors=[]): + self.context = context + self.errors = errors + + def change_auto_to_concrete_type(self, node, node_type_name="type", less_concrete=False): + typex = getattr(node, node_type_name) + if typex.name == "AUTO_TYPE": + try: + if less_concrete: + setattr(node, node_type_name, typex.get_lower(self.current_type)) + else: + setattr(node, node_type_name, typex.get_higher(self.current_type)) + except InferError as er: + self.add_semantic_error(er, node.row, node.column) + + @visitor.on('node') + def visit(self, node): + pass + + @visitor.when(ProgramNode) + def visit(self, node): + for declaration in node.declarations: + self.visit(declaration) + + @visitor.when(ClassDeclarationNode) + def visit(self, node): + self.current_type = self.context.get_type(node.id) + for feature in node.features: + self.visit(feature) + + @visitor.when(AttrDeclarationNode) + def visit(self, node): + self.change_auto_to_concrete_type(node) + if not node.expr: + node.expr = node.type.default + node.expr.type = node.type + self.visit(node.expr) + + @visitor.when(FuncDeclarationNode) + def visit(self, node): + self.change_auto_to_concrete_type(node) + for param in node.params: + self.visit(param) + + self.visit(node.body) + + @visitor.when(ParamNode) + def visit(self, node): + self.change_auto_to_concrete_type(node, less_concrete=True) + + @visitor.when(VarDeclarationNode) + def visit(self, node): + self.change_auto_to_concrete_type(node) + if not node.expr: + node.expr = node.type.default + node.expr.type = node.type + self.visit(node.expr) + + @visitor.when(AssignNode) + def visit(self, node): + self.visit(node.expr) + + @visitor.when(CallNode) + def visit(self, node): + self.visit(node.obj) + + for arg in node.args: + self.visit(arg) + + if node.at: + pass + + @visitor.when(BlockNode) + def visit(self, node:BlockNode): + for expr in node.expr_list: + self.visit(expr) + + @visitor.when(ConditionalNode) + def visit(self, node:ConditionalNode): + self.visit(node.condition) + self.visit(node.then_expr) + self.visit(node.else_expr) + + @visitor.when(LetNode) + def visit(self, node: LetNode): + for var_node in node.params: + self.visit(var_node) + self.visit(node.expr) + + @visitor.when(WhileNode) + def visit(self, node:WhileNode): + self.visit(node.condition) + self.visit(node.expr) + + @visitor.when(CheckNode) + def visit(self, node:CheckNode): + self.visit(node.expr) + + @visitor.when(CaseNode) + def visit(self, node:CaseNode): + self.visit(node.expr) + + for i,param in enumerate(node.params): + self.visit(param) + + @visitor.when(UnaryNode) + def visit(self, node): + self.visit(node.member) + + @visitor.when(BinaryNode) + def visit(self, node): + self.visit(node.left) + self.visit(node.right) + + @visitor.when(ConstantNumNode) + def visit(self, node): + pass + + @visitor.when(BoolNode) + def visit(self, node): + pass + + @visitor.when(StringNode) + def visit(self, node:StringNode): + pass + + @visitor.when(VoidNode) + def visit(self, node): + pass + + @visitor.when(VariableNode) + def visit(self, node): + pass + + @visitor.when(InstantiateNode) + def visit(self, node): + pass + + +class RunVisitor: + + def __init__(self, context:Context, scope, operator, errors=[]): + self.context = context + self.scope = scope + self.errors = errors + self.operator = operator + + @visitor.on('node') + def visit(self, node, scope): + pass + + @visitor.when(ProgramNode) + def visit(self, node:ProgramNode, scope=None): + for decl in node.declarations: + if decl.id == 'Main': + for method in [x.node for x,y in self.context.get_type('Main').all_methods()]: + if method.id == 'main': + main_type = self.context.get_type(decl.id) + main_instance = ClassInstance(main_type,self.context,self.operator,self.errors) + # method_scope = method.scope.copy() + # method_scope.set_variable_value('self',main_instance) + # method_scope.set_parent(main_instance.scope) + try: + value = self.visit(method.body,main_instance.scope) + except RunError as err: + self.errors.append(err) + return None + return value + + @visitor.when(VarDeclarationNode) + def visit(self, node:VarDeclarationNode, scope): + current_value = self.visit(node.expr,scope) + scope.define_variable(node.id,node.type) + scope.set_variable_value(node.id,current_value) + return current_value + + @visitor.when(AssignNode) + def visit(self, node:AssignNode, scope): + value = self.visit(node.expr,scope) + scope.set_variable_value(node.id,value) + return value + + @visitor.when(CallNode) + def visit(self, node:CallNode, scope): + value = self.visit(node.obj,scope) + if value.type.name == "Void": + raise RunError(DISPATCH_VOID + f" Line:{node.row} Column:{node.column}") + args = [self.visit(x,scope) for x in node.args] + if node.at: + method = node.at.get_method(node.id,len(node.args)) + else: + method = value.type.get_method(node.id,len(node.args)) + func_scope = method.node.scope.copy() + func_scope.set_parent(value.scope) + for arg,name in zip(args,method.param_names): + func_scope.set_variable_value(name,arg) + # func_scope.set_variable_value('self',value) + value = self.visit(method.node.body,func_scope) + return value + + @visitor.when(SpecialNode) + def visit(self, node:SpecialNode, scope): + try: + return node.func(scope,self.context,self.operator,self.errors) + except RunError as cexc: + raise RunError(cexc.text + f" Line:{node.row} Column:{node.column}") + + @visitor.when(BlockNode) + def visit(self, node:BlockNode, scope): + body_scope = scope.create_child() + values = [self.visit(x,body_scope) for x in node.expr_list] + return values[-1] + + @visitor.when(ConditionalNode) + def visit(self, node:ConditionalNode, scope): + condition = self.visit(node.condition,scope) + if condition.value is True: + return self.visit(node.then_expr,scope) + if condition.value is False: + return self.visit(node.else_expr,scope) + raise RunError(NO_BOOL_CONDITION + f" Line:{node.row} Column:{node.column}") # This connot happend BUT you never know + + @visitor.when(LetNode) + def visit(self, node:LetNode, scope): + let_scope = scope.create_child() + for var_decl in node.params: + value = self.visit(var_decl.expr,let_scope) + if not let_scope.is_local(var_decl.id): + let_scope.define_variable(var_decl.id,var_decl.type) + let_scope.set_variable_value(var_decl.id,value) + value = self.visit(node.expr,let_scope) + return value + + @visitor.when(WhileNode) + def visit(self, node:WhileNode, scope): + while self.visit(node.condition,scope).value == True: + self.visit(node.expr,scope) + typex = self.context.get_type('Void') + return ClassInstance(typex,self.context,self.operator,self.errors,value=None) + + @visitor.when(CheckNode) + def visit(self, node:CheckNode, scope,**kwargs): + node_scope = scope.create_child() + value = kwargs['value'] + node_scope.define_variable(node.id,value.type) + node_scope.set_variable_value(node.id,value) + return self.visit(node.expr,node_scope) + + @visitor.when(CaseNode) + def visit(self, node:CaseNode, scope): + value = self.visit(node.expr,scope) + + if value.type.name == "Void": + raise RunError(VOID_TYPE_CONFORMS + f" Line: {node.row} Column: {node.column}") + + try: + greaters = (x for x in [(i,x.type) for i,x in enumerate(node.params)] if value.type.conforms_to(x[1],value.type)) + least_pos,least = next(greaters) + for i,other in greaters: + least_pos,least = (least_pos,least) if least.conforms_to(other,least) else (i,other) + except StopIteration: + raise RunError(CASE_NO_BRANCH_SELECTED.format(value.type.name) + f" Line: {node.row} Column: {node.column}") + + return self.visit(node.params[least_pos],scope,value=value) + + @visitor.when(UnaryNode) + def visit(self, node, scope): + operator = self.operator.get_operator(node) + value = None + member_value = self.visit(node.member,scope) + try: + value = self.operator.operate(operator,member_value) + except RunError as err: + raise RunError(err.text + f' Line:{node.row} Column:{node.column}') + return value + + @visitor.when(BinaryNode) + def visit(self, node, scope): + operator = self.operator.get_operator(node) + value = None + left_value = self.visit(node.left,scope) + right_value = self.visit(node.right,scope) + try: + value = self.operator.operate(operator,left_value,right_value) + except RunError as err: + raise RunError(err.text + f' Line:{node.row} Column:{node.column}') + return value + + @visitor.when(ConstantNumNode) + def visit(self, node:ConstantNumNode, scope): + typex = self.context.get_type('Int') + return ClassInstance(typex,self.context,self.operator,self.errors,value=int(node.lex)) + + @visitor.when(BoolNode) + def visit(self, node, scope): + typex = self.context.get_type("Bool") + if node.lex[0]=='t': # true + return ClassInstance(typex,self.context,self.operator,self.errors,value=True) + return ClassInstance(typex,self.context,self.operator,self.errors,value=False) + + @visitor.when(StringNode) + def visit(self, node, scope): + typex = self.context.get_type('String') + string_without_delimitators = node.lex[1:len(node.lex)-1] + return ClassInstance(typex,self.context,self.operator,self.errors,value=string_without_delimitators) + + @visitor.when(VoidNode) + def visit(self, node, scope): + typex = self.context.get_type('Void') + return ClassInstance(typex,self.context,self.operator,self.errors,value=None) + + @visitor.when(VariableNode) + def visit(self, node:VariableNode, scope): + try: + value = scope.get_variable_value(node.lex) + except TypeError as exc: # Variable not assign yet searching in Attr + try: + attr = [x.type for x in scope.locals if x.name == 'self'][0].get_attribute(node.lex) + value = self.visit(attr.node.expr, scope) + except SemanticError as er: # Attr not found + er.set_position(node.row, node.column) + self.errors.append(er) + return value + + @visitor.when(InstantiateNode) + def visit(self, node:InstantiateNode, scope): + typex = self.context.get_type(node.lex,scope.find_variable('self').type) + return ClassInstance(typex,self.context,self.operator,self.errors) \ No newline at end of file diff --git a/src/cool2/cool_programs/program1.cl b/src/cool2/cool_programs/program1.cl index 32ea12c21..61881f022 100644 --- a/src/cool2/cool_programs/program1.cl +++ b/src/cool2/cool_programs/program1.cl @@ -1,35 +1,35 @@ -class B {} - -class Main { - main() : AUTO_TYPE - { - { - a:Int <- 20; - b:Int <- 30; - c:B <- new B; - d:String <- "hello"; - e:String <- "olleh"; - f:Bool <- false; - g:Bool <- true; - - r1:AUTO_TYPE <- a + b; - r2:AUTO_TYPE <- a + c; - r3:AUTO_TYPE <- a + d; - r4:AUTO_TYPE <- e + d; - r5:AUTO_TYPE <- ~a; - r6:AUTO_TYPE <- ~c; - r7:AUTO_TYPE <- ~d; - r8:AUTO_TYPE <- not a; - r9:AUTO_TYPE <- not c; - r10:AUTO_TYPE <- not d; - r11:AUTO_TYPE <- a = b; - r12:AUTO_TYPE <- c = b; - r13:AUTO_TYPE <- d = b; - r14:AUTO_TYPE <- d = e; - r15:AUTO_TYPE <- f + a; - r16:AUTO_TYPE <- f + d; - r17:AUTO_TYPE <- ~f; - r18:AUTO_TYPE <- not f; - } - }; +class B {} + +class Main { + main() : AUTO_TYPE + { + { + a:Int <- 20; + b:Int <- 30; + c:B <- new B; + d:String <- "hello"; + e:String <- "olleh"; + f:Bool <- false; + g:Bool <- true; + + r1:AUTO_TYPE <- a + b; + r2:AUTO_TYPE <- a + c; + r3:AUTO_TYPE <- a + d; + r4:AUTO_TYPE <- e + d; + r5:AUTO_TYPE <- ~a; + r6:AUTO_TYPE <- ~c; + r7:AUTO_TYPE <- ~d; + r8:AUTO_TYPE <- not a; + r9:AUTO_TYPE <- not c; + r10:AUTO_TYPE <- not d; + r11:AUTO_TYPE <- a = b; + r12:AUTO_TYPE <- c = b; + r13:AUTO_TYPE <- d = b; + r14:AUTO_TYPE <- d = e; + r15:AUTO_TYPE <- f + a; + r16:AUTO_TYPE <- f + d; + r17:AUTO_TYPE <- ~f; + r18:AUTO_TYPE <- not f; + } + }; } \ No newline at end of file diff --git a/src/cool2/cool_programs/program1.cl.infer.cl b/src/cool2/cool_programs/program1.cl.infer.cl index 3f68abcc8..defb588a2 100644 --- a/src/cool2/cool_programs/program1.cl.infer.cl +++ b/src/cool2/cool_programs/program1.cl.infer.cl @@ -1,65 +1,65 @@ -class B -{ -} - - -class Main -{ - main(): Bool - { - { - a:Int <- - 20; - b:Int <- - 30; - c:B <- - new B; - d:String <- - "hello"; - e:String <- - "olleh"; - f:Bool <- - false; - g:Bool <- - true; - r1:Int <- - (a + b); - r2:Object <- - (a + c); - r3:Object <- - (a + d); - r4:Object <- - (e + d); - r5:Int <- - ~ a; - r6:Object <- - ~ c; - r7:Object <- - ~ d; - r8:Object <- - not a; - r9:Object <- - not c; - r10:Object <- - not d; - r11:Bool <- - (a = b); - r12:Bool <- - (c = b); - r13:Bool <- - (d = b); - r14:Bool <- - (d = e); - r15:Object <- - (f + a); - r16:Object <- - (f + d); - r17:Object <- - ~ f; - r18:Bool <- - not f; - } - }; -} - - +class B +{ +} + + +class Main +{ + main(): Bool + { + { + a:Int <- + 20; + b:Int <- + 30; + c:B <- + new B; + d:String <- + "hello"; + e:String <- + "olleh"; + f:Bool <- + false; + g:Bool <- + true; + r1:Int <- + (a + b); + r2:Object <- + (a + c); + r3:Object <- + (a + d); + r4:Object <- + (e + d); + r5:Int <- + ~ a; + r6:Object <- + ~ c; + r7:Object <- + ~ d; + r8:Object <- + not a; + r9:Object <- + not c; + r10:Object <- + not d; + r11:Bool <- + (a = b); + r12:Bool <- + (c = b); + r13:Bool <- + (d = b); + r14:Bool <- + (d = e); + r15:Object <- + (f + a); + r16:Object <- + (f + d); + r17:Object <- + ~ f; + r18:Bool <- + not f; + } + }; +} + + diff --git a/src/cool2/lib/grammar/grammar_fixer.py b/src/cool2/lib/grammar/grammar_fixer.py index 39e250a2e..c04dd5452 100644 --- a/src/cool2/lib/grammar/grammar_fixer.py +++ b/src/cool2/lib/grammar/grammar_fixer.py @@ -1,401 +1,401 @@ -from cmp.pycompiler import (EOF, Epsilon, Grammar, NonTerminal, Production, - Sentence, SentenceList, Symbol, Terminal, SentenceFromIter) -from lib.utils.trie import TrieNode, Trie -from lib.utils.algorithms import permutation # FIX - -# GRAMMAR FIXER UTILS -def change_grammar_from_productions(gramm:Grammar,new_productions): - """ - Empty all non terminal and grammar productions\n - and add all productions in new_productions to gramm - """ - for x in gramm.nonTerminals: - x.productions = [] - - gramm.Productions = [] - - for x in new_productions: - gramm.Add_Production(x) - - return gramm - -def generate_name(G:Grammar,idx,prefix = 'N'): - new_key = prefix+'_' - cur_idx = idx - while f'{new_key}{cur_idx}' in G.symbDict.keys(): - cur_idx+=1 - return f'{new_key}{cur_idx}',cur_idx+1 - -def remove_unnecessary_symbols(gramm:Grammar,unnecesary): - new_prod = [] - - for prod in gramm.Productions: - if not prod.Left in unnecesary: - r_unne = [x for x in prod.Right if x in unnecesary] - if not r_unne: - new_prod.append(prod) - - for x in unnecesary: - try: - gramm.nonTerminals.remove(x) - except ValueError: - gramm.terminals.remove(x) - gramm.symbDict.__delitem__(x.Name) - - change_grammar_from_productions(gramm,new_prod) - -def build_reach(gramm:Grammar, symbol): - """ - Return a set with the symbols that reach symbol - """ - reach = set() - reach.add(symbol) - last_len = 0 - while last_len != len(reach): - last_len = len(reach) - for x in gramm.Productions: - if any([y for y in x.Right if y in reach]): - reach.add(x.Left) - return reach - -def fix_grammar(G,errors): - """ - Check if G is LL1 and fix it if possible\n - Removes the immediate left recursion and the common prefixes\n - return the new grammar fixed if possible\n - else return None - """ - if not G: - return None - # Your code goes here - new_errors = [] - - G = fix_common_prefix(G) - G = fix_left_recursion(G,new_errors) - G = fix_unnecesary_prod(G) - - errors.extend(new_errors) - if new_errors: - return None - return G - - -# LEFT RECURSION -# Esta implementado solamente la inmediata una sola vez Probar algo como E -> EE o algo por el estilo -def fix_left_recursion(grammar:Grammar, errors): - ''' - Fix immediate left recursion of grammar\n - return a fixed copy of grammar - ''' - new_grammar = grammar.copy() - new_grammar.Productions = [] - - for n_ter in grammar.nonTerminals: - for prod in n_ter.productions: - if not prod.Right.IsEpsilon and prod.Right[0] == prod.Left: - fix_non_terminal_left_recursion(n_ter,new_grammar, errors) - break - else: - new_grammar.Productions.extend(n_ter.productions) - - return new_grammar - -def fix_non_terminal_left_recursion(non_terminal:NonTerminal, grammar, errors): - ''' - Fix immediate left recursion non_terminal in grammar\n - ''' - new_name,idx = generate_name(grammar,0,non_terminal.Name) - new_non_terminal = grammar.NonTerminal(new_name) - - left_prod = non_terminal.productions - non_terminal.productions = [] - new_prod_new_n_ter = set() - new_prod_old_n_ter = set() - - for prod in left_prod: - if not prod.Right.IsEpsilon and prod.Right[0] == non_terminal: - if len(prod.Right) > 1: - sentence = [x for x in prod.Right[1:]] - sentence.append(new_non_terminal) - new_prod_new_n_ter.add(Production(new_non_terminal,SentenceFromIter(sentence))) - else: - sentence = [x for x in prod.Right] - sentence.append(new_non_terminal) - new_prod_new_n_ter.add(Production(new_non_terminal,grammar.Epsilon)) - new_prod_old_n_ter.add(Production(non_terminal,SentenceFromIter(sentence))) - - for prod in new_prod_new_n_ter: - grammar.Add_Production(prod) - - if not new_prod_old_n_ter: - errors.append(f'All productions of {non_terminal} begins with {non_terminal}, no string can be parsed by a left parse') - - for prod in new_prod_old_n_ter: - grammar.Add_Production(prod) - - -# CLEANING GRAMMAR -def fix_unnecesary_prod(grammar:Grammar): - grammar = fix_e_productions(grammar) - grammar = fix_useless_symbols(grammar) - return grammar - - -# COMMON PREFIX -def fix_common_prefix(grammar:Grammar): - """ - returns a copy of grammar without common prefixes - """ - G = grammar.copy() - G.Productions = [] - - for non_terminal in grammar.nonTerminals: - trie = Trie() - epsilon = False - for x in non_terminal.productions: - if not x.Right.IsEpsilon: - trie.add(x.Right) - else: - epsilon = True - non_terminal.productions = [] - if epsilon: - G.Add_Production(Production(x.Left,G.Epsilon)) - for node in trie.top.sons: - execute_node(trie.top.sons[node],non_terminal,[],G,0) - return G - -def execute_node(node:TrieNode,left,right:list,grammar:Grammar,idx): - """ - Fills productions with the new grammar productions\n - left: left part of the production\n - right: right part of the production in a list\n - productions: all the productions of the grammar without common prefixes\n - idx: index of the generated name - """ - right.append(node.value) - if len(node.sons) > 1 or (len(node.sons) == 1 and node.terminal): - name,idx = generate_name(grammar,idx,left.Name) - new_prod = grammar.NonTerminal(name) - right.append(new_prod) - grammar.Add_Production(Production(left,SentenceFromIter(right))) - left = new_prod - if node.terminal: - grammar.Add_Production(Production(left,grammar.Epsilon)) - for x in node.sons: - right = [] - execute_node(node.sons[x],left,right,grammar,idx) - elif len(node.sons) == 1: - for key in node.sons: - execute_node(node.sons[key],left,right,grammar,idx) - break - else: - grammar.Add_Production(Production(left,SentenceFromIter(right))) - - -# e-PRODUCTIONS -def remove_e_productions(G): - """ - returns a grammar with all epsilon productions eliminated\n - and a set with the Non Terminal that reached epsilon productions\n - return G,n_t_epsilon - """ - - n_t_epsilon = set() - new_productions = [] - # Removes all epsilon transitions from the non terminals - for x in G.Productions: - if x.Right.IsEpsilon: - n_t_epsilon.add(x.Left) - x.Left.productions.remove(x) - else: - new_productions.append(x) - - change = 0 - while change != len(n_t_epsilon): - change = len(n_t_epsilon) - for prod in G.Productions: - if len([x for x in prod.Right if x in n_t_epsilon]) == len(prod.Right): # All x in right is on n_t_epsilon - n_t_epsilon.add(prod.Left) - - G.Productions = new_productions - - return G,n_t_epsilon - -def add_new_productions(prod,new_productions,n_t_epsilon): - """ - Add to new_productions the corresponding productions from\n - permutate prod with the non_terminals in n_t_epsilon \n - Ex: A->BCB,n_t_eps={B}\n - add to new_productions {A->BCB, A->CB, A->BC, A->C} - """ - had_epsilon = [False]*len(prod.Right) - cant_epsilon = 0 - - for i,x in enumerate(prod.Right): - if x in n_t_epsilon: - had_epsilon[i] = True - cant_epsilon += 1 - - for perm in permutation(cant_epsilon,2): - new_prod_right = [] - perm_idx = 0 - for i,x in enumerate(had_epsilon): - if not x: - new_prod_right.append(prod.Right[i]) - elif perm[perm_idx]: - new_prod_right.append(prod.Right[i]) - perm_idx = perm_idx+1 if x else perm_idx - if new_prod_right: - new_productions.add(Production(prod.Left,SentenceFromIter(new_prod_right))) - if cant_epsilon == 0: - new_productions.add(Production(prod.Left,prod.Right)) - -def fix_e_productions(gramm:Grammar): - """ - Returns a grammar without epsilon transitions.\n - if gramm recognize epsilon then an augmented grammar\n - is return with an epsilon transition on the start symbol - """ - G = gramm - - G,n_t_epsilon = remove_e_productions(G) - - if G.startSymbol in n_t_epsilon: - G = G.AugmentedGrammar(force=True) - G.Add_Production(Production(G.startSymbol,G.Epsilon)) - else: - G = G.copy() - - new_productions = set() - - for prod in gramm.Productions: - add_new_productions(prod,new_productions,n_t_epsilon) - - for x in gramm.nonTerminals: - x.productions = [] - - for prod in new_productions: - G.Add_Production(prod) - - return G - - -# NON DERIVE TERMINAL -def update_derivation(production,derivation,left_derivation = True): - symbol,sentence = production.Left,production.Right - derive = [] - derive.append(production) - if not symbol in derivation: - if sentence.IsEpsilon: - derive.extend(derivation[symbol.Grammar.Epsilon]) - elif not left_derivation: - sentence = [ sentence[i] for i in range(len(sentence)-1,-1,-1) ] # sentence.reverse() - for x in sentence: - derive.extend(derivation[x]) - derivation[symbol] = derive - -def fix_non_derive_terminal(gramm:Grammar,return_derivations = False,left_derivation = True): - """ - Remove from gramm the non terminals A that dont satisfy:\n - A->*w where w in {G.T}* - return grammar - return grammar,derivation - """ - gramm = gramm.copy() - - derivation = { x:[Production(x,Sentence(x)),] for x in gramm.terminals } - derivation[gramm.Epsilon] = [Production(gramm.Epsilon,Sentence(gramm.Epsilon)),] - derive_something = set(gramm.terminals) - productions = set(gramm.Productions) - - change = -1 - while change != len(derive_something): - change = len(derive_something) - to_remove = [] - for x in productions: - if not any([y for y in x.Right if not y in derive_something]): # if y ->* w with w in T* - derive_something.add(x.Left) - update_derivation(x,derivation,left_derivation) - to_remove.append(x) - for x in to_remove: productions.remove(x) - - remove_unnecessary_symbols(gramm,[x for x in gramm.nonTerminals if x not in derive_something]) - - if return_derivations: - return gramm,derivation - return gramm - - -# UNREACHABLE SYMBOLS -def fix_unreachable_symbols(gramm:Grammar): - gramm = gramm.copy() - - pending = [gramm.startSymbol] - reachable = set(pending) - while pending: - symb = pending.pop() - for prod in symb.productions: - for r in prod.Right: - if not r in reachable: - reachable.add(r) - if isinstance(r,NonTerminal): - pending.append(r) - - remove_unnecessary_symbols(gramm,[x for x in gramm.nonTerminals + gramm.terminals if x not in reachable]) - - return gramm - - -# USELESS SYMBOLS -def fix_useless_symbols(gramm:Grammar): - """ - fix the non derive on terminal, non_terminals\n - and the unreachable symbols from the start symbol - """ - gramm = fix_unit_productions(gramm) - - gramm = fix_non_derive_terminal(gramm) - - gramm = fix_unreachable_symbols(gramm) - - return gramm - - -# UNIT PRODUCTIONS -def get_unit_tuples(unit_productions): - change = -1 - tuples = set([(x.Left,x.Right[0]) for x in unit_productions]) - while change != len(tuples): - change = len(tuples) - to_add = set() - for t in tuples: - for q in [x for x in tuples if t[1] == x[0]]: - to_add.add((t[0],q[1])) - tuples.update(to_add) - return tuples - - -def fix_unit_productions(gramm:Grammar): - """ - returns an equivalent grammar without productions of the form:\n - A -> B - """ - gramm = gramm.copy() - - unit_productions = {x for x in gramm.Productions if len(x.Right) == 1 and x.Right[0].IsNonTerminal} - - new_productions = set() - - for x in gramm.Productions: - if not x in unit_productions: - new_productions.add(x) - - pending = get_unit_tuples(unit_productions) - - while pending: - l,r = pending.pop() - for prod in r.productions: - if not prod in unit_productions: - new_productions.add(Production(l,prod.Right)) - - return change_grammar_from_productions(gramm,new_productions) +from cmp.pycompiler import (EOF, Epsilon, Grammar, NonTerminal, Production, + Sentence, SentenceList, Symbol, Terminal, SentenceFromIter) +from lib.utils.trie import TrieNode, Trie +from lib.utils.algorithms import permutation # FIX + +# GRAMMAR FIXER UTILS +def change_grammar_from_productions(gramm:Grammar,new_productions): + """ + Empty all non terminal and grammar productions\n + and add all productions in new_productions to gramm + """ + for x in gramm.nonTerminals: + x.productions = [] + + gramm.Productions = [] + + for x in new_productions: + gramm.Add_Production(x) + + return gramm + +def generate_name(G:Grammar,idx,prefix = 'N'): + new_key = prefix+'_' + cur_idx = idx + while f'{new_key}{cur_idx}' in G.symbDict.keys(): + cur_idx+=1 + return f'{new_key}{cur_idx}',cur_idx+1 + +def remove_unnecessary_symbols(gramm:Grammar,unnecesary): + new_prod = [] + + for prod in gramm.Productions: + if not prod.Left in unnecesary: + r_unne = [x for x in prod.Right if x in unnecesary] + if not r_unne: + new_prod.append(prod) + + for x in unnecesary: + try: + gramm.nonTerminals.remove(x) + except ValueError: + gramm.terminals.remove(x) + gramm.symbDict.__delitem__(x.Name) + + change_grammar_from_productions(gramm,new_prod) + +def build_reach(gramm:Grammar, symbol): + """ + Return a set with the symbols that reach symbol + """ + reach = set() + reach.add(symbol) + last_len = 0 + while last_len != len(reach): + last_len = len(reach) + for x in gramm.Productions: + if any([y for y in x.Right if y in reach]): + reach.add(x.Left) + return reach + +def fix_grammar(G,errors): + """ + Check if G is LL1 and fix it if possible\n + Removes the immediate left recursion and the common prefixes\n + return the new grammar fixed if possible\n + else return None + """ + if not G: + return None + # Your code goes here + new_errors = [] + + G = fix_common_prefix(G) + G = fix_left_recursion(G,new_errors) + G = fix_unnecesary_prod(G) + + errors.extend(new_errors) + if new_errors: + return None + return G + + +# LEFT RECURSION +# Esta implementado solamente la inmediata una sola vez Probar algo como E -> EE o algo por el estilo +def fix_left_recursion(grammar:Grammar, errors): + ''' + Fix immediate left recursion of grammar\n + return a fixed copy of grammar + ''' + new_grammar = grammar.copy() + new_grammar.Productions = [] + + for n_ter in grammar.nonTerminals: + for prod in n_ter.productions: + if not prod.Right.IsEpsilon and prod.Right[0] == prod.Left: + fix_non_terminal_left_recursion(n_ter,new_grammar, errors) + break + else: + new_grammar.Productions.extend(n_ter.productions) + + return new_grammar + +def fix_non_terminal_left_recursion(non_terminal:NonTerminal, grammar, errors): + ''' + Fix immediate left recursion non_terminal in grammar\n + ''' + new_name,idx = generate_name(grammar,0,non_terminal.Name) + new_non_terminal = grammar.NonTerminal(new_name) + + left_prod = non_terminal.productions + non_terminal.productions = [] + new_prod_new_n_ter = set() + new_prod_old_n_ter = set() + + for prod in left_prod: + if not prod.Right.IsEpsilon and prod.Right[0] == non_terminal: + if len(prod.Right) > 1: + sentence = [x for x in prod.Right[1:]] + sentence.append(new_non_terminal) + new_prod_new_n_ter.add(Production(new_non_terminal,SentenceFromIter(sentence))) + else: + sentence = [x for x in prod.Right] + sentence.append(new_non_terminal) + new_prod_new_n_ter.add(Production(new_non_terminal,grammar.Epsilon)) + new_prod_old_n_ter.add(Production(non_terminal,SentenceFromIter(sentence))) + + for prod in new_prod_new_n_ter: + grammar.Add_Production(prod) + + if not new_prod_old_n_ter: + errors.append(f'All productions of {non_terminal} begins with {non_terminal}, no string can be parsed by a left parse') + + for prod in new_prod_old_n_ter: + grammar.Add_Production(prod) + + +# CLEANING GRAMMAR +def fix_unnecesary_prod(grammar:Grammar): + grammar = fix_e_productions(grammar) + grammar = fix_useless_symbols(grammar) + return grammar + + +# COMMON PREFIX +def fix_common_prefix(grammar:Grammar): + """ + returns a copy of grammar without common prefixes + """ + G = grammar.copy() + G.Productions = [] + + for non_terminal in grammar.nonTerminals: + trie = Trie() + epsilon = False + for x in non_terminal.productions: + if not x.Right.IsEpsilon: + trie.add(x.Right) + else: + epsilon = True + non_terminal.productions = [] + if epsilon: + G.Add_Production(Production(x.Left,G.Epsilon)) + for node in trie.top.sons: + execute_node(trie.top.sons[node],non_terminal,[],G,0) + return G + +def execute_node(node:TrieNode,left,right:list,grammar:Grammar,idx): + """ + Fills productions with the new grammar productions\n + left: left part of the production\n + right: right part of the production in a list\n + productions: all the productions of the grammar without common prefixes\n + idx: index of the generated name + """ + right.append(node.value) + if len(node.sons) > 1 or (len(node.sons) == 1 and node.terminal): + name,idx = generate_name(grammar,idx,left.Name) + new_prod = grammar.NonTerminal(name) + right.append(new_prod) + grammar.Add_Production(Production(left,SentenceFromIter(right))) + left = new_prod + if node.terminal: + grammar.Add_Production(Production(left,grammar.Epsilon)) + for x in node.sons: + right = [] + execute_node(node.sons[x],left,right,grammar,idx) + elif len(node.sons) == 1: + for key in node.sons: + execute_node(node.sons[key],left,right,grammar,idx) + break + else: + grammar.Add_Production(Production(left,SentenceFromIter(right))) + + +# e-PRODUCTIONS +def remove_e_productions(G): + """ + returns a grammar with all epsilon productions eliminated\n + and a set with the Non Terminal that reached epsilon productions\n + return G,n_t_epsilon + """ + + n_t_epsilon = set() + new_productions = [] + # Removes all epsilon transitions from the non terminals + for x in G.Productions: + if x.Right.IsEpsilon: + n_t_epsilon.add(x.Left) + x.Left.productions.remove(x) + else: + new_productions.append(x) + + change = 0 + while change != len(n_t_epsilon): + change = len(n_t_epsilon) + for prod in G.Productions: + if len([x for x in prod.Right if x in n_t_epsilon]) == len(prod.Right): # All x in right is on n_t_epsilon + n_t_epsilon.add(prod.Left) + + G.Productions = new_productions + + return G,n_t_epsilon + +def add_new_productions(prod,new_productions,n_t_epsilon): + """ + Add to new_productions the corresponding productions from\n + permutate prod with the non_terminals in n_t_epsilon \n + Ex: A->BCB,n_t_eps={B}\n + add to new_productions {A->BCB, A->CB, A->BC, A->C} + """ + had_epsilon = [False]*len(prod.Right) + cant_epsilon = 0 + + for i,x in enumerate(prod.Right): + if x in n_t_epsilon: + had_epsilon[i] = True + cant_epsilon += 1 + + for perm in permutation(cant_epsilon,2): + new_prod_right = [] + perm_idx = 0 + for i,x in enumerate(had_epsilon): + if not x: + new_prod_right.append(prod.Right[i]) + elif perm[perm_idx]: + new_prod_right.append(prod.Right[i]) + perm_idx = perm_idx+1 if x else perm_idx + if new_prod_right: + new_productions.add(Production(prod.Left,SentenceFromIter(new_prod_right))) + if cant_epsilon == 0: + new_productions.add(Production(prod.Left,prod.Right)) + +def fix_e_productions(gramm:Grammar): + """ + Returns a grammar without epsilon transitions.\n + if gramm recognize epsilon then an augmented grammar\n + is return with an epsilon transition on the start symbol + """ + G = gramm + + G,n_t_epsilon = remove_e_productions(G) + + if G.startSymbol in n_t_epsilon: + G = G.AugmentedGrammar(force=True) + G.Add_Production(Production(G.startSymbol,G.Epsilon)) + else: + G = G.copy() + + new_productions = set() + + for prod in gramm.Productions: + add_new_productions(prod,new_productions,n_t_epsilon) + + for x in gramm.nonTerminals: + x.productions = [] + + for prod in new_productions: + G.Add_Production(prod) + + return G + + +# NON DERIVE TERMINAL +def update_derivation(production,derivation,left_derivation = True): + symbol,sentence = production.Left,production.Right + derive = [] + derive.append(production) + if not symbol in derivation: + if sentence.IsEpsilon: + derive.extend(derivation[symbol.Grammar.Epsilon]) + elif not left_derivation: + sentence = [ sentence[i] for i in range(len(sentence)-1,-1,-1) ] # sentence.reverse() + for x in sentence: + derive.extend(derivation[x]) + derivation[symbol] = derive + +def fix_non_derive_terminal(gramm:Grammar,return_derivations = False,left_derivation = True): + """ + Remove from gramm the non terminals A that dont satisfy:\n + A->*w where w in {G.T}* + return grammar + return grammar,derivation + """ + gramm = gramm.copy() + + derivation = { x:[Production(x,Sentence(x)),] for x in gramm.terminals } + derivation[gramm.Epsilon] = [Production(gramm.Epsilon,Sentence(gramm.Epsilon)),] + derive_something = set(gramm.terminals) + productions = set(gramm.Productions) + + change = -1 + while change != len(derive_something): + change = len(derive_something) + to_remove = [] + for x in productions: + if not any([y for y in x.Right if not y in derive_something]): # if y ->* w with w in T* + derive_something.add(x.Left) + update_derivation(x,derivation,left_derivation) + to_remove.append(x) + for x in to_remove: productions.remove(x) + + remove_unnecessary_symbols(gramm,[x for x in gramm.nonTerminals if x not in derive_something]) + + if return_derivations: + return gramm,derivation + return gramm + + +# UNREACHABLE SYMBOLS +def fix_unreachable_symbols(gramm:Grammar): + gramm = gramm.copy() + + pending = [gramm.startSymbol] + reachable = set(pending) + while pending: + symb = pending.pop() + for prod in symb.productions: + for r in prod.Right: + if not r in reachable: + reachable.add(r) + if isinstance(r,NonTerminal): + pending.append(r) + + remove_unnecessary_symbols(gramm,[x for x in gramm.nonTerminals + gramm.terminals if x not in reachable]) + + return gramm + + +# USELESS SYMBOLS +def fix_useless_symbols(gramm:Grammar): + """ + fix the non derive on terminal, non_terminals\n + and the unreachable symbols from the start symbol + """ + gramm = fix_unit_productions(gramm) + + gramm = fix_non_derive_terminal(gramm) + + gramm = fix_unreachable_symbols(gramm) + + return gramm + + +# UNIT PRODUCTIONS +def get_unit_tuples(unit_productions): + change = -1 + tuples = set([(x.Left,x.Right[0]) for x in unit_productions]) + while change != len(tuples): + change = len(tuples) + to_add = set() + for t in tuples: + for q in [x for x in tuples if t[1] == x[0]]: + to_add.add((t[0],q[1])) + tuples.update(to_add) + return tuples + + +def fix_unit_productions(gramm:Grammar): + """ + returns an equivalent grammar without productions of the form:\n + A -> B + """ + gramm = gramm.copy() + + unit_productions = {x for x in gramm.Productions if len(x.Right) == 1 and x.Right[0].IsNonTerminal} + + new_productions = set() + + for x in gramm.Productions: + if not x in unit_productions: + new_productions.add(x) + + pending = get_unit_tuples(unit_productions) + + while pending: + l,r = pending.pop() + for prod in r.productions: + if not prod in unit_productions: + new_productions.add(Production(l,prod.Right)) + + return change_grammar_from_productions(gramm,new_productions) diff --git a/src/cool2/lib/grammar/grammar_parser.py b/src/cool2/lib/grammar/grammar_parser.py index 9f8aa6f5f..f7e0a9933 100644 --- a/src/cool2/lib/grammar/grammar_parser.py +++ b/src/cool2/lib/grammar/grammar_parser.py @@ -1,174 +1,174 @@ -from cmp.pycompiler import (EOF, Epsilon, Grammar, NonTerminal, Production, - Sentence, SentenceList, Symbol, Terminal) -from cmp.ast import Node, AtomicNode, BinaryNode, TernaryNode -from cmp.utils import Token - -G_parser = Grammar() -Q = G_parser.NonTerminal('Q', True) -non_terminals = G_parser.NonTerminals('L S X Z E K T N M') -L, S, X, Z, E, K, T, N, M = non_terminals -non_terminals = { x.Name:x for x in non_terminals } -non_terminals[Q.Name] = Q - -terminals = G_parser.Terminals('pipe sym der end comma') -pipe, sym, der, end, comma = terminals -terminals = { x.Name:x for x in terminals } -terminals['eof'] = G_parser.EOF - -symbols = non_terminals.copy() -symbols.update(terminals) - -class GramConcatLines(BinaryNode): - @staticmethod - def operate(lvalue, rvalue): - return lvalue + '\n' + rvalue - -class GramNewProdNode(TernaryNode): - @staticmethod - def operate(lvalue, mvalue, rvalue): - return lvalue + ' > ' + mvalue + rvalue - -class GramConcatProdNode(BinaryNode): - @staticmethod - def operate(lvalue, rvalue): - return ' | ' + lvalue + rvalue - -class GramConcatNode(BinaryNode): - @staticmethod - def operate(lvalue, rvalue): - if rvalue == '': - return lvalue - return lvalue + ' ' + rvalue - -class SymbolNode(AtomicNode): - def evaluate(self): - return self.lex - - -############################ BEGIN PRODUCTIONS ############################ -# ======================================================================= # -Q %= E + end + T, lambda h,s: GramConcatLines(s[1],s[3]) -T %= E + end + T, lambda h,s: GramConcatLines(s[1],s[3]) -T %= G_parser.Epsilon, lambda h,s: SymbolNode('') -E %= sym + der + L, lambda h,s: s[3], None, None, lambda h,s: SymbolNode(s[1]) -L %= S + X, lambda h,s: GramNewProdNode(h[0],s[1],s[2]) -X %= pipe + S + X, lambda h,s: GramConcatProdNode(s[2],s[3]), None, None, lambda h,s: h[0] -X %= G_parser.Epsilon, lambda h,s: SymbolNode('') -S %= sym + K, lambda h,s: GramConcatNode(SymbolNode(s[1]),s[2]) -K %= sym + K, lambda h,s: GramConcatNode(SymbolNode(s[1]),s[2]) -K %= G_parser.Epsilon, lambda h,s: SymbolNode('') -# ======================================================================= # -############################# END PRODUCTIONS ############################# - -from lib.parser.parser_ll1 import ParserLL1 -from lib.grammar.grammar_tokens import get_lexer_from_text,nonzero_digits,digits,min_letters,cap_letters,separator,regex_char_separator - -# Grammar of the parser of Grammars -gram_parser = ParserLL1(G_parser) - -expand = '~' - -# Lexer of Grammar of the parser of Grammars -terms = f'sym{separator}[{min_letters}{cap_letters}_][_{min_letters}{cap_letters}{digits}]*{regex_char_separator}pipe{separator}\\|{regex_char_separator}end{separator};{regex_char_separator}der@{expand}{regex_char_separator}space{separator} *{regex_char_separator}comma{separator},{regex_char_separator}' -gram_lexer = None #get_lexer_from_text(terms,[]) ############################## - -def fix_gram_text(gram_text,errors): - gram_text = gram_text.replace('\n','') - - new_errors = [] - tokens = get_grammar_tokens(gram_text,new_errors) - if new_errors: - errors.append('Error getting the grammar tokens') - errors.extend(new_errors) - - new_errors = [] - gramm = gram_parser.evaluate(tokens,new_errors) - if new_errors: - errors.append('Error evaluating the grammar definition') - errors.extend(new_errors) - - return gramm - -def get_grammar_from_text(gramm_text, errors): - """ - returns a Grammar form gramm_text\n - gramm_text: is the grammar written by the user\n - NonTerminal_i ~ Production1_1 | ... | Production1_N;\n - ... - NonTerminal_j ~ ProductionQ_1 | ... | ProductionZ_P;\n - """ - gramm_text = fix_gram_text(gramm_text,errors) - if not gramm_text: - return None - - G = Grammar() - dic_sym = {} - - gramm = gramm_text.split('\n') - index = 0 - - distinguish = '' - symbols = set() - non_terminals = set() - for s in gramm: - if s: - s = s.split(' > ') - if not distinguish: - distinguish = s[0] - non_terminals.add(s[0]) - symbols.add(s[0]) - for ps in s[1].split(' | '): - for q in ps.split(' '): - symbols.add(q) - terminals = symbols.difference(non_terminals) - non_terminals.remove(distinguish) - S = G.NonTerminal(distinguish,True) - dic_sym[distinguish] = S - non_ter = ' '.join(non_terminals) - non_ter = G.NonTerminals(non_ter) - for x in non_ter: - dic_sym[x.Name] = x - ter = ' '.join(terminals) - ter = G.Terminals(ter) - for x in ter: - dic_sym[x.Name] = x - - dic_sym.update({"epsilon":G.Epsilon, "$":G.EOF}) - - s = gramm[index] - index+=1 - while s != "": - s = s.split(" > ") - q = s[1].split(" | ") - for prod in q: - p = prod.split(" ") - try: - temp = dic_sym[p[0]] - except KeyError: - errors.append(f'{p[0]} is not defined in the terminals or in the non_terminals') - break - for ter in p[1:]: - try: - temp += dic_sym[ter] - except KeyError: - errors.append(f'{ter} is not defined in the terminals or in the non_terminals') - break - try: - dic_sym[s[0]] %= temp - except TypeError: - errors.append(f'A Non Terminal cant be left part of a production: {s}') - break - s = gramm[index] - index+=1 - return G - -def get_grammar_tokens(gram_def,errors:list): - tokens = [] - for x in gram_lexer(gram_def): - if x.token_type != 'space': - try: - tok = Token(x.lex,symbols[x.token_type]) - tokens.append(tok) - except KeyError: - errors.append(f'Unknown Token({x.lex},{x.token_type}) in gram_def') - return tokens +from cmp.pycompiler import (EOF, Epsilon, Grammar, NonTerminal, Production, + Sentence, SentenceList, Symbol, Terminal) +from cmp.ast import Node, AtomicNode, BinaryNode, TernaryNode +from cmp.utils import Token + +G_parser = Grammar() +Q = G_parser.NonTerminal('Q', True) +non_terminals = G_parser.NonTerminals('L S X Z E K T N M') +L, S, X, Z, E, K, T, N, M = non_terminals +non_terminals = { x.Name:x for x in non_terminals } +non_terminals[Q.Name] = Q + +terminals = G_parser.Terminals('pipe sym der end comma') +pipe, sym, der, end, comma = terminals +terminals = { x.Name:x for x in terminals } +terminals['eof'] = G_parser.EOF + +symbols = non_terminals.copy() +symbols.update(terminals) + +class GramConcatLines(BinaryNode): + @staticmethod + def operate(lvalue, rvalue): + return lvalue + '\n' + rvalue + +class GramNewProdNode(TernaryNode): + @staticmethod + def operate(lvalue, mvalue, rvalue): + return lvalue + ' > ' + mvalue + rvalue + +class GramConcatProdNode(BinaryNode): + @staticmethod + def operate(lvalue, rvalue): + return ' | ' + lvalue + rvalue + +class GramConcatNode(BinaryNode): + @staticmethod + def operate(lvalue, rvalue): + if rvalue == '': + return lvalue + return lvalue + ' ' + rvalue + +class SymbolNode(AtomicNode): + def evaluate(self): + return self.lex + + +############################ BEGIN PRODUCTIONS ############################ +# ======================================================================= # +Q %= E + end + T, lambda h,s: GramConcatLines(s[1],s[3]) +T %= E + end + T, lambda h,s: GramConcatLines(s[1],s[3]) +T %= G_parser.Epsilon, lambda h,s: SymbolNode('') +E %= sym + der + L, lambda h,s: s[3], None, None, lambda h,s: SymbolNode(s[1]) +L %= S + X, lambda h,s: GramNewProdNode(h[0],s[1],s[2]) +X %= pipe + S + X, lambda h,s: GramConcatProdNode(s[2],s[3]), None, None, lambda h,s: h[0] +X %= G_parser.Epsilon, lambda h,s: SymbolNode('') +S %= sym + K, lambda h,s: GramConcatNode(SymbolNode(s[1]),s[2]) +K %= sym + K, lambda h,s: GramConcatNode(SymbolNode(s[1]),s[2]) +K %= G_parser.Epsilon, lambda h,s: SymbolNode('') +# ======================================================================= # +############################# END PRODUCTIONS ############################# + +from lib.parser.parser_ll1 import ParserLL1 +from lib.grammar.grammar_tokens import get_lexer_from_text,nonzero_digits,digits,min_letters,cap_letters,separator,regex_char_separator + +# Grammar of the parser of Grammars +gram_parser = ParserLL1(G_parser) + +expand = '~' + +# Lexer of Grammar of the parser of Grammars +terms = f'sym{separator}[{min_letters}{cap_letters}_][_{min_letters}{cap_letters}{digits}]*{regex_char_separator}pipe{separator}\\|{regex_char_separator}end{separator};{regex_char_separator}der@{expand}{regex_char_separator}space{separator} *{regex_char_separator}comma{separator},{regex_char_separator}' +gram_lexer = None #get_lexer_from_text(terms,[]) ############################## + +def fix_gram_text(gram_text,errors): + gram_text = gram_text.replace('\n','') + + new_errors = [] + tokens = get_grammar_tokens(gram_text,new_errors) + if new_errors: + errors.append('Error getting the grammar tokens') + errors.extend(new_errors) + + new_errors = [] + gramm = gram_parser.evaluate(tokens,new_errors) + if new_errors: + errors.append('Error evaluating the grammar definition') + errors.extend(new_errors) + + return gramm + +def get_grammar_from_text(gramm_text, errors): + """ + returns a Grammar form gramm_text\n + gramm_text: is the grammar written by the user\n + NonTerminal_i ~ Production1_1 | ... | Production1_N;\n + ... + NonTerminal_j ~ ProductionQ_1 | ... | ProductionZ_P;\n + """ + gramm_text = fix_gram_text(gramm_text,errors) + if not gramm_text: + return None + + G = Grammar() + dic_sym = {} + + gramm = gramm_text.split('\n') + index = 0 + + distinguish = '' + symbols = set() + non_terminals = set() + for s in gramm: + if s: + s = s.split(' > ') + if not distinguish: + distinguish = s[0] + non_terminals.add(s[0]) + symbols.add(s[0]) + for ps in s[1].split(' | '): + for q in ps.split(' '): + symbols.add(q) + terminals = symbols.difference(non_terminals) + non_terminals.remove(distinguish) + S = G.NonTerminal(distinguish,True) + dic_sym[distinguish] = S + non_ter = ' '.join(non_terminals) + non_ter = G.NonTerminals(non_ter) + for x in non_ter: + dic_sym[x.Name] = x + ter = ' '.join(terminals) + ter = G.Terminals(ter) + for x in ter: + dic_sym[x.Name] = x + + dic_sym.update({"epsilon":G.Epsilon, "$":G.EOF}) + + s = gramm[index] + index+=1 + while s != "": + s = s.split(" > ") + q = s[1].split(" | ") + for prod in q: + p = prod.split(" ") + try: + temp = dic_sym[p[0]] + except KeyError: + errors.append(f'{p[0]} is not defined in the terminals or in the non_terminals') + break + for ter in p[1:]: + try: + temp += dic_sym[ter] + except KeyError: + errors.append(f'{ter} is not defined in the terminals or in the non_terminals') + break + try: + dic_sym[s[0]] %= temp + except TypeError: + errors.append(f'A Non Terminal cant be left part of a production: {s}') + break + s = gramm[index] + index+=1 + return G + +def get_grammar_tokens(gram_def,errors:list): + tokens = [] + for x in gram_lexer(gram_def): + if x.token_type != 'space': + try: + tok = Token(x.lex,symbols[x.token_type]) + tokens.append(tok) + except KeyError: + errors.append(f'Unknown Token({x.lex},{x.token_type}) in gram_def') + return tokens diff --git a/src/cool2/lib/grammar/grammar_tokens.py b/src/cool2/lib/grammar/grammar_tokens.py index fdce5046b..826479a36 100644 --- a/src/cool2/lib/grammar/grammar_tokens.py +++ b/src/cool2/lib/grammar/grammar_tokens.py @@ -1,141 +1,141 @@ -from cmp.pycompiler import (EOF, Epsilon, Grammar, NonTerminal, Production, - Sentence, SentenceList, Symbol, Terminal) -from cmp.ast import Node, AtomicNode, BinaryNode, TernaryNode -from cmp.utils import Token -from lib.lexer.lexer import Lexer,nonzero_digits,digits,min_letters,cap_letters -from lib.lexer.regex import fixed_tokens, EPSILON, regex_automaton - - -digits_letters = f'{digits}{min_letters}{cap_letters}' -others = f',.^;:<>=-_!^&~/' -separator = '@' -regex_char_separator = '\n' -regex_separator = f'{regex_char_separator}*' - -# Lexer that recognise the token defined by the user -lex_token = Lexer([ #################################################### - # ('term',f'[{digits_letters}]*'), - # ('regex_ex',f'{separator}[ \\\\\\[\\]\\+\\?\\*\\(\\)\\|\\{EPSILON}{digits_letters}{others}]*'), - # ('regex_sep',regex_separator), -],'eof') - -# num@12w09djqdk\nlet@[asdcsac] ==> num - regex(12w09djqdk) let - regex([asdcasac]) - -def get_lexer_from_text(text,errors:list): - """ - Returns a Lexer that recognise tokens of the form @\\n@\\n...@\\n\n - where term is the name of the terminal and regex is the regular expression of term\n - the order in text is the same that the order of the lexer table - """ - tokens = lex_token(text) - lexer_table = [] - tok_iter = iter(tokens) - new_errors = [] - while True: - try: - terminal = next(tok_iter) - except StopIteration: - new_errors.append(f'Expected: token') - break - - if terminal.token_type == 'eof': - break - - if not terminal.token_type == 'term': - new_errors.append(f'Expected: {terminal.lex}.token_type == term, Recieve: {terminal.lex}.token_type == {terminal.token_type}') - break - - try: - regex = next(tok_iter) - except StopIteration: - new_errors.append(f'Expected: token') - break - - if not regex.token_type == 'regex_ex': - new_errors.append(f'Expected: {regex.lex}.token_type == regex_ex, Recieve: {terminal.lex}.token_type == {terminal.token_type}') - break - - try: - sep = next(tok_iter) - except StopIteration: - new_errors.append(f'Expected: token') - break - - if not (sep.token_type == 'regex_sep' or sep.token_type == 'eof'): - new_errors.append(f'Expected: {sep.lex}.token_type == regex_sep or eof, Recieve: {terminal.lex}.token_type == {terminal.token_type}') - break - - lexer_table.append((terminal.lex,regex.lex[1:])) - - if sep.token_type == 'eof': - break - - for x in new_errors: - errors.append(x) - try: - next(tok_iter) - errors.append('tokens left to parse') - return None - except StopIteration: - if new_errors: - return None - return Lexer(lexer_table,'eof') - -def get_lexer_dict_from_text(text,errors:list): - """ - Returns a dictionary that recognise tokens of the form @\\n@\\n...@\\n\n - where term is the name of the terminal and regex is the regular expression of term\n - """ - tokens = lex_token(text) - lexer_table = {} - tok_iter = iter(tokens) - new_errors = [] - while True: - try: - terminal = next(tok_iter) - except StopIteration: - new_errors.append(f'Expected: token') - break - - if terminal.token_type == 'eof': - break - - if not terminal.token_type == 'term': - new_errors.append(f'Expected: {terminal.lex}.token_type == term, Recieve: {terminal.lex}.token_type == {terminal.token_type}') - break - - try: - regex = next(tok_iter) - except StopIteration: - new_errors.append(f'Expected: token') - break - - if not regex.token_type == 'regex_ex': - new_errors.append(f'Expected: {regex.lex}.token_type == regex_ex, Recieve: {terminal.lex}.token_type == {terminal.token_type}') - break - - try: - sep = next(tok_iter) - except StopIteration: - new_errors.append(f'Expected: token') - break - - if not (sep.token_type == 'regex_sep' or sep.token_type == 'eof'): - new_errors.append(f'Expected: {sep.lex}.token_type == regex_sep or eof, Recieve: {terminal.lex}.token_type == {terminal.token_type}') - break - - lexer_table[terminal.lex] = regex_automaton(regex.lex[1:]) - - if sep.token_type == 'eof': - break - - for x in new_errors: - errors.append(x) - try: - next(tok_iter) - errors.append('tokens left to parse') - return None - except StopIteration: - if new_errors: - return None - return lexer_table +from cmp.pycompiler import (EOF, Epsilon, Grammar, NonTerminal, Production, + Sentence, SentenceList, Symbol, Terminal) +from cmp.ast import Node, AtomicNode, BinaryNode, TernaryNode +from cmp.utils import Token +from lib.lexer.lexer import Lexer,nonzero_digits,digits,min_letters,cap_letters +from lib.lexer.regex import fixed_tokens, EPSILON, regex_automaton + + +digits_letters = f'{digits}{min_letters}{cap_letters}' +others = f',.^;:<>=-_!^&~/' +separator = '@' +regex_char_separator = '\n' +regex_separator = f'{regex_char_separator}*' + +# Lexer that recognise the token defined by the user +lex_token = Lexer([ #################################################### + # ('term',f'[{digits_letters}]*'), + # ('regex_ex',f'{separator}[ \\\\\\[\\]\\+\\?\\*\\(\\)\\|\\{EPSILON}{digits_letters}{others}]*'), + # ('regex_sep',regex_separator), +],'eof') + +# num@12w09djqdk\nlet@[asdcsac] ==> num - regex(12w09djqdk) let - regex([asdcasac]) + +def get_lexer_from_text(text,errors:list): + """ + Returns a Lexer that recognise tokens of the form @\\n@\\n...@\\n\n + where term is the name of the terminal and regex is the regular expression of term\n + the order in text is the same that the order of the lexer table + """ + tokens = lex_token(text) + lexer_table = [] + tok_iter = iter(tokens) + new_errors = [] + while True: + try: + terminal = next(tok_iter) + except StopIteration: + new_errors.append(f'Expected: token') + break + + if terminal.token_type == 'eof': + break + + if not terminal.token_type == 'term': + new_errors.append(f'Expected: {terminal.lex}.token_type == term, Recieve: {terminal.lex}.token_type == {terminal.token_type}') + break + + try: + regex = next(tok_iter) + except StopIteration: + new_errors.append(f'Expected: token') + break + + if not regex.token_type == 'regex_ex': + new_errors.append(f'Expected: {regex.lex}.token_type == regex_ex, Recieve: {terminal.lex}.token_type == {terminal.token_type}') + break + + try: + sep = next(tok_iter) + except StopIteration: + new_errors.append(f'Expected: token') + break + + if not (sep.token_type == 'regex_sep' or sep.token_type == 'eof'): + new_errors.append(f'Expected: {sep.lex}.token_type == regex_sep or eof, Recieve: {terminal.lex}.token_type == {terminal.token_type}') + break + + lexer_table.append((terminal.lex,regex.lex[1:])) + + if sep.token_type == 'eof': + break + + for x in new_errors: + errors.append(x) + try: + next(tok_iter) + errors.append('tokens left to parse') + return None + except StopIteration: + if new_errors: + return None + return Lexer(lexer_table,'eof') + +def get_lexer_dict_from_text(text,errors:list): + """ + Returns a dictionary that recognise tokens of the form @\\n@\\n...@\\n\n + where term is the name of the terminal and regex is the regular expression of term\n + """ + tokens = lex_token(text) + lexer_table = {} + tok_iter = iter(tokens) + new_errors = [] + while True: + try: + terminal = next(tok_iter) + except StopIteration: + new_errors.append(f'Expected: token') + break + + if terminal.token_type == 'eof': + break + + if not terminal.token_type == 'term': + new_errors.append(f'Expected: {terminal.lex}.token_type == term, Recieve: {terminal.lex}.token_type == {terminal.token_type}') + break + + try: + regex = next(tok_iter) + except StopIteration: + new_errors.append(f'Expected: token') + break + + if not regex.token_type == 'regex_ex': + new_errors.append(f'Expected: {regex.lex}.token_type == regex_ex, Recieve: {terminal.lex}.token_type == {terminal.token_type}') + break + + try: + sep = next(tok_iter) + except StopIteration: + new_errors.append(f'Expected: token') + break + + if not (sep.token_type == 'regex_sep' or sep.token_type == 'eof'): + new_errors.append(f'Expected: {sep.lex}.token_type == regex_sep or eof, Recieve: {terminal.lex}.token_type == {terminal.token_type}') + break + + lexer_table[terminal.lex] = regex_automaton(regex.lex[1:]) + + if sep.token_type == 'eof': + break + + for x in new_errors: + errors.append(x) + try: + next(tok_iter) + errors.append('tokens left to parse') + return None + except StopIteration: + if new_errors: + return None + return lexer_table diff --git a/src/cool2/lib/grammar/grammar_util.py b/src/cool2/lib/grammar/grammar_util.py index 94ed80d0c..db5bcbeee 100644 --- a/src/cool2/lib/grammar/grammar_util.py +++ b/src/cool2/lib/grammar/grammar_util.py @@ -1,21 +1,21 @@ -from cmp.pycompiler import (EOF, Epsilon, Grammar, NonTerminal, Production, - Sentence, SentenceList, Symbol, Terminal) -from lib.grammar.grammar_parser import expand - - -def grammar_to_text(G:Grammar): - gram = '' - for n in G.nonTerminals: - gram += n.Name + f' {expand} ' - for p in n.productions: - if p.Right.IsEpsilon: - gram += ' epsilon ' - else: - for s in p.Right: - gram += f' {s.Name} ' - gram += ' | ' - gram = gram[:len(gram)-2] - gram += ';\n' - - return gram +from cmp.pycompiler import (EOF, Epsilon, Grammar, NonTerminal, Production, + Sentence, SentenceList, Symbol, Terminal) +from lib.grammar.grammar_parser import expand + + +def grammar_to_text(G:Grammar): + gram = '' + for n in G.nonTerminals: + gram += n.Name + f' {expand} ' + for p in n.productions: + if p.Right.IsEpsilon: + gram += ' epsilon ' + else: + for s in p.Right: + gram += f' {s.Name} ' + gram += ' | ' + gram = gram[:len(gram)-2] + gram += ';\n' + + return gram \ No newline at end of file diff --git a/src/cool2/lib/lang/language.py b/src/cool2/lib/lang/language.py index f68f6185f..0f421095a 100644 --- a/src/cool2/lib/lang/language.py +++ b/src/cool2/lib/lang/language.py @@ -1,72 +1,72 @@ -from cmp.utils import Token - -class Language(): - - parser = None - - grammar = None - - lexer = None - - def __init__(self, grammar,lexer,token_parse_dict={}): - self.grammar = grammar - self.lexer = lexer - self.token_parse = token_parse_dict - - def _fix_tokens(self,tokens,errors): - """ - If there are a token_type named 'space' this are discarted from the parsing tokens\n - also transform lexer tokens to grammar tokens - """ - fix_tokens = [] - for x in tokens: - if x.token_type != 'space': - try: - if x.token_type in self.token_parse: - tok = Token(self.token_parse[x.token_type](x.lex),x.token_type) - else: - tok = Token(x.lex,x.token_type) - fix_tokens.append(tok) - except KeyError: - errors.append(f'The grammar does not recognize the token {x}') - return fix_tokens - - def __call__(self, text, errors, tokens=None): - """ - returns a tuple of the parse and the tokens of the text - """ - - if tokens is None: - tokens = self.get_tokens(text, errors) - - parse_errors = [] - parse = self.parser(tokens,parse_errors) - - for x in parse_errors: - errors.append(x) - - if parse_errors: - return [],[] - - return parse,tokens - - def get_tokens(self, text, errors): - """ - Return the text tokens - """ - tokens = self.lexer(text) - tokens = self._fix_tokens(tokens,errors) - for tok in tokens: - if tok.token_type == "UNKNOWN": - errors.append(f'({tok.lex[1]+1}, {tok.lex[2]+1}) - LexicographicError: ERROR "{tok.lex[0]}"') - tokens = [x for x in tokens if x.token_type != "UNKNOWN"] - - return tokens - - def find_conflict(self): - return self.parser.find_conflict() - - def evaluate(self, text, errors:list,return_ast = False): - # tokens = [ x for x in self.lexer(text) if x.token_type != 'space'] - tokens = self._fix_tokens(self.lexer(text),errors) - return self.parser.evaluate(tokens,errors,return_ast) +from cmp.utils import Token + +class Language(): + + parser = None + + grammar = None + + lexer = None + + def __init__(self, grammar,lexer,token_parse_dict={}): + self.grammar = grammar + self.lexer = lexer + self.token_parse = token_parse_dict + + def _fix_tokens(self,tokens,errors): + """ + If there are a token_type named 'space' this are discarted from the parsing tokens\n + also transform lexer tokens to grammar tokens + """ + fix_tokens = [] + for x in tokens: + if x.token_type != 'space': + try: + if x.token_type in self.token_parse: + tok = Token(self.token_parse[x.token_type](x.lex),x.token_type) + else: + tok = Token(x.lex,x.token_type) + fix_tokens.append(tok) + except KeyError: + errors.append(f'The grammar does not recognize the token {x}') + return fix_tokens + + def __call__(self, text, errors, tokens=None): + """ + returns a tuple of the parse and the tokens of the text + """ + + if tokens is None: + tokens = self.get_tokens(text, errors) + + parse_errors = [] + parse = self.parser(tokens,parse_errors) + + for x in parse_errors: + errors.append(x) + + if parse_errors: + return [],[] + + return parse,tokens + + def get_tokens(self, text, errors): + """ + Return the text tokens + """ + tokens = self.lexer(text) + tokens = self._fix_tokens(tokens,errors) + for tok in tokens: + if tok.token_type == "UNKNOWN": + errors.append(f'({tok.lex[1]+1}, {tok.lex[2]+1}) - LexicographicError: ERROR "{tok.lex[0]}"') + tokens = [x for x in tokens if x.token_type != "UNKNOWN"] + + return tokens + + def find_conflict(self): + return self.parser.find_conflict() + + def evaluate(self, text, errors:list,return_ast = False): + # tokens = [ x for x in self.lexer(text) if x.token_type != 'space'] + tokens = self._fix_tokens(self.lexer(text),errors) + return self.parser.evaluate(tokens,errors,return_ast) diff --git a/src/cool2/lib/lang/language_ll1.py b/src/cool2/lib/lang/language_ll1.py index fc8654b0f..5793c024b 100644 --- a/src/cool2/lib/lang/language_ll1.py +++ b/src/cool2/lib/lang/language_ll1.py @@ -1,62 +1,62 @@ -from cmp.pycompiler import (EOF, Epsilon, Grammar, NonTerminal, Production, - Sentence, SentenceList, Symbol, Terminal, SentenceFromIter) -from cmp.utils import ContainerSet, inspect, pprint, Token -from lib.grammar.grammar_tokens import get_lexer_from_text -from lib.grammar.grammar_parser import get_grammar_from_text -from lib.lexer.lexer import digits,min_letters,cap_letters -from lib.parser.parser_ll1 import ParserLL1 -from lib.grammar.grammar_fixer import fix_grammar -from lib.lang.language import Language -class LanguageLL1(Language): - - def __init__(self, grammar,lexer): - super().__init__(grammar,lexer) - self.parser = ParserLL1(self.grammar) - - @property - def get_firsts(self): - return self.parser.get_firsts - - @property - def get_follows(self): - return self.parser.get_follows - - @property - def get_predictive_table(self): - return self.parser.get_parser_table - - def get_derivation_tree(self,parse): - return self.parser.get_derivation_tree(parse) - -def build_LL1(tokens_def, grammar_def, errors:list): - """ - token_def:\n - The definition of the non terminal symbols of the grammar\n - (the name must be equal to the non terminals written in grammar_def)\n - token_def --> same format than text in get_lexer function\n - token_def can be an instance of Lexer in case of already have it\n - grammar_def:\n - The definition by the user of the grammar in this format:\n - Production1;Production2;...ProductionN;\n - A production can be: T ~ A bc |r3 | epsilon\n - grammar_def can be an instance of Grammar in case of already have it\n - return None if the grammar is not LL1\n - return the LanguageLL1 defined in grammar_def \n - """ - new_errors = [] - lex_gram = get_lexer_from_text(tokens_def,new_errors) if isinstance(tokens_def,str) else tokens_def - Gramm = get_grammar_from_text(grammar_def,new_errors) if isinstance(grammar_def,str) else grammar_def - # Gramm = fix_grammar(Gramm,errors) - - if not Gramm or not lex_gram: - for x in new_errors: - errors.append(x) - return None - ll1 = LanguageLL1(Gramm,lex_gram) - for x in ll1.parser.errors: - new_errors.append(x) - for x in new_errors: - errors.append(x) - # if new_errors: - # return None - return ll1 +from cmp.pycompiler import (EOF, Epsilon, Grammar, NonTerminal, Production, + Sentence, SentenceList, Symbol, Terminal, SentenceFromIter) +from cmp.utils import ContainerSet, inspect, pprint, Token +from lib.grammar.grammar_tokens import get_lexer_from_text +from lib.grammar.grammar_parser import get_grammar_from_text +from lib.lexer.lexer import digits,min_letters,cap_letters +from lib.parser.parser_ll1 import ParserLL1 +from lib.grammar.grammar_fixer import fix_grammar +from lib.lang.language import Language +class LanguageLL1(Language): + + def __init__(self, grammar,lexer): + super().__init__(grammar,lexer) + self.parser = ParserLL1(self.grammar) + + @property + def get_firsts(self): + return self.parser.get_firsts + + @property + def get_follows(self): + return self.parser.get_follows + + @property + def get_predictive_table(self): + return self.parser.get_parser_table + + def get_derivation_tree(self,parse): + return self.parser.get_derivation_tree(parse) + +def build_LL1(tokens_def, grammar_def, errors:list): + """ + token_def:\n + The definition of the non terminal symbols of the grammar\n + (the name must be equal to the non terminals written in grammar_def)\n + token_def --> same format than text in get_lexer function\n + token_def can be an instance of Lexer in case of already have it\n + grammar_def:\n + The definition by the user of the grammar in this format:\n + Production1;Production2;...ProductionN;\n + A production can be: T ~ A bc |r3 | epsilon\n + grammar_def can be an instance of Grammar in case of already have it\n + return None if the grammar is not LL1\n + return the LanguageLL1 defined in grammar_def \n + """ + new_errors = [] + lex_gram = get_lexer_from_text(tokens_def,new_errors) if isinstance(tokens_def,str) else tokens_def + Gramm = get_grammar_from_text(grammar_def,new_errors) if isinstance(grammar_def,str) else grammar_def + # Gramm = fix_grammar(Gramm,errors) + + if not Gramm or not lex_gram: + for x in new_errors: + errors.append(x) + return None + ll1 = LanguageLL1(Gramm,lex_gram) + for x in ll1.parser.errors: + new_errors.append(x) + for x in new_errors: + errors.append(x) + # if new_errors: + # return None + return ll1 diff --git a/src/cool2/lib/lang/language_lr.py b/src/cool2/lib/lang/language_lr.py index 959b87bf6..f7e040b63 100644 --- a/src/cool2/lib/lang/language_lr.py +++ b/src/cool2/lib/lang/language_lr.py @@ -1,53 +1,53 @@ -from lib.parser.parser_lr import LR0Parser,LR1Parser,SLR1Parser,LALR1Parser -from lib.grammar.grammar_parser import get_grammar_from_text -from lib.grammar.grammar_tokens import get_lexer_from_text -from lib.lang.language import Language - -def return_lr_parser(G,parser): - parser = parser.lower() - if parser == 'lr0': - lr = LR0Parser(G) - elif parser == 'lalr1': - lr = LALR1Parser(G) - elif parser == 'slr1': - lr = SLR1Parser(G) - elif parser == 'lr1': - lr = LR1Parser(G) - else: - lr = None - return lr - -def build_LR(tok_def,gram_def,lr:str,errors): - """ - gram_def: grammar definition or an instance of Grammar\n - tok_def: tokens deefinition or an instance of Lexer\n - lr: type of lr parser, can be ['lalr1', 'lr1', 'lr0', 'slr1']\n - return a LanguageLR\n - return None in case of errors - """ - new_errors = [] - - lex_gram = get_lexer_from_text(tok_def,new_errors) if isinstance(tok_def,str) else tok_def - Gramm = get_grammar_from_text(gram_def,new_errors) if isinstance(gram_def,str) else gram_def - - for x in new_errors: - errors.append(x) - if lex_gram and Gramm: - lr = return_lr_parser(Gramm,lr) - if not lr: - errors.append(f'{lr} is not a valid LR type of parser, try [lalr1, lr1, lr0, slr1]') - return None - if lr.errors: - errors.extend(lr.errors) - # return None - return LanguageLR(Gramm,lex_gram,lr) - return None - -class LanguageLR(Language): - - def __init__(self, grammar, lexer, parser_lr,token_parse_dict={}): - super().__init__(grammar,lexer,token_parse_dict) - self.parser = parser_lr - - def get_derivation_tree(self,parse): - return self.parser.get_derivation_tree(parse,False) +from lib.parser.parser_lr import LR0Parser,LR1Parser,SLR1Parser,LALR1Parser +from lib.grammar.grammar_parser import get_grammar_from_text +from lib.grammar.grammar_tokens import get_lexer_from_text +from lib.lang.language import Language + +def return_lr_parser(G,parser): + parser = parser.lower() + if parser == 'lr0': + lr = LR0Parser(G) + elif parser == 'lalr1': + lr = LALR1Parser(G) + elif parser == 'slr1': + lr = SLR1Parser(G) + elif parser == 'lr1': + lr = LR1Parser(G) + else: + lr = None + return lr + +def build_LR(tok_def,gram_def,lr:str,errors): + """ + gram_def: grammar definition or an instance of Grammar\n + tok_def: tokens deefinition or an instance of Lexer\n + lr: type of lr parser, can be ['lalr1', 'lr1', 'lr0', 'slr1']\n + return a LanguageLR\n + return None in case of errors + """ + new_errors = [] + + lex_gram = get_lexer_from_text(tok_def,new_errors) if isinstance(tok_def,str) else tok_def + Gramm = get_grammar_from_text(gram_def,new_errors) if isinstance(gram_def,str) else gram_def + + for x in new_errors: + errors.append(x) + if lex_gram and Gramm: + lr = return_lr_parser(Gramm,lr) + if not lr: + errors.append(f'{lr} is not a valid LR type of parser, try [lalr1, lr1, lr0, slr1]') + return None + if lr.errors: + errors.extend(lr.errors) + # return None + return LanguageLR(Gramm,lex_gram,lr) + return None + +class LanguageLR(Language): + + def __init__(self, grammar, lexer, parser_lr,token_parse_dict={}): + super().__init__(grammar,lexer,token_parse_dict) + self.parser = parser_lr + + def get_derivation_tree(self,parse): + return self.parser.get_derivation_tree(parse,False) diff --git a/src/cool2/lib/lang/language_regular.py b/src/cool2/lib/lang/language_regular.py index 1e233fabe..fc5a87430 100644 --- a/src/cool2/lib/lang/language_regular.py +++ b/src/cool2/lib/lang/language_regular.py @@ -1,397 +1,397 @@ -from cmp.pycompiler import (EOF, Epsilon, Grammar, NonTerminal, Production, - Sentence, SentenceFromIter, SentenceList, Symbol, - Terminal) -from cmp.utils import ContainerSet, Token, inspect, pprint -from lib.grammar.grammar_parser import get_grammar_from_text -from lib.grammar.grammar_tokens import get_lexer_dict_from_text,get_lexer_from_text -from lib.lang.language_lr import LanguageLR,return_lr_parser -from lib.lexer.lexer import cap_letters, digits, min_letters -from lib.lexer.regex import EPSILON, regex_automaton -from lib.utils.automaton import * - -def copy_automaton(base_automaton): - """ - Returns a tuple of base_automaton properties\n - start, states, finals, transitions - """ - start = base_automaton.start - dif = base_automaton.states - finals = base_automaton.finals.copy() - - transitions = {} - for x in base_automaton.transitions: - for y in base_automaton.transitions[x]: - transitions[x,y] = base_automaton.transitions[x][y] - - return start,dif,finals,transitions - -def join(raw_automaton,raw_automaton_join,state1,state2): - """ - raw_automaton, raw_automaton_join: tuples of copy_automatons\n - state1, state2: states of raw_automaton\n - insert raw_automaton_join in raw_automaton between state1 and state2\n - ______ ______ - (state1)--[symb in transitions[start of join automaton]]-->raw_automaton_join--e-->(state2) - """ - start, dif, finals, transitions = raw_automaton - j_start, j_dif, j_finals, j_transitions = raw_automaton_join - - for symb in [s for x,s in j_transitions if x == j_start]: - try: - transitions[state1,symb].append(j_transitions[j_start,symb][0] + dif) - except KeyError: - transitions[state1,symb] = [j_transitions[j_start,symb][0] + dif] - - for f in j_finals: - transitions[f+dif,''] = [state2] - - for state,symb in j_transitions: - transitions[state + dif,symb] = [j_transitions[state,symb][0] + dif] - - return start,dif+j_dif,finals,transitions - -def join_automaton(base_automaton,lex_dic): ################ convertir los States de lex_dic en DFA - """ - base_automaton: automaton from grammar, (deterministic) - lex_dic: dictionary that maps the terminals to the automaton that recognize it - """ - - start, dif, finals, transitions = copy_automaton(base_automaton) - fixed_transitions = transitions.copy() - transitions = {} - raw = start, dif, finals, transitions - cached_automaton = {} - - for state,terminal in fixed_transitions: - try: - au = cur_start, cur_states, cur_finals, cur_transitions = cached_automaton[terminal] - except KeyError: - automaton = lex_dic[terminal] - au = cur_start, cur_states, cur_finals, cur_transitions = copy_automaton(automaton) - cached_automaton[terminal] = au - - raw = join(raw,au,state,fixed_transitions[state,terminal][0]) - - nfa = NFA(raw[1],raw[2],raw[3],raw[0]) - dfa = nfa_to_dfa(nfa) - mini = automata_minimization(dfa) - - return mini - -def build_automaton_from_grammar(tok_def,gram_def,errors,lexer=None,grammar=None): - """ - Return the NFA if the grammar is regular - else return None - """ - new_errors = [] - - lex = get_lexer_from_text(tok_def,errors) if not lexer else lexer - G = get_grammar_from_text(gram_def,errors) if not grammar else grammar - - if G == None or lex == None: - errors.append('Invalid grammar or token definition') - return None - - if not check_grammar_regular(G, errors): - errors.append('Invalid Regular Grammar') - return None - - symb_num = {} - transitions = {} - itr1 = iter(range(len(G.nonTerminals))) - for x in G.nonTerminals: - symb_num[x] = next(itr1) - - start = symb_num[G.startSymbol] - finals = [len(G.nonTerminals)] - - distinguish_on_epsilon = any([x for x in G.startSymbol.productions if x.IsEpsilon]) - if distinguish_on_epsilon: finals.append(start) - - for prod in G.Productions: - left = symb_num[prod.Left] - len_right = len(prod.Right) - first_symbol = prod.Right[0] - if len_right == 2: - try: - transitions[left,first_symbol.Name].add(symb_num[prod.Right[1]]) - except KeyError: - transitions[left,first_symbol.Name] = {symb_num[prod.Right[1]]} - elif len_right == 1: - try: - transitions[left,first_symbol.Name].add(finals[0]) - except KeyError: - transitions[left,first_symbol.Name] = {finals[0]} - - nfa = NFA(len(G.nonTerminals)+1,finals,transitions,start) - dfa = nfa_to_dfa(nfa) - mini = automata_minimization(dfa) - - return mini # join_automaton(mini,lex.regexs) ############################################## - -def check_grammar_regular(G:Grammar,errors): - """ - return True if the Grammar G is regular - """ - new_errors = [] - for prod in G.Productions: - left = prod.Left - right = prod.Right - if right.IsEpsilon : - if left != G.startSymbol: - new_errors.append(f'Invalid Production for Regular Grammar: {prod}; the only left part that can have an epsilon on right part is the distiguish state') - continue - if len(right) > 2: - new_errors.append(f'Invalid Production for Regular Grammar: {prod}; the lenght of right part must be at most 2 and is {len(right)}') - else: - if not right[0].IsTerminal: - new_errors.append(f'Invalid Production for Regular Grammar: {prod}; the first symbol on a right part must be a terminal') - if len(right) == 2: - if right[1].IsTerminal: - new_errors.append(f'Invalid Production for Regular Grammar: {prod}; the second symbol on a right part must be a non terminal') - errors.extend(new_errors) - return not bool(new_errors) - -# REGULAR GRAMMAR TO REGEX - -def eliminate_state(tupled_automaton,s): - start,states,finals,transitions = tupled_automaton - - assert s != start,"you cant eliminate the start state" - - start = start if start < s else start - 1 - try: - finals.remove(s) - except: - pass - finals = { x for x in map(lambda x: x if x < s else x-1,finals)} - - exit_s = [] - enter_s = [] - other = [] - new_transitions = {} - cycle = '' - - for x in transitions.items(): # cycles in s dont go anywhere just 'activate' cycle variable - (state,value),regex = x - if state == s: - if value == s: - cycle = f'({regex})*' - continue - exit_s.append(x) - elif value == s: - enter_s.append(x) - else: - other.append(x) - - for (state0, value0),regex0 in enter_s: - for (state1, value1),regex1 in exit_s: - third = first(lambda x: x[0][0] == state0 and x[0][1] == value1,other) - if third: - other.remove(third) - transition = f"{third[1]}|" if third else "" - state = state0 if state0 < s else state0 - 1 - value = value1 if value1 < s else value1 - 1 - new_transitions[state,value] = f"({transition}{regex0}{cycle}{regex1})" - - for (state0, value0),regex0 in other: - state0 = state0 if state0 < s else state0 - 1 - value0 = value0 if value0 < s else value0 - 1 - if [x for (state,value), regex in new_transitions.items() if state == state0 and value == value0]: - print("WTF!!") - new_transitions[state0,value0] = regex0 - - return start,states-1,finals,new_transitions - -def eliminate_all_non_start_non_finals(tupled_automaton): - """ - return a tuple of start,states,finals,transitions of the automaton from eliminate all the \n - non start and the non finals states - """ - start,states,finals,transitions = tupled_automaton - change = True - while change: - change = False - for s in range(states): - if not s in finals and s != start: - change = True - tupled_automaton = start,states,finals,transitions = eliminate_state(tupled_automaton,s) - break - return tupled_automaton - -def eliminate_all_non_start_non_t(tupled_automaton,t): - """ - return a tuple of start,states,finals,transitions of the automaton from eliminate all the \n - non start and the non t states - """ - start,states,finals,transitions = tupled_automaton - change = True - while change: - change = False - for s in range(states): - if s != t and s != start: - change = True - tupled_automaton = start,states,finals,transitions = eliminate_state(tupled_automaton,s) - t = t if s > t else t-1 - break - return tupled_automaton - -def get_key(start,end,transitions): - # key = first(lambda x: x[0][0] == start and x[1] == end,transitions.items()) - try: - key = transitions[start,end] - except KeyError: - key = '' - return key#[0][1] if key else '' - -def get_regex_from_reduced_automaton(tupled_automaton): - """ - tupled_automaton: an automaton with de form:\n - (A)--regex->(A)<-regex_left regex_right->(B)<-regex--(B)\n - or\n - (AB)--regex->(AB)\n - where A is start and B is final\n - """ - start,states,finals,transitions = tupled_automaton - if states == 2: - other = 0 if start == 1 else 1 - A = get_key(start,start,transitions) - B = get_key(start,other,transitions) - C = get_key(other,other,transitions) - D = get_key(other,start,transitions) - if not B: - return '' - if A and C and D: - return f"({A}|{B}{C}*{D})*{B}{C}*" - if A and D: - return f"({A}|{B}{D})*{B}" - if A and C: - return f"{A}*{B}{C}*" - if A: - return f"{A}*{B}" - if C and D: - return f"({B}{C}*{D})*{B}{C}*" - if C: - return f"{B}{C}*" - if D: - return f"({B}{D})*{B}" - return f"{B}" - if states == 1: - assert start in finals, "start is not a final state" - A = get_key(start,start,transitions) - if A: - return f"{A}*" - else: - return '' - assert False, "Wrong number of states in the tupled automaton" - -def regex_from_clean_automaton(tupled_automaton,s): - """ - tupled_automaton: an automaton with only finals states and the start state - s: final state to find regex - return the regex from eliminate all the states except the start state and the s state - """ - tupled_automaton = eliminate_all_non_start_non_t(tupled_automaton,s) - - return get_regex_from_reduced_automaton(tupled_automaton) - -def convert_to_regex_automaton(tupled_automaton): - """ - join al the transitions from state x to state y in one equivalent regex\n - example:\n - (A)-a,b,c->(B) => (A)-a|b|c->(B) - """ - start,states,finals,transitions = tupled_automaton - - temp_dic = {} - for (state,symbol),values in transitions.items(): - for value in values: - try: - regex = temp_dic[state,value] - regex = regex[:len(regex)-1] + f"|{symbol})" - except KeyError: - regex = f'({symbol})' - temp_dic[state,value] = regex - - transitions = temp_dic - - return start,states,finals,transitions - -def build_regex_from_grammar(tok_def,gram_def,errors:list,lexer=None,grammar=None,automaton=None): - """ - return the regex and the automaton from tok_def and gram_def or from lexer and grammar if automaton is not given\n - else return the regex of the automaton given - """ - new_errors = [] - - if not automaton: - automaton = build_automaton_from_grammar(tok_def,gram_def,new_errors,lexer,grammar) - if not automaton: - errors.extend(new_errors) - return None - - tupled_automaton = copy_automaton(automaton) - tupled_automaton = convert_to_regex_automaton(tupled_automaton) - tupled_automaton = start,states,finals,transitions = eliminate_all_non_start_non_finals(tupled_automaton) - iter_finals = finals.copy() - regex = [] - for s in iter_finals: - new_transitions = transitions.copy() - new_finals = finals.copy() - automaton = start,states,new_finals,new_transitions - new_regex = regex_from_clean_automaton(automaton,s) - regex.append(new_regex) - - if new_errors: - errors.extend(new_errors) - - regex = '|'.join(regex) - - tupl = regex_automaton(regex,return_regex=True) - return tupl[1] - - - -def build_Lan_Reg(tok_def,gram_def,errors): - """ - gram_def: grammar definition or an instance of Grammar\n - tok_def: tokens deefinition or an instance of Lexer\n - return a LanguageRegular\n - return None in case of errors - """ - lexer = get_lexer_from_text(tok_def,errors) if isinstance(tok_def,str) else tok_def - grammar = get_grammar_from_text(gram_def,errors) if isinstance(gram_def,str) else gram_def - - if not grammar or not lexer: - errors.append('Invalid grammar or token definition') - return None - - if not check_grammar_regular(grammar,errors): - errors.append('Invalid Regular Grammar') - return None - - return LanguageRegular(grammar,lexer) - -class LanguageRegular(LanguageLR): - - grammar_regex = None - - grammar_automaton = None - - def __init__(self, grammar, lexer): - super().__init__(grammar,lexer,None) - - def grammar_to_regex(self,errors): - self.grammar_regex = self.grammar_regex if self.grammar_regex else build_regex_from_grammar('','',errors,self.lexer,self.grammar,self.grammar_to_automaton(errors)) - return self.grammar_regex - - def grammar_to_automaton(self,errors): - self.grammar_automaton = self.grammar_automaton if self.grammar_automaton else build_automaton_from_grammar('','',errors,self.lexer,self.grammar) - return self.grammar_automaton - - def __call__(self, string,errors): - automaton = self.grammar_to_automaton(errors) - if automaton: - return automaton.recognize(string),None - return None,None - +from cmp.pycompiler import (EOF, Epsilon, Grammar, NonTerminal, Production, + Sentence, SentenceFromIter, SentenceList, Symbol, + Terminal) +from cmp.utils import ContainerSet, Token, inspect, pprint +from lib.grammar.grammar_parser import get_grammar_from_text +from lib.grammar.grammar_tokens import get_lexer_dict_from_text,get_lexer_from_text +from lib.lang.language_lr import LanguageLR,return_lr_parser +from lib.lexer.lexer import cap_letters, digits, min_letters +from lib.lexer.regex import EPSILON, regex_automaton +from lib.utils.automaton import * + +def copy_automaton(base_automaton): + """ + Returns a tuple of base_automaton properties\n + start, states, finals, transitions + """ + start = base_automaton.start + dif = base_automaton.states + finals = base_automaton.finals.copy() + + transitions = {} + for x in base_automaton.transitions: + for y in base_automaton.transitions[x]: + transitions[x,y] = base_automaton.transitions[x][y] + + return start,dif,finals,transitions + +def join(raw_automaton,raw_automaton_join,state1,state2): + """ + raw_automaton, raw_automaton_join: tuples of copy_automatons\n + state1, state2: states of raw_automaton\n + insert raw_automaton_join in raw_automaton between state1 and state2\n + ______ ______ + (state1)--[symb in transitions[start of join automaton]]-->raw_automaton_join--e-->(state2) + """ + start, dif, finals, transitions = raw_automaton + j_start, j_dif, j_finals, j_transitions = raw_automaton_join + + for symb in [s for x,s in j_transitions if x == j_start]: + try: + transitions[state1,symb].append(j_transitions[j_start,symb][0] + dif) + except KeyError: + transitions[state1,symb] = [j_transitions[j_start,symb][0] + dif] + + for f in j_finals: + transitions[f+dif,''] = [state2] + + for state,symb in j_transitions: + transitions[state + dif,symb] = [j_transitions[state,symb][0] + dif] + + return start,dif+j_dif,finals,transitions + +def join_automaton(base_automaton,lex_dic): ################ convertir los States de lex_dic en DFA + """ + base_automaton: automaton from grammar, (deterministic) + lex_dic: dictionary that maps the terminals to the automaton that recognize it + """ + + start, dif, finals, transitions = copy_automaton(base_automaton) + fixed_transitions = transitions.copy() + transitions = {} + raw = start, dif, finals, transitions + cached_automaton = {} + + for state,terminal in fixed_transitions: + try: + au = cur_start, cur_states, cur_finals, cur_transitions = cached_automaton[terminal] + except KeyError: + automaton = lex_dic[terminal] + au = cur_start, cur_states, cur_finals, cur_transitions = copy_automaton(automaton) + cached_automaton[terminal] = au + + raw = join(raw,au,state,fixed_transitions[state,terminal][0]) + + nfa = NFA(raw[1],raw[2],raw[3],raw[0]) + dfa = nfa_to_dfa(nfa) + mini = automata_minimization(dfa) + + return mini + +def build_automaton_from_grammar(tok_def,gram_def,errors,lexer=None,grammar=None): + """ + Return the NFA if the grammar is regular + else return None + """ + new_errors = [] + + lex = get_lexer_from_text(tok_def,errors) if not lexer else lexer + G = get_grammar_from_text(gram_def,errors) if not grammar else grammar + + if G == None or lex == None: + errors.append('Invalid grammar or token definition') + return None + + if not check_grammar_regular(G, errors): + errors.append('Invalid Regular Grammar') + return None + + symb_num = {} + transitions = {} + itr1 = iter(range(len(G.nonTerminals))) + for x in G.nonTerminals: + symb_num[x] = next(itr1) + + start = symb_num[G.startSymbol] + finals = [len(G.nonTerminals)] + + distinguish_on_epsilon = any([x for x in G.startSymbol.productions if x.IsEpsilon]) + if distinguish_on_epsilon: finals.append(start) + + for prod in G.Productions: + left = symb_num[prod.Left] + len_right = len(prod.Right) + first_symbol = prod.Right[0] + if len_right == 2: + try: + transitions[left,first_symbol.Name].add(symb_num[prod.Right[1]]) + except KeyError: + transitions[left,first_symbol.Name] = {symb_num[prod.Right[1]]} + elif len_right == 1: + try: + transitions[left,first_symbol.Name].add(finals[0]) + except KeyError: + transitions[left,first_symbol.Name] = {finals[0]} + + nfa = NFA(len(G.nonTerminals)+1,finals,transitions,start) + dfa = nfa_to_dfa(nfa) + mini = automata_minimization(dfa) + + return mini # join_automaton(mini,lex.regexs) ############################################## + +def check_grammar_regular(G:Grammar,errors): + """ + return True if the Grammar G is regular + """ + new_errors = [] + for prod in G.Productions: + left = prod.Left + right = prod.Right + if right.IsEpsilon : + if left != G.startSymbol: + new_errors.append(f'Invalid Production for Regular Grammar: {prod}; the only left part that can have an epsilon on right part is the distiguish state') + continue + if len(right) > 2: + new_errors.append(f'Invalid Production for Regular Grammar: {prod}; the lenght of right part must be at most 2 and is {len(right)}') + else: + if not right[0].IsTerminal: + new_errors.append(f'Invalid Production for Regular Grammar: {prod}; the first symbol on a right part must be a terminal') + if len(right) == 2: + if right[1].IsTerminal: + new_errors.append(f'Invalid Production for Regular Grammar: {prod}; the second symbol on a right part must be a non terminal') + errors.extend(new_errors) + return not bool(new_errors) + +# REGULAR GRAMMAR TO REGEX + +def eliminate_state(tupled_automaton,s): + start,states,finals,transitions = tupled_automaton + + assert s != start,"you cant eliminate the start state" + + start = start if start < s else start - 1 + try: + finals.remove(s) + except: + pass + finals = { x for x in map(lambda x: x if x < s else x-1,finals)} + + exit_s = [] + enter_s = [] + other = [] + new_transitions = {} + cycle = '' + + for x in transitions.items(): # cycles in s dont go anywhere just 'activate' cycle variable + (state,value),regex = x + if state == s: + if value == s: + cycle = f'({regex})*' + continue + exit_s.append(x) + elif value == s: + enter_s.append(x) + else: + other.append(x) + + for (state0, value0),regex0 in enter_s: + for (state1, value1),regex1 in exit_s: + third = first(lambda x: x[0][0] == state0 and x[0][1] == value1,other) + if third: + other.remove(third) + transition = f"{third[1]}|" if third else "" + state = state0 if state0 < s else state0 - 1 + value = value1 if value1 < s else value1 - 1 + new_transitions[state,value] = f"({transition}{regex0}{cycle}{regex1})" + + for (state0, value0),regex0 in other: + state0 = state0 if state0 < s else state0 - 1 + value0 = value0 if value0 < s else value0 - 1 + if [x for (state,value), regex in new_transitions.items() if state == state0 and value == value0]: + print("WTF!!") + new_transitions[state0,value0] = regex0 + + return start,states-1,finals,new_transitions + +def eliminate_all_non_start_non_finals(tupled_automaton): + """ + return a tuple of start,states,finals,transitions of the automaton from eliminate all the \n + non start and the non finals states + """ + start,states,finals,transitions = tupled_automaton + change = True + while change: + change = False + for s in range(states): + if not s in finals and s != start: + change = True + tupled_automaton = start,states,finals,transitions = eliminate_state(tupled_automaton,s) + break + return tupled_automaton + +def eliminate_all_non_start_non_t(tupled_automaton,t): + """ + return a tuple of start,states,finals,transitions of the automaton from eliminate all the \n + non start and the non t states + """ + start,states,finals,transitions = tupled_automaton + change = True + while change: + change = False + for s in range(states): + if s != t and s != start: + change = True + tupled_automaton = start,states,finals,transitions = eliminate_state(tupled_automaton,s) + t = t if s > t else t-1 + break + return tupled_automaton + +def get_key(start,end,transitions): + # key = first(lambda x: x[0][0] == start and x[1] == end,transitions.items()) + try: + key = transitions[start,end] + except KeyError: + key = '' + return key#[0][1] if key else '' + +def get_regex_from_reduced_automaton(tupled_automaton): + """ + tupled_automaton: an automaton with de form:\n + (A)--regex->(A)<-regex_left regex_right->(B)<-regex--(B)\n + or\n + (AB)--regex->(AB)\n + where A is start and B is final\n + """ + start,states,finals,transitions = tupled_automaton + if states == 2: + other = 0 if start == 1 else 1 + A = get_key(start,start,transitions) + B = get_key(start,other,transitions) + C = get_key(other,other,transitions) + D = get_key(other,start,transitions) + if not B: + return '' + if A and C and D: + return f"({A}|{B}{C}*{D})*{B}{C}*" + if A and D: + return f"({A}|{B}{D})*{B}" + if A and C: + return f"{A}*{B}{C}*" + if A: + return f"{A}*{B}" + if C and D: + return f"({B}{C}*{D})*{B}{C}*" + if C: + return f"{B}{C}*" + if D: + return f"({B}{D})*{B}" + return f"{B}" + if states == 1: + assert start in finals, "start is not a final state" + A = get_key(start,start,transitions) + if A: + return f"{A}*" + else: + return '' + assert False, "Wrong number of states in the tupled automaton" + +def regex_from_clean_automaton(tupled_automaton,s): + """ + tupled_automaton: an automaton with only finals states and the start state + s: final state to find regex + return the regex from eliminate all the states except the start state and the s state + """ + tupled_automaton = eliminate_all_non_start_non_t(tupled_automaton,s) + + return get_regex_from_reduced_automaton(tupled_automaton) + +def convert_to_regex_automaton(tupled_automaton): + """ + join al the transitions from state x to state y in one equivalent regex\n + example:\n + (A)-a,b,c->(B) => (A)-a|b|c->(B) + """ + start,states,finals,transitions = tupled_automaton + + temp_dic = {} + for (state,symbol),values in transitions.items(): + for value in values: + try: + regex = temp_dic[state,value] + regex = regex[:len(regex)-1] + f"|{symbol})" + except KeyError: + regex = f'({symbol})' + temp_dic[state,value] = regex + + transitions = temp_dic + + return start,states,finals,transitions + +def build_regex_from_grammar(tok_def,gram_def,errors:list,lexer=None,grammar=None,automaton=None): + """ + return the regex and the automaton from tok_def and gram_def or from lexer and grammar if automaton is not given\n + else return the regex of the automaton given + """ + new_errors = [] + + if not automaton: + automaton = build_automaton_from_grammar(tok_def,gram_def,new_errors,lexer,grammar) + if not automaton: + errors.extend(new_errors) + return None + + tupled_automaton = copy_automaton(automaton) + tupled_automaton = convert_to_regex_automaton(tupled_automaton) + tupled_automaton = start,states,finals,transitions = eliminate_all_non_start_non_finals(tupled_automaton) + iter_finals = finals.copy() + regex = [] + for s in iter_finals: + new_transitions = transitions.copy() + new_finals = finals.copy() + automaton = start,states,new_finals,new_transitions + new_regex = regex_from_clean_automaton(automaton,s) + regex.append(new_regex) + + if new_errors: + errors.extend(new_errors) + + regex = '|'.join(regex) + + tupl = regex_automaton(regex,return_regex=True) + return tupl[1] + + + +def build_Lan_Reg(tok_def,gram_def,errors): + """ + gram_def: grammar definition or an instance of Grammar\n + tok_def: tokens deefinition or an instance of Lexer\n + return a LanguageRegular\n + return None in case of errors + """ + lexer = get_lexer_from_text(tok_def,errors) if isinstance(tok_def,str) else tok_def + grammar = get_grammar_from_text(gram_def,errors) if isinstance(gram_def,str) else gram_def + + if not grammar or not lexer: + errors.append('Invalid grammar or token definition') + return None + + if not check_grammar_regular(grammar,errors): + errors.append('Invalid Regular Grammar') + return None + + return LanguageRegular(grammar,lexer) + +class LanguageRegular(LanguageLR): + + grammar_regex = None + + grammar_automaton = None + + def __init__(self, grammar, lexer): + super().__init__(grammar,lexer,None) + + def grammar_to_regex(self,errors): + self.grammar_regex = self.grammar_regex if self.grammar_regex else build_regex_from_grammar('','',errors,self.lexer,self.grammar,self.grammar_to_automaton(errors)) + return self.grammar_regex + + def grammar_to_automaton(self,errors): + self.grammar_automaton = self.grammar_automaton if self.grammar_automaton else build_automaton_from_grammar('','',errors,self.lexer,self.grammar) + return self.grammar_automaton + + def __call__(self, string,errors): + automaton = self.grammar_to_automaton(errors) + if automaton: + return automaton.recognize(string),None + return None,None + diff --git a/src/cool2/lib/lexer/lexer.py b/src/cool2/lib/lexer/lexer.py index 95f3f8bdb..2f5e89fd3 100644 --- a/src/cool2/lib/lexer/lexer.py +++ b/src/cool2/lib/lexer/lexer.py @@ -1,116 +1,116 @@ - -from cmp.utils import Token -from cmp.automata import State -from lib.lexer.regex import regex_automaton - -class DetailToken(Token): - def __init__(self, lex,token_type): - super().__init__(lex,token_type) - self.row = lex[1] - self.column = lex[2] - -class Lexer: - def __init__(self, table, eof): - self.eof = eof - self.regexs = self._build_regexs(table) - self.automaton = self._build_automaton() - self.table = { x:y for x,y in table} - - def _build_regexs(self, table): - regexs = {} - for n, (token_type, regex) in enumerate(table): - # Your code here!!! - # - Remember to tag the final states with the token_type and priority. - # - .tag might be useful for that purpose ;-) - auto = regex_automaton(regex) - auto, states = State.from_nfa(auto,True) - for st in states: - if st.final: - st.tag = (n,token_type) - regexs[token_type] = auto - return regexs - - def _build_automaton(self): - start = State('start') - # Your code here!!! - for x in self.regexs: - start.add_epsilon_transition(self.regexs[x]) - return start.to_deterministic() - - def _walk(self, string): - state = self.automaton - final = state if state.final else None - final_lex = lex = '' - - for symbol in string: - # Your code here!!! - if state.has_transition(symbol): - state = state[symbol][0] - if state.final: - final = state - final_lex = lex - # else: - # final = None - else: - final_lex = lex - break - lex += symbol - else: - final_lex = lex - - return final, final_lex - - def _tokenize(self, text): - - while text: - stop_state, lex = self._walk(text) - - if stop_state and len(lex)>0: - st_min = min([st.tag for st in [ final for final in stop_state.state if final.final]]) - yield lex,st_min[1] - else: - if len(lex) == 0: - lex = text[0] - yield lex,'UNKNOWN' - - text = text[len(lex):] - - yield '$', self.eof - - def __call__(self, text): - return [ Token(lex, ttype) for lex, ttype in self._tokenize(text) ] - -class DetailLexer(Lexer): - - def _tokenize(self, text): - row,column = 0,0 - while text: - stop_state, lex = self._walk(text) - - rows_in_lex = len([x for x in lex if x == '\n']) - row += rows_in_lex - if rows_in_lex: - column = len(lex.split('\n')[-1]) - else: - column += len(lex) - - if stop_state and len(lex)>0: - st_min = min([st.tag for st in [ final for final in stop_state.state if final.final]]) - yield (lex,row,column),st_min[1] - else: - if len(lex) == 0: - lex = text[0] - yield (lex,row,column),'UNKNOWN' - - text = text[len(lex):] - - yield ('$',row,column), self.eof - - def __call__(self, text): - return [ DetailToken(lex, ttype) for lex, ttype in self._tokenize(text) ] - - -nonzero_digits = '[123456789]' -min_letters = '[abcdefghijklmnopqrstuvwxyz]' -cap_letters = '[ABCDEFGHIJKLMNOPQRSTUVWXYZ]' -digits = f'({nonzero_digits}|0)' + +from cmp.utils import Token +from cmp.automata import State +from lib.lexer.regex import regex_automaton + +class DetailToken(Token): + def __init__(self, lex,token_type): + super().__init__(lex,token_type) + self.row = lex[1] + self.column = lex[2] + +class Lexer: + def __init__(self, table, eof): + self.eof = eof + self.regexs = self._build_regexs(table) + self.automaton = self._build_automaton() + self.table = { x:y for x,y in table} + + def _build_regexs(self, table): + regexs = {} + for n, (token_type, regex) in enumerate(table): + # Your code here!!! + # - Remember to tag the final states with the token_type and priority. + # - .tag might be useful for that purpose ;-) + auto = regex_automaton(regex) + auto, states = State.from_nfa(auto,True) + for st in states: + if st.final: + st.tag = (n,token_type) + regexs[token_type] = auto + return regexs + + def _build_automaton(self): + start = State('start') + # Your code here!!! + for x in self.regexs: + start.add_epsilon_transition(self.regexs[x]) + return start.to_deterministic() + + def _walk(self, string): + state = self.automaton + final = state if state.final else None + final_lex = lex = '' + + for symbol in string: + # Your code here!!! + if state.has_transition(symbol): + state = state[symbol][0] + if state.final: + final = state + final_lex = lex + # else: + # final = None + else: + final_lex = lex + break + lex += symbol + else: + final_lex = lex + + return final, final_lex + + def _tokenize(self, text): + + while text: + stop_state, lex = self._walk(text) + + if stop_state and len(lex)>0: + st_min = min([st.tag for st in [ final for final in stop_state.state if final.final]]) + yield lex,st_min[1] + else: + if len(lex) == 0: + lex = text[0] + yield lex,'UNKNOWN' + + text = text[len(lex):] + + yield '$', self.eof + + def __call__(self, text): + return [ Token(lex, ttype) for lex, ttype in self._tokenize(text) ] + +class DetailLexer(Lexer): + + def _tokenize(self, text): + row,column = 0,0 + while text: + stop_state, lex = self._walk(text) + + rows_in_lex = len([x for x in lex if x == '\n']) + row += rows_in_lex + if rows_in_lex: + column = len(lex.split('\n')[-1]) + else: + column += len(lex) + + if stop_state and len(lex)>0: + st_min = min([st.tag for st in [ final for final in stop_state.state if final.final]]) + yield (lex,row,column),st_min[1] + else: + if len(lex) == 0: + lex = text[0] + yield (lex,row,column),'UNKNOWN' + + text = text[len(lex):] + + yield ('$',row,column), self.eof + + def __call__(self, text): + return [ DetailToken(lex, ttype) for lex, ttype in self._tokenize(text) ] + + +nonzero_digits = '[123456789]' +min_letters = '[abcdefghijklmnopqrstuvwxyz]' +cap_letters = '[ABCDEFGHIJKLMNOPQRSTUVWXYZ]' +digits = f'({nonzero_digits}|0)' diff --git a/src/cool2/lib/lexer/regex.py b/src/cool2/lib/lexer/regex.py index 567467c2e..83ff9723b 100644 --- a/src/cool2/lib/lexer/regex.py +++ b/src/cool2/lib/lexer/regex.py @@ -1,147 +1,147 @@ -from cmp.ast import Node, AtomicNode, UnaryNode, BinaryNode -from cmp.utils import Token -from cmp.pycompiler import (EOF, Epsilon, Grammar, NonTerminal, Production, - Sentence, SentenceList, Symbol, Terminal) -from lib.utils.automaton import * -from lib.parser.parser_ll1 import ParserLL1 - - -G = Grammar() - -EPSILON = 'ε' - -E = G.NonTerminal('E', True) -T, F, A, X, Y, Z, R, Q = G.NonTerminals('T F A X Y Z R Q') -pipe, star, opar, cpar, symbol, epsilon, plus, qtn, obra, cbra = G.Terminals(f'| * ( ) symbol {EPSILON} + ? [ ]') - -# Your code here!!! -E %= T + X, lambda h,s: s[2], None,lambda h,s: s[1] - -X %= pipe + T + X, lambda h,s: UnionNode(h[0],s[3]), None, None,lambda h,s: s[2] -X %= G.Epsilon, lambda h,s: h[0] - -T %= A + Y, lambda h,s: s[2], None,lambda h,s: s[1] - -Y %= A + Y, lambda h,s: ConcatNode(h[0],s[2]), None,lambda h,s: s[1] -Y %= G.Epsilon, lambda h,s: h[0] - -A %= Z + F, lambda h,s: s[2] ,None ,lambda h,s: s[1] - -F %= star + F, lambda h,s: s[2], None, lambda h,s: ClosureNode(h[0]) -F %= plus + F, lambda h,s: s[2],None,lambda h,s: PlusNode(h[0]) -F %= qtn + F, lambda h,s: s[2], None, lambda h,s: QuestionNode(h[0]) -F %= G.Epsilon, lambda h,s: h[0] - -R %= A + Q, lambda h,s: s[2], None,lambda h,s: s[1] - -Q %= A + Q, lambda h,s: UnionNode(h[0],s[2]), None, lambda h,s: s[1] -Q %= G.Epsilon, lambda h,s: h[0] - -Z %= symbol, lambda h,s: SymbolNode(s[1]), None -Z %= opar + E + cpar, lambda h,s: s[2], None, None, None -Z %= obra + R + cbra, lambda h,s: s[2], None, None, None -Z %= epsilon, lambda h,s: EpsilonNode(s[1]), None - - -regex_parser = ParserLL1(G) - -class EpsilonNode(AtomicNode): - def evaluate(self): - automata = NFA(1,{0},{},0) - return '',automata - -class SymbolNode(AtomicNode): - def evaluate(self): - s = self.lex - # Your code here!!! - automata = NFA(2,{1},{(0,s):[1]},0) - return f'{s}',automata - -class ClosureNode(UnaryNode): - @staticmethod - def operate(value): - # Your code here!!! - return f'({value[0]})*',automata_closure(value[1]) - -class UnionNode(BinaryNode): - @staticmethod - def operate(lvalue, rvalue): - # Your code here!!! - return f'({lvalue[0]}|{rvalue[0]})',automata_union(lvalue[1],rvalue[1]) - -class ConcatNode(BinaryNode): - @staticmethod - def operate(lvalue, rvalue): - # Your code here!!! - return f'{lvalue[0]}{rvalue[0]}',automata_concatenation(lvalue[1],rvalue[1]) - -class PlusNode(UnaryNode): - @staticmethod - def operate(value): - # Your code here!!! - return f'({value[0]})+',automata_concatenation(value[1],automata_closure(value[1])) - -class QuestionNode(UnaryNode): - @staticmethod - def operate(value): - # Your code here!!! - epsilon = NFA(1,{0},{},0) - return f'({value[0]})?',automata_union(value[1],epsilon) - -fixed_tokens = { - '*' : Token('*' , star), - '(' : Token('(' , opar), - ')' : Token(')' , cpar), - '|' : Token('|' , pipe), - '?' : Token('?' , qtn), - '+' : Token('+' , plus), - '[' : Token('[' , obra), - ']' : Token(']' , cbra), - EPSILON : Token(EPSILON , epsilon), - } - -def regex_tokenizer(text, G, skip_whitespaces=True): - tokens = [] - # > fixed_tokens = ??? - # Your code here!!! - - skip = False - for i, char in enumerate(text): - - if skip: - skip = False - continue - - if skip_whitespaces and char.isspace(): - continue - # Your code here!!! - if char == '\\': - try: - tokens.append(Token(text[i+1],symbol)) - except IndexError: - tokens.append(Token('\\',symbol)) - skip = True - continue - - try: - tokens.append(fixed_tokens[char]) - except KeyError: - tokens.append(Token(char,symbol)) - - tokens.append(Token('$', G.EOF)) - return tokens - -def regex_automaton(regex,skip_whitespces=False,return_regex = False): - """ - returns the minimal dfa that recognize regex - """ - tokens = regex_tokenizer(regex,G,skip_whitespces) - errors = [] - regex,nfa = regex_parser.evaluate(tokens,errors) - if nfa: - dfa = nfa_to_dfa(nfa) - mini = automata_minimization(dfa) - if return_regex: - return mini,regex - return mini +from cmp.ast import Node, AtomicNode, UnaryNode, BinaryNode +from cmp.utils import Token +from cmp.pycompiler import (EOF, Epsilon, Grammar, NonTerminal, Production, + Sentence, SentenceList, Symbol, Terminal) +from lib.utils.automaton import * +from lib.parser.parser_ll1 import ParserLL1 + + +G = Grammar() + +EPSILON = 'ε' + +E = G.NonTerminal('E', True) +T, F, A, X, Y, Z, R, Q = G.NonTerminals('T F A X Y Z R Q') +pipe, star, opar, cpar, symbol, epsilon, plus, qtn, obra, cbra = G.Terminals(f'| * ( ) symbol {EPSILON} + ? [ ]') + +# Your code here!!! +E %= T + X, lambda h,s: s[2], None,lambda h,s: s[1] + +X %= pipe + T + X, lambda h,s: UnionNode(h[0],s[3]), None, None,lambda h,s: s[2] +X %= G.Epsilon, lambda h,s: h[0] + +T %= A + Y, lambda h,s: s[2], None,lambda h,s: s[1] + +Y %= A + Y, lambda h,s: ConcatNode(h[0],s[2]), None,lambda h,s: s[1] +Y %= G.Epsilon, lambda h,s: h[0] + +A %= Z + F, lambda h,s: s[2] ,None ,lambda h,s: s[1] + +F %= star + F, lambda h,s: s[2], None, lambda h,s: ClosureNode(h[0]) +F %= plus + F, lambda h,s: s[2],None,lambda h,s: PlusNode(h[0]) +F %= qtn + F, lambda h,s: s[2], None, lambda h,s: QuestionNode(h[0]) +F %= G.Epsilon, lambda h,s: h[0] + +R %= A + Q, lambda h,s: s[2], None,lambda h,s: s[1] + +Q %= A + Q, lambda h,s: UnionNode(h[0],s[2]), None, lambda h,s: s[1] +Q %= G.Epsilon, lambda h,s: h[0] + +Z %= symbol, lambda h,s: SymbolNode(s[1]), None +Z %= opar + E + cpar, lambda h,s: s[2], None, None, None +Z %= obra + R + cbra, lambda h,s: s[2], None, None, None +Z %= epsilon, lambda h,s: EpsilonNode(s[1]), None + + +regex_parser = ParserLL1(G) + +class EpsilonNode(AtomicNode): + def evaluate(self): + automata = NFA(1,{0},{},0) + return '',automata + +class SymbolNode(AtomicNode): + def evaluate(self): + s = self.lex + # Your code here!!! + automata = NFA(2,{1},{(0,s):[1]},0) + return f'{s}',automata + +class ClosureNode(UnaryNode): + @staticmethod + def operate(value): + # Your code here!!! + return f'({value[0]})*',automata_closure(value[1]) + +class UnionNode(BinaryNode): + @staticmethod + def operate(lvalue, rvalue): + # Your code here!!! + return f'({lvalue[0]}|{rvalue[0]})',automata_union(lvalue[1],rvalue[1]) + +class ConcatNode(BinaryNode): + @staticmethod + def operate(lvalue, rvalue): + # Your code here!!! + return f'{lvalue[0]}{rvalue[0]}',automata_concatenation(lvalue[1],rvalue[1]) + +class PlusNode(UnaryNode): + @staticmethod + def operate(value): + # Your code here!!! + return f'({value[0]})+',automata_concatenation(value[1],automata_closure(value[1])) + +class QuestionNode(UnaryNode): + @staticmethod + def operate(value): + # Your code here!!! + epsilon = NFA(1,{0},{},0) + return f'({value[0]})?',automata_union(value[1],epsilon) + +fixed_tokens = { + '*' : Token('*' , star), + '(' : Token('(' , opar), + ')' : Token(')' , cpar), + '|' : Token('|' , pipe), + '?' : Token('?' , qtn), + '+' : Token('+' , plus), + '[' : Token('[' , obra), + ']' : Token(']' , cbra), + EPSILON : Token(EPSILON , epsilon), + } + +def regex_tokenizer(text, G, skip_whitespaces=True): + tokens = [] + # > fixed_tokens = ??? + # Your code here!!! + + skip = False + for i, char in enumerate(text): + + if skip: + skip = False + continue + + if skip_whitespaces and char.isspace(): + continue + # Your code here!!! + if char == '\\': + try: + tokens.append(Token(text[i+1],symbol)) + except IndexError: + tokens.append(Token('\\',symbol)) + skip = True + continue + + try: + tokens.append(fixed_tokens[char]) + except KeyError: + tokens.append(Token(char,symbol)) + + tokens.append(Token('$', G.EOF)) + return tokens + +def regex_automaton(regex,skip_whitespces=False,return_regex = False): + """ + returns the minimal dfa that recognize regex + """ + tokens = regex_tokenizer(regex,G,skip_whitespces) + errors = [] + regex,nfa = regex_parser.evaluate(tokens,errors) + if nfa: + dfa = nfa_to_dfa(nfa) + mini = automata_minimization(dfa) + if return_regex: + return mini,regex + return mini return None \ No newline at end of file diff --git a/src/cool2/lib/parser/parser.py b/src/cool2/lib/parser/parser.py index 7e395d858..30aff9581 100644 --- a/src/cool2/lib/parser/parser.py +++ b/src/cool2/lib/parser/parser.py @@ -1,44 +1,44 @@ -from cmp.automata import State -class Parser(): - - def __call__(self,tokens,errors): - raise NotImplementedError() - - def find_conflit(self): - ''' - return a list of conflicts - ''' - raise NotImplementedError() - - def evaluate(self,tokens,errors,return_ast=False): - raise NotImplementedError() - - def get_derivation_tree(self,parse_list, left_to_right=True): - if not left_to_right: parse_list.reverse() - - node = State(parse_list[0].Left) - self.build_tree(node,[0],parse_list,left_to_right) - return node.graph('TD') - - def build_tree(self,current_node, index, parse_list, left_to_right): - - new_nodes = [] - if parse_list[index[0]].Right: - for x in parse_list[index[0]].Right: - new_nodes.append(State(x)) - else: - new_nodes.append(State(parse_list[index[0]].Right)) - - if not left_to_right: new_nodes.reverse() - - for x in new_nodes: - current_node.add_transition('',x) - if x.state.IsNonTerminal: - index[0] += 1 - self.build_tree(x,index,parse_list,left_to_right) - - if not left_to_right: - try: - current_node.transitions[''].reverse() - except: - pass +from cmp.automata import State +class Parser(): + + def __call__(self,tokens,errors): + raise NotImplementedError() + + def find_conflit(self): + ''' + return a list of conflicts + ''' + raise NotImplementedError() + + def evaluate(self,tokens,errors,return_ast=False): + raise NotImplementedError() + + def get_derivation_tree(self,parse_list, left_to_right=True): + if not left_to_right: parse_list.reverse() + + node = State(parse_list[0].Left) + self.build_tree(node,[0],parse_list,left_to_right) + return node.graph('TD') + + def build_tree(self,current_node, index, parse_list, left_to_right): + + new_nodes = [] + if parse_list[index[0]].Right: + for x in parse_list[index[0]].Right: + new_nodes.append(State(x)) + else: + new_nodes.append(State(parse_list[index[0]].Right)) + + if not left_to_right: new_nodes.reverse() + + for x in new_nodes: + current_node.add_transition('',x) + if x.state.IsNonTerminal: + index[0] += 1 + self.build_tree(x,index,parse_list,left_to_right) + + if not left_to_right: + try: + current_node.transitions[''].reverse() + except: + pass diff --git a/src/cool2/lib/parser/parser_ll1.py b/src/cool2/lib/parser/parser_ll1.py index f1467747a..c98805945 100644 --- a/src/cool2/lib/parser/parser_ll1.py +++ b/src/cool2/lib/parser/parser_ll1.py @@ -1,332 +1,332 @@ -from cmp.pycompiler import (EOF, Epsilon, Grammar, NonTerminal, Production, - Sentence, SentenceList, Symbol, Terminal) -from cmp.utils import ContainerSet, inspect, pprint, Token -from lib.utils.first_follow import compute_firsts, compute_follows, compute_local_first -from lib.parser.parser import Parser -from lib.grammar.grammar_fixer import fix_non_derive_terminal, build_reach - -class ParserLL1(Parser): - - grammar = None - - firsts = None - - follows = None - - parse_table = None - - parser = None - - errors = [] - - eval_errors = [] - - def __init__(self, G:Grammar): - """ - When created check for grammar errors in errors - """ - self.grammar = G - - @property - def get_follows(self): - if self.follows is None: - self.firsts = self.get_firsts - self.follows = compute_follows(self.grammar,self.firsts) - return self.follows - - @property - def get_firsts(self): - if self.firsts is None: - self.firsts = compute_firsts(self.grammar) - return self.firsts - - @property - def get_parser_table(self): - if self.parse_table is None: - self.parse_table = self.build_parsing_table() - return self.parse_table - - def __call__(self,tokens,errors): - if not self.parser: - self.parser = self.metodo_predictivo_no_recursivo() - return self.parser(tokens,errors) - - def build_parsing_table(self): - # init parsing table - M = {} - - G = self.grammar - firsts = self.get_firsts - follows = self.get_follows - - # P: X -> alpha - for production in G.Productions: - X = production.Left - alpha = production.Right - - ################################################### - # working with symbols on First(alpha) ... - ################################################### - # # - for t in firsts[alpha]: - try: - a = M[X,t] - if type(a) == type(list()): - self.errors.append(\ - f'Grammar Ambiguity: M[{X},{t}] = {a}, and also must be M[{X},{t}] = {alpha}') - if not alpha in a: - a.append(alpha) - elif a != alpha: - self.errors.append(\ - f'Grammar Ambiguity: M[{X},{t}] = {a}, and also must be M[{X},{t}] = {alpha}') - M[X,t] = [a,alpha] - except KeyError: - M[X,t] = alpha - ################################################### - - ################################################### - # working with epsilon... - ################################################### - # # - if firsts[alpha].contains_epsilon: - for t in follows[X]: - try: - a = M[X,t] - if a != alpha: - self.errors.append(\ - f'Grammar Ambiguity: M[{X},{t}] = {a}, and also must be M[{X},{t}] = {alpha}') - if type(a) == type(list()): - a.append(alpha) - else: - M[X,t] = [a,alpha] - except KeyError: - M[X,t] = alpha - ################################################### - - # parsing table is ready!!! - return M - - def return_prod(self, left,right): - for i,x in enumerate(left.productions,0): - if x.Right == right: - return left.productions[i]; - return None - - def metodo_predictivo_no_recursivo(self): - - G = self.grammar - M = self.get_parser_table - firsts = self.get_firsts - follows = self.get_follows - - # parser construction... - def parser(tokens, parser_errors:list): - - ################################################### - # tokens ends with $ (G.EOF) - ################################################### - # init: - stack = [G.EOF,G.startSymbol] - - cursor = 0 - output = [] - ################################################### - - # parsing tokens... - # print((top, a)) - - ################################################### - # # - while cursor < len(tokens): - top = stack.pop() - a = tokens[cursor] - prod = None - if top.IsTerminal: - if a.token_type == top: - cursor+=1 - continue - else: - parser_errors.append(f'Expected: {top}, Recieve: {a.token_type}') - break - try: - prod = M[top,a.token_type] - except KeyError: - parser_errors.append(f'M[{top},{a.token_type}] generate nothing: expected {[ x[1] for x in M if x[0] == top ]}') - break - - if a.token_type in firsts[prod]: - rev = prod[::-1] - for t in rev: - stack.append(t) - output.append(self.return_prod(top,prod)) - - elif prod.IsEpsilon and a.token_type in follows[top]: - output.append(self.return_prod(top,prod)) - else: - parser_errors.append(f'{a.token_type} Not in follows[{top}]') - break - - ################################################### - - # left parse is ready!!! - return output - - # parser is ready!!! - return parser - - def _evaluate(self, production, left_parse, tokens, inherited_value=None): - head, body = production - attributes = production.attributes - - # Insert your code here ... - # > synteticed = ... - synteticed = [None for _ in attributes] - # > inherited = ... - inherited = [None]*(len(body)+1) - # Anything to do with inherited_value? - inherited[0] = inherited_value - - for i, symbol in enumerate(body, 1): - if symbol.IsTerminal: - if not inherited[i] is None: - self.eval_errors.append(f'Terminals cant have inherited values. Terminal:{symbol}; Inherited:{inherited[i]}') - break - # Insert your code here ... - try: - synteticed[i] = next(tokens).lex # como es un terminal se sintetiza como el mismo - except StopIteration: - self.eval_errors.append(f'tokens stopped the iteration') - break - else: - try: - next_production = next(left_parse) - except StopIteration: - self.eval_errors.append(f'left_parse stopped the iteration') - break - if not symbol == next_production.Left: - self.eval_errors.append(f'{symbol} doesnt match with {next_production.Left}. Cant expand {symbol}') - break - # Insert your code here ... - if attributes[i]: - inherited[i] = attributes[i](inherited, synteticed) - synteticed[i] = self._evaluate(next_production, left_parse, tokens, inherited[i]) - - # Insert your code here ... - if attributes[0]: - synteticed[0] = attributes[0](inherited, synteticed) - # > return ... - return synteticed[0] - - def evaluate_parse(self, left_parse, tokens): - self.eval_errors.clear() - if not left_parse or not tokens: - self.eval_errors.append('Empty tokens or Empty left_parse') - return None - - left_parse = iter(left_parse) - tokens = iter(tokens) - result = self._evaluate(next(left_parse), left_parse, tokens) - - if not isinstance(next(tokens).token_type, EOF): - self.eval_errors.append('Last parsed token doesnt match with EOF') - return None - return result - - def evaluate(self,tokens,errors:list): - """ - If no errors then returns the evaluated tokens\n - else fills errors with errors returning None - """ - parser = self.metodo_predictivo_no_recursivo() - new_errors = [] - left_parse = parser(tokens,new_errors) - if not new_errors: - ast = self.evaluate_parse(left_parse, tokens) - if self.eval_errors: - for x in self.eval_errors: - errors.append(x) - return None - return ast.evaluate() - else: - for x in new_errors: - errors.append(x) - return None - - def find_conflict(self): - table = self.get_parser_table - conflicts = [] - for x in table: - if type(table[x]) == type([1,2]): - grammar,derive = fix_non_derive_terminal(self.grammar,True) - visited = set([grammar.startSymbol,]) - conflict = self._find_conflict(build_reach(self.grammar,x[0]), grammar.startSymbol,x,derive,visited,[]) - conflicts.append(conflict) - return conflicts - - def _find_conflict(self, reach, current, searching, derive, visited, productions): - """ - si X in reach => X puede alcanzar a searching - derive[X] producciones para hacer X una oracion - searching llave de la tabla que da problemas - productions producciones que va llevando - """ - if current == searching[0]: - return self._generate_conflict_error(searching,productions,derive) - - for x in current.productions: - new_productions = productions.copy() - new_productions.append(x) - for y in x.Right: - if y in reach: - if y not in visited: - visited.add(y) - conflict = self._find_conflict(reach,y,searching,derive,visited,new_productions) - if conflict: - return conflict - visited.remove(y) - new_productions.extend(derive[y]) - - return '' - - def _generate_conflict_error(self,searching,productions,derive): - string = self._generate_conflict_string(productions,derive) - aux = ' | '.join([str(x) for x in self.get_parser_table[searching]]) - conflict = f'The prefix "{string}{searching[1].Name}" generate the parse {productions}, that can be continued by these productions {searching[0]} -> {aux}' - return conflict - - def _generate_conflict_string(self,productions,derive): - - stack = [self.grammar.startSymbol] - cursor = 0 - output = [] - string = [] - - while cursor < len(productions): - top = stack.pop() - - if top.IsTerminal: - if not top.IsEpsilon: - string.append(top) - cursor += 1 - continue - - prod = productions[cursor] - - if prod.Right: - rev = prod.Right[::-1] - for t in rev: - stack.append(t) - else: - stack.append(self.grammar.Epsilon) - cursor += 1 - - stack.reverse() - for x in stack: - if x.IsTerminal: - string.append(x) - - output = '' - for x in string: - output += x.Name + ' ' - - return output +from cmp.pycompiler import (EOF, Epsilon, Grammar, NonTerminal, Production, + Sentence, SentenceList, Symbol, Terminal) +from cmp.utils import ContainerSet, inspect, pprint, Token +from lib.utils.first_follow import compute_firsts, compute_follows, compute_local_first +from lib.parser.parser import Parser +from lib.grammar.grammar_fixer import fix_non_derive_terminal, build_reach + +class ParserLL1(Parser): + + grammar = None + + firsts = None + + follows = None + + parse_table = None + + parser = None + + errors = [] + + eval_errors = [] + + def __init__(self, G:Grammar): + """ + When created check for grammar errors in errors + """ + self.grammar = G + + @property + def get_follows(self): + if self.follows is None: + self.firsts = self.get_firsts + self.follows = compute_follows(self.grammar,self.firsts) + return self.follows + + @property + def get_firsts(self): + if self.firsts is None: + self.firsts = compute_firsts(self.grammar) + return self.firsts + + @property + def get_parser_table(self): + if self.parse_table is None: + self.parse_table = self.build_parsing_table() + return self.parse_table + + def __call__(self,tokens,errors): + if not self.parser: + self.parser = self.metodo_predictivo_no_recursivo() + return self.parser(tokens,errors) + + def build_parsing_table(self): + # init parsing table + M = {} + + G = self.grammar + firsts = self.get_firsts + follows = self.get_follows + + # P: X -> alpha + for production in G.Productions: + X = production.Left + alpha = production.Right + + ################################################### + # working with symbols on First(alpha) ... + ################################################### + # # + for t in firsts[alpha]: + try: + a = M[X,t] + if type(a) == type(list()): + self.errors.append(\ + f'Grammar Ambiguity: M[{X},{t}] = {a}, and also must be M[{X},{t}] = {alpha}') + if not alpha in a: + a.append(alpha) + elif a != alpha: + self.errors.append(\ + f'Grammar Ambiguity: M[{X},{t}] = {a}, and also must be M[{X},{t}] = {alpha}') + M[X,t] = [a,alpha] + except KeyError: + M[X,t] = alpha + ################################################### + + ################################################### + # working with epsilon... + ################################################### + # # + if firsts[alpha].contains_epsilon: + for t in follows[X]: + try: + a = M[X,t] + if a != alpha: + self.errors.append(\ + f'Grammar Ambiguity: M[{X},{t}] = {a}, and also must be M[{X},{t}] = {alpha}') + if type(a) == type(list()): + a.append(alpha) + else: + M[X,t] = [a,alpha] + except KeyError: + M[X,t] = alpha + ################################################### + + # parsing table is ready!!! + return M + + def return_prod(self, left,right): + for i,x in enumerate(left.productions,0): + if x.Right == right: + return left.productions[i]; + return None + + def metodo_predictivo_no_recursivo(self): + + G = self.grammar + M = self.get_parser_table + firsts = self.get_firsts + follows = self.get_follows + + # parser construction... + def parser(tokens, parser_errors:list): + + ################################################### + # tokens ends with $ (G.EOF) + ################################################### + # init: + stack = [G.EOF,G.startSymbol] + + cursor = 0 + output = [] + ################################################### + + # parsing tokens... + # print((top, a)) + + ################################################### + # # + while cursor < len(tokens): + top = stack.pop() + a = tokens[cursor] + prod = None + if top.IsTerminal: + if a.token_type == top: + cursor+=1 + continue + else: + parser_errors.append(f'Expected: {top}, Recieve: {a.token_type}') + break + try: + prod = M[top,a.token_type] + except KeyError: + parser_errors.append(f'M[{top},{a.token_type}] generate nothing: expected {[ x[1] for x in M if x[0] == top ]}') + break + + if a.token_type in firsts[prod]: + rev = prod[::-1] + for t in rev: + stack.append(t) + output.append(self.return_prod(top,prod)) + + elif prod.IsEpsilon and a.token_type in follows[top]: + output.append(self.return_prod(top,prod)) + else: + parser_errors.append(f'{a.token_type} Not in follows[{top}]') + break + + ################################################### + + # left parse is ready!!! + return output + + # parser is ready!!! + return parser + + def _evaluate(self, production, left_parse, tokens, inherited_value=None): + head, body = production + attributes = production.attributes + + # Insert your code here ... + # > synteticed = ... + synteticed = [None for _ in attributes] + # > inherited = ... + inherited = [None]*(len(body)+1) + # Anything to do with inherited_value? + inherited[0] = inherited_value + + for i, symbol in enumerate(body, 1): + if symbol.IsTerminal: + if not inherited[i] is None: + self.eval_errors.append(f'Terminals cant have inherited values. Terminal:{symbol}; Inherited:{inherited[i]}') + break + # Insert your code here ... + try: + synteticed[i] = next(tokens).lex # como es un terminal se sintetiza como el mismo + except StopIteration: + self.eval_errors.append(f'tokens stopped the iteration') + break + else: + try: + next_production = next(left_parse) + except StopIteration: + self.eval_errors.append(f'left_parse stopped the iteration') + break + if not symbol == next_production.Left: + self.eval_errors.append(f'{symbol} doesnt match with {next_production.Left}. Cant expand {symbol}') + break + # Insert your code here ... + if attributes[i]: + inherited[i] = attributes[i](inherited, synteticed) + synteticed[i] = self._evaluate(next_production, left_parse, tokens, inherited[i]) + + # Insert your code here ... + if attributes[0]: + synteticed[0] = attributes[0](inherited, synteticed) + # > return ... + return synteticed[0] + + def evaluate_parse(self, left_parse, tokens): + self.eval_errors.clear() + if not left_parse or not tokens: + self.eval_errors.append('Empty tokens or Empty left_parse') + return None + + left_parse = iter(left_parse) + tokens = iter(tokens) + result = self._evaluate(next(left_parse), left_parse, tokens) + + if not isinstance(next(tokens).token_type, EOF): + self.eval_errors.append('Last parsed token doesnt match with EOF') + return None + return result + + def evaluate(self,tokens,errors:list): + """ + If no errors then returns the evaluated tokens\n + else fills errors with errors returning None + """ + parser = self.metodo_predictivo_no_recursivo() + new_errors = [] + left_parse = parser(tokens,new_errors) + if not new_errors: + ast = self.evaluate_parse(left_parse, tokens) + if self.eval_errors: + for x in self.eval_errors: + errors.append(x) + return None + return ast.evaluate() + else: + for x in new_errors: + errors.append(x) + return None + + def find_conflict(self): + table = self.get_parser_table + conflicts = [] + for x in table: + if type(table[x]) == type([1,2]): + grammar,derive = fix_non_derive_terminal(self.grammar,True) + visited = set([grammar.startSymbol,]) + conflict = self._find_conflict(build_reach(self.grammar,x[0]), grammar.startSymbol,x,derive,visited,[]) + conflicts.append(conflict) + return conflicts + + def _find_conflict(self, reach, current, searching, derive, visited, productions): + """ + si X in reach => X puede alcanzar a searching + derive[X] producciones para hacer X una oracion + searching llave de la tabla que da problemas + productions producciones que va llevando + """ + if current == searching[0]: + return self._generate_conflict_error(searching,productions,derive) + + for x in current.productions: + new_productions = productions.copy() + new_productions.append(x) + for y in x.Right: + if y in reach: + if y not in visited: + visited.add(y) + conflict = self._find_conflict(reach,y,searching,derive,visited,new_productions) + if conflict: + return conflict + visited.remove(y) + new_productions.extend(derive[y]) + + return '' + + def _generate_conflict_error(self,searching,productions,derive): + string = self._generate_conflict_string(productions,derive) + aux = ' | '.join([str(x) for x in self.get_parser_table[searching]]) + conflict = f'The prefix "{string}{searching[1].Name}" generate the parse {productions}, that can be continued by these productions {searching[0]} -> {aux}' + return conflict + + def _generate_conflict_string(self,productions,derive): + + stack = [self.grammar.startSymbol] + cursor = 0 + output = [] + string = [] + + while cursor < len(productions): + top = stack.pop() + + if top.IsTerminal: + if not top.IsEpsilon: + string.append(top) + cursor += 1 + continue + + prod = productions[cursor] + + if prod.Right: + rev = prod.Right[::-1] + for t in rev: + stack.append(t) + else: + stack.append(self.grammar.Epsilon) + cursor += 1 + + stack.reverse() + for x in stack: + if x.IsTerminal: + string.append(x) + + output = '' + for x in string: + output += x.Name + ' ' + + return output diff --git a/src/cool2/lib/parser/parser_lr.py b/src/cool2/lib/parser/parser_lr.py index 49d47415f..9d65ac52e 100644 --- a/src/cool2/lib/parser/parser_lr.py +++ b/src/cool2/lib/parser/parser_lr.py @@ -1,686 +1,686 @@ -from cmp.automata import State, lr0_formatter, multiline_formatter -from cmp.pycompiler import (EOF, Epsilon, Grammar, Item, NonTerminal, - Production, Sentence, SentenceFromIter, - SentenceList, Symbol, Terminal) -from lib.utils.first_follow import (compute_firsts, compute_follows, compute_local_first) -from cmp.utils import ContainerSet, DisjointNode, DisjointSet, Token -from lib.parser.parser import Parser -from lib.grammar.grammar_fixer import fix_non_derive_terminal -from lib.utils.automaton import state_transpose -from cool.error.errors import SyntacticCoolError, SYNTACTIC_ERROR - -###################### LR0 and SLR1 ########################### -def get_state(visited,pending,item): - if not item in visited.keys(): - new_state = State(item,True) - pending.append(item) - visited[item] = new_state - return new_state - return visited[item] - -def build_LR0_automaton(G:Grammar): - assert len(G.startSymbol.productions) == 1, 'Grammar must be augmented' - - start_production = G.startSymbol.productions[0] - start_item = Item(start_production, 0) - - automaton = State(start_item, True) - - pending = [ start_item ] - visited = { start_item: automaton } - - while pending: - current_item = pending.pop() - if current_item.IsReduceItem: - continue - - current_state = visited[current_item] - next_symbol = current_item.NextSymbol - next_item = current_item.NextItem() - - #- $(X --> a.cB) ---c---> (X --> ac.B) con c in V_T - #- $(X --> a.YB) ---Y---> (X --> aY.B) con Y in V_N - next_state = get_state(visited,pending,next_item) - current_state.add_transition(next_symbol.Name,next_state) - - if next_symbol in G.nonTerminals: - sym_productions = G.symbDict[next_symbol.Name].productions - #- $(X --> a.YB) ---e---> (Y --> .Z) con Y in V_N - for pr in [Item(x,0) for x in sym_productions]: - trans_state = get_state(visited,pending,pr) - current_state.add_epsilon_transition(trans_state) - - return automaton.to_deterministic(lr0_formatter) - -class ShiftReduceParser(Parser): - SHIFT = 'SHIFT' - REDUCE = 'REDUCE' - OK = 'OK' - lr_type = None - errors = None - automaton = None - grammar = None - error_states = None # self.error_states[key] is the set of posibles values - eval_errors = [] - - - state_dict = {} # state_dic[key] = State with idx==key - - def __init__(self, G, automaton_builder, verbose=False): - self.grammar = G - self.verbose = verbose - self.action = {} - self.goto = {} - self.errors = [] - self.error_states = {} - G = G.AugmentedGrammar(True) - self.automaton = automaton_builder(G) - self._build_parsing_table(G) - - def _build_parsing_table(self, G): - raise NotImplementedError() - - def __call__(self, tokens, errors,finding_conflict = False): - stack = [ 0 ] - cursor = 0 - output = [] - tokens = [x for x in tokens] - while True and cursor < len(tokens): - state = stack[-1] - lookahead = tokens[cursor] - if self.verbose: print(stack, '<---||--->', tokens[cursor:]) - - # Your code here!!! (Detect error) - try: - action = self.action[state, lookahead.token_type] - if isinstance(action,tuple): - action, tag = action - else: - return None if not finding_conflict else (state,lookahead,output) - except KeyError: - # errors.append(f'Invalid transition ({state},{lookahead}) doesnt exist expected {[ x[1] for x in self.action if x[0] == state ]}') - posibles = [x for x in self.action if x[0] == state ] - errors.append(SyntacticCoolError(SYNTACTIC_ERROR, lookahead.lex[0], token=lookahead)) - # errors.append(f"Invalid transition near '{lookahead.lex[0]}'. Expected: {', '.join([ str(x[1]) for x in posibles ])}. Line:{lookahead.lex[1] + 1} Column:{lookahead.lex[2] + 1}") - if len(posibles) == 1: - tokens.insert(cursor + 1, Token((str(posibles[0][1]), lookahead.lex[1], lookahead.lex[2]), posibles[0][1])) - cursor += 1 - continue - return None if not finding_conflict else (state,lookahead,output) - - if action == self.SHIFT: - # Your code here!!! (Shift case) - stack.append(lookahead.token_type) - stack.append(tag) - cursor+=1 - elif action == self.REDUCE: - # Your code here!!! (Reduce case) - for i in range(len(tag.Right)): - stack.pop() - top = stack.pop() - if not top == tag.Right[-(i+1)]: - errors.append(f"Productions reduce doesnt match: {top} != {tag.Right[-(i+1)]}") - - index = self.goto[stack[-1],tag.Left] - stack.append(tag.Left) - stack.append(index) - output.append(tag) - elif action == self.OK: - # Your code here!!! (OK case) - return output if not finding_conflict else (state,lookahead,output) - # Your code here!!! (Invalid case) - else: - errors.append(f"Invalid case: {action}") - return None if not finding_conflict else (state,lookahead,output) - - def _register(self, table, key, value): - if key in table: - key_value = table[key] - if type(key_value) == type(set()): - if value not in key_value: - self.errors.append(f'Shift-Reduce or Reduce-Reduce conflict: {key} already on table with values {table[key]} cannot register with value {value}') - key_value.add(value) - if key in self.error_states: - self.error_states[key].add(key) - else: - self.error_states[key] = set([value,key_value]) - elif key_value != value: - self.errors.append(f'Shift-Reduce or Reduce-Reduce conflict: {key} already on table with value {table[key]} cannot register with value {value}') - table[key] = set([value, key_value]) - if key in self.error_states: - self.error_states[key].add(key) - else: - self.error_states[key] = set([value,key_value]) - else: - table[key] = value - - def _evaluate(self, production, right_parse, tokens, inherited_value=None): - head, body = production - attributes = production.attributes - body = list(body) - body.reverse() - # Insert your code here ... - # > synteticed = ... - synteticed = [None for _ in attributes] - # > inherited = ... - inherited = [None]*(len(body)+1) - # Anything to do with inherited_value? - inherited[0] = inherited_value - - for i, symbol in zip(range(len(body),0,-1),body) : - if symbol.IsTerminal: - if not inherited[i] is None: - self.eval_errors.append(f'Terminals cant have inherited values. Terminal:{symbol}; Inherited:{inherited[i]}') - break - # Insert your code here ... - try: - synteticed[i] = next(tokens).lex # como es un terminal se sintetiza como el mismo - except StopIteration: - self.eval_errors.append(f'tokens stopped the iteration') - break - else: - try: - next_production = next(right_parse) - except StopIteration: - self.eval_errors.append(f'right_parse stopped the iteration') - break - if not symbol == next_production.Left: - self.eval_errors.append(f'{symbol} doesnt match with {next_production.Left}. Cant expand {symbol}') - break - # Insert your code here ... - if attributes[i]: - inherited[i] = attributes[i](inherited, synteticed) - synteticed[i] = self._evaluate(next_production, right_parse, tokens, inherited[i]) - - # Insert your code here ... - if attributes[0]: - synteticed[0] = attributes[0](inherited, synteticed) - # > return ... - return synteticed[0] - - def evaluate_parse(self, right_parse, tokens): - self.eval_errors.clear() - if not right_parse or not tokens: - self.eval_errors.append('Empty tokens or Empty right_parse') - return None - - right_parse = iter(right_parse) - end = tokens[-1] - tokens = tokens[:len(tokens)-1] - tokens.reverse() - tokens.append(end) - tokens = iter(tokens) - result = self._evaluate(next(right_parse), right_parse, tokens) - - if not isinstance(next(tokens).token_type, EOF): - self.eval_errors.append('Last parsed token doesnt match with EOF') - return None - return result - - def evaluate(self,tokens,errors:list,return_ast=False): - """ - If no errors then returns the evaluated tokens\n - else fills errors with errors returning None - """ - parser = self.__call__ - new_errors = [] - right_parse = parser(tokens,new_errors) - if not new_errors: - right_parse.reverse() - ast = self.evaluate_parse(right_parse, tokens) - if self.eval_errors: - for x in self.eval_errors: - errors.append(x) - return None - if return_ast: - return ast - return ast.evaluate() - else: - for x in new_errors: - errors.append(x) - return None - - def find_conflict(self): - conflicts = [] - automaton_reverse,state_dict = state_transpose(self.automaton) - for key,value in self.action.items(): - if type(value) == type(set()): - conflict_productions = [x[1] for x in value if isinstance(x[1],Production)] - for production in conflict_productions: - conflict_state = state_dict[key[0]][0] - conflict_item = find_conflict_item(conflict_state,production) - stack = [(conflict_state,conflict_item)] - - sentence = [ y for y in production.Right ] - sentence.reverse() - current_state = conflict_state - current_item = conflict_item - for y in sentence: - current_item = Item(current_item.production,current_item.pos-1,current_item.lookaheads) - current_state = go_back(current_state,y,current_item,state_dict) - stack.append((current_state,current_item)) - stack.reverse() - - initial_item = find_initial_item(self.automaton) - path = [(self.automaton,initial_item,False)] - visited = {(initial_item,self.automaton.idx)} - find_path_to(stack[0][0],stack[0][1],self.automaton,initial_item,path,visited) # una lista de tuplas que es (estado en que estoy,item en el que estoy) - - terminals = items_to_terminals(path[:len(path)-1],stack) - conflicts.append(self._generate_error(value,terminals,[x[1] for x in path] + [x[1] for x in stack[1:]],key[0],key[1])) - break - - return conflicts - - def _generate_error(self,conflict,terminals,items,state,lookahead): - string = ' '.join(x.Name for x in terminals) - return f'The string "{string}" is generated by the items {items}, stopping in state {state}, the conflict is because the automaton can do the following actions {conflict} looking at terminal "{lookahead}"' - -class LR0Parser(ShiftReduceParser): - - def __init__(self,G,verbose=False): - super().__init__(G,build_LR0_automaton,verbose) - self.lr_type = 'lr0' - - def _build_parsing_table(self,G): - assert len(G.startSymbol.productions) == 1, 'Grammar must be augmented' - - automaton = self.automaton - for i, node in enumerate(automaton): - if self.verbose: print(i, '\t', '\n\t '.join(str(x) for x in node.state), '\n') - node.idx = i - - for node in automaton: - idx = node.idx - for state in node.state: - item = state.state - # Your code here!!! - # - Fill `self.Action` and `self.Goto` according to `item`) - # - Feel free to use `self._register(...)`) - item = state.state - if item.IsReduceItem: - if item.production.Left == G.startSymbol: - self._register(self.action,(idx,G.EOF),(self.OK,None)) - else: - for c in G.terminals + [G.EOF]: - self._register(self.action, (idx,c),(self.REDUCE,item.production)) - else: - next_sym = item.NextSymbol - try: - next_state = node[next_sym.Name][0] - if next_sym.IsNonTerminal: - self._register(self.goto,(idx,next_sym),next_state.idx) - else: - self._register(self.action,(idx,next_sym),(self.SHIFT,next_state.idx)) - except KeyError: - self.errors.append(f'Node: {node} without transition with symbol {next_sym}') - return None - -class SLR1Parser(ShiftReduceParser): - - def __init__(self,G,verbose=False): - super().__init__(G,build_LR0_automaton,verbose) - self.lr_type = 'slr1' - - def _build_parsing_table(self,G): - assert len(G.startSymbol.productions) == 1, 'Grammar must be augmented' - - firsts = compute_firsts(G) - follows = compute_follows(G, firsts) - - automaton = self.automaton - for i, node in enumerate(automaton): - if self.verbose: print(i, '\t', '\n\t '.join(str(x) for x in node.state), '\n') - node.idx = i - - for node in automaton: - idx = node.idx - for state in node.state: - item = state.state - # Your code here!!! - # - Fill `self.Action` and `self.Goto` according to `item`) - # - Feel free to use `self._register(...)`) - item = state.state - if item.IsReduceItem: - if item.production.Left == G.startSymbol: - self._register(self.action,(idx,G.EOF),(self.OK,None)) - else: - for c in follows[item.production.Left]: - self._register(self.action, (idx,c),(self.REDUCE,item.production)) - else: - next_sym = item.NextSymbol - try: - next_state = node[next_sym.Name][0] - if next_sym.IsNonTerminal: - self._register(self.goto,(idx,next_sym),next_state.idx) - else: - self._register(self.action,(idx,next_sym),(self.SHIFT,next_state.idx)) - except KeyError: - self.errors.append(f'Node: {node} without transition with symbol {next_sym}') - return None - - -###################### LR1 ########################### -def expand(item, firsts): - next_symbol = item.NextSymbol - if next_symbol is None or not next_symbol.IsNonTerminal: - return [] - - lookaheads = ContainerSet() # lookahead = que yo quiero ver cuando vaya a reducir - # Your code here!!! (Compute lookahead for child items) - - for prev in item.Preview(): - lookaheads.update(compute_local_first(firsts,prev)) - - assert not lookaheads.contains_epsilon - - # Your code here!!! (Build and return child items) - return [ Item(x,0,lookaheads) for x in next_symbol.productions] - -def compress(items): - centers = {} - - for item in items: - center = item.Center() - try: - lookaheads = centers[center] - except KeyError: - centers[center] = lookaheads = set() - lookaheads.update(item.lookaheads) - - return { Item(x.production, x.pos, set(lookahead)) for x, lookahead in centers.items() } - -def closure_lr1(items, firsts): - closure = ContainerSet(*items) - - changed = True - while changed: - changed = False - - new_items = ContainerSet() - # Your code here!!! - for x in closure: - new_items.extend(expand(x,firsts)) - - changed = closure.update(new_items) - - return compress(closure) - -def goto_lr1(items, symbol, firsts=None, just_kernel=False): - assert just_kernel or firsts is not None, '`firsts` must be provided if `just_kernel=False`' - items = frozenset(item.NextItem() for item in items if item.NextSymbol == symbol) - return items if just_kernel else closure_lr1(items, firsts) - -def build_LR1_automaton(G): - assert len(G.startSymbol.productions) == 1, 'Grammar must be augmented' - - firsts = compute_firsts(G) - firsts[G.EOF] = ContainerSet(G.EOF) - - start_production = G.startSymbol.productions[0] - start_item = Item(start_production, 0, lookaheads=(G.EOF,)) - start = frozenset([start_item]) - - closure = closure_lr1(start, firsts) - automaton = State(frozenset(closure), True) - - pending = [ start ] - visited = { start: automaton } - - while pending: - current = pending.pop() - current_state = visited[current] - - for symbol in G.terminals + G.nonTerminals: - # Your code here!!! (Get/Build `next_state`) - next_state_key = goto_lr1(current_state.state,symbol,just_kernel=True) - # next_state_key = frozenset([i.NextItem() for i in current_state.state if i.NextSymbol == symbol]) - if not next_state_key: - continue - try: - next_state = visited[next_state_key] - except KeyError: - next_state_items = goto_lr1(current_state.state,symbol,firsts) - next_state = State(frozenset(next_state_items),True) - pending.append(next_state_key) - visited[next_state_key] = next_state - current_state.add_transition(symbol.Name, next_state) - - - automaton.set_formatter(multiline_formatter) - return automaton - -class LR1Parser(ShiftReduceParser): - - def __init__(self,G,verbose=False): - super().__init__(G,build_LR1_automaton,verbose) - self.lr_type = 'lr1' - - def _build_parsing_table(self,G): - assert len(G.startSymbol.productions) == 1, 'Grammar must be augmented' - - automaton = self.automaton - for i, node in enumerate(automaton): - if self.verbose: print(i, '\t', '\n\t '.join(str(x) for x in node.state), '\n') - node.idx = i - - for node in automaton: - idx = node.idx - for item in node.state: - # Your code here!!! - # - Fill `self.Action` and `self.Goto` according to `item`) - # - Feel free to use `self._register(...)`) - if item.IsReduceItem: - if item.production.Left == G.startSymbol: - self._register(self.action,(idx,G.EOF),(self.OK,None)) - else: - for c in item.lookaheads: - self._register(self.action, (idx,c),(self.REDUCE,item.production)) - else: - next_sym = item.NextSymbol - try: - next_state = node[next_sym.Name][0] - if next_sym.IsNonTerminal: - self._register(self.goto,(idx,next_sym),next_state.idx) - else: - self._register(self.action,(idx,next_sym),(self.SHIFT,next_state.idx)) - except KeyError: - self.errors.append(f'Node: {node} without transition with symbol {next_sym}') - return None - - -###################### LALR1 ########################### -def center_of(items): - return { item.Center() for item in items } - -def build_automaton_from_ds(initial:State, ds:DisjointSet): - for g in ds.groups: - r = g[0].representative.value - old_transitions = r.transitions - r.transitions = {} - for x in old_transitions: - node = None - for y in ds.groups: - if center_of(y[0].representative.value.state) == center_of(old_transitions[x][0].state): - node = y - break - assert node, 'group not found' - r.add_transition(x, node[0].representative.value) - end = [x for x in ds.groups if x[0].representative.value == initial][0] - return end[0].representative.value - -def from_lr1_to_lalr(lr1_automaton:State): - pending = [s for s in lr1_automaton] - ds = DisjointSet(*pending) - while pending: - to_merge = [] - pivot = center_of(pending[0].state) - for i,x in enumerate(pending): - if center_of(x.state) == pivot: - to_merge.append(x) - pending[i] = None - pending = [x for x in pending if x] - ds.merge(to_merge) - - for g in ds.groups: - r = g[0].representative - for x in [x for x in g if r != x]: - for prod in r.value.state: - for prod2 in x.value.state: - if prod.Center() == prod2.Center(): - new_lookahead = {x for x in prod.lookaheads} - new_lookahead.update({x for x in prod2.lookaheads}) - prod.lookaheads = frozenset(new_lookahead) - return build_automaton_from_ds(lr1_automaton,ds) - -def build_LALR1_automaton(G): - return from_lr1_to_lalr(build_LR1_automaton(G)) - -class LALR1Parser(ShiftReduceParser): - - def __init__(self,G,verbose=False): - super().__init__(G,build_LALR1_automaton,verbose) - self.lr_type = 'lalr1' - - def _build_parsing_table(self,G): - assert len(G.startSymbol.productions) == 1, 'Grammar must be augmented' - - automaton = self.automaton - for i, node in enumerate(automaton): - if self.verbose: print(i, '\t', '\n\t '.join(str(x) for x in node.state), '\n') - node.idx = i - - for node in automaton: - idx = node.idx - for item in node.state: - # Your code here!!! - # - Fill `self.Action` and `self.Goto` according to `item`) - # - Feel free to use `self._register(...)`) - if item.IsReduceItem: - if item.production.Left == G.startSymbol: - self._register(self.action,(idx,G.EOF),(self.OK,None)) - else: - for c in item.lookaheads: - self._register(self.action, (idx,c),(self.REDUCE,item.production)) - else: - next_sym = item.NextSymbol - try: - next_state = node[next_sym.Name][0] - if next_sym.IsNonTerminal: - self._register(self.goto,(idx,next_sym),next_state.idx) - else: - self._register(self.action,(idx,next_sym),(self.SHIFT,next_state.idx)) - except KeyError: - self.errors.append(f'Node: {node} without transition with symbol {next_sym}') - return None - - -############## Conflict Utils ####################### -def go_back(state_to_transpose, symbol,expected_item,transpose_state_dic): - """ - return a state that enter to state_to_transpose with symbol `symbol` - """ - candidates = transpose_state_dic[state_to_transpose.idx][1][symbol.Name] - candidates = [x for x in candidates if any([y for y in x.state if y.state == expected_item])] - return transpose_state_dic[candidates[0].idx][0] - -def find_path_to(goal_state,goal_item,current_state,current_item,path,item_checked): - """ - path: a list of tuple (item's state, item) that represent th items taken to reach current state - item_checked: a set of tuple (item, item's state.idx) that saves the visited items - return in path the items needed to reach the goal_item in goal_state from current_item in current_state - """ - if goal_state == current_state and goal_item == current_item: - return True - if current_item.IsReduceItem: - return False - - spanned_items = span_item(current_state,current_item) - - path.append((current_state[current_item[current_item.pos].Name][0],current_item.NextItem(),True if current_item[current_item.pos].IsNonTerminal else False)) - if not (path[-1][1],path[-1][0].idx) in item_checked: - item_checked.add((path[-1][1],path[-1][0].idx)) - if find_path_to(goal_state,goal_item,path[-1][0],path[-1][1],path,item_checked): - return True - path.pop() - - for next_item in spanned_items: - path.append((current_state,next_item,False)) - if not (path[-1][1],path[-1][0].idx) in item_checked: - item_checked.add((next_item,current_state.idx)) - if find_path_to(goal_state,goal_item,path[-1][0],path[-1][1],path,item_checked): - return True - path.pop() - return False - -def items_to_terminals(first_path, second_path): - """ - concat the terminals of first_path an the sentence of second_path - """ - terminals = [] - for i,(state,item,skipped) in enumerate(first_path): - if not item.IsReduceItem: - if i != 0 and skipped: # before skiped: - last_state,last_item,skipped = first_path[i-1] - spanned_items = span_item(last_state,last_item) - for next_item in spanned_items: - next_terminals = item_sentence(last_state,next_item) - if next_terminals: - terminals += next_terminals - break - if item[item.pos].IsTerminal: - terminals.append(item[item.pos]) - - for state,item in second_path: - if not item.IsReduceItem: - if item[item.pos].IsTerminal: - terminals.append(item[item.pos]) - else: - spanned_items = span_item(state,item) - terminals += item_sentence(state,spanned_items[0]) - return terminals - -def find_initial_item(initial_state): - for in_state in initial_state.state: - item = in_state.state - if len(item.production.Right) == 1 and item.production.Right[0].IsNonTerminal \ - and "'" in item.production.Left.Name: - return item - -def find_conflict_item(state,production): - for in_state in state.state: - if isinstance(in_state,Item): - if in_state.production == production: - return in_state - else: - item = in_state.state - if item.production == production: - return item - -def span_item(state,item): - return [x.state for x in state.state if x.state.production.Left == item[item.pos] and x.state.pos == 0] - # Tambien incluir los lookahead en la condicion? - -def item_sentence(state,item): - path = [] - _item_sentence(state,item,set(),path) - return path - -def _item_sentence(state:State,item,visited,path): - - if item.IsReduceItem: - return True - - next_symb = item.NextSymbol - if isinstance(next_symb,NonTerminal): - if not (state.idx,next_symb) in visited: - visited.add((state.idx,next_symb)) - for next_item in span_item(state,item): - if _item_sentence(state,next_item,visited,path): - if _item_sentence(state[next_symb.Name][0],item.NextItem(),visited,path): - return True - else: - path.append(next_symb) - if _item_sentence(state[next_symb.Name][0],item.NextItem(),visited,path): - return True +from cmp.automata import State, lr0_formatter, multiline_formatter +from cmp.pycompiler import (EOF, Epsilon, Grammar, Item, NonTerminal, + Production, Sentence, SentenceFromIter, + SentenceList, Symbol, Terminal) +from lib.utils.first_follow import (compute_firsts, compute_follows, compute_local_first) +from cmp.utils import ContainerSet, DisjointNode, DisjointSet, Token +from lib.parser.parser import Parser +from lib.grammar.grammar_fixer import fix_non_derive_terminal +from lib.utils.automaton import state_transpose +from cool.error.errors import SyntacticCoolError, SYNTACTIC_ERROR + +###################### LR0 and SLR1 ########################### +def get_state(visited,pending,item): + if not item in visited.keys(): + new_state = State(item,True) + pending.append(item) + visited[item] = new_state + return new_state + return visited[item] + +def build_LR0_automaton(G:Grammar): + assert len(G.startSymbol.productions) == 1, 'Grammar must be augmented' + + start_production = G.startSymbol.productions[0] + start_item = Item(start_production, 0) + + automaton = State(start_item, True) + + pending = [ start_item ] + visited = { start_item: automaton } + + while pending: + current_item = pending.pop() + if current_item.IsReduceItem: + continue + + current_state = visited[current_item] + next_symbol = current_item.NextSymbol + next_item = current_item.NextItem() + + #- $(X --> a.cB) ---c---> (X --> ac.B) con c in V_T + #- $(X --> a.YB) ---Y---> (X --> aY.B) con Y in V_N + next_state = get_state(visited,pending,next_item) + current_state.add_transition(next_symbol.Name,next_state) + + if next_symbol in G.nonTerminals: + sym_productions = G.symbDict[next_symbol.Name].productions + #- $(X --> a.YB) ---e---> (Y --> .Z) con Y in V_N + for pr in [Item(x,0) for x in sym_productions]: + trans_state = get_state(visited,pending,pr) + current_state.add_epsilon_transition(trans_state) + + return automaton.to_deterministic(lr0_formatter) + +class ShiftReduceParser(Parser): + SHIFT = 'SHIFT' + REDUCE = 'REDUCE' + OK = 'OK' + lr_type = None + errors = None + automaton = None + grammar = None + error_states = None # self.error_states[key] is the set of posibles values + eval_errors = [] + + + state_dict = {} # state_dic[key] = State with idx==key + + def __init__(self, G, automaton_builder, verbose=False): + self.grammar = G + self.verbose = verbose + self.action = {} + self.goto = {} + self.errors = [] + self.error_states = {} + G = G.AugmentedGrammar(True) + self.automaton = automaton_builder(G) + self._build_parsing_table(G) + + def _build_parsing_table(self, G): + raise NotImplementedError() + + def __call__(self, tokens, errors,finding_conflict = False): + stack = [ 0 ] + cursor = 0 + output = [] + tokens = [x for x in tokens] + while True and cursor < len(tokens): + state = stack[-1] + lookahead = tokens[cursor] + if self.verbose: print(stack, '<---||--->', tokens[cursor:]) + + # Your code here!!! (Detect error) + try: + action = self.action[state, lookahead.token_type] + if isinstance(action,tuple): + action, tag = action + else: + return None if not finding_conflict else (state,lookahead,output) + except KeyError: + # errors.append(f'Invalid transition ({state},{lookahead}) doesnt exist expected {[ x[1] for x in self.action if x[0] == state ]}') + posibles = [x for x in self.action if x[0] == state ] + errors.append(SyntacticCoolError(SYNTACTIC_ERROR, lookahead.lex[0], token=lookahead)) + # errors.append(f"Invalid transition near '{lookahead.lex[0]}'. Expected: {', '.join([ str(x[1]) for x in posibles ])}. Line:{lookahead.lex[1] + 1} Column:{lookahead.lex[2] + 1}") + if len(posibles) == 1: + tokens.insert(cursor + 1, Token((str(posibles[0][1]), lookahead.lex[1], lookahead.lex[2]), posibles[0][1])) + cursor += 1 + continue + return None if not finding_conflict else (state,lookahead,output) + + if action == self.SHIFT: + # Your code here!!! (Shift case) + stack.append(lookahead.token_type) + stack.append(tag) + cursor+=1 + elif action == self.REDUCE: + # Your code here!!! (Reduce case) + for i in range(len(tag.Right)): + stack.pop() + top = stack.pop() + if not top == tag.Right[-(i+1)]: + errors.append(f"Productions reduce doesnt match: {top} != {tag.Right[-(i+1)]}") + + index = self.goto[stack[-1],tag.Left] + stack.append(tag.Left) + stack.append(index) + output.append(tag) + elif action == self.OK: + # Your code here!!! (OK case) + return output if not finding_conflict else (state,lookahead,output) + # Your code here!!! (Invalid case) + else: + errors.append(f"Invalid case: {action}") + return None if not finding_conflict else (state,lookahead,output) + + def _register(self, table, key, value): + if key in table: + key_value = table[key] + if type(key_value) == type(set()): + if value not in key_value: + self.errors.append(f'Shift-Reduce or Reduce-Reduce conflict: {key} already on table with values {table[key]} cannot register with value {value}') + key_value.add(value) + if key in self.error_states: + self.error_states[key].add(key) + else: + self.error_states[key] = set([value,key_value]) + elif key_value != value: + self.errors.append(f'Shift-Reduce or Reduce-Reduce conflict: {key} already on table with value {table[key]} cannot register with value {value}') + table[key] = set([value, key_value]) + if key in self.error_states: + self.error_states[key].add(key) + else: + self.error_states[key] = set([value,key_value]) + else: + table[key] = value + + def _evaluate(self, production, right_parse, tokens, inherited_value=None): + head, body = production + attributes = production.attributes + body = list(body) + body.reverse() + # Insert your code here ... + # > synteticed = ... + synteticed = [None for _ in attributes] + # > inherited = ... + inherited = [None]*(len(body)+1) + # Anything to do with inherited_value? + inherited[0] = inherited_value + + for i, symbol in zip(range(len(body),0,-1),body) : + if symbol.IsTerminal: + if not inherited[i] is None: + self.eval_errors.append(f'Terminals cant have inherited values. Terminal:{symbol}; Inherited:{inherited[i]}') + break + # Insert your code here ... + try: + synteticed[i] = next(tokens).lex # como es un terminal se sintetiza como el mismo + except StopIteration: + self.eval_errors.append(f'tokens stopped the iteration') + break + else: + try: + next_production = next(right_parse) + except StopIteration: + self.eval_errors.append(f'right_parse stopped the iteration') + break + if not symbol == next_production.Left: + self.eval_errors.append(f'{symbol} doesnt match with {next_production.Left}. Cant expand {symbol}') + break + # Insert your code here ... + if attributes[i]: + inherited[i] = attributes[i](inherited, synteticed) + synteticed[i] = self._evaluate(next_production, right_parse, tokens, inherited[i]) + + # Insert your code here ... + if attributes[0]: + synteticed[0] = attributes[0](inherited, synteticed) + # > return ... + return synteticed[0] + + def evaluate_parse(self, right_parse, tokens): + self.eval_errors.clear() + if not right_parse or not tokens: + self.eval_errors.append('Empty tokens or Empty right_parse') + return None + + right_parse = iter(right_parse) + end = tokens[-1] + tokens = tokens[:len(tokens)-1] + tokens.reverse() + tokens.append(end) + tokens = iter(tokens) + result = self._evaluate(next(right_parse), right_parse, tokens) + + if not isinstance(next(tokens).token_type, EOF): + self.eval_errors.append('Last parsed token doesnt match with EOF') + return None + return result + + def evaluate(self,tokens,errors:list,return_ast=False): + """ + If no errors then returns the evaluated tokens\n + else fills errors with errors returning None + """ + parser = self.__call__ + new_errors = [] + right_parse = parser(tokens,new_errors) + if not new_errors: + right_parse.reverse() + ast = self.evaluate_parse(right_parse, tokens) + if self.eval_errors: + for x in self.eval_errors: + errors.append(x) + return None + if return_ast: + return ast + return ast.evaluate() + else: + for x in new_errors: + errors.append(x) + return None + + def find_conflict(self): + conflicts = [] + automaton_reverse,state_dict = state_transpose(self.automaton) + for key,value in self.action.items(): + if type(value) == type(set()): + conflict_productions = [x[1] for x in value if isinstance(x[1],Production)] + for production in conflict_productions: + conflict_state = state_dict[key[0]][0] + conflict_item = find_conflict_item(conflict_state,production) + stack = [(conflict_state,conflict_item)] + + sentence = [ y for y in production.Right ] + sentence.reverse() + current_state = conflict_state + current_item = conflict_item + for y in sentence: + current_item = Item(current_item.production,current_item.pos-1,current_item.lookaheads) + current_state = go_back(current_state,y,current_item,state_dict) + stack.append((current_state,current_item)) + stack.reverse() + + initial_item = find_initial_item(self.automaton) + path = [(self.automaton,initial_item,False)] + visited = {(initial_item,self.automaton.idx)} + find_path_to(stack[0][0],stack[0][1],self.automaton,initial_item,path,visited) # una lista de tuplas que es (estado en que estoy,item en el que estoy) + + terminals = items_to_terminals(path[:len(path)-1],stack) + conflicts.append(self._generate_error(value,terminals,[x[1] for x in path] + [x[1] for x in stack[1:]],key[0],key[1])) + break + + return conflicts + + def _generate_error(self,conflict,terminals,items,state,lookahead): + string = ' '.join(x.Name for x in terminals) + return f'The string "{string}" is generated by the items {items}, stopping in state {state}, the conflict is because the automaton can do the following actions {conflict} looking at terminal "{lookahead}"' + +class LR0Parser(ShiftReduceParser): + + def __init__(self,G,verbose=False): + super().__init__(G,build_LR0_automaton,verbose) + self.lr_type = 'lr0' + + def _build_parsing_table(self,G): + assert len(G.startSymbol.productions) == 1, 'Grammar must be augmented' + + automaton = self.automaton + for i, node in enumerate(automaton): + if self.verbose: print(i, '\t', '\n\t '.join(str(x) for x in node.state), '\n') + node.idx = i + + for node in automaton: + idx = node.idx + for state in node.state: + item = state.state + # Your code here!!! + # - Fill `self.Action` and `self.Goto` according to `item`) + # - Feel free to use `self._register(...)`) + item = state.state + if item.IsReduceItem: + if item.production.Left == G.startSymbol: + self._register(self.action,(idx,G.EOF),(self.OK,None)) + else: + for c in G.terminals + [G.EOF]: + self._register(self.action, (idx,c),(self.REDUCE,item.production)) + else: + next_sym = item.NextSymbol + try: + next_state = node[next_sym.Name][0] + if next_sym.IsNonTerminal: + self._register(self.goto,(idx,next_sym),next_state.idx) + else: + self._register(self.action,(idx,next_sym),(self.SHIFT,next_state.idx)) + except KeyError: + self.errors.append(f'Node: {node} without transition with symbol {next_sym}') + return None + +class SLR1Parser(ShiftReduceParser): + + def __init__(self,G,verbose=False): + super().__init__(G,build_LR0_automaton,verbose) + self.lr_type = 'slr1' + + def _build_parsing_table(self,G): + assert len(G.startSymbol.productions) == 1, 'Grammar must be augmented' + + firsts = compute_firsts(G) + follows = compute_follows(G, firsts) + + automaton = self.automaton + for i, node in enumerate(automaton): + if self.verbose: print(i, '\t', '\n\t '.join(str(x) for x in node.state), '\n') + node.idx = i + + for node in automaton: + idx = node.idx + for state in node.state: + item = state.state + # Your code here!!! + # - Fill `self.Action` and `self.Goto` according to `item`) + # - Feel free to use `self._register(...)`) + item = state.state + if item.IsReduceItem: + if item.production.Left == G.startSymbol: + self._register(self.action,(idx,G.EOF),(self.OK,None)) + else: + for c in follows[item.production.Left]: + self._register(self.action, (idx,c),(self.REDUCE,item.production)) + else: + next_sym = item.NextSymbol + try: + next_state = node[next_sym.Name][0] + if next_sym.IsNonTerminal: + self._register(self.goto,(idx,next_sym),next_state.idx) + else: + self._register(self.action,(idx,next_sym),(self.SHIFT,next_state.idx)) + except KeyError: + self.errors.append(f'Node: {node} without transition with symbol {next_sym}') + return None + + +###################### LR1 ########################### +def expand(item, firsts): + next_symbol = item.NextSymbol + if next_symbol is None or not next_symbol.IsNonTerminal: + return [] + + lookaheads = ContainerSet() # lookahead = que yo quiero ver cuando vaya a reducir + # Your code here!!! (Compute lookahead for child items) + + for prev in item.Preview(): + lookaheads.update(compute_local_first(firsts,prev)) + + assert not lookaheads.contains_epsilon + + # Your code here!!! (Build and return child items) + return [ Item(x,0,lookaheads) for x in next_symbol.productions] + +def compress(items): + centers = {} + + for item in items: + center = item.Center() + try: + lookaheads = centers[center] + except KeyError: + centers[center] = lookaheads = set() + lookaheads.update(item.lookaheads) + + return { Item(x.production, x.pos, set(lookahead)) for x, lookahead in centers.items() } + +def closure_lr1(items, firsts): + closure = ContainerSet(*items) + + changed = True + while changed: + changed = False + + new_items = ContainerSet() + # Your code here!!! + for x in closure: + new_items.extend(expand(x,firsts)) + + changed = closure.update(new_items) + + return compress(closure) + +def goto_lr1(items, symbol, firsts=None, just_kernel=False): + assert just_kernel or firsts is not None, '`firsts` must be provided if `just_kernel=False`' + items = frozenset(item.NextItem() for item in items if item.NextSymbol == symbol) + return items if just_kernel else closure_lr1(items, firsts) + +def build_LR1_automaton(G): + assert len(G.startSymbol.productions) == 1, 'Grammar must be augmented' + + firsts = compute_firsts(G) + firsts[G.EOF] = ContainerSet(G.EOF) + + start_production = G.startSymbol.productions[0] + start_item = Item(start_production, 0, lookaheads=(G.EOF,)) + start = frozenset([start_item]) + + closure = closure_lr1(start, firsts) + automaton = State(frozenset(closure), True) + + pending = [ start ] + visited = { start: automaton } + + while pending: + current = pending.pop() + current_state = visited[current] + + for symbol in G.terminals + G.nonTerminals: + # Your code here!!! (Get/Build `next_state`) + next_state_key = goto_lr1(current_state.state,symbol,just_kernel=True) + # next_state_key = frozenset([i.NextItem() for i in current_state.state if i.NextSymbol == symbol]) + if not next_state_key: + continue + try: + next_state = visited[next_state_key] + except KeyError: + next_state_items = goto_lr1(current_state.state,symbol,firsts) + next_state = State(frozenset(next_state_items),True) + pending.append(next_state_key) + visited[next_state_key] = next_state + current_state.add_transition(symbol.Name, next_state) + + + automaton.set_formatter(multiline_formatter) + return automaton + +class LR1Parser(ShiftReduceParser): + + def __init__(self,G,verbose=False): + super().__init__(G,build_LR1_automaton,verbose) + self.lr_type = 'lr1' + + def _build_parsing_table(self,G): + assert len(G.startSymbol.productions) == 1, 'Grammar must be augmented' + + automaton = self.automaton + for i, node in enumerate(automaton): + if self.verbose: print(i, '\t', '\n\t '.join(str(x) for x in node.state), '\n') + node.idx = i + + for node in automaton: + idx = node.idx + for item in node.state: + # Your code here!!! + # - Fill `self.Action` and `self.Goto` according to `item`) + # - Feel free to use `self._register(...)`) + if item.IsReduceItem: + if item.production.Left == G.startSymbol: + self._register(self.action,(idx,G.EOF),(self.OK,None)) + else: + for c in item.lookaheads: + self._register(self.action, (idx,c),(self.REDUCE,item.production)) + else: + next_sym = item.NextSymbol + try: + next_state = node[next_sym.Name][0] + if next_sym.IsNonTerminal: + self._register(self.goto,(idx,next_sym),next_state.idx) + else: + self._register(self.action,(idx,next_sym),(self.SHIFT,next_state.idx)) + except KeyError: + self.errors.append(f'Node: {node} without transition with symbol {next_sym}') + return None + + +###################### LALR1 ########################### +def center_of(items): + return { item.Center() for item in items } + +def build_automaton_from_ds(initial:State, ds:DisjointSet): + for g in ds.groups: + r = g[0].representative.value + old_transitions = r.transitions + r.transitions = {} + for x in old_transitions: + node = None + for y in ds.groups: + if center_of(y[0].representative.value.state) == center_of(old_transitions[x][0].state): + node = y + break + assert node, 'group not found' + r.add_transition(x, node[0].representative.value) + end = [x for x in ds.groups if x[0].representative.value == initial][0] + return end[0].representative.value + +def from_lr1_to_lalr(lr1_automaton:State): + pending = [s for s in lr1_automaton] + ds = DisjointSet(*pending) + while pending: + to_merge = [] + pivot = center_of(pending[0].state) + for i,x in enumerate(pending): + if center_of(x.state) == pivot: + to_merge.append(x) + pending[i] = None + pending = [x for x in pending if x] + ds.merge(to_merge) + + for g in ds.groups: + r = g[0].representative + for x in [x for x in g if r != x]: + for prod in r.value.state: + for prod2 in x.value.state: + if prod.Center() == prod2.Center(): + new_lookahead = {x for x in prod.lookaheads} + new_lookahead.update({x for x in prod2.lookaheads}) + prod.lookaheads = frozenset(new_lookahead) + return build_automaton_from_ds(lr1_automaton,ds) + +def build_LALR1_automaton(G): + return from_lr1_to_lalr(build_LR1_automaton(G)) + +class LALR1Parser(ShiftReduceParser): + + def __init__(self,G,verbose=False): + super().__init__(G,build_LALR1_automaton,verbose) + self.lr_type = 'lalr1' + + def _build_parsing_table(self,G): + assert len(G.startSymbol.productions) == 1, 'Grammar must be augmented' + + automaton = self.automaton + for i, node in enumerate(automaton): + if self.verbose: print(i, '\t', '\n\t '.join(str(x) for x in node.state), '\n') + node.idx = i + + for node in automaton: + idx = node.idx + for item in node.state: + # Your code here!!! + # - Fill `self.Action` and `self.Goto` according to `item`) + # - Feel free to use `self._register(...)`) + if item.IsReduceItem: + if item.production.Left == G.startSymbol: + self._register(self.action,(idx,G.EOF),(self.OK,None)) + else: + for c in item.lookaheads: + self._register(self.action, (idx,c),(self.REDUCE,item.production)) + else: + next_sym = item.NextSymbol + try: + next_state = node[next_sym.Name][0] + if next_sym.IsNonTerminal: + self._register(self.goto,(idx,next_sym),next_state.idx) + else: + self._register(self.action,(idx,next_sym),(self.SHIFT,next_state.idx)) + except KeyError: + self.errors.append(f'Node: {node} without transition with symbol {next_sym}') + return None + + +############## Conflict Utils ####################### +def go_back(state_to_transpose, symbol,expected_item,transpose_state_dic): + """ + return a state that enter to state_to_transpose with symbol `symbol` + """ + candidates = transpose_state_dic[state_to_transpose.idx][1][symbol.Name] + candidates = [x for x in candidates if any([y for y in x.state if y.state == expected_item])] + return transpose_state_dic[candidates[0].idx][0] + +def find_path_to(goal_state,goal_item,current_state,current_item,path,item_checked): + """ + path: a list of tuple (item's state, item) that represent th items taken to reach current state + item_checked: a set of tuple (item, item's state.idx) that saves the visited items + return in path the items needed to reach the goal_item in goal_state from current_item in current_state + """ + if goal_state == current_state and goal_item == current_item: + return True + if current_item.IsReduceItem: + return False + + spanned_items = span_item(current_state,current_item) + + path.append((current_state[current_item[current_item.pos].Name][0],current_item.NextItem(),True if current_item[current_item.pos].IsNonTerminal else False)) + if not (path[-1][1],path[-1][0].idx) in item_checked: + item_checked.add((path[-1][1],path[-1][0].idx)) + if find_path_to(goal_state,goal_item,path[-1][0],path[-1][1],path,item_checked): + return True + path.pop() + + for next_item in spanned_items: + path.append((current_state,next_item,False)) + if not (path[-1][1],path[-1][0].idx) in item_checked: + item_checked.add((next_item,current_state.idx)) + if find_path_to(goal_state,goal_item,path[-1][0],path[-1][1],path,item_checked): + return True + path.pop() + return False + +def items_to_terminals(first_path, second_path): + """ + concat the terminals of first_path an the sentence of second_path + """ + terminals = [] + for i,(state,item,skipped) in enumerate(first_path): + if not item.IsReduceItem: + if i != 0 and skipped: # before skiped: + last_state,last_item,skipped = first_path[i-1] + spanned_items = span_item(last_state,last_item) + for next_item in spanned_items: + next_terminals = item_sentence(last_state,next_item) + if next_terminals: + terminals += next_terminals + break + if item[item.pos].IsTerminal: + terminals.append(item[item.pos]) + + for state,item in second_path: + if not item.IsReduceItem: + if item[item.pos].IsTerminal: + terminals.append(item[item.pos]) + else: + spanned_items = span_item(state,item) + terminals += item_sentence(state,spanned_items[0]) + return terminals + +def find_initial_item(initial_state): + for in_state in initial_state.state: + item = in_state.state + if len(item.production.Right) == 1 and item.production.Right[0].IsNonTerminal \ + and "'" in item.production.Left.Name: + return item + +def find_conflict_item(state,production): + for in_state in state.state: + if isinstance(in_state,Item): + if in_state.production == production: + return in_state + else: + item = in_state.state + if item.production == production: + return item + +def span_item(state,item): + return [x.state for x in state.state if x.state.production.Left == item[item.pos] and x.state.pos == 0] + # Tambien incluir los lookahead en la condicion? + +def item_sentence(state,item): + path = [] + _item_sentence(state,item,set(),path) + return path + +def _item_sentence(state:State,item,visited,path): + + if item.IsReduceItem: + return True + + next_symb = item.NextSymbol + if isinstance(next_symb,NonTerminal): + if not (state.idx,next_symb) in visited: + visited.add((state.idx,next_symb)) + for next_item in span_item(state,item): + if _item_sentence(state,next_item,visited,path): + if _item_sentence(state[next_symb.Name][0],item.NextItem(),visited,path): + return True + else: + path.append(next_symb) + if _item_sentence(state[next_symb.Name][0],item.NextItem(),visited,path): + return True path.pop() \ No newline at end of file diff --git a/src/cool2/lib/shortcut.py b/src/cool2/lib/shortcut.py index 3d0d365bf..ebff7259f 100644 --- a/src/cool2/lib/shortcut.py +++ b/src/cool2/lib/shortcut.py @@ -1,12 +1,12 @@ -from lib.lang.language_ll1 import build_LL1 -from lib.lang.language_lr import build_LR -from lib.lang.language_regular import build_Lan_Reg -from lib.grammar.grammar_fixer import fix_common_prefix,fix_e_productions,fix_left_recursion,fix_useless_symbols -from lib.grammar.grammar_util import grammar_to_text -from lib.grammar.grammar_parser import get_grammar_from_text -from lib.utils.first_follow import compute_firsts, compute_follows - -def build_LR_2(lr_type): - def build(tok_def,gram_def,error): - return build_LR(tok_def,gram_def,lr_type,error) +from lib.lang.language_ll1 import build_LL1 +from lib.lang.language_lr import build_LR +from lib.lang.language_regular import build_Lan_Reg +from lib.grammar.grammar_fixer import fix_common_prefix,fix_e_productions,fix_left_recursion,fix_useless_symbols +from lib.grammar.grammar_util import grammar_to_text +from lib.grammar.grammar_parser import get_grammar_from_text +from lib.utils.first_follow import compute_firsts, compute_follows + +def build_LR_2(lr_type): + def build(tok_def,gram_def,error): + return build_LR(tok_def,gram_def,lr_type,error) return build \ No newline at end of file diff --git a/src/cool2/lib/utils/algorithms.py b/src/cool2/lib/utils/algorithms.py index 90405befe..a1e7bce34 100644 --- a/src/cool2/lib/utils/algorithms.py +++ b/src/cool2/lib/utils/algorithms.py @@ -1,21 +1,21 @@ - -def permutation(n,k): - """ - returns an iter from all permutations in n places of k elements\n - Ex: n = 2, k = 2\n - returns [0,0],[0,1],[1,1],[1,0] - """ - perm = [None]*n - if n == 0: - return [] - for t in inner_permutation(perm,0,k,n): - yield t - -def inner_permutation(perm,i,k,n): - for p in range(k): - perm[i] = p - if i < n-1: - for t in inner_permutation(perm,i+1,k,n): - yield t - else: - yield tuple(perm) + +def permutation(n,k): + """ + returns an iter from all permutations in n places of k elements\n + Ex: n = 2, k = 2\n + returns [0,0],[0,1],[1,1],[1,0] + """ + perm = [None]*n + if n == 0: + return [] + for t in inner_permutation(perm,0,k,n): + yield t + +def inner_permutation(perm,i,k,n): + for p in range(k): + perm[i] = p + if i < n-1: + for t in inner_permutation(perm,i+1,k,n): + yield t + else: + yield tuple(perm) diff --git a/src/cool2/lib/utils/automaton.py b/src/cool2/lib/utils/automaton.py index 2d04aee0d..a145cafd6 100644 --- a/src/cool2/lib/utils/automaton.py +++ b/src/cool2/lib/utils/automaton.py @@ -1,463 +1,463 @@ -from cmp.utils import ContainerSet -from cmp.utils import DisjointSet -from cmp.automata import State -import pydot - -def state_transpose(initial_state:State): - """ - returns the state equivalent to initial_state on the transpose graph and a dictionary mapping idx to new states transposed - """ - state_dict = {} # state_dict[key] = (original_state,copy_of_original_state_with_transposed_transitions) - idx = 0 - for x in initial_state: - new_state = State(x.state,x.final,x.formatter,x.shape) - if not hasattr(x,'idx'): - x.idx = idx - idx+=1 - new_state.idx = x.idx - state_dict[x.idx] = (x,new_state) - - for x in initial_state: - for key,states in x.transitions.items(): - for state in states: - state_dict[state.idx][1].add_transition(key,state_dict[x.idx][1]) - - return state_dict[initial_state.idx][1],state_dict - -class NFA: - def __init__(self, states, finals, transitions, start=0): - self.states = states - self.start = start - self.finals = set(finals) - self.map = transitions - self.vocabulary = set() - self.transitions = { state: {} for state in range(states) } - - for (origin, symbol), destinations in transitions.items(): - assert hasattr(destinations, '__iter__'), 'Invalid collection of states' - self.transitions[origin][symbol] = destinations - self.vocabulary.add(symbol) - - self.vocabulary.discard('') - - def epsilon_transitions(self, state): - assert state in self.transitions, 'Invalid state' - try: - return self.transitions[state][''] - except KeyError: - return () - - def graph(self): - G = pydot.Dot(rankdir='LR', margin=0.1) - G.add_node(pydot.Node('start', shape='plaintext', label='', width=0, height=0)) - - for (start, tran), destinations in self.map.items(): - tran = 'ε' if tran == '' else tran - G.add_node(pydot.Node(start, shape='circle', style='bold' if start in self.finals else '')) - for end in destinations: - G.add_node(pydot.Node(end, shape='circle', style='bold' if end in self.finals else '')) - G.add_edge(pydot.Edge(start, end, label=tran, labeldistance=2)) - - G.add_edge(pydot.Edge('start', self.start, label='', style='dashed')) - # G = 'Graph Drawed' - return G - - def _repr_svg_(self): - try: - return self.graph().create_svg().decode('utf8') - except: - pass - -class DFA(NFA): - - def __init__(self, states, finals, transitions, start=0): - assert all(isinstance(value, int) for value in transitions.values()) - assert all(len(symbol) > 0 for origin, symbol in transitions) - - transitions = { key: [value] for key, value in transitions.items() } - NFA.__init__(self, states, finals, transitions, start) - self.current = start - - def _move(self, symbol): - # Your code here - try: - self.current = self.transitions[self.current][symbol][0] - return True - except: # Ver el error q da - return False - - def _reset(self): - self.current = self.start - - def recognize(self, string): - # Your code here - self._reset() - for i in range(0,len(string)): - if not self._move(string[i]): - return False - return self.current in self.finals - -def goto(automaton:NFA, states, symbol): - # Devuelve los estados en los cuales existe una transicion desde alguno de los estados de states mediante - # symbol - moves = set() - for state in states: - # Your code here - try: - for i in automaton.transitions[state][symbol]: - moves.add(i) - except: - pass - return moves - -def epsilon_closure(automaton, states): - pending = [ s for s in states ] # equivalente a list(states) pero me gusta así :p - closure = { s for s in states } # equivalente a set(states) pero me gusta así :p - analiced = [False]*automaton.states - - while pending: - state = pending.pop() - # Your code here - try: - for i in automaton.transitions[state]['']: - if not analiced[i]: - closure.add(i) - pending.append(i) - except KeyError: # Ver el error q da - pass - analiced[state] = True - return ContainerSet(*closure) - -def first(cond, itera): - for x in iter(itera): - if cond(x): - return x - return None - -def nfa_to_dfa(automaton): - transitions = {} - - start = epsilon_closure(automaton, [automaton.start]) - start.id = 0 - start.is_final = any(s in automaton.finals for s in start) - states = [ start ] - pending = [ start ] - ids = 1 - while pending: - state = pending.pop() - - for symbol in automaton.vocabulary: - # Your code here - # ... - new_state = epsilon_closure(automaton,goto(automaton, state, symbol)) - - if len(new_state) == 0: # no genera nada - continue - - try: - transitions[state.id, symbol] - assert False, 'Invalid DFA!!!' - except KeyError: - # Your code here - if new_state in states: - transitions[state.id, symbol] = first(lambda x: x.set == new_state.set,states).id - continue - - new_state.id = ids - ids += 1 - new_state.is_final = any(s in automaton.finals for s in new_state) - transitions[state.id, symbol] = new_state.id - - pending.append(new_state) # agregar id al new_state - states.append(new_state) - - finals = [ state.id for state in states if state.is_final ] - dfa = DFA(len(states), finals, transitions) - return dfa - -def automata_union(a1, a2): - transitions = {} - - start = 0 - d1 = 1 - d2 = a1.states + d1 - final = a2.states + d2 - - for (origin, symbol), destinations in a1.map.items(): - ## Relocate a1 transitions ... - # Your code here - transitions[origin + d1,symbol] = [t for t in map(lambda x: x+d1,destinations)] - - - for (origin, symbol), destinations in a2.map.items(): - ## Relocate a2 transitions ... - # Your code here - transitions[origin + d2,symbol] = [t for t in map(lambda x: x+d2,destinations)] - - ## Add transitions from start state ... - # Your code here - transitions[start,''] = [a1.start + d1, a2.start + d2] # epsilon transitions to initials of a1 and a2 - - ## Add transitions to final state ... - # Your code here - for f1 in a1.finals: - try: - transitions[f1 + d1,''].append(final) - except KeyError: - transitions[f1 + d1,''] = [final] - - for f2 in a2.finals: - try: - transitions[f2 + d2,''].append(final) - except KeyError: - transitions[f2 + d2,''] = [final] - - states = a1.states + a2.states + 2 - finals = { final } - - return NFA(states, finals, transitions, start) - -def automata_concatenation(a1, a2): - transitions = {} - - start = 0 - d1 = 0 - d2 = a1.states + d1 - final = a2.states + d2 - - for (origin, symbol), destinations in a1.map.items(): - ## Relocate a1 transitions ... - # Your code here - transitions[origin + d1, symbol] = [t for t in map(lambda x: x+d1,destinations)] - - for (origin, symbol), destinations in a2.map.items(): - ## Relocate a2 transitions ... - # Your code here - transitions[origin + d2, symbol] = [t for t in map(lambda x: x+d2,destinations)] - - - ## Add transitions to final state ... - # Your code here - for f1 in a1.finals: - try: - transitions[f1 + d1,''].append(a2.start + d2) - except KeyError: - transitions[f1 + d1,''] = [a2.start + d2] - - for f2 in a2.finals: - try: - transitions[f2 + d2,''].append(a2.start + d2) - except KeyError: - transitions[f2 + d2,''] = [final] - states = a1.states + a2.states + 1 - finals = { final } - - return NFA(states, finals, transitions, start) - -def automata_closure(a1): - transitions = {} - - start = 0 - d1 = 1 - final = a1.states + d1 - - for (origin, symbol), destinations in a1.map.items(): - ## Relocate automaton transitions ... - # Your code here - transitions[origin + d1,symbol] = [t for t in map(lambda x: x+d1,destinations)] - - ## Add transitions from start state ... - # Your code here - transitions[start,''] = [a1.start + d1, final] - - ## Add transitions to final state and to start state ... - # Your code here - for f1 in a1.finals: - try: - transitions[f1 + d1,''].append(a1.start + d1) - transitions[f1 + d1,''].append(final) - except: - transitions[f1 + d1,''] = [a1.start + d1] - transitions[f1 + d1,''].append(final) - - - states = a1.states + 2 - finals = { final } - - return NFA(states, finals, transitions, start) - -def distinguish_states(group, automaton, partition): - split = {} - vocabulary = tuple(automaton.vocabulary) - for member1 in group: - # Your code here - # mira a ver a que grupo pertenece - for group_representative in split.keys(): - # chequea el vocabulario - for symbol in vocabulary: - err1, err2 = False, False - try: - m1 = automaton.transitions[partition[group_representative].value][symbol][0] - except KeyError: - err1 = True - try: - m2 = automaton.transitions[member1.value][symbol][0] - except KeyError: - err2 = True - - if err1 and err2: - continue - elif (not err1 and err2) or (err1 and not err2): - break - - if partition[m1].representative != partition[m2].representative: - break - else: - # matcheo todo el vocabulario - split[group_representative].append(member1.value) - break - else: - # no pertenece a ninguno por lo tanto creo uno nuevo - split[member1.value] = [member1.value] - return [group for group in split.values()] - -def state_minimization(automaton): - partition = DisjointSet(*range(automaton.states)) - - ## partition = { NON-FINALS | FINALS } - # Your code here - partition.merge([ x for x in automaton.finals]) - - non_finals = [nf for nf in filter(lambda x: not x in automaton.finals,range(automaton.states))] - partition.merge(non_finals) - - while True: - new_partition = DisjointSet(*range(automaton.states)) - - ## Split each group if needed (use distinguish_states(group, automaton, partition)) - # Your code here - for gr in partition.groups: - for gr in distinguish_states(gr,automaton,partition) : - new_partition.merge(gr) - - if len(new_partition) == len(partition): - break - - partition = new_partition - - return partition - -def automata_minimization(automaton): - partition = state_minimization(automaton) - - states = [s for s in partition.representatives] - transitions = {} - - for i, state in enumerate(states): - # Your code here - origin = state.value - for symbol, destinations in automaton.transitions[origin].items(): - # Your code here - transition = states.index(partition[destinations[0]].representative) - - try: - transitions[i,symbol] - assert False - except KeyError: - # Your code here - transitions[i,symbol] = transition - - # Your code here - finals = set() - for gr in partition.groups: - for i in gr: - if i.value in automaton.finals: - finals.add(states.index(i.representative)) - start = states.index(partition[automaton.start].representative) - - return DFA(len(states), finals, transitions, start) - -def automata_full_determined(a1): - transitions = {} - - if a1.states * len(a1.vocabulary) == len(a1.map.items()): - for (origin, symbol), destinations in a1.map.items(): - # Your code here - transitions[origin,symbol] = [t for t in destinations] - - return NFA(a1.states,a1.finals,transitions,a1.start) - - - start = 0 - # final = a1.states - end = a1.states #final + 1 - - for (origin, symbol), destinations in a1.map.items(): - # Your code here - transitions[origin,symbol] = [t for t in destinations] - - - for i in range(end): - for x in a1.vocabulary: - try: - transitions[i,x] - except KeyError: - transitions[i,x] = [end] - - for v in a1.vocabulary: - try: - transitions[end,v].append(end) - except KeyError: - transitions[end,v] = [end] - - states = a1.states + 1 - finals = set(a1.finals) - - return NFA(states, finals, transitions, start) - -def automata_complement(a1): - a1 = automata_full_determined(a1) - transitions = {} - - start = a1.start - final = a1.states # a1.states estado epsilon - - for (origin, symbol), destinations in a1.map.items(): - # Your code here - transitions[origin,symbol] = [t for t in destinations] - - for x in filter(lambda x: not x in a1.finals, range(a1.states)): - transitions[x,''] = [final] - - states = a1.states + 1 - finals = { final } - - return NFA(states, finals, transitions, start) - -def automata_intersection(a1,a2): - return automata_complement(automata_union(automata_complement(a1),automata_complement(a2))) - -def automata_difference(a1,a2): - return automata_intersection(a1,automata_complement(a2)) - -def automata_reverse(a1): - transitions = {} - - start = a1.states - final = a1.start - - for (origin, symbol), destinations in a1.map.items(): - ## Relocate automaton transitions ... - # Your code here - for x in destinations: - try: - transitions[x,symbol].append(origin) - except KeyError: - transitions[x,symbol] = [origin] - - transitions[start,''] = [x for x in a1.finals] - - states = a1.states + 1 - finals = { final } - - return NFA(states, finals, transitions, start) +from cmp.utils import ContainerSet +from cmp.utils import DisjointSet +from cmp.automata import State +import pydot + +def state_transpose(initial_state:State): + """ + returns the state equivalent to initial_state on the transpose graph and a dictionary mapping idx to new states transposed + """ + state_dict = {} # state_dict[key] = (original_state,copy_of_original_state_with_transposed_transitions) + idx = 0 + for x in initial_state: + new_state = State(x.state,x.final,x.formatter,x.shape) + if not hasattr(x,'idx'): + x.idx = idx + idx+=1 + new_state.idx = x.idx + state_dict[x.idx] = (x,new_state) + + for x in initial_state: + for key,states in x.transitions.items(): + for state in states: + state_dict[state.idx][1].add_transition(key,state_dict[x.idx][1]) + + return state_dict[initial_state.idx][1],state_dict + +class NFA: + def __init__(self, states, finals, transitions, start=0): + self.states = states + self.start = start + self.finals = set(finals) + self.map = transitions + self.vocabulary = set() + self.transitions = { state: {} for state in range(states) } + + for (origin, symbol), destinations in transitions.items(): + assert hasattr(destinations, '__iter__'), 'Invalid collection of states' + self.transitions[origin][symbol] = destinations + self.vocabulary.add(symbol) + + self.vocabulary.discard('') + + def epsilon_transitions(self, state): + assert state in self.transitions, 'Invalid state' + try: + return self.transitions[state][''] + except KeyError: + return () + + def graph(self): + G = pydot.Dot(rankdir='LR', margin=0.1) + G.add_node(pydot.Node('start', shape='plaintext', label='', width=0, height=0)) + + for (start, tran), destinations in self.map.items(): + tran = 'ε' if tran == '' else tran + G.add_node(pydot.Node(start, shape='circle', style='bold' if start in self.finals else '')) + for end in destinations: + G.add_node(pydot.Node(end, shape='circle', style='bold' if end in self.finals else '')) + G.add_edge(pydot.Edge(start, end, label=tran, labeldistance=2)) + + G.add_edge(pydot.Edge('start', self.start, label='', style='dashed')) + # G = 'Graph Drawed' + return G + + def _repr_svg_(self): + try: + return self.graph().create_svg().decode('utf8') + except: + pass + +class DFA(NFA): + + def __init__(self, states, finals, transitions, start=0): + assert all(isinstance(value, int) for value in transitions.values()) + assert all(len(symbol) > 0 for origin, symbol in transitions) + + transitions = { key: [value] for key, value in transitions.items() } + NFA.__init__(self, states, finals, transitions, start) + self.current = start + + def _move(self, symbol): + # Your code here + try: + self.current = self.transitions[self.current][symbol][0] + return True + except: # Ver el error q da + return False + + def _reset(self): + self.current = self.start + + def recognize(self, string): + # Your code here + self._reset() + for i in range(0,len(string)): + if not self._move(string[i]): + return False + return self.current in self.finals + +def goto(automaton:NFA, states, symbol): + # Devuelve los estados en los cuales existe una transicion desde alguno de los estados de states mediante + # symbol + moves = set() + for state in states: + # Your code here + try: + for i in automaton.transitions[state][symbol]: + moves.add(i) + except: + pass + return moves + +def epsilon_closure(automaton, states): + pending = [ s for s in states ] # equivalente a list(states) pero me gusta así :p + closure = { s for s in states } # equivalente a set(states) pero me gusta así :p + analiced = [False]*automaton.states + + while pending: + state = pending.pop() + # Your code here + try: + for i in automaton.transitions[state]['']: + if not analiced[i]: + closure.add(i) + pending.append(i) + except KeyError: # Ver el error q da + pass + analiced[state] = True + return ContainerSet(*closure) + +def first(cond, itera): + for x in iter(itera): + if cond(x): + return x + return None + +def nfa_to_dfa(automaton): + transitions = {} + + start = epsilon_closure(automaton, [automaton.start]) + start.id = 0 + start.is_final = any(s in automaton.finals for s in start) + states = [ start ] + pending = [ start ] + ids = 1 + while pending: + state = pending.pop() + + for symbol in automaton.vocabulary: + # Your code here + # ... + new_state = epsilon_closure(automaton,goto(automaton, state, symbol)) + + if len(new_state) == 0: # no genera nada + continue + + try: + transitions[state.id, symbol] + assert False, 'Invalid DFA!!!' + except KeyError: + # Your code here + if new_state in states: + transitions[state.id, symbol] = first(lambda x: x.set == new_state.set,states).id + continue + + new_state.id = ids + ids += 1 + new_state.is_final = any(s in automaton.finals for s in new_state) + transitions[state.id, symbol] = new_state.id + + pending.append(new_state) # agregar id al new_state + states.append(new_state) + + finals = [ state.id for state in states if state.is_final ] + dfa = DFA(len(states), finals, transitions) + return dfa + +def automata_union(a1, a2): + transitions = {} + + start = 0 + d1 = 1 + d2 = a1.states + d1 + final = a2.states + d2 + + for (origin, symbol), destinations in a1.map.items(): + ## Relocate a1 transitions ... + # Your code here + transitions[origin + d1,symbol] = [t for t in map(lambda x: x+d1,destinations)] + + + for (origin, symbol), destinations in a2.map.items(): + ## Relocate a2 transitions ... + # Your code here + transitions[origin + d2,symbol] = [t for t in map(lambda x: x+d2,destinations)] + + ## Add transitions from start state ... + # Your code here + transitions[start,''] = [a1.start + d1, a2.start + d2] # epsilon transitions to initials of a1 and a2 + + ## Add transitions to final state ... + # Your code here + for f1 in a1.finals: + try: + transitions[f1 + d1,''].append(final) + except KeyError: + transitions[f1 + d1,''] = [final] + + for f2 in a2.finals: + try: + transitions[f2 + d2,''].append(final) + except KeyError: + transitions[f2 + d2,''] = [final] + + states = a1.states + a2.states + 2 + finals = { final } + + return NFA(states, finals, transitions, start) + +def automata_concatenation(a1, a2): + transitions = {} + + start = 0 + d1 = 0 + d2 = a1.states + d1 + final = a2.states + d2 + + for (origin, symbol), destinations in a1.map.items(): + ## Relocate a1 transitions ... + # Your code here + transitions[origin + d1, symbol] = [t for t in map(lambda x: x+d1,destinations)] + + for (origin, symbol), destinations in a2.map.items(): + ## Relocate a2 transitions ... + # Your code here + transitions[origin + d2, symbol] = [t for t in map(lambda x: x+d2,destinations)] + + + ## Add transitions to final state ... + # Your code here + for f1 in a1.finals: + try: + transitions[f1 + d1,''].append(a2.start + d2) + except KeyError: + transitions[f1 + d1,''] = [a2.start + d2] + + for f2 in a2.finals: + try: + transitions[f2 + d2,''].append(a2.start + d2) + except KeyError: + transitions[f2 + d2,''] = [final] + states = a1.states + a2.states + 1 + finals = { final } + + return NFA(states, finals, transitions, start) + +def automata_closure(a1): + transitions = {} + + start = 0 + d1 = 1 + final = a1.states + d1 + + for (origin, symbol), destinations in a1.map.items(): + ## Relocate automaton transitions ... + # Your code here + transitions[origin + d1,symbol] = [t for t in map(lambda x: x+d1,destinations)] + + ## Add transitions from start state ... + # Your code here + transitions[start,''] = [a1.start + d1, final] + + ## Add transitions to final state and to start state ... + # Your code here + for f1 in a1.finals: + try: + transitions[f1 + d1,''].append(a1.start + d1) + transitions[f1 + d1,''].append(final) + except: + transitions[f1 + d1,''] = [a1.start + d1] + transitions[f1 + d1,''].append(final) + + + states = a1.states + 2 + finals = { final } + + return NFA(states, finals, transitions, start) + +def distinguish_states(group, automaton, partition): + split = {} + vocabulary = tuple(automaton.vocabulary) + for member1 in group: + # Your code here + # mira a ver a que grupo pertenece + for group_representative in split.keys(): + # chequea el vocabulario + for symbol in vocabulary: + err1, err2 = False, False + try: + m1 = automaton.transitions[partition[group_representative].value][symbol][0] + except KeyError: + err1 = True + try: + m2 = automaton.transitions[member1.value][symbol][0] + except KeyError: + err2 = True + + if err1 and err2: + continue + elif (not err1 and err2) or (err1 and not err2): + break + + if partition[m1].representative != partition[m2].representative: + break + else: + # matcheo todo el vocabulario + split[group_representative].append(member1.value) + break + else: + # no pertenece a ninguno por lo tanto creo uno nuevo + split[member1.value] = [member1.value] + return [group for group in split.values()] + +def state_minimization(automaton): + partition = DisjointSet(*range(automaton.states)) + + ## partition = { NON-FINALS | FINALS } + # Your code here + partition.merge([ x for x in automaton.finals]) + + non_finals = [nf for nf in filter(lambda x: not x in automaton.finals,range(automaton.states))] + partition.merge(non_finals) + + while True: + new_partition = DisjointSet(*range(automaton.states)) + + ## Split each group if needed (use distinguish_states(group, automaton, partition)) + # Your code here + for gr in partition.groups: + for gr in distinguish_states(gr,automaton,partition) : + new_partition.merge(gr) + + if len(new_partition) == len(partition): + break + + partition = new_partition + + return partition + +def automata_minimization(automaton): + partition = state_minimization(automaton) + + states = [s for s in partition.representatives] + transitions = {} + + for i, state in enumerate(states): + # Your code here + origin = state.value + for symbol, destinations in automaton.transitions[origin].items(): + # Your code here + transition = states.index(partition[destinations[0]].representative) + + try: + transitions[i,symbol] + assert False + except KeyError: + # Your code here + transitions[i,symbol] = transition + + # Your code here + finals = set() + for gr in partition.groups: + for i in gr: + if i.value in automaton.finals: + finals.add(states.index(i.representative)) + start = states.index(partition[automaton.start].representative) + + return DFA(len(states), finals, transitions, start) + +def automata_full_determined(a1): + transitions = {} + + if a1.states * len(a1.vocabulary) == len(a1.map.items()): + for (origin, symbol), destinations in a1.map.items(): + # Your code here + transitions[origin,symbol] = [t for t in destinations] + + return NFA(a1.states,a1.finals,transitions,a1.start) + + + start = 0 + # final = a1.states + end = a1.states #final + 1 + + for (origin, symbol), destinations in a1.map.items(): + # Your code here + transitions[origin,symbol] = [t for t in destinations] + + + for i in range(end): + for x in a1.vocabulary: + try: + transitions[i,x] + except KeyError: + transitions[i,x] = [end] + + for v in a1.vocabulary: + try: + transitions[end,v].append(end) + except KeyError: + transitions[end,v] = [end] + + states = a1.states + 1 + finals = set(a1.finals) + + return NFA(states, finals, transitions, start) + +def automata_complement(a1): + a1 = automata_full_determined(a1) + transitions = {} + + start = a1.start + final = a1.states # a1.states estado epsilon + + for (origin, symbol), destinations in a1.map.items(): + # Your code here + transitions[origin,symbol] = [t for t in destinations] + + for x in filter(lambda x: not x in a1.finals, range(a1.states)): + transitions[x,''] = [final] + + states = a1.states + 1 + finals = { final } + + return NFA(states, finals, transitions, start) + +def automata_intersection(a1,a2): + return automata_complement(automata_union(automata_complement(a1),automata_complement(a2))) + +def automata_difference(a1,a2): + return automata_intersection(a1,automata_complement(a2)) + +def automata_reverse(a1): + transitions = {} + + start = a1.states + final = a1.start + + for (origin, symbol), destinations in a1.map.items(): + ## Relocate automaton transitions ... + # Your code here + for x in destinations: + try: + transitions[x,symbol].append(origin) + except KeyError: + transitions[x,symbol] = [origin] + + transitions[start,''] = [x for x in a1.finals] + + states = a1.states + 1 + finals = { final } + + return NFA(states, finals, transitions, start) diff --git a/src/cool2/lib/utils/first_follow.py b/src/cool2/lib/utils/first_follow.py index dfbaabce9..df7f96a96 100644 --- a/src/cool2/lib/utils/first_follow.py +++ b/src/cool2/lib/utils/first_follow.py @@ -1,108 +1,108 @@ -from cmp.utils import ContainerSet - -def compute_local_first(firsts, alpha): - first_alpha = ContainerSet() - try: - alpha_is_epsilon = alpha.IsEpsilon - except: - alpha_is_epsilon = False - ################################################### - # alpha == epsilon ? First(alpha) = { epsilon } - ################################################### - # # - if alpha_is_epsilon: - first_alpha.set_epsilon() - return first_alpha - ################################################### - ################################################### - # alpha = X1 ... XN - # First(X1) subconjunto First(alpha) - # epsilon pertenece a First(X1)...First(Xi) ? First(Xi+1) subconjunto de First(X) y First(alpha) - # epsilon pertenece a First(X1)...First(XN) ? epsilon pertence a First(X) y al First(alpha) - ################################################### - # # - change = False - for i in alpha: - if not firsts[i].contains_epsilon: - # epsilon pertenece a First(X1)...First(Xi) ? First(Xi+1) subconjunto de First(X) y First(alpha) - change = True - for j in firsts[i]: - first_alpha.add(j) - if change: - break - if not change: - # epsilon pertenece a First(X1)...First(XN) ? epsilon pertence a First(X) y al First(alpha) - first_alpha.set_epsilon() - ################################################### - # First(alpha) - return first_alpha - - -def compute_firsts(G): - - firsts = {} - change = True - # init First(Vt) - for terminal in G.terminals: - firsts[terminal] = ContainerSet(terminal) - # init First(Vn) - for nonterminal in G.nonTerminals: - firsts[nonterminal] = ContainerSet() - while change: - change = False - # P: X -> alpha - for production in G.Productions: - X = production.Left - alpha = production.Right - # get current First(X) - first_X = firsts[X] - # init First(alpha) - try: - first_alpha = firsts[alpha] - except KeyError: - first_alpha = firsts[alpha] = ContainerSet() - # CurrentFirst(alpha)??? - local_first = compute_local_first(firsts, alpha) - # update First(X) and First(alpha) from CurrentFirst(alpha) - change |= first_alpha.hard_update(local_first) - change |= first_X.hard_update(local_first) - # First(Vt) + First(Vt) + First(RightSides) - return firsts - -def compute_follows(G,firsts): - - follows = { } - change = True - local_firsts = { } - # init Follow(Vn) - for nonterminal in G.nonTerminals: - follows[nonterminal] = ContainerSet() - follows[G.startSymbol] = ContainerSet(G.EOF) - while change: - change = False - # P: X -> alpha - for production in G.Productions: - X = production.Left - alpha = production.Right - follow_X = follows[X] - ################################################### - # X -> zeta Y beta - # First(beta) - { epsilon } subset of Follow(Y) - # beta ->* epsilon or X -> zeta Y ? Follow(X) subset of Follow(Y) - ################################################### - # # - j = 0 - alpha2 = {} - for i in alpha: - alpha2[j] = i - j+=1 - for i in alpha2.keys(): - y = alpha2[i] - if y.IsNonTerminal: - local_firsts = compute_local_first(firsts,alpha[i+1:]) - change |= follows[y].update(local_firsts) - if local_firsts.contains_epsilon: - change |= follows[y].update(follow_X) - ################################################### - # Follow(Vn) +from cmp.utils import ContainerSet + +def compute_local_first(firsts, alpha): + first_alpha = ContainerSet() + try: + alpha_is_epsilon = alpha.IsEpsilon + except: + alpha_is_epsilon = False + ################################################### + # alpha == epsilon ? First(alpha) = { epsilon } + ################################################### + # # + if alpha_is_epsilon: + first_alpha.set_epsilon() + return first_alpha + ################################################### + ################################################### + # alpha = X1 ... XN + # First(X1) subconjunto First(alpha) + # epsilon pertenece a First(X1)...First(Xi) ? First(Xi+1) subconjunto de First(X) y First(alpha) + # epsilon pertenece a First(X1)...First(XN) ? epsilon pertence a First(X) y al First(alpha) + ################################################### + # # + change = False + for i in alpha: + if not firsts[i].contains_epsilon: + # epsilon pertenece a First(X1)...First(Xi) ? First(Xi+1) subconjunto de First(X) y First(alpha) + change = True + for j in firsts[i]: + first_alpha.add(j) + if change: + break + if not change: + # epsilon pertenece a First(X1)...First(XN) ? epsilon pertence a First(X) y al First(alpha) + first_alpha.set_epsilon() + ################################################### + # First(alpha) + return first_alpha + + +def compute_firsts(G): + + firsts = {} + change = True + # init First(Vt) + for terminal in G.terminals: + firsts[terminal] = ContainerSet(terminal) + # init First(Vn) + for nonterminal in G.nonTerminals: + firsts[nonterminal] = ContainerSet() + while change: + change = False + # P: X -> alpha + for production in G.Productions: + X = production.Left + alpha = production.Right + # get current First(X) + first_X = firsts[X] + # init First(alpha) + try: + first_alpha = firsts[alpha] + except KeyError: + first_alpha = firsts[alpha] = ContainerSet() + # CurrentFirst(alpha)??? + local_first = compute_local_first(firsts, alpha) + # update First(X) and First(alpha) from CurrentFirst(alpha) + change |= first_alpha.hard_update(local_first) + change |= first_X.hard_update(local_first) + # First(Vt) + First(Vt) + First(RightSides) + return firsts + +def compute_follows(G,firsts): + + follows = { } + change = True + local_firsts = { } + # init Follow(Vn) + for nonterminal in G.nonTerminals: + follows[nonterminal] = ContainerSet() + follows[G.startSymbol] = ContainerSet(G.EOF) + while change: + change = False + # P: X -> alpha + for production in G.Productions: + X = production.Left + alpha = production.Right + follow_X = follows[X] + ################################################### + # X -> zeta Y beta + # First(beta) - { epsilon } subset of Follow(Y) + # beta ->* epsilon or X -> zeta Y ? Follow(X) subset of Follow(Y) + ################################################### + # # + j = 0 + alpha2 = {} + for i in alpha: + alpha2[j] = i + j+=1 + for i in alpha2.keys(): + y = alpha2[i] + if y.IsNonTerminal: + local_firsts = compute_local_first(firsts,alpha[i+1:]) + change |= follows[y].update(local_firsts) + if local_firsts.contains_epsilon: + change |= follows[y].update(follow_X) + ################################################### + # Follow(Vn) return follows \ No newline at end of file diff --git a/src/cool2/lib/utils/trie.py b/src/cool2/lib/utils/trie.py index 86ba0e9d0..4fb056398 100644 --- a/src/cool2/lib/utils/trie.py +++ b/src/cool2/lib/utils/trie.py @@ -1,63 +1,63 @@ -class TrieNode(): - - sons = None - value = None - terminal = None - - def __init__(self,value,terminal = False,sons = []): - self.value = value - self.sons = {} - self.terminal = terminal - for x in sons: - self.add_son(x) - - def add_son(self,item): - assert isinstance(item,TrieNode), "item must be a TrieNode" - self.sons[item.value] = item - - def find_son(self,son_value): - return self.sons[son_value] - - def __iter__(self): - yield self - for x in self.sons: - for y in self.sons[x]: - yield y - - def __str__(self): - rep = f'{self.value}[' - for x in self.sons: - rep += f'{self.sons[x]},' - rep += ']\n' - return rep - -class Trie: - top = None - - def __init__(self): - self.top = TrieNode('^') - - def LCP(self, item): - current = self.top - idx = 0 - for x in item: - try: - current = current.find_son(x) - idx += 1 - except KeyError: - break - return current,idx - - def add(self,item): - where_to,from_this = self.LCP(item) - for x in item[from_this:]: - new = TrieNode(x) - where_to.add_son(new) - where_to = new - where_to.terminal = True - - def __str__(self): - return str(self.top) - - def __iter__(self): +class TrieNode(): + + sons = None + value = None + terminal = None + + def __init__(self,value,terminal = False,sons = []): + self.value = value + self.sons = {} + self.terminal = terminal + for x in sons: + self.add_son(x) + + def add_son(self,item): + assert isinstance(item,TrieNode), "item must be a TrieNode" + self.sons[item.value] = item + + def find_son(self,son_value): + return self.sons[son_value] + + def __iter__(self): + yield self + for x in self.sons: + for y in self.sons[x]: + yield y + + def __str__(self): + rep = f'{self.value}[' + for x in self.sons: + rep += f'{self.sons[x]},' + rep += ']\n' + return rep + +class Trie: + top = None + + def __init__(self): + self.top = TrieNode('^') + + def LCP(self, item): + current = self.top + idx = 0 + for x in item: + try: + current = current.find_son(x) + idx += 1 + except KeyError: + break + return current,idx + + def add(self,item): + where_to,from_this = self.LCP(item) + for x in item[from_this:]: + new = TrieNode(x) + where_to.add_son(new) + where_to = new + where_to.terminal = True + + def __str__(self): + return str(self.top) + + def __iter__(self): return self.top.__iter__() \ No newline at end of file diff --git a/src/cool2/main.py b/src/cool2/main.py index 66c163533..37c214324 100644 --- a/src/cool2/main.py +++ b/src/cool2/main.py @@ -1,75 +1,75 @@ -from cool.pipeline import cool_pipeline, reconstr_pipeline -from cool.grammar.cool_grammar import G -from cool.parser.cool_parser import save_parser,cool_parser_path, cool_parser -from cool.lexer.cool_lexer import save_lexer,cool_lexer_path, cool_tokens_def -from cool.grammar.comment_grammar import C -from cool.parser.comment_parser import comment_parser_path, comment_parser -from cool.lexer.comment_lexer import comment_lexer_path, comment_tokens_def -import plac - -# plac annotation (description, type of arg [option, flag, positional], abrev, type, choices) -def main( - program_dir:("Path to cool program", "positional"), - output_dir:("Path for the compiled mips", "positional"), - out_infer:("Creates a file containing the inferred types", 'flag', 'i'), - verbose:("Print more info", 'flag', 'v'), - update_cool_parser:("Update pickled cool parser", 'flag', 'ucp'), - update_cool_lexer:("Update pickled cool lexer", 'flag', 'ucl'), - update_comment_parser:("Update pickled comment parser", 'flag', 'ump'), - update_comment_lexer:("Update pickled comment lexer", 'flag', 'uml')): - """ - Manage cool interpreter and run programs - """ - change = False - if update_cool_parser: - obj = save_parser(cool_parser_path,G) - print('Errors',obj.errors) - print("Parser updated") - change |= True - - if update_cool_lexer: - obj = save_lexer(cool_tokens_def,cool_lexer_path,G) - print("Lexer updated") - change |= True - - if update_comment_parser: - obj = save_parser(comment_parser_path,C) - print('Errors',obj.errors) - print("Comment Parser updated") - change |= True - - if update_comment_lexer: - obj = save_lexer(comment_tokens_def,comment_lexer_path,C) - print("Comment Lexer updated") - change |= True - - if change: - print("Parsers and Lexers updated") - exit() - - if program_dir == None: - print("If no update flag is provided 'program_dir' is required") - exit(1) - - with open(program_dir) as file: - file_content = file.read() - - if out_infer: - result = reconstr_pipeline(file_content, verbose=verbose) - else: - result = cool_pipeline(file_content,verbose=verbose) - ast, g_errors, parse, tokens, context, scope, operator, value, reconstr = [result.get(x, None) for x in ["ast", "errors", "text_parse", "text_tokens", "context", "scope", "operator", "value", "reconstructed_text"]] - - if reconstr and out_infer: - with open(program_dir + ".infer.cl", "w") as file: - file.write(reconstr) - - if g_errors: - for err in g_errors: - print(err) - exit(1) - # if hasattr(value, "value"): - # print("Value:",value.value) - -if __name__ == "__main__": +from cool.pipeline import cool_pipeline, reconstr_pipeline +from cool.grammar.cool_grammar import G +from cool.parser.cool_parser import save_parser,cool_parser_path, cool_parser +from cool.lexer.cool_lexer import save_lexer,cool_lexer_path, cool_tokens_def +from cool.grammar.comment_grammar import C +from cool.parser.comment_parser import comment_parser_path, comment_parser +from cool.lexer.comment_lexer import comment_lexer_path, comment_tokens_def +import plac + +# plac annotation (description, type of arg [option, flag, positional], abrev, type, choices) +def main( + program_dir:("Path to cool program", "positional"), + output_dir:("Path for the compiled mips", "positional"), + out_infer:("Creates a file containing the inferred types", 'flag', 'i'), + verbose:("Print more info", 'flag', 'v'), + update_cool_parser:("Update pickled cool parser", 'flag', 'ucp'), + update_cool_lexer:("Update pickled cool lexer", 'flag', 'ucl'), + update_comment_parser:("Update pickled comment parser", 'flag', 'ump'), + update_comment_lexer:("Update pickled comment lexer", 'flag', 'uml')): + """ + Manage cool interpreter and run programs + """ + change = False + if update_cool_parser: + obj = save_parser(cool_parser_path,G) + print('Errors',obj.errors) + print("Parser updated") + change |= True + + if update_cool_lexer: + obj = save_lexer(cool_tokens_def,cool_lexer_path,G) + print("Lexer updated") + change |= True + + if update_comment_parser: + obj = save_parser(comment_parser_path,C) + print('Errors',obj.errors) + print("Comment Parser updated") + change |= True + + if update_comment_lexer: + obj = save_lexer(comment_tokens_def,comment_lexer_path,C) + print("Comment Lexer updated") + change |= True + + if change: + print("Parsers and Lexers updated") + exit() + + if program_dir == None: + print("If no update flag is provided 'program_dir' is required") + exit(1) + + with open(program_dir) as file: + file_content = file.read() + + if out_infer: + result = reconstr_pipeline(file_content, verbose=verbose) + else: + result = cool_pipeline(file_content,verbose=verbose) + ast, g_errors, parse, tokens, context, scope, operator, value, reconstr = [result.get(x, None) for x in ["ast", "errors", "text_parse", "text_tokens", "context", "scope", "operator", "value", "reconstructed_text"]] + + if reconstr and out_infer: + with open(program_dir + ".infer.cl", "w") as file: + file.write(reconstr) + + if g_errors: + for err in g_errors: + print(err) + exit(1) + # if hasattr(value, "value"): + # print("Value:",value.value) + +if __name__ == "__main__": plac.call(main) \ No newline at end of file diff --git a/src/cool2/test/test_class.py b/src/cool2/test/test_class.py index f3fa23af1..87bcc1afd 100644 --- a/src/cool2/test/test_class.py +++ b/src/cool2/test/test_class.py @@ -1,54 +1,54 @@ -import sys -import pytest -import os -base_dir = os.path.dirname(__file__) -sys.path.append(os.path.join(base_dir, "..")) -from cool.pipeline import cool_pipeline, reconstr_pipeline -from cool.error.errors import CoolError -from .tests import tests, tests_run - -class TestPrograms: - - @pytest.mark.parametrize("name,progr,exp_result,exp_errors",[x for x in tests if "in_out" not in x[0]]) - def test_general(self, name, progr, exp_result, exp_errors): - result = cool_pipeline(progr) - ast, errors, parse, tokens, context, scope, operator, out = [result.get(x,None) for x in ["ast", "errors", "text_parse", "text_tokens", "context", "scope", "operator", "value"]] - if hasattr(out, "value"): - assert out.value == exp_result - assert errors == exp_errors - - @pytest.mark.parametrize("name,program", [(x[0],x[1]) for x in tests if "in_out" not in x[0]]) - def test_reconstruction(self, name, program): - result = reconstr_pipeline(program) - ast, errors, context, scope, operator, out, reconstr_text = [result.get(x,None) for x in ["ast", "errors", "context", "scope", "operator", "value", "reconstructed_text"]] - with open(os.path.join(base_dir, "test_reconstruction", name + ".cl"), "w") as f: - f.write(reconstr_text) - result = reconstr_pipeline(reconstr_text) - ast2, errors2, context2, scope2, operator2, out2, reconstr_text2 = [result.get(x,None) for x in ["ast", "errors", "context", "scope", "operator", "value", "reconstructed_text"]] - assert len(errors) == len(errors2) - assert reconstr_text == reconstr_text2 - if hasattr(out, "value"): - assert out.value == out2.value - - @pytest.mark.parametrize("progr",tests_run) - def test_run(self, progr): - result = cool_pipeline(progr) - errors, out = [result.get(x,None) for x in ["errors", "value"]] - assert len(errors) == 0 - - - def test_input(self, monkeypatch): - with monkeypatch.context() as m: - import builtins # Mocking input - def input(): - return "20" - monkeypatch.setattr(builtins, "input", input) - name, progr, exp_result, exp_errors = [x for x in tests if "in_out" in x[0]][0] - - result = cool_pipeline(progr) - ast, errors, parse, tokens, context, scope, operator, out = [result[x] for x in ["ast", "errors", "text_parse", "text_tokens", "context", "scope", "operator", "value"]] - - if hasattr(out, "value"): - assert out.value == "20" - assert errors == exp_errors - +import sys +import pytest +import os +base_dir = os.path.dirname(__file__) +sys.path.append(os.path.join(base_dir, "..")) +from cool.pipeline import cool_pipeline, reconstr_pipeline +from cool.error.errors import CoolError +from .tests import tests, tests_run + +class TestPrograms: + + @pytest.mark.parametrize("name,progr,exp_result,exp_errors",[x for x in tests if "in_out" not in x[0]]) + def test_general(self, name, progr, exp_result, exp_errors): + result = cool_pipeline(progr) + ast, errors, parse, tokens, context, scope, operator, out = [result.get(x,None) for x in ["ast", "errors", "text_parse", "text_tokens", "context", "scope", "operator", "value"]] + if hasattr(out, "value"): + assert out.value == exp_result + assert errors == exp_errors + + @pytest.mark.parametrize("name,program", [(x[0],x[1]) for x in tests if "in_out" not in x[0]]) + def test_reconstruction(self, name, program): + result = reconstr_pipeline(program) + ast, errors, context, scope, operator, out, reconstr_text = [result.get(x,None) for x in ["ast", "errors", "context", "scope", "operator", "value", "reconstructed_text"]] + with open(os.path.join(base_dir, "test_reconstruction", name + ".cl"), "w") as f: + f.write(reconstr_text) + result = reconstr_pipeline(reconstr_text) + ast2, errors2, context2, scope2, operator2, out2, reconstr_text2 = [result.get(x,None) for x in ["ast", "errors", "context", "scope", "operator", "value", "reconstructed_text"]] + assert len(errors) == len(errors2) + assert reconstr_text == reconstr_text2 + if hasattr(out, "value"): + assert out.value == out2.value + + @pytest.mark.parametrize("progr",tests_run) + def test_run(self, progr): + result = cool_pipeline(progr) + errors, out = [result.get(x,None) for x in ["errors", "value"]] + assert len(errors) == 0 + + + def test_input(self, monkeypatch): + with monkeypatch.context() as m: + import builtins # Mocking input + def input(): + return "20" + monkeypatch.setattr(builtins, "input", input) + name, progr, exp_result, exp_errors = [x for x in tests if "in_out" in x[0]][0] + + result = cool_pipeline(progr) + ast, errors, parse, tokens, context, scope, operator, out = [result[x] for x in ["ast", "errors", "text_parse", "text_tokens", "context", "scope", "operator", "value"]] + + if hasattr(out, "value"): + assert out.value == "20" + assert errors == exp_errors + diff --git a/src/cool2/test/test_data/test_SELF_TYPE.meta_test b/src/cool2/test/test_data/test_SELF_TYPE.meta_test index b3ea61549..fc2f1ebf0 100644 --- a/src/cool2/test/test_data/test_SELF_TYPE.meta_test +++ b/src/cool2/test/test_data/test_SELF_TYPE.meta_test @@ -1,37 +1,37 @@ -"{\"name\": \"test_SELF_TYPE\", \"result\": \"BBBB\", \"errors\": []}" - - -class A{ - a:SELF_TYPE; - get():SELF_TYPE { - self - }; - get2():SELF_TYPE { - new SELF_TYPE - }; - get3():SELF_TYPE { - { - a <- new SELF_TYPE; - a; - } - }; -} - -class B inherits A { - get4(): SELF_TYPE { - get3() - }; - } - -class Main inherits IO { - main() : String{ - { - a:A <- new A; - b:B <- new B; - b.get().type_name().concat( - b.get2().type_name().concat( - b.get3().type_name()).concat( - b.get4().type_name())); - } - }; -} +"{\"name\": \"test_SELF_TYPE\", \"result\": \"BBBB\", \"errors\": []}" + + +class A{ + a:SELF_TYPE; + get():SELF_TYPE { + self + }; + get2():SELF_TYPE { + new SELF_TYPE + }; + get3():SELF_TYPE { + { + a <- new SELF_TYPE; + a; + } + }; +} + +class B inherits A { + get4(): SELF_TYPE { + get3() + }; + } + +class Main inherits IO { + main() : String{ + { + a:A <- new A; + b:B <- new B; + b.get().type_name().concat( + b.get2().type_name().concat( + b.get3().type_name()).concat( + b.get4().type_name())); + } + }; +} diff --git a/src/cool2/test/test_data/test_at.meta_test b/src/cool2/test/test_data/test_at.meta_test index 983bee452..9a0d8b2c9 100644 --- a/src/cool2/test/test_data/test_at.meta_test +++ b/src/cool2/test/test_data/test_at.meta_test @@ -1,28 +1,28 @@ -"{\"name\": \"test_at\", \"result\": \"class A class B\", \"errors\": []}" - -class A { - name():String { - "class A ".concat(name2()) - }; - name2():String { - "class A" - }; -} - -class B inherits A { - name():String { - "class B" - }; - name2():String { - "class B" - }; -} - -class Main inherits IO { - main():String { - { - b:B <- new B; - b@A.name(); - } - }; -} +"{\"name\": \"test_at\", \"result\": \"class A class B\", \"errors\": []}" + +class A { + name():String { + "class A ".concat(name2()) + }; + name2():String { + "class A" + }; +} + +class B inherits A { + name():String { + "class B" + }; + name2():String { + "class B" + }; +} + +class Main inherits IO { + main():String { + { + b:B <- new B; + b@A.name(); + } + }; +} diff --git a/src/cool2/test/test_data/test_attr_dep_1.meta_test b/src/cool2/test/test_data/test_attr_dep_1.meta_test index b2705643d..f950af11b 100644 --- a/src/cool2/test/test_data/test_attr_dep_1.meta_test +++ b/src/cool2/test/test_data/test_attr_dep_1.meta_test @@ -1,19 +1,19 @@ -"{\"name\": \"test_attr_dep_1\", \"result\": 30, \"errors\": []}" - - -class Test -{ - test:Int <- 10; - test2:Int <- 20; -} - - -class Main inherits Test -{ - test3:Int <- test + test2; - main():Int { - { - test3; - } - }; -} +"{\"name\": \"test_attr_dep_1\", \"result\": 30, \"errors\": []}" + + +class Test +{ + test:Int <- 10; + test2:Int <- 20; +} + + +class Main inherits Test +{ + test3:Int <- test + test2; + main():Int { + { + test3; + } + }; +} diff --git a/src/cool2/test/test_data/test_attr_dep_2.meta_test b/src/cool2/test/test_data/test_attr_dep_2.meta_test index 0625ae2c2..cf291b589 100644 --- a/src/cool2/test/test_data/test_attr_dep_2.meta_test +++ b/src/cool2/test/test_data/test_attr_dep_2.meta_test @@ -1,18 +1,18 @@ -"{\"name\": \"test_attr_dep_2\", \"result\": 30, \"errors\": []}" - -class Test -{ - test3:Int <- test + test2; - test2:Int <- 2*test; - test:Int <- 10; -} - - -class Main inherits Test -{ - main():Int { - { - test3; - } - }; +"{\"name\": \"test_attr_dep_2\", \"result\": 30, \"errors\": []}" + +class Test +{ + test3:Int <- test + test2; + test2:Int <- 2*test; + test:Int <- 10; +} + + +class Main inherits Test +{ + main():Int { + { + test3; + } + }; } \ No newline at end of file diff --git a/src/cool2/test/test_data/test_auto_1.meta_test b/src/cool2/test/test_data/test_auto_1.meta_test index ab577727c..66bdc8c7d 100644 --- a/src/cool2/test/test_data/test_auto_1.meta_test +++ b/src/cool2/test/test_data/test_auto_1.meta_test @@ -1,11 +1,11 @@ -"{\"name\": \"test_auto_1\", \"result\": 0, \"errors\": []}" - -class Main inherits IO { - main() : AUTO_TYPE { - let x : AUTO_TYPE <- 3 + 2 in { - case x of - y : Int => out_string("Ok"); - esac; - } - }; -} +"{\"name\": \"test_auto_1\", \"result\": 0, \"errors\": []}" + +class Main inherits IO { + main() : AUTO_TYPE { + let x : AUTO_TYPE <- 3 + 2 in { + case x of + y : Int => out_string("Ok"); + esac; + } + }; +} diff --git a/src/cool2/test/test_data/test_auto_2.meta_test b/src/cool2/test/test_data/test_auto_2.meta_test index 841b0e5c0..2da85a0b2 100644 --- a/src/cool2/test/test_data/test_auto_2.meta_test +++ b/src/cool2/test/test_data/test_auto_2.meta_test @@ -1,8 +1,8 @@ -"{\"name\": \"test_auto_2\", \"result\": 11, \"errors\": []}" - -class Main inherits IO { - main():AUTO_TYPE { - succ(10) - }; - succ(n : Int) : AUTO_TYPE { n + 1 }; -} +"{\"name\": \"test_auto_2\", \"result\": 11, \"errors\": []}" + +class Main inherits IO { + main():AUTO_TYPE { + succ(10) + }; + succ(n : Int) : AUTO_TYPE { n + 1 }; +} diff --git a/src/cool2/test/test_data/test_auto_3.meta_test b/src/cool2/test/test_data/test_auto_3.meta_test index b5f63fa41..22c71a58d 100644 --- a/src/cool2/test/test_data/test_auto_3.meta_test +++ b/src/cool2/test/test_data/test_auto_3.meta_test @@ -1,9 +1,9 @@ -"{\"name\": \"test_auto_3\", \"result\": 11, \"errors\": []}" - -class Main inherits IO { - succ(n : AUTO_TYPE) : AUTO_TYPE { n + 1 }; - - main():AUTO_TYPE { - succ(10) - }; -} +"{\"name\": \"test_auto_3\", \"result\": 11, \"errors\": []}" + +class Main inherits IO { + succ(n : AUTO_TYPE) : AUTO_TYPE { n + 1 }; + + main():AUTO_TYPE { + succ(10) + }; +} diff --git a/src/cool2/test/test_data/test_auto_4.meta_test b/src/cool2/test/test_data/test_auto_4.meta_test index ea5e80d42..d6ca6d288 100644 --- a/src/cool2/test/test_data/test_auto_4.meta_test +++ b/src/cool2/test/test_data/test_auto_4.meta_test @@ -1,15 +1,15 @@ -"{\"name\": \"test_auto_4\", \"result\": 4, \"errors\": []}" - -class Main inherits IO { - main():AUTO_TYPE { - ackermann(0,3) - }; - - ackermann(m : AUTO_TYPE, n: AUTO_TYPE) : AUTO_TYPE { - if (m=0) then n+1 else - if (n=0) then ackermann(m-1, 1) else - ackermann(m-1, ackermann(m, n-1)) - fi - fi - }; -} +"{\"name\": \"test_auto_4\", \"result\": 4, \"errors\": []}" + +class Main inherits IO { + main():AUTO_TYPE { + ackermann(0,3) + }; + + ackermann(m : AUTO_TYPE, n: AUTO_TYPE) : AUTO_TYPE { + if (m=0) then n+1 else + if (n=0) then ackermann(m-1, 1) else + ackermann(m-1, ackermann(m, n-1)) + fi + fi + }; +} diff --git a/src/cool2/test/test_data/test_auto_5.meta_test b/src/cool2/test/test_data/test_auto_5.meta_test index 5ebee911b..987e607b3 100644 --- a/src/cool2/test/test_data/test_auto_5.meta_test +++ b/src/cool2/test/test_data/test_auto_5.meta_test @@ -1,19 +1,19 @@ -"{\"name\": \"test_auto_5\", \"result\": 1, \"errors\": []}" - -class Main inherits Object { - main():AUTO_TYPE { - f(1,1) - }; - - f(a: AUTO_TYPE, b: AUTO_TYPE) : AUTO_TYPE { - if (a=1) then b else - g(a + 1, b/2) - fi - }; - - g(a: AUTO_TYPE, b: AUTO_TYPE) : AUTO_TYPE { - if (b=1) then a else - f(a/2, b+1) - fi - }; -} +"{\"name\": \"test_auto_5\", \"result\": 1, \"errors\": []}" + +class Main inherits Object { + main():AUTO_TYPE { + f(1,1) + }; + + f(a: AUTO_TYPE, b: AUTO_TYPE) : AUTO_TYPE { + if (a=1) then b else + g(a + 1, b/2) + fi + }; + + g(a: AUTO_TYPE, b: AUTO_TYPE) : AUTO_TYPE { + if (b=1) then a else + f(a/2, b+1) + fi + }; +} diff --git a/src/cool2/test/test_data/test_auto_conditional.meta_test b/src/cool2/test/test_data/test_auto_conditional.meta_test index 2a0432b51..c59dec1cb 100644 --- a/src/cool2/test/test_data/test_auto_conditional.meta_test +++ b/src/cool2/test/test_data/test_auto_conditional.meta_test @@ -1,104 +1,104 @@ -"{\"name\": \"test_auto_conditional\", \"result\": 1, \"errors\": []}" - -class Base { - base():AUTO_TYPE { - self - }; -} - -class OtraBase { - otraBase():AUTO_TYPE { - self - }; -} - -class ConcBase1 inherits Base { - -} - -class ConcBase2 inherits OtraBase { - -} - -class Main { - a:AUTO_TYPE; - main() : AUTO_TYPE - { - { - a:AUTO_TYPE <- - if true then - new OtraBase - else - new ConcBase2 - fi; - b:AUTO_TYPE <- - if true then - new ConcBase2 - else - new ConcBase2 - fi; - c:AUTO_TYPE <- - if true then - new ConcBase1 - else - new ConcBase2 - fi; - d:AUTO_TYPE <- - if true then - ret1() - else - ret2() - fi; - e:AUTO_TYPE <- - if true then - ret1(new ConcBase1) - else - ret2(new ConcBase2) - fi; - 1; - } - }; - - ret1():AUTO_TYPE - { - c:AUTO_TYPE <- - if true then - new ConcBase1 - else - new ConcBase2 - fi - }; - - ret2():AUTO_TYPE - { - c:AUTO_TYPE <- - if true then - new OtraBase - else - new ConcBase2 - fi - }; - - ret1(param:AUTO_TYPE):AUTO_TYPE - { - { - c:AUTO_TYPE <- param.base(); - a:AUTO_TYPE <- case param of - b:Base => if true then b else param fi; - b:OtraBase => if false then b else param fi; - esac; - } - }; - - ret2(param:AUTO_TYPE):AUTO_TYPE - { - { - c:AUTO_TYPE <- param.otraBase(); - a:AUTO_TYPE <- case param of - b:Base => if true then b else b fi; - b:ConcBase1 => if true then b else b fi; - esac; - } - }; - +"{\"name\": \"test_auto_conditional\", \"result\": 1, \"errors\": []}" + +class Base { + base():AUTO_TYPE { + self + }; +} + +class OtraBase { + otraBase():AUTO_TYPE { + self + }; +} + +class ConcBase1 inherits Base { + +} + +class ConcBase2 inherits OtraBase { + +} + +class Main { + a:AUTO_TYPE; + main() : AUTO_TYPE + { + { + a:AUTO_TYPE <- + if true then + new OtraBase + else + new ConcBase2 + fi; + b:AUTO_TYPE <- + if true then + new ConcBase2 + else + new ConcBase2 + fi; + c:AUTO_TYPE <- + if true then + new ConcBase1 + else + new ConcBase2 + fi; + d:AUTO_TYPE <- + if true then + ret1() + else + ret2() + fi; + e:AUTO_TYPE <- + if true then + ret1(new ConcBase1) + else + ret2(new ConcBase2) + fi; + 1; + } + }; + + ret1():AUTO_TYPE + { + c:AUTO_TYPE <- + if true then + new ConcBase1 + else + new ConcBase2 + fi + }; + + ret2():AUTO_TYPE + { + c:AUTO_TYPE <- + if true then + new OtraBase + else + new ConcBase2 + fi + }; + + ret1(param:AUTO_TYPE):AUTO_TYPE + { + { + c:AUTO_TYPE <- param.base(); + a:AUTO_TYPE <- case param of + b:Base => if true then b else param fi; + b:OtraBase => if false then b else param fi; + esac; + } + }; + + ret2(param:AUTO_TYPE):AUTO_TYPE + { + { + c:AUTO_TYPE <- param.otraBase(); + a:AUTO_TYPE <- case param of + b:Base => if true then b else b fi; + b:ConcBase1 => if true then b else b fi; + esac; + } + }; + } \ No newline at end of file diff --git a/src/cool2/test/test_data/test_auto_defaults.meta_test b/src/cool2/test/test_data/test_auto_defaults.meta_test index 30d27848b..28ddbee4d 100644 --- a/src/cool2/test/test_data/test_auto_defaults.meta_test +++ b/src/cool2/test/test_data/test_auto_defaults.meta_test @@ -1,11 +1,11 @@ -"{\"name\": \"test_auto_defaults\", \"result\": 10, \"errors\": []}" - -class Main { - - a:AUTO_TYPE; - - main() : Int - { - let b:AUTO_TYPE in a + b + 10 - }; +"{\"name\": \"test_auto_defaults\", \"result\": 10, \"errors\": []}" + +class Main { + + a:AUTO_TYPE; + + main() : Int + { + let b:AUTO_TYPE in a + b + 10 + }; } \ No newline at end of file diff --git a/src/cool2/test/test_data/test_auto_undecidable.meta_test b/src/cool2/test/test_data/test_auto_undecidable.meta_test index b394f1cce..3358c8c59 100644 --- a/src/cool2/test/test_data/test_auto_undecidable.meta_test +++ b/src/cool2/test/test_data/test_auto_undecidable.meta_test @@ -1,23 +1,23 @@ -"{\"name\": \"test_auto_undecidable\", \"result\": 10, \"errors\": [\"Cant infer type in given context. Line:18 Column:7\"]}" - -class A{ - m1():Int{ - 10 - }; -} - -class B{ - m1():Int{ - 20 - }; -} - -class Main inherits IO { - main() : Int - { - 10 - }; - m(a:AUTO_TYPE):Int{ - a.m1() - }; +"{\"name\": \"test_auto_undecidable\", \"result\": 10, \"errors\": [\"Cant infer type in given context. Line:18 Column:7\"]}" + +class A{ + m1():Int{ + 10 + }; +} + +class B{ + m1():Int{ + 20 + }; +} + +class Main inherits IO { + main() : Int + { + 10 + }; + m(a:AUTO_TYPE):Int{ + a.m1() + }; } \ No newline at end of file diff --git a/src/cool2/test/test_data/test_case.meta_test b/src/cool2/test/test_data/test_case.meta_test index 4d0d6390b..86639347b 100644 --- a/src/cool2/test/test_data/test_case.meta_test +++ b/src/cool2/test/test_data/test_case.meta_test @@ -1,14 +1,14 @@ -"{\"name\": \"test_case\", \"result\": 50, \"errors\": []}" - -class Main inherits Object { - main():Object { - { - a:Object <- case 20+40 of - -- a:Bool => if a then true else false fi; - b:Int => if b = 60 then 50 else 0 fi; - c:Object => c; - esac; - a; - } - }; -} +"{\"name\": \"test_case\", \"result\": 50, \"errors\": []}" + +class Main inherits Object { + main():Object { + { + a:Object <- case 20+40 of + -- a:Bool => if a then true else false fi; + b:Int => if b = 60 then 50 else 0 fi; + c:Object => c; + esac; + a; + } + }; +} diff --git a/src/cool2/test/test_data/test_case_no_type.meta_test b/src/cool2/test/test_data/test_case_no_type.meta_test index d52ecd831..b6e622d37 100644 --- a/src/cool2/test/test_data/test_case_no_type.meta_test +++ b/src/cool2/test/test_data/test_case_no_type.meta_test @@ -1,12 +1,12 @@ -"{\"name\": \"test_case_no_type\", \"result\": 50, \"errors\": [\"No branch can be selected. Type Int is not conformed by any of the branches. Line: 4 Column: 28\"]}" - -class Main inherits Object { - main():Object { - { - a:Object <- case 20+40 of - a:Bool => if a then true else false fi; - esac; - a; - } - }; -} +"{\"name\": \"test_case_no_type\", \"result\": 50, \"errors\": [\"No branch can be selected. Type Int is not conformed by any of the branches. Line: 4 Column: 28\"]}" + +class Main inherits Object { + main():Object { + { + a:Object <- case 20+40 of + a:Bool => if a then true else false fi; + esac; + a; + } + }; +} diff --git a/src/cool2/test/test_data/test_case_repeated_type.meta_test b/src/cool2/test/test_data/test_case_repeated_type.meta_test index af02fcf29..aa654d0dc 100644 --- a/src/cool2/test/test_data/test_case_repeated_type.meta_test +++ b/src/cool2/test/test_data/test_case_repeated_type.meta_test @@ -1,15 +1,15 @@ -"{\"name\": \"test_case_repeated_type\", \"result\": 50, \"errors\": [\"Type Int already present in case expression. Line:7 Column:12\"]}" - -class Main inherits Object { - main():Object { - { - a:Object <- case 20+40 of - -- a:Bool => if a then true else false fi; - b:Int => if b = 60 then 50 else 0 fi; - b:Int => if b = 60 then 40 else 0 fi; - c:Object => c; - esac; - a; - } - }; -} +"{\"name\": \"test_case_repeated_type\", \"result\": 50, \"errors\": [\"Type Int already present in case expression. Line:7 Column:12\"]}" + +class Main inherits Object { + main():Object { + { + a:Object <- case 20+40 of + -- a:Bool => if a then true else false fi; + b:Int => if b = 60 then 50 else 0 fi; + b:Int => if b = 60 then 40 else 0 fi; + c:Object => c; + esac; + a; + } + }; +} diff --git a/src/cool2/test/test_data/test_case_void_eval.meta_test b/src/cool2/test/test_data/test_case_void_eval.meta_test index 9b6fd945d..79d61c88a 100644 --- a/src/cool2/test/test_data/test_case_void_eval.meta_test +++ b/src/cool2/test/test_data/test_case_void_eval.meta_test @@ -1,14 +1,14 @@ -"{\"name\": \"test_case_void_eval\", \"result\": 50, \"errors\": [\"Void is not a valid type. Line: 4 Column: 27\"]}" - -class Main inherits Object { - main():Object { - { - a:Object <- case while false loop 1 pool of - -- a:Bool => if a then true else false fi; - b:Int => if b = 60 then 50 else 0 fi; - c:Object => c; - esac; - a; - } - }; -} +"{\"name\": \"test_case_void_eval\", \"result\": 50, \"errors\": [\"Void is not a valid type. Line: 4 Column: 27\"]}" + +class Main inherits Object { + main():Object { + { + a:Object <- case while false loop 1 pool of + -- a:Bool => if a then true else false fi; + b:Int => if b = 60 then 50 else 0 fi; + c:Object => c; + esac; + a; + } + }; +} diff --git a/src/cool2/test/test_data/test_comments.meta_test b/src/cool2/test/test_data/test_comments.meta_test index ce1749df3..8f5f6be6e 100644 --- a/src/cool2/test/test_data/test_comments.meta_test +++ b/src/cool2/test/test_data/test_comments.meta_test @@ -1,12 +1,12 @@ -"{\"name\": \"test_comments\", \"result\": 0, \"errors\": []}" - -class Main inherits Object { - (* this is a comment *) - -- This is also a comment - (* This is a nested (* very nested comment *) *) - (* -- Double comment? *) - -- Double (* comment again *) - main():Object { - 0 - }; -} +"{\"name\": \"test_comments\", \"result\": 0, \"errors\": []}" + +class Main inherits Object { + (* this is a comment *) + -- This is also a comment + (* This is a nested (* very nested comment *) *) + (* -- Double comment? *) + -- Double (* comment again *) + main():Object { + 0 + }; +} diff --git a/src/cool2/test/test_data/test_conditional.meta_test b/src/cool2/test/test_data/test_conditional.meta_test index 521a7b061..8bbac1313 100644 --- a/src/cool2/test/test_data/test_conditional.meta_test +++ b/src/cool2/test/test_data/test_conditional.meta_test @@ -1,24 +1,24 @@ -"{\"name\": \"test_conditional\", \"result\": 3, \"errors\": []}" - - - class A { } - class B inherits A { } - class C inherits B { } - class D inherits B { } - class E inherits A { } - - class Main inherits IO { - - main() : Int { - { - d:Object <- if true then new E else new C fi; - e:B <- if true then new C else new D fi; - a:Int <- if true then 1 else 0 fi; - b:Int <- if false then 0 else 1 fi; - c:Int <- if if 1 > 2 then 2 > 1 else 2 > 1 fi then 1 else 0 fi; - out_string(d.type_name()).out_string(e.type_name()); - a+b+c; - - } - }; - } +"{\"name\": \"test_conditional\", \"result\": 3, \"errors\": []}" + + + class A { } + class B inherits A { } + class C inherits B { } + class D inherits B { } + class E inherits A { } + + class Main inherits IO { + + main() : Int { + { + d:Object <- if true then new E else new C fi; + e:B <- if true then new C else new D fi; + a:Int <- if true then 1 else 0 fi; + b:Int <- if false then 0 else 1 fi; + c:Int <- if if 1 > 2 then 2 > 1 else 2 > 1 fi then 1 else 0 fi; + out_string(d.type_name()).out_string(e.type_name()); + a+b+c; + + } + }; + } diff --git a/src/cool2/test/test_data/test_default.meta_test b/src/cool2/test/test_data/test_default.meta_test index f4735ece0..d8ec8f9f6 100644 --- a/src/cool2/test/test_data/test_default.meta_test +++ b/src/cool2/test/test_data/test_default.meta_test @@ -1,33 +1,33 @@ -"{\"name\": \"test_default\", \"result\": true, \"errors\": []}" - -class B {} - -class Main inherits IO { - a:Int; - s:String; - b:Bool; - o:Object; - o2:B; - - main():Bool { - { - if a = 0 then - if s = "" then - if not b then - if o = o2 then - true - else - false - fi - else - false - fi - else - false - fi - else - false - fi; - } - }; -} +"{\"name\": \"test_default\", \"result\": true, \"errors\": []}" + +class B {} + +class Main inherits IO { + a:Int; + s:String; + b:Bool; + o:Object; + o2:B; + + main():Bool { + { + if a = 0 then + if s = "" then + if not b then + if o = o2 then + true + else + false + fi + else + false + fi + else + false + fi + else + false + fi; + } + }; +} diff --git a/src/cool2/test/test_data/test_entry_point_1.meta_test b/src/cool2/test/test_data/test_entry_point_1.meta_test index 6479fc095..8f95d9a94 100644 --- a/src/cool2/test/test_data/test_entry_point_1.meta_test +++ b/src/cool2/test/test_data/test_entry_point_1.meta_test @@ -1,12 +1,12 @@ -"{\"name\": \"test_entry_point_1\", \"result\": 30, \"errors\": [\"Method 'main' is not defined locally in 'Main' with 0 params. The program must define a 'main' method with no parameters in the 'Main' class\"]}" - - -class Main{ - - main(a:Int) : Int { - { - 30; - } - }; - +"{\"name\": \"test_entry_point_1\", \"result\": 30, \"errors\": [\"Method 'main' is not defined locally in 'Main' with 0 params. The program must define a 'main' method with no parameters in the 'Main' class\"]}" + + +class Main{ + + main(a:Int) : Int { + { + 30; + } + }; + } \ No newline at end of file diff --git a/src/cool2/test/test_data/test_entry_point_2.meta_test b/src/cool2/test/test_data/test_entry_point_2.meta_test index c1eb4b197..9e50c689c 100644 --- a/src/cool2/test/test_data/test_entry_point_2.meta_test +++ b/src/cool2/test/test_data/test_entry_point_2.meta_test @@ -1,13 +1,13 @@ -"{\"name\": \"test_entry_point_2\", \"result\": 30, \"errors\": [\"Method 'main' is not defined locally in 'Main' with 0 params. The program must define a 'main' method with no parameters in the 'Main' class\"]}" - -class M{ - main() : Int { - { - 30; - } - }; -} - -class Main inherits M{ - +"{\"name\": \"test_entry_point_2\", \"result\": 30, \"errors\": [\"Method 'main' is not defined locally in 'Main' with 0 params. The program must define a 'main' method with no parameters in the 'Main' class\"]}" + +class M{ + main() : Int { + { + 30; + } + }; +} + +class Main inherits M{ + } \ No newline at end of file diff --git a/src/cool2/test/test_data/test_entry_point_3.meta_test b/src/cool2/test/test_data/test_entry_point_3.meta_test index 7ec2a8731..c6ac6fc33 100644 --- a/src/cool2/test/test_data/test_entry_point_3.meta_test +++ b/src/cool2/test/test_data/test_entry_point_3.meta_test @@ -1,12 +1,12 @@ -"{\"name\": \"test_entry_point_3\", \"result\": 30, \"errors\": [\"The program must define a 'Main' type\"]}" - - -class NoMain{ - - main() : Int { - { - 30; - } - }; - +"{\"name\": \"test_entry_point_3\", \"result\": 30, \"errors\": [\"The program must define a 'Main' type\"]}" + + +class NoMain{ + + main() : Int { + { + 30; + } + }; + } \ No newline at end of file diff --git a/src/cool2/test/test_data/test_equality.meta_test b/src/cool2/test/test_data/test_equality.meta_test index b191a6a22..85c8137e7 100644 --- a/src/cool2/test/test_data/test_equality.meta_test +++ b/src/cool2/test/test_data/test_equality.meta_test @@ -1,28 +1,28 @@ -"{\"name\": \"test_equality\", \"result\": true, \"errors\": []}" - -class A -{ - -} -class B -{ - -} - -class Main -{ - cClass:A; - dClass:B; - - main():Bool { - { - aClass:A <- new A; - bClass:A <- new A; - a:Bool <- aClass = aClass; - b:Bool <- not aClass = bClass; - c:Bool <- cClass = dClass; - - (c = (a = b)) = true; - } - }; -} +"{\"name\": \"test_equality\", \"result\": true, \"errors\": []}" + +class A +{ + +} +class B +{ + +} + +class Main +{ + cClass:A; + dClass:B; + + main():Bool { + { + aClass:A <- new A; + bClass:A <- new A; + a:Bool <- aClass = aClass; + b:Bool <- not aClass = bClass; + c:Bool <- cClass = dClass; + + (c = (a = b)) = true; + } + }; +} diff --git a/src/cool2/test/test_data/test_escape_line.meta_test b/src/cool2/test/test_data/test_escape_line.meta_test index c6541ca09..90fd54fac 100644 --- a/src/cool2/test/test_data/test_escape_line.meta_test +++ b/src/cool2/test/test_data/test_escape_line.meta_test @@ -1,12 +1,12 @@ -"{\"name\": \"test_escape_line\", \"result\": \"Hello People\", \"errors\": []}" - - -class Main\ -{ - main():String { - { - "Hello \ - People"; - } - }; -} +"{\"name\": \"test_escape_line\", \"result\": \"Hello People\", \"errors\": []}" + + +class Main\ +{ + main():String { + { + "Hello \ + People"; + } + }; +} diff --git a/src/cool2/test/test_data/test_in_out.meta_test b/src/cool2/test/test_data/test_in_out.meta_test index 5933bc05c..6bdd712c6 100644 --- a/src/cool2/test/test_data/test_in_out.meta_test +++ b/src/cool2/test/test_data/test_in_out.meta_test @@ -1,23 +1,23 @@ -"{\"name\": \"test_in_out\", \"result\": \"hello\", \"errors\": []}" - -class Main inherits IO { - a:Int; - - set(v:Int):Int { - a <- v - }; - - get():Int { - a - }; - main():String { - { - out_string("Write a number"); - a:Int <- in_int(); - out_string("Write a string"); - b:String <- in_string(); - out_int(a).out_string(b); - b; - } - }; -} +"{\"name\": \"test_in_out\", \"result\": \"hello\", \"errors\": []}" + +class Main inherits IO { + a:Int; + + set(v:Int):Int { + a <- v + }; + + get():Int { + a + }; + main():String { + { + out_string("Write a number"); + a:Int <- in_int(); + out_string("Write a string"); + b:String <- in_string(); + out_int(a).out_string(b); + b; + } + }; +} diff --git a/src/cool2/test/test_data/test_instances.meta_test b/src/cool2/test/test_data/test_instances.meta_test index 51783d31c..ebbee8685 100644 --- a/src/cool2/test/test_data/test_instances.meta_test +++ b/src/cool2/test/test_data/test_instances.meta_test @@ -1,21 +1,21 @@ -"{\"name\": \"test_instances\", \"result\": 30, \"errors\": []}" - -class Main { - a:Int; - - set(v:Int):Int { - a <- v - }; - - get():Int { - a - }; - main():Int { - { - a <- 10; - b:Main <- copy(); - a <- 20; - b.get() + get(); - } - }; -} +"{\"name\": \"test_instances\", \"result\": 30, \"errors\": []}" + +class Main { + a:Int; + + set(v:Int):Int { + a <- v + }; + + get():Int { + a + }; + main():Int { + { + a <- 10; + b:Main <- copy(); + a <- 20; + b.get() + get(); + } + }; +} diff --git a/src/cool2/test/test_data/test_isvoid.meta_test b/src/cool2/test/test_data/test_isvoid.meta_test index 83ad115b5..756f03e03 100644 --- a/src/cool2/test/test_data/test_isvoid.meta_test +++ b/src/cool2/test/test_data/test_isvoid.meta_test @@ -1,15 +1,15 @@ -"{\"name\": \"test_isvoid\", \"result\": true, \"errors\": []}" - -class Main inherits IO { - a:Int; - s:String; - o:Object; - main():Bool { - { - a:Int <- 10; - b:Bool <- isvoid a; - c:Bool <- isvoid o; - c; - } - }; -} +"{\"name\": \"test_isvoid\", \"result\": true, \"errors\": []}" + +class Main inherits IO { + a:Int; + s:String; + o:Object; + main():Bool { + { + a:Int <- 10; + b:Bool <- isvoid a; + c:Bool <- isvoid o; + c; + } + }; +} diff --git a/src/cool2/test/test_data/test_let.meta_test b/src/cool2/test/test_data/test_let.meta_test index 3a2061d3e..6a85e8917 100644 --- a/src/cool2/test/test_data/test_let.meta_test +++ b/src/cool2/test/test_data/test_let.meta_test @@ -1,19 +1,19 @@ -"{\"name\": \"test_let\", \"result\": 20, \"errors\": []}" - -class Main -{ - b:Bool; - main() : Int { - { - a:Int <- let a:Bool <- true, a:Int <- if a then 10 else 20 fi in a; - c:Int <- let a:SELF_TYPE <- change(), b:Int <- if b then 10 else 20 fi in b; - a + c; - } - }; - change():SELF_TYPE { - { - b <- not b; - self; - } - }; -} +"{\"name\": \"test_let\", \"result\": 20, \"errors\": []}" + +class Main +{ + b:Bool; + main() : Int { + { + a:Int <- let a:Bool <- true, a:Int <- if a then 10 else 20 fi in a; + c:Int <- let a:SELF_TYPE <- change(), b:Int <- if b then 10 else 20 fi in b; + a + c; + } + }; + change():SELF_TYPE { + { + b <- not b; + self; + } + }; +} diff --git a/src/cool2/test/test_data/test_other.meta_test b/src/cool2/test/test_data/test_other.meta_test index 90fd76326..9bd5fd4b9 100644 --- a/src/cool2/test/test_data/test_other.meta_test +++ b/src/cool2/test/test_data/test_other.meta_test @@ -1,21 +1,21 @@ -"{\"name\": \"test_other\", \"result\": null, \"errors\": [\"Method \\\"a\\\" already defined in \\\"A\\\" with a different signature. Line:10 Column:5\"]}" - -class A { - a:Int; - a(a:Object):Object { - a - }; -} - -class B inherits A { - -- a:Int; - a(self:Int):Object { - a - }; -} - -class Main inherits IO { - main():B { - new B - }; -} +"{\"name\": \"test_other\", \"result\": null, \"errors\": [\"Method \\\"a\\\" already defined in \\\"A\\\" with a different signature. Line:10 Column:5\"]}" + +class A { + a:Int; + a(a:Object):Object { + a + }; +} + +class B inherits A { + -- a:Int; + a(self:Int):Object { + a + }; +} + +class Main inherits IO { + main():B { + new B + }; +} diff --git a/src/cool2/test/test_data/test_recursive.meta_test b/src/cool2/test/test_data/test_recursive.meta_test index bc6c50dd1..9edcd2b93 100644 --- a/src/cool2/test/test_data/test_recursive.meta_test +++ b/src/cool2/test/test_data/test_recursive.meta_test @@ -1,11 +1,11 @@ -"{\"name\": \"test_recursive\", \"result\": 21, \"errors\": []}" - -class Main inherits Object { - main():Int { - fibo(7) - }; - - fibo(n:Int):Int { - if n > 1 then fibo(n-1) + fibo(n-2) else 1 fi - }; -} +"{\"name\": \"test_recursive\", \"result\": 21, \"errors\": []}" + +class Main inherits Object { + main():Int { + fibo(7) + }; + + fibo(n:Int):Int { + if n > 1 then fibo(n-1) + fibo(n-2) else 1 fi + }; +} diff --git a/src/cool2/test/test_data/test_substr_1.meta_test b/src/cool2/test/test_data/test_substr_1.meta_test index c5166d770..045d8c2b1 100644 --- a/src/cool2/test/test_data/test_substr_1.meta_test +++ b/src/cool2/test/test_data/test_substr_1.meta_test @@ -1,11 +1,11 @@ -"{\"name\": \"test_substr_1\", \"result\": \"01234567890123456789\", \"errors\": []}" - -class Main -{ - main():String { - { - s:String <- "0123456789"; - s.substr(0,10).concat(s.substr(0,5)).concat(s.substr(5,5)); - } - }; -} +"{\"name\": \"test_substr_1\", \"result\": \"01234567890123456789\", \"errors\": []}" + +class Main +{ + main():String { + { + s:String <- "0123456789"; + s.substr(0,10).concat(s.substr(0,5)).concat(s.substr(5,5)); + } + }; +} diff --git a/src/cool2/test/test_data/test_substr_2.meta_test b/src/cool2/test/test_data/test_substr_2.meta_test index ac12dd27d..d4869cce3 100644 --- a/src/cool2/test/test_data/test_substr_2.meta_test +++ b/src/cool2/test/test_data/test_substr_2.meta_test @@ -1,11 +1,11 @@ -"{\"name\": \"test_substr_2\", \"result\": \"\", \"errors\": [\"Index out of range in substr, word:'0123456789' substr begin:0 substr length:-1. Line:20 Column:10\"]}" - -class Main -{ - main():String { - { - s:String <- "0123456789"; - s.substr(0,~1); - } - }; -} +"{\"name\": \"test_substr_2\", \"result\": \"\", \"errors\": [\"Index out of range in substr, word:'0123456789' substr begin:0 substr length:-1. Line:20 Column:10\"]}" + +class Main +{ + main():String { + { + s:String <- "0123456789"; + s.substr(0,~1); + } + }; +} diff --git a/src/cool2/test/test_data/test_substr_3.meta_test b/src/cool2/test/test_data/test_substr_3.meta_test index 2e4aa0f16..09f2d1e25 100644 --- a/src/cool2/test/test_data/test_substr_3.meta_test +++ b/src/cool2/test/test_data/test_substr_3.meta_test @@ -1,11 +1,11 @@ -"{\"name\": \"test_substr_3\", \"result\": \"\", \"errors\": [\"Index out of range in substr, word:'0123456789' substr begin:0 substr length:11. Line:20 Column:10\"]}" - -class Main -{ - main():String { - { - s:String <- "0123456789"; - s.substr(0,11); - } - }; -} +"{\"name\": \"test_substr_3\", \"result\": \"\", \"errors\": [\"Index out of range in substr, word:'0123456789' substr begin:0 substr length:11. Line:20 Column:10\"]}" + +class Main +{ + main():String { + { + s:String <- "0123456789"; + s.substr(0,11); + } + }; +} diff --git a/src/cool2/test/test_data/test_void_dispatch.meta_test b/src/cool2/test/test_data/test_void_dispatch.meta_test index da5fe3fc7..07ade5979 100644 --- a/src/cool2/test/test_data/test_void_dispatch.meta_test +++ b/src/cool2/test/test_data/test_void_dispatch.meta_test @@ -1,12 +1,12 @@ -"{\"name\": \"test_void_dispatch\", \"result\": 0, \"errors\": [\"Can't dispatch a Void value. Line:8 Column:19\"]}" - - -class Main -{ - b:Object; - - main():String - { - b.type_name() - }; +"{\"name\": \"test_void_dispatch\", \"result\": 0, \"errors\": [\"Can't dispatch a Void value. Line:8 Column:19\"]}" + + +class Main +{ + b:Object; + + main():String + { + b.type_name() + }; } \ No newline at end of file diff --git a/src/cool2/test/test_data/test_while.meta_test b/src/cool2/test/test_data/test_while.meta_test index 34441e8e8..8465ed268 100644 --- a/src/cool2/test/test_data/test_while.meta_test +++ b/src/cool2/test/test_data/test_while.meta_test @@ -1,13 +1,13 @@ -"{\"name\": \"test_while\", \"result\": 10, \"errors\": []}" - -class Main inherits IO { - main():Int { - { - counter:Int <- 0; - a:Object <- while counter < 10 loop - counter <- counter + 1 - pool; - counter; - } - }; -} +"{\"name\": \"test_while\", \"result\": 10, \"errors\": []}" + +class Main inherits IO { + main():Int { + { + counter:Int <- 0; + a:Object <- while counter < 10 loop + counter <- counter + 1 + pool; + counter; + } + }; +} diff --git a/src/cool2/test/test_data/test_zero_division.meta_test b/src/cool2/test/test_data/test_zero_division.meta_test index f37582965..dc476ddb4 100644 --- a/src/cool2/test/test_data/test_zero_division.meta_test +++ b/src/cool2/test/test_data/test_zero_division.meta_test @@ -1,10 +1,10 @@ -"{\"name\": \"test_zero_division\", \"result\": 0, \"errors\": [\"Divison by 0 not defined. Line:5 Column:15\"]}" - -class Main -{ - main():Int { - { - 10/0; - } - }; -} +"{\"name\": \"test_zero_division\", \"result\": 0, \"errors\": [\"Divison by 0 not defined. Line:5 Column:15\"]}" + +class Main +{ + main():Int { + { + 10/0; + } + }; +} diff --git a/src/cool2/test/test_reconstruction/test_SELF_TYPE.cl b/src/cool2/test/test_reconstruction/test_SELF_TYPE.cl index 609a3f8f2..dd9d742d2 100644 --- a/src/cool2/test/test_reconstruction/test_SELF_TYPE.cl +++ b/src/cool2/test/test_reconstruction/test_SELF_TYPE.cl @@ -1,73 +1,73 @@ -class A -{ - a:SELF_TYPE; - get(): SELF_TYPE - { - self - }; - get2(): SELF_TYPE - { - new SELF_TYPE - }; - get3(): SELF_TYPE - { - { - a <- - new SELF_TYPE; - a; - } - }; -} - - -class B inherits A -{ - get4(): SELF_TYPE - { - self - .get3( - ) - }; -} - - -class Main inherits IO -{ - main(): String - { - { - a:A <- - new A; - b:B <- - new B; - b - .get( - ) - .type_name( - ) - .concat( - b - .get2( - ) - .type_name( - ) - .concat( - b - .get3( - ) - .type_name( - ) - ) - .concat( - b - .get4( - ) - .type_name( - ) - ) - ); - } - }; -} - - +class A +{ + a:SELF_TYPE; + get(): SELF_TYPE + { + self + }; + get2(): SELF_TYPE + { + new SELF_TYPE + }; + get3(): SELF_TYPE + { + { + a <- + new SELF_TYPE; + a; + } + }; +} + + +class B inherits A +{ + get4(): SELF_TYPE + { + self + .get3( + ) + }; +} + + +class Main inherits IO +{ + main(): String + { + { + a:A <- + new A; + b:B <- + new B; + b + .get( + ) + .type_name( + ) + .concat( + b + .get2( + ) + .type_name( + ) + .concat( + b + .get3( + ) + .type_name( + ) + ) + .concat( + b + .get4( + ) + .type_name( + ) + ) + ); + } + }; +} + + diff --git a/src/cool2/test/test_reconstruction/test_at.cl b/src/cool2/test/test_reconstruction/test_at.cl index cfef6f95f..6986ef90e 100644 --- a/src/cool2/test/test_reconstruction/test_at.cl +++ b/src/cool2/test/test_reconstruction/test_at.cl @@ -1,46 +1,46 @@ -class A -{ - name(): String - { - "class A " - .concat( - self - .name2( - ) - ) - }; - name2(): String - { - "class A" - }; -} - - -class B inherits A -{ - name(): String - { - "class B" - }; - name2(): String - { - "class B" - }; -} - - -class Main inherits IO -{ - main(): String - { - { - b:B <- - new B; - b - @A.name( - ); - } - }; -} - - +class A +{ + name(): String + { + "class A " + .concat( + self + .name2( + ) + ) + }; + name2(): String + { + "class A" + }; +} + + +class B inherits A +{ + name(): String + { + "class B" + }; + name2(): String + { + "class B" + }; +} + + +class Main inherits IO +{ + main(): String + { + { + b:B <- + new B; + b + @A.name( + ); + } + }; +} + + diff --git a/src/cool2/test/test_reconstruction/test_attr_dep_1.cl b/src/cool2/test/test_reconstruction/test_attr_dep_1.cl index d9b99bb88..70e20f2d1 100644 --- a/src/cool2/test/test_reconstruction/test_attr_dep_1.cl +++ b/src/cool2/test/test_reconstruction/test_attr_dep_1.cl @@ -1,19 +1,19 @@ -class Test -{ - test:Int <- 10; - test2:Int <- 20; -} - - -class Main inherits Test -{ - test3:Int <- (test + test2); - main(): Int - { - { - test3; - } - }; -} - - +class Test +{ + test:Int <- 10; + test2:Int <- 20; +} + + +class Main inherits Test +{ + test3:Int <- (test + test2); + main(): Int + { + { + test3; + } + }; +} + + diff --git a/src/cool2/test/test_reconstruction/test_attr_dep_2.cl b/src/cool2/test/test_reconstruction/test_attr_dep_2.cl index 78dea4533..df43af892 100644 --- a/src/cool2/test/test_reconstruction/test_attr_dep_2.cl +++ b/src/cool2/test/test_reconstruction/test_attr_dep_2.cl @@ -1,19 +1,19 @@ -class Test -{ - test3:Int <- (test + test2); - test2:Int <- (2 * test); - test:Int <- 10; -} - - -class Main inherits Test -{ - main(): Int - { - { - test3; - } - }; -} - - +class Test +{ + test3:Int <- (test + test2); + test2:Int <- (2 * test); + test:Int <- 10; +} + + +class Main inherits Test +{ + main(): Int + { + { + test3; + } + }; +} + + diff --git a/src/cool2/test/test_reconstruction/test_auto_1.cl b/src/cool2/test/test_reconstruction/test_auto_1.cl index 0446f20db..2a07c1345 100644 --- a/src/cool2/test/test_reconstruction/test_auto_1.cl +++ b/src/cool2/test/test_reconstruction/test_auto_1.cl @@ -1,23 +1,23 @@ -class Main inherits IO -{ - main(): Main - { - let - x:Int <- - (3 + 2) - in - { - case - x - of - y:Int => - self - .out_string( - "Ok" - ); - esac; - } - }; -} - - +class Main inherits IO +{ + main(): Main + { + let + x:Int <- + (3 + 2) + in + { + case + x + of + y:Int => + self + .out_string( + "Ok" + ); + esac; + } + }; +} + + diff --git a/src/cool2/test/test_reconstruction/test_auto_2.cl b/src/cool2/test/test_reconstruction/test_auto_2.cl index 87f525ed9..c87705cbd 100644 --- a/src/cool2/test/test_reconstruction/test_auto_2.cl +++ b/src/cool2/test/test_reconstruction/test_auto_2.cl @@ -1,16 +1,16 @@ -class Main inherits IO -{ - main(): Int - { - self - .succ( - 10 - ) - }; - succ(n:Int): Int - { - (n + 1) - }; -} - - +class Main inherits IO +{ + main(): Int + { + self + .succ( + 10 + ) + }; + succ(n:Int): Int + { + (n + 1) + }; +} + + diff --git a/src/cool2/test/test_reconstruction/test_auto_3.cl b/src/cool2/test/test_reconstruction/test_auto_3.cl index aab18e038..ef3818cfb 100644 --- a/src/cool2/test/test_reconstruction/test_auto_3.cl +++ b/src/cool2/test/test_reconstruction/test_auto_3.cl @@ -1,16 +1,16 @@ -class Main inherits IO -{ - succ(n:Int): Int - { - (n + 1) - }; - main(): Int - { - self - .succ( - 10 - ) - }; -} - - +class Main inherits IO +{ + succ(n:Int): Int + { + (n + 1) + }; + main(): Int + { + self + .succ( + 10 + ) + }; +} + + diff --git a/src/cool2/test/test_reconstruction/test_auto_4.cl b/src/cool2/test/test_reconstruction/test_auto_4.cl index d0b0c3d43..844adf578 100644 --- a/src/cool2/test/test_reconstruction/test_auto_4.cl +++ b/src/cool2/test/test_reconstruction/test_auto_4.cl @@ -1,41 +1,41 @@ -class Main inherits IO -{ - main(): Int - { - self - .ackermann( - 0, - 3 - ) - }; - ackermann(m:Int, n:Int): Int - { - if - (m = 0) - then - (n + 1) - else - if - (n = 0) - then - self - .ackermann( - (m - 1), - 1 - ) - else - self - .ackermann( - (m - 1), - self - .ackermann( - m, - (n - 1) - ) - ) - fi - fi - }; -} - - +class Main inherits IO +{ + main(): Int + { + self + .ackermann( + 0, + 3 + ) + }; + ackermann(m:Int, n:Int): Int + { + if + (m = 0) + then + (n + 1) + else + if + (n = 0) + then + self + .ackermann( + (m - 1), + 1 + ) + else + self + .ackermann( + (m - 1), + self + .ackermann( + m, + (n - 1) + ) + ) + fi + fi + }; +} + + diff --git a/src/cool2/test/test_reconstruction/test_auto_5.cl b/src/cool2/test/test_reconstruction/test_auto_5.cl index ba5179fea..8e83caae7 100644 --- a/src/cool2/test/test_reconstruction/test_auto_5.cl +++ b/src/cool2/test/test_reconstruction/test_auto_5.cl @@ -1,41 +1,41 @@ -class Main -{ - main(): Object - { - self - .f( - 1, - 1 - ) - }; - f(a:Int, b:Int): Object - { - if - (a = 1) - then - b - else - self - .g( - (a + 1), - (b / 2) - ) - fi - }; - g(a:Int, b:Int): Object - { - if - (b = 1) - then - a - else - self - .f( - (a / 2), - (b + 1) - ) - fi - }; -} - - +class Main +{ + main(): Object + { + self + .f( + 1, + 1 + ) + }; + f(a:Int, b:Int): Object + { + if + (a = 1) + then + b + else + self + .g( + (a + 1), + (b / 2) + ) + fi + }; + g(a:Int, b:Int): Object + { + if + (b = 1) + then + a + else + self + .f( + (a / 2), + (b + 1) + ) + fi + }; +} + + diff --git a/src/cool2/test/test_reconstruction/test_auto_conditional.cl b/src/cool2/test/test_reconstruction/test_auto_conditional.cl index 768dd49d5..765d4e844 100644 --- a/src/cool2/test/test_reconstruction/test_auto_conditional.cl +++ b/src/cool2/test/test_reconstruction/test_auto_conditional.cl @@ -1,172 +1,172 @@ -class Base -{ - base(): Base - { - self - }; -} - - -class OtraBase -{ - otraBase(): OtraBase - { - self - }; -} - - -class ConcBase1 inherits Base -{ -} - - -class ConcBase2 inherits OtraBase -{ -} - - -class Main -{ - a:Object; - main(): Int - { - { - a:OtraBase <- - if - true - then - new OtraBase - else - new ConcBase2 - fi; - b:ConcBase2 <- - if - true - then - new ConcBase2 - else - new ConcBase2 - fi; - c:Object <- - if - true - then - new ConcBase1 - else - new ConcBase2 - fi; - d:Object <- - if - true - then - self - .ret1( - ) - else - self - .ret2( - ) - fi; - e:Object <- - if - true - then - self - .ret1( - new ConcBase1 - ) - else - self - .ret2( - new ConcBase2 - ) - fi; - 1; - } - }; - ret1(): Object - { - c:Object <- - if - true - then - new ConcBase1 - else - new ConcBase2 - fi - }; - ret2(): OtraBase - { - c:OtraBase <- - if - true - then - new OtraBase - else - new ConcBase2 - fi - }; - ret1(param:Base): Object - { - { - c:Base <- - param - .base( - ); - a:Object <- - case - param - of - b:Base => - if - true - then - b - else - param - fi; - b:OtraBase => - if - false - then - b - else - param - fi; - esac; - } - }; - ret2(param:OtraBase): Base - { - { - c:OtraBase <- - param - .otraBase( - ); - a:Base <- - case - param - of - b:Base => - if - true - then - b - else - b - fi; - b:ConcBase1 => - if - true - then - b - else - b - fi; - esac; - } - }; -} - - +class Base +{ + base(): Base + { + self + }; +} + + +class OtraBase +{ + otraBase(): OtraBase + { + self + }; +} + + +class ConcBase1 inherits Base +{ +} + + +class ConcBase2 inherits OtraBase +{ +} + + +class Main +{ + a:Object; + main(): Int + { + { + a:OtraBase <- + if + true + then + new OtraBase + else + new ConcBase2 + fi; + b:ConcBase2 <- + if + true + then + new ConcBase2 + else + new ConcBase2 + fi; + c:Object <- + if + true + then + new ConcBase1 + else + new ConcBase2 + fi; + d:Object <- + if + true + then + self + .ret1( + ) + else + self + .ret2( + ) + fi; + e:Object <- + if + true + then + self + .ret1( + new ConcBase1 + ) + else + self + .ret2( + new ConcBase2 + ) + fi; + 1; + } + }; + ret1(): Object + { + c:Object <- + if + true + then + new ConcBase1 + else + new ConcBase2 + fi + }; + ret2(): OtraBase + { + c:OtraBase <- + if + true + then + new OtraBase + else + new ConcBase2 + fi + }; + ret1(param:Base): Object + { + { + c:Base <- + param + .base( + ); + a:Object <- + case + param + of + b:Base => + if + true + then + b + else + param + fi; + b:OtraBase => + if + false + then + b + else + param + fi; + esac; + } + }; + ret2(param:OtraBase): Base + { + { + c:OtraBase <- + param + .otraBase( + ); + a:Base <- + case + param + of + b:Base => + if + true + then + b + else + b + fi; + b:ConcBase1 => + if + true + then + b + else + b + fi; + esac; + } + }; +} + + diff --git a/src/cool2/test/test_reconstruction/test_auto_defaults.cl b/src/cool2/test/test_reconstruction/test_auto_defaults.cl index b0dfda219..e02ae50fc 100644 --- a/src/cool2/test/test_reconstruction/test_auto_defaults.cl +++ b/src/cool2/test/test_reconstruction/test_auto_defaults.cl @@ -1,14 +1,14 @@ -class Main -{ - a:Int; - main(): Int - { - let - b:Int <- - 0 - in - ((a + b) + 10) - }; -} - - +class Main +{ + a:Int; + main(): Int + { + let + b:Int <- + 0 + in + ((a + b) + 10) + }; +} + + diff --git a/src/cool2/test/test_reconstruction/test_auto_undecidable.cl b/src/cool2/test/test_reconstruction/test_auto_undecidable.cl index 3d0d950e9..068c0f652 100644 --- a/src/cool2/test/test_reconstruction/test_auto_undecidable.cl +++ b/src/cool2/test/test_reconstruction/test_auto_undecidable.cl @@ -1,33 +1,33 @@ -class A -{ - m1(): Int - { - 10 - }; -} - - -class B -{ - m1(): Int - { - 20 - }; -} - - -class Main inherits IO -{ - main(): Int - { - 10 - }; - m(a:AUTO_TYPE): Int - { - a - .m1( - ) - }; -} - - +class A +{ + m1(): Int + { + 10 + }; +} + + +class B +{ + m1(): Int + { + 20 + }; +} + + +class Main inherits IO +{ + main(): Int + { + 10 + }; + m(a:AUTO_TYPE): Int + { + a + .m1( + ) + }; +} + + diff --git a/src/cool2/test/test_reconstruction/test_case.cl b/src/cool2/test/test_reconstruction/test_case.cl index d4e22a245..58460cf90 100644 --- a/src/cool2/test/test_reconstruction/test_case.cl +++ b/src/cool2/test/test_reconstruction/test_case.cl @@ -1,26 +1,26 @@ -class Main -{ - main(): Object - { - { - a:Object <- - case - (20 + 40) - of - b:Int => - if - (b = 60) - then - 50 - else - 0 - fi; - c:Object => - c; - esac; - a; - } - }; -} - - +class Main +{ + main(): Object + { + { + a:Object <- + case + (20 + 40) + of + b:Int => + if + (b = 60) + then + 50 + else + 0 + fi; + c:Object => + c; + esac; + a; + } + }; +} + + diff --git a/src/cool2/test/test_reconstruction/test_case_no_type.cl b/src/cool2/test/test_reconstruction/test_case_no_type.cl index 2970e7d95..4c786ee93 100644 --- a/src/cool2/test/test_reconstruction/test_case_no_type.cl +++ b/src/cool2/test/test_reconstruction/test_case_no_type.cl @@ -1,24 +1,24 @@ -class Main -{ - main(): Object - { - { - a:Object <- - case - (20 + 40) - of - a:Bool => - if - a - then - true - else - false - fi; - esac; - a; - } - }; -} - - +class Main +{ + main(): Object + { + { + a:Object <- + case + (20 + 40) + of + a:Bool => + if + a + then + true + else + false + fi; + esac; + a; + } + }; +} + + diff --git a/src/cool2/test/test_reconstruction/test_case_repeated_type.cl b/src/cool2/test/test_reconstruction/test_case_repeated_type.cl index 5ae3dc3c0..6332e4c4a 100644 --- a/src/cool2/test/test_reconstruction/test_case_repeated_type.cl +++ b/src/cool2/test/test_reconstruction/test_case_repeated_type.cl @@ -1,34 +1,34 @@ -class Main -{ - main(): Object - { - { - a:Object <- - case - (20 + 40) - of - b:Int => - if - (b = 60) - then - 50 - else - 0 - fi; - b:Int => - if - (b = 60) - then - 40 - else - 0 - fi; - c:Object => - c; - esac; - a; - } - }; -} - - +class Main +{ + main(): Object + { + { + a:Object <- + case + (20 + 40) + of + b:Int => + if + (b = 60) + then + 50 + else + 0 + fi; + b:Int => + if + (b = 60) + then + 40 + else + 0 + fi; + c:Object => + c; + esac; + a; + } + }; +} + + diff --git a/src/cool2/test/test_reconstruction/test_case_void_eval.cl b/src/cool2/test/test_reconstruction/test_case_void_eval.cl index c5797b263..25d73b949 100644 --- a/src/cool2/test/test_reconstruction/test_case_void_eval.cl +++ b/src/cool2/test/test_reconstruction/test_case_void_eval.cl @@ -1,30 +1,30 @@ -class Main -{ - main(): Object - { - { - a:Object <- - case - while - false - loop - 1 - pool - of - b:Int => - if - (b = 60) - then - 50 - else - 0 - fi; - c:Object => - c; - esac; - a; - } - }; -} - - +class Main +{ + main(): Object + { + { + a:Object <- + case + while + false + loop + 1 + pool + of + b:Int => + if + (b = 60) + then + 50 + else + 0 + fi; + c:Object => + c; + esac; + a; + } + }; +} + + diff --git a/src/cool2/test/test_reconstruction/test_comments.cl b/src/cool2/test/test_reconstruction/test_comments.cl index 098ac7a18..77f4a4bc3 100644 --- a/src/cool2/test/test_reconstruction/test_comments.cl +++ b/src/cool2/test/test_reconstruction/test_comments.cl @@ -1,9 +1,9 @@ -class Main -{ - main(): Object - { - 0 - }; -} - - +class Main +{ + main(): Object + { + 0 + }; +} + + diff --git a/src/cool2/test/test_reconstruction/test_conditional.cl b/src/cool2/test/test_reconstruction/test_conditional.cl index a82e2e576..c2f764a09 100644 --- a/src/cool2/test/test_reconstruction/test_conditional.cl +++ b/src/cool2/test/test_reconstruction/test_conditional.cl @@ -1,93 +1,93 @@ -class A -{ -} - - -class B inherits A -{ -} - - -class C inherits B -{ -} - - -class D inherits B -{ -} - - -class E inherits A -{ -} - - -class Main inherits IO -{ - main(): Int - { - { - d:Object <- - if - true - then - new E - else - new C - fi; - e:B <- - if - true - then - new C - else - new D - fi; - a:Int <- - if - true - then - 1 - else - 0 - fi; - b:Int <- - if - false - then - 0 - else - 1 - fi; - c:Int <- - if - if - (1 > 2) - then - (2 > 1) - else - (2 > 1) - fi - then - 1 - else - 0 - fi; - self - .out_string( - d - .type_name( - ) - ) - .out_string( - e - .type_name( - ) - ); - ((a + b) + c); - } - }; -} - - +class A +{ +} + + +class B inherits A +{ +} + + +class C inherits B +{ +} + + +class D inherits B +{ +} + + +class E inherits A +{ +} + + +class Main inherits IO +{ + main(): Int + { + { + d:Object <- + if + true + then + new E + else + new C + fi; + e:B <- + if + true + then + new C + else + new D + fi; + a:Int <- + if + true + then + 1 + else + 0 + fi; + b:Int <- + if + false + then + 0 + else + 1 + fi; + c:Int <- + if + if + (1 > 2) + then + (2 > 1) + else + (2 > 1) + fi + then + 1 + else + 0 + fi; + self + .out_string( + d + .type_name( + ) + ) + .out_string( + e + .type_name( + ) + ); + ((a + b) + c); + } + }; +} + + diff --git a/src/cool2/test/test_reconstruction/test_default.cl b/src/cool2/test/test_reconstruction/test_default.cl index d8a5b1f0a..2781ccf1d 100644 --- a/src/cool2/test/test_reconstruction/test_default.cl +++ b/src/cool2/test/test_reconstruction/test_default.cl @@ -1,45 +1,45 @@ -class B -{ -} - - -class Main inherits IO -{ - a:Int; - s:String; - b:Bool; - o:Object; - o2:B; - main(): Bool - { - { - if - (a = 0) - then - if - (s = "") - then - if - not b - then - if - (o = o2) - then - true - else - false - fi - else - false - fi - else - false - fi - else - false - fi; - } - }; -} - - +class B +{ +} + + +class Main inherits IO +{ + a:Int; + s:String; + b:Bool; + o:Object; + o2:B; + main(): Bool + { + { + if + (a = 0) + then + if + (s = "") + then + if + not b + then + if + (o = o2) + then + true + else + false + fi + else + false + fi + else + false + fi + else + false + fi; + } + }; +} + + diff --git a/src/cool2/test/test_reconstruction/test_entry_point_1.cl b/src/cool2/test/test_reconstruction/test_entry_point_1.cl index e4d564db5..bc7d2f2bf 100644 --- a/src/cool2/test/test_reconstruction/test_entry_point_1.cl +++ b/src/cool2/test/test_reconstruction/test_entry_point_1.cl @@ -1,11 +1,11 @@ -class Main -{ - main(a:Int): Int - { - { - 30; - } - }; -} - - +class Main +{ + main(a:Int): Int + { + { + 30; + } + }; +} + + diff --git a/src/cool2/test/test_reconstruction/test_entry_point_2.cl b/src/cool2/test/test_reconstruction/test_entry_point_2.cl index 3ca834180..9abc3f27d 100644 --- a/src/cool2/test/test_reconstruction/test_entry_point_2.cl +++ b/src/cool2/test/test_reconstruction/test_entry_point_2.cl @@ -1,16 +1,16 @@ -class M -{ - main(): Int - { - { - 30; - } - }; -} - - -class Main inherits M -{ -} - - +class M +{ + main(): Int + { + { + 30; + } + }; +} + + +class Main inherits M +{ +} + + diff --git a/src/cool2/test/test_reconstruction/test_entry_point_3.cl b/src/cool2/test/test_reconstruction/test_entry_point_3.cl index 0a1f6c54e..9d6b17562 100644 --- a/src/cool2/test/test_reconstruction/test_entry_point_3.cl +++ b/src/cool2/test/test_reconstruction/test_entry_point_3.cl @@ -1,11 +1,11 @@ -class NoMain -{ - main(): Int - { - { - 30; - } - }; -} - - +class NoMain +{ + main(): Int + { + { + 30; + } + }; +} + + diff --git a/src/cool2/test/test_reconstruction/test_equality.cl b/src/cool2/test/test_reconstruction/test_equality.cl index a8f1bcde0..06cf1dab5 100644 --- a/src/cool2/test/test_reconstruction/test_equality.cl +++ b/src/cool2/test/test_reconstruction/test_equality.cl @@ -1,33 +1,33 @@ -class A -{ -} - - -class B -{ -} - - -class Main -{ - cClass:A; - dClass:B; - main(): Bool - { - { - aClass:A <- - new A; - bClass:A <- - new A; - a:Bool <- - (aClass = aClass); - b:Bool <- - not (aClass = bClass); - c:Bool <- - (cClass = dClass); - ((c = (a = b)) = true); - } - }; -} - - +class A +{ +} + + +class B +{ +} + + +class Main +{ + cClass:A; + dClass:B; + main(): Bool + { + { + aClass:A <- + new A; + bClass:A <- + new A; + a:Bool <- + (aClass = aClass); + b:Bool <- + not (aClass = bClass); + c:Bool <- + (cClass = dClass); + ((c = (a = b)) = true); + } + }; +} + + diff --git a/src/cool2/test/test_reconstruction/test_escape_line.cl b/src/cool2/test/test_reconstruction/test_escape_line.cl index 1e952ba9f..a17294169 100644 --- a/src/cool2/test/test_reconstruction/test_escape_line.cl +++ b/src/cool2/test/test_reconstruction/test_escape_line.cl @@ -1,11 +1,11 @@ -class Main -{ - main(): String - { - { - "Hello People"; - } - }; -} - - +class Main +{ + main(): String + { + { + "Hello People"; + } + }; +} + + diff --git a/src/cool2/test/test_reconstruction/test_instances.cl b/src/cool2/test/test_reconstruction/test_instances.cl index b228613c0..4a7a3104c 100644 --- a/src/cool2/test/test_reconstruction/test_instances.cl +++ b/src/cool2/test/test_reconstruction/test_instances.cl @@ -1,35 +1,35 @@ -class Main -{ - a:Int; - set(v:Int): Int - { - a <- - v - }; - get(): Int - { - a - }; - main(): Int - { - { - a <- - 10; - b:Main <- - self - .copy( - ); - a <- - 20; - ( b - .get( - ) - + self - .get( - ) -); - } - }; -} - - +class Main +{ + a:Int; + set(v:Int): Int + { + a <- + v + }; + get(): Int + { + a + }; + main(): Int + { + { + a <- + 10; + b:Main <- + self + .copy( + ); + a <- + 20; + ( b + .get( + ) + + self + .get( + ) +); + } + }; +} + + diff --git a/src/cool2/test/test_reconstruction/test_isvoid.cl b/src/cool2/test/test_reconstruction/test_isvoid.cl index 52758c9e0..ee0360060 100644 --- a/src/cool2/test/test_reconstruction/test_isvoid.cl +++ b/src/cool2/test/test_reconstruction/test_isvoid.cl @@ -1,20 +1,20 @@ -class Main inherits IO -{ - a:Int; - s:String; - o:Object; - main(): Bool - { - { - a:Int <- - 10; - b:Bool <- - isvoid a; - c:Bool <- - isvoid o; - c; - } - }; -} - - +class Main inherits IO +{ + a:Int; + s:String; + o:Object; + main(): Bool + { + { + a:Int <- + 10; + b:Bool <- + isvoid a; + c:Bool <- + isvoid o; + c; + } + }; +} + + diff --git a/src/cool2/test/test_reconstruction/test_let.cl b/src/cool2/test/test_reconstruction/test_let.cl index 22c92ecc6..b8ede9da5 100644 --- a/src/cool2/test/test_reconstruction/test_let.cl +++ b/src/cool2/test/test_reconstruction/test_let.cl @@ -1,50 +1,50 @@ -class Main -{ - b:Bool; - main(): Int - { - { - a:Int <- - let - a:Bool <- - true, - a:Int <- - if - a - then - 10 - else - 20 - fi - in - a; - c:Int <- - let - a:SELF_TYPE <- - self - .change( - ), - b:Int <- - if - b - then - 10 - else - 20 - fi - in - b; - (a + c); - } - }; - change(): SELF_TYPE - { - { - b <- - not b; - self; - } - }; -} - - +class Main +{ + b:Bool; + main(): Int + { + { + a:Int <- + let + a:Bool <- + true, + a:Int <- + if + a + then + 10 + else + 20 + fi + in + a; + c:Int <- + let + a:SELF_TYPE <- + self + .change( + ), + b:Int <- + if + b + then + 10 + else + 20 + fi + in + b; + (a + c); + } + }; + change(): SELF_TYPE + { + { + b <- + not b; + self; + } + }; +} + + diff --git a/src/cool2/test/test_reconstruction/test_other.cl b/src/cool2/test/test_reconstruction/test_other.cl index eeca2e9b8..77a2f0ed0 100644 --- a/src/cool2/test/test_reconstruction/test_other.cl +++ b/src/cool2/test/test_reconstruction/test_other.cl @@ -1,28 +1,28 @@ -class A -{ - a:Int; - a(a:Object): Object - { - a - }; -} - - -class B inherits A -{ - a(self:Int): Object - { - a - }; -} - - -class Main inherits IO -{ - main(): B - { - new B - }; -} - - +class A +{ + a:Int; + a(a:Object): Object + { + a + }; +} + + +class B inherits A +{ + a(self:Int): Object + { + a + }; +} + + +class Main inherits IO +{ + main(): B + { + new B + }; +} + + diff --git a/src/cool2/test/test_reconstruction/test_recursive.cl b/src/cool2/test/test_reconstruction/test_recursive.cl index 531d729bc..e04a33207 100644 --- a/src/cool2/test/test_reconstruction/test_recursive.cl +++ b/src/cool2/test/test_reconstruction/test_recursive.cl @@ -1,30 +1,30 @@ -class Main -{ - main(): Int - { - self - .fibo( - 7 - ) - }; - fibo(n:Int): Int - { - if - (n > 1) - then - ( self - .fibo( - (n - 1) - ) - + self - .fibo( - (n - 2) - ) -) - else - 1 - fi - }; -} - - +class Main +{ + main(): Int + { + self + .fibo( + 7 + ) + }; + fibo(n:Int): Int + { + if + (n > 1) + then + ( self + .fibo( + (n - 1) + ) + + self + .fibo( + (n - 2) + ) +) + else + 1 + fi + }; +} + + diff --git a/src/cool2/test/test_reconstruction/test_substr_1.cl b/src/cool2/test/test_reconstruction/test_substr_1.cl index ccd6f75e2..a4d37f6b0 100644 --- a/src/cool2/test/test_reconstruction/test_substr_1.cl +++ b/src/cool2/test/test_reconstruction/test_substr_1.cl @@ -1,31 +1,31 @@ -class Main -{ - main(): String - { - { - s:String <- - "0123456789"; - s - .substr( - 0, - 10 - ) - .concat( - s - .substr( - 0, - 5 - ) - ) - .concat( - s - .substr( - 5, - 5 - ) - ); - } - }; -} - - +class Main +{ + main(): String + { + { + s:String <- + "0123456789"; + s + .substr( + 0, + 10 + ) + .concat( + s + .substr( + 0, + 5 + ) + ) + .concat( + s + .substr( + 5, + 5 + ) + ); + } + }; +} + + diff --git a/src/cool2/test/test_reconstruction/test_substr_2.cl b/src/cool2/test/test_reconstruction/test_substr_2.cl index 3f7e50d6b..aba3766d7 100644 --- a/src/cool2/test/test_reconstruction/test_substr_2.cl +++ b/src/cool2/test/test_reconstruction/test_substr_2.cl @@ -1,17 +1,17 @@ -class Main -{ - main(): String - { - { - s:String <- - "0123456789"; - s - .substr( - 0, - ~ 1 - ); - } - }; -} - - +class Main +{ + main(): String + { + { + s:String <- + "0123456789"; + s + .substr( + 0, + ~ 1 + ); + } + }; +} + + diff --git a/src/cool2/test/test_reconstruction/test_substr_3.cl b/src/cool2/test/test_reconstruction/test_substr_3.cl index 59b4fcb22..f69a37a7c 100644 --- a/src/cool2/test/test_reconstruction/test_substr_3.cl +++ b/src/cool2/test/test_reconstruction/test_substr_3.cl @@ -1,17 +1,17 @@ -class Main -{ - main(): String - { - { - s:String <- - "0123456789"; - s - .substr( - 0, - 11 - ); - } - }; -} - - +class Main +{ + main(): String + { + { + s:String <- + "0123456789"; + s + .substr( + 0, + 11 + ); + } + }; +} + + diff --git a/src/cool2/test/test_reconstruction/test_void_dispatch.cl b/src/cool2/test/test_reconstruction/test_void_dispatch.cl index 12023ab3a..4e4d88461 100644 --- a/src/cool2/test/test_reconstruction/test_void_dispatch.cl +++ b/src/cool2/test/test_reconstruction/test_void_dispatch.cl @@ -1,12 +1,12 @@ -class Main -{ - b:Object; - main(): String - { - b - .type_name( - ) - }; -} - - +class Main +{ + b:Object; + main(): String + { + b + .type_name( + ) + }; +} + + diff --git a/src/cool2/test/test_reconstruction/test_while.cl b/src/cool2/test/test_reconstruction/test_while.cl index dbc37dd2c..8a86fc216 100644 --- a/src/cool2/test/test_reconstruction/test_while.cl +++ b/src/cool2/test/test_reconstruction/test_while.cl @@ -1,20 +1,20 @@ -class Main inherits IO -{ - main(): Int - { - { - counter:Int <- - 0; - a:Object <- - while - (counter < 10) - loop - counter <- - (counter + 1) - pool; - counter; - } - }; -} - - +class Main inherits IO +{ + main(): Int + { + { + counter:Int <- + 0; + a:Object <- + while + (counter < 10) + loop + counter <- + (counter + 1) + pool; + counter; + } + }; +} + + diff --git a/src/cool2/test/test_reconstruction/test_zero_division.cl b/src/cool2/test/test_reconstruction/test_zero_division.cl index d17986555..5e683a3a2 100644 --- a/src/cool2/test/test_reconstruction/test_zero_division.cl +++ b/src/cool2/test/test_reconstruction/test_zero_division.cl @@ -1,11 +1,11 @@ -class Main -{ - main(): Int - { - { - (10 / 0); - } - }; -} - - +class Main +{ + main(): Int + { + { + (10 / 0); + } + }; +} + + diff --git a/src/cool2/test/test_run/01_program.cl b/src/cool2/test/test_run/01_program.cl index cf8b86a51..c87561ca0 100644 --- a/src/cool2/test/test_run/01_program.cl +++ b/src/cool2/test/test_run/01_program.cl @@ -1,34 +1,34 @@ -class Main inherits IO { - number: Int <- 5; - - main () : Object { - testing_fibonacci(number) - }; - - testing_fibonacci(n: Int) : IO {{ - out_string("Iterative Fibonacci : "); - out_int(iterative_fibonacci(5)); - out_string("\n"); - - out_string("Recursive Fibonacci : "); - out_int(recursive_fibonacci(5)); - out_string("\n"); - }}; - - recursive_fibonacci (n: AUTO_TYPE) : AUTO_TYPE { - if n <= 2 then 1 else recursive_fibonacci(n - 1) + recursive_fibonacci(n - 2) fi - }; - - iterative_fibonacci(n: AUTO_TYPE) : AUTO_TYPE { - let i: Int <- 2, n1: Int <- 1, n2: Int <- 1, temp: Int in { - while i < n loop - let temp: Int <- n2 in { - n2 <- n2 + n1; - n1 <- temp; - i <- i + 1; - } - pool; - n2; - } - }; +class Main inherits IO { + number: Int <- 5; + + main () : Object { + testing_fibonacci(number) + }; + + testing_fibonacci(n: Int) : IO {{ + out_string("Iterative Fibonacci : "); + out_int(iterative_fibonacci(5)); + out_string("\n"); + + out_string("Recursive Fibonacci : "); + out_int(recursive_fibonacci(5)); + out_string("\n"); + }}; + + recursive_fibonacci (n: AUTO_TYPE) : AUTO_TYPE { + if n <= 2 then 1 else recursive_fibonacci(n - 1) + recursive_fibonacci(n - 2) fi + }; + + iterative_fibonacci(n: AUTO_TYPE) : AUTO_TYPE { + let i: Int <- 2, n1: Int <- 1, n2: Int <- 1, temp: Int in { + while i < n loop + let temp: Int <- n2 in { + n2 <- n2 + n1; + n1 <- temp; + i <- i + 1; + } + pool; + n2; + } + }; } \ No newline at end of file diff --git a/src/cool2/test/test_run/02_program.cl b/src/cool2/test/test_run/02_program.cl index 5c80aa2c7..eb3a70605 100644 --- a/src/cool2/test/test_run/02_program.cl +++ b/src/cool2/test/test_run/02_program.cl @@ -1,24 +1,24 @@ -class Main inherits IO { - main (): Object { - { - out_int((new Ackermann).ackermann(3, 2)); - out_string("\n"); - } - }; -} - -class Ackermann { - ackermann (m: Int, n: Int): Int { - if m = 0 - then - n + 1 - else - if n = 0 - then - self.ackermann(m - 1, 1) - else - self.ackermann(m - 1, self.ackermann(m, n - 1)) - fi - fi - }; +class Main inherits IO { + main (): Object { + { + out_int((new Ackermann).ackermann(3, 2)); + out_string("\n"); + } + }; +} + +class Ackermann { + ackermann (m: Int, n: Int): Int { + if m = 0 + then + n + 1 + else + if n = 0 + then + self.ackermann(m - 1, 1) + else + self.ackermann(m - 1, self.ackermann(m, n - 1)) + fi + fi + }; } \ No newline at end of file diff --git a/src/cool2/test/test_run/03_program.cl b/src/cool2/test/test_run/03_program.cl index d061381d3..604a206bb 100644 --- a/src/cool2/test/test_run/03_program.cl +++ b/src/cool2/test/test_run/03_program.cl @@ -1,22 +1,22 @@ -class A { } - -class B inherits A { } - -class C inherits A { } - -class D inherits C { } - -class Main inherits IO { - main () : Object { - testing_case() - }; - - testing_case() : IO { - let a: A <- new D in - case a of - x: B => out_string("Is type B.\n"); - x: C => out_string("Is type C.\n"); - x: D => out_string("Is type D.\n"); - esac - }; +class A { } + +class B inherits A { } + +class C inherits A { } + +class D inherits C { } + +class Main inherits IO { + main () : Object { + testing_case() + }; + + testing_case() : IO { + let a: A <- new D in + case a of + x: B => out_string("Is type B.\n"); + x: C => out_string("Is type C.\n"); + x: D => out_string("Is type D.\n"); + esac + }; } \ No newline at end of file diff --git a/src/cool2/test/test_run/04_program.cl b/src/cool2/test/test_run/04_program.cl index 1615934ff..ca0850a18 100644 --- a/src/cool2/test/test_run/04_program.cl +++ b/src/cool2/test/test_run/04_program.cl @@ -1,47 +1,47 @@ -class Main inherits IO { - main (): IO { - let vector: Vector2 <- (new Vector2).init(0, 0) in - vector.print_vector() - }; -} - -class Vector2 { - x: Int; - - y: Int; - - init (x_: Int, y_: Int): Vector2 { - { - x <- x_; - y <- y_; - self; - } - }; - - get_x (): Int { - x - }; - - get_y (): Int { - y - }; - - add (v: Vector2): Vector2 { - (new Vector2).init(x + v.get_x(), y + v.get_y()) - }; - - print_vector (): IO { - let io: IO <- (new IO) in - { - io.out_string("("); - io.out_int(self.get_x()); - io.out_string("; "); - io.out_int(self.get_y()); - io.out_string(")\n"); - } - }; - - clone_vector (): Vector2 { - (new Vector2).init(x, y) - }; +class Main inherits IO { + main (): IO { + let vector: Vector2 <- (new Vector2).init(0, 0) in + vector.print_vector() + }; +} + +class Vector2 { + x: Int; + + y: Int; + + init (x_: Int, y_: Int): Vector2 { + { + x <- x_; + y <- y_; + self; + } + }; + + get_x (): Int { + x + }; + + get_y (): Int { + y + }; + + add (v: Vector2): Vector2 { + (new Vector2).init(x + v.get_x(), y + v.get_y()) + }; + + print_vector (): IO { + let io: IO <- (new IO) in + { + io.out_string("("); + io.out_int(self.get_x()); + io.out_string("; "); + io.out_int(self.get_y()); + io.out_string(")\n"); + } + }; + + clone_vector (): Vector2 { + (new Vector2).init(x, y) + }; } \ No newline at end of file diff --git a/src/cool2/test/test_run/05_program.cl b/src/cool2/test/test_run/05_program.cl index 2bd4b5309..b67842198 100644 --- a/src/cool2/test/test_run/05_program.cl +++ b/src/cool2/test/test_run/05_program.cl @@ -1,23 +1,23 @@ -class Main { - main (): Object { - let total: Int <- 10, - i: Int <- 1, - io: IO <- (new IO) in - while i <= total loop - { - io.out_int(self.fibonacci(i)); - io.out_string("\n"); - i <- i + 1; - } - pool - }; - - fibonacci (n: Int): Int { - if n <= 2 - then - 1 - else - self.fibonacci(n - 1) + self.fibonacci(n - 2) - fi - }; +class Main { + main (): Object { + let total: Int <- 10, + i: Int <- 1, + io: IO <- (new IO) in + while i <= total loop + { + io.out_int(self.fibonacci(i)); + io.out_string("\n"); + i <- i + 1; + } + pool + }; + + fibonacci (n: Int): Int { + if n <= 2 + then + 1 + else + self.fibonacci(n - 1) + self.fibonacci(n - 2) + fi + }; } \ No newline at end of file diff --git a/src/cool2/test/tests.py b/src/cool2/test/tests.py index f1e84d080..5ba342512 100644 --- a/src/cool2/test/tests.py +++ b/src/cool2/test/tests.py @@ -1,35 +1,35 @@ -import json -import os - -base_dir = os.path.dirname(__file__) -test_dir = os.path.join(base_dir, "test_data") - -def dump_meta_test(name, code, result, errors): - meta = json.dumps({"name":name, "result":result, "errors":errors}) - dump_dir = os.path.join(test_dir, name+".meta_test") - with open(dump_dir, "w") as f: - json.dump(meta,f) - f.write("\n") - f.write(code) - -def load_meta_test(path): - with open(path, "r") as f: - meta = f.readline() - meta = json.loads(meta) - meta = json.loads(meta) - name, result, errors = [meta[x] for x in ["name", "result", "errors"]] - code = f.read() - return name, code, result, errors - -tests = [] - -for meta_file in os.listdir(test_dir): - tests.append(load_meta_test(os.path.join(test_dir, meta_file))) - -run_dir = os.path.join(base_dir, "test_run") - -tests_run = [] -for program in os.listdir(run_dir): - with open(os.path.join(run_dir, program)) as f: - program = f.read() +import json +import os + +base_dir = os.path.dirname(__file__) +test_dir = os.path.join(base_dir, "test_data") + +def dump_meta_test(name, code, result, errors): + meta = json.dumps({"name":name, "result":result, "errors":errors}) + dump_dir = os.path.join(test_dir, name+".meta_test") + with open(dump_dir, "w") as f: + json.dump(meta,f) + f.write("\n") + f.write(code) + +def load_meta_test(path): + with open(path, "r") as f: + meta = f.readline() + meta = json.loads(meta) + meta = json.loads(meta) + name, result, errors = [meta[x] for x in ["name", "result", "errors"]] + code = f.read() + return name, code, result, errors + +tests = [] + +for meta_file in os.listdir(test_dir): + tests.append(load_meta_test(os.path.join(test_dir, meta_file))) + +run_dir = os.path.join(base_dir, "test_run") + +tests_run = [] +for program in os.listdir(run_dir): + with open(os.path.join(run_dir, program)) as f: + program = f.read() tests_run.append(program) \ No newline at end of file diff --git a/src/cool_cmp/lexer/lexer2.py b/src/cool_cmp/lexer/lexer2.py index e1d01dced..37fcbeaa8 100644 --- a/src/cool_cmp/lexer/lexer2.py +++ b/src/cool_cmp/lexer/lexer2.py @@ -1,73 +1,73 @@ -""" -Lexer usando ply -""" -from typing import List, Tuple - -from cool_cmp.lexer.interface import ILexer -from cool_cmp.shared.token import ICoolToken -from cool_cmp.shared.errors import ErrorTracker -from cool_cmp.lexer.errors import LexerCoolError -from cool2.cool.lexer.cool_lexer import cool_lexer -from cool2.cool.lexer.comment_lexer import comment_lexer -from cool2.cool.pipeline import lexer_pipeline - -from cool2.lib.lexer.lexer import DetailToken, DetailLexer - -class CoolToken2(DetailToken, ICoolToken): - - def set_lex(self, lex:str): - self.lex = (lex, self.row, self.column) - - def get_lex(self)->str: - return self.lex[0] - - def set_type(self, typex:str): - self.token_type = typex - - def get_type(self)->str: - return self.token_type - - def set_position(self, line:int, column:int): - self.lex = (self.lex[0], line, column) - self.row = line - self.column = column - - def get_position(self)->Tuple[int,int]: - return (self.row,self.column) - - def __str__(self): - return f"{self.get_lex()}:{self.get_type()} Line:{self.get_position()[0]} Column:{self.get_position()[1]}" - - def __repr__(self): - return str(self) - -class CoolLexer2(ILexer): - - @property - def name(self)->str: - return "lexer_name" - - def __init__(self): - self.error_tracker = ErrorTracker() # Error tracker implementation - - def __call__(self, program_string:str) -> List[ICoolToken]: - result = lexer_pipeline(program_string) - tokens = result["text_tokens"] - for err in result["errors"]: - self.add_error(err) - - # Adding context - for key, value in result.items(): - self.add_extra_info(key, value) - - return [self.__DetailToken2CoolToken2(tok) for tok in tokens] - - def add_error(self, error:LexerCoolError): - self.error_tracker.add_error(error) - - def get_errors(self)->List[LexerCoolError]: - errors = self.error_tracker.get_errors() - return errors - - def __DetailToken2CoolToken2(self, detail_token:DetailToken)->CoolToken2: - return CoolToken2(detail_token.lex, detail_token.token_type) +""" +Lexer usando ply +""" +from typing import List, Tuple + +from cool_cmp.lexer.interface import ILexer +from cool_cmp.shared.token import ICoolToken +from cool_cmp.shared.errors import ErrorTracker +from cool_cmp.lexer.errors import LexerCoolError +from cool2.cool.lexer.cool_lexer import cool_lexer +from cool2.cool.lexer.comment_lexer import comment_lexer +from cool2.cool.pipeline import lexer_pipeline + +from cool2.lib.lexer.lexer import DetailToken, DetailLexer + +class CoolToken2(DetailToken, ICoolToken): + + def set_lex(self, lex:str): + self.lex = (lex, self.row, self.column) + + def get_lex(self)->str: + return self.lex[0] + + def set_type(self, typex:str): + self.token_type = typex + + def get_type(self)->str: + return self.token_type + + def set_position(self, line:int, column:int): + self.lex = (self.lex[0], line, column) + self.row = line + self.column = column + + def get_position(self)->Tuple[int,int]: + return (self.row,self.column) + + def __str__(self): + return f"{self.get_lex()}:{self.get_type()} Line:{self.get_position()[0]} Column:{self.get_position()[1]}" + + def __repr__(self): + return str(self) + +class CoolLexer2(ILexer): + + @property + def name(self)->str: + return "lexer_name" + + def __init__(self): + self.error_tracker = ErrorTracker() # Error tracker implementation + + def __call__(self, program_string:str) -> List[ICoolToken]: + result = lexer_pipeline(program_string) + tokens = result["text_tokens"] + for err in result["errors"]: + self.add_error(err) + + # Adding context + for key, value in result.items(): + self.add_extra_info(key, value) + + return [self.__DetailToken2CoolToken2(tok) for tok in tokens] + + def add_error(self, error:LexerCoolError): + self.error_tracker.add_error(error) + + def get_errors(self)->List[LexerCoolError]: + errors = self.error_tracker.get_errors() + return errors + + def __DetailToken2CoolToken2(self, detail_token:DetailToken)->CoolToken2: + return CoolToken2(detail_token.lex, detail_token.token_type) diff --git a/src/cool_cmp/parser/parser2.py b/src/cool_cmp/parser/parser2.py index c8ce731b1..4db2605ef 100644 --- a/src/cool_cmp/parser/parser2.py +++ b/src/cool_cmp/parser/parser2.py @@ -1,32 +1,32 @@ -from cool_cmp.parser.interface import IParser -from cool_cmp.lexer.interface import ICoolToken -from cool_cmp.shared.errors import ErrorTracker -from cool_cmp.parser.errors import SyntacticCoolError -from cool2.cool.parser.cool_parser import cool_parser -from cool2.cool.pipeline import syntax_pipeline -from cool_cmp.shared.ast import BaseAST -from typing import List,Dict - -class CoolParer2(IParser): - - @property - def name(self)->str: - return "parser" - - def __init__(self): - self.error_tracker = ErrorTracker() # Error tracker implementation - - def __call__(self, tokens:List[ICoolToken], context:Dict[str,object]) -> BaseAST: - result = syntax_pipeline(context) - new_keys = set(result.keys()).difference(set(context.keys())) - for key in new_keys: - self.add_extra_info(key, result[key]) - return BaseAST(result["ast"]) - - - def add_error(self, error:SyntacticCoolError): - self.error_tracker.add_error(error) - - def get_errors(self)->List[SyntacticCoolError]: - errors = self.error_tracker.get_errors() - return errors +from cool_cmp.parser.interface import IParser +from cool_cmp.lexer.interface import ICoolToken +from cool_cmp.shared.errors import ErrorTracker +from cool_cmp.parser.errors import SyntacticCoolError +from cool2.cool.parser.cool_parser import cool_parser +from cool2.cool.pipeline import syntax_pipeline +from cool_cmp.shared.ast import BaseAST +from typing import List,Dict + +class CoolParer2(IParser): + + @property + def name(self)->str: + return "parser" + + def __init__(self): + self.error_tracker = ErrorTracker() # Error tracker implementation + + def __call__(self, tokens:List[ICoolToken], context:Dict[str,object]) -> BaseAST: + result = syntax_pipeline(context) + new_keys = set(result.keys()).difference(set(context.keys())) + for key in new_keys: + self.add_extra_info(key, result[key]) + return BaseAST(result["ast"]) + + + def add_error(self, error:SyntacticCoolError): + self.error_tracker.add_error(error) + + def get_errors(self)->List[SyntacticCoolError]: + errors = self.error_tracker.get_errors() + return errors diff --git a/src/cool_cmp/semantic/implementations.py b/src/cool_cmp/semantic/implementations.py index 90c3468db..08814208f 100644 --- a/src/cool_cmp/semantic/implementations.py +++ b/src/cool_cmp/semantic/implementations.py @@ -1,247 +1,247 @@ -from cool_cmp.semantic.interface import IContext, IVariableInfo, IAttributeInfo, IMethodInfo, IType, IScope -from cool_cmp.semantic.errors import SemanticError, VARIABLE_ALREADY_DEFINED, ATTRIBUTE_ALREADY_DEFINED, \ - METHOD_ALREADY_DEFINED, METHOD_REDEFINITION_INVALID, METHOD_NOT_DEFINED, ATTRIBUTE_NOT_DEFINED, \ - TYPE_NOT_DEFINED, TYPE_ALREADY_DEFINED, VARIABLE_NOT_DEFINED, TYPE_NOT_INHERITABLE, METHOD_PARAMS_NAMES_EQUALS -from typing import List - -class VariableInfo(IVariableInfo): - """ - Base VariableInfo - """ - - def __init__(self, name:str, variable_type:IType): - self._name = name - self._type = variable_type - - @property - def name(self)->str: - return self._name - - @property - def type(self)->IType: - return self._type - -class AttributeInfo(IAttributeInfo): - """ - Base AttributeInfo - """ - - def __init__(self, name:str, variable_type:IType): - self._name = name - self._type = variable_type - - @property - def name(self)->str: - return self._name - - @property - def type(self)->IType: - return self._type - -class MethodInfo(IMethodInfo): - """ - Base MethodInfo - """ - - def __init__(self, name:str, parameters:List[IVariableInfo], return_type:IType): - self._name = name - self._parameters = parameters - self._return_type = return_type - - @property - def name(self)->str: - return self._name - - @property - def parameters(self)->List[IVariableInfo]: - return self._parameters - - @property - def return_type(self)->IType: - return self._return_type - -class CoolType(IType): - """ - Base Type - """ - - ATTRIBUTE_INFO_TYPE = AttributeInfo - METHOD_INFO_TYPE = MethodInfo - - def __init__(self, name:str, father:IType, inheritable:bool=True): - self._name = name - self._father = father - self._inheritable = inheritable - self._attributes = [] - self._methods = [] - - - @property - def name(self)->str: - return self._name - - @property - def father(self)->IType: - return self._father - - @property - def is_inheritable(self)->bool: - return self._inheritable - - def add_attribute(self, name:str, attr_type:IType)->IAttributeInfo: - if self.is_attribute_defined(name): - raise SemanticError(ATTRIBUTE_ALREADY_DEFINED(name, self.name)) - attribute = self.ATTRIBUTE_INFO_TYPE(name, attr_type) - self._attributes.append(attribute) - return attribute - - def is_attribute_defined(self, name:str)->bool: - try: - attr = self.get_attribute(name) - return bool(attr) - except SemanticError: - return False - - def add_method(self, name:str, params:List[IVariableInfo], return_type:IType)->IMethodInfo: - names = { x.name for x in params } - - if len(names) != len(params): - raise SemanticError(METHOD_PARAMS_NAMES_EQUALS(name)) - - try: - # Checking if a local method already exist with the given signature - local_method = next(x for x in self._methods if x.name == name and len(x.parameters) == len(params)) - raise SemanticError(METHOD_ALREADY_DEFINED(name, len(params), self.name)) - except StopIteration: - # Checking if the method already exist in parents types and can be redefined - if self.father: - try: - parent_method = self.father.get_method(name, len(params)) - if not all([parent_method.return_type == return_type, *[x.type == y.type for x,y in zip(params, parent_method.parameters)]]): - # The params types or the return type of the new and the old method doesn't match - raise SemanticError(METHOD_REDEFINITION_INVALID(name, parent_method.parameters + [parent_method.return_type])) - except SemanticError: - # No method exist - pass - - # The method can be defined in this type - method = self.METHOD_INFO_TYPE(name, params, return_type) - self._methods.append(method) - return method - - def is_method_defined(self, name:str, params_count:int)->bool: - try: - method = self.get_method(name, params_count) - return bool(method) - except SemanticError: - return False - - def get_method(self, name:str, params_count:int)->IMethodInfo: - try: - method = next(x for x in self.methods if x.name == name and len(x.parameters) == params_count) - return method - except StopIteration: - raise SemanticError(METHOD_NOT_DEFINED(name, params_count, self.name)) - - def get_attribute(self, name:str)->IAttributeInfo: - try: - attr = next(x for x in self.attributes if x.name == name) - return attr - except StopIteration: - raise SemanticError(ATTRIBUTE_NOT_DEFINED(name, self.name)) - - @property - def methods(self)->List[IMethodInfo]: - father_methods = [] - if self.father: - father_methods = self.father.methods - return self._methods + father_methods - - @property - def attributes(self)->List[IAttributeInfo]: - father_attrs = [] - if self.father: - father_attrs = self.father.attributes - return self._attributes + father_attrs - -class Context(IContext): - """ - Base Context - """ - - TYPE_TYPE = CoolType - - def __init__(self, basic_types:List[IType]=[]): - self._types = basic_types - - def add_type(self, name:str, father:IType)->IType: - if not father.is_inheritable: - raise SemanticError(TYPE_NOT_INHERITABLE(father.name)) - try: - typex = self.get_type(name) - except SemanticError: - typex = self.TYPE_TYPE(name, father) - self._types.append(typex) - return typex - raise SemanticError(TYPE_ALREADY_DEFINED(name)) - - def get_type(self, name:str)->IType: - try: - typex = next(x for x in self._types if x.name == name) - return typex - except StopIteration: - raise SemanticError(TYPE_NOT_DEFINED(name)) - - @property - def types(self)->List[IType]: - return self._types - - -class Scope(IScope): - """ - Base Scope - """ - - # Implementation type for IVariableInfo used by this scope - VARIABLE_INFO_TYPE = VariableInfo - - def __init__(self, father=None, children:List[IScope]=[], locals:List[IVariableInfo]=[]): - self._locals = locals - self._father = father - self.children = children - self.index = 0 if father is None else len(father) - - def __len__(self): - return len(self._locals) - - @property - def father(self)->IScope: - return self._father - - def find_variable(self, name: str, index=None)->IVariableInfo: - index = index if not index is None else len(self.local_variables) - for var in self.local_variables[:index]: - if var.name == name: - return var - if self.father: - return self.father.find_variable(name, self.index) - raise SemanticError(VARIABLE_NOT_DEFINED(name)) - - @property - def local_variables(self)->List[IVariableInfo]: - return self._locals - - def is_local_variable_defined(self, name: str)->bool: - return len([v for v in self.local_variables if v.name == name]) == 1 - - def add_variable(self, name: str, variable_type: IType)->IVariableInfo: - if self.is_local_variable_defined(name): - raise SemanticError(VARIABLE_ALREADY_DEFINED(name)) - variable = self.VARIABLE_INFO_TYPE(name, variable_type) - self._locals.append(variable) - return variable - - def create_child(self)->IScope: - child = type(self)(self) # In case of been inherited the Scope class the child created will have the same type of its father - self.children.append(child) +from cool_cmp.semantic.interface import IContext, IVariableInfo, IAttributeInfo, IMethodInfo, IType, IScope +from cool_cmp.semantic.errors import SemanticError, VARIABLE_ALREADY_DEFINED, ATTRIBUTE_ALREADY_DEFINED, \ + METHOD_ALREADY_DEFINED, METHOD_REDEFINITION_INVALID, METHOD_NOT_DEFINED, ATTRIBUTE_NOT_DEFINED, \ + TYPE_NOT_DEFINED, TYPE_ALREADY_DEFINED, VARIABLE_NOT_DEFINED, TYPE_NOT_INHERITABLE, METHOD_PARAMS_NAMES_EQUALS +from typing import List + +class VariableInfo(IVariableInfo): + """ + Base VariableInfo + """ + + def __init__(self, name:str, variable_type:IType): + self._name = name + self._type = variable_type + + @property + def name(self)->str: + return self._name + + @property + def type(self)->IType: + return self._type + +class AttributeInfo(IAttributeInfo): + """ + Base AttributeInfo + """ + + def __init__(self, name:str, variable_type:IType): + self._name = name + self._type = variable_type + + @property + def name(self)->str: + return self._name + + @property + def type(self)->IType: + return self._type + +class MethodInfo(IMethodInfo): + """ + Base MethodInfo + """ + + def __init__(self, name:str, parameters:List[IVariableInfo], return_type:IType): + self._name = name + self._parameters = parameters + self._return_type = return_type + + @property + def name(self)->str: + return self._name + + @property + def parameters(self)->List[IVariableInfo]: + return self._parameters + + @property + def return_type(self)->IType: + return self._return_type + +class CoolType(IType): + """ + Base Type + """ + + ATTRIBUTE_INFO_TYPE = AttributeInfo + METHOD_INFO_TYPE = MethodInfo + + def __init__(self, name:str, father:IType, inheritable:bool=True): + self._name = name + self._father = father + self._inheritable = inheritable + self._attributes = [] + self._methods = [] + + + @property + def name(self)->str: + return self._name + + @property + def father(self)->IType: + return self._father + + @property + def is_inheritable(self)->bool: + return self._inheritable + + def add_attribute(self, name:str, attr_type:IType)->IAttributeInfo: + if self.is_attribute_defined(name): + raise SemanticError(ATTRIBUTE_ALREADY_DEFINED(name, self.name)) + attribute = self.ATTRIBUTE_INFO_TYPE(name, attr_type) + self._attributes.append(attribute) + return attribute + + def is_attribute_defined(self, name:str)->bool: + try: + attr = self.get_attribute(name) + return bool(attr) + except SemanticError: + return False + + def add_method(self, name:str, params:List[IVariableInfo], return_type:IType)->IMethodInfo: + names = { x.name for x in params } + + if len(names) != len(params): + raise SemanticError(METHOD_PARAMS_NAMES_EQUALS(name)) + + try: + # Checking if a local method already exist with the given signature + local_method = next(x for x in self._methods if x.name == name and len(x.parameters) == len(params)) + raise SemanticError(METHOD_ALREADY_DEFINED(name, len(params), self.name)) + except StopIteration: + # Checking if the method already exist in parents types and can be redefined + if self.father: + try: + parent_method = self.father.get_method(name, len(params)) + if not all([parent_method.return_type == return_type, *[x.type == y.type for x,y in zip(params, parent_method.parameters)]]): + # The params types or the return type of the new and the old method doesn't match + raise SemanticError(METHOD_REDEFINITION_INVALID(name, parent_method.parameters + [parent_method.return_type])) + except SemanticError: + # No method exist + pass + + # The method can be defined in this type + method = self.METHOD_INFO_TYPE(name, params, return_type) + self._methods.append(method) + return method + + def is_method_defined(self, name:str, params_count:int)->bool: + try: + method = self.get_method(name, params_count) + return bool(method) + except SemanticError: + return False + + def get_method(self, name:str, params_count:int)->IMethodInfo: + try: + method = next(x for x in self.methods if x.name == name and len(x.parameters) == params_count) + return method + except StopIteration: + raise SemanticError(METHOD_NOT_DEFINED(name, params_count, self.name)) + + def get_attribute(self, name:str)->IAttributeInfo: + try: + attr = next(x for x in self.attributes if x.name == name) + return attr + except StopIteration: + raise SemanticError(ATTRIBUTE_NOT_DEFINED(name, self.name)) + + @property + def methods(self)->List[IMethodInfo]: + father_methods = [] + if self.father: + father_methods = self.father.methods + return self._methods + father_methods + + @property + def attributes(self)->List[IAttributeInfo]: + father_attrs = [] + if self.father: + father_attrs = self.father.attributes + return self._attributes + father_attrs + +class Context(IContext): + """ + Base Context + """ + + TYPE_TYPE = CoolType + + def __init__(self, basic_types:List[IType]=[]): + self._types = basic_types + + def add_type(self, name:str, father:IType)->IType: + if not father.is_inheritable: + raise SemanticError(TYPE_NOT_INHERITABLE(father.name)) + try: + typex = self.get_type(name) + except SemanticError: + typex = self.TYPE_TYPE(name, father) + self._types.append(typex) + return typex + raise SemanticError(TYPE_ALREADY_DEFINED(name)) + + def get_type(self, name:str)->IType: + try: + typex = next(x for x in self._types if x.name == name) + return typex + except StopIteration: + raise SemanticError(TYPE_NOT_DEFINED(name)) + + @property + def types(self)->List[IType]: + return self._types + + +class Scope(IScope): + """ + Base Scope + """ + + # Implementation type for IVariableInfo used by this scope + VARIABLE_INFO_TYPE = VariableInfo + + def __init__(self, father=None, children:List[IScope]=[], locals:List[IVariableInfo]=[]): + self._locals = locals + self._father = father + self.children = children + self.index = 0 if father is None else len(father) + + def __len__(self): + return len(self._locals) + + @property + def father(self)->IScope: + return self._father + + def find_variable(self, name: str, index=None)->IVariableInfo: + index = index if not index is None else len(self.local_variables) + for var in self.local_variables[:index]: + if var.name == name: + return var + if self.father: + return self.father.find_variable(name, self.index) + raise SemanticError(VARIABLE_NOT_DEFINED(name)) + + @property + def local_variables(self)->List[IVariableInfo]: + return self._locals + + def is_local_variable_defined(self, name: str)->bool: + return len([v for v in self.local_variables if v.name == name]) == 1 + + def add_variable(self, name: str, variable_type: IType)->IVariableInfo: + if self.is_local_variable_defined(name): + raise SemanticError(VARIABLE_ALREADY_DEFINED(name)) + variable = self.VARIABLE_INFO_TYPE(name, variable_type) + self._locals.append(variable) + return variable + + def create_child(self)->IScope: + child = type(self)(self) # In case of been inherited the Scope class the child created will have the same type of its father + self.children.append(child) return child \ No newline at end of file diff --git a/src/cool_cmp/semantic/tests/type_test.py b/src/cool_cmp/semantic/tests/type_test.py index 02e459423..5380b1d61 100644 --- a/src/cool_cmp/semantic/tests/type_test.py +++ b/src/cool_cmp/semantic/tests/type_test.py @@ -1,44 +1,44 @@ -from cool_cmp.semantic.implementations import CoolType, SemanticError, VariableInfo -import pytest - -# To execute the test $ pytest src/cool_cmp/semantic/tests/ - -def test_type_attribute(): - """ - Attribute Redefinition in same type - """ - obj = CoolType("Object", None) - attr = obj.add_attribute("a", obj) - with pytest.raises(SemanticError): - obj.add_attribute("a", obj) - - -def test_type_attribute_2(): - """ - Attribute Redefinition in child type - """ - obj = CoolType("Object", None) - attr = obj.add_attribute("a", obj) - obj2 = CoolType("Object2", obj) - with pytest.raises(SemanticError): - obj2.add_attribute("a", obj2) - - -def test_type_method(): - """ - Method Definition with same signature - """ - obj = CoolType("Object", None) - attr = obj.add_method("a", [VariableInfo("a", obj)], obj) - with pytest.raises(SemanticError): - obj.add_method("a", [VariableInfo("b", obj)], obj) - - -def test_type_method_2(): - """ - Method Definition with same params names - """ - obj = CoolType("Object", None) - with pytest.raises(SemanticError): - attr = obj.add_method("a", [VariableInfo("a", obj), VariableInfo("a", obj)], obj) - +from cool_cmp.semantic.implementations import CoolType, SemanticError, VariableInfo +import pytest + +# To execute the test $ pytest src/cool_cmp/semantic/tests/ + +def test_type_attribute(): + """ + Attribute Redefinition in same type + """ + obj = CoolType("Object", None) + attr = obj.add_attribute("a", obj) + with pytest.raises(SemanticError): + obj.add_attribute("a", obj) + + +def test_type_attribute_2(): + """ + Attribute Redefinition in child type + """ + obj = CoolType("Object", None) + attr = obj.add_attribute("a", obj) + obj2 = CoolType("Object2", obj) + with pytest.raises(SemanticError): + obj2.add_attribute("a", obj2) + + +def test_type_method(): + """ + Method Definition with same signature + """ + obj = CoolType("Object", None) + attr = obj.add_method("a", [VariableInfo("a", obj)], obj) + with pytest.raises(SemanticError): + obj.add_method("a", [VariableInfo("b", obj)], obj) + + +def test_type_method_2(): + """ + Method Definition with same params names + """ + obj = CoolType("Object", None) + with pytest.raises(SemanticError): + attr = obj.add_method("a", [VariableInfo("a", obj), VariableInfo("a", obj)], obj) + diff --git a/src/cool_cmp/semantic/types.py b/src/cool_cmp/semantic/types.py index 46015522a..65acd7f7e 100644 --- a/src/cool_cmp/semantic/types.py +++ b/src/cool_cmp/semantic/types.py @@ -1,45 +1,45 @@ -from cool_cmp.semantic.interface import IContext, IVariableInfo, IAttributeInfo, IMethodInfo, IType, IScope -from cool_cmp.semantic.implementations import CoolType -from typing import List - -class ObjectType(CoolType): - """ - Cool Object Type - """ - - def __init__(self): - super().__init__("Object", None) - -class IntType(CoolType): - """ - Cool Int Type - """ - - def __init__(self, father:ObjectType = ObjectType()): - super().__init__("Int", father, False) - -class BoolType(CoolType): - """ - Cool Bool Type - """ - - def __init__(self, father:ObjectType = ObjectType()): - super().__init__("Bool", father, False) - -class StringType(CoolType): - """ - Cool String Type - """ - - def __init__(self, father:ObjectType = ObjectType()): - super().__init__("String", father, False) - -class IOType(CoolType): - """ - Cool IO Type - """ - - def __init__(self, father:ObjectType = ObjectType()): - super().__init__("IO", father) - +from cool_cmp.semantic.interface import IContext, IVariableInfo, IAttributeInfo, IMethodInfo, IType, IScope +from cool_cmp.semantic.implementations import CoolType +from typing import List + +class ObjectType(CoolType): + """ + Cool Object Type + """ + + def __init__(self): + super().__init__("Object", None) + +class IntType(CoolType): + """ + Cool Int Type + """ + + def __init__(self, father:ObjectType = ObjectType()): + super().__init__("Int", father, False) + +class BoolType(CoolType): + """ + Cool Bool Type + """ + + def __init__(self, father:ObjectType = ObjectType()): + super().__init__("Bool", father, False) + +class StringType(CoolType): + """ + Cool String Type + """ + + def __init__(self, father:ObjectType = ObjectType()): + super().__init__("String", father, False) + +class IOType(CoolType): + """ + Cool IO Type + """ + + def __init__(self, father:ObjectType = ObjectType()): + super().__init__("IO", father) + \ No newline at end of file diff --git a/src/cool_cmp/semantic/visitors/type_collector.py b/src/cool_cmp/semantic/visitors/type_collector.py index 4a0f3b678..633403320 100644 --- a/src/cool_cmp/semantic/visitors/type_collector.py +++ b/src/cool_cmp/semantic/visitors/type_collector.py @@ -1,121 +1,121 @@ -import cool_cmp.shared.visitor as visitor -from cool_cmp.shared.ast import BaseAST -from cool_cmp.semantic.interface import IContext, ISemantic -from cool_cmp.shared.errors import ErrorTracker, IErrorTraceable, CoolError -from cool_cmp.shared.ast.cool import * -from cool_cmp.semantic.implementations import Context, CoolType -from cool_cmp.semantic.types import * -from cool_cmp.semantic.errors import SemanticError, TYPE_ALREADY_DEFINED, TYPE_NOT_DEFINED, \ - TYPE_CYCLIC_DEPENDENCY - -class TypeCollectorVisitor(IErrorTraceable): - """ - Collects the types by saving them to an IContext - """ - - def __init__(self): - self.errors = ErrorTracker() - self.context = Context() - - def add_semantic_error(self, error:SemanticError, line:int, pos:int): - error.set_position(line, pos) - self.add_error(error) - - @visitor.on('node') - def visit(self, node): - pass - - @visitor.when(ProgramNode) - def visit(self, node:ProgramNode): - - if not self.context: - obj = ObjectType() - int_ = IntType(obj) - bool_ = BoolType(obj) - string = StringType(obj) - io = IOType(obj) - - self.context = Context([ - obj, - int_, - bool_, - string, - io, - ]) - - default_names = [x.name for x in self.context.types] - type_names = [x.name for x in default_names] - - for class_decl in node.declarations: # Collecting defined classes - if class_decl.id in type_names: - error = SemanticError(TYPE_ALREADY_DEFINED(class_decl.id)) - self.add_semantic_error(error, class_decl.row, class_decl.column) - else: - type_names.append(class_decl.id) - - for class_decl in node.declarations: # Verify that parent classes exist - if class_decl.parent not in type_names: - error = SemanticError(TYPE_NOT_DEFINED(class_decl.parent)) - self.add_semantic_error(error, class_decl.row, class_decl.column) # TODO row and column should point to 'parent' token position - - - # Search for cyclic dependencies - import cool_cmp.semantic.visitors.utils as ut - graph = ut.build_graph_list([(x.id, x.parent) for x in node.declarations]) - cycles, sort = ut.any_cycles(graph) - - for cycle in cycles: # Adding cyclic dependency errors - error = SemanticError(TYPE_CYCLIC_DEPENDENCY(cycle)) - self.add_error(error) - - for class_name in sort: # Defining types in topological order - if class_name in default_names: # Class already in context - continue - - class_decl = [x for x in node.declarations if x.id == class_name] - if len(class_decl) != 1: # Error already captured. Multiple definitions or no definition - continue - - class_decl = class_decl[0] - - # At this point shold work whitout the try except - try: - father = self.context.get_type(class_decl.parent) - except SemanticError as exc: - self.add_semantic_error(exc, class_decl.row, class_decl.column) - - try: - self.context.add_type(class_decl, father) - except SemanticError as exc: - self.add_semantic_error(exc, class_decl.row, class_decl.column) - - return self.context - - def add_error(self, error:CoolError): - self.errors.add_error(error) - - def get_errors(self)->List[CoolError]: - self.errors.get_errors() - -class CollectorService(ISemantic): - - - def __init__(self): - self._errors = ErrorTracker() - - @property - def name(self)->str: - return 'context' - - def __call__(self, ast:BaseAST) -> BaseAST: - collector = TypeCollectorVisitor() - context = collector.visit(ast.node) - for error in collector.get_errors(): - self.add_error(error) - return context - - def add_error(self, error:CoolError): - self._errors.add_error(error) - - def get_errors(self)->List[CoolError]: +import cool_cmp.shared.visitor as visitor +from cool_cmp.shared.ast import BaseAST +from cool_cmp.semantic.interface import IContext, ISemantic +from cool_cmp.shared.errors import ErrorTracker, IErrorTraceable, CoolError +from cool_cmp.shared.ast.cool import * +from cool_cmp.semantic.implementations import Context, CoolType +from cool_cmp.semantic.types import * +from cool_cmp.semantic.errors import SemanticError, TYPE_ALREADY_DEFINED, TYPE_NOT_DEFINED, \ + TYPE_CYCLIC_DEPENDENCY + +class TypeCollectorVisitor(IErrorTraceable): + """ + Collects the types by saving them to an IContext + """ + + def __init__(self): + self.errors = ErrorTracker() + self.context = Context() + + def add_semantic_error(self, error:SemanticError, line:int, pos:int): + error.set_position(line, pos) + self.add_error(error) + + @visitor.on('node') + def visit(self, node): + pass + + @visitor.when(ProgramNode) + def visit(self, node:ProgramNode): + + if not self.context: + obj = ObjectType() + int_ = IntType(obj) + bool_ = BoolType(obj) + string = StringType(obj) + io = IOType(obj) + + self.context = Context([ + obj, + int_, + bool_, + string, + io, + ]) + + default_names = [x.name for x in self.context.types] + type_names = [x.name for x in default_names] + + for class_decl in node.declarations: # Collecting defined classes + if class_decl.id in type_names: + error = SemanticError(TYPE_ALREADY_DEFINED(class_decl.id)) + self.add_semantic_error(error, class_decl.row, class_decl.column) + else: + type_names.append(class_decl.id) + + for class_decl in node.declarations: # Verify that parent classes exist + if class_decl.parent not in type_names: + error = SemanticError(TYPE_NOT_DEFINED(class_decl.parent)) + self.add_semantic_error(error, class_decl.row, class_decl.column) # TODO row and column should point to 'parent' token position + + + # Search for cyclic dependencies + import cool_cmp.semantic.visitors.utils as ut + graph = ut.build_graph_list([(x.id, x.parent) for x in node.declarations]) + cycles, sort = ut.any_cycles(graph) + + for cycle in cycles: # Adding cyclic dependency errors + error = SemanticError(TYPE_CYCLIC_DEPENDENCY(cycle)) + self.add_error(error) + + for class_name in sort: # Defining types in topological order + if class_name in default_names: # Class already in context + continue + + class_decl = [x for x in node.declarations if x.id == class_name] + if len(class_decl) != 1: # Error already captured. Multiple definitions or no definition + continue + + class_decl = class_decl[0] + + # At this point shold work whitout the try except + try: + father = self.context.get_type(class_decl.parent) + except SemanticError as exc: + self.add_semantic_error(exc, class_decl.row, class_decl.column) + + try: + self.context.add_type(class_decl, father) + except SemanticError as exc: + self.add_semantic_error(exc, class_decl.row, class_decl.column) + + return self.context + + def add_error(self, error:CoolError): + self.errors.add_error(error) + + def get_errors(self)->List[CoolError]: + self.errors.get_errors() + +class CollectorService(ISemantic): + + + def __init__(self): + self._errors = ErrorTracker() + + @property + def name(self)->str: + return 'context' + + def __call__(self, ast:BaseAST) -> BaseAST: + collector = TypeCollectorVisitor() + context = collector.visit(ast.node) + for error in collector.get_errors(): + self.add_error(error) + return context + + def add_error(self, error:CoolError): + self._errors.add_error(error) + + def get_errors(self)->List[CoolError]: return self._errors.get_errors() \ No newline at end of file diff --git a/src/cool_cmp/semantic/visitors/utils.py b/src/cool_cmp/semantic/visitors/utils.py index a65697292..0824a367a 100644 --- a/src/cool_cmp/semantic/visitors/utils.py +++ b/src/cool_cmp/semantic/visitors/utils.py @@ -1,47 +1,47 @@ -from typing import List, Tuple, Dict - -Graph = Dict[str,List[str]] - -def build_graph_list(type_list:List[Tuple[str,str]])->Graph: - graph = { x:[] for x,_ in type_list } - - for x,y in type_list: - graph[x].append(y) - - add_keys = [y for x,y in type_list if y not in graph] - - for key in add_keys: - graph[key] = [] - - return graph - -def any_cycles(graph:Graph)->Tuple[Tuple[str,str],List[str]]: - d,f,time,stack,cycles = { x:0 for x in graph },{ x:0 for x in graph },[1], [], [] - for x in graph: - if not d[x]: - d[x] = time[0]; time[0]+=1 - topological_order(graph,x,d,f,time,stack,cycles) - f[x] = time[0]; time[0]+=1 - stack.append(x) - stack.reverse() - return cycles, stack - - -def topological_order(graph, node, d, f, time, stack, cycles): - - for adj in graph[node]: - if not d[adj]: - d[adj] = time[0]; time[0]+=1 - topological_order(graph,adj,d,f,time,stack,cycles) - f[adj] = time[0]; time[0]+=1 - stack.append(adj) - elif not f[adj]: - cycles.append((node,adj)) - -gr = build_graph_list([('a','b'), ('b','c')]) # a->b->c -gr = build_graph_list([('b','a'), ('c','b')]) # a<-b<-c -gr = build_graph_list([('b','a'), ('b','b')]) # a<-b<-c - -cycles, sort = any_cycles(gr) - +from typing import List, Tuple, Dict + +Graph = Dict[str,List[str]] + +def build_graph_list(type_list:List[Tuple[str,str]])->Graph: + graph = { x:[] for x,_ in type_list } + + for x,y in type_list: + graph[x].append(y) + + add_keys = [y for x,y in type_list if y not in graph] + + for key in add_keys: + graph[key] = [] + + return graph + +def any_cycles(graph:Graph)->Tuple[Tuple[str,str],List[str]]: + d,f,time,stack,cycles = { x:0 for x in graph },{ x:0 for x in graph },[1], [], [] + for x in graph: + if not d[x]: + d[x] = time[0]; time[0]+=1 + topological_order(graph,x,d,f,time,stack,cycles) + f[x] = time[0]; time[0]+=1 + stack.append(x) + stack.reverse() + return cycles, stack + + +def topological_order(graph, node, d, f, time, stack, cycles): + + for adj in graph[node]: + if not d[adj]: + d[adj] = time[0]; time[0]+=1 + topological_order(graph,adj,d,f,time,stack,cycles) + f[adj] = time[0]; time[0]+=1 + stack.append(adj) + elif not f[adj]: + cycles.append((node,adj)) + +gr = build_graph_list([('a','b'), ('b','c')]) # a->b->c +gr = build_graph_list([('b','a'), ('c','b')]) # a<-b<-c +gr = build_graph_list([('b','a'), ('b','b')]) # a<-b<-c + +cycles, sort = any_cycles(gr) + print(gr,cycles,sort) \ No newline at end of file diff --git a/src/cool_cmp/shared/visitor.py b/src/cool_cmp/shared/visitor.py index 94946b9df..1853dcb97 100644 --- a/src/cool_cmp/shared/visitor.py +++ b/src/cool_cmp/shared/visitor.py @@ -1,80 +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) +# 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/coolc.sh b/src/coolc.sh index 223ee34ea..559e74309 100755 --- a/src/coolc.sh +++ b/src/coolc.sh @@ -1,16 +1,16 @@ -# Incluya aquí las instrucciones necesarias para ejecutar su compilador - -INPUT_FILE=$1 -OUTPUT_FILE=${INPUT_FILE:0: -2}mips -BASEDIR=$(dirname "$0") -PROGRAM=$BASEDIR/cool2/main.py - -# Si su compilador no lo hace ya, aquí puede imprimir la información de contacto -echo "WLD CoolCompiler v0.0" -echo "Copyright (c) 2019: Luis Ernesto, Damián, Luis Enrique" - -# Llamar al compilador -# echo "Compiling $INPUT_FILE into $OUTPUT_FILE" - -python3 $PROGRAM $INPUT_FILE $OUTPUT_FILE +# Incluya aquí las instrucciones necesarias para ejecutar su compilador + +INPUT_FILE=$1 +OUTPUT_FILE=${INPUT_FILE:0: -2}mips +BASEDIR=$(dirname "$0") +PROGRAM=$BASEDIR/cool2/main.py + +# Si su compilador no lo hace ya, aquí puede imprimir la información de contacto +echo "WLD CoolCompiler v0.0" +echo "Copyright (c) 2019: Luis Ernesto, Damián, Luis Enrique" + +# Llamar al compilador +# echo "Compiling $INPUT_FILE into $OUTPUT_FILE" + +python3 $PROGRAM $INPUT_FILE $OUTPUT_FILE exit $? \ No newline at end of file diff --git a/src/requirements.txt b/src/requirements.txt index 4b76ab6cb..5322b41fd 100644 --- a/src/requirements.txt +++ b/src/requirements.txt @@ -1,5 +1,5 @@ -nbformat==4.4.0 -plac==1.1.3 -streamlit==0.51.0 -pydot==1.4.1 -pytest==6.1.2 +nbformat==4.4.0 +plac==1.1.3 +streamlit==0.51.0 +pydot==1.4.1 +pytest==6.1.2 From 37d6f8139dd59f6b70440fa08194c0c5814af966 Mon Sep 17 00:00:00 2001 From: Luiso Date: Fri, 23 Apr 2021 20:05:12 -0400 Subject: [PATCH 015/143] Semantics fixes TODO: dispatch3.cl and below --- .vscode/launch.json | 2 +- src/makefile => makefile | 0 src/cool2/cool/error/errors.py | 29 +++-- src/cool2/cool/semantic/operations.py | 6 +- src/cool2/cool/semantic/type.py | 13 +- src/cool2/cool/visitors/visitors.py | 92 ++++++++----- src/coolc.sh | 30 ++--- src/testing.cl | 180 ++++++-------------------- tests/lexer_test.py | 4 +- 9 files changed, 146 insertions(+), 210 deletions(-) rename src/makefile => makefile (100%) diff --git a/.vscode/launch.json b/.vscode/launch.json index e8f224d78..df6605fb3 100644 --- a/.vscode/launch.json +++ b/.vscode/launch.json @@ -8,7 +8,7 @@ "name": "Python: Archivo actual", "type": "python", "request": "launch", - "program": "${file}", + "program": "src/cool2/main.py", "args": ["src/testing.cl", "src/testing.mips"], "console": "integratedTerminal" } diff --git a/src/makefile b/makefile similarity index 100% rename from src/makefile rename to makefile diff --git a/src/cool2/cool/error/errors.py b/src/cool2/cool/error/errors.py index fbad6e62b..3f806a500 100644 --- a/src/cool2/cool/error/errors.py +++ b/src/cool2/cool/error/errors.py @@ -1,17 +1,20 @@ STRING_TOO_LONG = "String too long. Max length is 1024 chars, have {0} chars" SYNTACTIC_ERROR = 'at or near "{0}"' - +REDEFINITION_BASIC_CLASS = 'Redefinition of basic class {0}.' WRONG_SIGNATURE = 'Method "{0}" already defined in "{1}" with a different signature.' SELF_IS_READONLY = 'Variable "self" is read-only.' ATTRIBUTE_NOT_DEFINED = 'Attribute "{0}" is not defined in class "{1}".' ATTRIBUTE_ALREADY_DEFINED = 'Attribute "{0}" is already defined in class "{1}".' LOCAL_ALREADY_DEFINED = 'Variable "{0}" is already defined in method "{1}".' INCOMPATIBLE_TYPES = 'Cannot convert "{0}" into "{1}".' -VARIABLE_NOT_DEFINED = 'Variable "{0}" is not defined in "{1}".' -INVALID_UNARY_OPERATION = 'Operation {0} is not defined with "{1}".' -INVALID_BINARY_OPERATION = 'Operation {0} is not defined between "{1}" and "{2}".' -NOT_BOOLEAN_CONDITION = 'A conditional must be a Bool type instead of {0} type.' +UNDEFINED_CLASS_CASE_BRANCH = 'Class {0} of case branch is undefined.' +ATTRIBUTE_INCOMPATIBLE_TYPES = 'Inferred type {2} of initialization of attribute {0} does not conform to declared type {1}.' +METHOD_INCOMPATIBLE_RETURN_TYPE = "Inferred return type {2} of method {0} does not conform to declared return type {1}." +VARIABLE_NOT_DEFINED = 'Undeclared identifier {0}.' +INVALID_UNARY_OPERATION = "Argument of '{0}' has type {1} instead of {2}." +INVALID_BINARY_OPERATION = 'non-{1} arguments: {1} {0} {2}' +NOT_BOOLEAN_CONDITION = "Predicate of 'if' does not have type Bool." CIRCULAR_DEPENDENCY = 'Circular Dependency with types "{0}" and "{1}".' VOID_TYPE_CONFORMS = "Void is not a valid type." NO_OPERATION_DEFINDED = "No operations defined with operator {0} and types {1}." @@ -19,19 +22,21 @@ CASE_NO_BRANCH_SELECTED = "No branch can be selected. Type {0} is not conformed by any of the branches." NO_BOOL_CONDITION = "The condition value type is not Bool." DISPATCH_VOID = "Can't dispatch a Void value." +DISPATCH_UNDEFINED_METHOD = "Dispatch to undefined method {0}." +DISPATCH_METHOD_WRONG_ARGS = "Method {0} called with wrong number of arguments." METHOD_NOT_DEFINED = "Method '{0}' is not defined{1} in '{2}' with {3} params." METHOD_ALREADY_DEFINED = "Method '{0}' with {1} params already defined in {2}" NO_ENTRY_POINT = "The program must define a 'main' method with no parameters in the 'Main' class" NO_MAIN_TYPE = "The program must define a 'Main' type" -CASE_TYPES_REPEATED = "Type {0} already present in case expression." +CASE_TYPES_REPEATED = "Duplicate branch {0} in case statement." ZERO_DIVISION = "Divison by 0 not defined." SUBSTR_OUT_RANGE = "Index out of range in substr, word:'{0}' substr begin:{1} substr length:{2}." ATTRIBUTE_CANT_INFER = "No attribute '{0}' in inferred types." METHOD_CANT_INFER = "No method '{0}' with {1} params in inferred types." TYPE_NOT_DEFINED = "Type '{0}' is not defined." -TYPE_ALREADY_DEFINED = "Type with the same name ({0}) already in context." +TYPE_ALREADY_DEFINED = "Classes may not be redefined" TYPE_CANT_INFER = "Cant infer type in given context." -TYPE_CANT_BE_INHERITED = "Type {0} cant be inherited" +TYPE_CANT_BE_INHERITED = "Class {0} cannot inherit class {1}. " NO_COMMON_TYPE = "No common types between {0} and {1}" READ_IS_NOT_INT = "Invalid type: {0} is not an Int" @@ -111,10 +116,16 @@ class SyntacticCoolError(PositionError): class SemanticError(PositionError): FORMAT = "({}, {}) - {}: {}" ERROR_TYPE = "SemanticError" - + +class NameCoolError(SemanticError): + ERROR_TYPE = "NameError" + class TypeCoolError(SemanticError): ERROR_TYPE = "TypeError" +class AttributeCoolError(SemanticError): + ERROR_TYPE = "AttributeError" + class InferError(SemanticError): ERROR_TYPE = "InferenceError" diff --git a/src/cool2/cool/semantic/operations.py b/src/cool2/cool/semantic/operations.py index 01bce0b2e..66bbc9ae1 100644 --- a/src/cool2/cool/semantic/operations.py +++ b/src/cool2/cool/semantic/operations.py @@ -88,7 +88,11 @@ def __str__(self): class OperationDict: def __init__(self, default:"(operator,*types)->(func_operator,return_type)"={}): self.operations = default - + + def get_valid_operators_of(self, operator): + valid_operators_types = [args for op,args in self.operations if op == operator] + return valid_operators_types + def get(self,key,default=None): operator, types = key types = list(types) diff --git a/src/cool2/cool/semantic/type.py b/src/cool2/cool/semantic/type.py index 662e5298a..b61f685aa 100644 --- a/src/cool2/cool/semantic/type.py +++ b/src/cool2/cool/semantic/type.py @@ -19,11 +19,14 @@ def add_special_method(self,func,method_name,method_args): f.node.body.type = old_type def set_parent(self,parent): - DeprecatedType.set_parent(self,parent) - if not parent.can_have_children: - self.parent = None - raise SemanticError(TYPE_CANT_BE_INHERITED, parent.name) - + try: + DeprecatedType.set_parent(self,parent) + if not parent.can_have_children: + self.parent = None + raise SemanticError(TYPE_CANT_BE_INHERITED, self.name, parent.name) + except DeprecatedSemanticError as er: + raise SemanticError(er.text) + def __hash__(self): return hash(self.name) diff --git a/src/cool2/cool/visitors/visitors.py b/src/cool2/cool/visitors/visitors.py index 7c7866617..aea3ffea2 100644 --- a/src/cool2/cool/visitors/visitors.py +++ b/src/cool2/cool/visitors/visitors.py @@ -360,21 +360,27 @@ def visit(self, node): }) for class_decl in node.declarations: - try: - self.context.create_type(class_decl.id) - except SemanticError as er: - self.add_semantic_error(er, class_decl.row, class_decl.column) + if class_decl.id in self.context.special_types: + self.add_semantic_error(SemanticError(REDEFINITION_BASIC_CLASS, class_decl.id), class_decl.row, class_decl.column) + else: + try: + self.context.create_type(class_decl.id) + except SemanticError as er: + self.add_semantic_error(er, class_decl.row, class_decl.column) for class_decl in node.declarations: - try: - curr_type = self.context.get_type(class_decl.id) - except SemanticError as er: - self.add_semantic_error(er, class_decl.row, class_decl.column) - if class_decl.parent: + if not class_decl.id in self.context.special_types: try: - curr_type.set_parent(self.context.get_type(class_decl.parent)) + curr_type = self.context.get_type(class_decl.id) except SemanticError as er: self.add_semantic_error(er, class_decl.row, class_decl.column) + if class_decl.parent: + try: + parent_type = self.context.get_type(class_decl.parent) + if not curr_type.parent: + curr_type.set_parent(parent_type) + except SemanticError as er: + self.add_semantic_error(er, class_decl.row, class_decl.column) cycles = ut.any_cycles(ut.build_graph_dict(self.context.types)) @@ -505,7 +511,7 @@ def visit(self, node, scope): self.visit(node.expr,scope) if not node.expr.type.conforms_to(node.type,self.current_type): - self.add_semantic_error(TypeCoolError(INCOMPATIBLE_TYPES, node.expr.type.name, node.type.name),node.row,node.column) + self.add_semantic_error(TypeCoolError(ATTRIBUTE_INCOMPATIBLE_TYPES, node.id, node.type.name, node.expr.type.name),node.row,node.column) @visitor.when(FuncDeclarationNode) def visit(self, node, scope): @@ -529,7 +535,7 @@ def visit(self, node, scope): self.visit(node.body,f_scope) if not isinstance(node.type,VoidType) and not node.body.type.conforms_to(node.type,self.current_type): - err = TypeCoolError(INCOMPATIBLE_TYPES, node.body.type.name,node.type.name) + err = TypeCoolError(METHOD_INCOMPATIBLE_RETURN_TYPE, node.id, node.type.name, node.body.type.name) self.add_semantic_error(err, node.row, node.column) @visitor.when(ParamNode) @@ -580,13 +586,11 @@ def visit(self, node, scope): self.visit(node.expr,scope) if not scope.is_defined(node.id): - er = SemanticError(VARIABLE_NOT_DEFINED, node.id,self.current_method.name) + er = NameCoolError(VARIABLE_NOT_DEFINED, node.id) self.add_semantic_error(er, node.row, node.column) - node.type = node.expr.type - else: - var_info = scope.find_variable(node.id) - node.type = var_info.type + node.type = node.expr.type + if not node.expr.type.conforms_to(node.type,self.current_type): er = TypeCoolError(INCOMPATIBLE_TYPES, node.expr.type.name, node.type.name) self.add_semantic_error(er, node.row, node.column) @@ -604,15 +608,22 @@ def visit(self, node, scope): if not node.obj.type.conforms_to(node.at,self.current_type): er = TypeCoolError(INCOMPATIBLE_TYPES, node.obj.type.name, node.at.name) self.add_semantic_error(er, node.row, node.column) - method = node.at.get_method(node.id,len(node.args),self.current_type) + dispatch_type = node.at else: - method = node.obj.type.get_method(node.id,len(node.args),self.current_type) - not_conform = [(x,y.type) for x,y in zip(method.param_types,node.args) if not y.type.conforms_to(x,self.current_type)] - for x,y in not_conform: - er = TypeCoolError(INCOMPATIBLE_TYPES, y.name, x.name) - self.add_semantic_error(er, node.row, node.column) - node.type = method.return_type if method.return_type.name != "SELF_TYPE" else node.obj.type - + dispatch_type = node.obj.type + try: + method = dispatch_type.get_method(node.id,len(node.args),self.current_type) + not_conform = [(x,y.type) for x,y in zip(method.param_types,node.args) if not y.type.conforms_to(x,self.current_type)] + for x,y in not_conform: + er = TypeCoolError(INCOMPATIBLE_TYPES, y.name, x.name) + self.add_semantic_error(er, node.row, node.column) + node.type = method.return_type if method.return_type.name != "SELF_TYPE" else node.obj.type + except SemanticError as er: + if not any(meth for meth, typex in dispatch_type.all_methods() if meth.name == node.id): + raise AttributeCoolError(DISPATCH_UNDEFINED_METHOD, node.id) + else: + raise SemanticError(DISPATCH_METHOD_WRONG_ARGS, node.id) + except SemanticError as er: node.type = ErrorType() self.add_semantic_error(er, node.row, node.column) @@ -633,8 +644,8 @@ def visit(self, node:ConditionalNode, scope): self.visit(node.then_expr,scope) self.visit(node.else_expr,scope) - if node.condition.type != self.context.get_type('Bool') and node.condition.type != ErrorType(): - er = TypeCoolError(NOT_BOOLEAN_CONDITION, node.condition.type.name) + if node.condition.type != self.context.get_type('Bool') and not node.condition.type is ErrorType: + er = TypeCoolError(NOT_BOOLEAN_CONDITION) self.add_semantic_error(er, node.row, node.column) try: node.type = node.get_return_type(self.current_type) @@ -669,7 +680,13 @@ def visit(self, node:CheckNode, scope): er = SemanticError(SELF_IS_READONLY) self.add_semantic_error(er, node.row, node.column) - node.type = self.context.get_type(node.type) + try: + node.type = self.context.get_type(node.type) + except SemanticError: + er = TypeCoolError(UNDEFINED_CLASS_CASE_BRANCH, node.type) + self.add_semantic_error(er, node.row, node.column) + node.type = ErrorType() + node.scope = scope.create_child() node.scope.define_variable(node.id,node.type) @@ -683,13 +700,16 @@ def visit(self, node:CaseNode, scope): self.visit(node.expr,scope) types = [] + param_types = [] for i,param in enumerate(node.params): self.visit(param,scope) # if not (node.expr.type.conforms_to(param.type,self.current_type) or param.type.conforms_to(node.expr.type,self.current_type)): # self.errors.append(f"Incompatible types {param.type.name} with {node.expr.type.name}" + f' Line:{node.row} Column:{node.column}') - if any(x for x in types if x[1] == param.type): - er = TypeCoolError(CASE_TYPES_REPEATED, param.expr.type.name) + if any(x for x in param_types if x == param.type): + er = SemanticError(CASE_TYPES_REPEATED, param.type.name) self.add_semantic_error(er, param.row, param.column) + else: + param_types.append(param.type) types.append((i,param.expr.type)) static_type = types[0][1] @@ -703,7 +723,15 @@ def visit(self, node, scope): if not self.operator.operation_defined(node,node.member.type): node.type = ErrorType() - er = TypeCoolError(INVALID_UNARY_OPERATION, self.operator.get_operator(node), node.member.type.name) + node_operator = self.operator.get_operator(node) + correct_types = self.operator.operations.get_valid_operators_of(node_operator) + unary_correct_types = [x for x in correct_types if len(x) == 1] + if len(unary_correct_types) == 0: + correct_type_name = "No Type" + else: + correct_type_name = " or ".join([x[0].name for x in unary_correct_types]) + + er = TypeCoolError(INVALID_UNARY_OPERATION, node_operator, node.member.type.name, correct_type_name) self.add_semantic_error(er, node.row, node.column) else: node.type = self.operator.type_of_operation(node,node.member.type) @@ -746,7 +774,7 @@ def visit(self, node, scope): node.type = var.type else: node.type = ErrorType() - er = SemanticError(VARIABLE_NOT_DEFINED, node.lex, self.current_method.name) + er = NameCoolError(VARIABLE_NOT_DEFINED, node.lex) self.add_semantic_error(er, node.row, node.column) @visitor.when(InstantiateNode) diff --git a/src/coolc.sh b/src/coolc.sh index 559e74309..223ee34ea 100755 --- a/src/coolc.sh +++ b/src/coolc.sh @@ -1,16 +1,16 @@ -# Incluya aquí las instrucciones necesarias para ejecutar su compilador - -INPUT_FILE=$1 -OUTPUT_FILE=${INPUT_FILE:0: -2}mips -BASEDIR=$(dirname "$0") -PROGRAM=$BASEDIR/cool2/main.py - -# Si su compilador no lo hace ya, aquí puede imprimir la información de contacto -echo "WLD CoolCompiler v0.0" -echo "Copyright (c) 2019: Luis Ernesto, Damián, Luis Enrique" - -# Llamar al compilador -# echo "Compiling $INPUT_FILE into $OUTPUT_FILE" - -python3 $PROGRAM $INPUT_FILE $OUTPUT_FILE +# Incluya aquí las instrucciones necesarias para ejecutar su compilador + +INPUT_FILE=$1 +OUTPUT_FILE=${INPUT_FILE:0: -2}mips +BASEDIR=$(dirname "$0") +PROGRAM=$BASEDIR/cool2/main.py + +# Si su compilador no lo hace ya, aquí puede imprimir la información de contacto +echo "WLD CoolCompiler v0.0" +echo "Copyright (c) 2019: Luis Ernesto, Damián, Luis Enrique" + +# Llamar al compilador +# echo "Compiling $INPUT_FILE into $OUTPUT_FILE" + +python3 $PROGRAM $INPUT_FILE $OUTPUT_FILE exit $? \ No newline at end of file diff --git a/src/testing.cl b/src/testing.cl index 511873774..ecb1535db 100644 --- a/src/testing.cl +++ b/src/testing.cl @@ -1,144 +1,36 @@ -class Main inherits IO { - number: Int <- 5; - - main () : Object { - testing_fibonacci(number) - }; - - testing_fibonacci(n: Int) : IO {{ - out_string("Iterative Fibonacci : "); - out_int(iterative_fibonacci(5)); - out_string("\n"); - - out_string("Recursive Fibonacci : "); - out_int(recursive_fibonacci(5)); - out_string("\n"); - }}; - - recursive_fibonacci (n: AUTO_TYPE) : AUTO_TYPE { - if n <= 2 then 1 else recursive_fibonacci(n - 1) + recursive_fibonacci(n - 2) fi - }; - - iterative_fibonacci(n: AUTO_TYPE) : AUTO_TYPE { - let i: Int <- 2, n1: Int <- 1, n2: Int <- 1, temp: Int in { - while i < n loop - let temp: Int <- n2 in { - n2 <- n2 + n1; - n1 <- temp; - i <- i + 1; - } - pool; - n2; - } - }; -}; - -(* Integers, Identifiers, and Special Notation *) -(* 0007 123 +1 -1 +90 -09 +11113 -4r *a *self* c++ -5! = 120, 2 + 2 = 5 or E = mc2; p + 1 @ p = 1: for x in range(len(b)) -new / <- <<==> {( Int: Objet, Bool; String.string SELF_TYPE isvoid }) -class Class if then else fi testing Testing ~007agent_bond james_007B0N3SS___ -loop pool while tRuE or noT faLsE let in case of ESAC *) -(* -#3 INT_CONST 0007 -#3 INT_CONST 123 -#3 '+' -#3 INT_CONST 1 -#3 '-' -#3 INT_CONST 1 -#3 '+' -#3 INT_CONST 90 -#3 '-' -#3 INT_CONST 09 -#3 '+' -#3 INT_CONST 11113 -#3 '-' -#3 INT_CONST 4 -#3 OBJECTID r -#3 '*' -#3 OBJECTID a -#3 '*' -#3 OBJECTID self -#3 '*' -#3 OBJECTID c -#3 '+' -#3 '+' -#4 INT_CONST 5 -#4 ERROR "!" -#4 '=' -#4 INT_CONST 120 -#4 ',' -#4 INT_CONST 2 -#4 '+' -#4 INT_CONST 2 -#4 '=' -#4 INT_CONST 5 -#4 OBJECTID or -#4 TYPEID E -#4 '=' -#4 OBJECTID mc2 -#4 ';' -#4 OBJECTID p -#4 '+' -#4 INT_CONST 1 -#4 '@' -#4 OBJECTID p -#4 '=' -#4 INT_CONST 1 -#4 ':' -#4 OBJECTID for -#4 OBJECTID x -#4 IN -#4 OBJECTID range -#4 '(' -#4 OBJECTID len -#4 '(' -#4 OBJECTID b -#4 ')' -#4 ')' -#5 NEW -#5 '/' -#5 ASSIGN -#5 '<' -#5 LE -#5 DARROW -#5 '{' -#5 '(' -#5 TYPEID Int -#5 ':' -#5 TYPEID Objet -#5 ',' -#5 TYPEID Bool -#5 ';' -#5 TYPEID String -#5 '.' -#5 OBJECTID string -#5 TYPEID SELF_TYPE -#5 ISVOID -#5 '}' -#5 ')' -#6 CLASS -#6 CLASS -#6 IF -#6 THEN -#6 ELSE -#6 FI -#6 OBJECTID testing -#6 TYPEID Testing -#6 '~' -#6 INT_CONST 007 -#6 OBJECTID agent_bond -#6 OBJECTID james_007B0N3SS___ -#7 LOOP -#7 POOL -#7 WHILE -#7 BOOL_CONST true -#7 OBJECTID or -#7 NOT -#7 BOOL_CONST false -#7 LET -#7 IN -#7 CASE -#7 OF -#7 ESAC -*) \ No newline at end of file +(* +e0 .f(e1, . . . , en ) +Assume e0 has static type A. +Class A must have a method f +the static type of the ith actual parameter must conform to the declared type of the ith formal parameter. +*) + +class A inherits IO { + f(x: Int, y: Int): Int { x + y }; + g(x: Int): Int { x + x }; +}; +class B inherits A { + f(a: Int, b: Int): Int { a - b }; +}; +class C inherits B { + ident(m: Int): Int { m }; + f(m: Int, n: Int): Int { m * n }; +}; +class D inherits B { + ident(v: String): IO { new IO.out_string(v) }; + f(v: Int, w: Int): Int { v / w }; + g(v: Int): Int { v + v + v }; + + back(s: String): B { { + out_string(s); + self; + } }; + + alphabet(a: A, b: B, c: C): D { self }; +}; + +class Main inherits IO { + main(): IO { out_string("Hello World!")}; + + test: B <- new D.alphabet(new D, new D, new D.back("Hello ")).back("World!"); +}; \ No newline at end of file diff --git a/tests/lexer_test.py b/tests/lexer_test.py index 0f42ea4ab..a21fd880a 100644 --- a/tests/lexer_test.py +++ b/tests/lexer_test.py @@ -10,6 +10,4 @@ @pytest.mark.run(order=1) @pytest.mark.parametrize("cool_file", tests) def test_lexer_errors(compiler_path, cool_file): - compare_errors(compiler_path, tests_dir + cool_file, tests_dir + cool_file[:-3] + '_error.txt') - -test_lexer_errors("/media/luiso/Data/Universidad/Cuarto/Compilación/cool-compiler-2021/src/coolc.sh", tests[1]) \ No newline at end of file + compare_errors(compiler_path, tests_dir + cool_file, tests_dir + cool_file[:-3] + '_error.txt') \ No newline at end of file From 5c109f16547ffd3390ff93db30b0ee734affd975 Mon Sep 17 00:00:00 2001 From: Luiso Date: Fri, 30 Apr 2021 21:45:35 -0400 Subject: [PATCH 016/143] Semantics fixs added TODO: Fix cyclic attribute/method fetch --- src/cool2/cmp/semantic.py | 4 +-- src/cool2/cool/error/errors.py | 14 ++++++++--- src/cool2/cool/semantic/type.py | 15 ++++++----- src/cool2/cool/visitors/visitors.py | 33 ++++++++++++++++-------- src/cool2/lib/lang/language.py | 3 ++- src/testing.cl | 39 ++++++++--------------------- 6 files changed, 57 insertions(+), 51 deletions(-) diff --git a/src/cool2/cmp/semantic.py b/src/cool2/cmp/semantic.py index 10341b3bc..f984b6a52 100644 --- a/src/cool2/cmp/semantic.py +++ b/src/cool2/cmp/semantic.py @@ -87,13 +87,13 @@ def define_method(self, name:str, param_names:list, param_types:list, return_typ return method def all_attributes(self, clean=True): - plain = OrderedDict() if self.parent is None else self.parent.all_attributes(False) + plain = OrderedDict() if self.parent is None or self.parent == self else self.parent.all_attributes(False) for attr in self.attributes: plain[attr.name] = (attr, self) return plain.values() if clean else plain def all_methods(self, clean=True): - plain = OrderedDict() if self.parent is None else self.parent.all_methods(False) + plain = OrderedDict() if self.parent is None or self.parent == self else self.parent.all_methods(False) for method in self.methods: plain[method.name] = (method, self) return plain.values() if clean else plain diff --git a/src/cool2/cool/error/errors.py b/src/cool2/cool/error/errors.py index 3f806a500..b7ca2b070 100644 --- a/src/cool2/cool/error/errors.py +++ b/src/cool2/cool/error/errors.py @@ -5,7 +5,8 @@ WRONG_SIGNATURE = 'Method "{0}" already defined in "{1}" with a different signature.' SELF_IS_READONLY = 'Variable "self" is read-only.' ATTRIBUTE_NOT_DEFINED = 'Attribute "{0}" is not defined in class "{1}".' -ATTRIBUTE_ALREADY_DEFINED = 'Attribute "{0}" is already defined in class "{1}".' +ATTRIBUTE_ALREADY_DEFINED = 'Attribute {0} is multiply defined in class.' +ATTRIBUTE_ALREADY_DEFINED_IN_PARENT = 'Attribute {0} is an attribute of an inherited class.' LOCAL_ALREADY_DEFINED = 'Variable "{0}" is already defined in method "{1}".' INCOMPATIBLE_TYPES = 'Cannot convert "{0}" into "{1}".' UNDEFINED_CLASS_CASE_BRANCH = 'Class {0} of case branch is undefined.' @@ -15,7 +16,7 @@ INVALID_UNARY_OPERATION = "Argument of '{0}' has type {1} instead of {2}." INVALID_BINARY_OPERATION = 'non-{1} arguments: {1} {0} {2}' NOT_BOOLEAN_CONDITION = "Predicate of 'if' does not have type Bool." -CIRCULAR_DEPENDENCY = 'Circular Dependency with types "{0}" and "{1}".' +CIRCULAR_DEPENDENCY = 'Class {0}, or an ancestor of {0}, is involved in an inheritance cycle.' VOID_TYPE_CONFORMS = "Void is not a valid type." NO_OPERATION_DEFINDED = "No operations defined with operator {0} and types {1}." MULTIPLE_OPERATION_DEFINED = "Multiple operations defined with operator {0} and types {1}." @@ -24,10 +25,14 @@ DISPATCH_VOID = "Can't dispatch a Void value." DISPATCH_UNDEFINED_METHOD = "Dispatch to undefined method {0}." DISPATCH_METHOD_WRONG_ARGS = "Method {0} called with wrong number of arguments." +STATIC_DISPATCH_INCOMPATIBLE_TYPES = "Expression type {0} does not conform to declared static dispatch type {1}." +INCOMPATIBLE_PARAMS_TYPES = "In call of method {0}, type {1} of parameter {2} does not conform to declared type {3}." METHOD_NOT_DEFINED = "Method '{0}' is not defined{1} in '{2}' with {3} params." -METHOD_ALREADY_DEFINED = "Method '{0}' with {1} params already defined in {2}" +METHOD_ALREADY_DEFINED = "Method {0} is multiply defined." +ATTRIBUTE_TYPE_UNDEFINED = "Class {0} of attribute {1} is undefined." NO_ENTRY_POINT = "The program must define a 'main' method with no parameters in the 'Main' class" NO_MAIN_TYPE = "The program must define a 'Main' type" +INVALID_EQUAL_BASIC_TYPE_OPERATION = "Illegal comparison with a basic type." CASE_TYPES_REPEATED = "Duplicate branch {0} in case statement." ZERO_DIVISION = "Divison by 0 not defined." SUBSTR_OUT_RANGE = "Index out of range in substr, word:'{0}' substr begin:{1} substr length:{2}." @@ -36,7 +41,8 @@ TYPE_NOT_DEFINED = "Type '{0}' is not defined." TYPE_ALREADY_DEFINED = "Classes may not be redefined" TYPE_CANT_INFER = "Cant infer type in given context." -TYPE_CANT_BE_INHERITED = "Class {0} cannot inherit class {1}. " +TYPE_CANT_BE_INHERITED = "Class {0} cannot inherit class {1}." +UNDEFINED_INHERITED_TYPE = "Class {0} inherits from an undefined class {1}." NO_COMMON_TYPE = "No common types between {0} and {1}" READ_IS_NOT_INT = "Invalid type: {0} is not an Int" diff --git a/src/cool2/cool/semantic/type.py b/src/cool2/cool/semantic/type.py index b61f685aa..df2e8dc37 100644 --- a/src/cool2/cool/semantic/type.py +++ b/src/cool2/cool/semantic/type.py @@ -7,7 +7,7 @@ VOID_TYPE_CONFORMS, METHOD_NOT_DEFINED, METHOD_ALREADY_DEFINED, \ SUBSTR_OUT_RANGE, ATTRIBUTE_NOT_DEFINED, ATTRIBUTE_ALREADY_DEFINED, \ ATTRIBUTE_CANT_INFER, METHOD_CANT_INFER, TYPE_CANT_INFER, TYPE_CANT_BE_INHERITED, \ - NO_COMMON_TYPE, READ_IS_NOT_INT + NO_COMMON_TYPE, READ_IS_NOT_INT, ATTRIBUTE_ALREADY_DEFINED_IN_PARENT import cool.visitors.utils as ut class Type(DeprecatedType): @@ -38,7 +38,7 @@ def get_method(self,name:str,args:int,current_type = None, only_local = False): try: return next(method for method in self.methods if method.name == name and len(method.param_names)==args) except StopIteration: - if self.parent is None: + if self.parent is None or self.parent == self: raise SemanticError(METHOD_NOT_DEFINED,name, "", self.name, args) try: if not only_local: @@ -49,7 +49,7 @@ def get_method(self,name:str,args:int,current_type = None, only_local = False): def define_method(self, name:str, param_names:list, param_types:list, return_type): if (name,len(param_names)) in ((method.name,len(method.param_names)) for method in self.methods): - raise SemanticError(METHOD_ALREADY_DEFINED, name, len(param_names), self.name) + raise SemanticError(METHOD_ALREADY_DEFINED, name) method = Method(name, param_names, param_types, return_type) self.methods.append(method) return method @@ -65,7 +65,7 @@ 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: + if self.parent is None or self.parent == self: raise SemanticError(ATTRIBUTE_NOT_DEFINED, name, self.name) try: return self.parent.get_attribute(name) @@ -74,13 +74,16 @@ def get_attribute(self, name:str): def define_attribute(self, name:str, typex): try: - self.get_attribute(name) + attribute = self.get_attribute(name) except SemanticError: attribute = Attribute(name, typex) self.attributes.append(attribute) return attribute else: - raise SemanticError(ATTRIBUTE_ALREADY_DEFINED, name, self.name) + if attribute in self.attributes: + raise SemanticError(ATTRIBUTE_ALREADY_DEFINED, name) + else: + raise SemanticError(ATTRIBUTE_ALREADY_DEFINED_IN_PARENT, name) def conforms_to(self,other,current_type): self_type = self if not isinstance(self,SelfType) else current_type diff --git a/src/cool2/cool/visitors/visitors.py b/src/cool2/cool/visitors/visitors.py index aea3ffea2..09d214b17 100644 --- a/src/cool2/cool/visitors/visitors.py +++ b/src/cool2/cool/visitors/visitors.py @@ -364,7 +364,8 @@ def visit(self, node): self.add_semantic_error(SemanticError(REDEFINITION_BASIC_CLASS, class_decl.id), class_decl.row, class_decl.column) else: try: - self.context.create_type(class_decl.id) + typex = self.context.create_type(class_decl.id) + typex.class_node = class_decl except SemanticError as er: self.add_semantic_error(er, class_decl.row, class_decl.column) @@ -377,6 +378,10 @@ def visit(self, node): if class_decl.parent: try: parent_type = self.context.get_type(class_decl.parent) + except SemanticError as er: + er = SemanticError(UNDEFINED_INHERITED_TYPE, class_decl.id, class_decl.parent) + self.add_semantic_error(er, class_decl.row, class_decl.column) + try: if not curr_type.parent: curr_type.set_parent(parent_type) except SemanticError as er: @@ -385,8 +390,8 @@ def visit(self, node): cycles = ut.any_cycles(ut.build_graph_dict(self.context.types)) for type1,type2 in cycles: - error = SemanticError(CIRCULAR_DEPENDENCY, type1.name, type2.name) - self.errors.append(error) + error = SemanticError(CIRCULAR_DEPENDENCY, type1.name) + self.add_semantic_error(error, type1.class_node.row, type1.class_node.column) class TypeBuilder: def __init__(self, context, errors=[]): @@ -410,7 +415,6 @@ def visit(self, node): @visitor.when(ClassDeclarationNode) def visit(self, node): self.current_type = self.context.get_type(node.id) - self.current_type.class_node = node for child in node.features: self.visit(child) @@ -419,6 +423,7 @@ def visit(self, node): try: node.type = self.context.get_type(node.type) except SemanticError as er: + er = TypeCoolError(ATTRIBUTE_TYPE_UNDEFINED, node.type, node.id) node.type = ErrorType() self.add_semantic_error(er, node.row, node.column) try: @@ -606,16 +611,16 @@ def visit(self, node, scope): if node.at: node.at = self.context.get_type(node.at) if not node.obj.type.conforms_to(node.at,self.current_type): - er = TypeCoolError(INCOMPATIBLE_TYPES, node.obj.type.name, node.at.name) + er = TypeCoolError(STATIC_DISPATCH_INCOMPATIBLE_TYPES, node.obj.type.name, node.at.name) self.add_semantic_error(er, node.row, node.column) dispatch_type = node.at else: dispatch_type = node.obj.type try: method = dispatch_type.get_method(node.id,len(node.args),self.current_type) - not_conform = [(x,y.type) for x,y in zip(method.param_types,node.args) if not y.type.conforms_to(x,self.current_type)] - for x,y in not_conform: - er = TypeCoolError(INCOMPATIBLE_TYPES, y.name, x.name) + not_conform = [(x,y.type,name) for x,y,name in zip(method.param_types,node.args,method.param_names) if not y.type.conforms_to(x,self.current_type)] + for x,y,name in not_conform: + er = TypeCoolError(INCOMPATIBLE_PARAMS_TYPES, method.name, y.name, name, x.name) self.add_semantic_error(er, node.row, node.column) node.type = method.return_type if method.return_type.name != "SELF_TYPE" else node.obj.type except SemanticError as er: @@ -732,7 +737,8 @@ def visit(self, node, scope): correct_type_name = " or ".join([x[0].name for x in unary_correct_types]) er = TypeCoolError(INVALID_UNARY_OPERATION, node_operator, node.member.type.name, correct_type_name) - self.add_semantic_error(er, node.row, node.column) + if not isinstance(node.member.type, ErrorType): + self.add_semantic_error(er, node.row, node.column) else: node.type = self.operator.type_of_operation(node,node.member.type) @@ -743,7 +749,14 @@ def visit(self, node, scope): if not self.operator.operation_defined(node,node.left.type,node.right.type): node.type = ErrorType() - er = TypeCoolError(INVALID_BINARY_OPERATION,self.operator.get_operator(node), node.left.type.name,node.right.type.name) + operator = self.operator.get_operator(node) + if operator == self.operator.get_operator(EqualNode(None, None)): + er_message = INVALID_EQUAL_BASIC_TYPE_OPERATION + er_args = () + else: + er_message = INVALID_BINARY_OPERATION + er_args = (operator, node.left.type.name,node.right.type.name) + er = TypeCoolError(er_message, *er_args) self.add_semantic_error(er, node.row, node.column) else: node.type = self.operator.type_of_operation(node,node.left.type,node.right.type) diff --git a/src/cool2/lib/lang/language.py b/src/cool2/lib/lang/language.py index 0f421095a..80a28edc0 100644 --- a/src/cool2/lib/lang/language.py +++ b/src/cool2/lib/lang/language.py @@ -57,8 +57,9 @@ def get_tokens(self, text, errors): tokens = self.lexer(text) tokens = self._fix_tokens(tokens,errors) for tok in tokens: + tok.lex = (tok.lex[0], tok.lex[1] + 1, tok.lex[2] - len(tok.lex[0]) + 1) if tok.token_type == "UNKNOWN": - errors.append(f'({tok.lex[1]+1}, {tok.lex[2]+1}) - LexicographicError: ERROR "{tok.lex[0]}"') + errors.append(f'({tok.lex[1]}, {tok.lex[2]}) - LexicographicError: ERROR "{tok.lex[0]}"') tokens = [x for x in tokens if x.token_type != "UNKNOWN"] return tokens diff --git a/src/testing.cl b/src/testing.cl index ecb1535db..c985484ba 100644 --- a/src/testing.cl +++ b/src/testing.cl @@ -1,36 +1,19 @@ -(* -e0 .f(e1, . . . , en ) -Assume e0 has static type A. -Class A must have a method f -the static type of the ith actual parameter must conform to the declared type of the ith formal parameter. -*) +--The parent-child relation on classes defines a graph. This graph may not contain cycles. -class A inherits IO { - f(x: Int, y: Int): Int { x + y }; - g(x: Int): Int { x + x }; -}; -class B inherits A { - f(a: Int, b: Int): Int { a - b }; -}; -class C inherits B { - ident(m: Int): Int { m }; - f(m: Int, n: Int): Int { m * n }; +class Main inherits IO { + main(): IO { out_string("hi!") }; + + main: IO <- out_string("bye!"); }; -class D inherits B { - ident(v: String): IO { new IO.out_string(v) }; - f(v: Int, w: Int): Int { v / w }; - g(v: Int): Int { v + v + v }; - back(s: String): B { { - out_string(s); - self; - } }; +class A inherits B { + x: Int <- 3; - alphabet(a: A, b: B, c: C): D { self }; + x(): String { ":)" }; }; -class Main inherits IO { - main(): IO { out_string("Hello World!")}; +class B inherits A { + y: Int <- 2; - test: B <- new D.alphabet(new D, new D, new D.back("Hello ")).back("World!"); + div(a: Int, b: Int): Int { a / b}; }; \ No newline at end of file From 0e53cfd57cc0160391bbf388f016e58b6c1d28cd Mon Sep 17 00:00:00 2001 From: Luiso Date: Tue, 13 Jul 2021 11:01:50 -0400 Subject: [PATCH 017/143] Semantic Tests Completed --- src/cool2/cmp/semantic.py | 9 +++- src/cool2/cool/ast/ast.py | 10 ++++- src/cool2/cool/error/errors.py | 16 ++++++- src/cool2/cool/grammar/cool_grammar.py | 4 +- src/cool2/cool/semantic/type.py | 8 +++- src/cool2/cool/visitors/visitors.py | 61 +++++++++++++++++++------- 6 files changed, 84 insertions(+), 24 deletions(-) diff --git a/src/cool2/cmp/semantic.py b/src/cool2/cmp/semantic.py index f984b6a52..f92ea87e5 100644 --- a/src/cool2/cmp/semantic.py +++ b/src/cool2/cmp/semantic.py @@ -93,7 +93,14 @@ def all_attributes(self, clean=True): return plain.values() if clean else plain def all_methods(self, clean=True): - plain = OrderedDict() if self.parent is None or self.parent == self else self.parent.all_methods(False) + return self._all_methods(clean, set()) + + def _all_methods(self, clean:bool, visited_types:set): + if self.parent is None or self.parent == self or self in visited_types: + plain = OrderedDict() + else: + visited_types.add(self) + plain = self.parent._all_methods(False, visited_types) for method in self.methods: plain[method.name] = (method, self) return plain.values() if clean else plain diff --git a/src/cool2/cool/ast/ast.py b/src/cool2/cool/ast/ast.py index 97e5675bf..0c6cbdf48 100644 --- a/src/cool2/cool/ast/ast.py +++ b/src/cool2/cool/ast/ast.py @@ -36,7 +36,9 @@ def __init__(self, idx, params, return_type, body,row=None,column=None): super().__init__(row,column) self.id = idx self.params = params - self.type = return_type + self.type = return_type[0] + self.type_row = return_type[1] + self.type_column = return_type[2] self.body = body def __iter__(self): @@ -228,7 +230,11 @@ class VariableNode(AtomicNode): pass class InstantiateNode(AtomicNode): - pass + + def __init__(self, type, row=None,column=None): + super().__init__(type[0], row, column) + self.type_row = type[1] + self.type_column = type[2] class NotNode(UnaryNode): pass diff --git a/src/cool2/cool/error/errors.py b/src/cool2/cool/error/errors.py index b7ca2b070..b0cf13f12 100644 --- a/src/cool2/cool/error/errors.py +++ b/src/cool2/cool/error/errors.py @@ -2,20 +2,30 @@ SYNTACTIC_ERROR = 'at or near "{0}"' REDEFINITION_BASIC_CLASS = 'Redefinition of basic class {0}.' -WRONG_SIGNATURE = 'Method "{0}" already defined in "{1}" with a different signature.' +METHOD_REDEFINED_WRONG_SIGNATURE_PARAM = 'In redefined method {0}, parameter type {1} is different from original type {2}.' +METHOD_REDEFINED_WRONG_SIGNATURE_RETURN = "In redefined method {0}, return type {1} is different from original return type {2}." +METHOD_REDEFINED_WRONG_PARAM_AMOUNT = "Incompatible number of formal parameters in redefined method {0}." SELF_IS_READONLY = 'Variable "self" is read-only.' +ASSIGN_SELF = "Cannot assign to 'self'." +LET_BOUND_SELF = "'self' cannot be bound in a 'let' expression." +PARAM_NAME_SELF = "'self' cannot be the name of a formal parameter." +ATTRIBUTE_NAME_SELF = "'self' cannot be the name of an attribute." ATTRIBUTE_NOT_DEFINED = 'Attribute "{0}" is not defined in class "{1}".' ATTRIBUTE_ALREADY_DEFINED = 'Attribute {0} is multiply defined in class.' ATTRIBUTE_ALREADY_DEFINED_IN_PARENT = 'Attribute {0} is an attribute of an inherited class.' LOCAL_ALREADY_DEFINED = 'Variable "{0}" is already defined in method "{1}".' -INCOMPATIBLE_TYPES = 'Cannot convert "{0}" into "{1}".' +INCOMPATIBLE_TYPES = "Inferred type {0} of initialization of {1} does not conform to identifier's declared type {2}." UNDEFINED_CLASS_CASE_BRANCH = 'Class {0} of case branch is undefined.' +UNDEFINED_PARAM_TYPE = "Class {0} of formal parameter {1} is undefined." +UNDEFINED_RETURN_TYPE = "Undefined return type {0} in method {1}." +UNDEFINED_NEW_TYPE = "'new' used with undefined class {0}." ATTRIBUTE_INCOMPATIBLE_TYPES = 'Inferred type {2} of initialization of attribute {0} does not conform to declared type {1}.' METHOD_INCOMPATIBLE_RETURN_TYPE = "Inferred return type {2} of method {0} does not conform to declared return type {1}." VARIABLE_NOT_DEFINED = 'Undeclared identifier {0}.' INVALID_UNARY_OPERATION = "Argument of '{0}' has type {1} instead of {2}." INVALID_BINARY_OPERATION = 'non-{1} arguments: {1} {0} {2}' NOT_BOOLEAN_CONDITION = "Predicate of 'if' does not have type Bool." +WHILE_NOT_BOOLEAN_CONDITION = "Loop condition does not have type Bool." CIRCULAR_DEPENDENCY = 'Class {0}, or an ancestor of {0}, is involved in an inheritance cycle.' VOID_TYPE_CONFORMS = "Void is not a valid type." NO_OPERATION_DEFINDED = "No operations defined with operator {0} and types {1}." @@ -29,6 +39,7 @@ INCOMPATIBLE_PARAMS_TYPES = "In call of method {0}, type {1} of parameter {2} does not conform to declared type {3}." METHOD_NOT_DEFINED = "Method '{0}' is not defined{1} in '{2}' with {3} params." METHOD_ALREADY_DEFINED = "Method {0} is multiply defined." +METHOD_REPEATED_ARGS_NAME = "Formal parameter {0} is multiply defined." ATTRIBUTE_TYPE_UNDEFINED = "Class {0} of attribute {1} is undefined." NO_ENTRY_POINT = "The program must define a 'main' method with no parameters in the 'Main' class" NO_MAIN_TYPE = "The program must define a 'Main' type" @@ -39,6 +50,7 @@ ATTRIBUTE_CANT_INFER = "No attribute '{0}' in inferred types." METHOD_CANT_INFER = "No method '{0}' with {1} params in inferred types." TYPE_NOT_DEFINED = "Type '{0}' is not defined." +LET_BOUND_TYPE_NOT_DEFINED = "Class {0} of let-bound identifier {1} is undefined." TYPE_ALREADY_DEFINED = "Classes may not be redefined" TYPE_CANT_INFER = "Cant infer type in given context." TYPE_CANT_BE_INHERITED = "Class {0} cannot inherit class {1}." diff --git a/src/cool2/cool/grammar/cool_grammar.py b/src/cool2/cool/grammar/cool_grammar.py index 6ae5bcbe2..19bee9b66 100644 --- a/src/cool2/cool/grammar/cool_grammar.py +++ b/src/cool2/cool/grammar/cool_grammar.py @@ -52,7 +52,7 @@ def_attr %= idx + colon + typex + assign + expr, lambda h,s: AttrDeclarationNode(s[1][0],s[3][0],s[5],row=s[1][1] ,column=s[1][2]) # ??? -def_func %= idx + opar + param_list + cpar + colon + typex + ocur + expr + ccur, lambda h,s: FuncDeclarationNode(s[1][0],s[3],s[6][0],s[8],row=s[1][1] ,column=s[1][2]) +def_func %= idx + opar + param_list + cpar + colon + typex + ocur + expr + ccur, lambda h,s: FuncDeclarationNode(s[1][0],s[3],s[6],s[8],row=s[1][1] ,column=s[1][2]) param_list %= G.Epsilon, lambda h,s: [ ] param_list %= param, lambda h,s: [ s[1] ] @@ -128,7 +128,7 @@ atom %= true, lambda h,s: BoolNode(s[1][0],row=s[1][1] ,column=s[1][2]) atom %= false, lambda h,s: BoolNode(s[1][0],row=s[1][1] ,column=s[1][2]) atom %= idx, lambda h,s: VariableNode(s[1][0],row=s[1][1] ,column=s[1][2]) -atom %= new + typex, lambda h,s: InstantiateNode(s[2][0],row=s[1][1] ,column=s[1][2]) +atom %= new + typex, lambda h,s: InstantiateNode(s[2],row=s[1][1] ,column=s[1][2]) atom %= func_call, lambda h,s: CallNode(s[1][0],s[1][1],s[1][2],None,row=s[1][3] ,column=s[1][4]) atom %= at + typex + dot + func_call, lambda h,s: CallNode(s[4][0],s[4][1],s[4][2],s[2][0],row=s[4][3] ,column=s[4][4]) atom %= opar + expr + cpar, lambda h,s: s[2] diff --git a/src/cool2/cool/semantic/type.py b/src/cool2/cool/semantic/type.py index df2e8dc37..36928a41c 100644 --- a/src/cool2/cool/semantic/type.py +++ b/src/cool2/cool/semantic/type.py @@ -62,13 +62,19 @@ def get_parents(self): return [] def get_attribute(self, name:str): + return self._get_attribute(name, set()) + + def _get_attribute(self, name:str, visited_types:set): + if self in visited_types: + raise SemanticError(ATTRIBUTE_NOT_DEFINED, name, self.name) try: return next(attr for attr in self.attributes if attr.name == name) except StopIteration: if self.parent is None or self.parent == self: raise SemanticError(ATTRIBUTE_NOT_DEFINED, name, self.name) try: - return self.parent.get_attribute(name) + visited_types.add(self) + return self.parent._get_attribute(name, visited_types) except SemanticError: raise SemanticError(ATTRIBUTE_NOT_DEFINED, name, self.name) diff --git a/src/cool2/cool/visitors/visitors.py b/src/cool2/cool/visitors/visitors.py index 09d214b17..33a1e1cf3 100644 --- a/src/cool2/cool/visitors/visitors.py +++ b/src/cool2/cool/visitors/visitors.py @@ -420,6 +420,11 @@ def visit(self, node): @visitor.when(AttrDeclarationNode) def visit(self, node): + if node.id == 'self': + er = SemanticError(ATTRIBUTE_NAME_SELF) + node.type = ErrorType() + self.add_semantic_error(er, node.row, node.column) + return try: node.type = self.context.get_type(node.type) except SemanticError as er: @@ -434,16 +439,25 @@ def visit(self, node): @visitor.when(FuncDeclarationNode) def visit(self, node): - for x in node.params: + for i,x in enumerate(node.params): + if x.id == 'self': + er = SemanticError(PARAM_NAME_SELF) + self.add_semantic_error(er, x.row, x.column) try: x.type = self.context.get_type(x.type) except SemanticError as er: + er = TypeCoolError(UNDEFINED_PARAM_TYPE, x.type, x.id) x.type = ErrorType() - self.add_semantic_error(er, node.row, node.column) + self.add_semantic_error(er, x.row, x.column) + if x.id in [n.id for n in node.params[:i]]: + er = SemanticError(METHOD_REPEATED_ARGS_NAME, x.id) + self.add_semantic_error(er, x.row, x.column) try: node.type = self.context.get_type(node.type) except SemanticError as er: - self.add_semantic_error(er, node.row, node.column) + er = TypeCoolError(UNDEFINED_RETURN_TYPE, node.type, node.id) + node.type = ErrorType() + self.add_semantic_error(er, node.type_row, node.type_column) try: method = self.current_type.define_method(node.id,[x.id for x in node.params],[x.type for x in node.params],node.type) method.node = node @@ -524,12 +538,22 @@ def visit(self, node, scope): current_method = self.current_type.get_method(node.id,len(node.params)) if self.current_type.parent: methods = self.current_type.parent.all_methods() - methods = [x for x,typex in methods if x.name == node.id and len(x.param_names) == len(node.params)] + methods = [x for x,typex in methods if x.name == node.id] methods = [x for x in methods if x != current_method] if methods: - err = SemanticError(WRONG_SIGNATURE,methods[0].name,self.current_type.parent.name) - self.add_semantic_error(err, node.row, node.column) - + for method in methods: + if len(method.param_names) == len(node.params): + for i,(x,y) in [(i,(x,y)) for i,(x,y) in enumerate(zip(method.param_types, current_method.param_types)) if x != y]: + err = SemanticError(METHOD_REDEFINED_WRONG_SIGNATURE_PARAM,method.name, y.name, x.name) + self.add_semantic_error(err, node.params[i].row, node.params[i].column) + else: + err = SemanticError(METHOD_REDEFINED_WRONG_PARAM_AMOUNT, node.id) + self.add_semantic_error(err, node.row, node.column) + if method.return_type != current_method.return_type: + err = SemanticError(METHOD_REDEFINED_WRONG_SIGNATURE_RETURN, method.name, current_method.return_type.name, method.return_type.name) + self.add_semantic_error(err, node.type_row, node.type_column) + + f_scope = scope.create_child() node.scope = f_scope @@ -548,14 +572,19 @@ def visit(self, node, scope): scope.define_variable(node.id,node.type) @visitor.when(VarDeclarationNode) - def visit(self, node, scope): + def visit(self, node, scope, let_variable=False): if node.id == 'self': - err = SemanticError(SELF_IS_READONLY) + if let_variable: + err = SemanticError(LET_BOUND_SELF) + else: + err = SemanticError(SELF_IS_READONLY) self.add_semantic_error(err, node.row, node.column) try: node.type = self.context.get_type(node.type) except SemanticError as er: + if let_variable: + er = TypeCoolError(LET_BOUND_TYPE_NOT_DEFINED, node.type, node.id) node.type = ErrorType() self.add_semantic_error(er, node.row, node.column) if not node.expr: @@ -579,13 +608,13 @@ def visit(self, node, scope): scope.define_variable(node.id,node.type) if not node.expr.type.conforms_to(node.type,self.current_type): - er = TypeCoolError(INCOMPATIBLE_TYPES, node.expr.type.name, node.type.name) + er = TypeCoolError(INCOMPATIBLE_TYPES, node.expr.type.name, node.id, node.type.name) self.add_semantic_error(er, node.row, node.column) @visitor.when(AssignNode) def visit(self, node, scope): if node.id == 'self': - er = SemanticError(SELF_IS_READONLY) + er = SemanticError(ASSIGN_SELF) self.add_semantic_error(er, node.row, node.column) self.visit(node.expr,scope) @@ -597,7 +626,7 @@ def visit(self, node, scope): node.type = node.expr.type if not node.expr.type.conforms_to(node.type,self.current_type): - er = TypeCoolError(INCOMPATIBLE_TYPES, node.expr.type.name, node.type.name) + er = TypeCoolError(INCOMPATIBLE_TYPES, node.expr.type.name, node.id, node.type.name) self.add_semantic_error(er, node.row, node.column) @visitor.when(CallNode) @@ -664,7 +693,7 @@ def visit(self, node: LetNode, scope): curr_scope = let_scope for var_node in node.params: attr_scope = curr_scope.create_child() - self.visit(var_node,attr_scope) + self.visit(var_node,attr_scope,True) curr_scope = attr_scope body_scope = curr_scope.create_child() self.visit(node.expr,body_scope) @@ -675,7 +704,7 @@ def visit(self, node:WhileNode, scope): node.type = self.context.get_type('Object') self.visit(node.condition,scope) if node.condition.type != self.context.get_type('Bool'): - er = TypeCoolError(NOT_BOOLEAN_CONDITION, node.condition.type.name) + er = TypeCoolError(WHILE_NOT_BOOLEAN_CONDITION) self.add_semantic_error(er, node.row, node.column) self.visit(node.expr,scope) @@ -794,10 +823,10 @@ def visit(self, node, scope): def visit(self, node, scope): try: node.type = self.context.get_type(node.lex) - except SemanticError as er: + er = TypeCoolError(UNDEFINED_NEW_TYPE, node.lex) node.type = ErrorType() - self.add_semantic_error(er, node.row, node.column) + self.add_semantic_error(er, node.type_row, node.type_column) class AutoResolver: From 3e663f25b49da79f6e2d5d58e60c07f69014bc1d Mon Sep 17 00:00:00 2001 From: Luiso Date: Tue, 13 Jul 2021 17:25:22 -0400 Subject: [PATCH 018/143] Tests fixed according new grammar --- src/cool2/cool/semantic/type.py | 2 +- src/cool2/cool/visitors/visitors.py | 6 ++++++ src/cool2/test/test_data/test_SELF_TYPE.meta_test | 6 +++--- src/cool2/test/test_data/test_at.meta_test | 6 +++--- src/cool2/test/test_data/test_attr_dep_1.meta_test | 4 ++-- src/cool2/test/test_data/test_attr_dep_2.meta_test | 4 ++-- src/cool2/test/test_data/test_auto_1.meta_test | 2 +- src/cool2/test/test_data/test_auto_2.meta_test | 2 +- src/cool2/test/test_data/test_auto_3.meta_test | 2 +- src/cool2/test/test_data/test_auto_4.meta_test | 2 +- src/cool2/test/test_data/test_auto_5.meta_test | 2 +- .../test/test_data/test_auto_conditional.meta_test | 10 +++++----- .../test/test_data/test_auto_defaults.meta_test | 2 +- .../test/test_data/test_auto_undecidable.meta_test | 6 +++--- src/cool2/test/test_data/test_case.meta_test | 2 +- src/cool2/test/test_data/test_case_no_type.meta_test | 2 +- .../test/test_data/test_case_repeated_type.meta_test | 2 +- .../test/test_data/test_case_void_eval.meta_test | 2 +- src/cool2/test/test_data/test_comments.meta_test | 2 +- src/cool2/test/test_data/test_conditional.meta_test | 12 ++++++------ src/cool2/test/test_data/test_default.meta_test | 4 ++-- .../test/test_data/test_entry_point_1.meta_test | 2 +- .../test/test_data/test_entry_point_2.meta_test | 4 ++-- .../test/test_data/test_entry_point_3.meta_test | 2 +- src/cool2/test/test_data/test_equality.meta_test | 6 +++--- src/cool2/test/test_data/test_escape_line.meta_test | 2 +- src/cool2/test/test_data/test_in_out.meta_test | 2 +- src/cool2/test/test_data/test_instances.meta_test | 2 +- src/cool2/test/test_data/test_isvoid.meta_test | 2 +- src/cool2/test/test_data/test_let.meta_test | 2 +- src/cool2/test/test_data/test_other.meta_test | 6 +++--- src/cool2/test/test_data/test_recursive.meta_test | 2 +- src/cool2/test/test_data/test_substr_1.meta_test | 2 +- src/cool2/test/test_data/test_substr_2.meta_test | 2 +- src/cool2/test/test_data/test_substr_3.meta_test | 2 +- .../test/test_data/test_void_dispatch.meta_test | 2 +- src/cool2/test/test_data/test_while.meta_test | 2 +- .../test/test_data/test_zero_division.meta_test | 2 +- 38 files changed, 66 insertions(+), 60 deletions(-) diff --git a/src/cool2/cool/semantic/type.py b/src/cool2/cool/semantic/type.py index 36928a41c..3c2f2aa19 100644 --- a/src/cool2/cool/semantic/type.py +++ b/src/cool2/cool/semantic/type.py @@ -308,7 +308,7 @@ def __init__(self,context): Type.__init__(self, 'AUTO_TYPE') self.parent = None self.context = context - self.possibles = [ x for x in context.types.values() if not isinstance(x, ErrorType) ] + self.possibles = [ x for x in context.types.values() if all([not isinstance(x, ErrorType), not isinstance(x, VoidType)])] self.equals = [self,] def update_possibles(self, new_possibles): diff --git a/src/cool2/cool/visitors/visitors.py b/src/cool2/cool/visitors/visitors.py index 33a1e1cf3..bd1f36604 100644 --- a/src/cool2/cool/visitors/visitors.py +++ b/src/cool2/cool/visitors/visitors.py @@ -361,6 +361,8 @@ def visit(self, node): for class_decl in node.declarations: if class_decl.id in self.context.special_types: + typex = self.context.get_type(class_decl.id) + typex.class_node = class_decl self.add_semantic_error(SemanticError(REDEFINITION_BASIC_CLASS, class_decl.id), class_decl.row, class_decl.column) else: try: @@ -834,6 +836,10 @@ def __init__(self, context:Context, errors=[]): self.context = context self.errors = errors + def add_semantic_error(self, error:SemanticError, row:int, column:int): + error.set_position(row, column) + self.errors.append(error) + def change_auto_to_concrete_type(self, node, node_type_name="type", less_concrete=False): typex = getattr(node, node_type_name) if typex.name == "AUTO_TYPE": diff --git a/src/cool2/test/test_data/test_SELF_TYPE.meta_test b/src/cool2/test/test_data/test_SELF_TYPE.meta_test index fc2f1ebf0..179c2488d 100644 --- a/src/cool2/test/test_data/test_SELF_TYPE.meta_test +++ b/src/cool2/test/test_data/test_SELF_TYPE.meta_test @@ -15,13 +15,13 @@ class A{ a; } }; -} +}; class B inherits A { get4(): SELF_TYPE { get3() }; - } + }; class Main inherits IO { main() : String{ @@ -34,4 +34,4 @@ class Main inherits IO { b.get4().type_name())); } }; -} +}; diff --git a/src/cool2/test/test_data/test_at.meta_test b/src/cool2/test/test_data/test_at.meta_test index 9a0d8b2c9..160a89e24 100644 --- a/src/cool2/test/test_data/test_at.meta_test +++ b/src/cool2/test/test_data/test_at.meta_test @@ -7,7 +7,7 @@ class A { name2():String { "class A" }; -} +}; class B inherits A { name():String { @@ -16,7 +16,7 @@ class B inherits A { name2():String { "class B" }; -} +}; class Main inherits IO { main():String { @@ -25,4 +25,4 @@ class Main inherits IO { b@A.name(); } }; -} +}; diff --git a/src/cool2/test/test_data/test_attr_dep_1.meta_test b/src/cool2/test/test_data/test_attr_dep_1.meta_test index f950af11b..888fd1abd 100644 --- a/src/cool2/test/test_data/test_attr_dep_1.meta_test +++ b/src/cool2/test/test_data/test_attr_dep_1.meta_test @@ -5,7 +5,7 @@ class Test { test:Int <- 10; test2:Int <- 20; -} +}; class Main inherits Test @@ -16,4 +16,4 @@ class Main inherits Test test3; } }; -} +}; diff --git a/src/cool2/test/test_data/test_attr_dep_2.meta_test b/src/cool2/test/test_data/test_attr_dep_2.meta_test index cf291b589..ef8d7656f 100644 --- a/src/cool2/test/test_data/test_attr_dep_2.meta_test +++ b/src/cool2/test/test_data/test_attr_dep_2.meta_test @@ -5,7 +5,7 @@ class Test test3:Int <- test + test2; test2:Int <- 2*test; test:Int <- 10; -} +}; class Main inherits Test @@ -15,4 +15,4 @@ class Main inherits Test test3; } }; -} \ No newline at end of file +}; \ No newline at end of file diff --git a/src/cool2/test/test_data/test_auto_1.meta_test b/src/cool2/test/test_data/test_auto_1.meta_test index 66bdc8c7d..e92ecab70 100644 --- a/src/cool2/test/test_data/test_auto_1.meta_test +++ b/src/cool2/test/test_data/test_auto_1.meta_test @@ -8,4 +8,4 @@ class Main inherits IO { esac; } }; -} +}; diff --git a/src/cool2/test/test_data/test_auto_2.meta_test b/src/cool2/test/test_data/test_auto_2.meta_test index 2da85a0b2..3ffc5a826 100644 --- a/src/cool2/test/test_data/test_auto_2.meta_test +++ b/src/cool2/test/test_data/test_auto_2.meta_test @@ -5,4 +5,4 @@ class Main inherits IO { succ(10) }; succ(n : Int) : AUTO_TYPE { n + 1 }; -} +}; diff --git a/src/cool2/test/test_data/test_auto_3.meta_test b/src/cool2/test/test_data/test_auto_3.meta_test index 22c71a58d..e5957f5b3 100644 --- a/src/cool2/test/test_data/test_auto_3.meta_test +++ b/src/cool2/test/test_data/test_auto_3.meta_test @@ -6,4 +6,4 @@ class Main inherits IO { main():AUTO_TYPE { succ(10) }; -} +}; diff --git a/src/cool2/test/test_data/test_auto_4.meta_test b/src/cool2/test/test_data/test_auto_4.meta_test index d6ca6d288..43da6ba7d 100644 --- a/src/cool2/test/test_data/test_auto_4.meta_test +++ b/src/cool2/test/test_data/test_auto_4.meta_test @@ -12,4 +12,4 @@ class Main inherits IO { fi fi }; -} +}; diff --git a/src/cool2/test/test_data/test_auto_5.meta_test b/src/cool2/test/test_data/test_auto_5.meta_test index 987e607b3..44f4ef511 100644 --- a/src/cool2/test/test_data/test_auto_5.meta_test +++ b/src/cool2/test/test_data/test_auto_5.meta_test @@ -16,4 +16,4 @@ class Main inherits Object { f(a/2, b+1) fi }; -} +}; diff --git a/src/cool2/test/test_data/test_auto_conditional.meta_test b/src/cool2/test/test_data/test_auto_conditional.meta_test index c59dec1cb..96858cc47 100644 --- a/src/cool2/test/test_data/test_auto_conditional.meta_test +++ b/src/cool2/test/test_data/test_auto_conditional.meta_test @@ -4,21 +4,21 @@ class Base { base():AUTO_TYPE { self }; -} +}; class OtraBase { otraBase():AUTO_TYPE { self }; -} +}; class ConcBase1 inherits Base { -} +}; class ConcBase2 inherits OtraBase { -} +}; class Main { a:AUTO_TYPE; @@ -101,4 +101,4 @@ class Main { } }; -} \ No newline at end of file +}; \ No newline at end of file diff --git a/src/cool2/test/test_data/test_auto_defaults.meta_test b/src/cool2/test/test_data/test_auto_defaults.meta_test index 28ddbee4d..277ec71a6 100644 --- a/src/cool2/test/test_data/test_auto_defaults.meta_test +++ b/src/cool2/test/test_data/test_auto_defaults.meta_test @@ -8,4 +8,4 @@ class Main { { let b:AUTO_TYPE in a + b + 10 }; -} \ No newline at end of file +}; \ No newline at end of file diff --git a/src/cool2/test/test_data/test_auto_undecidable.meta_test b/src/cool2/test/test_data/test_auto_undecidable.meta_test index 3358c8c59..d02def0db 100644 --- a/src/cool2/test/test_data/test_auto_undecidable.meta_test +++ b/src/cool2/test/test_data/test_auto_undecidable.meta_test @@ -4,13 +4,13 @@ class A{ m1():Int{ 10 }; -} +}; class B{ m1():Int{ 20 }; -} +}; class Main inherits IO { main() : Int @@ -20,4 +20,4 @@ class Main inherits IO { m(a:AUTO_TYPE):Int{ a.m1() }; -} \ No newline at end of file +}; \ No newline at end of file diff --git a/src/cool2/test/test_data/test_case.meta_test b/src/cool2/test/test_data/test_case.meta_test index 86639347b..0fb806412 100644 --- a/src/cool2/test/test_data/test_case.meta_test +++ b/src/cool2/test/test_data/test_case.meta_test @@ -11,4 +11,4 @@ class Main inherits Object { a; } }; -} +}; diff --git a/src/cool2/test/test_data/test_case_no_type.meta_test b/src/cool2/test/test_data/test_case_no_type.meta_test index b6e622d37..9964a4630 100644 --- a/src/cool2/test/test_data/test_case_no_type.meta_test +++ b/src/cool2/test/test_data/test_case_no_type.meta_test @@ -9,4 +9,4 @@ class Main inherits Object { a; } }; -} +}; diff --git a/src/cool2/test/test_data/test_case_repeated_type.meta_test b/src/cool2/test/test_data/test_case_repeated_type.meta_test index aa654d0dc..0fb79fd46 100644 --- a/src/cool2/test/test_data/test_case_repeated_type.meta_test +++ b/src/cool2/test/test_data/test_case_repeated_type.meta_test @@ -12,4 +12,4 @@ class Main inherits Object { a; } }; -} +}; diff --git a/src/cool2/test/test_data/test_case_void_eval.meta_test b/src/cool2/test/test_data/test_case_void_eval.meta_test index 79d61c88a..a99e152c4 100644 --- a/src/cool2/test/test_data/test_case_void_eval.meta_test +++ b/src/cool2/test/test_data/test_case_void_eval.meta_test @@ -11,4 +11,4 @@ class Main inherits Object { a; } }; -} +}; diff --git a/src/cool2/test/test_data/test_comments.meta_test b/src/cool2/test/test_data/test_comments.meta_test index 8f5f6be6e..9e9ea3e93 100644 --- a/src/cool2/test/test_data/test_comments.meta_test +++ b/src/cool2/test/test_data/test_comments.meta_test @@ -9,4 +9,4 @@ class Main inherits Object { main():Object { 0 }; -} +}; diff --git a/src/cool2/test/test_data/test_conditional.meta_test b/src/cool2/test/test_data/test_conditional.meta_test index 8bbac1313..058b98cd6 100644 --- a/src/cool2/test/test_data/test_conditional.meta_test +++ b/src/cool2/test/test_data/test_conditional.meta_test @@ -1,11 +1,11 @@ "{\"name\": \"test_conditional\", \"result\": 3, \"errors\": []}" - class A { } - class B inherits A { } - class C inherits B { } - class D inherits B { } - class E inherits A { } + class A { }; + class B inherits A { }; + class C inherits B { }; + class D inherits B { }; + class E inherits A { }; class Main inherits IO { @@ -21,4 +21,4 @@ } }; - } + }; diff --git a/src/cool2/test/test_data/test_default.meta_test b/src/cool2/test/test_data/test_default.meta_test index d8ec8f9f6..5a492699e 100644 --- a/src/cool2/test/test_data/test_default.meta_test +++ b/src/cool2/test/test_data/test_default.meta_test @@ -1,6 +1,6 @@ "{\"name\": \"test_default\", \"result\": true, \"errors\": []}" -class B {} +class B {}; class Main inherits IO { a:Int; @@ -30,4 +30,4 @@ class Main inherits IO { fi; } }; -} +}; diff --git a/src/cool2/test/test_data/test_entry_point_1.meta_test b/src/cool2/test/test_data/test_entry_point_1.meta_test index 8f95d9a94..c54320f16 100644 --- a/src/cool2/test/test_data/test_entry_point_1.meta_test +++ b/src/cool2/test/test_data/test_entry_point_1.meta_test @@ -9,4 +9,4 @@ class Main{ } }; -} \ No newline at end of file +}; \ No newline at end of file diff --git a/src/cool2/test/test_data/test_entry_point_2.meta_test b/src/cool2/test/test_data/test_entry_point_2.meta_test index 9e50c689c..7e1d06c0d 100644 --- a/src/cool2/test/test_data/test_entry_point_2.meta_test +++ b/src/cool2/test/test_data/test_entry_point_2.meta_test @@ -6,8 +6,8 @@ class M{ 30; } }; -} +}; class Main inherits M{ -} \ No newline at end of file +}; \ No newline at end of file diff --git a/src/cool2/test/test_data/test_entry_point_3.meta_test b/src/cool2/test/test_data/test_entry_point_3.meta_test index c6ac6fc33..2a82207a5 100644 --- a/src/cool2/test/test_data/test_entry_point_3.meta_test +++ b/src/cool2/test/test_data/test_entry_point_3.meta_test @@ -9,4 +9,4 @@ class NoMain{ } }; -} \ No newline at end of file +}; \ No newline at end of file diff --git a/src/cool2/test/test_data/test_equality.meta_test b/src/cool2/test/test_data/test_equality.meta_test index 85c8137e7..c452b2cdf 100644 --- a/src/cool2/test/test_data/test_equality.meta_test +++ b/src/cool2/test/test_data/test_equality.meta_test @@ -3,11 +3,11 @@ class A { -} +}; class B { -} +}; class Main { @@ -25,4 +25,4 @@ class Main (c = (a = b)) = true; } }; -} +}; diff --git a/src/cool2/test/test_data/test_escape_line.meta_test b/src/cool2/test/test_data/test_escape_line.meta_test index 90fd54fac..e01b70593 100644 --- a/src/cool2/test/test_data/test_escape_line.meta_test +++ b/src/cool2/test/test_data/test_escape_line.meta_test @@ -9,4 +9,4 @@ class Main\ People"; } }; -} +}; diff --git a/src/cool2/test/test_data/test_in_out.meta_test b/src/cool2/test/test_data/test_in_out.meta_test index 6bdd712c6..2b3f9b01f 100644 --- a/src/cool2/test/test_data/test_in_out.meta_test +++ b/src/cool2/test/test_data/test_in_out.meta_test @@ -20,4 +20,4 @@ class Main inherits IO { b; } }; -} +}; diff --git a/src/cool2/test/test_data/test_instances.meta_test b/src/cool2/test/test_data/test_instances.meta_test index ebbee8685..b22f2bb00 100644 --- a/src/cool2/test/test_data/test_instances.meta_test +++ b/src/cool2/test/test_data/test_instances.meta_test @@ -18,4 +18,4 @@ class Main { b.get() + get(); } }; -} +}; diff --git a/src/cool2/test/test_data/test_isvoid.meta_test b/src/cool2/test/test_data/test_isvoid.meta_test index 756f03e03..2cd71cf1a 100644 --- a/src/cool2/test/test_data/test_isvoid.meta_test +++ b/src/cool2/test/test_data/test_isvoid.meta_test @@ -12,4 +12,4 @@ class Main inherits IO { c; } }; -} +}; diff --git a/src/cool2/test/test_data/test_let.meta_test b/src/cool2/test/test_data/test_let.meta_test index 6a85e8917..d32c677ae 100644 --- a/src/cool2/test/test_data/test_let.meta_test +++ b/src/cool2/test/test_data/test_let.meta_test @@ -16,4 +16,4 @@ class Main self; } }; -} +}; diff --git a/src/cool2/test/test_data/test_other.meta_test b/src/cool2/test/test_data/test_other.meta_test index 9bd5fd4b9..769408bf0 100644 --- a/src/cool2/test/test_data/test_other.meta_test +++ b/src/cool2/test/test_data/test_other.meta_test @@ -5,17 +5,17 @@ class A { a(a:Object):Object { a }; -} +}; class B inherits A { -- a:Int; a(self:Int):Object { a }; -} +}; class Main inherits IO { main():B { new B }; -} +}; diff --git a/src/cool2/test/test_data/test_recursive.meta_test b/src/cool2/test/test_data/test_recursive.meta_test index 9edcd2b93..1a9c5b9c3 100644 --- a/src/cool2/test/test_data/test_recursive.meta_test +++ b/src/cool2/test/test_data/test_recursive.meta_test @@ -8,4 +8,4 @@ class Main inherits Object { fibo(n:Int):Int { if n > 1 then fibo(n-1) + fibo(n-2) else 1 fi }; -} +}; diff --git a/src/cool2/test/test_data/test_substr_1.meta_test b/src/cool2/test/test_data/test_substr_1.meta_test index 045d8c2b1..ff41e7dc8 100644 --- a/src/cool2/test/test_data/test_substr_1.meta_test +++ b/src/cool2/test/test_data/test_substr_1.meta_test @@ -8,4 +8,4 @@ class Main s.substr(0,10).concat(s.substr(0,5)).concat(s.substr(5,5)); } }; -} +}; diff --git a/src/cool2/test/test_data/test_substr_2.meta_test b/src/cool2/test/test_data/test_substr_2.meta_test index d4869cce3..fc0646c56 100644 --- a/src/cool2/test/test_data/test_substr_2.meta_test +++ b/src/cool2/test/test_data/test_substr_2.meta_test @@ -8,4 +8,4 @@ class Main s.substr(0,~1); } }; -} +}; diff --git a/src/cool2/test/test_data/test_substr_3.meta_test b/src/cool2/test/test_data/test_substr_3.meta_test index 09f2d1e25..7c00e3aa5 100644 --- a/src/cool2/test/test_data/test_substr_3.meta_test +++ b/src/cool2/test/test_data/test_substr_3.meta_test @@ -8,4 +8,4 @@ class Main s.substr(0,11); } }; -} +}; diff --git a/src/cool2/test/test_data/test_void_dispatch.meta_test b/src/cool2/test/test_data/test_void_dispatch.meta_test index 07ade5979..aefa67c73 100644 --- a/src/cool2/test/test_data/test_void_dispatch.meta_test +++ b/src/cool2/test/test_data/test_void_dispatch.meta_test @@ -9,4 +9,4 @@ class Main { b.type_name() }; -} \ No newline at end of file +}; \ No newline at end of file diff --git a/src/cool2/test/test_data/test_while.meta_test b/src/cool2/test/test_data/test_while.meta_test index 8465ed268..c05badb08 100644 --- a/src/cool2/test/test_data/test_while.meta_test +++ b/src/cool2/test/test_data/test_while.meta_test @@ -10,4 +10,4 @@ class Main inherits IO { counter; } }; -} +}; diff --git a/src/cool2/test/test_data/test_zero_division.meta_test b/src/cool2/test/test_data/test_zero_division.meta_test index dc476ddb4..3a9192165 100644 --- a/src/cool2/test/test_data/test_zero_division.meta_test +++ b/src/cool2/test/test_data/test_zero_division.meta_test @@ -7,4 +7,4 @@ class Main 10/0; } }; -} +}; From 93c5d05fe82f579313b2e22130250d1f5e20f774 Mon Sep 17 00:00:00 2001 From: NazDia Date: Wed, 21 Jul 2021 19:46:59 +0200 Subject: [PATCH 019/143] ply lexer improved --- src/cool_cmp/lexer/__init__.py | 4 +- src/cool_cmp/lexer/errors.py | 2 +- src/cool_cmp/lexer/lexer.py | 78 ++++++++++++++++++++++++++++++---- 3 files changed, 73 insertions(+), 11 deletions(-) diff --git a/src/cool_cmp/lexer/__init__.py b/src/cool_cmp/lexer/__init__.py index 8a71bba02..c7cb6c3fc 100644 --- a/src/cool_cmp/lexer/__init__.py +++ b/src/cool_cmp/lexer/__init__.py @@ -7,8 +7,10 @@ from cool_cmp.shared.pipeline import Pipeline, Pipe from cool_cmp.shared import SymbolTable from cool_cmp.lexer.lexer2 import CoolLexer2 +from cool_cmp.lexer.lexer import PlyLexer -default_lexer = CoolLexer2() + +default_lexer = PlyLexer() class LexerPipeline(Pipeline): diff --git a/src/cool_cmp/lexer/errors.py b/src/cool_cmp/lexer/errors.py index 4416779b4..902f3d2f9 100644 --- a/src/cool_cmp/lexer/errors.py +++ b/src/cool_cmp/lexer/errors.py @@ -19,4 +19,4 @@ def __init__(self, error_message:str, error_token:ICoolToken): self.token = error_token def __str__(self): - return self.FORMAT.format(self.token.get_position()[0],self.token.get_position()[1], self.ERROR_TYPE, f'ERROR "{self.token.get_lex()}"') + return self.FORMAT.format(self.token.get_position()[0],self.token.get_position()[1], self.ERROR_TYPE, self.error) diff --git a/src/cool_cmp/lexer/lexer.py b/src/cool_cmp/lexer/lexer.py index 46e6821c6..bf26da651 100644 --- a/src/cool_cmp/lexer/lexer.py +++ b/src/cool_cmp/lexer/lexer.py @@ -78,7 +78,8 @@ def __init__(self): } tokens = ( - 'ID', + 'OBJECTID', + 'TYPEID', 'NUMBER', 'PLUS', 'MINUS', @@ -104,7 +105,50 @@ def __init__(self): ) + tuple(reserved[x] for x in reserved.keys()) - t_STRING = r'"([^\n"\\]|(\\.)){0,1024}"' + def t_COMMENTMULTI(t): + r'\(\*(.|\n)*?\*\)' + t.lexer.lineno += t.value.count("\n") + + def t_COMMENTMULTIUNFINISHED(t): + r'\(\*(.|\n)*' + t.lexer.lineno += t.value.count("\n") + msg = 'EOF in comment' + self.add_error(LexerCoolError(msg, PlyCoolToken(t.value, t.type, t.lexer.lineno - 1, t.lexer.lexpos - 1))) + + def t_STRING(t): + r'"([^\r\n"\\]|(\\\n)|(\\.)){0,1024}"' + t.lexer.lineno += t.value.count("\n") + null_ch = 'String contains null character' + for i in range(len(t.value)): + if t.value[i] == '\x00': + pos = t.lexer.lexpos - (len(t.value) - i) + line = t.lexer.lineno - t.value[:i].count("\n") + self.add_error(LexerCoolError(null_ch, PlyCoolToken(t.value, t.type, line, pos))) + return t + + def t_STRINGUNFINISHED(t): + r'"([^\r\n"\\]|(\\\n)|(\\.)){0,1024}\n' + t.lexer.lineno += t.value.count("\n") + null_ch = 'String contains null character' + for i in range(len(t.value)): + if t.value[i] == '\x00': + pos = t.lexer.lexpos - (len(t.value) - i) + line = t.lexer.lineno - t.value[:i].count("\n") + self.add_error(LexerCoolError(null_ch, PlyCoolToken(t.value, t.type, line, pos))) + msg = 'Unfinished string constant' + self.add_error(LexerCoolError(msg, PlyCoolToken(t.value, t.type, t.lexer.lineno - 1, t.lexer.lexpos - 1))) + + def t_STRINGUNFINISHEDEOF(t): + r'"([^\r\n"\\]|(\\\n)|(\\.)){0,1024}' + t.lexer.lineno += t.value.count("\n") + null_ch = 'String contains null character' + for i in range(len(t.value)): + if t.value[i] == '\x00': + pos = t.lexer.lexpos - (len(t.value) - i) + line = t.lexer.lineno - t.value[:i].count("\n") + self.add_error(LexerCoolError(null_ch, PlyCoolToken(t.value, t.type, line, pos))) + msg = 'EOF in string constant' + self.add_error(LexerCoolError(msg, PlyCoolToken(t.value, t.type, t.lexer.lineno, t.lexer.lexpos))) def t_NUMBER(t): r'\d+' @@ -116,17 +160,29 @@ def t_NUMBER(t): t.value = 'Invalid' return t - def t_ID(t): - r'[a-zA-Z_][a-zA-Z0-9_]*' + def t_OBJECTID(t): + r'[a-z][a-zA-Z0-9_]*' + low = t.value.lower() + t.type = reserved.get(low,'OBJECTID') + return t + + def t_TYPEID(t): + r'[A-Z][a-zA-Z0-9_]*' low = t.value.lower() - if (t.value[0] == 't' or t.value[0] == 'f') and (reserved.get(low) == 'TRUE' or reserved.get(low) == 'FALSE'): - t.type = reserved.get(low) + if low == 'true': + t.type = 'TYPEID' + + elif low == 'false': + t.type = 'TYPEID' + else: - t.type = reserved.get(t.value,'ID') + t.type = reserved.get(low, 'TYPEID') + return t - def t_COMMENT(t): - r'(--.*)|(\(\*(.|\n)*\*\))' + def t_COMMENTSINGLE(t): + r'(--.*)' + t_PLUS = r'\+' t_MINUS = r'-' @@ -169,6 +225,10 @@ def __call__(self, program_string:str): if not tok: break result.append(PlyCoolToken(tok.value, tok.type, tok.lineno, self.find_column(program_string, tok))) + + for error in self.error_tracker.get_errors(): + error.token.set_position(error.token.lineno, self.find_column(program_string, error.token)) + return result def add_error(self, error:LexerCoolError): From ec3dc716ae354e391187fc254d3803aa2c9f8691 Mon Sep 17 00:00:00 2001 From: NazDia Date: Sun, 25 Jul 2021 04:14:53 +0200 Subject: [PATCH 020/143] Added the lexer's result for the next stage --- src/cool_cmp/lexer/lexer.py | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/src/cool_cmp/lexer/lexer.py b/src/cool_cmp/lexer/lexer.py index bf26da651..6e3a9d755 100644 --- a/src/cool_cmp/lexer/lexer.py +++ b/src/cool_cmp/lexer/lexer.py @@ -22,6 +22,7 @@ def set_lex(self, lex:str): def set_type(self, typex:str): self.type = typex + self.token_type = typex self.typex = typex def set_position(self, line:int, column:int): @@ -228,6 +229,11 @@ def __call__(self, program_string:str): for error in self.error_tracker.get_errors(): error.token.set_position(error.token.lineno, self.find_column(program_string, error.token)) + + self.add_extra_info('text', program_string) + self.add_extra_info('verbose', False) + self.add_extra_info('text_tokens', result) + self.add_extra_info('errors', self.get_errors()) return result From dbc3e26137f0544cf5f9f3daa3e6ede400cb0aa0 Mon Sep 17 00:00:00 2001 From: NazDia Date: Wed, 28 Jul 2021 01:47:43 +0200 Subject: [PATCH 021/143] Fixed multilines comments --- src/cool_cmp/lexer/lexer.py | 59 +++++++++++++++++++++++++++++++------ 1 file changed, 50 insertions(+), 9 deletions(-) diff --git a/src/cool_cmp/lexer/lexer.py b/src/cool_cmp/lexer/lexer.py index 6e3a9d755..a6f115c54 100644 --- a/src/cool_cmp/lexer/lexer.py +++ b/src/cool_cmp/lexer/lexer.py @@ -106,15 +106,15 @@ def __init__(self): ) + tuple(reserved[x] for x in reserved.keys()) - def t_COMMENTMULTI(t): - r'\(\*(.|\n)*?\*\)' - t.lexer.lineno += t.value.count("\n") + # def t_COMMENTMULTI(t): + # r'\(\*(.|\n)*?\*\)' + # t.lexer.lineno += t.value.count("\n") - def t_COMMENTMULTIUNFINISHED(t): - r'\(\*(.|\n)*' - t.lexer.lineno += t.value.count("\n") - msg = 'EOF in comment' - self.add_error(LexerCoolError(msg, PlyCoolToken(t.value, t.type, t.lexer.lineno - 1, t.lexer.lexpos - 1))) + # def t_COMMENTMULTIUNFINISHED(t): + # r'\(\*(.|\n)*' + # t.lexer.lineno += t.value.count("\n") + # msg = 'EOF in comment' + # self.add_error(LexerCoolError(msg, PlyCoolToken(t.value, t.type, t.lexer.lineno - 1, t.lexer.lexpos - 1))) def t_STRING(t): r'"([^\r\n"\\]|(\\\n)|(\\.)){0,1024}"' @@ -219,7 +219,45 @@ def t_error(t): self.lexer = lex.lex() def __call__(self, program_string:str): - self.lexer.input(program_string) + count = 0 + lines = 0 + passes = 0 + semi_clean_string = [] + for i in range(len(program_string)): + if passes > 0: + passes -= 1 + elif program_string[i] == '(' and i + 1 < len(program_string) and program_string[i + 1] == '*': + count += 1 + semi_clean_string.append(' ') + semi_clean_string.append(' ') + if i + 2 < len(program_string) and program_string[i + 2] == ')': + semi_clean_string.append(' ') + passes = 2 + + else: + passes = 1 + + elif program_string[i] == '*' and i + 1 < len(program_string) and program_string[i + 1] == ')' and count > 0: + count -= 1 + semi_clean_string.append(' ') + semi_clean_string.append(' ') + passes = 1 + + elif count > 0 and program_string[i] != '\n': + semi_clean_string.append(' ') + + elif program_string[i] == '\n': + semi_clean_string.append(program_string[i]) + lines += 1 + + else: + semi_clean_string.append(program_string[i]) + + clean = ''.join(semi_clean_string) + print(clean) + print(len(clean)) + print(len(program_string)) + self.lexer.input(clean) result = [] while True: tok = self.lexer.token() @@ -227,6 +265,9 @@ def __call__(self, program_string:str): break result.append(PlyCoolToken(tok.value, tok.type, tok.lineno, self.find_column(program_string, tok))) + if count > 0: + self.add_error(LexerCoolError('EOF in comment', PlyCoolToken('', '', lines + 1, len(program_string)))) + for error in self.error_tracker.get_errors(): error.token.set_position(error.token.lineno, self.find_column(program_string, error.token)) From c430ee5cc0b324f5f4ee8318b7c75d6ba5bd503c Mon Sep 17 00:00:00 2001 From: NazDia Date: Sat, 31 Jul 2021 22:45:24 +0200 Subject: [PATCH 022/143] Tried adding the ply lexer to cool2 and failed --- src/cool2/cool/pipeline.py | 11 ++++++----- src/cool2/cool/pipes/pipes.py | 33 +++++++++++++++++++++++++++++++++ src/cool_cmp/lexer/__init__.py | 3 +++ src/cool_cmp/lexer/lexer.py | 8 ++++---- 4 files changed, 46 insertions(+), 9 deletions(-) diff --git a/src/cool2/cool/pipeline.py b/src/cool2/cool/pipeline.py index fd9efbe35..04d328f48 100644 --- a/src/cool2/cool/pipeline.py +++ b/src/cool2/cool/pipeline.py @@ -1,15 +1,16 @@ from cool.pipes.pipes import start_pipe, change_escaped_lines, remove_comments_pipe,\ parse_text_pipe, ast_pipe, type_collector_pipe, build_types_pipe, \ check_types_pipe, run_program_pipe, reconstruct_pipe, void_as_type_pipe, \ - auto_resolver_pipe, string_escape_pipe, tokenize_text_pipe + auto_resolver_pipe, string_escape_pipe, tokenize_text_pipe, ply_lexer_pipe from cool.libs import add_std_pipe from cool.pipes.pipeline import Pipeline lexer_pipeline = Pipeline(start_pipe, - change_escaped_lines, - remove_comments_pipe, - tokenize_text_pipe, - string_escape_pipe, + ply_lexer_pipe + # change_escaped_lines, + # remove_comments_pipe, + # tokenize_text_pipe, + # string_escape_pipe, ) syntax_pipeline = Pipeline(parse_text_pipe, diff --git a/src/cool2/cool/pipes/pipes.py b/src/cool2/cool/pipes/pipes.py index 313231322..5e5434d11 100644 --- a/src/cool2/cool/pipes/pipes.py +++ b/src/cool2/cool/pipes/pipes.py @@ -1,5 +1,7 @@ from cool.lexer.cool_lexer import cool_lexer +from cool.lexer.lexer import PlyLexer +# from cool2.cool.lexer.lexer import PlyLexer from cool.lexer.comment_lexer import comment_lexer from lib.lang.language_lr import LanguageLR from cool.parser.cool_parser import cool_parser @@ -90,6 +92,37 @@ def tokenize_text_pipe(result:dict, language_grammar=G, language_lexer=cool_lexe tokenize_text_pipe = Pipe(tokenize_text_pipe) +def ply_lexer_pipe(result:dict, language_grammar=G, language_lexer=PlyLexer(), language_parser=cool_parser): + """ + Tokenize with ply + """ + text = result["text"] + + lang = LanguageLR(language_grammar, language_lexer, language_parser) + + errors = [] + tokens = lang.get_tokens(text, errors) + + result.update({ + "parser" : language_parser, + "lexer" : language_lexer, + "language" : lang, + "text_tokens" : tokens + }) + + if result.get("verbose", False): + if errors: + print_errors("Lexer Errors", errors) + + print('================== TOKENS =====================') + pprint_tokens(tokens) + + result["errors"].extend(errors) + + return result + +ply_lexer_pipe = Pipe(ply_lexer_pipe) + def parse_text_pipe(result:dict, language_grammar=G, language_lexer=cool_lexer, language_parser=cool_parser): """ Parse the text diff --git a/src/cool_cmp/lexer/__init__.py b/src/cool_cmp/lexer/__init__.py index c7cb6c3fc..ad44bc071 100644 --- a/src/cool_cmp/lexer/__init__.py +++ b/src/cool_cmp/lexer/__init__.py @@ -24,6 +24,9 @@ def get_tokens(program:str): result.context.update(lexer.get_extra_info()) for error in errors: result.add_error(error) + + print(errors) + print() return result super().__init__(Pipe(get_tokens)) diff --git a/src/cool_cmp/lexer/lexer.py b/src/cool_cmp/lexer/lexer.py index a6f115c54..ab62181ad 100644 --- a/src/cool_cmp/lexer/lexer.py +++ b/src/cool_cmp/lexer/lexer.py @@ -136,7 +136,7 @@ def t_STRINGUNFINISHED(t): pos = t.lexer.lexpos - (len(t.value) - i) line = t.lexer.lineno - t.value[:i].count("\n") self.add_error(LexerCoolError(null_ch, PlyCoolToken(t.value, t.type, line, pos))) - msg = 'Unfinished string constant' + msg = 'Unterminated string constant' self.add_error(LexerCoolError(msg, PlyCoolToken(t.value, t.type, t.lexer.lineno - 1, t.lexer.lexpos - 1))) def t_STRINGUNFINISHEDEOF(t): @@ -254,9 +254,9 @@ def __call__(self, program_string:str): semi_clean_string.append(program_string[i]) clean = ''.join(semi_clean_string) - print(clean) - print(len(clean)) - print(len(program_string)) + # print(clean) + # print(len(clean)) + # print(len(program_string)) self.lexer.input(clean) result = [] while True: From d2951409a8056ab2d20bd4d6a95e920212f8c8d6 Mon Sep 17 00:00:00 2001 From: Luiso Date: Sat, 31 Jul 2021 21:48:55 -0400 Subject: [PATCH 023/143] Ply running Cyclic dependency fixed between cool_cmp.lexer.lexer2 and cool2.lib.lexer.lexer Path updated to provide support for cool_cmp --- src/cool2/cool/pipes/pipes.py | 4 ++-- src/cool2/main.py | 4 ++++ src/cool_cmp/lexer/lexer2.py | 12 ++++++------ 3 files changed, 12 insertions(+), 8 deletions(-) diff --git a/src/cool2/cool/pipes/pipes.py b/src/cool2/cool/pipes/pipes.py index 5e5434d11..88b2af003 100644 --- a/src/cool2/cool/pipes/pipes.py +++ b/src/cool2/cool/pipes/pipes.py @@ -1,7 +1,7 @@ from cool.lexer.cool_lexer import cool_lexer -from cool.lexer.lexer import PlyLexer -# from cool2.cool.lexer.lexer import PlyLexer +# from cool.lexer.lexer import PlyLexer +from cool_cmp.lexer.lexer import PlyLexer from cool.lexer.comment_lexer import comment_lexer from lib.lang.language_lr import LanguageLR from cool.parser.cool_parser import cool_parser diff --git a/src/cool2/main.py b/src/cool2/main.py index 37c214324..159f59052 100644 --- a/src/cool2/main.py +++ b/src/cool2/main.py @@ -1,3 +1,7 @@ +import os, sys +base_dir = os.path.dirname(__file__) +sys.path.append(os.path.join(base_dir, "..")) + from cool.pipeline import cool_pipeline, reconstr_pipeline from cool.grammar.cool_grammar import G from cool.parser.cool_parser import save_parser,cool_parser_path, cool_parser diff --git a/src/cool_cmp/lexer/lexer2.py b/src/cool_cmp/lexer/lexer2.py index 37fcbeaa8..4f9435046 100644 --- a/src/cool_cmp/lexer/lexer2.py +++ b/src/cool_cmp/lexer/lexer2.py @@ -7,13 +7,13 @@ from cool_cmp.shared.token import ICoolToken from cool_cmp.shared.errors import ErrorTracker from cool_cmp.lexer.errors import LexerCoolError -from cool2.cool.lexer.cool_lexer import cool_lexer -from cool2.cool.lexer.comment_lexer import comment_lexer -from cool2.cool.pipeline import lexer_pipeline +# from cool2.cool.lexer.cool_lexer import cool_lexer +# from cool2.cool.lexer.comment_lexer import comment_lexer +# from cool2.cool.pipeline import lexer_pipeline -from cool2.lib.lexer.lexer import DetailToken, DetailLexer +# from cool2.lib.lexer.lexer import DetailToken, DetailLexer -class CoolToken2(DetailToken, ICoolToken): +class CoolToken2(ICoolToken): def set_lex(self, lex:str): self.lex = (lex, self.row, self.column) @@ -69,5 +69,5 @@ def get_errors(self)->List[LexerCoolError]: errors = self.error_tracker.get_errors() return errors - def __DetailToken2CoolToken2(self, detail_token:DetailToken)->CoolToken2: + def __DetailToken2CoolToken2(self, detail_token)->CoolToken2: return CoolToken2(detail_token.lex, detail_token.token_type) From bd2530215088d27f71cdeb22eee4e3adbd84bc4e Mon Sep 17 00:00:00 2001 From: NazDia Date: Sun, 1 Aug 2021 23:02:20 +0200 Subject: [PATCH 024/143] Excluded DetailLexer completely (I think) --- src/cool2/cool/libs.py | 7 +++--- src/cool2/cool/pipes/pipes.py | 2 +- src/cool2/lib/lang/language.py | 12 +++++------ src/cool_cmp/lexer/lexer.py | 39 ++++++++++++++++++++++++++++++++-- 4 files changed, 48 insertions(+), 12 deletions(-) diff --git a/src/cool2/cool/libs.py b/src/cool2/cool/libs.py index 651ec7093..1b98fce98 100644 --- a/src/cool2/cool/libs.py +++ b/src/cool2/cool/libs.py @@ -1,5 +1,5 @@ import os -from cool.pipes.pipes import start_pipe, change_escaped_lines, remove_comments_pipe, parse_text_pipe, ast_pipe, type_collector_pipe, build_types_pipe, check_types_pipe +from cool.pipes.pipes import start_pipe, ply_lexer_pipe, change_escaped_lines, remove_comments_pipe, parse_text_pipe, ast_pipe, type_collector_pipe, build_types_pipe, check_types_pipe from cool.pipes.pipeline import Pipeline, Pipe def get_std(): @@ -10,8 +10,9 @@ def get_std(): def get_std_context(): std_pipeline = Pipeline(start_pipe, - change_escaped_lines, - remove_comments_pipe, + ply_lexer_pipe, + # change_escaped_lines, + # remove_comments_pipe, parse_text_pipe, ast_pipe, type_collector_pipe, diff --git a/src/cool2/cool/pipes/pipes.py b/src/cool2/cool/pipes/pipes.py index 88b2af003..1dc3e47ed 100644 --- a/src/cool2/cool/pipes/pipes.py +++ b/src/cool2/cool/pipes/pipes.py @@ -123,7 +123,7 @@ def ply_lexer_pipe(result:dict, language_grammar=G, language_lexer=PlyLexer(), l ply_lexer_pipe = Pipe(ply_lexer_pipe) -def parse_text_pipe(result:dict, language_grammar=G, language_lexer=cool_lexer, language_parser=cool_parser): +def parse_text_pipe(result:dict, language_grammar=G, language_lexer=PlyLexer(), language_parser=cool_parser): """ Parse the text """ diff --git a/src/cool2/lib/lang/language.py b/src/cool2/lib/lang/language.py index 80a28edc0..0e95418ea 100644 --- a/src/cool2/lib/lang/language.py +++ b/src/cool2/lib/lang/language.py @@ -55,12 +55,12 @@ def get_tokens(self, text, errors): Return the text tokens """ tokens = self.lexer(text) - tokens = self._fix_tokens(tokens,errors) - for tok in tokens: - tok.lex = (tok.lex[0], tok.lex[1] + 1, tok.lex[2] - len(tok.lex[0]) + 1) - if tok.token_type == "UNKNOWN": - errors.append(f'({tok.lex[1]}, {tok.lex[2]}) - LexicographicError: ERROR "{tok.lex[0]}"') - tokens = [x for x in tokens if x.token_type != "UNKNOWN"] + # tokens = self._fix_tokens(tokens,errors) + # for tok in tokens: + # tok.lex = (tok.lex[0], tok.lex[1] + 1, tok.lex[2] - len(tok.lex[0]) + 1) + # if tok.token_type == "UNKNOWN": + # errors.append(f'({tok.lex[1]}, {tok.lex[2]}) - LexicographicError: ERROR "{tok.lex[0]}"') + # tokens = [x for x in tokens if x.token_type != "UNKNOWN"] return tokens diff --git a/src/cool_cmp/lexer/lexer.py b/src/cool_cmp/lexer/lexer.py index ab62181ad..dd4aab397 100644 --- a/src/cool_cmp/lexer/lexer.py +++ b/src/cool_cmp/lexer/lexer.py @@ -9,6 +9,35 @@ from cool_cmp.lexer.errors import LexerCoolError import ply.lex as lex +class Lex(): + def __init__(self, lex): + self.lex = lex + + def set_type(self, new): + self.type = new + + def set_row(self, new): + self.row = new + + def set_column(self, new): + self.column = new + + def __str__(self): + return self.lex + + def __getitem__(self, i): + if i == 0: + return self.lex + + elif i == 1: + return self.row + + elif i == 2: + return self.column + + elif i == 3: + return self.type + class PlyCoolToken(lex.LexToken, ICoolToken): def __init__(self, lex:str, typex:str, line:int, column:int): @@ -17,15 +46,21 @@ def __init__(self, lex:str, typex:str, line:int, column:int): self.set_position(line, column) def set_lex(self, lex:str): - self.lex = lex - self.value = lex + self.lex = Lex(lex) + self.value = Lex(lex) def set_type(self, typex:str): + self.lex.set_type(typex) + self.value.set_type(typex) self.type = typex self.token_type = typex self.typex = typex def set_position(self, line:int, column:int): + self.lex.set_row(line) + self.value.set_row(line) + self.lex.set_column(line) + self.value.set_column(line) self.lineno = line self.line = line self.column = column From 271079f285b7a34c74937b4589ad2d14040f8969 Mon Sep 17 00:00:00 2001 From: Luiso Date: Tue, 3 Aug 2021 12:27:52 -0400 Subject: [PATCH 025/143] Lexer process changed Comment grammar, lexer and parser are deprecated. Multiline comments are handled in a pipe base manner (remove_comment_tokens_pipe) --- src/cool2/cool/ast/ast.py | 4 +- src/cool2/cool/grammar/cool_grammar.py | 5 ++- src/cool2/cool/lexer/cool_lexer.obj | Bin 139064 -> 139984 bytes src/cool2/cool/lexer/cool_lexer.py | 4 +- src/cool2/cool/parser/comment_parser.obj | Bin 2591 -> 2591 bytes src/cool2/cool/parser/cool_parser.obj | Bin 151457 -> 151549 bytes src/cool2/cool/parser/utils.py | 20 ++++----- src/cool2/cool/pipeline.py | 51 +++++++++++++++++++---- src/cool2/cool/pipes/pipes.py | 41 +++++++++++++++++- 9 files changed, 100 insertions(+), 25 deletions(-) diff --git a/src/cool2/cool/ast/ast.py b/src/cool2/cool/ast/ast.py index 0c6cbdf48..9e140e875 100644 --- a/src/cool2/cool/ast/ast.py +++ b/src/cool2/cool/ast/ast.py @@ -23,7 +23,9 @@ class ClassDeclarationNode(DeclarationNode): def __init__(self, idx, features, parent=None,row=None,column=None): super().__init__(row,column) self.id = idx - self.parent = parent if parent else 'Object' + self.parent = parent[0] if parent else 'Object' + self.parent_row = parent[1] if parent else -1 + self.parent_column = parent[2] if parent else -1 self.features = features def __iter__(self): diff --git a/src/cool2/cool/grammar/cool_grammar.py b/src/cool2/cool/grammar/cool_grammar.py index 19bee9b66..c5c535c8c 100644 --- a/src/cool2/cool/grammar/cool_grammar.py +++ b/src/cool2/cool/grammar/cool_grammar.py @@ -24,6 +24,7 @@ true,false,num,string = G.Terminals('true false num string') classx, inherits, new, isvoid = G.Terminals('class inherits new isvoid') idx,typex,at = G.Terminals('id type @') # 'at' is @ +comment_open, comment_close = G.Terminals('(* *)') # productions program %= class_list, lambda h,s: ProgramNode(s[1],row=0,column=0) @@ -34,10 +35,10 @@ # ??? def_class %= classx + typex + ocur + feature_list + ccur + semi, lambda h,s: ClassDeclarationNode(s[2][0],s[4],row=s[1][1] ,column=s[1][2]) -def_class %= classx + typex + inherits + typex + ocur + feature_list + ccur + semi, lambda h,s: ClassDeclarationNode(s[2][0],s[6],s[4][0],row=s[1][1] ,column=s[1][2]) +def_class %= classx + typex + inherits + typex + ocur + feature_list + ccur + semi, lambda h,s: ClassDeclarationNode(s[2][0],s[6],s[4],row=s[1][1] ,column=s[1][2]) def_class %= classx + typex + ocur + ccur + semi, lambda h,s: ClassDeclarationNode(s[2][0],[],row=s[1][1] ,column=s[1][2]) -def_class %= classx + typex + inherits + typex + ocur + ccur + semi, lambda h,s: ClassDeclarationNode(s[2][0],[],s[4][0],row=s[1][1] ,column=s[1][2]) +def_class %= classx + typex + inherits + typex + ocur + ccur + semi, lambda h,s: ClassDeclarationNode(s[2][0],[],s[4],row=s[1][1] ,column=s[1][2]) # ??? feature_list %= feature + semi, lambda h,s: [s[1]] diff --git a/src/cool2/cool/lexer/cool_lexer.obj b/src/cool2/cool/lexer/cool_lexer.obj index a14c5e1045ff9c09fdbd1feda349f39801f8c269..8a5420f57e68caf3a6e49e4ec9dcbdd41e7d76ea 100644 GIT binary patch literal 139984 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 K)z-HBsQ(XHYhj82 literal 139064 zcmeHw3!EH9wRa}(H?OFGAPErIBqSgr!MF+UXOKh)64(TsYVuPBen z7mLLQ|FvIb*lQx;TwhwNez*InO$02md$Eyx}>QCX*M(Ngr=@^OY7-!Eqh~j z+Ev-hvvrwaP3_Iu%*4i(t7ffQ)7ZXpRZFYBof&rGf|K%@hM$pM*_6$s4$Njoo!pUL zxia064MqkN!cxX9>}X%!-PqOA-qx92nym{)X7)I?tE;1BS$9`c{$8ZS)0>*RbQzg( zXSBC1YU)_o(w1%wZwKQtW9nCRw6D}UBTNaVP2J0aBg2Xf?^?5}spsX?Ww~qlHcYH< zZc2A`cQh4g#WB&!L|QQ>_pG}OrxbW>tZ95&_o(~1g|X-Its2_0JXinx#nhbKwK*Tp zW`+mp&LDf;==;-Od~nJObr;usnfA);C?Usz6Ow z#A>=YSRSnScs97;y3w~?Gj8FA2d>Uo?~morySbuBJDS`t zzBcya{9~i)(;Y2cf%~BwVh`OA>6FxrTy673jjZp|qugE6##l)kqa{u6S<>kG=5%9M zdx8GFGgemg{pqgu0`I>!_Dr7csVj1CzZX>B@TH@MANQ{lvKG|~JwL3idnHS_+VtA3 zBkOlPsC&a}37;R?sU>LJidYdhjX8hz(Vsl|v4lko@9OBzi51l0rHfCTvS9a1wfKyU zvqEhc(VTAWjF<4J#1Ti?e)^N^K5%NWS4GC+xccVqw#J!_>DJafS4ITS8&Rf^f&W_M;jwyP ztSBOW|E?b%nHhEBs?L_y_O?(bX$3T@iPtcmV|^e`eB{b<5ZUepQ?%tI8$eX`fD3>)i;BCjBS@-5Oo9_9A z^|lyyMEfgsOzvf|-kO!W`Uc+h_^BZL9*`b<)I@&;D@2XP>g(6(7N`1uKGW zXvdHZ|2A{>UvBS=c;s9qg#HdY1&eX61*r`RAZ+`-9(#HTbv-@BUo<0Z)D|UQ`foaGt+Mwsfv;&nr;M zplko}^RJAaeZ?r2jMqJ}dC8a0zhK^)&dAHE{_;>+MQh z`S*suK6=3Cu1j1*3PMQ_S2pA3ZHK=KYxddgmgj#Sf4idyb)zoTTnAfx@pH%g<>qf(W+a_R#mE~j z=Npjmq-{r@_uKQHy+8g2U8jh!j*7&gEzLRpiPn2~SJ2evywQ|bu(6g7ZEj(H2+_5N zpF8{eZ@pk^f`Szp*4m^WgxQ!EixZ70zdGGfqyxTXcR>E}F}cU{N;5KhQV&~CCbXD) zJO@J`9T1%vgk1O3np@sD_qrP`VWI4rn}XFu>3X?^0>AzLAN$_*=}VT7w$bVye&v>)6d&HynQn}+`Cm5uVCnK3zin%xf^0tG;z0W>aoUv%8S-=Kc4JAKN@(<|pG62#U{~#ng26&ZMEX z5fV2v$JgswTY6;?-q^>KpoZ#hjvA8+*CY01ehiO7q0R~&WL2opKnywJTk95oXz16z z@wYfVdc0mq!5D~AGYrHHtDfC**E7GoBF7fn%9ke?J9p3 z1s_&>%Du^w>@Ue-l1!K62fEkQk0kl7Bu_~4x+Jek@)w;17Y3_>ZuN1&Uy$T$k~}NP z5Gi53{Q9&`g3E)egLUe|f&)obNpguKSL!6#pccsQbL96I)UopWRQY|Enk>JkN^+E3 z_oit^}x8>cL@PzKIJ&H3m|1DHz zte;{=21bLib5;avf@^~tf{l6}aBuKN@OLw{fZh_7l^3N=nK9x+5l^WpTHP+U@08@r zlH4uHaG}B|Nyh4gTo5$C`Z0RYOjBM9wgsP2XUP*M>Lj>5xGLDBmJ4o`q)U>uk}Q$r zeo1~N$qSPFQj)hMc|#}KhxSD9Oz=?fSTGyh_ll~Q>x=bNYES*6DKoCKE8Wp`mhs1C zgS|5&RxL8_&Pi&Blp?|_7!{1kj24&e2`!CX;+qb}2V;VLg1v)-H3<*ZL_AKD@k~v~ zw@dR3HBSppQSTBYGDX!%V&u#^DbNa*M7~jz`4&y+FKJSLO%wY~P42fe z!DmQrnGUM&OY*QJ-a*P4oM98za+m*AACiAnU1(uewp4FDK#=( zGDd!xei<*nOy?Y}yXO|kyHO{>Qr$}%1Q|nhSNUc7YhU?gI_)>w2k|@Iai7&)ceY$L z{dcnbGM#vb?#6%B9r=Ln%4WH0`m;@bnND3Tzf8|wA-~p2a=s)>B-td1>FB5Bm+9>n z<<|?6yex_7_kYT-wk{d1?YT>LrL&n4XLY5!A};l)_D>_Okn1<`k`*NY`x=@nUBq%g$CvY9c>?H#(ldaP#8$c*R=(%P|~8QIv< z(WuQ7ZA)cRC&|k$(yvZ;t1kU#eO9g3f6h>sm~@R!)ulQynOh`tt41X=gXc;#Rb8fw za!@1{r--`3oh?q1Xzk~x%=l8{9AjOcCkXu`~=OXi0YWzN;(J4tdI5!{2C7R?Kd|B>+_qc-a|I{K!^_*?Dj zn;t#5z?I8pngWu^>gY`B1Xp1r3J<{HhA_^4&ZL%0VcV0CH=Pr9uP4f#NwrJS+Y)B} zkYxUKqRi?)bvB**rDVA&qF+doTPt&WG}`=gJ6>*QvpchZ1n9v(7rQzWUG5yTBZ5~7 z^yE2G(xhPt^l3UbtaeG3`z|zjT$0?d3T^6uJ43;zcGpGe+S=JHR#!K>X>E5&fq`Su zT0P}1En&}LOgsBzq-nIXTXK{>A_?u%>OoZTuw=Q(*@q;{{WX+bmn`>QI-BmH1C!*2 z{cm@sO@6k`&SsPU|G9g~W{m>bdPv??uJHv1a`xpY(->!8W>O2KutiBol2-pfI~J6c zdw#OqmJGtTAk%TB= z_q3IjTP4eVE{NHjB)6VSmKYJ4RAXTt&WqFn#Iz)DCY5#-X7}^vWP`9kwjv!kCYbH+ zJ?iugNobbNSpYXA%AH9aD{WqvD07aDu1SF%R%ptSpvv^KOc?n#!LBJImba>IvXlgJinSGe15tEB?=ToDs&oO!>S zL^PEiP120epnfDtZn&%LM%#SxFS@7%a{PLCHd~HwPDVmb7VWyBL!4YT-n7O|LbCRY zNr)0kg6ERuz6I@mI$7@9ko(Cbx#2^!NnjJSfc~_3l71q)v?2tZVBYU0C>>+llQbh# zjDJXy8}3qfHS?OOfUL494e+hCMP_ZqEM>Pn0{8I#XylD^cd$v~^mt z+?-lYO_rNFa!Rt?Y~;R4atBZJ;P$vxkR!0&xi+&OqTl9FqZZ?3Qr#~0v;|uMUN0hw zb`73za}!5@ItekfTlQ?J-qJ*w)rn~G;zYSSqmE?9@=owfkB*3oo!nB0-4XHbs#67| zy**|NOn$8XD&*9)d%r-3pYiLdag*8nBeltIHn2XL?l;EM6C{i&^C^uR4=V zxxK5AETZ?IPJC=AS#5XJs0h!$%`DFMsz$->J*s1M)*X(zpvF$dd3$hs?<#UtkBY=4 zkSbvISk$Al&+hfOU@4@0iqQBS!BxF#SGv@2DVafsTG*|zy6A!Ox@gZ}Q?HtWV2|Q; zF;OAqRc9|#ZtrSjf=>EUqnx>HqEsC9E@Er$Z9-mpRqv90ju;xdVHT@Pu#Kb2MVR=V zrrcgt8L>QejpB@`3e?D|w*&+^>Rp7?`GC2AxwwB^<4U=woNs~QE1OLUeFMP24{gTz_6PW=)7-Ss21 z4^^$xscwP+nbdjmJbzyh`F%_**gl0xzdil}}uh z&-K?x6EmsP@;B)7=M#tLZ_qE!8}A8kWKu^&e!nmB`@-;dZgCQo+8TMcV*CK^(AB_q z%nzT>q!x$2bD;!yf*xEx@qv7$_&eu*;sb}} zZ|sgWAKqA#zcD0sjzyWALjM($12M@XVn#95LJELk2K1eH4 z0v$t20FV*@qyzvd0YFL^P#z`lyo*ijE-&IkC`j(gHOxWB;ISU#DL&iHr1lrv&~8}V z`&OqY`@U*;nerB3Gc0icDtiDr;{d3u0jPHYs5SwpcLAts0jPul$N~WDbpT350M1nb zC>0Gb>v6bDT?Rl;n*o{BQlSj;i{A!WsJd`jTm`sH@rTReZZf|zsRP0?GO2^Y--G;StFm(b_W2p)4vhD>U0zQ(l4@DAE! z0JOXSI8p(q)(ps`rsNCb*g`HEHvkmD09zxxl}Vi+KA%Z_IQ*@sx$p#y5rF5T1f#u+ zJG6HJXzv=3NgbRonK~Z1Xu1O28ZDV>-P|#LHT;rQMEn|``=zL+QOE{(QUGY60nk7* zAd@;eU!z+hk7QB}`5ROsc$_vAA72vY%A}@-zjLcrc!Cx?02M3%tzrNQTL9X=0JKQ~ zXodpN#SK9F5`dN`0PRZyGO5Y=dQyxd7gZGiearwaM|*_Up}7;tQX%qiRscZL5P+tk z0Wn?S5UC|S|9X4!s~ui@nYS$B~qoF_Q89eh~A+p!X2t20IDJYsv-l-0K;V}B>*ZV z04gPb7o%^aUcnve6#(iL0O}P0>J@-rM@yq(!5u0V04f#$Di(m>Mhp0T6i~Gw8&wMc zRSN)B3jkFM096aXpQ1%mx8M$S3jlQs0CkH2Mz`RX=ob7E-GZ#tEdbOl0Mso2)GYu* z!D9eaE&wBRM!jsc*K0icdCASQl$qhr#F;=!l_bvP&w za6}Xw83jj0K|yuks84rh3=a&^2d~;1t4>{|?KHJql1-94E6FD%8KwQ!YO*B9OR`Lo zk4SQdB$jB?dQ%Rx$;Kk5X{h%49p94|{X~-AO7fN@3$0I1{usN?`>_W)340?_UOpxpyNy9a>M6o7^k z0PP+CT0{WUC;&8^04S&dsB;0Ra{(x|0jP5UD8d0~Uja~A0c_FZB!Gm6lCM zMsld{=t2AOY7!rIRYkrj*;1uvRB|HLpcisZ0*tO90$^|sXesaP!|}ckL-pe|by|5% z9d47vknb-|?X9cufI<+q^48U2W9jt4x*E{p9#pP8+Z|!3?2Y=g>Mv0dn|~IbrgApB zpP|2>rt0N}=v>k@O?6s8a#Unhnelcl<={&*FRH3qLcCPoqheD-&J3+xYJlqAzm(2} zgo7a7YORpf>f*HNW5j{vJ(+3cjh_B9dZ(!8QBED zB{TqrIpF~bbHZh=lHsz1IpH!F!f;u_oN(D4=A_Q=xkeR@S#`uHq!nYFP%guo0Jx3_ zz%?`g#yA1|G1^t@Fki$St|tR5lsRY19UjRln0dV>X z!09UhBbxvyRsa~;1i;8904|aOaO?wc!4rV1?EqYD2jFTu03(|KxWEoTy$wLU4M6Q| zfQf9vFNtizFNthIR<1(>FtQ1NCKdqKhXICa{s{|UWE1W%vI&50MgT720x+@(fRRlA zjBEm+#R0%L0RXPj13VICCa%Tf4%f&5sCoey*#y8PZU8QR18|8OfQzsIT-65Psx|;4 zn*d&p){QIF=8lQ%Lmn z#{f{208o?wP@w=&hX9aG0jN*_s89gNvjEgc0MsD>R5bt`w*b^g02~eglxzT$Yycb^ z0F-P194G)(TmTda02B!T96|sbLI4~>02GY?6pa8>egGU?032KZl%N1qh5(c{0F*WW z9D@MVlmHx%05mKBsMG*BCIL7m0jTZ_C^S;?o;_f zn)e|`90ES_p?8rFR5&(&neZ_AXrDFT^f9pBa}34jJ;(j$IgSMi)3yVZY4rin(3?0; zcrX?yOr3&k)SCb_^Z*80Y;a6(MpwvOXBDekw2#cPb4uEOq@q*&$=9Qld~cs2XfqRrYtZG60$ePGJD#Ny1R&WgW$aM-XskETz(uv~`Es&{e1X`rhi8x6 zu6b|2S17o)B80tiyqJVB&sWUZRSRKYF^?d9^cc^D-GhUNNM{QNlwKRFZ6Y0%j7aVe zv#5VaK;bBD+gf_Ez!#U)xbhd*O9s*e5|3vq@H5MQQSN+nyC}0r+93^<(40bETtpzO z(>Zi)00vrUj@@u+u96QKTAdrN-VK5_qX|A7DRdx#AoOTDIYK^!Syde2tfhS1v#M$d z!n(rf?E1@BOpUA=AoWMfd>CLlcjzq$;==&bY5;@*R-SKHPmr024_^&Ki!eTX_-Z=$ z5gibuyzo_*CMhh5n+*^8fb6tlNF_++1G2+aGZ18F24ru=$~h{sGsE5&o53Mq-+H*@J?k+69Efc77t37)_D#9ts{N&Z4MzqMzGb9-!gbUAOL z*yr?ngRp~B_6Lsd{i;>H@RUYlYg}KPR;To$(8A~K#$rThCEEh_4-z9HQr0_7z{1tJ zchc@QqZhXqS!hRNyj~0s8UjK5me+Ld)5ahOS2qF5lmB?82IRl;dzRD6wP*QNV*v~R zXC=?@u%+mIdc~mJJu82V#;Hf;t2qn7e#3LUctdQDD~}nl%l)COb&SjD?rL9LWfUg_ zMxPzWj25*?vP+H{-jV^K`)tYZ;0K$~lRTkwhmiCWp=0MDkm@M9@Hq(nxQ&{ad#V_3 zcy>tVzUGSYQTfwX{?9Jr<=_?>ymV(e`(5Y!Tx6=~7fSs?sjzCvbAD`nZhQ$p)&`|F zoC@o6n`l1~1Kj(~@==?8wl-sm&Kp5t3_f_0f#*9)>)}0BqxJ6A4(c#I*!%Fj4^I?b zS#dw5_cQRAR$I`^7vJe4%AfVd5cRL_D)rb5j}QQz697EE7l4)<01vwZ;E{F!6oUXf zU=e`EKLDd70qCCu;Q4$2JirWq`-KcJXSm=p_b33+oeRL@d+~s57r;~g1lSs7HJ;szJHL-onaTop zI7I@W7Z-rXo&xaLQve=&3c#~_0eI3W0MEezppy@P5pxEZvwQJN&hEu8IlC8Gc?Ku| z&+Y}_YCZt>xC8L)UI3om3&68`0qB_q;AxrwJi8ZwXZHf|>|Ou{3j)w_2|xn{;E^aJ z(=mfP+zUzgVC!7$AX|mXoaG{J}0`S3b1)_11w*6(cE5j&ff??n{9yL(VsQEJ10Q}Md{8p{!0>u$M`C#v&=kXi z-U2Gu0)pV#ie&|WowaN!Ps3wHoqxC7v#C;%5l0l10>uolrP{od(u;kdEf@O$sAd3SY9{s_ONI#`8{>Qc7}^8C&>jE=5CAY7 z2!PQ%01Wy8U=RgBA6UwkD~1U=JQLIu6M&2QORk_a2DJb%+y#I}50Up7iVm-fZf_TW7t8IfePY#25qHAMbt#<5gsoodw?)FU}VUF|D7NM6v_ zy0U78{*wzs4UdV2M68I(Zq-da8rEuxu2rG9y9!naMfcaV)NY=!Vx|^}TN^MfLq+N5 z1aJje;!c5$@`f7IDoG6|{byD3jcBlUc%)U;OeN4ITeV~hwP+t^rzp*(h-_v%T=Ut0 zaD{Mr94+JWH~`l^065s~W|~oaWVMYcXD#PcTP;CY@9GQ}9q0AThI$pHqM&)$F@;lfc$kqNK=G`^tv2YsJ7kR@vRE^$~5FW?? z0nH<+0JcO|QF*p3?$ow~H#8_M;VZKF4D=s7_FeD}F_H(sO8$|fj7dFgwAA;gD2N3p zaHlyAY2Z$C08WOSGFiS-SCjsJCpF|Nbu~#14DS9~kF;rPzDXy(HBVa*+G~HW{Y9Er zB=TzjY-As7wglSavL#0DDg9hEQGqTAzs(pz6^S?jU3|nz_%Tcnq!zYr)Bgbrl>QHZ zVq2y@Js4aBw)taL6*mrsmVtfCTMn_QajPao(M4Ijq#}z$c3n)*+)=AN146r00`7Q~ zkXGI;6&@N2rr^>20Q@YN0j1qi^g}#jOrp(t)P>$qzuD7o_7ppL@NBDq=W+qrKr&dxn#WoibK9evk@htKq?t3LpFwJLTQQ z(a7dG+D}ybbD~rio})#wRp;@C zwLRH@=-V0%v2gAB{IMDDKE$Q}7Gq12{O86|Q+bl#Z@RV?0!=sQZ@M@Z2QMvukue_Z z#chZ7RO=6Zs->!Ps%7_-SsB}2Z4L%MDb5{%=y$0Orhqr!n+bYz>^@>KyY>k|EL6=t zpma^8cqN-eE?`C0`Ma<06gRjfGc4DTcT=*dk=7jJGub4 zqsxHOJGv^*w|_Ghv)A-tOeeajq^3tU&$mO)P?zX|XnnY>zwD_#Q~5WBy$7juD3ALd zGNh;lsW|{ZISBDqJrHrvs_Zl*MyhQ*91T>8UVd$ z0Q8~(@H9ez!Si84w8wv#Fs(`+(duNjVvZ-!)-zjL(U&<0n9a!q#e7YJnhBiLDCW5u z)YL%ZsxzEg8DU1IogAqg!RJi-_4*$Ss5fGZ>rcPbyBhWFG$ zd|K6z{7l2%wLtRnzKAo;&}EQ*5vR(c^ts6X*L_9O(!B&x;5WI~;3jv*Lcrq^3$tMO zgKZ?6fS8l@@~--_PI`Eu{uo_zFS<0B^3kIE43xVBV+7+eI#RyOf z+SSP`i#)H?ZrnL|zYpRE1$av=!Ra35?GXWCOIo!HCd)jb<|3YJo8bG=Qi9~D5sT!NTYV@ixvOBVGmbvniz1)!%P;NE1%dgMYmQzZ#c_~%9Qp(8@9iAg< zmm~TtWKckD8;^ztrH#j`nK++6MW%s+bSq44v*HobwWV3Hwuv+%8IcNXk9xaosyb9B z*2y($wR;cLd&=Jf)v3C)kAHnut=4~>n_=03mO`&nt@;7=NN(<+_LaR~FX(GsS+zp{ zxkd##y*yi|Dj#)Y)lEGb)@q7tGg{><1XWe|*dNB1*!4DRBvW;jt^VE-G)!1!WaCLi z&iYTQWc{6QoK?v;qQSj6+sOwJrPV+`0qJnfT7xi1%{h#YthQm~tmT|)t0e@4AM68f zu%qWnE@dHG%`Ih>$I)x{mx*4&IGb2(j*qkHQ=ARBv$khhh6bglqJ56isky)$FjwIK zfh~H%8G1BbCrYA#K_FroMtY$UbNv;RT^?N)n|IejZ15nRZPl3$^ykf{l~-2@Jg1e9 zkbjq9NFO1eAVS`oSv(rOw3$`-81qW+|BqR}+&t8nxlVu7 zOZwgbao0nJRbH*&JvFCRob2&y&mQ3f(Z|(=pWww1DLk>}T-wP}+dNBomWudY%8UMu z8L~Eiy{=iS=yI3+fLXEL?itcEWdATExU)RJ7@nyGzgX2ASp$*}QcJS+Kv~Kf&>wR0 z!>Cw9TY2JTg<*$U5HGdum7Oj9T-v+U2WxA^xKFYZvR+`h^XfG)tv3#Oqb< zq>05?_IajWY{N&|Gm1A%ykQcP=dr-Z{t6d#MN0qCDCL@4YlQ(+3v+R_bO+DH{Yq`Q zE4ApRk{-0@&2X*luR`}By4SYG%<|*Lf#Vpl>wl}R`p&W@?YbCR-Z5}!i4oUl%Qj{w zyEH7`<29@gk`<>xf7x=3FZ|j;Qs`zq>cS~Rd46zk=KzSMMMZrqZ4JiKR$fN`n;Ax4 zMtd3EpE9~SH!}RdOd-nKMYh3=4=;RcQ25edaa#Kym2;Aw)Jerw7NGU}Fyli3eHe*$ z!=KKb4OP^EzhO^(O0)b^J2>XfuI_V*s=K3WiC&3Ws_Q})heU?%)>qN0N-vcHT%8Q4DrXK8h211Y+8}oZ%HcOWn4Iwr}8?}n}hWGL)#)u zT598RW<*zdS!+{PKFD&r-rkfM-OzIC(uU@fw2^r?u>LLAvg%I3yEb$!TG|ktvb3S; z#H9^wXXxu+#&yB1r!Q^jJj>k6s(QJ;rzuaat^Ux`hV})ftcK+$EX@Y5WX4MYrl5<@ z(FG0HgD^9xLH{-{kcvowy5OuDCHHnUSPGHX-!uBOs=8jrbREHMe=;^SyPE$BA>j(Q0Oh>la@;hC9i%atB#XO zO*So;2Ii>MaVAKiD_SH%7nat27GqUO| zxp1~5CrWa%+`3)gnw|}=$_#63@5%<7f(O)cx!Ej9t4=aQ>*r=wmq7VYkXmc5&C}PG z2$YWv1h{jjuPB_kUtaN0W=2E! z(xv&I`u%mYqHxBvqt_mm4Iaz1?%&s@UGd?AryMfp{q@Jpoi}+&!;+;-rXM \\[\\]\\*/-\\+.\\?\\(\\)\\\\#$%^&\\|'+'{}'+']*' +text = f'[{lower_case}{upper_case}_{numbers}~`\'!,:;<@=>≤”“ \\[\\]\\*/-\\+.\\?\\(\\)\\\\#$%^&\\|'+'{}'+']*' ignore = [('space',f'[ \n\r\b\f\t\v]+|(--({text}|")*\n)')] other = [ + (comment_open, "\\(\\*"), + (comment_close, "\\*\\)"), (idx, f'[{lower_case}][{lower_case}{upper_case}_{numbers}]*'), (typex, f'[{upper_case}][{lower_case}{upper_case}_{numbers}]*'), (num, f'[123456789][{numbers}]*(.[{numbers}]+)?|0'), diff --git a/src/cool2/cool/parser/comment_parser.obj b/src/cool2/cool/parser/comment_parser.obj index 43689ab6ac74622686cc81c67f360ebb7e32ccc7..525ba88186f2361d916b92a7d89ebd898488f748 100644 GIT binary patch delta 831 zcmbO)GGAmv86$@x%ZeSP4_Mbtu48oO2(IX`7M}3T5j6rCEbLJl#U3cryZJ88d(yK(dp0nPr86 zvKdieaVDs^+GJa1>B%X~CY}g6R+t{3e%1^-xan+Qc9b_eP@Ek}4Lgz=_6(oN51Fk@ zMSyCUfo@X*(kwt%fM_P5X&`YnAPo^`2lF|Arh)mB16T?s%di^AElAC9ik+g7A(Ek# zu_!ge1;n<_u*=X%&2YL*SC%FoekoKg$aJwL-RV?EG48$bs6ph>CWmXd-=q3hvCk(dY!^|Z{q#GEM| zJ=`TliFw7DlmD@*x~&I#9c-l?hNVdE!IVUD5m0inABWiFdNygE_1;Wy<0da-o6o)g z=n9|>lYQ8oSRt&3>>}(SM}tJyvFiavfm$X5MZ=KIL<<09x1dSE1HcYV0vZ65y*W&P zmM{Y?Y2nae2Z^$PEflkdhc_Yrp_vS|669zecrZhP!H{D;F!2CG)NZmbyW-?F&Y1ws C%{Kc0 delta 798 zcmbO)GGAmv86$`L-R5;MD~-7e{8R1zT&TVDe;Xreiuj zZBu&Kf?Qofon5D77`9FEX2`H^o01UeLLUSfGB945f^oeAt0&S9roszLS5y+6qkb_#Pm?6?SCBvYLL!otbMKjmEAEik^ zc^uAAgG&H|@;R%G+X7%f0G(@x#V>|1wb&%#27x6f`*Da(u4j|xS>VkCH*WGWw)yO< zfrbEWnC!#u#0p_OWEWuvg$77;9lIV-6sTn~P&5q5LiBJ&vJPDe9AhV4CqC01jGDo$gu#Jc0fTm*_T~$avSGN E07=;)LjV8( diff --git a/src/cool2/cool/parser/cool_parser.obj b/src/cool2/cool/parser/cool_parser.obj index 0d1711928aaacede2d6f49978a3f338b4a1ec09a..941d97e4eaa8ffd8e7749f2f68fba1bd9b7281a2 100644 GIT binary patch literal 151549 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 literal 151457 zcmeHw3zS_|k!b!2{R@yl^XZPrw+RrS`E)`zA<#5QbAf1wkO)I3H%W)nhfd$v9|#&S z&0lDK(ufg9Gb|p?_~9{%x^S;-}vHz{w>RP=7)y+hWMXN149$zwHKXx zQO~I#pzw+NCypPRxPGE#eEwBK`5im*3%a_t9( z3fo4vj`SA>hbOL_Xo=>HFFJQ*WT=13=t!UXH${EUMSa^xCJ0*XC56FD`-XP(59SBZ zKVOpH(Kj)^U&qd&0?4s)BAOrVH(pDAA~w;&{OFePxjp??MU>Wpb$x>)eS=&36rly1 zhOgVPr7$o&5iMd3iw+*I>)1MwA09qwpnrITQH~A)p&K~H0nriBVNnzR^*;1uZO690 z?I+Qv-4C|l2SE)Bti}-m=28l@O`}_)Q>9>YMy}i0XM<;cRKQdl8alT3-G)xjV>Pt#d16-gb2;rf)v8(j+l2fIIP)w4JL9&A@i|d`IGVU)!6}z+ zI(Fujf9x3DLiD1{p;2y$)NrUdQwo~b(YI@-N75~tr0b$h(U#jLqRa1C zaOXX5Z$IeQpIC43*YE{@@(}Sl^3YMA|55u3e?Itlv4=G6Zz&W8`tpNrEskiiDTU1* zED(027ZA;i+{&cA`q9%q_wWC@!C(ii^8_KSA~mmLgmiT`r3W=i4`ND7ZAuF|2K%nck9eB$utrPia9;oLH3j934>L7> z?f+cY-Mr@2uNrF9iKs>_=-8g$I#Tes)>k~NXWk$h@fF7P!b0;u=B_^Ce1oh0yy8ka zU;psV{K!_1A)dv2TcT&N{#FUrxYNvBCZLK*ZO8V}!L284%?}JHBcXoylFnouX3}Dl zt0A6qRgv-O`-I%Q@%ddlhx-Q#gL2R&1LXWZ4r30l4F>R(8k@J=Lc78rpa0RdNBwit znT9qETrBU2Lf-GQu76MGp(mX7+(Qvi=+j2!na6Wf*4_WRu)F!o8-L#ph-CiZz}N0x z`Q@(L{?6d9S%nzo#por*?YA3Op8mI6_Wr0SzB0_bj{Hc$-ErcXWOmn7qWpdK;OGu6 zJ5y7~jr}hlxOUBd>THXVm^Vyj@xiM!DtFW!^S5Vj?EQH-l{q6rqhexXk{tBh*A6=1 zvd4FX)0n$GKQOEl*|qzX?>)Zrg@cobtYbXyK4Zl|;V1iknuLxO*mte)lQUSq{orZ8 z{oRe5o;W9o$V-gp(iN{f)AQ!ye@;MeiC!R68gUQiP8)vlnEpaz!Pq|_#0pS`*Z$P1HO3I*y$f%cv>?D zGMY1v|Jdlfr(akLKr{>#8LCef+IpB#n78Gf<%gbd=dn0$y_dfWCQG={Li)s^Mhe6?IzkU9Fdq1(EE||is4pWHV-_kgE!>+|23??y? zjqAjmycw@QFzZK4{;B)PU<$KVnnEmn>#uJ59}}<5Do%na_&@&l#%KQVv;VyjaHW^d z{OYcQ|M=wp{HzAN_@Dpq7st#Qq27ReZ;Lk6Ek|)xEe-hm(@lJjIZ)!zduBVg4(sL5 z&-m=QzghmN2E9!V67hqCDupw*_{q<&U9)uW-1~W3um@ewIWWYZ-Sk)AzW>?iY@Gw? z&uDgQL66Vs-!A+I%ROf#>Kk{OC6X2aIPU;@rNrPIp>0B zmj1V{jM$W%5q^up8MW@w<396B|KC4l+kIBQvJcbvQGfsZy)Rt&vza;*zv<^}&C7{T zH)TQkgi_@J9e{q))~Ijm)kTc)rlC^QlSqCnDJXlWP8k2vcvhGLIoa8ApxHvZ2y6K% zv#$NSU!K%L!X_F!u`%J4?|!E7uf99q3~#LaPLkDWOz%DX`GY^(^V(0ifZX;d zS(IM)#_x`P^2vtIuNdMLPsAZqcUtF=S$aSS)_GMkFG&LPJBs+m5sbncc-D_OP=(&g z<>q%#icV>k8q)Z>An^(24sU*A?#q|maOkDFGA#BnCHxkJvo1LN_-z}9Pi?)O>pOaz zmI;ca(p85TaKR*2zdg~&iZq%yPHx~2{Mtvj) zCq*na?4A)VkJ?Sj-8~O4`phfGem;c>nHpEVeP`RX{0B6#w*(n*|7B4G&c>I?j!587@sZJU(~lrL?L# zZ@fmUL`VM`y)iz!YwZQ33>|MJoJ2L7)E{OZ&AZz80;nIK-@kPPisJEAAW$E)w=r5A z9Z7V)lxY2IqW6o5<`08^rRwjBI^!eZ7Yv&5iSPl#ReT?aPUFyRWFo(Y*yL7XmAi>u z_7clHLTvK{vCeG3qG|oh(M!?45HmeT4D~!fArOk6h7Sly;$OiB%*OH0-~%Sd_zUns zE%s&jM$Ps(e51DeDtw~`tO0?j73<*}HRb;BjoR}pV$p{IcOUtPHWI_`g3(C1in!>xDmcl zU)TcQs6%`UzEQ7u4Zcy=_%VE={_#`zMxA69$V@%u9e`1H$rFb;9DY%+ISRf}*QteX z)PEMiH|j*E6E|7|G+=CtPXRb}s4o$hT2Gv+jkwi!q8CV_H$MNGzM(Az5{rF&d>*L* zlGP6IiX3soTPMcnu3OW6!KD+qnYWVvBT_Ah2MNL4Eb?O>0l9}jW=HG9 z&w22ZYF$9)5Xc;=c>$SAAag0xc|Bd{ukY-dprau`sdo~*>!C3)L=xb<=rZx-eERcM z@$)+JbH0*w0r|Ng+9dvUKlmva1Hou5f$SIU76=XWr=(U-f6A7eOn*KpkQc&Fs&WBY zL?Pg+0#ZXCk;54fX4{P0Tcq>C?JOrNDcKb0RdrZtOnN5z9?{@fi=n-pn)~ip#=iSR-?QDbXXH< z8m^(8R^UVHYIKX#M4HZPsOt-SXh;q1RszBrVrrv?b`yaETCLIbQxj=+skOM&S`DwY zXx3V7simDy@CCcn>e{HabY5#IuvQnfR@YCh)%;prW3@)pbp6*_Ca%>DTBozC(;u(X zn^UK=uCs{OS!%2^*=dHTv-DGEslCopd!41jI?E7sx>oBfi`Q9-t+#}!w;EV)HLzZ9 zV7(<*{rj)ayF0w;EFKXo%TC>MgC->-wqJy{JK#p+T3qLAB^$;`0r< zgbkL`8Vur=o*OLj8Z3i0=mIxbdRVA)S*Q!SP;bIQi{nDQ6$>o^7Fz9EXv<(VZGSrN zlOcl6aS%2WjqNX{OdSaEiiHUwhY5_{HX$?~v4D>`o~5yqg$aR|1%xQZ1V%4fkO>O{ zeOzxE1X~smB0Ccpea?i?@XG{7Zb`}uAI0JZqW;@7si0uy0Y$t)( zb`pqfCxFU!0;1VY0JW#V8B(B7E2WqyHKx{h+#I_SaWjg`UY$t)(b`pqf zCxO^@5{PXlfXa3fB5fxj(slx(*-iqn?F3NSPCzu<381o_fM~XpKx{h+#I}<_Y&#BI zq}dMY4a9bfG}}oaww(lG+XfE zWl90jOeulbloE(dDS_CO9C)w_nHq`J4%SS03^V1yTCbEqY)T2lrW8PBN&(SKDS_CO z5{OMHf!LH1h)pSg%9Ii!O(`MLlmeoeQUb9l1yGq%Kr~YdpfaU^Xr`1vY)T2lrj$Tz zN)BvP!7wE0jaHc2XeAMiR)pMWg~W|kveRg#GmUy&-Dt(kjd}vrC|x3Yor?OGY^)e0 zp---cLJmL>D9bb|M}?|Rqn;@=>bXp#o+cckG;=6<_E1%QI*j~0OpRuTlb?qxW4*}y z|8QFB6VZ_Xs;X2Id8$e0*rYguEt+)knsnit^rkggIY^V0kT&W1Y_b&Iq+6uPGFg*l z=qAh1O_qB!SuWIMxmmLf(Y4X6x3}49U9+yPW=kQ>R(qQ*;hQbtn{}l%TWx8!bkeM= zu~|1hCqE|{Fo-ZU2&leJi=L-Q;^M!=S`9ebEd?6s(d?A5&z7Rk)UkHdcUkIQYf&@gH zFC-Ap7ZQl)3kk&Y1qU9DZ5KUC^n5fn9EUupG|wT(WE_I5!ywXv90ASj9D>ZmA;?A? zf(*nUk+20v*kH+qbj}|Ur3U^8Ec0-m;RvG0z!6}bNJF5jPdWr?m_LQoOCiT-x_yl3 z_83jK41#sbAXv8yf^|zFO1BIF>y|;VZW#pYmO-#?DMaa(AzGr=UWFN8`gJ9h<2-YoyC`~d1tZD|qx@8coTL!_p zWe}`e3X!8q#C}RQW# zAlNPpg6#s36I7skBKdiu>JcZApC{>NTBe!ld4jM^GZTYgGcgD@6NM-Ju_Od{D+NR*lR!1;NP&^z6q>M|glLnFd=^e=GSs_=3O zG2$tzKb}f{o~rClb}!{xtxUPq%9LBJ(m|`9S{Z$-UW;he zQ|VSK!Ed#y3awT;-)bf7t$M=Vs%Pk@;VuweP2@aH8;ckOIX}aZStS1{7L+*!JnS$E z$gw#N@@fu2F3lmxLpTJvC54=>$-k4xf4U|=gJAh7M9I&7O8FT+mY+ee{0xHSXAmqu zgJAg?1j`SQGc@`4PzV}-A_l?oQ;3qE{gm=Ed@MhMVEGvY%g-QKeg?tvGYFO+AZOCp zX%GUEHu#B~L$?M~AZn2UqDD!eY?p$Frb&o4a>DLo$^tb|VWKuFAZn%pqLwNkYODhC zv4}Rgo3iFZ7KN;$kPU1Gp^%jnvIjv{QwSe9DNZMa>_RwQ1kz@W0BzR5)n*M`ZPu93 zrp6^G@wMsWQkykmwCN*8n?5eJ=>y1eF}Bl&tRVE4V;_j_r4Zx*9D=ORA-wreh~p5h zGzF1!D@VY7&KMy#;5f(!I0QKWgQ)nQ5#i=jeCZ})QK(@!XyW5U#H`1;G>~@akD#fI z<8WV6jOYwYHQ7$|w!(6N6_x|6upD58j&q4-v!;qssYA}>-v1tWqSgFhmZ9fp914hoU}z-c09;1E2RFe1neI1cgw3OO6=mLA6;>OC8)R{@cR z6%Z*|0g;|1P^wx&w2pZ(>zHS29a934&eh{c?GmUurhFDDU!vi$S^`zal+Pk7C^RvK zONf}=V2D;gWDW&H7EwTC6aidmbNmq7C~Jhnk=L)4!w}H_2T&h_56B2}THMfEDgcP`seqkHBBoMoe z04l9Yh)fU!L~{=b#O@)1*gYf=yN3i~+esjH1`a&W>Nw}=9p^kv18`!JXy81p<47RZ zfCS=>BY>)p2#8j6l0Y;G(!-jRHP(#8#F~*ntQi4R9Y;Wf5704F0 zEfA7GJl;wm?l>ISse5dv-c34n2kzASNT=0_I`vN1X?3Dbs}prvJ-5^9R_6<)$&Pcr z-f_;?+y>6z65E}xxs3#3w-G?4RSA(DM?kcWBZ1gGBoMoY1Y-A)K-_U85IX}0cIjs7 z(#_PRo2g4TQaxsqfn}x(G`qmDXkwQOG`mP3wu=BNyGV$% zi-2f$kw9!03B-1hKx`KY#CDNDY!?C4{cyEzrq#NcR_kV3t($4JWv11-nO0k7T5XwW zwPmK&mYLRAW?G}SVvS}sILA(`wnnp>1Y)ZRpt72TNUI5mW;F@KR+B(%H3`I4lR#`W z3B*?8z;4}4-MX2&bu)GAX6m-g)UBJT+cHzPWu|V+Ox>25F0{;ap>C!Nv6-UViH$DA zHsTOuAO?}v;Xj3GI6j&LIRqJnLy$!{1et?FkS#a_8G=G`O6{;Bkh64>vr76oOZhoV z+c|6LA!jWvLWlAj->XZ6L}eGh^3=<4dxb=#wegXi?(_@*hN4IZ}ZjK(y6?-hl>#>}w z$8x+L%Q<^=ujsM-u*dTGb>iGT)$OIEy_eGF(*W`}Y;eAmUV9)CB@QH476_Gs3D#>k zKW7hKrUC!JfExsma@I$NPE0o${)BB%kk9?0k za-{&$r+NumujO*RmK*n4ZrrQ8aj)fpy-FD9DZP3>>a~2hSMPVdde7>$+_l$o*Ivu# zd#%3HtM`Ilz4vX>oDDABA(C&>oJ|6;vk9OwrG!Xl6A;bWBoI5B1Y&2CK(zMMoKaJibKl8fD@|I)dEuH5r4d*Q@60VVK`(L zbue08VGzZI5!quRVCUgPkmqm+avKgoKEoM_>=dE90%1BI0O|ED5Ou*{nv@^`*44a7zFE{L9p&AMCqO( zVBIqa4r&+#>z+Zd?imE@o)YnMGDwaXB&b{WE3!aW%R)-8iz-7*N) zErVd)0%W_U+g%j05BUOvVBJ!PGBo=syAH$0YGe?sMh3xZWDu-I2Ei?25Ue+VT&22c zM1DrNIrK6!3PdeZK-4G&L_Q;d(rqL}tMGMDbrII6)d~|eTmezr6%aLF0@b!M!9}{3 z!jap`1dhnNOkI2;dK(Ct(uBwrMVRvaz6z`_EJFPgbIlKPyqWyryhhbKPHBY zHEZ6<^c)K`+Bylu))YV`wuA^rr5Eo}7T9V6O7JV1J)!tU`?b0`oue+PmBXrj4)tD1OwJ=KVU@! z16I^9pwG_(Rzxsh1tUAOHa^7KI4JBzJ*Pm_Sg^Pkbk!9sUIj}n1>F_}UE2j+O9e|O z1xqId%N7MoCk0E}1xpD9U2g^5dk8@BvmSoeov%O8d>w)|mO_lIHKABJ^*7!jtS{xC|EGitFI)s-`< zQy8^0GpcK5)N0qLrQT7?BBPeZM)ih_S`8ny)HSLbWmMP9sAYmt-D=l}EeupS*AnGi zt96z~*no4bHVR1~?kobRI*WwJ&LSYfB6LQRK-^g*5O)>{#GOR~ac7Z0+*vqqmzaZS zyb3jj>r}vVG#ebQQ~vM~g76VlZ~G|u`B7DGBUd)&cB>{YV~_7v1Uck-6}H_#9=Smg z{22N9F-33>`MF2A!5H~DrrUH(cg``(na3=59n;-)%yQQ;%df_i2B6e2rn}jg<;-K2 zGmoiOz;%pcmNSp(t~h3OnK8?IZxnt=-Ss9y_9kpidOtA%-mLTw_Y_kIWE1qMTgcB_ z6au&SEvmg>@mm!FTyjhxw`r_lec*P%nv(rEfqWe94rRG79L@PSzBDuf$Q|Ml>U*#` z_f8C<7cCRuxBybhcM-^4f;)Zi9Dpc+=aHXxV+49{GXdVC{0erG-m5HfANhHoPT@X5 zfinFBLHL9w#{C3xKW-7dBpQIS)dDi1k$QmOJfM;KB!PTVBLz2415lCTkOw*1_^h?L z2`Ghs3O^SuB#^!IAu2W85xZAIIGR8{jUn`|Ym&W>&$+5FQsDQ@E+$wHlKW;`;Cb@* z2=ez+Um;xB{Os7Gr! zw;Il^hI6UmTx&SD8cw=~vKk^+eFDot@|Q_l%b%;|tZO;TT27~y)2~&mYdPs!PP&eh zs^h42{Lwm2x{l-5DH3(^QP!|J&b5w8cXh6g-r!6G?a$Ry&{gCw(?vZOv|drG=K|Do z0qQy9dM-#k7oeVVZQ%F~{NEcmegnsAP^25=BVg3+W<|1@lW6ALnmLPR zPNJEUY^J0xCZw2jOXzb)ldC_NI+pO~mMGFo_@nQqe~%*PPQhoFl7o%#cNsbA#5CK= zNwlg*Tlu4{oLej9ej0g%b;efCt(8mN%3IM!NiHX+6_`|Q9Is80ZsVle_#-Pg>I#m! zLZPnUs4FPyS-EzO+RjniDZGQgS!ZllkFKPGoReG0A6dyCS&32DQ!BY3tK{EjiF#Uc zt2otFTurNJ+t1CdQY2P!>YW^=lXL0hk9I25PKgQ{JU`dTxpnbJyZEDB{LwD;XcvF9 ziwbZ-u8Tjqnm@XlKf0Pfx>`NDnm@XlKDs8inm^jjAMNIkcJoKO)uY|~(Qf+ag}HA2 zXpTRc|oMMsV-g60mXG(5E?h<-eJkij4vU!yf*0k0RK^dG%1<>j(+vhCN*7^@7o5WYzJPY#LdgyF&E+KORsjxrDu3tUM=~r=&KM6%{64om0zju<~Y#1nTt_9Amz@INDE(K1yzy&LCL54ZWVNPn8lN#ovhB>KW%5sFH;j+!c zoa8VkIm}6pa#Ew5^r(7dls_^mQCS}!zEQ`j58eL497U5TNL$M1kYPZWXi&&yLh;CmkO8e zq7l{IB=lq`_i>c_D4c|(3Ux0J#`e-+?9-#;^ZJH{3PbSXjPW}9brX3Z#Yo>U3E{@~ z%a4-xIFffc4w8R_*DCYhd3b#QV9A$Yj?bk}(N`(Y=^p9Zq5h>ty#;w9-a;Zg`PyRj zMxI&gj)ON?Ea3lC3$Hg`7)`%Gg}p!}ZmXX$qc6UK=nfQ<<-1ej6_oUqj5N(~*(*4p zoXuXmF;(B35^o|xQ~Be~@C{rmA}>jyFGr!NNgNT-^m|Z3-KqrUvMVe(2H zc$?0B6XdO1rxChrxQfptU&&x~0r``g5w9kH{CMnTP!?*`>Lj`_kSKk*6edbu662Nh zM2%<>Oj>u)kSS5Oth4}NBSW+9Bh$rpcsBJ<lTE(aEoe!V-{Sq zipdGD4P&f1GGc8yB66?*WTgRH45Lvk(ODs_TC4j2j66=)=2n`W*n0B5H0;^52Ii!= zTF3Kord2#06_%`=B(Ko%yd>dGZh@#uwaZ1?B?4}X#Cnla@f|h7w3i(8b zPhx7~8BD{5FRl~r#a^9=hMIUL&wM~T>2-BlKZ`y`H=0oH=oNjitAxDqZ6bPs?qOkX zXoC$TE~}B(Cpx>BWwKp?c<*R9Wr(vjYb3nEVZA(KjSfK}1f@<`1rcEdBk_}RN^q7WM%;7>@n7o@7 zvn`h#y^AiGVyFSh%XG1jt|^MVgB0c?H9a}|>)c8>0(b)f!2^is1Z5ASxWs*HK}+ zIVG)vu`o}ED>6zWWdxEf^o_zH!K8PT-37Ajfe(lyX+1PJnDkGvWl@DqR=Rp%2bXiq zfIY!ssGhOZC1{A>dPe$E$j)i=h4ztYP4E!YXr! z1yp$6HD1yi_B4Mm2OY{e#I?c#)?jkM^Ib5-u%RxToKdoY2VE)-%L{_p1`nH*VsH;u z{+;MDp>upX0d~W1fMahuR5%rCBojWx$Dt`{IzjFFh);m9v72%a>R?7N?Pcq#m#Oh2 zn4cpk_p$UB{uEytNKodXPH6_HUhZ~)>O(ctCNEx4N=YDQ!}Lk3rnQmM(YKIIj_PF}pI-W3&>rb7HKIj5k_4kC4P@Br;tMDjH` ztmZ(44DA?U1%jd^g96bXLR1>Gi^m`3!3uz??a~5ia7bznWxmw}vj*eEa~D?15z*iE z%c5!*i@a@em$b^^?inG=B57oo-qyHhk#bl&1~MOaM{4gwjoh=ROVFU9r`{ZTL{{yh z7&=a*SZJSKK-WX_EY$DG##|N+@e#9N=XK5L1@!3!bQn(Lieykd*s+A<^RO81_|Y?2 zsDrsOS;wNBm#R1GcJ-LxG4M_)YnEj#PvYy6G^B6)7gSsnL)&g7YUn zdqIAo>fAV}hiW&UK%=+Vo(Nh6ryH0y`$h`YQF(pNEooA*QD`Wn+a(or)6lX72A6WJb2!4r}XApg*Z2B z?-l~03>e?T;>9BS0#ecOgbAR9RCKHz0iMRYw>4Wz4{->)VBqE%u%adWFxa(|cLAg& zX%w72O-Zic3cev4^u*DnS)+}(j24@UEZ!m)s%0bRW-3WpJ5?d&8#K7QAtbo8COLW? zpxSOw@o>znsg|`tVn~7fa8gR8+Wz>tS&VG;9=MDB@`*JK# z)$7m(&5CzEN6k75Ke4APqhTc~OAi7KnriWf%nPo>ae|#`DJ^NV>gF!)gmLB4Rx9Q* zBq&X%_BK0%Y-QDI&7>~w6;-QMU8%MNVghuRLrSx%u_EAV5(~hy?V2 zL9?PBYE*%$$=cotSkGFQ9tj@?65=}dO*q!Mh+M3t^{*T&=HE;2uwUgkUdA zyV_sL7{y*1sVOWuv63r@}?z28b5^3F3zMa8hg!2p!N7l_I*!(s>N&1Mha* zpWN*h9~Iap6}L|5HXQ(Msts(Dn&75%n+}9FEedRtst!)+HZ6iSof6n4H62V&n}RO; zLz9-cJJ6BfOn$FFh-g0a_Q%BpH|+LD9x7GIKIYm5s!vr(0%ODt+dgOIG>*kuzIzPs zErac2^d>CVE;geoScGE8+BBNF#Evw6dn8&BBTKyCTL3-nQ_I5GS+t7-Yd& zUkfZkF{B3?WrrfvLN#M&5?K)Kf@GqysNMR(d!OUjrx1OZVu`*=ld&}=x%YU1 zv}|P-2GpQkXVL4ro}GiDeVECo7ki=Ii)pR%dNEbhncWCwF8RQ{c_paN-U3s^Q-k|d z$d)iM&Ih@_z9dmZ#2{+=bx!uTlx8t!JjhqX zEGm!{W+{TK0xI#YEI+b4qc6oO?y9$^yM?I7Tln_S3>_EH%7jp6Ndu!1n`W4YefI!Z z+Ws+#6GM_R_rkmt^QOE6z?zroUI$mAXBeL1dAJ9e-CB|FH;T@a2# zU>;QYvje;=q;p)61OmFWZO1@f!+7=-mth+tvB{$8MzWFxNjtOfs>sMIym+3?M;=v% zI;Fh~kV*5cLTE9nAzjj_mwB~h%%y%jJ(nz3JkNnLXYaA&kdj9NI|?b8log`+Ri`hZ zma94=^C(6x*r}}$G83v+Hs@85olj&UfU;3JwM27G%7_8|jj|#@FRGFEWYKsdJ@&G; zm&G&TuCL}6BN;N9*(sd2J;s$RemuSCRJMdx-st6QuP78_?(4%<3T1CU5N+9|dJc556t!SojfrDIh8qH#)fcDoPczKjY|`)vI`Gf}J^0r>Ld` zR&BBB5?JMwYD-|$3ta43-jTfv8f(K?o5cOAG0BCz!&0$WaFvF`-LVn`oF!m$eKBkZt%+BQ(7 z{fUOvqVJ~DCEqPk^?qI53EogrOVH*UQKy_FcY}+2JE41o9_ly}4t2bo&ivwK^t9-- zkAxiR1;!QZN`dFtjv@^j4a9waiqkJgq`-Y2DD{y5rHVPaNVm^wsxZ7cP(w#UOP&a6 z39k+o@%zbb$uXoQ@objaFRPz1nB>`Q_0sBt2D+BTNi1}{DK4SFub*Z6B}s?-|GJNOzx5%yWRy2Y9Y)yHLNtUu8o9>W<}R*CvmOL zKJ}l8jl?iFSqT82JKXF`^N-12+q2&`l#Q(>_gc9ruu7&Mt;@;tGF52}a~`75m!1M^t|@MO_iVGSG+WO`@k+CGAG;~raE=Z4 zie^Rot!Hz-;$E67PH(Ov2bT030kRpp+=oY$xhm=`VkOo+$XAp#+N4s1dqoRV`gL0+ znbWKc#b01+t>xbhqn%02a3iWg>=h>FMTsvv5C|hiv$4=w8c9<89%y;Uq68Oo8IR`(U$?8dWkw zK#S*-m7~1ZS1Rm_*o-_aP3KEJ)K6l5JlSg$d_&=6`TZmo{?)mZDmC7UMBzLGyF9iE zr-@kdzc(cKRbiJq@UCoMsYX;XPkHZ#sc4Tx`MG)efp0oU zo{@VG1<4_YTaibWAE_pKq!Bd+E>xWbt3YtR^+^c2(z zAo~{~iNxgZt(xAfKErc)oDnoubl#edS;;Z%3LnUtj#)>ySUQ;en3Y!+CLwB>0^83g zx2pZ#4VT};+qvJ9Ro#$)RV*e?*=V(+GSO-~AsZUmE(Z}pl!=zBs!$>?E&4yZGWu*I zWaAG_7p;T0kxXx+A;rTYXJzSWLH`AHWFO(tXHc{FjTuh^i+FTg3%>PpTs(>d3S_tYZ6yI?0x^>(#b^MDGu%Fj3b zA(xRDwTR6$?FYea%CJOnH+&0C|IQKV-;e3@*{__egxo0(%$<^!F34aObh$m=@$Pze zldDWd7Nq2fTzV*k?wNP6qxClelu486&mhwsNL$ZQsk-*h!_X%=DGCu^=->DvrU1F(${7=f$MOimaMp zyjz?uMQ3Yz*F|p`bKv{JGq8ghHdgnw9&96i$l+{MxOA^F6dgPhC$gePzsn+xj!nD6 z|Dx38sBhT`j_$s`81^#p1&Cjz3GdH^@P0aNBhlVwU!blcC%3~T*6L9vP_?w}yh^8S zs~~L~B03A%^H!}f9c@6wLt@5*ZdG!fDuvXJ%bK#)R$}otv-!Mekvl6ci97zYVpRF? zB=G=8R!yl|+Fpf#tLT}V%oL`k96XF=6k%7PwpG~-!F_F5w!>EO>j#U5YDqC>^)l$H zrJmI)%O)Q9%&G^9jbXU^W$R$*f}nDWKvp_13FoK(w9__qOXd_x+s+qh+us}vt2(ef zuTtwXoe=1MWIE6{4=ZFg)D&H!&y34-pdY=UR^+13C9MEX2m1Ca!4q{hw@S-q(}6x* z)s_@!zUu>hb=hG#A(FKZV>*9;O$h-*;B@{lBYqWB^x0)MJ(-?CGA>oXTrFVOFTK>x<+Kz};WS8=j)=UTP)ELCYkOV#$sIqvn# zxTaLKZEMqke!6%dp04Om`d(Od(R-HJga{pQr8uL7n(2iC;xnbi-|ymu0xN)=uIO9m z7E?v!X0RWtfuNim!m?9&f&QB61^TNY(BCp0=uZdw(}6yjVbr0XtPT%N2m0v?W{%VO z*(OKTIXyBR=#zOxZdSIY-s#f&bm`r5YiedI-Tc*tnI?Iep9!;~$LT0P+bCbW`ECk@ z_!m|$#P24oLQi;~Sv|2@39I42i+WE@fqM!x0eVp90k9n^A95xksqCA0tiy_N#Nm#> zcd5D72vsne$R3?4A%W6PSf&hrja$-&%|!0yHoiiyLys!(=62U+6T?mwyS`<6(L?BO zx#vvw9zXBJ14*^}%G-X@Ulo3yDe(VpdoUd2}|6WN#KPKjp?6WNyAYR`R3)f#Xf zEfy9eHXi9bb*vxlEXKO%N}4xxK-=)L>>IAzSgK^8_~wd?P0_CGm0x^YRH84H-I0s(dCSjYz@U8vVkUELfvM{YkoX1e{($_5CEp1f8J%&9?P; zFSN2U&|(x(xLCCRIMQWbdmMEDFREQs@uFIL3yL)>NKtJtVBjioF^a*8vesU*plYN} z#Yl!UU96rada;^-=ftbw?FwjoH)l#?A*E~?^+ow|$=L_Ki%|@!}r8xEZdY6U)=28R;}5%E_Vy2B*M@<RVQof2CH0(aS?jpkyQ&ZJ>>Q7Q|vlfAdYT3V~Nxy)s_ER^b~a%MqxlJ`$nVrx;Q z_I2rNp;h-Oef`YEzG5?lRls#|uju&K1tTk7#BxbcsrFamJPsPsvz&bsZ`-MS@TuMl zm}SGvB`AIDQ~M>{hl%hdvtsH^-ad}r(&cVSh1gz^p|huGZDn?#8*94N?6;2H3A)>a z<$-3pkoYE;%J;EjLOi6B19D_+Psv2D*4~F|I*aR`X;m>u)6-KYfw8qk!`1MLnlpo= zL`J>nPk-G~RhhC}>If@BZzAQeDN*R}O;N+UlHi8c1`mOH-;~>l{R!*3@&QyGaD`zrOb!wGa7yRgZ$t`Kl;+!XyD5vKgSK{9 zwN5qoQL)};182HTtXzRS=8D`}*0+CvHeAiM!)za>QHjH<7I_72#alr;os2S+kEtC> z6yY1XGseyVlu;}z2NP9GtHLfmm0Lw6Z|ir1r{b*6hiOzPtoG2tTRJD>U0F27?LAf{ zNpa@xO?x2KYN#fS#kC94EF` z47`|j)&)DZ0G;-KCSj0CUe|rv?ON6fiB~WyFY{trskax?S|M_ngB?A1JA7&}-s4@O z`s`{umreMH>L70g%Vr;(Ez;m1BfIxV(mo6>L&pW$2ZN~T$6LK-vrakqFpaERZ>$iH z3L7Mzeq6-I)Xukkm=Pa+#rYg}7jf^Q1e$4kr1Ox3E9eOx#P$5w+fIEk%rXO5Al(Tc zJ69~BcNo#=yFOK0e>faZtiVCVe+k$^A$(aJ4WqtnXgF~5#1_cvo+CWJ2**T2Q>A>0 z6l^nam@Gx{&fs3b)JFF#)=&)-M86BMB^S}1&{tZ-Krh*r!rog;@~FlX91aE!>bUI_ zAzJ23S?U|5?COa1Ozos(pU z=Y`&S;92l0)!~gz^kw%S@8YLRKos;AHK4hR3`*37qJvf+{Ofk*Naxt%3*=m5hOxd# z+u*{R+=~oU03#-H?-*z;FZ!D_edmGDrq4U`q`y=G&*(cx^aLq3&qFY%7HFqO5J;7@v^8|`BIhx~fsYBy2LW4mG%~ z8SWhZB7_o;mZ{Rs1Zp>XlD``#CCY7&dAiPLKeKl1!$Zpui7j#LSVP6WU?aOXVIoQ; z*u#?{DI14Nr*Deo`-esB zXm+grMOz<+R536^+&NV;#6k+82d9eAwIIl!#5)wwZOvIGxL+niT*%fN=b;;8exMhJ zgvAtgf(tu$T$zx)C6}^sp_-Yfm!!8cZ_E@xAbf*%U?$466k6|(12fT-RP9XEr>vk% zG#z&{6ZNvzUq})91lOl~M}q4`wG&(~YGQ&5PlioK zp$Tp{MeizmSrd$%;QF)^mf!}r$4p$kZ1p3p)TLY#wIjjxk}RI!hBv{z&#EkYBqq4w z(mk!MN|rm*nHwm4%JuIQD@QM8f-AVc=Mr2MWM!G)K2FB9fQUV$$OgNF+}j_$Z!Nb+ zXdi0OnwT{}``>jL5VWUnk8?YXbp9051(b@?OXt|u=F>Kcu|zQBMGcHx!dsuXCX&AC zi3H%3ASo(H(Ez(j;%h+AqHQ?m#N~hz+%F)tEP<+ZJA&5>LnP!5+|k@rPOaVCqvF1X1W8KQtjx^Y?;pF(gx-s4=0iSp2Bl%) zy%MciB+l^x6{loFzK0`r{?aAm8oSb#q`n#$v8#MxK6~X;Cfvi$e_fkmb?2I?vbr;x zLgQuEw{x6H!xUEg-Ph3USX=U6wMVf?^%fGhFD-HKd1 zgIw4Pz!f#FKbU!-lxk87di%Gc#@Amw)B^o{cM6yJXbRkq5Zw4kVw7|qCf1aMhnXr0 zxi%$M4l8PPK5{`Xa989KhL$MEfQp=mOMKG7UWcUK=?P2WL`G8d?5yV7YeC|0ht=7= zWil7@&{a!MwptEJ$HnRNq@7OF3f-ORu}h|2^(Z@w2vFGB(xkVDr;EtSyJiyPl+(Lt z<)RpB8%NR2+jO;t4d*V z@7i!#?Qt)*mqpUG3aAqd>WiL2wNA;sA_Cd6tJ`?0HD!@>?|X5P4Dnh|oup)P?zPHN z$vLXDK+2_Re+AFQJhb~c-Xi7|z+1yCOMqiw;>D*@#rkhKRL!Ehw@r2h)r)FxK=7fO zWl=Aler>4#dYHC~=*TFG`lR$;DC``0gB{Pag$1Ngaaho}=BNyM+hUhF1*#D^|LN~Q zSK-zIUT_oPmZ6F`^a>|MalZtLVP(0SQlw8e65J_qmUt|M(0fwgek=v<4JmN1C%Ey6 zfqADY8i+jKU0hH_2o2A0tgzQ8OkRmZwL;V(+^tnw(TX@mYgsAfnfJka<3sa&C4s12 zxauH*m5W=ErT_AX6gSk~lER&CCb*$D-(HsjcOJow&JM}%Ph`;quVh56aUXGRh^P#E zgbr&FC&+6VMF{RCb5{xpc{QU5!LVWS<0;H@dkWm!K;;*QG-x$bIq&Q8vJz9_Va>UN zu#FE39FP_hyeygD32p8QXmi|QaTbw;S*?i9(Ipgkrg}o%hm#YvWkJqjyOyyPwF!1L1MRu*_ZdPp+@)k~7EKnOf9NoD3lR276IqAC-bx-?9~hVH+uhuc;{#vr5u zQJ)nmFenEO-FJ(w_-h!qMeq0z_(8pO_WM%|=XViep$G8B32x|cKAYgiYXUp&TG@Ss zhG3YETAiuyc}0*7g@%=s6+uz(F2Y@jY9pK6gDTKdzhlNqL8653S5Oq%iuw$L=|fes{IDh;44`bO;O+W(+ry`?@!T_KS6M#^<-vCF^nlMNY+AVlBj4V zYawt(MBXY=*>W}e$rPG>AO$W7Ls?9f#Xx>v3dZ-Qz`cjyh7S6(QqY}AaHGbM;Hr&G zwR+G526YATVIAc`;Cy5N=V)P;Y(E8Tmxi!CT94I4Yt2{5_2GA0@u17uil${^D*O9HL~kwtiQph%ji#>m_folbPWm4o0S<|{{r~^~ diff --git a/src/cool2/cool/parser/utils.py b/src/cool2/cool/parser/utils.py index a72b7e77e..4239adac1 100644 --- a/src/cool2/cool/parser/utils.py +++ b/src/cool2/cool/parser/utils.py @@ -3,18 +3,18 @@ import pickle def save_parser(path,G): + pType = G.pType + G.pType = Production + attributes = [] + for prod in G.Productions: + attributes.append(prod.attributes) + prod.__delattr__('attributes') + parser = LALR1Parser(G) with open(path,'wb') as f: - pType = G.pType - G.pType = Production - attributes = [] - for prod in G.Productions: - attributes.append(prod.attributes) - prod.__delattr__('attributes') - parser = LALR1Parser(G) pickle.dump(parser,f,pickle.HIGHEST_PROTOCOL) - for prod,attr in zip(parser.grammar.Productions,attributes): - prod.attributes = attr - G.pType = pType + for prod,attr in zip(parser.grammar.Productions,attributes): + prod.attributes = attr + G.pType = pType return parser def load_parser(path,G): diff --git a/src/cool2/cool/pipeline.py b/src/cool2/cool/pipeline.py index fd9efbe35..b355c9879 100644 --- a/src/cool2/cool/pipeline.py +++ b/src/cool2/cool/pipeline.py @@ -1,27 +1,60 @@ from cool.pipes.pipes import start_pipe, change_escaped_lines, remove_comments_pipe,\ parse_text_pipe, ast_pipe, type_collector_pipe, build_types_pipe, \ check_types_pipe, run_program_pipe, reconstruct_pipe, void_as_type_pipe, \ - auto_resolver_pipe, string_escape_pipe, tokenize_text_pipe + auto_resolver_pipe, string_escape_pipe, tokenize_text_pipe, remove_comment_tokens_pipe from cool.libs import add_std_pipe -from cool.pipes.pipeline import Pipeline +from cool.pipes.pipeline import Pipeline, Pipe lexer_pipeline = Pipeline(start_pipe, change_escaped_lines, - remove_comments_pipe, tokenize_text_pipe, + remove_comment_tokens_pipe, string_escape_pipe, ) +# lexer_pipeline = Pipeline(start_pipe, +# change_escaped_lines, +# remove_comments_pipe, +# tokenize_text_pipe, +# string_escape_pipe, +# ) syntax_pipeline = Pipeline(parse_text_pipe, ) +pre_semantic_pipeline = Pipeline(ast_pipe, + void_as_type_pipe, + type_collector_pipe, + build_types_pipe, + check_types_pipe, + auto_resolver_pipe, + ) + + +def get_std(): + import os + std_dir = os.path.join(os.path.dirname(__file__),"lib","std.cool") + with open(std_dir, "r") as f: + std = f.read() + return std + +def get_std_context(): + std_pipeline = Pipeline(lexer_pipeline, + syntax_pipeline, + pre_semantic_pipeline) + + std_result = std_pipeline(get_std()) + return std_result['context'] + +def add_std_pipe(result:dict): + std_context = get_std_context() + result['context'] = std_context + return result + +add_std_pipe = Pipe(add_std_pipe) + + semantic_pipeline = Pipeline(add_std_pipe, - ast_pipe, - void_as_type_pipe, - type_collector_pipe, - build_types_pipe, - check_types_pipe, - auto_resolver_pipe, + pre_semantic_pipeline, ) execution_pipeline = Pipeline(run_program_pipe) diff --git a/src/cool2/cool/pipes/pipes.py b/src/cool2/cool/pipes/pipes.py index 313231322..cc196cc86 100644 --- a/src/cool2/cool/pipes/pipes.py +++ b/src/cool2/cool/pipes/pipes.py @@ -5,7 +5,7 @@ from cool.parser.cool_parser import cool_parser from cool.parser.comment_parser import comment_parser from cool.visitors.visitors import * -from cool.grammar.cool_grammar import G +from cool.grammar.cool_grammar import G, comment_open, comment_close from cool.grammar.comment_grammar import C from cool.semantic.scope import Scope from cool.pipes.utils import pprint_tokens, print_errors @@ -38,7 +38,7 @@ def remove_comments_pipe(result:dict, comment_grammar=C, comment_lexer=comment_l """ text = result["text"] - lang = LanguageLR(C, comment_lexer, comment_parser) + lang = LanguageLR(comment_grammar, comment_lexer, comment_parser) errors = [] parse, tokens = lang(text, errors) if not errors: @@ -60,6 +60,43 @@ def remove_comments_pipe(result:dict, comment_grammar=C, comment_lexer=comment_l remove_comments_pipe = Pipe(remove_comments_pipe) +def remove_comment_tokens_pipe(result:dict): + """ + Remove all tokens between (* *) and their respective errors if any + """ + tokens = result["text_tokens"] + errors = result["errors"] + new_tokens = [] + new_errors = [] + deep = 0 + start_comment_position, end_comment_position = None, None + for tok in tokens: + if tok.token_type == comment_open: + deep+=1 + start_comment_position = (tok.lex[1], tok.lex[2]) + elif tok.token_type == comment_close: + deep-=1 + if deep == 0: + end_comment_position = (tok.lex[1], tok.lex[2]) + # Removing errors related to comments + errors = [x for x in errors if not (start_comment_position <= (x.row, x.column) <= end_comment_position)] + elif not deep: + new_tokens.append(tok) + + if result.get("verbose",False): + if errors: + print_errors("Nested Comment Elimination Errors", errors) + + result["errors"] = errors + result["errors"].extend(new_errors) + + result.update({ + "text_tokens": new_tokens + }) + return result + +remove_comment_tokens_pipe = Pipe(remove_comment_tokens_pipe) + def tokenize_text_pipe(result:dict, language_grammar=G, language_lexer=cool_lexer, language_parser=cool_parser): """ Tokenize the text From 2b08ed8e3df464590432192a1baa85c179a8322f Mon Sep 17 00:00:00 2001 From: Luiso Date: Tue, 3 Aug 2021 12:30:55 -0400 Subject: [PATCH 026/143] Semantic errors fixed Some of the remaining lexer caused errors fixed Tests configured --- .vscode/settings.json | 8 +++++++- src/cool2/cool/semantic/type.py | 4 ++-- src/cool2/cool/visitors/visitors.py | 8 ++++---- src/cool2/lib/lang/language.py | 6 ++++-- src/cool2/lib/parser/parser_lr.py | 2 +- tests/conftest.py | 4 +++- 6 files changed, 21 insertions(+), 11 deletions(-) diff --git a/.vscode/settings.json b/.vscode/settings.json index 5c425b56f..7a4a17169 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -1,3 +1,9 @@ { - "python.pythonPath": "/usr/bin/python3" + "python.testing.cwd": "./src", + "python.testing.pytestArgs": [ + ".." + ], + "python.testing.unittestEnabled": false, + "python.testing.nosetestsEnabled": false, + "python.testing.pytestEnabled": true } \ No newline at end of file diff --git a/src/cool2/cool/semantic/type.py b/src/cool2/cool/semantic/type.py index 3c2f2aa19..868bb955e 100644 --- a/src/cool2/cool/semantic/type.py +++ b/src/cool2/cool/semantic/type.py @@ -125,10 +125,10 @@ def join(self,other,current_type): @property def can_have_children(self): return True - + @property def default(self): - return InstantiateNode("Void") + return InstantiateNode(("Void", -1, -1), -1, -1) class ObjectType(Type): def __init__(self): diff --git a/src/cool2/cool/visitors/visitors.py b/src/cool2/cool/visitors/visitors.py index bd1f36604..6db7a51d1 100644 --- a/src/cool2/cool/visitors/visitors.py +++ b/src/cool2/cool/visitors/visitors.py @@ -381,8 +381,8 @@ def visit(self, node): try: parent_type = self.context.get_type(class_decl.parent) except SemanticError as er: - er = SemanticError(UNDEFINED_INHERITED_TYPE, class_decl.id, class_decl.parent) - self.add_semantic_error(er, class_decl.row, class_decl.column) + er = TypeCoolError(UNDEFINED_INHERITED_TYPE, class_decl.id, class_decl.parent) + self.add_semantic_error(er, class_decl.parent_row, class_decl.parent_column) try: if not curr_type.parent: curr_type.set_parent(parent_type) @@ -532,7 +532,7 @@ def visit(self, node, scope): self.visit(node.expr,scope) if not node.expr.type.conforms_to(node.type,self.current_type): - self.add_semantic_error(TypeCoolError(ATTRIBUTE_INCOMPATIBLE_TYPES, node.id, node.type.name, node.expr.type.name),node.row,node.column) + self.add_semantic_error(TypeCoolError(ATTRIBUTE_INCOMPATIBLE_TYPES, node.id, node.type.name, node.expr.type.name),node.expr.row,node.expr.column) @visitor.when(FuncDeclarationNode) def visit(self, node, scope): @@ -643,7 +643,7 @@ def visit(self, node, scope): node.at = self.context.get_type(node.at) if not node.obj.type.conforms_to(node.at,self.current_type): er = TypeCoolError(STATIC_DISPATCH_INCOMPATIBLE_TYPES, node.obj.type.name, node.at.name) - self.add_semantic_error(er, node.row, node.column) + self.add_semantic_error(er, node.obj.row, node.obj.column) dispatch_type = node.at else: dispatch_type = node.obj.type diff --git a/src/cool2/lib/lang/language.py b/src/cool2/lib/lang/language.py index 80a28edc0..14d7896d6 100644 --- a/src/cool2/lib/lang/language.py +++ b/src/cool2/lib/lang/language.py @@ -1,5 +1,5 @@ from cmp.utils import Token - +from cool.error.errors import LexerCoolError class Language(): parser = None @@ -59,7 +59,9 @@ def get_tokens(self, text, errors): for tok in tokens: tok.lex = (tok.lex[0], tok.lex[1] + 1, tok.lex[2] - len(tok.lex[0]) + 1) if tok.token_type == "UNKNOWN": - errors.append(f'({tok.lex[1]}, {tok.lex[2]}) - LexicographicError: ERROR "{tok.lex[0]}"') + er = LexerCoolError('"{}"', tok.lex[0], token=tok) + # errors.append(f'({tok.lex[1]}, {tok.lex[2]}) - LexicographicError: ERROR "{tok.lex[0]}"') + errors.append(er) tokens = [x for x in tokens if x.token_type != "UNKNOWN"] return tokens diff --git a/src/cool2/lib/parser/parser_lr.py b/src/cool2/lib/parser/parser_lr.py index 9d65ac52e..1d5f129a2 100644 --- a/src/cool2/lib/parser/parser_lr.py +++ b/src/cool2/lib/parser/parser_lr.py @@ -118,7 +118,7 @@ def __call__(self, tokens, errors,finding_conflict = False): for i in range(len(tag.Right)): stack.pop() top = stack.pop() - if not top == tag.Right[-(i+1)]: + if top != tag.Right[-(i+1)]: errors.append(f"Productions reduce doesnt match: {top} != {tag.Right[-(i+1)]}") index = self.goto[stack[-1],tag.Left] diff --git a/tests/conftest.py b/tests/conftest.py index 561d8bafc..23c8e65de 100644 --- a/tests/conftest.py +++ b/tests/conftest.py @@ -3,4 +3,6 @@ @pytest.fixture def compiler_path(): - return os.path.abspath('./coolc.sh') \ No newline at end of file + curr = os.path.dirname(__file__) + return os.path.join(curr, "..", "src", 'coolc.sh') + # return os.path.abspath('./coolc.sh') \ No newline at end of file From 0d2cb37c3fb2c4dc4c3b4205cfca2cd7dcdcf2f4 Mon Sep 17 00:00:00 2001 From: Luiso Date: Sun, 8 Aug 2021 11:12:12 -0400 Subject: [PATCH 027/143] The text was been parsed twice. Used dictionary computed parse to avoid issue --- src/cool2/cool/pipes/pipes.py | 7 ++++--- src/cool2/lib/parser/parser_lr.py | 7 +++++-- 2 files changed, 9 insertions(+), 5 deletions(-) diff --git a/src/cool2/cool/pipes/pipes.py b/src/cool2/cool/pipes/pipes.py index cc196cc86..68490593d 100644 --- a/src/cool2/cool/pipes/pipes.py +++ b/src/cool2/cool/pipes/pipes.py @@ -168,11 +168,12 @@ def ast_pipe(result:dict): """ parser = result["parser"] tokens = result["text_tokens"] + text_parsed = result.get("text_parse", None) errors = [] - ast = parser.evaluate(tokens,errors,True) - - result["ast"] = ast + if text_parsed or tokens: + ast = parser.evaluate(tokens,errors,True,text_parsed) + result["ast"] = ast if result.get("verbose", False): if errors: diff --git a/src/cool2/lib/parser/parser_lr.py b/src/cool2/lib/parser/parser_lr.py index 1d5f129a2..b6b1ae3e9 100644 --- a/src/cool2/lib/parser/parser_lr.py +++ b/src/cool2/lib/parser/parser_lr.py @@ -217,14 +217,17 @@ def evaluate_parse(self, right_parse, tokens): return None return result - def evaluate(self,tokens,errors:list,return_ast=False): + def evaluate(self,tokens,errors:list,return_ast=False,parsed_tokens=None): """ If no errors then returns the evaluated tokens\n else fills errors with errors returning None """ parser = self.__call__ new_errors = [] - right_parse = parser(tokens,new_errors) + if parsed_tokens is None: + right_parse = parser(tokens,new_errors) + else: + right_parse = parsed_tokens if not new_errors: right_parse.reverse() ast = self.evaluate_parse(right_parse, tokens) From 31504ff63d26c9f8a8eaed4f18fd20240fbc33a2 Mon Sep 17 00:00:00 2001 From: "Luis E. Dalmau" Date: Mon, 9 Aug 2021 12:20:05 -0400 Subject: [PATCH 028/143] testing changed --- src/testing.cl | 42 ++++++++++++++++++++++++++++++------------ 1 file changed, 30 insertions(+), 12 deletions(-) diff --git a/src/testing.cl b/src/testing.cl index c985484ba..75b4c5bbd 100644 --- a/src/testing.cl +++ b/src/testing.cl @@ -1,19 +1,37 @@ ---The parent-child relation on classes defines a graph. This graph may not contain cycles. +(* An assignment has the form <- *) -class Main inherits IO { - main(): IO { out_string("hi!") }; - - main: IO <- out_string("bye!"); +class Main { + main(): Object { + (new Alpha).print() + }; }; -class A inherits B { - x: Int <- 3; +class Test { + test1: Object; + + testing1(): Int { + 2 + 2 + }; - x(): String { ":)" }; -}; + test2: Int <- 1; + + test3: String <- "1"; -class B inherits A { - y: Int <- 2; + testing2(a: Alpha, b: Int): Int { + 2 + 2 + }; + + testing3(): String { + "2 + 2" + }; + + testing4(): String { + Test1 <- "Hello World" -- Identifiers begin with a lower case letter + }; +}; - div(a: Int, b: Int): Int { a / b}; +class Alpha inherits IO { + print() : Object { + out_string("reached!!\n") + }; }; \ No newline at end of file From 8e7efde702422cb0025fcf21e015c57618ddad54 Mon Sep 17 00:00:00 2001 From: NazDia Date: Sun, 8 Aug 2021 18:50:16 +0200 Subject: [PATCH 029/143] Ply token types map --- src/cool_cmp/lexer/lexer.py | 52 +++++++++++++++++++++++++++++++++++++ 1 file changed, 52 insertions(+) diff --git a/src/cool_cmp/lexer/lexer.py b/src/cool_cmp/lexer/lexer.py index dd4aab397..5ca81baa4 100644 --- a/src/cool_cmp/lexer/lexer.py +++ b/src/cool_cmp/lexer/lexer.py @@ -7,6 +7,7 @@ from cool_cmp.shared.token import ICoolToken from cool_cmp.shared.errors import ErrorTracker from cool_cmp.lexer.errors import LexerCoolError +from cool.grammar import cool_grammar as cool_G import ply.lex as lex class Lex(): @@ -140,6 +141,54 @@ def __init__(self): 'COMMA' ) + tuple(reserved[x] for x in reserved.keys()) + + self.terminals = { + 'IF' : cool_G.ifx, + 'THEN' : cool_G.then, + 'ELSE' : cool_G.elsex, + 'FI' : cool_G.if_r, + 'WHILE' : cool_G.whilex, + 'LOOP' : cool_G.loop, + 'POOL' : cool_G.loop_r, + 'OCUR' : cool_G.ocur, + 'CCUR' : cool_G.ccur, + 'COLON' : cool_G.colon, + 'SEMI' : cool_G.semi, + 'COMMA' : cool_G.comma, + 'DOT' : cool_G.dot, + 'OPAR' : cool_G.opar, + 'CPAR' : cool_G.cpar, + 'PLUS' : cool_G.plus, + 'MINUS' : cool_G.minus, + 'DIV' : cool_G.div, + 'STAR' : cool_G.star, + 'NOT' : cool_G.notx, + 'NEG' : cool_G.roof, + 'LESS' : cool_G.less, + 'LEQ' : cool_G.less_eq, + # 'GREAT' : cool_G.greater, + # 'GEQ' : cool_G.greater_eq, + 'EQ' : cool_G.equal, + 'LET' : cool_G.let, + 'IN' : cool_G.inx, + 'CASE' : cool_G.case, + 'OF' : cool_G.of, + 'ESAC' : cool_G.case_r, + 'SIGNALER' : cool_G.arrow, + 'ASSIGN' : cool_G.assign, + 'TRUE' : cool_G.true, + 'FALSE' : cool_G.false, + 'NUMBER' : cool_G.num, + 'STRING' : cool_G.string, + 'CLASS' : cool_G.classx, + 'INHERITS' : cool_G.inherits, + 'NEW' : cool_G.new, + 'ISVOID' : cool_G.isvoid, + 'OBJECTID' : cool_G.idx, + 'TYPEID' : cool_G.typex, + 'ARROBA' : cool_G.at, + 'COMMENT' : cool_G.comment_open, + 'COMMENT' : cool_G.comment_close} # def t_COMMENTMULTI(t): # r'\(\*(.|\n)*?\*\)' @@ -311,6 +360,9 @@ def __call__(self, program_string:str): self.add_extra_info('text_tokens', result) self.add_extra_info('errors', self.get_errors()) + for token in result: + token.set_type(self.terminals[token.token_type]) + return result def add_error(self, error:LexerCoolError): From 7aecac2d0e397b28d37a619a093aac2acb8d790f Mon Sep 17 00:00:00 2001 From: NazDia Date: Sun, 8 Aug 2021 19:57:17 +0200 Subject: [PATCH 030/143] Added EOF in lexer --- src/cool2/lib/parser/parser_lr.py | 6 ++++++ src/cool_cmp/lexer/lexer.py | 13 ++++++++++--- 2 files changed, 16 insertions(+), 3 deletions(-) diff --git a/src/cool2/lib/parser/parser_lr.py b/src/cool2/lib/parser/parser_lr.py index 1d5f129a2..f8abb64e6 100644 --- a/src/cool2/lib/parser/parser_lr.py +++ b/src/cool2/lib/parser/parser_lr.py @@ -132,6 +132,12 @@ def __call__(self, tokens, errors,finding_conflict = False): else: errors.append(f"Invalid case: {action}") return None if not finding_conflict else (state,lookahead,output) + + if cursor == len(tokens): + errors.append('EOF token missing') + + else: + errors.append('No valid derivation tree can be built with the given tokens') def _register(self, table, key, value): if key in table: diff --git a/src/cool_cmp/lexer/lexer.py b/src/cool_cmp/lexer/lexer.py index 5ca81baa4..19d9c89db 100644 --- a/src/cool_cmp/lexer/lexer.py +++ b/src/cool_cmp/lexer/lexer.py @@ -41,7 +41,7 @@ def __getitem__(self, i): class PlyCoolToken(lex.LexToken, ICoolToken): - def __init__(self, lex:str, typex:str, line:int, column:int): + def __init__(self, lex:str, typex, line:int, column:int): self.set_lex(lex) self.set_type(typex) self.set_position(line, column) @@ -343,11 +343,15 @@ def __call__(self, program_string:str): # print(len(program_string)) self.lexer.input(clean) result = [] + base_line = -1 while True: tok = self.lexer.token() + if base_line < 0: + base_line = tok.lineno - 1 + if not tok: break - result.append(PlyCoolToken(tok.value, tok.type, tok.lineno, self.find_column(program_string, tok))) + result.append(PlyCoolToken(tok.value, tok.type, tok.lineno - base_line, self.find_column(program_string, tok))) if count > 0: self.add_error(LexerCoolError('EOF in comment', PlyCoolToken('', '', lines + 1, len(program_string)))) @@ -362,7 +366,10 @@ def __call__(self, program_string:str): for token in result: token.set_type(self.terminals[token.token_type]) - + + result.append(PlyCoolToken('$', cool_G.G.EOF, lines - 1, len(program_string))) + result[-1].set_position(result[-1].line, self.find_column(program_string, result[-1])) + return result def add_error(self, error:LexerCoolError): From 65be05004054a1eb6f6d996d8b4200347dbe4bca Mon Sep 17 00:00:00 2001 From: NazDia Date: Sun, 8 Aug 2021 21:44:33 +0200 Subject: [PATCH 031/143] Fixed tokens position, and empty tokens in parser --- src/cool2/cool/pipes/pipes.py | 6 +++++- src/cool2/lib/lang/language.py | 18 +++++++++--------- src/cool_cmp/lexer/lexer.py | 13 +++++++------ 3 files changed, 21 insertions(+), 16 deletions(-) diff --git a/src/cool2/cool/pipes/pipes.py b/src/cool2/cool/pipes/pipes.py index 98b24e20d..02a3dec71 100644 --- a/src/cool2/cool/pipes/pipes.py +++ b/src/cool2/cool/pipes/pipes.py @@ -13,6 +13,8 @@ from cool.pipes.utils import pprint_tokens, print_errors from cool.pipes.pipeline import Pipe +ply_lexer = PlyLexer() + def start_pipe(text, verbose = False): """ Start the pipeline @@ -129,7 +131,7 @@ def tokenize_text_pipe(result:dict, language_grammar=G, language_lexer=cool_lexe tokenize_text_pipe = Pipe(tokenize_text_pipe) -def ply_lexer_pipe(result:dict, language_grammar=G, language_lexer=PlyLexer(), language_parser=cool_parser): +def ply_lexer_pipe(result:dict, language_grammar=G, language_lexer=ply_lexer, language_parser=cool_parser): """ Tokenize with ply """ @@ -140,6 +142,8 @@ def ply_lexer_pipe(result:dict, language_grammar=G, language_lexer=PlyLexer(), l errors = [] tokens = lang.get_tokens(text, errors) + errors = language_lexer.get_errors() + result.update({ "parser" : language_parser, "lexer" : language_lexer, diff --git a/src/cool2/lib/lang/language.py b/src/cool2/lib/lang/language.py index 14d7896d6..a0d7286bc 100644 --- a/src/cool2/lib/lang/language.py +++ b/src/cool2/lib/lang/language.py @@ -46,7 +46,7 @@ def __call__(self, text, errors, tokens=None): errors.append(x) if parse_errors: - return [],[] + return [],tokens return parse,tokens @@ -55,14 +55,14 @@ def get_tokens(self, text, errors): Return the text tokens """ tokens = self.lexer(text) - tokens = self._fix_tokens(tokens,errors) - for tok in tokens: - tok.lex = (tok.lex[0], tok.lex[1] + 1, tok.lex[2] - len(tok.lex[0]) + 1) - if tok.token_type == "UNKNOWN": - er = LexerCoolError('"{}"', tok.lex[0], token=tok) - # errors.append(f'({tok.lex[1]}, {tok.lex[2]}) - LexicographicError: ERROR "{tok.lex[0]}"') - errors.append(er) - tokens = [x for x in tokens if x.token_type != "UNKNOWN"] + # tokens = self._fix_tokens(tokens,errors) + # for tok in tokens: + # tok.lex = (tok.lex[0], tok.lex[1] + 1, tok.lex[2] - len(tok.lex[0]) + 1) + # if tok.token_type == "UNKNOWN": + # er = LexerCoolError('"{}"', tok.lex[0], token=tok) + # # errors.append(f'({tok.lex[1]}, {tok.lex[2]}) - LexicographicError: ERROR "{tok.lex[0]}"') + # errors.append(er) + # tokens = [x for x in tokens if x.token_type != "UNKNOWN"] return tokens diff --git a/src/cool_cmp/lexer/lexer.py b/src/cool_cmp/lexer/lexer.py index 19d9c89db..e29b2d53e 100644 --- a/src/cool_cmp/lexer/lexer.py +++ b/src/cool_cmp/lexer/lexer.py @@ -60,8 +60,8 @@ def set_type(self, typex:str): def set_position(self, line:int, column:int): self.lex.set_row(line) self.value.set_row(line) - self.lex.set_column(line) - self.value.set_column(line) + self.lex.set_column(column) + self.value.set_column(column) self.lineno = line self.line = line self.column = column @@ -303,6 +303,7 @@ def t_error(t): self.lexer = lex.lex() def __call__(self, program_string:str): + self.error_tracker = ErrorTracker() count = 0 lines = 0 passes = 0 @@ -346,12 +347,12 @@ def __call__(self, program_string:str): base_line = -1 while True: tok = self.lexer.token() - if base_line < 0: - base_line = tok.lineno - 1 + # if base_line < 0: + # base_line = tok.lineno - 1 if not tok: break - result.append(PlyCoolToken(tok.value, tok.type, tok.lineno - base_line, self.find_column(program_string, tok))) + result.append(PlyCoolToken(tok.value, tok.type, tok.lineno, self.find_column(program_string, tok))) if count > 0: self.add_error(LexerCoolError('EOF in comment', PlyCoolToken('', '', lines + 1, len(program_string)))) @@ -367,7 +368,7 @@ def __call__(self, program_string:str): for token in result: token.set_type(self.terminals[token.token_type]) - result.append(PlyCoolToken('$', cool_G.G.EOF, lines - 1, len(program_string))) + result.append(PlyCoolToken('$', cool_G.G.EOF, lines + 1, len(program_string))) result[-1].set_position(result[-1].line, self.find_column(program_string, result[-1])) return result From e30a37d6f5036f4e442262f21901cfdc98a0a0dc Mon Sep 17 00:00:00 2001 From: "Luis E. Dalmau" Date: Mon, 9 Aug 2021 15:22:11 -0400 Subject: [PATCH 032/143] .. for ../tests --- .vscode/settings.json | 2 +- .../test_reconstruction/test_SELF_TYPE.cl | 146 ++++---- src/cool2/test/test_reconstruction/test_at.cl | 92 ++--- .../test_reconstruction/test_attr_dep_1.cl | 38 +- .../test_reconstruction/test_attr_dep_2.cl | 38 +- .../test/test_reconstruction/test_auto_1.cl | 46 +-- .../test/test_reconstruction/test_auto_2.cl | 32 +- .../test/test_reconstruction/test_auto_3.cl | 32 +- .../test/test_reconstruction/test_auto_4.cl | 82 ++--- .../test/test_reconstruction/test_auto_5.cl | 82 ++--- .../test_auto_conditional.cl | 344 +++++++++--------- .../test_reconstruction/test_auto_defaults.cl | 28 +- .../test_auto_undecidable.cl | 66 ++-- .../test_reconstruction/test_case_no_type.cl | 48 +-- .../test_reconstruction/test_conditional.cl | 186 +++++----- .../test_reconstruction/test_entry_point_3.cl | 22 +- .../test_reconstruction/test_escape_line.cl | 22 +- .../test_reconstruction/test_instances.cl | 70 ++-- .../test/test_reconstruction/test_isvoid.cl | 40 +- .../test/test_reconstruction/test_let.cl | 100 ++--- .../test/test_reconstruction/test_other.cl | 56 +-- .../test_reconstruction/test_recursive.cl | 60 +-- .../test/test_reconstruction/test_substr_1.cl | 62 ++-- .../test/test_reconstruction/test_substr_2.cl | 34 +- .../test/test_reconstruction/test_substr_3.cl | 34 +- .../test_reconstruction/test_void_dispatch.cl | 24 +- .../test/test_reconstruction/test_while.cl | 40 +- .../test_reconstruction/test_zero_division.cl | 22 +- 28 files changed, 924 insertions(+), 924 deletions(-) diff --git a/.vscode/settings.json b/.vscode/settings.json index 7a4a17169..4a89b1bab 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -1,7 +1,7 @@ { "python.testing.cwd": "./src", "python.testing.pytestArgs": [ - ".." + "../tests" ], "python.testing.unittestEnabled": false, "python.testing.nosetestsEnabled": false, diff --git a/src/cool2/test/test_reconstruction/test_SELF_TYPE.cl b/src/cool2/test/test_reconstruction/test_SELF_TYPE.cl index dd9d742d2..609a3f8f2 100644 --- a/src/cool2/test/test_reconstruction/test_SELF_TYPE.cl +++ b/src/cool2/test/test_reconstruction/test_SELF_TYPE.cl @@ -1,73 +1,73 @@ -class A -{ - a:SELF_TYPE; - get(): SELF_TYPE - { - self - }; - get2(): SELF_TYPE - { - new SELF_TYPE - }; - get3(): SELF_TYPE - { - { - a <- - new SELF_TYPE; - a; - } - }; -} - - -class B inherits A -{ - get4(): SELF_TYPE - { - self - .get3( - ) - }; -} - - -class Main inherits IO -{ - main(): String - { - { - a:A <- - new A; - b:B <- - new B; - b - .get( - ) - .type_name( - ) - .concat( - b - .get2( - ) - .type_name( - ) - .concat( - b - .get3( - ) - .type_name( - ) - ) - .concat( - b - .get4( - ) - .type_name( - ) - ) - ); - } - }; -} - - +class A +{ + a:SELF_TYPE; + get(): SELF_TYPE + { + self + }; + get2(): SELF_TYPE + { + new SELF_TYPE + }; + get3(): SELF_TYPE + { + { + a <- + new SELF_TYPE; + a; + } + }; +} + + +class B inherits A +{ + get4(): SELF_TYPE + { + self + .get3( + ) + }; +} + + +class Main inherits IO +{ + main(): String + { + { + a:A <- + new A; + b:B <- + new B; + b + .get( + ) + .type_name( + ) + .concat( + b + .get2( + ) + .type_name( + ) + .concat( + b + .get3( + ) + .type_name( + ) + ) + .concat( + b + .get4( + ) + .type_name( + ) + ) + ); + } + }; +} + + diff --git a/src/cool2/test/test_reconstruction/test_at.cl b/src/cool2/test/test_reconstruction/test_at.cl index 6986ef90e..cfef6f95f 100644 --- a/src/cool2/test/test_reconstruction/test_at.cl +++ b/src/cool2/test/test_reconstruction/test_at.cl @@ -1,46 +1,46 @@ -class A -{ - name(): String - { - "class A " - .concat( - self - .name2( - ) - ) - }; - name2(): String - { - "class A" - }; -} - - -class B inherits A -{ - name(): String - { - "class B" - }; - name2(): String - { - "class B" - }; -} - - -class Main inherits IO -{ - main(): String - { - { - b:B <- - new B; - b - @A.name( - ); - } - }; -} - - +class A +{ + name(): String + { + "class A " + .concat( + self + .name2( + ) + ) + }; + name2(): String + { + "class A" + }; +} + + +class B inherits A +{ + name(): String + { + "class B" + }; + name2(): String + { + "class B" + }; +} + + +class Main inherits IO +{ + main(): String + { + { + b:B <- + new B; + b + @A.name( + ); + } + }; +} + + diff --git a/src/cool2/test/test_reconstruction/test_attr_dep_1.cl b/src/cool2/test/test_reconstruction/test_attr_dep_1.cl index 70e20f2d1..d9b99bb88 100644 --- a/src/cool2/test/test_reconstruction/test_attr_dep_1.cl +++ b/src/cool2/test/test_reconstruction/test_attr_dep_1.cl @@ -1,19 +1,19 @@ -class Test -{ - test:Int <- 10; - test2:Int <- 20; -} - - -class Main inherits Test -{ - test3:Int <- (test + test2); - main(): Int - { - { - test3; - } - }; -} - - +class Test +{ + test:Int <- 10; + test2:Int <- 20; +} + + +class Main inherits Test +{ + test3:Int <- (test + test2); + main(): Int + { + { + test3; + } + }; +} + + diff --git a/src/cool2/test/test_reconstruction/test_attr_dep_2.cl b/src/cool2/test/test_reconstruction/test_attr_dep_2.cl index df43af892..78dea4533 100644 --- a/src/cool2/test/test_reconstruction/test_attr_dep_2.cl +++ b/src/cool2/test/test_reconstruction/test_attr_dep_2.cl @@ -1,19 +1,19 @@ -class Test -{ - test3:Int <- (test + test2); - test2:Int <- (2 * test); - test:Int <- 10; -} - - -class Main inherits Test -{ - main(): Int - { - { - test3; - } - }; -} - - +class Test +{ + test3:Int <- (test + test2); + test2:Int <- (2 * test); + test:Int <- 10; +} + + +class Main inherits Test +{ + main(): Int + { + { + test3; + } + }; +} + + diff --git a/src/cool2/test/test_reconstruction/test_auto_1.cl b/src/cool2/test/test_reconstruction/test_auto_1.cl index 2a07c1345..0446f20db 100644 --- a/src/cool2/test/test_reconstruction/test_auto_1.cl +++ b/src/cool2/test/test_reconstruction/test_auto_1.cl @@ -1,23 +1,23 @@ -class Main inherits IO -{ - main(): Main - { - let - x:Int <- - (3 + 2) - in - { - case - x - of - y:Int => - self - .out_string( - "Ok" - ); - esac; - } - }; -} - - +class Main inherits IO +{ + main(): Main + { + let + x:Int <- + (3 + 2) + in + { + case + x + of + y:Int => + self + .out_string( + "Ok" + ); + esac; + } + }; +} + + diff --git a/src/cool2/test/test_reconstruction/test_auto_2.cl b/src/cool2/test/test_reconstruction/test_auto_2.cl index c87705cbd..87f525ed9 100644 --- a/src/cool2/test/test_reconstruction/test_auto_2.cl +++ b/src/cool2/test/test_reconstruction/test_auto_2.cl @@ -1,16 +1,16 @@ -class Main inherits IO -{ - main(): Int - { - self - .succ( - 10 - ) - }; - succ(n:Int): Int - { - (n + 1) - }; -} - - +class Main inherits IO +{ + main(): Int + { + self + .succ( + 10 + ) + }; + succ(n:Int): Int + { + (n + 1) + }; +} + + diff --git a/src/cool2/test/test_reconstruction/test_auto_3.cl b/src/cool2/test/test_reconstruction/test_auto_3.cl index ef3818cfb..aab18e038 100644 --- a/src/cool2/test/test_reconstruction/test_auto_3.cl +++ b/src/cool2/test/test_reconstruction/test_auto_3.cl @@ -1,16 +1,16 @@ -class Main inherits IO -{ - succ(n:Int): Int - { - (n + 1) - }; - main(): Int - { - self - .succ( - 10 - ) - }; -} - - +class Main inherits IO +{ + succ(n:Int): Int + { + (n + 1) + }; + main(): Int + { + self + .succ( + 10 + ) + }; +} + + diff --git a/src/cool2/test/test_reconstruction/test_auto_4.cl b/src/cool2/test/test_reconstruction/test_auto_4.cl index 844adf578..d0b0c3d43 100644 --- a/src/cool2/test/test_reconstruction/test_auto_4.cl +++ b/src/cool2/test/test_reconstruction/test_auto_4.cl @@ -1,41 +1,41 @@ -class Main inherits IO -{ - main(): Int - { - self - .ackermann( - 0, - 3 - ) - }; - ackermann(m:Int, n:Int): Int - { - if - (m = 0) - then - (n + 1) - else - if - (n = 0) - then - self - .ackermann( - (m - 1), - 1 - ) - else - self - .ackermann( - (m - 1), - self - .ackermann( - m, - (n - 1) - ) - ) - fi - fi - }; -} - - +class Main inherits IO +{ + main(): Int + { + self + .ackermann( + 0, + 3 + ) + }; + ackermann(m:Int, n:Int): Int + { + if + (m = 0) + then + (n + 1) + else + if + (n = 0) + then + self + .ackermann( + (m - 1), + 1 + ) + else + self + .ackermann( + (m - 1), + self + .ackermann( + m, + (n - 1) + ) + ) + fi + fi + }; +} + + diff --git a/src/cool2/test/test_reconstruction/test_auto_5.cl b/src/cool2/test/test_reconstruction/test_auto_5.cl index 8e83caae7..f69476f80 100644 --- a/src/cool2/test/test_reconstruction/test_auto_5.cl +++ b/src/cool2/test/test_reconstruction/test_auto_5.cl @@ -1,41 +1,41 @@ -class Main -{ - main(): Object - { - self - .f( - 1, - 1 - ) - }; - f(a:Int, b:Int): Object - { - if - (a = 1) - then - b - else - self - .g( - (a + 1), - (b / 2) - ) - fi - }; - g(a:Int, b:Int): Object - { - if - (b = 1) - then - a - else - self - .f( - (a / 2), - (b + 1) - ) - fi - }; -} - - +class Main +{ + main(): AUTO_TYPE + { + self + .f( + 1, + 1 + ) + }; + f(a:Int, b:Int): AUTO_TYPE + { + if + (a = 1) + then + b + else + self + .g( + (a + 1), + (b / 2) + ) + fi + }; + g(a:Int, b:Int): AUTO_TYPE + { + if + (b = 1) + then + a + else + self + .f( + (a / 2), + (b + 1) + ) + fi + }; +} + + diff --git a/src/cool2/test/test_reconstruction/test_auto_conditional.cl b/src/cool2/test/test_reconstruction/test_auto_conditional.cl index 765d4e844..0a446f922 100644 --- a/src/cool2/test/test_reconstruction/test_auto_conditional.cl +++ b/src/cool2/test/test_reconstruction/test_auto_conditional.cl @@ -1,172 +1,172 @@ -class Base -{ - base(): Base - { - self - }; -} - - -class OtraBase -{ - otraBase(): OtraBase - { - self - }; -} - - -class ConcBase1 inherits Base -{ -} - - -class ConcBase2 inherits OtraBase -{ -} - - -class Main -{ - a:Object; - main(): Int - { - { - a:OtraBase <- - if - true - then - new OtraBase - else - new ConcBase2 - fi; - b:ConcBase2 <- - if - true - then - new ConcBase2 - else - new ConcBase2 - fi; - c:Object <- - if - true - then - new ConcBase1 - else - new ConcBase2 - fi; - d:Object <- - if - true - then - self - .ret1( - ) - else - self - .ret2( - ) - fi; - e:Object <- - if - true - then - self - .ret1( - new ConcBase1 - ) - else - self - .ret2( - new ConcBase2 - ) - fi; - 1; - } - }; - ret1(): Object - { - c:Object <- - if - true - then - new ConcBase1 - else - new ConcBase2 - fi - }; - ret2(): OtraBase - { - c:OtraBase <- - if - true - then - new OtraBase - else - new ConcBase2 - fi - }; - ret1(param:Base): Object - { - { - c:Base <- - param - .base( - ); - a:Object <- - case - param - of - b:Base => - if - true - then - b - else - param - fi; - b:OtraBase => - if - false - then - b - else - param - fi; - esac; - } - }; - ret2(param:OtraBase): Base - { - { - c:OtraBase <- - param - .otraBase( - ); - a:Base <- - case - param - of - b:Base => - if - true - then - b - else - b - fi; - b:ConcBase1 => - if - true - then - b - else - b - fi; - esac; - } - }; -} - - +class Base +{ + base(): Base + { + self + }; +} + + +class OtraBase +{ + otraBase(): OtraBase + { + self + }; +} + + +class ConcBase1 inherits Base +{ +} + + +class ConcBase2 inherits OtraBase +{ +} + + +class Main +{ + a:AUTO_TYPE <- new Void; + main(): Int + { + { + a:OtraBase <- + if + true + then + new OtraBase + else + new ConcBase2 + fi; + b:ConcBase2 <- + if + true + then + new ConcBase2 + else + new ConcBase2 + fi; + c:Object <- + if + true + then + new ConcBase1 + else + new ConcBase2 + fi; + d:AUTO_TYPE <- + if + true + then + self + .ret1( + ) + else + self + .ret2( + ) + fi; + e:AUTO_TYPE <- + if + true + then + self + .ret1( + new ConcBase1 + ) + else + self + .ret2( + new ConcBase2 + ) + fi; + 1; + } + }; + ret1(): Object + { + c:Object <- + if + true + then + new ConcBase1 + else + new ConcBase2 + fi + }; + ret2(): OtraBase + { + c:OtraBase <- + if + true + then + new OtraBase + else + new ConcBase2 + fi + }; + ret1(param:Base): Object + { + { + c:Base <- + param + .base( + ); + a:Object <- + case + param + of + b:Base => + if + true + then + b + else + param + fi; + b:OtraBase => + if + false + then + b + else + param + fi; + esac; + } + }; + ret2(param:OtraBase): Base + { + { + c:OtraBase <- + param + .otraBase( + ); + a:Base <- + case + param + of + b:Base => + if + true + then + b + else + b + fi; + b:ConcBase1 => + if + true + then + b + else + b + fi; + esac; + } + }; +} + + diff --git a/src/cool2/test/test_reconstruction/test_auto_defaults.cl b/src/cool2/test/test_reconstruction/test_auto_defaults.cl index e02ae50fc..b0dfda219 100644 --- a/src/cool2/test/test_reconstruction/test_auto_defaults.cl +++ b/src/cool2/test/test_reconstruction/test_auto_defaults.cl @@ -1,14 +1,14 @@ -class Main -{ - a:Int; - main(): Int - { - let - b:Int <- - 0 - in - ((a + b) + 10) - }; -} - - +class Main +{ + a:Int; + main(): Int + { + let + b:Int <- + 0 + in + ((a + b) + 10) + }; +} + + diff --git a/src/cool2/test/test_reconstruction/test_auto_undecidable.cl b/src/cool2/test/test_reconstruction/test_auto_undecidable.cl index 068c0f652..3d0d950e9 100644 --- a/src/cool2/test/test_reconstruction/test_auto_undecidable.cl +++ b/src/cool2/test/test_reconstruction/test_auto_undecidable.cl @@ -1,33 +1,33 @@ -class A -{ - m1(): Int - { - 10 - }; -} - - -class B -{ - m1(): Int - { - 20 - }; -} - - -class Main inherits IO -{ - main(): Int - { - 10 - }; - m(a:AUTO_TYPE): Int - { - a - .m1( - ) - }; -} - - +class A +{ + m1(): Int + { + 10 + }; +} + + +class B +{ + m1(): Int + { + 20 + }; +} + + +class Main inherits IO +{ + main(): Int + { + 10 + }; + m(a:AUTO_TYPE): Int + { + a + .m1( + ) + }; +} + + diff --git a/src/cool2/test/test_reconstruction/test_case_no_type.cl b/src/cool2/test/test_reconstruction/test_case_no_type.cl index 4c786ee93..2970e7d95 100644 --- a/src/cool2/test/test_reconstruction/test_case_no_type.cl +++ b/src/cool2/test/test_reconstruction/test_case_no_type.cl @@ -1,24 +1,24 @@ -class Main -{ - main(): Object - { - { - a:Object <- - case - (20 + 40) - of - a:Bool => - if - a - then - true - else - false - fi; - esac; - a; - } - }; -} - - +class Main +{ + main(): Object + { + { + a:Object <- + case + (20 + 40) + of + a:Bool => + if + a + then + true + else + false + fi; + esac; + a; + } + }; +} + + diff --git a/src/cool2/test/test_reconstruction/test_conditional.cl b/src/cool2/test/test_reconstruction/test_conditional.cl index c2f764a09..a82e2e576 100644 --- a/src/cool2/test/test_reconstruction/test_conditional.cl +++ b/src/cool2/test/test_reconstruction/test_conditional.cl @@ -1,93 +1,93 @@ -class A -{ -} - - -class B inherits A -{ -} - - -class C inherits B -{ -} - - -class D inherits B -{ -} - - -class E inherits A -{ -} - - -class Main inherits IO -{ - main(): Int - { - { - d:Object <- - if - true - then - new E - else - new C - fi; - e:B <- - if - true - then - new C - else - new D - fi; - a:Int <- - if - true - then - 1 - else - 0 - fi; - b:Int <- - if - false - then - 0 - else - 1 - fi; - c:Int <- - if - if - (1 > 2) - then - (2 > 1) - else - (2 > 1) - fi - then - 1 - else - 0 - fi; - self - .out_string( - d - .type_name( - ) - ) - .out_string( - e - .type_name( - ) - ); - ((a + b) + c); - } - }; -} - - +class A +{ +} + + +class B inherits A +{ +} + + +class C inherits B +{ +} + + +class D inherits B +{ +} + + +class E inherits A +{ +} + + +class Main inherits IO +{ + main(): Int + { + { + d:Object <- + if + true + then + new E + else + new C + fi; + e:B <- + if + true + then + new C + else + new D + fi; + a:Int <- + if + true + then + 1 + else + 0 + fi; + b:Int <- + if + false + then + 0 + else + 1 + fi; + c:Int <- + if + if + (1 > 2) + then + (2 > 1) + else + (2 > 1) + fi + then + 1 + else + 0 + fi; + self + .out_string( + d + .type_name( + ) + ) + .out_string( + e + .type_name( + ) + ); + ((a + b) + c); + } + }; +} + + diff --git a/src/cool2/test/test_reconstruction/test_entry_point_3.cl b/src/cool2/test/test_reconstruction/test_entry_point_3.cl index 9d6b17562..0a1f6c54e 100644 --- a/src/cool2/test/test_reconstruction/test_entry_point_3.cl +++ b/src/cool2/test/test_reconstruction/test_entry_point_3.cl @@ -1,11 +1,11 @@ -class NoMain -{ - main(): Int - { - { - 30; - } - }; -} - - +class NoMain +{ + main(): Int + { + { + 30; + } + }; +} + + diff --git a/src/cool2/test/test_reconstruction/test_escape_line.cl b/src/cool2/test/test_reconstruction/test_escape_line.cl index a17294169..1e952ba9f 100644 --- a/src/cool2/test/test_reconstruction/test_escape_line.cl +++ b/src/cool2/test/test_reconstruction/test_escape_line.cl @@ -1,11 +1,11 @@ -class Main -{ - main(): String - { - { - "Hello People"; - } - }; -} - - +class Main +{ + main(): String + { + { + "Hello People"; + } + }; +} + + diff --git a/src/cool2/test/test_reconstruction/test_instances.cl b/src/cool2/test/test_reconstruction/test_instances.cl index 4a7a3104c..b228613c0 100644 --- a/src/cool2/test/test_reconstruction/test_instances.cl +++ b/src/cool2/test/test_reconstruction/test_instances.cl @@ -1,35 +1,35 @@ -class Main -{ - a:Int; - set(v:Int): Int - { - a <- - v - }; - get(): Int - { - a - }; - main(): Int - { - { - a <- - 10; - b:Main <- - self - .copy( - ); - a <- - 20; - ( b - .get( - ) - + self - .get( - ) -); - } - }; -} - - +class Main +{ + a:Int; + set(v:Int): Int + { + a <- + v + }; + get(): Int + { + a + }; + main(): Int + { + { + a <- + 10; + b:Main <- + self + .copy( + ); + a <- + 20; + ( b + .get( + ) + + self + .get( + ) +); + } + }; +} + + diff --git a/src/cool2/test/test_reconstruction/test_isvoid.cl b/src/cool2/test/test_reconstruction/test_isvoid.cl index ee0360060..52758c9e0 100644 --- a/src/cool2/test/test_reconstruction/test_isvoid.cl +++ b/src/cool2/test/test_reconstruction/test_isvoid.cl @@ -1,20 +1,20 @@ -class Main inherits IO -{ - a:Int; - s:String; - o:Object; - main(): Bool - { - { - a:Int <- - 10; - b:Bool <- - isvoid a; - c:Bool <- - isvoid o; - c; - } - }; -} - - +class Main inherits IO +{ + a:Int; + s:String; + o:Object; + main(): Bool + { + { + a:Int <- + 10; + b:Bool <- + isvoid a; + c:Bool <- + isvoid o; + c; + } + }; +} + + diff --git a/src/cool2/test/test_reconstruction/test_let.cl b/src/cool2/test/test_reconstruction/test_let.cl index b8ede9da5..22c92ecc6 100644 --- a/src/cool2/test/test_reconstruction/test_let.cl +++ b/src/cool2/test/test_reconstruction/test_let.cl @@ -1,50 +1,50 @@ -class Main -{ - b:Bool; - main(): Int - { - { - a:Int <- - let - a:Bool <- - true, - a:Int <- - if - a - then - 10 - else - 20 - fi - in - a; - c:Int <- - let - a:SELF_TYPE <- - self - .change( - ), - b:Int <- - if - b - then - 10 - else - 20 - fi - in - b; - (a + c); - } - }; - change(): SELF_TYPE - { - { - b <- - not b; - self; - } - }; -} - - +class Main +{ + b:Bool; + main(): Int + { + { + a:Int <- + let + a:Bool <- + true, + a:Int <- + if + a + then + 10 + else + 20 + fi + in + a; + c:Int <- + let + a:SELF_TYPE <- + self + .change( + ), + b:Int <- + if + b + then + 10 + else + 20 + fi + in + b; + (a + c); + } + }; + change(): SELF_TYPE + { + { + b <- + not b; + self; + } + }; +} + + diff --git a/src/cool2/test/test_reconstruction/test_other.cl b/src/cool2/test/test_reconstruction/test_other.cl index 77a2f0ed0..eeca2e9b8 100644 --- a/src/cool2/test/test_reconstruction/test_other.cl +++ b/src/cool2/test/test_reconstruction/test_other.cl @@ -1,28 +1,28 @@ -class A -{ - a:Int; - a(a:Object): Object - { - a - }; -} - - -class B inherits A -{ - a(self:Int): Object - { - a - }; -} - - -class Main inherits IO -{ - main(): B - { - new B - }; -} - - +class A +{ + a:Int; + a(a:Object): Object + { + a + }; +} + + +class B inherits A +{ + a(self:Int): Object + { + a + }; +} + + +class Main inherits IO +{ + main(): B + { + new B + }; +} + + diff --git a/src/cool2/test/test_reconstruction/test_recursive.cl b/src/cool2/test/test_reconstruction/test_recursive.cl index e04a33207..531d729bc 100644 --- a/src/cool2/test/test_reconstruction/test_recursive.cl +++ b/src/cool2/test/test_reconstruction/test_recursive.cl @@ -1,30 +1,30 @@ -class Main -{ - main(): Int - { - self - .fibo( - 7 - ) - }; - fibo(n:Int): Int - { - if - (n > 1) - then - ( self - .fibo( - (n - 1) - ) - + self - .fibo( - (n - 2) - ) -) - else - 1 - fi - }; -} - - +class Main +{ + main(): Int + { + self + .fibo( + 7 + ) + }; + fibo(n:Int): Int + { + if + (n > 1) + then + ( self + .fibo( + (n - 1) + ) + + self + .fibo( + (n - 2) + ) +) + else + 1 + fi + }; +} + + diff --git a/src/cool2/test/test_reconstruction/test_substr_1.cl b/src/cool2/test/test_reconstruction/test_substr_1.cl index a4d37f6b0..ccd6f75e2 100644 --- a/src/cool2/test/test_reconstruction/test_substr_1.cl +++ b/src/cool2/test/test_reconstruction/test_substr_1.cl @@ -1,31 +1,31 @@ -class Main -{ - main(): String - { - { - s:String <- - "0123456789"; - s - .substr( - 0, - 10 - ) - .concat( - s - .substr( - 0, - 5 - ) - ) - .concat( - s - .substr( - 5, - 5 - ) - ); - } - }; -} - - +class Main +{ + main(): String + { + { + s:String <- + "0123456789"; + s + .substr( + 0, + 10 + ) + .concat( + s + .substr( + 0, + 5 + ) + ) + .concat( + s + .substr( + 5, + 5 + ) + ); + } + }; +} + + diff --git a/src/cool2/test/test_reconstruction/test_substr_2.cl b/src/cool2/test/test_reconstruction/test_substr_2.cl index aba3766d7..3f7e50d6b 100644 --- a/src/cool2/test/test_reconstruction/test_substr_2.cl +++ b/src/cool2/test/test_reconstruction/test_substr_2.cl @@ -1,17 +1,17 @@ -class Main -{ - main(): String - { - { - s:String <- - "0123456789"; - s - .substr( - 0, - ~ 1 - ); - } - }; -} - - +class Main +{ + main(): String + { + { + s:String <- + "0123456789"; + s + .substr( + 0, + ~ 1 + ); + } + }; +} + + diff --git a/src/cool2/test/test_reconstruction/test_substr_3.cl b/src/cool2/test/test_reconstruction/test_substr_3.cl index f69a37a7c..59b4fcb22 100644 --- a/src/cool2/test/test_reconstruction/test_substr_3.cl +++ b/src/cool2/test/test_reconstruction/test_substr_3.cl @@ -1,17 +1,17 @@ -class Main -{ - main(): String - { - { - s:String <- - "0123456789"; - s - .substr( - 0, - 11 - ); - } - }; -} - - +class Main +{ + main(): String + { + { + s:String <- + "0123456789"; + s + .substr( + 0, + 11 + ); + } + }; +} + + diff --git a/src/cool2/test/test_reconstruction/test_void_dispatch.cl b/src/cool2/test/test_reconstruction/test_void_dispatch.cl index 4e4d88461..12023ab3a 100644 --- a/src/cool2/test/test_reconstruction/test_void_dispatch.cl +++ b/src/cool2/test/test_reconstruction/test_void_dispatch.cl @@ -1,12 +1,12 @@ -class Main -{ - b:Object; - main(): String - { - b - .type_name( - ) - }; -} - - +class Main +{ + b:Object; + main(): String + { + b + .type_name( + ) + }; +} + + diff --git a/src/cool2/test/test_reconstruction/test_while.cl b/src/cool2/test/test_reconstruction/test_while.cl index 8a86fc216..dbc37dd2c 100644 --- a/src/cool2/test/test_reconstruction/test_while.cl +++ b/src/cool2/test/test_reconstruction/test_while.cl @@ -1,20 +1,20 @@ -class Main inherits IO -{ - main(): Int - { - { - counter:Int <- - 0; - a:Object <- - while - (counter < 10) - loop - counter <- - (counter + 1) - pool; - counter; - } - }; -} - - +class Main inherits IO +{ + main(): Int + { + { + counter:Int <- + 0; + a:Object <- + while + (counter < 10) + loop + counter <- + (counter + 1) + pool; + counter; + } + }; +} + + diff --git a/src/cool2/test/test_reconstruction/test_zero_division.cl b/src/cool2/test/test_reconstruction/test_zero_division.cl index 5e683a3a2..d17986555 100644 --- a/src/cool2/test/test_reconstruction/test_zero_division.cl +++ b/src/cool2/test/test_reconstruction/test_zero_division.cl @@ -1,11 +1,11 @@ -class Main -{ - main(): Int - { - { - (10 / 0); - } - }; -} - - +class Main +{ + main(): Int + { + { + (10 / 0); + } + }; +} + + From 909786566b5613d676554e266d8a1b701603d5ee Mon Sep 17 00:00:00 2001 From: Luiso Date: Thu, 16 Sep 2021 21:52:36 -0400 Subject: [PATCH 033/143] PlyLexer added to cool2 package --- src/cool2/cool/error/error_tracker.py | 16 ++ src/cool2/cool/lexer/ply_cool_lexer.py | 371 +++++++++++++++++++++++++ src/cool2/cool/pipes/pipes.py | 3 +- 3 files changed, 388 insertions(+), 2 deletions(-) create mode 100644 src/cool2/cool/error/error_tracker.py create mode 100644 src/cool2/cool/lexer/ply_cool_lexer.py diff --git a/src/cool2/cool/error/error_tracker.py b/src/cool2/cool/error/error_tracker.py new file mode 100644 index 000000000..edfd609a5 --- /dev/null +++ b/src/cool2/cool/error/error_tracker.py @@ -0,0 +1,16 @@ +from typing import List +from cool2.cool.error.errors import CoolError + +class ErrorTracker(): + """ + Basic Error tracking system + """ + + def __init__(self): + self.__errors = [] + + def add_error(self, error:CoolError): + self.__errors.append(error) + + def get_errors(self)->List[CoolError]: + return self.__errors \ No newline at end of file diff --git a/src/cool2/cool/lexer/ply_cool_lexer.py b/src/cool2/cool/lexer/ply_cool_lexer.py new file mode 100644 index 000000000..1b90d973e --- /dev/null +++ b/src/cool2/cool/lexer/ply_cool_lexer.py @@ -0,0 +1,371 @@ +from typing import List, Tuple + +from cool2.cool.error.error_tracker import ErrorTracker +from cool2.cool.error.errors import LexerCoolError +from cool.grammar import cool_grammar as cool_G +import ply.lex as lex + +class Lex(): + def __init__(self, lex): + self.lex = lex + + def set_type(self, new): + self.type = new + + def set_row(self, new): + self.row = new + + def set_column(self, new): + self.column = new + + def __str__(self): + return self.lex + + def __getitem__(self, i): + if i == 0: + return self.lex + + elif i == 1: + return self.row + + elif i == 2: + return self.column + + elif i == 3: + return self.type + +class PlyCoolToken(lex.LexToken): + + def __init__(self, lex:str, typex, line:int, column:int): + self.set_lex(lex) + self.set_type(typex) + self.set_position(line, column) + + def set_lex(self, lex:str): + self.lex = Lex(lex) + self.value = Lex(lex) + + def set_type(self, typex:str): + self.lex.set_type(typex) + self.value.set_type(typex) + self.type = typex + self.token_type = typex + self.typex = typex + + def set_position(self, line:int, column:int): + self.lex.set_row(line) + self.value.set_row(line) + self.lex.set_column(column) + self.value.set_column(column) + self.lineno = line + self.line = line + self.column = column + self.lexpos = column + + def get_lex(self)->str: + return self.lex + + def get_type(self)->str: + return self.typex + + def get_position(self)->Tuple[int,int]: + return (self.line, self.column) + + def __str__(self): + return f"{self.lex}:{self.type} Line {self.line} Column{self.column}" + + def __repr__(self): + return str(self) + +class PlyLexer(): + + @staticmethod + def find_column(input, token): + line_start = input.rfind('\n', 0, token.lexpos) + 1 + return (token.lexpos - line_start) + 1 + + def __init__(self): + self.error_tracker = ErrorTracker() # Error tracker implementation + + reserved = { + 'if' : 'IF', + 'then' : 'THEN', + 'fi' : 'FI', + 'else' : 'ELSE', + 'case' : 'CASE', + 'of' : 'OF', + 'esac' : 'ESAC', + 'class' : 'CLASS', + 'inherits' : 'INHERITS', + 'let' : 'LET', + 'in' : 'IN', + 'while' : 'WHILE', + 'loop' : 'LOOP', + 'pool' : 'POOL', + 'new' : 'NEW', + 'isvoid' : 'ISVOID', + 'not' : 'NOT', + 'true' : 'TRUE', + 'false' : 'FALSE' + } + + tokens = ( + 'OBJECTID', + 'TYPEID', + 'NUMBER', + 'PLUS', + 'MINUS', + 'STAR', + 'DIV', + 'EQ', + 'LEQ', + 'LESS', + 'NEG', + 'OPAR', + 'CPAR', + 'OCUR', + 'CCUR', + 'ASSIGN', + 'SEMI', + 'COLON', + 'SIGNALER', + 'ARROBA', + 'STRING', + 'COMMENT', + 'DOT', + 'COMMA' + ) + tuple(reserved[x] for x in reserved.keys()) + + + self.terminals = { + 'IF' : cool_G.ifx, + 'THEN' : cool_G.then, + 'ELSE' : cool_G.elsex, + 'FI' : cool_G.if_r, + 'WHILE' : cool_G.whilex, + 'LOOP' : cool_G.loop, + 'POOL' : cool_G.loop_r, + 'OCUR' : cool_G.ocur, + 'CCUR' : cool_G.ccur, + 'COLON' : cool_G.colon, + 'SEMI' : cool_G.semi, + 'COMMA' : cool_G.comma, + 'DOT' : cool_G.dot, + 'OPAR' : cool_G.opar, + 'CPAR' : cool_G.cpar, + 'PLUS' : cool_G.plus, + 'MINUS' : cool_G.minus, + 'DIV' : cool_G.div, + 'STAR' : cool_G.star, + 'NOT' : cool_G.notx, + 'NEG' : cool_G.roof, + 'LESS' : cool_G.less, + 'LEQ' : cool_G.less_eq, + # 'GREAT' : cool_G.greater, + # 'GEQ' : cool_G.greater_eq, + 'EQ' : cool_G.equal, + 'LET' : cool_G.let, + 'IN' : cool_G.inx, + 'CASE' : cool_G.case, + 'OF' : cool_G.of, + 'ESAC' : cool_G.case_r, + 'SIGNALER' : cool_G.arrow, + 'ASSIGN' : cool_G.assign, + 'TRUE' : cool_G.true, + 'FALSE' : cool_G.false, + 'NUMBER' : cool_G.num, + 'STRING' : cool_G.string, + 'CLASS' : cool_G.classx, + 'INHERITS' : cool_G.inherits, + 'NEW' : cool_G.new, + 'ISVOID' : cool_G.isvoid, + 'OBJECTID' : cool_G.idx, + 'TYPEID' : cool_G.typex, + 'ARROBA' : cool_G.at, + 'COMMENT' : cool_G.comment_open, + 'COMMENT' : cool_G.comment_close} + + # def t_COMMENTMULTI(t): + # r'\(\*(.|\n)*?\*\)' + # t.lexer.lineno += t.value.count("\n") + + # def t_COMMENTMULTIUNFINISHED(t): + # r'\(\*(.|\n)*' + # t.lexer.lineno += t.value.count("\n") + # msg = 'EOF in comment' + # self.add_error(LexerCoolError(msg, PlyCoolToken(t.value, t.type, t.lexer.lineno - 1, t.lexer.lexpos - 1))) + + def t_STRING(t): + r'"([^\r\n"\\]|(\\\n)|(\\.)){0,1024}"' + t.lexer.lineno += t.value.count("\n") + null_ch = 'String contains null character' + for i in range(len(t.value)): + if t.value[i] == '\x00': + pos = t.lexer.lexpos - (len(t.value) - i) + line = t.lexer.lineno - t.value[:i].count("\n") + self.add_error(LexerCoolError(null_ch, PlyCoolToken(t.value, t.type, line, pos))) + return t + + def t_STRINGUNFINISHED(t): + r'"([^\r\n"\\]|(\\\n)|(\\.)){0,1024}\n' + t.lexer.lineno += t.value.count("\n") + null_ch = 'String contains null character' + for i in range(len(t.value)): + if t.value[i] == '\x00': + pos = t.lexer.lexpos - (len(t.value) - i) + line = t.lexer.lineno - t.value[:i].count("\n") + self.add_error(LexerCoolError(null_ch, PlyCoolToken(t.value, t.type, line, pos))) + msg = 'Unterminated string constant' + self.add_error(LexerCoolError(msg, PlyCoolToken(t.value, t.type, t.lexer.lineno - 1, t.lexer.lexpos - 1))) + + def t_STRINGUNFINISHEDEOF(t): + r'"([^\r\n"\\]|(\\\n)|(\\.)){0,1024}' + t.lexer.lineno += t.value.count("\n") + null_ch = 'String contains null character' + for i in range(len(t.value)): + if t.value[i] == '\x00': + pos = t.lexer.lexpos - (len(t.value) - i) + line = t.lexer.lineno - t.value[:i].count("\n") + self.add_error(LexerCoolError(null_ch, PlyCoolToken(t.value, t.type, line, pos))) + msg = 'EOF in string constant' + self.add_error(LexerCoolError(msg, PlyCoolToken(t.value, t.type, t.lexer.lineno, t.lexer.lexpos))) + + def t_NUMBER(t): + r'\d+' + try: + int(t.value) + except ValueError: + msg = "Integer value too large %d", t.value + self.add_error(LexerCoolError(msg, PlyCoolToken(t.value, t.type, t.lineno, t.lexpos))) # TODO Set Token column + t.value = 'Invalid' + return t + + def t_OBJECTID(t): + r'[a-z][a-zA-Z0-9_]*' + low = t.value.lower() + t.type = reserved.get(low,'OBJECTID') + return t + + def t_TYPEID(t): + r'[A-Z][a-zA-Z0-9_]*' + low = t.value.lower() + if low == 'true': + t.type = 'TYPEID' + + elif low == 'false': + t.type = 'TYPEID' + + else: + t.type = reserved.get(low, 'TYPEID') + + return t + + def t_COMMENTSINGLE(t): + r'(--.*)' + + + t_PLUS = r'\+' + t_MINUS = r'-' + t_STAR = r'\*' + t_DIV = r'/' + t_OPAR = r'\(' + t_CPAR = r'\)' + t_OCUR = r'\{' + t_CCUR = r'\}' + t_EQ = r'=' + t_ASSIGN = r'<-' + t_LEQ = r'<=' + t_LESS = r'<' + t_NEG = r'~' + t_SEMI = r';' + t_COLON = r':' + t_SIGNALER = r'=>' + t_ARROBA = r'@' + t_DOT = r'\.' + t_COMMA = r',' + + t_ignore = " \t" + + def t_newline(t): + r'\n+' + t.lexer.lineno += t.value.count("\n") + + def t_error(t): + msg = f'ERROR "{t.value[0]}"' + self.add_error(LexerCoolError(msg, PlyCoolToken(t.value[0], t.type, t.lineno, t.lexpos))) # TODO Set Token column + t.lexer.skip(1) + + self.lexer = lex.lex() + + def __call__(self, program_string:str): + self.error_tracker = ErrorTracker() + count = 0 + lines = 0 + passes = 0 + semi_clean_string = [] + for i in range(len(program_string)): + if passes > 0: + passes -= 1 + elif program_string[i] == '(' and i + 1 < len(program_string) and program_string[i + 1] == '*': + count += 1 + semi_clean_string.append(' ') + semi_clean_string.append(' ') + if i + 2 < len(program_string) and program_string[i + 2] == ')': + semi_clean_string.append(' ') + passes = 2 + + else: + passes = 1 + + elif program_string[i] == '*' and i + 1 < len(program_string) and program_string[i + 1] == ')' and count > 0: + count -= 1 + semi_clean_string.append(' ') + semi_clean_string.append(' ') + passes = 1 + + elif count > 0 and program_string[i] != '\n': + semi_clean_string.append(' ') + + elif program_string[i] == '\n': + semi_clean_string.append(program_string[i]) + lines += 1 + + else: + semi_clean_string.append(program_string[i]) + + clean = ''.join(semi_clean_string) + # print(clean) + # print(len(clean)) + # print(len(program_string)) + self.lexer.input(clean) + result = [] + base_line = -1 + while True: + tok = self.lexer.token() + # if base_line < 0: + # base_line = tok.lineno - 1 + + if not tok: + break + result.append(PlyCoolToken(tok.value, tok.type, tok.lineno, self.find_column(program_string, tok))) + + if count > 0: + self.add_error(LexerCoolError('EOF in comment', PlyCoolToken('', '', lines + 1, len(program_string)))) + + for error in self.error_tracker.get_errors(): + error.token.set_position(error.token.lineno, self.find_column(program_string, error.token)) + + for token in result: + token.set_type(self.terminals[token.token_type]) + + result.append(PlyCoolToken('$', cool_G.G.EOF, lines + 1, len(program_string))) + result[-1].set_position(result[-1].line, self.find_column(program_string, result[-1])) + + return result + + def add_error(self, error:LexerCoolError): + self.error_tracker.add_error(error) + + def get_errors(self)->List[LexerCoolError]: + errors = self.error_tracker.get_errors() + return errors diff --git a/src/cool2/cool/pipes/pipes.py b/src/cool2/cool/pipes/pipes.py index ad2ec90e3..99333f9c7 100644 --- a/src/cool2/cool/pipes/pipes.py +++ b/src/cool2/cool/pipes/pipes.py @@ -1,7 +1,6 @@ from cool.lexer.cool_lexer import cool_lexer -# from cool.lexer.lexer import PlyLexer -from cool_cmp.lexer.lexer import PlyLexer +from cool2.cool.lexer.ply_cool_lexer import PlyLexer from cool.lexer.comment_lexer import comment_lexer from lib.lang.language_lr import LanguageLR from cool.parser.cool_parser import cool_parser From d40628a7eba1dc37781d885394f9b5ffb36fb2d7 Mon Sep 17 00:00:00 2001 From: Luiso Date: Thu, 16 Sep 2021 21:58:22 -0400 Subject: [PATCH 034/143] Removed cool2 module access inside cool2 package --- src/cool2/cool/error/error_tracker.py | 2 +- src/cool2/cool/lexer/ply_cool_lexer.py | 4 ++-- src/cool2/cool/pipes/pipes.py | 2 +- src/cool2/main.py | 6 +++--- 4 files changed, 7 insertions(+), 7 deletions(-) diff --git a/src/cool2/cool/error/error_tracker.py b/src/cool2/cool/error/error_tracker.py index edfd609a5..a40af7fab 100644 --- a/src/cool2/cool/error/error_tracker.py +++ b/src/cool2/cool/error/error_tracker.py @@ -1,5 +1,5 @@ from typing import List -from cool2.cool.error.errors import CoolError +from cool.error.errors import CoolError class ErrorTracker(): """ diff --git a/src/cool2/cool/lexer/ply_cool_lexer.py b/src/cool2/cool/lexer/ply_cool_lexer.py index 1b90d973e..985ffec26 100644 --- a/src/cool2/cool/lexer/ply_cool_lexer.py +++ b/src/cool2/cool/lexer/ply_cool_lexer.py @@ -1,7 +1,7 @@ from typing import List, Tuple -from cool2.cool.error.error_tracker import ErrorTracker -from cool2.cool.error.errors import LexerCoolError +from cool.error.error_tracker import ErrorTracker +from cool.error.errors import LexerCoolError from cool.grammar import cool_grammar as cool_G import ply.lex as lex diff --git a/src/cool2/cool/pipes/pipes.py b/src/cool2/cool/pipes/pipes.py index 99333f9c7..11a23610f 100644 --- a/src/cool2/cool/pipes/pipes.py +++ b/src/cool2/cool/pipes/pipes.py @@ -1,6 +1,6 @@ from cool.lexer.cool_lexer import cool_lexer -from cool2.cool.lexer.ply_cool_lexer import PlyLexer +from cool.lexer.ply_cool_lexer import PlyLexer from cool.lexer.comment_lexer import comment_lexer from lib.lang.language_lr import LanguageLR from cool.parser.cool_parser import cool_parser diff --git a/src/cool2/main.py b/src/cool2/main.py index 159f59052..eb661b4e3 100644 --- a/src/cool2/main.py +++ b/src/cool2/main.py @@ -1,6 +1,6 @@ -import os, sys -base_dir = os.path.dirname(__file__) -sys.path.append(os.path.join(base_dir, "..")) +# import os, sys +# base_dir = os.path.dirname(__file__) +# sys.path.append(os.path.join(base_dir, "..")) from cool.pipeline import cool_pipeline, reconstr_pipeline from cool.grammar.cool_grammar import G From 0f8b5f2b829e11c03a11628fe000517434ebeb1a Mon Sep 17 00:00:00 2001 From: Luiso Date: Thu, 16 Sep 2021 22:26:43 -0400 Subject: [PATCH 035/143] Folder structure changed --- .vscode/launch.json | 2 +- src/cool2/__init__.py | 5 - src/cool2/cool/lexer/comment_lexer.obj | Bin 6215 -> 0 bytes src/cool2/cool/lexer/cool_lexer.obj | Bin 139984 -> 0 bytes src/cool2/cool/lib/std.cool | 35 -- src/cool2/cool/parser/comment_parser.obj | Bin 2591 -> 0 bytes src/cool2/cool/parser/cool_parser.obj | Bin 151549 -> 0 bytes src/cool2/lib/shortcut.py | 12 - src/cool_cmp/__init__.py | 8 +- src/cool_cmp/cil/__init__.py | 15 - src/cool_cmp/cil/errors.py | 5 - src/cool_cmp/cil/interface.py | 10 - src/cool_cmp/cil/visitors/__init__.py | 3 - src/{cool2 => cool_cmp}/cmp/__init__.py | 0 src/{cool2 => cool_cmp}/cmp/ast.py | 0 src/{cool2 => cool_cmp}/cmp/automata.py | 0 src/{cool2 => cool_cmp}/cmp/cil.py | 0 src/{cool2 => cool_cmp}/cmp/evaluation.py | 0 src/{cool2 => cool_cmp}/cmp/languages.py | 0 src/{cool2 => cool_cmp}/cmp/nbpackage.py | 0 src/{cool2 => cool_cmp}/cmp/pycompiler.py | 0 src/{cool2 => cool_cmp}/cmp/semantic.py | 0 src/{cool2 => cool_cmp}/cmp/tools/__init__.py | 0 src/{cool2 => cool_cmp}/cmp/tools/automata.py | 0 .../cmp/tools/evaluation.py | 0 src/{cool2 => cool_cmp}/cmp/tools/parsing.py | 0 src/{cool2 => cool_cmp}/cmp/tools/regex.py | 0 src/{cool2 => cool_cmp}/cmp/utils.py | 0 src/{cool2 => cool_cmp}/cmp/visitor.py | 0 .../cool => cool_cmp/cmp_tools}/__init__.py | 0 .../cmp_tools}/grammar/grammar_fixer.py | 4 +- .../cmp_tools}/grammar/grammar_parser.py | 4 +- .../cmp_tools}/grammar/grammar_tokens.py | 4 +- .../cmp_tools}/grammar/grammar_util.py | 2 +- .../cmp_tools}/lang/language.py | 0 .../cmp_tools}/lang/language_ll1.py | 12 +- .../cmp_tools}/lang/language_lr.py | 8 +- .../cmp_tools}/lang/language_regular.py | 12 +- .../lib => cool_cmp/cmp_tools}/lexer/lexer.py | 2 +- .../lib => cool_cmp/cmp_tools}/lexer/regex.py | 4 +- .../cmp_tools/parser}/__init__.py | 0 .../cmp_tools}/parser/parser.py | 0 .../cmp_tools}/parser/parser_ll1.py | 6 +- .../cmp_tools}/parser/parser_lr.py | 8 +- src/cool_cmp/cmp_tools/shortcut.py | 12 + .../cmp_tools}/utils/algorithms.py | 0 .../cmp_tools}/utils/automaton.py | 0 .../cmp_tools}/utils/first_follow.py | 0 .../lib => cool_cmp/cmp_tools}/utils/trie.py | 0 .../lib/parser => cool_cmp/cool}/__init__.py | 0 src/{cool2 => cool_cmp}/cool/ast/ast.py | 0 .../cool/error/error_tracker.py | 0 src/{cool2 => cool_cmp}/cool/error/errors.py | 0 .../cool/grammar/comment_grammar.py | 0 .../cool/grammar/cool_grammar.py | 0 src/cool_cmp/cool/lexer/comment_lexer.obj | Bin 0 -> 6221 bytes .../cool/lexer/comment_lexer.py | 6 +- src/cool_cmp/cool/lexer/cool_lexer.obj | Bin 0 -> 139990 bytes .../cool/lexer/cool_lexer.py | 6 +- .../cool/lexer/ply_cool_lexer.py | 0 src/{cool2 => cool_cmp}/cool/lexer/utils.py | 2 +- src/{cool2 => cool_cmp}/cool/libs.py | 0 src/cool_cmp/cool/parser/comment_parser.obj | Bin 0 -> 2597 bytes .../cool/parser/comment_parser.py | 6 +- src/cool_cmp/cool/parser/cool_parser.obj | Bin 0 -> 151555 bytes .../cool/parser/cool_parser.py | 6 +- src/{cool2 => cool_cmp}/cool/parser/utils.py | 2 +- src/{cool2 => cool_cmp}/cool/pipeline.py | 0 .../cool/pipes/pipeline.py | 0 src/{cool2 => cool_cmp}/cool/pipes/pipes.py | 2 +- src/{cool2 => cool_cmp}/cool/pipes/utils.py | 0 .../cool/semantic/atomic.py | 0 .../cool/semantic/context.py | 0 .../cool/semantic/operations.py | 0 .../cool/semantic/scope.py | 0 src/{cool2 => cool_cmp}/cool/semantic/type.py | 0 .../cool/visitors/utils.py | 0 .../cool/visitors/visitors.py | 0 .../cool_programs/program1.cl | 0 .../cool_programs/program1.cl.infer.cl | 0 src/cool_cmp/lexer/__init__.py | 35 -- src/cool_cmp/lexer/errors.py | 22 - src/cool_cmp/lexer/interface.py | 11 - src/cool_cmp/lexer/lexer.py | 381 ------------------ src/cool_cmp/lexer/lexer2.py | 73 ---- src/{cool2 => cool_cmp}/main.py | 0 src/cool_cmp/mips/__init__.py | 15 - src/cool_cmp/mips/errors.py | 5 - src/cool_cmp/mips/interface.py | 12 - src/cool_cmp/mips/visitors/__init__.py | 3 - src/cool_cmp/parser/__init__.py | 18 - src/cool_cmp/parser/errors.py | 23 -- src/cool_cmp/parser/interface.py | 13 - src/cool_cmp/parser/parser2.py | 32 -- src/cool_cmp/semantic/__init__.py | 16 - src/cool_cmp/semantic/errors.py | 41 -- src/cool_cmp/semantic/implementations.py | 247 ------------ src/cool_cmp/semantic/interface.py | 134 ------ src/cool_cmp/semantic/tests/__init__.py | 0 .../semantic/tests/collector_visitor_test.py | 0 src/cool_cmp/semantic/tests/context_test.py | 0 src/cool_cmp/semantic/tests/scope_test.py | 0 src/cool_cmp/semantic/tests/type_test.py | 44 -- src/cool_cmp/semantic/types.py | 45 --- src/cool_cmp/semantic/visitors/__init__.py | 3 - .../semantic/visitors/type_collector.py | 121 ------ src/cool_cmp/semantic/visitors/utils.py | 47 --- src/cool_cmp/shared/__init__.py | 82 ---- src/cool_cmp/shared/ast/__init__.py | 22 - src/cool_cmp/shared/ast/cil.py | 4 - src/cool_cmp/shared/ast/cool.py | 263 ------------ src/cool_cmp/shared/ast/mips.py | 4 - src/cool_cmp/shared/errors.py | 46 --- src/cool_cmp/shared/pipeline/__init__.py | 15 - src/cool_cmp/shared/pipeline/pipes.py | 39 -- src/cool_cmp/shared/token.py | 30 -- src/cool_cmp/shared/visitor.py | 80 ---- src/{cool2 => cool_cmp}/test/__init__.py | 0 src/{cool2 => cool_cmp}/test/test_class.py | 0 .../test/test_data/test_SELF_TYPE.meta_test | 0 .../test/test_data/test_at.meta_test | 0 .../test/test_data/test_attr_dep_1.meta_test | 0 .../test/test_data/test_attr_dep_2.meta_test | 0 .../test/test_data/test_auto_1.meta_test | 0 .../test/test_data/test_auto_2.meta_test | 0 .../test/test_data/test_auto_3.meta_test | 0 .../test/test_data/test_auto_4.meta_test | 0 .../test/test_data/test_auto_5.meta_test | 0 .../test_data/test_auto_conditional.meta_test | 0 .../test_data/test_auto_defaults.meta_test | 0 .../test_data/test_auto_undecidable.meta_test | 0 .../test/test_data/test_case.meta_test | 0 .../test_data/test_case_no_type.meta_test | 0 .../test_case_repeated_type.meta_test | 0 .../test_data/test_case_void_eval.meta_test | 0 .../test/test_data/test_comments.meta_test | 0 .../test/test_data/test_conditional.meta_test | 0 .../test/test_data/test_default.meta_test | 0 .../test_data/test_entry_point_1.meta_test | 0 .../test_data/test_entry_point_2.meta_test | 0 .../test_data/test_entry_point_3.meta_test | 0 .../test/test_data/test_equality.meta_test | 0 .../test/test_data/test_escape_line.meta_test | 0 .../test/test_data/test_in_out.meta_test | 0 .../test/test_data/test_instances.meta_test | 0 .../test/test_data/test_isvoid.meta_test | 0 .../test/test_data/test_let.meta_test | 0 .../test/test_data/test_other.meta_test | 0 .../test/test_data/test_recursive.meta_test | 0 .../test/test_data/test_substr_1.meta_test | 0 .../test/test_data/test_substr_2.meta_test | 0 .../test/test_data/test_substr_3.meta_test | 0 .../test_data/test_void_dispatch.meta_test | 0 .../test/test_data/test_while.meta_test | 0 .../test_data/test_zero_division.meta_test | 0 .../test_reconstruction/test_SELF_TYPE.cl | 0 .../test/test_reconstruction/test_at.cl | 0 .../test_reconstruction/test_attr_dep_1.cl | 0 .../test_reconstruction/test_attr_dep_2.cl | 0 .../test/test_reconstruction/test_auto_1.cl | 0 .../test/test_reconstruction/test_auto_2.cl | 0 .../test/test_reconstruction/test_auto_3.cl | 0 .../test/test_reconstruction/test_auto_4.cl | 0 .../test/test_reconstruction/test_auto_5.cl | 0 .../test_auto_conditional.cl | 0 .../test_reconstruction/test_auto_defaults.cl | 0 .../test_auto_undecidable.cl | 0 .../test/test_reconstruction/test_case.cl | 0 .../test_reconstruction/test_case_no_type.cl | 0 .../test_case_repeated_type.cl | 0 .../test_case_void_eval.cl | 0 .../test/test_reconstruction/test_comments.cl | 0 .../test_reconstruction/test_conditional.cl | 0 .../test/test_reconstruction/test_default.cl | 0 .../test_reconstruction/test_entry_point_1.cl | 0 .../test_reconstruction/test_entry_point_2.cl | 0 .../test_reconstruction/test_entry_point_3.cl | 0 .../test/test_reconstruction/test_equality.cl | 0 .../test_reconstruction/test_escape_line.cl | 0 .../test_reconstruction/test_instances.cl | 0 .../test/test_reconstruction/test_isvoid.cl | 0 .../test/test_reconstruction/test_let.cl | 0 .../test/test_reconstruction/test_other.cl | 0 .../test_reconstruction/test_recursive.cl | 0 .../test/test_reconstruction/test_substr_1.cl | 0 .../test/test_reconstruction/test_substr_2.cl | 0 .../test/test_reconstruction/test_substr_3.cl | 0 .../test_reconstruction/test_void_dispatch.cl | 0 .../test/test_reconstruction/test_while.cl | 0 .../test_reconstruction/test_zero_division.cl | 0 .../test/test_run/01_program.cl | 0 .../test/test_run/02_program.cl | 0 .../test/test_run/03_program.cl | 0 .../test/test_run/04_program.cl | 0 .../test/test_run/05_program.cl | 0 src/{cool2 => cool_cmp}/test/tests.py | 0 196 files changed, 74 insertions(+), 2055 deletions(-) delete mode 100644 src/cool2/__init__.py delete mode 100644 src/cool2/cool/lexer/comment_lexer.obj delete mode 100644 src/cool2/cool/lexer/cool_lexer.obj delete mode 100644 src/cool2/cool/lib/std.cool delete mode 100644 src/cool2/cool/parser/comment_parser.obj delete mode 100644 src/cool2/cool/parser/cool_parser.obj delete mode 100644 src/cool2/lib/shortcut.py delete mode 100644 src/cool_cmp/cil/__init__.py delete mode 100644 src/cool_cmp/cil/errors.py delete mode 100644 src/cool_cmp/cil/interface.py delete mode 100644 src/cool_cmp/cil/visitors/__init__.py rename src/{cool2 => cool_cmp}/cmp/__init__.py (100%) rename src/{cool2 => cool_cmp}/cmp/ast.py (100%) rename src/{cool2 => cool_cmp}/cmp/automata.py (100%) rename src/{cool2 => cool_cmp}/cmp/cil.py (100%) rename src/{cool2 => cool_cmp}/cmp/evaluation.py (100%) rename src/{cool2 => cool_cmp}/cmp/languages.py (100%) rename src/{cool2 => cool_cmp}/cmp/nbpackage.py (100%) rename src/{cool2 => cool_cmp}/cmp/pycompiler.py (100%) rename src/{cool2 => cool_cmp}/cmp/semantic.py (100%) rename src/{cool2 => cool_cmp}/cmp/tools/__init__.py (100%) rename src/{cool2 => cool_cmp}/cmp/tools/automata.py (100%) rename src/{cool2 => cool_cmp}/cmp/tools/evaluation.py (100%) rename src/{cool2 => cool_cmp}/cmp/tools/parsing.py (100%) rename src/{cool2 => cool_cmp}/cmp/tools/regex.py (100%) rename src/{cool2 => cool_cmp}/cmp/utils.py (100%) rename src/{cool2 => cool_cmp}/cmp/visitor.py (100%) rename src/{cool2/cool => cool_cmp/cmp_tools}/__init__.py (100%) rename src/{cool2/lib => cool_cmp/cmp_tools}/grammar/grammar_fixer.py (96%) rename src/{cool2/lib => cool_cmp/cmp_tools}/grammar/grammar_parser.py (94%) rename src/{cool2/lib => cool_cmp/cmp_tools}/grammar/grammar_tokens.py (94%) rename src/{cool2/lib => cool_cmp/cmp_tools}/grammar/grammar_util.py (86%) rename src/{cool2/lib => cool_cmp/cmp_tools}/lang/language.py (100%) rename src/{cool2/lib => cool_cmp/cmp_tools}/lang/language_ll1.py (83%) rename src/{cool2/lib => cool_cmp/cmp_tools}/lang/language_lr.py (83%) rename src/{cool2/lib => cool_cmp/cmp_tools}/lang/language_regular.py (94%) rename src/{cool2/lib => cool_cmp/cmp_tools}/lexer/lexer.py (95%) rename src/{cool2/lib => cool_cmp/cmp_tools}/lexer/regex.py (94%) rename src/{cool2/lib => cool_cmp/cmp_tools/parser}/__init__.py (100%) rename src/{cool2/lib => cool_cmp/cmp_tools}/parser/parser.py (100%) rename src/{cool2/lib => cool_cmp/cmp_tools}/parser/parser_ll1.py (95%) rename src/{cool2/lib => cool_cmp/cmp_tools}/parser/parser_lr.py (96%) create mode 100644 src/cool_cmp/cmp_tools/shortcut.py rename src/{cool2/lib => cool_cmp/cmp_tools}/utils/algorithms.py (100%) rename src/{cool2/lib => cool_cmp/cmp_tools}/utils/automaton.py (100%) rename src/{cool2/lib => cool_cmp/cmp_tools}/utils/first_follow.py (100%) rename src/{cool2/lib => cool_cmp/cmp_tools}/utils/trie.py (100%) rename src/{cool2/lib/parser => cool_cmp/cool}/__init__.py (100%) rename src/{cool2 => cool_cmp}/cool/ast/ast.py (100%) rename src/{cool2 => cool_cmp}/cool/error/error_tracker.py (100%) rename src/{cool2 => cool_cmp}/cool/error/errors.py (100%) rename src/{cool2 => cool_cmp}/cool/grammar/comment_grammar.py (100%) rename src/{cool2 => cool_cmp}/cool/grammar/cool_grammar.py (100%) create mode 100644 src/cool_cmp/cool/lexer/comment_lexer.obj rename src/{cool2 => cool_cmp}/cool/lexer/comment_lexer.py (85%) create mode 100644 src/cool_cmp/cool/lexer/cool_lexer.obj rename src/{cool2 => cool_cmp}/cool/lexer/cool_lexer.py (92%) rename src/{cool2 => cool_cmp}/cool/lexer/ply_cool_lexer.py (100%) rename src/{cool2 => cool_cmp}/cool/lexer/utils.py (87%) rename src/{cool2 => cool_cmp}/cool/libs.py (100%) create mode 100644 src/cool_cmp/cool/parser/comment_parser.obj rename src/{cool2 => cool_cmp}/cool/parser/comment_parser.py (70%) create mode 100644 src/cool_cmp/cool/parser/cool_parser.obj rename src/{cool2 => cool_cmp}/cool/parser/cool_parser.py (70%) rename src/{cool2 => cool_cmp}/cool/parser/utils.py (88%) rename src/{cool2 => cool_cmp}/cool/pipeline.py (100%) rename src/{cool2 => cool_cmp}/cool/pipes/pipeline.py (100%) rename src/{cool2 => cool_cmp}/cool/pipes/pipes.py (95%) rename src/{cool2 => cool_cmp}/cool/pipes/utils.py (100%) rename src/{cool2 => cool_cmp}/cool/semantic/atomic.py (100%) rename src/{cool2 => cool_cmp}/cool/semantic/context.py (100%) rename src/{cool2 => cool_cmp}/cool/semantic/operations.py (100%) rename src/{cool2 => cool_cmp}/cool/semantic/scope.py (100%) rename src/{cool2 => cool_cmp}/cool/semantic/type.py (100%) rename src/{cool2 => cool_cmp}/cool/visitors/utils.py (100%) rename src/{cool2 => cool_cmp}/cool/visitors/visitors.py (100%) rename src/{cool2 => cool_cmp}/cool_programs/program1.cl (100%) rename src/{cool2 => cool_cmp}/cool_programs/program1.cl.infer.cl (100%) delete mode 100644 src/cool_cmp/lexer/__init__.py delete mode 100644 src/cool_cmp/lexer/errors.py delete mode 100644 src/cool_cmp/lexer/interface.py delete mode 100644 src/cool_cmp/lexer/lexer.py delete mode 100644 src/cool_cmp/lexer/lexer2.py rename src/{cool2 => cool_cmp}/main.py (100%) delete mode 100644 src/cool_cmp/mips/__init__.py delete mode 100644 src/cool_cmp/mips/errors.py delete mode 100644 src/cool_cmp/mips/interface.py delete mode 100644 src/cool_cmp/mips/visitors/__init__.py delete mode 100644 src/cool_cmp/parser/__init__.py delete mode 100644 src/cool_cmp/parser/errors.py delete mode 100644 src/cool_cmp/parser/interface.py delete mode 100644 src/cool_cmp/parser/parser2.py delete mode 100644 src/cool_cmp/semantic/__init__.py delete mode 100644 src/cool_cmp/semantic/errors.py delete mode 100644 src/cool_cmp/semantic/implementations.py delete mode 100644 src/cool_cmp/semantic/interface.py delete mode 100644 src/cool_cmp/semantic/tests/__init__.py delete mode 100644 src/cool_cmp/semantic/tests/collector_visitor_test.py delete mode 100644 src/cool_cmp/semantic/tests/context_test.py delete mode 100644 src/cool_cmp/semantic/tests/scope_test.py delete mode 100644 src/cool_cmp/semantic/tests/type_test.py delete mode 100644 src/cool_cmp/semantic/types.py delete mode 100644 src/cool_cmp/semantic/visitors/__init__.py delete mode 100644 src/cool_cmp/semantic/visitors/type_collector.py delete mode 100644 src/cool_cmp/semantic/visitors/utils.py delete mode 100644 src/cool_cmp/shared/__init__.py delete mode 100644 src/cool_cmp/shared/ast/__init__.py delete mode 100644 src/cool_cmp/shared/ast/cil.py delete mode 100644 src/cool_cmp/shared/ast/cool.py delete mode 100644 src/cool_cmp/shared/ast/mips.py delete mode 100644 src/cool_cmp/shared/errors.py delete mode 100644 src/cool_cmp/shared/pipeline/__init__.py delete mode 100644 src/cool_cmp/shared/pipeline/pipes.py delete mode 100644 src/cool_cmp/shared/token.py delete mode 100644 src/cool_cmp/shared/visitor.py rename src/{cool2 => cool_cmp}/test/__init__.py (100%) rename src/{cool2 => cool_cmp}/test/test_class.py (100%) rename src/{cool2 => cool_cmp}/test/test_data/test_SELF_TYPE.meta_test (100%) rename src/{cool2 => cool_cmp}/test/test_data/test_at.meta_test (100%) rename src/{cool2 => cool_cmp}/test/test_data/test_attr_dep_1.meta_test (100%) rename src/{cool2 => cool_cmp}/test/test_data/test_attr_dep_2.meta_test (100%) rename src/{cool2 => cool_cmp}/test/test_data/test_auto_1.meta_test (100%) rename src/{cool2 => cool_cmp}/test/test_data/test_auto_2.meta_test (100%) rename src/{cool2 => cool_cmp}/test/test_data/test_auto_3.meta_test (100%) rename src/{cool2 => cool_cmp}/test/test_data/test_auto_4.meta_test (100%) rename src/{cool2 => cool_cmp}/test/test_data/test_auto_5.meta_test (100%) rename src/{cool2 => cool_cmp}/test/test_data/test_auto_conditional.meta_test (100%) rename src/{cool2 => cool_cmp}/test/test_data/test_auto_defaults.meta_test (100%) rename src/{cool2 => cool_cmp}/test/test_data/test_auto_undecidable.meta_test (100%) rename src/{cool2 => cool_cmp}/test/test_data/test_case.meta_test (100%) rename src/{cool2 => cool_cmp}/test/test_data/test_case_no_type.meta_test (100%) rename src/{cool2 => cool_cmp}/test/test_data/test_case_repeated_type.meta_test (100%) rename src/{cool2 => cool_cmp}/test/test_data/test_case_void_eval.meta_test (100%) rename src/{cool2 => cool_cmp}/test/test_data/test_comments.meta_test (100%) rename src/{cool2 => cool_cmp}/test/test_data/test_conditional.meta_test (100%) rename src/{cool2 => cool_cmp}/test/test_data/test_default.meta_test (100%) rename src/{cool2 => cool_cmp}/test/test_data/test_entry_point_1.meta_test (100%) rename src/{cool2 => cool_cmp}/test/test_data/test_entry_point_2.meta_test (100%) rename src/{cool2 => cool_cmp}/test/test_data/test_entry_point_3.meta_test (100%) rename src/{cool2 => cool_cmp}/test/test_data/test_equality.meta_test (100%) rename src/{cool2 => cool_cmp}/test/test_data/test_escape_line.meta_test (100%) rename src/{cool2 => cool_cmp}/test/test_data/test_in_out.meta_test (100%) rename src/{cool2 => cool_cmp}/test/test_data/test_instances.meta_test (100%) rename src/{cool2 => cool_cmp}/test/test_data/test_isvoid.meta_test (100%) rename src/{cool2 => cool_cmp}/test/test_data/test_let.meta_test (100%) rename src/{cool2 => cool_cmp}/test/test_data/test_other.meta_test (100%) rename src/{cool2 => cool_cmp}/test/test_data/test_recursive.meta_test (100%) rename src/{cool2 => cool_cmp}/test/test_data/test_substr_1.meta_test (100%) rename src/{cool2 => cool_cmp}/test/test_data/test_substr_2.meta_test (100%) rename src/{cool2 => cool_cmp}/test/test_data/test_substr_3.meta_test (100%) rename src/{cool2 => cool_cmp}/test/test_data/test_void_dispatch.meta_test (100%) rename src/{cool2 => cool_cmp}/test/test_data/test_while.meta_test (100%) rename src/{cool2 => cool_cmp}/test/test_data/test_zero_division.meta_test (100%) rename src/{cool2 => cool_cmp}/test/test_reconstruction/test_SELF_TYPE.cl (100%) rename src/{cool2 => cool_cmp}/test/test_reconstruction/test_at.cl (100%) rename src/{cool2 => cool_cmp}/test/test_reconstruction/test_attr_dep_1.cl (100%) rename src/{cool2 => cool_cmp}/test/test_reconstruction/test_attr_dep_2.cl (100%) rename src/{cool2 => cool_cmp}/test/test_reconstruction/test_auto_1.cl (100%) rename src/{cool2 => cool_cmp}/test/test_reconstruction/test_auto_2.cl (100%) rename src/{cool2 => cool_cmp}/test/test_reconstruction/test_auto_3.cl (100%) rename src/{cool2 => cool_cmp}/test/test_reconstruction/test_auto_4.cl (100%) rename src/{cool2 => cool_cmp}/test/test_reconstruction/test_auto_5.cl (100%) rename src/{cool2 => cool_cmp}/test/test_reconstruction/test_auto_conditional.cl (100%) rename src/{cool2 => cool_cmp}/test/test_reconstruction/test_auto_defaults.cl (100%) rename src/{cool2 => cool_cmp}/test/test_reconstruction/test_auto_undecidable.cl (100%) rename src/{cool2 => cool_cmp}/test/test_reconstruction/test_case.cl (100%) rename src/{cool2 => cool_cmp}/test/test_reconstruction/test_case_no_type.cl (100%) rename src/{cool2 => cool_cmp}/test/test_reconstruction/test_case_repeated_type.cl (100%) rename src/{cool2 => cool_cmp}/test/test_reconstruction/test_case_void_eval.cl (100%) rename src/{cool2 => cool_cmp}/test/test_reconstruction/test_comments.cl (100%) rename src/{cool2 => cool_cmp}/test/test_reconstruction/test_conditional.cl (100%) rename src/{cool2 => cool_cmp}/test/test_reconstruction/test_default.cl (100%) rename src/{cool2 => cool_cmp}/test/test_reconstruction/test_entry_point_1.cl (100%) rename src/{cool2 => cool_cmp}/test/test_reconstruction/test_entry_point_2.cl (100%) rename src/{cool2 => cool_cmp}/test/test_reconstruction/test_entry_point_3.cl (100%) rename src/{cool2 => cool_cmp}/test/test_reconstruction/test_equality.cl (100%) rename src/{cool2 => cool_cmp}/test/test_reconstruction/test_escape_line.cl (100%) rename src/{cool2 => cool_cmp}/test/test_reconstruction/test_instances.cl (100%) rename src/{cool2 => cool_cmp}/test/test_reconstruction/test_isvoid.cl (100%) rename src/{cool2 => cool_cmp}/test/test_reconstruction/test_let.cl (100%) rename src/{cool2 => cool_cmp}/test/test_reconstruction/test_other.cl (100%) rename src/{cool2 => cool_cmp}/test/test_reconstruction/test_recursive.cl (100%) rename src/{cool2 => cool_cmp}/test/test_reconstruction/test_substr_1.cl (100%) rename src/{cool2 => cool_cmp}/test/test_reconstruction/test_substr_2.cl (100%) rename src/{cool2 => cool_cmp}/test/test_reconstruction/test_substr_3.cl (100%) rename src/{cool2 => cool_cmp}/test/test_reconstruction/test_void_dispatch.cl (100%) rename src/{cool2 => cool_cmp}/test/test_reconstruction/test_while.cl (100%) rename src/{cool2 => cool_cmp}/test/test_reconstruction/test_zero_division.cl (100%) rename src/{cool2 => cool_cmp}/test/test_run/01_program.cl (100%) rename src/{cool2 => cool_cmp}/test/test_run/02_program.cl (100%) rename src/{cool2 => cool_cmp}/test/test_run/03_program.cl (100%) rename src/{cool2 => cool_cmp}/test/test_run/04_program.cl (100%) rename src/{cool2 => cool_cmp}/test/test_run/05_program.cl (100%) rename src/{cool2 => cool_cmp}/test/tests.py (100%) diff --git a/.vscode/launch.json b/.vscode/launch.json index df6605fb3..d0006c931 100644 --- a/.vscode/launch.json +++ b/.vscode/launch.json @@ -8,7 +8,7 @@ "name": "Python: Archivo actual", "type": "python", "request": "launch", - "program": "src/cool2/main.py", + "program": "src/cool_cmp/main.py", "args": ["src/testing.cl", "src/testing.mips"], "console": "integratedTerminal" } diff --git a/src/cool2/__init__.py b/src/cool2/__init__.py deleted file mode 100644 index 912fcd4be..000000000 --- a/src/cool2/__init__.py +++ /dev/null @@ -1,5 +0,0 @@ -import sys -import os -base_dir = os.path.dirname(__file__) -# sys.path.append(os.path.join(base_dir, "..")) -sys.path.append(base_dir) \ No newline at end of file diff --git a/src/cool2/cool/lexer/comment_lexer.obj b/src/cool2/cool/lexer/comment_lexer.obj deleted file mode 100644 index f54cc410648fb4d72204cc16e0a4d6f55b76927a..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 6215 zcmeI$duS9#90%~kyuGASRIF*OZ*!MuG`=5EqcOe`<0J8zi*fJVWNvdW?{-aMjIBs~ zjO7fAVWw|Qul@SAy8Wj=_h>G0BDT>~o>meJ zd%OWZbx(ZLG}$_4d{yIJvgT^l*{H4Q2R8TKT6^*OZR=okx9lv34WqBq$)1Z{$&Nmp z>!8D;ucF15-;H_pdz;i|cCI(eW20rB>5tq2xOv$uN9Gx-bywh65i4c|GPi|QDWq1I zS5!AHeXz1ADC?{n8_UMCG?u~BjmtgF<5rE~#WGn2`-){Vhv@HAR>Vp~Gb31mW^{n3 zhU7ZAF>F>W;F-o4lr%NqlT=CPDHW=uM%~90@yViGn#@yb&B3yn+tiTc4|`zLnV(jz zvkL2~EGkW5i#M|+c)UvDiDSgrWCkh`Rfsu=nTV+f;{urPk`O}?D-kxt5QKslhzKE6 zLJBhUkM>fe0W*AVwnQB3clm5z7(vh;@j0h|Y+7#9~B$ z#8Sj?L=mDdB8YGy#v(k3eu!C!Vni)s9AY|R6=F6b6EO<01hE=18Ig|A5akFNu?DdW z(TEt2@F5Bj3lLsJ17a{@En*R30%97X2H`{uLbwrG2tT3;k%6c~3`0yp0s8XF?n!pOggqcwr2#H5sOfz~pPYE+g%p*M2?FqTPvaY!#R%1SP zOoDJj$Pjf99*BAfA4C9R6+{?9gJ^(gf@p?lf%pbuJ;VlxR)|dyTOhVUY=_tZu@hn! z#BPW^5PKo^LF|V(0C5oF5X51KBM?U+jzJuUI011I;uOSbh%*ppA6Ya)*iA;=% zI58@V6B81ub!DjtF)KP^t0h_0lxnC|W7G;OeMRihmHr~&PAUTh%auXmRml-A;@)W; zs>RMjH!nb3gt!E88R81WRfua4*CB2|+=RFVaU0?eL>t6ihMK|Kt{bM zZg)Ge;(w;Q8qcuyX<{cE>4jZ6CT>!sEGBhhv#8@mhD&MHs#JavmA^NqkvNx(!Fl7& zQF}PLNUTcikfY8he~8A+6~mGwW;HnGCds5al801J@{t0hRirRUBQ=nkNX?`c(l?~_ zqz$B2(k9Xt(l*j|(hky2(k{|&(jL-Y(mv9D(gD&z(jn4e(h<^8(lOF;(h1T@(kaqu z(izfO(mB$3(go5*(k0Sm(iPHG(lyd`(hbs0(k;?$(j8J8=`QIW=|1TJ=^^P6=`raE z=_%SafmMJN0SYmTLY(R3|Ea;49zT((;iGlj z+`lR(#1gQPR)zSLBC7^&eFL4um9chxk)=y9_PSIVH-(x}x823Q-LYtGn4&)IRLrME zZ8_o%<(S^I`(f-8o=0P@gt1wN$$J90mMD1VD?I{Fu8yvL;d_W#uth^MHMOZ;QQ_;+LhZ>?I@ zAcxc@%@YVp;gz*@OsV&-X>_l4`F-;jmn^7Ux^ULqc~dH;O`kDyc4^tNCB=)Tmd~jv z7(Qa;sL^A_jvLlFD?KyA)^FX4oW6s{Pbe&!IH`}b%2_>hh%+zWIoav#KVabULC&?! tE!DYetL)C)JiF8G$aRQUA%~FDktcqB7V%`KIIFW_fn29U7yp>e{|g7uM9u&J diff --git a/src/cool2/cool/lexer/cool_lexer.obj b/src/cool2/cool/lexer/cool_lexer.obj deleted file mode 100644 index 8a5420f57e68caf3a6e49e4ec9dcbdd41e7d76ea..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 139984 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 K)z-HBsQ(XHYhj82 diff --git a/src/cool2/cool/lib/std.cool b/src/cool2/cool/lib/std.cool deleted file mode 100644 index 3a3eee3c3..000000000 --- a/src/cool2/cool/lib/std.cool +++ /dev/null @@ -1,35 +0,0 @@ --- Standar Cool Library --- Some methods changed when building the ast to provide a python implementation for it -class Object { - abort() : Object{ - self - }; - - type_name() : String { - "Object" - }; - - copy() : SELF_TYPE { - self - }; -}; - -class String inherits Object { - - length() : Int { 0 }; - concat(s : String) : String { s }; - substr(i : Int, l : Int) : String { "string" }; -}; - -class IO inherits Object { - out_string(x : String) : SELF_TYPE { self }; - out_int(x : Int) : SELF_TYPE { self }; - in_string() : String { "string" }; - in_int() : Int { 0 }; -}; - -class Int inherits Object { }; - -class Bool inherits Object { }; - -class Void { }; \ No newline at end of file diff --git a/src/cool2/cool/parser/comment_parser.obj b/src/cool2/cool/parser/comment_parser.obj deleted file mode 100644 index 525ba88186f2361d916b92a7d89ebd898488f748..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 2591 zcmb7F&ud&&6wb`NU&(|9$&gAAp&`U+4TFQYN;9;Yq^2`WYLadOGJP|-$$9k6yy3n3 zv{?wcFw}CTgx(^EUROHLQI28xy4Tqz ze%tBTt^(KXHsQ>;OvxQu;b6iz%XbIPn1XW=S9k^rOy6|&L6)?(NQaV6i=_3eMsUz> zdTzjBI?kD4=H(WIo!e1)cvjF(3(moF@O&J40eMbWf}Ug-Hev3NGY#wj_&w`_ z_Qm(%WBseJi9ml*gPE({z;V4!LJupMB=(6`MHUI0?{j$Rp7qA^ze}Hg__r*DPL);> z=HSJooow+^k4iyxU%HsMktNRpd{1t=3X*-6(BZLk1ygpJd9HIZ)# zDzHdcKA^Twx8y{*7=3s9pi9IyC;D~`FT*P=x4N;05M1wx1-s46$CQ{Q_Ojx%sL&kL zp$6xn0Q1<`GAzLrJg)?cVc2BmTf}dA0pTAo!~w?pc+ zZHnS%lj7NkziqJu5tV43=`u?`71}x_?G%|vFFJ?yt+6D?}l;q$uI6>KoR{@Ug{%Z$?=w|L#W^VoS z-4B0>d&F{GFF@_F3D@&(+d%By0IS!IIOcv56a5s*@HGzn8$oFhS{Mz@_MpL>sXpKc zhrB1>(Yq{9eY+Dl(St1dEMX%b#XLSPDwM>znTS$lh?K&27*rU?;1P!(@4`>L^M0{m z{;XrOea9xrY&w1n8)_5xIm=OdpV!&=p68>+@M+@kkWIHkmpZP~A&u1k07nskjgJyD zT8`gx(a1TwcTnJA(+=U+`y76&!|(71Jc7sYXElRvI6sQ)ac@R)0SVFJGC5IZWY1z< za@51K6f0Jt^o-?Eq(n72!QpD^W=1QpG5{qq8w3)WNg$yXi3*oIa&96qCzd>@54dtx zSDKQdDD^4DsAQ&{4JkbBErOo1J6)lgqz>!C>gnRiY2q^x4Th79Gn4ewlQeXnqVF;B UFVyuLRQ#i6x@)_B8K0)o|6|4u_W%F@ diff --git a/src/cool2/cool/parser/cool_parser.obj b/src/cool2/cool/parser/cool_parser.obj deleted file mode 100644 index 941d97e4eaa8ffd8e7749f2f68fba1bd9b7281a2..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 151549 zcmeHw36xz`m1xMEngS9igsR98w7@{11dEU&1VRzQW3s}aB1vA73OAQZD%Jo&22%)# zQVfBD82SIBw2QW#x)t^Rc0}4n7A?PNhyQ8O#%@}F+uEYi_UE^{{AZtWpR><-@7#Cq zdlj@;i&yXM?7PoCdpi5H6b|rHs4nAviba>mQvC#qZZ-)Bp3kJ51PBOISi-vYwIxxI_+m7-e z`sa(v+Xp5m7WD2M9s)jAPA2n`1ryEeCu$~HSRUInF{gjq)d{CHf8D^2(SaSC2MnS4 z8%D0*zG-N1WHLEebSyb^qNR89V0mQZgu!hiqk?jB7%<%?F%C+OOb$;v z3~W7tKka|83qJ^Am`^?{H;@IH-gN>0{{%^tme=KuvCaD#%GKD7029Vm~E4G&c4 z#R|*}>cs-}tiKPd1CKTF&X<{5{m+%G=S;6=Zrh^xH^2i7A_4;|J2$^*RmP6pTa`^nweQhX?!(-)c2%9-G%&9^N*Z z_#c|E9-2T(IpB?oTSIDY?y1mq!C#dBmb+WH@)*$hAYp z8Xpm2JnWM@=NDzRpxx#Ms}7*HwP5)1a7x0c>>FCm1-J0%>gGWsA;6Rck9@W%_nRw4-Oh7AwPW9 zZe$&9q>GYkVwQ7th4}P5BR6+qUf<4RUr0L*dewQkKpBJwG%B{a^zi;^P&j5u1D=HswoJ1Aw{^_fJ`-^3Nvatk+ zZ2S?x7Y?1>^Ro}!_`yLJ9C-@fOa=fNY)ensm)x%RPrJ2!uMp#|Lck@+2u9a#Qn z3wZ4hUwox+{M1_{P|#em@|J&SyW#$~0}zFQ3ZZ(LlGY=F!hd^q{x9$Q)!%%pB85i; zvv+NpIQ`$RKjw0qh5u+f>peZ+e#{|IVcgNVz+S<^U;f?FFa7qq2d=0{VXt7J^ZC24 z_{Jqq{aUhsK=bwJ+@n_!f=-)eJ?NQVk5PKa8Kl|`8hxR}7=g&k_nAz(x z@y!KS-}u|dHoM}jz=VkFOr5eD^Pc?P@Uw0Eeh|;ZN|%Xie|6@+o%FkVyDF0q0{+b% z=YRD5yS_09xY|lL{QTpkzy9u9-faPIz5la^Ki}0n=1|>ag}T#FUe(MKzVL99-s1*J zoO&OyLTX2N`FDTt;4dF9?fWykbq|SjFQdxg!Yt;TGwYhJ|MQ0T$-ZCk34*X-2(GxG`e-6$7gQas_X~zJ$p16*x~($0TV45RTI-@ZrvvFMXdSSvhj7t zz4L1~#xgQ%aDY815>YckweiWpwdLUo3iy(%0Q2|)^|;A9(0nZYy7OdY4o;pEQwN~{ zJlD{s_=cZ-{>7V*nEN_g7^%y-Ap8=C3u=$ta{I66KK!<~P`%IGX7nQ@{`0@S=c(Vn zce2%PB&_<`o6CCOvsF2yZNiB1pk6>fVRJID`I-vK_^~5WH?17Q;X3NG8-E{u58~$t0s#uB7xh`R)4rS%odu9&~4ej*)!%jAKwB%<;N(O}l@MKY2-AxDoLcEmfsIeRJK=oU}^?Q38E>|rn7DIeta zDs1e9bm5L0W_*Z8b7TLBU07MoA4paRE;e@m&y6qKw&(MT7_^N%;LF!6{oYaEdi1OC z&xoQ^H|nsI{@OF1mc15^{U;ND{`uoaKlclVtX*8|N{$q=zVw5C z_tAe__miJmB!2a0Z9o6ro<$-HSdv}_%`Z*QW`EN2;p1WeoSF8*pEct2;Uz~K9zhv`AYI+ z@=Ef%bPm8<;o}ha=!B1B;p2Gt=!TCT_~>OH$#;?$k{>5O9hhj@VXCI0;JS#_EW4Ot zmQwEitGQor9}!6lD;g!YCKJit!~&?(KuMMpPd^GA zd>lSL1s|V=kNF^sX834hAJQU{%Y`qz#HKROF$bJXuK?V&>?3(NxiR@dn!xX!@UaU% zcEiU;`1niscn&^(2p=!P#|$9l#u#GIX%^7`xCi6*ONo&00+;>4`Ji_c{R>vS9A zN!7hsrQ(1k(kUnAL#6qgZJS4-e4cIx)~^A{v?q&_qnUP&XZksVY3M?xqr>4}xuW`# z)#=gj3&Lx<6h0uZrF)>0JY(I$hWu-p_HJkTyPIinAJgGSm=-_F^f(K!cmd#B$+O81 zm_onKRQe=9A!SOx0w0jPq`!a<7`M}Zfe#o)(~rRi*X<|a8&~jOz&Ebt$KV@RbrUee z_1y~JxY7@VZ(Q@IGu=M|xF2F4$y=ET?1NvB7bm|=UP}I!*~0gjHT;^{!-LEs-Uk25 z&7uU~xNUrxS;x)HKEA{(Bn2oplVSMAZRK6?jT_7@@Qqu|Ciuop=Zo--+t2sl8#khV zf^XcCegxmRIn4x~xlO$SFm707W?DzUFK%DQz&CDW&G3y|+QIORo7*YO;LZaY5Czkd z0M1SC6U_K7W9HYx4Dg%D(`gz0c>{cGfsak>g9WgOdDjjMZyI8m-1{cxvI-(wD*;O> zF*Ck>a$?T9^VXhw>164E+u8pKt2Lyf?6J~J_H!n0PeJYhNOGF`Ig9vyCHw z?I}nnhk%7C$bk&fWQ(FHv6yV)wx#f)(Wb=Ws)@U-B8_HST+KwD&9;tO>?T_5$6E+XEzaXa zEG$x zZA4IQwu;+`WZG=ewUI6?w0T}gcvxuju+Z+*Lc-EQ!oxzl;|od07ZR=x;~r8^D8y3;_c zI}OCT(?F~{34D#EJDBz|-Mz->PD8Zr6vWb<24dZ5Al97%8r>;~r8^D8x>G=-I}Opg zQxHpc8i;kLfmnAMh;^rdSa%9&bf+Me?lchVP6M&-G!W}f1F`NT@L)@KP_1CPJJ{$> zL$vM`#L}GxV%=#V)|~rk(r;dlb=XXH z*nRA-ds5@-Ab=a!uAiC=yGo211+YVv_owg`CNnbh%W1Y4% zI*If;ZRvFqHaaOAL?E5EkUNP;J8hA65`lEu66>_(*-6B;m^882eq^zc70kgG6P_2_ zEG@R>u$V}4u`S8P9+nnxs}{9jsMo#D*#D92=aD@4^Gfki?B`L2!=u^HqYVQPbB?iw zj~?MK$C%+mL-g>WAlC4qfq3}PKs9%V9If|Vj6 zNFx%0)FC0T$|Kp35TpwUxx&TKS`Y|Y1@fn=Y{%$a2gn<+NxiEI6H$n z-crOX4B>d~L5cXO+(-Ph)Wi|2@FgHvO@fFPsX)Lw5)iB-@rVvz;-?N@0w3#LK(Lww z1gl9vu$lw}tBFIjQ^@{eHHk;Cngj%^NkFig1O%%IAa68@?o#%1sZrAj?B@x#nohLT z^dv(#(WpuM)N0~CEj0-U_9g+rY7#`Wngjw?lYn3~iAS`W#80gzfsfTBAXrTTg4HA- zSWNwX`R|1DG1h=eh_<>0V1twpc#@hK_q95W(`RSQwKwhrsJW8SVNL}&{rZ` z#y3x?aY|z%g&LYjry8i$sv*{pqzNMB8cd{L14Jq|K%`{_JjwXu$xLe}8_lxaOQmk( zkIVQ^Yb#4PnfZ3xGv97f5a=e;`EE0rg~CxcsVa2a^Zjlzo$n?S_HKK^-c70>-DFDM zP0AhJcBSG?+B}kL7@u##HZLLAqXiDqg2cg*LEs>JkT|fZP1=%#;2+C{sjc{FCdtI0m1wW2Av0Ff{a5XsX3kw`UAOI1Ux8G+=yhs1;)yTsRH;uw_pdPv0Ru_H#09h7=VpzE;% zT@Q&TJtU5uW?AgLjE&Rq@RtY(l8l5Pu?UEkiA2DbCWuIr;CnSWL=Se7f=*WiKGLS7 ziBlkfgKR_M>@p@KA;COO= z`V0_Rg$8QFFc4)M2BOkq4Y7Q%PehN?trSQDk&&3kk)3FuNrCjU$apjwwjTq;DUf~~ z*_FXWrlo)@i1Dnjjc0{zJS&Lttgww|g>5`5i1Dnjjb{Zho)yG+da>~&4>8N>#g-!> z$ZRA8*^GoBgOLzqEfRuEML@Kj$e+qcv1^Fsm=_AiJi~HK4Mg%ck0TXmpm9w7EK-CRX-#4%Ue zj=9oy%$3A3SK5xb(ss<1#4%Uej=7RJ=1St2XWC=anU*cVNik+iXIi$Tf!LNb5ZjUh z>JgtGJ>vwCiy0s`B>k+aRA@Al4JsUKo~wb_Ry7bgvL>i9TRyednAnyy5L=E0ZZuwO zXksJLnAk`(&`kzb5i?jtw6@ANq*b<^tg_8um2GCLh}o>N?P?VAP&nKh?S^;*p)O8yOIWCYtcaLN)ou5IL>O@uvZg5T5Y?=YGUWBZAV*ed)8{( z?^YAfT1{N(9OF1}I+vNmIhLJhAQrC%Vo_)yHVF;HGS@)tI2wp;PXn>-DWGv21+g4Q z0d2=Qhd53jQB$9-raq#kK3h$FL`{9Rn)+-t_1S9bBWmg+YC6}b3C>S5HJxjzNdvK( zG!Uyv1F@Pk5UWW8v6?gxt4RZ~niSBeNkJ?%DWI*UbBUVP5H+o_)wG7FX^pL>HAGEo zY&EU1)wIS|(;A|tHAGG488yKPcc!NEEH!B$R+9!|HEAGLlLlfnX&_dU24Xd7AXbwC z8Z{}1r6vWm)pQ3prGDkai>lsYXJOUL*u5MM989Bm}8LK(sC-0vb;x0%V~&891M4p+sa@vdix! zBlBfU0wtogk}cH|kzt7}MwG~6M9E%jC=u0^$TC66R&I%`HI!^)DB0FkBGz?*(e+l= z(+ezJ3kcS=fM8t<2-dZLU|kCc*0q3OT?+`-HHR2o3k0ldfLy3DG_LN87~~?OyNlV+ zi_O~NCG6)VW(Ddk?B`qf`F&+A{S4A?9tYL;+luZd8tJzs+)uRJPmHafXrbTMT0gP3 ze%n;~Z8PX6meWtnuiv)tequTO#D>?YWB6RQmojNw$}c`pmHc0c7=9_27@v+w$YmDV zzX~*Vw}Fx~=;d+?=Qjf93I*gXzLlZ9Rof@OJp~}xL=6yGsR1HGH9%ym28hhn0FlKS zATnA5M0Trz+H^I<8Zy2wy8JfN!KWm|T9TH)w_Cu+1?JoNvs_kJGRa(NBn+G0-(fxS zZSlxe3do;Y&(PMJr@-LX+orglnAUpR_SO?iTTfhIz3n6GiT$s)&3ip@nf1gY*4vJ` zp4jtxV$bV|^RBm@VuR5I+}FdTvB5Gn1vHvf5Uc8?f!O>s5L=oCVoTFNY-t*ZElmTl zNoybqmHhf4)}4)-^W?|;=S#{Z%j{>Fh`nsfr)--_nJA@fE2V6jgX1J+qLDH&o-#3> zvTYk>VvuE`*0OD)Wnvp;+sMndeU^!RvinhG&`W;GI4_N%*G+{ zQ*RvNKh>DU7pepV=~__W*W#!~1rE}(#6d+f3AuvXEkANDIhnMGXbYGBs{19HKsF-@ zqPQv{yWA$!Fk!l*tGMvUSQnGfZt~DZ0zu_l*%Wdh2|59j(bOi)US3t0I1q1~Q*@|AN5C~Yd0)k~L zAXv5nf@LcpShfJ!YPz!vep;dy5X`@TV6p;&1uG!9M*@Q4Um_q_Wded##v#Tj1p*c? zK(01!ny{Y<=AU1M20-np2BMr*L$E~|AmY#f5t9apBw&DuTLVM}rh(eV3OVc=36;PpB!;}UhlQWMa9ciF;YYoAEZGg!93=mnM0U~QLKxBssxXrqaDrJgG zwQ=%W_5fn(_f8?1)JR4_EFowh=0pRr5Ht|ks%aVn0Dm58`u+%LL6NXEuNd#oEI8^z4%wU!*=!6EaShq>93rwB zBJv!vr7>g+bBG9Yh=^;5NMncyaEQofh#138m1S_@ypsv%oyM);qDp{RZl!?6trSGb znVX*mVz<&j>{c3x-AV(oTWKJ6D-FbMrGeP3BydVrzVa z=xf9_p%L5OMr?~3A?7h+8{`Nvxe;OyBSfkZB>Kh~C8YAKwBSIb{0=!l^0vFD8OgPsW zXZeVTHrFW}?gbi%(j|3<@!gxtg-T*x=GM{YI* z-_3r$+YsE%e(pB*InI8L6U~kjlN&c(fP%-kZNuZlAjfT+AGfV*+_t@OV!h+UT*rxD zjN2AIPONd9*zmaNINZ57PR#WdWnf&A?_oIa!P?}v8#CaoTq7LkHvUs96fQkxklT#7 zK;yR=eMybqZXlIkNBX*yKu!KQAh#~x@Y&LP?6Ns?~TaPfST z#BVZ9HA#|9vZ*G?O%rcoxYR63HcJ+oCFy2Ks#!MCY)Cds63voyvn1WjN$x1MND?g) zuf;stB3o@{tu1b%jJ-VNh2}l$8=?C1>Q!(n`t5O8Ll2^T;aM?NwYHXO&h- z6077RtK=iAB*|6g(be*wR!b7A<)f=H3WT?FN~`75t0mPw`DmYfv`;?TXCCe244qr* zlaKbvN7u+l*T_fL$Vb`LF;`hfhNnOU)R>gqO zFRApK2Kr^2{gPn6zPz@&A8H#YzLD|ZXq&~z+>|`|( zq1YiwV#u^MBwHJjtqf@rVtg8sq(>yl5&7>UlGKPHH6lrkNKzw`(GkwYXlX=~65NbQ z5@Ql?Og1ql@x~;*G1J5tCv`2G$0`yca~P9NUe8~dz%+4F>3aUo1orh-HYX6_d0aLy zE*lt^4Ud}!#$}u1lHfRR@D@fwG&C+7zD+fBd+9dQ&|M~Nx{IetceC_Wq)&Ir^l6Vl z*~3vF_Y^35B+5RXgnf`@cRVQ@o0vN=JUlcEuicnv;a@kfmsE@njIb1LVnKO~y#w1KZ8NG?{lOPo@dW{PatY&HH?2u6qN# z)ndNYrWJxRC1H!w1Ig$jRl3ZJB;I~ZvCEh$VkP#>CpOA1&C zm`-{YGk^)uhrHIKQ`6|iN9(m&{fAhjTcwf}Sterz6lcgCGP3-k- z*OZfi@|F?y5}s=q%{`N2o6_Zst_WsnFZ;?u*#+!RiM^4D{R!iVcT8ba_6C?B(WMsA z3YffrU?Fp&Nw=5Rg%s0=$7lSJs8DsFsG|KnY%tjhuYQ`mW1@Lj4XS&^jzUeIUFE_TS*T56<%Dt5yPSA@7+?2N>gbq+8i1 zdnsp6M5mC-W+J2Us|=ZqM6MtN_Bu1pXLU@}DawP>sjlSoh+ZWZ+g1S4$ZK1RwCt73 z1W}DnXH_=<{+>4MUpydsS%6o(+1$bcl@FD@2ri1Gl6-YO*&F5JD4uEKDGpub@b_g! z=m#0nY$8mDD1Yl#Hkxy)(VRF~zK4AS4g~j1PRtrDUp;xz#Dc9u!z>?VFN>Q@yO}dh z9K3yOaCF|LN%zeDojw?x>eNJKTY z{EClUKKq3&#x~Vpf8f0>Hmc|kd!6IcraKuOh`klnlBQQgC>FqmGOr`f32>J$xa_vM z3(5u#!rG`ccLYvz+N46QCZD(QWRtK5kO$GMs(8{zE|+%sseB(*t`%f7yMgD8@DWsS zE0K%nZ$1WLhn~$~P}+~$czLBQM{}9Sg-75|722$Ljj+e%@`({{*!A(5k1FhEv8jfX zBi_1fbt{CLY>gwC?25zVM}i_z4d?9m8gJzG+n~DnnAn56JP8 zam{Crfm~3JaQU4^IOSXc= zgbIrO5W&~?tC@9(WEzU&`KShy4&HU0>&4V=f`e~nQi1%XR&4~w@_eGBMK@s3=VP9E zZ3s1xblW1NQ5yo9uVvS3L(TGok9NokqQYxSZB*g4`J@@DJ>;W$E8#WRtj{!Oq zgct}^;sL$pm;o2D232K};tclWj25BJgjb33^e6GpxPW9Wo$(A+!Ty-RS1pwXQEBcI zM5Q@J2-Teuhwy@#WG*vy6YgN^v=S0R4J@Pqdd=nzcYhaa?oNFw)S2K!BcIfrHL6hS z{@O;qrv0-|;J|oVOj2OtUy(^Gzl9*E>00e4u*7J0Ivgvb648H#tTbvDPcVdwQSx~FUqV!$6C^0giu4o#j5?1WY9?l{c7(4Mb9)r)AgSV)!P zc*dO_DO`MEyXuDmHs@9sLZ%T>*T~4EKZ52{&Yim7g+@L)>j;V|G-J%wuevYw=i_d- zZE8W;1n0eWD2N&2XwDOAwbC*cJTtx24?=)@iFlIX9hZz-sYE;|ZU6dJ%^NE!EUMI? zy!0U*A5=#|aWP_fuNDSEGwxgt$Zyk#^g&wt461TJT3}=d)Lnla&qRx?stC?? zYbWVch5a`X3+l`oLXBKmCB}N-` zRCo-oo78T3;i;QuElX%|su=+ZlY(pjFSG)Xqq}B?t`3>Nn^!qOxGv1B(4zCeR{+Lj z@{%+zUdOAn1rXB4;ILF}W}DfzNXgnSOs8TTK&oXYMFpQr?9~Z>qqOQDz^k4gXBA#x z?yOBe$}0magqlbu|FJ8~enB`(4}PRtHe4W0W~`l>l=3G$xx6_hxum-d0=~%jf_p0Y zTS~wYg>UlFhu4C+s3E^5q87U_z%0CpP!CYGwktN813DPiVGl_$`%;|oxY3fLwPN7F zzptinOL23^Mt9x}TdFqEU=G~*M+0T=Nl_Zux;tJsSni%(Mh)h`9V0!mNAVctN<3uS z@!5>0RRS~J?Az#G+jZ+gB4|phBn=TJE#ee1#yC6lJYA{Rxb5!dYp`v2j^BAYU%XS$ za!i9e?q<54Ue`_KPO-m%O?&Dt4VJs-kW!;Lpmn=KVu*bhu$U-Is|F1gmRr1Rq^**5 zhhk((iws4oKhJu+J5lB98fFrj9tdk4Un+V!%IBUc*FmKsJKf1T9nF&U)P;lifwex( zN2L^XY1&iu4M&aoaFAtquL}q64zETFfbL^&GzYZ8*kBIg>kM_V0MF7^qXpn>c7x_= zji)Z1kDuw+rTI)V{rYf_C3C6^2Wp)Wug%m+`xWvA$QJ$%@`m&XW_>ZKgAp9&+_S1A zE9_2v(o-p^k~%otN8asrAiLWwJtneGW)H)D-KT?~PtB2iG6~jx-KW<;pAL@flSv!) z>pmR}eL5+!PbL$Xnm$Ec_J`^M>h8cg509v9~`iVz^PaKcd}L4N{T2)n8?+ z4Qk$zecbjr6GPROlb6`NjC|gxZ+FYQX6@EY{Oi9;JZim2O;&U4o#-Ua(Ep+l}y@7db>ocV362NUu!_Vjf!$>#@%kYL_tvZr1H&0_(K} zcgf6eGIaH%<_7K4#~v~yi>|=FXLXKRUYsC)8e?+g+(by{&MB-A8a>Jv!c2Y~!ym1M zC~?#tr%=jN+<-{W`~^EMx3DxQT_YKby@tiO%rknrd+P!L)m^hwN;J-$$w@rx3w}3u zswuj&IgID~ZtM%{*{LJv>@(ebqX_2cjAuo~FG#xSw+LB!*7HR3PM)i8j;wl?#JA=m zswiJ%dW)%|d`|0E=Jds^U42`VRk`qU4B{KX(#PXbpK?(dvq8GgR6uqG9yk3m%^O)o zG*6M8=A*9GPfdShD6Q0a!zx8~TaS82h-qEDQlf(BP3sidZ9V3?eRqd@g{CW{vI!Uj zY+EJ4SL{F!?N}v2KWuXz4-X^Lvk?{gWkjmpmJvpF7c^AlX=yMk|C%|E^S1&rUlN!zIzZXZT}PFC1O(M#16g~?-jaza4n2y*Ba$GU`7G4+P|AI zSNXjq?s*g;V~0)#10yO9_8^?tSZfyidCEX&h;$PBVC&8?&LAF5{DP>CzbKXL0v$jU z%-EB#1LTp9=XD@(Ue{-ew8*HkjXVta=LR(!oHLCKDu-6fm40*><{TINq{VG;-)s1G zde1)TBJ@d$Rbgq7Sw$E%RDi2FY6sT{9f}drm`)3W{ZP9i?RlmA(5lXv^k&p5)5CjtB7{a+BcU{D<6KcMSh=5_18T3onHq$tNPRVowpO z&Rf68TB1{?@a&V0K7dF~r}7OZkBWFM!-{z2h41ndo}tL-z*Z;49S*rp$hcX5BYPcv z{eji(!p;WKakSAE=j3}0Zr(HSG@1jtBO<`T1G8tah{FDp+fCGXE-x<0zTMmPeov|z zV5M#x(DG}8IdCUr&^#Z=OdeCO2dVKH4Mmq1TG z8`Bdx>SMd#uX=JU>q$CGOy*B%oiXFM@yVMeyV4%^wabk)s(_3;)3`As(s*AFs)_Pk zGdJ0b-HS^=7xlqNFL9M2pxc1S%AoH!;mSJ%lj$V?nPMdjs+#i>zFoU=97 z+DSZ;;c#j)LXnYYa5l;9ob*CI>hG8N>xmhh?F5D!o9HjifcwS_JalEiUCeOfW>NBl zsT}7DIqMNWGpoye(rU9*kzGRDjjCq^h4jh^qg_!)Lezd18R~1&rqgg-{FYW^4tIdZ zi!1tL*SOtwYdZ##yDh)B9t#Tb8^S@B zC6c;uph^#F;{Z2NQl+j-({zhaAzD=tR&SUVOAqTSSsi~nkEY9VWFK0VMFZxfF`swE4 z2CAPP?mqh^7H>|n7TFb4r7M)I85s&CYo?=(l)V~| z3hj!S6XhZ6_;^?HV2oSPlWC9)_-7e)IriWlee$GOD5F+Mwo1D5W!TOo290bwdcs;4 zp=X+0)`f%k$z@%dNBbbFXIAy$!0hLmDh_%x%&_VsgQy91z1-h(i$J4e-=o%4Bv(N- zV|8Kb54d<m&cH@FVG)y-j82HI)M!BKt`Vdq zW`+?;$+SjQpV(Y|lTO{@K;_LJ4i1EzjPqKyA!THt)Vc}1m4}Dml-})&328R+q4c}rq4bHPQuR!`((5u&%d|DD5DuKwtgEqiM8zkpYMEod z%~pHsldY~`WMebi(}0Dj?BDExZG6o*GW$QHKJx5gWYf1z7pK4=6 zXr{N(%s?d?oDVrmnMIXoNa23EXl=dxpwX28GeS>=^368f3Z*BtheYiuL8fSLD~aC9 zGR3=hocF9Wayv`)BhW~?O5jQXwzM8cQ-&pqN%$5!{X0`l|NezPU;N6+ zN|-yPadRg%Ust1-RuPY?Tsxj?epu{U3-N%z@vqt&ZL+*sfxj_F^T3aba~}4x)>Naw zkN3S~Swd8vhTKCFn&0ANX51>9Vxlw>KW)h)rA(Xrb2UV-Ly6N!oUoh{cN}SDt5FU@ zbI9TwQ8%|Ka|-9lc~s$UqA0!}nT`$Q=}M*@QF$7QUq;B)O7uqTJgsC}6U)<(`=X*c z7+RJa5b|^duvY@Mxzr~sL1%Wy71WtjlvYRDX#mDB1XPNq-El1eEYUDUMAgy`vM z8+#2|&S~2@YTEW!hr+53Y|pFL`pjxb^xrj|=yyd9{+UMTxRbmYV}hH$Sw+@X&?<~t1gw+n5 zI`}(!(Lb+oiT)Z$^fyf>`qPR2bfO=+jB7g4=Y?hGzP37~ z7t@J;l@n==*<+(#R#aqu6n~~FueVK?^QX)CHSEcaD;C7vACS?6UoGW)6&I#c{QN2Y z`Hf5Q*RrN!Pk6t%acp(7rbZ&K>N+_C?n%%Ecyq|wgJ3&UxgPiMngS$0t!4I%?}9xE za>;HteBTHk{KPqw(9@ml!*>Ea*(&xL7CGc5b$DTIWCEg&`sf_7ATgoSKX~`Ab7PTg zy>KOEzC5>Z4k8bO)Jp*F4r0DL{Y9{AylJ35x485`Gk9DN&DQE=dyzZm>Lz>n#$jC& z6>ohD-E(1S1maVrs8b|O^g7r=$b>i5D_ zy@8^-szQe(-JPxR3%7NP5b|}_h1xx-i8Mr*?wVyIeT}Y*hg&yCH$xdlwE5gwA5V~P zVK3As%!{83B^zBisauen@+%?J*|6shrx}HWS@(9CJO<77X=a*P+);<_IC~}<*d4Td zTcqtrF5lK`>H`4=#61@3npgsX_1x^zY(o&9W%F{*X8 zwzk3jAx$g}vLF8pKa$hgx6KRTn|UMa%?y?v2r%<*)-MVQ--W_U`cLA=-LHWkM~ENu z`M(?^ejLPqG>adH^B)I`AIIO-s z566jf5IyW`mzy7|5=)-yr6aS*>eU5Mk-C<#_<6XRCApn_`zrqihb5ol-{97j`XApb zF-Br5fy;pDY!XIMEKeQF74naS9aPgVYCVsNU9(C5l{Ie-q3I0f8@}mu`kQ#vUwKiJ zbwJkJ?Yg6S)fN~g#2M9q<3HIzJ) zLP0ipI#7}gR%7j23xC?Ha9Wg-6;G>SUiGDE8`R0XR7rXtWWX)Q7SQ+LMnZ9UQ-uO| zNSZXK4)@2o>#vCZ&_tvFs%|9|k>ni3%F-oB((Ef{R@x3d@GpL2B-Zxo|;djKNm@=XPsXg>$IA z0PWJ7cw`aC2~aT#rZ4khQXHr==KUbSlTzR@l^dI{4HZ?+ijow?k%z;Ne~FZf=>eY7dgk1bm7U9( zQeq1#mw^If88!7_83AirhH%7}&J;zxs-*q2A-kQNDuE-$%i!#u?km2qZ z1-nkh$o^0}O`l>8CW>IjBA#Qg+Fj>u`-_Y_G8o1Y8E_BJfcsj8n?4yC#2(kWP+?eU z!p)7+ZwFDG(2)ExCLJ*3R8)^>Sw-3)s_9Qvs>Tc@Dho_Oy3gp&Q)H+6Vqp0RUv`jc zxRDPmUy0O-k&G&BDz==KA_tatFoTF4Sgy=qXm1BYi|v%D;}luW|6ZI(6T1dm(+K4H zgInD2tcUWg`=XK{XQeN(b-V&L_&O)R8`yNccI44{Jaj5~hE?wDtzPw#si!{CU=G}S zn;WTBQt&9oMdU_w6Eb<9a&0rM*Cs=!blqs(Pu*z; zF~anwjJUXR3j7os6#*Xa=?_VSQVqK`fM?$tD1#y>nyJoL(^U@Ercdri7bXJ z-CtJcIPBDmy8htTElrfSmpWM4#mqYQLMZZ871Ys2M{JR0whgQtwqF$b*Jr5VUBz%? zYlDYDy>GwUiTz`dF3R$C^0IGXGWn?o$hmPGilEj?_D(xB3Pn*WTW)B~!X~MK2dvDk+4<3Yb>ILd&AY75 zWHz$o1#5!nZ&i7`T5Rx!h2XX*Hrxwuu0hVe!_|HseWlC*Ce0iy8ERL^WDs`MLi?5R z0#l2q;PYg{9O%M2@O}F*q=bc*>uaXfyC72N^YO z@|&Ee0_7K2;!f`TXsM?PYb#o5If#0uK7o$grDol>G4w13 zLnbkSMjy3|-JC7g#-627-;5;CuDy_;FJvw{l z;r`ChYBiFjI#7_gPh`6=BgzAXla0RWr)pOo0ml>Ta8U6F5t~cY=&wqBJ_$$8>Ndfw z?(3}0Ps1_M*r`%E!xU^Iv@lhIl5+~R(WJ5Uh~(EXx|A%c-P?_2G15x5WYG8Knk=e4 zLkovO3+A}(XJe!+L$}&s1yc6bNcixj;xNn7vhQYbB3-0}2UwrdS&S>znTPp_?QgMf zALZXNyXQh6c7z~S)G_jv{Ajt6I(T%o&9UlMJvB>tNvs~2Qq{*n4ST6Bq~L4cF}W2? zWaHe}Fbg>X%v@790UPs!$b37OO-hZTfx! z8eM~>Tcb`D+Jov0l`%~ffe+Lf8Ycye;2}o$x==B?l%&8TUoDN zL`FY~$&h}Wa#>K6bj<_zTU09iLv+Z9-0QxCa!kF2M^|s5fXl@jJIp$!>NimFqLLLU zRcd-h6)9?9VEWdBu5c_2%k@brT;vUgf54L9fuT0gQ(MYDTwy~gvKPgUJw)|-I*3db(TIfo|DhM znrqk56Dt$s)pT2Vt0RrbdGU5N1$n5Wro#CH-@AAQ|1W`Z--A#WE38~Lm|-e)4fJDw zn6tM4Bd_8sCecl2MNZvYBaiKqW#trJC*~d3L(@`|c=!iaAQ;YN{JWj>P*ej+B+)$O z2=^dpc>hl>W@%m%Qh=%=tF$46M|bsxbxJ27L8`%tF;A>&(NkX_Q8e>NI?Lw96dDfw z^XFXl8Js`oqFU`Lz^whYxA@SR?rxCuM);^KKYLRF$u?_I@|=K@bIC8bo5z?AnyFOm zvuDN^bOExrsdh=*+wZ~-T7BAIgHqfgN~pumUcw-kff<*cI%YLz@BR`oU9P3KPkOiL z8?SNs^us3BXhpBnYAp3fPEqm_G3)#76@)|^?2b|pM zMh(*R%;Q1~xRbLG9?F8MB@ERr_^aZ+&P3?dJS~h{95My{B2S+j7@4t~*3Gp_e6vih zrM|euws&TjA@1B?W{5)>nBK9!Fx?D{{6V}!0R>lkf{RwBYuNwtNtRbeY`sCPt1zfm zyWI(1ph~Z^7`_LJ#zF}9-uW_}T8CPWD9J)A0S16U9|P60vH-bIf2xfVffa{fpL?N4 z_@IDyx)ES9*k}KF8Y|b!NHTobDHl~*1P@AzmIy+q)#jC1bre_f$tX~_juGsPGe-*H zrEznltX$gXGWgS8FRM29=ALUN;K5#0uZCpRUQazdjGkr+izcGBmr*O8*?LHz?hGY_ z7dh+7%IHV5@a-aGwo%U=qhbml&{0(V2*&e>X>rz0M-)gRx%143LJACR%uxFxY8g81 ziv+_KZoeomi07G%g?bY{te9)K36F32l6G8C2oow$dn1{T~+9tH}fd02>C z%*$!uSxQlP&L7s5S7GOeD-TfayF^5vX ze64%)ms})0)6rm`XmAao2GV}6%Nk6>vw#?C*|V!8h#IPy=MtzpnFzJ+mQ6!=^f;(0 zS;G@G+p%|HEmy~b3zNC1`d3i%k@V)7xk!4F!=OHC6()r0u670S{L52-UTKXg6yw5M zFF8f+zY#+OiY|6-INQKYhEV#7ZMg{&IIPtt!g-n9?+inIXr(KcvAK(-z@0ZM2o+0m zweFcY1R0>4IYOv`ec!g=V+<}>w6IBK&|zqOEt3?T)bkI2Ny=pG@})~P-DKy8Eq#oc zdPK&K!p=PL%BMnFk6V(uF2m~1wfoEJ&R7PfNB0+|M=~%yyuUEr#2EQQd@=czVlpU) z`Op^a$W6(+_7`m}$Y5ggGvLmH4#Fnkst0LLgw3jWRZ3mlLXx5mjj4|R>Q&5}sGQ~Y>N_>O!BxaqOFThV`2P23B>4K=;k>%I6UiF*6Dp{9%IUTTqceq#oc zxgi7YyBKbIG*e29g&Dh1Ckm9^IQomb55SO9H>ZTiMZLh?kgF0};uL7esh2^9xP!e8 z)x0xM8q`xHnr6abjqph|vz12e4FG&^a&Ho>s;G3v8Kin6u!o}!zaYZLfLp%^k_^s5 zKuU>v$9zUf#k?~dXT3j$#yUpj=&PI@+F1N--@^HORixO2{DtlO! zYZx*Hi11=nX3m0pa&mc=TkH?1A~k7;EJ)}H5=rA}(JV-H3q-r9(K>QZNkBhA*ACS; z^SSngq+3@o2)nem=15JKT!S(Vmis^m+HV7b`lDT-H}*YWzGFR*R;RI7I2noys!)t8 z%e^PV^ywCcyI-6o9?!t^?hLr^&VYM!2Hcw%Zn`vb-f4;kYM$@Ex!M#fEIfB|8ujvD z)26Bh_}et|d{rQkH3$-}4R;FDPq7}QlqX*Yr|o0s`KIrxc9G+H+S`$)KXqh=8)|RM zU{1F(+}N9Mug`!xm*FO7#LVxPis4o5I_gfZVa5&9VaNJn!&)KrSyXtcT}L&d3jWvZZm+l)l$su?`fPx5WYPI<<%<6o;xR&|2m#?qa|aMSZ59rt4G7D_@8rju@8-7-D90oFDR)U>YZrZYJnJPby9rKZj)S81)k)!kID_M=& zRY4(4Gj6E7KZ7UV$8eL&Sggnp#*E)pmqP3$(a@|eg~$;RS*e=J=9TP&86^8a23(eg zikzy5K)xqK!}n&uy@%n(2K|{C=pMjullF+@Dm??P(d1P!l~aO3eMx+rqwIy2kB(?L zSy*JVAAn|$k7zc%OpkDGhe_yMVyAyfFfLwtz9uQ7abhMKF0z{ARL4Y-Q3ZM9Yc5&T zEsR`cjK`?{gdmYJaAoC0=C0%vCi`tXZHrMpVprv?O1ci^$h7T)O8-w}$qHL diff --git a/src/cool2/lib/shortcut.py b/src/cool2/lib/shortcut.py deleted file mode 100644 index ebff7259f..000000000 --- a/src/cool2/lib/shortcut.py +++ /dev/null @@ -1,12 +0,0 @@ -from lib.lang.language_ll1 import build_LL1 -from lib.lang.language_lr import build_LR -from lib.lang.language_regular import build_Lan_Reg -from lib.grammar.grammar_fixer import fix_common_prefix,fix_e_productions,fix_left_recursion,fix_useless_symbols -from lib.grammar.grammar_util import grammar_to_text -from lib.grammar.grammar_parser import get_grammar_from_text -from lib.utils.first_follow import compute_firsts, compute_follows - -def build_LR_2(lr_type): - def build(tok_def,gram_def,error): - return build_LR(tok_def,gram_def,lr_type,error) - return build \ No newline at end of file diff --git a/src/cool_cmp/__init__.py b/src/cool_cmp/__init__.py index 4907db10f..912fcd4be 100644 --- a/src/cool_cmp/__init__.py +++ b/src/cool_cmp/__init__.py @@ -1,3 +1,5 @@ -""" -Cool Compiler -""" \ No newline at end of file +import sys +import os +base_dir = os.path.dirname(__file__) +# sys.path.append(os.path.join(base_dir, "..")) +sys.path.append(base_dir) \ No newline at end of file diff --git a/src/cool_cmp/cil/__init__.py b/src/cool_cmp/cil/__init__.py deleted file mode 100644 index 3827461e0..000000000 --- a/src/cool_cmp/cil/__init__.py +++ /dev/null @@ -1,15 +0,0 @@ -""" -CIL package -""" - -from typing import List -from cool_cmp.shared import SymbolTable, InterfacePipeline -from cool_cmp.semantic import SemanticPipeline -from cool_cmp.cil.interface import ICil - -class CilPipeline(InterfacePipeline): - def __init__(self, semantic_pipeline:SemanticPipeline, *cils:List[ICil]): - super().__init__(semantic_pipeline, *cils) - - def __call__(self, program:str)->SymbolTable: - return super().__call__(program) diff --git a/src/cool_cmp/cil/errors.py b/src/cool_cmp/cil/errors.py deleted file mode 100644 index ec5ebfbcd..000000000 --- a/src/cool_cmp/cil/errors.py +++ /dev/null @@ -1,5 +0,0 @@ -""" -CIL errors -""" - -from cool_cmp.shared.errors import CoolError \ No newline at end of file diff --git a/src/cool_cmp/cil/interface.py b/src/cool_cmp/cil/interface.py deleted file mode 100644 index fef155074..000000000 --- a/src/cool_cmp/cil/interface.py +++ /dev/null @@ -1,10 +0,0 @@ -from cool_cmp.shared.ast import BaseAST -from cool_cmp.shared import ICoolService - -class ICil(ICoolService): - """ - CIL interface to implement - """ - - def __call__(self, ast:BaseAST) -> BaseAST: - raise NotImplementedError() diff --git a/src/cool_cmp/cil/visitors/__init__.py b/src/cool_cmp/cil/visitors/__init__.py deleted file mode 100644 index 89f4a99e2..000000000 --- a/src/cool_cmp/cil/visitors/__init__.py +++ /dev/null @@ -1,3 +0,0 @@ -""" -Middle code visitors -""" \ No newline at end of file diff --git a/src/cool2/cmp/__init__.py b/src/cool_cmp/cmp/__init__.py similarity index 100% rename from src/cool2/cmp/__init__.py rename to src/cool_cmp/cmp/__init__.py diff --git a/src/cool2/cmp/ast.py b/src/cool_cmp/cmp/ast.py similarity index 100% rename from src/cool2/cmp/ast.py rename to src/cool_cmp/cmp/ast.py diff --git a/src/cool2/cmp/automata.py b/src/cool_cmp/cmp/automata.py similarity index 100% rename from src/cool2/cmp/automata.py rename to src/cool_cmp/cmp/automata.py diff --git a/src/cool2/cmp/cil.py b/src/cool_cmp/cmp/cil.py similarity index 100% rename from src/cool2/cmp/cil.py rename to src/cool_cmp/cmp/cil.py diff --git a/src/cool2/cmp/evaluation.py b/src/cool_cmp/cmp/evaluation.py similarity index 100% rename from src/cool2/cmp/evaluation.py rename to src/cool_cmp/cmp/evaluation.py diff --git a/src/cool2/cmp/languages.py b/src/cool_cmp/cmp/languages.py similarity index 100% rename from src/cool2/cmp/languages.py rename to src/cool_cmp/cmp/languages.py diff --git a/src/cool2/cmp/nbpackage.py b/src/cool_cmp/cmp/nbpackage.py similarity index 100% rename from src/cool2/cmp/nbpackage.py rename to src/cool_cmp/cmp/nbpackage.py diff --git a/src/cool2/cmp/pycompiler.py b/src/cool_cmp/cmp/pycompiler.py similarity index 100% rename from src/cool2/cmp/pycompiler.py rename to src/cool_cmp/cmp/pycompiler.py diff --git a/src/cool2/cmp/semantic.py b/src/cool_cmp/cmp/semantic.py similarity index 100% rename from src/cool2/cmp/semantic.py rename to src/cool_cmp/cmp/semantic.py diff --git a/src/cool2/cmp/tools/__init__.py b/src/cool_cmp/cmp/tools/__init__.py similarity index 100% rename from src/cool2/cmp/tools/__init__.py rename to src/cool_cmp/cmp/tools/__init__.py diff --git a/src/cool2/cmp/tools/automata.py b/src/cool_cmp/cmp/tools/automata.py similarity index 100% rename from src/cool2/cmp/tools/automata.py rename to src/cool_cmp/cmp/tools/automata.py diff --git a/src/cool2/cmp/tools/evaluation.py b/src/cool_cmp/cmp/tools/evaluation.py similarity index 100% rename from src/cool2/cmp/tools/evaluation.py rename to src/cool_cmp/cmp/tools/evaluation.py diff --git a/src/cool2/cmp/tools/parsing.py b/src/cool_cmp/cmp/tools/parsing.py similarity index 100% rename from src/cool2/cmp/tools/parsing.py rename to src/cool_cmp/cmp/tools/parsing.py diff --git a/src/cool2/cmp/tools/regex.py b/src/cool_cmp/cmp/tools/regex.py similarity index 100% rename from src/cool2/cmp/tools/regex.py rename to src/cool_cmp/cmp/tools/regex.py diff --git a/src/cool2/cmp/utils.py b/src/cool_cmp/cmp/utils.py similarity index 100% rename from src/cool2/cmp/utils.py rename to src/cool_cmp/cmp/utils.py diff --git a/src/cool2/cmp/visitor.py b/src/cool_cmp/cmp/visitor.py similarity index 100% rename from src/cool2/cmp/visitor.py rename to src/cool_cmp/cmp/visitor.py diff --git a/src/cool2/cool/__init__.py b/src/cool_cmp/cmp_tools/__init__.py similarity index 100% rename from src/cool2/cool/__init__.py rename to src/cool_cmp/cmp_tools/__init__.py diff --git a/src/cool2/lib/grammar/grammar_fixer.py b/src/cool_cmp/cmp_tools/grammar/grammar_fixer.py similarity index 96% rename from src/cool2/lib/grammar/grammar_fixer.py rename to src/cool_cmp/cmp_tools/grammar/grammar_fixer.py index c04dd5452..076ee232b 100644 --- a/src/cool2/lib/grammar/grammar_fixer.py +++ b/src/cool_cmp/cmp_tools/grammar/grammar_fixer.py @@ -1,7 +1,7 @@ from cmp.pycompiler import (EOF, Epsilon, Grammar, NonTerminal, Production, Sentence, SentenceList, Symbol, Terminal, SentenceFromIter) -from lib.utils.trie import TrieNode, Trie -from lib.utils.algorithms import permutation # FIX +from cmp_tools.utils.trie import TrieNode, Trie +from cmp_tools.utils.algorithms import permutation # FIX # GRAMMAR FIXER UTILS def change_grammar_from_productions(gramm:Grammar,new_productions): diff --git a/src/cool2/lib/grammar/grammar_parser.py b/src/cool_cmp/cmp_tools/grammar/grammar_parser.py similarity index 94% rename from src/cool2/lib/grammar/grammar_parser.py rename to src/cool_cmp/cmp_tools/grammar/grammar_parser.py index f7e0a9933..c68e7720b 100644 --- a/src/cool2/lib/grammar/grammar_parser.py +++ b/src/cool_cmp/cmp_tools/grammar/grammar_parser.py @@ -60,8 +60,8 @@ def evaluate(self): # ======================================================================= # ############################# END PRODUCTIONS ############################# -from lib.parser.parser_ll1 import ParserLL1 -from lib.grammar.grammar_tokens import get_lexer_from_text,nonzero_digits,digits,min_letters,cap_letters,separator,regex_char_separator +from cmp_tools.parser.parser_ll1 import ParserLL1 +from cmp_tools.grammar.grammar_tokens import get_lexer_from_text,nonzero_digits,digits,min_letters,cap_letters,separator,regex_char_separator # Grammar of the parser of Grammars gram_parser = ParserLL1(G_parser) diff --git a/src/cool2/lib/grammar/grammar_tokens.py b/src/cool_cmp/cmp_tools/grammar/grammar_tokens.py similarity index 94% rename from src/cool2/lib/grammar/grammar_tokens.py rename to src/cool_cmp/cmp_tools/grammar/grammar_tokens.py index 826479a36..92649b232 100644 --- a/src/cool2/lib/grammar/grammar_tokens.py +++ b/src/cool_cmp/cmp_tools/grammar/grammar_tokens.py @@ -2,8 +2,8 @@ Sentence, SentenceList, Symbol, Terminal) from cmp.ast import Node, AtomicNode, BinaryNode, TernaryNode from cmp.utils import Token -from lib.lexer.lexer import Lexer,nonzero_digits,digits,min_letters,cap_letters -from lib.lexer.regex import fixed_tokens, EPSILON, regex_automaton +from cmp_tools.lexer.lexer import Lexer,nonzero_digits,digits,min_letters,cap_letters +from cmp_tools.lexer.regex import fixed_tokens, EPSILON, regex_automaton digits_letters = f'{digits}{min_letters}{cap_letters}' diff --git a/src/cool2/lib/grammar/grammar_util.py b/src/cool_cmp/cmp_tools/grammar/grammar_util.py similarity index 86% rename from src/cool2/lib/grammar/grammar_util.py rename to src/cool_cmp/cmp_tools/grammar/grammar_util.py index db5bcbeee..a6e59f739 100644 --- a/src/cool2/lib/grammar/grammar_util.py +++ b/src/cool_cmp/cmp_tools/grammar/grammar_util.py @@ -1,6 +1,6 @@ from cmp.pycompiler import (EOF, Epsilon, Grammar, NonTerminal, Production, Sentence, SentenceList, Symbol, Terminal) -from lib.grammar.grammar_parser import expand +from cmp_tools.grammar.grammar_parser import expand def grammar_to_text(G:Grammar): diff --git a/src/cool2/lib/lang/language.py b/src/cool_cmp/cmp_tools/lang/language.py similarity index 100% rename from src/cool2/lib/lang/language.py rename to src/cool_cmp/cmp_tools/lang/language.py diff --git a/src/cool2/lib/lang/language_ll1.py b/src/cool_cmp/cmp_tools/lang/language_ll1.py similarity index 83% rename from src/cool2/lib/lang/language_ll1.py rename to src/cool_cmp/cmp_tools/lang/language_ll1.py index 5793c024b..842d40b0a 100644 --- a/src/cool2/lib/lang/language_ll1.py +++ b/src/cool_cmp/cmp_tools/lang/language_ll1.py @@ -1,12 +1,12 @@ from cmp.pycompiler import (EOF, Epsilon, Grammar, NonTerminal, Production, Sentence, SentenceList, Symbol, Terminal, SentenceFromIter) from cmp.utils import ContainerSet, inspect, pprint, Token -from lib.grammar.grammar_tokens import get_lexer_from_text -from lib.grammar.grammar_parser import get_grammar_from_text -from lib.lexer.lexer import digits,min_letters,cap_letters -from lib.parser.parser_ll1 import ParserLL1 -from lib.grammar.grammar_fixer import fix_grammar -from lib.lang.language import Language +from cmp_tools.grammar.grammar_tokens import get_lexer_from_text +from cmp_tools.grammar.grammar_parser import get_grammar_from_text +from cmp_tools.lexer.lexer import digits,min_letters,cap_letters +from cmp_tools.parser.parser_ll1 import ParserLL1 +from cmp_tools.grammar.grammar_fixer import fix_grammar +from cmp_tools.lang.language import Language class LanguageLL1(Language): def __init__(self, grammar,lexer): diff --git a/src/cool2/lib/lang/language_lr.py b/src/cool_cmp/cmp_tools/lang/language_lr.py similarity index 83% rename from src/cool2/lib/lang/language_lr.py rename to src/cool_cmp/cmp_tools/lang/language_lr.py index f7e040b63..f7cda24e4 100644 --- a/src/cool2/lib/lang/language_lr.py +++ b/src/cool_cmp/cmp_tools/lang/language_lr.py @@ -1,7 +1,7 @@ -from lib.parser.parser_lr import LR0Parser,LR1Parser,SLR1Parser,LALR1Parser -from lib.grammar.grammar_parser import get_grammar_from_text -from lib.grammar.grammar_tokens import get_lexer_from_text -from lib.lang.language import Language +from cmp_tools.parser.parser_lr import LR0Parser,LR1Parser,SLR1Parser,LALR1Parser +from cmp_tools.grammar.grammar_parser import get_grammar_from_text +from cmp_tools.grammar.grammar_tokens import get_lexer_from_text +from cmp_tools.lang.language import Language def return_lr_parser(G,parser): parser = parser.lower() diff --git a/src/cool2/lib/lang/language_regular.py b/src/cool_cmp/cmp_tools/lang/language_regular.py similarity index 94% rename from src/cool2/lib/lang/language_regular.py rename to src/cool_cmp/cmp_tools/lang/language_regular.py index fc5a87430..35b9d85f1 100644 --- a/src/cool2/lib/lang/language_regular.py +++ b/src/cool_cmp/cmp_tools/lang/language_regular.py @@ -2,12 +2,12 @@ Sentence, SentenceFromIter, SentenceList, Symbol, Terminal) from cmp.utils import ContainerSet, Token, inspect, pprint -from lib.grammar.grammar_parser import get_grammar_from_text -from lib.grammar.grammar_tokens import get_lexer_dict_from_text,get_lexer_from_text -from lib.lang.language_lr import LanguageLR,return_lr_parser -from lib.lexer.lexer import cap_letters, digits, min_letters -from lib.lexer.regex import EPSILON, regex_automaton -from lib.utils.automaton import * +from cmp_tools.grammar.grammar_parser import get_grammar_from_text +from cmp_tools.grammar.grammar_tokens import get_lexer_dict_from_text,get_lexer_from_text +from cmp_tools.lang.language_lr import LanguageLR,return_lr_parser +from cmp_tools.lexer.lexer import cap_letters, digits, min_letters +from cmp_tools.lexer.regex import EPSILON, regex_automaton +from cmp_tools.utils.automaton import * def copy_automaton(base_automaton): """ diff --git a/src/cool2/lib/lexer/lexer.py b/src/cool_cmp/cmp_tools/lexer/lexer.py similarity index 95% rename from src/cool2/lib/lexer/lexer.py rename to src/cool_cmp/cmp_tools/lexer/lexer.py index 2f5e89fd3..9f825d22e 100644 --- a/src/cool2/lib/lexer/lexer.py +++ b/src/cool_cmp/cmp_tools/lexer/lexer.py @@ -1,7 +1,7 @@ from cmp.utils import Token from cmp.automata import State -from lib.lexer.regex import regex_automaton +from cmp_tools.lexer.regex import regex_automaton class DetailToken(Token): def __init__(self, lex,token_type): diff --git a/src/cool2/lib/lexer/regex.py b/src/cool_cmp/cmp_tools/lexer/regex.py similarity index 94% rename from src/cool2/lib/lexer/regex.py rename to src/cool_cmp/cmp_tools/lexer/regex.py index 83ff9723b..b4ed38395 100644 --- a/src/cool2/lib/lexer/regex.py +++ b/src/cool_cmp/cmp_tools/lexer/regex.py @@ -2,8 +2,8 @@ from cmp.utils import Token from cmp.pycompiler import (EOF, Epsilon, Grammar, NonTerminal, Production, Sentence, SentenceList, Symbol, Terminal) -from lib.utils.automaton import * -from lib.parser.parser_ll1 import ParserLL1 +from cmp_tools.utils.automaton import * +from cmp_tools.parser.parser_ll1 import ParserLL1 G = Grammar() diff --git a/src/cool2/lib/__init__.py b/src/cool_cmp/cmp_tools/parser/__init__.py similarity index 100% rename from src/cool2/lib/__init__.py rename to src/cool_cmp/cmp_tools/parser/__init__.py diff --git a/src/cool2/lib/parser/parser.py b/src/cool_cmp/cmp_tools/parser/parser.py similarity index 100% rename from src/cool2/lib/parser/parser.py rename to src/cool_cmp/cmp_tools/parser/parser.py diff --git a/src/cool2/lib/parser/parser_ll1.py b/src/cool_cmp/cmp_tools/parser/parser_ll1.py similarity index 95% rename from src/cool2/lib/parser/parser_ll1.py rename to src/cool_cmp/cmp_tools/parser/parser_ll1.py index c98805945..ad02b7ff9 100644 --- a/src/cool2/lib/parser/parser_ll1.py +++ b/src/cool_cmp/cmp_tools/parser/parser_ll1.py @@ -1,9 +1,9 @@ from cmp.pycompiler import (EOF, Epsilon, Grammar, NonTerminal, Production, Sentence, SentenceList, Symbol, Terminal) from cmp.utils import ContainerSet, inspect, pprint, Token -from lib.utils.first_follow import compute_firsts, compute_follows, compute_local_first -from lib.parser.parser import Parser -from lib.grammar.grammar_fixer import fix_non_derive_terminal, build_reach +from cmp_tools.utils.first_follow import compute_firsts, compute_follows, compute_local_first +from cmp_tools.parser.parser import Parser +from cmp_tools.grammar.grammar_fixer import fix_non_derive_terminal, build_reach class ParserLL1(Parser): diff --git a/src/cool2/lib/parser/parser_lr.py b/src/cool_cmp/cmp_tools/parser/parser_lr.py similarity index 96% rename from src/cool2/lib/parser/parser_lr.py rename to src/cool_cmp/cmp_tools/parser/parser_lr.py index 2e0109439..5e96f62b6 100644 --- a/src/cool2/lib/parser/parser_lr.py +++ b/src/cool_cmp/cmp_tools/parser/parser_lr.py @@ -2,11 +2,11 @@ from cmp.pycompiler import (EOF, Epsilon, Grammar, Item, NonTerminal, Production, Sentence, SentenceFromIter, SentenceList, Symbol, Terminal) -from lib.utils.first_follow import (compute_firsts, compute_follows, compute_local_first) +from cmp_tools.utils.first_follow import (compute_firsts, compute_follows, compute_local_first) from cmp.utils import ContainerSet, DisjointNode, DisjointSet, Token -from lib.parser.parser import Parser -from lib.grammar.grammar_fixer import fix_non_derive_terminal -from lib.utils.automaton import state_transpose +from cmp_tools.parser.parser import Parser +from cmp_tools.grammar.grammar_fixer import fix_non_derive_terminal +from cmp_tools.utils.automaton import state_transpose from cool.error.errors import SyntacticCoolError, SYNTACTIC_ERROR ###################### LR0 and SLR1 ########################### diff --git a/src/cool_cmp/cmp_tools/shortcut.py b/src/cool_cmp/cmp_tools/shortcut.py new file mode 100644 index 000000000..5cde6e1f1 --- /dev/null +++ b/src/cool_cmp/cmp_tools/shortcut.py @@ -0,0 +1,12 @@ +from cmp_tools.lang.language_ll1 import build_LL1 +from cmp_tools.lang.language_lr import build_LR +from cmp_tools.lang.language_regular import build_Lan_Reg +from cmp_tools.grammar.grammar_fixer import fix_common_prefix,fix_e_productions,fix_left_recursion,fix_useless_symbols +from cmp_tools.grammar.grammar_util import grammar_to_text +from cmp_tools.grammar.grammar_parser import get_grammar_from_text +from cmp_tools.utils.first_follow import compute_firsts, compute_follows + +def build_LR_2(lr_type): + def build(tok_def,gram_def,error): + return build_LR(tok_def,gram_def,lr_type,error) + return build \ No newline at end of file diff --git a/src/cool2/lib/utils/algorithms.py b/src/cool_cmp/cmp_tools/utils/algorithms.py similarity index 100% rename from src/cool2/lib/utils/algorithms.py rename to src/cool_cmp/cmp_tools/utils/algorithms.py diff --git a/src/cool2/lib/utils/automaton.py b/src/cool_cmp/cmp_tools/utils/automaton.py similarity index 100% rename from src/cool2/lib/utils/automaton.py rename to src/cool_cmp/cmp_tools/utils/automaton.py diff --git a/src/cool2/lib/utils/first_follow.py b/src/cool_cmp/cmp_tools/utils/first_follow.py similarity index 100% rename from src/cool2/lib/utils/first_follow.py rename to src/cool_cmp/cmp_tools/utils/first_follow.py diff --git a/src/cool2/lib/utils/trie.py b/src/cool_cmp/cmp_tools/utils/trie.py similarity index 100% rename from src/cool2/lib/utils/trie.py rename to src/cool_cmp/cmp_tools/utils/trie.py diff --git a/src/cool2/lib/parser/__init__.py b/src/cool_cmp/cool/__init__.py similarity index 100% rename from src/cool2/lib/parser/__init__.py rename to src/cool_cmp/cool/__init__.py diff --git a/src/cool2/cool/ast/ast.py b/src/cool_cmp/cool/ast/ast.py similarity index 100% rename from src/cool2/cool/ast/ast.py rename to src/cool_cmp/cool/ast/ast.py diff --git a/src/cool2/cool/error/error_tracker.py b/src/cool_cmp/cool/error/error_tracker.py similarity index 100% rename from src/cool2/cool/error/error_tracker.py rename to src/cool_cmp/cool/error/error_tracker.py diff --git a/src/cool2/cool/error/errors.py b/src/cool_cmp/cool/error/errors.py similarity index 100% rename from src/cool2/cool/error/errors.py rename to src/cool_cmp/cool/error/errors.py diff --git a/src/cool2/cool/grammar/comment_grammar.py b/src/cool_cmp/cool/grammar/comment_grammar.py similarity index 100% rename from src/cool2/cool/grammar/comment_grammar.py rename to src/cool_cmp/cool/grammar/comment_grammar.py diff --git a/src/cool2/cool/grammar/cool_grammar.py b/src/cool_cmp/cool/grammar/cool_grammar.py similarity index 100% rename from src/cool2/cool/grammar/cool_grammar.py rename to src/cool_cmp/cool/grammar/cool_grammar.py diff --git a/src/cool_cmp/cool/lexer/comment_lexer.obj b/src/cool_cmp/cool/lexer/comment_lexer.obj new file mode 100644 index 0000000000000000000000000000000000000000..4223c828ada87d05f28c62363b302afa60bfbdaf GIT binary patch literal 6221 zcmeI0e~4676vrLipUrL(CZ!c=raQBntDC=V)zx*?TwTrG{5jkDX7EZXiwcu|Cn6CM5hfB55fLE~A|gUWNuBr3J!fXzrIo1ua{rj`=e~R2 zedpaD-*flvDS2wqt;4)OH9C?=ciE{_JX;?(d(8~}I?BJ;w2fGNg?r6;(h2T$b~ynx z!c0XSbtB$WpYDsK66shRtJR2>RZH@XO4^Ks=_udbjw)Z8F%k(Q<3JfyOE3P_nN)W! zV#iX+tkdZPpiGTgXxo`sIA@#rx`G~8m{A-1Q2w@5vct?IVo4(|)!&UaV?<6(ksItfFp7%aXLH#{%8To`Kl9aREG7Mz0>TSt8uy3%-bSuTtf#hsR^ z@|JWq7EdK5Jbcq~Q(tpm)xFEih4(u!<6y;^cRx-<#~o|c7YoA*Kn+Zl*BKtDbtwT+D3 z?v1G94t=_#FKs$-Gt7p$P!5%hyn0>o( z@j<&Dm$j|KVBG;&@5ZqauZIlfOSlbOsAgzjn94ARp_3uZFrHy9!#0NL3@r?G3=c3Y zV|bXMouPtZ7Q+&T2ty;oR))0J!$yV&8SZD;#IS*3DMOB78p90? zX@(UH6ByzQ>lsoE%NZs!v=QXgDD7!og*)^RrWM=had2BUpkY=#aS>t@z0YBk4gm9@sB9CEA)C=|an5wXphguhk|{$RwpYib1<3ezUpM$${Non$A; zZjwDDkC5ym=_ffra*#xkJWg_$udWfiZ1!R`F0M!2R{!d<`I+MU zh2%WR1(J&-mq;#?RFVAZiB%XE{6R)m4e(Hu4;LRz;SLB&!lk(^xQ7oqMFun+n*W<{bE;CG)9@slk0Ovq^mF%|=W7LsJ;HAYJPgiVA^{ zB@`D*3Z;cILbeb(w|)kiw(2qht z37r%AS?Cv`^FkMdE(%=|x-3*Bgch$OKx5a0-mVGlT@yMykwqr23*_^O;d|@1j$A!4ppv zbykqF2EG02yyC?wu>^{O;j{8OH?ko|dB_It*_UPvY@zEuYQS|LHQ*Y3bl_fk)7^0H zvHb5goLfI|k-y-s>wz1}J-tg~K03dAO}lf*Jy6u$K=*jN=+Q&$mbb3hxW0MqqPCS?4fjr)K4a#r*>k4eP+d_~>A!3D=9=;M%)PI% zY2N&Cp-rLADU(BW^`Ql!(1g1uJ~Sz`bH}dE+MSyMq1w7YC=jd-;xAGVDHN>3fA^YH SNeF8lXhx_F1s(h_TmKi?VL`S4 literal 0 HcmV?d00001 diff --git a/src/cool2/cool/lexer/comment_lexer.py b/src/cool_cmp/cool/lexer/comment_lexer.py similarity index 85% rename from src/cool2/cool/lexer/comment_lexer.py rename to src/cool_cmp/cool/lexer/comment_lexer.py index cbf9a7324..58508093d 100644 --- a/src/cool2/cool/lexer/comment_lexer.py +++ b/src/cool_cmp/cool/lexer/comment_lexer.py @@ -21,4 +21,8 @@ base_dir = os.path.dirname(__file__) comment_lexer_path = os.path.join(base_dir, 'comment_lexer.obj') # comment_lexer = save_lexer(comment_tokens_def,comment_lexer_path,C) -comment_lexer = load_lexer(comment_lexer_path) \ No newline at end of file +comment_lexer = None +try: + comment_lexer = load_lexer(comment_lexer_path) +except Exception as e: + print(e) \ No newline at end of file diff --git a/src/cool_cmp/cool/lexer/cool_lexer.obj b/src/cool_cmp/cool/lexer/cool_lexer.obj new file mode 100644 index 0000000000000000000000000000000000000000..f0c60d55ef3c09171e2abf96d22bccd88a1b9992 GIT binary patch literal 139990 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�Z{k=D0~1E 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 literal 0 HcmV?d00001 diff --git a/src/cool2/cool/lexer/cool_lexer.py b/src/cool_cmp/cool/lexer/cool_lexer.py similarity index 92% rename from src/cool2/cool/lexer/cool_lexer.py rename to src/cool_cmp/cool/lexer/cool_lexer.py index 569a5d13a..f60dcab9f 100644 --- a/src/cool2/cool/lexer/cool_lexer.py +++ b/src/cool_cmp/cool/lexer/cool_lexer.py @@ -47,4 +47,8 @@ base_dir = os.path.dirname(__file__) cool_lexer_path = os.path.join(base_dir, 'cool_lexer.obj') # cool_lexer = save_lexer(cool_tokens_def,cool_lexer_path,G) -cool_lexer = load_lexer(cool_lexer_path) \ No newline at end of file +cool_lexer = None +try: + cool_lexer = load_lexer(cool_lexer_path) +except Exception as e: + print(e) \ No newline at end of file diff --git a/src/cool2/cool/lexer/ply_cool_lexer.py b/src/cool_cmp/cool/lexer/ply_cool_lexer.py similarity index 100% rename from src/cool2/cool/lexer/ply_cool_lexer.py rename to src/cool_cmp/cool/lexer/ply_cool_lexer.py diff --git a/src/cool2/cool/lexer/utils.py b/src/cool_cmp/cool/lexer/utils.py similarity index 87% rename from src/cool2/cool/lexer/utils.py rename to src/cool_cmp/cool/lexer/utils.py index 6929616ae..2993e7f0c 100644 --- a/src/cool2/cool/lexer/utils.py +++ b/src/cool_cmp/cool/lexer/utils.py @@ -1,4 +1,4 @@ -from lib.lexer.lexer import DetailLexer as Lexer +from cmp_tools.lexer.lexer import DetailLexer as Lexer import pickle def save_lexer(table,path,G): diff --git a/src/cool2/cool/libs.py b/src/cool_cmp/cool/libs.py similarity index 100% rename from src/cool2/cool/libs.py rename to src/cool_cmp/cool/libs.py diff --git a/src/cool_cmp/cool/parser/comment_parser.obj b/src/cool_cmp/cool/parser/comment_parser.obj new file mode 100644 index 0000000000000000000000000000000000000000..7ee11f46912c11f23cea5d8f431a28a8e2483533 GIT binary patch literal 2597 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: - return super().__call__(program) diff --git a/src/cool_cmp/lexer/errors.py b/src/cool_cmp/lexer/errors.py deleted file mode 100644 index 902f3d2f9..000000000 --- a/src/cool_cmp/lexer/errors.py +++ /dev/null @@ -1,22 +0,0 @@ -""" -Lexer errors -""" - -from cool_cmp.shared.errors import CoolError -from cool_cmp.shared.token import ICoolToken - -class LexerCoolError(CoolError): - """ - Error class for lexical errors - """ - - ERROR_TYPE = "LexicographicError" - - FORMAT = "({}, {}) - {}: {}" - - def __init__(self, error_message:str, error_token:ICoolToken): - super().__init__(error_message) - self.token = error_token - - def __str__(self): - return self.FORMAT.format(self.token.get_position()[0],self.token.get_position()[1], self.ERROR_TYPE, self.error) diff --git a/src/cool_cmp/lexer/interface.py b/src/cool_cmp/lexer/interface.py deleted file mode 100644 index 8f7f381e1..000000000 --- a/src/cool_cmp/lexer/interface.py +++ /dev/null @@ -1,11 +0,0 @@ -from typing import List -from cool_cmp.shared.token import ICoolToken -from cool_cmp.shared import ICoolService - -class ILexer(ICoolService): - """ - Lexer interface to implement - """ - - def __call__(self, program_string:str) -> List[ICoolToken]: - raise NotImplementedError() diff --git a/src/cool_cmp/lexer/lexer.py b/src/cool_cmp/lexer/lexer.py deleted file mode 100644 index e29b2d53e..000000000 --- a/src/cool_cmp/lexer/lexer.py +++ /dev/null @@ -1,381 +0,0 @@ -""" -Lexer usando ply -""" -from typing import List, Tuple - -from cool_cmp.lexer.interface import ILexer -from cool_cmp.shared.token import ICoolToken -from cool_cmp.shared.errors import ErrorTracker -from cool_cmp.lexer.errors import LexerCoolError -from cool.grammar import cool_grammar as cool_G -import ply.lex as lex - -class Lex(): - def __init__(self, lex): - self.lex = lex - - def set_type(self, new): - self.type = new - - def set_row(self, new): - self.row = new - - def set_column(self, new): - self.column = new - - def __str__(self): - return self.lex - - def __getitem__(self, i): - if i == 0: - return self.lex - - elif i == 1: - return self.row - - elif i == 2: - return self.column - - elif i == 3: - return self.type - -class PlyCoolToken(lex.LexToken, ICoolToken): - - def __init__(self, lex:str, typex, line:int, column:int): - self.set_lex(lex) - self.set_type(typex) - self.set_position(line, column) - - def set_lex(self, lex:str): - self.lex = Lex(lex) - self.value = Lex(lex) - - def set_type(self, typex:str): - self.lex.set_type(typex) - self.value.set_type(typex) - self.type = typex - self.token_type = typex - self.typex = typex - - def set_position(self, line:int, column:int): - self.lex.set_row(line) - self.value.set_row(line) - self.lex.set_column(column) - self.value.set_column(column) - self.lineno = line - self.line = line - self.column = column - self.lexpos = column - - def get_lex(self)->str: - return self.lex - - def get_type(self)->str: - return self.typex - - def get_position(self)->Tuple[int,int]: - return (self.line, self.column) - - def __str__(self): - return f"{self.lex}:{self.type} Line {self.line} Column{self.column}" - - def __repr__(self): - return str(self) - -class PlyLexer(ILexer): - - @staticmethod - def find_column(input, token): - line_start = input.rfind('\n', 0, token.lexpos) + 1 - return (token.lexpos - line_start) + 1 - - def __init__(self): - self.error_tracker = ErrorTracker() # Error tracker implementation - - reserved = { - 'if' : 'IF', - 'then' : 'THEN', - 'fi' : 'FI', - 'else' : 'ELSE', - 'case' : 'CASE', - 'of' : 'OF', - 'esac' : 'ESAC', - 'class' : 'CLASS', - 'inherits' : 'INHERITS', - 'let' : 'LET', - 'in' : 'IN', - 'while' : 'WHILE', - 'loop' : 'LOOP', - 'pool' : 'POOL', - 'new' : 'NEW', - 'isvoid' : 'ISVOID', - 'not' : 'NOT', - 'true' : 'TRUE', - 'false' : 'FALSE' - } - - tokens = ( - 'OBJECTID', - 'TYPEID', - 'NUMBER', - 'PLUS', - 'MINUS', - 'STAR', - 'DIV', - 'EQ', - 'LEQ', - 'LESS', - 'NEG', - 'OPAR', - 'CPAR', - 'OCUR', - 'CCUR', - 'ASSIGN', - 'SEMI', - 'COLON', - 'SIGNALER', - 'ARROBA', - 'STRING', - 'COMMENT', - 'DOT', - 'COMMA' - ) + tuple(reserved[x] for x in reserved.keys()) - - - self.terminals = { - 'IF' : cool_G.ifx, - 'THEN' : cool_G.then, - 'ELSE' : cool_G.elsex, - 'FI' : cool_G.if_r, - 'WHILE' : cool_G.whilex, - 'LOOP' : cool_G.loop, - 'POOL' : cool_G.loop_r, - 'OCUR' : cool_G.ocur, - 'CCUR' : cool_G.ccur, - 'COLON' : cool_G.colon, - 'SEMI' : cool_G.semi, - 'COMMA' : cool_G.comma, - 'DOT' : cool_G.dot, - 'OPAR' : cool_G.opar, - 'CPAR' : cool_G.cpar, - 'PLUS' : cool_G.plus, - 'MINUS' : cool_G.minus, - 'DIV' : cool_G.div, - 'STAR' : cool_G.star, - 'NOT' : cool_G.notx, - 'NEG' : cool_G.roof, - 'LESS' : cool_G.less, - 'LEQ' : cool_G.less_eq, - # 'GREAT' : cool_G.greater, - # 'GEQ' : cool_G.greater_eq, - 'EQ' : cool_G.equal, - 'LET' : cool_G.let, - 'IN' : cool_G.inx, - 'CASE' : cool_G.case, - 'OF' : cool_G.of, - 'ESAC' : cool_G.case_r, - 'SIGNALER' : cool_G.arrow, - 'ASSIGN' : cool_G.assign, - 'TRUE' : cool_G.true, - 'FALSE' : cool_G.false, - 'NUMBER' : cool_G.num, - 'STRING' : cool_G.string, - 'CLASS' : cool_G.classx, - 'INHERITS' : cool_G.inherits, - 'NEW' : cool_G.new, - 'ISVOID' : cool_G.isvoid, - 'OBJECTID' : cool_G.idx, - 'TYPEID' : cool_G.typex, - 'ARROBA' : cool_G.at, - 'COMMENT' : cool_G.comment_open, - 'COMMENT' : cool_G.comment_close} - - # def t_COMMENTMULTI(t): - # r'\(\*(.|\n)*?\*\)' - # t.lexer.lineno += t.value.count("\n") - - # def t_COMMENTMULTIUNFINISHED(t): - # r'\(\*(.|\n)*' - # t.lexer.lineno += t.value.count("\n") - # msg = 'EOF in comment' - # self.add_error(LexerCoolError(msg, PlyCoolToken(t.value, t.type, t.lexer.lineno - 1, t.lexer.lexpos - 1))) - - def t_STRING(t): - r'"([^\r\n"\\]|(\\\n)|(\\.)){0,1024}"' - t.lexer.lineno += t.value.count("\n") - null_ch = 'String contains null character' - for i in range(len(t.value)): - if t.value[i] == '\x00': - pos = t.lexer.lexpos - (len(t.value) - i) - line = t.lexer.lineno - t.value[:i].count("\n") - self.add_error(LexerCoolError(null_ch, PlyCoolToken(t.value, t.type, line, pos))) - return t - - def t_STRINGUNFINISHED(t): - r'"([^\r\n"\\]|(\\\n)|(\\.)){0,1024}\n' - t.lexer.lineno += t.value.count("\n") - null_ch = 'String contains null character' - for i in range(len(t.value)): - if t.value[i] == '\x00': - pos = t.lexer.lexpos - (len(t.value) - i) - line = t.lexer.lineno - t.value[:i].count("\n") - self.add_error(LexerCoolError(null_ch, PlyCoolToken(t.value, t.type, line, pos))) - msg = 'Unterminated string constant' - self.add_error(LexerCoolError(msg, PlyCoolToken(t.value, t.type, t.lexer.lineno - 1, t.lexer.lexpos - 1))) - - def t_STRINGUNFINISHEDEOF(t): - r'"([^\r\n"\\]|(\\\n)|(\\.)){0,1024}' - t.lexer.lineno += t.value.count("\n") - null_ch = 'String contains null character' - for i in range(len(t.value)): - if t.value[i] == '\x00': - pos = t.lexer.lexpos - (len(t.value) - i) - line = t.lexer.lineno - t.value[:i].count("\n") - self.add_error(LexerCoolError(null_ch, PlyCoolToken(t.value, t.type, line, pos))) - msg = 'EOF in string constant' - self.add_error(LexerCoolError(msg, PlyCoolToken(t.value, t.type, t.lexer.lineno, t.lexer.lexpos))) - - def t_NUMBER(t): - r'\d+' - try: - int(t.value) - except ValueError: - msg = "Integer value too large %d", t.value - self.add_error(LexerCoolError(msg, PlyCoolToken(t.value, t.type, t.lineno, t.lexpos))) # TODO Set Token column - t.value = 'Invalid' - return t - - def t_OBJECTID(t): - r'[a-z][a-zA-Z0-9_]*' - low = t.value.lower() - t.type = reserved.get(low,'OBJECTID') - return t - - def t_TYPEID(t): - r'[A-Z][a-zA-Z0-9_]*' - low = t.value.lower() - if low == 'true': - t.type = 'TYPEID' - - elif low == 'false': - t.type = 'TYPEID' - - else: - t.type = reserved.get(low, 'TYPEID') - - return t - - def t_COMMENTSINGLE(t): - r'(--.*)' - - - t_PLUS = r'\+' - t_MINUS = r'-' - t_STAR = r'\*' - t_DIV = r'/' - t_OPAR = r'\(' - t_CPAR = r'\)' - t_OCUR = r'\{' - t_CCUR = r'\}' - t_EQ = r'=' - t_ASSIGN = r'<-' - t_LEQ = r'<=' - t_LESS = r'<' - t_NEG = r'~' - t_SEMI = r';' - t_COLON = r':' - t_SIGNALER = r'=>' - t_ARROBA = r'@' - t_DOT = r'\.' - t_COMMA = r',' - - t_ignore = " \t" - - def t_newline(t): - r'\n+' - t.lexer.lineno += t.value.count("\n") - - def t_error(t): - msg = f'ERROR "{t.value[0]}"' - self.add_error(LexerCoolError(msg, PlyCoolToken(t.value[0], t.type, t.lineno, t.lexpos))) # TODO Set Token column - t.lexer.skip(1) - - self.lexer = lex.lex() - - def __call__(self, program_string:str): - self.error_tracker = ErrorTracker() - count = 0 - lines = 0 - passes = 0 - semi_clean_string = [] - for i in range(len(program_string)): - if passes > 0: - passes -= 1 - elif program_string[i] == '(' and i + 1 < len(program_string) and program_string[i + 1] == '*': - count += 1 - semi_clean_string.append(' ') - semi_clean_string.append(' ') - if i + 2 < len(program_string) and program_string[i + 2] == ')': - semi_clean_string.append(' ') - passes = 2 - - else: - passes = 1 - - elif program_string[i] == '*' and i + 1 < len(program_string) and program_string[i + 1] == ')' and count > 0: - count -= 1 - semi_clean_string.append(' ') - semi_clean_string.append(' ') - passes = 1 - - elif count > 0 and program_string[i] != '\n': - semi_clean_string.append(' ') - - elif program_string[i] == '\n': - semi_clean_string.append(program_string[i]) - lines += 1 - - else: - semi_clean_string.append(program_string[i]) - - clean = ''.join(semi_clean_string) - # print(clean) - # print(len(clean)) - # print(len(program_string)) - self.lexer.input(clean) - result = [] - base_line = -1 - while True: - tok = self.lexer.token() - # if base_line < 0: - # base_line = tok.lineno - 1 - - if not tok: - break - result.append(PlyCoolToken(tok.value, tok.type, tok.lineno, self.find_column(program_string, tok))) - - if count > 0: - self.add_error(LexerCoolError('EOF in comment', PlyCoolToken('', '', lines + 1, len(program_string)))) - - for error in self.error_tracker.get_errors(): - error.token.set_position(error.token.lineno, self.find_column(program_string, error.token)) - - self.add_extra_info('text', program_string) - self.add_extra_info('verbose', False) - self.add_extra_info('text_tokens', result) - self.add_extra_info('errors', self.get_errors()) - - for token in result: - token.set_type(self.terminals[token.token_type]) - - result.append(PlyCoolToken('$', cool_G.G.EOF, lines + 1, len(program_string))) - result[-1].set_position(result[-1].line, self.find_column(program_string, result[-1])) - - return result - - def add_error(self, error:LexerCoolError): - self.error_tracker.add_error(error) - - def get_errors(self)->List[LexerCoolError]: - errors = self.error_tracker.get_errors() - return errors diff --git a/src/cool_cmp/lexer/lexer2.py b/src/cool_cmp/lexer/lexer2.py deleted file mode 100644 index 4f9435046..000000000 --- a/src/cool_cmp/lexer/lexer2.py +++ /dev/null @@ -1,73 +0,0 @@ -""" -Lexer usando ply -""" -from typing import List, Tuple - -from cool_cmp.lexer.interface import ILexer -from cool_cmp.shared.token import ICoolToken -from cool_cmp.shared.errors import ErrorTracker -from cool_cmp.lexer.errors import LexerCoolError -# from cool2.cool.lexer.cool_lexer import cool_lexer -# from cool2.cool.lexer.comment_lexer import comment_lexer -# from cool2.cool.pipeline import lexer_pipeline - -# from cool2.lib.lexer.lexer import DetailToken, DetailLexer - -class CoolToken2(ICoolToken): - - def set_lex(self, lex:str): - self.lex = (lex, self.row, self.column) - - def get_lex(self)->str: - return self.lex[0] - - def set_type(self, typex:str): - self.token_type = typex - - def get_type(self)->str: - return self.token_type - - def set_position(self, line:int, column:int): - self.lex = (self.lex[0], line, column) - self.row = line - self.column = column - - def get_position(self)->Tuple[int,int]: - return (self.row,self.column) - - def __str__(self): - return f"{self.get_lex()}:{self.get_type()} Line:{self.get_position()[0]} Column:{self.get_position()[1]}" - - def __repr__(self): - return str(self) - -class CoolLexer2(ILexer): - - @property - def name(self)->str: - return "lexer_name" - - def __init__(self): - self.error_tracker = ErrorTracker() # Error tracker implementation - - def __call__(self, program_string:str) -> List[ICoolToken]: - result = lexer_pipeline(program_string) - tokens = result["text_tokens"] - for err in result["errors"]: - self.add_error(err) - - # Adding context - for key, value in result.items(): - self.add_extra_info(key, value) - - return [self.__DetailToken2CoolToken2(tok) for tok in tokens] - - def add_error(self, error:LexerCoolError): - self.error_tracker.add_error(error) - - def get_errors(self)->List[LexerCoolError]: - errors = self.error_tracker.get_errors() - return errors - - def __DetailToken2CoolToken2(self, detail_token)->CoolToken2: - return CoolToken2(detail_token.lex, detail_token.token_type) diff --git a/src/cool2/main.py b/src/cool_cmp/main.py similarity index 100% rename from src/cool2/main.py rename to src/cool_cmp/main.py diff --git a/src/cool_cmp/mips/__init__.py b/src/cool_cmp/mips/__init__.py deleted file mode 100644 index 55fed0448..000000000 --- a/src/cool_cmp/mips/__init__.py +++ /dev/null @@ -1,15 +0,0 @@ -""" -MIPS package -""" - -from typing import List -from cool_cmp.mips.interface import IMips -from cool_cmp.cil import CilPipeline -from cool_cmp.shared import SymbolTable, InterfacePipeline - -class MipsPipeline(InterfacePipeline): - def __init__(self, cil_pipeline:CilPipeline, *mipses:List[IMips]): - super().__init__(cil_pipeline, *mipses) - - def __call__(self, program:str)->SymbolTable: - return super().__call__(program) \ No newline at end of file diff --git a/src/cool_cmp/mips/errors.py b/src/cool_cmp/mips/errors.py deleted file mode 100644 index 98b03911c..000000000 --- a/src/cool_cmp/mips/errors.py +++ /dev/null @@ -1,5 +0,0 @@ -""" -MIPS errors -""" - -from cool_cmp.shared.errors import CoolError \ No newline at end of file diff --git a/src/cool_cmp/mips/interface.py b/src/cool_cmp/mips/interface.py deleted file mode 100644 index a9e5571bc..000000000 --- a/src/cool_cmp/mips/interface.py +++ /dev/null @@ -1,12 +0,0 @@ - -from cool_cmp.shared.ast import BaseAST -from cool_cmp.shared import ICoolService - -class IMips(ICoolService): - """ - MIPS interface to implement - """ - - def __call__(self, ast:BaseAST) -> BaseAST: - raise NotImplementedError() - diff --git a/src/cool_cmp/mips/visitors/__init__.py b/src/cool_cmp/mips/visitors/__init__.py deleted file mode 100644 index 7b708afa2..000000000 --- a/src/cool_cmp/mips/visitors/__init__.py +++ /dev/null @@ -1,3 +0,0 @@ -""" -MIPS Visitors -""" \ No newline at end of file diff --git a/src/cool_cmp/parser/__init__.py b/src/cool_cmp/parser/__init__.py deleted file mode 100644 index 19c05f937..000000000 --- a/src/cool_cmp/parser/__init__.py +++ /dev/null @@ -1,18 +0,0 @@ -""" -Cool parsing package -""" - -from cool_cmp.parser.interface import IParser -from cool_cmp.lexer import LexerPipeline -from cool_cmp.parser.parser2 import CoolParer2 -from cool_cmp.shared import SymbolTable, InterfacePipeline - -default_parser = CoolParer2() - -class ParserPipeline(InterfacePipeline): - - def __init__(self, lexer_pipeline:LexerPipeline, parser:IParser=default_parser): - super().__init__(lexer_pipeline, *[parser,]) - - def __call__(self, program:str)->SymbolTable: - return super().__call__(program) diff --git a/src/cool_cmp/parser/errors.py b/src/cool_cmp/parser/errors.py deleted file mode 100644 index 8b7c9d914..000000000 --- a/src/cool_cmp/parser/errors.py +++ /dev/null @@ -1,23 +0,0 @@ -""" -Parser errors -""" - -from cool_cmp.shared.errors import CoolError -from cool_cmp.shared.token import ICoolToken - -class SyntacticCoolError(CoolError): - """ - Error class for syntactic errors - """ - - ERROR_TYPE = "SyntacticError" - - FORMAT = "({}, {}) - {}: {}" - - def __init__(self, error_message:str, error_token:ICoolToken): - super().__init__(error_message) - self.token = error_token - - def __str__(self): - return self.FORMAT.format(self.token.get_position()[0],self.token.get_position()[1], self.ERROR_TYPE, f'ERROR at or near "{self.token.get_lex()}"') - diff --git a/src/cool_cmp/parser/interface.py b/src/cool_cmp/parser/interface.py deleted file mode 100644 index 2d0301df0..000000000 --- a/src/cool_cmp/parser/interface.py +++ /dev/null @@ -1,13 +0,0 @@ -from typing import List -from cool_cmp.shared.token import ICoolToken -from cool_cmp.shared.ast import BaseAST -from cool_cmp.shared import ICoolService -from cool2.cmp.pycompiler import AttributeProduction - -class IParser(ICoolService): - """ - Parser interface to implement - """ - - def __call__(self, tokens:List[ICoolToken], context) -> List[AttributeProduction]: - raise NotImplementedError() diff --git a/src/cool_cmp/parser/parser2.py b/src/cool_cmp/parser/parser2.py deleted file mode 100644 index 4db2605ef..000000000 --- a/src/cool_cmp/parser/parser2.py +++ /dev/null @@ -1,32 +0,0 @@ -from cool_cmp.parser.interface import IParser -from cool_cmp.lexer.interface import ICoolToken -from cool_cmp.shared.errors import ErrorTracker -from cool_cmp.parser.errors import SyntacticCoolError -from cool2.cool.parser.cool_parser import cool_parser -from cool2.cool.pipeline import syntax_pipeline -from cool_cmp.shared.ast import BaseAST -from typing import List,Dict - -class CoolParer2(IParser): - - @property - def name(self)->str: - return "parser" - - def __init__(self): - self.error_tracker = ErrorTracker() # Error tracker implementation - - def __call__(self, tokens:List[ICoolToken], context:Dict[str,object]) -> BaseAST: - result = syntax_pipeline(context) - new_keys = set(result.keys()).difference(set(context.keys())) - for key in new_keys: - self.add_extra_info(key, result[key]) - return BaseAST(result["ast"]) - - - def add_error(self, error:SyntacticCoolError): - self.error_tracker.add_error(error) - - def get_errors(self)->List[SyntacticCoolError]: - errors = self.error_tracker.get_errors() - return errors diff --git a/src/cool_cmp/semantic/__init__.py b/src/cool_cmp/semantic/__init__.py deleted file mode 100644 index b8a7df09a..000000000 --- a/src/cool_cmp/semantic/__init__.py +++ /dev/null @@ -1,16 +0,0 @@ -""" -Cool semantic package -""" - -from typing import List -from cool_cmp.semantic.interface import ISemantic -from cool_cmp.parser import ParserPipeline -from cool_cmp.shared import SymbolTable, InterfacePipeline - -class SemanticPipeline(InterfacePipeline): - - def __init__(self, parser_pipeline:ParserPipeline, *semantics:List[ISemantic]): - super().__init__(parser_pipeline, *semantics) - - def __call__(self, program:str)->SymbolTable: - return super().__call__(program) diff --git a/src/cool_cmp/semantic/errors.py b/src/cool_cmp/semantic/errors.py deleted file mode 100644 index cdacd76cf..000000000 --- a/src/cool_cmp/semantic/errors.py +++ /dev/null @@ -1,41 +0,0 @@ -""" -Semantic errors -""" - -from cool_cmp.shared.errors import CoolError - -VARIABLE_ALREADY_DEFINED = lambda variable_name: f"Variable {variable_name} already defined" -VARIABLE_NOT_DEFINED = lambda variable_name: f"Variable {variable_name} is not defined" -METHOD_PARAMS_NAMES_EQUALS = lambda method_name: f"Params with equal names present in {method_name}" -METHOD_ALREADY_DEFINED = lambda method_name, param_count, type_name: f"Method {method_name} with {param_count} params already defined in {type_name}" -METHOD_REDEFINITION_INVALID = lambda method_name, old_signature: f"Method {method_name} already defined with {old_signature} in a parent class" -METHOD_NOT_DEFINED = lambda method_name, params_count, type_name: f"Method {method_name} with {params_count} params is not defined in {type_name}" -ATTRIBUTE_ALREADY_DEFINED = lambda attribute_name, type_name: f"Attribute {attribute_name} already defined in {type_name}" -ATTRIBUTE_NOT_DEFINED = lambda attr_name, type_name: f"Attribute {attr_name} not defined in {type_name}" -TYPE_NOT_DEFINED = lambda type_name: f"Type {type_name} not defined" -TYPE_ALREADY_DEFINED = lambda type_name: f"Type {type_name} already defined" -TYPE_NOT_INHERITABLE = lambda type_name: f"Can't inherit from {type_name}" -TYPE_CYCLIC_DEPENDENCY = lambda cycle: f"Types {', '.join(cycle)} form a cyclic dependency" - -class SemanticError(CoolError): - """ - Base Semantic Error - """ - - ERROR_TYPE = "SemanticError" - - FORMAT = "({}, {}) - {}: {}" - - def set_position(self, line:int, col:int): - """ - Set the line and column of this error - """ - self.line = line - self.column = col - - def __str__(self): - if hasattr(self, 'line') and hasattr(self, 'column'): - return self.FORMAT.format(self.line, self.column, self.ERROR_TYPE, self.error) - return self.ERROR_TYPE + " - " + self.error - - diff --git a/src/cool_cmp/semantic/implementations.py b/src/cool_cmp/semantic/implementations.py deleted file mode 100644 index 08814208f..000000000 --- a/src/cool_cmp/semantic/implementations.py +++ /dev/null @@ -1,247 +0,0 @@ -from cool_cmp.semantic.interface import IContext, IVariableInfo, IAttributeInfo, IMethodInfo, IType, IScope -from cool_cmp.semantic.errors import SemanticError, VARIABLE_ALREADY_DEFINED, ATTRIBUTE_ALREADY_DEFINED, \ - METHOD_ALREADY_DEFINED, METHOD_REDEFINITION_INVALID, METHOD_NOT_DEFINED, ATTRIBUTE_NOT_DEFINED, \ - TYPE_NOT_DEFINED, TYPE_ALREADY_DEFINED, VARIABLE_NOT_DEFINED, TYPE_NOT_INHERITABLE, METHOD_PARAMS_NAMES_EQUALS -from typing import List - -class VariableInfo(IVariableInfo): - """ - Base VariableInfo - """ - - def __init__(self, name:str, variable_type:IType): - self._name = name - self._type = variable_type - - @property - def name(self)->str: - return self._name - - @property - def type(self)->IType: - return self._type - -class AttributeInfo(IAttributeInfo): - """ - Base AttributeInfo - """ - - def __init__(self, name:str, variable_type:IType): - self._name = name - self._type = variable_type - - @property - def name(self)->str: - return self._name - - @property - def type(self)->IType: - return self._type - -class MethodInfo(IMethodInfo): - """ - Base MethodInfo - """ - - def __init__(self, name:str, parameters:List[IVariableInfo], return_type:IType): - self._name = name - self._parameters = parameters - self._return_type = return_type - - @property - def name(self)->str: - return self._name - - @property - def parameters(self)->List[IVariableInfo]: - return self._parameters - - @property - def return_type(self)->IType: - return self._return_type - -class CoolType(IType): - """ - Base Type - """ - - ATTRIBUTE_INFO_TYPE = AttributeInfo - METHOD_INFO_TYPE = MethodInfo - - def __init__(self, name:str, father:IType, inheritable:bool=True): - self._name = name - self._father = father - self._inheritable = inheritable - self._attributes = [] - self._methods = [] - - - @property - def name(self)->str: - return self._name - - @property - def father(self)->IType: - return self._father - - @property - def is_inheritable(self)->bool: - return self._inheritable - - def add_attribute(self, name:str, attr_type:IType)->IAttributeInfo: - if self.is_attribute_defined(name): - raise SemanticError(ATTRIBUTE_ALREADY_DEFINED(name, self.name)) - attribute = self.ATTRIBUTE_INFO_TYPE(name, attr_type) - self._attributes.append(attribute) - return attribute - - def is_attribute_defined(self, name:str)->bool: - try: - attr = self.get_attribute(name) - return bool(attr) - except SemanticError: - return False - - def add_method(self, name:str, params:List[IVariableInfo], return_type:IType)->IMethodInfo: - names = { x.name for x in params } - - if len(names) != len(params): - raise SemanticError(METHOD_PARAMS_NAMES_EQUALS(name)) - - try: - # Checking if a local method already exist with the given signature - local_method = next(x for x in self._methods if x.name == name and len(x.parameters) == len(params)) - raise SemanticError(METHOD_ALREADY_DEFINED(name, len(params), self.name)) - except StopIteration: - # Checking if the method already exist in parents types and can be redefined - if self.father: - try: - parent_method = self.father.get_method(name, len(params)) - if not all([parent_method.return_type == return_type, *[x.type == y.type for x,y in zip(params, parent_method.parameters)]]): - # The params types or the return type of the new and the old method doesn't match - raise SemanticError(METHOD_REDEFINITION_INVALID(name, parent_method.parameters + [parent_method.return_type])) - except SemanticError: - # No method exist - pass - - # The method can be defined in this type - method = self.METHOD_INFO_TYPE(name, params, return_type) - self._methods.append(method) - return method - - def is_method_defined(self, name:str, params_count:int)->bool: - try: - method = self.get_method(name, params_count) - return bool(method) - except SemanticError: - return False - - def get_method(self, name:str, params_count:int)->IMethodInfo: - try: - method = next(x for x in self.methods if x.name == name and len(x.parameters) == params_count) - return method - except StopIteration: - raise SemanticError(METHOD_NOT_DEFINED(name, params_count, self.name)) - - def get_attribute(self, name:str)->IAttributeInfo: - try: - attr = next(x for x in self.attributes if x.name == name) - return attr - except StopIteration: - raise SemanticError(ATTRIBUTE_NOT_DEFINED(name, self.name)) - - @property - def methods(self)->List[IMethodInfo]: - father_methods = [] - if self.father: - father_methods = self.father.methods - return self._methods + father_methods - - @property - def attributes(self)->List[IAttributeInfo]: - father_attrs = [] - if self.father: - father_attrs = self.father.attributes - return self._attributes + father_attrs - -class Context(IContext): - """ - Base Context - """ - - TYPE_TYPE = CoolType - - def __init__(self, basic_types:List[IType]=[]): - self._types = basic_types - - def add_type(self, name:str, father:IType)->IType: - if not father.is_inheritable: - raise SemanticError(TYPE_NOT_INHERITABLE(father.name)) - try: - typex = self.get_type(name) - except SemanticError: - typex = self.TYPE_TYPE(name, father) - self._types.append(typex) - return typex - raise SemanticError(TYPE_ALREADY_DEFINED(name)) - - def get_type(self, name:str)->IType: - try: - typex = next(x for x in self._types if x.name == name) - return typex - except StopIteration: - raise SemanticError(TYPE_NOT_DEFINED(name)) - - @property - def types(self)->List[IType]: - return self._types - - -class Scope(IScope): - """ - Base Scope - """ - - # Implementation type for IVariableInfo used by this scope - VARIABLE_INFO_TYPE = VariableInfo - - def __init__(self, father=None, children:List[IScope]=[], locals:List[IVariableInfo]=[]): - self._locals = locals - self._father = father - self.children = children - self.index = 0 if father is None else len(father) - - def __len__(self): - return len(self._locals) - - @property - def father(self)->IScope: - return self._father - - def find_variable(self, name: str, index=None)->IVariableInfo: - index = index if not index is None else len(self.local_variables) - for var in self.local_variables[:index]: - if var.name == name: - return var - if self.father: - return self.father.find_variable(name, self.index) - raise SemanticError(VARIABLE_NOT_DEFINED(name)) - - @property - def local_variables(self)->List[IVariableInfo]: - return self._locals - - def is_local_variable_defined(self, name: str)->bool: - return len([v for v in self.local_variables if v.name == name]) == 1 - - def add_variable(self, name: str, variable_type: IType)->IVariableInfo: - if self.is_local_variable_defined(name): - raise SemanticError(VARIABLE_ALREADY_DEFINED(name)) - variable = self.VARIABLE_INFO_TYPE(name, variable_type) - self._locals.append(variable) - return variable - - def create_child(self)->IScope: - child = type(self)(self) # In case of been inherited the Scope class the child created will have the same type of its father - self.children.append(child) - return child \ No newline at end of file diff --git a/src/cool_cmp/semantic/interface.py b/src/cool_cmp/semantic/interface.py deleted file mode 100644 index 778956361..000000000 --- a/src/cool_cmp/semantic/interface.py +++ /dev/null @@ -1,134 +0,0 @@ -from cool_cmp.shared.ast import BaseAST -from cool_cmp.shared import ICoolService -from cool2.cmp.pycompiler import AttributeProduction -from typing import List - -class ISemantic(ICoolService): - """ - Semantic interface to implement - """ - - def __call__(self, parse:List[AttributeProduction]) -> BaseAST: - raise NotImplementedError() - - -class IType: - """ - Type interface to implement - """ - - @property - def name(self)->str: - raise NotImplementedError() - - @property - def father(self)->'IType': - raise NotImplementedError() - - @property - def is_inheritable(self)->bool: - raise NotImplementedError() - - def add_attribute(self, name:str, attr_type:'IType')->'IAttributeInfo': - raise NotImplementedError() - - def is_attribute_defined(self, name:str)->bool: - raise NotImplementedError() - - def add_method(self, name:str, params:List['IVariableInfo'], return_type:'IType')->'IMethodInfo': - raise NotImplementedError() - - def is_method_defined(self, name:str, params_count:int)->bool: - raise NotImplementedError() - - def get_method(self, name:str, params_count:int)->'IMethodInfo': - raise NotImplementedError() - - def get_attribute(self, name:str)->'IAttributeInfo': - raise NotImplementedError() - - @property - def methods(self)->List['IMethodInfo']: - raise NotImplementedError() - - @property - def attributes(self)->List['IAttributeInfo']: - raise NotImplementedError() - - -class IVariableInfo: - """ - Variable info interface to implement - """ - - @property - def name(self)->str: - raise NotImplementedError() - - @property - def type(self)->IType: - raise NotImplementedError() - -class IMethodInfo: - """ - Method info interface to implement - """ - - @property - def name(self)->str: - raise NotImplementedError() - - @property - def parameters(self)->List[IVariableInfo]: - raise NotImplementedError() - - @property - def return_type(self)->IType: - raise NotImplementedError() - -class IAttributeInfo(IVariableInfo): - """ - Attribute info interface to implement - """ - pass - -class IContext: - """ - Context interface to implement - """ - - def add_type(self, name:str)->IType: - raise NotImplementedError() - - def get_type(self, name:str, father:IType)->IType: - raise NotImplementedError() - - @property - def types(self)->List[IType]: - raise NotImplementedError() - - -class IScope: - """ - Scope interface to implement - """ - - @property - def father(self)->'IScope': - raise NotImplementedError() - - def find_variable(self, name: str)->IVariableInfo: - raise NotImplementedError() - - @property - def local_variables(self)->List[IVariableInfo]: - raise NotImplementedError() - - def is_local_variable_defined(self, name: str)->bool: - raise NotImplementedError() - - def add_variable(self, name: str, variable_type: IType)->IVariableInfo: - raise NotImplementedError() - - def create_child(self)->'IScope': - raise NotImplementedError() \ No newline at end of file diff --git a/src/cool_cmp/semantic/tests/__init__.py b/src/cool_cmp/semantic/tests/__init__.py deleted file mode 100644 index e69de29bb..000000000 diff --git a/src/cool_cmp/semantic/tests/collector_visitor_test.py b/src/cool_cmp/semantic/tests/collector_visitor_test.py deleted file mode 100644 index e69de29bb..000000000 diff --git a/src/cool_cmp/semantic/tests/context_test.py b/src/cool_cmp/semantic/tests/context_test.py deleted file mode 100644 index e69de29bb..000000000 diff --git a/src/cool_cmp/semantic/tests/scope_test.py b/src/cool_cmp/semantic/tests/scope_test.py deleted file mode 100644 index e69de29bb..000000000 diff --git a/src/cool_cmp/semantic/tests/type_test.py b/src/cool_cmp/semantic/tests/type_test.py deleted file mode 100644 index 5380b1d61..000000000 --- a/src/cool_cmp/semantic/tests/type_test.py +++ /dev/null @@ -1,44 +0,0 @@ -from cool_cmp.semantic.implementations import CoolType, SemanticError, VariableInfo -import pytest - -# To execute the test $ pytest src/cool_cmp/semantic/tests/ - -def test_type_attribute(): - """ - Attribute Redefinition in same type - """ - obj = CoolType("Object", None) - attr = obj.add_attribute("a", obj) - with pytest.raises(SemanticError): - obj.add_attribute("a", obj) - - -def test_type_attribute_2(): - """ - Attribute Redefinition in child type - """ - obj = CoolType("Object", None) - attr = obj.add_attribute("a", obj) - obj2 = CoolType("Object2", obj) - with pytest.raises(SemanticError): - obj2.add_attribute("a", obj2) - - -def test_type_method(): - """ - Method Definition with same signature - """ - obj = CoolType("Object", None) - attr = obj.add_method("a", [VariableInfo("a", obj)], obj) - with pytest.raises(SemanticError): - obj.add_method("a", [VariableInfo("b", obj)], obj) - - -def test_type_method_2(): - """ - Method Definition with same params names - """ - obj = CoolType("Object", None) - with pytest.raises(SemanticError): - attr = obj.add_method("a", [VariableInfo("a", obj), VariableInfo("a", obj)], obj) - diff --git a/src/cool_cmp/semantic/types.py b/src/cool_cmp/semantic/types.py deleted file mode 100644 index 65acd7f7e..000000000 --- a/src/cool_cmp/semantic/types.py +++ /dev/null @@ -1,45 +0,0 @@ -from cool_cmp.semantic.interface import IContext, IVariableInfo, IAttributeInfo, IMethodInfo, IType, IScope -from cool_cmp.semantic.implementations import CoolType -from typing import List - -class ObjectType(CoolType): - """ - Cool Object Type - """ - - def __init__(self): - super().__init__("Object", None) - -class IntType(CoolType): - """ - Cool Int Type - """ - - def __init__(self, father:ObjectType = ObjectType()): - super().__init__("Int", father, False) - -class BoolType(CoolType): - """ - Cool Bool Type - """ - - def __init__(self, father:ObjectType = ObjectType()): - super().__init__("Bool", father, False) - -class StringType(CoolType): - """ - Cool String Type - """ - - def __init__(self, father:ObjectType = ObjectType()): - super().__init__("String", father, False) - -class IOType(CoolType): - """ - Cool IO Type - """ - - def __init__(self, father:ObjectType = ObjectType()): - super().__init__("IO", father) - - \ No newline at end of file diff --git a/src/cool_cmp/semantic/visitors/__init__.py b/src/cool_cmp/semantic/visitors/__init__.py deleted file mode 100644 index 7965bc611..000000000 --- a/src/cool_cmp/semantic/visitors/__init__.py +++ /dev/null @@ -1,3 +0,0 @@ -""" -Semantic visitors -""" \ No newline at end of file diff --git a/src/cool_cmp/semantic/visitors/type_collector.py b/src/cool_cmp/semantic/visitors/type_collector.py deleted file mode 100644 index 633403320..000000000 --- a/src/cool_cmp/semantic/visitors/type_collector.py +++ /dev/null @@ -1,121 +0,0 @@ -import cool_cmp.shared.visitor as visitor -from cool_cmp.shared.ast import BaseAST -from cool_cmp.semantic.interface import IContext, ISemantic -from cool_cmp.shared.errors import ErrorTracker, IErrorTraceable, CoolError -from cool_cmp.shared.ast.cool import * -from cool_cmp.semantic.implementations import Context, CoolType -from cool_cmp.semantic.types import * -from cool_cmp.semantic.errors import SemanticError, TYPE_ALREADY_DEFINED, TYPE_NOT_DEFINED, \ - TYPE_CYCLIC_DEPENDENCY - -class TypeCollectorVisitor(IErrorTraceable): - """ - Collects the types by saving them to an IContext - """ - - def __init__(self): - self.errors = ErrorTracker() - self.context = Context() - - def add_semantic_error(self, error:SemanticError, line:int, pos:int): - error.set_position(line, pos) - self.add_error(error) - - @visitor.on('node') - def visit(self, node): - pass - - @visitor.when(ProgramNode) - def visit(self, node:ProgramNode): - - if not self.context: - obj = ObjectType() - int_ = IntType(obj) - bool_ = BoolType(obj) - string = StringType(obj) - io = IOType(obj) - - self.context = Context([ - obj, - int_, - bool_, - string, - io, - ]) - - default_names = [x.name for x in self.context.types] - type_names = [x.name for x in default_names] - - for class_decl in node.declarations: # Collecting defined classes - if class_decl.id in type_names: - error = SemanticError(TYPE_ALREADY_DEFINED(class_decl.id)) - self.add_semantic_error(error, class_decl.row, class_decl.column) - else: - type_names.append(class_decl.id) - - for class_decl in node.declarations: # Verify that parent classes exist - if class_decl.parent not in type_names: - error = SemanticError(TYPE_NOT_DEFINED(class_decl.parent)) - self.add_semantic_error(error, class_decl.row, class_decl.column) # TODO row and column should point to 'parent' token position - - - # Search for cyclic dependencies - import cool_cmp.semantic.visitors.utils as ut - graph = ut.build_graph_list([(x.id, x.parent) for x in node.declarations]) - cycles, sort = ut.any_cycles(graph) - - for cycle in cycles: # Adding cyclic dependency errors - error = SemanticError(TYPE_CYCLIC_DEPENDENCY(cycle)) - self.add_error(error) - - for class_name in sort: # Defining types in topological order - if class_name in default_names: # Class already in context - continue - - class_decl = [x for x in node.declarations if x.id == class_name] - if len(class_decl) != 1: # Error already captured. Multiple definitions or no definition - continue - - class_decl = class_decl[0] - - # At this point shold work whitout the try except - try: - father = self.context.get_type(class_decl.parent) - except SemanticError as exc: - self.add_semantic_error(exc, class_decl.row, class_decl.column) - - try: - self.context.add_type(class_decl, father) - except SemanticError as exc: - self.add_semantic_error(exc, class_decl.row, class_decl.column) - - return self.context - - def add_error(self, error:CoolError): - self.errors.add_error(error) - - def get_errors(self)->List[CoolError]: - self.errors.get_errors() - -class CollectorService(ISemantic): - - - def __init__(self): - self._errors = ErrorTracker() - - @property - def name(self)->str: - return 'context' - - def __call__(self, ast:BaseAST) -> BaseAST: - collector = TypeCollectorVisitor() - context = collector.visit(ast.node) - for error in collector.get_errors(): - self.add_error(error) - return context - - def add_error(self, error:CoolError): - self._errors.add_error(error) - - def get_errors(self)->List[CoolError]: - return self._errors.get_errors() \ No newline at end of file diff --git a/src/cool_cmp/semantic/visitors/utils.py b/src/cool_cmp/semantic/visitors/utils.py deleted file mode 100644 index 0824a367a..000000000 --- a/src/cool_cmp/semantic/visitors/utils.py +++ /dev/null @@ -1,47 +0,0 @@ -from typing import List, Tuple, Dict - -Graph = Dict[str,List[str]] - -def build_graph_list(type_list:List[Tuple[str,str]])->Graph: - graph = { x:[] for x,_ in type_list } - - for x,y in type_list: - graph[x].append(y) - - add_keys = [y for x,y in type_list if y not in graph] - - for key in add_keys: - graph[key] = [] - - return graph - -def any_cycles(graph:Graph)->Tuple[Tuple[str,str],List[str]]: - d,f,time,stack,cycles = { x:0 for x in graph },{ x:0 for x in graph },[1], [], [] - for x in graph: - if not d[x]: - d[x] = time[0]; time[0]+=1 - topological_order(graph,x,d,f,time,stack,cycles) - f[x] = time[0]; time[0]+=1 - stack.append(x) - stack.reverse() - return cycles, stack - - -def topological_order(graph, node, d, f, time, stack, cycles): - - for adj in graph[node]: - if not d[adj]: - d[adj] = time[0]; time[0]+=1 - topological_order(graph,adj,d,f,time,stack,cycles) - f[adj] = time[0]; time[0]+=1 - stack.append(adj) - elif not f[adj]: - cycles.append((node,adj)) - -gr = build_graph_list([('a','b'), ('b','c')]) # a->b->c -gr = build_graph_list([('b','a'), ('c','b')]) # a<-b<-c -gr = build_graph_list([('b','a'), ('b','b')]) # a<-b<-c - -cycles, sort = any_cycles(gr) - -print(gr,cycles,sort) \ No newline at end of file diff --git a/src/cool_cmp/shared/__init__.py b/src/cool_cmp/shared/__init__.py deleted file mode 100644 index f33cf5e43..000000000 --- a/src/cool_cmp/shared/__init__.py +++ /dev/null @@ -1,82 +0,0 @@ -""" -Shared code between packages -""" - -from cool_cmp.shared.pipeline import IPipeable, Pipe, Pipeline -from cool_cmp.shared.errors import ErrorTracker, IErrorTraceable, CoolError -from typing import List, Dict - -class ICoolService(IErrorTraceable, IPipeable): - """ - Interface that should implement all parts of the cool pipeline - """ - - def __call__(self, arg:object, context:Dict[str,object]): - raise NotImplementedError() - - def add_extra_info(self, key:str, value:object): - """ - Add extra information that will be pass to all following CoolServices - """ - if not hasattr(self, "_extra"): - self._extra = {} - self._extra[key] = value - - def get_extra_info(self) -> Dict[str,object]: - """ - Get current extra information - """ - if not hasattr(self, "_extra"): - self._extra = {} - return self._extra - - -class SymbolTable(IErrorTraceable): - """ - Class that should return all Cool pipelines - It holds all the shared information between stages - Defines a last property that returns the last object setted as an atribute - """ - - def __init__(self): - self.last = None - self.context = {} - self.__errors = ErrorTracker() - - def __setattr__(self, name:str, value): - if name != "last": - self.last = value - super().__setattr__(name, value) - - def add_error(self, error:CoolError): - self.__errors.add_error(error) - - def get_errors(self)->List[CoolError]: - return self.__errors.get_errors() - - def __str__(self): - result = "SymbolTable:\n" - for attr in self.__dict__.keys(): - result += f" {attr}\n" - return result - - -class InterfacePipeline(Pipeline): - - def __init__(self, pipeline:'InterfacePipeline', *interfaces:List[ICoolService]): - - def make_pipe(result:SymbolTable, interface:ICoolService): - returned = interface(result.last, result.context.copy()) - for error in interface.get_errors(): - result.add_error(error) - result.__setattr__(interface.name, returned) - result.context.update(interface.get_extra_info()) - return result - - pipes = [] - for interface in interfaces: - pipes.append(Pipe(lambda x, y=interface: make_pipe(x,y))) - if pipeline: - super().__init__(pipeline, *pipes) - else: - super().__init__(*pipes) \ No newline at end of file diff --git a/src/cool_cmp/shared/ast/__init__.py b/src/cool_cmp/shared/ast/__init__.py deleted file mode 100644 index fafc67d0e..000000000 --- a/src/cool_cmp/shared/ast/__init__.py +++ /dev/null @@ -1,22 +0,0 @@ -""" -AST package -""" - -from cool_cmp.shared.errors import ErrorTracker - -class Node: - """ - Base node for AST - """ - def __init__(self,row:int=None,column:int=None): - self.row = row - self.column = column - -class BaseAST(ErrorTracker): - """ - Base AST class - """ - - def __init__(self, initial_node:Node): - self.node = initial_node - super().__init__() diff --git a/src/cool_cmp/shared/ast/cil.py b/src/cool_cmp/shared/ast/cil.py deleted file mode 100644 index 51809dafb..000000000 --- a/src/cool_cmp/shared/ast/cil.py +++ /dev/null @@ -1,4 +0,0 @@ -""" -CIL AST nodes -""" -from cool_cmp.shared.ast import Node diff --git a/src/cool_cmp/shared/ast/cool.py b/src/cool_cmp/shared/ast/cool.py deleted file mode 100644 index 6aa141833..000000000 --- a/src/cool_cmp/shared/ast/cool.py +++ /dev/null @@ -1,263 +0,0 @@ -""" -Cool AST nodes -""" -from cool_cmp.shared.ast import Node -from typing import List - - -class DeclarationNode(Node): - pass - -class ExpressionNode(Node): - pass - -class ClassDeclarationNode(DeclarationNode): - def __init__(self, idx:str, features:List[DeclarationNode], parent=None,row=None,column=None): - super().__init__(row,column) - self.id = idx - self.parent = parent if parent else 'Object' - self.features = features - - def __iter__(self): - yield self - for x in self.features: - yield from x - -class ProgramNode(Node): - def __init__(self, declarations:List[ClassDeclarationNode],row:int=None,column:int=None): - super().__init__(row,column) - self.declarations = declarations - - def __iter__(self): - for x in self.declarations: - yield from x - - -class ParamNode(DeclarationNode): - def __init__(self, idx:str, typex:str, row:int=None,column:int=None): - super().__init__(row,column) - self.id = idx - self.type = typex - - def __iter__(self): - yield self - -class FuncDeclarationNode(DeclarationNode): - def __init__(self, idx:int, params:List[ParamNode], return_type:str, body:ExpressionNode, row:int=None,column:int=None): - super().__init__(row,column) - self.id = idx - self.params = params - self.type = return_type - self.body = body - - def __iter__(self): - yield self - yield from self.params - yield from self.body - -class AttrDeclarationNode(DeclarationNode): - def __init__(self, idx:str, typex:str, expr:ExpressionNode=None,row:int=None,column:int=None): - super().__init__(row,column) - self.id = idx - self.type = typex - self.expr = expr - - def __iter__(self): - yield self - if self.expr: - yield from self.expr - -class SpecialNode(ExpressionNode): - def __init__(self, func,row=None,column=None): - super().__init__(row,column) - self.func = func - - def __iter__(self): - yield self - -class VarDeclarationNode(ExpressionNode): - def __init__(self, idx:str, typex:str, expr:ExpressionNode, row=None, column=None): - super().__init__(row, column) - self.id = idx - self.type = typex - self.expr = expr - - def __iter__(self): - yield self - if self.expr: - yield from self.expr - -class AssignNode(ExpressionNode): - def __init__(self, idx:str, expr:ExpressionNode, row=None,column=None): - super().__init__(row,column) - self.id = idx - self.expr = expr - - def __iter__(self): - yield self - if self.expr: - yield from self.expr - -class CallNode(ExpressionNode): - def __init__(self, obj:ExpressionNode, idx:str, args:List[ExpressionNode], at_type:str, row:int = None, column:int = None): - super().__init__(row,column) - self.obj = obj - self.id = idx - self.args = args - self.at = at_type - - def __iter__(self): - yield from self.obj - for x in self.args: - yield from x - yield self - -class BlockNode(ExpressionNode): - def __init__(self, expr_list:List[ExpressionNode],row=None,column=None): - super().__init__(row,column) - self.expr_list = expr_list - - def __iter__(self): - yield self - for x in self.expr_list: - yield from x - -class ConditionalNode(ExpressionNode): - def __init__(self, condition:ExpressionNode, then_expr:ExpressionNode, else_expr:ExpressionNode, row:int=None,column:int=None): - super().__init__(row,column) - self.condition = condition - self.then_expr = then_expr - self.else_expr = else_expr - - def __iter__(self): - yield self - for x in [self.condition, self.then_expr, self.else_expr]: - yield from x - -class CheckNode(ExpressionNode): - def __init__(self, idx:str, typex:str, expr:ExpressionNode, row:int=None, column:int=None): - super().__init__(row,column) - self.id = idx - self.type = typex - self.expr = expr - - def __iter__(self): - yield self - yield from self.expr - -class LetNode(ExpressionNode): - def __init__(self, dec_var_list:List[VarDeclarationNode], expr:ExpressionNode, row:int=None,column:int=None): - super().__init__(row,column) - self.params = dec_var_list - self.expr = expr - - def __iter__(self): - yield self - for x in self.params: - yield from x - - -class CaseNode(ExpressionNode): - def __init__(self, expr:ExpressionNode, check_list:List[CheckNode], row:int=None,column:int=None): - super().__init__(row,column) - self.expr = expr - self.params = check_list - - def __iter__(self): - yield self - yield from self.expr - for x in self.params: - yield from x - -class WhileNode(ExpressionNode): - def __init__(self, condition:ExpressionNode, expr:ExpressionNode, row:int=None, column:int=None): - super().__init__(row,column) - self.condition = condition - self.expr = expr - - def __iter__(self): - yield self - yield self.condition - yield self.expr - -class AtomicNode(ExpressionNode): - def __init__(self, lex:str, row:int=None,column:int=None): - super().__init__(row,column) - self.lex = lex - - def __iter__(self): - yield self - -class UnaryNode(ExpressionNode): - def __init__(self, member:ExpressionNode, row:int=None,column:int=None): - super().__init__(row,column) - self.member = member - - def __iter__(self): - yield self - yield self.member - -class BinaryNode(ExpressionNode): - def __init__(self, left:ExpressionNode, right:ExpressionNode, row:int=None,column:int=None): - super().__init__(row,column) - self.left = left - self.right = right - - def __iter__(self): - yield self - yield self.left - yield self.right - -class StringNode(AtomicNode): - pass - -class BoolNode(AtomicNode): - pass - -class ConstantNumNode(AtomicNode): - pass - -class VoidNode(AtomicNode): - pass - -class VariableNode(AtomicNode): - pass - -class InstantiateNode(AtomicNode): - pass - -class NotNode(UnaryNode): - pass - -class RoofNode(UnaryNode): - pass - -class IsVoidNode(UnaryNode): - pass - -class PlusNode(BinaryNode): - pass - -class MinusNode(BinaryNode): - pass - -class StarNode(BinaryNode): - pass - -class DivNode(BinaryNode): - pass - -class EqualNode(BinaryNode): - pass - -class GreaterNode(BinaryNode): - pass - -class GreaterEqualNode(BinaryNode): - pass - -class LesserNode(BinaryNode): - pass - -class LesserEqualNode(BinaryNode): - pass \ No newline at end of file diff --git a/src/cool_cmp/shared/ast/mips.py b/src/cool_cmp/shared/ast/mips.py deleted file mode 100644 index 00e6ad3dc..000000000 --- a/src/cool_cmp/shared/ast/mips.py +++ /dev/null @@ -1,4 +0,0 @@ -""" -Mips AST Nodes -""" -from cool_cmp.shared.ast import Node diff --git a/src/cool_cmp/shared/errors.py b/src/cool_cmp/shared/errors.py deleted file mode 100644 index 1eb6b3c9a..000000000 --- a/src/cool_cmp/shared/errors.py +++ /dev/null @@ -1,46 +0,0 @@ -from typing import List -class CoolError(Exception): - """ - Base Cool Error - """ - - ERROR_TYPE = "CoolError" - - FORMAT = "{}: {}" - - def __init__(self, msg:str): - self.error = msg - - def print_error(self): - print(str(self)) - - def __str__(self): - return self.FORMAT.format(self.ERROR_TYPE, self.error) - - def __repr__(self): - return str(self) - -class IErrorTraceable: - """ - Interface for error tracking system - """ - - def add_error(self, error:CoolError): - raise NotImplementedError() - - def get_errors(self)->List[CoolError]: - raise NotImplementedError() - -class ErrorTracker(IErrorTraceable): - """ - Basic Error tracking system - """ - - def __init__(self): - self.__errors = [] - - def add_error(self, error:CoolError): - self.__errors.append(error) - - def get_errors(self)->List[CoolError]: - return self.__errors \ No newline at end of file diff --git a/src/cool_cmp/shared/pipeline/__init__.py b/src/cool_cmp/shared/pipeline/__init__.py deleted file mode 100644 index af648efe7..000000000 --- a/src/cool_cmp/shared/pipeline/__init__.py +++ /dev/null @@ -1,15 +0,0 @@ -""" -Cool pipes package -""" - -from typing import List -from cool_cmp.shared.pipeline.pipes import * - -class IPipeable: - - @property - def name(self)->str: - raise NotImplementedError() - - def __call__(self, *args, **kwargs): - raise NotImplementedError() diff --git a/src/cool_cmp/shared/pipeline/pipes.py b/src/cool_cmp/shared/pipeline/pipes.py deleted file mode 100644 index bcc63b7c2..000000000 --- a/src/cool_cmp/shared/pipeline/pipes.py +++ /dev/null @@ -1,39 +0,0 @@ -from typing import Callable - -class Pipe: - def __init__(self, pipe_func:Callable[[object],object], pipe_check:Callable[[object],bool]=None): - """ - pipe_func: callable that recieve an argument and return a value - pipe_check: callable that recieve an argument and return if the pipe can be executed - """ - self.func = pipe_func - if pipe_check: - self.check = pipe_check - else: - self.check = lambda x: True - - def __call__(self, *args, **kwargs): - return self.func(*args, **kwargs) - - def can_execute(self, dictionary): - return self.check(dictionary) - -class Pipeline(Pipe): - - def __init__(self, *pipes): - super().__init__(self.__call__) - self.pipes = [pipe for pipe in pipes] - - def __call__(self, *args, **kwargs): - start_pipe = self.pipes[0] - - result = start_pipe(*args, **kwargs) - - for pipe in self.pipes[1:]: - if pipe.can_execute(result): - result = pipe(result) - else: - break - - return result - diff --git a/src/cool_cmp/shared/token.py b/src/cool_cmp/shared/token.py deleted file mode 100644 index 5f9170675..000000000 --- a/src/cool_cmp/shared/token.py +++ /dev/null @@ -1,30 +0,0 @@ -from typing import Tuple - -class ICoolToken: - """ - Cool Token Interface - """ - - def set_lex(self, lex:str): - raise NotImplementedError() - - def get_lex(self)->str: - raise NotImplementedError() - - def set_type(self, typex:str): - raise NotImplementedError() - - def get_type(self)->str: - raise NotImplementedError() - - def set_position(self, line:int, column:int): - raise NotImplementedError - - def get_position(self)->Tuple[int,int]: - raise NotImplementedError - - def __str__(self): - return f"{self.get_lex()}:{self.get_type()} Line:{self.get_position()[0]} Column:{self.get_position()[1]}" - - def __repr__(self): - return str(self) \ No newline at end of file diff --git a/src/cool_cmp/shared/visitor.py b/src/cool_cmp/shared/visitor.py deleted file mode 100644 index 1853dcb97..000000000 --- a/src/cool_cmp/shared/visitor.py +++ /dev/null @@ -1,80 +0,0 @@ -# The MIT License (MIT) -# -# Copyright (c) 2013 Curtis Schlak -# -# Permission is hereby granted, free of charge, to any person obtaining a copy -# of this software and associated documentation files (the "Software"), to deal -# in the Software without restriction, including without limitation the rights -# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -# copies of the Software, and to permit persons to whom the Software is -# furnished to do so, subject to the following conditions: -# -# The above copyright notice and this permission notice shall be included in -# all copies or substantial portions of the Software. -# -# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN -# THE SOFTWARE. - -import inspect - -__all__ = ['on', 'when'] - -def on(param_name): - def f(fn): - dispatcher = Dispatcher(param_name, fn) - return dispatcher - return f - - -def when(param_type): - def f(fn): - frame = inspect.currentframe().f_back - func_name = fn.func_name if 'func_name' in dir(fn) else fn.__name__ - dispatcher = frame.f_locals[func_name] - if not isinstance(dispatcher, Dispatcher): - dispatcher = dispatcher.dispatcher - dispatcher.add_target(param_type, fn) - def ff(*args, **kw): - return dispatcher(*args, **kw) - ff.dispatcher = dispatcher - return ff - return f - - -class Dispatcher(object): - def __init__(self, param_name, fn): - frame = inspect.currentframe().f_back.f_back - top_level = frame.f_locals == frame.f_globals - self.param_index = self.__argspec(fn).args.index(param_name) - self.param_name = param_name - self.targets = {} - - def __call__(self, *args, **kw): - typ = args[self.param_index].__class__ - d = self.targets.get(typ) - if d is not None: - return d(*args, **kw) - else: - issub = issubclass - t = self.targets - ks = t.keys() - ans = [t[k](*args, **kw) for k in ks if issub(typ, k)] - if len(ans) == 1: - return ans.pop() - return ans - - def add_target(self, typ, target): - self.targets[typ] = target - - @staticmethod - def __argspec(fn): - # Support for Python 3 type hints requires inspect.getfullargspec - if hasattr(inspect, 'getfullargspec'): - return inspect.getfullargspec(fn) - else: - return inspect.getargspec(fn) diff --git a/src/cool2/test/__init__.py b/src/cool_cmp/test/__init__.py similarity index 100% rename from src/cool2/test/__init__.py rename to src/cool_cmp/test/__init__.py diff --git a/src/cool2/test/test_class.py b/src/cool_cmp/test/test_class.py similarity index 100% rename from src/cool2/test/test_class.py rename to src/cool_cmp/test/test_class.py diff --git a/src/cool2/test/test_data/test_SELF_TYPE.meta_test b/src/cool_cmp/test/test_data/test_SELF_TYPE.meta_test similarity index 100% rename from src/cool2/test/test_data/test_SELF_TYPE.meta_test rename to src/cool_cmp/test/test_data/test_SELF_TYPE.meta_test diff --git a/src/cool2/test/test_data/test_at.meta_test b/src/cool_cmp/test/test_data/test_at.meta_test similarity index 100% rename from src/cool2/test/test_data/test_at.meta_test rename to src/cool_cmp/test/test_data/test_at.meta_test diff --git a/src/cool2/test/test_data/test_attr_dep_1.meta_test b/src/cool_cmp/test/test_data/test_attr_dep_1.meta_test similarity index 100% rename from src/cool2/test/test_data/test_attr_dep_1.meta_test rename to src/cool_cmp/test/test_data/test_attr_dep_1.meta_test diff --git a/src/cool2/test/test_data/test_attr_dep_2.meta_test b/src/cool_cmp/test/test_data/test_attr_dep_2.meta_test similarity index 100% rename from src/cool2/test/test_data/test_attr_dep_2.meta_test rename to src/cool_cmp/test/test_data/test_attr_dep_2.meta_test diff --git a/src/cool2/test/test_data/test_auto_1.meta_test b/src/cool_cmp/test/test_data/test_auto_1.meta_test similarity index 100% rename from src/cool2/test/test_data/test_auto_1.meta_test rename to src/cool_cmp/test/test_data/test_auto_1.meta_test diff --git a/src/cool2/test/test_data/test_auto_2.meta_test b/src/cool_cmp/test/test_data/test_auto_2.meta_test similarity index 100% rename from src/cool2/test/test_data/test_auto_2.meta_test rename to src/cool_cmp/test/test_data/test_auto_2.meta_test diff --git a/src/cool2/test/test_data/test_auto_3.meta_test b/src/cool_cmp/test/test_data/test_auto_3.meta_test similarity index 100% rename from src/cool2/test/test_data/test_auto_3.meta_test rename to src/cool_cmp/test/test_data/test_auto_3.meta_test diff --git a/src/cool2/test/test_data/test_auto_4.meta_test b/src/cool_cmp/test/test_data/test_auto_4.meta_test similarity index 100% rename from src/cool2/test/test_data/test_auto_4.meta_test rename to src/cool_cmp/test/test_data/test_auto_4.meta_test diff --git a/src/cool2/test/test_data/test_auto_5.meta_test b/src/cool_cmp/test/test_data/test_auto_5.meta_test similarity index 100% rename from src/cool2/test/test_data/test_auto_5.meta_test rename to src/cool_cmp/test/test_data/test_auto_5.meta_test diff --git a/src/cool2/test/test_data/test_auto_conditional.meta_test b/src/cool_cmp/test/test_data/test_auto_conditional.meta_test similarity index 100% rename from src/cool2/test/test_data/test_auto_conditional.meta_test rename to src/cool_cmp/test/test_data/test_auto_conditional.meta_test diff --git a/src/cool2/test/test_data/test_auto_defaults.meta_test b/src/cool_cmp/test/test_data/test_auto_defaults.meta_test similarity index 100% rename from src/cool2/test/test_data/test_auto_defaults.meta_test rename to src/cool_cmp/test/test_data/test_auto_defaults.meta_test diff --git a/src/cool2/test/test_data/test_auto_undecidable.meta_test b/src/cool_cmp/test/test_data/test_auto_undecidable.meta_test similarity index 100% rename from src/cool2/test/test_data/test_auto_undecidable.meta_test rename to src/cool_cmp/test/test_data/test_auto_undecidable.meta_test diff --git a/src/cool2/test/test_data/test_case.meta_test b/src/cool_cmp/test/test_data/test_case.meta_test similarity index 100% rename from src/cool2/test/test_data/test_case.meta_test rename to src/cool_cmp/test/test_data/test_case.meta_test diff --git a/src/cool2/test/test_data/test_case_no_type.meta_test b/src/cool_cmp/test/test_data/test_case_no_type.meta_test similarity index 100% rename from src/cool2/test/test_data/test_case_no_type.meta_test rename to src/cool_cmp/test/test_data/test_case_no_type.meta_test diff --git a/src/cool2/test/test_data/test_case_repeated_type.meta_test b/src/cool_cmp/test/test_data/test_case_repeated_type.meta_test similarity index 100% rename from src/cool2/test/test_data/test_case_repeated_type.meta_test rename to src/cool_cmp/test/test_data/test_case_repeated_type.meta_test diff --git a/src/cool2/test/test_data/test_case_void_eval.meta_test b/src/cool_cmp/test/test_data/test_case_void_eval.meta_test similarity index 100% rename from src/cool2/test/test_data/test_case_void_eval.meta_test rename to src/cool_cmp/test/test_data/test_case_void_eval.meta_test diff --git a/src/cool2/test/test_data/test_comments.meta_test b/src/cool_cmp/test/test_data/test_comments.meta_test similarity index 100% rename from src/cool2/test/test_data/test_comments.meta_test rename to src/cool_cmp/test/test_data/test_comments.meta_test diff --git a/src/cool2/test/test_data/test_conditional.meta_test b/src/cool_cmp/test/test_data/test_conditional.meta_test similarity index 100% rename from src/cool2/test/test_data/test_conditional.meta_test rename to src/cool_cmp/test/test_data/test_conditional.meta_test diff --git a/src/cool2/test/test_data/test_default.meta_test b/src/cool_cmp/test/test_data/test_default.meta_test similarity index 100% rename from src/cool2/test/test_data/test_default.meta_test rename to src/cool_cmp/test/test_data/test_default.meta_test diff --git a/src/cool2/test/test_data/test_entry_point_1.meta_test b/src/cool_cmp/test/test_data/test_entry_point_1.meta_test similarity index 100% rename from src/cool2/test/test_data/test_entry_point_1.meta_test rename to src/cool_cmp/test/test_data/test_entry_point_1.meta_test diff --git a/src/cool2/test/test_data/test_entry_point_2.meta_test b/src/cool_cmp/test/test_data/test_entry_point_2.meta_test similarity index 100% rename from src/cool2/test/test_data/test_entry_point_2.meta_test rename to src/cool_cmp/test/test_data/test_entry_point_2.meta_test diff --git a/src/cool2/test/test_data/test_entry_point_3.meta_test b/src/cool_cmp/test/test_data/test_entry_point_3.meta_test similarity index 100% rename from src/cool2/test/test_data/test_entry_point_3.meta_test rename to src/cool_cmp/test/test_data/test_entry_point_3.meta_test diff --git a/src/cool2/test/test_data/test_equality.meta_test b/src/cool_cmp/test/test_data/test_equality.meta_test similarity index 100% rename from src/cool2/test/test_data/test_equality.meta_test rename to src/cool_cmp/test/test_data/test_equality.meta_test diff --git a/src/cool2/test/test_data/test_escape_line.meta_test b/src/cool_cmp/test/test_data/test_escape_line.meta_test similarity index 100% rename from src/cool2/test/test_data/test_escape_line.meta_test rename to src/cool_cmp/test/test_data/test_escape_line.meta_test diff --git a/src/cool2/test/test_data/test_in_out.meta_test b/src/cool_cmp/test/test_data/test_in_out.meta_test similarity index 100% rename from src/cool2/test/test_data/test_in_out.meta_test rename to src/cool_cmp/test/test_data/test_in_out.meta_test diff --git a/src/cool2/test/test_data/test_instances.meta_test b/src/cool_cmp/test/test_data/test_instances.meta_test similarity index 100% rename from src/cool2/test/test_data/test_instances.meta_test rename to src/cool_cmp/test/test_data/test_instances.meta_test diff --git a/src/cool2/test/test_data/test_isvoid.meta_test b/src/cool_cmp/test/test_data/test_isvoid.meta_test similarity index 100% rename from src/cool2/test/test_data/test_isvoid.meta_test rename to src/cool_cmp/test/test_data/test_isvoid.meta_test diff --git a/src/cool2/test/test_data/test_let.meta_test b/src/cool_cmp/test/test_data/test_let.meta_test similarity index 100% rename from src/cool2/test/test_data/test_let.meta_test rename to src/cool_cmp/test/test_data/test_let.meta_test diff --git a/src/cool2/test/test_data/test_other.meta_test b/src/cool_cmp/test/test_data/test_other.meta_test similarity index 100% rename from src/cool2/test/test_data/test_other.meta_test rename to src/cool_cmp/test/test_data/test_other.meta_test diff --git a/src/cool2/test/test_data/test_recursive.meta_test b/src/cool_cmp/test/test_data/test_recursive.meta_test similarity index 100% rename from src/cool2/test/test_data/test_recursive.meta_test rename to src/cool_cmp/test/test_data/test_recursive.meta_test diff --git a/src/cool2/test/test_data/test_substr_1.meta_test b/src/cool_cmp/test/test_data/test_substr_1.meta_test similarity index 100% rename from src/cool2/test/test_data/test_substr_1.meta_test rename to src/cool_cmp/test/test_data/test_substr_1.meta_test diff --git a/src/cool2/test/test_data/test_substr_2.meta_test b/src/cool_cmp/test/test_data/test_substr_2.meta_test similarity index 100% rename from src/cool2/test/test_data/test_substr_2.meta_test rename to src/cool_cmp/test/test_data/test_substr_2.meta_test diff --git a/src/cool2/test/test_data/test_substr_3.meta_test b/src/cool_cmp/test/test_data/test_substr_3.meta_test similarity index 100% rename from src/cool2/test/test_data/test_substr_3.meta_test rename to src/cool_cmp/test/test_data/test_substr_3.meta_test diff --git a/src/cool2/test/test_data/test_void_dispatch.meta_test b/src/cool_cmp/test/test_data/test_void_dispatch.meta_test similarity index 100% rename from src/cool2/test/test_data/test_void_dispatch.meta_test rename to src/cool_cmp/test/test_data/test_void_dispatch.meta_test diff --git a/src/cool2/test/test_data/test_while.meta_test b/src/cool_cmp/test/test_data/test_while.meta_test similarity index 100% rename from src/cool2/test/test_data/test_while.meta_test rename to src/cool_cmp/test/test_data/test_while.meta_test diff --git a/src/cool2/test/test_data/test_zero_division.meta_test b/src/cool_cmp/test/test_data/test_zero_division.meta_test similarity index 100% rename from src/cool2/test/test_data/test_zero_division.meta_test rename to src/cool_cmp/test/test_data/test_zero_division.meta_test diff --git a/src/cool2/test/test_reconstruction/test_SELF_TYPE.cl b/src/cool_cmp/test/test_reconstruction/test_SELF_TYPE.cl similarity index 100% rename from src/cool2/test/test_reconstruction/test_SELF_TYPE.cl rename to src/cool_cmp/test/test_reconstruction/test_SELF_TYPE.cl diff --git a/src/cool2/test/test_reconstruction/test_at.cl b/src/cool_cmp/test/test_reconstruction/test_at.cl similarity index 100% rename from src/cool2/test/test_reconstruction/test_at.cl rename to src/cool_cmp/test/test_reconstruction/test_at.cl diff --git a/src/cool2/test/test_reconstruction/test_attr_dep_1.cl b/src/cool_cmp/test/test_reconstruction/test_attr_dep_1.cl similarity index 100% rename from src/cool2/test/test_reconstruction/test_attr_dep_1.cl rename to src/cool_cmp/test/test_reconstruction/test_attr_dep_1.cl diff --git a/src/cool2/test/test_reconstruction/test_attr_dep_2.cl b/src/cool_cmp/test/test_reconstruction/test_attr_dep_2.cl similarity index 100% rename from src/cool2/test/test_reconstruction/test_attr_dep_2.cl rename to src/cool_cmp/test/test_reconstruction/test_attr_dep_2.cl diff --git a/src/cool2/test/test_reconstruction/test_auto_1.cl b/src/cool_cmp/test/test_reconstruction/test_auto_1.cl similarity index 100% rename from src/cool2/test/test_reconstruction/test_auto_1.cl rename to src/cool_cmp/test/test_reconstruction/test_auto_1.cl diff --git a/src/cool2/test/test_reconstruction/test_auto_2.cl b/src/cool_cmp/test/test_reconstruction/test_auto_2.cl similarity index 100% rename from src/cool2/test/test_reconstruction/test_auto_2.cl rename to src/cool_cmp/test/test_reconstruction/test_auto_2.cl diff --git a/src/cool2/test/test_reconstruction/test_auto_3.cl b/src/cool_cmp/test/test_reconstruction/test_auto_3.cl similarity index 100% rename from src/cool2/test/test_reconstruction/test_auto_3.cl rename to src/cool_cmp/test/test_reconstruction/test_auto_3.cl diff --git a/src/cool2/test/test_reconstruction/test_auto_4.cl b/src/cool_cmp/test/test_reconstruction/test_auto_4.cl similarity index 100% rename from src/cool2/test/test_reconstruction/test_auto_4.cl rename to src/cool_cmp/test/test_reconstruction/test_auto_4.cl diff --git a/src/cool2/test/test_reconstruction/test_auto_5.cl b/src/cool_cmp/test/test_reconstruction/test_auto_5.cl similarity index 100% rename from src/cool2/test/test_reconstruction/test_auto_5.cl rename to src/cool_cmp/test/test_reconstruction/test_auto_5.cl diff --git a/src/cool2/test/test_reconstruction/test_auto_conditional.cl b/src/cool_cmp/test/test_reconstruction/test_auto_conditional.cl similarity index 100% rename from src/cool2/test/test_reconstruction/test_auto_conditional.cl rename to src/cool_cmp/test/test_reconstruction/test_auto_conditional.cl diff --git a/src/cool2/test/test_reconstruction/test_auto_defaults.cl b/src/cool_cmp/test/test_reconstruction/test_auto_defaults.cl similarity index 100% rename from src/cool2/test/test_reconstruction/test_auto_defaults.cl rename to src/cool_cmp/test/test_reconstruction/test_auto_defaults.cl diff --git a/src/cool2/test/test_reconstruction/test_auto_undecidable.cl b/src/cool_cmp/test/test_reconstruction/test_auto_undecidable.cl similarity index 100% rename from src/cool2/test/test_reconstruction/test_auto_undecidable.cl rename to src/cool_cmp/test/test_reconstruction/test_auto_undecidable.cl diff --git a/src/cool2/test/test_reconstruction/test_case.cl b/src/cool_cmp/test/test_reconstruction/test_case.cl similarity index 100% rename from src/cool2/test/test_reconstruction/test_case.cl rename to src/cool_cmp/test/test_reconstruction/test_case.cl diff --git a/src/cool2/test/test_reconstruction/test_case_no_type.cl b/src/cool_cmp/test/test_reconstruction/test_case_no_type.cl similarity index 100% rename from src/cool2/test/test_reconstruction/test_case_no_type.cl rename to src/cool_cmp/test/test_reconstruction/test_case_no_type.cl diff --git a/src/cool2/test/test_reconstruction/test_case_repeated_type.cl b/src/cool_cmp/test/test_reconstruction/test_case_repeated_type.cl similarity index 100% rename from src/cool2/test/test_reconstruction/test_case_repeated_type.cl rename to src/cool_cmp/test/test_reconstruction/test_case_repeated_type.cl diff --git a/src/cool2/test/test_reconstruction/test_case_void_eval.cl b/src/cool_cmp/test/test_reconstruction/test_case_void_eval.cl similarity index 100% rename from src/cool2/test/test_reconstruction/test_case_void_eval.cl rename to src/cool_cmp/test/test_reconstruction/test_case_void_eval.cl diff --git a/src/cool2/test/test_reconstruction/test_comments.cl b/src/cool_cmp/test/test_reconstruction/test_comments.cl similarity index 100% rename from src/cool2/test/test_reconstruction/test_comments.cl rename to src/cool_cmp/test/test_reconstruction/test_comments.cl diff --git a/src/cool2/test/test_reconstruction/test_conditional.cl b/src/cool_cmp/test/test_reconstruction/test_conditional.cl similarity index 100% rename from src/cool2/test/test_reconstruction/test_conditional.cl rename to src/cool_cmp/test/test_reconstruction/test_conditional.cl diff --git a/src/cool2/test/test_reconstruction/test_default.cl b/src/cool_cmp/test/test_reconstruction/test_default.cl similarity index 100% rename from src/cool2/test/test_reconstruction/test_default.cl rename to src/cool_cmp/test/test_reconstruction/test_default.cl diff --git a/src/cool2/test/test_reconstruction/test_entry_point_1.cl b/src/cool_cmp/test/test_reconstruction/test_entry_point_1.cl similarity index 100% rename from src/cool2/test/test_reconstruction/test_entry_point_1.cl rename to src/cool_cmp/test/test_reconstruction/test_entry_point_1.cl diff --git a/src/cool2/test/test_reconstruction/test_entry_point_2.cl b/src/cool_cmp/test/test_reconstruction/test_entry_point_2.cl similarity index 100% rename from src/cool2/test/test_reconstruction/test_entry_point_2.cl rename to src/cool_cmp/test/test_reconstruction/test_entry_point_2.cl diff --git a/src/cool2/test/test_reconstruction/test_entry_point_3.cl b/src/cool_cmp/test/test_reconstruction/test_entry_point_3.cl similarity index 100% rename from src/cool2/test/test_reconstruction/test_entry_point_3.cl rename to src/cool_cmp/test/test_reconstruction/test_entry_point_3.cl diff --git a/src/cool2/test/test_reconstruction/test_equality.cl b/src/cool_cmp/test/test_reconstruction/test_equality.cl similarity index 100% rename from src/cool2/test/test_reconstruction/test_equality.cl rename to src/cool_cmp/test/test_reconstruction/test_equality.cl diff --git a/src/cool2/test/test_reconstruction/test_escape_line.cl b/src/cool_cmp/test/test_reconstruction/test_escape_line.cl similarity index 100% rename from src/cool2/test/test_reconstruction/test_escape_line.cl rename to src/cool_cmp/test/test_reconstruction/test_escape_line.cl diff --git a/src/cool2/test/test_reconstruction/test_instances.cl b/src/cool_cmp/test/test_reconstruction/test_instances.cl similarity index 100% rename from src/cool2/test/test_reconstruction/test_instances.cl rename to src/cool_cmp/test/test_reconstruction/test_instances.cl diff --git a/src/cool2/test/test_reconstruction/test_isvoid.cl b/src/cool_cmp/test/test_reconstruction/test_isvoid.cl similarity index 100% rename from src/cool2/test/test_reconstruction/test_isvoid.cl rename to src/cool_cmp/test/test_reconstruction/test_isvoid.cl diff --git a/src/cool2/test/test_reconstruction/test_let.cl b/src/cool_cmp/test/test_reconstruction/test_let.cl similarity index 100% rename from src/cool2/test/test_reconstruction/test_let.cl rename to src/cool_cmp/test/test_reconstruction/test_let.cl diff --git a/src/cool2/test/test_reconstruction/test_other.cl b/src/cool_cmp/test/test_reconstruction/test_other.cl similarity index 100% rename from src/cool2/test/test_reconstruction/test_other.cl rename to src/cool_cmp/test/test_reconstruction/test_other.cl diff --git a/src/cool2/test/test_reconstruction/test_recursive.cl b/src/cool_cmp/test/test_reconstruction/test_recursive.cl similarity index 100% rename from src/cool2/test/test_reconstruction/test_recursive.cl rename to src/cool_cmp/test/test_reconstruction/test_recursive.cl diff --git a/src/cool2/test/test_reconstruction/test_substr_1.cl b/src/cool_cmp/test/test_reconstruction/test_substr_1.cl similarity index 100% rename from src/cool2/test/test_reconstruction/test_substr_1.cl rename to src/cool_cmp/test/test_reconstruction/test_substr_1.cl diff --git a/src/cool2/test/test_reconstruction/test_substr_2.cl b/src/cool_cmp/test/test_reconstruction/test_substr_2.cl similarity index 100% rename from src/cool2/test/test_reconstruction/test_substr_2.cl rename to src/cool_cmp/test/test_reconstruction/test_substr_2.cl diff --git a/src/cool2/test/test_reconstruction/test_substr_3.cl b/src/cool_cmp/test/test_reconstruction/test_substr_3.cl similarity index 100% rename from src/cool2/test/test_reconstruction/test_substr_3.cl rename to src/cool_cmp/test/test_reconstruction/test_substr_3.cl diff --git a/src/cool2/test/test_reconstruction/test_void_dispatch.cl b/src/cool_cmp/test/test_reconstruction/test_void_dispatch.cl similarity index 100% rename from src/cool2/test/test_reconstruction/test_void_dispatch.cl rename to src/cool_cmp/test/test_reconstruction/test_void_dispatch.cl diff --git a/src/cool2/test/test_reconstruction/test_while.cl b/src/cool_cmp/test/test_reconstruction/test_while.cl similarity index 100% rename from src/cool2/test/test_reconstruction/test_while.cl rename to src/cool_cmp/test/test_reconstruction/test_while.cl diff --git a/src/cool2/test/test_reconstruction/test_zero_division.cl b/src/cool_cmp/test/test_reconstruction/test_zero_division.cl similarity index 100% rename from src/cool2/test/test_reconstruction/test_zero_division.cl rename to src/cool_cmp/test/test_reconstruction/test_zero_division.cl diff --git a/src/cool2/test/test_run/01_program.cl b/src/cool_cmp/test/test_run/01_program.cl similarity index 100% rename from src/cool2/test/test_run/01_program.cl rename to src/cool_cmp/test/test_run/01_program.cl diff --git a/src/cool2/test/test_run/02_program.cl b/src/cool_cmp/test/test_run/02_program.cl similarity index 100% rename from src/cool2/test/test_run/02_program.cl rename to src/cool_cmp/test/test_run/02_program.cl diff --git a/src/cool2/test/test_run/03_program.cl b/src/cool_cmp/test/test_run/03_program.cl similarity index 100% rename from src/cool2/test/test_run/03_program.cl rename to src/cool_cmp/test/test_run/03_program.cl diff --git a/src/cool2/test/test_run/04_program.cl b/src/cool_cmp/test/test_run/04_program.cl similarity index 100% rename from src/cool2/test/test_run/04_program.cl rename to src/cool_cmp/test/test_run/04_program.cl diff --git a/src/cool2/test/test_run/05_program.cl b/src/cool_cmp/test/test_run/05_program.cl similarity index 100% rename from src/cool2/test/test_run/05_program.cl rename to src/cool_cmp/test/test_run/05_program.cl diff --git a/src/cool2/test/tests.py b/src/cool_cmp/test/tests.py similarity index 100% rename from src/cool2/test/tests.py rename to src/cool_cmp/test/tests.py From b68021a06e46869aca4aa611c4004f69f59b0b3f Mon Sep 17 00:00:00 2001 From: Luiso Date: Thu, 16 Sep 2021 23:15:48 -0400 Subject: [PATCH 036/143] Added position by token feature to PositionError --- src/a.cl | 34 --- src/cool_cmp/cmp_tools/lang/language.py | 11 - src/cool_cmp/cool/error/errors.py | 23 +- src/cool_cmp/cool/lexer/ply_cool_lexer.py | 18 +- src/coolc.sh | 2 +- src/main.py | 272 ---------------------- 6 files changed, 27 insertions(+), 333 deletions(-) delete mode 100644 src/a.cl delete mode 100644 src/main.py diff --git a/src/a.cl b/src/a.cl deleted file mode 100644 index c87561ca0..000000000 --- a/src/a.cl +++ /dev/null @@ -1,34 +0,0 @@ -class Main inherits IO { - number: Int <- 5; - - main () : Object { - testing_fibonacci(number) - }; - - testing_fibonacci(n: Int) : IO {{ - out_string("Iterative Fibonacci : "); - out_int(iterative_fibonacci(5)); - out_string("\n"); - - out_string("Recursive Fibonacci : "); - out_int(recursive_fibonacci(5)); - out_string("\n"); - }}; - - recursive_fibonacci (n: AUTO_TYPE) : AUTO_TYPE { - if n <= 2 then 1 else recursive_fibonacci(n - 1) + recursive_fibonacci(n - 2) fi - }; - - iterative_fibonacci(n: AUTO_TYPE) : AUTO_TYPE { - let i: Int <- 2, n1: Int <- 1, n2: Int <- 1, temp: Int in { - while i < n loop - let temp: Int <- n2 in { - n2 <- n2 + n1; - n1 <- temp; - i <- i + 1; - } - pool; - n2; - } - }; -} \ No newline at end of file diff --git a/src/cool_cmp/cmp_tools/lang/language.py b/src/cool_cmp/cmp_tools/lang/language.py index a0d7286bc..3b5af81c3 100644 --- a/src/cool_cmp/cmp_tools/lang/language.py +++ b/src/cool_cmp/cmp_tools/lang/language.py @@ -1,5 +1,4 @@ from cmp.utils import Token -from cool.error.errors import LexerCoolError class Language(): parser = None @@ -55,21 +54,11 @@ def get_tokens(self, text, errors): Return the text tokens """ tokens = self.lexer(text) - # tokens = self._fix_tokens(tokens,errors) - # for tok in tokens: - # tok.lex = (tok.lex[0], tok.lex[1] + 1, tok.lex[2] - len(tok.lex[0]) + 1) - # if tok.token_type == "UNKNOWN": - # er = LexerCoolError('"{}"', tok.lex[0], token=tok) - # # errors.append(f'({tok.lex[1]}, {tok.lex[2]}) - LexicographicError: ERROR "{tok.lex[0]}"') - # errors.append(er) - # tokens = [x for x in tokens if x.token_type != "UNKNOWN"] - return tokens def find_conflict(self): return self.parser.find_conflict() def evaluate(self, text, errors:list,return_ast = False): - # tokens = [ x for x in self.lexer(text) if x.token_type != 'space'] tokens = self._fix_tokens(self.lexer(text),errors) return self.parser.evaluate(tokens,errors,return_ast) diff --git a/src/cool_cmp/cool/error/errors.py b/src/cool_cmp/cool/error/errors.py index b0cf13f12..54a140e98 100644 --- a/src/cool_cmp/cool/error/errors.py +++ b/src/cool_cmp/cool/error/errors.py @@ -100,17 +100,28 @@ def __init__(self, error_message:str, *args, **kwargs): lex: Representation of the error """ super().__init__(error_message, *args) - self.row = kwargs.get("row") - self.column = kwargs.get("column") + self.__row = kwargs.get("row") + self.__column = kwargs.get("column") self.lex = kwargs.get("lex") if "token" in kwargs: - self.row = kwargs["token"].lex[1] - self.column = kwargs["token"].lex[2] + self.__row = kwargs["token"].lex[1] + self.__column = kwargs["token"].lex[2] self.lex = kwargs["token"].lex[0] + self.token = kwargs["token"] + else: + self.token = None + + @property + def row(self): + return self.__row if self.token == None else self.token.line + + @property + def column(self): + return self.__column if self.token == None else self.token.column def set_position(self, row:int, column:int): - self.row = row - self.column = column + self.__row = row + self.__column = column def __str__(self): if self.row == None or self.column == None: diff --git a/src/cool_cmp/cool/lexer/ply_cool_lexer.py b/src/cool_cmp/cool/lexer/ply_cool_lexer.py index 985ffec26..a4d186a24 100644 --- a/src/cool_cmp/cool/lexer/ply_cool_lexer.py +++ b/src/cool_cmp/cool/lexer/ply_cool_lexer.py @@ -193,7 +193,7 @@ def __init__(self): # r'\(\*(.|\n)*' # t.lexer.lineno += t.value.count("\n") # msg = 'EOF in comment' - # self.add_error(LexerCoolError(msg, PlyCoolToken(t.value, t.type, t.lexer.lineno - 1, t.lexer.lexpos - 1))) + # self.add_error(LexerCoolError(msg, token = PlyCoolToken(t.value, t.type, t.lexer.lineno - 1, t.lexer.lexpos - 1))) def t_STRING(t): r'"([^\r\n"\\]|(\\\n)|(\\.)){0,1024}"' @@ -203,7 +203,7 @@ def t_STRING(t): if t.value[i] == '\x00': pos = t.lexer.lexpos - (len(t.value) - i) line = t.lexer.lineno - t.value[:i].count("\n") - self.add_error(LexerCoolError(null_ch, PlyCoolToken(t.value, t.type, line, pos))) + self.add_error(LexerCoolError(null_ch, token = PlyCoolToken(t.value, t.type, line, pos))) return t def t_STRINGUNFINISHED(t): @@ -214,9 +214,9 @@ def t_STRINGUNFINISHED(t): if t.value[i] == '\x00': pos = t.lexer.lexpos - (len(t.value) - i) line = t.lexer.lineno - t.value[:i].count("\n") - self.add_error(LexerCoolError(null_ch, PlyCoolToken(t.value, t.type, line, pos))) + self.add_error(LexerCoolError(null_ch, token = PlyCoolToken(t.value, t.type, line, pos))) msg = 'Unterminated string constant' - self.add_error(LexerCoolError(msg, PlyCoolToken(t.value, t.type, t.lexer.lineno - 1, t.lexer.lexpos - 1))) + self.add_error(LexerCoolError(msg, token = PlyCoolToken(t.value, t.type, t.lexer.lineno - 1, t.lexer.lexpos - 1))) def t_STRINGUNFINISHEDEOF(t): r'"([^\r\n"\\]|(\\\n)|(\\.)){0,1024}' @@ -226,9 +226,9 @@ def t_STRINGUNFINISHEDEOF(t): if t.value[i] == '\x00': pos = t.lexer.lexpos - (len(t.value) - i) line = t.lexer.lineno - t.value[:i].count("\n") - self.add_error(LexerCoolError(null_ch, PlyCoolToken(t.value, t.type, line, pos))) + self.add_error(LexerCoolError(null_ch, token = PlyCoolToken(t.value, t.type, line, pos))) msg = 'EOF in string constant' - self.add_error(LexerCoolError(msg, PlyCoolToken(t.value, t.type, t.lexer.lineno, t.lexer.lexpos))) + self.add_error(LexerCoolError(msg, token = PlyCoolToken(t.value, t.type, t.lexer.lineno, t.lexer.lexpos))) def t_NUMBER(t): r'\d+' @@ -236,7 +236,7 @@ def t_NUMBER(t): int(t.value) except ValueError: msg = "Integer value too large %d", t.value - self.add_error(LexerCoolError(msg, PlyCoolToken(t.value, t.type, t.lineno, t.lexpos))) # TODO Set Token column + self.add_error(LexerCoolError(msg, token = PlyCoolToken(t.value, t.type, t.lineno, t.lexpos))) # TODO Set Token column t.value = 'Invalid' return t @@ -292,7 +292,7 @@ def t_newline(t): def t_error(t): msg = f'ERROR "{t.value[0]}"' - self.add_error(LexerCoolError(msg, PlyCoolToken(t.value[0], t.type, t.lineno, t.lexpos))) # TODO Set Token column + self.add_error(LexerCoolError(msg, token = PlyCoolToken(t.value[0], t.type, t.lineno, t.lexpos))) # TODO Set Token column t.lexer.skip(1) self.lexer = lex.lex() @@ -350,7 +350,7 @@ def __call__(self, program_string:str): result.append(PlyCoolToken(tok.value, tok.type, tok.lineno, self.find_column(program_string, tok))) if count > 0: - self.add_error(LexerCoolError('EOF in comment', PlyCoolToken('', '', lines + 1, len(program_string)))) + self.add_error(LexerCoolError('EOF in comment', token = PlyCoolToken('', '', lines + 1, len(program_string)))) for error in self.error_tracker.get_errors(): error.token.set_position(error.token.lineno, self.find_column(program_string, error.token)) diff --git a/src/coolc.sh b/src/coolc.sh index 223ee34ea..a69f557e9 100755 --- a/src/coolc.sh +++ b/src/coolc.sh @@ -3,7 +3,7 @@ INPUT_FILE=$1 OUTPUT_FILE=${INPUT_FILE:0: -2}mips BASEDIR=$(dirname "$0") -PROGRAM=$BASEDIR/cool2/main.py +PROGRAM=$BASEDIR/cool_cmp/main.py # Si su compilador no lo hace ya, aquí puede imprimir la información de contacto echo "WLD CoolCompiler v0.0" diff --git a/src/main.py b/src/main.py deleted file mode 100644 index 03d1da4f8..000000000 --- a/src/main.py +++ /dev/null @@ -1,272 +0,0 @@ -from cool_cmp.lexer.interface import ILexer -from cool_cmp.lexer import LexerPipeline -from cool_cmp.parser.interface import IParser -from cool_cmp.parser import ParserPipeline -from cool_cmp.semantic.interface import ISemantic -from cool_cmp.semantic import SemanticPipeline -from cool_cmp.cil.interface import ICil -from cool_cmp.cil import CilPipeline -from cool_cmp.mips.interface import IMips -from cool_cmp.mips import MipsPipeline -from cool_cmp.shared.token import ICoolToken -from cool_cmp.shared.ast import BaseAST -from cool_cmp.shared.ast.cool import ClassDeclarationNode, ProgramNode -# from cool_cmp.lexer.lexer import PlyLexer -from cool_cmp.shared.errors import CoolError -from typing import List, Tuple - -# Mock implementations - -class MockLexer(ILexer): - """ - Mock implementation of lexer - """ - program = "class ` \n\tCoolClass\n{}" - class MockToken(ICoolToken): - def set_lex(self, lex:str): - self.lex = lex - - def set_type(self, typex:str): - self.typex = typex - - def set_position(self, line:int, column:int): - self.line = line - self.column = column - - def get_lex(self)->str: - return self.lex - - def get_type(self)->str: - return self.typex - - def get_position(self)->Tuple[int,int]: - return (self.line, self.column) - - @property - def mock_tokens(self)->List[ICoolToken]: - info = [ - ("class", "CLASS", 0, 0), - ("CoolClass", "ID", 1, 0), - ("{", "OCUR", 2, 0), - ("}", "CCUR", 2, 1), - ] - tokens = [] - for lex, typex, line, col in info: - token = MockLexer.MockToken() - token.set_lex(lex); token.set_type(typex); token.set_position(line, col) - tokens.append(token) - return tokens - - - @property - def name(self)->str: - return "lexer" - - errors = [] - - def add_error(self, error:CoolError): - self.errors.append(error) - - def get_errors(self)->List[CoolError]: - return self.errors - - def __call__(self, program_string:str) -> List[ICoolToken]: - print("Tokenizing program:\n", program_string) - return self.mock_tokens - -class MockParser(IParser): - """ - Mock implementation of parser - """ - - @property - def name(self)->str: - return "parser" - - def __call__(self, tokens:List[ICoolToken]) -> BaseAST: - print("Building AST from tokens",tokens) - class_token = MockLexer().mock_tokens - class_token = class_token[0] - return BaseAST(ProgramNode([ClassDeclarationNode(class_token.lex, [], None, class_token.line, class_token.column)])) - - errors = [] - - def add_error(self, error:CoolError): - self.errors.append(error) - - def get_errors(self)->List[CoolError]: - return self.errors - -class MockSemantic1(ISemantic): - """ - Mock Semantic interface 1 - """ - - @property - def name(self)->str: - return "semantic1" - - def __call__(self, ast:BaseAST) -> BaseAST: - print("Doing some crazy shit with ast, apply visitor, etc") - print("Collecting Types...") - print("Building Types...") - print("Type Checking...") - print("Setted mock1 property to true as a way of communicate between pipes") - ast.mock1 = True - return ast - - errors = [] - - def add_error(self, error:CoolError): - self.errors.append(error) - - def get_errors(self)->List[CoolError]: - return self.errors - -class MockSemantic2(ISemantic): - """ - Mock Semantic interface 2 - """ - - @property - def name(self)->str: - return "semantic2" - - def __call__(self, ast:BaseAST) -> BaseAST: - print("Doing another crazy shit with previously returned ast") - print("Verifying that ast has mock1 property", hasattr(ast, "mock1")) - print("Converting COOL AST into CIL AST...") - return ast - - errors = [] - - def add_error(self, error:CoolError): - self.errors.append(error) - - def get_errors(self)->List[CoolError]: - return self.errors - -class MockCil1(ICil): - """ - Mock Cil interface 1 - """ - - @property - def name(self)->str: - return "cil1" - - def __call__(self, ast:BaseAST) -> BaseAST: - print("Optimizing CIL AST...") - return ast - - errors = [] - - def add_error(self, error:CoolError): - self.errors.append(error) - - def get_errors(self)->List[CoolError]: - return self.errors - -class MockCil2(ICil): - """ - Mock Cil interface 2 - """ - - @property - def name(self)->str: - print("Converting CIL AST into MIPS AST...") - return "cil2" - - def __call__(self, ast:BaseAST) -> BaseAST: - return ast - - errors = [] - - def add_error(self, error:CoolError): - self.errors.append(error) - - def get_errors(self)->List[CoolError]: - return self.errors - -class MockMips1(IMips): - """ - Mock MIPS interface 1 - """ - - @property - def name(self)->str: - return "mips1" - - def __call__(self, ast:BaseAST) -> BaseAST: - print("Doing MIPS things...") - return ast - - errors = [] - - def add_error(self, error:CoolError): - self.errors.append(error) - - def get_errors(self)->List[CoolError]: - return self.errors - -class MockMips2(IMips): - """ - Mock MIPS interface 2 - """ - - @property - def name(self)->str: - return "mips2" - - def __call__(self, ast:BaseAST) -> BaseAST: - print("Generating MIPS code...") - return ast - - errors = [] - - def add_error(self, error:CoolError): - self.errors.append(error) - - def get_errors(self)->List[CoolError]: - return self.errors - -# Testing pipeline - -# pipe = LexerPipeline(MockLexer()) - -# # pipe = LexerPipeline(PlyLexer()) - -# pipe = ParserPipeline(pipe, MockParser()) - -# pipe = SemanticPipeline(pipe, MockSemantic1(), MockSemantic2()) - -# pipe = CilPipeline(pipe, MockCil1(), MockCil2()) - -# pipe = MipsPipeline(pipe, MockMips1(), MockMips2()) - -# result = pipe(MockLexer.program) - -# print(result.get_errors()) - -# from cool2.main import main - -# main('src/a.cl', None,False,False,False,False,False) - -if __name__ == "__main__": - import sys - with open(sys.argv[1]) as file: - program = file.read() - pipe = LexerPipeline() - pipe = ParserPipeline(pipe) - result = pipe(program) - for err in result.get_errors(): - if isinstance(err, str): - print(err) - else: - err.print_error() - if result.get_errors(): - exit(1) - with open(sys.argv[2], 'w') as file: - file.write("GENERATED MIPS") - - exit(0) From 6ae75d30e043161588335bb70f3c80a0941cf579 Mon Sep 17 00:00:00 2001 From: "Luis E. Dalmau" Date: Fri, 17 Sep 2021 00:33:56 -0400 Subject: [PATCH 037/143] minor changes --- .vscode/settings.json | 2 +- .../test/test_reconstruction/test_comments.cl | 18 ++-- .../test_reconstruction/test_conditional.cl | 93 ------------------- .../test_reconstruction/test_escape_line.cl | 3 +- .../test_reconstruction/test_recursive.cl | 30 ------ src/testing.cl | 63 ++++++------- 6 files changed, 38 insertions(+), 171 deletions(-) diff --git a/.vscode/settings.json b/.vscode/settings.json index 4a89b1bab..27c354135 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -1,7 +1,7 @@ { "python.testing.cwd": "./src", "python.testing.pytestArgs": [ - "../tests" + "../" ], "python.testing.unittestEnabled": false, "python.testing.nosetestsEnabled": false, diff --git a/src/cool2/test/test_reconstruction/test_comments.cl b/src/cool2/test/test_reconstruction/test_comments.cl index 77f4a4bc3..098ac7a18 100644 --- a/src/cool2/test/test_reconstruction/test_comments.cl +++ b/src/cool2/test/test_reconstruction/test_comments.cl @@ -1,9 +1,9 @@ -class Main -{ - main(): Object - { - 0 - }; -} - - +class Main +{ + main(): Object + { + 0 + }; +} + + diff --git a/src/cool2/test/test_reconstruction/test_conditional.cl b/src/cool2/test/test_reconstruction/test_conditional.cl index a82e2e576..e69de29bb 100644 --- a/src/cool2/test/test_reconstruction/test_conditional.cl +++ b/src/cool2/test/test_reconstruction/test_conditional.cl @@ -1,93 +0,0 @@ -class A -{ -} - - -class B inherits A -{ -} - - -class C inherits B -{ -} - - -class D inherits B -{ -} - - -class E inherits A -{ -} - - -class Main inherits IO -{ - main(): Int - { - { - d:Object <- - if - true - then - new E - else - new C - fi; - e:B <- - if - true - then - new C - else - new D - fi; - a:Int <- - if - true - then - 1 - else - 0 - fi; - b:Int <- - if - false - then - 0 - else - 1 - fi; - c:Int <- - if - if - (1 > 2) - then - (2 > 1) - else - (2 > 1) - fi - then - 1 - else - 0 - fi; - self - .out_string( - d - .type_name( - ) - ) - .out_string( - e - .type_name( - ) - ); - ((a + b) + c); - } - }; -} - - diff --git a/src/cool2/test/test_reconstruction/test_escape_line.cl b/src/cool2/test/test_reconstruction/test_escape_line.cl index 1e952ba9f..bf4af7314 100644 --- a/src/cool2/test/test_reconstruction/test_escape_line.cl +++ b/src/cool2/test/test_reconstruction/test_escape_line.cl @@ -3,7 +3,8 @@ class Main main(): String { { - "Hello People"; + "Hello \ + People"; } }; } diff --git a/src/cool2/test/test_reconstruction/test_recursive.cl b/src/cool2/test/test_reconstruction/test_recursive.cl index 531d729bc..e69de29bb 100644 --- a/src/cool2/test/test_reconstruction/test_recursive.cl +++ b/src/cool2/test/test_reconstruction/test_recursive.cl @@ -1,30 +0,0 @@ -class Main -{ - main(): Int - { - self - .fibo( - 7 - ) - }; - fibo(n:Int): Int - { - if - (n > 1) - then - ( self - .fibo( - (n - 1) - ) - + self - .fibo( - (n - 2) - ) -) - else - 1 - fi - }; -} - - diff --git a/src/testing.cl b/src/testing.cl index 75b4c5bbd..795221ffb 100644 --- a/src/testing.cl +++ b/src/testing.cl @@ -1,37 +1,26 @@ -(* An assignment has the form <- *) - -class Main { - main(): Object { - (new Alpha).print() - }; -}; - -class Test { - test1: Object; - - testing1(): Int { - 2 + 2 - }; - - test2: Int <- 1; - - test3: String <- "1"; - - testing2(a: Alpha, b: Int): Int { - 2 + 2 - }; - - testing3(): String { - "2 + 2" - }; - - testing4(): String { - Test1 <- "Hello World" -- Identifiers begin with a lower case letter - }; -}; - -class Alpha inherits IO { - print() : Object { - out_string("reached!!\n") - }; -}; \ No newline at end of file +--evaluates to true if expr is void and evaluates to false if expr is not void. + +class A { }; +class B inherits A { }; +class C inherits B { }; +class D inherits B { }; +class E inherits B { }; +class F inherits A { }; + +class Main inherits IO { + main(): IO { out_string("Hello World!")}; + + b: B <- if isvoid new F then + new C + else + if false then new D + else new E fi + fi; + + test: B <- isvoid ( if isvoid new F then + new C + else + if false then new D + else new E fi + fi ); +}; \ No newline at end of file From c6225159777fd79de8dc6232c67fef897b4857bf Mon Sep 17 00:00:00 2001 From: Luiso Date: Mon, 20 Sep 2021 19:59:50 -0400 Subject: [PATCH 038/143] Grammar fixed Only one test to go. Check dispatch9.cl --- src/cool_cmp/cmp_tools/parser/parser_lr.py | 5 +- src/cool_cmp/cool/error/errors.py | 2 +- src/cool_cmp/cool/grammar/cool_grammar.py | 22 +++--- src/cool_cmp/cool/lexer/ply_cool_lexer.py | 9 ++- src/cool_cmp/cool/parser/cool_parser.obj | Bin 151555 -> 154067 bytes src/cool_cmp/cool/pipes/pipes.py | 5 +- src/testing.cl | 87 +++++++++++++++------ tests/lexer/comment1.mips | 1 - tests/lexer/iis1.mips | 1 - tests/lexer/iis2.mips | 1 - tests/lexer/iis3.mips | 1 - tests/lexer/iis4.mips | 1 - tests/lexer/iis5.mips | 1 - tests/lexer/iis6.mips | 1 - tests/lexer/mixed1.mips | 1 - tests/lexer/mixed2.mips | 1 - tests/lexer/string1.mips | 1 - tests/lexer/string2.mips | 1 - tests/lexer/string3.mips | 1 - tests/lexer/string4.mips | 1 - 20 files changed, 87 insertions(+), 56 deletions(-) delete mode 100644 tests/lexer/comment1.mips delete mode 100644 tests/lexer/iis1.mips delete mode 100644 tests/lexer/iis2.mips delete mode 100644 tests/lexer/iis3.mips delete mode 100644 tests/lexer/iis4.mips delete mode 100644 tests/lexer/iis5.mips delete mode 100644 tests/lexer/iis6.mips delete mode 100644 tests/lexer/mixed1.mips delete mode 100644 tests/lexer/mixed2.mips delete mode 100644 tests/lexer/string1.mips delete mode 100644 tests/lexer/string2.mips delete mode 100644 tests/lexer/string3.mips delete mode 100644 tests/lexer/string4.mips diff --git a/src/cool_cmp/cmp_tools/parser/parser_lr.py b/src/cool_cmp/cmp_tools/parser/parser_lr.py index 5e96f62b6..06f954a71 100644 --- a/src/cool_cmp/cmp_tools/parser/parser_lr.py +++ b/src/cool_cmp/cmp_tools/parser/parser_lr.py @@ -100,9 +100,10 @@ def __call__(self, tokens, errors,finding_conflict = False): except KeyError: # errors.append(f'Invalid transition ({state},{lookahead}) doesnt exist expected {[ x[1] for x in self.action if x[0] == state ]}') posibles = [x for x in self.action if x[0] == state ] - errors.append(SyntacticCoolError(SYNTACTIC_ERROR, lookahead.lex[0], token=lookahead)) + arg = f"{lookahead.lex[0]}" if lookahead.is_eof else lookahead.lex[0] + errors.append(SyntacticCoolError(SYNTACTIC_ERROR, arg, token=lookahead)) # errors.append(f"Invalid transition near '{lookahead.lex[0]}'. Expected: {', '.join([ str(x[1]) for x in posibles ])}. Line:{lookahead.lex[1] + 1} Column:{lookahead.lex[2] + 1}") - if len(posibles) == 1: + if len(posibles) == 1 and not lookahead.is_eof: tokens.insert(cursor + 1, Token((str(posibles[0][1]), lookahead.lex[1], lookahead.lex[2]), posibles[0][1])) cursor += 1 continue diff --git a/src/cool_cmp/cool/error/errors.py b/src/cool_cmp/cool/error/errors.py index 54a140e98..cbcd2bc72 100644 --- a/src/cool_cmp/cool/error/errors.py +++ b/src/cool_cmp/cool/error/errors.py @@ -1,6 +1,6 @@ STRING_TOO_LONG = "String too long. Max length is 1024 chars, have {0} chars" -SYNTACTIC_ERROR = 'at or near "{0}"' +SYNTACTIC_ERROR = 'at or near {0}' REDEFINITION_BASIC_CLASS = 'Redefinition of basic class {0}.' METHOD_REDEFINED_WRONG_SIGNATURE_PARAM = 'In redefined method {0}, parameter type {1} is different from original type {2}.' METHOD_REDEFINED_WRONG_SIGNATURE_RETURN = "In redefined method {0}, return type {1} is different from original return type {2}." diff --git a/src/cool_cmp/cool/grammar/cool_grammar.py b/src/cool_cmp/cool/grammar/cool_grammar.py index c5c535c8c..6a148a3cc 100644 --- a/src/cool_cmp/cool/grammar/cool_grammar.py +++ b/src/cool_cmp/cool/grammar/cool_grammar.py @@ -57,7 +57,7 @@ param_list %= G.Epsilon, lambda h,s: [ ] param_list %= param, lambda h,s: [ s[1] ] -param_list %= param + comma + param_list, lambda h,s: [ s[1] ] + s[3] +param_list %= param_list + comma + param, lambda h,s: s[1] + [ s[3] ] # ??? param %= idx + colon + typex, lambda h,s: ParamNode(s[1][0],s[3][0],row=s[1][1],column = s[1][2]) @@ -66,7 +66,7 @@ def_var %= idx + colon + typex, lambda h,s: VarDeclarationNode(s[1][0],s[3][0],None,row=s[1][1] ,column=s[1][2]) def_var_list %= def_var, lambda h,s: [ s[1] ] -def_var_list %= def_var + comma + def_var_list, lambda h,s: [ s[1] ] + s[3] +def_var_list %= def_var_list + comma + def_var, lambda h,s: s[1] + [ s[3] ] case_check %= idx + colon + typex + arrow + expr + semi, lambda h,s: CheckNode(s[1][0],s[3][0],s[5],row=s[1][1] ,column=s[1][2]) @@ -79,7 +79,6 @@ expr %= idx + colon + typex + assign + expr, lambda h,s: VarDeclarationNode(s[1][0],s[3][0],s[5],row=s[1][1] ,column=s[1][2]) expr %= boolean, lambda h,s: s[1] expr %= ocur + expr_list + ccur, lambda h,s: BlockNode(s[2],row=s[1][1] ,column=s[1][2]) -expr %= ifx + expr + then + expr + elsex + expr + if_r, lambda h,s: ConditionalNode(s[2],s[4],s[6],row=s[1][1] ,column=s[1][2]) expr %= let + def_var_list + inx + expr, lambda h,s: LetNode(s[2],s[4],row=s[1][1] ,column=s[1][2]) expr %= case + expr + of + case_check_list + case_r, lambda h,s: CaseNode(s[2],s[4],row=s[1][1] ,column=s[1][2]) expr %= whilex + expr + loop + expr + loop_r, lambda h,s: WhileNode(s[2],s[4],row=s[1][1] ,column=s[1][2]) @@ -93,11 +92,11 @@ boolean %= compare, lambda h,s: s[1] # ??? -compare %= arith + equal + arith , lambda h,s: EqualNode(s[1],s[3],s[2][1],s[2][2]) -compare %= arith + less + arith , lambda h,s: LesserNode(s[1],s[3],s[2][1],s[2][2]) -compare %= arith + less_eq + arith , lambda h,s: LesserEqualNode(s[1],s[3],s[2][1],s[2][2]) -compare %= arith + greater + arith , lambda h,s: GreaterNode(s[1],s[3],s[2][1],s[2][2]) -compare %= arith + greater_eq + arith, lambda h,s: GreaterEqualNode(s[1],s[3],s[2][1],s[2][2]) +compare %= arith + equal + boolean , lambda h,s: EqualNode(s[1],s[3],s[2][1],s[2][2]) +compare %= arith + less + boolean , lambda h,s: LesserNode(s[1],s[3],s[2][1],s[2][2]) +compare %= arith + less_eq + boolean , lambda h,s: LesserEqualNode(s[1],s[3],s[2][1],s[2][2]) +compare %= arith + greater + boolean , lambda h,s: GreaterNode(s[1],s[3],s[2][1],s[2][2]) +compare %= arith + greater_eq + boolean, lambda h,s: GreaterEqualNode(s[1],s[3],s[2][1],s[2][2]) compare %= arith, lambda h,s: s[1] # ??? @@ -111,7 +110,7 @@ term %= factor, lambda h,s: s[1] # ??? -factor %= isvoid + factor, lambda h,s: IsVoidNode(s[2],row=s[1][1] ,column=s[1][2]) +factor %= isvoid + negate, lambda h,s: IsVoidNode(s[2],row=s[1][1] ,column=s[1][2]) factor %= negate, lambda h,s: s[1] # @@ -131,12 +130,13 @@ atom %= idx, lambda h,s: VariableNode(s[1][0],row=s[1][1] ,column=s[1][2]) atom %= new + typex, lambda h,s: InstantiateNode(s[2],row=s[1][1] ,column=s[1][2]) atom %= func_call, lambda h,s: CallNode(s[1][0],s[1][1],s[1][2],None,row=s[1][3] ,column=s[1][4]) -atom %= at + typex + dot + func_call, lambda h,s: CallNode(s[4][0],s[4][1],s[4][2],s[2][0],row=s[4][3] ,column=s[4][4]) +# atom %= at + typex + dot + func_call, lambda h,s: CallNode(s[4][0],s[4][1],s[4][2],s[2][0],row=s[4][3] ,column=s[4][4]) atom %= opar + expr + cpar, lambda h,s: s[2] +atom %= ifx + expr + then + expr + elsex + expr + if_r, lambda h,s: ConditionalNode(s[2],s[4],s[6],row=s[1][1] ,column=s[1][2]) # ??? func_call %= idx + opar + arg_list + cpar, lambda h,s: (VariableNode('self',s[1][1],s[1][2]),s[1][0],s[3],s[1][1],s[1][2]) arg_list %= G.Epsilon, lambda h,s: [ ] arg_list %= expr, lambda h,s: [ s[1] ] -arg_list %= expr + comma + arg_list, lambda h,s: [ s[1] ] + s[3] +arg_list %= arg_list + comma + expr, lambda h,s: s[1] + [ s[3] ] diff --git a/src/cool_cmp/cool/lexer/ply_cool_lexer.py b/src/cool_cmp/cool/lexer/ply_cool_lexer.py index a4d186a24..8af943cd6 100644 --- a/src/cool_cmp/cool/lexer/ply_cool_lexer.py +++ b/src/cool_cmp/cool/lexer/ply_cool_lexer.py @@ -36,10 +36,15 @@ def __getitem__(self, i): class PlyCoolToken(lex.LexToken): - def __init__(self, lex:str, typex, line:int, column:int): + def __init__(self, lex:str, typex, line:int, column:int, is_eof=False): self.set_lex(lex) self.set_type(typex) self.set_position(line, column) + self.__is_eof = is_eof + + @property + def is_eof(self): + return self.__is_eof def set_lex(self, lex:str): self.lex = Lex(lex) @@ -358,7 +363,7 @@ def __call__(self, program_string:str): for token in result: token.set_type(self.terminals[token.token_type]) - result.append(PlyCoolToken('$', cool_G.G.EOF, lines + 1, len(program_string))) + result.append(PlyCoolToken('EOF', cool_G.G.EOF, lines + 1, len(program_string), True)) result[-1].set_position(result[-1].line, self.find_column(program_string, result[-1])) return result diff --git a/src/cool_cmp/cool/parser/cool_parser.obj b/src/cool_cmp/cool/parser/cool_parser.obj index 4d92b378b7dffcdc845078ea36f6cba16b699764..9d8f2a7ff6dbfff0c1824116ef17cc5aba57e3c5 100644 GIT binary patch literal 154067 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 Date: Mon, 20 Sep 2021 20:28:50 -0400 Subject: [PATCH 039/143] FInal grammar fixes Variable declaration only available in let --- src/cool_cmp/cool/grammar/cool_grammar.py | 13 ++++++++----- src/cool_cmp/cool/parser/cool_parser.obj | Bin 154067 -> 148264 bytes 2 files changed, 8 insertions(+), 5 deletions(-) diff --git a/src/cool_cmp/cool/grammar/cool_grammar.py b/src/cool_cmp/cool/grammar/cool_grammar.py index 6a148a3cc..27d50c08d 100644 --- a/src/cool_cmp/cool/grammar/cool_grammar.py +++ b/src/cool_cmp/cool/grammar/cool_grammar.py @@ -13,6 +13,7 @@ func_call, arg_list, dispatch = G.NonTerminals(' ') def_var, def_var_list = G.NonTerminals(' ') case_check, case_check_list = G.NonTerminals(' ') +param_list_not_empty, arg_list_not_empty = G.NonTerminals(' ') # Terminals @@ -56,8 +57,9 @@ def_func %= idx + opar + param_list + cpar + colon + typex + ocur + expr + ccur, lambda h,s: FuncDeclarationNode(s[1][0],s[3],s[6],s[8],row=s[1][1] ,column=s[1][2]) param_list %= G.Epsilon, lambda h,s: [ ] -param_list %= param, lambda h,s: [ s[1] ] -param_list %= param_list + comma + param, lambda h,s: s[1] + [ s[3] ] +param_list %= param_list_not_empty, lambda h,s: s[1] +param_list_not_empty %= param, lambda h,s: [ s[1] ] +param_list_not_empty %= param_list_not_empty + comma + param, lambda h,s: s[1] + [ s[3] ] # ??? param %= idx + colon + typex, lambda h,s: ParamNode(s[1][0],s[3][0],row=s[1][1],column = s[1][2]) @@ -76,7 +78,7 @@ # ??? expr %= idx + assign + expr, lambda h,s: AssignNode(s[1][0],s[3],row=s[1][1] ,column=s[1][2]) -expr %= idx + colon + typex + assign + expr, lambda h,s: VarDeclarationNode(s[1][0],s[3][0],s[5],row=s[1][1] ,column=s[1][2]) +# expr %= idx + colon + typex + assign + expr, lambda h,s: VarDeclarationNode(s[1][0],s[3][0],s[5],row=s[1][1] ,column=s[1][2]) expr %= boolean, lambda h,s: s[1] expr %= ocur + expr_list + ccur, lambda h,s: BlockNode(s[2],row=s[1][1] ,column=s[1][2]) expr %= let + def_var_list + inx + expr, lambda h,s: LetNode(s[2],s[4],row=s[1][1] ,column=s[1][2]) @@ -138,5 +140,6 @@ func_call %= idx + opar + arg_list + cpar, lambda h,s: (VariableNode('self',s[1][1],s[1][2]),s[1][0],s[3],s[1][1],s[1][2]) arg_list %= G.Epsilon, lambda h,s: [ ] -arg_list %= expr, lambda h,s: [ s[1] ] -arg_list %= arg_list + comma + expr, lambda h,s: s[1] + [ s[3] ] +arg_list %= arg_list_not_empty, lambda h,s: s[1] +arg_list_not_empty %= expr, lambda h,s: [ s[1] ] +arg_list_not_empty %= arg_list_not_empty + comma + expr, lambda h,s: s[1] + [ s[3] ] diff --git a/src/cool_cmp/cool/parser/cool_parser.obj b/src/cool_cmp/cool/parser/cool_parser.obj index 9d8f2a7ff6dbfff0c1824116ef17cc5aba57e3c5..c2a2d54c07702142db6dfa8b3df3a1aaf398a7da 100644 GIT binary patch literal 148264 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 Date: Mon, 27 Sep 2021 19:37:53 -0400 Subject: [PATCH 040/143] Generating CIL code First steps in CIL generation process. --- .vscode/launch.json | 2 +- src/cool_cmp/cmp/cil.py | 23 +- src/cool_cmp/cool/ast/cil_ast.py | 32 ++ src/cool_cmp/cool/ast/{ast.py => cool_ast.py} | 3 +- src/cool_cmp/cool/grammar/comment_grammar.py | 2 +- src/cool_cmp/cool/grammar/cool_grammar.py | 2 +- src/cool_cmp/cool/pipeline.py | 27 +- src/cool_cmp/cool/pipes/pipes.py | 42 +- src/cool_cmp/cool/semantic/operations.py | 2 +- src/cool_cmp/cool/semantic/type.py | 27 +- src/cool_cmp/cool/visitors/cil_visitor.py | 509 ++++++++++++++++++ src/cool_cmp/cool/visitors/visitors.py | 4 +- src/cool_cmp/main.py | 10 +- src/testing.cl | 62 +-- src/testing.cl.cil | 169 ++++++ src/testing.cl.infer.cl | 12 + 16 files changed, 834 insertions(+), 94 deletions(-) create mode 100644 src/cool_cmp/cool/ast/cil_ast.py rename src/cool_cmp/cool/ast/{ast.py => cool_ast.py} (94%) create mode 100644 src/cool_cmp/cool/visitors/cil_visitor.py create mode 100644 src/testing.cl.cil create mode 100644 src/testing.cl.infer.cl diff --git a/.vscode/launch.json b/.vscode/launch.json index d0006c931..055ec7838 100644 --- a/.vscode/launch.json +++ b/.vscode/launch.json @@ -9,7 +9,7 @@ "type": "python", "request": "launch", "program": "src/cool_cmp/main.py", - "args": ["src/testing.cl", "src/testing.mips"], + "args": ["src/testing.cl", "src/testing.mips", "-i"], "console": "integratedTerminal" } ] diff --git a/src/cool_cmp/cmp/cil.py b/src/cool_cmp/cmp/cil.py index a281a45f0..bcb63d7f4 100644 --- a/src/cool_cmp/cmp/cil.py +++ b/src/cool_cmp/cmp/cil.py @@ -121,16 +121,25 @@ def __init__(self, dest, msg): self.msg = msg class LengthNode(InstructionNode): - pass + def __init__(self, dest, string) -> None: + self.dest = dest + self.string = string class ConcatNode(InstructionNode): - pass + def __init__(self, dest, string1, string2) -> None: + self.dest = dest + self.string1 = string1 + self.string2 = string2 class PrefixNode(InstructionNode): pass class SubstringNode(InstructionNode): - pass + def __init__(self, dest, string, index, length) -> None: + self.dest = dest + self.string = string + self.index = index + self.length = length class ToStrNode(InstructionNode): def __init__(self, dest, ivalue): @@ -145,6 +154,14 @@ class PrintNode(InstructionNode): def __init__(self, str_addr): self.str_addr = str_addr +class AbortNode(InstructionNode): + pass + +class CopyNode(InstructionNode): + def __init__(self, instance, result): + self.instance = instance + self.result = result + def get_formatter(): class PrintVisitor(object): diff --git a/src/cool_cmp/cool/ast/cil_ast.py b/src/cool_cmp/cool/ast/cil_ast.py new file mode 100644 index 000000000..8dd8bc0e3 --- /dev/null +++ b/src/cool_cmp/cool/ast/cil_ast.py @@ -0,0 +1,32 @@ +from cmp.cil import * + +class ObjectAbortNode(InstructionNode): + pass + +class ObjectCopyNode(InstructionNode): + pass + +class ObjectTypeNameNode(InstructionNode): + pass + +class StringLengthNode(InstructionNode): + pass + +class StringConcatNode(InstructionNode): + pass + +class StringSubstringNode(InstructionNode): + pass + +class IOOutStringNode(InstructionNode): + pass + +class IOOutIntNode(InstructionNode): + pass + +class IOInStringNode(InstructionNode): + pass + +class IOInIntNode(InstructionNode): + pass + \ No newline at end of file diff --git a/src/cool_cmp/cool/ast/ast.py b/src/cool_cmp/cool/ast/cool_ast.py similarity index 94% rename from src/cool_cmp/cool/ast/ast.py rename to src/cool_cmp/cool/ast/cool_ast.py index 9e140e875..ba45eb990 100644 --- a/src/cool_cmp/cool/ast/ast.py +++ b/src/cool_cmp/cool/ast/cool_ast.py @@ -70,9 +70,10 @@ def __iter__(self): yield self class SpecialNode(ExpressionNode): - def __init__(self, func,row=None,column=None): + def __init__(self, func,row=None,column=None, cil_node_type=None): super().__init__(row,column) self.func = func + self.cil_node_type = cil_node_type def __iter__(self): yield self diff --git a/src/cool_cmp/cool/grammar/comment_grammar.py b/src/cool_cmp/cool/grammar/comment_grammar.py index 7bf285ca5..6df5ca67c 100644 --- a/src/cool_cmp/cool/grammar/comment_grammar.py +++ b/src/cool_cmp/cool/grammar/comment_grammar.py @@ -1,5 +1,5 @@ from cmp.pycompiler import Grammar, NonTerminal, Terminal -from cool.ast.ast import * +from cool.ast.cool_ast import * C = Grammar() diff --git a/src/cool_cmp/cool/grammar/cool_grammar.py b/src/cool_cmp/cool/grammar/cool_grammar.py index 27d50c08d..2eda67024 100644 --- a/src/cool_cmp/cool/grammar/cool_grammar.py +++ b/src/cool_cmp/cool/grammar/cool_grammar.py @@ -1,5 +1,5 @@ from cmp.pycompiler import Grammar, NonTerminal, Terminal -from cool.ast.ast import * +from cool.ast.cool_ast import * G = Grammar() diff --git a/src/cool_cmp/cool/pipeline.py b/src/cool_cmp/cool/pipeline.py index 493646189..7145d9f48 100644 --- a/src/cool_cmp/cool/pipeline.py +++ b/src/cool_cmp/cool/pipeline.py @@ -1,4 +1,4 @@ -from cool.pipes.pipes import start_pipe, change_escaped_lines, remove_comments_pipe,\ +from cool.pipes.pipes import cil_ast_to_text_pipe, cool_to_cil_pipe, start_pipe, change_escaped_lines, remove_comments_pipe,\ parse_text_pipe, ast_pipe, type_collector_pipe, build_types_pipe, \ check_types_pipe, run_program_pipe, reconstruct_pipe, void_as_type_pipe, \ auto_resolver_pipe, string_escape_pipe, tokenize_text_pipe, ply_lexer_pipe, remove_comment_tokens_pipe @@ -66,15 +66,22 @@ def add_std_pipe(result:dict): pre_semantic_pipeline, ) -execution_pipeline = Pipeline(run_program_pipe) +execution_pipe = Pipe(run_program_pipe) -cool_pipeline = Pipeline(lexer_pipeline, - syntax_pipeline, - semantic_pipeline, - execution_pipeline) +base_cool_pipeline = Pipeline(lexer_pipeline, + syntax_pipeline, + semantic_pipeline,) -reconstr_pipeline = Pipeline(lexer_pipeline, - syntax_pipeline, - semantic_pipeline, +cool_pipeline = Pipeline(base_cool_pipeline, + cool_to_cil_pipe, + cil_ast_to_text_pipe) + +interprete_cool_pipeline = Pipeline(base_cool_pipeline, + execution_pipe) + +reconstr_pipeline = Pipeline(base_cool_pipeline, reconstruct_pipe, - execution_pipeline) + cool_to_cil_pipe, + cil_ast_to_text_pipe, + execution_pipe) + diff --git a/src/cool_cmp/cool/pipes/pipes.py b/src/cool_cmp/cool/pipes/pipes.py index 008f6d37c..fa77b7f41 100644 --- a/src/cool_cmp/cool/pipes/pipes.py +++ b/src/cool_cmp/cool/pipes/pipes.py @@ -11,6 +11,7 @@ from cool.semantic.scope import Scope from cool.pipes.utils import pprint_tokens, print_errors from cool.pipes.pipeline import Pipe +from cool.visitors.cil_visitor import CILPrintVisitor, COOLToCILVisitor ply_lexer = PlyLexer() @@ -402,4 +403,43 @@ def string_escape_pipe(result:dict): tok_str.lex = (text, tok_str.lex[1], tok_str.lex[2]) return result -string_escape_pipe = Pipe(string_escape_pipe) \ No newline at end of file +string_escape_pipe = Pipe(string_escape_pipe) + +def cool_to_cil_pipe(result: dict, cool_to_cil=COOLToCILVisitor): + context = result.get("context",None) # TODO Ver bien que hace falta aqui + scope = result.get("scope", None) + ast = result.get("ast",) + if any(x == None for x in [context, ast, scope]): + return result # TODO se devuelve result pq el error ya deveria de estar seteado + + errors = [] + cool_to_cil_visitor = cool_to_cil(context, errors) # TODO Ver los argumentos + + cil_ast = cool_to_cil_visitor.visit(ast, scope) # TODO Devuelve un AST de CIL + + result['cil_ast'] = cil_ast + + if result.get("verbose", False): + if errors: + print_errors("COOL to CIL Errors", errors) + + result["errors"].extend(errors) + + return result + +cool_to_cil_pipe = Pipe(cool_to_cil_pipe) + +def cil_ast_to_text_pipe(result: dict, formatter=CILPrintVisitor): + ast = result.get("cil_ast",None) + if any(x == None for x in [ast]): + return result + + formatter = formatter() + cil_text = formatter.visit(ast) + if result.get("verbose", False): + print("============== CIL Text ===============") + print(cil_text) + result['cil_text'] = cil_text + return result + +cil_ast_to_text_pipe = Pipe(cil_ast_to_text_pipe) \ No newline at end of file diff --git a/src/cool_cmp/cool/semantic/operations.py b/src/cool_cmp/cool/semantic/operations.py index 66bbc9ae1..7ec0da427 100644 --- a/src/cool_cmp/cool/semantic/operations.py +++ b/src/cool_cmp/cool/semantic/operations.py @@ -1,5 +1,5 @@ from cool.error.errors import SemanticError,RunError, NO_OPERATION_DEFINDED, MULTIPLE_OPERATION_DEFINED, ZERO_DIVISION -from cool.ast.ast import * +from cool.ast.cool_ast import * from cool.semantic.type import * from cool.semantic.atomic import * diff --git a/src/cool_cmp/cool/semantic/type.py b/src/cool_cmp/cool/semantic/type.py index 868bb955e..9aecbd869 100644 --- a/src/cool_cmp/cool/semantic/type.py +++ b/src/cool_cmp/cool/semantic/type.py @@ -1,7 +1,8 @@ from cmp.semantic import Attribute, Method from cmp.semantic import Type as DeprecatedType from cmp.semantic import SemanticError as DeprecatedSemanticError -from cool.ast.ast import VoidNode,ConstantNumNode,StringNode,BoolNode,SpecialNode,InstantiateNode +from cool.ast.cool_ast import VoidNode,ConstantNumNode,StringNode,BoolNode,SpecialNode,InstantiateNode +import cool.ast.cil_ast as cil from cool.semantic.atomic import ClassInstance from cool.error.errors import RunError, SemanticError, TypeCoolError, InferError, \ VOID_TYPE_CONFORMS, METHOD_NOT_DEFINED, METHOD_ALREADY_DEFINED, \ @@ -12,10 +13,10 @@ class Type(DeprecatedType): - def add_special_method(self,func,method_name,method_args): + def add_special_method(self,func,method_name,method_args, cil_node_type): f = self.get_method(method_name,method_args) old_type = f.node.body.type - f.node.body = SpecialNode(func,f.node.row,f.node.column) + f.node.body = SpecialNode(func,f.node.row,f.node.column, cil_node_type) f.node.body.type = old_type def set_parent(self,parent): @@ -136,9 +137,9 @@ def __init__(self): self.parent = None def complete(self): - self.add_special_method(self.abort,'abort',0) - self.add_special_method(self.copy,'copy',0) - self.add_special_method(self.type_name,'type_name',0) + self.add_special_method(self.abort,'abort',0, cil.ObjectAbortNode) + self.add_special_method(self.copy,'copy',0, cil.ObjectCopyNode) + self.add_special_method(self.type_name,'type_name',0, cil.ObjectTypeNameNode) def set_parent(self,parent): pass @@ -196,9 +197,9 @@ def __init__(self): Type.__init__(self, 'String') def complete(self): - self.add_special_method(self.length,'length',0) - self.add_special_method(self.concat,'concat',1) - self.add_special_method(self.substr,'substr',2) + self.add_special_method(self.length,'length',0, cil.StringLengthNode) + self.add_special_method(self.concat,'concat',1, cil.StringConcatNode) + self.add_special_method(self.substr,'substr',2, cil.StringSubstringNode) @staticmethod def length(scope,context,operator,errors,**kwargs): @@ -264,10 +265,10 @@ def __init__(self): Type.__init__(self, 'IO') def complete(self): - self.add_special_method(self.out_string,'out_string',1) - self.add_special_method(self.out_int,'out_int',1) - self.add_special_method(self.in_string,'in_string',0) - self.add_special_method(self.in_int,'in_int',0) + self.add_special_method(self.out_string,'out_string',1, cil.IOOutStringNode) + self.add_special_method(self.out_int,'out_int',1, cil.IOOutIntNode) + self.add_special_method(self.in_string,'in_string',0, cil.IOInStringNode) + self.add_special_method(self.in_int,'in_int',0, cil.IOInIntNode) @staticmethod def out_string(scope,context,operator,errors,**kwargs): diff --git a/src/cool_cmp/cool/visitors/cil_visitor.py b/src/cool_cmp/cool/visitors/cil_visitor.py new file mode 100644 index 000000000..46336fedc --- /dev/null +++ b/src/cool_cmp/cool/visitors/cil_visitor.py @@ -0,0 +1,509 @@ +import cool.ast.cil_ast as cil +from cool.ast.cool_ast import * +import cmp.visitor as visitor +from cmp.semantic import VariableInfo +from cool.semantic.context import Context + +class CILPrintVisitor(): + @visitor.on('node') + def visit(self, node): + pass + + @visitor.when(cil.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(cil.DataNode) + def visit(self, node): + return f"{node.name} = {node.value}" + + @visitor.when(cil.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(cil.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(cil.ParamNode) + def visit(self, node): + return f'PARAM {node.name}' + + @visitor.when(cil.LocalNode) + def visit(self, node): + return f'LOCAL {node.name}' + + @visitor.when(cil.AssignNode) + def visit(self, node): + return f'{node.dest} = {node.source}' + + @visitor.when(cil.PlusNode) + def visit(self, node): + return f'{node.dest} = {node.left} + {node.right}' + + @visitor.when(cil.MinusNode) + def visit(self, node): + return f'{node.dest} = {node.left} - {node.right}' + + @visitor.when(cil.StarNode) + def visit(self, node): + return f'{node.dest} = {node.left} * {node.right}' + + @visitor.when(cil.DivNode) + def visit(self, node): + return f'{node.dest} = {node.left} / {node.right}' + + @visitor.when(cil.AllocateNode) + def visit(self, node): + return f'{node.dest} = ALLOCATE {node.type}' + + @visitor.when(cil.TypeOfNode) + def visit(self, node): + return f'{node.dest} = TYPEOF {node.obj}' + + @visitor.when(cil.StaticCallNode) + def visit(self, node): + return f'{node.dest} = CALL {node.function}' + + @visitor.when(cil.DynamicCallNode) + def visit(self, node): + return f'{node.dest} = VCALL {node.type} {node.method}' + + @visitor.when(cil.ArgNode) + def visit(self, node): + return f'ARG {node.name}' + + @visitor.when(cil.ReturnNode) + def visit(self, node): + return f'RETURN {node.value if node.value is not None else ""}' + + @visitor.when(cil.AbortNode) + def visit(self, node): + return f'ABORT' + + @visitor.when(cil.CopyNode) + def visit(self, node): + return f'{node.result} = COPY {node.instance}' + + @visitor.when(cil.LengthNode) + def visit(self, node): + return f'{node.dest} = LENGTH {node.string}' + + @visitor.when(cil.ConcatNode) + def visit(self, node): + return f'{node.dest} = CONCAT {node.string1} {node.string2}' + + @visitor.when(cil.SubstringNode) + def visit(self, node): + return f'{node.dest} = SUBSTRING {node.string} {node.index} {node.length}' + + @visitor.when(cil.PrintNode) + def visit(self, node): + return f'PRINT {node.str_addr}' + + @visitor.when(cil.ToStrNode) + def visit(self, node): + return f'{node.dest} = TOSTR {node.ivalue}' + + @visitor.when(cil.ReadNode) + def visit(self, node): + return f'{node.dest} = READ' + + @visitor.when(cil.LoadNode) + def visit(self, node:cil.LoadNode): + return f'{node.dest} = LOAD {node.msg}' + +class COOLToCILVisitor(): + + def __init__(self, context:Context, errors=[]): + self.errors = errors + self.dottypes = [] + self.dotdata = [] + self.dotcode = [] + self.current_type = None + self.current_method = None + self.current_function = None + self.context = context + + @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_local(self, vinfo): + vinfo.name = f'local_{self.current_function.name[9:]}_{vinfo.name}_{len(self.localvars)}' + local_node = cil.LocalNode(vinfo.name) + self.localvars.append(local_node) + return vinfo.name + + def define_internal_local(self): + vinfo = VariableInfo('internal', None) + return self.register_local(vinfo) + + def register_instruction(self, instruction): + self.instructions.append(instruction) + return instruction + + def to_function_name(self, method_name, type_name): + return f'function_{method_name}_at_{type_name}' + + def register_function(self, function_name): + function_node = cil.FunctionNode(function_name, [], [], []) + self.dotcode.append(function_node) + return function_node + + def register_type(self, name): + type_node = cil.TypeNode(name) + self.dottypes.append(type_node) + return type_node + + def register_data(self, value): + vname = f'data_{len(self.dotdata)}' + data_node = cil.DataNode(vname, value) + self.dotdata.append(data_node) + return data_node + + @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() + result = self.define_internal_local() + main_method_name = self.to_function_name('main', 'Main') + self.register_instruction(cil.AllocateNode('Main', instance)) + self.register_instruction(cil.ArgNode(instance)) + self.register_instruction(cil.StaticCallNode(main_method_name, result)) + self.register_instruction(cil.ReturnNode(0)) + self.current_function = None + + for type_name, typex in self.context.types.items(): + if type_name in self.context.special_types and type_name not in ["Error", "Void"]: + self.visit(typex.class_node, typex.class_node.scope) + + for declaration, child_scope in zip(node.declarations, scope.children): + self.visit(declaration, child_scope) + + return cil.ProgramNode(self.dottypes, self.dotdata, self.dotcode) + + @visitor.when(ClassDeclarationNode) + def visit(self, node, scope): + #################################################################### + # node.id -> str + # node.parent -> str + # node.features -> [ FuncDeclarationNode/AttrDeclarationNode ... ] + #################################################################### + + self.current_type = self.context.get_type(node.id) + + type_node = self.register_type(self.current_type.name) + + for attr,typex in self.current_type.all_attributes(): + type_node.attributes.append(attr.name) + + for method,typex in self.current_type.all_methods(): # Register methods + if typex != self.current_type: + method_name = self.to_function_name(method.name, typex.name) + if all(x.name != method_name for x in self.dotcode): + new_function = self.register_function(method_name) + else: + new_function = next(x for x in self.dotcode if x.name == method_name) + type_node.methods.append((method.name,new_function.name)) + + + func_declarations = (f for f in node.features if isinstance(f, FuncDeclarationNode)) + for feature, child_scope in zip(func_declarations, scope.children): + self.current_function = self.register_function(self.to_function_name(feature.id,self.current_type.name)) + type_node.methods.append((feature.id,self.current_function.name)) + self.visit(feature, child_scope) + + 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, len(node.params)) + + # Your code here!!! (Handle PARAMS) + self.current_function.params.append(cil.ParamNode('self')) + for param in self.current_method.param_names: + self.current_function.params.append(cil.ParamNode(param)) + + value = self.visit(node.body, scope) + + # Your code here!!! (Handle RETURN) + self.register_instruction(cil.ReturnNode(value)) + self.current_method = None + + @visitor.when(VarDeclarationNode) + def visit(self, node, scope): + ############################### + # node.id -> str + # node.type -> str + # node.expr -> ExpressionNode + ############################### + # Your code here!!! + local = scope.find_variable(node.id) + cil_local = self.register_local(local) + local.cil = cil_local + value = self.visit(node.expr,scope) + self.register_instruction(cil.AssignNode(cil_local,value)) + return cil_local + + @visitor.when(SpecialNode) + def visit(self, node, scope=None): + return self.visit(node.cil_node_type(), scope) + + @visitor.when(cil.ObjectCopyNode) + def visit(self, node, scope=None): + instance = self.current_function.params[0] + result = self.define_internal_local() + self.register_instruction(cil.CopyNode(instance.name, result)) + return result + + @visitor.when(cil.ObjectAbortNode) + def visit(self, node, scope=None): + self.register_instruction(cil.AbortNode()) + return "0" + + @visitor.when(cil.ObjectTypeNameNode) + def visit(self, node, scope=None): + instance = self.current_function.params[0] + result = self.define_internal_local() + self.register_instruction(cil.TypeOfNode(instance.name, result)) + return result + + @visitor.when(cil.StringConcatNode) + def visit(self, node, scope=None): + string1 = self.current_function.params[0] + string2 = self.current_function.params[1] + result = self.define_internal_local() + self.register_instruction(cil.ConcatNode(result, string1.name, string2.name)) + return result + + @visitor.when(cil.StringLengthNode) + def visit(self, node, scope=None): + string = self.current_function.params[0] + result = self.define_internal_local() + self.register_instruction(cil.LengthNode(result, string.name)) + return result + + + @visitor.when(cil.StringSubstringNode) + def visit(self, node, scope=None): + string = self.current_function.params[0] + index = self.current_function.params[1] + length = self.current_function.params[2] + result = self.define_internal_local() + self.register_instruction(cil.SubstringNode(result, string.name, index.name, length.name)) + return result + + @visitor.when(cil.IOInStringNode) + def visit(self, node, scope=None): + result = self.define_internal_local() + self.register_instruction(cil.ReadNode(result)) + return result + + @visitor.when(cil.IOInIntNode) + def visit(self, node, scope=None): + result = self.define_internal_local() + self.register_instruction(cil.ReadNode(result)) + # TODO Convertir String a Int + return result + + @visitor.when(cil.IOOutIntNode) + def visit(self, node, scope=None): + integer = self.current_function.params[1] + string_message = self.define_internal_local() + self.register_instruction(cil.ToStrNode(string_message, integer.name)) + self.register_instruction(cil.PrintNode(string_message)) + return "0" + + @visitor.when(cil.IOOutStringNode) + def visit(self, node, scope=None): + string = self.current_function.params[1] + self.register_instruction(cil.PrintNode(string.name)) + return "0" + + @visitor.when(AssignNode) + def visit(self, node, scope): + ############################### + # node.id -> str + # node.expr -> ExpressionNode + ############################### + local = scope.find_variable(node.id) + value = self.visit(node.expr,scope) + if hasattr(local,'cil'): + self.register_instruction(cil.AssignNode(local.cil,value)) + return local.cil + else: + if any(x for x in self.current_function.params if x.name == local.name): + self.register_instruction(cil.AssignNode(local.name,value)) # Param + return local.name + else: + self.register_instruction(cil.SetAttribNode('self',local.name,value)) + # value = self.define_internal_local() # Attr + # self.register_instruction(cil.GetAttribNode('self',local.name,value)) + return value # or self ? + + @visitor.when(CallNode) + def visit(self, node, scope): + ############################### + # node.obj -> AtomicNode + # node.id -> str + # node.args -> [ ExpressionNode ... ] + ############################### + + obj_value = self.visit(node.obj,scope) + + args = [] + method = node.obj.type.get_method(node.id, len(node.args)) + for arg_node in node.args: + value = self.visit(arg_node,scope) + args.append(value) + + result = self.define_internal_local() + + self.register_instruction(cil.ArgNode(obj_value)) # self + for arg,value in zip(method.param_names,args): + self.register_instruction(cil.ArgNode(value)) + + self.register_instruction(cil.DynamicCallNode(node.obj.type.name,node.id,result)) + + return result + + @visitor.when(ConstantNumNode) + def visit(self, node, scope): + ############################### + # node.lex -> str + ############################### + + return node.lex + + @visitor.when(VariableNode) + def visit(self, node, scope): + ############################### + # node.lex -> str + ############################### + + # Your code here!!! + try: + return scope.find_variable(node.lex).cil # is a Local Variable + except AttributeError: + if any(x for x in self.current_function.params if x.name == node.lex): + return node.lex # Param + else: + value = self.define_internal_local() # Attr + self.register_instruction(cil.GetAttribNode('self',node.lex,value)) + return value + + @visitor.when(InstantiateNode) + def visit(self, node, scope): + ############################### + # node.lex -> str + ############################### + + # Your code here!!! + instance = self.define_internal_local() + self.register_instruction(cil.AllocateNode(node.lex, instance)) + return instance + + @visitor.when(PlusNode) + def visit(self, node, scope): + ############################### + # node.left -> ExpressionNode + # node.right -> ExpressionNode + ############################### + + # Your code here!!! + left = self.visit(node.left,scope) + right = self.visit(node.right,scope) + + value = self.define_internal_local() + self.register_instruction(cil.PlusNode(value,left,right)) + return value + + @visitor.when(MinusNode) + def visit(self, node, scope): + ############################### + # node.left -> ExpressionNode + # node.right -> ExpressionNode + ############################### + + # Your code here!!! + left = self.visit(node.left,scope) + right = self.visit(node.right,scope) + + value = self.define_internal_local() + self.register_instruction(cil.MinusNode(value,left,right)) + return value + + @visitor.when(StarNode) + def visit(self, node, scope): + ############################### + # node.left -> ExpressionNode + # node.right -> ExpressionNode + ############################### + + # Your code here!!! + left = self.visit(node.left,scope) + right = self.visit(node.right,scope) + + value = self.define_internal_local() + self.register_instruction(cil.StarNode(value,left,right)) + return value + + @visitor.when(DivNode) + def visit(self, node, scope): + ############################### + # node.left -> ExpressionNode + # node.right -> ExpressionNode + ############################### + + # Your code here!!! + left = self.visit(node.left,scope) + right = self.visit(node.right,scope) + + value = self.define_internal_local() + self.register_instruction(cil.DivNode(value,left,right)) + return value + + @visitor.when(StringNode) + def visit(self, node, scope): + value = self.register_data(node.lex) + result = self.define_internal_local() + self.register_instruction(cil.LoadNode(result, value.name)) + return result + + # ====================================================================== diff --git a/src/cool_cmp/cool/visitors/visitors.py b/src/cool_cmp/cool/visitors/visitors.py index 6db7a51d1..ff1f3148e 100644 --- a/src/cool_cmp/cool/visitors/visitors.py +++ b/src/cool_cmp/cool/visitors/visitors.py @@ -1,5 +1,5 @@ import cmp.visitor as visitor -from cool.ast.ast import * +from cool.ast.cool_ast import * from cool.semantic.type import Type,ErrorType,StringType,IntType,IOType,BoolType,ObjectType,SelfType,VoidType,AutoType from cool.semantic.context import Context from cool.semantic.atomic import ClassInstance @@ -363,6 +363,8 @@ def visit(self, node): if class_decl.id in self.context.special_types: typex = self.context.get_type(class_decl.id) typex.class_node = class_decl + if typex.name != "Object": + typex.parent = self.context.get_type(class_decl.parent) if class_decl.parent is not None else None self.add_semantic_error(SemanticError(REDEFINITION_BASIC_CLASS, class_decl.id), class_decl.row, class_decl.column) else: try: diff --git a/src/cool_cmp/main.py b/src/cool_cmp/main.py index eb661b4e3..f2b7f1dea 100644 --- a/src/cool_cmp/main.py +++ b/src/cool_cmp/main.py @@ -61,12 +61,18 @@ def main( if out_infer: result = reconstr_pipeline(file_content, verbose=verbose) else: - result = cool_pipeline(file_content,verbose=verbose) - ast, g_errors, parse, tokens, context, scope, operator, value, reconstr = [result.get(x, None) for x in ["ast", "errors", "text_parse", "text_tokens", "context", "scope", "operator", "value", "reconstructed_text"]] + result = cool_pipeline(file_content,verbose=True) + ast, g_errors, parse, tokens, context, scope, operator, value, reconstr, cil_text = [result.get(x, None) for x in ["ast", "errors", "text_parse", "text_tokens", "context", "scope", "operator", "value", "reconstructed_text", "cil_text"]] if reconstr and out_infer: with open(program_dir + ".infer.cl", "w") as file: file.write(reconstr) + + if cil_text and out_infer: + with open(program_dir + ".cil", "w") as file: + file.write(cil_text) + + if g_errors: for err in g_errors: diff --git a/src/testing.cl b/src/testing.cl index 5fdef22d6..e4b79dbac 100644 --- a/src/testing.cl +++ b/src/testing.cl @@ -1,61 +1,5 @@ -(* There are three forms of dispatch (i.e. method call) in Cool. The three forms differ only in how the called method is selected *) - -class Main { - main(): Object { - (new Alpha).print() +class Main inherits IO { + main(): IO { + out_string("Hello") }; }; - -class Test { - test1: Object; - - testing1(): Int { - 2 + 2 - }; - - test2: Int <- 1; - - test3: String <- "1"; - - testing2(a: Alpha, b: Int): Int { - 2 + 2 - }; - - testing3(): String { - "2 + 2" - }; - - testing4(x: Int, y: Int): Test { - self - }; -}; - -class Test2 { - test1: Test <- new Test; - - testing1(): Test { - test1.testing4(1 + 1, 1 + 2).testing4(2 + 3, 3 + 5).testing4(5 + 8, 8 + 13) - }; - - testing2(x: Int, y: Int): Test2 { - self - }; - - testing3(): Test2 { - testing2(1 + 1, 1 + 2).testing2(2 + 3, 3 + 5).testing2(5 + 8, true + fALSE) - }; - - testing4(): Object { - test1@Object.copy() - }; - - testing5(): Object { - test1:Object.copy() -- Must be '@' not ':' - }; -} - -class Alpha inherits IO { - print() : Object { - out_string("reached!!\n") - }; -}; \ No newline at end of file diff --git a/src/testing.cl.cil b/src/testing.cl.cil new file mode 100644 index 000000000..e582855ad --- /dev/null +++ b/src/testing.cl.cil @@ -0,0 +1,169 @@ +.TYPES +type Object { + + + method abort: function_abort_at_Object + method type_name: function_type_name_at_Object + method copy: function_copy_at_Object +} +type String { + + + method abort: function_abort_at_Object + method type_name: function_type_name_at_Object + method copy: function_copy_at_Object + method length: function_length_at_String + method concat: function_concat_at_String + method substr: function_substr_at_String +} +type Bool { + + + method abort: function_abort_at_Object + method type_name: function_type_name_at_Object + method copy: function_copy_at_Object +} +type Int { + + + method abort: function_abort_at_Object + method type_name: function_type_name_at_Object + method copy: function_copy_at_Object +} +type IO { + + + method abort: function_abort_at_Object + method type_name: function_type_name_at_Object + method copy: function_copy_at_Object + method out_string: function_out_string_at_IO + method out_int: function_out_int_at_IO + method in_string: function_in_string_at_IO + method in_int: function_in_int_at_IO +} +type Main { + + + method abort: function_abort_at_Object + method type_name: function_type_name_at_Object + method copy: function_copy_at_Object + method out_string: function_out_string_at_IO + method out_int: function_out_int_at_IO + method in_string: function_in_string_at_IO + method in_int: function_in_int_at_IO + method main: function_main_at_Main +} + +.DATA +data_0 = "Hello" + +.CODE +function entry { + + + LOCAL local__internal_0 + LOCAL local__internal_1 + + local__internal_0 = ALLOCATE Main + ARG local__internal_0 + local__internal_1 = CALL function_main_at_Main + RETURN 0 +} +function function_abort_at_Object { + PARAM self + + + + ABORT + RETURN 0 +} +function function_type_name_at_Object { + PARAM self + + LOCAL local_type_name_at_Object_internal_0 + + local_type_name_at_Object_internal_0 = TYPEOF self + RETURN local_type_name_at_Object_internal_0 +} +function function_copy_at_Object { + PARAM self + + LOCAL local_copy_at_Object_internal_0 + + local_copy_at_Object_internal_0 = COPY self + RETURN local_copy_at_Object_internal_0 +} +function function_length_at_String { + PARAM self + + LOCAL local_length_at_String_internal_0 + + local_length_at_String_internal_0 = LENGTH self + RETURN local_length_at_String_internal_0 +} +function function_concat_at_String { + PARAM self + PARAM s + + LOCAL local_concat_at_String_internal_0 + + local_concat_at_String_internal_0 = CONCAT self s + RETURN local_concat_at_String_internal_0 +} +function function_substr_at_String { + PARAM self + PARAM i + PARAM l + + LOCAL local_substr_at_String_internal_0 + + local_substr_at_String_internal_0 = SUBSTRING self i l + RETURN local_substr_at_String_internal_0 +} +function function_out_string_at_IO { + PARAM self + PARAM x + + + + PRINT x + RETURN 0 +} +function function_out_int_at_IO { + PARAM self + PARAM x + + LOCAL local_out_int_at_IO_internal_0 + + local_out_int_at_IO_internal_0 = TOSTR x + PRINT local_out_int_at_IO_internal_0 + RETURN 0 +} +function function_in_string_at_IO { + PARAM self + + LOCAL local_in_string_at_IO_internal_0 + + local_in_string_at_IO_internal_0 = READ + RETURN local_in_string_at_IO_internal_0 +} +function function_in_int_at_IO { + PARAM self + + LOCAL local_in_int_at_IO_internal_0 + + local_in_int_at_IO_internal_0 = READ + RETURN local_in_int_at_IO_internal_0 +} +function function_main_at_Main { + PARAM self + + LOCAL local_main_at_Main_internal_0 + LOCAL local_main_at_Main_internal_1 + + local_main_at_Main_internal_0 = LOAD data_0 + ARG self + ARG local_main_at_Main_internal_0 + local_main_at_Main_internal_1 = VCALL Main out_string + RETURN local_main_at_Main_internal_1 +} \ No newline at end of file diff --git a/src/testing.cl.infer.cl b/src/testing.cl.infer.cl new file mode 100644 index 000000000..50e70b4f0 --- /dev/null +++ b/src/testing.cl.infer.cl @@ -0,0 +1,12 @@ +class Main inherits IO +{ + main(): IO + { + self + .out_string( + "Hello" + ) + }; +} + + From 983f4c5985d4ff0ce45853d0f1b4a3423774b0e3 Mon Sep 17 00:00:00 2001 From: Luiso Date: Tue, 28 Sep 2021 05:37:53 -0400 Subject: [PATCH 041/143] Test Problems Fixed. When a new instance is created all the attributes initializers are called --- src/cool_cmp/cmp/cil.py | 20 ++++++++--- src/cool_cmp/cool/error/errors.py | 2 ++ src/cool_cmp/cool/pipes/pipes.py | 9 +++-- src/cool_cmp/cool/semantic/operations.py | 3 ++ src/cool_cmp/cool/visitors/cil_visitor.py | 42 ++++++++++++++++++++--- src/cool_cmp/cool/visitors/visitors.py | 5 ++- src/cool_cmp/main.py | 2 +- src/testing.cl | 6 +++- src/testing.cl.cil | 24 ++++++++++--- src/testing.cl.infer.cl | 3 +- 10 files changed, 96 insertions(+), 20 deletions(-) diff --git a/src/cool_cmp/cmp/cil.py b/src/cool_cmp/cmp/cil.py index bcb63d7f4..0830dfbbb 100644 --- a/src/cool_cmp/cmp/cil.py +++ b/src/cool_cmp/cmp/cil.py @@ -63,16 +63,28 @@ class DivNode(ArithmeticNode): pass class GetAttribNode(InstructionNode): - pass + def __init__(self, source, attr, dest): + self.source = source + self.attr = attr + self.dest = dest class SetAttribNode(InstructionNode): - pass + def __init__(self, source, attr, value): + self.source = source + self.attr = attr + self.value = value class GetIndexNode(InstructionNode): - pass + def __init__(self, source, index, dest): + self.source = source + self.index = index + self.dest = dest class SetIndexNode(InstructionNode): - pass + def __init__(self, source, index, value): + self.source = source + self.index = index + self.value = value class AllocateNode(InstructionNode): def __init__(self, itype, dest): diff --git a/src/cool_cmp/cool/error/errors.py b/src/cool_cmp/cool/error/errors.py index cbcd2bc72..e925e4c93 100644 --- a/src/cool_cmp/cool/error/errors.py +++ b/src/cool_cmp/cool/error/errors.py @@ -133,6 +133,8 @@ class LexerCoolError(PositionError): Error class for lexical errors """ + FORMAT = "({}, {}) - {}: {}" + ERROR_TYPE = "LexicographicError" class SyntacticCoolError(PositionError): diff --git a/src/cool_cmp/cool/pipes/pipes.py b/src/cool_cmp/cool/pipes/pipes.py index fa77b7f41..67911b684 100644 --- a/src/cool_cmp/cool/pipes/pipes.py +++ b/src/cool_cmp/cool/pipes/pipes.py @@ -406,11 +406,14 @@ def string_escape_pipe(result:dict): string_escape_pipe = Pipe(string_escape_pipe) def cool_to_cil_pipe(result: dict, cool_to_cil=COOLToCILVisitor): - context = result.get("context",None) # TODO Ver bien que hace falta aqui + context = result.get("context",None) scope = result.get("scope", None) - ast = result.get("ast",) + ast = result.get("ast", None) if any(x == None for x in [context, ast, scope]): - return result # TODO se devuelve result pq el error ya deveria de estar seteado + return result + + if result.get("errors"): + return result errors = [] cool_to_cil_visitor = cool_to_cil(context, errors) # TODO Ver los argumentos diff --git a/src/cool_cmp/cool/semantic/operations.py b/src/cool_cmp/cool/semantic/operations.py index 7ec0da427..4993d9e69 100644 --- a/src/cool_cmp/cool/semantic/operations.py +++ b/src/cool_cmp/cool/semantic/operations.py @@ -162,6 +162,9 @@ def get_covariant(self,key): def __getitem__(self,key): operator,types = key + if operator == "=" and len(types) == 2: # Special Equal Restriction + if any(isinstance(types[0], x) for x in [BoolType, IntType, StringType] ) and types[0] != types[1]: + raise KeyError() try: return self.operations[key] except KeyError: diff --git a/src/cool_cmp/cool/visitors/cil_visitor.py b/src/cool_cmp/cool/visitors/cil_visitor.py index 46336fedc..dc2a0788e 100644 --- a/src/cool_cmp/cool/visitors/cil_visitor.py +++ b/src/cool_cmp/cool/visitors/cil_visitor.py @@ -80,6 +80,14 @@ def visit(self, node): def visit(self, node): return f'{node.dest} = VCALL {node.type} {node.method}' + @visitor.when(cil.GetAttribNode) + def visit(self, node): + return f'{node.dest} = GETATTR {node.source} {node.attr}' + + @visitor.when(cil.SetAttribNode) + def visit(self, node:cil.SetAttribNode): + return f'SETATTR {node.source} {node.attr} {node.value}' + @visitor.when(cil.ArgNode) def visit(self, node): return f'ARG {node.name}' @@ -162,6 +170,9 @@ def register_instruction(self, instruction): self.instructions.append(instruction) return instruction + def to_init_attr_function_name(self, attr_name, type_name): + return f'init_{attr_name}_at_{type_name}' + def to_function_name(self, method_name, type_name): return f'function_{method_name}_at_{type_name}' @@ -196,7 +207,10 @@ def visit(self, node, scope): instance = self.define_internal_local() result = self.define_internal_local() main_method_name = self.to_function_name('main', 'Main') - self.register_instruction(cil.AllocateNode('Main', instance)) + main_type = self.context.get_type("Main") + main_node = InstantiateNode(("Main",0,0),0,0) + instance = self.visit(main_node, main_type.class_node.scope) + # self.register_instruction(cil.AllocateNode('Main', instance)) # TODO Hacer Inicializador para los Allocate y los atributos self.register_instruction(cil.ArgNode(instance)) self.register_instruction(cil.StaticCallNode(main_method_name, result)) self.register_instruction(cil.ReturnNode(0)) @@ -225,6 +239,10 @@ def visit(self, node, scope): for attr,typex in self.current_type.all_attributes(): type_node.attributes.append(attr.name) + new_function = self.register_function(self.to_init_attr_function_name(attr.name, self.current_type.name)) + type_node.methods.append((f"${new_function.name}", new_function.name)) # Prefixed with $ to avoid collisions + self.current_function = new_function + self.visit(attr.node, attr.node.scope) for method,typex in self.current_type.all_methods(): # Register methods if typex != self.current_type: @@ -235,12 +253,14 @@ def visit(self, node, scope): new_function = next(x for x in self.dotcode if x.name == method_name) type_node.methods.append((method.name,new_function.name)) + + func_declarations = (f for f in node.features if isinstance(f, FuncDeclarationNode)) - for feature, child_scope in zip(func_declarations, scope.children): + for feature in func_declarations: self.current_function = self.register_function(self.to_function_name(feature.id,self.current_type.name)) type_node.methods.append((feature.id,self.current_function.name)) - self.visit(feature, child_scope) + self.visit(feature, feature.scope) self.current_type = None @@ -266,6 +286,14 @@ def visit(self, node, scope): self.register_instruction(cil.ReturnNode(value)) self.current_method = None + @visitor.when(AttrDeclarationNode) + def visit(self, node, scope): + self.current_function.params.append(cil.ParamNode('self')) + result = self.visit(node.expr, scope) + self.register_instruction(cil.SetAttribNode("self", node.id, result)) + self.register_instruction(cil.ReturnNode()) + return result + @visitor.when(VarDeclarationNode) def visit(self, node, scope): ############################### @@ -425,7 +453,7 @@ def visit(self, node, scope): return node.lex # Param else: value = self.define_internal_local() # Attr - self.register_instruction(cil.GetAttribNode('self',node.lex,value)) + self.register_instruction(cil.GetAttribNode('self',node.lex, value)) return value @visitor.when(InstantiateNode) @@ -437,6 +465,12 @@ def visit(self, node, scope): # Your code here!!! instance = self.define_internal_local() self.register_instruction(cil.AllocateNode(node.lex, instance)) + instance_typex = self.context.get_type(node.lex) + for attr,typex in instance_typex.all_attributes(): # Initialize Attributes + result = self.define_internal_local() + self.register_instruction(cil.ArgNode(instance)) + self.register_instruction(cil.StaticCallNode( + self.to_init_attr_function_name(attr.name, instance_typex.name), result)) return instance @visitor.when(PlusNode) diff --git a/src/cool_cmp/cool/visitors/visitors.py b/src/cool_cmp/cool/visitors/visitors.py index ff1f3148e..730c61f10 100644 --- a/src/cool_cmp/cool/visitors/visitors.py +++ b/src/cool_cmp/cool/visitors/visitors.py @@ -531,7 +531,10 @@ def visit(self, node, scope): node.expr.type = node.type # if type is void then is not assigned in the visit else the type is overridden else: return # No default expr can be assing at this moment - self.visit(node.expr,scope) + a_scope = scope.create_child() + self.visit(node.expr,a_scope) + + node.scope = a_scope if not node.expr.type.conforms_to(node.type,self.current_type): self.add_semantic_error(TypeCoolError(ATTRIBUTE_INCOMPATIBLE_TYPES, node.id, node.type.name, node.expr.type.name),node.expr.row,node.expr.column) diff --git a/src/cool_cmp/main.py b/src/cool_cmp/main.py index f2b7f1dea..935dcfcc9 100644 --- a/src/cool_cmp/main.py +++ b/src/cool_cmp/main.py @@ -61,7 +61,7 @@ def main( if out_infer: result = reconstr_pipeline(file_content, verbose=verbose) else: - result = cool_pipeline(file_content,verbose=True) + result = cool_pipeline(file_content,verbose=verbose) ast, g_errors, parse, tokens, context, scope, operator, value, reconstr, cil_text = [result.get(x, None) for x in ["ast", "errors", "text_parse", "text_tokens", "context", "scope", "operator", "value", "reconstructed_text", "cil_text"]] if reconstr and out_infer: diff --git a/src/testing.cl b/src/testing.cl index e4b79dbac..bd44a8c4a 100644 --- a/src/testing.cl +++ b/src/testing.cl @@ -1,5 +1,9 @@ class Main inherits IO { + + msg:String <- "Hello World"; + main(): IO { - out_string("Hello") + --new IO + out_string(msg) }; }; diff --git a/src/testing.cl.cil b/src/testing.cl.cil index e582855ad..1d5a58d6c 100644 --- a/src/testing.cl.cil +++ b/src/testing.cl.cil @@ -42,8 +42,9 @@ type IO { method in_int: function_in_int_at_IO } type Main { - + attribute msg + method $init_msg_at_Main: init_msg_at_Main method abort: function_abort_at_Object method type_name: function_type_name_at_Object method copy: function_copy_at_Object @@ -55,7 +56,7 @@ type Main { } .DATA -data_0 = "Hello" +data_0 = "Hello World" .CODE function entry { @@ -63,9 +64,13 @@ function entry { LOCAL local__internal_0 LOCAL local__internal_1 + LOCAL local__internal_2 + LOCAL local__internal_3 - local__internal_0 = ALLOCATE Main - ARG local__internal_0 + local__internal_2 = ALLOCATE Main + ARG local__internal_2 + local__internal_3 = CALL init_msg_at_Main + ARG local__internal_2 local__internal_1 = CALL function_main_at_Main RETURN 0 } @@ -155,13 +160,22 @@ function function_in_int_at_IO { local_in_int_at_IO_internal_0 = READ RETURN local_in_int_at_IO_internal_0 } +function init_msg_at_Main { + PARAM self + + LOCAL local_at_Main_internal_0 + + local_at_Main_internal_0 = LOAD data_0 + SETATTR self msg local_at_Main_internal_0 + RETURN +} function function_main_at_Main { PARAM self LOCAL local_main_at_Main_internal_0 LOCAL local_main_at_Main_internal_1 - local_main_at_Main_internal_0 = LOAD data_0 + local_main_at_Main_internal_0 = GETATTR self msg ARG self ARG local_main_at_Main_internal_0 local_main_at_Main_internal_1 = VCALL Main out_string diff --git a/src/testing.cl.infer.cl b/src/testing.cl.infer.cl index 50e70b4f0..9957e56b1 100644 --- a/src/testing.cl.infer.cl +++ b/src/testing.cl.infer.cl @@ -1,10 +1,11 @@ class Main inherits IO { + msg:String <- "Hello World"; main(): IO { self .out_string( - "Hello" + msg ) }; } From e7e7f026557409af5b015d8860d7670ba3870793 Mon Sep 17 00:00:00 2001 From: Luiso Date: Mon, 4 Oct 2021 20:09:04 -0400 Subject: [PATCH 042/143] Nodes added. Check TODOs in cil_visitor.py --- doc/Cool2Cil.md | 1 + src/cool_cmp/cmp/cil.py | 13 +- src/cool_cmp/cool/ast/cil_ast.py | 22 ++- src/cool_cmp/cool/visitors/cil_visitor.py | 200 +++++++++++++++++++++- src/testing.cl | 12 +- src/testing.cl.cil | 19 +- src/testing.cl.infer.cl | 14 +- 7 files changed, 261 insertions(+), 20 deletions(-) create mode 100644 doc/Cool2Cil.md diff --git a/doc/Cool2Cil.md b/doc/Cool2Cil.md new file mode 100644 index 000000000..7c9b500a2 --- /dev/null +++ b/doc/Cool2Cil.md @@ -0,0 +1 @@ +Los atributos se inicializan llamando a las funciones CIL generadas para eso. \ No newline at end of file diff --git a/src/cool_cmp/cmp/cil.py b/src/cool_cmp/cmp/cil.py index 0830dfbbb..678c0f6d3 100644 --- a/src/cool_cmp/cmp/cil.py +++ b/src/cool_cmp/cmp/cil.py @@ -22,11 +22,12 @@ def __init__(self, vname, value): self.value = value class FunctionNode(Node): - def __init__(self, fname, params, localvars, instructions): + def __init__(self, fname, params, localvars, instructions, labels): self.name = fname self.params = params self.localvars = localvars self.instructions = instructions + self.labels = labels class ParamNode(Node): def __init__(self, name): @@ -100,13 +101,17 @@ def __init__(self, obj, dest): self.dest = dest class LabelNode(InstructionNode): - pass + def __init__(self, label) -> None: + self.label = label class GotoNode(InstructionNode): - pass + def __init__(self, label) -> None: + self.label = label class GotoIfNode(InstructionNode): - pass + def __init__(self, condition_value, label) -> None: + self.condition_value = condition_value + self.label = label class StaticCallNode(InstructionNode): def __init__(self, function, dest): diff --git a/src/cool_cmp/cool/ast/cil_ast.py b/src/cool_cmp/cool/ast/cil_ast.py index 8dd8bc0e3..7b37ede4b 100644 --- a/src/cool_cmp/cool/ast/cil_ast.py +++ b/src/cool_cmp/cool/ast/cil_ast.py @@ -29,4 +29,24 @@ class IOInStringNode(InstructionNode): class IOInIntNode(InstructionNode): pass - \ No newline at end of file + +class UnaryArithmeticNode(InstructionNode): + def __init__(self, dest, value) -> None: + self.dest = dest + self.value = value + +class NotNode(UnaryArithmeticNode): + pass + +class EqualNode(ArithmeticNode): + pass + +class GreaterNode(ArithmeticNode): + pass + +class LesserNode(ArithmeticNode): + pass + +class VoidNode(InstructionNode): + def __init__(self, dest) -> None: + self.dest = dest \ No newline at end of file diff --git a/src/cool_cmp/cool/visitors/cil_visitor.py b/src/cool_cmp/cool/visitors/cil_visitor.py index dc2a0788e..dc3519806 100644 --- a/src/cool_cmp/cool/visitors/cil_visitor.py +++ b/src/cool_cmp/cool/visitors/cil_visitor.py @@ -131,7 +131,40 @@ def visit(self, node): @visitor.when(cil.LoadNode) def visit(self, node:cil.LoadNode): return f'{node.dest} = LOAD {node.msg}' - + + @visitor.when(cil.LabelNode) + def visit(self, node:cil.LabelNode): + return f'LABEL {node.label}' + + @visitor.when(cil.GotoIfNode) + def visit(self, node:cil.GotoIfNode): + return f'IF {node.condition_value} GOTO {node.label}' + + @visitor.when(cil.GotoNode) + def visit(self, node:cil.GotoNode): + return f'GOTO {node.label}' + + @visitor.when(cil.NotNode) + def visit(self, node:cil.NotNode): + return f'{node.dest} = NOT {node.value}' + + @visitor.when(cil.EqualNode) + def visit(self, node:cil.EqualNode): + return f'{node.dest} = EQUAL {node.left} {node.right}' + + @visitor.when(cil.GreaterNode) + def visit(self, node:cil.GreaterNode): + return f'{node.dest} = {node.left} > {node.right}' + + @visitor.when(cil.LesserNode) + def visit(self, node:cil.LesserNode): + return f'{node.dest} = {node.left} < {node.right}' + + @visitor.when(cil.VoidNode) + def visit(self, node:cil.VoidNode): + return f'{node.dest} = VOID' + + class COOLToCILVisitor(): def __init__(self, context:Context, errors=[]): @@ -152,6 +185,10 @@ def params(self): def localvars(self): return self.current_function.localvars + @property + def labels(self): + return self.current_function.labels + @property def instructions(self): return self.current_function.instructions @@ -166,6 +203,12 @@ def define_internal_local(self): vinfo = VariableInfo('internal', None) return self.register_local(vinfo) + def define_label(self): + name = f'{self.current_function.name[9:]}_label_{len(self.labels)}' + label_node = cil.LabelNode(name) + self.labels.append(label_node) + return label_node + def register_instruction(self, instruction): self.instructions.append(instruction) return instruction @@ -177,7 +220,7 @@ def to_function_name(self, method_name, type_name): return f'function_{method_name}_at_{type_name}' def register_function(self, function_name): - function_node = cil.FunctionNode(function_name, [], [], []) + function_node = cil.FunctionNode(function_name, [], [], [], []) self.dotcode.append(function_node) return function_node @@ -430,7 +473,160 @@ def visit(self, node, scope): self.register_instruction(cil.DynamicCallNode(node.obj.type.name,node.id,result)) return result + + @visitor.when(LetNode) + def visit(self, node, scope): + # TODO + raise NotImplementedError() + + @visitor.when(CheckNode) + def visit(self, node, scope): + # TODO + raise NotImplementedError() + + @visitor.when(CaseNode) + def visit(self, node, scope): + # TODO + raise NotImplementedError() + + @visitor.when(IsVoidNode) + def visit(self, node, scope): + # TODO + raise NotImplementedError() + + @visitor.when(ConditionalNode) + def visit(self, node:ConditionalNode, scope): + result = self.define_internal_local() + condition_value = self.visit(node.condition, scope) + then_label = self.define_label() + end_label = self.define_label() + + self.register_instruction(cil.GotoIfNode(condition_value, then_label.label)) + + else_value = self.visit(node.else_expr, scope) + self.register_instruction(cil.AssignNode(result, else_value)) + self.register_instruction(cil.GotoNode(end_label.label)) + + self.register_instruction(then_label) + then_value = self.visit(node.then_expr,scope) + self.register_instruction(cil.AssignNode(result, then_value)) + + self.register_instruction(end_label) + return result + + @visitor.when(BlockNode) + def visit(self, node:BlockNode, scope): + result = self.define_internal_local() + for expr in node.expr_list: + result = self.visit(expr, scope) + return result + + @visitor.when(WhileNode) + def visit(self, node:WhileNode, scope): + result = self.visit(VoidNode(None), scope) + start_label = self.define_label() + loop_label = self.define_label() + end_label = self.define_label() + + self.register_instruction(start_label) + condition_result = self.visit(node.condition, scope) + + self.register_instruction(cil.GotoIfNode(condition_result, loop_label.label)) + self.register_instruction(cil.GotoNode(end_label.label)) + + self.register_instruction(loop_label) + self.visit(node.expr, scope) + + self.register_instruction(cil.GotoNode(start_label.label)) + self.register_instruction(end_label) + + return result + + @visitor.when(NotNode) + def visit(self, node, scope): + result = self.define_internal_local() + value = self.visit(node.member, scope) + self.register_instruction(cil.NotNode(result, value)) + return result + + @visitor.when(RoofNode) + def visit(self, node:RoofNode, scope): + result = self.define_internal_local() + value = self.visit(node.member, scope) + self.register_instruction(cil.MinusNode(result, "0", value)) + return result + + @visitor.when(EqualNode) + def visit(self, node:EqualNode, scope): + result = self.define_internal_local() + + value1 = self.visit(node.left, scope) + value2 = self.visit(node.right, scope) + + self.register_instruction(cil.EqualNode(result, value1, value2)) + + return result + + @visitor.when(GreaterNode) + def visit(self, node:GreaterNode, scope): + result = self.define_internal_local() + + value1 = self.visit(node.left, scope) + value2 = self.visit(node.right, scope) + + self.register_instruction(cil.GreaterNode(result, value1, value2)) + + return result + + @visitor.when(GreaterEqualNode) + def visit(self, node, scope): + result = self.define_internal_local() + + value1 = self.visit(node.left, scope) + value2 = self.visit(node.right, scope) + + self.register_instruction(cil.LesserNode(result, value1, value2)) + self.register_instruction(cil.NotNode(result, result)) + + return result + + @visitor.when(LesserNode) + def visit(self, node, scope): + result = self.define_internal_local() + + value1 = self.visit(node.left, scope) + value2 = self.visit(node.right, scope) + + self.register_instruction(cil.LesserNode(result, value1, value2)) + + return result + + @visitor.when(LesserEqualNode) + def visit(self, node, scope): + result = self.define_internal_local() + + value1 = self.visit(node.left, scope) + value2 = self.visit(node.right, scope) + + self.register_instruction(cil.GreaterNode(result, value1, value2)) + self.register_instruction(cil.NotNode(result, result)) + + return result + + @visitor.when(VoidNode) + def visit(self, node, scope): + result = self.define_internal_local() + self.register_instruction(cil.VoidNode(result)) + return result + + @visitor.when(BoolNode) + def visit(self, node, scope): + ############################### + # node.lex -> str + ############################### + return "0" if node.lex.lower() == "false" else "1" + @visitor.when(ConstantNumNode) def visit(self, node, scope): ############################### diff --git a/src/testing.cl b/src/testing.cl index bd44a8c4a..fc35a8102 100644 --- a/src/testing.cl +++ b/src/testing.cl @@ -2,8 +2,16 @@ class Main inherits IO { msg:String <- "Hello World"; - main(): IO { + main(): AUTO_TYPE { --new IO - out_string(msg) + --out_string(msg) + --if not 5 + 10 = 15 then 0 else 1 fi + --if not 5 + 10 < 15 then 0 else 1 fi + while false loop + { + 3+2; + 3-2; + } + pool }; }; diff --git a/src/testing.cl.cil b/src/testing.cl.cil index 1d5a58d6c..f76817ab5 100644 --- a/src/testing.cl.cil +++ b/src/testing.cl.cil @@ -174,10 +174,17 @@ function function_main_at_Main { LOCAL local_main_at_Main_internal_0 LOCAL local_main_at_Main_internal_1 - - local_main_at_Main_internal_0 = GETATTR self msg - ARG self - ARG local_main_at_Main_internal_0 - local_main_at_Main_internal_1 = VCALL Main out_string - RETURN local_main_at_Main_internal_1 + LOCAL local_main_at_Main_internal_2 + LOCAL local_main_at_Main_internal_3 + + local_main_at_Main_internal_0 = VOID + LABEL main_at_Main_label_0 + IF 0 GOTO main_at_Main_label_1 + GOTO main_at_Main_label_2 + LABEL main_at_Main_label_1 + local_main_at_Main_internal_2 = 3 + 2 + local_main_at_Main_internal_3 = 3 - 2 + GOTO main_at_Main_label_0 + LABEL main_at_Main_label_2 + RETURN local_main_at_Main_internal_0 } \ No newline at end of file diff --git a/src/testing.cl.infer.cl b/src/testing.cl.infer.cl index 9957e56b1..60039d8c3 100644 --- a/src/testing.cl.infer.cl +++ b/src/testing.cl.infer.cl @@ -1,12 +1,16 @@ class Main inherits IO { msg:String <- "Hello World"; - main(): IO + main(): Object { - self - .out_string( - msg - ) + while + false + loop + { + (3 + 2); + (3 - 2); + } + pool }; } From 9cccd07f0d470424b8a01d88c93c9205f318c2a5 Mon Sep 17 00:00:00 2001 From: Luiso Date: Fri, 8 Oct 2021 20:08:51 -0400 Subject: [PATCH 043/143] Let In CIL - @ calls - Bug naming initialization functions missing $ in generated CIL. --- src/cool_cmp/cool/visitors/cil_visitor.py | 53 ++++++++++------ src/cool_cmp/cool/visitors/visitors.py | 1 + src/testing.cl | 32 +++++++--- src/testing.cl.cil | 74 ++++++++++++++++------- src/testing.cl.infer.cl | 40 +++++++++--- 5 files changed, 143 insertions(+), 57 deletions(-) diff --git a/src/cool_cmp/cool/visitors/cil_visitor.py b/src/cool_cmp/cool/visitors/cil_visitor.py index dc3519806..4a868cd97 100644 --- a/src/cool_cmp/cool/visitors/cil_visitor.py +++ b/src/cool_cmp/cool/visitors/cil_visitor.py @@ -194,10 +194,10 @@ def instructions(self): return self.current_function.instructions def register_local(self, vinfo): - vinfo.name = f'local_{self.current_function.name[9:]}_{vinfo.name}_{len(self.localvars)}' - local_node = cil.LocalNode(vinfo.name) + vinfo.cil_name = f'local_{self.current_function.name[9:]}_{vinfo.name}_{len(self.localvars)}' + local_node = cil.LocalNode(vinfo.cil_name) self.localvars.append(local_node) - return vinfo.name + return vinfo.cil_name def define_internal_local(self): vinfo = VariableInfo('internal', None) @@ -214,7 +214,7 @@ def register_instruction(self, instruction): return instruction def to_init_attr_function_name(self, attr_name, type_name): - return f'init_{attr_name}_at_{type_name}' + return f'$init_{attr_name}_at_{type_name}' # Prefixed with $ to avoid collisions def to_function_name(self, method_name, type_name): return f'function_{method_name}_at_{type_name}' @@ -283,7 +283,7 @@ def visit(self, node, scope): for attr,typex in self.current_type.all_attributes(): type_node.attributes.append(attr.name) new_function = self.register_function(self.to_init_attr_function_name(attr.name, self.current_type.name)) - type_node.methods.append((f"${new_function.name}", new_function.name)) # Prefixed with $ to avoid collisions + type_node.methods.append((new_function.name, new_function.name)) self.current_function = new_function self.visit(attr.node, attr.node.scope) @@ -347,7 +347,6 @@ def visit(self, node, scope): # Your code here!!! local = scope.find_variable(node.id) cil_local = self.register_local(local) - local.cil = cil_local value = self.visit(node.expr,scope) self.register_instruction(cil.AssignNode(cil_local,value)) return cil_local @@ -435,9 +434,9 @@ def visit(self, node, scope): ############################### local = scope.find_variable(node.id) value = self.visit(node.expr,scope) - if hasattr(local,'cil'): - self.register_instruction(cil.AssignNode(local.cil,value)) - return local.cil + if hasattr(local,'cil_name'): + self.register_instruction(cil.AssignNode(local.cil_name,value)) + return local.cil_name else: if any(x for x in self.current_function.params if x.name == local.name): self.register_instruction(cil.AssignNode(local.name,value)) # Param @@ -449,7 +448,7 @@ def visit(self, node, scope): return value # or self ? @visitor.when(CallNode) - def visit(self, node, scope): + def visit(self, node:CallNode, scope): ############################### # node.obj -> AtomicNode # node.id -> str @@ -459,7 +458,11 @@ def visit(self, node, scope): obj_value = self.visit(node.obj,scope) args = [] - method = node.obj.type.get_method(node.id, len(node.args)) + if node.at: + method = node.at.get_method(node.id, len(node.args)) + else: + method = node.obj.type.get_method(node.id, len(node.args)) + for arg_node in node.args: value = self.visit(arg_node,scope) args.append(value) @@ -470,14 +473,22 @@ def visit(self, node, scope): for arg,value in zip(method.param_names,args): self.register_instruction(cil.ArgNode(value)) - self.register_instruction(cil.DynamicCallNode(node.obj.type.name,node.id,result)) + if node.at: + self.register_instruction(cil.DynamicCallNode(node.at.name, node.id, result)) + else: + self.register_instruction(cil.DynamicCallNode(node.obj.type.name,node.id,result)) return result @visitor.when(LetNode) - def visit(self, node, scope): - # TODO - raise NotImplementedError() + def visit(self, node:LetNode, scope): + current_scope = node.scope + for param in node.params: + current_scope = current_scope.children[0] + self.visit(param, current_scope) + current_scope = current_scope.children[-1] + result = self.visit(node.expr, current_scope) + return result @visitor.when(CheckNode) def visit(self, node, scope): @@ -490,9 +501,13 @@ def visit(self, node, scope): raise NotImplementedError() @visitor.when(IsVoidNode) - def visit(self, node, scope): - # TODO - raise NotImplementedError() + def visit(self, node:IsVoidNode, scope): + result = self.define_internal_local() + value = self.visit(node.member, scope) + void_value = self.define_internal_local() + self.register_instruction(cil.VoidNode(void_value)) + self.register_instruction(cil.EqualNode(result, value, void_value)) + return result @visitor.when(ConditionalNode) def visit(self, node:ConditionalNode, scope): @@ -643,7 +658,7 @@ def visit(self, node, scope): # Your code here!!! try: - return scope.find_variable(node.lex).cil # is a Local Variable + return scope.find_variable(node.lex).cil_name # is a Local Variable except AttributeError: if any(x for x in self.current_function.params if x.name == node.lex): return node.lex # Param diff --git a/src/cool_cmp/cool/visitors/visitors.py b/src/cool_cmp/cool/visitors/visitors.py index 730c61f10..68d484f8d 100644 --- a/src/cool_cmp/cool/visitors/visitors.py +++ b/src/cool_cmp/cool/visitors/visitors.py @@ -697,6 +697,7 @@ def visit(self, node:ConditionalNode, scope): @visitor.when(LetNode) def visit(self, node: LetNode, scope): let_scope = scope.create_child() + node.scope = let_scope curr_scope = let_scope for var_node in node.params: attr_scope = curr_scope.create_child() diff --git a/src/testing.cl b/src/testing.cl index fc35a8102..4035b8fe7 100644 --- a/src/testing.cl +++ b/src/testing.cl @@ -1,17 +1,35 @@ -class Main inherits IO { +class A inherits IO { + p(): Int { + 0 + }; +}; + +class Main inherits A { msg:String <- "Hello World"; + p(): Int { + 1 + }; + main(): AUTO_TYPE { --new IO --out_string(msg) --if not 5 + 10 = 15 then 0 else 1 fi --if not 5 + 10 < 15 then 0 else 1 fi - while false loop - { - 3+2; - 3-2; - } - pool + --while false loop + --{ + -- 3+2; + -- 3-2; + --} + --pool + --self@A.p() + let x0: Int <- 0, + x1: Int <- 1, + x2: Int <- + let x1: Int <- 2, + x2: Int <- x1 + 1 + in x0 + x1 + x2 + in x1 + x2 }; }; diff --git a/src/testing.cl.cil b/src/testing.cl.cil index f76817ab5..6b87457e5 100644 --- a/src/testing.cl.cil +++ b/src/testing.cl.cil @@ -41,10 +41,22 @@ type IO { method in_string: function_in_string_at_IO method in_int: function_in_int_at_IO } +type A { + + + method abort: function_abort_at_Object + method type_name: function_type_name_at_Object + method copy: function_copy_at_Object + method out_string: function_out_string_at_IO + method out_int: function_out_int_at_IO + method in_string: function_in_string_at_IO + method in_int: function_in_int_at_IO + method p: function_p_at_A +} type Main { attribute msg - method $init_msg_at_Main: init_msg_at_Main + method $init_msg_at_Main: $init_msg_at_Main method abort: function_abort_at_Object method type_name: function_type_name_at_Object method copy: function_copy_at_Object @@ -52,6 +64,7 @@ type Main { method out_int: function_out_int_at_IO method in_string: function_in_string_at_IO method in_int: function_in_int_at_IO + method p: function_p_at_Main method main: function_main_at_Main } @@ -69,7 +82,7 @@ function entry { local__internal_2 = ALLOCATE Main ARG local__internal_2 - local__internal_3 = CALL init_msg_at_Main + local__internal_3 = CALL $init_msg_at_Main ARG local__internal_2 local__internal_1 = CALL function_main_at_Main RETURN 0 @@ -160,31 +173,50 @@ function function_in_int_at_IO { local_in_int_at_IO_internal_0 = READ RETURN local_in_int_at_IO_internal_0 } -function init_msg_at_Main { +function function_p_at_A { PARAM self - LOCAL local_at_Main_internal_0 + - local_at_Main_internal_0 = LOAD data_0 - SETATTR self msg local_at_Main_internal_0 + RETURN 0 +} +function $init_msg_at_Main { + PARAM self + + LOCAL local__at_Main_internal_0 + + local__at_Main_internal_0 = LOAD data_0 + SETATTR self msg local__at_Main_internal_0 RETURN } +function function_p_at_Main { + PARAM self + + + + RETURN 1 +} function function_main_at_Main { PARAM self - LOCAL local_main_at_Main_internal_0 - LOCAL local_main_at_Main_internal_1 - LOCAL local_main_at_Main_internal_2 - LOCAL local_main_at_Main_internal_3 - - local_main_at_Main_internal_0 = VOID - LABEL main_at_Main_label_0 - IF 0 GOTO main_at_Main_label_1 - GOTO main_at_Main_label_2 - LABEL main_at_Main_label_1 - local_main_at_Main_internal_2 = 3 + 2 - local_main_at_Main_internal_3 = 3 - 2 - GOTO main_at_Main_label_0 - LABEL main_at_Main_label_2 - RETURN local_main_at_Main_internal_0 + LOCAL local_main_at_Main_x0_0 + LOCAL local_main_at_Main_x1_1 + LOCAL local_main_at_Main_x2_2 + LOCAL local_main_at_Main_x1_3 + LOCAL local_main_at_Main_x2_4 + LOCAL local_main_at_Main_internal_5 + LOCAL local_main_at_Main_internal_6 + LOCAL local_main_at_Main_internal_7 + LOCAL local_main_at_Main_internal_8 + + local_main_at_Main_x0_0 = 0 + local_main_at_Main_x1_1 = 1 + local_main_at_Main_x1_3 = 2 + local_main_at_Main_internal_5 = local_main_at_Main_x1_3 + 1 + local_main_at_Main_x2_4 = local_main_at_Main_internal_5 + local_main_at_Main_internal_6 = local_main_at_Main_x0_0 + local_main_at_Main_x1_3 + local_main_at_Main_internal_7 = local_main_at_Main_internal_6 + local_main_at_Main_x2_4 + local_main_at_Main_x2_2 = local_main_at_Main_internal_7 + local_main_at_Main_internal_8 = local_main_at_Main_x1_1 + local_main_at_Main_x2_2 + RETURN local_main_at_Main_internal_8 } \ No newline at end of file diff --git a/src/testing.cl.infer.cl b/src/testing.cl.infer.cl index 60039d8c3..85a5e0173 100644 --- a/src/testing.cl.infer.cl +++ b/src/testing.cl.infer.cl @@ -1,16 +1,36 @@ -class Main inherits IO +class A inherits IO +{ + p(): Int + { + 0 + }; +} + + +class Main inherits A { msg:String <- "Hello World"; - main(): Object + p(): Int + { + 1 + }; + main(): Int { - while - false - loop - { - (3 + 2); - (3 - 2); - } - pool + let + x0:Int <- + 0, + x1:Int <- + 1, + x2:Int <- + let + x1:Int <- + 2, + x2:Int <- + (x1 + 1) + in + ((x0 + x1) + x2) + in + (x1 + x2) }; } From 3dd79f31997f887fa29bfe6f85d2414c52db0ba6 Mon Sep 17 00:00:00 2001 From: Luiso Date: Sat, 9 Oct 2021 12:07:15 -0400 Subject: [PATCH 044/143] CIL Interpreter Main program args updated --- .vscode/launch.json | 2 +- src/cool_cmp/cool/pipeline.py | 20 +- src/cool_cmp/cool/pipes/pipes.py | 21 +- src/cool_cmp/cool/visitors/cil_visitor.py | 349 +++++++++++++++++++++- src/cool_cmp/main.py | 19 +- src/cool_cmp/test/test_class.py | 6 +- src/testing.cl | 55 ++-- src/testing.cl.cil | 183 ++++++++---- 8 files changed, 556 insertions(+), 99 deletions(-) diff --git a/.vscode/launch.json b/.vscode/launch.json index 055ec7838..27040bd3f 100644 --- a/.vscode/launch.json +++ b/.vscode/launch.json @@ -9,7 +9,7 @@ "type": "python", "request": "launch", "program": "src/cool_cmp/main.py", - "args": ["src/testing.cl", "src/testing.mips", "-i"], + "args": ["src/testing.cl", "src/testing.mips", "-icil", "-c"], "console": "integratedTerminal" } ] diff --git a/src/cool_cmp/cool/pipeline.py b/src/cool_cmp/cool/pipeline.py index 7145d9f48..1b06f9abe 100644 --- a/src/cool_cmp/cool/pipeline.py +++ b/src/cool_cmp/cool/pipeline.py @@ -1,4 +1,4 @@ -from cool.pipes.pipes import cil_ast_to_text_pipe, cool_to_cil_pipe, start_pipe, change_escaped_lines, remove_comments_pipe,\ +from cool.pipes.pipes import cil_ast_to_text_pipe, cool_to_cil_pipe, run_cil_pipe, start_pipe, change_escaped_lines, remove_comments_pipe,\ parse_text_pipe, ast_pipe, type_collector_pipe, build_types_pipe, \ check_types_pipe, run_program_pipe, reconstruct_pipe, void_as_type_pipe, \ auto_resolver_pipe, string_escape_pipe, tokenize_text_pipe, ply_lexer_pipe, remove_comment_tokens_pipe @@ -76,12 +76,16 @@ def add_std_pipe(result:dict): cool_to_cil_pipe, cil_ast_to_text_pipe) -interprete_cool_pipeline = Pipeline(base_cool_pipeline, - execution_pipe) +generate_cil_pipeline = Pipeline(base_cool_pipeline, + cool_to_cil_pipe, + cil_ast_to_text_pipe) + +interprete_cil_pipeline = Pipeline(generate_cil_pipeline, + run_cil_pipe) -reconstr_pipeline = Pipeline(base_cool_pipeline, - reconstruct_pipe, - cool_to_cil_pipe, - cil_ast_to_text_pipe, - execution_pipe) +generate_cool_pipeline = Pipeline(base_cool_pipeline, + reconstruct_pipe) + +interprete_cool_pipeline = Pipeline(generate_cool_pipeline, + execution_pipe) diff --git a/src/cool_cmp/cool/pipes/pipes.py b/src/cool_cmp/cool/pipes/pipes.py index 67911b684..724875867 100644 --- a/src/cool_cmp/cool/pipes/pipes.py +++ b/src/cool_cmp/cool/pipes/pipes.py @@ -11,7 +11,7 @@ from cool.semantic.scope import Scope from cool.pipes.utils import pprint_tokens, print_errors from cool.pipes.pipeline import Pipe -from cool.visitors.cil_visitor import CILPrintVisitor, COOLToCILVisitor +from cool.visitors.cil_visitor import CILPrintVisitor, CILRunnerVisitor, COOLToCILVisitor ply_lexer = PlyLexer() @@ -445,4 +445,21 @@ def cil_ast_to_text_pipe(result: dict, formatter=CILPrintVisitor): result['cil_text'] = cil_text return result -cil_ast_to_text_pipe = Pipe(cil_ast_to_text_pipe) \ No newline at end of file +cil_ast_to_text_pipe = Pipe(cil_ast_to_text_pipe) + +def run_cil_pipe(result: dict, runner= CILRunnerVisitor): + ast = result.get("cil_ast",None) + if ast is None: + return result + + runner = runner() + value = runner.visit(ast) + result["errors"].extend(runner.errors) + if result.get("verbose", False): + print("============== CIL Result ===============") + print(value) + print_errors("============ CIL Run Error =============", runner.errors) + result['cil_value'] = value + return result + +run_cil_pipe = Pipe(run_cil_pipe) \ No newline at end of file diff --git a/src/cool_cmp/cool/visitors/cil_visitor.py b/src/cool_cmp/cool/visitors/cil_visitor.py index 4a868cd97..51bb8d22e 100644 --- a/src/cool_cmp/cool/visitors/cil_visitor.py +++ b/src/cool_cmp/cool/visitors/cil_visitor.py @@ -3,6 +3,7 @@ import cmp.visitor as visitor from cmp.semantic import VariableInfo from cool.semantic.context import Context +from cool.error.errors import RunError, ZERO_DIVISION class CILPrintVisitor(): @visitor.on('node') @@ -164,7 +165,353 @@ def visit(self, node:cil.LesserNode): def visit(self, node:cil.VoidNode): return f'{node.dest} = VOID' +class CILRunnerVisitor(): + def __init__(self) -> None: + self.data = {} + self.types = {} + self.function = {} + self.errors = [] + + def jump_to(self, label): + return ("jump",label) + + def return_value(self, value): + return ("return", value) + + def next_instruction(self): + return ("next", None) + + def raise_error(self, message, *args): + raise RunError(message, *args) + + def get_value(self, source, function_scope): + try: + value = int(source) + except ValueError: + if source not in function_scope: + self.raise_error("Variable {0} doesn't exist", source) + value = function_scope[source] + return value + + def set_value(self, dest, value, function_scope): + if dest not in function_scope: + self.raise_error("Variable {0} isn't defined", dest) + function_scope[dest] = value + + def get_value_str(self, source, function_scope, error): + value = self.get_value(source, function_scope) + if not isinstance(value, str): + self.raise_error(error) + return value + + def get_value_int(self, source, function_scope, error): + value = self.get_value(source, function_scope) + if not isinstance(value, int): + self.raise_error(error) + return value + + + def get_type(self, type_name) -> cil.TypeNode: + try: + return self.types[type_name] + except KeyError: + self.raise_error("Type {0} isn't defined", type_name) + + def binary_node(self, node, function_scope, func): + left = self.get_value_int(node.left, function_scope, "OPERATION not defined with non Int argument") + right = self.get_value_int(node.right, function_scope, "OPERATION not defined with non Int argument") + value = func(left, right) + self.set_value(node.dest, value, function_scope) + return self.next_instruction() + + @visitor.on('node') + def visit(self, node): + pass + + @visitor.when(cil.ProgramNode) + def visit(self, node:cil.ProgramNode): + for t in node.dottypes: + self.visit(t) + for t in node.dotdata: + self.visit(t) + for t in node.dotcode: + self.visit(t) + try: + return self.visit(cil.StaticCallNode("entry", "result"), [], {"result":None}) + except RunError as er: + self.errors.append(er) + return None + + @visitor.when(cil.DataNode) + def visit(self, node:cil.DataNode): + if node.name in self.data: + self.raise_error("Data {0} already defined") + self.data[node.name] = node.value + + @visitor.when(cil.TypeNode) + def visit(self, node:cil.TypeNode): + if node.name in self.types: + self.raise_error("Type {0} already defined", node.name) + self.types[node.name] = node + + @visitor.when(cil.FunctionNode) + def visit(self, node): + self.function[node.name] = node + + @visitor.when(cil.StaticCallNode) + def visit(self, node:cil.StaticCallNode, args: list, caller_fun_scope: dict): + try: + func_node = self.function[node.function] + except KeyError: + self.raise_error("Function {0} doesn't exist", node.function) + + fun_scope = {} + label_dict = {} + current_args = [] + if len(args) != len(func_node.params): + self.raise_error("Argument amount {0} doesn't match with params amount {1} at {2}", len(args), len(func_node.params), node.function) + + for p in func_node.params: + self.visit(p, args, fun_scope) + + for i,label_node in [(i,x) for i,x in enumerate(func_node.instructions) if isinstance(x, cil.LabelNode)]: + if label_node.label in label_dict: + self.raise_error("Repeated label {0} at {1}", label_node.name, node.function) + label_dict[label_node.label] = i + + for local in func_node.localvars: + self.visit(local, current_args, fun_scope) + + i = 0 + while i < len(func_node.instructions): + instr = func_node.instructions[i] + action,action_info = self.visit(instr, current_args, fun_scope) + i+=1 + if action == "jump": + try: + i = label_dict[action_info] + except KeyError: + self.raise_error("Missing label {0} at function {1}", action_info, node.function) + if action == "return": + if isinstance(instr, cil.ReturnNode): + self.set_value(node.dest, action_info, caller_fun_scope) + return self.return_value(action_info) + else: # A Function call + self.set_value(instr.dest, action_info, fun_scope) + self.raise_error("Missing return at {0}", node.function) + + @visitor.when(cil.DynamicCallNode) + def visit(self, node, args: list, caller_fun_scope: dict): + typex = self.get_type(node.type) + try: + func_name = next(static_name for name, static_name in typex.methods if name == node.method) + except StopIteration: + self.raise_error("Type {0} doesn't contains method {1}", node.type, node.method) + return self.visit(cil.StaticCallNode(func_name, node.dest), args, caller_fun_scope) + + @visitor.when(cil.ParamNode) + def visit(self, node:cil.ParamNode, args: list, caller_fun_scope: dict): + if node.name in caller_fun_scope: + self.raise_error("Parameter {0} already defined", node.name) + value = args.pop(0) # Removes argument from caller's list + caller_fun_scope[node.name] = value + return self.next_instruction() + + @visitor.when(cil.LocalNode) + def visit(self, node:cil.LocalNode, args: list, caller_fun_scope: dict): + if node.name in caller_fun_scope: + self.raise_error("Variable {0} already defined", node.name) + caller_fun_scope[node.name] = None + return self.next_instruction() + + @visitor.when(cil.AssignNode) + def visit(self, node:cil.AssignNode, args: list, caller_fun_scope: dict): + value = self.get_value(node.source, caller_fun_scope) + self.set_value(node.dest, value, caller_fun_scope) + return self.next_instruction() + + @visitor.when(cil.PlusNode) + def visit(self, node, args: list, caller_fun_scope: dict): + return self.binary_node(node, caller_fun_scope, lambda x,y: x+y) + + @visitor.when(cil.MinusNode) + def visit(self, node, args: list, caller_fun_scope: dict): + return self.binary_node(node, caller_fun_scope, lambda x,y: x-y) + + @visitor.when(cil.StarNode) + def visit(self, node, args: list, caller_fun_scope: dict): + return self.binary_node(node, caller_fun_scope, lambda x,y: x*y) + + @visitor.when(cil.DivNode) + def visit(self, node, args: list, caller_fun_scope: dict): + try: + return self.binary_node(node, caller_fun_scope, lambda x,y: x/y) + except ZeroDivisionError: + self.raise_error(ZERO_DIVISION) + + @visitor.when(cil.AllocateNode) + def visit(self, node, args: list, caller_fun_scope: dict): + typex = self.get_type(node.type) + value = { + "$type": typex, + } + for attr in typex.attributes: + value[attr] = None + + self.set_value(node.dest, value, caller_fun_scope) + return self.next_instruction() + + @visitor.when(cil.TypeOfNode) + def visit(self, node, args: list, caller_fun_scope: dict): + value = self.get_value(node.obj) + if isinstance(value, int): + self.set_value(node.dest, "Int", caller_fun_scope) + elif isinstance(value, str): + self.set_value(node.dest, "String", caller_fun_scope) + else: + self.set_value(node.dest, value["$type"].name, caller_fun_scope) + return self.next_instruction() + + @visitor.when(cil.GetAttribNode) + def visit(self, node, args: list, caller_fun_scope: dict): + obj_value = self.get_value(node.source, caller_fun_scope) + try: + value = obj_value[node.attr] + except KeyError: + self.raise_error("Attribute {0} not found at object {1}", node.attr, node.source) + self.set_value(node.dest, value, caller_fun_scope) + return self.next_instruction() + + @visitor.when(cil.SetAttribNode) + def visit(self, node:cil.SetAttribNode, args: list, caller_fun_scope: dict): + obj_value = self.get_value(node.source, caller_fun_scope) + if node.attr not in obj_value: + self.raise_error("Attribute {0} not found at object {1}", node.attr, node.source) + obj_value[node.attr] = self.get_value(node.value, caller_fun_scope) + return self.next_instruction() + + @visitor.when(cil.ArgNode) + def visit(self, node, args: list, caller_fun_scope: dict): + value = self.get_value(node.name, caller_fun_scope) + args.append(value) + return self.next_instruction() + + @visitor.when(cil.ReturnNode) + def visit(self, node, args: list, caller_fun_scope: dict): + if node.value is not None: + value = self.get_value(node.value, caller_fun_scope) + else: + value = None + return self.return_value(value) + + @visitor.when(cil.AbortNode) + def visit(self, node, args: list, caller_fun_scope: dict): + self.raise_error("Execution aborted") + + @visitor.when(cil.CopyNode) + def visit(self, node, args: list, caller_fun_scope: dict): + value = self.get_value(node.instance, caller_fun_scope) + value = value.copy() + self.set_value(node.result, value, caller_fun_scope) + return self.next_instruction() + + @visitor.when(cil.LengthNode) + def visit(self, node, args: list, caller_fun_scope: dict): + value = self.get_value_str(node.string, caller_fun_scope, "LENGTH operation undefined with non String type") + self.set_value(node.dest, len(value), caller_fun_scope) + return self.next_instruction() + + @visitor.when(cil.ConcatNode) + def visit(self, node, args: list, caller_fun_scope: dict): + error = "CONCAT operation undefined with non String type" + value1 = self.get_value_str(node.string1, caller_fun_scope, error) + value2 = self.get_value_str(node.string2, caller_fun_scope, error) + self.set_value(node.dest, value1 + value2, caller_fun_scope) + return self.next_instruction() + + @visitor.when(cil.SubstringNode) + def visit(self, node, args: list, caller_fun_scope: dict): + error1 = "SUBSTRING operation undefined with non String type" + error2 = "SUBSTRING operation undefined with non Int index" + error3 = "SUBSTRING operation undefined with non Int length" + value1 = self.get_value_str(node.string, caller_fun_scope, error1) + value2 = self.get_value_str(node.index, caller_fun_scope, error2) + value3 = self.get_value_str(node.length, caller_fun_scope, error3) + self.set_value(node.dest, value1[value2:value2+value3], caller_fun_scope) + return self.next_instruction() + + @visitor.when(cil.PrintNode) + def visit(self, node, args: list, caller_fun_scope: dict): + value = self.get_value_str(node.str_addr, caller_fun_scope, "PRINT operation undefined with non String type") + print(value, end="") + return self.next_instruction() + + @visitor.when(cil.ToStrNode) + def visit(self, node, args: list, caller_fun_scope: dict): + value = self.get_value_int(node.ivalue, caller_fun_scope, "TOSTR operation undefined with non Int type") + value = str(value) + self.set_value(node.dest, value, caller_fun_scope) + return self.next_instruction() + + @visitor.when(cil.ReadNode) + def visit(self, node, args: list, caller_fun_scope: dict): + value = input() + self.set_value(node.dest, value, caller_fun_scope) + return self.next_instruction() + + @visitor.when(cil.LoadNode) + def visit(self, node:cil.LoadNode, args: list, caller_fun_scope: dict): + try: + value = self.data[node.msg] + except KeyError: + self.raise_error("Data {0} isn't defined", node.msg) + self.set_value(node.dest, value, caller_fun_scope) + return self.next_instruction() + + @visitor.when(cil.LabelNode) + def visit(self, node:cil.LabelNode, args: list, caller_fun_scope: dict): + return self.next_instruction() + + @visitor.when(cil.GotoIfNode) + def visit(self, node:cil.GotoIfNode, args: list, caller_fun_scope: dict): + value = self.get_value(node.condition_value, caller_fun_scope) + jump = True + if isinstance(value, int): + jump = value != 0 + if value is None: + jump = False + if jump: + return self.jump_to(node.label) + return self.next_instruction() + + @visitor.when(cil.GotoNode) + def visit(self, node:cil.GotoNode, args: list, caller_fun_scope: dict): + return self.jump_to(node.label) + + @visitor.when(cil.NotNode) + def visit(self, node:cil.NotNode, args: list, caller_fun_scope: dict): + value = self.get_value(node.value, caller_fun_scope) + self.set_value(node.dest, not bool(value), caller_fun_scope) + return self.next_instruction() + + @visitor.when(cil.EqualNode) + def visit(self, node:cil.EqualNode, args: list, caller_fun_scope: dict): + return self.binary_node(node, caller_fun_scope, lambda x,y: x==y) + + @visitor.when(cil.GreaterNode) + def visit(self, node:cil.GreaterNode, args: list, caller_fun_scope: dict): + return self.binary_node(node, caller_fun_scope, lambda x,y: x>y) + + @visitor.when(cil.LesserNode) + def visit(self, node:cil.LesserNode, args: list, caller_fun_scope: dict): + return self.binary_node(node, caller_fun_scope, lambda x,y: x 2 + local_recursive_fibonacci_at_Main_internal_1 = NOT local_recursive_fibonacci_at_Main_internal_1 + IF local_recursive_fibonacci_at_Main_internal_1 GOTO recursive_fibonacci_at_Main_label_0 + local_recursive_fibonacci_at_Main_internal_2 = n - 1 + ARG self + ARG local_recursive_fibonacci_at_Main_internal_2 + local_recursive_fibonacci_at_Main_internal_3 = VCALL Main recursive_fibonacci + local_recursive_fibonacci_at_Main_internal_4 = n - 2 + ARG self + ARG local_recursive_fibonacci_at_Main_internal_4 + local_recursive_fibonacci_at_Main_internal_5 = VCALL Main recursive_fibonacci + local_recursive_fibonacci_at_Main_internal_6 = local_recursive_fibonacci_at_Main_internal_3 + local_recursive_fibonacci_at_Main_internal_5 + local_recursive_fibonacci_at_Main_internal_0 = local_recursive_fibonacci_at_Main_internal_6 + GOTO recursive_fibonacci_at_Main_label_1 + LABEL recursive_fibonacci_at_Main_label_0 + local_recursive_fibonacci_at_Main_internal_0 = 1 + LABEL recursive_fibonacci_at_Main_label_1 + RETURN local_recursive_fibonacci_at_Main_internal_0 +} +function function_iterative_fibonacci_at_Main { + PARAM self + PARAM n + + LOCAL local_iterative_fibonacci_at_Main_i_0 + LOCAL local_iterative_fibonacci_at_Main_n1_1 + LOCAL local_iterative_fibonacci_at_Main_n2_2 + LOCAL local_iterative_fibonacci_at_Main_temp_3 + LOCAL local_iterative_fibonacci_at_Main_internal_4 + LOCAL local_iterative_fibonacci_at_Main_internal_5 + LOCAL local_iterative_fibonacci_at_Main_internal_6 + LOCAL local_iterative_fibonacci_at_Main_temp_7 + LOCAL local_iterative_fibonacci_at_Main_internal_8 + LOCAL local_iterative_fibonacci_at_Main_internal_9 + LOCAL local_iterative_fibonacci_at_Main_internal_10 + + local_iterative_fibonacci_at_Main_i_0 = 2 + local_iterative_fibonacci_at_Main_n1_1 = 1 + local_iterative_fibonacci_at_Main_n2_2 = 1 + local_iterative_fibonacci_at_Main_temp_3 = 0 + local_iterative_fibonacci_at_Main_internal_5 = VOID + LABEL iterative_fibonacci_at_Main_label_0 + local_iterative_fibonacci_at_Main_internal_6 = local_iterative_fibonacci_at_Main_i_0 < n + IF local_iterative_fibonacci_at_Main_internal_6 GOTO iterative_fibonacci_at_Main_label_1 + GOTO iterative_fibonacci_at_Main_label_2 + LABEL iterative_fibonacci_at_Main_label_1 + local_iterative_fibonacci_at_Main_temp_7 = local_iterative_fibonacci_at_Main_n2_2 + local_iterative_fibonacci_at_Main_internal_9 = local_iterative_fibonacci_at_Main_n2_2 + local_iterative_fibonacci_at_Main_n1_1 + local_iterative_fibonacci_at_Main_n2_2 = local_iterative_fibonacci_at_Main_internal_9 + local_iterative_fibonacci_at_Main_n1_1 = local_iterative_fibonacci_at_Main_temp_7 + local_iterative_fibonacci_at_Main_internal_10 = local_iterative_fibonacci_at_Main_i_0 + 1 + local_iterative_fibonacci_at_Main_i_0 = local_iterative_fibonacci_at_Main_internal_10 + GOTO iterative_fibonacci_at_Main_label_0 + LABEL iterative_fibonacci_at_Main_label_2 + RETURN local_iterative_fibonacci_at_Main_n2_2 } \ No newline at end of file From 8103705d25720bb485469f6a1a0db199d32d8f71 Mon Sep 17 00:00:00 2001 From: Luiso Date: Mon, 18 Oct 2021 03:45:42 -0400 Subject: [PATCH 045/143] Inicio del reporte MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Se inicializan los tipos mediante un llamado a una función generada en CIL --- doc/TODO.md | 6 + doc/report.md | 174 ++++++++++++++++++++++ src/cool_cmp/cool/visitors/cil_visitor.py | 62 +++++--- src/testing.cl.cil | 67 +++++++-- 4 files changed, 276 insertions(+), 33 deletions(-) create mode 100644 doc/TODO.md create mode 100644 doc/report.md diff --git a/doc/TODO.md b/doc/TODO.md new file mode 100644 index 000000000..d7ee37c99 --- /dev/null +++ b/doc/TODO.md @@ -0,0 +1,6 @@ +# TODOs +[ ] Anotar los nodos para poder generar código con comentarios que lo relacionen con el código COOL original +[ ] Hacer una función para inicializar las instancias de clase en CIL, actualmente lo que hay es que se inicializan en el mismo código +[ ] Terminar de hacer la parte del Check en CIL. +[ ] Probar como es que funciona el SELF_TYPE y si lo hace bien decirlo en el informe +[ ] Arreglar los test hechos por nosotros del proyecto \ No newline at end of file diff --git a/doc/report.md b/doc/report.md new file mode 100644 index 000000000..9fe807743 --- /dev/null +++ b/doc/report.md @@ -0,0 +1,174 @@ +# Informe + +El objetivo principal de la aplicación es contruir un compilador que convierta un programa de COOL en un programa funcionalmente equivalente en MIPS. Se mostrará cómo se usa el compilador y se describirá la arquitectura y procesos relacionados con la contrucción y funcionamiento de este. + +## ¿Cómo usar el compilador? + +Para el uso del compilador es necesario correr el archivo `src/coolc.sh`. + +- `$ ./src/coolc.sh ` Uso básico +- TODO Agregar más usos (COOL Interpretado, Salida de AUTO_TYPE Inferido, Salida de CIL generado) + +El compilador viene con una ayuda que ayuda a documentar los otros usos posibles de este. + +## Arquitectura + +### Estructura + +El compilador se divide en tres módulos principales: +- cmp: + - Clases bases de la API de Gramática + - Implementación del patrón visitor + - Clases bases para herramientas de análisis semántico + - Nodos bases de AST de Cool y CIL +- cmp_tools: + - Implementación de lexer + - Implementación de parsers +- cool: + - Gramática, parser, lexer, AST de Cool + - Implementación de los visitors del compilador. + +TODO tal vez se creen nuevos módulos en relación a los diferentes lenguajes en el proyecto por ejemplo ahora esta solamente cool, pero ahí se mezcla con cil, si se separan estos sería un poco más organizado el proyecto, y luego vendría mips. Finalmente quedarían además de los que ya están **cil** y **mips** + +### Flujo de trabajo + +La dinámica de flujo de trabajo del compilador se ve repressentada por tubos y tuberías (Pipe, Pipeline), las cuales permiten una gran flexibilidad y desacomplamiento a la hora de agregar pasos en el proceso a seguir. + +TODO Alguna foto explicativa de los pipes y pipelines + +Gracias a esto es posible crear y tener diferentes flujos de trabajo para usarlos fácilmente. Por ejemplo en el proyecto se tienen varios flujos de trabajo listos para su uso: +- `generate_cil_pipeline`: Genera el código CIL correspondiente al programa COOL que se dió de entrada. +- `generate_cool_pipeline`: Reconstruye el programa COOL de entrada con los tipos inferidos sustituídos +- `interprete_cool_pipeline`: Interpreta el programa COOL en Python. + +#### Errores + +Detectar y mostrar los errores son un paso crucial en cualquier aplicación que permite al usuario ahorrar mucho tiempo al proveerle una descripción lo más precisa posible de este. El flujo principal de la aplicación posee una lista, la cuál contiene todos los errores incurridos por etapas anteriores. Al final de la ejecución los errores son escritos en la salida estándar del programa. + +En la implementación de los flujos, generalmente se ignora realizar una fase si existe algún error previo, por esto se asume en las explicaciones que siempre se llega a ellas sin errores previos. + +### Fases + +El compilador se divide en dos fases principales: +- Fase de análisis: Empieza leyendo el código fuente (COOL) y termina en la construcción de un AST de código intermedio (CIL) +- Fase de síntesis: Empieza recibiendo el AST de código intermedio y termina en la generación de código MIPS. + +La segunda fase puede que se omita en caso de que lo que se quiera no necesite generar código MIPS, un ejemplo de esto es cuando se quiere interpretar COOL. + +### Fase de análisis + +En la fase de análisis se contemplan las siguientes etapas: +1. Lectura del programa +2. Tokenización del programa +3. Análisis sintáctico +4. Análisis semántico +5. Acción correspondiente a la entrada (Construir AST CIL, Interpretar COOL, etc) + +#### Tokenización + +Este proceso es el encarado de convertir el texto plano de un programa de COOL en una lista de tokens para poder trabajar con ellas en próximas etapas. + +Siguiendo el flujo de trabajo propuesto, esta etapa se realiza usando el pipe `lexer_pipeline` en la cual se lee el texto y se tokeniza usando el módulo `ply` y se añade la lista de tokens resultante al flujo. + +En este paso también se ignoran los comentarios en la salida de los tokens ya que estos no son necesarios para el análisis semántico. Un problema que se pudo apreciar en esta etapa es que los comentarios anidados no pueden ser eliminados con expresiones reglares ya que estas fallan al identificar este tipo de estructuras, para resolver este problema se (TODO WATA Como fue que se resolvió esto). + +#### Análisis sintáctico + +Este proceso es el encargado de convertir los tokens del programa en un árbol de sintaxis abstracta, en caso de que estos coincidan con la gramática del lenguaje. + +El pipeline encargado de esta etapa es `syntax_pipeline` el cual evalúa el parser con los tokens dados y añade al flujo la derivación de extrema derecha de los tokens que luego será evaluada y convertida en un AST por la gramátca atributada. + +##### Gramática + +La gramática propuesta para COOL se encuentra en `cool/grammar/cool_grammar.py`. Esta es una gramática atributada la cual al ser evaluada devuelve el AST correspondiente al de entrada de COOL. + +##### Parser + +El parser es contruído en base a la gramática previamente definida, este consiste en un parser LALR1. Ya que su construcción dinámica incurre en un consumo de tiempo relativamente alto, este se encuentra serializado para un mejor rendimiento del inicio del programa. + +#### Análisis semántico + +Este proceso es el encargado de verificar la correcta semántica del programa de COOL. Se aplica fuertemente el patrón visitor, pasando por diferentes etapas las cuales van construyendo diferentes estructuras y analizando el contenido del AST para verificar su correctitud. + +El pipeline encargado de esta etapa es `semantic_pipeline`. + +##### Construcción del Contexto + +El contexto de un programa de COOL se puede definir como una colección de los tipos que están definidos en el programa. Estos tipos tiene otras informaciones como son los atributos, métodos y padre. La construcción del contexto pasa por tres etapas: + +1. Construcción del contexto inicial (Contexto que posee la información de las clases básicas Ej: Object, Int). +2. Recolección de tipos y construcción del árbol de dependencias. +3. Construcción de tipos. + +Para la construcción del contexto inicial se creó un esqueleto contentiendo las definiciones de las clases bases con sus respectivos métodos (ver `cool/lib/std.cool`). Este esqueleto es analizado por la infraestructura existente y devuelve el contexto inicial. Se prefirió esta aproximación ya que permite obtener el contexto de forma natural sin tener que recurrir a tenerlo fijado dentro del código. + +La recolección de tipos constituye el paso en el que se agregan los tipos definidos al contexto del programa, en esta etapa se comprueba que no existan ciclos en la relación de padre de los tipos. + +Finalmente se agrega la información restante a los tipos como son los métodos y atributos que los conforman. + +##### Comprobación de tipos y construcción del ámbito + +Una vez se tiene el contexto contruído es necesario verificar las demás reglas semánticas de COOL. En esta etapa se: + +- Se anotan los nodos que contienen tipos con las expresiones correspondientes. +- Se construye el ámbito (Scope) del programa. +- Comprueban las reglas semánticas de COOL. + +Para realizar el chequeo de tipos de un programa de COOL es necesario conocer el tipo estático de las expresiones, esto se realiza con un recorrido del AST de COOL en post-orden anotando el tipo de las expresiones de manera bottom-up para comprobar la correctitud de las operaciones entre estas. En el mismo recorrido se va construyendo el Scope que verifica la correctitud en el uso de variables. También se verifican las demás reglas semánticas como por ejemplo las relacionadas con la sobreescritura de métodos. + +##### Tipo especial SELF_TYPE + +TODO Ver si funciona bien y como es que se trabaja en el análisis semántico + +##### Inferencia de tipos + +El proyecto soporta inferencia de tipos mediante la anotación del tipo con el nombre especial de AUTO_TYPE. + +El algoritmo propuesto para la inferencia de los tipos AUTO_TYPE se basa en la idea de ir +descartando los tipos que no pueden ser, basándose en las operaciones realizadas sobre los +AUTO_TYPE y las operaciones válidas sobre los posibles tipos. Estas operaciones pueden ser +asignación, operaciones aritméticas y despachado de métodos. Luego que se tiene toda la +información necesaria, se comprueba que no haya incoherencias en los posibles tipos +resultantes (Ej: métodos con igual nombre y parámetros en clases no relacionadas) y se +sustituye en los nodos del AST, el AUTO_TYPE, por el tipo el más adecuado según su posición +(Ej: argumentos el más abstracto, en tipo de retorno el más concreto). + +La primera parte del algoritmo consiste en reunir toda la información sobre las operaciones que +actúan sobre los AUTO_TYPE, esto se hace de manera automática en el chequeo de tipos +mediante la llamada a los métodos conforms_to, get_attribute y get_method de la clase +AutoType y operation_defined de Operator. Estos métodos son llamados en el recorrido +TypeChecker para comprobar que las operaciones a realizar sobre los tipos cumplen las reglas +semánticas. El algoritmo se aprovecha de esto y elimina en esta etapa los tipos que no +satisfacen las operaciones (Vea cool.semantic.type.AutoType y +cool.semantic.operations.Operator.operation_defined). +La segunda parte consiste en verificar la validez de los posibles tipos. Definimos a un +AUTO_TYPE como válido si el grafo de sus posibles tipos (tomando la relación A padre de B) es +unilateralmente conexo y no vacío. Esta definición se debe a que en caso de no ser +unilateralmente conexo, significaría que habría dos sub herencias disjuntas que podrían ocupar +el lugar del AUTO_TYPE y en este caso no se podría decidir entre una de estas. Por otra parte en +el caso de ser vacío significaría que no hay posibles tipos que satisfagan las operaciones +realizadas sobre el AUTO_TYPE. Una vez comprobada la validez del AUTO_TYPE se pone a los +nodos el tipo correspondiente a su posición ya sea el más abstracto o concreto. Esta segunda +parte se realiza en el recorrido AutoResolver. Ejemplos de casos válidos (Izquierda) y no válidos +(Derecha): + +TODO: Poner las fotos del informe del segundo proyecto + +#### Fin del análisis semántico + +Al final del análisis semántico se sabe que el programa es correcto o no y se tiene un AST de este listo para ser procesado según haga falta. + +##### Interpretar COOL + +Este árbol es posible interpretarlo, para esto se creó un visitor que ejecuta los nodos en el mismo Python. Esto es útil a la hora de verificar la correctitud del código sin tener que compilarlo y aumentar las probabilidades de que el error sea otro. + +##### Convertir AST COOL a AST CIL + +TODO Seguir con esto +La conversión del AST de COOL al de CIL se realiza en una sola pasada al árbol. +- Para instanciar nuevos tipos se generaron nuevas funciones inicializadores ejecutan las funciones necesarias para ponerle los valores iniciales a los atributos. + + +### Fase de síntesis +TODO + diff --git a/src/cool_cmp/cool/visitors/cil_visitor.py b/src/cool_cmp/cool/visitors/cil_visitor.py index 51bb8d22e..b0ada2a49 100644 --- a/src/cool_cmp/cool/visitors/cil_visitor.py +++ b/src/cool_cmp/cool/visitors/cil_visitor.py @@ -541,7 +541,8 @@ def instructions(self): return self.current_function.instructions def register_local(self, vinfo): - vinfo.cil_name = f'local_{self.current_function.name[9:]}_{vinfo.name}_{len(self.localvars)}' + m = min([i+1 for i,s in enumerate(self.current_function.name) if s == "_"],default=len(self.current_function.name)-1) + vinfo.cil_name = f'local_{self.current_function.name[m:]}_{vinfo.name}_{len(self.localvars)}' local_node = cil.LocalNode(vinfo.cil_name) self.localvars.append(local_node) return vinfo.cil_name @@ -551,7 +552,8 @@ def define_internal_local(self): return self.register_local(vinfo) def define_label(self): - name = f'{self.current_function.name[9:]}_label_{len(self.labels)}' + m = min([i+1 for i,s in enumerate(self.current_function.name) if s == "_"],default=len(self.current_function.name)-1) + name = f'{self.current_function.name[m:]}_label_{len(self.labels)}' label_node = cil.LabelNode(name) self.labels.append(label_node) return label_node @@ -563,6 +565,9 @@ def register_instruction(self, instruction): def to_init_attr_function_name(self, attr_name, type_name): return f'$init_{attr_name}_at_{type_name}' # Prefixed with $ to avoid collisions + def to_init_type_function_name(self, type_name): + return f"$init_{type_name}_type" + def to_function_name(self, method_name, type_name): return f'function_{method_name}_at_{type_name}' @@ -600,7 +605,6 @@ def visit(self, node, scope): main_type = self.context.get_type("Main") main_node = InstantiateNode(("Main",0,0),0,0) instance = self.visit(main_node, main_type.class_node.scope) - # self.register_instruction(cil.AllocateNode('Main', instance)) # TODO Hacer Inicializador para los Allocate y los atributos self.register_instruction(cil.ArgNode(instance)) self.register_instruction(cil.StaticCallNode(main_method_name, result)) self.register_instruction(cil.ReturnNode("0")) @@ -627,12 +631,25 @@ def visit(self, node, scope): type_node = self.register_type(self.current_type.name) + self.current_function = init_function = self.register_function(self.to_init_type_function_name(self.current_type.name)) + type_node.methods.append((init_function.name, init_function.name)) + self.params.append(cil.ParamNode('self')) + for attr,typex in self.current_type.all_attributes(): + # Defining attribute's init functions type_node.attributes.append(attr.name) new_function = self.register_function(self.to_init_attr_function_name(attr.name, self.current_type.name)) type_node.methods.append((new_function.name, new_function.name)) self.current_function = new_function self.visit(attr.node, attr.node.scope) + + # Calling function in type init function + self.current_function = init_function + dest = self.define_internal_local() + self.register_instruction(cil.ArgNode('self')) + self.register_instruction(cil.StaticCallNode(new_function.name, dest)) + + self.register_instruction(cil.ReturnNode('self')) # Returning created instance for method,typex in self.current_type.all_methods(): # Register methods if typex != self.current_type: @@ -666,9 +683,9 @@ def visit(self, node, scope): self.current_method = self.current_type.get_method(node.id, len(node.params)) # Your code here!!! (Handle PARAMS) - self.current_function.params.append(cil.ParamNode('self')) + self.params.append(cil.ParamNode('self')) for param in self.current_method.param_names: - self.current_function.params.append(cil.ParamNode(param)) + self.params.append(cil.ParamNode(param)) value = self.visit(node.body, scope) @@ -678,7 +695,7 @@ def visit(self, node, scope): @visitor.when(AttrDeclarationNode) def visit(self, node, scope): - self.current_function.params.append(cil.ParamNode('self')) + self.params.append(cil.ParamNode('self')) result = self.visit(node.expr, scope) self.register_instruction(cil.SetAttribNode("self", node.id, result)) self.register_instruction(cil.ReturnNode()) @@ -704,7 +721,7 @@ def visit(self, node, scope=None): @visitor.when(cil.ObjectCopyNode) def visit(self, node, scope=None): - instance = self.current_function.params[0] + instance = self.params[0] result = self.define_internal_local() self.register_instruction(cil.CopyNode(instance.name, result)) return result @@ -716,22 +733,22 @@ def visit(self, node, scope=None): @visitor.when(cil.ObjectTypeNameNode) def visit(self, node, scope=None): - instance = self.current_function.params[0] + instance = self.params[0] result = self.define_internal_local() self.register_instruction(cil.TypeOfNode(instance.name, result)) return result @visitor.when(cil.StringConcatNode) def visit(self, node, scope=None): - string1 = self.current_function.params[0] - string2 = self.current_function.params[1] + string1 = self.params[0] + string2 = self.params[1] result = self.define_internal_local() self.register_instruction(cil.ConcatNode(result, string1.name, string2.name)) return result @visitor.when(cil.StringLengthNode) def visit(self, node, scope=None): - string = self.current_function.params[0] + string = self.params[0] result = self.define_internal_local() self.register_instruction(cil.LengthNode(result, string.name)) return result @@ -739,9 +756,9 @@ def visit(self, node, scope=None): @visitor.when(cil.StringSubstringNode) def visit(self, node, scope=None): - string = self.current_function.params[0] - index = self.current_function.params[1] - length = self.current_function.params[2] + string = self.params[0] + index = self.params[1] + length = self.params[2] result = self.define_internal_local() self.register_instruction(cil.SubstringNode(result, string.name, index.name, length.name)) return result @@ -761,7 +778,7 @@ def visit(self, node, scope=None): @visitor.when(cil.IOOutIntNode) def visit(self, node, scope=None): - integer = self.current_function.params[1] + integer = self.params[1] string_message = self.define_internal_local() self.register_instruction(cil.ToStrNode(string_message, integer.name)) self.register_instruction(cil.PrintNode(string_message)) @@ -769,7 +786,7 @@ def visit(self, node, scope=None): @visitor.when(cil.IOOutStringNode) def visit(self, node, scope=None): - string = self.current_function.params[1] + string = self.params[1] self.register_instruction(cil.PrintNode(string.name)) return "0" @@ -785,7 +802,7 @@ def visit(self, node, scope): self.register_instruction(cil.AssignNode(local.cil_name,value)) return local.cil_name else: - if any(x for x in self.current_function.params if x.name == local.name): + if any(x for x in self.params if x.name == local.name): self.register_instruction(cil.AssignNode(local.name,value)) # Param return local.name else: @@ -1007,7 +1024,7 @@ def visit(self, node, scope): try: return scope.find_variable(node.lex).cil_name # is a Local Variable except AttributeError: - if any(x for x in self.current_function.params if x.name == node.lex): + if any(x for x in self.params if x.name == node.lex): return node.lex # Param else: value = self.define_internal_local() # Attr @@ -1022,13 +1039,10 @@ def visit(self, node, scope): # Your code here!!! instance = self.define_internal_local() - self.register_instruction(cil.AllocateNode(node.lex, instance)) instance_typex = self.context.get_type(node.lex) - for attr,typex in instance_typex.all_attributes(): # Initialize Attributes - result = self.define_internal_local() - self.register_instruction(cil.ArgNode(instance)) - self.register_instruction(cil.StaticCallNode( - self.to_init_attr_function_name(attr.name, instance_typex.name), result)) + self.register_instruction(cil.AllocateNode(instance_typex.name, instance)) + self.register_instruction(cil.ArgNode(instance)) + self.register_instruction(cil.StaticCallNode(self.to_init_type_function_name(instance_typex.name), instance)) return instance @visitor.when(PlusNode) diff --git a/src/testing.cl.cil b/src/testing.cl.cil index 5a397a274..feb744431 100644 --- a/src/testing.cl.cil +++ b/src/testing.cl.cil @@ -2,6 +2,7 @@ type Object { + method $init_Object_type: $init_Object_type method abort: function_abort_at_Object method type_name: function_type_name_at_Object method copy: function_copy_at_Object @@ -9,6 +10,7 @@ type Object { type String { + method $init_String_type: $init_String_type method abort: function_abort_at_Object method type_name: function_type_name_at_Object method copy: function_copy_at_Object @@ -19,6 +21,7 @@ type String { type Bool { + method $init_Bool_type: $init_Bool_type method abort: function_abort_at_Object method type_name: function_type_name_at_Object method copy: function_copy_at_Object @@ -26,6 +29,7 @@ type Bool { type Int { + method $init_Int_type: $init_Int_type method abort: function_abort_at_Object method type_name: function_type_name_at_Object method copy: function_copy_at_Object @@ -33,6 +37,7 @@ type Int { type IO { + method $init_IO_type: $init_IO_type method abort: function_abort_at_Object method type_name: function_type_name_at_Object method copy: function_copy_at_Object @@ -44,6 +49,7 @@ type IO { type Main { attribute number + method $init_Main_type: $init_Main_type method $init_number_at_Main: $init_number_at_Main method abort: function_abort_at_Object method type_name: function_type_name_at_Object @@ -68,18 +74,24 @@ data_3 = "\n" function entry { - LOCAL local__internal_0 - LOCAL local__internal_1 - LOCAL local__internal_2 - LOCAL local__internal_3 + LOCAL local_y_internal_0 + LOCAL local_y_internal_1 + LOCAL local_y_internal_2 - local__internal_2 = ALLOCATE Main - ARG local__internal_2 - local__internal_3 = CALL $init_number_at_Main - ARG local__internal_2 - local__internal_1 = CALL function_main_at_Main + local_y_internal_2 = ALLOCATE Main + ARG local_y_internal_2 + local_y_internal_2 = CALL $init_Main_type + ARG local_y_internal_2 + local_y_internal_1 = CALL function_main_at_Main RETURN 0 } +function $init_Object_type { + PARAM self + + + + RETURN self +} function function_abort_at_Object { PARAM self @@ -104,6 +116,13 @@ function function_copy_at_Object { local_copy_at_Object_internal_0 = COPY self RETURN local_copy_at_Object_internal_0 } +function $init_String_type { + PARAM self + + + + RETURN self +} function function_length_at_String { PARAM self @@ -131,6 +150,27 @@ function function_substr_at_String { local_substr_at_String_internal_0 = SUBSTRING self i l RETURN local_substr_at_String_internal_0 } +function $init_Bool_type { + PARAM self + + + + RETURN self +} +function $init_Int_type { + PARAM self + + + + RETURN self +} +function $init_IO_type { + PARAM self + + + + RETURN self +} function function_out_string_at_IO { PARAM self PARAM x @@ -166,6 +206,15 @@ function function_in_int_at_IO { local_in_int_at_IO_internal_0 = READ RETURN local_in_int_at_IO_internal_0 } +function $init_Main_type { + PARAM self + + LOCAL local_Main_type_internal_0 + + ARG self + local_Main_type_internal_0 = CALL $init_number_at_Main + RETURN self +} function $init_number_at_Main { PARAM self From c826cb09d9edd9ce6081650ed2e894aeb84a4909 Mon Sep 17 00:00:00 2001 From: Luiso Date: Mon, 18 Oct 2021 05:22:35 -0400 Subject: [PATCH 046/143] SELF_TYPE working CIL --- doc/TODO.md | 6 +- doc/report.md | 2 +- src/cool_cmp/cool/visitors/cil_visitor.py | 29 ++- src/cool_cmp/main.py | 4 +- src/testing.cl | 56 ++-- src/testing.cl.cil | 298 ++++++++++++---------- 6 files changed, 218 insertions(+), 177 deletions(-) diff --git a/doc/TODO.md b/doc/TODO.md index d7ee37c99..a8cc2987e 100644 --- a/doc/TODO.md +++ b/doc/TODO.md @@ -1,6 +1,8 @@ # TODOs + [ ] Anotar los nodos para poder generar código con comentarios que lo relacionen con el código COOL original -[ ] Hacer una función para inicializar las instancias de clase en CIL, actualmente lo que hay es que se inicializan en el mismo código +[x] Hacer una función para inicializar las instancias de clase en CIL, actualmente lo que hay es que se inicializan en el mismo código [ ] Terminar de hacer la parte del Check en CIL. [ ] Probar como es que funciona el SELF_TYPE y si lo hace bien decirlo en el informe -[ ] Arreglar los test hechos por nosotros del proyecto \ No newline at end of file +[ ] Arreglar los test hechos por nosotros del proyecto +[ ] VERIFICAR LOS TODOS que hay por ahí diff --git a/doc/report.md b/doc/report.md index 9fe807743..ff6f67cab 100644 --- a/doc/report.md +++ b/doc/report.md @@ -80,7 +80,7 @@ El pipeline encargado de esta etapa es `syntax_pipeline` el cual evalúa el pars ##### Gramática -La gramática propuesta para COOL se encuentra en `cool/grammar/cool_grammar.py`. Esta es una gramática atributada la cual al ser evaluada devuelve el AST correspondiente al de entrada de COOL. +La gramática propuesta para COOL se encuentra en `cool/grammar/cool_grammar.py`. Esta es una gramática atributada la cual al ser evaluada devuelve el AST correspondiente al programa de entrada de COOL. ##### Parser diff --git a/src/cool_cmp/cool/visitors/cil_visitor.py b/src/cool_cmp/cool/visitors/cil_visitor.py index b0ada2a49..f849bdcbb 100644 --- a/src/cool_cmp/cool/visitors/cil_visitor.py +++ b/src/cool_cmp/cool/visitors/cil_visitor.py @@ -303,7 +303,10 @@ def visit(self, node:cil.StaticCallNode, args: list, caller_fun_scope: dict): @visitor.when(cil.DynamicCallNode) def visit(self, node, args: list, caller_fun_scope: dict): - typex = self.get_type(node.type) + if node.type in caller_fun_scope: # TODO Remove duality between Value and ReferencedValue + typex = self.get_type(caller_fun_scope[node.type]) + else: + typex = self.get_type(node.type) try: func_name = next(static_name for name, static_name in typex.methods if name == node.method) except StopIteration: @@ -352,7 +355,10 @@ def visit(self, node, args: list, caller_fun_scope: dict): @visitor.when(cil.AllocateNode) def visit(self, node, args: list, caller_fun_scope: dict): - typex = self.get_type(node.type) + if node.type in caller_fun_scope: # TODO Remove duality between Value and ReferencedValue + typex = self.get_type(caller_fun_scope[node.type]) + else: + typex = self.get_type(node.type) value = { "$type": typex, } @@ -364,7 +370,7 @@ def visit(self, node, args: list, caller_fun_scope: dict): @visitor.when(cil.TypeOfNode) def visit(self, node, args: list, caller_fun_scope: dict): - value = self.get_value(node.obj) + value = self.get_value(node.obj, caller_fun_scope) if isinstance(value, int): self.set_value(node.dest, "Int", caller_fun_scope) elif isinstance(value, str): @@ -632,7 +638,7 @@ def visit(self, node, scope): type_node = self.register_type(self.current_type.name) self.current_function = init_function = self.register_function(self.to_init_type_function_name(self.current_type.name)) - type_node.methods.append((init_function.name, init_function.name)) + type_node.methods.append(("$init", init_function.name)) self.params.append(cil.ParamNode('self')) for attr,typex in self.current_type.all_attributes(): @@ -1040,9 +1046,18 @@ def visit(self, node, scope): # Your code here!!! instance = self.define_internal_local() instance_typex = self.context.get_type(node.lex) - self.register_instruction(cil.AllocateNode(instance_typex.name, instance)) - self.register_instruction(cil.ArgNode(instance)) - self.register_instruction(cil.StaticCallNode(self.to_init_type_function_name(instance_typex.name), instance)) + if instance_typex.name == "Void": + self.register_instruction(cil.VoidNode(instance)) + elif instance_typex.name == "SELF_TYPE": + dynamic_type = self.define_internal_local() + self.register_instruction(cil.TypeOfNode("self", dynamic_type)) + self.register_instruction(cil.AllocateNode(dynamic_type, instance)) + self.register_instruction(cil.ArgNode(instance)) + self.register_instruction(cil.DynamicCallNode(dynamic_type, "$init", instance)) + else: + self.register_instruction(cil.AllocateNode(instance_typex.name, instance)) + self.register_instruction(cil.ArgNode(instance)) + self.register_instruction(cil.StaticCallNode(self.to_init_type_function_name(instance_typex.name), instance)) return instance @visitor.when(PlusNode) diff --git a/src/cool_cmp/main.py b/src/cool_cmp/main.py index bdab3ddb8..59b7dfb1a 100644 --- a/src/cool_cmp/main.py +++ b/src/cool_cmp/main.py @@ -62,9 +62,9 @@ def main( file_content = file.read() if run_cool: - result = generate_cool_pipeline(file_content, verbose=verbose) - elif out_infer: result = interprete_cool_pipeline(file_content, verbose=verbose) + elif out_infer: + result = generate_cool_pipeline(file_content, verbose=verbose) elif run_cil: result = interprete_cil_pipeline(file_content, verbose=verbose) elif out_cil: diff --git a/src/testing.cl b/src/testing.cl index 995708d79..8a495c7e8 100644 --- a/src/testing.cl +++ b/src/testing.cl @@ -1,34 +1,32 @@ -class Main inherits IO { - number: Int <- 5; - - main () : Object { - testing_fibonacci(number) +class A{ + a:SELF_TYPE; + get():SELF_TYPE { + self }; + get2():SELF_TYPE { + new SELF_TYPE + }; + get3():SELF_TYPE { + { + a <- new SELF_TYPE; + a; + } + }; +}; - testing_fibonacci(n: Int) : IO {{ - out_string("Iterative Fibonacci : "); - out_int(iterative_fibonacci(5)); - out_string("\n"); - - out_string("Recursive Fibonacci : "); - out_int(recursive_fibonacci(5)); - out_string("\n"); - }}; - - recursive_fibonacci (n: AUTO_TYPE) : AUTO_TYPE { - if n <= 2 then 1 else recursive_fibonacci(n - 1) + recursive_fibonacci(n - 2) fi +class B inherits A { + get4(): SELF_TYPE { + get3() }; + }; - iterative_fibonacci(n: AUTO_TYPE) : AUTO_TYPE { - let i: Int <- 2, n1: Int <- 1, n2: Int <- 1, temp: Int in { - while i < n loop - let temp: Int <- n2 in { - n2 <- n2 + n1; - n1 <- temp; - i <- i + 1; - } - pool; - n2; - } +class Main inherits IO { + main() : AUTO_TYPE { + out_string(let a:A <- new A, b:B <- new B in + b.get().type_name().concat( + b.get2().type_name()).concat( + b.get3().type_name()).concat( + b.get4().type_name()).concat( + type_name())) }; -}; \ No newline at end of file +}; diff --git a/src/testing.cl.cil b/src/testing.cl.cil index feb744431..fb10b9dcc 100644 --- a/src/testing.cl.cil +++ b/src/testing.cl.cil @@ -2,7 +2,7 @@ type Object { - method $init_Object_type: $init_Object_type + method $init: $init_Object_type method abort: function_abort_at_Object method type_name: function_type_name_at_Object method copy: function_copy_at_Object @@ -10,7 +10,7 @@ type Object { type String { - method $init_String_type: $init_String_type + method $init: $init_String_type method abort: function_abort_at_Object method type_name: function_type_name_at_Object method copy: function_copy_at_Object @@ -21,7 +21,7 @@ type String { type Bool { - method $init_Bool_type: $init_Bool_type + method $init: $init_Bool_type method abort: function_abort_at_Object method type_name: function_type_name_at_Object method copy: function_copy_at_Object @@ -29,7 +29,7 @@ type Bool { type Int { - method $init_Int_type: $init_Int_type + method $init: $init_Int_type method abort: function_abort_at_Object method type_name: function_type_name_at_Object method copy: function_copy_at_Object @@ -37,7 +37,7 @@ type Int { type IO { - method $init_IO_type: $init_IO_type + method $init: $init_IO_type method abort: function_abort_at_Object method type_name: function_type_name_at_Object method copy: function_copy_at_Object @@ -46,11 +46,35 @@ type IO { method in_string: function_in_string_at_IO method in_int: function_in_int_at_IO } +type A { + attribute a + + method $init: $init_A_type + method $init_a_at_A: $init_a_at_A + method abort: function_abort_at_Object + method type_name: function_type_name_at_Object + method copy: function_copy_at_Object + method get: function_get_at_A + method get2: function_get2_at_A + method get3: function_get3_at_A +} +type B { + attribute a + + method $init: $init_B_type + method $init_a_at_B: $init_a_at_B + method abort: function_abort_at_Object + method type_name: function_type_name_at_Object + method copy: function_copy_at_Object + method get: function_get_at_A + method get2: function_get2_at_A + method get3: function_get3_at_A + method get4: function_get4_at_B +} type Main { - attribute number + - method $init_Main_type: $init_Main_type - method $init_number_at_Main: $init_number_at_Main + method $init: $init_Main_type method abort: function_abort_at_Object method type_name: function_type_name_at_Object method copy: function_copy_at_Object @@ -59,16 +83,10 @@ type Main { method in_string: function_in_string_at_IO method in_int: function_in_int_at_IO method main: function_main_at_Main - method testing_fibonacci: function_testing_fibonacci_at_Main - method recursive_fibonacci: function_recursive_fibonacci_at_Main - method iterative_fibonacci: function_iterative_fibonacci_at_Main } .DATA -data_0 = "Iterative Fibonacci : " -data_1 = "\n" -data_2 = "Recursive Fibonacci : " -data_3 = "\n" + .CODE function entry { @@ -206,147 +224,155 @@ function function_in_int_at_IO { local_in_int_at_IO_internal_0 = READ RETURN local_in_int_at_IO_internal_0 } -function $init_Main_type { +function $init_A_type { PARAM self - LOCAL local_Main_type_internal_0 + LOCAL local_A_type_internal_0 ARG self - local_Main_type_internal_0 = CALL $init_number_at_Main + local_A_type_internal_0 = CALL $init_a_at_A RETURN self } -function $init_number_at_Main { +function $init_a_at_A { PARAM self - + LOCAL local_a_at_A_internal_0 - SETATTR self number 5 + local_a_at_A_internal_0 = VOID + SETATTR self a local_a_at_A_internal_0 RETURN } -function function_main_at_Main { +function function_get_at_A { PARAM self - LOCAL local_main_at_Main_internal_0 - LOCAL local_main_at_Main_internal_1 + - local_main_at_Main_internal_0 = GETATTR self number - ARG self - ARG local_main_at_Main_internal_0 - local_main_at_Main_internal_1 = VCALL Main testing_fibonacci - RETURN local_main_at_Main_internal_1 + RETURN self } -function function_testing_fibonacci_at_Main { +function function_get2_at_A { PARAM self - PARAM n - - LOCAL local_testing_fibonacci_at_Main_internal_0 - LOCAL local_testing_fibonacci_at_Main_internal_1 - LOCAL local_testing_fibonacci_at_Main_internal_2 - LOCAL local_testing_fibonacci_at_Main_internal_3 - LOCAL local_testing_fibonacci_at_Main_internal_4 - LOCAL local_testing_fibonacci_at_Main_internal_5 - LOCAL local_testing_fibonacci_at_Main_internal_6 - LOCAL local_testing_fibonacci_at_Main_internal_7 - LOCAL local_testing_fibonacci_at_Main_internal_8 - LOCAL local_testing_fibonacci_at_Main_internal_9 - LOCAL local_testing_fibonacci_at_Main_internal_10 - LOCAL local_testing_fibonacci_at_Main_internal_11 - LOCAL local_testing_fibonacci_at_Main_internal_12 - - local_testing_fibonacci_at_Main_internal_1 = LOAD data_0 - ARG self - ARG local_testing_fibonacci_at_Main_internal_1 - local_testing_fibonacci_at_Main_internal_2 = VCALL Main out_string - ARG self - ARG 5 - local_testing_fibonacci_at_Main_internal_3 = VCALL Main iterative_fibonacci - ARG self - ARG local_testing_fibonacci_at_Main_internal_3 - local_testing_fibonacci_at_Main_internal_4 = VCALL Main out_int - local_testing_fibonacci_at_Main_internal_5 = LOAD data_1 - ARG self - ARG local_testing_fibonacci_at_Main_internal_5 - local_testing_fibonacci_at_Main_internal_6 = VCALL Main out_string - local_testing_fibonacci_at_Main_internal_7 = LOAD data_2 - ARG self - ARG local_testing_fibonacci_at_Main_internal_7 - local_testing_fibonacci_at_Main_internal_8 = VCALL Main out_string - ARG self - ARG 5 - local_testing_fibonacci_at_Main_internal_9 = VCALL Main recursive_fibonacci + + LOCAL local_get2_at_A_internal_0 + LOCAL local_get2_at_A_internal_1 + + local_get2_at_A_internal_1 = TYPEOF self + local_get2_at_A_internal_0 = ALLOCATE local_get2_at_A_internal_1 + ARG local_get2_at_A_internal_0 + local_get2_at_A_internal_0 = VCALL local_get2_at_A_internal_1 $init + RETURN local_get2_at_A_internal_0 +} +function function_get3_at_A { + PARAM self + + LOCAL local_get3_at_A_internal_0 + LOCAL local_get3_at_A_internal_1 + LOCAL local_get3_at_A_internal_2 + LOCAL local_get3_at_A_internal_3 + + local_get3_at_A_internal_2 = TYPEOF self + local_get3_at_A_internal_1 = ALLOCATE local_get3_at_A_internal_2 + ARG local_get3_at_A_internal_1 + local_get3_at_A_internal_1 = VCALL local_get3_at_A_internal_2 $init + SETATTR self a local_get3_at_A_internal_1 + local_get3_at_A_internal_3 = GETATTR self a + RETURN local_get3_at_A_internal_3 +} +function $init_B_type { + PARAM self + + LOCAL local_B_type_internal_0 + ARG self - ARG local_testing_fibonacci_at_Main_internal_9 - local_testing_fibonacci_at_Main_internal_10 = VCALL Main out_int - local_testing_fibonacci_at_Main_internal_11 = LOAD data_3 + local_B_type_internal_0 = CALL $init_a_at_B + RETURN self +} +function $init_a_at_B { + PARAM self + + LOCAL local_a_at_B_internal_0 + + local_a_at_B_internal_0 = VOID + SETATTR self a local_a_at_B_internal_0 + RETURN +} +function function_get4_at_B { + PARAM self + + LOCAL local_get4_at_B_internal_0 + ARG self - ARG local_testing_fibonacci_at_Main_internal_11 - local_testing_fibonacci_at_Main_internal_12 = VCALL Main out_string - RETURN local_testing_fibonacci_at_Main_internal_12 + local_get4_at_B_internal_0 = VCALL B get3 + RETURN local_get4_at_B_internal_0 } -function function_recursive_fibonacci_at_Main { +function $init_Main_type { PARAM self - PARAM n - - LOCAL local_recursive_fibonacci_at_Main_internal_0 - LOCAL local_recursive_fibonacci_at_Main_internal_1 - LOCAL local_recursive_fibonacci_at_Main_internal_2 - LOCAL local_recursive_fibonacci_at_Main_internal_3 - LOCAL local_recursive_fibonacci_at_Main_internal_4 - LOCAL local_recursive_fibonacci_at_Main_internal_5 - LOCAL local_recursive_fibonacci_at_Main_internal_6 - - local_recursive_fibonacci_at_Main_internal_1 = n > 2 - local_recursive_fibonacci_at_Main_internal_1 = NOT local_recursive_fibonacci_at_Main_internal_1 - IF local_recursive_fibonacci_at_Main_internal_1 GOTO recursive_fibonacci_at_Main_label_0 - local_recursive_fibonacci_at_Main_internal_2 = n - 1 + + + + RETURN self +} +function function_main_at_Main { + PARAM self + + LOCAL local_main_at_Main_a_0 + LOCAL local_main_at_Main_internal_1 + LOCAL local_main_at_Main_b_2 + LOCAL local_main_at_Main_internal_3 + LOCAL local_main_at_Main_internal_4 + LOCAL local_main_at_Main_internal_5 + LOCAL local_main_at_Main_internal_6 + LOCAL local_main_at_Main_internal_7 + LOCAL local_main_at_Main_internal_8 + LOCAL local_main_at_Main_internal_9 + LOCAL local_main_at_Main_internal_10 + LOCAL local_main_at_Main_internal_11 + LOCAL local_main_at_Main_internal_12 + LOCAL local_main_at_Main_internal_13 + LOCAL local_main_at_Main_internal_14 + LOCAL local_main_at_Main_internal_15 + LOCAL local_main_at_Main_internal_16 + LOCAL local_main_at_Main_internal_17 + + local_main_at_Main_internal_1 = ALLOCATE A + ARG local_main_at_Main_internal_1 + local_main_at_Main_internal_1 = CALL $init_A_type + local_main_at_Main_a_0 = local_main_at_Main_internal_1 + local_main_at_Main_internal_3 = ALLOCATE B + ARG local_main_at_Main_internal_3 + local_main_at_Main_internal_3 = CALL $init_B_type + local_main_at_Main_b_2 = local_main_at_Main_internal_3 + ARG local_main_at_Main_b_2 + local_main_at_Main_internal_4 = VCALL B get + ARG local_main_at_Main_internal_4 + local_main_at_Main_internal_5 = VCALL B type_name + ARG local_main_at_Main_b_2 + local_main_at_Main_internal_6 = VCALL B get2 + ARG local_main_at_Main_internal_6 + local_main_at_Main_internal_7 = VCALL B type_name + ARG local_main_at_Main_internal_5 + ARG local_main_at_Main_internal_7 + local_main_at_Main_internal_8 = VCALL String concat + ARG local_main_at_Main_b_2 + local_main_at_Main_internal_9 = VCALL B get3 + ARG local_main_at_Main_internal_9 + local_main_at_Main_internal_10 = VCALL B type_name + ARG local_main_at_Main_internal_8 + ARG local_main_at_Main_internal_10 + local_main_at_Main_internal_11 = VCALL String concat + ARG local_main_at_Main_b_2 + local_main_at_Main_internal_12 = VCALL B get4 + ARG local_main_at_Main_internal_12 + local_main_at_Main_internal_13 = VCALL B type_name + ARG local_main_at_Main_internal_11 + ARG local_main_at_Main_internal_13 + local_main_at_Main_internal_14 = VCALL String concat ARG self - ARG local_recursive_fibonacci_at_Main_internal_2 - local_recursive_fibonacci_at_Main_internal_3 = VCALL Main recursive_fibonacci - local_recursive_fibonacci_at_Main_internal_4 = n - 2 + local_main_at_Main_internal_15 = VCALL Main type_name + ARG local_main_at_Main_internal_14 + ARG local_main_at_Main_internal_15 + local_main_at_Main_internal_16 = VCALL String concat ARG self - ARG local_recursive_fibonacci_at_Main_internal_4 - local_recursive_fibonacci_at_Main_internal_5 = VCALL Main recursive_fibonacci - local_recursive_fibonacci_at_Main_internal_6 = local_recursive_fibonacci_at_Main_internal_3 + local_recursive_fibonacci_at_Main_internal_5 - local_recursive_fibonacci_at_Main_internal_0 = local_recursive_fibonacci_at_Main_internal_6 - GOTO recursive_fibonacci_at_Main_label_1 - LABEL recursive_fibonacci_at_Main_label_0 - local_recursive_fibonacci_at_Main_internal_0 = 1 - LABEL recursive_fibonacci_at_Main_label_1 - RETURN local_recursive_fibonacci_at_Main_internal_0 -} -function function_iterative_fibonacci_at_Main { - PARAM self - PARAM n - - LOCAL local_iterative_fibonacci_at_Main_i_0 - LOCAL local_iterative_fibonacci_at_Main_n1_1 - LOCAL local_iterative_fibonacci_at_Main_n2_2 - LOCAL local_iterative_fibonacci_at_Main_temp_3 - LOCAL local_iterative_fibonacci_at_Main_internal_4 - LOCAL local_iterative_fibonacci_at_Main_internal_5 - LOCAL local_iterative_fibonacci_at_Main_internal_6 - LOCAL local_iterative_fibonacci_at_Main_temp_7 - LOCAL local_iterative_fibonacci_at_Main_internal_8 - LOCAL local_iterative_fibonacci_at_Main_internal_9 - LOCAL local_iterative_fibonacci_at_Main_internal_10 - - local_iterative_fibonacci_at_Main_i_0 = 2 - local_iterative_fibonacci_at_Main_n1_1 = 1 - local_iterative_fibonacci_at_Main_n2_2 = 1 - local_iterative_fibonacci_at_Main_temp_3 = 0 - local_iterative_fibonacci_at_Main_internal_5 = VOID - LABEL iterative_fibonacci_at_Main_label_0 - local_iterative_fibonacci_at_Main_internal_6 = local_iterative_fibonacci_at_Main_i_0 < n - IF local_iterative_fibonacci_at_Main_internal_6 GOTO iterative_fibonacci_at_Main_label_1 - GOTO iterative_fibonacci_at_Main_label_2 - LABEL iterative_fibonacci_at_Main_label_1 - local_iterative_fibonacci_at_Main_temp_7 = local_iterative_fibonacci_at_Main_n2_2 - local_iterative_fibonacci_at_Main_internal_9 = local_iterative_fibonacci_at_Main_n2_2 + local_iterative_fibonacci_at_Main_n1_1 - local_iterative_fibonacci_at_Main_n2_2 = local_iterative_fibonacci_at_Main_internal_9 - local_iterative_fibonacci_at_Main_n1_1 = local_iterative_fibonacci_at_Main_temp_7 - local_iterative_fibonacci_at_Main_internal_10 = local_iterative_fibonacci_at_Main_i_0 + 1 - local_iterative_fibonacci_at_Main_i_0 = local_iterative_fibonacci_at_Main_internal_10 - GOTO iterative_fibonacci_at_Main_label_0 - LABEL iterative_fibonacci_at_Main_label_2 - RETURN local_iterative_fibonacci_at_Main_n2_2 + ARG local_main_at_Main_internal_16 + local_main_at_Main_internal_17 = VCALL Main out_string + RETURN local_main_at_Main_internal_17 } \ No newline at end of file From 0e2039031fd2990d1282c705aa3200fd2682029d Mon Sep 17 00:00:00 2001 From: Luiso Date: Mon, 18 Oct 2021 14:23:30 -0400 Subject: [PATCH 047/143] SELF_TYPE and CaseNodes handled --- doc/Cool2Cil.md | 7 +- doc/TODO.md | 5 +- src/cool_cmp/cmp/cil.py | 9 +- src/cool_cmp/cool/ast/cil_ast.py | 7 +- src/cool_cmp/cool/pipes/pipes.py | 4 +- src/cool_cmp/cool/visitors/cil_visitor.py | 319 ++++++++++++++++++---- src/testing.cl | 11 +- src/testing.cl.cil | 128 +++++---- 8 files changed, 379 insertions(+), 111 deletions(-) diff --git a/doc/Cool2Cil.md b/doc/Cool2Cil.md index 7c9b500a2..e64e30ddd 100644 --- a/doc/Cool2Cil.md +++ b/doc/Cool2Cil.md @@ -1 +1,6 @@ -Los atributos se inicializan llamando a las funciones CIL generadas para eso. \ No newline at end of file +# Detalles de COOL a CIL + +- Los atributos se inicializan llamando a las funciones CIL generadas para eso. +- Las clases se inicializan llamando una función generada +- Los SELF_TYPE se resuelven tomando el tipo dinámico de la función y llamando al método virtual correspondiente +- Se anotaron los tipos con los padres diff --git a/doc/TODO.md b/doc/TODO.md index a8cc2987e..fd35e9929 100644 --- a/doc/TODO.md +++ b/doc/TODO.md @@ -2,7 +2,8 @@ [ ] Anotar los nodos para poder generar código con comentarios que lo relacionen con el código COOL original [x] Hacer una función para inicializar las instancias de clase en CIL, actualmente lo que hay es que se inicializan en el mismo código -[ ] Terminar de hacer la parte del Check en CIL. -[ ] Probar como es que funciona el SELF_TYPE y si lo hace bien decirlo en el informe +[x] Terminar de hacer la parte del Check en CIL. +[x?] Probar como es que funciona el SELF_TYPE y si lo hace bien decirlo en el informe [ ] Arreglar los test hechos por nosotros del proyecto +[ ] Ver si la dicotomia entre el valor de las variables y lo que apunta el valor de las varibles en CIL influye en la generación luego [ ] VERIFICAR LOS TODOS que hay por ahí diff --git a/src/cool_cmp/cmp/cil.py b/src/cool_cmp/cmp/cil.py index 678c0f6d3..563321ce1 100644 --- a/src/cool_cmp/cmp/cil.py +++ b/src/cool_cmp/cmp/cil.py @@ -11,8 +11,9 @@ def __init__(self, dottypes, dotdata, dotcode): self.dotcode = dotcode class TypeNode(Node): - def __init__(self, name): + def __init__(self, name, parent): self.name = name + self.parent = parent self.attributes = [] self.methods = [] @@ -93,7 +94,11 @@ def __init__(self, itype, dest): self.dest = dest class ArrayNode(InstructionNode): - pass + def __init__(self, dest, type, length) -> None: + super().__init__() + self.dest = dest + self.type = type + self.length = length class TypeOfNode(InstructionNode): def __init__(self, obj, dest): diff --git a/src/cool_cmp/cool/ast/cil_ast.py b/src/cool_cmp/cool/ast/cil_ast.py index 7b37ede4b..28aefe775 100644 --- a/src/cool_cmp/cool/ast/cil_ast.py +++ b/src/cool_cmp/cool/ast/cil_ast.py @@ -34,7 +34,12 @@ class UnaryArithmeticNode(InstructionNode): def __init__(self, dest, value) -> None: self.dest = dest self.value = value - + +class GetFatherNode(InstructionNode): + def __init__(self, dest, type) -> None: + self.dest = dest + self.type = type + class NotNode(UnaryArithmeticNode): pass diff --git a/src/cool_cmp/cool/pipes/pipes.py b/src/cool_cmp/cool/pipes/pipes.py index 724875867..f4740bda5 100644 --- a/src/cool_cmp/cool/pipes/pipes.py +++ b/src/cool_cmp/cool/pipes/pipes.py @@ -416,9 +416,9 @@ def cool_to_cil_pipe(result: dict, cool_to_cil=COOLToCILVisitor): return result errors = [] - cool_to_cil_visitor = cool_to_cil(context, errors) # TODO Ver los argumentos + cool_to_cil_visitor = cool_to_cil(context, errors) - cil_ast = cool_to_cil_visitor.visit(ast, scope) # TODO Devuelve un AST de CIL + cil_ast = cool_to_cil_visitor.visit(ast, scope) result['cil_ast'] = cil_ast diff --git a/src/cool_cmp/cool/visitors/cil_visitor.py b/src/cool_cmp/cool/visitors/cil_visitor.py index f849bdcbb..e7db05324 100644 --- a/src/cool_cmp/cool/visitors/cil_visitor.py +++ b/src/cool_cmp/cool/visitors/cil_visitor.py @@ -27,7 +27,7 @@ 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}}' + return f'type {node.name} {{\n\tparent: {node.parent}\n\t{attributes}\n\n\t{methods}\n}}' @visitor.when(cil.FunctionNode) def visit(self, node): @@ -165,6 +165,22 @@ def visit(self, node:cil.LesserNode): def visit(self, node:cil.VoidNode): return f'{node.dest} = VOID' + @visitor.when(cil.GetFatherNode) + def visit(self, node:cil.GetFatherNode): + return f'{node.dest} = FATHER {node.type}' + + @visitor.when(cil.ArrayNode) + def visit(self, node:cil.ArrayNode): + return f'{node.dest} = ARRAY {node.type} {node.length}' + + @visitor.when(cil.SetIndexNode) + def visit(self, node:cil.SetIndexNode): + return f'SETINDEX {node.source} {node.index} {node.value}' + + @visitor.when(cil.GetIndexNode) + def visit(self, node:cil.GetIndexNode): + return f'{node.dest} = GETINDEX {node.source} {node.index}' + class CILRunnerVisitor(): def __init__(self) -> None: @@ -182,16 +198,41 @@ def return_value(self, value): def next_instruction(self): return ("next", None) + def get_type(self, typex) -> cil.TypeNode: + if isinstance(typex, dict): + return typex["$type"] + try: + return self.types[typex] + except KeyError: + self.raise_error("Type {0} isn't defined", typex) + + def get_dynamic_type(self, type_name, caller_fun_scope): + if type_name in caller_fun_scope: # TODO Remove duality between Value and ReferencedValue + typex = self.get_type(caller_fun_scope[type_name]) + else: + typex = self.get_type(type_name) + return typex + def raise_error(self, message, *args): raise RunError(message, *args) + def create_type_instance(self, name): + typex = self.get_type(name) + return { + "$type": typex, + } + def get_value(self, source, function_scope): + value = None try: value = int(source) except ValueError: - if source not in function_scope: + if source in function_scope: + value = function_scope[source] + elif source in self.types: + value = self.create_type_instance(source) + else: self.raise_error("Variable {0} doesn't exist", source) - value = function_scope[source] return value def set_value(self, dest, value, function_scope): @@ -199,28 +240,25 @@ def set_value(self, dest, value, function_scope): self.raise_error("Variable {0} isn't defined", dest) function_scope[dest] = value - def get_value_str(self, source, function_scope, error): + def get_value_str(self, source, function_scope, error=None): value = self.get_value(source, function_scope) if not isinstance(value, str): + if not error: + error = f"String expected at {source}" self.raise_error(error) return value - def get_value_int(self, source, function_scope, error): + def get_value_int(self, source, function_scope, error=None): value = self.get_value(source, function_scope) if not isinstance(value, int): + if not error: + error = f"Int expected at {source}" self.raise_error(error) return value - - def get_type(self, type_name) -> cil.TypeNode: - try: - return self.types[type_name] - except KeyError: - self.raise_error("Type {0} isn't defined", type_name) - def binary_node(self, node, function_scope, func): - left = self.get_value_int(node.left, function_scope, "OPERATION not defined with non Int argument") - right = self.get_value_int(node.right, function_scope, "OPERATION not defined with non Int argument") + left = self.get_value(node.left, function_scope) + right = self.get_value(node.right, function_scope) value = func(left, right) self.set_value(node.dest, value, function_scope) return self.next_instruction() @@ -231,14 +269,15 @@ def visit(self, node): @visitor.when(cil.ProgramNode) def visit(self, node:cil.ProgramNode): - for t in node.dottypes: + for t in node.dottypes + [cil.TypeNode("$Array", None)]: self.visit(t) for t in node.dotdata: self.visit(t) for t in node.dotcode: self.visit(t) try: - return self.visit(cil.StaticCallNode("entry", "result"), [], {"result":None}) + result = self.visit(cil.StaticCallNode("entry", "result"), [], {"result":None}) + return result except RunError as er: self.errors.append(er) return None @@ -303,10 +342,7 @@ def visit(self, node:cil.StaticCallNode, args: list, caller_fun_scope: dict): @visitor.when(cil.DynamicCallNode) def visit(self, node, args: list, caller_fun_scope: dict): - if node.type in caller_fun_scope: # TODO Remove duality between Value and ReferencedValue - typex = self.get_type(caller_fun_scope[node.type]) - else: - typex = self.get_type(node.type) + typex = self.get_dynamic_type(node.type, caller_fun_scope) try: func_name = next(static_name for name, static_name in typex.methods if name == node.method) except StopIteration: @@ -355,28 +391,73 @@ def visit(self, node, args: list, caller_fun_scope: dict): @visitor.when(cil.AllocateNode) def visit(self, node, args: list, caller_fun_scope: dict): - if node.type in caller_fun_scope: # TODO Remove duality between Value and ReferencedValue - typex = self.get_type(caller_fun_scope[node.type]) - else: - typex = self.get_type(node.type) - value = { - "$type": typex, - } + typex = self.get_dynamic_type(node.type, caller_fun_scope) + + value = self.create_type_instance(typex.name) + for attr in typex.attributes: value[attr] = None self.set_value(node.dest, value, caller_fun_scope) return self.next_instruction() + @visitor.when(cil.GetFatherNode) + def visit(self, node:cil.GetFatherNode, args: list, caller_fun_scope: dict): + typex = self.get_dynamic_type(node.type, caller_fun_scope) + if typex.parent == None: + value = None + else: + value = self.create_type_instance(typex.parent) + + self.set_value(node.dest, value, caller_fun_scope) + return self.next_instruction() + + @visitor.when(cil.ArrayNode) + def visit(self, node:cil.ArrayNode, args: list, caller_fun_scope: dict): + array_type = self.get_dynamic_type(node.type, caller_fun_scope) + length = self.get_value_int(node.length, caller_fun_scope) + value = { + "$type": self.get_type("$Array"), + "length": length, + "items": [None for _ in range(length)] + } + self.set_value(node.dest, value, caller_fun_scope) + return self.next_instruction() + + @visitor.when(cil.SetIndexNode) + def visit(self, node:cil.SetIndexNode, args: list, caller_fun_scope: dict): + value = self.get_value(node.value, caller_fun_scope) + array = self.get_value(node.source, caller_fun_scope) + index = self.get_value_int(node.index, caller_fun_scope) + try: + array["items"][index] = value + except IndexError: + self.raise_error(f"Index {node.index} Out Of Range at {node.source}") + return self.next_instruction() + + @visitor.when(cil.GetIndexNode) + def visit(self, node:cil.GetIndexNode, args: list, caller_fun_scope: dict): + array = self.get_value(node.source, caller_fun_scope) + index = self.get_value_int(node.index, caller_fun_scope) + try: + value = array["items"][index] + self.set_value(node.dest, value, caller_fun_scope) + except IndexError: + self.raise_error(f"Index {node.index} Out Of Range at {node.source}") + + return self.next_instruction() + @visitor.when(cil.TypeOfNode) def visit(self, node, args: list, caller_fun_scope: dict): + # value = node.obj + # while isinstance(value, str): value = self.get_value(node.obj, caller_fun_scope) if isinstance(value, int): - self.set_value(node.dest, "Int", caller_fun_scope) + self.set_value(node.dest, self.create_type_instance("Int"), caller_fun_scope) elif isinstance(value, str): - self.set_value(node.dest, "String", caller_fun_scope) + self.set_value(node.dest, self.create_type_instance("String"), caller_fun_scope) else: - self.set_value(node.dest, value["$type"].name, caller_fun_scope) + self.set_value(node.dest, self.create_type_instance(value["$type"].name), caller_fun_scope) return self.next_instruction() @visitor.when(cil.GetAttribNode) @@ -503,7 +584,25 @@ def visit(self, node:cil.NotNode, args: list, caller_fun_scope: dict): @visitor.when(cil.EqualNode) def visit(self, node:cil.EqualNode, args: list, caller_fun_scope: dict): - return self.binary_node(node, caller_fun_scope, lambda x,y: x==y) + def equal(x,y): + if isinstance(x, int) and isinstance(y, int): + return x == y + if isinstance(x, str) and isinstance(y, str): + if len(x) == 1 and len(y) == 1: + return x == y + self.raise_error("Only character comparation available") + + if all(not isinstance(z, (int,str)) for z in [x,y]): + cmp1 = None + cmp2 = None + if x is not None: + cmp1 = x["$type"].name + if y is not None: + cmp2 = y["$type"].name + return cmp1 == cmp2 + else: + self.raise_error("OPERATION must have both argument ") + return self.binary_node(node, caller_fun_scope, equal) @visitor.when(cil.GreaterNode) def visit(self, node:cil.GreaterNode, args: list, caller_fun_scope: dict): @@ -582,8 +681,10 @@ def register_function(self, function_name): self.dotcode.append(function_node) return function_node - def register_type(self, name): - type_node = cil.TypeNode(name) + def register_type(self, name, parent): + if parent: + parent = parent.name + type_node = cil.TypeNode(name, parent) self.dottypes.append(type_node) return type_node @@ -593,17 +694,7 @@ def register_data(self, value): self.dotdata.append(data_node) return data_node - @visitor.on('node') - def visit(self, node): - pass - - @visitor.when(ProgramNode) - def visit(self, node, scope): - ###################################################### - # node.declarations -> [ ClassDeclarationNode ... ] - ###################################################### - - + def create_entry_function(self): self.current_function = self.register_function('entry') instance = self.define_internal_local() result = self.define_internal_local() @@ -615,6 +706,53 @@ def visit(self, node, scope): self.register_instruction(cil.StaticCallNode(main_method_name, result)) self.register_instruction(cil.ReturnNode("0")) self.current_function = None + + def create_type_distance_function(self): + self.current_function = self.register_function('type_distance') + type1 = "type1" + type2 = "type2" + self.params.append(cil.ParamNode(type1)) + self.params.append(cil.ParamNode(type2)) + + start_label = self.define_label() + end_label = self.define_label() + fail_label = self.define_label() + + index = self.define_internal_local() + equal = self.define_internal_local() + equal_void = self.define_internal_local() + void = self.define_internal_local() + self.register_instruction(cil.VoidNode(void)) + self.register_instruction(cil.AssignNode(index, "0")) + + self.register_instruction(start_label) + self.register_instruction(cil.EqualNode(equal, type1, type2)) + self.register_instruction(cil.GotoIfNode(equal, end_label.label)) + self.register_instruction(cil.GetFatherNode(type1, type1)) + self.register_instruction(cil.EqualNode(equal_void, type1, void)) + self.register_instruction(cil.GotoIfNode(equal_void, fail_label.label)) + self.register_instruction(cil.PlusNode(index, index, "1")) + self.register_instruction(cil.GotoNode(start_label.label)) + + self.register_instruction(fail_label) + self.register_instruction(cil.AssignNode(index, "-1")) + self.register_instruction(end_label) + self.register_instruction(cil.ReturnNode(index)) + + self.current_function = None + + @visitor.on('node') + def visit(self, node): + pass + + @visitor.when(ProgramNode) + def visit(self, node, scope): + ###################################################### + # node.declarations -> [ ClassDeclarationNode ... ] + ###################################################### + + self.create_entry_function() + self.create_type_distance_function() for type_name, typex in self.context.types.items(): if type_name in self.context.special_types and type_name not in ["Error", "Void"]: @@ -635,7 +773,7 @@ def visit(self, node, scope): self.current_type = self.context.get_type(node.id) - type_node = self.register_type(self.current_type.name) + type_node = self.register_type(self.current_type.name, self.current_type.parent) self.current_function = init_function = self.register_function(self.to_init_type_function_name(self.current_type.name)) type_node.methods.append(("$init", init_function.name)) @@ -861,14 +999,93 @@ def visit(self, node:LetNode, scope): return result @visitor.when(CheckNode) - def visit(self, node, scope): - # TODO - raise NotImplementedError() + def visit(self, node:CheckNode, scope): + result = self.visit(node.expr, scope) + return result @visitor.when(CaseNode) - def visit(self, node, scope): - # TODO - raise NotImplementedError() + def visit(self, node: CaseNode, scope): + + value = self.visit(node.expr, scope) + + type_value = self.define_internal_local() + self.register_instruction(cil.TypeOfNode(value, type_value)) + checks = len(node.params) + + array_types = self.define_internal_local() + self.register_instruction(cil.ArrayNode(array_types, "Int", str(checks))) # Type Int because at the end all are Ints + + for i,param in enumerate(node.params): + self.register_instruction(cil.SetIndexNode(array_types, str(i), param.type.name)) + + index = self.define_internal_local() + minim_index = self.define_internal_local() + minim = self.define_internal_local() + distance = self.define_internal_local() + current_type = self.define_internal_local() + self.register_instruction(cil.AssignNode(index, "-1")) + self.register_instruction(cil.AssignNode(minim, "-2")) + + start_label = self.define_label() + minim_label = self.define_label() + end_label = self.define_label() + abort_label = self.define_label() + stop_for = self.define_internal_local() + not_valid_distance = self.define_internal_local() + minim_cond = self.define_internal_local() + + self.register_instruction(start_label) + self.register_instruction(cil.PlusNode(index, index, "1")) + self.register_instruction(cil.EqualNode(stop_for, index, str(checks))) + self.register_instruction(cil.GotoIfNode(stop_for, end_label.label)) + + + self.register_instruction(cil.GetIndexNode(array_types, index, current_type)) + + self.register_instruction(cil.ArgNode(type_value)) + self.register_instruction(cil.ArgNode(current_type)) + self.register_instruction(cil.StaticCallNode("type_distance", distance)) + + self.register_instruction(cil.EqualNode(not_valid_distance, distance, "-1")) + self.register_instruction(cil.GotoIfNode(not_valid_distance, start_label.label)) + + self.register_instruction(cil.EqualNode(minim_cond, minim, "-2")) + self.register_instruction(cil.GotoIfNode(minim_cond, minim_label.label)) + self.register_instruction(cil.GreaterNode(minim_cond, minim, distance)) + self.register_instruction(cil.GotoIfNode(minim_cond, minim_label.label)) + self.register_instruction(cil.GotoNode(start_label.label)) + + self.register_instruction(minim_label) + self.register_instruction(cil.AssignNode(minim, distance)) + self.register_instruction(cil.AssignNode(minim_index, index)) + + self.register_instruction(cil.GotoNode(start_label.label)) + self.register_instruction(end_label) + + self.register_instruction(cil.EqualNode(minim_cond, minim, "-2")) + self.register_instruction(cil.GotoIfNode(minim_cond, abort_label.label)) + + self.register_instruction(cil.GetIndexNode(array_types, minim_index, current_type)) + + final_label = self.define_label() + not_equal_types = self.define_internal_local() + end_labels = [self.define_label() for _ in node.params] + for lbl, param, child_scope in zip(end_labels, node.params, scope.children): + self.register_instruction(cil.EqualNode(not_equal_types, param.type.name, current_type)) + self.register_instruction(cil.NotNode(not_equal_types, not_equal_types)) + self.register_instruction(cil.GotoIfNode(not_equal_types, lbl.label)) + + result = self.visit(param, child_scope) + self.register_instruction(cil.AssignNode(value, result)) + + self.register_instruction(cil.GotoNode(final_label.label)) + self.register_instruction(lbl) + + self.register_instruction(abort_label) + self.register_instruction(cil.AbortNode()) + self.register_instruction(final_label) + + return value @visitor.when(IsVoidNode) def visit(self, node:IsVoidNode, scope): diff --git a/src/testing.cl b/src/testing.cl index 8a495c7e8..c9d2de25a 100644 --- a/src/testing.cl +++ b/src/testing.cl @@ -22,11 +22,10 @@ class B inherits A { class Main inherits IO { main() : AUTO_TYPE { - out_string(let a:A <- new A, b:B <- new B in - b.get().type_name().concat( - b.get2().type_name()).concat( - b.get3().type_name()).concat( - b.get4().type_name()).concat( - type_name())) + out_int(case new B of + obj: Object => 0; + a: A => 1; + b: B => 2; + esac) }; }; diff --git a/src/testing.cl.cil b/src/testing.cl.cil index fb10b9dcc..1eb0c33e7 100644 --- a/src/testing.cl.cil +++ b/src/testing.cl.cil @@ -1,5 +1,6 @@ .TYPES type Object { + parent: None method $init: $init_Object_type @@ -8,6 +9,7 @@ type Object { method copy: function_copy_at_Object } type String { + parent: Object method $init: $init_String_type @@ -19,6 +21,7 @@ type String { method substr: function_substr_at_String } type Bool { + parent: Object method $init: $init_Bool_type @@ -27,6 +30,7 @@ type Bool { method copy: function_copy_at_Object } type Int { + parent: Object method $init: $init_Int_type @@ -35,6 +39,7 @@ type Int { method copy: function_copy_at_Object } type IO { + parent: Object method $init: $init_IO_type @@ -47,6 +52,7 @@ type IO { method in_int: function_in_int_at_IO } type A { + parent: Object attribute a method $init: $init_A_type @@ -59,6 +65,7 @@ type A { method get3: function_get3_at_A } type B { + parent: A attribute a method $init: $init_B_type @@ -72,6 +79,7 @@ type B { method get4: function_get4_at_B } type Main { + parent: IO method $init: $init_Main_type @@ -103,6 +111,30 @@ function entry { local_y_internal_1 = CALL function_main_at_Main RETURN 0 } +function type_distance { + PARAM type1 + PARAM type2 + + LOCAL local_distance_internal_0 + LOCAL local_distance_internal_1 + LOCAL local_distance_internal_2 + LOCAL local_distance_internal_3 + + local_distance_internal_3 = VOID + local_distance_internal_0 = 0 + LABEL distance_label_0 + local_distance_internal_1 = EQUAL type1 type2 + IF local_distance_internal_1 GOTO distance_label_1 + type1 = FATHER type1 + local_distance_internal_2 = EQUAL type1 local_distance_internal_3 + IF local_distance_internal_2 GOTO distance_label_2 + local_distance_internal_0 = local_distance_internal_0 + 1 + GOTO distance_label_0 + LABEL distance_label_2 + local_distance_internal_0 = -1 + LABEL distance_label_1 + RETURN local_distance_internal_0 +} function $init_Object_type { PARAM self @@ -314,9 +346,9 @@ function $init_Main_type { function function_main_at_Main { PARAM self - LOCAL local_main_at_Main_a_0 + LOCAL local_main_at_Main_internal_0 LOCAL local_main_at_Main_internal_1 - LOCAL local_main_at_Main_b_2 + LOCAL local_main_at_Main_internal_2 LOCAL local_main_at_Main_internal_3 LOCAL local_main_at_Main_internal_4 LOCAL local_main_at_Main_internal_5 @@ -327,52 +359,56 @@ function function_main_at_Main { LOCAL local_main_at_Main_internal_10 LOCAL local_main_at_Main_internal_11 LOCAL local_main_at_Main_internal_12 - LOCAL local_main_at_Main_internal_13 - LOCAL local_main_at_Main_internal_14 - LOCAL local_main_at_Main_internal_15 - LOCAL local_main_at_Main_internal_16 - LOCAL local_main_at_Main_internal_17 - local_main_at_Main_internal_1 = ALLOCATE A + local_main_at_Main_internal_0 = ALLOCATE Object + ARG local_main_at_Main_internal_0 + local_main_at_Main_internal_0 = CALL $init_Object_type + local_main_at_Main_internal_1 = TYPEOF local_main_at_Main_internal_0 + local_main_at_Main_internal_2 = ARRAY Int 2 + SETINDEX local_main_at_Main_internal_2 0 A + SETINDEX local_main_at_Main_internal_2 1 B + local_main_at_Main_internal_3 = -1 + local_main_at_Main_internal_5 = -2 + LABEL main_at_Main_label_0 + local_main_at_Main_internal_3 = local_main_at_Main_internal_3 + 1 + local_main_at_Main_internal_8 = EQUAL local_main_at_Main_internal_3 2 + IF local_main_at_Main_internal_8 GOTO main_at_Main_label_2 + local_main_at_Main_internal_7 = GETINDEX local_main_at_Main_internal_2 local_main_at_Main_internal_3 ARG local_main_at_Main_internal_1 - local_main_at_Main_internal_1 = CALL $init_A_type - local_main_at_Main_a_0 = local_main_at_Main_internal_1 - local_main_at_Main_internal_3 = ALLOCATE B - ARG local_main_at_Main_internal_3 - local_main_at_Main_internal_3 = CALL $init_B_type - local_main_at_Main_b_2 = local_main_at_Main_internal_3 - ARG local_main_at_Main_b_2 - local_main_at_Main_internal_4 = VCALL B get - ARG local_main_at_Main_internal_4 - local_main_at_Main_internal_5 = VCALL B type_name - ARG local_main_at_Main_b_2 - local_main_at_Main_internal_6 = VCALL B get2 - ARG local_main_at_Main_internal_6 - local_main_at_Main_internal_7 = VCALL B type_name - ARG local_main_at_Main_internal_5 ARG local_main_at_Main_internal_7 - local_main_at_Main_internal_8 = VCALL String concat - ARG local_main_at_Main_b_2 - local_main_at_Main_internal_9 = VCALL B get3 - ARG local_main_at_Main_internal_9 - local_main_at_Main_internal_10 = VCALL B type_name - ARG local_main_at_Main_internal_8 - ARG local_main_at_Main_internal_10 - local_main_at_Main_internal_11 = VCALL String concat - ARG local_main_at_Main_b_2 - local_main_at_Main_internal_12 = VCALL B get4 - ARG local_main_at_Main_internal_12 - local_main_at_Main_internal_13 = VCALL B type_name - ARG local_main_at_Main_internal_11 - ARG local_main_at_Main_internal_13 - local_main_at_Main_internal_14 = VCALL String concat - ARG self - local_main_at_Main_internal_15 = VCALL Main type_name - ARG local_main_at_Main_internal_14 - ARG local_main_at_Main_internal_15 - local_main_at_Main_internal_16 = VCALL String concat + local_main_at_Main_internal_6 = CALL type_distance + local_main_at_Main_internal_9 = EQUAL local_main_at_Main_internal_6 -1 + IF local_main_at_Main_internal_9 GOTO main_at_Main_label_0 + local_main_at_Main_internal_10 = EQUAL local_main_at_Main_internal_5 -2 + IF local_main_at_Main_internal_10 GOTO main_at_Main_label_1 + local_main_at_Main_internal_10 = local_main_at_Main_internal_5 > local_main_at_Main_internal_6 + IF local_main_at_Main_internal_10 GOTO main_at_Main_label_1 + GOTO main_at_Main_label_0 + LABEL main_at_Main_label_1 + local_main_at_Main_internal_5 = local_main_at_Main_internal_6 + local_main_at_Main_internal_4 = local_main_at_Main_internal_3 + GOTO main_at_Main_label_0 + LABEL main_at_Main_label_2 + local_main_at_Main_internal_10 = EQUAL local_main_at_Main_internal_5 -2 + IF local_main_at_Main_internal_10 GOTO main_at_Main_label_3 + local_main_at_Main_internal_7 = GETINDEX local_main_at_Main_internal_2 local_main_at_Main_internal_4 + local_main_at_Main_internal_11 = EQUAL A local_main_at_Main_internal_7 + local_main_at_Main_internal_11 = NOT local_main_at_Main_internal_11 + IF local_main_at_Main_internal_11 GOTO main_at_Main_label_5 + local_main_at_Main_internal_0 = 1 + GOTO main_at_Main_label_4 + LABEL main_at_Main_label_5 + local_main_at_Main_internal_11 = EQUAL B local_main_at_Main_internal_7 + local_main_at_Main_internal_11 = NOT local_main_at_Main_internal_11 + IF local_main_at_Main_internal_11 GOTO main_at_Main_label_6 + local_main_at_Main_internal_0 = 2 + GOTO main_at_Main_label_4 + LABEL main_at_Main_label_6 + LABEL main_at_Main_label_3 + ABORT + LABEL main_at_Main_label_4 ARG self - ARG local_main_at_Main_internal_16 - local_main_at_Main_internal_17 = VCALL Main out_string - RETURN local_main_at_Main_internal_17 + ARG local_main_at_Main_internal_0 + local_main_at_Main_internal_12 = VCALL Main out_int + RETURN local_main_at_Main_internal_12 } \ No newline at end of file From 710e372d9b1acfee553d3becea89ef3b5b8d4e64 Mon Sep 17 00:00:00 2001 From: Luiso Date: Sat, 30 Oct 2021 17:10:38 -0400 Subject: [PATCH 048/143] GitHub testing fixes --- src/makefile | 12 ++++++++++++ 1 file changed, 12 insertions(+) create mode 100644 src/makefile diff --git a/src/makefile b/src/makefile new file mode 100644 index 000000000..cd83fb352 --- /dev/null +++ b/src/makefile @@ -0,0 +1,12 @@ +.PHONY: clean + +main: + # Compiling the compiler :) + +clean: + rm -rf build/* + rm -rf ../tests/*/*.mips + +test: + pytest ../tests -v --tb=short -m=${TAG} + From 2d6e9860f62c408c157fd17d8256252311d25c09 Mon Sep 17 00:00:00 2001 From: Luiso Date: Sun, 31 Oct 2021 06:25:44 -0400 Subject: [PATCH 049/143] Fixing requirements --- requirements.txt | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/requirements.txt b/requirements.txt index c250faba6..0bc228eac 100644 --- a/requirements.txt +++ b/requirements.txt @@ -1,2 +1,6 @@ pytest pytest-ordering +nbformat==4.4.0 +plac==1.1.3 +streamlit==0.51.0 +pydot==1.4.1 \ No newline at end of file From eb10cce26a444514deedfc8760c7d14e0817e2cd Mon Sep 17 00:00:00 2001 From: Luiso Date: Sun, 31 Oct 2021 19:15:28 -0400 Subject: [PATCH 050/143] MIPS Update - MIPS Infrastructure added - MIPS Documentation added - MIPS Example code added TODO: - Complete CIL to MIPS visitor --- .vscode/launch.json | 3 +- doc/MIPS_Instruction_Set.pdf | Bin 0 -> 149425 bytes doc/Register_use_convention.md | 36 +++ doc/TODO.md | 1 + src/cool_cmp/cool/pipeline.py | 21 +- src/cool_cmp/cool/pipes/pipes.py | 35 ++- src/cool_cmp/main.py | 12 +- src/cool_cmp/mips/__init__.py | 0 src/cool_cmp/mips/ast/mips_ast.py | 199 +++++++++++++++ src/cool_cmp/mips/error/errors.py | 16 ++ src/cool_cmp/mips/example_code/fun_call.asm | 84 +++++++ .../mips/example_code/hello_world.asm | 28 +++ src/cool_cmp/mips/registers.py | 50 ++++ src/cool_cmp/mips/visitors/mips_visitors.py | 235 ++++++++++++++++++ src/testing.cl.cil | 27 +- src/testing.cl.mips | 17 ++ 16 files changed, 741 insertions(+), 23 deletions(-) create mode 100644 doc/MIPS_Instruction_Set.pdf create mode 100644 doc/Register_use_convention.md create mode 100644 src/cool_cmp/mips/__init__.py create mode 100644 src/cool_cmp/mips/ast/mips_ast.py create mode 100644 src/cool_cmp/mips/error/errors.py create mode 100644 src/cool_cmp/mips/example_code/fun_call.asm create mode 100644 src/cool_cmp/mips/example_code/hello_world.asm create mode 100644 src/cool_cmp/mips/registers.py create mode 100644 src/cool_cmp/mips/visitors/mips_visitors.py create mode 100644 src/testing.cl.mips diff --git a/.vscode/launch.json b/.vscode/launch.json index 27040bd3f..4c5c0ce57 100644 --- a/.vscode/launch.json +++ b/.vscode/launch.json @@ -9,7 +9,8 @@ "type": "python", "request": "launch", "program": "src/cool_cmp/main.py", - "args": ["src/testing.cl", "src/testing.mips", "-icil", "-c"], + "args": ["src/testing.cl", "src/testing.mips", "-m"], + // "args": ["src/testing.cl", "src/testing.mips", "-icil", "-c"], "console": "integratedTerminal" } ] diff --git a/doc/MIPS_Instruction_Set.pdf b/doc/MIPS_Instruction_Set.pdf new file mode 100644 index 0000000000000000000000000000000000000000..d34ae7c62ddb89c62c800966c5a4a9703b9434da GIT binary patch literal 149425 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