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.
Files changed (155) hide show
  1. forterp-0.1.0/CHANGELOG.md +105 -0
  2. forterp-0.1.0/LICENSE +21 -0
  3. forterp-0.1.0/MANIFEST.in +18 -0
  4. forterp-0.1.0/PKG-INFO +255 -0
  5. forterp-0.1.0/README.md +222 -0
  6. forterp-0.1.0/demos/ASTRO.FOR +840 -0
  7. forterp-0.1.0/demos/CHARTR.FOR +973 -0
  8. forterp-0.1.0/demos/EISPACK.FOR +413 -0
  9. forterp-0.1.0/demos/EXAMPLE1.FOR +21 -0
  10. forterp-0.1.0/demos/EXAMPLE2.FOR +18 -0
  11. forterp-0.1.0/demos/EXAMPLE3.FOR +28 -0
  12. forterp-0.1.0/demos/EXAMPLE4.FOR +42 -0
  13. forterp-0.1.0/demos/FFT.FOR +552 -0
  14. forterp-0.1.0/demos/LIFE.FOR +159 -0
  15. forterp-0.1.0/demos/LINPACK.FOR +398 -0
  16. forterp-0.1.0/demos/NORMAL.FOR +65 -0
  17. forterp-0.1.0/demos/README.md +146 -0
  18. forterp-0.1.0/demos/RKF45.FOR +622 -0
  19. forterp-0.1.0/demos/WGMM11.FOR +950 -0
  20. forterp-0.1.0/demos/WKDAY.FOR +69 -0
  21. forterp-0.1.0/docs/API.md +435 -0
  22. forterp-0.1.0/docs/CLI.md +113 -0
  23. forterp-0.1.0/docs/DESIGN.md +365 -0
  24. forterp-0.1.0/docs/FORTRAN66.md +374 -0
  25. forterp-0.1.0/docs/LICENSE.md +28 -0
  26. forterp-0.1.0/docs/README.md +54 -0
  27. forterp-0.1.0/examples/README.md +43 -0
  28. forterp-0.1.0/examples/dialects.py +36 -0
  29. forterp-0.1.0/examples/fortran_as_kernel.py +32 -0
  30. forterp-0.1.0/examples/parse_and_inspect.py +32 -0
  31. forterp-0.1.0/examples/run_and_capture.py +29 -0
  32. forterp-0.1.0/examples/targets.py +28 -0
  33. forterp-0.1.0/pyproject.toml +63 -0
  34. forterp-0.1.0/setup.cfg +4 -0
  35. forterp-0.1.0/src/forterp/__init__.py +142 -0
  36. forterp-0.1.0/src/forterp/__main__.py +7 -0
  37. forterp-0.1.0/src/forterp/ast.py +7 -0
  38. forterp-0.1.0/src/forterp/ast_nodes.py +259 -0
  39. forterp-0.1.0/src/forterp/cli.py +219 -0
  40. forterp-0.1.0/src/forterp/command.py +354 -0
  41. forterp-0.1.0/src/forterp/debug.py +313 -0
  42. forterp-0.1.0/src/forterp/diagnostics.py +53 -0
  43. forterp-0.1.0/src/forterp/dialect.py +74 -0
  44. forterp-0.1.0/src/forterp/engine.py +2377 -0
  45. forterp-0.1.0/src/forterp/fmt.py +562 -0
  46. forterp-0.1.0/src/forterp/forbin.py +259 -0
  47. forterp-0.1.0/src/forterp/forlib.py +191 -0
  48. forterp-0.1.0/src/forterp/format.py +9 -0
  49. forterp-0.1.0/src/forterp/frontend.py +24 -0
  50. forterp-0.1.0/src/forterp/hostlib.py +529 -0
  51. forterp-0.1.0/src/forterp/interpreter.py +162 -0
  52. forterp-0.1.0/src/forterp/lexer.py +234 -0
  53. forterp-0.1.0/src/forterp/parser.py +1480 -0
  54. forterp-0.1.0/src/forterp/repl.py +262 -0
  55. forterp-0.1.0/src/forterp/runtime.py +117 -0
  56. forterp-0.1.0/src/forterp/source.py +385 -0
  57. forterp-0.1.0/src/forterp/target.py +153 -0
  58. forterp-0.1.0/src/forterp/uuolib.py +107 -0
  59. forterp-0.1.0/src/forterp.egg-info/PKG-INFO +255 -0
  60. forterp-0.1.0/src/forterp.egg-info/SOURCES.txt +153 -0
  61. forterp-0.1.0/src/forterp.egg-info/dependency_links.txt +1 -0
  62. forterp-0.1.0/src/forterp.egg-info/entry_points.txt +4 -0
  63. forterp-0.1.0/src/forterp.egg-info/requires.txt +7 -0
  64. forterp-0.1.0/src/forterp.egg-info/top_level.txt +1 -0
  65. forterp-0.1.0/tests/conftest.py +136 -0
  66. forterp-0.1.0/tests/fcvs/FM001.FOR +212 -0
  67. forterp-0.1.0/tests/fcvs/FM002.FOR +334 -0
  68. forterp-0.1.0/tests/fcvs/FM003.FOR +366 -0
  69. forterp-0.1.0/tests/fcvs/FM004.FOR +487 -0
  70. forterp-0.1.0/tests/fcvs/FM005.FOR +396 -0
  71. forterp-0.1.0/tests/fcvs/FM006.FOR +764 -0
  72. forterp-0.1.0/tests/fcvs/FM007.FOR +542 -0
  73. forterp-0.1.0/tests/fcvs/FM008.FOR +863 -0
  74. forterp-0.1.0/tests/fcvs/FM009.FOR +792 -0
  75. forterp-0.1.0/tests/fcvs/FM010.FOR +317 -0
  76. forterp-0.1.0/tests/fcvs/FM011.FOR +295 -0
  77. forterp-0.1.0/tests/fcvs/FM012.FOR +583 -0
  78. forterp-0.1.0/tests/fcvs/FM013.FOR +291 -0
  79. forterp-0.1.0/tests/fcvs/FM014.FOR +253 -0
  80. forterp-0.1.0/tests/fcvs/FM016.FOR +834 -0
  81. forterp-0.1.0/tests/fcvs/FM017.FOR +888 -0
  82. forterp-0.1.0/tests/fcvs/FM018.FOR +899 -0
  83. forterp-0.1.0/tests/fcvs/FM019.FOR +704 -0
  84. forterp-0.1.0/tests/fcvs/FM020.FOR +483 -0
  85. forterp-0.1.0/tests/fcvs/FM021.FOR +1028 -0
  86. forterp-0.1.0/tests/fcvs/FM022.FOR +812 -0
  87. forterp-0.1.0/tests/fcvs/FM023.FOR +453 -0
  88. forterp-0.1.0/tests/fcvs/FM024.FOR +362 -0
  89. forterp-0.1.0/tests/fcvs/FM025.FOR +439 -0
  90. forterp-0.1.0/tests/fcvs/FM026.FOR +260 -0
  91. forterp-0.1.0/tests/fcvs/FM028.FOR +266 -0
  92. forterp-0.1.0/tests/fcvs/FM030.FOR +849 -0
  93. forterp-0.1.0/tests/fcvs/FM031.FOR +770 -0
  94. forterp-0.1.0/tests/fcvs/FM032.FOR +796 -0
  95. forterp-0.1.0/tests/fcvs/FM033.FOR +842 -0
  96. forterp-0.1.0/tests/fcvs/FM034.FOR +893 -0
  97. forterp-0.1.0/tests/fcvs/FM035.FOR +849 -0
  98. forterp-0.1.0/tests/fcvs/FM036.FOR +724 -0
  99. forterp-0.1.0/tests/fcvs/FM037.FOR +731 -0
  100. forterp-0.1.0/tests/fcvs/FM038.FOR +799 -0
  101. forterp-0.1.0/tests/fcvs/FM039.FOR +782 -0
  102. forterp-0.1.0/tests/fcvs/FM040.FOR +898 -0
  103. forterp-0.1.0/tests/fcvs/FM041.FOR +843 -0
  104. forterp-0.1.0/tests/fcvs/FM042.FOR +876 -0
  105. forterp-0.1.0/tests/fcvs/FM043.FOR +977 -0
  106. forterp-0.1.0/tests/fcvs/FM044.FOR +786 -0
  107. forterp-0.1.0/tests/fcvs/FM045.FOR +478 -0
  108. forterp-0.1.0/tests/fcvs/FM050.FOR +566 -0
  109. forterp-0.1.0/tests/fcvs/FM056.FOR +532 -0
  110. forterp-0.1.0/tests/fcvs/FM060.FOR +854 -0
  111. forterp-0.1.0/tests/fcvs/FM061.FOR +788 -0
  112. forterp-0.1.0/tests/fcvs/FM062.FOR +899 -0
  113. forterp-0.1.0/tests/fcvs/FM080.FOR +721 -0
  114. forterp-0.1.0/tests/fcvs/FM097.FOR +881 -0
  115. forterp-0.1.0/tests/fcvs/FM098.FOR +852 -0
  116. forterp-0.1.0/tests/fcvs/FM099.FOR +721 -0
  117. forterp-0.1.0/tests/fcvs/FM109.FOR +604 -0
  118. forterp-0.1.0/tests/fcvs_runner.py +124 -0
  119. forterp-0.1.0/tests/test_arg_assoc.py +29 -0
  120. forterp-0.1.0/tests/test_arithmetic.py +101 -0
  121. forterp-0.1.0/tests/test_cli.py +240 -0
  122. forterp-0.1.0/tests/test_command.py +144 -0
  123. forterp-0.1.0/tests/test_complex.py +102 -0
  124. forterp-0.1.0/tests/test_control_flow.py +123 -0
  125. forterp-0.1.0/tests/test_conversions.py +63 -0
  126. forterp-0.1.0/tests/test_debug.py +200 -0
  127. forterp-0.1.0/tests/test_diagnostics.py +118 -0
  128. forterp-0.1.0/tests/test_dialect.py +262 -0
  129. forterp-0.1.0/tests/test_error_handling.py +97 -0
  130. forterp-0.1.0/tests/test_f66_conformance.py +132 -0
  131. forterp-0.1.0/tests/test_f66_features.py +425 -0
  132. forterp-0.1.0/tests/test_fcvs_conformance.py +84 -0
  133. forterp-0.1.0/tests/test_forbin.py +249 -0
  134. forterp-0.1.0/tests/test_format.py +280 -0
  135. forterp-0.1.0/tests/test_format_descriptors.py +107 -0
  136. forterp-0.1.0/tests/test_free_crlf.py +71 -0
  137. forterp-0.1.0/tests/test_frontend_v5.py +84 -0
  138. forterp-0.1.0/tests/test_hostlib.py +372 -0
  139. forterp-0.1.0/tests/test_interpreter.py +220 -0
  140. forterp-0.1.0/tests/test_intrinsics.py +48 -0
  141. forterp-0.1.0/tests/test_io_correctness.py +85 -0
  142. forterp-0.1.0/tests/test_logical_model.py +78 -0
  143. forterp-0.1.0/tests/test_math_intrinsics.py +108 -0
  144. forterp-0.1.0/tests/test_native_target.py +111 -0
  145. forterp-0.1.0/tests/test_numeric_edges.py +77 -0
  146. forterp-0.1.0/tests/test_packed_ascii.py +61 -0
  147. forterp-0.1.0/tests/test_primitives.py +149 -0
  148. forterp-0.1.0/tests/test_printer_device.py +62 -0
  149. forterp-0.1.0/tests/test_repl.py +143 -0
  150. forterp-0.1.0/tests/test_runtime_errors.py +237 -0
  151. forterp-0.1.0/tests/test_source_reader.py +189 -0
  152. forterp-0.1.0/tests/test_stdlib_sweep.py +56 -0
  153. forterp-0.1.0/tests/test_tier1_features.py +392 -0
  154. forterp-0.1.0/tests/test_uuolib.py +76 -0
  155. 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
+ [![Tests](https://github.com/nyxcraft/forterp/actions/workflows/tests.yml/badge.svg)](https://github.com/nyxcraft/forterp/actions/workflows/tests.yml)
37
+ [![PyPI](https://img.shields.io/pypi/v/forterp.svg)](https://pypi.org/project/forterp/)
38
+ [![Python versions](https://img.shields.io/pypi/pyversions/forterp.svg)](https://pypi.org/project/forterp/)
39
+ [![License: MIT](https://img.shields.io/badge/License-MIT-blue.svg)](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).
@@ -0,0 +1,222 @@
1
+ # forterp
2
+
3
+ [![Tests](https://github.com/nyxcraft/forterp/actions/workflows/tests.yml/badge.svg)](https://github.com/nyxcraft/forterp/actions/workflows/tests.yml)
4
+ [![PyPI](https://img.shields.io/pypi/v/forterp.svg)](https://pypi.org/project/forterp/)
5
+ [![Python versions](https://img.shields.io/pypi/pyversions/forterp.svg)](https://pypi.org/project/forterp/)
6
+ [![License: MIT](https://img.shields.io/badge/License-MIT-blue.svg)](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).