예제 #1
0
    def test_tuple_of_tuple_constant(self):
        input = ((1, 2), (3, 4, 5, 6), (7, ))
        expected_output = (
            Opcode.INITSSLOT + b'\x01'
            # tuple[2]
            + Opcode.PUSH7  # 7
            + Opcode.PUSH1  # tuple length
            + Opcode.PACK
            # tuple[1]
            + Opcode.PUSH6  # 6
            + Opcode.PUSH5  # 7
            + Opcode.PUSH4  # 4
            + Opcode.PUSH3  # 3
            + Opcode.PUSH4  # tuple length
            + Opcode.PACK
            # tuple[0]
            + Opcode.PUSH2  # 2
            + Opcode.PUSH1  # 1
            + Opcode.PUSH2  # tuple length
            + Opcode.PACK + Opcode.PUSH3  # tuple length
            + Opcode.PACK + Opcode.DROP + Opcode.RET)

        analyser = Analyser(ast.parse(str(input)))
        analyser.symbol_table['x'] = Variable(Type.any)
        output = CodeGenerator.generate_code(analyser)

        self.assertEqual(expected_output, output)
예제 #2
0
    def test_list_of_list_constant(self):
        input = [[1, 2], [3, 4, 5, 6], [7]]
        expected_output = (
            # list[2]
            Opcode.INITSSLOT + b'\x01' + Opcode.PUSH7  # 7
            + Opcode.PUSH1  # list length
            + Opcode.PACK
            # list[1]
            + Opcode.PUSH6  # 6
            + Opcode.PUSH5  # 5
            + Opcode.PUSH4  # 4
            + Opcode.PUSH3  # 3
            + Opcode.PUSH4  # list length
            + Opcode.PACK
            # list[0]
            + Opcode.PUSH2  # 2
            + Opcode.PUSH1  # 1
            + Opcode.PUSH2  # list length
            + Opcode.PACK + Opcode.PUSH3  # list length
            + Opcode.PACK + Opcode.DROP + Opcode.RET)

        analyser = Analyser(ast.parse(str(input)))
        analyser.symbol_table['x'] = Variable(Type.any)
        output = CodeGenerator.generate_code(analyser)

        self.assertEqual(expected_output, output)
예제 #3
0
    def test_integer_list_constant(self):
        input = [1, 2, 3]
        expected_output = (
            Opcode.INITSSLOT + b'\x01' + Opcode.PUSH3  # 3
            + Opcode.PUSH2  # 2
            + Opcode.PUSH1  # 1
            + Opcode.PUSH3  # list length
            + Opcode.PACK + Opcode.DROP + Opcode.RET)

        analyser = Analyser(ast.parse(str(input)))
        analyser.symbol_table['x'] = Variable(Type.any)
        output = CodeGenerator.generate_code(analyser)

        self.assertEqual(expected_output, output)
예제 #4
0
    def _analyse(self, path: str, log: bool = True):
        """
        Load a Python file and analyses its syntax

        :param path: the path of the Python file to compile
        :param log: if compiler errors should be logged.
        """
        self._analyser = Analyser.analyse(path, log)
예제 #5
0
    def test_any_list_constant(self):
        input = [1, '2', False]
        byte_input1 = String(input[1]).to_bytes()

        expected_output = (
            Opcode.INITSSLOT + b'\x01' + Opcode.PUSH0  # False
            + Opcode.PUSHDATA1  # '2'
            + Integer(len(byte_input1)).to_byte_array() + byte_input1 +
            Opcode.PUSH1  # 1
            + Opcode.PUSH3  # list length
            + Opcode.PACK + Opcode.DROP + Opcode.RET)

        analyser = Analyser(ast.parse(str(input)))
        analyser.symbol_table['x'] = Variable(Type.any)
        output = CodeGenerator.generate_code(analyser)

        self.assertEqual(expected_output, output)
예제 #6
0
    def test_integer_list_constant(self):
        input = [1, 2, 3]
        node = ast.parse(str(input)).body[0].value
        expected_output = ListType(Type.int)

        typeanalyser = TypeAnalyser(Analyser(node), {})
        output = typeanalyser.get_type(input)

        self.assertEqual(expected_output, output)
예제 #7
0
    def test_any_tuple_constant(self):
        input = (1, '2', False)
        node = ast.parse(str(input)).body[0].value
        expected_output = TupleType(UnionType.build([Type.str, Type.int]))

        typeanalyser = TypeAnalyser(Analyser(node), {})
        output = typeanalyser.get_type(input)

        self.assertEqual(expected_output, output)
예제 #8
0
    def test_string_tuple_constant(self):
        input = (1, 2, 3)
        node = ast.parse(str(input)).body[0].value
        expected_output = TupleType(Type.int)

        typeanalyser = TypeAnalyser(Analyser(node), {})
        output = typeanalyser.get_type(input)

        self.assertEqual(expected_output, output)
예제 #9
0
    def test_boolean_tuple_constant(self):
        input = (True, False)
        node = ast.parse(str(input)).body[0].value
        expected_output = TupleType(Type.bool)

        typeanalyser = TypeAnalyser(Analyser(node), {})
        output = typeanalyser.get_type(input)

        self.assertEqual(expected_output, output)
예제 #10
0
    def test_string_constant(self):
        input = 'unit_test'
        node = ast.parse(str(input)).body[0].value
        expected_output = Type.str

        typeanalyser = TypeAnalyser(Analyser(node), {})
        output = typeanalyser.get_type(input)

        self.assertEqual(expected_output, output)
예제 #11
0
    def test_negative_integer_constant(self):
        input = -10
        node = ast.parse(str(input)).body[0].value
        expected_output = Type.int

        typeanalyser = TypeAnalyser(Analyser(node), {})
        output = typeanalyser.get_type(input)

        self.assertEqual(expected_output, output)
예제 #12
0
    def _analyse(self, path: str, root_folder: str = None, log: bool = True):
        """
        Load a Python file and analyses its syntax

        :param path: the path of the Python file to compile
        :param root_folder: the root path of the project
        :param log: if compiler errors should be logged.
        """
        self._analyser = Analyser.analyse(path, log=log, root=root_folder)
예제 #13
0
    def test_any_list_constant(self):
        input = [1, '2', False]
        node = ast.parse(str(input)).body[0].value
        expected_output = ListType(Type.any)

        typeanalyser = TypeAnalyser(Analyser(node), {})
        output = typeanalyser.get_type(input)

        self.assertEqual(expected_output, output)
예제 #14
0
    def __init__(self, import_target: str):
        self.can_be_imported: bool = False
        self._import_identifier: str = import_target

        try:
            module_origin: str = importlib.util.find_spec(import_target).origin
        except BaseException:
            return

        path: List[str] = module_origin.split(os.sep)
        self.path: str = module_origin.replace(os.sep, '/')

        super().__init__(ast.Module(body=[]), path[-1])
        if import_target == 'typing':
            self.symbols.update(
                {symbol_id: symbol for symbol_id, symbol in self._get_types_from_typing_lib().items()
                 if symbol_id not in Type.builtin_types()
                 })
            self.can_be_imported = True

        else:
            import re

            inside_python_folder = any(re.search(r'python(\d\.?)*', folder.lower()) for folder in path)
            updated_tree = None

            if 'boa3' in path and '/'.join(path[path.index('boa3'):]).startswith('boa3/builtin'):
                pkg_start_index = path.index('builtin') + 1
                if path[pkg_start_index] == path[-1]:
                    self.symbols = self._get_boa3_builtin_symbols()
                else:
                    pkg = import_target.split('.')
                    pkg = pkg[pkg.index('builtin') + 1:]
                    self.symbols = self._get_boa3_builtin_package(pkg)
                self.can_be_imported = True

            elif not (inside_python_folder and 'lib' in path):
                # TODO: only user modules and typing lib imports are implemented
                try:
                    from boa3.analyser.analyser import Analyser
                    analyser = Analyser.analyse(module_origin)

                    # include only imported symbols
                    if analyser.is_analysed:
                        self.symbols.update(
                            {symbol_id: symbol for symbol_id, symbol in analyser.symbol_table.items()
                             if symbol_id not in Type.all_types()
                             })
                    updated_tree = analyser.ast_tree
                    self.can_be_imported = analyser.is_analysed
                except FileNotFoundError:
                    self.can_be_imported = False

                if updated_tree is not None:
                    self._tree = updated_tree
예제 #15
0
    def test_string_tuple_constant(self):
        input = ('1', '2', '3')
        byte_input0 = String(input[0]).to_bytes()
        byte_input1 = String(input[1]).to_bytes()
        byte_input2 = String(input[2]).to_bytes()

        expected_output = (
            Opcode.INITSSLOT + b'\x01' + Opcode.PUSHDATA1  # '3'
            + Integer(len(byte_input2)).to_byte_array() + byte_input2 +
            Opcode.PUSHDATA1  # '2'
            + Integer(len(byte_input1)).to_byte_array() + byte_input1 +
            Opcode.PUSHDATA1  # '1'
            + Integer(len(byte_input0)).to_byte_array() + byte_input0 +
            Opcode.PUSH3  # tuple length
            + Opcode.PACK + Opcode.DROP + Opcode.RET)

        analyser = Analyser(ast.parse(str(input)))
        analyser.symbol_table['x'] = Variable(Type.any)
        output = CodeGenerator.generate_code(analyser)

        self.assertEqual(expected_output, output)
예제 #16
0
    def _find_package(self,
                      module_origin: str,
                      origin_file: Optional[str] = None):
        path: List[str] = module_origin.split(os.sep)

        package = imports.builtin.get_package(self._import_identifier)
        if hasattr(package, 'symbols'):
            if hasattr(package, 'inner_packages'):
                # when have symbol and packages with the same id, prioritize symbol
                self.symbols: Dict[str, ISymbol] = package.inner_packages
                self.symbols.update(package.symbols)
            else:
                self.symbols = package.symbols

            self.can_be_imported = True
            self.is_builtin_import = True
            return

        if (os.path.commonpath([self.path, constants.BOA_PACKAGE_PATH
                                ]) != constants.BOA_PACKAGE_PATH
                or ('boa3' in path and constants.PATH_SEPARATOR.join(
                    path[path.index('boa3'):]).startswith('boa3/builtin'))):
            # doesn't analyse boa3.builtin packages that aren't included in the imports.builtin as an user module
            # TODO: refactor when importing from user modules is accepted
            import re

            inside_python_folder = any(
                re.search(r'python(\d\.?)*', folder.lower())
                for folder in path)
            updated_tree = None

            if not (inside_python_folder and 'lib' in path):
                # check circular imports to avoid recursions inside the compiler
                if self.path in self._import_stack:
                    self.recursive_import = True
                    return

                # TODO: only user modules and typing lib imports are implemented
                try:
                    if self.path in self._imported_files:
                        analyser = self._imported_files[self.path]
                    else:
                        from boa3.analyser.analyser import Analyser
                        origin = origin_file.replace(os.sep,
                                                     constants.PATH_SEPARATOR)
                        files = self._import_stack
                        files.append(origin)
                        if self.is_namespace_package:
                            analyser = Analyser(self.tree, module_origin,
                                                self.root_folder, self._log)
                            if self._include_inner_packages(analyser):
                                analyser.is_analysed = True
                                self._imported_files[self.path] = analyser
                        else:
                            analyser = Analyser.analyse(
                                module_origin,
                                root=self.root_folder,
                                imported_files=self._imported_files,
                                import_stack=files,
                                log=self._log)
                            self._include_inner_packages(analyser)

                        if analyser.is_analysed:
                            self._imported_files[self.path] = analyser

                    # include only imported symbols
                    if analyser.is_analysed:
                        for symbol_id, symbol in analyser.symbol_table.items():
                            if symbol_id not in Type.all_types():
                                if not self._get_from_entry:
                                    symbol.defined_by_entry = False
                                self.symbols[symbol_id] = symbol

                    self.errors.extend(analyser.errors)
                    self.warnings.extend(analyser.warnings)

                    updated_tree = analyser.ast_tree
                    self.analyser = analyser
                    self.can_be_imported = analyser.is_analysed
                except FileNotFoundError:
                    self.can_be_imported = False

                if updated_tree is not None:
                    self._tree = updated_tree