Skip to main content

A FORTRAN-66 / DEC FORTRAN-10 interpreter in Python

Project description

forterp

Tests PyPI Python versions License: MIT

Software Architecture, Design & Engineering by Nicholas J. Kisseberth.
Code Synthesized via Anthropic Claude Code / Opus 4.8.
Automated Code Review via OpenAI Codex / ChatGPT 5.5.

A configurable FORTRAN-66 interpreter in Python: the machine value model and the front-end dialect are both pluggable, so one core runs FORTRAN against whatever representation you select.

The value model is a Target — integer word width and overflow, the logical-truth convention, and how characters pack into words. Two ship:

  • NATIVE (the default) — a clean 64-bit host machine for running standard FORTRAN-66 portably: 64-bit two's-complement integers, 8-bit ASCII, .TRUE.=1 with boolean logical operators. import forterp; forterp.run_source(...) uses this.
  • PDP10 — the DEC FORTRAN-10 model: 36-bit two's-complement words, 5×7-bit packed character storage, .TRUE.=−1 with bit-wise logicals. Select with Engine(..., target=forterp.PDP10).

The PDP-10 target was extracted from an interpreter built to run real 1970s DEC FORTRAN unmodified, so it is exercised against real period code — not just toy snippets — and validated against the DEC FORTRAN-10 V5 manual and the FCVS conformance corpus.

Install

pip install forterp          # (once published)
# or, from a checkout:
pip install -e .

Quick start

import forterp

# The default dialect is strict ANSI F66; FORTRAN10 enables the DEC niceties
# (here the quoted-string FORMAT -- F66 itself uses Hollerith nH). Note fixed form:
# the statement label sits in columns 1-5 and the body starts in column 7.
eng = forterp.run_source('''      PROGRAM HELLO
      WRITE(6,10)
   10 FORMAT(' HELLO, WORLD')
      END
''', dialect=forterp.FORTRAN10, printer=print)

Lower-level building blocks (the expert surface lives under forterp.runtime):

units = forterp.parse_source(src)              # {name: ProgramUnit}
eng   = forterp.runtime.make_engine(units)     # Engine with the FORTRAN-10 runtime installed
eng.run_program("MAIN")                         # or run_program() for the first unit

The package root exposes only the focused names above; the rest is organized into namespaces: forterp.runtime (the Engine and builders), forterp.frontend (lexer/parser stages), forterp.format (the FORMAT engine), forterp.ast (AST nodes), forterp.hostlib (host-builtin marshalling), and forterp.debug (the interactive tracer/profiler and the out-of-bounds census — forterp.debug.oob_census() counts or logs the faithful unchecked-array accesses without changing them).

Command line

Installing puts three commands on your PATH — thin dialect front-ends over the engine (like g77/gfortran over gcc):

pyf66 prog.for              # run as strict ANSI FORTRAN-66 (rejects DEC extensions)
pyfortran10 prog.for        # run as DEC FORTRAN-10 (the superset: octal, IMPLICIT, '...', …)
forterp --std fortran10 prog.for   # general driver; --std f66|fortran10 (default: f66)

--target native|pdp10|vax selects the value model and --program NAME picks the main unit. --check parses and lists every diagnostic without running (a compile-check) — so pyf66 --check prog.for is a strict-ANSI-F66 conformance linter. --version prints the version, --help the usage. Before install, use python -m forterp ….

Pass several source files and they are linked together by unit name, the way a compiler links separately-compiled units — so a driver and a separately-held library run as one program:

forterp main.for lib.for           # main.for's PROGRAM calls SUBROUTINEs in lib.for

Launched with no file, each command drops into an interactive command processor (a small, FORTRAN-focused descendant of the TOPS-10 . prompt — it operates on whole source files, not a statement REPL, since F66 has no incremental-execution model):

f66> RUN prog.for            # compile + run (alias EXECUTE); CHECK = parse-only
f66> SET STD fortran10       # switch dialect, target, or main unit between runs
f10> LOAD prog.for           # parse into the session; START runs it
f10> SHOW /BLOCK/            # inspect a COMMON block after a run; SHOW = settings
f10> !cmd   @file   HELP   EXIT

The command set is identical across the three commands; only the starting dialect differs (pyf66 → f66, pyfortran10 → fortran10), and SET STD flips it.

IMMEDIATE (alias REPL) drops into interactive FORTRAN — statements run as you type, a DO loop is collected across lines, and a bare expression is evaluated (so typing a name inspects it). After a LOAD, the REPL can call straight into the program:

f66* NFAC = 1
f66* DO 10 I=1,5
cont> NFAC = NFAC * I
cont> 10 CONTINUE
f66* NFAC                    # -> 120
f66* ISQ(9)                  # call a function from the LOADed program -> 81
f66* 2 + 3 * 4               # calculator -> 14

(COMMON/EQUIVALENCE need a full program unit — put those in a file and LOAD it; F66 has no incremental model for control flow, so the unit of work is a statement or a DO block, never a bare GOTO.)

The command processor also debugs and profiles a RUN/START. BREAK <line> + STEP drop into a (dbg) prompt where you step (step/next/cont), backtrace (where), and inspect any expression by typing it; TRACE echoes each statement; PROFILE/COVERAGE report per-line execution counts and which lines were reached. The profiler counts statements (deterministic), not wall-clock seconds. All of it rides one off-by-default hook, so a plain run pays nothing:

f66> BREAK 6
f66> RUN fac.for
-- stopped at FAC:6 (Assign)
(dbg) NF                     # inspect a variable by name -> 1
(dbg) where                  # backtrace -> #0 FAC:6
(dbg) cont
f66> PROFILE                 #    5  FAC:6   (the loop body ran 5 times)

What's pluggable

  • Machine targetforterp.Target(word_bits, chars_per_word, logical_true, bitwise_logic, bits_per_char, little_endian, truth) fixes the value model. forterp.NATIVE (64-bit, 8-bit ASCII, boolean logicals) is the default; forterp.PDP10 (36-bit, 5×7-bit packed, .TRUE.=−1, bit-wise logicals) is the DEC target; forterp.VAX (32-bit, little-endian, low-bit logical) is a provisional, unvalidated guess. Pass Engine(..., target=...).
  • Front-end dialectforterp.FORTRAN10 (DEC extensions on) vs forterp.F66 (ANSI). Threaded through the source reader and lexer.
  • OPEN deviceseng.register_device(name, handler) plugs in special devices.
  • Unformatted I/O codecforterp.runtime.install_runtime(eng) wires the FOROTS binary-record + DEC-10 float codec used by binary READ/WRITE.

Supported language

Standard FORTRAN-66 (arithmetic/logical/relational expressions, the full control-flow set, DO loops with F66 one-trip semantics, COMMON/EQUIVALENCE storage association, DATA, subprograms + ENTRY, statement functions), formatted + list-directed + unformatted I/O with the complete FORMAT edit-descriptor set, ENCODE/DECODE, and the DEC FORTRAN-10 extensions (octal literals, Hollerith, IAND/IOR/shift intrinsics, tab-format source, random-access READ(u'r)). See docs/.

Examples & demos

Two directories of runnable material:

  • examples/ — short Python scripts showing how to use forterp as a library: running source and capturing output, choosing a dialect or target, feeding input via readline, and reading results back out of COMMON. Start with examples/run_and_capture.py.
  • demos/ — genuine 1970s FORTRAN to run through the interpreter: verbatim netlib numerical libraries (EISPACK, LINPACK, FFT, RKF45) each with a small driver, DECsystem-10 sources recovered from DECUS tapes, and a 1971 Game of Life. Every one is real period source, run as-is — the corpus that flushes out interpreter gaps.

Tests & lint

pip install -e ".[dev]"
pytest
ruff check           # lint (config in pyproject.toml)
ruff format --check  # formatting — run `ruff format` to apply

The suite is the interpreter's unit tests plus the FCVS (FORTRAN Compiler Validation System) conformance corpus — the standard-conformance audits — exercised through the real source-reader → lexer → parser → engine pipeline.

Security & trust model

forterp is an interpreter, not a sandbox. A program it runs executes with the full privileges of the invoking process: FORTRAN OPEN/READ/WRITE reach the real filesystem, and an absolute path or one containing .. reads or writes files outside the save_root base directory. There is no network access, Python eval, or subprocess reachable from a FORTRAN program — but file access alone means you should not run untrusted source expecting containment. To run code you don't trust, confine the process at the OS level (an unprivileged user, a container, a read-only filesystem, seccomp).

Two guards keep an accidental or hostile program from taking down the host, each raising a clean error rather than hanging or OOM-ing: a statement budget (eng.max_steps, default 50M) bounds execution, and max_array_words (default 50M, settable on the Engine / make_engine) bounds array/COMMON allocation — including DATA repetition counts and EQUIVALENCE extension, not just a bare DIMENSION. INCLUDE resolves only within its base directory (the CLI uses the source file's own directory; the library default is the current directory), and its 'FILE/SWITCH' target is split on /, so it cannot escape via an absolute or .. path. These bound resource use; they are not a security sandbox — file access via OPEN is unrestricted, so still confine genuinely untrusted source at the OS level.

The interactive command processor additionally offers a ! shell escape and @file command scripts (not reachable from a running FORTRAN program); these run with your shell's privileges, so treat a command script as trusted input and don't wire the command processor to an untrusted source.

Authorship & attribution

  • Software Architecture, Design & Engineering by Nicholas J. Kisseberth.
  • Code Synthesized via Anthropic Claude Code / Opus 4.8.
  • Automated Code Review via OpenAI Codex / ChatGPT 5.5.

License

© 2026 Nicholas J. Kisseberth · forterp is MIT-licensed.

Project details


Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

forterp-0.1.0.tar.gz (585.7 kB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

forterp-0.1.0-py3-none-any.whl (121.6 kB view details)

Uploaded Python 3

File details

Details for the file forterp-0.1.0.tar.gz.

File metadata

  • Download URL: forterp-0.1.0.tar.gz
  • Upload date:
  • Size: 585.7 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: twine/6.1.0 CPython/3.13.12

File hashes

Hashes for forterp-0.1.0.tar.gz
Algorithm Hash digest
SHA256 ccad8a4aef626c229cd9ccd2f77eb5fe63ef9633bd2081c8be138a3ba7710469
MD5 bc70e4caf92048a5acfbc35495b870c0
BLAKE2b-256 87ae4e1dda995df2664467b34cf021568cea2a1edf0a338bcaac62d88ba4dc09

See more details on using hashes here.

Provenance

The following attestation bundles were made for forterp-0.1.0.tar.gz:

Publisher: release.yml on nyxcraft/forterp

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

File details

Details for the file forterp-0.1.0-py3-none-any.whl.

File metadata

  • Download URL: forterp-0.1.0-py3-none-any.whl
  • Upload date:
  • Size: 121.6 kB
  • Tags: Python 3
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: twine/6.1.0 CPython/3.13.12

File hashes

Hashes for forterp-0.1.0-py3-none-any.whl
Algorithm Hash digest
SHA256 5c071f3a19a6ec33c77baaac779854e91ca0ff50807814fe556fa0be2d892979
MD5 31e65d7fc89d803295d5d86ce7ca4679
BLAKE2b-256 cce76be01231fba9e21af83b46a7eaa44ea938a948c18e079587a0a014a3a7e7

See more details on using hashes here.

Provenance

The following attestation bundles were made for forterp-0.1.0-py3-none-any.whl:

Publisher: release.yml on nyxcraft/forterp

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Pingdom Monitoring Sentry Error logging StatusPage Status page