forterp 0.1.0__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- forterp-0.1.0/CHANGELOG.md +105 -0
- forterp-0.1.0/LICENSE +21 -0
- forterp-0.1.0/MANIFEST.in +18 -0
- forterp-0.1.0/PKG-INFO +255 -0
- forterp-0.1.0/README.md +222 -0
- forterp-0.1.0/demos/ASTRO.FOR +840 -0
- forterp-0.1.0/demos/CHARTR.FOR +973 -0
- forterp-0.1.0/demos/EISPACK.FOR +413 -0
- forterp-0.1.0/demos/EXAMPLE1.FOR +21 -0
- forterp-0.1.0/demos/EXAMPLE2.FOR +18 -0
- forterp-0.1.0/demos/EXAMPLE3.FOR +28 -0
- forterp-0.1.0/demos/EXAMPLE4.FOR +42 -0
- forterp-0.1.0/demos/FFT.FOR +552 -0
- forterp-0.1.0/demos/LIFE.FOR +159 -0
- forterp-0.1.0/demos/LINPACK.FOR +398 -0
- forterp-0.1.0/demos/NORMAL.FOR +65 -0
- forterp-0.1.0/demos/README.md +146 -0
- forterp-0.1.0/demos/RKF45.FOR +622 -0
- forterp-0.1.0/demos/WGMM11.FOR +950 -0
- forterp-0.1.0/demos/WKDAY.FOR +69 -0
- forterp-0.1.0/docs/API.md +435 -0
- forterp-0.1.0/docs/CLI.md +113 -0
- forterp-0.1.0/docs/DESIGN.md +365 -0
- forterp-0.1.0/docs/FORTRAN66.md +374 -0
- forterp-0.1.0/docs/LICENSE.md +28 -0
- forterp-0.1.0/docs/README.md +54 -0
- forterp-0.1.0/examples/README.md +43 -0
- forterp-0.1.0/examples/dialects.py +36 -0
- forterp-0.1.0/examples/fortran_as_kernel.py +32 -0
- forterp-0.1.0/examples/parse_and_inspect.py +32 -0
- forterp-0.1.0/examples/run_and_capture.py +29 -0
- forterp-0.1.0/examples/targets.py +28 -0
- forterp-0.1.0/pyproject.toml +63 -0
- forterp-0.1.0/setup.cfg +4 -0
- forterp-0.1.0/src/forterp/__init__.py +142 -0
- forterp-0.1.0/src/forterp/__main__.py +7 -0
- forterp-0.1.0/src/forterp/ast.py +7 -0
- forterp-0.1.0/src/forterp/ast_nodes.py +259 -0
- forterp-0.1.0/src/forterp/cli.py +219 -0
- forterp-0.1.0/src/forterp/command.py +354 -0
- forterp-0.1.0/src/forterp/debug.py +313 -0
- forterp-0.1.0/src/forterp/diagnostics.py +53 -0
- forterp-0.1.0/src/forterp/dialect.py +74 -0
- forterp-0.1.0/src/forterp/engine.py +2377 -0
- forterp-0.1.0/src/forterp/fmt.py +562 -0
- forterp-0.1.0/src/forterp/forbin.py +259 -0
- forterp-0.1.0/src/forterp/forlib.py +191 -0
- forterp-0.1.0/src/forterp/format.py +9 -0
- forterp-0.1.0/src/forterp/frontend.py +24 -0
- forterp-0.1.0/src/forterp/hostlib.py +529 -0
- forterp-0.1.0/src/forterp/interpreter.py +162 -0
- forterp-0.1.0/src/forterp/lexer.py +234 -0
- forterp-0.1.0/src/forterp/parser.py +1480 -0
- forterp-0.1.0/src/forterp/repl.py +262 -0
- forterp-0.1.0/src/forterp/runtime.py +117 -0
- forterp-0.1.0/src/forterp/source.py +385 -0
- forterp-0.1.0/src/forterp/target.py +153 -0
- forterp-0.1.0/src/forterp/uuolib.py +107 -0
- forterp-0.1.0/src/forterp.egg-info/PKG-INFO +255 -0
- forterp-0.1.0/src/forterp.egg-info/SOURCES.txt +153 -0
- forterp-0.1.0/src/forterp.egg-info/dependency_links.txt +1 -0
- forterp-0.1.0/src/forterp.egg-info/entry_points.txt +4 -0
- forterp-0.1.0/src/forterp.egg-info/requires.txt +7 -0
- forterp-0.1.0/src/forterp.egg-info/top_level.txt +1 -0
- forterp-0.1.0/tests/conftest.py +136 -0
- forterp-0.1.0/tests/fcvs/FM001.FOR +212 -0
- forterp-0.1.0/tests/fcvs/FM002.FOR +334 -0
- forterp-0.1.0/tests/fcvs/FM003.FOR +366 -0
- forterp-0.1.0/tests/fcvs/FM004.FOR +487 -0
- forterp-0.1.0/tests/fcvs/FM005.FOR +396 -0
- forterp-0.1.0/tests/fcvs/FM006.FOR +764 -0
- forterp-0.1.0/tests/fcvs/FM007.FOR +542 -0
- forterp-0.1.0/tests/fcvs/FM008.FOR +863 -0
- forterp-0.1.0/tests/fcvs/FM009.FOR +792 -0
- forterp-0.1.0/tests/fcvs/FM010.FOR +317 -0
- forterp-0.1.0/tests/fcvs/FM011.FOR +295 -0
- forterp-0.1.0/tests/fcvs/FM012.FOR +583 -0
- forterp-0.1.0/tests/fcvs/FM013.FOR +291 -0
- forterp-0.1.0/tests/fcvs/FM014.FOR +253 -0
- forterp-0.1.0/tests/fcvs/FM016.FOR +834 -0
- forterp-0.1.0/tests/fcvs/FM017.FOR +888 -0
- forterp-0.1.0/tests/fcvs/FM018.FOR +899 -0
- forterp-0.1.0/tests/fcvs/FM019.FOR +704 -0
- forterp-0.1.0/tests/fcvs/FM020.FOR +483 -0
- forterp-0.1.0/tests/fcvs/FM021.FOR +1028 -0
- forterp-0.1.0/tests/fcvs/FM022.FOR +812 -0
- forterp-0.1.0/tests/fcvs/FM023.FOR +453 -0
- forterp-0.1.0/tests/fcvs/FM024.FOR +362 -0
- forterp-0.1.0/tests/fcvs/FM025.FOR +439 -0
- forterp-0.1.0/tests/fcvs/FM026.FOR +260 -0
- forterp-0.1.0/tests/fcvs/FM028.FOR +266 -0
- forterp-0.1.0/tests/fcvs/FM030.FOR +849 -0
- forterp-0.1.0/tests/fcvs/FM031.FOR +770 -0
- forterp-0.1.0/tests/fcvs/FM032.FOR +796 -0
- forterp-0.1.0/tests/fcvs/FM033.FOR +842 -0
- forterp-0.1.0/tests/fcvs/FM034.FOR +893 -0
- forterp-0.1.0/tests/fcvs/FM035.FOR +849 -0
- forterp-0.1.0/tests/fcvs/FM036.FOR +724 -0
- forterp-0.1.0/tests/fcvs/FM037.FOR +731 -0
- forterp-0.1.0/tests/fcvs/FM038.FOR +799 -0
- forterp-0.1.0/tests/fcvs/FM039.FOR +782 -0
- forterp-0.1.0/tests/fcvs/FM040.FOR +898 -0
- forterp-0.1.0/tests/fcvs/FM041.FOR +843 -0
- forterp-0.1.0/tests/fcvs/FM042.FOR +876 -0
- forterp-0.1.0/tests/fcvs/FM043.FOR +977 -0
- forterp-0.1.0/tests/fcvs/FM044.FOR +786 -0
- forterp-0.1.0/tests/fcvs/FM045.FOR +478 -0
- forterp-0.1.0/tests/fcvs/FM050.FOR +566 -0
- forterp-0.1.0/tests/fcvs/FM056.FOR +532 -0
- forterp-0.1.0/tests/fcvs/FM060.FOR +854 -0
- forterp-0.1.0/tests/fcvs/FM061.FOR +788 -0
- forterp-0.1.0/tests/fcvs/FM062.FOR +899 -0
- forterp-0.1.0/tests/fcvs/FM080.FOR +721 -0
- forterp-0.1.0/tests/fcvs/FM097.FOR +881 -0
- forterp-0.1.0/tests/fcvs/FM098.FOR +852 -0
- forterp-0.1.0/tests/fcvs/FM099.FOR +721 -0
- forterp-0.1.0/tests/fcvs/FM109.FOR +604 -0
- forterp-0.1.0/tests/fcvs_runner.py +124 -0
- forterp-0.1.0/tests/test_arg_assoc.py +29 -0
- forterp-0.1.0/tests/test_arithmetic.py +101 -0
- forterp-0.1.0/tests/test_cli.py +240 -0
- forterp-0.1.0/tests/test_command.py +144 -0
- forterp-0.1.0/tests/test_complex.py +102 -0
- forterp-0.1.0/tests/test_control_flow.py +123 -0
- forterp-0.1.0/tests/test_conversions.py +63 -0
- forterp-0.1.0/tests/test_debug.py +200 -0
- forterp-0.1.0/tests/test_diagnostics.py +118 -0
- forterp-0.1.0/tests/test_dialect.py +262 -0
- forterp-0.1.0/tests/test_error_handling.py +97 -0
- forterp-0.1.0/tests/test_f66_conformance.py +132 -0
- forterp-0.1.0/tests/test_f66_features.py +425 -0
- forterp-0.1.0/tests/test_fcvs_conformance.py +84 -0
- forterp-0.1.0/tests/test_forbin.py +249 -0
- forterp-0.1.0/tests/test_format.py +280 -0
- forterp-0.1.0/tests/test_format_descriptors.py +107 -0
- forterp-0.1.0/tests/test_free_crlf.py +71 -0
- forterp-0.1.0/tests/test_frontend_v5.py +84 -0
- forterp-0.1.0/tests/test_hostlib.py +372 -0
- forterp-0.1.0/tests/test_interpreter.py +220 -0
- forterp-0.1.0/tests/test_intrinsics.py +48 -0
- forterp-0.1.0/tests/test_io_correctness.py +85 -0
- forterp-0.1.0/tests/test_logical_model.py +78 -0
- forterp-0.1.0/tests/test_math_intrinsics.py +108 -0
- forterp-0.1.0/tests/test_native_target.py +111 -0
- forterp-0.1.0/tests/test_numeric_edges.py +77 -0
- forterp-0.1.0/tests/test_packed_ascii.py +61 -0
- forterp-0.1.0/tests/test_primitives.py +149 -0
- forterp-0.1.0/tests/test_printer_device.py +62 -0
- forterp-0.1.0/tests/test_repl.py +143 -0
- forterp-0.1.0/tests/test_runtime_errors.py +237 -0
- forterp-0.1.0/tests/test_source_reader.py +189 -0
- forterp-0.1.0/tests/test_stdlib_sweep.py +56 -0
- forterp-0.1.0/tests/test_tier1_features.py +392 -0
- forterp-0.1.0/tests/test_uuolib.py +76 -0
- forterp-0.1.0/tests/test_vax_target.py +45 -0
|
@@ -0,0 +1,105 @@
|
|
|
1
|
+
# Changelog
|
|
2
|
+
|
|
3
|
+
## 2026-06-24 — the monitor rename, override docs, and PyPI release wiring
|
|
4
|
+
|
|
5
|
+
- **09:58** — Dropped the `@builtin` decorator alias — `@fcall`/`@uuo` are the two authoring decorators; "builtin" stays the registry noun (`register_builtins`/`builtins_in`/`BUILTINS`). Synced the guides + CHANGELOG to the host-layer work.
|
|
6
|
+
- **10:23** — Reformatted this changelog as reverse-chronological day blocks (newest first), terse timed one-liners.
|
|
7
|
+
- **10:44** — Renamed the interactive shell `Monitor` → `CommandProcessor` (`forterp.command`) and dropped the REPL's `MONITOR` reserved word — freeing "monitor" for the executive facade.
|
|
8
|
+
- **10:59** — Renamed the `@uuo` facade `Host` → `Monitor` (`monitor(eng)`, `eng.monitor`, the `monitor=` builder kwarg) — `mon` now abbreviates its actual type.
|
|
9
|
+
- **11:09** — API guide: how to override the `Monitor` facade with your own (subclass + inject via `monitor=` or `eng.monitor`; the bundled `uuolib` UUOs read the engine seam directly, not the facade).
|
|
10
|
+
- **11:33** — `_load_builtins` no longer leaks `sys.path`/`sys.modules`; the `STR` arg mode strips a packed word's padding so it equals the literal text.
|
|
11
|
+
- **11:38** — Documented the `hostlib.host_ppn`/`host_user` identity helpers alongside `mon.identity`.
|
|
12
|
+
- **11:56** — PyPI release wiring: publish through the `forterp-pypi` environment, with a manual approval gate (the `release` environment + a required reviewer) fronting the tag-triggered publish.
|
|
13
|
+
|
|
14
|
+
## 2026-06-23 — host services, terminal modes, and real binary files
|
|
15
|
+
|
|
16
|
+
- **02:17** — `hostlib` `STR` arg mode: `args=(STR,)` gives the body a Python `str` (a quoted literal verbatim, or a packed word decoded via the target codec), lifting the StrLit-vs-packed resolution out of each body.
|
|
17
|
+
- **04:05** — Real FOROTS binary files (opt-in `Engine(dec_files=True)`): on-disk word↔byte packing + LSCW framing (`forbin`), validated against the V5 D-7/D-8 example byte-for-byte; plus end-to-end host-routine docs (API `@fcall`/`@uuo` + arg modes + `raw`, DESIGN §7a).
|
|
18
|
+
- **09:56** — Terminal echo-control seam (`Engine(set_echo=fn)` + `mon.tty.echo`); renamed the host-services facade `HostServices`/`host_services` → `Host`/`host`.
|
|
19
|
+
- **12:24** — `run_source` installs a default terminal echo (`runtime.default_terminal_echo` flips the tty's termios `ECHO`, restored after); added the autowrap seam (`set_autowrap` + `mon.tty.autowrap`, `TRMOP.` `.TONFC`).
|
|
20
|
+
- **17:50** — Host identity (`mon.identity` — uid/gid/login/PPN); `GETTAB` models the job tables (`.GTPPN` → guest `[0,0]`, `.GTJTC` → 0), with an `eng.gettab` registry and `UnmodeledMonitorTable` for the rest.
|
|
21
|
+
- **21:14** — Review fixes: `OUTSTR` uses the target's `chars_per_word`; restored the `OPEN` read's `with`; a host `builtins=` table no longer shadows a program-defined unit; a stdlib-shadowing `.py` arg gives a clean `?`-diagnostic.
|
|
22
|
+
- **21:52** — Two-word DOUBLE PRECISION binary I/O (the KL10 doubleword, lossless where the single rounded); `_bin_words`/`_assign_words` code per declared type, per element; a config-mismatched binary file fails loud (`OSError`) instead of a garbage text read.
|
|
23
|
+
- **23:29** — FORTRAN-10 free CR-LF: `emit()` wraps terminal output at the carriage width host-side (`Engine(tty_width=80, tty_autowrap=True)`, deferred margin); strict F66 never wraps; CLI `--no-wrap`.
|
|
24
|
+
|
|
25
|
+
## 2026-06-22 — host services for the embedder
|
|
26
|
+
|
|
27
|
+
- **12:33** — `hostlib` host-services half: a baseline `HostServices` facade (`tty`/`files`/`clock`, over the engine's host seam) + an `@uuo` decorator that injects it — the counterpart to `@fcall` for routines that talk to the host; injectable via `eng.host_services`. `@builtin`/`@fcall`/`@uuo` gain `alias=`/`origin=`.
|
|
28
|
+
- **13:17** — CLI `--recover-shifted-cols` (opt-in shifted-column recovery); a dropped-in `*.py` may define a `register(eng)` hook, and `run_source` gains `setup=fn(eng)`.
|
|
29
|
+
- **14:43** — `OPEN` decodes a packed numeric `FILE=`/`NAME=` as a SIXBIT/ASCII filename (as it already does `DEVICE=`), not `str()` of the raw word.
|
|
30
|
+
- **14:53** — `pyfortran10` defaults `--target pdp10` (and `pyf66` stays `native`), matching the prebuilt interpreters.
|
|
31
|
+
- **16:21** — `uuolib`: the standard TOPS-10 monitor UUOs (`OUTSTR`/`OUTCHR`/`MSTIME`/`SLEEP`/`GETTAB`), installed under the FORTRAN-10 dialect on the engine's host seam.
|
|
32
|
+
|
|
33
|
+
## 2026-06-21 — genuine-source demos, then the release-readiness sweep
|
|
34
|
+
|
|
35
|
+
- **16:16** — `demos/`: genuine 1970s FORTRAN run as-is — netlib EISPACK/LINPACK/FFT/RKF45 with drivers, DECUS-tape sources, and Paul Boltwood's 1971 Game of Life.
|
|
36
|
+
- **16:16** — `examples/`: short Python scripts driving forterp as a library.
|
|
37
|
+
- **16:16** — Multi-file linking: several source files linked by unit name, like `f77 main.f lib.f`.
|
|
38
|
+
- **16:16** — Dialect gaps closed (gated; F66 still rejects them): the optional comma before an I/O list, two-word `END FILE`, `DATA` as an array name.
|
|
39
|
+
- **16:16** — `READ`/`ACCEPT` EOF fix: terminal input past end-of-stream branches to `END=` instead of looping.
|
|
40
|
+
- **16:16** — Sequence-association fix: an array element passed where the dummy is an array is re-viewed as a based array (LINPACK/RKF45 work-vector passing).
|
|
41
|
+
- **16:18** — Readability pass: clearer names, smaller focused dispatchers.
|
|
42
|
+
- **16:40** — Error-handling pass: clean `?`-diagnostics in place of raw tracebacks.
|
|
43
|
+
- **17:15** — Trimmed the evaluator hot path (~1.5× on tight loops).
|
|
44
|
+
- **18:14** — Capped a single array/`COMMON` allocation; wrote down the interpreter-not-a-sandbox trust model.
|
|
45
|
+
- **18:26** — Correctness vs `gfortran` (differential testing): per-record carriage control, list-directed grammar, three-digit exponents.
|
|
46
|
+
- **18:52** — Extended the dual-run harness to compare terminal output; pinned the public-API contracts.
|
|
47
|
+
- **20:00** — Packaging & CI: PyPI/distribution readiness — `build` + `twine`, a 3.9–3.13 test matrix.
|
|
48
|
+
- **20:13** — Committed the built docs site, kept in sync automatically by a pre-commit hook.
|
|
49
|
+
- **20:51** — Hardened the resource limits, `INCLUDE` resolution, and duplicate-unit detection.
|
|
50
|
+
- **20:58** — Tag-triggered PyPI release via OIDC Trusted Publishing, gated on tests/lint/format and `tag == __version__`.
|
|
51
|
+
- **21:42** — Docs hygiene: folded the origin history into this changelog; removed the HISTORY/HANDOFF/third-party files; recorded FCVS as public domain; trimmed the overuse of "faithful".
|
|
52
|
+
- **22:41** — ruff maintained at commit time (a pre-commit hook), with import-sort and dead-`noqa` rules.
|
|
53
|
+
- **23:06** — CLI `--version`; moved the engine builders into `forterp.runtime`.
|
|
54
|
+
- **23:23** — Slimmed the package root to exactly `__all__` — dropped the back-compat aliases.
|
|
55
|
+
- **23:33** — `forterp.debug.oob_census()`: a public OOB-access census, so consumers no longer poke engine internals.
|
|
56
|
+
- **23:47** — New docs: a CLI reference and a `forterp.*` API programmer's guide.
|
|
57
|
+
- **23:51** — Docs-site polish: a "Docs" breadcrumb, interpreter-design vocabulary on the home-page pipeline, a high-contrast beta stamp.
|
|
58
|
+
- **23:58** — CLI loads `.py` host-routine modules beside FORTRAN source: a `*.py` argument is imported and its `@builtin` routines are discovered (`hostlib.builtins_in`) and registered — drop them in, no registry/`__init__`.
|
|
59
|
+
|
|
60
|
+
## 2026-06-20 — real-machine defaults, host marshalling, docs site
|
|
61
|
+
|
|
62
|
+
- **00:00** — The `fortran10` preset drops cols 73+ by default; shifted-column recovery is opt-in.
|
|
63
|
+
- **17:03** — `hostlib`: a declarative marshalling layer for host builtins.
|
|
64
|
+
- **23:25** — A GitHub Pages docs site: a `markdown-it-py` static-site generator (`gh-pages/`), Actions deploy.
|
|
65
|
+
|
|
66
|
+
## 2026-06-19 — CLI, monitor, REPL, debugger, conformance
|
|
67
|
+
|
|
68
|
+
- **00:00** — Three console front-ends: `pyf66`, `pyfortran10`, `forterp --std`.
|
|
69
|
+
- **00:48** — Gated the DEC I/O surface, intrinsics, and random-access I/O under F66; added the V5 math/rotate intrinsics.
|
|
70
|
+
- **01:11** — `--check`: parse and list diagnostics without running.
|
|
71
|
+
- **10:16** — An interactive command monitor (a TOPS-10 `.`-prompt descendant).
|
|
72
|
+
- **10:20** — An immediate-mode REPL, then refactored onto two reusable primitives.
|
|
73
|
+
- **11:03** — Factored target/dialect config into shared registries + `engine_kwargs`.
|
|
74
|
+
- **12:31** — An off-by-default per-statement tracer hook; on it, a debugger + profiler.
|
|
75
|
+
- **14:24** — Formatted-input conformance; fixed the random-access write clobber; CLI error hygiene.
|
|
76
|
+
- **21:10** — Exposed the embedding API; added the prebuilt `fortran10` / `f66` interpreters.
|
|
77
|
+
- **21:47** — Gated every non-F66 feature behind a `Dialect` flag; dual-run F66 tests under both dialects.
|
|
78
|
+
- **22:11** — Illegal `EQUIVALENCE` shapes now raise; documented the multi-word storage boundary.
|
|
79
|
+
- **23:06** — list-directed/NAMELIST bad fields raise like formatted; `forbin` rejects unrepresentable floats.
|
|
80
|
+
|
|
81
|
+
## 2026-06-18 — pluggable seams, then standalone
|
|
82
|
+
|
|
83
|
+
- **08:58** — A pluggable `OPEN` device registry.
|
|
84
|
+
- **09:23** — Extracted the machine value model behind a pluggable `Target`.
|
|
85
|
+
- **09:43** — Parameterized the front-end dialect (`Dialect`).
|
|
86
|
+
- **11:23** — A `fortran10` layer atop the `f66` core; moved FOROTS binary I/O into it.
|
|
87
|
+
- **13:44** — **Split out to a standalone repo** — a `src/` package with a clean public API and the FCVS corpus.
|
|
88
|
+
- **14:09** — Routed every wrap/pack/truthy site through `Target` (INT/LSH, the logical algebra, the char codec).
|
|
89
|
+
- **14:47** — Added the `NATIVE` 64-bit target and made it the default; `PDP10` the opt-in machine.
|
|
90
|
+
- **15:19** — A provisional, unvalidated `VAX` target.
|
|
91
|
+
- **15:39** — Curated the FCVS corpus to F66-only (dropped the 140 F77/`CHARACTER` routines).
|
|
92
|
+
- **16:25** — Adopted `ruff` lint + `ruff format`.
|
|
93
|
+
- **23:59** — Renamed `f66` → `forterp`; made `F66` the default dialect.
|
|
94
|
+
|
|
95
|
+
## 2026-06-17 — hardening for the next program
|
|
96
|
+
|
|
97
|
+
- **22:49** — `COMMON` sizing, dummy procedures, continuation comments, lowercase `nH` Hollerith.
|
|
98
|
+
|
|
99
|
+
## 2026-06-16 — and so it begins...
|
|
100
|
+
|
|
101
|
+
- **15:29** — Initial FORTRAN-10 / F66 interpreter: lexer, parser, AST, tree-walking engine, the FORMAT runtime, the `forlib` library, diagnostics.
|
|
102
|
+
- **16:07** — F66 §3.1.6 blanks-insignificance, via a tokenizer parse-retry.
|
|
103
|
+
- **18:49** — `RAN`/`SETRAN`, COMPLEX formatted input, NAMELIST and random-access I/O, `%FTNLID` warnings.
|
|
104
|
+
- **18:51** — FOROTS binary-record codec (LSCW framing + DEC-10 float), `MODE='BINARY'`.
|
|
105
|
+
- **23:12** — Front-end: DEC TAB-format source, the bare main program, integer-vs-`.EQ.` lexing.
|
forterp-0.1.0/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 Nicholas J. Kisseberth
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
# What rides along in the source distribution (the wheel is the package alone).
|
|
2
|
+
#
|
|
3
|
+
# Without this file setuptools' legacy default shipped tests/test_*.py but neither
|
|
4
|
+
# conftest.py (the shared run/out harness) nor tests/fcvs/ (the conformance corpus) --
|
|
5
|
+
# i.e. a test tree that couldn't actually run. Make the sdist a complete, buildable and
|
|
6
|
+
# testable snapshot: the full test suite plus the demo programs and prose docs that the
|
|
7
|
+
# README and docs site point at.
|
|
8
|
+
|
|
9
|
+
graft tests
|
|
10
|
+
graft demos
|
|
11
|
+
graft docs
|
|
12
|
+
graft examples
|
|
13
|
+
include CHANGELOG.md
|
|
14
|
+
|
|
15
|
+
# never ship build/cache droppings
|
|
16
|
+
global-exclude *.py[cod]
|
|
17
|
+
global-exclude .DS_Store
|
|
18
|
+
prune **/__pycache__
|
forterp-0.1.0/PKG-INFO
ADDED
|
@@ -0,0 +1,255 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: forterp
|
|
3
|
+
Version: 0.1.0
|
|
4
|
+
Summary: A FORTRAN-66 / DEC FORTRAN-10 interpreter in Python
|
|
5
|
+
Author-email: "Nicholas J. Kisseberth" <nkissebe@gmail.com>
|
|
6
|
+
License: MIT
|
|
7
|
+
Project-URL: Homepage, https://github.com/nyxcraft/forterp
|
|
8
|
+
Project-URL: Documentation, https://nyxcraft.github.io/forterp/
|
|
9
|
+
Project-URL: Source, https://github.com/nyxcraft/forterp
|
|
10
|
+
Project-URL: Issues, https://github.com/nyxcraft/forterp/issues
|
|
11
|
+
Project-URL: Changelog, https://github.com/nyxcraft/forterp/blob/main/CHANGELOG.md
|
|
12
|
+
Keywords: fortran,fortran66,f66,fortran-10,pdp-10,interpreter,retrocomputing
|
|
13
|
+
Classifier: Development Status :: 4 - Beta
|
|
14
|
+
Classifier: Intended Audience :: Developers
|
|
15
|
+
Classifier: License :: OSI Approved :: MIT License
|
|
16
|
+
Classifier: Programming Language :: Python :: 3
|
|
17
|
+
Classifier: Programming Language :: Python :: 3.9
|
|
18
|
+
Classifier: Programming Language :: Python :: 3.10
|
|
19
|
+
Classifier: Programming Language :: Python :: 3.11
|
|
20
|
+
Classifier: Programming Language :: Python :: 3.12
|
|
21
|
+
Classifier: Programming Language :: Python :: 3.13
|
|
22
|
+
Classifier: Topic :: Software Development :: Interpreters
|
|
23
|
+
Classifier: Topic :: System :: Emulators
|
|
24
|
+
Requires-Python: >=3.9
|
|
25
|
+
Description-Content-Type: text/markdown
|
|
26
|
+
License-File: LICENSE
|
|
27
|
+
Provides-Extra: dev
|
|
28
|
+
Requires-Dist: pytest; extra == "dev"
|
|
29
|
+
Requires-Dist: ruff; extra == "dev"
|
|
30
|
+
Provides-Extra: docs
|
|
31
|
+
Requires-Dist: markdown-it-py; extra == "docs"
|
|
32
|
+
Dynamic: license-file
|
|
33
|
+
|
|
34
|
+
# forterp
|
|
35
|
+
|
|
36
|
+
[](https://github.com/nyxcraft/forterp/actions/workflows/tests.yml)
|
|
37
|
+
[](https://pypi.org/project/forterp/)
|
|
38
|
+
[](https://pypi.org/project/forterp/)
|
|
39
|
+
[](LICENSE)
|
|
40
|
+
|
|
41
|
+
> **Software Architecture, Design & Engineering by Nicholas J. Kisseberth.**
|
|
42
|
+
> Code Synthesized via Anthropic Claude Code / Opus 4.8.
|
|
43
|
+
> Automated Code Review via OpenAI Codex / ChatGPT 5.5.
|
|
44
|
+
|
|
45
|
+
A **configurable FORTRAN-66 interpreter** in Python: the machine value model and the
|
|
46
|
+
front-end dialect are both pluggable, so one core runs FORTRAN against whatever
|
|
47
|
+
representation you select.
|
|
48
|
+
|
|
49
|
+
The value model is a `Target` — integer word width and overflow, the logical-truth
|
|
50
|
+
convention, and how characters pack into words. Two ship:
|
|
51
|
+
|
|
52
|
+
- **`NATIVE` (the default)** — a clean 64-bit host machine for running standard
|
|
53
|
+
FORTRAN-66 portably: 64-bit two's-complement integers, 8-bit ASCII, `.TRUE.`=1 with
|
|
54
|
+
boolean logical operators. `import forterp; forterp.run_source(...)` uses this.
|
|
55
|
+
- **`PDP10`** — the DEC FORTRAN-10 model: 36-bit two's-complement words,
|
|
56
|
+
5×7-bit packed character storage, `.TRUE.`=−1 with bit-wise logicals. Select with
|
|
57
|
+
`Engine(..., target=forterp.PDP10)`.
|
|
58
|
+
|
|
59
|
+
The PDP-10 target was extracted from an interpreter built to run real 1970s DEC FORTRAN
|
|
60
|
+
unmodified, so it is exercised against real period code — not just toy snippets — and
|
|
61
|
+
validated against the DEC FORTRAN-10 V5 manual and the **FCVS** conformance corpus.
|
|
62
|
+
|
|
63
|
+
## Install
|
|
64
|
+
|
|
65
|
+
```sh
|
|
66
|
+
pip install forterp # (once published)
|
|
67
|
+
# or, from a checkout:
|
|
68
|
+
pip install -e .
|
|
69
|
+
```
|
|
70
|
+
|
|
71
|
+
## Quick start
|
|
72
|
+
|
|
73
|
+
```python
|
|
74
|
+
import forterp
|
|
75
|
+
|
|
76
|
+
# The default dialect is strict ANSI F66; FORTRAN10 enables the DEC niceties
|
|
77
|
+
# (here the quoted-string FORMAT -- F66 itself uses Hollerith nH). Note fixed form:
|
|
78
|
+
# the statement label sits in columns 1-5 and the body starts in column 7.
|
|
79
|
+
eng = forterp.run_source(''' PROGRAM HELLO
|
|
80
|
+
WRITE(6,10)
|
|
81
|
+
10 FORMAT(' HELLO, WORLD')
|
|
82
|
+
END
|
|
83
|
+
''', dialect=forterp.FORTRAN10, printer=print)
|
|
84
|
+
```
|
|
85
|
+
|
|
86
|
+
Lower-level building blocks (the expert surface lives under `forterp.runtime`):
|
|
87
|
+
|
|
88
|
+
```python
|
|
89
|
+
units = forterp.parse_source(src) # {name: ProgramUnit}
|
|
90
|
+
eng = forterp.runtime.make_engine(units) # Engine with the FORTRAN-10 runtime installed
|
|
91
|
+
eng.run_program("MAIN") # or run_program() for the first unit
|
|
92
|
+
```
|
|
93
|
+
|
|
94
|
+
The package root exposes only the focused names above; the rest is organized into
|
|
95
|
+
namespaces: `forterp.runtime` (the `Engine` and builders), `forterp.frontend` (lexer/parser
|
|
96
|
+
stages), `forterp.format` (the FORMAT engine), `forterp.ast` (AST nodes), `forterp.hostlib`
|
|
97
|
+
(host-builtin marshalling), and `forterp.debug` (the interactive tracer/profiler and the
|
|
98
|
+
out-of-bounds census — `forterp.debug.oob_census()` counts or logs the faithful unchecked-array
|
|
99
|
+
accesses without changing them).
|
|
100
|
+
|
|
101
|
+
## Command line
|
|
102
|
+
|
|
103
|
+
Installing puts three commands on your PATH — thin dialect front-ends over the engine
|
|
104
|
+
(like `g77`/`gfortran` over gcc):
|
|
105
|
+
|
|
106
|
+
```sh
|
|
107
|
+
pyf66 prog.for # run as strict ANSI FORTRAN-66 (rejects DEC extensions)
|
|
108
|
+
pyfortran10 prog.for # run as DEC FORTRAN-10 (the superset: octal, IMPLICIT, '...', …)
|
|
109
|
+
forterp --std fortran10 prog.for # general driver; --std f66|fortran10 (default: f66)
|
|
110
|
+
```
|
|
111
|
+
|
|
112
|
+
`--target native|pdp10|vax` selects the value model and `--program NAME` picks the main
|
|
113
|
+
unit. `--check` parses and lists every diagnostic without running (a compile-check) — so
|
|
114
|
+
`pyf66 --check prog.for` is a strict-ANSI-F66 conformance linter. `--version` prints the
|
|
115
|
+
version, `--help` the usage. Before install, use `python -m forterp …`.
|
|
116
|
+
|
|
117
|
+
Pass several source files and they are linked together by unit name, the way a compiler
|
|
118
|
+
links separately-compiled units — so a driver and a separately-held library run as one program:
|
|
119
|
+
|
|
120
|
+
```sh
|
|
121
|
+
forterp main.for lib.for # main.for's PROGRAM calls SUBROUTINEs in lib.for
|
|
122
|
+
```
|
|
123
|
+
|
|
124
|
+
Launched with **no file**, each command drops into an interactive command processor (a small,
|
|
125
|
+
FORTRAN-focused descendant of the TOPS-10 `.` prompt — it operates on whole source
|
|
126
|
+
files, not a statement REPL, since F66 has no incremental-execution model):
|
|
127
|
+
|
|
128
|
+
```text
|
|
129
|
+
f66> RUN prog.for # compile + run (alias EXECUTE); CHECK = parse-only
|
|
130
|
+
f66> SET STD fortran10 # switch dialect, target, or main unit between runs
|
|
131
|
+
f10> LOAD prog.for # parse into the session; START runs it
|
|
132
|
+
f10> SHOW /BLOCK/ # inspect a COMMON block after a run; SHOW = settings
|
|
133
|
+
f10> !cmd @file HELP EXIT
|
|
134
|
+
```
|
|
135
|
+
|
|
136
|
+
The command set is identical across the three commands; only the starting dialect
|
|
137
|
+
differs (`pyf66` → f66, `pyfortran10` → fortran10), and `SET STD` flips it.
|
|
138
|
+
|
|
139
|
+
`IMMEDIATE` (alias `REPL`) drops into interactive FORTRAN — statements run as you type,
|
|
140
|
+
a DO loop is collected across lines, and a bare expression is evaluated (so typing a
|
|
141
|
+
name inspects it). After a `LOAD`, the REPL can call straight into the program:
|
|
142
|
+
|
|
143
|
+
```text
|
|
144
|
+
f66* NFAC = 1
|
|
145
|
+
f66* DO 10 I=1,5
|
|
146
|
+
cont> NFAC = NFAC * I
|
|
147
|
+
cont> 10 CONTINUE
|
|
148
|
+
f66* NFAC # -> 120
|
|
149
|
+
f66* ISQ(9) # call a function from the LOADed program -> 81
|
|
150
|
+
f66* 2 + 3 * 4 # calculator -> 14
|
|
151
|
+
```
|
|
152
|
+
|
|
153
|
+
(`COMMON`/`EQUIVALENCE` need a full program unit — put those in a file and `LOAD` it;
|
|
154
|
+
F66 has no incremental model for control flow, so the unit of work is a statement or a
|
|
155
|
+
DO block, never a bare `GOTO`.)
|
|
156
|
+
|
|
157
|
+
The command processor also debugs and profiles a `RUN`/`START`. `BREAK <line>` + `STEP` drop into
|
|
158
|
+
a `(dbg)` prompt where you step (`step`/`next`/`cont`), backtrace (`where`), and inspect
|
|
159
|
+
any expression by typing it; `TRACE` echoes each statement; `PROFILE`/`COVERAGE` report
|
|
160
|
+
per-line execution counts and which lines were reached. The profiler counts *statements*
|
|
161
|
+
(deterministic), not wall-clock seconds. All of it rides one off-by-default hook, so a
|
|
162
|
+
plain run pays nothing:
|
|
163
|
+
|
|
164
|
+
```text
|
|
165
|
+
f66> BREAK 6
|
|
166
|
+
f66> RUN fac.for
|
|
167
|
+
-- stopped at FAC:6 (Assign)
|
|
168
|
+
(dbg) NF # inspect a variable by name -> 1
|
|
169
|
+
(dbg) where # backtrace -> #0 FAC:6
|
|
170
|
+
(dbg) cont
|
|
171
|
+
f66> PROFILE # 5 FAC:6 (the loop body ran 5 times)
|
|
172
|
+
```
|
|
173
|
+
|
|
174
|
+
## What's pluggable
|
|
175
|
+
|
|
176
|
+
- **Machine target** — `forterp.Target(word_bits, chars_per_word, logical_true, bitwise_logic,
|
|
177
|
+
bits_per_char, little_endian, truth)` fixes the value model. `forterp.NATIVE` (64-bit, 8-bit
|
|
178
|
+
ASCII, boolean logicals) is the default; `forterp.PDP10` (36-bit, 5×7-bit packed, `.TRUE.`=−1,
|
|
179
|
+
bit-wise logicals) is the DEC target; `forterp.VAX` (32-bit, little-endian, low-bit
|
|
180
|
+
logical) is a *provisional, unvalidated* guess. Pass `Engine(..., target=...)`.
|
|
181
|
+
- **Front-end dialect** — `forterp.FORTRAN10` (DEC extensions on) vs `forterp.F66`
|
|
182
|
+
(ANSI). Threaded through the source reader and lexer.
|
|
183
|
+
- **OPEN devices** — `eng.register_device(name, handler)` plugs in special devices.
|
|
184
|
+
- **Unformatted I/O codec** — `forterp.runtime.install_runtime(eng)` wires the FOROTS binary-record +
|
|
185
|
+
DEC-10 float codec used by binary `READ`/`WRITE`.
|
|
186
|
+
|
|
187
|
+
## Supported language
|
|
188
|
+
|
|
189
|
+
Standard FORTRAN-66 (arithmetic/logical/relational expressions, the full control-flow
|
|
190
|
+
set, `DO` loops with F66 one-trip semantics, `COMMON`/`EQUIVALENCE` storage association,
|
|
191
|
+
`DATA`, subprograms + `ENTRY`, statement functions), formatted + list-directed +
|
|
192
|
+
unformatted I/O with the complete `FORMAT` edit-descriptor set, `ENCODE`/`DECODE`, and
|
|
193
|
+
the DEC FORTRAN-10 extensions (octal literals, Hollerith, `IAND`/`IOR`/shift intrinsics,
|
|
194
|
+
tab-format source, random-access `READ(u'r)`). See [`docs/`](docs/).
|
|
195
|
+
|
|
196
|
+
## Examples & demos
|
|
197
|
+
|
|
198
|
+
Two directories of runnable material:
|
|
199
|
+
|
|
200
|
+
- **[`examples/`](examples/)** — short Python scripts showing how to *use forterp as a
|
|
201
|
+
library*: running source and capturing output, choosing a dialect or target, feeding
|
|
202
|
+
input via `readline`, and reading results back out of `COMMON`. Start with
|
|
203
|
+
[`examples/run_and_capture.py`](examples/run_and_capture.py).
|
|
204
|
+
- **[`demos/`](demos/)** — genuine 1970s FORTRAN to *run through* the interpreter:
|
|
205
|
+
verbatim netlib numerical libraries (EISPACK, LINPACK, FFT, RKF45) each with a small
|
|
206
|
+
driver, DECsystem-10 sources recovered from DECUS tapes, and a 1971 Game of Life. Every
|
|
207
|
+
one is real period source, run as-is — the corpus that flushes out interpreter gaps.
|
|
208
|
+
|
|
209
|
+
## Tests & lint
|
|
210
|
+
|
|
211
|
+
```sh
|
|
212
|
+
pip install -e ".[dev]"
|
|
213
|
+
pytest
|
|
214
|
+
ruff check # lint (config in pyproject.toml)
|
|
215
|
+
ruff format --check # formatting — run `ruff format` to apply
|
|
216
|
+
```
|
|
217
|
+
|
|
218
|
+
The suite is the interpreter's unit tests plus the **FCVS** (FORTRAN Compiler Validation
|
|
219
|
+
System) conformance corpus — the standard-conformance audits — exercised through the
|
|
220
|
+
real source-reader → lexer → parser → engine pipeline.
|
|
221
|
+
|
|
222
|
+
## Security & trust model
|
|
223
|
+
|
|
224
|
+
forterp is an **interpreter, not a sandbox**. A program it runs executes with the full
|
|
225
|
+
privileges of the invoking process: FORTRAN `OPEN`/`READ`/`WRITE` reach the real
|
|
226
|
+
filesystem, and an absolute path or one containing `..` reads or writes files *outside*
|
|
227
|
+
the `save_root` base directory. There is no network access, Python `eval`, or subprocess
|
|
228
|
+
reachable from a FORTRAN program — but file access alone means **you should not run
|
|
229
|
+
untrusted source expecting containment.** To run code you don't trust, confine the process
|
|
230
|
+
at the OS level (an unprivileged user, a container, a read-only filesystem, seccomp).
|
|
231
|
+
|
|
232
|
+
Two guards keep an accidental or hostile program from taking down the host, each raising a
|
|
233
|
+
clean error rather than hanging or OOM-ing: a statement budget (`eng.max_steps`, default
|
|
234
|
+
50M) bounds execution, and `max_array_words` (default 50M, settable on the `Engine` /
|
|
235
|
+
`make_engine`) bounds array/`COMMON` allocation — including DATA repetition counts and
|
|
236
|
+
EQUIVALENCE extension, not just a bare `DIMENSION`. `INCLUDE` resolves only within its base
|
|
237
|
+
directory (the CLI uses the source file's own directory; the library default is the current
|
|
238
|
+
directory), and its `'FILE/SWITCH'` target is split on `/`, so it cannot escape via an
|
|
239
|
+
absolute or `..` path. These bound resource use; they are not a security sandbox — file
|
|
240
|
+
access via `OPEN` is unrestricted, so still confine genuinely untrusted source at the OS
|
|
241
|
+
level.
|
|
242
|
+
|
|
243
|
+
The interactive command processor additionally offers a `!` shell escape and `@file` command scripts
|
|
244
|
+
(not reachable from a running FORTRAN program); these run with your shell's privileges, so
|
|
245
|
+
treat a command script as trusted input and don't wire the command processor to an untrusted source.
|
|
246
|
+
|
|
247
|
+
## Authorship & attribution
|
|
248
|
+
|
|
249
|
+
- Software Architecture, Design & Engineering by Nicholas J. Kisseberth.
|
|
250
|
+
- Code Synthesized via Anthropic Claude Code / Opus 4.8.
|
|
251
|
+
- Automated Code Review via OpenAI Codex / ChatGPT 5.5.
|
|
252
|
+
|
|
253
|
+
## License
|
|
254
|
+
|
|
255
|
+
© 2026 Nicholas J. Kisseberth · forterp is [MIT-licensed](LICENSE).
|
forterp-0.1.0/README.md
ADDED
|
@@ -0,0 +1,222 @@
|
|
|
1
|
+
# forterp
|
|
2
|
+
|
|
3
|
+
[](https://github.com/nyxcraft/forterp/actions/workflows/tests.yml)
|
|
4
|
+
[](https://pypi.org/project/forterp/)
|
|
5
|
+
[](https://pypi.org/project/forterp/)
|
|
6
|
+
[](LICENSE)
|
|
7
|
+
|
|
8
|
+
> **Software Architecture, Design & Engineering by Nicholas J. Kisseberth.**
|
|
9
|
+
> Code Synthesized via Anthropic Claude Code / Opus 4.8.
|
|
10
|
+
> Automated Code Review via OpenAI Codex / ChatGPT 5.5.
|
|
11
|
+
|
|
12
|
+
A **configurable FORTRAN-66 interpreter** in Python: the machine value model and the
|
|
13
|
+
front-end dialect are both pluggable, so one core runs FORTRAN against whatever
|
|
14
|
+
representation you select.
|
|
15
|
+
|
|
16
|
+
The value model is a `Target` — integer word width and overflow, the logical-truth
|
|
17
|
+
convention, and how characters pack into words. Two ship:
|
|
18
|
+
|
|
19
|
+
- **`NATIVE` (the default)** — a clean 64-bit host machine for running standard
|
|
20
|
+
FORTRAN-66 portably: 64-bit two's-complement integers, 8-bit ASCII, `.TRUE.`=1 with
|
|
21
|
+
boolean logical operators. `import forterp; forterp.run_source(...)` uses this.
|
|
22
|
+
- **`PDP10`** — the DEC FORTRAN-10 model: 36-bit two's-complement words,
|
|
23
|
+
5×7-bit packed character storage, `.TRUE.`=−1 with bit-wise logicals. Select with
|
|
24
|
+
`Engine(..., target=forterp.PDP10)`.
|
|
25
|
+
|
|
26
|
+
The PDP-10 target was extracted from an interpreter built to run real 1970s DEC FORTRAN
|
|
27
|
+
unmodified, so it is exercised against real period code — not just toy snippets — and
|
|
28
|
+
validated against the DEC FORTRAN-10 V5 manual and the **FCVS** conformance corpus.
|
|
29
|
+
|
|
30
|
+
## Install
|
|
31
|
+
|
|
32
|
+
```sh
|
|
33
|
+
pip install forterp # (once published)
|
|
34
|
+
# or, from a checkout:
|
|
35
|
+
pip install -e .
|
|
36
|
+
```
|
|
37
|
+
|
|
38
|
+
## Quick start
|
|
39
|
+
|
|
40
|
+
```python
|
|
41
|
+
import forterp
|
|
42
|
+
|
|
43
|
+
# The default dialect is strict ANSI F66; FORTRAN10 enables the DEC niceties
|
|
44
|
+
# (here the quoted-string FORMAT -- F66 itself uses Hollerith nH). Note fixed form:
|
|
45
|
+
# the statement label sits in columns 1-5 and the body starts in column 7.
|
|
46
|
+
eng = forterp.run_source(''' PROGRAM HELLO
|
|
47
|
+
WRITE(6,10)
|
|
48
|
+
10 FORMAT(' HELLO, WORLD')
|
|
49
|
+
END
|
|
50
|
+
''', dialect=forterp.FORTRAN10, printer=print)
|
|
51
|
+
```
|
|
52
|
+
|
|
53
|
+
Lower-level building blocks (the expert surface lives under `forterp.runtime`):
|
|
54
|
+
|
|
55
|
+
```python
|
|
56
|
+
units = forterp.parse_source(src) # {name: ProgramUnit}
|
|
57
|
+
eng = forterp.runtime.make_engine(units) # Engine with the FORTRAN-10 runtime installed
|
|
58
|
+
eng.run_program("MAIN") # or run_program() for the first unit
|
|
59
|
+
```
|
|
60
|
+
|
|
61
|
+
The package root exposes only the focused names above; the rest is organized into
|
|
62
|
+
namespaces: `forterp.runtime` (the `Engine` and builders), `forterp.frontend` (lexer/parser
|
|
63
|
+
stages), `forterp.format` (the FORMAT engine), `forterp.ast` (AST nodes), `forterp.hostlib`
|
|
64
|
+
(host-builtin marshalling), and `forterp.debug` (the interactive tracer/profiler and the
|
|
65
|
+
out-of-bounds census — `forterp.debug.oob_census()` counts or logs the faithful unchecked-array
|
|
66
|
+
accesses without changing them).
|
|
67
|
+
|
|
68
|
+
## Command line
|
|
69
|
+
|
|
70
|
+
Installing puts three commands on your PATH — thin dialect front-ends over the engine
|
|
71
|
+
(like `g77`/`gfortran` over gcc):
|
|
72
|
+
|
|
73
|
+
```sh
|
|
74
|
+
pyf66 prog.for # run as strict ANSI FORTRAN-66 (rejects DEC extensions)
|
|
75
|
+
pyfortran10 prog.for # run as DEC FORTRAN-10 (the superset: octal, IMPLICIT, '...', …)
|
|
76
|
+
forterp --std fortran10 prog.for # general driver; --std f66|fortran10 (default: f66)
|
|
77
|
+
```
|
|
78
|
+
|
|
79
|
+
`--target native|pdp10|vax` selects the value model and `--program NAME` picks the main
|
|
80
|
+
unit. `--check` parses and lists every diagnostic without running (a compile-check) — so
|
|
81
|
+
`pyf66 --check prog.for` is a strict-ANSI-F66 conformance linter. `--version` prints the
|
|
82
|
+
version, `--help` the usage. Before install, use `python -m forterp …`.
|
|
83
|
+
|
|
84
|
+
Pass several source files and they are linked together by unit name, the way a compiler
|
|
85
|
+
links separately-compiled units — so a driver and a separately-held library run as one program:
|
|
86
|
+
|
|
87
|
+
```sh
|
|
88
|
+
forterp main.for lib.for # main.for's PROGRAM calls SUBROUTINEs in lib.for
|
|
89
|
+
```
|
|
90
|
+
|
|
91
|
+
Launched with **no file**, each command drops into an interactive command processor (a small,
|
|
92
|
+
FORTRAN-focused descendant of the TOPS-10 `.` prompt — it operates on whole source
|
|
93
|
+
files, not a statement REPL, since F66 has no incremental-execution model):
|
|
94
|
+
|
|
95
|
+
```text
|
|
96
|
+
f66> RUN prog.for # compile + run (alias EXECUTE); CHECK = parse-only
|
|
97
|
+
f66> SET STD fortran10 # switch dialect, target, or main unit between runs
|
|
98
|
+
f10> LOAD prog.for # parse into the session; START runs it
|
|
99
|
+
f10> SHOW /BLOCK/ # inspect a COMMON block after a run; SHOW = settings
|
|
100
|
+
f10> !cmd @file HELP EXIT
|
|
101
|
+
```
|
|
102
|
+
|
|
103
|
+
The command set is identical across the three commands; only the starting dialect
|
|
104
|
+
differs (`pyf66` → f66, `pyfortran10` → fortran10), and `SET STD` flips it.
|
|
105
|
+
|
|
106
|
+
`IMMEDIATE` (alias `REPL`) drops into interactive FORTRAN — statements run as you type,
|
|
107
|
+
a DO loop is collected across lines, and a bare expression is evaluated (so typing a
|
|
108
|
+
name inspects it). After a `LOAD`, the REPL can call straight into the program:
|
|
109
|
+
|
|
110
|
+
```text
|
|
111
|
+
f66* NFAC = 1
|
|
112
|
+
f66* DO 10 I=1,5
|
|
113
|
+
cont> NFAC = NFAC * I
|
|
114
|
+
cont> 10 CONTINUE
|
|
115
|
+
f66* NFAC # -> 120
|
|
116
|
+
f66* ISQ(9) # call a function from the LOADed program -> 81
|
|
117
|
+
f66* 2 + 3 * 4 # calculator -> 14
|
|
118
|
+
```
|
|
119
|
+
|
|
120
|
+
(`COMMON`/`EQUIVALENCE` need a full program unit — put those in a file and `LOAD` it;
|
|
121
|
+
F66 has no incremental model for control flow, so the unit of work is a statement or a
|
|
122
|
+
DO block, never a bare `GOTO`.)
|
|
123
|
+
|
|
124
|
+
The command processor also debugs and profiles a `RUN`/`START`. `BREAK <line>` + `STEP` drop into
|
|
125
|
+
a `(dbg)` prompt where you step (`step`/`next`/`cont`), backtrace (`where`), and inspect
|
|
126
|
+
any expression by typing it; `TRACE` echoes each statement; `PROFILE`/`COVERAGE` report
|
|
127
|
+
per-line execution counts and which lines were reached. The profiler counts *statements*
|
|
128
|
+
(deterministic), not wall-clock seconds. All of it rides one off-by-default hook, so a
|
|
129
|
+
plain run pays nothing:
|
|
130
|
+
|
|
131
|
+
```text
|
|
132
|
+
f66> BREAK 6
|
|
133
|
+
f66> RUN fac.for
|
|
134
|
+
-- stopped at FAC:6 (Assign)
|
|
135
|
+
(dbg) NF # inspect a variable by name -> 1
|
|
136
|
+
(dbg) where # backtrace -> #0 FAC:6
|
|
137
|
+
(dbg) cont
|
|
138
|
+
f66> PROFILE # 5 FAC:6 (the loop body ran 5 times)
|
|
139
|
+
```
|
|
140
|
+
|
|
141
|
+
## What's pluggable
|
|
142
|
+
|
|
143
|
+
- **Machine target** — `forterp.Target(word_bits, chars_per_word, logical_true, bitwise_logic,
|
|
144
|
+
bits_per_char, little_endian, truth)` fixes the value model. `forterp.NATIVE` (64-bit, 8-bit
|
|
145
|
+
ASCII, boolean logicals) is the default; `forterp.PDP10` (36-bit, 5×7-bit packed, `.TRUE.`=−1,
|
|
146
|
+
bit-wise logicals) is the DEC target; `forterp.VAX` (32-bit, little-endian, low-bit
|
|
147
|
+
logical) is a *provisional, unvalidated* guess. Pass `Engine(..., target=...)`.
|
|
148
|
+
- **Front-end dialect** — `forterp.FORTRAN10` (DEC extensions on) vs `forterp.F66`
|
|
149
|
+
(ANSI). Threaded through the source reader and lexer.
|
|
150
|
+
- **OPEN devices** — `eng.register_device(name, handler)` plugs in special devices.
|
|
151
|
+
- **Unformatted I/O codec** — `forterp.runtime.install_runtime(eng)` wires the FOROTS binary-record +
|
|
152
|
+
DEC-10 float codec used by binary `READ`/`WRITE`.
|
|
153
|
+
|
|
154
|
+
## Supported language
|
|
155
|
+
|
|
156
|
+
Standard FORTRAN-66 (arithmetic/logical/relational expressions, the full control-flow
|
|
157
|
+
set, `DO` loops with F66 one-trip semantics, `COMMON`/`EQUIVALENCE` storage association,
|
|
158
|
+
`DATA`, subprograms + `ENTRY`, statement functions), formatted + list-directed +
|
|
159
|
+
unformatted I/O with the complete `FORMAT` edit-descriptor set, `ENCODE`/`DECODE`, and
|
|
160
|
+
the DEC FORTRAN-10 extensions (octal literals, Hollerith, `IAND`/`IOR`/shift intrinsics,
|
|
161
|
+
tab-format source, random-access `READ(u'r)`). See [`docs/`](docs/).
|
|
162
|
+
|
|
163
|
+
## Examples & demos
|
|
164
|
+
|
|
165
|
+
Two directories of runnable material:
|
|
166
|
+
|
|
167
|
+
- **[`examples/`](examples/)** — short Python scripts showing how to *use forterp as a
|
|
168
|
+
library*: running source and capturing output, choosing a dialect or target, feeding
|
|
169
|
+
input via `readline`, and reading results back out of `COMMON`. Start with
|
|
170
|
+
[`examples/run_and_capture.py`](examples/run_and_capture.py).
|
|
171
|
+
- **[`demos/`](demos/)** — genuine 1970s FORTRAN to *run through* the interpreter:
|
|
172
|
+
verbatim netlib numerical libraries (EISPACK, LINPACK, FFT, RKF45) each with a small
|
|
173
|
+
driver, DECsystem-10 sources recovered from DECUS tapes, and a 1971 Game of Life. Every
|
|
174
|
+
one is real period source, run as-is — the corpus that flushes out interpreter gaps.
|
|
175
|
+
|
|
176
|
+
## Tests & lint
|
|
177
|
+
|
|
178
|
+
```sh
|
|
179
|
+
pip install -e ".[dev]"
|
|
180
|
+
pytest
|
|
181
|
+
ruff check # lint (config in pyproject.toml)
|
|
182
|
+
ruff format --check # formatting — run `ruff format` to apply
|
|
183
|
+
```
|
|
184
|
+
|
|
185
|
+
The suite is the interpreter's unit tests plus the **FCVS** (FORTRAN Compiler Validation
|
|
186
|
+
System) conformance corpus — the standard-conformance audits — exercised through the
|
|
187
|
+
real source-reader → lexer → parser → engine pipeline.
|
|
188
|
+
|
|
189
|
+
## Security & trust model
|
|
190
|
+
|
|
191
|
+
forterp is an **interpreter, not a sandbox**. A program it runs executes with the full
|
|
192
|
+
privileges of the invoking process: FORTRAN `OPEN`/`READ`/`WRITE` reach the real
|
|
193
|
+
filesystem, and an absolute path or one containing `..` reads or writes files *outside*
|
|
194
|
+
the `save_root` base directory. There is no network access, Python `eval`, or subprocess
|
|
195
|
+
reachable from a FORTRAN program — but file access alone means **you should not run
|
|
196
|
+
untrusted source expecting containment.** To run code you don't trust, confine the process
|
|
197
|
+
at the OS level (an unprivileged user, a container, a read-only filesystem, seccomp).
|
|
198
|
+
|
|
199
|
+
Two guards keep an accidental or hostile program from taking down the host, each raising a
|
|
200
|
+
clean error rather than hanging or OOM-ing: a statement budget (`eng.max_steps`, default
|
|
201
|
+
50M) bounds execution, and `max_array_words` (default 50M, settable on the `Engine` /
|
|
202
|
+
`make_engine`) bounds array/`COMMON` allocation — including DATA repetition counts and
|
|
203
|
+
EQUIVALENCE extension, not just a bare `DIMENSION`. `INCLUDE` resolves only within its base
|
|
204
|
+
directory (the CLI uses the source file's own directory; the library default is the current
|
|
205
|
+
directory), and its `'FILE/SWITCH'` target is split on `/`, so it cannot escape via an
|
|
206
|
+
absolute or `..` path. These bound resource use; they are not a security sandbox — file
|
|
207
|
+
access via `OPEN` is unrestricted, so still confine genuinely untrusted source at the OS
|
|
208
|
+
level.
|
|
209
|
+
|
|
210
|
+
The interactive command processor additionally offers a `!` shell escape and `@file` command scripts
|
|
211
|
+
(not reachable from a running FORTRAN program); these run with your shell's privileges, so
|
|
212
|
+
treat a command script as trusted input and don't wire the command processor to an untrusted source.
|
|
213
|
+
|
|
214
|
+
## Authorship & attribution
|
|
215
|
+
|
|
216
|
+
- Software Architecture, Design & Engineering by Nicholas J. Kisseberth.
|
|
217
|
+
- Code Synthesized via Anthropic Claude Code / Opus 4.8.
|
|
218
|
+
- Automated Code Review via OpenAI Codex / ChatGPT 5.5.
|
|
219
|
+
|
|
220
|
+
## License
|
|
221
|
+
|
|
222
|
+
© 2026 Nicholas J. Kisseberth · forterp is [MIT-licensed](LICENSE).
|