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)
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)
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)
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)
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)
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)
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)
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)
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)
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)
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)
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)
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)
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
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)
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