Skip to content
Merged
Show file tree
Hide file tree
Changes from 5 commits
Commits
Show all changes
26 commits
Select commit Hold shift + click to select a range
8c39ec0
Added Directive node separated from comments
LonelyCat124 Jun 26, 2025
80a4d18
Merge branch 'master' into 468_directives
LonelyCat124 Jun 26, 2025
6fb2a20
Formatting
LonelyCat124 Jun 26, 2025
4b6bf3d
Merge branch '468_directives' of github.com:stfc/fparser into 468_dir…
LonelyCat124 Jun 26, 2025
a953ed3
Fix coverage and update docs
LonelyCat124 Jun 26, 2025
22d27a2
Changes for review
LonelyCat124 Jun 30, 2025
1e8c84b
changes for review
LonelyCat124 Jun 30, 2025
826f493
Formatting
LonelyCat124 Jun 30, 2025
af58dde
Applied black to Fortran2003.py
LonelyCat124 Jun 30, 2025
b89e33b
Merge branch 'master' into 468_directives
arporter Jul 29, 2025
06280aa
Changes for review
LonelyCat124 Aug 6, 2025
0bd5e99
Merge branch '468_directives' of github.com:stfc/fparser into 468_dir…
LonelyCat124 Aug 6, 2025
cb37dec
Merge branch 'master' into 468_directives
LonelyCat124 Aug 6, 2025
c1ea3fb
formatting
LonelyCat124 Aug 6, 2025
a77f658
Merge branch '468_directives' of github.com:stfc/fparser into 468_dir…
LonelyCat124 Aug 6, 2025
ab39d9b
Changes for review
LonelyCat124 Aug 29, 2025
cd692dc
Formatting fixes
LonelyCat124 Aug 29, 2025
5b13ff4
Applied black to F2003
LonelyCat124 Aug 29, 2025
927693e
Merge branch 'master' into 468_directives
LonelyCat124 Aug 29, 2025
445ce3e
Changes for review
LonelyCat124 Sep 4, 2025
5cca730
Fix issues with always processing directives
LonelyCat124 Sep 4, 2025
39adf57
Example of usage
LonelyCat124 Sep 4, 2025
9822fd7
Changes for review
LonelyCat124 Sep 5, 2025
12195ad
Missing comment
LonelyCat124 Sep 5, 2025
0be460f
changes for review
LonelyCat124 Sep 8, 2025
ef9ae0f
#468 fix Black formatting
arporter Sep 8, 2025
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
80 changes: 79 additions & 1 deletion src/fparser/two/Fortran2003.py
Original file line number Diff line number Diff line change
Expand Up @@ -116,9 +116,86 @@
# R102: <xyz-name> = <name>
# R103: <scalar-xyz> = <xyz>


#
# SECTION 2
#
class Directive(Base):
"""
Represents a Directive.
"""

subclass_names = []

@show_result
def __new__(cls, string: str | FortranReaderBase, parent_cls=None):
"""
Create a new Directive instance.

:param type cls: the class of object to create.
:param string: (source of) Fortran string to parse.
:param parent_cls: the parent class of this object.
:type parent_cls: :py:type:`type`

"""
from fparser.common import readfortran

if isinstance(string, readfortran.Comment):
# Directives must start with a $
if not string.comment[1:].lstrip().startswith("$"):
return
# We were after a directive and we got a directive. Construct
# one manually to avoid recursively calling this __new__
# method again...
obj = object.__new__(cls)
obj.init(string)
return obj
elif isinstance(string, FortranReaderBase):
reader = string
item = reader.get_item()
if item is None:
return
if isinstance(item, readfortran.Comment):
# This effectively recursively calls this routine
res = Directive(item)
if not res:
# We didn't get a directive so put the item back in
# the FIFO
reader.put_item(item)
return res
else:
# We didn't get a directive so put the item back in the FIFO
reader.put_item(item)
return
else:
# We didn't get a directive
return

def init(self, comment) -> None:
"""
Initialise this Directive

:param comment: The comment object produced by the reader
:type comment: :py:class:`readfortran.Comment`
"""
self.items = [comment.comment]
self.item = comment

def tostr(self) -> str:
"""
:returns: this directive as a string.
"""
return str(self.items[0])

def restore_reader(self, reader) -> None:
"""
Undo the read of this directive by putting its content back
into the reader (which has a FIFO buffer)

:param reader: the reader instance to return the directive to
:type reader: :py:class:`fparser.readfortran.FortranReaderBase`
"""
reader.put_item(self.item)


class Comment(Base):
Expand Down Expand Up @@ -207,7 +284,8 @@ def match_comment_or_include(reader):
:py:class:`fparser.two.Fortran2003.Include_Stmt`

"""
obj = Comment(reader)
obj = Directive(reader)
obj = Comment(reader) if not obj else obj
obj = Include_Stmt(reader) if not obj else obj
return obj

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,7 @@
"""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, Directive, Subroutine_Subprogram
from fparser.two.utils import walk
from fparser.api import get_reader

Expand Down Expand Up @@ -409,3 +409,47 @@ def test_action_stmts():
assert "a big array" in str(ifstmt)
cmt = get_child(ifstmt, Comment)
assert cmt.parent is ifstmt


def test_directive_stmts():
"""Test that directives are created instead of comments when
appropriate."""
source = """
Program my_prog
integer :: x !$dir inline

!$omp target
!$omp loop
do x= 1 , 100
! A comment!
end do
End Program"""
reader = get_reader(source, isfree=True, ignore_comments=False)
program = Program(reader)
out = walk(program, Directive)
assert len(out) == 3
assert out[0].items[0] == "!$dir inline"
assert out[1].items[0] == "!$omp target"
assert out[2].items[0] == "!$omp loop"

assert out[2].tostr() == "!$omp loop"

# Check the restore_reader works correctly for directive.
old = reader.get_item()
assert old == None
out[2].restore_reader(reader)
old = reader.get_item()
assert old is not None

out = walk(program, Comment)
comments = 0
for comment in out:
if comment.items[0] != "":
assert comment.items[0] == "! A comment!"
comments = comments + 1
assert comments == 1

# Check that passing something that isn't a comment into a Directive
# __new__ call doesn't create a Directive.
out = Directive(program)
assert out is None
6 changes: 4 additions & 2 deletions src/fparser/two/utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -331,6 +331,7 @@ def import_now():
End_Select_Type_Stmt,
Case_Stmt,
End_Select_Stmt,
Directive,
Comment,
Include_Stmt,
add_comments_includes_directives,
Expand All @@ -347,6 +348,7 @@ def import_now():
DynamicImport.End_Select_Type_Stmt = End_Select_Type_Stmt
DynamicImport.Case_Stmt = Case_Stmt
DynamicImport.End_Select_Stmt = End_Select_Stmt
DynamicImport.Directive = Directive
DynamicImport.Comment = Comment
DynamicImport.Include_Stmt = Include_Stmt
DynamicImport.C99Preprocessor = C99Preprocessor
Expand Down Expand Up @@ -710,8 +712,8 @@ 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]
# Directives, Comments and Include statements are always valid sub-classes
classes = subclasses + [di.Directive, di.Comment, di.Include_Stmt]
# Preprocessor directives are always valid sub-classes
cpp_classes = [
getattr(di.C99Preprocessor, cls_name)
Expand Down