From f532b4257a6a14f43f0f76156603ed180d7d0f09 Mon Sep 17 00:00:00 2001 From: rupertford Date: Thu, 16 Jun 2022 17:14:33 +0100 Subject: [PATCH 1/9] issue #353. Adding strict order statements. --- src/fparser/two/Fortran2003.py | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/fparser/two/Fortran2003.py b/src/fparser/two/Fortran2003.py index 20cbde65..a282ae52 100644 --- a/src/fparser/two/Fortran2003.py +++ b/src/fparser/two/Fortran2003.py @@ -410,7 +410,7 @@ class Specification_Part(BlockBase): # R204 def match(reader): return BlockBase.match(None, [Use_Stmt, Import_Stmt, Implicit_Part, Declaration_Construct], - None, reader) + None, reader, strict_order=True) class Implicit_Part(BlockBase): # R205 @@ -9329,7 +9329,7 @@ class Module(BlockBase): # R1104 def match(reader): return BlockBase.match(Module_Stmt, [Specification_Part, Module_Subprogram_Part], - End_Module_Stmt, reader) + End_Module_Stmt, reader, strict_order=True) class Module_Stmt(StmtBase, WORDClsBase): # R1105 @@ -10552,7 +10552,7 @@ def match(reader): Execution_Part, Internal_Subprogram_Part], End_Function_Stmt, - reader) + reader, strict_order=True) class Function_Stmt(StmtBase): # R1224 @@ -10814,7 +10814,7 @@ def match(reader): Execution_Part, Internal_Subprogram_Part], End_Subroutine_Stmt, - reader) + reader, strict_order=True) match = staticmethod(match) From 8b90bc087bbb96efa66d1467c4e7f794a588fff9 Mon Sep 17 00:00:00 2001 From: rupertford Date: Mon, 20 Jun 2022 23:15:29 +0100 Subject: [PATCH 2/9] pr #358. Fix for 0 or 1 blockbase matching. --- src/fparser/two/Fortran2003.py | 8 ++++---- src/fparser/two/utils.py | 6 +++++- 2 files changed, 9 insertions(+), 5 deletions(-) diff --git a/src/fparser/two/Fortran2003.py b/src/fparser/two/Fortran2003.py index a282ae52..d28dba2a 100644 --- a/src/fparser/two/Fortran2003.py +++ b/src/fparser/two/Fortran2003.py @@ -9191,7 +9191,7 @@ def match(reader): return BlockBase.match( Program_Stmt, [Specification_Part, Execution_Part, Internal_Subprogram_Part], End_Program_Stmt, - reader, match_names=True, strict_order=True) + reader, match_names=True, strict_order=True, once_only=True) class Main_Program0(BlockBase): @@ -9329,7 +9329,7 @@ class Module(BlockBase): # R1104 def match(reader): return BlockBase.match(Module_Stmt, [Specification_Part, Module_Subprogram_Part], - End_Module_Stmt, reader, strict_order=True) + End_Module_Stmt, reader, strict_order=True, once_only=True) class Module_Stmt(StmtBase, WORDClsBase): # R1105 @@ -10552,7 +10552,7 @@ def match(reader): Execution_Part, Internal_Subprogram_Part], End_Function_Stmt, - reader, strict_order=True) + reader, strict_order=True, once_only=True) class Function_Stmt(StmtBase): # R1224 @@ -10814,7 +10814,7 @@ def match(reader): Execution_Part, Internal_Subprogram_Part], End_Subroutine_Stmt, - reader, strict_order=True) + reader, strict_order=True, once_only=True) match = staticmethod(match) diff --git a/src/fparser/two/utils.py b/src/fparser/two/utils.py index daceb342..c8cf5285 100644 --- a/src/fparser/two/utils.py +++ b/src/fparser/two/utils.py @@ -527,7 +527,8 @@ def match(startcls, subclasses, endcls, reader, enable_if_construct_hook=False, enable_where_construct_hook=False, strict_order=False, - strict_match_names=False): + strict_match_names=False, + once_only=False): ''' Checks whether the content in reader matches the given type of block statement (e.g. DO..END DO, IF...END IF etc.) @@ -677,6 +678,9 @@ def match(startcls, subclasses, endcls, reader, if not strict_order: # Return to start of classes list now that we've matched. i = 0 + if once_only: + # Move to next class if we only allow up to one match. + i += 1 if enable_if_construct_hook: if isinstance(obj, di.Else_If_Stmt): # Got an else-if so go back to start of possible From c20d9e11922c1e6b4522c708adc7f860fba4151d Mon Sep 17 00:00:00 2001 From: rupertford Date: Tue, 21 Jun 2022 22:37:57 +0100 Subject: [PATCH 3/9] pr #358. Updated tests. --- src/fparser/two/tests/test_comments.py | 7 +++---- src/fparser/two/tests/utils/test_blockbase.py | 21 ++++++++++--------- src/fparser/two/utils.py | 9 ++++---- 3 files changed, 19 insertions(+), 18 deletions(-) diff --git a/src/fparser/two/tests/test_comments.py b/src/fparser/two/tests/test_comments.py index 9b8569d0..c03a7667 100644 --- a/src/fparser/two/tests/test_comments.py +++ b/src/fparser/two/tests/test_comments.py @@ -264,7 +264,7 @@ def test_function_comments(): comment = fn_unit.content[1].content[0].content[0] assert isinstance(comment, Comment) assert "! This is a function" in str(comment) - comment = fn_unit.content[1].content[2].content[0] + comment = fn_unit.content[1].content[2] assert isinstance(comment, Comment) assert "! Comment1" in str(comment) exec_part = fn_unit.content[2] @@ -291,14 +291,13 @@ def test_subroutine_comments(): reader = get_reader(source, isfree=True, ignore_comments=False) fn_unit = Subroutine_Subprogram(reader) assert isinstance(fn_unit, Subroutine_Subprogram) - walk(fn_unit.children, Comment, debug=True) spec_part = fn_unit.content[1] comment = spec_part.content[0].content[0] assert isinstance(comment, Comment) assert "! First comment" in str(comment) - comment = spec_part.content[2].content[0] + comment = spec_part.content[2] assert isinstance(comment, Comment) - assert comment.parent is spec_part.content[2] + assert comment.parent is spec_part assert "! Body comment" in str(comment) exec_part = fn_unit.content[2] comment = exec_part.content[1] diff --git a/src/fparser/two/tests/utils/test_blockbase.py b/src/fparser/two/tests/utils/test_blockbase.py index 082c97c9..b034b0d4 100644 --- a/src/fparser/two/tests/utils/test_blockbase.py +++ b/src/fparser/two/tests/utils/test_blockbase.py @@ -74,20 +74,21 @@ def test_include(f2003_create): "end program test\n" "! I should be ignored" "include 'so should I'"), ignore_comments=False) - result = BlockBase.match(startcls, subclasses, endcls, reader) + result = BlockBase.match(startcls, subclasses, endcls, reader, + strict_order=True, once_only=True) assert ( "([Include_Stmt(Include_Filename('1')), Comment('! comment1'), " "Program_Stmt('PROGRAM', Name('test')), Specification_Part(" "Implicit_Part(Include_Stmt(Include_Filename('2')), " - "Comment('! comment2')), Type_Declaration_Stmt(Intrinsic_Type_Spec(" - "'INTEGER', None), None, Entity_Decl_List(',', (Entity_Decl(Name(" - "'i'), None, None, None),))), Implicit_Part(Include_Stmt(" - "Include_Filename('3')), Comment('! comment3'))), Execution_Part(" - "Assignment_Stmt(Name('i'), '=', Int_Literal_Constant('1', None)), " - "Include_Stmt(Include_Filename('4')), Comment('! comment4')), " - "Internal_Subprogram_Part(Contains_Stmt('CONTAINS'), Include_Stmt(" - "Include_Filename('5')), Comment('! comment5')), End_Program_Stmt(" - "'PROGRAM', Name('test'))],)" in str(result).replace("u'", "'")) + "Comment('! comment2')), Type_Declaration_Stmt(Intrinsic_Type_Spec" + "('INTEGER', None), None, Entity_Decl_List(',', (Entity_Decl(Name" + "('i'), None, None, None),))), Include_Stmt(Include_Filename('3'))), " + "Execution_Part(Comment('! comment3'), Assignment_Stmt(Name('i'), " + "'=', Int_Literal_Constant('1', None)), Include_Stmt(" + "Include_Filename('4')), Comment('! comment4')), " + "Internal_Subprogram_Part(Contains_Stmt('CONTAINS'), " + "Include_Stmt(Include_Filename('5')), Comment('! comment5')), " + "End_Program_Stmt('PROGRAM', Name('test'))],)" in str(result)) assert "should" not in str(result) diff --git a/src/fparser/two/utils.py b/src/fparser/two/utils.py index c8cf5285..f18093c0 100644 --- a/src/fparser/two/utils.py +++ b/src/fparser/two/utils.py @@ -529,8 +529,7 @@ def match(startcls, subclasses, endcls, reader, strict_order=False, strict_match_names=False, once_only=False): - ''' - Checks whether the content in reader matches the given + '''Checks whether the content in reader matches the given type of block statement (e.g. DO..END DO, IF...END IF etc.) :param type startcls: the class marking the beginning of the block @@ -551,6 +550,9 @@ def match(startcls, subclasses, endcls, reader, given subclasses. :param bool strict_match_names: if start name present, end name \ must exist and match. + :param bool once_only: whether to restrict matching to at most \ + once for a subclass. This is only active if strict_order is \ + also set. :return: instance of startcls or None if no match is found :rtype: startcls @@ -678,8 +680,7 @@ def match(startcls, subclasses, endcls, reader, if not strict_order: # Return to start of classes list now that we've matched. i = 0 - if once_only: - # Move to next class if we only allow up to one match. + elif once_only: i += 1 if enable_if_construct_hook: if isinstance(obj, di.Else_If_Stmt): From 0f7b914aec017ba3793af6961635994fc86d1f9b Mon Sep 17 00:00:00 2001 From: rupertford Date: Sun, 26 Jun 2022 17:22:36 +0100 Subject: [PATCH 4/9] pr #358. Fixed comment/include/preprocess problem. --- src/fparser/two/tests/test_comments.py | 29 ++++++++----------- src/fparser/two/tests/test_fortran2003.py | 19 ++++++++++++ src/fparser/two/tests/utils/test_blockbase.py | 21 +++++++------- src/fparser/two/utils.py | 16 +++++++--- 4 files changed, 54 insertions(+), 31 deletions(-) diff --git a/src/fparser/two/tests/test_comments.py b/src/fparser/two/tests/test_comments.py index c03a7667..46b0b298 100644 --- a/src/fparser/two/tests/test_comments.py +++ b/src/fparser/two/tests/test_comments.py @@ -1,4 +1,4 @@ -# Copyright (c) 2017-2020 Science and Technology Facilities Council +# Copyright (c) 2017-2022 Science and Technology Facilities Council # All rights reserved. # # Modifications made as part of the fparser project are distributed @@ -191,24 +191,21 @@ def test_prog_comments(): # |--> Comment # |--> Main_Program # . |--> Program_Stmt - # . |--> Specification_Part - # . . \--> Implicit_Part - # . . \--> Comment + # . |--> Comment # |--> Execution_Part # | |--> Write_Stmt - # | \--> Comment + # | |--> Comment # . . # . # |--> Comment from fparser.two.Fortran2003 import Main_Program, Write_Stmt, \ End_Program_Stmt - walk(obj.children, Comment, debug=True) assert type(obj.content[0]) == Comment assert str(obj.content[0]) == "! A troublesome comment" assert type(obj.content[1]) == Main_Program main_prog = obj.content[1] - assert type(main_prog.content[1].content[0].content[0]) == Comment - assert (str(main_prog.content[1].content[0].content[0]) == + assert type(main_prog.content[1]) == Comment + assert (str(main_prog.content[1]) == "! A full comment line") exec_part = main_prog.content[2] assert type(exec_part.content[0]) == Write_Stmt @@ -257,17 +254,15 @@ def test_function_comments(): # # # - # - # - # + # # , "'! This is a function'" - comment = fn_unit.content[1].content[0].content[0] + comment = fn_unit.content[1] assert isinstance(comment, Comment) assert "! This is a function" in str(comment) - comment = fn_unit.content[1].content[2] + comment = fn_unit.content[2].content[2] assert isinstance(comment, Comment) assert "! Comment1" in str(comment) - exec_part = fn_unit.content[2] + exec_part = fn_unit.content[3] comment = exec_part.content[1] assert isinstance(comment, Comment) assert "! Comment2" in str(comment) @@ -291,15 +286,15 @@ def test_subroutine_comments(): reader = get_reader(source, isfree=True, ignore_comments=False) fn_unit = Subroutine_Subprogram(reader) assert isinstance(fn_unit, Subroutine_Subprogram) - spec_part = fn_unit.content[1] - comment = spec_part.content[0].content[0] + comment = fn_unit.content[1] assert isinstance(comment, Comment) assert "! First comment" in str(comment) + spec_part = fn_unit.children[2] comment = spec_part.content[2] assert isinstance(comment, Comment) assert comment.parent is spec_part assert "! Body comment" in str(comment) - exec_part = fn_unit.content[2] + exec_part = fn_unit.content[3] comment = exec_part.content[1] assert isinstance(comment, Comment) assert comment.parent is exec_part diff --git a/src/fparser/two/tests/test_fortran2003.py b/src/fparser/two/tests/test_fortran2003.py index 9a580713..4443a091 100644 --- a/src/fparser/two/tests/test_fortran2003.py +++ b/src/fparser/two/tests/test_fortran2003.py @@ -131,6 +131,25 @@ def test_specification_part(): assert isinstance(obj, tcls), repr(obj) assert 'TYPE :: a\nEND TYPE a\nTYPE :: b\nEND TYPE b' in str(obj) + obj = tcls(get_reader('''\ +! comment1 +use a +! comment2 +use b +! comment3 +#define x +use c +include 'fred' +#ifdef x +use d +#endif +''', ignore_comments=False)) + assert isinstance(obj, tcls), repr(obj) + expected = ( + "! comment1\nUSE a\n! comment2\nUSE b\n! comment3\n#define x\n" + "USE c\nINCLUDE 'fred'\n#ifdef x\nUSE d\n#endif") + assert expected in str(obj) + # # SECTION 3 # diff --git a/src/fparser/two/tests/utils/test_blockbase.py b/src/fparser/two/tests/utils/test_blockbase.py index b034b0d4..42c336f4 100644 --- a/src/fparser/two/tests/utils/test_blockbase.py +++ b/src/fparser/two/tests/utils/test_blockbase.py @@ -76,19 +76,20 @@ def test_include(f2003_create): "include 'so should I'"), ignore_comments=False) result = BlockBase.match(startcls, subclasses, endcls, reader, strict_order=True, once_only=True) + print(str(result)) assert ( "([Include_Stmt(Include_Filename('1')), Comment('! comment1'), " - "Program_Stmt('PROGRAM', Name('test')), Specification_Part(" - "Implicit_Part(Include_Stmt(Include_Filename('2')), " - "Comment('! comment2')), Type_Declaration_Stmt(Intrinsic_Type_Spec" - "('INTEGER', None), None, Entity_Decl_List(',', (Entity_Decl(Name" - "('i'), None, None, None),))), Include_Stmt(Include_Filename('3'))), " - "Execution_Part(Comment('! comment3'), Assignment_Stmt(Name('i'), " - "'=', Int_Literal_Constant('1', None)), Include_Stmt(" + "Program_Stmt('PROGRAM', Name('test')), Include_Stmt(" + "Include_Filename('2')), Comment('! comment2'), Specification_Part" + "(Type_Declaration_Stmt(Intrinsic_Type_Spec('INTEGER', None), None, " + "Entity_Decl_List(',', (Entity_Decl(Name('i'), None, None, None),))), " + "Include_Stmt(Include_Filename('3')), Comment('! comment3')), " + "Execution_Part(Assignment_Stmt(Name('i'), '=', " + "Int_Literal_Constant('1', None)), Include_Stmt(" "Include_Filename('4')), Comment('! comment4')), " - "Internal_Subprogram_Part(Contains_Stmt('CONTAINS'), " - "Include_Stmt(Include_Filename('5')), Comment('! comment5')), " - "End_Program_Stmt('PROGRAM', Name('test'))],)" in str(result)) + "Internal_Subprogram_Part(Contains_Stmt('CONTAINS'), Include_Stmt(" + "Include_Filename('5')), Comment('! comment5')), End_Program_Stmt(" + "'PROGRAM', Name('test'))],)" in str(result)) assert "should" not in str(result) diff --git a/src/fparser/two/utils.py b/src/fparser/two/utils.py index f18093c0..c83067a8 100644 --- a/src/fparser/two/utils.py +++ b/src/fparser/two/utils.py @@ -600,15 +600,19 @@ def match(startcls, subclasses, endcls, reader, start_name = obj.get_start_name() # Comments and Include statements are always valid sub-classes - classes = subclasses + [di.Comment, di.Include_Stmt] + # classes = subclasses + [di.Comment, di.Include_Stmt] # Preprocessor directives are always valid sub-classes - cpp_classes = [getattr(di.C99Preprocessor, cls_name) - for cls_name in di.C99Preprocessor.CPP_CLASS_NAMES] - classes += cpp_classes + # cpp_classes = [getattr(di.C99Preprocessor, cls_name) + # for cls_name in di.C99Preprocessor.CPP_CLASS_NAMES] + # classes += cpp_classes + classes = subclasses if endcls is not None: classes += [endcls] endcls_all = tuple([endcls]+endcls.subclasses[endcls.__name__]) + # Deal with any preceding comments, includes, and/or directives + DynamicImport.add_comments_includes_directives(content, reader) + try: # Start trying to match the various subclasses, starting from # the beginning of the list (where else?) @@ -677,6 +681,10 @@ def match(startcls, subclasses, endcls, reader, # We've found the enclosing end statement so break out found_end = True break + + # Deal with any following comments, includes, and/or directives + DynamicImport.add_comments_includes_directives(content, reader) + if not strict_order: # Return to start of classes list now that we've matched. i = 0 From 800af82e0c6fbe30471accd8a1caec802f4aef0e Mon Sep 17 00:00:00 2001 From: rupertford Date: Sun, 26 Jun 2022 22:09:45 +0100 Subject: [PATCH 5/9] pr #358. black changes. --- src/fparser/two/Fortran2003.py | 11 ++++++----- src/fparser/two/tests/utils/test_blockbase.py | 7 ++++--- src/fparser/two/utils.py | 2 +- 3 files changed, 11 insertions(+), 9 deletions(-) diff --git a/src/fparser/two/Fortran2003.py b/src/fparser/two/Fortran2003.py index 5c404503..526d7174 100644 --- a/src/fparser/two/Fortran2003.py +++ b/src/fparser/two/Fortran2003.py @@ -447,7 +447,7 @@ def match(reader): [Use_Stmt, Import_Stmt, Implicit_Part, Declaration_Construct], None, reader, - strict_order=True + strict_order=True, ) @@ -10078,7 +10078,7 @@ def match(reader): reader, match_names=True, strict_order=True, - once_only=True + once_only=True, ) @@ -10233,7 +10233,7 @@ def match(reader): [Specification_Part, Module_Subprogram_Part], End_Module_Stmt, reader, - once_only=True + once_only=True, ) @@ -11598,7 +11598,7 @@ def match(reader): [Specification_Part, Execution_Part, Internal_Subprogram_Part], End_Function_Stmt, reader, - once_only=True + once_only=True, ) @@ -11877,8 +11877,9 @@ def match(reader): [Specification_Part, Execution_Part, Internal_Subprogram_Part], End_Subroutine_Stmt, reader, - once_only=True + once_only=True, ) + match = staticmethod(match) diff --git a/src/fparser/two/tests/utils/test_blockbase.py b/src/fparser/two/tests/utils/test_blockbase.py index f1d43127..9b6d8745 100644 --- a/src/fparser/two/tests/utils/test_blockbase.py +++ b/src/fparser/two/tests/utils/test_blockbase.py @@ -1,4 +1,4 @@ -# Copyright (c) 2019-2021, Science and Technology Facilities Council +# Copyright (c) 2019-2022, Science and Technology Facilities Council # All rights reserved. @@ -81,8 +81,9 @@ def test_include(f2003_create): ), ignore_comments=False, ) - result = BlockBase.match(startcls, subclasses, endcls, reader, - strict_order=True, once_only=True) + result = BlockBase.match( + startcls, subclasses, endcls, reader, strict_order=True, once_only=True + ) assert ( "([Include_Stmt(Include_Filename('1')), Comment('! comment1'), " "Program_Stmt('PROGRAM', Name('test')), Include_Stmt(" diff --git a/src/fparser/two/utils.py b/src/fparser/two/utils.py index c685ef9c..6e32ff69 100644 --- a/src/fparser/two/utils.py +++ b/src/fparser/two/utils.py @@ -551,7 +551,7 @@ def match( enable_where_construct_hook=False, strict_order=False, strict_match_names=False, - once_only=False + once_only=False, ): """ Checks whether the content in reader matches the given From 8b371f483aaf6147245f8e841467f901bee2e610 Mon Sep 17 00:00:00 2001 From: rupertford Date: Sun, 26 Jun 2022 22:12:56 +0100 Subject: [PATCH 6/9] pr #358. Black changes. --- src/fparser/two/tests/test_fortran2003.py | 14 ++++++++++---- 1 file changed, 10 insertions(+), 4 deletions(-) diff --git a/src/fparser/two/tests/test_fortran2003.py b/src/fparser/two/tests/test_fortran2003.py index 7bb4bcd1..fd43eab6 100644 --- a/src/fparser/two/tests/test_fortran2003.py +++ b/src/fparser/two/tests/test_fortran2003.py @@ -140,8 +140,9 @@ def test_specification_part(): assert isinstance(obj, tcls), repr(obj) assert "TYPE :: a\nEND TYPE a\nTYPE :: b\nEND TYPE b" in str(obj) - - obj = tcls(get_reader('''\ + obj = tcls( + get_reader( + """\ ! comment1 use a ! comment2 @@ -153,13 +154,18 @@ def test_specification_part(): #ifdef x use d #endif -''', ignore_comments=False)) +""", + ignore_comments=False, + ) + ) assert isinstance(obj, tcls), repr(obj) expected = ( "! comment1\nUSE a\n! comment2\nUSE b\n! comment3\n#define x\n" - "USE c\nINCLUDE 'fred'\n#ifdef x\nUSE d\n#endif") + "USE c\nINCLUDE 'fred'\n#ifdef x\nUSE d\n#endif" + ) assert expected in str(obj) + # # SECTION 3 # From e4baa7f1d5c469a78b025e050e7cc34e7a1dabfc Mon Sep 17 00:00:00 2001 From: rupertford Date: Thu, 11 Aug 2022 10:13:40 +0100 Subject: [PATCH 7/9] pr #358. Added test for comment placement as requested by @arporter. --- src/fparser/two/tests/test_comments.py | 28 +++++++++++++++++++++++++- 1 file changed, 27 insertions(+), 1 deletion(-) diff --git a/src/fparser/two/tests/test_comments.py b/src/fparser/two/tests/test_comments.py index dec7fa95..5d699f60 100644 --- a/src/fparser/two/tests/test_comments.py +++ b/src/fparser/two/tests/test_comments.py @@ -34,7 +34,8 @@ """ Module containing tests for aspects of fparser2 related to comments """ import pytest -from fparser.two.Fortran2003 import Program, Comment, Subroutine_Subprogram +from fparser.two.Fortran2003 import Program, Comment, Subroutine_Subprogram, \ + Execution_Part, Specification_Part, Block_Nonlabel_Do_Construct from fparser.two.utils import walk from fparser.api import get_reader @@ -403,3 +404,28 @@ def test_action_stmts(): assert "a big array" in str(ifstmt) cmt = get_child(ifstmt, Comment) assert cmt.parent is ifstmt + + +def test_do_loop_coments(): + """Check that comments around and within do loops appear in the expected + places in the tree.""" + source = """\ +program test +integer :: arg1 +integer :: iterator +!comment_out 1 +arg1 = 10 +!comment_out 2 +do iterator = 0,arg1 + !comment_in + print *, iterator +end do +!comment_out 3 +end program test""" + reader = get_reader(source, isfree=True, ignore_comments=False) + obj = Program(reader) + comments = walk(obj, Comment) + assert isinstance(comments[0].parent, Specification_Part) + assert isinstance(comments[1].parent, Execution_Part) + assert isinstance(comments[2].parent, Block_Nonlabel_Do_Construct) + assert isinstance(comments[3].parent, Execution_Part) From e4efa6a99236e838e2808516eef20ce995665bf7 Mon Sep 17 00:00:00 2001 From: rupertford Date: Thu, 11 Aug 2022 10:18:49 +0100 Subject: [PATCH 8/9] pr #358. Back to black. --- src/fparser/two/tests/test_comments.py | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/src/fparser/two/tests/test_comments.py b/src/fparser/two/tests/test_comments.py index 5d699f60..2521d39c 100644 --- a/src/fparser/two/tests/test_comments.py +++ b/src/fparser/two/tests/test_comments.py @@ -34,8 +34,14 @@ """ Module containing tests for aspects of fparser2 related to comments """ import pytest -from fparser.two.Fortran2003 import Program, Comment, Subroutine_Subprogram, \ - Execution_Part, Specification_Part, Block_Nonlabel_Do_Construct +from fparser.two.Fortran2003 import ( + Program, + Comment, + Subroutine_Subprogram, + Execution_Part, + Specification_Part, + Block_Nonlabel_Do_Construct, +) from fparser.two.utils import walk from fparser.api import get_reader From 512cdfe7562c5eaf99b2941b8836b9dc358d318a Mon Sep 17 00:00:00 2001 From: rupertford Date: Thu, 15 Sep 2022 17:10:29 +0100 Subject: [PATCH 9/9] pr #358. Addressed some of reviewer's comments. --- src/fparser/two/tests/test_comments.py | 21 +++++++++++---------- src/fparser/two/tests/test_fortran2003.py | 19 ++++++++++++++++++- src/fparser/two/utils.py | 7 +++++-- 3 files changed, 34 insertions(+), 13 deletions(-) diff --git a/src/fparser/two/tests/test_comments.py b/src/fparser/two/tests/test_comments.py index 2521d39c..fb99e399 100644 --- a/src/fparser/two/tests/test_comments.py +++ b/src/fparser/two/tests/test_comments.py @@ -34,6 +34,8 @@ """ Module containing tests for aspects of fparser2 related to comments """ import pytest + +from fparser.api import get_reader from fparser.two.Fortran2003 import ( Program, Comment, @@ -41,11 +43,15 @@ Execution_Part, Specification_Part, Block_Nonlabel_Do_Construct, -) -from fparser.two.utils import walk -from fparser.api import get_reader - + Main_Program, + Write_Stmt, + End_Program_Stmt, + If_Construct, + Allocate_Stmt, + Derived_Type_Def, + Function_Subprogram) from fparser.two.parser import ParserFactory +from fparser.two.utils import walk, get_child # this is required to setup the fortran2003 classes _ = ParserFactory().create(std="f2003") @@ -256,7 +262,6 @@ def test_prog_comments(): # . . # . # |--> Comment - from fparser.two.Fortran2003 import Main_Program, Write_Stmt, End_Program_Stmt assert type(obj.content[0]) == Comment assert str(obj.content[0]) == "! A troublesome comment" @@ -303,7 +308,6 @@ def test_function_comments(): ! That was a function end function my_mod """ - from fparser.two.Fortran2003 import Function_Subprogram reader = get_reader(source, isfree=True, ignore_comments=False) fn_unit = Function_Subprogram(reader) @@ -348,7 +352,7 @@ def test_subroutine_comments(): assert isinstance(comment, Comment) assert "! First comment" in str(comment) spec_part = fn_unit.children[2] - comment = spec_part.content[2] + comment = spec_part.children[2] assert isinstance(comment, Comment) assert comment.parent is spec_part assert "! Body comment" in str(comment) @@ -369,7 +373,6 @@ def test_derived_type(): ! Ending comment end type my_type """ - from fparser.two.Fortran2003 import Derived_Type_Def reader = get_reader(source, isfree=True, ignore_comments=False) dtype = Derived_Type_Def(reader) @@ -400,8 +403,6 @@ def test_action_stmts(): my_array2(size)) end if """ - from fparser.two.Fortran2003 import If_Construct, Allocate_Stmt - from fparser.two.utils import get_child reader = get_reader(source, isfree=True, ignore_comments=False) ifstmt = If_Construct(reader) diff --git a/src/fparser/two/tests/test_fortran2003.py b/src/fparser/two/tests/test_fortran2003.py index 67bc6b0e..f94079fe 100644 --- a/src/fparser/two/tests/test_fortran2003.py +++ b/src/fparser/two/tests/test_fortran2003.py @@ -103,7 +103,6 @@ def assert_raises(exc, fcls, string): # SECTION 2 # - def test_specification_part(): """Tests for parsing specification-part (R204).""" reader = get_reader( @@ -165,6 +164,24 @@ def test_specification_part(): ) assert expected in str(obj) + # Test a correct order of subclasses matches and then an incorrect + # order of subclasses fails to match (due to the strict_order flag + # being set). In this case Use_Stmt should precede + # Declaration_Construct + code = ( + "USE my_mod\n" + "INTEGER :: a") + reader = get_reader(code) + tcls = Specification_Part + obj = tcls(reader) + assert str(obj) == code + + reader = get_reader( + "integer :: a\n" + "use my_mod\n") + tcls = Specification_Part + obj = tcls(reader) + assert obj is None # # SECTION 3 diff --git a/src/fparser/two/utils.py b/src/fparser/two/utils.py index 50f1cc54..0e22f55a 100644 --- a/src/fparser/two/utils.py +++ b/src/fparser/two/utils.py @@ -698,11 +698,11 @@ def match( reader, f"Name '{end_name}' has no corresponding starting name", ) - elif strict_match_names and start_name and not end_name: + if strict_match_names and start_name and not end_name: raise FortranSyntaxError( reader, f"Expecting name '{start_name}' but none given" ) - elif ( + if ( start_name and end_name and (start_name.lower() != end_name.lower()) @@ -722,6 +722,9 @@ def match( # Return to start of classes list now that we've matched. i = 0 elif once_only: + # There was a match for this sub-class and + # once_only is set so move on to the next + # sub-class i += 1 if enable_if_construct_hook: if isinstance(obj, di.Else_If_Stmt):