Fortran to Python interface generator with derived type support for automated wrapper generation
—
Comprehensive parsing of Fortran 90/95/2003/2008 source code into a structured Abstract Syntax Tree (AST) with full support for modern Fortran constructs including modules, derived types, procedures, and interfaces.
Parse Fortran source files into a complete Abstract Syntax Tree representation.
def read_files(args, doc_plugin_filename=None):
"""
Parse Fortran source files into AST.
Parameters:
- args: list of Fortran source file paths
- doc_plugin_filename: optional documentation plugin file
Returns:
Root AST node containing parsed tree
"""class Fortran(object):
"""Base class for all AST nodes."""
class Root(Fortran):
"""Root node of the parse tree."""
class Program(Fortran):
"""Fortran program node."""
class Module(Fortran):
"""Fortran module node with name, procedures, types."""class Procedure(Fortran):
"""Base class for procedures (subroutines and functions)."""
class Subroutine(Procedure):
"""Subroutine node with arguments, local variables."""
class Function(Procedure):
"""Function node with arguments, return value, local variables."""
class Interface(Fortran):
"""Interface block node containing procedure prototypes."""
class Prototype(Fortran):
"""Procedure prototype within an interface."""
class Binding(Fortran):
"""Type-bound procedure binding."""class Type(Fortran):
"""Derived type definition with components and procedures."""
class Declaration(Fortran):
"""Variable or parameter declaration."""
class Element(Declaration):
"""Component element within a derived type."""
class Argument(Declaration):
"""Procedure argument with intent and attributes."""def iter_fields(node):
"""Iterate over all fields of an AST node."""
def iter_child_nodes(node):
"""Iterate over all child nodes of an AST node."""
def walk(node):
"""Recursively walk the entire AST tree."""
def walk_modules(node):
"""Walk only module nodes in the tree."""
def walk_procedures(tree, include_ret_val=True):
"""Walk all procedure nodes in the tree."""def find(tree, pattern):
"""Find nodes matching a specific pattern."""
def find_types(tree, skipped_types=None):
"""Find all derived type definitions in the tree."""
def find_procedure_module(tree, node):
"""
Find the module in tree that contains node.
Parameters:
- tree: AST tree to search
- node: procedure node to locate
Returns:
Module node containing the procedure, or None if not found
"""
def find_source(node):
"""Get source location information for a node."""
def print_source(node, out=None):
"""Print the source code representation of a node."""
def dump(node):
"""Debug dump of node structure and contents."""class F90File(object):
"""Represents a Fortran source file being parsed."""def check_program(cl, file):
"""Parse PROGRAM statements."""
def check_module(cl, file):
"""Parse MODULE statements and module contents."""
def check_subt(cl, file, grab_hold_doc=True):
"""Parse SUBROUTINE and TYPE statements."""
def check_funct(cl, file, grab_hold_doc=True):
"""Parse FUNCTION statements."""
def check_type(cl, file):
"""Parse TYPE definitions and components."""
def check_interface(cl, file):
"""Parse INTERFACE blocks and contained procedures."""def check_decl(cl, file):
"""Parse variable and parameter declarations."""
def check_arg(cl, file):
"""Parse procedure arguments with attributes."""
def check_binding(cl, file):
"""Parse type-bound procedure bindings."""
def check_prototype(cl, file):
"""Parse procedure prototypes in interfaces."""def check_uses(cline, file):
"""Parse USE statements and module imports."""
def check_doc(cline, file):
"""Parse documentation comments (Doxygen-style)."""
def check_doc_rv(cline, file):
"""Parse return value documentation."""
def check_cont(cline, file):
"""Handle line continuations in source code."""def remove_delimited(line, d1, d2):
"""Remove delimited sections (quotes, comments) from code lines."""
def recover_delimited(line, d1, d2, delimited):
"""Restore previously removed delimited sections."""
def split_attribs(atr):
"""Parse Fortran attribute strings into components."""
def splitnames(names):
"""Split comma-separated name lists into individual names."""def implicit_type_rule(var):
"""Apply Fortran implicit typing rules to variables."""
def implicit_to_explicit_arguments(argl, ag_temp):
"""Convert implicit argument lists to explicit form."""class FortranVisitor(object):
"""Base visitor class for traversing AST nodes."""
class FortranTransformer(FortranVisitor):
"""Base transformer class for modifying AST nodes."""
class FortranTreeDumper(FortranVisitor):
"""Debug visitor for dumping tree structure."""
class LowerCaseConverter(FortranTransformer):
"""Convert all names in AST to lowercase."""
class RepeatedInterfaceCollapser(FortranTransformer):
"""Collapse interfaces that appear multiple times with identical signatures."""
class AccessUpdater(FortranTransformer):
"""Update access control attributes (public/private) throughout the tree."""
class PrivateSymbolsRemover(FortranTransformer):
"""Remove private symbols that should not be accessible from Python."""def fix_argument_attributes(node):
"""Fix parsing issues with argument attributes."""
def remove_private_symbols(node):
"""Remove private symbols from module nodes."""import f90wrap.parser as parser
# Parse a single file
tree = parser.read_files(['source.f90'])
# Parse multiple files
tree = parser.read_files(['mod1.f90', 'mod2.f90', 'main.f90'])
# Parse with documentation plugin
tree = parser.read_files(['source.f90'], doc_plugin_filename='doc_plugin.py')from f90wrap.fortran import walk, walk_modules, find_types
# Walk all nodes
for node in walk(tree):
print(f"Node type: {type(node).__name__}")
# Walk only modules
for module in walk_modules(tree):
print(f"Module: {module.name}")
# Find all derived types
types = find_types(tree)
for type_node in types:
print(f"Type: {type_node.name}")from f90wrap.fortran import FortranVisitor
class MyVisitor(FortranVisitor):
def visit_Module(self, node):
print(f"Visiting module: {node.name}")
self.generic_visit(node)
def visit_Subroutine(self, node):
print(f"Visiting subroutine: {node.name}")
self.generic_visit(node)
visitor = MyVisitor()
visitor.visit(tree)Install with Tessl CLI
npx tessl i tessl/pypi-f90wrap