A modular, open-source FORTRAN tooling ecosystem in Rust. Ferrum provides a collection of small, composable libraries that form the foundation for FORTRAN analysis, refactoring, and modernization tools.
Ferrum now includes a production-ready dead code analyzer that identifies:
- ποΈ Unused variables and parameters
- β οΈ Dead/unreachable subroutines and functions
- π Comprehensive cleanup recommendations with statistics
Perfect for refactoring large legacy FORTRAN codebases in aerospace, physics simulations, and scientific computing.
- Modular: Small, focused crates that work together
- Fast: Built with Rust for performance
- Composable: Use what you need, combine as needed
- Open Source: MIT licensed
- Production Ready: Comprehensive error handling and testing
Fast, modular lexer for FORTRAN source code supporting both fixed-format and free-format FORTRAN.
Features:
- β Free-format FORTRAN lexing (FORTRAN 90+)
- β Fixed-format FORTRAN lexing (FORTRAN 77 and earlier)
- β Automatic format detection (fixed vs free format)
- β Column-based parsing for fixed-format (labels, continuation, code sections)
- β Comment line handling with any characters (c, C, *, !)
- β Case-insensitive keyword recognition
- β Comprehensive token types (keywords, identifiers, literals, operators, punctuation)
- β Source location tracking (line, column, span)
- β Error reporting with precise location information
Status: β Production ready with full FORTRAN 77 support
Abstract Syntax Tree (AST) data structures and analysis infrastructure for FORTRAN programs.
Features:
- β Complete AST representation of FORTRAN program units (PROGRAM, SUBROUTINE, FUNCTION, MODULE)
- β Declaration structures (variable declarations, type specifications, attributes)
- β Expression trees (arithmetic, logical, comparison, function calls)
- β Statement structures (IF, DO, SELECT CASE, I/O statements, etc.)
- β Source span tracking for all nodes
- β Visitor pattern for AST traversal
- β Symbol table system for tracking definitions and usage
- β Call graph builder for procedure dependency analysis
- β
AnalysisVisitortrait for extensible static analysis - β Comprehensive scope management (global, program, subroutine, function)
- β Optional serialization support (serde)
Status: β Production ready with analysis infrastructure
Recursive descent parser that converts tokens into a structured AST with full FORTRAN 77 support.
Features:
- β Automatic format detection and parsing (fixed-format and free-format)
- β Parses FORTRAN program units (PROGRAM, SUBROUTINE, FUNCTION, MODULE)
- β Parses declarations (variable declarations, type specifications, attributes)
- β Parses executable statements (IF, DO, READ, WRITE, PRINT, RETURN, STOP, etc.)
- β Parses expressions (arithmetic, logical, comparison, function calls)
- β Error reporting with location information
- β Handles whitespace and comments gracefully
- β Legacy FORTRAN 77 support with fixed-format parsing
- β Real-world compatibility with production numerical libraries
Status: β Production ready with legacy FORTRAN support
Auto-formatter for FORTRAN source code (like rustfmt or black).
Features:
- β Configurable indentation (spaces/tabs, width)
- β Keyword case normalization (UPPER, lower, preserve)
- β Identifier case normalization
- β Spacing around operators
- β Column alignment for declarations
- β Line length enforcement
- β Comment preservation
- β FORTRAN 77 and modern style presets
Status: β Core functionality ready
# Clone the repository
git clone https://github.com/MarsZDF/ferrum.git
cd ferrum
# Build all crates
cargo build --alluse fortran_lexer::{tokenize, detect_format};
// Free-format FORTRAN (90+)
let modern_source = r#"
program hello_world
implicit none
print *, 'Hello, World!'
end program hello_world
"#;
// Fixed-format FORTRAN (77 and earlier)
let legacy_source = r#"
SUBROUTINE HELLO
PRINT *, 'Hello from FORTRAN 77!'
END
"#;
// Automatic format detection and tokenization
let format = detect_format(modern_source);
let tokens = tokenize(modern_source, format)?;
for token in tokens {
if !token.is_trivial() {
println!("{:?} at line {}:{}", token.token_type, token.line, token.column);
}
}use fortran_parser::parse;
use fortran_ast::AnalysisVisitor;
// Legacy FORTRAN with dead code
let fortran_code = r#"
SUBROUTINE MAIN
INTEGER USED_VAR, UNUSED_VAR
REAL ANOTHER_UNUSED
USED_VAR = 42
CALL USED_SUBROUTINE(USED_VAR)
END
SUBROUTINE USED_SUBROUTINE(X)
INTEGER X
PRINT *, X
END
SUBROUTINE DEAD_SUBROUTINE
INTEGER DEAD_VAR
DEAD_VAR = 99
END
"#;
let program = parse(fortran_code)?;
let mut analyzer = DeadCodeAnalyzer::new();
analyzer.analyze_program(&program);
// Outputs detailed analysis:
// ποΈ UNUSED VARIABLES: UNUSED_VAR, ANOTHER_UNUSED
// β οΈ DEAD PROCEDURES: DEAD_SUBROUTINE
// π Dead code percentage: 50.0%
println!("{}", analyzer.generate_report());# Run the lexer example
cd fortran-lexer && cargo run --example basic_tokenize
# Modernization analysis tools
cd fortran-parser && cargo run --example extract_signature your_fortran_file.f
cd fortran-parser && cargo run --example extract_docs your_fortran_file.f
cd fortran-parser && cargo run --example type_mapper your_fortran_file.f
# Convert fixed-format to free-format
cd fortran-parser && cargo run --example fixed_to_free legacy.f modern.f90
# Dead code analysis for legacy FORTRAN cleanup
cd fortran-parser && cargo run --example dead_code_analyzer your_fortran_file.f
# Parse any FORTRAN file (auto-detects format)
cd fortran-parser && cargo run your_fortran_file.fβββββββββββββββββββββββ
β FORTRAN Source β
β (.f, .f90, .f95) β
ββββββββββββ¬βββββββββββ
β
v
βββββββββββββββββββββββ
β fortran-lexer β Tokenizes source code
β β (fixed-format β
, free-format β
)
ββββββββββββ¬βββββββββββ
β
v
βββββββββββββββββββββββ
β fortran-parser β Parses tokens into AST
β β (recursive descent)
ββββββββββββ¬βββββββββββ
β
v
βββββββββββββββββββββββ
β fortran-ast β AST + Symbol Tables + Call Graphs
β β (visitor pattern + analysis)
ββββββββββββ¬βββββββββββ
β
v
βββββββββββββββββββββββ
β Analysis Tools β Dead code detection β
β β Performance hints, migration tools
βββββββββββββββββββββββ
- Rust 1.70+ (stable, beta, or nightly)
- Cargo (comes with Rust)
# Build all crates
cargo build --all
# Build a specific crate
cd fortran-lexer && cargo build# Run all tests
cargo test --all
# Run tests for a specific crate
cd fortran-lexer && cargo test
# Run with output
cargo test --all -- --nocapture# Run lexer example
cd fortran-lexer && cargo run --example basic_tokenize# Format code
cargo fmt --all
# Run clippy
cargo clippy --all -- -D warningsWe use GitHub Actions for continuous integration:
- β Tests on stable, beta, and nightly Rust
- β Tests on Linux, Windows, and macOS
- β Linting with clippy and rustfmt
- β Builds examples and documentation
- β All crates tested in the pipeline
See .github/workflows/ci.yml for details.
Contributions are welcome! This project follows standard Rust conventions:
- Fork the repository
- Create a feature branch (
git checkout -b feature/amazing-feature) - Make your changes
- Add tests for new functionality
- Ensure all tests pass (
cargo test --all) - Run clippy and fix warnings (
cargo clippy --all) - Format code (
cargo fmt --all) - Update documentation as needed
- Submit a pull request
- Follow Rust naming conventions
- Write comprehensive tests
- Document public APIs with rustdoc
- Handle errors explicitly (use
Resulttypes) - Keep crates focused and modular
- Use workspace dependencies where appropriate
Licensed under the MIT License - see LICENSE for details.
- fortran-lexer - Free-format FORTRAN lexer
- fortran-lexer - Fixed-format FORTRAN 77 lexer
- fortran-ast - Core AST structures with analysis infrastructure
- fortran-parser - Basic parser implementation with format detection
- Dead code analyzer - Production-ready static analysis tool
- Symbol table and call graph analysis infrastructure
- Modernization analysis tools (signature extraction, documentation, type mapping)
- Comprehensive test suite for lexer, parser, and analyzer
- CI/CD pipeline setup
- Production readiness (error handling, documentation, examples)
- fortran-parser - Full FORTRAN grammar support (remaining statements and expressions)
- fortran-analyzer-* - Additional analysis modules
- Performance analysis hints
- Code quality metrics
- Automated migration from fixed-format to free-format
- Modernization suggestions
- Language server support (LSP)
- Enhanced formatter with more configuration options
- Refactoring tools
- REPL for FORTRAN exploration
This project aims to modernize FORTRAN tooling using Rust's excellent performance and safety guarantees. Special thanks to:
- The Rust community for excellent tooling and documentation
- FORTRAN maintainers for keeping scientific computing systems running
- Contributors and users of this project
- Issues: GitHub Issues
- Discussions: GitHub Discussions
- Pull Requests: GitHub Pull Requests
This section is designed to help AI coding assistants (like Cursor, Claude Code, GitHub Copilot) quickly understand and work with the Ferrum codebase.
ferrum/
βββ fortran-lexer/ # Tokenization layer
β βββ src/
β β βββ lib.rs # Main exports
β β βββ token.rs # Token types and Token struct
β β βββ lexer.rs # Lexer implementation (FreeFormatLexer)
β β βββ error.rs # LexError types
β βββ tests/
β βββ examples/
βββ fortran-ast/ # AST data structures + analysis
β βββ src/
β β βββ lib.rs # Main exports
β β βββ program.rs # Program, ProgramUnit, MainProgram, etc.
β β βββ declaration.rs # Declarations, TypeSpec, Attributes
β β βββ statement.rs # Statements (IF, DO, SELECT CASE, etc.)
β β βββ expression.rs # Expressions (arithmetic, logical, calls)
β β βββ span.rs # Source location tracking
β β βββ visitor.rs # Visitor pattern + AnalysisVisitor trait
β β βββ analysis.rs # Symbol tables, call graphs, analysis infra
β βββ tests/
βββ fortran-parser/ # Parsing layer
β βββ src/
β β βββ lib.rs # Main exports and parse() function
β β βββ parser.rs # Recursive descent parser (large file ~1500 lines)
β βββ tests/
β β βββ integration_tests.rs # Comprehensive test suite
β βββ examples/
β βββ dead_code_analyzer.rs # Production dead code analysis tool
βββ Cargo.toml # Workspace configuration
- Tokenization β Parsing β AST: Source code flows through lexer β parser β AST
- Recursive Descent Parsing: The parser uses recursive descent with precedence climbing
- Zero-Copy Where Possible: Uses
&strreferences instead of ownedStrings - Source Location Tracking: All AST nodes are wrapped in
Spanned<T>for error reporting - Error Handling: Custom error types (
LexError,ParseError) with precise location info
- Lexer: Add to
KEYWORDSconstant infortran-lexer/src/token.rs - Token Type: Add variant to
TokenTypeenum infortran-lexer/src/token.rs - Parser: Add handling in
fortran-parser/src/parser.rs(usually inparse_statement_opt()or similar) - AST: Add corresponding AST structure if needed (in
fortran-ast/src/statement.rsordeclaration.rs)
- AST: Define in
fortran-ast/src/statement.rs(add toStatementenum) - Parser: Add parsing method in
fortran-parser/src/parser.rs:- Add detection in
is_executable_statement()if needed - Add case in
parse_statement_opt() - Implement
parse_<statement_type>()method
- Add detection in
- Tests: Add test in
fortran-parser/tests/integration_tests.rs
- Lexer: Add operator to
TokenTypeinfortran-lexer/src/token.rs - AST: Add to
BinaryOporUnaryOpinfortran-ast/src/expression.rs - Parser:
- Add to
parse_binary_operator()orparse_unary_operator() - Add precedence in
get_operator_precedence() - Handle in expression parsing logic
- Add to
- Identify: Run
cargo test --package fortran-parser --test integration_teststo see failing tests - Locate: Find the relevant parsing method in
fortran-parser/src/parser.rs - Common Issues:
- Token position tracking:
peek(),advance(),self.currentmanagement - Whitespace handling: Use
is_trivial()to skip whitespace/comments - Expression precedence: Check
parse_binary_expression()and precedence values - Statement boundaries: Check
is_executable_statement()detection logic
- Token position tracking:
The parser (fortran-parser/src/parser.rs) is a large recursive descent parser:
- Main entry point:
parse()function createsParserand callsparse_program() - Token management:
peek()- get next non-trivial token (skips whitespace/comments)advance()- consume current token and return next non-trivial onecheck_token()- check if current token matches expected typeself.current- index intoself.tokensvector
- Common patterns:
parse_*_opt()methods returnOption(for optional constructs)parse_*()methods returnResult(for required constructs)- Use
Spanned::new()to wrap AST nodes with source location - Use
self.create_span()for span creation
- Lexer errors:
LexErrorwith line/column info - Parser errors:
ParseErrorenum with variants:UnexpectedToken { expected, found }UnexpectedEof { expected }InvalidSyntax { message, line, column }
- Always include expected tokens and found token in error messages
- Use
self.current_line()andself.current_column()for error locations
- Integration tests:
fortran-parser/tests/integration_tests.rs- comprehensive test suite - Unit tests: Inline
#[cfg(test)]modules in source files - Test naming:
test_parse_<feature>for parser tests - Test structure:
#[test] fn test_parse_feature() { let source = r#"FORTRAN code here"#; let result = parse(source); assert!(result.is_ok(), "Failed to parse: {:?}", result.err()); // ... assertions }
Status: β COMPLETE - Full FORTRAN 77 fixed-format support implemented
Implementation Details:
- β
FixedFormatLexerstruct infortran-lexer/src/lexer.rs - β
Column-based parsing:
- Columns 1-5: Statement label (optional)
- Column 6: Continuation indicator (space/0 = new statement, other = continuation)
- Columns 7-72: FORTRAN code
- Columns 73-80: Comments/sequence numbers (ignored)
- Column 1 = C, c, *, !: Comment line
- β Automatic format detection and parser integration
- β Real-world compatibility with legacy numerical libraries
Usage:
use fortran_lexer::{tokenize, detect_format};
let legacy_fortran = r#"
c This is a comment
SUBROUTINE HELLO
PRINT *, 'Hello World'
END
"#;
let format = detect_format(legacy_fortran); // Returns FixedFormat
let tokens = tokenize(legacy_fortran, format)?; // Works seamlessly- Error handling: Prefer
Result<T, E>over panics - Ownership: Use references (
&str) where possible, clone only when necessary - Documentation: Public APIs should have rustdoc comments
- Naming: Follow Rust conventions (snake_case for functions, PascalCase for types)
- Imports: Group by: std, external crates, workspace crates, local modules
- Add debug prints:
eprintln!("Current token: {:?}", self.peek()); - Check token stream:
eprintln!("Tokens: {:?}", self.tokens); - Verify position:
eprintln!("Current index: {}", self.current); - Test incrementally: Run
cargo test --package fortran-parser --test integration_tests <test_name>for specific tests - Use backtrace:
RUST_BACKTRACE=1 cargo test ...
- Token definitions:
fortran-lexer/src/token.rs - Lexer logic:
fortran-lexer/src/lexer.rs - AST definitions:
fortran-ast/src/*.rs - Analysis infrastructure:
fortran-ast/src/analysis.rs - Parser logic:
fortran-parser/src/parser.rs(main file) - Dead code analyzer:
fortran-parser/examples/dead_code_analyzer.rs - Parser tests:
fortran-parser/tests/integration_tests.rs - Workspace config:
Cargo.toml(root) - CI/CD:
.github/workflows/ci.yml
- Start with tests: Write a failing test first
- Update AST: Add necessary data structures
- Update lexer: Add token types if needed
- Update parser: Implement parsing logic
- Run tests:
cargo test --all - Update docs: Add examples and documentation
- Run linter:
cargo clippy --all -- -D warnings
Built with β€οΈ in Rust