コード例 #1
0
def test_multi_stmt_line1():
    '''Check that simple statements separated by ; on a single line are
    correctly split into multiple lines by FortranReaderBase

    '''
    code = "do i=1,10;b=20 ; c=30"
    reader = FortranStringReader(code)
    mode = FortranFormat(True, False)
    reader.set_format(mode)
    line1 = reader.next()
    assert isinstance(line1, Line)
    assert line1.line == "do i=1,10"
    assert line1.span == (1, 1)
    assert line1.label is None
    assert line1.name is None
    assert line1.reader is reader
    line2 = reader.next()
    assert isinstance(line2, Line)
    assert line2.line == "b=20"
    assert line2.span is line1.span
    assert line2.label is None
    assert line2.name is None
    assert line2.reader is reader
    line3 = reader.next()
    assert isinstance(line3, Line)
    assert line3.line == "c=30"
    assert line3.span is line1.span
    assert line3.label is None
    assert line3.name is None
    assert line3.reader is reader
コード例 #2
0
ファイル: f2pygen.py プロジェクト: ScottWales/PSyclone
    def __init__(self, parent, expr="UNSET", typeselect=False):
        '''
        Construct a SelectionGen for creating a SELECT block

        :param parent: node to which to add this select block as a child
        :type parent: :py:class:`psyclone.f2pygen.BaseGen`
        :param str expr: the CASE expression
        :param bool typeselect: whether or not this is a SELECT TYPE rather
                                than a SELECT CASE
        '''
        self._typeselect = typeselect
        reader = FortranStringReader(
            "SELECT CASE (x)\nCASE (1)\nCASE DEFAULT\nEND SELECT")
        reader.set_format(FortranFormat(True, True))  # free form, strict
        select_line = reader.next()
        self._case_line = reader.next()
        self._case_default_line = reader.next()
        end_select_line = reader.next()
        if self._typeselect:
            select = SelectType(parent.root, select_line)
        else:
            select = SelectCase(parent.root, select_line)
        endselect = EndSelect(select, end_select_line)
        select.expr = expr
        select.content.append(endselect)
        BaseGen.__init__(self, parent, select)
コード例 #3
0
def parse(cls, line, label='', isfree=True, isstrict=False):
    '''Tries to parse a Fortran line using the given class cls.
    If successful, it then converts the parsed statement back to
    a string. If isstrict is false, it will then try to parse
    this string again (recursively calling itself, with isstrict
    set to true) and make sure that the re-parsed string is
    identical to the input.
    It returns the string representation of the parsed input line.
    '''

    if label:
        line = label + ' : ' + line
    reader = FortranStringReader(line)
    reader.set_format(fparser.common.sourceinfo.FortranFormat(
        isfree, isstrict))
    item = next(reader)
    if not cls.match(item.get_line()):
        raise ValueError('%r does not match %s pattern' % (line, cls.__name__))
    stmt = cls(item, item)
    if stmt.isvalid:
        # Check that we can successfully parse the string representation
        # of the parsed object
        stmt_string = str(stmt)
        if not isstrict:
            reparsed_stmt_string = parse(cls, stmt_string, isstrict=True)
            if stmt_string != reparsed_stmt_string:
                raise ValueError(
                    'Failed to parse %r with %s pattern in Pyf '
                    'mode, got %r' %
                    (stmt_string, cls.__name__, reparsed_stmt_string))
        return stmt_string
    raise ValueError('parsing %r with %s pattern failed' %
                     (line, cls.__name__))
コード例 #4
0
ファイル: test_block_stmts.py プロジェクト: kdeyev/fparser
def test_implicit_topyf(monkeypatch):
    ''' Tests for the topyf() method of HasImplicitStmt. '''
    from fparser.common.readfortran import FortranStringReader
    from fparser.common.sourceinfo import FortranFormat
    from fparser.one.parsefortran import FortranParser
    # We can't just create a HasImplicitStmt object so we get the parser
    # to create a module object as that sub-classes HasImplicitStmt (amongst
    # other things).
    string = '''\
module some_block
  implicit real (a-e)
  implicit integer (f-z)
end module some_block
'''
    reader = FortranStringReader(string)
    reader.set_format(FortranFormat(True, False))
    parser = FortranParser(reader)
    parser.parse()
    # Get the module object
    mod = parser.block.content[0]
    code = mod.topyf()
    assert "! default IMPLICIT rules apply" in code
    mod.content[0].analyze()
    mod.content[1].analyze()
    code = mod.topyf()
    assert "REAL (a, b, c, d, e)" in code
    assert "INTEGER (f, g, h" in code
    monkeypatch.setattr(mod.a, "implicit_rules", None)
    code = mod.topyf()
    assert "IMPLICIT NONE" in code
コード例 #5
0
ファイル: f2pygen.py プロジェクト: ScottWales/PSyclone
def adduse(name, parent, only=False, funcnames=None):
    '''
    Adds a use statement with the specified name to the supplied object.
    This routine is required when modifying an existing AST (e.g. when
    modifying a kernel). The classes are used when creating an AST from
    scratch (for the PSy layer).

    :param str name: name of module to USE
    :param parent: node in fparser1 AST to which to add this USE as a child
    :type parent: :py:class:`fparser.one.block_statements.*`
    :param bool only: whether this USE has an "ONLY" clause
    :param list funcnames: list of quantities to follow the "ONLY" clause

    :returns: an fparser1 Use object
    :rtype: :py:class:`fparser.one.block_statements.Use`
    '''
    reader = FortranStringReader("use kern,only : func1_kern=>func1")
    reader.set_format(FortranFormat(True, True))  # free form, strict
    myline = reader.next()

    # find an appropriate place to add in our use statement
    while not (isinstance(parent, fparser1.block_statements.Program) or
               isinstance(parent, fparser1.block_statements.Module) or
               isinstance(parent, fparser1.block_statements.Subroutine)):
        parent = parent.parent
    use = fparser1.block_statements.Use(parent, myline)
    use.name = name
    use.isonly = only
    if funcnames is None:
        funcnames = []
        use.isonly = False
    use.items = funcnames

    parent.content.insert(0, use)
    return use
コード例 #6
0
ファイル: f2pygen.py プロジェクト: ScottWales/PSyclone
    def __init__(self, parent, name="", args=None, implicitnone=False):
        '''
        :param parent: node in AST to which to add Subroutine as a child
        :type parent: :py:class:`psyclone.f2pygen.BaseGen`
        :param str name: name of the Fortran subroutine
        :param list args: list of arguments accepted by the subroutine
        :param bool implicitnone: whether or not we should specify
                                  "implicit none" for the body of this
                                  subroutine
        '''
        reader = FortranStringReader(
            "subroutine vanilla(vanilla_arg)\nend subroutine")
        reader.set_format(FortranFormat(True, True))  # free form, strict
        subline = reader.next()
        endsubline = reader.next()

        from fparser.one.block_statements import Subroutine, EndSubroutine
        self._sub = Subroutine(parent.root, subline)
        self._sub.name = name
        if args is None:
            args = []
        self._sub.args = args
        endsub = EndSubroutine(self._sub, endsubline)
        self._sub.content.append(endsub)
        ProgUnitGen.__init__(self, parent, self._sub)
        if implicitnone:
            self.add(ImplicitNoneGen(self))
コード例 #7
0
ファイル: test_block_stmts.py プロジェクト: kdeyev/fparser
def test_get_type_by_name(monkeypatch):
    ''' Tests for HasImplicitStmt.get_type_by_name(). '''
    from fparser.common.utils import AnalyzeError
    from fparser.common.readfortran import FortranStringReader
    from fparser.common.sourceinfo import FortranFormat
    from fparser.one.typedecl_statements import Real, Integer
    from fparser.one.parsefortran import FortranParser
    # We can't just create a HasImplicitStmt object so we get the parser
    # to create a module object as that sub-classes HasImplicitStmt (amongst
    # other things).
    string = '''\
module some_block
end module some_block
'''
    reader = FortranStringReader(string)
    reader.set_format(FortranFormat(True, False))
    parser = FortranParser(reader)
    parser.parse()
    mod = parser.block.content[0]
    # Now we have a Module object, we can call get_type_by_name()...
    rtype = mod.get_type_by_name("a_real")
    assert isinstance(rtype, Real)
    itype = mod.get_type_by_name("i_int")
    assert isinstance(itype, Integer)
    # Check that we raise the correct error if we don't have any implicit
    # rules set
    monkeypatch.setattr(mod.a, "implicit_rules", None)
    with pytest.raises(AnalyzeError) as err:
        _ = mod.get_type_by_name("i_int")
    assert "Implicit rules mapping is null" in str(err)
コード例 #8
0
ファイル: test_block_stmts.py プロジェクト: kdeyev/fparser
def test_get_type_by_name_implicit():
    ''' Tests for HasImplicitStmt.get_type_by_name() when the source code
    contains IMPLICIT statements. '''
    from fparser.common.readfortran import FortranStringReader
    from fparser.common.sourceinfo import FortranFormat
    from fparser.one.typedecl_statements import Real, Integer
    from fparser.one.parsefortran import FortranParser
    # We can't just create a HasImplicitStmt object so we get the parser
    # to create a module object as that sub-classes HasImplicitStmt (amongst
    # other things).
    string = '''\
module some_block
  implicit real (a-e)
  implicit integer (f-z)
end module some_block
'''
    reader = FortranStringReader(string)
    reader.set_format(FortranFormat(True, False))
    parser = FortranParser(reader)
    parser.parse()
    # Get the module object
    mod = parser.block.content[0]
    # We have to run the analyze method on the Implicit objects
    # produced by the parser in order to populate the implicit_rules
    # of the module.
    mod.content[0].analyze()
    mod.content[1].analyze()
    # Now we can call get_type_by_name()...
    rtype = mod.get_type_by_name("a_real")
    assert isinstance(rtype, Real)
    itype = mod.get_type_by_name("f_int")
    assert isinstance(itype, Integer)
コード例 #9
0
def test_base_fixed_continuation(log):
    '''
    Tests that FortranReaderBase.get_source_item() logs the correct messages
    when there are quote mismatches across a continuation in fixed format.
    '''
    code = '     character(4) :: cheese = "a & !\n     & b'
    log.reset()
    unit_under_test = FortranStringReader(code)
    mode = FortranFormat(False, False)
    unit_under_test.set_format(mode)  # Force sloppy fixed format
    unit_under_test.get_source_item()
    assert log.messages['debug'] == []
    assert log.messages['info'] == []
    assert log.messages['error'] == []
    assert log.messages['critical'] == []
    expected = 'following character continuation: \'"\', expected None.'
    result = log.messages['warning'][0].split('<==')[1].lstrip()
    assert result == expected

    code = '     x=1 &\n     +1 &\n     -2'
    log.reset()
    unit_under_test = FortranStringReader(code)
    mode = FortranFormat(False, False)
    unit_under_test.set_format(mode)  # Force sloppy fixed format
    unit_under_test.get_source_item()
    assert log.messages['debug'] == []
    assert log.messages['info'] == []
    assert log.messages['error'] == []
    assert log.messages['critical'] == []
    expected = 'free format line continuation character `&\' detected ' \
               + 'in fix format code\n    2:     +1 &\n    3:     -2'
    result = log.messages['warning'][0].split('<==')[1].lstrip()
    assert result == expected
コード例 #10
0
def test_base_handle_multilines(log):
    '''
    Tests that FortranReaderBase.get_source_item() logs the correct messages
    when there are quote discrepancies.
    '''
    code = 'character(8) :: test = \'foo"""bar'
    log.reset()
    unit_under_test = FortranStringReader(code)
    mode = FortranFormat(True, True)
    unit_under_test.set_format(mode)  # Force strict free format
    unit_under_test.get_source_item()
    assert log.messages['debug'] == []
    assert log.messages['info'] == []
    assert log.messages['error'] == []
    assert log.messages['critical'] == []
    expected = 'multiline prefix contains odd number of "\'" characters'
    result = log.messages['warning'][0].split('<==')[1].lstrip()
    assert result == expected

    code = 'goo """boo\n doo""" soo \'foo'
    log.reset()
    unit_under_test = FortranStringReader(code)
    mode = FortranFormat(True, True)
    unit_under_test.set_format(mode)  # Force strict free format
    unit_under_test.get_source_item()
    assert log.messages['debug'] == []
    assert log.messages['info'] == []
    assert log.messages['error'] == []
    assert log.messages['critical'] == []
    expected = 'following character continuation: "\'", expected None.'
    result = log.messages['warning'][0].split('<==')[1].lstrip()
    assert result == expected
コード例 #11
0
ファイル: f2pygen.py プロジェクト: ScottWales/PSyclone
    def __init__(self, parent, datatype="", entity_decls=None, intent="",
                 pointer=False, kind="", dimension="", allocatable=False):
        '''
        :param parent: node to which to add this declaration as a child
        :type parent: :py:class:`psyclone.f2pygen.BaseGen`
        :param str datatype: the (intrinsic) type for this declaration
        :param list entity_decls: list of variable names to declare
        :param str intent: the INTENT attribute of this declaration
        :param bool pointer: whether or not this is a pointer declaration
        :param str kind: the KIND attribute to use for this declaration
        :param str dimension: the DIMENSION specifier (i.e. the xx in
                              DIMENSION(xx))
        :param bool allocatable: whether this declaration is for an
                                 ALLOCATABLE quantity

        :raises RuntimeError: if no variable names are specified
        :raises RuntimeError: if datatype is not one of "integer" or "real"
        '''
        if entity_decls is None:
            raise RuntimeError(
                "Cannot create a variable declaration without specifying the "
                "name(s) of the variable(s)")
        fort_fmt = FortranFormat(True, False)  # free form, strict
        if datatype.lower() == "integer":
            reader = FortranStringReader("integer :: vanilla")
            reader.set_format(fort_fmt)
            myline = reader.next()
            self._decl = fparser1.typedecl_statements.Integer(parent.root,
                                                              myline)
        elif datatype.lower() == "real":
            reader = FortranStringReader("real :: vanilla")
            reader.set_format(fort_fmt)
            myline = reader.next()
            self._decl = fparser1.typedecl_statements.Real(parent.root, myline)
        else:
            raise RuntimeError(
                "f2pygen:DeclGen:init: Only integer and real are currently"
                " supported and you specified '{0}'".format(datatype))
        # make a copy of entity_decls as we may modify it
        local_entity_decls = entity_decls[:]
        self._decl.entity_decls = local_entity_decls
        my_attrspec = []
        if intent != "":
            my_attrspec.append("intent({0})".format(intent))
        if pointer is not False:
            my_attrspec.append("pointer")
        if allocatable is not False:
            my_attrspec.append("allocatable")
        self._decl.attrspec = my_attrspec
        if dimension != "":
            my_attrspec.append("dimension({0})".format(dimension))
        if kind is not "":
            self._decl.selector = ('', kind)
        BaseGen.__init__(self, parent, self._decl)
コード例 #12
0
def test_nonblock_do_construct_tofortran_non_ascii():
    ''' Check that the tofortran() method works when the non-block
    do-construct contains a character string with non-ascii characters. '''
    from fparser.common.readfortran import FortranStringReader
    from fparser.common.sourceinfo import FortranFormat
    code = (u"      DO 50\n" u" 50   WRITE(*,*) ' for e1=1\xb0'\n")
    reader = FortranStringReader(code)
    # Ensure reader in in 'fixed-format' mode
    reader.set_format(FortranFormat(False, True))
    obj = Nonblock_Do_Construct(reader)
    out_str = str(obj)
    assert "for e1=1" in out_str
コード例 #13
0
ファイル: f2pygen.py プロジェクト: ScottWales/PSyclone
    def __init__(self, parent, content):
        '''
        :param parent: node in AST to which to add the Comment as a child
        :type parent: :py:class:`psyclone.f2pygen.BaseGen`
        :param str content: the content of the comment
        '''
        reader = FortranStringReader("! content\n")
        reader.set_format(FortranFormat(True, True))  # free form, strict
        subline = reader.next()

        my_comment = Comment(parent.root, subline)
        my_comment.content = content

        BaseGen.__init__(self, parent, my_comment)
コード例 #14
0
ファイル: f2pygen.py プロジェクト: ScottWales/PSyclone
    def __init__(self, parent, clause):
        '''
        :param parent: Node to which to add this IfThen as a child
        :type parent: :py:class:`psyclone.f2pygen.BaseGen`
        :param str clause: the condition, xx, to evaluate in the if(xx)then
        '''
        reader = FortranStringReader("if (dummy) then\nend if")
        reader.set_format(FortranFormat(True, True))  # free form, strict
        ifthenline = reader.next()
        endifline = reader.next()

        my_if = fparser1.block_statements.IfThen(parent.root, ifthenline)
        my_if.expr = clause
        my_endif = fparser1.block_statements.EndIfThen(my_if, endifline)
        my_if.content.append(my_endif)

        BaseGen.__init__(self, parent, my_if)
コード例 #15
0
def test_do(name, label, control_comma, terminal_expression, end_name,
            end_label):
    # pylint: disable=redefined-outer-name, too-many-arguments, too-many-locals
    '''
    Checks that the "do" loop parser understands the "for-next" variant of
    the syntax. This is defined in BS ISO/IEC 1539-1:2010 with R814-R822.

    TODO: Only the terminal expression is tested. This is a short-cut and
          relies on expression handling being applied identically across
          all expressions. This was true at the time of writing the test.
    '''
    name_snippet = name + ': ' if name else None
    label_snippet = label + ' ' if label else None
    comma_snippet = ', ' if control_comma else None
    # TODO: Although the Fortran standard allows for "continue" to be used in
    # place of "end do" fparser does not support it.
    end_snippet = 'continue' if end_name == 'continue' \
                  else get_end_do(end_name)
    do_code = '''{name}do {label}{comma}variable = 1, {term}, 1
  write (6, '(I0)') variable
{endlabel} {end}
'''.format(name=name_snippet or '',
           label=label_snippet or '',
           comma=comma_snippet or '',
           term=terminal_expression,
           endlabel=end_label or '',
           end=end_snippet)
    do_expected = '''  {name}DO {label}variable = 1, {term}, 1
    WRITE (6, '(I0)') variable
{endlabel} {endstmt}
'''.format(name=name_snippet or '',
           label=label_snippet or '',
           term=terminal_expression,
           endlabel=end_label or ' ',
           endstmt=get_end_do(end_name))
    do_reader = FortranStringReader(do_code)
    do_reader.set_format(FortranFormat(True, False))
    do_parser = FortranParser(do_reader)
    if (name != end_name) or (label and (label != end_label)):
        with pytest.raises(AnalyzeError):
            do_parser.parse()
    else:
        do_parser.parse()
        loop = do_parser.block.content[0]
        assert str(loop).splitlines() == do_expected.splitlines()
コード例 #16
0
def test_base_free_continuation(log):
    '''
    Tests that FortranReaderBase.get_source_item() logs the correct messages
    when there are quote mismatches across a continuation in free format.
    '''
    code = 'character(4) :: "boo & que'
    log.reset()
    unit_under_test = FortranStringReader(code)
    mode = FortranFormat(True, False)
    unit_under_test.set_format(mode)  # Force sloppy free format
    unit_under_test.get_source_item()
    assert log.messages['debug'] == []
    assert log.messages['info'] == []
    assert log.messages['warning'] == []
    assert log.messages['critical'] == []
    expected = 'following character continuation: \'"\', expected None.'
    result = log.messages['error'][0].split('<==')[1].lstrip()
    assert result == expected
コード例 #17
0
ファイル: f2pygen.py プロジェクト: ScottWales/PSyclone
    def __init__(self, parent, name="", args=None):
        '''
        :param parent: node in AST to which to add CallGen as a child
        :type parent: :py:class:`psyclone.f2pygen.BaseGen`
        :param str name: the name of the routine to call
        :param list args: list of arguments to pass to the call
        '''
        reader = FortranStringReader("call vanilla(vanilla_arg)")
        reader.set_format(FortranFormat(True, True))  # free form, strict
        myline = reader.next()

        from fparser.one.block_statements import Call
        self._call = Call(parent.root, myline)
        self._call.designator = name
        if args is None:
            args = []
        self._call.items = args

        BaseGen.__init__(self, parent, self._call)
コード例 #18
0
ファイル: f2pygen.py プロジェクト: ScottWales/PSyclone
    def __init__(self, parent):
        '''
        :param parent: node in AST to which to add 'implicit none' as a child
        :type parent: :py:class:`psyclone.f2pygen.ModuleGen` or
                      :py:class:`psyclone.f2pygen.SubroutineGen`

        :raises Exception: if `parent` is not a ModuleGen or SubroutineGen
        '''
        if not isinstance(parent, ModuleGen) and not isinstance(parent,
                                                                SubroutineGen):
            raise Exception(
                "The parent of ImplicitNoneGen must be a module or a "
                "subroutine, but found {0}".format(type(parent)))
        reader = FortranStringReader("IMPLICIT NONE\n")
        reader.set_format(FortranFormat(True, True))  # free form, strict
        subline = reader.next()

        from fparser.one.typedecl_statements import Implicit
        my_imp_none = Implicit(parent.root, subline)

        BaseGen.__init__(self, parent, my_imp_none)
コード例 #19
0
ファイル: f2pygen.py プロジェクト: ScottWales/PSyclone
    def __init__(self, parent, variable_name, start, end, step=None):
        '''
        :param parent: the node to which to add this do loop as a child
        :type parent: :py:class:`psyclone.f2pygen.BaseGen`
        :param str variable_name: the name of the loop variable
        :param str start: start value for Do loop
        :param str end: upper-limit of Do loop
        :param str step: increment to use in Do loop
        '''
        reader = FortranStringReader("do i=1,n\nend do")
        reader.set_format(FortranFormat(True, True))  # free form, strict
        doline = reader.next()
        enddoline = reader.next()
        dogen = fparser1.block_statements.Do(parent.root, doline)
        dogen.loopcontrol = variable_name + "=" + start + "," + end
        if step is not None:
            dogen.loopcontrol = dogen.loopcontrol + "," + step
        enddo = fparser1.block_statements.EndDo(dogen, enddoline)
        dogen.content.append(enddo)

        BaseGen.__init__(self, parent, dogen)
コード例 #20
0
def test_class_internal_error(monkeypatch, capsys):
    ''' Check that expected errors are raised when invalid CLASS
    statements are encountered '''
    from fparser.one.block_statements import ClassIs
    from fparser.common.readfortran import FortranStringReader
    reader = FortranStringReader('CLASS IS (yes)')
    reader.set_format(fparser.common.sourceinfo.FortranFormat(True, False))
    item = next(reader)
    stmt = ClassIs(item, item)
    # Monkeypatch our valid Case object so that get_line() now
    # returns something invalid. We have to do it this way
    # because if we started with this text then we wouldn't get
    # past the match() method
    monkeypatch.setattr(stmt.item, "get_line", lambda: "class invalid")
    # Monkeypatch the Case object so that a call to self.warning
    # (which normally results in a call to the logger) gets replaced
    # with a call to our print_wrapper() function
    monkeypatch.setattr(stmt, "warning", print_wrapper)
    stmt.process_item()
    output, _ = capsys.readouterr()
    assert "Internal error when parsing CLASS statement" in output
コード例 #21
0
ファイル: f2pygen.py プロジェクト: ScottWales/PSyclone
 def __init__(self, parent, name="", only=False, funcnames=None):
     '''
     :param parent: node in AST to which to add UseGen as a child
     :type parent: :py:class:`psyclone.f2pygen.BaseGen`
     :param str name: name of the module to USE
     :param bool only: whether this USE has an ONLY clause
     :param list funcnames: list of names to follow ONLY clause
     '''
     reader = FortranStringReader("use kern,only : func1_kern=>func1")
     reader.set_format(FortranFormat(True, True))  # free form, strict
     myline = reader.next()
     root = parent.root
     from fparser.one.block_statements import Use
     use = Use(root, myline)
     use.name = name
     use.isonly = only
     if funcnames is None:
         funcnames = []
         use.isonly = False
     local_funcnames = funcnames[:]
     use.items = local_funcnames
     BaseGen.__init__(self, parent, use)
コード例 #22
0
ファイル: f2pygen.py プロジェクト: ScottWales/PSyclone
    def __init__(self, parent, content):
        '''
        :param parent: node to which to add this DEALLOCATE as a child
        :type parent: :py:class:`psyclone.f2pygen.BaseGen`
        :param content: string or list of variables to deallocate
        :type content: list of strings or a single string

        :raises RuntimeError: if `content` is not of correct type
        '''
        reader = FortranStringReader("deallocate(dummy)")
        reader.set_format(FortranFormat(True, False))  # free form, strict
        myline = reader.next()
        self._decl = fparser1.statements.Deallocate(parent.root, myline)
        if isinstance(content, str):
            self._decl.items = [content]
        elif isinstance(content, list):
            self._decl.items = content
        else:
            raise RuntimeError(
                "DeallocateGen expected the content argument to be a str"
                " or a list, but found {0}".format(type(content)))
        BaseGen.__init__(self, parent, self._decl)
コード例 #23
0
def test_multi_stmt_line3():
    '''Check that named do loops separated by ; on a single line are correctly
    split into multiple lines by FortranReaderBase

    '''
    code = "name:do i=1,10;name2 : do j=1,10"
    reader = FortranStringReader(code)
    mode = FortranFormat(True, False)
    reader.set_format(mode)
    line1 = reader.next()
    assert isinstance(line1, Line)
    assert line1.line == "do i=1,10"
    assert line1.span == (1, 1)
    assert line1.label is None
    assert line1.name == "name"
    assert line1.reader is reader
    line2 = reader.next()
    assert line2.line == "do j=1,10"
    assert line2.span is line1.span
    assert line2.label is None
    assert line2.name == "name2"
    assert line2.reader is reader
コード例 #24
0
def test_multi_stmt_line2():
    '''Check that format statements separated by ; on a single line are
    correctly split into multiple lines by FortranReaderBase

    '''
    code = "10 format(a); 20 format(b)"
    reader = FortranStringReader(code)
    mode = FortranFormat(True, False)
    reader.set_format(mode)
    line1 = reader.next()
    assert isinstance(line1, Line)
    assert line1.line == "format(a)"
    assert line1.span == (1, 1)
    assert line1.label == 10
    assert line1.name is None
    assert line1.reader is reader
    line2 = reader.next()
    assert line2.line == "format(b)"
    assert line2.span is line1.span
    assert line2.label == 20
    assert line2.name is None
    assert line2.reader is reader
コード例 #25
0
ファイル: f2pygen.py プロジェクト: ScottWales/PSyclone
 def __init__(self, parent, lhs="", rhs="", pointer=False):
     '''
     :param parent: the node to which to add this assignment as a child
     :type parent: :py:class:`psyclone.f2pygen.BaseGen`
     :param str lhs: the LHS of the assignment expression
     :param str rhs: the RHS of the assignment expression
     :param bool pointer: whether or not this is a pointer assignment
     '''
     if pointer:
         reader = FortranStringReader("lhs=>rhs")
     else:
         reader = FortranStringReader("lhs=rhs")
     reader.set_format(FortranFormat(True, True))  # free form, strict
     myline = reader.next()
     if pointer:
         self._assign = fparser1.statements.PointerAssignment(parent.root,
                                                              myline)
     else:
         self._assign = fparser1.statements.Assignment(parent.root, myline)
     self._assign.expr = rhs
     self._assign.variable = lhs
     BaseGen.__init__(self, parent, self._assign)
コード例 #26
0
def test_do_while(name, label, control_comma, terminal_expression, end_name,
                  end_label):
    # pylint: disable=redefined-outer-name, too-many-arguments
    '''
    Checks that the "do" loop parser understands the "do-while" variant of
    the syntax. This is defined in BS ISO/IEC 1539-1:2010 with R814-R822.
    '''
    name_snippet = name + ': ' if name else None
    label_snippet = label + ' ' if label else None
    comma_snippet = ', ' if control_comma else None
    code = '''{name}do {label}{comma}while ({term})
  write (6, '(I0)') variable
{endlabel} {endstmt}
'''.format(name=name_snippet or '',
           label=label_snippet or '',
           comma=comma_snippet or '',
           term=terminal_expression,
           endlabel=end_label or '',
           endstmt=get_end_do(end_name))
    expected = '''  {name}DO {label}while ({term})
    WRITE (6, '(I0)') variable
{endlabel} {endstmt}
'''.format(name=name_snippet or '',
           label=label_snippet or '',
           term=terminal_expression,
           endlabel=end_label or ' ',
           endstmt=get_end_do(end_name))
    print(code)
    reader = FortranStringReader(code)
    reader.set_format(FortranFormat(True, False))
    parser = FortranParser(reader)
    if (name != end_name) or (label and (label != end_label)):
        with pytest.raises(AnalyzeError):
            parser.parse()
    else:
        parser.parse()
        loop = parser.block.content[0]
        assert str(loop).splitlines() == expected.splitlines()
コード例 #27
0
ファイル: f2pygen.py プロジェクト: ScottWales/PSyclone
    def __init__(self, parent, language, position, directive_type, content):
        '''
        :param parent: node in AST to which to add directive as a child
        :type parent: :py:class:`psyclone.f2pygen.BaseGen`
        :param str language: the type of directive (e.g. OMP or ACC)
        :param str position: "end" if this is the end of a directive block
        :param str directive_type: the directive itself (e.g. "PARALLEL DO")
        :param str content: any additional arguments to add to the directive
                            (e.g. "PRIVATE(ji)")

        :raises RuntimeError: if an unrecognised directive language is
                              specified
        '''
        self._supported_languages = ["omp"]
        self._language = language
        self._directive_type = directive_type

        reader = FortranStringReader("! content\n")
        reader.set_format(FortranFormat(True, True))  # free form, strict
        subline = reader.next()

        if language == "omp":
            my_comment = OMPDirective(parent.root, subline, position,
                                      directive_type)
            my_comment.content = "$omp"
            if position == "end":
                my_comment.content += " end"
            my_comment.content += " " + directive_type
            if content != "":
                my_comment.content += " " + content
        else:
            raise RuntimeError(
                "Error, unsupported directive language. Expecting one of "
                "{0} but found '{1}'".format(str(self._supported_languages),
                                             language))

        BaseGen.__init__(self, parent, my_comment)
コード例 #28
0
ファイル: f2pygen.py プロジェクト: ScottWales/PSyclone
    def __init__(self, parent, datatype="", entity_decls=None, intent="",
                 pointer=False, attrspec=None):
        '''
        :param parent: the node to which to add this type delcn as a child
        :type parent: :py:class:`psyclone.f2pygen.BaseGen`
        :param str datatype: the derived type
        :param list entity_decls: List of variable names to declare
        :param str intent: the intent attribute for the declaration
        :param bool pointer: whether or not this is a pointer declaration
        :param attrspec: list of other attributes to add to declaration

        :raises RuntimeError: if no variable names are specified
        '''
        if entity_decls is None:
            raise RuntimeError(
                "Cannot create a declaration of a derived-type variable "
                "without specifying the name(s) of the variable(s)")
        # make a copy of entity_decls as we may modify it
        local_entity_decls = entity_decls[:]
        if attrspec is None:
            attrspec = []
        my_attrspec = [spec for spec in attrspec]
        if intent != "":
            my_attrspec.append("intent({0})".format(intent))
        if pointer is not False:
            my_attrspec.append("pointer")
        self._names = local_entity_decls

        reader = FortranStringReader("type(vanillatype) :: vanilla")
        reader.set_format(FortranFormat(True, False))  # free form, strict
        myline = reader.next()

        self._typedecl = fparser1.typedecl_statements.Type(parent.root, myline)
        self._typedecl.selector = ('', datatype)
        self._typedecl.attrspec = my_attrspec
        self._typedecl.entity_decls = local_entity_decls
        BaseGen.__init__(self, parent, self._typedecl)
コード例 #29
0
def test_base_fixed_nonlabel(log):
    '''
    Tests that FortranReaderBase.get_source_item() logs the correct messages
    when there is an unexpected character in the initial 6 columns.
    '''
    # Checks that a bad character in the first column causes an event to be
    # logged.
    code = 'w    integer :: i'
    log.reset()
    unit_under_test = FortranStringReader(code)
    mode = FortranFormat(False, True)
    unit_under_test.set_format(mode)  # Force fixed format
    unit_under_test.get_source_item()
    assert log.messages['debug'] == []
    assert log.messages['info'] == []
    assert log.messages['error'] == []
    assert log.messages['critical'] == []
    result = log.messages['warning'][0].split('<==')[1].lstrip()
    expected = "non-space/digit char 'w' found in column 1 of fixed " \
               + "Fortran code, interpreting line as comment line"
    assert result == expected

    # Checks a bad character in columns 2-6
    for i in range(1, 5):
        code = ' ' * i + 'w' + ' ' * (5 - i) + 'integer :: i'
        log.reset()
        unit_under_test = FortranStringReader(code)
        mode = FortranFormat(False, True)
        unit_under_test.set_format(mode)  # Force strict fixed format
        unit_under_test.get_source_item()
        assert log.messages['debug'] == []
        assert log.messages['info'] == []
        assert log.messages['error'] == []
        assert log.messages['critical'] == []
        result = log.messages['warning'][0].split('<==')[1].lstrip()
        expected = "non-space/digit char 'w' found in column {col} " \
                   + "of fixed Fortran code"
        assert result == expected.format(col=i + 1)

    # Checks for a bad character, not in the first column, with "sloppy" mode
    # engaged.
    code = ' w   integer :: i'
    log.reset()
    unit_under_test = FortranStringReader(code)
    mode = FortranFormat(False, False)
    unit_under_test.set_format(mode)  # Force sloppy fixed format
    unit_under_test.get_source_item()
    assert log.messages['debug'] == []
    assert log.messages['info'] == []
    assert log.messages['error'] == []
    assert log.messages['critical'] == []
    expected = "non-space/digit char 'w' found in column 2 " \
               + "of fixed Fortran code, switching to free format mode"
    result = log.messages['warning'][0].split('<==')[1].lstrip()
    assert result == expected
コード例 #30
0
    def update(self):
        '''
        Update the underlying fparser2 parse tree to implement the profiling
        region represented by this Node. This involves adding the necessary
        module use statement as well as the calls to the profiling API.

        TODO #435 - remove this whole method once the NEMO API uses the
        Fortran backend of the PSyIR.

        :raises NotImplementedError: if the routine which is to have \
                             profiling added to it does not already have a \
                             Specification Part (i.e. some declarations).
        :raises NotImplementedError: if there would be a name clash with \
                             existing variable/module names in the code to \
                             be transformed.
        :raises InternalError: if we fail to find the node in the parse tree \
                             corresponding to the end of the profiling region.

        '''
        from fparser.common.sourceinfo import FortranFormat
        from fparser.common.readfortran import FortranStringReader
        from fparser.two.utils import walk_ast
        from fparser.two import Fortran2003
        from psyclone.psyGen import object_index, Schedule, InternalError

        # Ensure child nodes are up-to-date
        super(ProfileNode, self).update()

        # Get the parse tree of the routine containing this region
        ptree = self.root.invoke._ast
        # Rather than repeatedly walk the tree, we do it once for all of
        # the node types we will be interested in...
        node_list = walk_ast([ptree], [
            Fortran2003.Main_Program, Fortran2003.Subroutine_Stmt,
            Fortran2003.Function_Stmt, Fortran2003.Specification_Part,
            Fortran2003.Use_Stmt, Fortran2003.Name
        ])
        for node in node_list:
            if isinstance(
                    node,
                (Fortran2003.Main_Program, Fortran2003.Subroutine_Stmt,
                 Fortran2003.Function_Stmt)):
                names = walk_ast([node], [Fortran2003.Name])
                routine_name = str(names[0]).lower()
                break

        for node in node_list:
            if isinstance(node, Fortran2003.Specification_Part):
                spec_part = node
                break
        else:
            # This limitation will be removed when we use the Fortran
            # backend of the PSyIR (#435)
            raise NotImplementedError(
                "Addition of profiling regions to routines without any "
                "existing declarations is not supported and '{0}' has no "
                "Specification-Part".format(routine_name))

        # Get the existing use statements
        found = False
        for node in node_list[:]:
            if isinstance(node, Fortran2003.Use_Stmt) and \
               self.fortran_module == str(node.items[2]).lower():
                # Check that the use statement matches the one we would
                # insert (i.e. the code doesn't already contain a module
                # with the same name as that used by the profiling API)
                if str(node).lower() != self.use_stmt.lower():
                    raise NotImplementedError(
                        "Cannot add profiling to '{0}' because it already "
                        "'uses' a module named '{1}'".format(
                            routine_name, self.fortran_module))
                found = True
                # To make our check on name clashes below easier, remove
                # the Name nodes associated with this use from our
                # list of nodes.
                names = walk_ast([node], [Fortran2003.Name])
                for name in names:
                    node_list.remove(name)

        if not found:
            # We don't already have a use for the profiling module so
            # add one.
            reader = FortranStringReader(
                "use profile_mod, only: ProfileData, ProfileStart, ProfileEnd")
            # Tell the reader that the source is free format
            reader.set_format(FortranFormat(True, False))
            use = Fortran2003.Use_Stmt(reader)
            spec_part.content.insert(0, use)

        # Check that we won't have any name-clashes when we insert the
        # symbols required for profiling. This check uses the list of symbols
        # that we created before adding the `use profile_mod...` statement.
        if not self.root.profiling_name_clashes_checked:
            for node in node_list:
                if isinstance(node, Fortran2003.Name):
                    text = str(node).lower()
                    # Check for the symbols we import from the profiling module
                    for symbol in self.profiling_symbols:
                        if text == symbol.lower():
                            raise NotImplementedError(
                                "Cannot add profiling to '{0}' because it "
                                "already contains a symbol that clashes with "
                                "one of those ('{1}') that must be imported "
                                "from the PSyclone profiling module.".format(
                                    routine_name, symbol))
                    # Check for the name of the profiling module itself
                    if text == self.fortran_module:
                        raise NotImplementedError(
                            "Cannot add profiling to '{0}' because it already "
                            "contains a symbol that clashes with the name of "
                            "the PSyclone profiling module ('profile_mod')".
                            format(routine_name))
                    # Check for the names of profiling variables
                    if text.startswith(self.profiling_var):
                        raise NotImplementedError(
                            "Cannot add profiling to '{0}' because it already"
                            " contains symbols that potentially clash with "
                            "the variables we will insert for each profiling "
                            "region ('{1}*').".format(routine_name,
                                                      self.profiling_var))
        # Flag that we have now checked for name clashes so that if there's
        # more than one profiling node we don't fall over on the symbols
        # we've previous inserted.
        self.root.profiling_name_clashes_checked = True

        # Create a name for this region by finding where this profiling
        # node is in the list of profiling nodes in this Invoke.
        sched = self.root
        pnodes = sched.walk(ProfileNode)
        region_idx = pnodes.index(self)
        region_name = "r{0}".format(region_idx)
        var_name = "psy_profile{0}".format(region_idx)

        # Create a variable for this profiling region
        reader = FortranStringReader(
            "type(ProfileData), save :: {0}".format(var_name))
        # Tell the reader that the source is free format
        reader.set_format(FortranFormat(True, False))
        decln = Fortran2003.Type_Declaration_Stmt(reader)
        spec_part.content.append(decln)

        # Find the parent in the parse tree - first get a pointer to the
        # AST for the content of this region.
        if isinstance(self.children[0], Schedule) and \
           not self.children[0].ast:
            # TODO #435 Schedule should really have a valid ast pointer.
            content_ast = self.children[0][0].ast
        else:
            content_ast = self.children[0].ast
        # Now store the parent of this region
        fp_parent = content_ast._parent
        # Find the location of the AST of our first child node in the
        # list of child nodes of our parent in the fparser parse tree.
        ast_start_index = object_index(fp_parent.content, content_ast)
        # Finding the location of the end is harder as it might be the
        # end of a clause within an If or Select block. We therefore
        # work back up the fparser2 parse tree until we find a node that is
        # a direct child of the parent node.
        ast_end_index = None
        if self.children[-1].ast_end:
            ast_end = self.children[-1].ast_end
        else:
            ast_end = self.children[-1].ast
        # Keep a copy of the pointer into the parse tree in case of errors
        ast_end_copy = ast_end

        while ast_end_index is None:
            try:
                ast_end_index = object_index(fp_parent.content, ast_end)
            except ValueError:
                # ast_end is not a child of fp_parent so go up to its parent
                # and try again
                if hasattr(ast_end, "_parent") and ast_end._parent:
                    ast_end = ast_end._parent
                else:
                    raise InternalError(
                        "Failed to find the location of '{0}' in the fparser2 "
                        "Parse Tree:\n{1}\n".format(str(ast_end_copy),
                                                    str(fp_parent.content)))

        # Add the profiling-end call
        reader = FortranStringReader("CALL ProfileEnd({0})".format(var_name))
        # Tell the reader that the source is free format
        reader.set_format(FortranFormat(True, False))
        pecall = Fortran2003.Call_Stmt(reader)
        fp_parent.content.insert(ast_end_index + 1, pecall)

        # Add the profiling-start call
        reader = FortranStringReader(
            "CALL ProfileStart('{0}', '{1}', {2})".format(
                routine_name, region_name, var_name))
        reader.set_format(FortranFormat(True, False))
        pscall = Fortran2003.Call_Stmt(reader)
        fp_parent.content.insert(ast_start_index, pscall)