Parsing Fortran Projects with Open Fortran Parser: Step-by-Step TutorialFortran remains widely used in scientific computing, engineering simulations, and legacy numerical codebases. The Open Fortran Parser (OFP) is a robust open-source tool for parsing Fortran source files, producing an abstract syntax tree (AST), and enabling static analysis, refactoring, and code transformation. This tutorial walks through using OFP to parse Fortran projects, inspect the AST, and perform simple analyses and transformations. It’s targeted at developers familiar with programming and build tools but new to Fortran parsing and OFP.
What is the Open Fortran Parser (OFP)?
Open Fortran Parser (OFP) is an open-source Fortran parser (originally part of the Open Fortran Project) that supports Fortran 77, 90, 95 and many modern constructs. It parses source files and builds an AST you can traverse programmatically. OFP is implemented in Java, and commonly used via its Java API; third-party bindings and tools may expose its functionality in other languages.
Prerequisites
- Java JDK 8+ installed and configured in PATH.
- Maven or Gradle (optional but convenient for Java projects).
- A Fortran project or sample Fortran files (.f, .f90, .f95, etc.).
- Familiarity with command line and basic Java development.
Installing and obtaining OFP
- Clone the repository or download a release:
- If OFP is hosted on GitHub or another SCM, clone it:
git clone https://github.com//open-fortran-parser.git
- If OFP is hosted on GitHub or another SCM, clone it:
- Build with Maven (if a pom.xml is provided):
mvn clean package
After building, you’ll have OFP jars in the target directory. If a packaged jar is available from releases, download that jar instead.
Basic usage overview
There are two common ways to use OFP:
- Programmatically through its Java API to parse files and traverse the AST.
- Via a command-line wrapper or utility provided with the project to parse files and output an intermediate representation (if available).
This tutorial focuses on the Java API approach, which offers the most flexibility.
Step 1 — Create a Java project that uses OFP
Using Maven, create a new project and add OFP as a dependency. If OFP is not available in Maven Central, add the built jar to your local repository or reference it as a system dependency.
Example Maven snippet (if OFP were in a repo):
<dependency> <groupId>org.openfortran</groupId> <artifactId>openfortranparser</artifactId> <version>1.0.0</version> </dependency>
If you must reference a local jar:
<dependency> <groupId>org.openfortran</groupId> <artifactId>openfortranparser</artifactId> <version>1.0.0</version> <scope>system</scope> <systemPath>${project.basedir}/lib/openfortranparser.jar</systemPath> </dependency>
Step 2 — Parsing a Fortran file
The typical API offers a parser class you instantiate and call to parse source code into an AST node (often named Program, CompilationUnit, or FileNode). Example Java code (adapt to actual OFP API names):
import org.openfortran.parser.FortranParser; import org.openfortran.parser.ast.ProgramUnit; import java.io.File; public class ParseExample { public static void main(String[] args) throws Exception { File source = new File("src/main/resources/example.f90"); FortranParser parser = new FortranParser(); ProgramUnit program = parser.parse(source); System.out.println("Parsed program: " + program.getName()); } }
Key points:
- Provide correct file encoding and free-form vs fixed-form flags if API supports them.
- Collect parser diagnostics to detect syntax errors or unsupported constructs.
Step 3 — Inspecting the AST
Once you have the AST root, traverse it to find program units, modules, subroutines, functions, variable declarations, and statements. OFP’s AST nodes typically provide visitor patterns or tree traversal utilities.
Example of a visitor pattern:
import org.openfortran.parser.ast.*; import org.openfortran.parser.ast.visitor.DefaultVisitor; public class MyVisitor extends DefaultVisitor { @Override public void visit(FunctionSubprogram node) { System.out.println("Function: " + node.getName()); super.visit(node); } @Override public void visit(SubroutineSubprogram node) { System.out.println("Subroutine: " + node.getName()); super.visit(node); } }
Run the visitor on the root node to print function/subroutine names and explore variable declarations.
Step 4 — Common analyses
Here are practical analyses you can implement once you can traverse the AST:
- Symbol extraction: collect variable, parameter, module, function and subroutine names and types.
- Call graph: find CALL statements and build a directed call graph between subroutines/functions.
- Dependency analysis: detect module usage and module-to-module dependencies.
- Lineage/tracking: map variables to assignment sites and usages for simple dataflow.
- Style and legacy checks: find COMMON blocks, EQUIVALENCE usage, implicit typing reliance.
Example: collecting CALL targets
@Override public void visit(CallStmt node) { System.out.println("Call: " + node.getSubroutineName()); super.visit(node); }
Step 5 — Transformations and refactoring
OFP allows programmatic modifications of the AST (depending on implementation completeness). Typical refactorings:
- Rename a subroutine or module (update declarations and CALL sites).
- Convert implicit typing to explicit declarations (insert declarations).
- Extract constants from repeated literals into PARAMETERS.
- Modernize fixed-form source to free-form formatting (requires printing support).
After changes, pretty-print or serialize the AST back to Fortran source. Use OFP’s pretty-printer or integrate a formatter to preserve style.
Step 6 — Parsing entire projects
For multi-file projects:
- Collect all Fortran source files (recursively).
- Determine compilation units and module/file-level dependencies (USE statements, module procedures).
- Parse files in dependency order if transformations require module symbols (or parse all and then resolve).
- Maintain a symbol table across files to resolve references (modules, interfaces, EXTERNAL procedures).
A simple project traversal in Java:
Files.walk(Paths.get("project")) .filter(p -> p.toString().endsWith(".f90") || p.toString().endsWith(".f")) .forEach(p -> parseAndIndex(p.toFile()));
Index parsed units into maps keyed by module/subroutine name for quick lookup.
Step 7 — Error handling and unsupported constructs
- Capture parser diagnostics; record file, line, message.
- Some modern Fortran features or vendor extensions may be unsupported — detect and report them.
- For partial parsing, skip or stub unknown constructs and continue analysis where possible.
Step 8 — Integrations and toolchain ideas
- Static analysis CLI: create a command-line tool that scans a project and emits warnings (unused variables, implicit typing).
- Automated modernization: batch-refactor COMMON blocks into module-based storage.
- Visualization: export call graphs to DOT format and render with Graphviz.
- CI integration: run OFP-based checks in continuous integration to gate commits.
Example DOT export for call graph nodes/edges:
digraph calls { "main" -> "compute"; "compute" -> "integrate"; }
Practical example: building a simple call-graph generator
- Parse all files and visit ASTs to collect:
- Definitions: functions/subroutines with fully-qualified names.
- Calls: (caller -> callee).
- Resolve names by matching call identifiers to definitions (account for module scoping).
- Output DOT or JSON.
This is a robust exercise in symbol resolution and demonstrates parsing, indexing, and analysis.
Tips and gotchas
- Fortran has many dialects and legacy forms: ensure you configure fixed/free form correctly.
- Preprocessing: Fortran source sometimes uses cpp-style preprocessing; run the preprocessor first or use OFP options if supported.
- Continue to test on real-world codebases: small contrived examples often differ from messy legacy projects.
- Preserve comments if you plan to re-generate source; some printers discard comment placements.
Resources and next steps
- Read OFP’s API docs and source for exact class and method names (they vary by fork/version).
- Explore existing projects that consume OFP for examples (refactoring tools, analyzers).
- Try incremental features: start with parsing and listing symbols, then add call-graph, then transformations.
Parsing Fortran projects with OFP opens doors to maintain, analyze, and modernize scientific codebases. Start small, iterate on symbol resolution, and build tooling around the AST to improve code quality and automation.
Leave a Reply