Skip to content
Merged
Show file tree
Hide file tree
Changes from 3 commits
Commits
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
50 changes: 42 additions & 8 deletions src/fparser/common/readfortran.py
Original file line number Diff line number Diff line change
Expand Up @@ -1186,20 +1186,28 @@ def handle_cf2py_start(self, line):
return newline
return line

def handle_inline_comment(self, line, lineno, quotechar=None):
def handle_inline_comment(
self,
line: str,
lineno: int,
quotechar=None,
buffer_comments_to_fifo: bool = True,
):
"""
Any in-line comment is extracted from the line. If
keep_inline_comments==True then the extracted comments are put back
into the fifo sequence where they will subsequently be processed as
buffer_comments_to_fifo==True (the default) then the extracted comments are
put back into the fifo sequence where they will subsequently be processed as
a comment line.

:param str line: line of code from which to remove in-line comment
:param int lineno: line-no. in orig. file
:param line: line of code from which to remove in-line comment
:param lineno: line-no. in orig. file
:param quotechar: String to use as character-string delimiter
:type quotechar: {None, str}
:param buffer_comments_to_fifo: whether or not to put any comments back into
the fifo buffer for future processing.

:return: line_with_no_comments, quotechar, had_comment
:rtype: 3-tuple of str, str, bool
:rtype: Tuple[str, str, bool]

"""
had_comment = False
Expand All @@ -1212,8 +1220,13 @@ def handle_inline_comment(self, line, lineno, quotechar=None):
# There's no comment on this line
return line, quotechar, had_comment

if buffer_comments_to_fifo:
put_item = self.fifo_item.append
else:
# We're not putting any Comments into the FIFO buffer.
put_item = lambda x: None

idx = line.find("!")
put_item = self.fifo_item.append
if quotechar is None and idx != -1:
# first try a quick method:
newline = line[:idx]
Expand Down Expand Up @@ -1245,7 +1258,9 @@ def handle_inline_comment(self, line, lineno, quotechar=None):
# go to next iteration:
newline = "".join(noncomment_items) + commentline[5:]
self.f2py_comment_lines.append(lineno)
return self.handle_inline_comment(newline, lineno, quotechar)
return self.handle_inline_comment(
newline, lineno, quotechar, buffer_comments_to_fifo
)
put_item(self.comment_item(commentline, lineno, lineno))
had_comment = True
return "".join(noncomment_items), newquotechar, had_comment
Expand Down Expand Up @@ -1627,6 +1642,25 @@ def get_source_item(self):
# A blank line is represented as an empty comment
return Comment("", (startlineno, endlineno), self)

def is_comment_line(self, line: str) -> bool:
"""
Utility that examines the supplied line of code and determines whether this
reader instance would identify it as a comment.

:param line: the line of Fortran to examine.

:returns: whether or not the supplied line is considered to be a comment.

"""
if self._format.is_fixed:
return _is_fix_comment(
line, self._format.is_strict, self._format.f2py_enabled
)
new_line, _, had_comment = self.handle_inline_comment(
line, 0, buffer_comments_to_fifo=False
)
return not new_line.strip() and had_comment


class FortranFileReader(FortranReaderBase):
"""
Expand Down
19 changes: 19 additions & 0 deletions src/fparser/common/tests/test_readfortran.py
Original file line number Diff line number Diff line change
Expand Up @@ -263,6 +263,25 @@ def test_base_handle_multilines(log):
assert result == expected


def test_fortranreaderbase_is_comment_line():
"""
Tests for the is_comment_line() utility method.
"""
reader = FortranStringReader(" ")
# Make the reader free-format.
reader.set_format(FortranFormat(True, True))
assert not reader.is_comment_line(" ")
assert reader.is_comment_line("!")
assert reader.is_comment_line("! ")
assert not reader.is_comment_line("call a_func()")
# Make the reader fixed-format.
reader.set_format(FortranFormat(False, True))
assert not reader.is_comment_line(" ")
assert reader.is_comment_line("! a comment")
assert reader.is_comment_line("c a comment")
assert reader.is_comment_line("c ")


def test_base_fixed_nonlabel(log):
"""
Tests that FortranReaderBase.get_source_item() logs the correct messages
Expand Down
4 changes: 3 additions & 1 deletion src/fparser/two/Fortran2003.py
Original file line number Diff line number Diff line change
Expand Up @@ -315,7 +315,9 @@ def match(reader):
try:
while True:
obj = Program_Unit(reader)
content.append(obj)
if obj:
# obj could be None if there are only Comments
content.append(obj)
add_comments_includes_directives(content, reader)
# cause a StopIteration exception if there are no more lines
next_line = reader.next()
Expand Down
10 changes: 10 additions & 0 deletions src/fparser/two/tests/fortran2003/test_program_r201.py
Original file line number Diff line number Diff line change
Expand Up @@ -262,6 +262,16 @@ def test_missing_prog(f2003_create):
)
ast = Program(reader)
assert "END" in str(ast)
reader = get_reader(
"""\
! This is a program without a program declaration
end
""",
isfree=True,
ignore_comments=False,
)
ast = Program(reader)
assert "END" in str(ast)


@pytest.mark.xfail(reason="Only the main program is output")
Expand Down
23 changes: 23 additions & 0 deletions src/fparser/two/tests/test_utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -141,3 +141,26 @@ def test_endstmtbase_match():
require_stmt_type=True,
)
assert result == ("SUBROUTINE", Fortran2003.Name("sub"))


@pytest.mark.parametrize("ignore_comments", [True, False])
def test_base_all_comments(ignore_comments):
"""
Check that supplying a reader with text that consists purely of comments does
not result in a syntax error from the Base class.

"""
# Check with free-format
reader = get_reader(
"! just a comment\n! and another", isfree=True, ignore_comments=ignore_comments
)
result = utils.Base(reader)
assert result is None
# Check for fixed format
reader = get_reader(
"c just a comment\n! and another",
isfree=False,
ignore_comments=ignore_comments,
)
result = utils.Base(reader)
assert result is None
23 changes: 9 additions & 14 deletions src/fparser/two/utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -485,20 +485,13 @@ def __new__(cls, string, parent_cls=None, _deepcopy=False):
raise AssertionError(repr(result))
# If we get to here then we've failed to match the current line
if isinstance(string, FortranReaderBase):
content = False
for index in range(string.linecount):
# Check all lines up to this one for content. We
# should be able to only check the current line but
# but as the line number returned is not always
# correct (due to coding errors) we cannot assume the
# line pointed to is the line where the error actually
# happened.
if string.source_lines[index].strip():
content = True
break
if not content:
# There are no lines in the input or all lines up to
# this one are empty or contain only white space. This
freader: FortranReaderBase = string
if not freader.source_lines or all(
(line.strip() == "" or freader.is_comment_line(line))
for line in freader.source_lines
):
# There are no lines in the input or all lines up to this one
# are empty, comments or contain only white space. This
# is typically accepted by fortran compilers so we
# follow their lead and do not raise an exception.
return
Expand Down Expand Up @@ -908,6 +901,8 @@ def tofortran(self, tab="", isfix=None):
:rtype: str
"""
mylist = []
if not self.content:
return ""
start = self.content[0]
end = self.content[-1]
extra_tab = ""
Expand Down