Пример #1
0
    def find_struct(struct_name: str) -> Optional[RIALIdentifiedStructType]:
        # Search with name
        struct = ParserState.search_structs(struct_name)

        # Search with current module specifier
        if struct is None:
            struct = ParserState.search_structs(
                f"{ParserState.module().name}:{struct_name}")

        # Iterate through usings
        if struct is None:
            structs_found: List[Tuple] = list()
            for using in ParserState.module().dependencies:
                s = ParserState.search_structs(f"{using}:{struct_name}")

                if s is not None:
                    structs_found.append((using, s))
            if len(structs_found) == 0:
                return None

            if len(structs_found) > 1:
                log_fail(f"Multiple declarations found for {struct_name}")
                log_fail(
                    f"Specify one of them by using {structs_found[0][0]}:{struct_name} for example"
                )
                return None
            struct = structs_found[0][1]

        return struct
Пример #2
0
    def external_function_decl(self, tree: Tree):
        nodes = tree.children
        access_modifier: RIALAccessModifier = nodes[0].access_modifier
        unsafe: bool = nodes[0].unsafe
        linkage = "external"
        calling_convention = "ccc"
        return_type = nodes[1].value
        name = nodes[2].value

        # External functions cannot be declared in a struct
        if self.llvmgen.current_struct is not None:
            log_fail(f"External function {name} cannot be declared inside a class!")
            raise Discard()

        args: List[RIALVariable] = list()
        var_args = False
        i = 3
        while i < len(nodes):
            if var_args is True:
                log_fail("PARAMS must be last in arguments")
                break
            if nodes[i].type == "PARAMS":
                var_args = True
                i += 1
            if nodes[i].type == "IDENTIFIER":
                arg_type = nodes[i].value
                i += 1
                arg_name = nodes[i].value

                if var_args:
                    arg_name += "..."

                args.append(RIALVariable(arg_name, arg_type, None))
                i += 1

        if not unsafe and not self.llvmgen.currently_unsafe:
            raise PermissionError("Can only declare external functions in unsafe blocks or as unsafe functions.")

        # Map RIAL args to llvm arg types
        llvm_args = [arg.llvm_type for arg in args if not arg.name.endswith("...")]

        # Hasn't been declared previously, redeclare the function type here
        llvm_return_type = ParserState.map_type_to_llvm(return_type)
        func_type = self.llvmgen.create_function_type(llvm_return_type, llvm_args, var_args)

        # Create the actual function in IR
        func = self.llvmgen.create_function_with_type(name, name, func_type, linkage,
                                                      calling_convention,
                                                      FunctionDefinition(return_type, access_modifier, args, "",
                                                                         unsafe))

        # Update backing values
        for i, arg in enumerate(func.args):
            args[i].backing_value = arg

        raise Discard()
Пример #3
0
    def compile_ir(self, module: RIALModule) -> ModuleRef:
        with self.lock:
            llvm_ir = str(module)
            try:
                mod = self.binding.parse_assembly(llvm_ir)
                mod.verify()
            except Exception as e:
                log_fail(llvm_ir)
                raise e

            self._optimize_module(mod)

        return mod
Пример #4
0
    def switch_block(self, tree: Tree):
        nodes = tree.children
        parent = self.llvmgen.current_block
        variable: RIALVariable = self.transform_helper(nodes[0])
        end_block = self.llvmgen.create_block(
            f"{self.llvmgen.current_block.block.name}.end_switch",
            sibling=self.llvmgen.current_block)
        self.llvmgen.end_block = end_block
        switch = self.llvmgen.builder.switch(variable.raw_backing_value, None)
        collected_empties = list()

        i = 1
        while i < len(nodes):
            var, block = self.transform_helper(nodes[i])

            # Check if just case, collect for future use
            if block is None:
                collected_empties.append(var)
            else:
                self.llvmgen.enter_block_end(block)

                # Check if default case and set as default
                if var is None:
                    if switch.default is not None:
                        log_fail(
                            f"{ParserState.module().name} Duplicate default case in switch statement"
                        )
                    else:
                        switch.default = block.block
                else:
                    switch.add_case(var, block.block)

                # Add all collected cases
                for collected_empty in collected_empties:
                    switch.add_case(collected_empty, block.block)

                collected_empties.clear()

            self.llvmgen.enter_block_end(parent)

            i += 1

        if switch.default is None:
            switch.default = end_block.block

        if len(collected_empties) > 0:
            log_fail("Empty cases in switch statement!")

        self.llvmgen.end_block = None
        self.llvmgen.enter_block(end_block)
Пример #5
0
    def map_type_to_llvm_no_pointer(name: str):
        llvm_type = map_type_to_llvm(name)

        # Check if builtin type
        if llvm_type is None:
            llvm_type = ParserState.find_struct(name)

            if llvm_type is None:
                # Arrays
                match = re.match(r"^([^\[]+)\[([0-9]+)?\]$", name)

                if match is not None:
                    ty = match.group(1)
                    count = match.group(2)
                    if count is not None:
                        return ir.ArrayType(ParserState.map_type_to_llvm(ty),
                                            int(count))
                    else:
                        return ParserState.map_type_to_llvm(ty).as_pointer()

                # Function type
                match = re.match(r"^([^(]+)\(([^,)]+\s*,?\s*)*\)$", name)

                if match is not None:
                    return_type = ""
                    arg_types = list()
                    var_args = False
                    for i, group in enumerate(match.groups()):
                        if i == 0:
                            return_type = group.strip()
                        elif group == "...":
                            var_args = True
                        elif group is None:
                            break
                        else:
                            arg_types.append(group.strip())

                    return ir.FunctionType(
                        ParserState.map_type_to_llvm(return_type), [
                            ParserState.map_type_to_llvm(arg)
                            for arg in arg_types
                        ], var_args)

                log_fail(f"Referenced unknown type {name}")
                return None

        return llvm_type
Пример #6
0
    def constructor_call(self, tree: Tree):
        nodes = tree.children
        name = nodes[0]
        struct = ParserState.find_struct(name)

        if struct is None:
            log_fail(f"Instantiation of unknown type {name}")

        arguments = list()

        instantiated = self.llvmgen.builder.alloca(struct)
        instantiated = RIALVariable(name, name, instantiated)
        arguments.append(instantiated)

        arguments.extend(self.transform_helper(nodes[1]))
        llvm_args = [arg.backing_value for arg in arguments]

        constructor_name = mangle_function_name(
            "constructor", [arg.llvm_type for arg in arguments], name)

        try:
            call_instr = self.llvmgen.gen_function_call([constructor_name],
                                                        llvm_args)

            if call_instr is None:
                log_fail(
                    f"Failed to generate call to function {constructor_name}")
                return NULL

            return instantiated
        except IndexError:
            log_fail("Missing argument in function call")

        return NULL
Пример #7
0
    def return_rule(self, tree: Tree):
        nodes = tree.children

        if self.llvmgen.current_block.block.terminator is not None:
            log_fail(f"'return' after return found!")
            return None

        variable: RIALVariable = self.transform_helper(nodes[0])

        if variable.rial_type != self.llvmgen.current_func.definition.rial_return_type:
            raise TypeError(
                variable,
                self.llvmgen.current_func.definition.rial_return_type)

        if isinstance(variable.raw_llvm_type, ir.VoidType):
            self.llvmgen.builder.ret_void()
        elif variable.is_primitive:
            self.llvmgen.builder.ret(variable.raw_backing_value)
        else:
            self.llvmgen.builder.ret(variable.backing_value)

        return variable
Пример #8
0
    def function_call(self, tree: Tree):
        nodes = tree.children
        full_function_name: str
        function_name: str

        # Function call specialisation
        if isinstance(nodes[0], Tree):
            return self.visit(nodes[0])

        function_name = nodes[0].value
        arguments: List[RIALVariable] = self.transform_helper(nodes[1])

        arg_types = [arg.llvm_type for arg in arguments]

        try:
            call_instr = self.llvmgen.gen_function_call([
                mangle_function_name(function_name, arg_types), function_name
            ], [arg.backing_value for arg in arguments])

            if call_instr is None:
                log_fail(
                    f"Failed to generate call to function {function_name}")
                return NULL

            rial_func = call_instr.callee

            if isinstance(rial_func, RIALFunction):
                return RIALVariable(f"{rial_func.name}_call",
                                    rial_func.definition.rial_return_type,
                                    call_instr)
            return RIALVariable(f"{rial_func.name}_call", "Unknown",
                                call_instr)
        except IndexError:
            log_fail("Missing argument in function call")

        return NULL
Пример #9
0
    def nested_function_call(self, tree: Tree):
        nodes = tree.children
        i = 0
        full_name = ""
        arguments = list()
        while i < len(nodes):
            if not isinstance(nodes[i], Token):
                break

            full_name += f".{nodes[i].value}"
            i += 1

        implicit_parameter_name = '.'.join(full_name.split('.')[0:-1])
        function_name = full_name.split('.')[-1]
        implicit_parameter: RIALVariable = self.llvmgen.get_definition(
            implicit_parameter_name)

        if implicit_parameter is None:
            log_fail(
                f"Could not find implicit parameter {implicit_parameter_name} in function call {full_name}"
            )
            return NULL

        arguments.append(implicit_parameter)
        arguments.extend(self.transform_helper(nodes[i]))

        arg_types = [arg.llvm_type for arg in arguments]
        mangled_names = list()

        # Generate mangled names for implicit parameter and derived
        if implicit_parameter is not None:
            ty = implicit_parameter.rial_type

            # Check if it's a builtin type
            if ty in ParserState.builtin_types:
                mangled_names.append(
                    mangle_function_name(function_name, arg_types, ty))
            else:
                mangled_names.append(
                    mangle_function_name(function_name, arg_types, ty))

                struct = ParserState.find_struct(ty)

                # Also mangle base structs to see if it's a derived function
                for base_struct in struct.definition.base_structs:
                    arg_tys = arg_types
                    arg_tys.pop(0)
                    arg_tys.insert(0, base_struct)
                    mangled_names.append(
                        mangle_function_name(function_name, arg_types,
                                             base_struct))
        else:
            mangled_names.append(mangle_function_name(function_name,
                                                      arg_types))

        try:
            call_instr = self.llvmgen.gen_function_call(
                [*mangled_names, function_name],
                [arg.backing_value for arg in arguments])

            if call_instr is None:
                log_fail(
                    f"Failed to generate call to function {function_name}")
                return NULL

            rial_func = call_instr.callee

            if isinstance(rial_func, RIALFunction):
                return RIALVariable(f"{rial_func.name}_call",
                                    rial_func.definition.rial_return_type,
                                    call_instr)
            return RIALVariable(f"{rial_func.name}_call", "Unknown",
                                call_instr)
        except IndexError:
            log_fail(
                f"Missing argument in function call to function {function_name}"
            )

        return NULL
Пример #10
0
    def extension_function_decl(self, tree: Tree):
        nodes = tree.children
        access_modifier: RIALAccessModifier = nodes[0].access_modifier
        unsafe: bool = nodes[0].unsafe
        linkage = access_modifier.get_linkage()
        calling_convention = self.default_cc
        return_type = nodes[1].value
        name = nodes[2].value

        # Extension functions cannot be declared inside other classes.
        if self.llvmgen.current_struct is not None:
            log_fail(f"Extension function {name} cannot be declared inside another class!")
            raise Discard()

        if not self.mangling:
            log_fail(f"Extension function {name} does not qualify for no mangling.")
            raise Discard()

        args: List[RIALVariable] = list()
        this_arg = map_shortcut_to_type(nodes[3].value)
        has_body = False

        args.append(RIALVariable(nodes[4].value, nodes[3].value, None))

        i = 5
        while i < len(nodes):
            if not isinstance(nodes[i], Token):
                has_body = True
                break
            if nodes[i].type == "IDENTIFIER":
                arg_type = nodes[i].value
                i += 1
                arg_name = nodes[i].value
                args.append(RIALVariable(arg_name, arg_type, None))
                i += 1
            else:
                break

        # Map RIAL args to llvm arg types
        llvm_args = [arg.llvm_type for arg in args if not arg.name.endswith("...")]

        full_function_name = mangle_function_name(name, llvm_args, this_arg)
        full_function_name = f"{ParserState.module().name}:{full_function_name}"

        # Hasn't been declared previously, redeclare the function type here
        llvm_return_type = ParserState.map_type_to_llvm(return_type)
        func_type = self.llvmgen.create_function_type(llvm_return_type, llvm_args, False)

        # Create the actual function in IR
        func = self.llvmgen.create_function_with_type(full_function_name, name, func_type, linkage,
                                                      calling_convention,
                                                      FunctionDefinition(return_type, access_modifier, args,
                                                                         self.llvmgen.current_struct is not None and self.llvmgen.current_struct.name or "",
                                                                         unsafe))

        # Update backing values
        for i, arg in enumerate(func.args):
            args[i].backing_value = arg

        if is_builtin_type(this_arg):
            if this_arg not in ParserState.builtin_types:
                ParserState.builtin_types[this_arg] = dict()

            ParserState.builtin_types[this_arg][func.name] = func

            if not this_arg in ParserState.module().builtin_type_methods:
                ParserState.module().builtin_type_methods[this_arg] = list()
            ParserState.module().builtin_type_methods[this_arg].append(func.name)
        else:
            struct = ParserState.find_struct(this_arg)

            if struct is None:
                log_fail(f"Extension function for non-existing type {this_arg}")
                ParserState.module().functions.remove(func)
                raise Discard()

            struct.definition.functions.append(func.name)

        if not has_body:
            raise Discard()

        token = nodes[2]
        metadata_token = MetadataToken(token.type, token.value)
        metadata_token.metadata["func"] = func
        metadata_token.metadata["body_start"] = i
        nodes.remove(token)
        nodes.insert(0, metadata_token)

        return Tree('function_decl', nodes)
Пример #11
0
def compiler():
    global exceptions
    exceptions = False

    if not CompilationManager.config.raw_opts.disable_cache:
        Cache.load_cache()

    if CompilationManager.config.raw_opts.file is not None:
        path = Path(CompilationManager.config.raw_opts.file)
    else:
        path = CompilationManager.config.rial_path.joinpath(
            "builtin").joinpath("start.rial")

    if not path.exists():
        raise FileNotFoundError(str(path))

    # Clear global data that can sometimes remain (especially during test execution).
    context.global_context.scope._useset.clear()
    context.global_context.identified_types.clear()

    # Collect all always imported paths
    builtin_path = str(
        CompilationManager.config.rial_path.joinpath("builtin").joinpath(
            "always_imported"))
    for file in [
            join(builtin_path, f) for f in listdir(builtin_path)
            if isfile(join(builtin_path, f))
    ]:
        CompilationManager.request_file(file)
        module_name = CompilationManager.mod_name_from_path(
            CompilationManager.filename_from_path(str(file)))
        CompilationManager.always_imported.append(module_name)

    # Request main file
    CompilationManager.request_file(str(path))

    if CompilationManager.config.raw_opts.profile_gil:
        import gil_load
        gil_load.init()
        gil_load.start()

    futures = list()
    with concurrent.futures.ThreadPoolExecutor() as executor:
        while True:
            for future in futures:
                if future.done():
                    futures.remove(future)

            try:
                # This timeout is so small, it shouldn't matter.
                # However it serves a crucial role in regards to Python's GIL.
                # Setting this on a higher timeout obviously means that there may be some excessive time spent waiting
                # for nothing to appear.
                # BUT, setting it to non-blocking means that the GIL never gives any of the compilation threads priority
                # as well as holding up the internal threading.Lock(s).
                # So setting this timeout so small means not much time is spent waiting for nothing, but it gives other
                # things the opportunity to get control of the GIL and execute.
                path = CompilationManager.files_to_compile.get(timeout=0.01)
                future = executor.submit(compile_file, path)
                futures.append(future)
            except Empty:
                pass

            if len(futures) == 0 and CompilationManager.files_to_compile.empty(
            ):
                break
        executor.shutdown(True)

    CompilationManager.files_to_compile.join()

    if CompilationManager.config.raw_opts.profile_gil:
        import gil_load
        gil_load.stop()
        print(f"Main Thread ID: {threading.current_thread().ident}")
        print(gil_load.format(gil_load.get()))

    if exceptions:
        return

    if not CompilationManager.config.raw_opts.disable_cache:
        Cache.save_cache()

    execute_stage(Stage.POST_IR_GEN)

    modules: Dict[str, ModuleRef] = dict()

    for key, mod in CompilationManager.modules.items():
        if not CompilationManager.config.raw_opts.disable_cache:
            cache_path = str(
                CompilationManager.get_cache_path_str(key)).replace(
                    ".rial", ".cache")
            if not Path(cache_path).exists():
                CompilationManager.codegen.save_module(mod, cache_path)

        with run_with_profiling(CompilationManager.filename_from_path(key),
                                ExecutionStep.COMPILE_MOD):
            try:
                modules[key] = CompilationManager.codegen.compile_ir(mod)
            except Exception as e:
                log_fail(f"Exception when compiling module {mod.name}")
                log_fail(e)
                log_fail(traceback.format_exc())
                return

    object_files: List[str] = list()
    CompilationManager.codegen.generate_final_modules(list(modules.values()))

    for path in list(modules.keys()):
        mod = modules[path]

        if CompilationManager.config.raw_opts.print_ir:
            ir_file = str(
                CompilationManager.get_output_path_str(path)).replace(
                    ".rial", ".ll")
            if check_needs_output(mod.name, ir_file):
                CompilationManager.codegen.save_ir(ir_file, mod)

        if CompilationManager.config.raw_opts.print_asm:
            asm_file = str(
                CompilationManager.get_cache_path_str(path)).replace(
                    ".rial", ".asm")
            if check_needs_output(mod.name, asm_file):
                CompilationManager.codegen.save_assembly(asm_file, mod)

        if not CompilationManager.config.raw_opts.use_object_files:
            llvm_bitcode_file = str(
                CompilationManager.get_output_path_str(path)).replace(
                    ".rial", ".o")
            if check_needs_output(mod.name, llvm_bitcode_file):
                CompilationManager.codegen.save_llvm_bitcode(
                    llvm_bitcode_file, mod)
            object_files.append(llvm_bitcode_file)
        else:
            object_file = str(
                CompilationManager.get_output_path_str(path)).replace(
                    ".rial", ".o")
            if check_needs_output(mod.name, object_file):
                CompilationManager.codegen.save_object(object_file, mod)
            object_files.append(object_file)

    with run_with_profiling(CompilationManager.config.project_name,
                            ExecutionStep.LINK_EXE):
        exe_path = str(
            CompilationManager.config.bin_path.joinpath(
                f"{CompilationManager.config.project_name}{Platform.get_exe_file_extension()}"
            ))

        Linker.link_files(
            object_files, exe_path,
            CompilationManager.config.raw_opts.print_link_command,
            CompilationManager.config.raw_opts.strip)
Пример #12
0
def compile_file(path: str):
    global exceptions
    try:
        last_modified = os.path.getmtime(path)
        filename = CompilationManager.filename_from_path(path)

        if check_cache(path):
            CompilationManager.finish_file(path)
            CompilationManager.files_to_compile.task_done()
            return

        module_name = CompilationManager.mod_name_from_path(filename)
        module = CompilationManager.codegen.get_module(
            module_name, filename, str(CompilationManager.config.source_path))
        ParserState.set_module(module)
        ParserState.set_llvmgen(LLVMGen())

        if not ParserState.module().name.startswith(
                "rial:builtin:always_imported"):
            ParserState.module().dependencies.extend(
                CompilationManager.always_imported)

        # Remove the current module in case it's in the always imported list
        if module_name in ParserState.module().dependencies:
            ParserState.module().dependencies.remove(module_name)

        with run_with_profiling(filename, ExecutionStep.READ_FILE):
            with open(path, "r") as file:
                contents = file.read()

        desugar_transformer = DesugarTransformer()
        primitive_transformer = PrimitiveASTTransformer()
        parser = Lark_StandAlone(postlex=Postlexer())

        # Parse the file
        with run_with_profiling(filename, ExecutionStep.PARSE_FILE):
            try:
                ast = parser.parse(contents)
            except Exception as e:
                log_fail(f"Exception when parsing {filename}")
                log_fail(e)
                exceptions = True
                CompilationManager.finish_file(path)
                CompilationManager.files_to_compile.task_done()
                return

        if CompilationManager.config.raw_opts.print_tokens:
            print(ast.pretty())

        # Generate primitive IR (things we don't need other modules for)
        with run_with_profiling(filename, ExecutionStep.GEN_IR):
            ast = desugar_transformer.transform(ast)
            ast = primitive_transformer.transform(ast)

        struct_declaration_transformer = StructDeclarationTransformer()
        function_declaration_transformer = FunctionDeclarationTransformer()
        global_declaration_transformer = GlobalDeclarationTransformer()
        transformer = ASTVisitor()

        with run_with_profiling(filename, ExecutionStep.GEN_IR):
            ast = struct_declaration_transformer.visit(ast)

            if ast is not None:
                ast = function_declaration_transformer.visit(ast)

            if ast is not None:
                ast = global_declaration_transformer.visit(ast)

            # Declarations are all already collected so we can move on.
            CompilationManager.modules[str(path)] = ParserState.module()
            CompilationManager.finish_file(path)

            if ast is not None:
                transformer.visit(ast)

        cache_path = str(CompilationManager.get_cache_path_str(path)).replace(
            ".rial", ".cache")
        if Path(cache_path).exists(
        ) and CompilationManager.config.raw_opts.disable_cache:
            os.remove(cache_path)
        elif not CompilationManager.config.raw_opts.disable_cache:
            Cache.cache_module(ParserState.module(), path, cache_path,
                               last_modified)

        CompilationManager.files_to_compile.task_done()
    except Exception as e:
        log_fail("Internal Compiler Error: ")
        log_fail(traceback.format_exc())
        exceptions = True
        CompilationManager.finish_file(path)
        CompilationManager.files_to_compile.task_done()
Пример #13
0
    def find_function(
            full_function_name: str,
            rial_arg_types: List[str] = None) -> Optional[RIALFunction]:
        # Check by canonical name if we got args to check
        if rial_arg_types is not None:
            func = ParserState.module().get_function(full_function_name)

            # If couldn't find it, iterate through usings and try to find function
            if func is None:
                functions_found: List[Tuple[str, RIALFunction]] = list()

                for use in ParserState.module().dependencies:
                    path = CompilationManager.path_from_mod_name(use)
                    if path not in CompilationManager.modules:
                        continue
                    module = CompilationManager.modules[path]

                    function = module.get_function(full_function_name)

                    if function is None:
                        continue
                    functions_found.append((
                        use,
                        function,
                    ))

                # Check each function if the arguments match the passed arguments
                if len(functions_found) > 1:
                    for tup in functions_found:
                        function = tup[1]
                        matches = True

                        for i, arg in enumerate(function.definition.rial_args):
                            if arg[0] != rial_arg_types[i]:
                                matches = False
                                break
                        if matches:
                            func = function
                            break

                # Check for number of functions found
                elif len(functions_found) == 1:
                    func = functions_found[0][1]

            if func is not None:
                # Function is in current module and only a declaration, safe to assume that it's a redeclared function
                # from another module or originally declared in this module anyways
                if func.module.name != ParserState.module(
                ).name and not func.is_declaration:
                    # Function cannot be accessed if:
                    #   - Function is not public and
                    #   - Function is internal but not in same TLM (top level module) or
                    #   - Function is private but not in same module
                    func_def: FunctionDefinition = func.definition
                    if func_def.access_modifier != RIALAccessModifier.PUBLIC and \
                            ((func_def.access_modifier == RIALAccessModifier.INTERNAL and
                              func.module.name.split(':')[0] != ParserState.module().name.split(':')[0]) or
                             (func_def.access_modifier == RIALAccessModifier.PRIVATE and
                              func.module.name != ParserState.module().name)):
                        log_fail(
                            f"Cannot access method {full_function_name} in module {func.module.name}!"
                        )
                        return None

        # Try to find function in current module
        func: RIALFunction = ParserState.module().get_global_safe(
            full_function_name)

        # Try to find function in current module with module specifier
        if func is None:
            func = ParserState.module().get_global_safe(
                f"{ParserState.module().name}:{full_function_name}")

        # If func isn't in current module
        if func is None:
            # If couldn't find it, iterate through usings and try to find function
            if func is None:
                functions_found: List[Tuple[str, RIALFunction]] = list()

                for use in ParserState.module().dependencies:
                    path = CompilationManager.path_from_mod_name(use)
                    if path not in CompilationManager.modules:
                        continue
                    module = CompilationManager.modules[path]

                    function = module.get_global_safe(full_function_name)

                    if function is None:
                        function = module.get_global_safe(
                            f"{use}:{full_function_name}")

                    if function is None:
                        continue
                    functions_found.append((
                        use,
                        function,
                    ))

                if len(functions_found) > 1:
                    log_fail(
                        f"Function {full_function_name} has been declared multiple times!"
                    )
                    log_fail(
                        f"Specify the specific function to use by adding the namespace to the function call"
                    )
                    log_fail(
                        f"E.g. {functions_found[0][0]}:{full_function_name}()")
                    return None

                # Check for number of functions found
                if len(functions_found) == 1:
                    func = functions_found[0][1]

            if func is not None:
                # Function is in current module and only a declaration, safe to assume that it's a redeclared function
                # from another module or originally declared in this module anyways
                if func.module.name != ParserState.module(
                ).name and not func.is_declaration:
                    # Function cannot be accessed if:
                    #   - Function is not public and
                    #   - Function is internal but not in same TLM (top level module) or
                    #   - Function is private but not in same module
                    func_def: FunctionDefinition = func.definition
                    if func_def.access_modifier != RIALAccessModifier.PUBLIC and \
                            ((func_def.access_modifier == RIALAccessModifier.INTERNAL and
                              func.module.name.split(':')[0] != ParserState.module().name.split(':')[0]) or
                             (func_def.access_modifier == RIALAccessModifier.PRIVATE and
                              func.module.name != ParserState.module().name)):
                        log_fail(
                            f"Cannot access method {full_function_name} in module {func.module.name}!"
                        )
                        return None

        return func
Пример #14
0
    def break_rule(self, tree: Tree):
        if self.llvmgen.end_block is None:
            log_fail(f"'break' outside of loop or conditional block")
            return None

        return self.llvmgen.create_jump(self.llvmgen.end_block)
Пример #15
0
    def continue_rule(self, tree: Tree):
        if self.llvmgen.conditional_block is None:
            log_fail(f"'continue' outside of loop")
            return None

        return self.llvmgen.create_jump(self.llvmgen.conditional_block)
Пример #16
0
    def struct_decl(self, tree: Tree):
        nodes: List = tree.children
        access_modifier = nodes[0].access_modifier
        name = nodes[1].value

        full_name = f"{ParserState.module().name}:{name}"

        if ParserState.search_structs(full_name) is not None:
            log_fail(f"Struct {full_name} has been previously declared!")
            raise Discard()

        body: List[RIALVariable] = list()
        function_decls: List[Tree] = list()
        bases: List[str] = list()

        # Find body of struct (variables)
        i = 2
        while i < len(nodes):
            node: Tree = nodes[i]

            if isinstance(node,
                          Tree) and node.data == "struct_property_declaration":
                variable = node.children
                acc_modifier = variable[0].access_modifier
                rial_type = variable[1].value
                variable_name = variable[2].value
                variable_value = None

                if len(variable) > 3:
                    variable_value = variable[3]

                body.append(
                    RIALVariable(variable_name,
                                 rial_type,
                                 backing_value=variable_value,
                                 access_modifier=acc_modifier))
            elif isinstance(node, Tree) and node.data == "function_decl":
                function_decls.append(node)
            elif isinstance(node, Token) and node.type == "IDENTIFIER":
                bases.append(node.value)
            i += 1

        base_llvm_structs = list()

        for base in bases:
            llvm_struct = ParserState.find_struct(base)

            if llvm_struct is not None:
                base_llvm_structs.append(llvm_struct)
            else:
                log_fail(f"Derived from undeclared type {base}")

        base_constructor = Tree('function_decl', [
            RIALFunctionDeclarationModifier(access_modifier),
            Token('IDENTIFIER', "void"),
            Token('IDENTIFIER', "constructor"), *[
                Tree('function_call', [
                    Token(
                        'IDENTIFIER',
                        mangle_function_name("constructor", [base],
                                             base.name)),
                    Tree('function_args', [
                        Tree('cast', [
                            Token('IDENTIFIER', base.name),
                            Tree('var', [Token('IDENTIFIER', "this")])
                        ])
                    ])
                ]) for base in base_llvm_structs
            ], *[
                Tree('variable_assignment', [
                    Tree('var', [Token('IDENTIFIER', f"this.{bod.name}")]),
                    Token('ASSIGN', '='), bod.backing_value
                ]) for bod in body
            ],
            Tree('return', [Token('IDENTIFIER', "void")])
        ])
        function_decls.insert(0, base_constructor)

        llvm_struct = self.llvmgen.create_identified_struct(
            full_name, access_modifier.get_linkage(), access_modifier,
            base_llvm_structs, body)

        declared_functions = list()

        # Create functions
        for function_decl in function_decls:
            metadata_node = self.fdt.visit(function_decl)
            if metadata_node is not None:
                declared_functions.append(metadata_node)
            try:
                nodes.remove(function_decl)
            except ValueError:
                pass

        self.llvmgen.finish_struct()

        node: Token = nodes[1]
        md_node = MetadataToken(node.type, node.value)

        md_node.metadata['struct_name'] = full_name
        md_node.metadata['functions'] = declared_functions
        nodes.remove(node)
        nodes.insert(0, md_node)

        return Tree('struct_decl', nodes)
Пример #17
0
    def function_decl(self, tree: Tree):
        nodes = tree.children

        # Function specialisation
        if isinstance(nodes[0], Tree):
            return self.visit(nodes[0])

        access_modifier: RIALAccessModifier = nodes[0].access_modifier
        unsafe: bool = nodes[0].unsafe
        linkage = access_modifier.get_linkage()
        main_function = False
        calling_convention = self.default_cc
        return_type = nodes[1].value
        name = nodes[2].value

        args: List[RIALVariable] = list()

        # Add class as implicit parameter
        if self.llvmgen.current_struct is not None:
            args.append(RIALVariable("this", self.llvmgen.current_struct.name, None))

        i = 3
        has_body = False

        while i < len(nodes):
            if not isinstance(nodes[i], Token):
                has_body = True
                break
            if nodes[i].type == "IDENTIFIER":
                arg_type = nodes[i].value
                i += 1
                arg_name = nodes[i].value
                args.append(RIALVariable(arg_name, arg_type, None))
                i += 1
            else:
                break

        # Map RIAL args to llvm arg types
        llvm_args = [arg.llvm_type for arg in args if not arg.name.endswith("...")]

        # If the function has the NoMangleAttribute we need to use the normal name
        if not self.mangling:
            full_function_name = name
        else:
            if self.llvmgen.current_struct is not None:
                full_function_name = mangle_function_name(name, llvm_args,
                                                          self.llvmgen.current_struct.name)
            else:
                full_function_name = mangle_function_name(name, llvm_args)
                full_function_name = f"{ParserState.module().name}:{full_function_name}"

        # Check if main method
        if full_function_name.endswith("main:main") and full_function_name.count(':') == 2:
            main_function = True

            # Check that main method returns either Int32 or void
            if return_type != "Int32" and return_type != "void":
                log_fail(f"Main method must return an integer status code or void, {return_type} given!")

        # Search for function in the archives
        # func = ParserState.search_function(full_function_name)

        # Function has been previously declared in other module
        # if func is not None and (func.module.name != ParserState.module().name or not func.is_declaration):
        #     # Check if either:
        #     # - has no body (cannot redeclare functions) or
        #     # - is already implemented and
        #     #   - is either public or
        #     #     - internal and
        #     #     - is in same package (cannot reimplement functions)
        #     # TODO: LLVM checks this anyways but maybe we wanna do it, too?
        #     log_fail(f"Function {full_function_name} already declared elsewhere")
        #     raise Discard()

        # Hasn't been declared previously, redeclare the function type here
        llvm_return_type = ParserState.map_type_to_llvm(return_type)
        func_type = self.llvmgen.create_function_type(llvm_return_type, llvm_args, False)

        # Create the actual function in IR
        func = self.llvmgen.create_function_with_type(full_function_name, name, func_type, linkage,
                                                      calling_convention,
                                                      FunctionDefinition(return_type, access_modifier, args,
                                                                         self.llvmgen.current_struct is not None and self.llvmgen.current_struct.name or "",
                                                                         unsafe))

        # Update backing values
        for i, arg in enumerate(func.args):
            args[i].backing_value = arg

        if self.llvmgen.current_struct is not None:
            self.llvmgen.current_struct.definition.functions.append(func.name)

        # Always inline the main function into the compiler supplied one
        if main_function:
            func.attributes.add('alwaysinline')

        # If it has no body we do not need to go through it later as it's already declared with this method.
        if not has_body:
            raise Discard()

        token = nodes[2]
        metadata_token = MetadataToken(token.type, token.value)
        metadata_token.metadata["func"] = func
        metadata_token.metadata["body_start"] = i
        nodes.remove(token)
        nodes.insert(0, metadata_token)

        return Tree('function_decl', nodes)