Beispiel #1
0
def get_kernel_ast(module_name, alg_filename, kernel_path, line_length):
    '''Search for the kernel source code containing a module with the name
    'module_name' looking in the directory and subdirectories
    associated with the supplied 'kernel_path' or in the same
    directory as the 'algorithm_filename' if the kernel path is
    empty. If the file is found then check it conforms to the
    'line_length' restriction if this is set and then parse this file
    and return the parsed file.

    :param str module_name: the name of the module to search for.
    :param str alg_filename: the name of the algorithm file.
    :param str kernel_path: directory in which to search for the module \
    file.
    :param bool line_length: whether to check that the kernel code \
    conforms to the 132 character line length limit (True) or not \
    (False).

    :returns: Parse tree of the kernel module with the name 'module_name'
    :rtype: :py:class:`fparser.one.block_statements.BeginSource`

    '''
    filepath = get_kernel_filepath(module_name, kernel_path, alg_filename)
    if line_length:
        check_line_length(filepath)
    parse_tree = get_kernel_parse_tree(filepath)
    return parse_tree
Beispiel #2
0
    def parse(self, alg_filename):
        '''Takes a PSyclone conformant algorithm file as input and outputs a
        parse tree of the code contained therein and an object
        containing information about the 'invoke' calls in the
        algorithm file and any associated kernels within the invoke
        calls. If the NEMO API is being used then the parsed code is
        returned without any additional information about the code.

        :param str alg_filename: The file containing the algorithm code.

        :returns: 2-tuple consisting of the fparser2 parse tree of the \
            algorithm code and an object holding details of the \
            algorithm code and the invokes found within it, unless it \
            is the NEMO API, where the first entry of the tuple is \
            None and the second is the fparser2 parse tree of the \
            code.
        :rtype: (:py:class:`fparser.two.Fortran2003.Program`, \
            :py:class:`psyclone.parse.FileInfo`) or (NoneType, \
            :py:class:`fparser.two.Fortran2003.Program`)

        '''
        self._alg_filename = alg_filename
        if self._line_length:
            # Make sure the code conforms to the line length limit.
            check_line_length(alg_filename)
        alg_parse_tree = parse_fp2(alg_filename)

        if self._api == "nemo":
            # For this API we just parse the NEMO code and return the resulting
            # fparser2 AST with None for the Algorithm AST.
            return None, alg_parse_tree

        return alg_parse_tree, self.invoke_info(alg_parse_tree)
Beispiel #3
0
def test_no_file():
    '''Check that an exception is raised as expected when the file
    supplied to the check_line_length function does not exist.

    '''
    with pytest.raises(InternalError) as excinfo:
        check_line_length("file_does_not_exist")
    assert ("In utils.py:check_line_length: [Errno 2] No such file or "
            "directory: 'file_does_not_exist'") in str(excinfo.value)
Beispiel #4
0
def test_line_length_too_long():
    '''Check that a file containing a long comment
    raises a ParseError.

    '''
    with tempfile.NamedTemporaryFile(mode='w') as tmp_file:
        tmp_file.write('''
            ! A fortran line that is too long... {}
        '''.format('a' * 100))
        tmp_file.flush()
        with pytest.raises(ParseError) as excinfo:
            check_line_length(tmp_file.name)
    expected = 'file does not conform to the specified 132 line length limit'
    assert expected in str(excinfo.value)
Beispiel #5
0
def test_line_length_unicode():
    '''Check that a file containing unicode character comments
    parses correctly.

    Note: This test failed with Python >3,<3.7 before explicit codecs
          were defined in the open(filename, ...) call.
    '''
    kwargs = dict(encoding='utf8') if six.PY3 else {}
    with tempfile.NamedTemporaryFile(mode='w', **kwargs) as tmp_file:
        content = u'''
            ! A fortran comment with a unicode character "{}"
        '''.format(u"\u2014")
        if six.PY3:
            tmp_file.write(content)
        else:
            tmp_file.write(content.encode('utf8'))
        tmp_file.flush()

        assert check_line_length(tmp_file.name) is None
Beispiel #6
0
    def parse(self, alg_filename):
        '''Takes a PSyclone conformant algorithm file as input and outputs a
        parse tree of the code contained therein and an object containing
        information about the 'invoke' calls in the algorithm file and any
        associated kernels within the invoke calls.

        :param str alg_filename: The file containing the algorithm code.
        :returns: 2-tuple consisting of the fparser2 parse tree of the \
        algorithm code and an object holding details of the algorithm \
        code and the invokes found within it.
        :rtype: (:py:class:`fparser.two.Fortran2003.Program`, \
                 :py:class:`psyclone.parse.FileInfo`)
        :raises ParseError: if a program, module, subroutine or \
        function is not found in the input file.

        '''
        self._alg_filename = alg_filename

        if self._line_length:
            # Make sure the code conforms to the line length limit.
            check_line_length(alg_filename)

        alg_parse_tree = parse_fp2(alg_filename)

        if self._api == "nemo":
            # For this API we just parse the NEMO code and return the resulting
            # fparser2 AST with None for the Algorithm AST.
            return None, alg_parse_tree

        # Find the first program, module, subroutine or function in the
        # parse tree. The assumption here is that the first is the one
        # that is required. See issue #307.
        container_name = None
        for child in alg_parse_tree.content:
            if isinstance(child, (Main_Program, Module, Subroutine_Subprogram,
                                  Function_Subprogram)):
                container_name = str(child.content[0].items[1])
                break

        if not container_name:
            # Nothing relevant found.
            raise ParseError(
                "algorithm.py:parser:parse: Program, module, function or "
                "subroutine not found in parse tree for file "
                "'{0}'".format(alg_filename))

        self._unique_invoke_labels = []
        self._arg_name_to_module_name = OrderedDict()
        invoke_calls = []

        for statement in walk(alg_parse_tree.content):

            if isinstance(statement, Use_Stmt):
                # found a Fortran use statement
                self.update_arg_to_module_map(statement)

            if isinstance(statement, Call_Stmt):
                # found a Fortran call statement
                call_name = str(statement.items[0])
                if call_name.lower() == self._invoke_name.lower():
                    # The call statement is an invoke
                    invoke_call = self.create_invoke_call(statement)
                    invoke_calls.append(invoke_call)

        return alg_parse_tree, FileInfo(container_name, invoke_calls)