-
Notifications
You must be signed in to change notification settings - Fork 30
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Adding strict order statements (closes #353) #358
base: master
Are you sure you want to change the base?
Changes from 13 commits
f532b42
8b90bc0
2c831c3
c20d9e1
3eb33d4
0f7b914
77e0b10
800af82
8b371f4
b7b4f27
e4baa7f
e4efa6a
9f0dcbc
365a80d
512cdfe
2fea1f7
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -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 | ||
|
@@ -34,7 +34,14 @@ | |
""" 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 | ||
|
||
|
@@ -242,24 +249,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 | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Please move these imports to the top (and check for any subsequent repeats). There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Moved all imports to the top. |
||
|
||
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]) == "! A full comment line" | ||
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 | ||
# Check that we have the in-line comment as a second statement | ||
|
@@ -308,17 +312,15 @@ def test_function_comments(): | |
# <class 'fparser.two.Fortran2003.Name'> | ||
# <type 'NoneType'> | ||
# <type 'NoneType'> | ||
# <class 'fparser.two.Fortran2003.Specification_Part'> | ||
# <class 'fparser.two.Fortran2003.Implicit_Part'> | ||
# <class 'fparser.two.Fortran2003.Comment'> | ||
# <class 'fparser.two.Fortran2003.Comment'> | ||
# <type 'str'>, "'! 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].content[0] | ||
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) | ||
|
@@ -342,16 +344,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) | ||
walk(fn_unit.children, Comment, debug=True) | ||
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) | ||
comment = spec_part.content[2].content[0] | ||
spec_part = fn_unit.children[2] | ||
comment = spec_part.content[2] | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. s/content/children/ since you use that on the previous line? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Good point. |
||
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] | ||
exec_part = fn_unit.content[3] | ||
comment = exec_part.content[1] | ||
assert isinstance(comment, Comment) | ||
assert comment.parent is exec_part | ||
|
@@ -409,3 +410,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) |
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -551,6 +551,7 @@ def match( | |
enable_where_construct_hook=False, | ||
strict_order=False, | ||
strict_match_names=False, | ||
once_only=False, | ||
): | ||
""" | ||
Checks whether the content in reader matches the given | ||
|
@@ -574,6 +575,9 @@ def match( | |
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 | ||
|
@@ -619,18 +623,14 @@ def match( | |
if match_names: | ||
start_name = obj.get_start_name() | ||
|
||
# Comments and Include statements are always valid sub-classes | ||
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 | ||
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?) | ||
|
@@ -714,9 +714,15 @@ def match( | |
# We've found the enclosing end statement so break out | ||
found_end = True | ||
break | ||
|
||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Not strictly yours but since we're here pylint complains that the There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Modified. |
||
# 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 | ||
elif once_only: | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Suggest a comment: we had a match for this sub-class and There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Added comment. |
||
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 | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Each sub-class can only match once for a valid module? (I initially got worried that this wouldn't work for multiple modules in a file but it's fine.)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yes, a Module can only contain one Module_Stmt, one Specification_Part, one Module_Subprogram_Part and one End_Module_Stmt. Multiple modules are supported in 'higher level' rules, such as Program_Unit.