Skip to content
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

Open
wants to merge 16 commits into
base: master
Choose a base branch
from

Conversation

rupertford
Copy link
Collaborator

@rupertford rupertford commented Jun 20, 2022

Some of the rules using blockbase have a strict order. This PR enforces this where it was not already used. The parser runs a bit faster as a result.

However, there is an additional case where we only want blockbase to allow 0 or 1 instances of a particular rule to match. At the moment that is not enforced and we can end up with multiple Specification_Part entries within a Main_Program if, for example, an implicit none is incorrectly added before a use statement. This is not a good outcome as tools might rightly rely on there only being one of these and we end up with strange errors, rather than it being caught by fparser in the first place. Note that on master we only get one Specification_Part but the implicit none and use statements are allowed to be in the wrong order.

@rupertford
Copy link
Collaborator Author

I've fixed the 0 or 1 instance problem but now break the addition of comments so I think I'll need to add a comment loop before incrementing the class pointer.

Note, there may be cases where it might be worth having a list of classes that are 0 or 1, as a subset of the classes might require this. I should look at this.

@rupertford
Copy link
Collaborator Author

There appears to be a noticable improvement in performance for the example benchmark ...

master: Time taken for parse = 36.27s
Branch: Time taken for parse = 29.06s

@codecov
Copy link

codecov bot commented Jun 21, 2022

Codecov Report

Base: 91.48% // Head: 91.25% // Decreases project coverage by -0.22% ⚠️

Coverage data is based on head (9f0dcbc) compared to base (8df46ac).
Patch coverage: 100.00% of modified lines in pull request are covered.

❗ Current head 9f0dcbc differs from pull request most recent head 512cdfe. Consider uploading reports for the commit 512cdfe to get more accurate results

Additional details and impacted files
@@            Coverage Diff             @@
##           master     #358      +/-   ##
==========================================
- Coverage   91.48%   91.25%   -0.23%     
==========================================
  Files          37       37              
  Lines       13138    13134       -4     
==========================================
- Hits        12019    11986      -33     
- Misses       1119     1148      +29     
Impacted Files Coverage Δ
src/fparser/two/Fortran2003.py 93.61% <ø> (-0.60%) ⬇️
src/fparser/two/utils.py 95.47% <100.00%> (-0.12%) ⬇️
src/fparser/two/Fortran2008.py 100.00% <0.00%> (ø)

Help us with your feedback. Take ten seconds to tell us how you rate us. Have a feature suggestion? Share it here.

☔ View full report at Codecov.
📢 Do you have feedback about the report comment? Let us know in this issue.

@rupertford
Copy link
Collaborator Author

When testing the latest version with LFRic algorithm code I am finding parse errors in code like ...

...
use x
#if defined(UM_PHYSICS)
use y
...

which is not correct (although PSyclone does not promise to support ifdef'ed code).

I guess it is because we are incrementing the class match without dealing with comments and ifdefs. I need to look at how to fix this. This probably related to the change in location of comments that is observed in tests (which I convinced myself was OK).

@rupertford
Copy link
Collaborator Author

Interestingly comments between use statements seem to be OK but ifdef's are not. I thought we processed these in the same way. I guess there must be a difference somewhere.

@rupertford
Copy link
Collaborator Author

OK, I now realise that comment, include and preprocess rules are added to the general blockbase rules, so if we do them in order they will not be picked up at the right point. I think we need to not add them to the rules and then consume all of them as we go along (without incrementing the class reference), but it needs thinking about.

@rupertford
Copy link
Collaborator Author

Comment restructuring seems to be working. All tests pass, LFRic code is being parsed OK with psyclone and psyclone tests pass after finding and fixing another bug in one of the examples.

Annoyingly, running psyclone on all the lfric algorithm codes does not seem to give any performance improvement.

@arporter
Copy link
Member

Hi @rupertford, do you think you could add in a test that I wrote for #366 into this PR:

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[1].parent, Execution_Part)
    assert isinstance(comments[2].parent, TODO)

@rupertford
Copy link
Collaborator Author

Added test, which seems to work as expected, giving the following (which preserves comment locations) ...

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

@rupertford
Copy link
Collaborator Author

I'm not sure why I didn't make this ready for review before so here we go ...

Copy link
Member

@arporter arporter left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

All looks good apart from the fact that there seem to be no tests for the new once_only flag - I'd expect to see some tests that we reject code that has more than one implicit none in it for example.
Coverage is fine and Black means I don't have to worry much about style (although see inline for a couple of pylint things).
I don't think that the Dev Guide needs to be updated.

if not strict_order:
# Return to start of classes list now that we've matched.
i = 0
elif once_only:
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggest a comment: we had a match for this sub-class and once_only is set so move on to the next sub-class.

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Added comment.

@@ -714,9 +714,15 @@ def match(
# We've found the enclosing end statement so break out
found_end = True
break

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Not strictly yours but since we're here pylint complains that the elif at L701 could be an if. Probably the same goes for L705.

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Modified.

@@ -10157,6 +10159,7 @@ def match(reader):
[Specification_Part, Module_Subprogram_Part],
End_Module_Stmt,
reader,
Copy link
Member

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.)

Copy link
Collaborator Author

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.

# |--> Execution_Part
# | |--> Write_Stmt
# | \--> Comment
# | |--> Comment
# . .
# .
# |--> Comment
from fparser.two.Fortran2003 import Main_Program, Write_Stmt, End_Program_Stmt
Copy link
Member

Choose a reason for hiding this comment

The 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).

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Moved all imports to the top.

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]
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

s/content/children/ since you use that on the previous line?

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Good point.

@arporter arporter added reviewed with actions PR has been reviewed and is back with developer and removed under review labels Aug 31, 2022
@rupertford
Copy link
Collaborator Author

I started adding a test for strict_order and found that the implementation is not working correctly. For example

integer :: a
use my_mod

should not match with Specification_Part as use my_mod should precede integer :: a.

The rule order is ...

[Use_Stmt, Import_Stmt, Implicit_Part, Declaration_Construct]

However, integer :: a is returned as a match with use my_mod being ignored.

@rupertford rupertford added in progress and removed reviewed with actions PR has been reviewed and is back with developer labels Sep 15, 2022
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging this pull request may close these issues.

2 participants