Beispiel #1
0
def test_array_notation_rank():
    ''' Check that the _array_notation_rank() utility handles various examples
    of array notation.

    TODO #754 fix test so that 'disable_declaration_check' fixture is not
    required.
    '''
    fake_parent = Schedule()
    processor = Fparser2Reader()
    reader = FortranStringReader("  z1_st(:, 2, :) = ptsu(:, :, 3)")
    fparser2spec = Fortran2003.Assignment_Stmt(reader)
    processor.process_nodes(fake_parent, [fparser2spec])
    assert processor._array_notation_rank(fake_parent[0].lhs) == 2
    reader = FortranStringReader("  z1_st(:, :, 2, :) = ptsu(:, :, :, 3)")
    fparser2spec = Fortran2003.Assignment_Stmt(reader)
    processor.process_nodes(fake_parent, [fparser2spec])
    assert processor._array_notation_rank(fake_parent[1].lhs) == 3
    # We don't support bounds on slices
    reader = FortranStringReader("  z1_st(:, 1:n, 2, :) = ptsu(:, :, :, 3)")
    fparser2spec = Fortran2003.Assignment_Stmt(reader)
    processor.process_nodes(fake_parent, [fparser2spec])
    with pytest.raises(NotImplementedError) as err:
        processor._array_notation_rank(fake_parent[2].lhs)
    assert ("Only array notation of the form my_array(:, :, ...) is "
            "supported." in str(err.value))
def test_brackets_array_constructor(left, right):
    ''' Test parsing of array constructor specified with both valid types of
    bracket. '''
    fcode = "array = {0} 1, 2, 3{1}".format(left, right)
    reader = FortranStringReader(fcode)
    ast = Fortran2003.Assignment_Stmt(reader)
    assert isinstance(ast, Fortran2003.Assignment_Stmt)
    assert isinstance(ast.children[2], Fortran2003.Array_Constructor)
    assert isinstance(ast.children[2].children[1], Fortran2003.Ac_Value_List)
    assert "array = {0}1, 2, 3{1}".format(left, right) in str(ast)
    # Invalid content between valid brackets
    fcode = "array = {0}call hello(){1}".format(left, right)
    reader = FortranStringReader(fcode)
    ast = Fortran2003.Assignment_Stmt(reader)
    assert ast is None
def test_handling_literal_precision_2(value, dprecision, intrinsic):
    '''Check that the fparser2 frontend can handle literals with a
    specified precision value.

    TODO #754 fix test so that 'disable_declaration_check' fixture is not
    required.
    '''
    if intrinsic == ScalarType.Intrinsic.CHARACTER:
        code = "x={0}_{1}".format(dprecision, value)
    else:
        code = "x={0}_{1}".format(value, dprecision)
    reader = FortranStringReader(code)
    astmt = Fortran2003.Assignment_Stmt(reader)
    fake_parent = Schedule()
    processor = Fparser2Reader()
    processor.process_nodes(fake_parent, [astmt])
    assert not fake_parent.walk(CodeBlock)
    literal = fake_parent.children[0].children[1]
    assert isinstance(literal, Literal)
    assert literal.datatype.intrinsic == intrinsic
    if intrinsic == ScalarType.Intrinsic.BOOLEAN:
        assert ".{0}.".format(literal.value) == value.lower()
    else:
        assert literal.value == value
    assert isinstance(literal.datatype.precision, int)
    assert literal.datatype.precision == dprecision
def test_1d_array_not_implicit_loop():
    ''' Check that we do not identify the use of array-notation in 1D loops
    as being implicit loops (since we don't know what the loop is over). '''
    code = "z1d(:) =  1.0d0"
    reader = FortranStringReader(code)
    assign = Fortran2003.Assignment_Stmt(reader)
    assert not nemo.NemoImplicitLoop.match(assign)
Beispiel #5
0
def test_get_int_array_constructor_err(monkeypatch):
    ''' Check that we raise the appropriate error if we fail to parse the
    array constructor expression. '''
    from fparser.two import Fortran2003
    # First create a valid KernelType object
    ast = parse(DIFF_BASIS, ignore_comments=False)
    ktype = KernelType(ast)
    # Create a valid fparser2 result
    assign = Fortran2003.Assignment_Stmt("gh_evaluator_targets(2) = [1, 2]")
    # Break the array constructor expression (tuples are immutable so make a
    # new one)
    assign.items[2].items[1].items = tuple(["hello", "goodbye"])

    # Use monkeypatch to ensure that that's the result that is returned
    # when we attempt to use fparser2 from within the routine under test

    def my_init(self, _):
        ''' dummy class '''
        self.items = assign.items

    monkeypatch.setattr(Fortran2003.Assignment_Stmt, "__init__", my_init)
    with pytest.raises(InternalError) as err:
        _ = ktype.get_integer_array("gh_evaluator_targets")
    assert ("Failed to parse array constructor: '[hello, goodbye]'"
            in str(err.value))
def test_handling_literal_precision_1(value, dprecision, intrinsic):
    '''Check that the fparser2 frontend can handle literals with a
    specified precision kind symbol.

    '''
    if intrinsic == ScalarType.Intrinsic.CHARACTER:
        code = "x={0}_{1}".format(dprecision, value)
    else:
        code = "x={0}_{1}".format(value, dprecision)
    reader = FortranStringReader(code)
    astmt = Fortran2003.Assignment_Stmt(reader)
    fake_parent = Schedule()
    # Ensure the symbol table has an entry for "x"
    fake_parent.symbol_table.add(
        DataSymbol("x", ScalarType(ScalarType.Intrinsic.INTEGER, 4)))
    processor = Fparser2Reader()
    processor.process_nodes(fake_parent, [astmt])
    assert not fake_parent.walk(CodeBlock)
    literal = fake_parent.children[0].children[1]
    assert isinstance(literal, Literal)
    assert literal.datatype.intrinsic == intrinsic
    if intrinsic == ScalarType.Intrinsic.BOOLEAN:
        assert ".{0}.".format(literal.value) == value.lower()
    else:
        assert literal.value == value
    assert isinstance(literal.datatype.precision, DataSymbol)
    assert literal.datatype.precision.name == dprecision
    assert isinstance(literal.datatype.precision.datatype, ScalarType)
    assert (literal.datatype.precision.datatype.intrinsic ==
            ScalarType.Intrinsic.INTEGER)
def test_call_not_implicit_loop():
    ''' Check we do not incorrectly identify an implicit loop when array
    notation is used in the arguments to a function call. '''
    code = "z3d(1,:,:) =  ptr_sjk( pvtr(:,:,:), btmsk(:,:,jn)*btm30(:,:) )"
    reader = FortranStringReader(code)
    assign = Fortran2003.Assignment_Stmt(reader)
    assert not nemo.NemoImplicitLoop.match(assign)
Beispiel #8
0
    def get_integer_variable(self, name):
        ''' Parse the kernel meta-data and find the value of the
        integer variable with the supplied name. Return None if no
        matching variable is found.

        :param str name: the name of the integer variable to find.

        :returns: value of the specified integer variable or None.
        :rtype: str

        :raises ParseError: if the RHS of the assignment is not a Name.

        '''
        # Ensure the Fortran2003 parser is initialised
        _ = ParserFactory().create()

        for statement, _ in fpapi.walk(self._ktype, -1):
            if isinstance(statement, fparser1.typedecl_statements.Integer):
                # fparser only goes down to the statement level. We use
                # fparser2 to parse the statement itself (eventually we'll
                # use fparser2 to parse the whole thing).
                assign = Fortran2003.Assignment_Stmt(statement.entity_decls[0])
                if str(assign.items[0]) == name:
                    if not isinstance(assign.items[2], Fortran2003.Name):
                        raise ParseError(
                            "get_integer_variable: RHS of assignment is not "
                            "a variable name: '{0}'".format(str(assign)))
                    return str(assign.items[2])
        return None
Beispiel #9
0
def test_get_int_array_err1(monkeypatch):
    ''' Tests that we raise the correct error if there is something wrong
    with the assignment statement obtained from fparser2. '''
    from fparser.two import Fortran2003
    # This is difficult as we have to break the result returned by fparser2.
    # We therefore create a valid KernelType object
    ast = fpapi.parse(MDATA, ignore_comments=False)
    ktype = KernelType(ast)
    # Next we create a valid fparser2 result
    my_assign = Fortran2003.Assignment_Stmt("my_array(2) = [1, 2]")
    # Break its `items` property by replacing the Name object with a string
    # (tuples are immutable so make a new one)
    broken_items = tuple(["invalid"] + list(my_assign.items[1:]))

    # Use monkeypatch to ensure that that the Assignment_Stmt that
    # is returned when we attempt to use fparser2 from within the
    # routine under test now has the broken tuple of items.

    def my_init(self, _):
        ''' dummy class '''
        self.items = broken_items
    monkeypatch.setattr(Fortran2003.Assignment_Stmt, "__init__", my_init)

    with pytest.raises(InternalError) as err:
        _ = ktype.get_integer_array("gh_evaluator_targets")
    assert "Unsupported assignment statement: 'invalid = [1, 2]'" in str(err)
Beispiel #10
0
def test_get_int_array_section_subscript_err(monkeypatch):
    ''' Check that we raise the appropriate error if the parse tree for the
    LHS of the array declaration is broken. '''
    from fparser.two import Fortran2003
    # First create a valid KernelType object
    ast = parse(DIFF_BASIS, ignore_comments=False)
    ktype = KernelType(ast)
    # Create a valid fparser2 result
    assign = Fortran2003.Assignment_Stmt("gh_evaluator_targets(2) = [1, 2]")
    # Break the array constructor expression by replacing the
    # Section_Subscript_List with a str
    assign.children[0].items = (assign.children[0].items[0], "hello")

    # Use monkeypatch to ensure that that's the result that is returned
    # when we attempt to use fparser2 from within the routine under test

    def my_init(self, _):
        ''' dummy constructor '''
        self.items = assign.items

    monkeypatch.setattr(Fortran2003.Assignment_Stmt, "__init__", my_init)

    with pytest.raises(InternalError) as err:
        _ = ktype.get_integer_array("gh_evaluator_targets")
    assert ("expected array declaration to have a Section_Subscript_List but "
            "found" in str(err.value))
Beispiel #11
0
def test_invalid_if_clause():
    ''' Check that we raise the expected error if the NemoIfClause
    is passed something that isn't an if-clause. '''
    from psyclone.nemo import NemoIfClause
    reader = FortranStringReader("umask(:, :, :, :) = 0")
    assign = Fortran2003.Assignment_Stmt(reader)
    with pytest.raises(InternalError) as err:
        _ = NemoIfClause([assign])
    assert "Unrecognised member of if block: " in str(err)
def test_get_literal_precision_missing_table():
    ''' Check that get_literal_precision raises the expected error if it
    fails to find a symbol table. '''
    code = "x=0.0_rdef"
    reader = FortranStringReader(code)
    astmt = Fortran2003.Assignment_Stmt(reader)
    # Pass get_literal_precision just a Literal() (which does not have an
    # associated symbol table).
    with pytest.raises(InternalError) as err:
        get_literal_precision(astmt.children[2], Literal("1", INTEGER_TYPE))
    assert ("Failed to find a symbol table to which to add the kind"
            in str(err.value))
def test_get_literal_precision_type(monkeypatch):
    '''Make sure the get_literal_precision function in fparser2.py behaves
    as expected when an unsupported datatype is found

    '''
    monkeypatch.setattr(fparser2, "CONSTANT_TYPE_MAP", {})
    code = "x=0.0"
    reader = FortranStringReader(code)
    astmt = Fortran2003.Assignment_Stmt(reader)
    fparser2_literal = astmt.children[2]
    with pytest.raises(NotImplementedError) as excinfo:
        _ = get_literal_precision(fparser2_literal, Node())
    assert ("Could not process Real_Literal_Constant. Only 'real', 'integer', "
            "'logical' and 'character' intrinsic types are supported."
            in str(excinfo.value))
Beispiel #14
0
    def get_integer_array(self, name):
        ''' Parse the kernel meta-data and find the values of the
        integer array variable with the supplied name. Returns an empty list
        if no matching variable is found.

        :param str name: the name of the integer array to find.
        :returns: list of values.
        :rtype: list of str.

        :raises InternalError: if we fail to parse the LHS of the array \
                               declaration or the array constructor.
        :raises ParseError: if the RHS of the declaration is not an array \
                            constructor.

        '''
        # Ensure the classes are setup for the Fortran2003 parser
        _ = ParserFactory().create()

        for statement, _ in fpapi.walk(self._ktype, -1):
            if not isinstance(statement, fparser1.typedecl_statements.Integer):
                # This isn't an integer declaration so skip it
                continue
            # fparser only goes down to the statement level. We use fparser2 to
            # parse the statement itself.
            assign = Fortran2003.Assignment_Stmt(statement.entity_decls[0])
            names = walk_ast(assign.items, [Fortran2003.Name])
            if not names:
                raise InternalError(
                    "Unsupported assignment statement: '{0}'".format(
                        str(assign)))
            if str(names[0]) == name:
                # This is the variable declaration we're looking for
                if not isinstance(assign.items[2],
                                  Fortran2003.Array_Constructor):
                    raise ParseError(
                        "get_integer_array: RHS of assignment is not "
                        "an array constructor: '{0}'".format(str(assign)))
                # fparser2 AST for Array_Constructor is:
                # Array_Constructor('[', Ac_Value_List(',', (Name('w0'),
                #                                      Name('w1'))), ']')
                # Construct a list of the names in the array constructor
                names = walk_ast(assign.items[2].items, [Fortran2003.Name])
                if not names:
                    raise InternalError("Failed to parse array constructor: "
                                        "'{0}'".format(str(assign.items[2])))
                return [str(name) for name in names]
        return []
def test_literal_constant_value_format(value, result):
    '''Test that the Fortran real literal value format which does not have
    a digit before the decimal point is modified to include a "0"
    e.g. ".3" -> "0.3", "-.3e4" -> "-0.3e4"

    TODO #754 fix test so that 'disable_declaration_check' fixture is not
    required.
    '''
    reader = FortranStringReader("a = {0}".format(value))
    astmt = Fortran2003.Assignment_Stmt(reader)
    fake_parent = Schedule()
    processor = Fparser2Reader()
    processor.process_nodes(fake_parent, [astmt])
    literal = fake_parent.children[0].children[1]
    assert isinstance(literal, Literal)
    assert literal.value == result
    assert literal.datatype.intrinsic == ScalarType.Intrinsic.REAL
def test_handling_invalid_logic_literal():
    ''' Test that a logic fparser2 literal with an invalid value produces
    an error.

    TODO #754 fix test so that 'disable_declaration_check' fixture is not
    required.
    '''
    from psyclone.errors import GenerationError
    reader = FortranStringReader("x = .true.")
    astmt = Fortran2003.Assignment_Stmt(reader)
    astmt.items[2].items = ('invalid', None)
    fake_parent = Schedule()
    processor = Fparser2Reader()
    with pytest.raises(GenerationError) as error:
        processor.process_nodes(fake_parent, [astmt])
    assert "Expected to find '.true.' or '.false.' as fparser2 logical " \
        "literal, but found 'invalid' instead." in str(error.value)
def test_get_literal_precision():
    '''Make sure the get_literal_precision function in fparser2.py behaves
    as expected when the arguments are invalid.

    '''
    with pytest.raises(InternalError) as excinfo:
        _ = get_literal_precision(None, None)
    assert ("Unsupported literal type 'NoneType' found in "
            "get_literal_precision." in str(excinfo.value))
    code = "x=0.0"
    reader = FortranStringReader(code)
    astmt = Fortran2003.Assignment_Stmt(reader)
    fparser2_literal = astmt.children[2]
    with pytest.raises(InternalError) as excinfo:
        _ = get_literal_precision(fparser2_literal, None)
    assert ("Expecting argument psyir_literal_parent to be a PSyIR Node but "
            "found 'NoneType' in get_literal_precision." in str(excinfo.value))
def test_handling_literal_precision_3(value, dprecision):
    '''Check that the fparser2 frontend can handle literals with a
    precision value specified by the exponent. The literal value

    TODO #754 fix test so that 'disable_declaration_check' fixture is not
    required.
    should always use a lower case "e" for the exponent.
    '''
    code = "x={0}".format(value)
    reader = FortranStringReader(code)
    astmt = Fortran2003.Assignment_Stmt(reader)
    fake_parent = Schedule()
    processor = Fparser2Reader()
    processor.process_nodes(fake_parent, [astmt])
    assert not fake_parent.walk(CodeBlock)
    literal = fake_parent.children[0].children[1]
    assert isinstance(literal, Literal)
    assert literal.value.lower() == "0.0e0"
    assert literal.datatype.intrinsic == ScalarType.Intrinsic.REAL
    assert literal.datatype.precision == dprecision
def test_handling_literal(code, dtype):
    ''' Check that the fparser2 frontend can handle literals of all
    supported datatypes. Note that signed literals are represented in the
    PSyIR as a Unary operation on an unsigned literal.

    TODO #754 fix test so that 'disable_declaration_check' fixture is not
    required.
    '''
    reader = FortranStringReader("x=" + code)
    astmt = Fortran2003.Assignment_Stmt(reader)
    fake_parent = Schedule()
    processor = Fparser2Reader()
    processor.process_nodes(fake_parent, [astmt])
    assert not fake_parent.walk(CodeBlock)
    literal = fake_parent.children[0].children[1]
    assert isinstance(literal, Literal)
    assert literal.datatype.intrinsic == dtype
    if dtype != ScalarType.Intrinsic.BOOLEAN:
        assert literal.value == code
    else:
        assert literal.value == code.lower()[1:-1]  # Remove wrapping dots
Beispiel #20
0
    def get_integer_array(self, name):
        ''' Parse the kernel meta-data and find the values of the
        integer array variable with the supplied name. Returns an empty list
        if no matching variable is found. The search is not case sensitive.

        :param str name: the name of the integer array to find.

        :return: list of values (lower-case).
        :rtype: list of str.

        :raises InternalError: if we fail to parse the LHS of the array \
                               declaration or the array constructor.
        :raises ParseError: if the array is not of rank 1.
        :raises ParseError: if the array extent is not specified using an \
                            integer literal.
        :raises ParseError: if the RHS of the declaration is not an array \
                            constructor.
        :raises InternalError: if the parse tree for the array constructor \
                               does not have the expected structure.
        :raises ParseError: if the number of items in the array constructor \
                            does not match the extent of the array.

        '''
        # Ensure the classes are setup for the Fortran2008 parser
        _ = ParserFactory().create(std="f2008")
        # Fortran is not case sensitive so nor is our matching
        lower_name = name.lower()

        for statement, _ in fpapi.walk(self._ktype):
            if not isinstance(statement, fparser1.typedecl_statements.Integer):
                # This isn't an integer declaration so skip it
                continue
            # fparser only goes down to the statement level. We use fparser2 to
            # parse the statement itself.
            assign = Fortran2003.Assignment_Stmt(statement.entity_decls[0])
            names = walk(assign.children, Fortran2003.Name)
            if not names:
                raise InternalError("Unsupported assignment statement: '{0}'".
                                    format(str(assign)))

            if str(names[0]).lower() != lower_name:
                # This is not the variable declaration we're looking for
                continue

            if not isinstance(assign.children[0], Fortran2003.Part_Ref):
                # Not an array declaration
                return []

            if not isinstance(assign.children[0].children[1],
                              Fortran2003.Section_Subscript_List):
                raise InternalError(
                    "get_integer_array: expected array declaration to have a "
                    "Section_Subscript_List but found '{0}' for: '{1}'".format(
                        type(assign.children[0].children[1]).__name__,
                        str(assign)))

            dim_stmt = assign.children[0].children[1]
            if len(dim_stmt.children) != 1:
                raise ParseError(
                    "get_integer_array: array must be 1D but found an array "
                    "with {0} dimensions for name '{1}'".format(
                        len(dim_stmt.children), name))
            if not isinstance(dim_stmt.children[0],
                              Fortran2003.Int_Literal_Constant):
                raise ParseError(
                    "get_integer_array: array extent must be specified using "
                    "an integer literal but found '{0}' for array '{1}'".
                    format(str(dim_stmt.children[0]), name))
            # Get the declared size of the array
            array_extent = int(str(dim_stmt.children[0]))

            if not isinstance(assign.children[2],
                              Fortran2003.Array_Constructor):
                raise ParseError(
                    "get_integer_array: RHS of assignment is not "
                    "an array constructor: '{0}'".format(str(assign)))
            # fparser2 AST for Array_Constructor is:
            # Array_Constructor('[', Ac_Value_List(',', (Name('w0'),
            #                                      Name('w1'))), ']')
            # Construct a list of the names in the array constructor
            names = walk(assign.children[2].children, Fortran2003.Name)
            if not names:
                raise InternalError("Failed to parse array constructor: "
                                    "'{0}'".format(str(assign.items[2])))
            if len(names) != array_extent:
                # Ideally fparser would catch this but it isn't yet mature
                # enough.
                raise ParseError(
                    "get_integer_array: declared length of array '{0}' is {1} "
                    "but constructor only contains {2} names: '{3}'".format(
                        name, array_extent, len(names), str(assign)))
            return [str(name).lower() for name in names]
        # No matching declaration for the provided name was found
        return []
def test_array_constructor_no_match(example):
    ''' Check that incorrect constructors are not matched. '''
    fcode = "array = " + example
    reader = FortranStringReader(fcode)
    ast = Fortran2003.Assignment_Stmt(reader)
    assert ast is None