Skip to content

Commit

Permalink
Merge branch 'master' into 2831_PSyclone_Argument_Consistency
Browse files Browse the repository at this point in the history
  • Loading branch information
mo-alistairp authored Jan 3, 2025
2 parents 526aeab + d6dc2bd commit fef8d1f
Show file tree
Hide file tree
Showing 24 changed files with 1,223 additions and 103 deletions.
4 changes: 4 additions & 0 deletions changelog
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,10 @@
frontend to chase down some (or all) module imports when constructing
the PSyIR for existing code.

4) PR #2821 for #1247. Adds support for parsing comments from the fparser2
AST tree. There are two new FortranReader arguments to select the behaviour
for comments and directives (by default they are not parsed).

release 3.0.0 6th of December 2024

1) PR #2477 for #2463. Add support for Fortran Namelist statements.
Expand Down
2 changes: 1 addition & 1 deletion doc/developer_guide/psyir.rst
Original file line number Diff line number Diff line change
Expand Up @@ -898,7 +898,7 @@ class, for example:

.. code-block:: python
from psyclone.psyir.nodes.commentable_mixin import CommentableMixin
from psyclone.psyir.commentable_mixin import CommentableMixin
class MyNode(Node, CommentableMixin):
''' Example node '''
Expand Down
3 changes: 2 additions & 1 deletion src/psyclone/generator.py
Original file line number Diff line number Diff line change
Expand Up @@ -728,7 +728,8 @@ def code_transformation_mode(input_file, recipe_file, output_file,
sys.exit(1)

# Parse file
psyir = FortranReader(resolve_mods).psyir_from_file(input_file)
psyir = FortranReader(resolve_modules=resolve_mods)\
.psyir_from_file(input_file)

# Modify file
if trans_recipe:
Expand Down
37 changes: 30 additions & 7 deletions src/psyclone/psyir/backend/fortran.py
Original file line number Diff line number Diff line change
Expand Up @@ -536,6 +536,11 @@ def gen_vardecl(self, symbol, include_visibility=False):
f"and should not be provided to 'gen_vardecl'."
)

result = ""
if len(symbol.preceding_comment) > 0:
for line in symbol.preceding_comment.splitlines():
result += f"{self._nindent}{self._COMMENT_PREFIX}{line}\n"

# Whether we're dealing with an array declaration and, if so, the
# shape of that array.
if isinstance(symbol.datatype, ArrayType):
Expand All @@ -554,10 +559,14 @@ def gen_vardecl(self, symbol, include_visibility=False):
# blocks appearing in SAVE statements.
decln = add_accessibility_to_unsupported_declaration(
symbol)
return f"{self._nindent}{decln}\n"

decln = symbol.datatype.declaration
return f"{self._nindent}{decln}\n"
else:
decln = symbol.datatype.declaration
result += f"{self._nindent}{decln}"
if symbol.inline_comment != "":
result += (f" {self._COMMENT_PREFIX}"
f"{symbol.inline_comment}")
result += "\n"
return result
# The Fortran backend only handles UnsupportedFortranType
# declarations.
raise VisitorError(
Expand All @@ -566,7 +575,7 @@ def gen_vardecl(self, symbol, include_visibility=False):
f"supported by the Fortran backend.")

datatype = gen_datatype(symbol.datatype, symbol.name)
result = f"{self._nindent}{datatype}"
result += f"{self._nindent}{datatype}"

if ArrayType.Extent.DEFERRED in array_shape:
# A 'deferred' array extent means this is an allocatable array
Expand Down Expand Up @@ -616,6 +625,9 @@ def gen_vardecl(self, symbol, include_visibility=False):
f"However it has an interface of '{symbol.interface}'.")
result += " = " + self._visit(symbol.initial_value)

if symbol.inline_comment != "":
result += f" {self._COMMENT_PREFIX}{symbol.inline_comment}"

return result + "\n"

def gen_interfacedecl(self, symbol):
Expand Down Expand Up @@ -696,7 +708,12 @@ def gen_typedecl(self, symbol, include_visibility=True):
f"Fortran backend cannot generate code for symbol "
f"'{symbol.name}' of type '{type(symbol.datatype).__name__}'")

result = f"{self._nindent}type"
result = ""
if symbol.preceding_comment != "":
for line in symbol.preceding_comment.splitlines():
result += f"{self._nindent}{self._COMMENT_PREFIX}{line}\n"

result += f"{self._nindent}type"

if include_visibility:
if symbol.visibility == Symbol.Visibility.PRIVATE:
Expand Down Expand Up @@ -726,7 +743,13 @@ def gen_typedecl(self, symbol, include_visibility=True):
include_visibility=include_visibility)
self._depth -= 1

result += f"{self._nindent}end type {symbol.name}\n"
result += f"{self._nindent}end type {symbol.name}"

if symbol.inline_comment != "":
result += f" {self._COMMENT_PREFIX}{symbol.inline_comment}"

result += "\n"

return result

def gen_default_access_stmt(self, symbol_table):
Expand Down
2 changes: 1 addition & 1 deletion src/psyclone/psyir/backend/visitor.py
Original file line number Diff line number Diff line change
Expand Up @@ -45,7 +45,7 @@

from psyclone.errors import PSycloneError
from psyclone.psyir.nodes import Node, Schedule, Container
from psyclone.psyir.nodes.commentable_mixin import CommentableMixin
from psyclone.psyir.commentable_mixin import CommentableMixin


class VisitorError(PSycloneError):
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -63,6 +63,8 @@ def preceding_comment(self):
def preceding_comment(self, comment):
'''
:param str comment: comment preceding this statement.
:raises TypeError: if the comment is not a string.
'''
if not isinstance(comment, str):
raise TypeError(f"The preceding_comment must be a string but"
Expand All @@ -73,6 +75,8 @@ def append_preceding_comment(self, comment):
'''
:param str comment: comment to append after an newline in this
statement-preceding comment.
:raises TypeError: if the comment is not a string.
'''
if not isinstance(comment, str):
raise TypeError(f"The preceding_comment must be a string but"
Expand All @@ -94,10 +98,16 @@ def inline_comment(self):
def inline_comment(self, comment):
'''
:param str comment: inline comment associated with this statement.
:raises TypeError: if the comment is not a string.
:raises ValueError: if the comment contains a newline character.
'''
if not isinstance(comment, str):
raise TypeError(f"The inline_comment must be a string but"
f" found '{type(comment).__name__}'.")
if '\n' in comment:
raise ValueError(f"The inline_comment must be a single line but "
f"found a newline character in '{comment}'.")
self._inline_comment = comment


Expand Down
46 changes: 33 additions & 13 deletions src/psyclone/psyir/frontend/fortran.py
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,19 @@ class FortranReader():
''' PSyIR Fortran frontend. This frontend translates Fortran from a string
or a file into PSyIR using the fparser2 utilities.
:param free_form: If parsing free-form code or not (default True).
:param ignore_comments: If comments should be ignored or not
(default True).
:param ignore_directives: If directives should be ignored or not
(default True). Only has an effect
if ignore_comments is False.
:param last_comments_as_codeblocks: If the last comments in the
a given block (e.g. subroutine,
do, if-then body, etc.) should
be kept as code blocks or lost
(default False).
Only has an effect if ignore_comments
is False.
:param resolve_modules: Whether to resolve modules while parsing a file,
for more precise control it also accepts a list of module names.
Defaults to False.
Expand All @@ -63,10 +76,17 @@ class FortranReader():
# Save parser object across instances to reduce the initialisation time
_parser = None

def __init__(self, resolve_modules: Union[bool, List[str]] = False):
def __init__(self, free_form: bool = True, ignore_comments: bool = True,
ignore_directives: bool = True,
last_comments_as_codeblocks: bool = False,
resolve_modules: Union[bool, List[str]] = False):
if not self._parser:
self._parser = ParserFactory().create(std="f2008")
self._processor = Fparser2Reader(resolve_modules)
self._free_form = free_form
self._ignore_comments = ignore_comments
self._processor = Fparser2Reader(ignore_directives,
last_comments_as_codeblocks,
resolve_modules)
SYMBOL_TABLES.clear()

@staticmethod
Expand All @@ -89,22 +109,23 @@ def validate_name(name: str):
raise ValueError(
f"Invalid Fortran name '{name}' found.")

def psyir_from_source(self, source_code: str, free_form: bool = True):
def psyir_from_source(self, source_code: str):
''' Generate the PSyIR tree representing the given Fortran source code.
:param source_code: text representation of the code to be parsed.
:param free_form: If parsing free-form code or not (default True).
:param str source_code: text representation of the code to be parsed.
:returns: PSyIR representing the provided Fortran source code.
:rtype: :py:class:`psyclone.psyir.nodes.Node`
'''
SYMBOL_TABLES.clear()
string_reader = FortranStringReader(
source_code, include_dirs=Config.get().include_paths)
source_code, include_dirs=Config.get().include_paths,
ignore_comments=self._ignore_comments)
# Set reader to free format.
string_reader.set_format(FortranFormat(free_form, False))
string_reader.set_format(FortranFormat(self._free_form, False))
parse_tree = self._parser(string_reader)

psyir = self._processor.generate_psyir(parse_tree)
return psyir

Expand Down Expand Up @@ -198,15 +219,12 @@ def psyir_from_statement(self, source_code: str,
self._processor.process_nodes(fake_parent, exec_part.children)
return fake_parent[0].detach()

def psyir_from_file(self, file_path, free_form=True):
def psyir_from_file(self, file_path):
''' Generate the PSyIR tree representing the given Fortran file.
:param file_path: path of the file to be read and parsed.
:type file_path: str or any Python Path format.
:param free_form: If parsing free-form code or not (default True).
:type free_form: bool
:returns: PSyIR representing the provided Fortran file.
:rtype: :py:class:`psyclone.psyir.nodes.Node`
Expand All @@ -221,10 +239,12 @@ def psyir_from_file(self, file_path, free_form=True):
# Using the FortranFileReader instead of manually open the file allows
# fparser to keep the filename information in the tree
reader = FortranFileReader(file_path,
include_dirs=Config.get().include_paths)
reader.set_format(FortranFormat(free_form, False))
include_dirs=Config.get().include_paths,
ignore_comments=self._ignore_comments)
reader.set_format(FortranFormat(self._free_form, False))
parse_tree = self._parser(reader)
_, filename = os.path.split(file_path)

psyir = self._processor.generate_psyir(parse_tree, filename)
return psyir

Expand Down
Loading

0 comments on commit fef8d1f

Please sign in to comment.