Source code for psyclone.psyir.frontend.fortran

# BSD 3-Clause License
#
# Copyright (c) 2021-2026, Science and Technology Facilities Council.
# All rights reserved.
#
# Redistribution and use in source and binary forms, with or without
# modification, are permitted provided that the following conditions are met:
#
# * Redistributions of source code must retain the above copyright notice, this
#   list of conditions and the following disclaimer.
#
# * Redistributions in binary form must reproduce the above copyright notice,
#   this list of conditions and the following disclaimer in the documentation
#   and/or other materials provided with the distribution.
#
# * Neither the name of the copyright holder nor the names of its
#   contributors may be used to endorse or promote products derived from
#   this software without specific prior written permission.
#
# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
# FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
# COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
# INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
# BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
# LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
# CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
# ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
# POSSIBILITY OF SUCH DAMAGE.
# -----------------------------------------------------------------------------
# Author: S. Siso, STFC Daresbury Lab
# Modifications: A. R. Porter, N. Nobre and R. W. Ford, STFC Daresbury Lab
#                J. Henrichs, Bureau of Meteorology
# -----------------------------------------------------------------------------

''' This module provides the PSyIR Fortran front-end.'''

import re

from typing import Optional, Union, List
from psyclone.configuration import Config
from psyclone.psyir.nodes import Assignment, Node, Routine, Schedule
from psyclone.psyir.symbols import SymbolTable


[docs] class FortranReader(): ''' PSyIR Fortran frontend. This frontend translates Fortran from a string or a file into PSyIR. :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 conditional_openmp_statements: whether to keep statements with the OpenMP conditional compilation prefix. :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 Codeblocks 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. :raises ValueError: If ignore_directives is set to False but ignore_comments is set to True. ''' def __init__(self, free_form: bool = True, ignore_comments: bool = True, ignore_directives: bool = True, conditional_openmp_statements: bool = False, last_comments_as_codeblocks: bool = False, resolve_modules: Union[bool, List[str]] = False): if ignore_comments and not ignore_directives: raise ValueError( "Setting ignore_directives to False in the FortranReader " "will only have an effect if ignore_comments is also set " "to False." ) # The frontend reader imports are intentionally inside this condition # to lazily import them only when they are needed # pylint: disable=import-outside-toplevel if Config.get().frontend == 'treesitter': from psyclone.psyir.frontend.fortran_treesitter_reader import ( FortranTreeSitterReader) factory = FortranTreeSitterReader else: from psyclone.psyir.frontend.fparser2 import Fparser2Reader factory = Fparser2Reader # Instantiate processor self._processor = factory( ignore_directives, last_comments_as_codeblocks, resolve_modules, ignore_comments, free_form, conditional_openmp_statements )
[docs] @staticmethod def validate_name(name: str) -> None: ''' Utility method that checks that the supplied name is a valid Fortran name. :param name: the name to check. :raises TypeError: if the name is not a string. :raises ValueError: if this is not a valid name. ''' if not isinstance(name, str): raise TypeError( f"A name should be a string, but found " f"'{type(name).__name__}'.") if not re.match(r"^[A-Z]\w*$", name, flags=re.I): raise ValueError( f"Invalid Fortran name '{name}' found.")
[docs] def psyir_from_source(self, source_code: str) -> Node: ''' Generate the PSyIR tree for the given Fortran source code. :param source_code: text representation of the code to be parsed. :returns: the PSyIR of the provided Fortran source code. ''' tree = self._processor.generate_parse_tree_from_source(source_code) psyir = self._processor.generate_psyir(tree) return psyir
[docs] def psyir_from_expression( self, source_code: str, symbol_table: Optional[SymbolTable] = None ) -> Node: '''Generate the PSyIR tree for the supplied Fortran statement. The symbol table is expected to provide all symbols found in the expression. :param source_code: text of the expression to be parsed. :param symbol_table: the SymbolTable in which to search for any symbols that are encountered. :returns: PSyIR representing the provided Fortran expression. :raises TypeError: if no valid SymbolTable is supplied. ''' if symbol_table is None: symbol_table = SymbolTable() elif not isinstance(symbol_table, SymbolTable): raise TypeError(f"Must be supplied with a valid SymbolTable but " f"got '{type(symbol_table).__name__}'") tree = self._processor.generate_parse_tree_from_source( source_code, partial_code="expression") # Create a fake sub-tree connected to the supplied symbol table so # that we can process the expression and lookup any symbols that it # references. We provide the SymbolTable directly to the private # attribute to avoid the symbol table's node link to connect to the # new Schedule and therefore stealing it from its original scope. # pylint: disable=protected-access fake_parent = Schedule() # pylint: disable=protected-access fake_parent._symbol_table = symbol_table fake_parent.addchild(Assignment()) self._processor.process_nodes(fake_parent[0], [tree]) return fake_parent[0].children[0].detach()
[docs] def psyir_from_statement( self, source_code: str, symbol_table: Optional[SymbolTable] = None ) -> Node: '''Generate the PSyIR tree for the supplied Fortran statement. The symbol table is expected to provide all symbols found in the statement. :param source_code: text of the statement to be parsed. :param symbol_table: the SymbolTable in which to search for any symbols that are encountered. :returns: PSyIR representing the provided Fortran statement. :raises TypeError: if no valid SymbolTable is supplied. ''' if symbol_table is None: symbol_table = SymbolTable() elif not isinstance(symbol_table, SymbolTable): raise TypeError(f"Must be supplied with a valid SymbolTable but " f"got '{type(symbol_table).__name__}'") tree = self._processor.generate_parse_tree_from_source( source_code, partial_code="statement") # Create a fake sub-tree connected to the supplied symbol table so # that we can process the statement and lookup any symbols that it # references. routine_name = "dummy" fake_parent = Routine.create( routine_name, SymbolTable(), []) # pylint: disable=protected-access fake_parent._symbol_table = symbol_table # Process the statement, giving the Routine we've just # created as the parent. self._processor.process_nodes(fake_parent, tree.children) return fake_parent[0].detach()
[docs] def psyir_from_file(self, file_path) -> Node: ''' 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. :returns: PSyIR representing the provided Fortran file. ''' tree = self._processor.generate_parse_tree_from_file(file_path) psyir = self._processor.generate_psyir(tree) psyir.name = str(file_path).rsplit('/', maxsplit=1)[-1] return psyir
# For Sphinx AutoAPI documentation generation __all__ = ['FortranReader']