Skip to content
Merged
Show file tree
Hide file tree
Changes from all 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
3 changes: 3 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,9 @@ Modifications by (in alphabetical order):
* P. Vitt, University of Siegen, Germany
* A. Voysey, UK Met Office

04/06/2026 PR #509 for #505. Fix truncated syntax error reporting when files
have line breaks before the module or program.

21/04/2026 PR #502. Widen Proc_Decl (R1214) for Fortran 2008 to accept an
initial-proc-target (R1217) on the right-hand side of ``=>``,
not only null-init. See J3/10-007r1 §12.4.3.6.
Expand Down
14 changes: 7 additions & 7 deletions src/fparser/common/readfortran.py
Original file line number Diff line number Diff line change
Expand Up @@ -141,6 +141,7 @@
import re
import sys
import traceback
from collections import deque
from typing import Optional, Tuple
from io import StringIO

Expand Down Expand Up @@ -594,7 +595,7 @@ def __init__(
self.process_directives = process_directives

self.filo_line = [] # used for un-consuming lines.
self.fifo_item = []
self.fifo_item = deque()
self.source_lines = [] # source lines cache

self.f2py_comment_lines = [] # line numbers of f2py directives
Expand Down Expand Up @@ -825,7 +826,7 @@ def put_item(self, item):
# of the corresponding reader.
self.reader.put_item(item)
else:
self.fifo_item.insert(0, item)
self.fifo_item.appendleft(item)

# Iterator methods:

Expand Down Expand Up @@ -930,11 +931,10 @@ def _next(self, ignore_comments=None):
"""
if ignore_comments is None:
ignore_comments = self._ignore_comments
fifo_item_pop = self.fifo_item.pop
while 1:
try:
# first empty the FIFO item buffer:
item = fifo_item_pop(0)
item = self.fifo_item.popleft()
except IndexError:
# construct a new item from source
item = self.get_source_item()
Expand Down Expand Up @@ -986,8 +986,8 @@ def _next(self, ignore_comments=None):
items.append(new_line)
items.reverse()
for newitem in items:
self.fifo_item.insert(0, newitem)
return fifo_item_pop(0)
self.fifo_item.appendleft(newitem)
return self.fifo_item.popleft()
return item

# Interface to returned items:
Expand Down Expand Up @@ -1663,7 +1663,7 @@ def get_source_item(self):
# blank. If it is a comment, it has been pushed onto the
# fifo_item list.
try:
return self.fifo_item.pop(0)
return self.fifo_item.popleft()
except IndexError:
# A blank line is represented as an empty comment
return Comment("", (startlineno, endlineno), self)
Expand Down
2 changes: 1 addition & 1 deletion src/fparser/one/parsefortran.py
Original file line number Diff line number Diff line change
Expand Up @@ -118,7 +118,7 @@ def put_item(self, item):
"""
Pushes the given item to the reader.
"""
self.reader.fifo_item.insert(0, item)
self.reader.fifo_item.appendleft(item)
return

def parse(self):
Expand Down
7 changes: 1 addition & 6 deletions src/fparser/two/Fortran2003.py
Original file line number Diff line number Diff line change
Expand Up @@ -418,13 +418,8 @@ def match(reader):
# Found a syntax error for this rule. Now look to match
# (via Main_Program0) with a program containing no program
# statement as this is optional in Fortran.
#
result = BlockBase.match(Main_Program0, [], None, reader)
if not result and comments:
# This program only contains comments.
return (content,)
else:
return result
return result
except StopIteration:
# Reader has no more lines.
pass
Expand Down
8 changes: 3 additions & 5 deletions src/fparser/two/tests/fortran2003/test_program_r201.py
Original file line number Diff line number Diff line change
Expand Up @@ -42,14 +42,12 @@
from fparser.api import get_reader
from fparser.two.Fortran2003 import Program

# Test no content or just white space. This is not officially a
# Fortran rule but fortran compilers tend to accept empty content so
# we follow their lead.


def test_empty_input(f2003_create):
"""Test that empty input or input only containing white space can be
parsed succesfully
parsed succesfully. This is not valid fortran but it is accepted by
compilers and some applications produce it when there is files with
preprocessor ifdefs and includes but all resolve to emtpy strings.

"""
for code in ["", " ", " \n \n\n"]:
Expand Down
60 changes: 50 additions & 10 deletions src/fparser/two/tests/test_comments_and_directives.py
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,7 @@

import pytest
from fparser.two.Fortran2003 import Program, Comment, Directive, Subroutine_Subprogram
from fparser.two.utils import walk
from fparser.two.utils import walk, FortranSyntaxError
from fparser.api import get_reader

from fparser.two.parser import ParserFactory
Expand Down Expand Up @@ -441,15 +441,11 @@ def test_directive_stmts():
old = reader.get_item()
assert old is not None

out = walk(program, Comment)
comments = 0
for comment in out:
if comment.items[0] != "":
comments = comments + 1
assert comments == 3
assert str(out[1]) == "!$dir inline"
assert str(out[3]) == "! A comment!"
assert str(out[4]) == "!!$ Another comment"
comments = walk(program, Comment)
assert len(comments) == 5
assert str(comments[1]) == "!$dir inline"
assert str(comments[3]) == "! A comment!"
assert str(comments[4]) == "!!$ Another comment"

# Check that passing something that isn't a comment into a Directive
# __new__ call doesn't create a Directive.
Expand Down Expand Up @@ -606,3 +602,47 @@ def test_inline_directive_is_comment():
program = Program(reader)
out = walk(program, Directive)
assert len(out) == 0


def test_syntax_error_with_comments():
"""Test that when we keep comments we still correctly give syntax errors
when the first line of the file is a blank line."""
source = """
Comment thread
sergisiso marked this conversation as resolved.


! This is module m


module m
integer :: x
contains
subroutine foo()
if (.true.)
x = 0
end if
end subroutine
end module"""
reader = get_reader(source, ignore_comments=False)
with pytest.raises(FortranSyntaxError) as err:
program = Program(reader)
assert "at line 11\n" in str(err.value)
assert ">>> if (.true.)\n" in str(err.value)


def test_base_to_fortran_empty_comment():
"""Test that if we have an empty comment we get the correct
to_fortran from the base class implementation (i.e. no tab)"""
source = """
!Comment
program test
end program
"""
reader = get_reader(source, ignore_comments=False)
program = Program(reader)
out = walk(program, Comment)
comment = out[1]
assert comment.tofortran(tab=" ") == " !Comment"
# Change the comment to be an empty comment.
comment.items = [""]
comment.item = ""
assert comment.tofortran(tab=" ") == ""
Loading