Skip to content

Repository files navigation

F2C

English · 中文文档

A modern, high-performance Fortran-to-C17 transpiler designed to generate portable, production-grade C code. It is written in C17 and requires no additional generated-code runtime.

Highlights

  • Automatic free-form and fixed-form source detection, with explicit CLI overrides.
  • Typed expression and statement ASTs with kind, rank, shape, and value-category metadata.
  • Runtime-free C17 output intended for desktop, server, mobile, and WebAssembly toolchains.
  • Strict cross-platform CI, sanitizers, deterministic-output checks, fuzzing, BLAS/LAPACK differential validation, and performance gates.

Requirements and build

The project requires CMake 3.20 or newer and a C17 compiler. Python 3 and gfortran are additionally required for the complete numerical differential suite.

cmake -S . -B build \
  -DCMAKE_BUILD_TYPE=Release \
  -DF2C_BUILD_TESTING=ON \
  -DF2C_ENABLE_WARNINGS_AS_ERRORS=ON
cmake --build build --parallel
ctest --test-dir build --output-on-failure

All generated files and local build products must stay below the root build/ directory. The project deliberately has no CMake installation rules.

Command-line usage

Translate one source file and compile the generated C:

build/f2c input.f90 -o output.c
cc -std=c17 -O3 output.c -lm -o output

Translate several files as one project and emit a shared C interface:

build/f2c caller.f90 implementation.f90 -o project.c --header project.h

Use - as an input or output path for pipelines, for example build/f2c - --free-form -o - < input.f90. File outputs are staged before replacement; if any requested artifact cannot be written, existing C and header outputs are preserved.

Source form is automatic by default. .f, .for, and .ftn use fixed form; modern extensions such as .f90 use free form. --free-form and --fixed-form override detection when a file name does not describe its physical layout. --comments retains source lines as generated C comments.

Conditional preprocessing is explicit and case-sensitive. Use -DNAME or -DNAME=EXPR to provide a definition and -UNAME to remove an earlier command-line definition, for example:

build/f2c -I include -DUSE_ISNAN=1 source.F90 -o output.c

The built-in contract supports object-like and function-like #define expansion, #undef, #if, #ifdef, #ifndef, #elif, #else, #endif, #include, #line, numeric line markers, and standard Fortran INCLUDE. Function-like macros support zero or nested arguments, variadic arguments, stringizing, token pasting, and backslash-continued preprocessing directives. Include operands may be produced by macro expansion. Integer conditions use deterministic signed and unsigned 64-bit values, character constants, normal precedence, and short-circuit evaluation. Macro diagnostics retain both expansion and definition spelling ranges; incompatible redefinitions and malformed or recursive invocations are hard errors. No project- or platform-specific feature macro is implicitly guessed. API and CLI definitions are object-like and apply to every project input, while definitions made inside a source file remain local to that input. -I configures CLI include directories; quoted includes search the including file's directory first.

Run build/f2c --help for the complete CLI reference.

Library API

The public interface in include/f2c/f2c.h supports in-memory translation:

#include <f2c/f2c.h>

F2cOptions options = {"input.f90", F2C_SOURCE_AUTO, 0};
F2cResult result = f2c_transpile(source, source_length, &options);

if (result.error_count == 0U) {
    /* result.code contains self-contained C17. */
}

f2c_result_free(&result);

Use f2c_transpile_project with an F2cInput[] array to translate several sources with a shared procedure registry. result.header contains declarations for the external procedures defined by that project. On a hard error, code and header are NULL and diagnostics contains actionable file, line, and column information.

Long-running services can set request-local resource budgets without process-global state:

F2cConfig config = {0};
config.structure_size = sizeof(config);
config.limits.max_input_bytes = 64U * 1024U * 1024U;
config.limits.max_output_bytes = 128U * 1024U * 1024U;

F2cPreprocessorDefinition definitions[] = {{"USE_ISNAN", "1"}};
config.preprocessor_definitions = definitions;
config.preprocessor_definition_count = 1U;

/* Optional: provide #include/INCLUDE sources without binding the library to a file system. */
config.include_resolver = resolve_include;
config.include_release = release_include;
config.include_user_data = resolver_context;

F2cResult result = f2c_transpile_project_config(inputs, input_count, &config);

A zero limit selects the corresponding F2C_DEFAULT_* value. Budgets cover aggregate input and retained preprocessed bytes, conditional definitions, macro arguments, macro/include depth and include count, logical lines, canonical tokens, expression AST nodes and depth, constant-evaluation work, diagnostics, diagnostic bytes, and each generated code/header artifact. Limit failures return no partial generated C. Include resolution is request-local and callback-driven, so the core library does not require a file system. A synchronous F2cDiagnosticCallback can additionally consume structured diagnostic categories, severity, expansion/spelling source ranges, and messages without parsing the text rendering. structure_size must equal sizeof(F2cConfig). The project has not frozen a public ABI yet, so older or larger configuration layouts are rejected and all fields belong to the current API.

Support status

For supported REAL kind 4/8 extrema, MIN/MAX and MINVAL/MAXVAL use one processor policy: any selected NaN propagates, including the pinned LAPACK installation contract. Mixed signed zeros produce positive zero for maximum and negative zero for minimum. MINLOC/MAXLOC use numerical ties (including signed zeros), selecting the first match or the last with BACK=.TRUE.; if any selected value is NaN, they choose the first or last selected NaN. Constant evaluation follows the generated-code policy. This is a documented choice where Fortran does not specify NaN or zero-sign results, not a guarantee of bitwise identity with every Fortran processor. Empty or fully masked real value reductions return the finite kind-specific boundaries; nonempty selections retain genuine infinities.

The currently tested implementation includes:

  • normalized free and fixed source forms, continuations, labels, bounded object-macro and conditional preprocessing, callback-provided includes, line remapping, program units, internal procedures, modules, host association, and USE association;
  • tested local declarations and dummy/result bindings shadowing module or procedure hosts, nearest-host lookup, explicit USE rebinding, and sibling-call capture forwarding; imported constants and module shapes retain their defining scope, including private dependencies and re-exports; scope-local ASYNCHRONOUS/VOLATILE attributes preserve associated storage;
  • intrinsic numeric, logical, CHARACTER, and complex types; explicit and implicit typing; typed expressions, array constructors, sections, vector subscripts, reductions, and selected transformational intrinsics;
  • tested static storage and whole-array arguments for local, host-associated, and imported PARAMETER arrays; shared typed column-major constant evaluation covers nested constructors, implied DO, parameter dependencies, supported transforms, and fixed component initialization, preserving declared kinds, lower bounds, and character padding; explicit constructor type-specs, arbitrary constant array expressions, and dynamic derived payloads remain incomplete;
  • explicit, abstract, generic, and procedure-pointer interfaces; positional, keyword, optional, and procedure arguments on the supported ABI paths;
  • allocatable and pointer objects, descriptors, automatic reallocation, SOURCE=, MOLD=, MOVE_ALLOC, derived components, intrinsic assignment, and ownership cleanup;
  • derived types, inheritance, dynamic type tags, SELECT TYPE, type-bound dispatch, exact-rank FINAL procedures, and construct-scope finalization on supported control-flow paths;
  • length-aware CHARACTER assignment and comparison, substrings, arrays, deferred lengths, function results, and the gfortran-compatible trailing-length ABI used by the validation corpus;
  • explicit-parent CHARACTER designators for array-element, section, and component substrings; tested overlap-safe writes, pointer aliases, constant padding, and single-evaluation bounds;
  • tested character substring copy-in/copy-out for ordinary subroutine and scalar-result function arguments, with procedure-scoped typed character result-length specifications, normalized negative declared lengths, checked target-width conversions, scalar-broadcast length reallocation, and preserved procedure execution for zero-length character assignment results; ordinary scalar character functions retain their entry result length when their body modifies length arguments or host variables, including scalar-broadcast assignment;
  • tested type-bound scalar character results with first/nonfirst PASS, NOPASS, inherited overrides, reordered keywords, optional arguments, noncontiguous substring copy-back, and a shared entry-length snapshot;
  • tested derived-component default initialization for fixed-shape numeric, logical, complex, character, nested, and inherited components; automatic, block, module, SAVE, INTENT(OUT), and allocation paths share defaults, while constructors and deep copies preserve explicit data;
  • nested constructor, elemental, inquiry, and reduction expressions on the tested typed-lowering paths, including statement-owned snapshots of component arrays and noncontiguous reduction operands; allocation-free direct reductions remain for simple array/scalar comparisons;
  • structured and legacy control flow, formatted and list-directed I/O, internal files, nonadvancing I/O, defined I/O, and recursive NAMELIST handling on the documented paths;
  • tested pre-counted legacy REAL kind 4/8 DO, single-evaluation controls, qualified storage, and post-loop values; termination no longer depends on an accumulated floating-point value crossing a bound, and invalid steps or unrepresentable counts fail before execution;
  • shared DECIMAL/ROUND/SIGN/DELIM scopes, binary32/64-aware exact input rounding, escaped list-directed CHARACTER output, and inherited DT child controls and record positions;
  • RESHAPE, PACK, UNPACK, SPREAD, CSHIFT, EOSHIFT, and FINDLOC lowering for the tested numeric, CHARACTER, and derived-type combinations.
  • Intrinsic FINDLOC comparisons across tested numeric categories/kinds and LOGICAL kinds, blank-padded CHARACTER searches, scalar DIM results, conformable masks, strided views, selected result kinds, and typed assignment conversions.
  • Automatic allocation of tested intrinsic scalar assignments, including allocatable components, dummies and function results; scoped function-header kinds from host/module association and USE.

Important remaining work includes complete token-stream coverage for declarations and modules, all kind/rank and arbitrary-array-expression combinations, complete module generics and submodules, dynamic polymorphic allocation, named/association construct finalization boundaries, byte-strided views preserving persistent TARGET aliases, complete result-specification scope and argument mappings, every formatted-I/O layout rule, complete list-directed null/repeat/slash input combinations, pointer reassociation during NAMELIST input, and multi-compiler ABI certification. Unsupported semantics must produce diagnostics rather than plausible but incorrect C. Complete intrinsic/model/shape combinations and independent certification remain open. The detailed checklist is maintained in TODO.md.

Validation

The pinned Reference LAPACK corpus currently provides these automated gates:

  • 155/155 Reference BLAS and 3,535/3,535 BLAS/LAPACK/INSTALL/TESTING sources translated and warning-clean compiled as strict C17;
  • a generated BLAS/LAPACK archive executing an end-to-end DGESV solve;
  • all four INSTALL checks and 52,512 S/D/C/Z RFP checks matched against native Fortran;
  • official BLAS Level 1/2/3, complete S/D/C/Z LIN, and all 80 EIG input suites differentially checked against the same pinned native build;
  • exhaustive internal audit artifacts for 100 numerical drivers, with platform-profiled union counts recorded in the machine-readable manifest;
  • a 71-case generated-C/native-Fortran performance matrix with a per-case 1.05 ratio ceiling.

The exhaustive audit intentionally records finite rounding differences, unmatched internal records, and NaN-policy differences. Passing it means no new generated-side official-threshold or coverage regression; it does not claim bitwise equivalence with native Fortran.

Representative local gates are:

sh test/reference_blas_compile.sh build/f2c
sh test/reference_lapack_core_compile.sh build/f2c
sh test/reference_blas_tests.sh build/f2c
sh test/reference_lapack_lin.sh build/f2c
sh test/reference_lapack_eig.sh build/f2c
sh test/reference_blas_exhaustive.sh build/f2c
sh test/reference_lapack_exhaustive.sh build/f2c
sh test/reference_performance_matrix.sh build/f2c

CI responsibilities and trigger policies are documented in .github/workflows/README.md.

Project layout

include/f2c/   Public embedding API
src/cli/       Command-line adapter
src/core/      Public API implementation and pipeline orchestration
src/frontend/  Source normalization, program units, declarations, and interfaces
src/semantic/  Types, constants, intrinsics, CHARACTER, and procedure semantics
src/ast/       Owned expression/statement AST, lexer, parser, and visitors
src/codegen/   C17 lowering, arrays, I/O, ownership, types, and program units
src/internal/  Private cross-module data structures and interfaces
test/          Unit, execution, differential, fuzz, and performance tests

Contributing

Focused changes that improve portable C17 generation and auditable Fortran semantics are welcome. Please preserve these project invariants:

  • use the project name f2c and keep all generated artifacts below build/;
  • do not change archived sources or the project scope specification;
  • generated C may depend on ISO C17, libc, and libm only—do not add a separate runtime;
  • extend the typed AST/statement IR instead of reparsing generated text;
  • diagnose unsupported semantics instead of emitting an approximate implementation;
  • preserve unrelated worktree changes and format C code with the repository .clang-format.

Every parser, semantic, ABI, ownership, control-flow, I/O, or code-generation change should add an executed regression. Run the strict CMake/CTest commands above before submitting a change. Broad translator changes should also run the affected BLAS/LAPACK gates; performance-sensitive changes must run test/reference_performance_matrix.sh on an otherwise idle machine.

Security

The current default branch and latest tagged release receive security fixes. Do not disclose a suspected vulnerability in a public issue, discussion, or pull request. Use GitHub private vulnerability reporting and include, when available:

  • the affected version or commit, platform, and C compiler;
  • the smallest Fortran input that reproduces the issue;
  • generated C that can be shared safely;
  • impact, sanitizer diagnostics, and resource-usage observations.

Generated-code memory safety, parser resource exhaustion, path handling, release provenance, and unexpected access outside the requested inputs are treated as security issues. Maintainers will acknowledge, reproduce, assess, and coordinate disclosure through the private report.

Tagged release assets include SHA-256 checksums and GitHub artifact attestations:

sha256sum -c f2c-<version>-<platform>.<archive>.sha256
gh attestation verify f2c-<version>-<platform>.<archive> --repo abandoft/f2c

On macOS, use shasum -a 256 -c for the checksum file.

License

  • The code in f2c is licensed under the MIT License.
  • The code in netlib-f2c remains subject to the original project’s license.

Note: f2c is a newly refactored project and does not depend on netlib-f2c.

About

A modern, high-performance Fortran-to-C17 transpiler designed to generate portable, production-grade C code.

Topics

Resources

Stars

0 stars

Watchers

0 watching

Forks

Releases

Contributors

Languages