From 41abad0159c162a34444e1df5d3e41d33098309a Mon Sep 17 00:00:00 2001 From: Claude Date: Tue, 10 Nov 2026 11:21:32 +0000 Subject: [PATCH 01/10] docs: The Book of Fabric -- the FABRIC design record as one book, built and published with every release (#138) [skip ci] `make fabric` -> build/docs/BookOfFabric.pdf (722 pages): Part I the living record (FABRIC-3), Part II the closed documents in the order written, Part III the forward-looking notes; its title page carries the version (git describe --tags: a release's tag) and the commit. Built from the same pandoc fragments as the book; the book's preamble moved into docs/book/preamble.tex, shared by both (the book builds as before, 821 pages). scripts/nightly.sh: the docs stage builds it ($DOCS/fabric.pdf); publish sends it as classifier `fabric`; gitea-release attaches every PDF in $DOCS, so every release carries it. Progress for the rest of alpha.4's documents: docs/v4.0.0/plans/2026-11-10-alpha4-documents.md. Co-Authored-By: Claude Opus 5.5 Claude-Session: https://claude.ai/code/session_01GE3SDvQn5EoxdGufk84zz2 --- Makefile | 6 +- docs/book/Makefile | 35 ++++++- docs/book/fabric.tex | 69 +++++++++++++ docs/book/main.tex | 94 +----------------- docs/book/preamble.tex | 97 +++++++++++++++++++ .../plans/2026-11-10-alpha4-documents.md | 26 +++++ scripts/nightly.sh | 9 +- 7 files changed, 234 insertions(+), 102 deletions(-) create mode 100644 docs/book/fabric.tex create mode 100644 docs/book/preamble.tex create mode 100644 docs/v4.0.0/plans/2026-11-10-alpha4-documents.md diff --git a/Makefile b/Makefile index c4754628..b1a7db49 100644 --- a/Makefile +++ b/Makefile @@ -78,7 +78,7 @@ BOARD_DIR := $(if $(TARGET),$(BOARD_DIR_$(TARGET))) SUBMAKE := $(MAKE) --no-print-directory .DEFAULT_GOAL := all -.PHONY: all boot_image boot_images write_stick docs clean clean-docs kernel boards help help-hosted sbom sbom-board \ +.PHONY: all boot_image boot_images write_stick docs fabric clean clean-docs kernel boards help help-hosted sbom sbom-board \ qemu qemu-esp qemu-gdb init # ARCH given on the command line and no TARGET: `clean` is the kernel's for that @@ -203,6 +203,10 @@ docs: fi @$(SUBMAKE) -f docs/book/Makefile BOARD=$(BOARD_DIR) BOARD_NAME=$(TARGET) +# fabric -- The Book of Fabric (#138): build/docs/BookOfFabric.pdf +fabric: + @$(SUBMAKE) -f docs/book/Makefile fabric + clean-docs: rm -rf build/docs diff --git a/docs/book/Makefile b/docs/book/Makefile index b797a48a..cc6c3d12 100644 --- a/docs/book/Makefile +++ b/docs/book/Makefile @@ -57,8 +57,15 @@ chapter_name = $(word 1,$(subst :, ,$(1))) chapter_src = $(word 2,$(subst :, ,$(1))) FRAGMENTS := $(foreach c,$(CHAPTERS),$(GEN)/$(call chapter_name,$(c)).tex) -.PHONY: all tools FORCE +# The Book of Fabric (#138): the FABRIC design record alone, as one book, from +# the same fragments -- `make fabric` -> build/docs/BookOfFabric.pdf. +FABRIC_DIR := $(OUT)/fabric +FABRIC_PDF := $(OUT)/BookOfFabric.pdf +FABRIC_FRAGS := $(foreach n,fabric-0 fabric-1 fabric-2 fabric-3 fabric-3-5 fabric-3-6 fabric-3-7 fabric-4,$(GEN)/$(n).tex) + +.PHONY: all fabric tools FORCE all: $(PDF) +fabric: $(FABRIC_PDF) tools: @missing=""; \ @@ -86,25 +93,32 @@ $(GEN)/pandoc-highlighting.tex: $(BOOK_DIR)/pandoc/highlighting.latex | tools @mkdir -p $(GEN) @printf '```c\nx\n```\n' | $(PANDOC) -f gfm -t latex -s --template=$< -o $@ -# Per-build facts the book prints: the commit every \datasource refers to, +# Per-build facts the books print: the commit every \datasource refers to, +# the version (git describe: a release's tag, or the tag and the commits since), # and which board appendix (if any) is included. -$(VAR_DIR)/meta.tex: FORCE - @mkdir -p $(VAR_DIR) +define meta_cmds + @mkdir -p $(dir $@) @{ \ c=$$(git rev-parse --short=12 HEAD 2>/dev/null || echo unknown); \ git diff --quiet HEAD -- 2>/dev/null || c="$$c (+ uncommitted changes)"; \ printf '\\newcommand{\\bookcommit}{%s}\n' "$$c"; \ printf '\\newcommand{\\bookdate}{%s}\n' "$$(date -u +%Y-%m-%d)"; \ + printf '\\newcommand{\\bookversion}{%s}\n' "$$(git describe --tags --always 2>/dev/null || echo unknown)"; \ printf '\\def\\bookboard{%s}\n' "$(BOARD_NAME)"; \ $(if $(BOARD),printf '\\newcommand{\\bookboardappendix}{board-%s}\n' "$(BOARD)";) \ } > $@.tmp @cmp -s $@.tmp $@ && rm -f $@.tmp || mv $@.tmp $@ +endef +$(VAR_DIR)/meta.tex: FORCE + $(meta_cmds) +$(FABRIC_DIR)/meta.tex: FORCE + $(meta_cmds) FORCE: TEXINPUTS_BOOK := $(abspath $(VAR_DIR)):$(abspath $(GEN)):$(abspath $(BOOK_DIR)): -$(PDF): $(BOOK_DIR)/main.tex $(FRAGMENTS) $(GEN)/pandoc-highlighting.tex $(VAR_DIR)/meta.tex | tools +$(PDF): $(BOOK_DIR)/main.tex $(BOOK_DIR)/preamble.tex $(FRAGMENTS) $(GEN)/pandoc-highlighting.tex $(VAR_DIR)/meta.tex | tools @echo " LATEXMK $(BOOK_DIR)/main.tex -> $@" @TEXINPUTS=$(TEXINPUTS_BOOK) $(LATEXMK) -xelatex -interaction=nonstopmode -halt-on-error \ -file-line-error -outdir=$(VAR_DIR) $(BOOK_DIR)/main.tex > $(VAR_DIR)/latexmk.log 2>&1 || { \ @@ -112,3 +126,14 @@ $(PDF): $(BOOK_DIR)/main.tex $(FRAGMENTS) $(GEN)/pandoc-highlighting.tex $(VAR_D echo "Error: LaTeX failed; full log: $(VAR_DIR)/main.log"; exit 1; } @cp $(VAR_DIR)/main.pdf $@ @echo " PDF $@" + +TEXINPUTS_FABRIC := $(abspath $(FABRIC_DIR)):$(abspath $(GEN)):$(abspath $(BOOK_DIR)): + +$(FABRIC_PDF): $(BOOK_DIR)/fabric.tex $(BOOK_DIR)/preamble.tex $(FABRIC_FRAGS) $(GEN)/pandoc-highlighting.tex $(FABRIC_DIR)/meta.tex | tools + @echo " LATEXMK $(BOOK_DIR)/fabric.tex -> $@" + @TEXINPUTS=$(TEXINPUTS_FABRIC) $(LATEXMK) -xelatex -interaction=nonstopmode -halt-on-error \ + -file-line-error -outdir=$(FABRIC_DIR) $(BOOK_DIR)/fabric.tex > $(FABRIC_DIR)/latexmk.log 2>&1 || { \ + grep -A4 -E '^(.*:[0-9]+:|!)' $(FABRIC_DIR)/fabric.log | head -40; \ + echo "Error: LaTeX failed; full log: $(FABRIC_DIR)/fabric.log"; exit 1; } + @cp $(FABRIC_DIR)/fabric.pdf $@ + @echo " PDF $@" diff --git a/docs/book/fabric.tex b/docs/book/fabric.tex new file mode 100644 index 00000000..f56dbdcf --- /dev/null +++ b/docs/book/fabric.tex @@ -0,0 +1,69 @@ +% docs/book/fabric.tex -- master file of The Book of Fabric (#138): the FABRIC +% design record as one book, published with every release (Captain Bob, +% 2026-11-07: "build a 'The Book of Fabric' and publish it as version +% every release"; 2026-11-08: "Publish. Fabric is all about steady +% state."). +% +% Built by `make fabric` (docs/book/Makefile) with xelatex, from the same +% pandoc fragments as the book (fabric-0 ... fabric-4, from docs/fabric/). +\documentclass[11pt,openany]{book} + +\input{preamble} + +\title{The Book of Fabric\\[0.5em]\large The StarshipOS design record} +\author{Robert A. James (Captain Bob)} +\date{Version \texttt{\bookversion}\\Built \bookdate{} from commit \texttt{\bookcommit}} + +\begin{document} +\frontmatter +\maketitle +\tableofcontents + +\chapter{About this book} +FABRIC is the design record of StarshipOS, LithosAnanke and StarForth: every +ruling, the reasoning behind it, what was built, what was found, and what was +abandoned, written as the work happened. It is reproduced here as written -- +struck-through corrections, superseded plans and all -- because the record of +what was believed, and when, is part of the record. + +One document is living at a time; when its topic closes, it is closed and a +successor opens. Part~I is the living document. Part~II holds the closed +ones, in the order they were written; each states, at its head, when and why +it closed. Part~III holds the forward-looking notes. + +Each chapter is the Markdown file named below, converted when this book is +built; the version and commit on the title page say exactly which. + +\begin{longtable}{@{}lll@{}} +\toprule +Document & Topic & Status \\ +\midrule +\endhead +FABRIC-3 & Bare metal boot & Living \\ +FABRIC-0 & The Stadium & Closed 2026-08-12 \\ +FABRIC-1 & The Stadium, continued & Closed 2026-08-25 \\ +FABRIC-2 & The Stadium, continued again & Closed 2026-09-04 \\ +FABRIC-3.5 & The Tripod/kernel reshuffle & Closed 2026-09-22 \\ +FABRIC-3.6 & The reshuffle's execution log & Closed 2026-09-22 \\ +FABRIC-3.7 & Phase 8 PKI: the elevation entrypoint & Closed 2026-10-14 \\ +FABRIC-4 & Forward-looking design notes & Notes \\ +\bottomrule +\end{longtable} + +\mainmatter + +\part{The living record} +\input{fabric-3} + +\part{The closed records} +\input{fabric-0} +\input{fabric-1} +\input{fabric-2} +\input{fabric-3-5} +\input{fabric-3-6} +\input{fabric-3-7} + +\part{Forward-looking notes} +\input{fabric-4} + +\end{document} diff --git a/docs/book/main.tex b/docs/book/main.tex index cbce2066..ab3087a1 100644 --- a/docs/book/main.tex +++ b/docs/book/main.tex @@ -6,99 +6,7 @@ % build/docs/gen/; meta.tex is generated per build. Neither is committed. \documentclass[11pt,openany]{book} -\usepackage{amsmath,amssymb} -\usepackage{fontspec} -% fonts-dejavu-core has no serif/sans italics (fonts-dejavu-extra does); when a -% shape is missing, slant the upright one rather than silently drop emphasis. -\setmainfont{DejaVu Serif}[AutoFakeSlant=0.2] -\setsansfont{DejaVu Sans}[AutoFakeSlant=0.2] -\setmonofont{DejaVu Sans Mono}[Scale=0.85] -\newfontfamily\symbolfont{DejaVu Sans} - -\usepackage[letterpaper,margin=1in]{geometry} -\usepackage{xcolor} -\usepackage{graphicx} -\usepackage{longtable,booktabs,array,calc,multirow} -\usepackage{fancyvrb} -\usepackage[normalem]{ulem} -\usepackage{enumitem} -\usepackage{newunicodechar} -\usepackage{pgfplots} -\usepackage{pgfplotstable} -\pgfplotsset{compat=1.18} -\usepackage[hidelinks]{hyperref} -\usepackage{bookmark} - -% --- what pandoc's LaTeX fragments expect (normally from its own template) -\providecommand{\tightlist}{\setlength{\itemsep}{0pt}\setlength{\parskip}{0pt}} -\providecommand{\pandocbounded}[1]{#1} -\providecommand{\st}[1]{\sout{#1}} -\newcounter{none} % pandoc: {\def\LTcaptype{none} ...} marks unnumbered tables -\usepackage{etoolbox} -\makeatletter -\def\fnum@table{\tablename~\thetable} -\patchcmd\longtable{\par}{\if@noskipsec\mbox{}\fi\par}{}{} -\makeatother -\usepackage{fvextra} -\input{pandoc-highlighting} -% Code and diagrams are wider than the page in places: wrap, never overflow. -\RecustomVerbatimEnvironment{Highlighting}{Verbatim}{commandchars=\\\{\},breaklines,breakanywhere,fontsize=\small} -\RecustomVerbatimEnvironment{verbatim}{Verbatim}{breaklines,breakanywhere,fontsize=\small} -\setlistdepth{9} -\renewlist{itemize}{itemize}{9} -\setlist[itemize]{label=\textbullet} -\renewlist{enumerate}{enumerate}{9} -\setlist[enumerate]{label=\arabic*.} -% Running heads: chapter on the left page, section on the right, both cut to -% the page width (several design-record titles are a full sentence long). -\usepackage{fancyhdr} -\usepackage[fit]{truncate} -\pagestyle{fancy} -\fancyhf{} -\renewcommand{\chaptermark}[1]{\markboth{#1}{}} -\renewcommand{\sectionmark}[1]{\markright{#1}} -\fancyhead[LE]{\small\truncate{\dimexpr\headwidth-3em}{\leftmark}} -\fancyhead[RO]{\small\truncate{\dimexpr\headwidth-3em}{\rightmark}} -\fancyhead[RE,LO]{\small\thepage} -\renewcommand{\headrulewidth}{0.4pt} -\setlength{\headheight}{14pt} -\fancypagestyle{plain}{\fancyhf{}\fancyfoot[C]{\small\thepage}\renewcommand{\headrulewidth}{0pt}} -\setlength{\emergencystretch}{3em} -\setlength{\parindent}{0pt} -\setlength{\parskip}{0.5em} - -% --- glyphs DejaVu Serif lacks: the status marks used across the docs -\newunicodechar{✅}{{\symbolfont ✔}} -\newunicodechar{✓}{{\symbolfont ✓}} -\newunicodechar{❌}{{\symbolfont ✘}} -\newunicodechar{✗}{{\symbolfont ✗}} -\newunicodechar{⬜}{{\symbolfont ☐}} -\newunicodechar{⭐}{{\symbolfont ★}} -\newunicodechar{🔶}{{\symbolfont ◆}} -\newunicodechar{⚠}{{\symbolfont ⚠}} -\newunicodechar{📋}{} -\newunicodechar{🐛}{[bug]} -\newunicodechar{🎯}{[goal]} -\newunicodechar{🟡}{{\symbolfont ●}} -\newunicodechar{📍}{{\symbolfont ▸}} -\newunicodechar{🔓}{[unlocked]} -\newunicodechar{❓}{?} -\newunicodechar{⟺}{\ensuremath{\Longleftrightarrow}} -\newunicodechar{⋯}{\ensuremath{\cdots}} -\newunicodechar{^^^^fe0f}{} - -% --- the documents number their own sections (§III.1, D-3, ...) -\setcounter{secnumdepth}{0} -\setcounter{tocdepth}{1} - -\input{meta} - -% \datasource{path/to/data.csv}: cite the data behind a figure or table. -% Every figure or table of measured numbers reads its CSV at build time -% (\addplot table / \pgfplotstabletypeset) and is followed by this line, so -% the reader can find the exact file at the exact commit. -\newcommand{\datasource}[1]{% - \par{\small Data: \texttt{\detokenize{#1}} at commit \texttt{\bookcommit}.}\par} +\input{preamble} \title{LithosAnanke and StarForth} \author{} diff --git a/docs/book/preamble.tex b/docs/book/preamble.tex new file mode 100644 index 00000000..5483b24f --- /dev/null +++ b/docs/book/preamble.tex @@ -0,0 +1,97 @@ +% docs/book/preamble.tex -- what both books share: the packages, the fonts, +% what pandoc's fragments expect, the glyphs, the running heads, and meta.tex +% (the commit, the date, the version). Input by main.tex (the book) and +% fabric.tex (the Book of Fabric, #138). +\usepackage{amsmath,amssymb} +\usepackage{fontspec} +% fonts-dejavu-core has no serif/sans italics (fonts-dejavu-extra does); when a +% shape is missing, slant the upright one rather than silently drop emphasis. +\setmainfont{DejaVu Serif}[AutoFakeSlant=0.2] +\setsansfont{DejaVu Sans}[AutoFakeSlant=0.2] +\setmonofont{DejaVu Sans Mono}[Scale=0.85] +\newfontfamily\symbolfont{DejaVu Sans} + +\usepackage[letterpaper,margin=1in]{geometry} +\usepackage{xcolor} +\usepackage{graphicx} +\usepackage{longtable,booktabs,array,calc,multirow} +\usepackage{fancyvrb} +\usepackage[normalem]{ulem} +\usepackage{enumitem} +\usepackage{newunicodechar} +\usepackage{pgfplots} +\usepackage{pgfplotstable} +\pgfplotsset{compat=1.18} +\usepackage[hidelinks]{hyperref} +\usepackage{bookmark} + +% --- what pandoc's LaTeX fragments expect (normally from its own template) +\providecommand{\tightlist}{\setlength{\itemsep}{0pt}\setlength{\parskip}{0pt}} +\providecommand{\pandocbounded}[1]{#1} +\providecommand{\st}[1]{\sout{#1}} +\newcounter{none} % pandoc: {\def\LTcaptype{none} ...} marks unnumbered tables +\usepackage{etoolbox} +\makeatletter +\def\fnum@table{\tablename~\thetable} +\patchcmd\longtable{\par}{\if@noskipsec\mbox{}\fi\par}{}{} +\makeatother +\usepackage{fvextra} +\input{pandoc-highlighting} +% Code and diagrams are wider than the page in places: wrap, never overflow. +\RecustomVerbatimEnvironment{Highlighting}{Verbatim}{commandchars=\\\{\},breaklines,breakanywhere,fontsize=\small} +\RecustomVerbatimEnvironment{verbatim}{Verbatim}{breaklines,breakanywhere,fontsize=\small} +\setlistdepth{9} +\renewlist{itemize}{itemize}{9} +\setlist[itemize]{label=\textbullet} +\renewlist{enumerate}{enumerate}{9} +\setlist[enumerate]{label=\arabic*.} +% Running heads: chapter on the left page, section on the right, both cut to +% the page width (several design-record titles are a full sentence long). +\usepackage{fancyhdr} +\usepackage[fit]{truncate} +\pagestyle{fancy} +\fancyhf{} +\renewcommand{\chaptermark}[1]{\markboth{#1}{}} +\renewcommand{\sectionmark}[1]{\markright{#1}} +\fancyhead[LE]{\small\truncate{\dimexpr\headwidth-3em}{\leftmark}} +\fancyhead[RO]{\small\truncate{\dimexpr\headwidth-3em}{\rightmark}} +\fancyhead[RE,LO]{\small\thepage} +\renewcommand{\headrulewidth}{0.4pt} +\setlength{\headheight}{14pt} +\fancypagestyle{plain}{\fancyhf{}\fancyfoot[C]{\small\thepage}\renewcommand{\headrulewidth}{0pt}} +\setlength{\emergencystretch}{3em} +\setlength{\parindent}{0pt} +\setlength{\parskip}{0.5em} + +% --- glyphs DejaVu Serif lacks: the status marks used across the docs +\newunicodechar{✅}{{\symbolfont ✔}} +\newunicodechar{✓}{{\symbolfont ✓}} +\newunicodechar{❌}{{\symbolfont ✘}} +\newunicodechar{✗}{{\symbolfont ✗}} +\newunicodechar{⬜}{{\symbolfont ☐}} +\newunicodechar{⭐}{{\symbolfont ★}} +\newunicodechar{🔶}{{\symbolfont ◆}} +\newunicodechar{⚠}{{\symbolfont ⚠}} +\newunicodechar{📋}{} +\newunicodechar{🐛}{[bug]} +\newunicodechar{🎯}{[goal]} +\newunicodechar{🟡}{{\symbolfont ●}} +\newunicodechar{📍}{{\symbolfont ▸}} +\newunicodechar{🔓}{[unlocked]} +\newunicodechar{❓}{?} +\newunicodechar{⟺}{\ensuremath{\Longleftrightarrow}} +\newunicodechar{⋯}{\ensuremath{\cdots}} +\newunicodechar{^^^^fe0f}{} + +% --- the documents number their own sections (§III.1, D-3, ...) +\setcounter{secnumdepth}{0} +\setcounter{tocdepth}{1} + +\input{meta} + +% \datasource{path/to/data.csv}: cite the data behind a figure or table. +% Every figure or table of measured numbers reads its CSV at build time +% (\addplot table / \pgfplotstabletypeset) and is followed by this line, so +% the reader can find the exact file at the exact commit. +\newcommand{\datasource}[1]{% + \par{\small Data: \texttt{\detokenize{#1}} at commit \texttt{\bookcommit}.}\par} diff --git a/docs/v4.0.0/plans/2026-11-10-alpha4-documents.md b/docs/v4.0.0/plans/2026-11-10-alpha4-documents.md new file mode 100644 index 00000000..9b102e64 --- /dev/null +++ b/docs/v4.0.0/plans/2026-11-10-alpha4-documents.md @@ -0,0 +1,26 @@ +# alpha.4's documents and pages -- progress (kept current, one line per increment) + +Captain Bob, 2026-11-10: "Do it" -- the rest of alpha.4 that lives in the tree, after #92 (merged, +#160). #133 and #134 are the server's (Captain Bob's). + +## #138 -- The Book of Fabric +- [x] `docs/book/fabric.tex`: the FABRIC record as one book -- Part I the living record (FABRIC-3), + Part II the closed ones in order, Part III the forward-looking notes; title page with the + version (`git describe --tags`) and the commit. The book's preamble moved into + `docs/book/preamble.tex`, shared by both books (the book itself builds unchanged, 821 pages). +- [x] `make fabric` -> `build/docs/BookOfFabric.pdf` (722 pages, 42 s). +- [x] `scripts/nightly.sh docs` builds it (`$DOCS/fabric.pdf`); `publish` sends it as + `org.starship:documentation:`, classifier `fabric`; `gitea-release` attaches every + PDF in `$DOCS`, so the release carries it. + +## #157 -- the dictionary of v4, and the .com's Documents page +- [ ] the live word list of the built system (318 words) +- [ ] the table in the tree: name, stack effect, description, where defined +- [ ] the check: every live word has an entry, every entry is a live word +- [ ] the PDF, alphabetical; `make dictionary`; the nightly's docs stage, classifier `dictionary` +- [ ] the Documents page: canvas artboard, then `site/com` + +## #126 -- the Downloads page live +- [ ] `site/com` builds Downloads (and Documents) from the release: its tag, `SHA256SUMS`, the + Root CA's fingerprint +- [ ] the nav links on Home, Try it, Inquiries diff --git a/scripts/nightly.sh b/scripts/nightly.sh index c127c679..d1cbb2ce 100755 --- a/scripts/nightly.sh +++ b/scripts/nightly.sh @@ -42,7 +42,8 @@ # The book (make docs), the formal volumes (make -C docs/formal vols) and the API reference # (make -C docs/formal doxygen), into build/nightly-docs/. # scripts/nightly.sh publish-docs -# org.starship:documentation:: classifier book, vol1, vol2, vol3, api (pdf). +# org.starship:documentation:: classifier book, fabric (The Book of Fabric, #138), +# vol1, vol2, vol3, api (pdf). # # VERSION is -SNAPSHOT, or NIGHTLY_VERSION when set (a release sets it from its # tag). NEXUS_USER (default ci-publisher) and NEXUS_PASSWORD; publishing needs mvn and a JRE. @@ -173,8 +174,10 @@ publish) docs) rm -rf "$DOCS"; mkdir -p "$DOCS" make --no-print-directory docs + make --no-print-directory fabric make --no-print-directory -C docs/formal vols doxygen cp build/docs/LithosAnanke.pdf "$DOCS/book.pdf" + cp build/docs/BookOfFabric.pdf "$DOCS/fabric.pdf" for v in 1 2 3; do cp "docs/formal/build/vol$v/main.pdf" "$DOCS/vol$v.pdf"; done cp docs/formal/build/doxygen/StarForth-API-Reference.pdf "$DOCS/api.pdf" (cd "$DOCS" && sha256sum ./*.pdf | tee SHA256SUMS) @@ -217,8 +220,8 @@ print(json.dumps(notes.strip() + "\n\n## Checksums (SHA-256)\n\n```\n" + sums.st ;; publish-docs) url=${2:-}; [ -n "$url" ] || usage - need "$DOCS/book.pdf" "$DOCS/vol1.pdf" "$DOCS/vol2.pdf" "$DOCS/vol3.pdf" "$DOCS/api.pdf" - deploy "$url" documentation "$DOCS/book.pdf" pdf book \ + need "$DOCS/book.pdf" "$DOCS/fabric.pdf" "$DOCS/vol1.pdf" "$DOCS/vol2.pdf" "$DOCS/vol3.pdf" "$DOCS/api.pdf" + deploy "$url" documentation "$DOCS/book.pdf" pdf book "$DOCS/fabric.pdf" pdf fabric \ "$DOCS/vol1.pdf" pdf vol1 "$DOCS/vol2.pdf" pdf vol2 "$DOCS/vol3.pdf" pdf vol3 \ "$DOCS/api.pdf" pdf api ;; -- 2.54.0 From a53cc82dd579b3fcb976fedc96a8a4c8746a60c3 Mon Sep 17 00:00:00 2001 From: Claude Date: Tue, 10 Nov 2026 11:25:01 +0000 Subject: [PATCH 02/10] docs(dictionary): mkdictionary.py and the draft table -- every live v4 word, 318 (#157) [skip ci] The names from the system itself (the hosted v4's WORDS, plus the kernel's sk_v4_words[]); the stack notation and description in docs/reference/v4-dictionary.tsv, drafted from the source where it says (54 stacks, 105 descriptions), the rest to write. check refuses a missing, unfinished or stale entry. Co-Authored-By: Claude Opus 5.5 Claude-Session: https://claude.ai/code/session_01GE3SDvQn5EoxdGufk84zz2 --- docs/reference/v4-dictionary.tsv | 321 ++++++++++++++++++ .../plans/2026-11-10-alpha4-documents.md | 6 +- v4/tools/mkdictionary.py | 170 ++++++++++ 3 files changed, 494 insertions(+), 3 deletions(-) create mode 100644 docs/reference/v4-dictionary.tsv create mode 100644 v4/tools/mkdictionary.py diff --git a/docs/reference/v4-dictionary.tsv b/docs/reference/v4-dictionary.tsv new file mode 100644 index 00000000..0999e72d --- /dev/null +++ b/docs/reference/v4-dictionary.tsv @@ -0,0 +1,321 @@ +# docs/reference/v4-dictionary.tsv -- the dictionary of v4 (#157): name, stack notation, +# description, tab-separated. The names are the live system's (v4/tools/mkdictionary.py +# check refuses a missing or a stale one). A row whose description starts TODO is a draft. +! TODO +# TODO +#> TODO +#S TODO +' ( -- addr ) TODO FORTH-79: the next word's parameter field address; if compiling, compiled as a literal +( TODO ---- comments (5.15) ------------------------------------------------------- PAREN skips to the closing parenthesis; BACKSLASH skips the rest of the line. +(.") TODO The run time of ." : print the string after the call, go on after it. It takes the return address off before it calls anything and keeps the count there instead, so a word that prints text goes no deeper than one that calls any other word. +(ABORT") ( flag -- ) TODO the run time of ABORT" : if the flag is not zero print the string after the call, start a new line and ABORT; otherwise go on after the string. +(BITS) ( -- n ) TODO +(CONSOLE) TODO +(LOG") ( level -- ) TODO the run time of LOG-xxx" : the text is after the call, as for ." ; log it at that level and go on after it. +(LOST) TODO +(ME) TODO +(NB) TODO +(REFUSED) TODO +(S") TODO The run time of S" : leave the address and length of the string after the call, and go on after it. +(SEAL) ( -- ) TODO WHAT IS IN THE DICTIONARY NOW IS THE SYSTEM: COLD comes back to it and FORGET will not go below it. Whoever has loaded a node's capsules has it do this (system.f18, (BOOT) and FENCE). +(UD) TODO +(WORD-DEFINED) TODO +(WORD-FORGOTTEN) TODO +* TODO The low cell of the product, by N steps of +* with nothing but the count on the return stack. UM* drop gives the same cell but needs two more return entries, which a word inside two DO loops does not have (D-2). The low cell is exact: what +* loses at the top of T (D-3) takes N shifts to reach the bit that moves into A, and the loop ends first. +*/ TODO +*/MOD TODO ++ TODO ---- arithmetic ---- ++! TODO ++LOOP TODO +, TODO , calls nothing: the code generator calls it from as deep as anything goes. +- TODO +-! TODO +--> ( -- ) TODO go on with the next block. Only in a block being loaded: at the terminal it is an error (code 14). +-ROT TODO +-TRAILING TODO +. TODO +." TODO FORTH-79: ." text" prints the text -- now, if interpreting; when the word it is compiled into runs, if compiling. +.R TODO +.S ( -- ) TODO as v3: the depth between < and >, then every value on the stack, the deepest first, each followed by a blank; then a new line. The stack is left as it was. It reads each value with PICK, so it needs a few cells of the stack free to work in. +/ TODO +/MOD TODO +0< TODO +0<> TODO ---- comparison ---- +0= TODO +0> TODO +1+ TODO +1- TODO +2! TODO +2* TODO +2+ TODO +2, TODO +2- TODO +2/ TODO +2>R TODO +2@ TODO ---- memory ---- +2DROP TODO +2DUP TODO +2OVER TODO +2R> TODO +2R@ TODO +2ROT TODO +2SWAP TODO ---- stack ---- +79-STANDARD TODO FORTH-79: execute to be sure a FORTH-79 system is there. It is: nothing happens. (v3's prints two lines and leaves a flag.) +: TODO FORTH-79: start a definition; it cannot be found until ; ends it. The CONTEXT vocabulary is set to the CURRENT one. (C)+2 holds LATEST while the entry is made, to tell whether one was. +; TODO FORTH-79: compile the return, let the word be found, stop compiling +< TODO +<# TODO ---- pictured output ---- +<= TODO +<> TODO += TODO +> TODO +>= TODO +>BODY TODO +>IN TODO +>LINK TODO +>NAME TODO +>R TODO ---- return stack: in line, and only inside a definition ---- +? ( addr -- ) TODO print what the cell holds +?DO TODO +?DUP TODO +?TERMINAL TODO ---- strings and memory ---- +@ TODO ---- memory ---- +ABORT TODO FORTH-79: clear the data and return stacks, set execution mode, return control to the terminal. As v3, the line it stops ends with " ok". Both are emptied before anything is called: ABORT may be run with the return stack full. +ABORT" ( flag -- ) TODO ABORT" text" as v3 (it is FORTH-83's, not FORTH-79's): if the flag is not zero, print the text and ABORT. +ABS TODO +ACL-ALLOW! TODO +ACL-ALLOW@ TODO +ACL-HEAT@ ( xt -- heat ) TODO how often the word has been used. The node's heat counters are not readable from a programme yet (D-6): 0. +ACL-HOOK TODO +ACL-INHERIT ( src dst -- ) TODO as v3: dst takes src's mode; it is not pinned, its TTL is 0 and it is allowed. This is the one word that unpins. +ACL-INIT-PRIMITIVES ( -- ) TODO as v3: do that to every word in the dictionary, in every vocabulary +ACL-MODE! TODO +ACL-MODE@ TODO +ACL-PIN TODO +ACL-PINNED? TODO +ACL-TTL! TODO as v3: below 0 is 0. The TTL is sixteen bits here: above 65535 is 65535. +ACL-TTL@ TODO +ACL-WORD-ID ( xt -- n ) TODO a number for the word that never changes: its address +AGAIN TODO +ALIGN TODO +ALLOT TODO +AND TODO ---- logic and comparison ---- +AWAIT ( node -- how ) TODO WAIT FOR THE NODE'S WORD OF HOW TEXT ENDED: 0 QUIT, 1 completed, 2 an error. (AWAIT-FROM) holds the node while this node waits, and 0 otherwise. It sleeps, executing nothing, until a message comes; then it passes on what is for other nodes, (SERVE), and goes through what is for itself (MESH.md 7d.4): the answer -- a message of type 3 from that node -- is taken, and the wait is over; a NACK is taken: about that node, the wait ends in error 19, Message refused; about another it is counted, as at any time; a GONE is taken: believed (its centre sent it: (GONE?)) and about that node, the wait ends in error 20, Node gone; text from its console ends the wait in error 21, Interrupted (7b.6). The text is not taken: it stays on its wire and is done when this text has been finished with. It is the one way to end a wait on a node that is alive and never answers, short of Hera's killing that node; anything else stays on its wire, in its order, for when this node has nothing to do. WHAT HAS COME ALREADY is on the wires and is found the first time through: the node looks before it sleeps. A message it left on a wire does not keep it awake. The stack what it does needs is tried first, (ROOM), core.f18: text that has left too little ends "Stack overflow" with nothing taken off a wire. +BASE TODO +BEGIN TODO A loop that tests at its end jumps back with the flag still there, so it jumps back to a drop laid just ahead of the body: BEGIN a UNTIL jump L0 Ld: drop L0: a if Ld drop BEGIN a WHILE b REPEAT jump L0 Ld: drop L0: a if Lx drop b jump L0 Lx: drop BEGIN a AGAIN jump L0 Ld: drop L0: a jump L0 The control-flow stack holds Ld, L0 and 3. +BL TODO ---- in-line addresses and constants ---- +BLANK TODO +BLK TODO +BLOCK ( n -- baddr ) TODO FORTH-79: the address of a buffer that holds block n, read from storage if it is not already in one. +BUFFER ( n -- baddr ) TODO FORTH-79: the address of a buffer assigned to block n. What the buffer holds is not read from storage. +BYE TODO +C! TODO +C, TODO +C@ TODO +CASE TODO +CELLS TODO +CFA TODO +CMOVE TODO ---- section 5.9: strings ------------------------------------------------ +CMOVE> TODO +COLD ( -- ) TODO the system as it was when the capsule had just been loaded: everything defined since is gone, there is one vocabulary, numbers are decimal, the block buffers are empty. Then as WARM. +COMPARE TODO +COMPILE TODO COMPILE xxx FORTH-79: when the word this is in runs, xxx is compiled +CONSTANT ( n -- ) TODO FORTH-79: an entry whose word leaves n +CONTEXT TODO ---- vocabularies (FORTH-79) ---------------------------------------------------- A vocabulary is two cells: its head -- the xt of its newest entry, or 0 -- and the address of the vocabulary defined before it, or 0. VOC-LINK holds the newest one's address. FORTH's head is (LATEST) and it is in no chain. +CONVERT ( d1 baddr1 -- d2 baddr2 ) TODO FORTH-79: the characters from baddr1 + 1 on are taken as digits in the current BASE, each accumulated into the double after it is multiplied by BASE, until one is not a digit; baddr2 is its address. (P)+2 holds the digit while the double is multiplied and (P)+5 the address, so nothing waits on the return stack across the calls. +COUNT TODO +CR TODO +CREATE TODO FORTH-79: an entry whose word leaves the address of its parameter field +CURRENT TODO +D+ TODO ---- section 5.7: doubles ------------------------------------------------ +D- TODO ---- doubles and mixed ---- +D. TODO +D.R TODO +D0< TODO +D0= TODO +D2* TODO +D2/ TODO +D< TODO +D= TODO +DABS TODO +DECIMAL TODO +DEFAULT-ROUTE ( port -- ) TODO the port, by its number, leads toward every node not told of +DEFER TODO +DEFER@ TODO +DEFINITIONS TODO FORTH-79: new definitions go into the CONTEXT vocabulary from now on. +DEPTH TODO FORTH-79: how many values were on the stack before DEPTH +DMAX TODO +DMIN TODO +DNEGATE TODO +DO TODO +DOES> TODO +DROP TODO +DUMP TODO +DUP TODO ---- stack ---- +ELSE TODO +EMIT TODO EMIT puts the character with what has been printed. Everything the node prints goes through EMIT. A is kept: the words that print have always been free to use it across an EMIT. As v3, only the low 8 bits of the character are printed. A BUFFER FOUND FULL is sent first. It is full when a flush of it ended in a fault -- the stack too deep to begin a message -- and what is printed next is that fault's own message: put where (OUT^) points, it would go past the buffer's end, and (OUT^) would never again be at the end to be flushed (MESH.md 7c.6). +EMPTY-BUFFERS TODO FORTH-79: forget what is in the buffers; nothing is written. +ENCLOSE ( baddr c -- baddr n1 n2 n3 ) TODO in the zero-terminated string at baddr: n1 the offset of the first character that is not c, n2 the offset of the first c after it, n3 the offset of the first character after that run of c. +ENDCASE TODO +ENDOF TODO +ERASE TODO +EXECUTE TODO +EXIT TODO FORTH-79: return from the definition at this point +EXPECT ( baddr n -- ) TODO FORTH-79: characters from the terminal are stored from baddr upward until a new-line or until n have been received; the new-line is taken but not stored. A zero is added after the text, so the buffer must hold n + 1 bytes. No action for n <= 0. SPAN is how many characters were stored (SPAN is not FORTH-79; v3 has it). A line longer than n leaves the rest, its new-line included, to be read next. It is what the prompt reads every line with, with whatever the user has left on the stack beneath it, so it works in (P) -- (P)+0 where the next character goes, (P)+1 how many more may come, (P)+2 where the first went -- and has at most three cells of its own on the data stack (D-2). +FALSE TODO +FENCE TODO +FILL TODO +FIND ( -- xt | 0 ) TODO FORTH-79: the compilation address of the next word in the input stream, or 0 if it is not in the dictionary. +FLUSH TODO As v3: the same. +FORGET TODO FORGET xxx FORTH-79: remove xxx and every word defined after it, in whatever vocabulary; the space they took is free again. A vocabulary that goes is no longer CONTEXT or CURRENT: FORTH is. A word that is not there is an error, and so is one below FENCE, which is where the capsule's own words are (code 10, "Protected word"). +FORTH TODO FORTH-79: the primary vocabulary; it becomes the CONTEXT vocabulary. +GONE ( n node -- ) TODO tell the node that node n is gone. It acts on that only if this node is its centre: it forgets the way to n, and if it is waiting for n's answer the wait ends. +HERE TODO +HEX TODO +HIDDEN TODO +HOLD TODO +I TODO +IF TODO Native if leaves the flag, so both ways out of it drop it (section 2): IF a THEN if L1 drop a jump L2 L1: drop L2: IF a ELSE b THEN if L1 drop a jump L2 L1: drop b L2: +IMMEDIATE TODO +INTERPRET ( -- ) TODO FORTH-79: take words from the input stream until it is exhausted. A word in the dictionary is executed, or, when compiling and not immediate, compiled. Anything else must be a number in the current BASE: it is left on the stack, or compiled as a literal. What is neither, or a compile-only word met while interpreting, abandons the line, with v3's message: UNKNOWN WORD: 'xxx' xxx: compile-only +INVERT TODO +IS TODO +J TODO +KERNEL-WORD ( n -- ) TODO KERNEL-WORD xxx A WORD THAT ASKS THE KERNEL (docs/v4.0.0/ ENGINE.md 3.3). xxx is made a word whose whole body is "write n to the port": n is the number of a request, the port is the node's way to its kernel, and the write blocks the node until the kernel has served it (DECOMPOSITION.md section 6). xxx's arguments are whatever it finds on the data stack and its results are what the kernel leaves there. This is what a word that is a C function in v3 -- BIRTH, BLOCK, BYE -- is on a node. Not FORTH-79 and not in v3, where such a word is registered from C. The constant (PORT) is the port's word address. +KEY TODO +LATEST TODO +LEAVE TODO +LFA TODO +LINK> TODO +LIST TODO ---- LIST ---------------------------------------------------------------------- ( n -- ) as v3: a new line, "Block n", then sixteen lines of 64 characters, each after its number, and an empty line. SCR is left holding n. A character that is not printable is shown as a blank. (Q)+0 is the line, (Q)+1 the column, (Q)+2 the block's number. +LITERAL ( x -- ) TODO FORTH-79: if compiling, compile x as a literal +LOAD ( n -- ) TODO FORTH-79: interpret block n, then go on with what follows. +LOG-DEBUG TODO +LOG-DEBUG" TODO +LOG-DEBUG-STR ( baddr u -- ) TODO +LOG-ERROR TODO +LOG-ERROR" TODO +LOG-ERROR-STR ( baddr u -- ) TODO +LOG-INFO TODO +LOG-INFO" TODO +LOG-INFO-STR ( baddr u -- ) TODO +LOG-LEVEL! ( n -- ) TODO as v3: a level below 0 is 0, one above 4 is 4 +LOG-LEVEL@ TODO +LOG-TEST TODO +LOG-TEST" TODO +LOG-TEST-STR ( baddr u -- ) TODO +LOG-WARN TODO +LOG-WARN" TODO +LOG-WARN-STR ( baddr u -- ) TODO +LOOP TODO +LSHIFT TODO ---- shifts ---- As v3, a count that is negative or as large as the cell is wide is an error (code 9, "Shift count out of range"). +M* TODO The product's sign waits in (F), not on the return stack, for the same reason as SM/REM's. +M+ TODO +M- TODO +M/MOD TODO +MAX TODO +MIN TODO +MINT-ZUSE TODO +MOD TODO +MOVE TODO FORTH-79: n cells from addr1 to addr2, the cell at addr1 first. +NAME> TODO +NEGATE TODO +NEIGHBOUR ( node port -- ) TODO the node on the other end of the port, by its number; 0: none, or a device. Whoever wires the node tells it, and tells it again when the wiring changes. It is how a node knows who its centre is, the one node whose GONE it believes: (CENTRE). +NFA TODO +NO-ROUTE ( node -- ) TODO forget the way to that node, if one is known: what is for it then goes by the way for every node not told of, if there is one. The last way in the table takes its place. +NO-ROUTES ( -- ) TODO forget every way: for when the wiring has changed +NODE-ERROR TODO FORTH-79: clear the return stack, set execution mode, return control to the terminal; no message is given. The data stack is left as it is. NODE-ERROR, by name: how a definition written in FORTH raises an error. A store of a code that is not zero is the trap of D-18 -- the word goes no further, the message for the code is printed (-1: the word has printed its own) and the line ends with ERROR. The codes are listed in core.f18. +NOP TODO +NOT TODO +NUMBER ( baddr -- d ) TODO FORTH-79. What is not a number gives 0 and sets NODE-ERROR. +OCTAL TODO +OF TODO +OR TODO +ORDER ( -- ) TODO as v3's, but true: where names are looked for, and where new ones go +OVER TODO +PAD TODO +PAGE ( -- ) TODO clear the screen and put the cursor at the top, as v3: ESC [2J ESC [H +PFA TODO +PICK ( n -- x ) TODO FORTH-79: a copy of the n-th value, not counting n itself. 1 PICK is DUP, 2 PICK is OVER. n less than one is an error. +PORT! ( w port -- ) TODO write a cell to the port, by its number. It waits until the neighbour has taken it. This is how a node is sent a capsule of F18 code (docs/v4.0.0/MESH.md section 5): the cells go as they are, not as a message, to a node that is executing from its ports. +Q.* TODO ---- multiply ---- +Q.+ TODO ---- the double words under other names ---- +Q.- TODO +Q./ TODO +Q.0 TODO +Q.0= TODO +Q.1 TODO +Q.< TODO +Q.= TODO +Q.> TODO +Q.ABS TODO +Q.COS TODO +Q.EXP TODO ---- e to the x ---- +Q.FROM-INT TODO ---- conversion and constants ---- +Q.LOG TODO ---- natural logarithm ---- +Q.MAX TODO +Q.MIN TODO +Q.NEG TODO +Q.PRINT TODO ---- printing ---- The integer part, a point, five digits, a blank; always in decimal. +Q.SCALE TODO +Q.SIN TODO +Q.SQRT TODO ---- square root ---- +Q.TO-INT TODO +QUERY ( -- ) TODO FORTH-79: up to 80 characters, or a line, into TIB; >IN to 0. +QUIT TODO +R> TODO +R@ TODO +REPEAT TODO +ROLL ( n -- ) TODO FORTH-79: the n-th value, not counting n itself, is taken out and put on top. 3 ROLL is ROT, 2 ROLL is SWAP, 1 ROLL does nothing. n less than one is an error. +ROT TODO +ROUTE ( node port -- ) TODO the port, by its number, leads toward that node +RSHIFT TODO +S" ( -- baddr u ) TODO S" text" as v3. In a definition the text is compiled into it. At the prompt it is copied to PAD, where it stays until PAD is used again: WORD's buffer is overwritten by the very next word of the line. +S>D TODO +SAVE-BUFFERS TODO FORTH-79: write every block that has been UPDATEd. +SCAN TODO +SCR TODO +SEARCH TODO +SEND ( baddr u node -- ) TODO send the node u characters of text to interpret, by the way this node knows to it. What it prints there goes to that node's console. With no way to the node it is error 12. +SEND-ON ( baddr u node port -- ) TODO the same by a port this node names, by its number, whatever ways it knows: for a neighbour that has no number of its own yet, to which text is sent as to node 0. +SIGN TODO +SKIP TODO +SMUDGE TODO +SOURCE ( -- baddr u ) TODO the input buffer and how much of it is in use +SPACE TODO +SPACES TODO ---- spaces and the number base ---- +SPAN TODO +STATE TODO +SWAP TODO +THEN TODO +THRU ( first last -- ) TODO as v3: LOAD each block from first to last. +TIB TODO +TRAVERSE ( baddr n -- baddr' ) TODO as v3: for n > 0, from a name's count byte to the byte after its last character; otherwise the address unchanged. +TRUE TODO +TYPE TODO +U* TODO +U. TODO +U.R TODO +U/MOD TODO +U< TODO +U> TODO +UM* TODO ---- section 4: multiply ------------------------------------------------ +UNLOOP TODO +UNTIL TODO +UPDATE TODO FORTH-79: mark the block most recently asked for as changed, so that it is written before its buffer is used for another. +VARIABLE TODO FORTH-79: CREATE and one cell. The standard leaves its first value to the programme; here it is 0. +VERSION ( -- ) TODO which system this is, and its cell width +VLIST TODO +VOCABULARY TODO +WARM ( -- ) TODO as v3's message, and then ABORT: both stacks emptied, the prompt +WHILE TODO +WITHIN TODO +WORD TODO +WORDS ( -- ) TODO the names in the CONTEXT vocabulary, the newest first, a blank after each, a new line whenever one has passed column 64. Hidden entries -- a definition under way -- are not shown. (Q)+0 is the column. +XOR TODO +[ TODO +['] TODO ['] xxx the same inside a definition, whatever STATE is; and [LITERAL], which v3 has as another name for LITERAL. +[COMPILE] TODO [COMPILE] xxx FORTH-79: compile xxx even though it is immediate +[LITERAL] TODO +\ TODO +] TODO diff --git a/docs/v4.0.0/plans/2026-11-10-alpha4-documents.md b/docs/v4.0.0/plans/2026-11-10-alpha4-documents.md index 9b102e64..0fa63526 100644 --- a/docs/v4.0.0/plans/2026-11-10-alpha4-documents.md +++ b/docs/v4.0.0/plans/2026-11-10-alpha4-documents.md @@ -14,9 +14,9 @@ Captain Bob, 2026-11-10: "Do it" -- the rest of alpha.4 that lives in the tree, PDF in `$DOCS`, so the release carries it. ## #157 -- the dictionary of v4, and the .com's Documents page -- [ ] the live word list of the built system (318 words) -- [ ] the table in the tree: name, stack effect, description, where defined -- [ ] the check: every live word has an entry, every entry is a live word +- [x] the live word list of the built system (318 words): `v4/tools/mkdictionary.py words` -- the hosted v4's `WORDS` (317) plus the kernel's `sk_v4_words[]` (`MINT-ZUSE`) +- [~] the table in the tree, `docs/reference/v4-dictionary.tsv`: drafted for all 318 (`mkdictionary.py draft`; the source gave a stack comment for 54, a description for 105); the rest written by hand in batches, alphabetical -- next: batch 1 +- [x] the check: `mkdictionary.py check` -- every live word has a finished entry, every entry a live word (fails on all 318 drafts today, as it should); not yet in the build - [ ] the PDF, alphabetical; `make dictionary`; the nightly's docs stage, classifier `dictionary` - [ ] the Documents page: canvas artboard, then `site/com` diff --git a/v4/tools/mkdictionary.py b/v4/tools/mkdictionary.py new file mode 100644 index 00000000..7aa3fc6f --- /dev/null +++ b/v4/tools/mkdictionary.py @@ -0,0 +1,170 @@ +#!/usr/bin/env python3 +# SPDX-License-Identifier: LicenseRef-Starship-1.0 +# Copyright (c) 2023-2026 Robert A. James +"""mkdictionary.py -- the dictionary of v4 (#157): every word the system boots with, alphabetical, +with its stack notation, a description, and where it is defined. + + v4/tools/mkdictionary.py words the live word list, one per line + v4/tools/mkdictionary.py draft TSV a table for every live word not in TSV yet, appended, + its stack and description taken from the source where + the source has them (to be read and finished by hand) + v4/tools/mkdictionary.py check TSV every live word has an entry with a stack and a + description, and every entry is a live word: 0, or 1 + with what is wrong + v4/tools/mkdictionary.py md TSV OUT.md the dictionary as Markdown, alphabetical, for the book + +The live word list is the system's own: the hosted v4 (`make -C v4 hosted`) booted and asked +`WORDS`, plus the kernel's words of the lone node (`sk_v4_words[]` in kernel/src/v4/sk_v4.c) -- +the words a booted StarshipOS has. The table (docs/reference/v4-dictionary.tsv) holds the stack +notation and the description, which only a person can write; the names are never typed by hand. +""" +import os, re, subprocess, sys + +ROOT = os.path.abspath(os.path.join(os.path.dirname(__file__), "..", "..")) +HOSTED = os.path.join(ROOT, "v4", "build", "starforth4-amd64") +SOURCES = ([os.path.join("capsules/v4/sys", f) for f in sorted(os.listdir(os.path.join(ROOT, "capsules/v4/sys"))) + if f.endswith(".f18")] + + [os.path.join("capsules/v4", f) for f in sorted(os.listdir(os.path.join(ROOT, "capsules/v4"))) + if f.endswith(".4th")]) +KERNEL_WORDS_SRC = "kernel/src/v4/sk_v4.c" + + +def live_words(): + """The hosted system's WORDS, then the kernel's own words not among them.""" + if not os.path.exists(HOSTED): + sys.exit(f"mkdictionary: {HOSTED} is missing: make -C v4 hosted") + out = subprocess.run([HOSTED], input="WORDS\n", capture_output=True, text=True, timeout=120).stdout + lines = out.split("\n") + try: + start = next(k for k, l in enumerate(lines) if l.startswith("ok> ") and l[4:].strip()) + except StopIteration: + sys.exit("mkdictionary: the hosted system printed no WORDS") + got = [] + for l in lines[start:]: + if l.startswith("ok> ") and got: + break + got.append(l[4:] if l.startswith("ok> ") else l) + text = " ".join(got) + if " ok" in text: + text = text[:text.rfind(" ok")] + words = text.split() + src = open(os.path.join(ROOT, KERNEL_WORDS_SRC)).read() + m = re.search(r"sk_v4_words\[\]\s*=\s*\{([^}]*)\}", src) + for k in re.findall(r'"([^"]+)"', m.group(1) if m else ""): + if k not in words: + words.append(k) + return words + + +def definitions(): + """Where each name is defined, and what its source says of it: {name: (file, stack, text)}.""" + found = {} + for rel in SOURCES: + lines = open(os.path.join(ROOT, rel), errors="replace").read().split("\n") + for i, l in enumerate(lines): + names = [] + if rel.endswith(".f18"): + names = re.findall(r'",\s+(\S+)\s+#,', l) + else: + names = re.findall(r"(?:^|\s):\s+(\S+)", l) + names += re.findall(r"\b(?:CONSTANT|VARIABLE|DEFER|CREATE|VOCABULARY)\s+(\S+)", l) + for n in names: + if n in found: + continue + stack, text = "", "" + inline = re.search(r":\s+" + re.escape(n) + r"\s+(\([^)]*\))", l) + if inline: + stack = inline.group(1) + # the comment block just above + j, block = i - 1, [] + while j >= 0 and lines[j].startswith("\\"): + block.insert(0, lines[j].lstrip("\\").strip()) + j -= 1 + comment = " ".join(b for b in block if b) + cm = re.match(r"(\([^)]*--[^)]*\))\s*(.*)", comment) + if cm and not stack: + stack, comment = cm.group(1), cm.group(2) + text = comment + found[n] = (rel, stack, text) + for k in re.findall(r'"([^"]+)"', re.search(r"sk_v4_words\[\]\s*=\s*\{([^}]*)\}", + open(os.path.join(ROOT, KERNEL_WORDS_SRC)).read()).group(1)): + found.setdefault(k, (KERNEL_WORDS_SRC, "", "")) + return found + + +def read_table(path): + table = {} + if os.path.exists(path): + for l in open(path).read().split("\n"): + # a comment is a line beginning with # that has no tab: a row always has tabs, and + # some words begin with # (#, #>, #S) + if not l.strip() or (l.startswith("#") and "\t" not in l): + continue + f = l.split("\t") + while len(f) < 3: + f.append("") + table[f[0]] = (f[1].strip(), f[2].strip()) + return table + + +def main(): + if len(sys.argv) < 2: + sys.exit(__doc__) + mode = sys.argv[1] + if mode == "words": + print("\n".join(live_words())) + return + path = sys.argv[2] + table = read_table(path) + words = live_words() + if mode == "draft": + defs = definitions() + new = [w for w in words if w not in table] + with open(path, "a") as f: + if not table: + f.write("# docs/reference/v4-dictionary.tsv -- the dictionary of v4 (#157): name, stack notation,\n" + "# description, tab-separated. The names are the live system's (v4/tools/mkdictionary.py\n" + "# check refuses a missing or a stale one). A row whose description starts TODO is a draft.\n") + for w in sorted(new): + rel, stack, text = defs.get(w, ("", "", "")) + desc = ("TODO " + text) if text else "TODO" + f.write(f"{w}\t{stack}\t{desc}\n") + print(f"mkdictionary: {len(words)} live words; {len(new)} drafted into {path}") + return + if mode == "check": + bad = [] + for w in words: + if w not in table: + bad.append(f"no entry: {w}") + elif not table[w][0] or not table[w][1] or table[w][1].startswith("TODO"): + bad.append(f"unfinished: {w}") + for w in table: + if w not in words: + bad.append(f"not a live word: {w}") + if bad: + print("\n".join(bad)) + print(f"mkdictionary: {len(bad)} problem(s) in {path} ({len(words)} live words)") + sys.exit(1) + print(f"mkdictionary: {path} covers all {len(words)} live words") + return + if mode == "md": + defs = definitions() + out = sys.argv[3] + with open(out, "w") as f: + f.write("# The dictionary of v4\n\n") + f.write(f"Every word StarForth v4 boots with -- {len(words)} of them -- in alphabetical order: its " + "stack notation (what it takes and leaves: `( before -- after )`), what it does, and " + "where it is defined.\n\n") + f.write("| Word | Stack | Description | Defined in |\n|---|---|---|---|\n") + for w in sorted(words, key=lambda x: (x.upper(), x)): + stack, desc = table.get(w, ("", "")) + rel = defs.get(w, ("", "", ""))[0] + cell = lambda x: x.replace("|", "\\|") + f.write(f"| `{cell(w)}` | `{cell(stack)}` | {cell(desc)} | `{rel}` |\n") + print(f"mkdictionary: {out}, {len(words)} words") + return + sys.exit(__doc__) + + +if __name__ == "__main__": + main() -- 2.54.0 From 1c9a65c970d651923441fb6c9e1de33a8ab6dff6 Mon Sep 17 00:00:00 2001 From: Claude Date: Tue, 10 Nov 2026 11:26:58 +0000 Subject: [PATCH 03/10] docs(dictionary): batch 1 -- the symbols, the digits, A..C (#157) [skip ci] Co-Authored-By: Claude Opus 5.5 Claude-Session: https://claude.ai/code/session_01GE3SDvQn5EoxdGufk84zz2 --- docs/reference/v4-dictionary.tsv | 252 +++++++++++++++---------------- 1 file changed, 126 insertions(+), 126 deletions(-) diff --git a/docs/reference/v4-dictionary.tsv b/docs/reference/v4-dictionary.tsv index 0999e72d..63e27537 100644 --- a/docs/reference/v4-dictionary.tsv +++ b/docs/reference/v4-dictionary.tsv @@ -1,132 +1,132 @@ # docs/reference/v4-dictionary.tsv -- the dictionary of v4 (#157): name, stack notation, # description, tab-separated. The names are the live system's (v4/tools/mkdictionary.py # check refuses a missing or a stale one). A row whose description starts TODO is a draft. -! TODO -# TODO -#> TODO -#S TODO -' ( -- addr ) TODO FORTH-79: the next word's parameter field address; if compiling, compiled as a literal -( TODO ---- comments (5.15) ------------------------------------------------------- PAREN skips to the closing parenthesis; BACKSLASH skips the rest of the line. -(.") TODO The run time of ." : print the string after the call, go on after it. It takes the return address off before it calls anything and keeps the count there instead, so a word that prints text goes no deeper than one that calls any other word. -(ABORT") ( flag -- ) TODO the run time of ABORT" : if the flag is not zero print the string after the call, start a new line and ABORT; otherwise go on after the string. -(BITS) ( -- n ) TODO -(CONSOLE) TODO -(LOG") ( level -- ) TODO the run time of LOG-xxx" : the text is after the call, as for ." ; log it at that level and go on after it. -(LOST) TODO -(ME) TODO -(NB) TODO -(REFUSED) TODO -(S") TODO The run time of S" : leave the address and length of the string after the call, and go on after it. -(SEAL) ( -- ) TODO WHAT IS IN THE DICTIONARY NOW IS THE SYSTEM: COLD comes back to it and FORGET will not go below it. Whoever has loaded a node's capsules has it do this (system.f18, (BOOT) and FENCE). -(UD) TODO -(WORD-DEFINED) TODO -(WORD-FORGOTTEN) TODO -* TODO The low cell of the product, by N steps of +* with nothing but the count on the return stack. UM* drop gives the same cell but needs two more return entries, which a word inside two DO loops does not have (D-2). The low cell is exact: what +* loses at the top of T (D-3) takes N shifts to reach the bit that moves into A, and the loop ends first. -*/ TODO -*/MOD TODO -+ TODO ---- arithmetic ---- -+! TODO -+LOOP TODO -, TODO , calls nothing: the code generator calls it from as deep as anything goes. -- TODO --! TODO ---> ( -- ) TODO go on with the next block. Only in a block being loaded: at the terminal it is an error (code 14). --ROT TODO --TRAILING TODO -. TODO -." TODO FORTH-79: ." text" prints the text -- now, if interpreting; when the word it is compiled into runs, if compiling. -.R TODO -.S ( -- ) TODO as v3: the depth between < and >, then every value on the stack, the deepest first, each followed by a blank; then a new line. The stack is left as it was. It reads each value with PICK, so it needs a few cells of the stack free to work in. -/ TODO -/MOD TODO -0< TODO -0<> TODO ---- comparison ---- -0= TODO -0> TODO -1+ TODO -1- TODO -2! TODO -2* TODO -2+ TODO -2, TODO -2- TODO -2/ TODO -2>R TODO -2@ TODO ---- memory ---- -2DROP TODO -2DUP TODO -2OVER TODO -2R> TODO -2R@ TODO -2ROT TODO -2SWAP TODO ---- stack ---- -79-STANDARD TODO FORTH-79: execute to be sure a FORTH-79 system is there. It is: nothing happens. (v3's prints two lines and leaves a flag.) -: TODO FORTH-79: start a definition; it cannot be found until ; ends it. The CONTEXT vocabulary is set to the CURRENT one. (C)+2 holds LATEST while the entry is made, to tell whether one was. -; TODO FORTH-79: compile the return, let the word be found, stop compiling -< TODO -<# TODO ---- pictured output ---- -<= TODO -<> TODO -= TODO -> TODO ->= TODO ->BODY TODO ->IN TODO ->LINK TODO ->NAME TODO ->R TODO ---- return stack: in line, and only inside a definition ---- -? ( addr -- ) TODO print what the cell holds -?DO TODO -?DUP TODO -?TERMINAL TODO ---- strings and memory ---- -@ TODO ---- memory ---- -ABORT TODO FORTH-79: clear the data and return stacks, set execution mode, return control to the terminal. As v3, the line it stops ends with " ok". Both are emptied before anything is called: ABORT may be run with the return stack full. -ABORT" ( flag -- ) TODO ABORT" text" as v3 (it is FORTH-83's, not FORTH-79's): if the flag is not zero, print the text and ABORT. -ABS TODO -ACL-ALLOW! TODO -ACL-ALLOW@ TODO -ACL-HEAT@ ( xt -- heat ) TODO how often the word has been used. The node's heat counters are not readable from a programme yet (D-6): 0. -ACL-HOOK TODO -ACL-INHERIT ( src dst -- ) TODO as v3: dst takes src's mode; it is not pinned, its TTL is 0 and it is allowed. This is the one word that unpins. -ACL-INIT-PRIMITIVES ( -- ) TODO as v3: do that to every word in the dictionary, in every vocabulary -ACL-MODE! TODO -ACL-MODE@ TODO -ACL-PIN TODO -ACL-PINNED? TODO -ACL-TTL! TODO as v3: below 0 is 0. The TTL is sixteen bits here: above 65535 is 65535. -ACL-TTL@ TODO -ACL-WORD-ID ( xt -- n ) TODO a number for the word that never changes: its address -AGAIN TODO -ALIGN TODO -ALLOT TODO -AND TODO ---- logic and comparison ---- -AWAIT ( node -- how ) TODO WAIT FOR THE NODE'S WORD OF HOW TEXT ENDED: 0 QUIT, 1 completed, 2 an error. (AWAIT-FROM) holds the node while this node waits, and 0 otherwise. It sleeps, executing nothing, until a message comes; then it passes on what is for other nodes, (SERVE), and goes through what is for itself (MESH.md 7d.4): the answer -- a message of type 3 from that node -- is taken, and the wait is over; a NACK is taken: about that node, the wait ends in error 19, Message refused; about another it is counted, as at any time; a GONE is taken: believed (its centre sent it: (GONE?)) and about that node, the wait ends in error 20, Node gone; text from its console ends the wait in error 21, Interrupted (7b.6). The text is not taken: it stays on its wire and is done when this text has been finished with. It is the one way to end a wait on a node that is alive and never answers, short of Hera's killing that node; anything else stays on its wire, in its order, for when this node has nothing to do. WHAT HAS COME ALREADY is on the wires and is found the first time through: the node looks before it sleeps. A message it left on a wire does not keep it awake. The stack what it does needs is tried first, (ROOM), core.f18: text that has left too little ends "Stack overflow" with nothing taken off a wire. -BASE TODO -BEGIN TODO A loop that tests at its end jumps back with the flag still there, so it jumps back to a drop laid just ahead of the body: BEGIN a UNTIL jump L0 Ld: drop L0: a if Ld drop BEGIN a WHILE b REPEAT jump L0 Ld: drop L0: a if Lx drop b jump L0 Lx: drop BEGIN a AGAIN jump L0 Ld: drop L0: a jump L0 The control-flow stack holds Ld, L0 and 3. -BL TODO ---- in-line addresses and constants ---- -BLANK TODO -BLK TODO -BLOCK ( n -- baddr ) TODO FORTH-79: the address of a buffer that holds block n, read from storage if it is not already in one. -BUFFER ( n -- baddr ) TODO FORTH-79: the address of a buffer assigned to block n. What the buffer holds is not read from storage. -BYE TODO -C! TODO -C, TODO -C@ TODO -CASE TODO -CELLS TODO -CFA TODO -CMOVE TODO ---- section 5.9: strings ------------------------------------------------ -CMOVE> TODO -COLD ( -- ) TODO the system as it was when the capsule had just been loaded: everything defined since is gone, there is one vocabulary, numbers are decimal, the block buffers are empty. Then as WARM. -COMPARE TODO -COMPILE TODO COMPILE xxx FORTH-79: when the word this is in runs, xxx is compiled -CONSTANT ( n -- ) TODO FORTH-79: an entry whose word leaves n -CONTEXT TODO ---- vocabularies (FORTH-79) ---------------------------------------------------- A vocabulary is two cells: its head -- the xt of its newest entry, or 0 -- and the address of the vocabulary defined before it, or 0. VOC-LINK holds the newest one's address. FORTH's head is (LATEST) and it is in no chain. -CONVERT ( d1 baddr1 -- d2 baddr2 ) TODO FORTH-79: the characters from baddr1 + 1 on are taken as digits in the current BASE, each accumulated into the double after it is multiplied by BASE, until one is not a digit; baddr2 is its address. (P)+2 holds the digit while the double is multiplied and (P)+5 the address, so nothing waits on the return stack across the calls. -COUNT TODO -CR TODO -CREATE TODO FORTH-79: an entry whose word leaves the address of its parameter field -CURRENT TODO +! ( x addr -- ) Store x in the cell at addr. +# ( ud1 -- ud2 ) Pictured output: divide ud1 by BASE, put the remainder's digit in front of the string being built, leave the quotient. +#> ( ud -- baddr u ) End pictured output: drop ud and leave the string built since <#. +#S ( ud -- 0 0 ) Pictured output: convert digits with # until the number is zero (at least one digit). +' ( -- addr ) ' xxx: the parameter field address of xxx; compiled as a literal when compiling. +( ( -- ) ( text): a comment, ended by the first ); immediate. +(.") ( -- ) The run time of .": print the string compiled after the call, and go on after it. +(ABORT") ( flag -- ) The run time of ABORT": if flag is not zero, print the string compiled after the call and ABORT. +(BITS) ( -- n ) The number of bits in a cell, counted at run time (forth79.4th; used by U/MOD). +(CONSOLE) ( -- addr ) Variable: the node that what this node prints is sent to, as messages (core.f18). +(LOG") ( level -- ) The run time of LOG-xxx": print the string compiled after the call if level is at or below LOG-LEVEL. +(LOST) ( -- addr ) Variable: a count of the messages this node has dropped -- nowhere to send them, or no room. +(ME) ( -- addr ) Variable: this node's number in the mesh. +(NB) ( -- n ) Constant: the bits in a cell, (BITS) counted once at load (forth79.4th). +(REFUSED) ( -- addr ) Variable: a count of the messages that came back to this node, refused. +(S") ( -- baddr u ) The run time of S": leave the address and length of the string compiled after the call. +(SEAL) ( -- ) What is in the dictionary now is the system: COLD returns to it and FORGET will not go below it. +(UD) ( -- addr ) Variable: U/MOD's divisor while it divides (forth79.4th). +(WORD-DEFINED) ( -- addr ) Variable: the hook told when an entry is made, with the xt its code will be at. +(WORD-FORGOTTEN) ( -- addr ) Variable: the hook told when entries are forgotten, with the word address they went from. +* ( n1 n2 -- n3 ) Multiply: the low cell of the product. +*/ ( n1 n2 n3 -- n4 ) n1 times n2 divided by n3, through a double-cell intermediate product. +*/MOD ( n1 n2 n3 -- n4 n5 ) n1 times n2 divided by n3, through a double product: n4 the remainder, n5 the quotient. ++ ( n1 n2 -- n3 ) Add. ++! ( n addr -- ) Add n to the cell at addr. ++LOOP ( n -- ) Compile-only: end a DO loop, adding n to the index; the loop ends when the index crosses the limit. +, ( x -- ) Compile x into the next cell of the dictionary. +- ( n1 n2 -- n3 ) Subtract: n1 minus n2. +-! ( n addr -- ) Subtract n from the cell at addr. +--> ( -- ) Go on loading with the next block; only in a block being loaded. +-ROT ( x1 x2 x3 -- x3 x1 x2 ) Rotate the top three values the other way from ROT. +-TRAILING ( baddr u1 -- baddr u2 ) The string without its trailing blanks. +. ( n -- ) Print n in the current BASE, then a blank. +." ( -- ) ." text": print the text -- now when interpreting, when the word runs when compiling. +.R ( n width -- ) Print n right-justified in a field of width characters. +.S ( -- ) Print the stack's depth and every value on it, the stack unchanged. +/ ( n1 n2 -- n3 ) Divide: the quotient of n1 by n2. Division by zero is error 11. +/MOD ( n1 n2 -- n3 n4 ) Divide n1 by n2: n3 the remainder, n4 the quotient. +0< ( n -- flag ) True if n is negative. +0<> ( x -- flag ) True if x is not zero. +0= ( x -- flag ) True if x is zero. +0> ( n -- flag ) True if n is greater than zero. +1+ ( n -- n+1 ) Add one. +1- ( n -- n-1 ) Subtract one. +2! ( x1 x2 addr -- ) Store the cell pair at addr: x2 at addr, x1 in the next cell. +2* ( n -- 2n ) Shift left one bit. +2+ ( n -- n+2 ) Add two. +2, ( x1 x2 -- ) Compile the cell pair into the dictionary. +2- ( n -- n-2 ) Subtract two. +2/ ( n -- n/2 ) Shift right one bit, keeping the sign. +2>R ( x1 x2 -- ) ( R: -- x1 x2 ) Compile-only: move the pair to the return stack. +2@ ( addr -- x1 x2 ) Fetch the cell pair at addr. +2DROP ( x1 x2 -- ) Drop the top pair. +2DUP ( x1 x2 -- x1 x2 x1 x2 ) Copy the top pair. +2OVER ( x1 x2 x3 x4 -- x1 x2 x3 x4 x1 x2 ) Copy the second pair to the top. +2R> ( -- x1 x2 ) ( R: x1 x2 -- ) Compile-only: move the pair from the return stack. +2R@ ( -- x1 x2 ) ( R: x1 x2 -- x1 x2 ) Compile-only: copy the pair on the return stack. +2ROT ( x1 x2 x3 x4 x5 x6 -- x3 x4 x5 x6 x1 x2 ) Rotate the top three pairs. +2SWAP ( x1 x2 x3 x4 -- x3 x4 x1 x2 ) Exchange the top two pairs. +79-STANDARD ( -- ) Run to be sure a FORTH-79 system is there; it is. +: ( -- ) : xxx: start a definition of xxx; it cannot be found until ; ends it. +; ( -- ) Compile-only: end the definition -- compile its return, let it be found, stop compiling. +< ( n1 n2 -- flag ) True if n1 is less than n2. +<# ( -- ) Begin pictured numeric output. +<= ( n1 n2 -- flag ) True if n1 is less than or equal to n2. +<> ( x1 x2 -- flag ) True if x1 and x2 differ. += ( x1 x2 -- flag ) True if x1 equals x2. +> ( n1 n2 -- flag ) True if n1 is greater than n2. +>= ( n1 n2 -- flag ) True if n1 is greater than or equal to n2. +>BODY ( xt -- addr ) The parameter field of the word whose execution token is xt. +>IN ( -- addr ) Variable: the offset in the input buffer of the next character to read. +>LINK ( xt -- lfa ) The link field of the entry whose execution token is xt. +>NAME ( xt -- nfa ) The name field of the entry whose execution token is xt. +>R ( x -- ) ( R: -- x ) Compile-only: move x to the return stack. +? ( addr -- ) Print the number in the cell at addr. +?DO ( limit index -- ) Compile-only: begin a DO loop that is skipped when limit equals index. +?DUP ( x -- x x | 0 ) Copy x if it is not zero. +?TERMINAL ( -- flag ) True if a key has been pressed and is waiting. +@ ( addr -- x ) Fetch the cell at addr. +ABORT ( -- ) Empty the data and return stacks, set execution mode, return control to the terminal. +ABORT" ( flag -- ) ABORT" text": if flag is not zero, print the text and ABORT. +ABS ( n -- u ) The absolute value. +ACL-ALLOW! ( flag xt -- ) Set the word's allow flag (the word-level ACL); nothing on a pinned word. +ACL-ALLOW@ ( xt -- flag ) The word's allow flag. +ACL-HEAT@ ( xt -- heat ) How often the word has been used: its heat counter. +ACL-HOOK ( -- addr ) Variable: the execution token of the ACL recheck word, or 0 for none. +ACL-INHERIT ( src dst -- ) dst takes src's ACL mode; it is not pinned, its TTL is 0. +ACL-INIT-PRIMITIVES ( -- ) Set every word in every vocabulary to its starting ACL fields. +ACL-MODE! ( mode xt -- ) Set the word's ACL mode; nothing on a pinned word. +ACL-MODE@ ( xt -- mode ) The word's ACL mode. +ACL-PIN ( xt -- ) Pin the word: its ACL fields can no longer be changed. One-way. +ACL-PINNED? ( xt -- flag ) True if the word is pinned. +ACL-TTL! ( n xt -- ) Set the word's ACL time-to-live (0 to 65535); nothing on a pinned word. +ACL-TTL@ ( xt -- n ) The word's ACL time-to-live. +ACL-WORD-ID ( xt -- n ) A number for the word that never changes: its address. +AGAIN ( -- ) Compile-only: end a BEGIN loop that repeats for ever. +ALIGN ( -- ) Align the dictionary pointer to the next cell. +ALLOT ( n -- ) Reserve n cells of dictionary space. +AND ( x1 x2 -- x3 ) Bitwise and. +AWAIT ( node -- how ) Wait for the node's word on how its text ended: 0 QUIT, 1 completed, otherwise an error. +BASE ( -- addr ) Variable: the number base for conversion in and out. +BEGIN ( -- ) Compile-only: mark the start of a loop (UNTIL, WHILE ... REPEAT, AGAIN). +BL ( -- c ) Constant: the blank character, 32. +BLANK ( baddr u -- ) Fill u bytes from baddr with blanks. +BLK ( -- addr ) Variable: the block being interpreted, or 0 for the terminal. +BLOCK ( n -- baddr ) A buffer holding block n, read from storage if it is not already in one. +BUFFER ( n -- baddr ) A buffer assigned to block n, not read from storage. +BYE ( -- ) A cold restart of the machine (the kernel's word). +C! ( c baddr -- ) Store the byte c at baddr. +C, ( c -- ) Compile the byte c into the dictionary. +C@ ( baddr -- c ) Fetch the byte at baddr. +CASE ( -- ) Compile-only: begin a CASE ... OF ... ENDOF ... ENDCASE selection. +CELLS ( n1 -- n2 ) n1 cells, in address units. +CFA ( pfa -- cfa ) The code field address from the parameter field address. +CMOVE ( baddr1 baddr2 u -- ) Copy u bytes from baddr1 to baddr2, lowest first. +CMOVE> ( baddr1 baddr2 u -- ) Copy u bytes from baddr1 to baddr2, highest first (for overlapping moves up). +COLD ( -- ) Return the system to what it was when its capsules had just been loaded. +COMPARE ( baddr1 u1 baddr2 u2 -- n ) Compare two strings: 0 if equal, -1 if the first is lower, 1 if higher. +COMPILE ( -- ) COMPILE xxx: when the word containing this runs, compile xxx. +CONSTANT ( n -- ) CONSTANT xxx: define xxx, which leaves n. +CONTEXT ( -- addr ) Variable: the vocabulary searched first. +CONVERT ( d1 baddr1 -- d2 baddr2 ) Convert the digits from baddr1+1 into d1 in BASE; baddr2 is the first character that is not a digit. +COUNT ( baddr1 -- baddr2 u ) From a counted string: its first character's address and its length. +CR ( -- ) Print a new line. +CREATE ( -- ) CREATE xxx: an entry whose word leaves the address of its parameter field. +CURRENT ( -- addr ) Variable: the vocabulary new definitions go into. D+ TODO ---- section 5.7: doubles ------------------------------------------------ D- TODO ---- doubles and mixed ---- D. TODO -- 2.54.0 From e3fc57fb17fe30e418c142f3d4995909fc08b7ea Mon Sep 17 00:00:00 2001 From: Claude Date: Tue, 10 Nov 2026 11:28:10 +0000 Subject: [PATCH 04/10] docs(dictionary): batch 2 -- D..P (#157) [skip ci] Co-Authored-By: Claude Opus 5.5 Claude-Session: https://claude.ai/code/session_01GE3SDvQn5EoxdGufk84zz2 --- docs/reference/v4-dictionary.tsv | 222 +++++++++++++++---------------- 1 file changed, 111 insertions(+), 111 deletions(-) diff --git a/docs/reference/v4-dictionary.tsv b/docs/reference/v4-dictionary.tsv index 63e27537..5ae98578 100644 --- a/docs/reference/v4-dictionary.tsv +++ b/docs/reference/v4-dictionary.tsv @@ -127,117 +127,117 @@ COUNT ( baddr1 -- baddr2 u ) From a counted string: its first character's addres CR ( -- ) Print a new line. CREATE ( -- ) CREATE xxx: an entry whose word leaves the address of its parameter field. CURRENT ( -- addr ) Variable: the vocabulary new definitions go into. -D+ TODO ---- section 5.7: doubles ------------------------------------------------ -D- TODO ---- doubles and mixed ---- -D. TODO -D.R TODO -D0< TODO -D0= TODO -D2* TODO -D2/ TODO -D< TODO -D= TODO -DABS TODO -DECIMAL TODO -DEFAULT-ROUTE ( port -- ) TODO the port, by its number, leads toward every node not told of -DEFER TODO -DEFER@ TODO -DEFINITIONS TODO FORTH-79: new definitions go into the CONTEXT vocabulary from now on. -DEPTH TODO FORTH-79: how many values were on the stack before DEPTH -DMAX TODO -DMIN TODO -DNEGATE TODO -DO TODO -DOES> TODO -DROP TODO -DUMP TODO -DUP TODO ---- stack ---- -ELSE TODO -EMIT TODO EMIT puts the character with what has been printed. Everything the node prints goes through EMIT. A is kept: the words that print have always been free to use it across an EMIT. As v3, only the low 8 bits of the character are printed. A BUFFER FOUND FULL is sent first. It is full when a flush of it ended in a fault -- the stack too deep to begin a message -- and what is printed next is that fault's own message: put where (OUT^) points, it would go past the buffer's end, and (OUT^) would never again be at the end to be flushed (MESH.md 7c.6). -EMPTY-BUFFERS TODO FORTH-79: forget what is in the buffers; nothing is written. -ENCLOSE ( baddr c -- baddr n1 n2 n3 ) TODO in the zero-terminated string at baddr: n1 the offset of the first character that is not c, n2 the offset of the first c after it, n3 the offset of the first character after that run of c. -ENDCASE TODO -ENDOF TODO -ERASE TODO -EXECUTE TODO -EXIT TODO FORTH-79: return from the definition at this point -EXPECT ( baddr n -- ) TODO FORTH-79: characters from the terminal are stored from baddr upward until a new-line or until n have been received; the new-line is taken but not stored. A zero is added after the text, so the buffer must hold n + 1 bytes. No action for n <= 0. SPAN is how many characters were stored (SPAN is not FORTH-79; v3 has it). A line longer than n leaves the rest, its new-line included, to be read next. It is what the prompt reads every line with, with whatever the user has left on the stack beneath it, so it works in (P) -- (P)+0 where the next character goes, (P)+1 how many more may come, (P)+2 where the first went -- and has at most three cells of its own on the data stack (D-2). -FALSE TODO -FENCE TODO -FILL TODO -FIND ( -- xt | 0 ) TODO FORTH-79: the compilation address of the next word in the input stream, or 0 if it is not in the dictionary. -FLUSH TODO As v3: the same. -FORGET TODO FORGET xxx FORTH-79: remove xxx and every word defined after it, in whatever vocabulary; the space they took is free again. A vocabulary that goes is no longer CONTEXT or CURRENT: FORTH is. A word that is not there is an error, and so is one below FENCE, which is where the capsule's own words are (code 10, "Protected word"). -FORTH TODO FORTH-79: the primary vocabulary; it becomes the CONTEXT vocabulary. -GONE ( n node -- ) TODO tell the node that node n is gone. It acts on that only if this node is its centre: it forgets the way to n, and if it is waiting for n's answer the wait ends. -HERE TODO -HEX TODO -HIDDEN TODO -HOLD TODO -I TODO -IF TODO Native if leaves the flag, so both ways out of it drop it (section 2): IF a THEN if L1 drop a jump L2 L1: drop L2: IF a ELSE b THEN if L1 drop a jump L2 L1: drop b L2: -IMMEDIATE TODO -INTERPRET ( -- ) TODO FORTH-79: take words from the input stream until it is exhausted. A word in the dictionary is executed, or, when compiling and not immediate, compiled. Anything else must be a number in the current BASE: it is left on the stack, or compiled as a literal. What is neither, or a compile-only word met while interpreting, abandons the line, with v3's message: UNKNOWN WORD: 'xxx' xxx: compile-only -INVERT TODO -IS TODO -J TODO -KERNEL-WORD ( n -- ) TODO KERNEL-WORD xxx A WORD THAT ASKS THE KERNEL (docs/v4.0.0/ ENGINE.md 3.3). xxx is made a word whose whole body is "write n to the port": n is the number of a request, the port is the node's way to its kernel, and the write blocks the node until the kernel has served it (DECOMPOSITION.md section 6). xxx's arguments are whatever it finds on the data stack and its results are what the kernel leaves there. This is what a word that is a C function in v3 -- BIRTH, BLOCK, BYE -- is on a node. Not FORTH-79 and not in v3, where such a word is registered from C. The constant (PORT) is the port's word address. -KEY TODO -LATEST TODO -LEAVE TODO -LFA TODO -LINK> TODO -LIST TODO ---- LIST ---------------------------------------------------------------------- ( n -- ) as v3: a new line, "Block n", then sixteen lines of 64 characters, each after its number, and an empty line. SCR is left holding n. A character that is not printable is shown as a blank. (Q)+0 is the line, (Q)+1 the column, (Q)+2 the block's number. -LITERAL ( x -- ) TODO FORTH-79: if compiling, compile x as a literal -LOAD ( n -- ) TODO FORTH-79: interpret block n, then go on with what follows. -LOG-DEBUG TODO -LOG-DEBUG" TODO -LOG-DEBUG-STR ( baddr u -- ) TODO -LOG-ERROR TODO -LOG-ERROR" TODO -LOG-ERROR-STR ( baddr u -- ) TODO -LOG-INFO TODO -LOG-INFO" TODO -LOG-INFO-STR ( baddr u -- ) TODO -LOG-LEVEL! ( n -- ) TODO as v3: a level below 0 is 0, one above 4 is 4 -LOG-LEVEL@ TODO -LOG-TEST TODO -LOG-TEST" TODO -LOG-TEST-STR ( baddr u -- ) TODO -LOG-WARN TODO -LOG-WARN" TODO -LOG-WARN-STR ( baddr u -- ) TODO -LOOP TODO -LSHIFT TODO ---- shifts ---- As v3, a count that is negative or as large as the cell is wide is an error (code 9, "Shift count out of range"). -M* TODO The product's sign waits in (F), not on the return stack, for the same reason as SM/REM's. -M+ TODO -M- TODO -M/MOD TODO -MAX TODO -MIN TODO -MINT-ZUSE TODO -MOD TODO -MOVE TODO FORTH-79: n cells from addr1 to addr2, the cell at addr1 first. -NAME> TODO -NEGATE TODO -NEIGHBOUR ( node port -- ) TODO the node on the other end of the port, by its number; 0: none, or a device. Whoever wires the node tells it, and tells it again when the wiring changes. It is how a node knows who its centre is, the one node whose GONE it believes: (CENTRE). -NFA TODO -NO-ROUTE ( node -- ) TODO forget the way to that node, if one is known: what is for it then goes by the way for every node not told of, if there is one. The last way in the table takes its place. -NO-ROUTES ( -- ) TODO forget every way: for when the wiring has changed -NODE-ERROR TODO FORTH-79: clear the return stack, set execution mode, return control to the terminal; no message is given. The data stack is left as it is. NODE-ERROR, by name: how a definition written in FORTH raises an error. A store of a code that is not zero is the trap of D-18 -- the word goes no further, the message for the code is printed (-1: the word has printed its own) and the line ends with ERROR. The codes are listed in core.f18. -NOP TODO -NOT TODO -NUMBER ( baddr -- d ) TODO FORTH-79. What is not a number gives 0 and sets NODE-ERROR. -OCTAL TODO -OF TODO -OR TODO -ORDER ( -- ) TODO as v3's, but true: where names are looked for, and where new ones go -OVER TODO -PAD TODO -PAGE ( -- ) TODO clear the screen and put the cursor at the top, as v3: ESC [2J ESC [H -PFA TODO -PICK ( n -- x ) TODO FORTH-79: a copy of the n-th value, not counting n itself. 1 PICK is DUP, 2 PICK is OVER. n less than one is an error. -PORT! ( w port -- ) TODO write a cell to the port, by its number. It waits until the neighbour has taken it. This is how a node is sent a capsule of F18 code (docs/v4.0.0/MESH.md section 5): the cells go as they are, not as a message, to a node that is executing from its ports. +D+ ( d1 d2 -- d3 ) Add two double-cell numbers. +D- ( d1 d2 -- d3 ) Subtract d2 from d1. +D. ( d -- ) Print the double-cell number d, then a blank. +D.R ( d width -- ) Print d right-justified in a field of width characters. +D0< ( d -- flag ) True if d is negative. +D0= ( d -- flag ) True if d is zero. +D2* ( d1 -- d2 ) Shift d1 left one bit. +D2/ ( d1 -- d2 ) Shift d1 right one bit, keeping the sign. +D< ( d1 d2 -- flag ) True if d1 is less than d2. +D= ( d1 d2 -- flag ) True if d1 equals d2. +DABS ( d -- ud ) The absolute value of d. +DECIMAL ( -- ) Set BASE to ten. +DEFAULT-ROUTE ( port -- ) The port, by its number, leads toward every node this node has not been told of. +DEFER ( -- ) DEFER xxx: define xxx, whose action is set later by IS; run unset, it is error 15. +DEFER@ ( -- xt ) DEFER@ xxx: the execution token xxx runs now; compiled when compiling. +DEFINITIONS ( -- ) New definitions go into the CONTEXT vocabulary from now on. +DEPTH ( -- n ) The number of values on the stack before DEPTH. +DMAX ( d1 d2 -- d3 ) The greater of two doubles. +DMIN ( d1 d2 -- d3 ) The lesser of two doubles. +DNEGATE ( d -- -d ) Negate a double-cell number. +DO ( limit index -- ) Compile-only: begin a counted loop, ended by LOOP or +LOOP. +DOES> ( -- ) Compile-only: in a defining word, what the words it defines do, with their parameter field's address on the stack. +DROP ( x -- ) Drop the top value. +DUMP ( baddr u -- ) Print u bytes from baddr in the current BASE, with their addresses. +DUP ( x -- x x ) Copy the top value. +ELSE ( -- ) Compile-only: the other branch of IF ... ELSE ... THEN. +EMIT ( c -- ) Print the character c. +EMPTY-BUFFERS ( -- ) Forget what is in the block buffers; nothing is written. +ENCLOSE ( baddr c -- baddr n1 n2 n3 ) Find a word delimited by c in the string at baddr: n1 its start, n2 its end, n3 the offset after the delimiter. +ENDCASE ( x -- ) Compile-only: end a CASE selection, dropping the value if no OF matched. +ENDOF ( -- ) Compile-only: end an OF branch; go on after ENDCASE. +ERASE ( baddr u -- ) Fill u bytes from baddr with zeros. +EXECUTE ( xt -- ) Run the word whose execution token is xt. +EXIT ( -- ) Compile-only: return from the definition at this point. +EXPECT ( baddr n -- ) Read up to n characters, or a line, from the terminal into baddr; SPAN holds how many. +FALSE ( -- 0 ) Constant: the false flag, zero. +FENCE ( -- addr ) Variable: FORGET will not remove anything below the address it holds. +FILL ( baddr u c -- ) Fill u bytes from baddr with the byte c. +FIND ( -- xt | 0 ) FIND xxx: the execution token of xxx, from the input stream, or 0 if it is not found. +FLUSH ( -- ) Write every changed block (SAVE-BUFFERS). +FORGET ( -- ) FORGET xxx: remove xxx and every word defined after it, never below FENCE. +FORTH ( -- ) Make FORTH, the primary vocabulary, the CONTEXT vocabulary. +GONE ( n node -- ) Tell the node that node n is gone; it acts on that only if this node is its centre. +HERE ( -- addr ) The address of the next free dictionary cell. +HEX ( -- ) Set BASE to sixteen. +HIDDEN ( -- ) Hide the newest entry: it cannot be found until revealed. +HOLD ( c -- ) Pictured output: put the character c in front of the string being built. +I ( -- n ) Compile-only: the index of the innermost DO loop. +IF ( flag -- ) Compile-only: run what follows if flag is not zero, else skip to ELSE or THEN. +IMMEDIATE ( -- ) Make the newest definition run when compiling instead of being compiled. +INTERPRET ( -- ) Interpret words from the input stream until it is exhausted. +INVERT ( x1 -- x2 ) Invert every bit. +IS ( xt -- ) IS xxx: make the deferred word xxx run xt; compiled when compiling. +J ( -- n ) Compile-only: the index of the next outer DO loop. +KERNEL-WORD ( n -- ) KERNEL-WORD xxx: define xxx, a word that asks the kernel to do request n (as BYE, BLOCK). +KEY ( -- c ) Wait for a key and leave its character. +LATEST ( -- xt ) The newest entry of the CURRENT vocabulary. +LEAVE ( -- ) Compile-only: leave the innermost DO loop at its next LOOP or +LOOP. +LFA ( xt -- addr ) The link field of the entry (>LINK). +LINK> ( addr -- xt ) The entry that a link field points to. +LIST ( n -- ) Print block n as 16 lines of 64 characters; SCR holds n. +LITERAL ( x -- ) If compiling, compile x as a literal. +LOAD ( n -- ) Interpret block n, then go on with what follows. +LOG-DEBUG ( -- 4 ) Constant: the debug log level. +LOG-DEBUG" ( -- ) LOG-DEBUG" text": log the text at the debug level. +LOG-DEBUG-STR ( baddr u -- ) Log the string at the debug level. +LOG-ERROR ( -- 0 ) Constant: the error log level. +LOG-ERROR" ( -- ) LOG-ERROR" text": log the text at the error level. +LOG-ERROR-STR ( baddr u -- ) Log the string at the error level. +LOG-INFO ( -- 2 ) Constant: the info log level. +LOG-INFO" ( -- ) LOG-INFO" text": log the text at the info level. +LOG-INFO-STR ( baddr u -- ) Log the string at the info level. +LOG-LEVEL! ( n -- ) Set the log level, held to 0 .. 4: messages at or below it are printed. +LOG-LEVEL@ ( -- n ) The log level. +LOG-TEST ( -- 3 ) Constant: the test log level. +LOG-TEST" ( -- ) LOG-TEST" text": log the text at the test level. +LOG-TEST-STR ( baddr u -- ) Log the string at the test level. +LOG-WARN ( -- 1 ) Constant: the warning log level. +LOG-WARN" ( -- ) LOG-WARN" text": log the text at the warning level. +LOG-WARN-STR ( baddr u -- ) Log the string at the warning level. +LOOP ( -- ) Compile-only: end a DO loop, adding one to the index. +LSHIFT ( x1 u -- x2 ) Shift x1 left u bits; a count that is negative or a cell or more gives 0. +M* ( n1 n2 -- d ) Multiply: the double-cell product, signed. +M+ ( d1 n -- d2 ) Add a single-cell number to a double. +M- ( d1 n -- d2 ) Subtract a single-cell number from a double. +M/MOD ( d n1 -- n2 n3 ) Divide d by n1: n2 the remainder, n3 the quotient. +MAX ( n1 n2 -- n3 ) The greater of two numbers. +MIN ( n1 n2 -- n3 ) The lesser of two numbers. +MINT-ZUSE ( -- ) Mint the real Zuse onto a second stick: asks for the stick, writes nothing until YES (the kernel's word; STICK.md 6a). +MOD ( n1 n2 -- n3 ) The remainder of n1 divided by n2. +MOVE ( addr1 addr2 n -- ) Copy n cells from addr1 to addr2, the first cell first. +NAME> ( baddr -- xt ) The entry whose name field is at baddr. +NEGATE ( n -- -n ) Negate. +NEIGHBOUR ( node port -- ) Record the node on the other end of the port, by its number; 0: none. +NFA ( xt -- baddr ) The name field of the entry (>NAME). +NO-ROUTE ( node -- ) Forget the way to that node, if one is known. +NO-ROUTES ( -- ) Forget every way to every node: for when the wiring has changed. +NODE-ERROR ( -- addr ) Variable: storing a non-zero code raises that error -- the word goes no further and the line ends with ERROR. +NOP ( -- ) Do nothing. +NOT ( flag1 -- flag2 ) True if flag1 is zero (as 0=). +NUMBER ( baddr -- d ) Convert the counted string at baddr to a double; what is not a number gives 0 and sets NODE-ERROR. +OCTAL ( -- ) Set BASE to eight. +OF ( x1 x2 -- | x1 ) Compile-only: in CASE, run the branch to ENDOF if x1 equals x2. +OR ( x1 x2 -- x3 ) Bitwise or. +ORDER ( -- ) Print where names are looked for, and where new ones go. +OVER ( x1 x2 -- x1 x2 x1 ) Copy the second value to the top. +PAD ( -- baddr ) A scratch area for text. +PAGE ( -- ) Clear the screen and put the cursor at the top. +PFA ( xt -- addr ) The parameter field of the entry. +PICK ( n -- x ) A copy of the n-th value, not counting n itself: 1 PICK is DUP. +PORT! ( w port -- ) Write a cell to the port, by its number; waits until the neighbour reads it. Q.* TODO ---- multiply ---- Q.+ TODO ---- the double words under other names ---- Q.- TODO -- 2.54.0 From 135cccb37d450b38aa0e40e49732c99c11a6af8a Mon Sep 17 00:00:00 2001 From: Claude Date: Tue, 10 Nov 2026 11:29:20 +0000 Subject: [PATCH 05/10] docs(dictionary): batch 3 -- Q..Z and the brackets; every one of the 318 live words has its entry (#157) [skip ci] LSHIFT corrected (a count out of range is error 9, as DECOMPOSITION.md says, not 0). mkdictionary.py check: covers all 318 live words. Co-Authored-By: Claude Opus 5.5 Claude-Session: https://claude.ai/code/session_01GE3SDvQn5EoxdGufk84zz2 --- docs/reference/v4-dictionary.tsv | 164 +++++++++++++++---------------- 1 file changed, 82 insertions(+), 82 deletions(-) diff --git a/docs/reference/v4-dictionary.tsv b/docs/reference/v4-dictionary.tsv index 5ae98578..d53df2c8 100644 --- a/docs/reference/v4-dictionary.tsv +++ b/docs/reference/v4-dictionary.tsv @@ -208,7 +208,7 @@ LOG-WARN ( -- 1 ) Constant: the warning log level. LOG-WARN" ( -- ) LOG-WARN" text": log the text at the warning level. LOG-WARN-STR ( baddr u -- ) Log the string at the warning level. LOOP ( -- ) Compile-only: end a DO loop, adding one to the index. -LSHIFT ( x1 u -- x2 ) Shift x1 left u bits; a count that is negative or a cell or more gives 0. +LSHIFT ( x1 u -- x2 ) Shift x1 left u bits; a count that is negative or a cell wide or more is error 9. M* ( n1 n2 -- d ) Multiply: the double-cell product, signed. M+ ( d1 n -- d2 ) Add a single-cell number to a double. M- ( d1 n -- d2 ) Subtract a single-cell number from a double. @@ -238,84 +238,84 @@ PAGE ( -- ) Clear the screen and put the cursor at the top. PFA ( xt -- addr ) The parameter field of the entry. PICK ( n -- x ) A copy of the n-th value, not counting n itself: 1 PICK is DUP. PORT! ( w port -- ) Write a cell to the port, by its number; waits until the neighbour reads it. -Q.* TODO ---- multiply ---- -Q.+ TODO ---- the double words under other names ---- -Q.- TODO -Q./ TODO -Q.0 TODO -Q.0= TODO -Q.1 TODO -Q.< TODO -Q.= TODO -Q.> TODO -Q.ABS TODO -Q.COS TODO -Q.EXP TODO ---- e to the x ---- -Q.FROM-INT TODO ---- conversion and constants ---- -Q.LOG TODO ---- natural logarithm ---- -Q.MAX TODO -Q.MIN TODO -Q.NEG TODO -Q.PRINT TODO ---- printing ---- The integer part, a point, five digits, a blank; always in decimal. -Q.SCALE TODO -Q.SIN TODO -Q.SQRT TODO ---- square root ---- -Q.TO-INT TODO -QUERY ( -- ) TODO FORTH-79: up to 80 characters, or a line, into TIB; >IN to 0. -QUIT TODO -R> TODO -R@ TODO -REPEAT TODO -ROLL ( n -- ) TODO FORTH-79: the n-th value, not counting n itself, is taken out and put on top. 3 ROLL is ROT, 2 ROLL is SWAP, 1 ROLL does nothing. n less than one is an error. -ROT TODO -ROUTE ( node port -- ) TODO the port, by its number, leads toward that node -RSHIFT TODO -S" ( -- baddr u ) TODO S" text" as v3. In a definition the text is compiled into it. At the prompt it is copied to PAD, where it stays until PAD is used again: WORD's buffer is overwritten by the very next word of the line. -S>D TODO -SAVE-BUFFERS TODO FORTH-79: write every block that has been UPDATEd. -SCAN TODO -SCR TODO -SEARCH TODO -SEND ( baddr u node -- ) TODO send the node u characters of text to interpret, by the way this node knows to it. What it prints there goes to that node's console. With no way to the node it is error 12. -SEND-ON ( baddr u node port -- ) TODO the same by a port this node names, by its number, whatever ways it knows: for a neighbour that has no number of its own yet, to which text is sent as to node 0. -SIGN TODO -SKIP TODO -SMUDGE TODO -SOURCE ( -- baddr u ) TODO the input buffer and how much of it is in use -SPACE TODO -SPACES TODO ---- spaces and the number base ---- -SPAN TODO -STATE TODO -SWAP TODO -THEN TODO -THRU ( first last -- ) TODO as v3: LOAD each block from first to last. -TIB TODO -TRAVERSE ( baddr n -- baddr' ) TODO as v3: for n > 0, from a name's count byte to the byte after its last character; otherwise the address unchanged. -TRUE TODO -TYPE TODO -U* TODO -U. TODO -U.R TODO -U/MOD TODO -U< TODO -U> TODO -UM* TODO ---- section 4: multiply ------------------------------------------------ -UNLOOP TODO -UNTIL TODO -UPDATE TODO FORTH-79: mark the block most recently asked for as changed, so that it is written before its buffer is used for another. -VARIABLE TODO FORTH-79: CREATE and one cell. The standard leaves its first value to the programme; here it is 0. -VERSION ( -- ) TODO which system this is, and its cell width -VLIST TODO -VOCABULARY TODO -WARM ( -- ) TODO as v3's message, and then ABORT: both stacks emptied, the prompt -WHILE TODO -WITHIN TODO -WORD TODO -WORDS ( -- ) TODO the names in the CONTEXT vocabulary, the newest first, a blank after each, a new line whenever one has passed column 64. Hidden entries -- a definition under way -- are not shown. (Q)+0 is the column. -XOR TODO -[ TODO -['] TODO ['] xxx the same inside a definition, whatever STATE is; and [LITERAL], which v3 has as another name for LITERAL. -[COMPILE] TODO [COMPILE] xxx FORTH-79: compile xxx even though it is immediate -[LITERAL] TODO -\ TODO -] TODO +Q.* ( q1 q2 -- q3 ) Multiply two Q48.16 fixed-point numbers. +Q.+ ( q1 q2 -- q3 ) Add two Q48.16 numbers (D+). +Q.- ( q1 q2 -- q3 ) Subtract q2 from q1 (D-). +Q./ ( q1 q2 -- q3 ) Divide q1 by q2; by zero is error 11. +Q.0 ( -- q ) The Q48.16 zero, 0 0. +Q.0= ( q -- flag ) True if q is zero. +Q.1 ( -- q ) The Q48.16 one, 65536 0. +Q.< ( q1 q2 -- flag ) True if q1 is less than q2. +Q.= ( q1 q2 -- flag ) True if q1 equals q2. +Q.> ( q1 q2 -- flag ) True if q1 is greater than q2. +Q.ABS ( q1 -- q2 ) The absolute value. +Q.COS ( q1 -- q2 ) The cosine of q1, in radians. +Q.EXP ( q1 -- q2 ) e to the power q1. +Q.FROM-INT ( n -- q ) The integer n as a Q48.16 number. +Q.LOG ( q1 -- q2 ) The natural logarithm of q1; not above zero is error 12. +Q.MAX ( q1 q2 -- q3 ) The greater of two Q48.16 numbers. +Q.MIN ( q1 q2 -- q3 ) The lesser of two Q48.16 numbers. +Q.NEG ( q1 -- q2 ) Negate. +Q.PRINT ( q -- ) Print q: the integer part, a point, five digits, a blank; always in decimal. +Q.SCALE ( -- q ) The scale of the fraction, 65536 0 (1.0). +Q.SIN ( q1 -- q2 ) The sine of q1, in radians. +Q.SQRT ( q1 -- q2 ) The square root of q1; below zero is error 12. +Q.TO-INT ( q -- n ) The integer part of q. +QUERY ( -- ) Read up to 80 characters, or a line, from the terminal into TIB; set >IN to 0. +QUIT ( -- ) Empty the return stack, set execution mode and go back to the terminal, without a message. +R> ( -- x ) ( R: x -- ) Compile-only: move the top of the return stack to the data stack. +R@ ( -- x ) ( R: x -- x ) Compile-only: copy the top of the return stack. +REPEAT ( -- ) Compile-only: end a BEGIN ... WHILE ... REPEAT loop, jumping back to BEGIN. +ROLL ( n -- ) Move the n-th value, not counting n itself, to the top: 2 ROLL is ROT. +ROT ( x1 x2 x3 -- x2 x3 x1 ) Rotate the third value to the top. +ROUTE ( node port -- ) The port, by its number, leads toward that node. +RSHIFT ( x1 u -- x2 ) Shift x1 right u bits, filling with zeros; a count that is negative or a cell wide or more is error 9. +S" ( -- baddr u ) S" text": the text's address and length; compiled into the definition when compiling. +S>D ( n -- d ) Extend n to a double-cell number, keeping its sign. +SAVE-BUFFERS ( -- ) Write every block that has been UPDATEd. +SCAN ( baddr u c -- baddr' u' ) Skip to the first byte c in the string: what is left of it from there. +SCR ( -- addr ) Variable: the block most recently LISTed. +SEARCH ( baddr1 u1 baddr2 u2 -- baddr3 u3 flag ) Look for the second string in the first: if found, true and the first from there on. +SEND ( baddr u node -- ) Send the node u characters of text to interpret, by the way this node knows to it. +SEND-ON ( baddr u node port -- ) Send the node text to interpret by the port named, whatever ways are known. +SIGN ( n -- ) Pictured output: put a minus sign in front if n is negative. +SKIP ( baddr u c -- baddr' u' ) Skip the leading bytes c of the string: what is left of it. +SMUDGE ( -- ) Toggle the newest entry's hidden flag. +SOURCE ( -- baddr u ) The input buffer and how much of it is in use. +SPACE ( -- ) Print a blank. +SPACES ( n -- ) Print n blanks. +SPAN ( -- addr ) Variable: how many characters the last EXPECT read. +STATE ( -- addr ) Variable: not zero while compiling. +SWAP ( x1 x2 -- x2 x1 ) Exchange the top two values. +THEN ( -- ) Compile-only: end an IF ... THEN or IF ... ELSE ... THEN. +THRU ( first last -- ) LOAD each block from first to last. +TIB ( -- baddr ) The terminal input buffer. +TRAVERSE ( baddr n -- baddr' ) Step across a name field: for n > 0 from its count byte to its last byte, for n < 0 back. +TRUE ( -- -1 ) Constant: the true flag, all bits set. +TYPE ( baddr u -- ) Print the u characters at baddr. +U* ( u1 u2 -- ud ) The unsigned double-cell product (forth79.4th). +U. ( u -- ) Print u, unsigned, then a blank. +U.R ( u width -- ) Print u, unsigned, right-justified in a field of width characters. +U/MOD ( ud u1 -- u2 u3 ) Unsigned divide: u2 the remainder, u3 the quotient; by zero is error 11 (forth79.4th). +U< ( u1 u2 -- flag ) True if u1 is less than u2, unsigned. +U> ( u1 u2 -- flag ) True if u1 is greater than u2, unsigned. +UM* ( u1 u2 -- ud ) Multiply, unsigned: the double-cell product. +UNLOOP ( -- ) Compile-only: discard the innermost DO loop's index and limit, before an EXIT from inside it. +UNTIL ( flag -- ) Compile-only: end a BEGIN loop, jumping back to BEGIN while flag is zero. +UPDATE ( -- ) Mark the block most recently asked for as changed, so it is written. +VARIABLE ( -- ) VARIABLE xxx: define xxx, which leaves the address of one cell. +VERSION ( -- ) Print which system this is, and its cell width. +VLIST ( -- ) List the names in the CONTEXT vocabulary (WORDS). +VOCABULARY ( -- ) VOCABULARY xxx: define a vocabulary xxx; running it makes it the CONTEXT vocabulary. +WARM ( -- ) Print the system's message and ABORT: both stacks emptied, the prompt. +WHILE ( flag -- ) Compile-only: in BEGIN ... WHILE ... REPEAT, leave the loop if flag is zero. +WITHIN ( n low high -- flag ) True if low <= n < high. +WORD ( c -- baddr ) Read the next word from the input stream, delimited by c, as a counted string. +WORDS ( -- ) List the names in the CONTEXT vocabulary, the newest first. +XOR ( x1 x2 -- x3 ) Bitwise exclusive or. +[ ( -- ) Stop compiling: interpret what follows; immediate. +['] ( -- xt ) Compile-only: ['] xxx compiles xxx's execution token as a literal. +[COMPILE] ( -- ) Compile-only: [COMPILE] xxx compiles xxx even though it is immediate. +[LITERAL] ( x -- ) Another name for LITERAL, as v3 has it. +\ ( -- ) \ text: a comment to the end of the line; immediate. +] ( -- ) Start compiling what follows. -- 2.54.0 From 1a9b142d80c4b1c29a77d8e5211beee03402bfc6 Mon Sep 17 00:00:00 2001 From: Claude Date: Tue, 10 Nov 2026 11:31:07 +0000 Subject: [PATCH 06/10] docs: The Dictionary of v4 -- every live word, alphabetical, its stack notation and description; built and published with every release (#157) [skip ci] make dictionary -> build/docs/Dictionary.pdf (20 pages): mkdictionary.py checks docs/reference/v4-dictionary.tsv against the booted system's words first, so a stale dictionary fails the build; then Markdown, pandoc, docs/book/dictionary.tex (the books' preamble, its own title page: version and commit). Quoted FORTH tokens set as code, so their quotes print straight. The nightly's docs stage builds it; publish sends classifier dictionary; the Gitea release attaches it. Co-Authored-By: Claude Opus 5.5 Claude-Session: https://claude.ai/code/session_01GE3SDvQn5EoxdGufk84zz2 --- Makefile | 6 ++- docs/book/Makefile | 36 +++++++++++++++- docs/book/dictionary.tex | 42 +++++++++++++++++++ .../plans/2026-11-10-alpha4-documents.md | 6 +-- scripts/nightly.sh | 6 ++- v4/tools/mkdictionary.py | 3 ++ 6 files changed, 93 insertions(+), 6 deletions(-) create mode 100644 docs/book/dictionary.tex diff --git a/Makefile b/Makefile index b1a7db49..6cd81994 100644 --- a/Makefile +++ b/Makefile @@ -78,7 +78,7 @@ BOARD_DIR := $(if $(TARGET),$(BOARD_DIR_$(TARGET))) SUBMAKE := $(MAKE) --no-print-directory .DEFAULT_GOAL := all -.PHONY: all boot_image boot_images write_stick docs fabric clean clean-docs kernel boards help help-hosted sbom sbom-board \ +.PHONY: all boot_image boot_images write_stick docs fabric dictionary clean clean-docs kernel boards help help-hosted sbom sbom-board \ qemu qemu-esp qemu-gdb init # ARCH given on the command line and no TARGET: `clean` is the kernel's for that @@ -207,6 +207,10 @@ docs: fabric: @$(SUBMAKE) -f docs/book/Makefile fabric +# dictionary -- The Dictionary of v4 (#157): build/docs/Dictionary.pdf +dictionary: + @$(SUBMAKE) -f docs/book/Makefile dictionary + clean-docs: rm -rf build/docs diff --git a/docs/book/Makefile b/docs/book/Makefile index cc6c3d12..82b00b45 100644 --- a/docs/book/Makefile +++ b/docs/book/Makefile @@ -63,9 +63,18 @@ FABRIC_DIR := $(OUT)/fabric FABRIC_PDF := $(OUT)/BookOfFabric.pdf FABRIC_FRAGS := $(foreach n,fabric-0 fabric-1 fabric-2 fabric-3 fabric-3-5 fabric-3-6 fabric-3-7 fabric-4,$(GEN)/$(n).tex) -.PHONY: all fabric tools FORCE +# The Dictionary of v4 (#157): every live word, alphabetical, with its stack +# notation and description -- `make dictionary` -> build/docs/Dictionary.pdf. +# Its names are the booted system's (v4/tools/mkdictionary.py); the check +# refuses a table that has fallen behind them. +DICT_DIR := $(OUT)/dictionary +DICT_PDF := $(OUT)/Dictionary.pdf +DICT_TSV := docs/reference/v4-dictionary.tsv + +.PHONY: all fabric dictionary tools FORCE all: $(PDF) fabric: $(FABRIC_PDF) +dictionary: $(DICT_PDF) tools: @missing=""; \ @@ -113,6 +122,8 @@ $(VAR_DIR)/meta.tex: FORCE $(meta_cmds) $(FABRIC_DIR)/meta.tex: FORCE $(meta_cmds) +$(DICT_DIR)/meta.tex: FORCE + $(meta_cmds) FORCE: @@ -137,3 +148,26 @@ $(FABRIC_PDF): $(BOOK_DIR)/fabric.tex $(BOOK_DIR)/preamble.tex $(FABRIC_FRAGS) $ echo "Error: LaTeX failed; full log: $(FABRIC_DIR)/fabric.log"; exit 1; } @cp $(FABRIC_DIR)/fabric.pdf $@ @echo " PDF $@" + +# the live word list needs the hosted v4; the check fails the build on a word with no entry, an +# unfinished entry, or an entry for a word the system no longer has +$(GEN)/dictionary-words.md: $(DICT_TSV) v4/tools/mkdictionary.py FORCE | tools + @mkdir -p $(GEN) + @$(MAKE) --no-print-directory -C v4 hosted >/dev/null + @python3 v4/tools/mkdictionary.py check $(DICT_TSV) + @python3 v4/tools/mkdictionary.py md $(DICT_TSV) $@ + +$(GEN)/dictionary-words.tex: $(GEN)/dictionary-words.md $(PANDOC_FILTERS) | tools + @echo " PANDOC $< -> $@" + @$(PANDOC) $(PANDOC_FLAGS) --id-prefix=dictionary- $< -o $@ + +TEXINPUTS_DICT := $(abspath $(DICT_DIR)):$(abspath $(GEN)):$(abspath $(BOOK_DIR)): + +$(DICT_PDF): $(BOOK_DIR)/dictionary.tex $(BOOK_DIR)/preamble.tex $(GEN)/dictionary-words.tex $(GEN)/pandoc-highlighting.tex $(DICT_DIR)/meta.tex | tools + @echo " LATEXMK $(BOOK_DIR)/dictionary.tex -> $@" + @TEXINPUTS=$(TEXINPUTS_DICT) $(LATEXMK) -xelatex -interaction=nonstopmode -halt-on-error \ + -file-line-error -outdir=$(DICT_DIR) $(BOOK_DIR)/dictionary.tex > $(DICT_DIR)/latexmk.log 2>&1 || { \ + grep -A4 -E '^(.*:[0-9]+:|!)' $(DICT_DIR)/dictionary.log | head -40; \ + echo "Error: LaTeX failed; full log: $(DICT_DIR)/dictionary.log"; exit 1; } + @cp $(DICT_DIR)/dictionary.pdf $@ + @echo " PDF $@" diff --git a/docs/book/dictionary.tex b/docs/book/dictionary.tex new file mode 100644 index 00000000..7a98c5c9 --- /dev/null +++ b/docs/book/dictionary.tex @@ -0,0 +1,42 @@ +% docs/book/dictionary.tex -- master file of The Dictionary of v4 (#157): +% every word StarForth v4 boots with, alphabetical, with its stack notation, +% a description and where it is defined (Captain Bob, 2026-11-08: "a +% 'dictionary' document with the word, stack notations and descriptions. +% Alphabetical."; v4's words only). +% +% Built by `make dictionary` (docs/book/Makefile): v4/tools/mkdictionary.py +% checks docs/reference/v4-dictionary.tsv against the live system and writes +% the Markdown that pandoc turns into the fragment input below. +\documentclass[11pt,openany]{book} + +\input{preamble} + +\title{The Dictionary of StarForth v4\\[0.5em]\large Every word, its stack notation, and what it does} +\author{Robert A. James (Captain Bob)} +\date{Version \texttt{\bookversion}\\Built \bookdate{} from commit \texttt{\bookcommit}} + +\begin{document} +\frontmatter +\maketitle + +\chapter{How to read it} +Every word of the dictionary StarForth v4 boots with is here, in +alphabetical order. The names are not typed by hand: they are the booted +system's own list (\texttt{WORDS}, and the kernel's words of the node), and +the build refuses a dictionary that has fallen behind it. + +\textbf{Stack notation.} \texttt{( before -{}- after )}: the values a word +takes from the stack, the top on the right, and what it leaves there. +\texttt{( R: ... )} is the return stack. \texttt{n} a signed number, +\texttt{u} an unsigned one, \texttt{d} and \texttt{ud} double-cell numbers, +\texttt{q} a Q48.16 fixed-point number, \texttt{x} any cell, \texttt{flag} +a truth value (0 false, -1 true), \texttt{c} a character, \texttt{addr} a +cell address, \texttt{baddr} a byte address, \texttt{xt} an execution token. + +\textbf{xxx.} Where a word reads a name or text from the input stream, the +description shows it, as \texttt{' xxx} or \texttt{.\textquotedbl{} text\textquotedbl}. + +\mainmatter +\input{dictionary-words} + +\end{document} diff --git a/docs/v4.0.0/plans/2026-11-10-alpha4-documents.md b/docs/v4.0.0/plans/2026-11-10-alpha4-documents.md index 0fa63526..7f4dbeb2 100644 --- a/docs/v4.0.0/plans/2026-11-10-alpha4-documents.md +++ b/docs/v4.0.0/plans/2026-11-10-alpha4-documents.md @@ -15,9 +15,9 @@ Captain Bob, 2026-11-10: "Do it" -- the rest of alpha.4 that lives in the tree, ## #157 -- the dictionary of v4, and the .com's Documents page - [x] the live word list of the built system (318 words): `v4/tools/mkdictionary.py words` -- the hosted v4's `WORDS` (317) plus the kernel's `sk_v4_words[]` (`MINT-ZUSE`) -- [~] the table in the tree, `docs/reference/v4-dictionary.tsv`: drafted for all 318 (`mkdictionary.py draft`; the source gave a stack comment for 54, a description for 105); the rest written by hand in batches, alphabetical -- next: batch 1 -- [x] the check: `mkdictionary.py check` -- every live word has a finished entry, every entry a live word (fails on all 318 drafts today, as it should); not yet in the build -- [ ] the PDF, alphabetical; `make dictionary`; the nightly's docs stage, classifier `dictionary` +- [x] the table in the tree, `docs/reference/v4-dictionary.tsv`: all 318 entries, written in three alphabetical batches against v4's own definitions (the source gave 54 stacks and 105 descriptions to start from); `check` passes +- [x] the check: `mkdictionary.py check` -- every live word has a finished entry, every entry a live word; run by `make dictionary`, so a stale table fails the build and the nightly +- [x] the PDF, alphabetical: `make dictionary` -> `build/docs/Dictionary.pdf` (20 pages; `docs/book/dictionary.tex`); the nightly's docs stage builds it, `publish` sends classifier `dictionary`, the release attaches it - [ ] the Documents page: canvas artboard, then `site/com` ## #126 -- the Downloads page live diff --git a/scripts/nightly.sh b/scripts/nightly.sh index d1cbb2ce..c1b63dd1 100755 --- a/scripts/nightly.sh +++ b/scripts/nightly.sh @@ -43,6 +43,7 @@ # (make -C docs/formal doxygen), into build/nightly-docs/. # scripts/nightly.sh publish-docs # org.starship:documentation:: classifier book, fabric (The Book of Fabric, #138), +# dictionary (The Dictionary of v4, #157 -- it fails the stage if a live word has no entry), # vol1, vol2, vol3, api (pdf). # # VERSION is -SNAPSHOT, or NIGHTLY_VERSION when set (a release sets it from its @@ -175,9 +176,11 @@ docs) rm -rf "$DOCS"; mkdir -p "$DOCS" make --no-print-directory docs make --no-print-directory fabric + make --no-print-directory dictionary make --no-print-directory -C docs/formal vols doxygen cp build/docs/LithosAnanke.pdf "$DOCS/book.pdf" cp build/docs/BookOfFabric.pdf "$DOCS/fabric.pdf" + cp build/docs/Dictionary.pdf "$DOCS/dictionary.pdf" for v in 1 2 3; do cp "docs/formal/build/vol$v/main.pdf" "$DOCS/vol$v.pdf"; done cp docs/formal/build/doxygen/StarForth-API-Reference.pdf "$DOCS/api.pdf" (cd "$DOCS" && sha256sum ./*.pdf | tee SHA256SUMS) @@ -220,8 +223,9 @@ print(json.dumps(notes.strip() + "\n\n## Checksums (SHA-256)\n\n```\n" + sums.st ;; publish-docs) url=${2:-}; [ -n "$url" ] || usage - need "$DOCS/book.pdf" "$DOCS/fabric.pdf" "$DOCS/vol1.pdf" "$DOCS/vol2.pdf" "$DOCS/vol3.pdf" "$DOCS/api.pdf" + need "$DOCS/book.pdf" "$DOCS/fabric.pdf" "$DOCS/dictionary.pdf" "$DOCS/vol1.pdf" "$DOCS/vol2.pdf" "$DOCS/vol3.pdf" "$DOCS/api.pdf" deploy "$url" documentation "$DOCS/book.pdf" pdf book "$DOCS/fabric.pdf" pdf fabric \ + "$DOCS/dictionary.pdf" pdf dictionary \ "$DOCS/vol1.pdf" pdf vol1 "$DOCS/vol2.pdf" pdf vol2 "$DOCS/vol3.pdf" pdf vol3 \ "$DOCS/api.pdf" pdf api ;; diff --git a/v4/tools/mkdictionary.py b/v4/tools/mkdictionary.py index 7aa3fc6f..1ad21e6d 100644 --- a/v4/tools/mkdictionary.py +++ b/v4/tools/mkdictionary.py @@ -160,6 +160,9 @@ def main(): stack, desc = table.get(w, ("", "")) rel = defs.get(w, ("", "", ""))[0] cell = lambda x: x.replace("|", "\\|") + # a token with a quote in it is FORTH (." text" S" ABORT"): set as code, so its + # quotes print straight, not as typographic ones + desc = re.sub(r'(\S*"\S*)', lambda mm: "`" + mm.group(1) + "`", desc) f.write(f"| `{cell(w)}` | `{cell(stack)}` | {cell(desc)} | `{rel}` |\n") print(f"mkdictionary: {out}, {len(words)} words") return -- 2.54.0 From be2c204d568c3cdd7bf5830e28dde154cb6be62b Mon Sep 17 00:00:00 2001 From: Claude Date: Tue, 10 Nov 2026 11:38:50 +0000 Subject: [PATCH 07/10] site(com): Downloads and Documents, built from a release; the nav links; the release's SHA256SUMS covers every file (#126, #157) [skip ci] The design canvas (v17) gains the Documents artboard -- the Book of Fabric and the Dictionary of v4 first, then the book, the three volumes and the API reference, each with its SHA-256, and the SSRN paper -- and Downloads and Documents in every .com nav; Downloads' reading list becomes a pointer to Documents. site/com/canvas/ copied from it. site/com/build.py --release TAG --sums SHA256SUMS builds Downloads and Documents from the release ([TAG], [RELEASE]/, [SHA:]; a file the release's SHA256SUMS lacks fails the build). Without a release it builds Home, Try it and Inquiries exactly as before -- byte for byte -- with no link to the two pages, so they go live only with alpha.4. install.sh installs them when built. scripts/nightly.sh gitea-release: the release's SHA256SUMS now covers every file it attaches -- the images' sums, then the PDFs and the Root CA's certificate -- so `sha256sum -c SHA256SUMS` checks all of it and Documents finds each sum. The Nexus artifacts keep their own. Co-Authored-By: Claude Opus 5.5 Claude-Session: https://claude.ai/code/session_01GE3SDvQn5EoxdGufk84zz2 --- .../plans/2026-11-10-alpha4-documents.md | 14 +- scripts/nightly.sh | 15 +- site/com/build.py | 53 ++++++- site/com/canvas/Documents.dc.html | 109 +++++++++++++ site/com/canvas/Downloads.dc.html | 145 ++++++++++++++++++ site/com/canvas/Inquiries.dc.html | 2 + site/com/canvas/Main.dc.html | 2 + site/com/canvas/TryIt.dc.html | 2 + site/com/install.sh | 9 +- 9 files changed, 337 insertions(+), 14 deletions(-) create mode 100644 site/com/canvas/Documents.dc.html create mode 100644 site/com/canvas/Downloads.dc.html diff --git a/docs/v4.0.0/plans/2026-11-10-alpha4-documents.md b/docs/v4.0.0/plans/2026-11-10-alpha4-documents.md index 7f4dbeb2..6580aa22 100644 --- a/docs/v4.0.0/plans/2026-11-10-alpha4-documents.md +++ b/docs/v4.0.0/plans/2026-11-10-alpha4-documents.md @@ -18,9 +18,15 @@ Captain Bob, 2026-11-10: "Do it" -- the rest of alpha.4 that lives in the tree, - [x] the table in the tree, `docs/reference/v4-dictionary.tsv`: all 318 entries, written in three alphabetical batches against v4's own definitions (the source gave 54 stacks and 105 descriptions to start from); `check` passes - [x] the check: `mkdictionary.py check` -- every live word has a finished entry, every entry a live word; run by `make dictionary`, so a stale table fails the build and the nightly - [x] the PDF, alphabetical: `make dictionary` -> `build/docs/Dictionary.pdf` (20 pages; `docs/book/dictionary.tex`); the nightly's docs stage builds it, `publish` sends classifier `dictionary`, the release attaches it -- [ ] the Documents page: canvas artboard, then `site/com` +- [x] the Documents page: the canvas artboard (`project/Documents.dc.html`, canvas v17: the Book of Fabric and the Dictionary first, then the book, the volumes, the API; each with its SHA-256; the SSRN paper), then `site/com/canvas/Documents.dc.html` ## #126 -- the Downloads page live -- [ ] `site/com` builds Downloads (and Documents) from the release: its tag, `SHA256SUMS`, the - Root CA's fingerprint -- [ ] the nav links on Home, Try it, Inquiries +- [x] `site/com/build.py --release TAG --sums SHA256SUMS` builds Downloads and Documents: `[TAG]`, `[RELEASE]/` (the + Gitea release's file), `[SHA:]`; a file not in SHA256SUMS fails the build. Without a release it builds the three + pages as before, byte for byte, with no link to the two. `install.sh` installs them when they are built. The release's + SHA256SUMS now covers every file it attaches (the images, the PDFs, the Root CA's certificate): `nightly.sh gitea-release` +- [x] the nav links on Home, Try it, Inquiries (canvas v17; shown only when the two pages are built) + +**At the alpha.4 release** (after `gitea-release`): download the release's `SHA256SUMS`, then +`python3 site/com/build.py --release v4.0.0-alpha.4 --sums SHA256SUMS` and +`XWIKI_USER=rajames XWIKI_PASSWORD=... site/com/install.sh` (Captain Bob's; the .com's login). diff --git a/scripts/nightly.sh b/scripts/nightly.sh index c1b63dd1..c46890e4 100755 --- a/scripts/nightly.sh +++ b/scripts/nightly.sh @@ -195,15 +195,23 @@ gitea-release) : "${RELEASE_TOKEN:?RELEASE_TOKEN: a token that may write the repository}" api=${GITEA_API:-${GITHUB_SERVER_URL:-https://gitea.starshipos-f18.org}/api/v1/repos/${GITHUB_REPOSITORY:-rajames/LithosAnanke}} need "$OUT/StarshipOS.iso" "$OUT/pc-stick.img.xz" "$OUT/raspi5.img.xz" "$OUT/MilkV.img.xz" "$OUT/SHA256SUMS" - files=("$OUT/StarshipOS.iso" "$OUT/pc-stick.img.xz" "$OUT/raspi5.img.xz" "$OUT/MilkV.img.xz" "$OUT/SHA256SUMS") - for f in "$DOCS"/*.pdf ci/pki/starshipos-root-ca.pem; do [ -f "$f" ] && files+=("$f"); done + # The release's SHA256SUMS covers every file it attaches -- the images' sums as the images + # stage wrote them, then the documents and the Root CA's certificate -- so that + # `sha256sum -c SHA256SUMS` checks all of it, and the .com's Documents page (site/com/build.py, + # #157) finds each document's sum. The Nexus artifacts keep their own SHA256SUMS. + rel=$(mktemp -d) + cp "$OUT/SHA256SUMS" "$rel/SHA256SUMS" + extra=("$DOCS"/*.pdf ci/pki/starshipos-root-ca.pem) + for f in "${extra[@]}"; do [ -f "$f" ] && (cd "$(dirname "$f")" && sha256sum "$(basename "$f")") >> "$rel/SHA256SUMS"; done + files=("$OUT/StarshipOS.iso" "$OUT/pc-stick.img.xz" "$OUT/raspi5.img.xz" "$OUT/MilkV.img.xz" "$rel/SHA256SUMS") + for f in "${extra[@]}"; do [ -f "$f" ] && files+=("$f"); done gitea() { curl -fsS -H "Authorization: token $RELEASE_TOKEN" "$@"; } body=$(python3 -c ' import json, sys notes, sums, version = sys.argv[1], open(sys.argv[2]).read(), sys.argv[3] print(json.dumps(notes.strip() + "\n\n## Checksums (SHA-256)\n\n```\n" + sums.strip() + "\n```\n\n" "Also in Nexus: `org.starship:kernelImage:" + version + "` and `org.starship:documentation:" + version + "`.\n"))' \ - "$(git tag -l --format='%(contents)' "$tag")" "$OUT/SHA256SUMS" "$VERSION") + "$(git tag -l --format='%(contents)' "$tag")" "$rel/SHA256SUMS" "$VERSION") id=$(gitea "$api/releases/tags/$tag" 2>/dev/null | python3 -c 'import json,sys; print(json.load(sys.stdin)["id"])' 2>/dev/null || true) if [ -z "$id" ]; then pre=false; case "$tag" in *-*) pre=true;; esac @@ -220,6 +228,7 @@ print(json.dumps(notes.strip() + "\n\n## Checksums (SHA-256)\n\n```\n" + sums.st case " $have " in *" $n "*) echo " $n: there"; continue;; esac gitea -o /dev/null -X POST -F "attachment=@$f" "$api/releases/$id/assets?name=$n" && echo " $n: attached" done + rm -f "$rel/SHA256SUMS" && rmdir "$rel" ;; publish-docs) url=${2:-}; [ -n "$url" ] || usage diff --git a/site/com/build.py b/site/com/build.py index 7bb4c353..14f2b198 100755 --- a/site/com/build.py +++ b/site/com/build.py @@ -3,7 +3,9 @@ # Copyright (c) 2023-2026 Robert A. James """build.py -- the starshipos-f18.com XWiki pages, made from the "StarshipOS.com" design canvas. - site/com/build.py canvas/{Main,Inquiries,TryIt}.dc.html -> {Main,Inquiries,TryIt}.WebHome.xwiki + site/com/build.py [--release TAG --sums SHA256SUMS] + canvas/{Main,Inquiries,TryIt}.dc.html -> {Main,Inquiries,TryIt}.WebHome.xwiki, + and with a release, canvas/{Downloads,Documents}.dc.html too The canvas is the source: its artboards are copied into canvas/ as they are, and this turns each into one {{html}} block (which needs a page author with script rights). What changes on the way: @@ -15,6 +17,10 @@ one {{html}} block (which needs a page author with script rights). What changes - Try it: the drawn console becomes the real one, an iframe of try.starshipos-f18.com; - the inquiry form posts to the .org's PublicInquiry, which files it under Inquiries there (admin-only, site/org/inquiries); the announcements form, which has no server, sends by e-mail; + - Downloads and Documents (#126, #157) are built from a release: --release TAG and the release's + SHA256SUMS fill in [TAG], [RELEASE]/ (the file on the Gitea release) and [SHA:]. + Without a release they are not built, and the other pages' links to them are taken out, so + nothing on the site points at a page that is not there (they go live with alpha.4); install.sh puts the pages and the images on the wiki. """ @@ -31,6 +37,8 @@ FONTS = ('\n' '&family=Exo+2:wght@600;700&display=swap" rel="stylesheet">') ORG = "https://starshipos-f18.org/" PAGES = ["Main", "Inquiries", "TryIt"] +RELEASE_PAGES = ["Downloads", "Documents"] +RELEASES = "https://gitea.starshipos-f18.org/rajames/LithosAnanke/releases/download/" IMAGES = {"/_blob/5b564d54d98e02ad600c913a9a1888f2": "badge.jpg", "/_blob/5483451f292782a8b366e437181c4bcf": "captain-bob.jpg"} @@ -44,11 +52,15 @@ CONSOLE = '''
If the console does not connect, every machine is in use: try again in a few minutes.

''' -def body(src): +def body(src, pages): m = re.search(r"(.*)", src, re.S) html = re.sub(r".*?", "", m.group(1), flags=re.S).strip() html = html.replace("{{accent}}", ACCENT) - for p in PAGES: + # a page not built this time is not linked to: its taken out (the nav, the footers) + for p in RELEASE_PAGES: + if p not in pages: + html = re.sub(r']*>[^<]*\n?' % p, "", html) + for p in pages: html = html.replace('href="%s.dc.html"' % p, 'href="/bin/view/%s/"' % p) html = html.replace('href="https://starshipos.org"', 'href="%s"' % ORG) html = html.replace(">starshipos.org", ">starshipos-f18.org") @@ -90,14 +102,43 @@ def forms(html, page): return html +def release(html, tag, sums): + """[TAG], [RELEASE]/ and [SHA:] from the release; a file it has not is an error.""" + html = html.replace("[TAG]", tag) + html = html.replace("[RELEASE]/", RELEASES + tag + "/") + + def sha(m): + if m.group(1) not in sums: + sys.exit("build: %s is not in the release's SHA256SUMS" % m.group(1)) + return sums[m.group(1)] + return re.sub(r"\[SHA:([^\]]+)\]", sha, html) + + if __name__ == "__main__": + import argparse + ap = argparse.ArgumentParser() + ap.add_argument("--release", help="the release's tag, e.g. v4.0.0-alpha.4") + ap.add_argument("--sums", help="the release's SHA256SUMS") + a = ap.parse_args() + if bool(a.release) != bool(a.sums): + sys.exit("build: --release and --sums go together") + sums = {} + if a.sums: + for l in open(a.sums).read().split("\n"): + f = l.split() + if len(f) == 2: + sums[f[1].lstrip("*").lstrip("./")] = f[0] os.chdir(os.path.dirname(os.path.abspath(__file__))) - for p in PAGES: - html = forms(body(open("canvas/%s.dc.html" % p).read()), p) + pages = PAGES + (RELEASE_PAGES if a.release else []) + for p in pages: + html = forms(body(open("canvas/%s.dc.html" % p).read(), pages), p) + if p in RELEASE_PAGES: + html = release(html, a.release, sums) if p == "TryIt": html = try_it(html) html = colorlock.lock(html, '
+ + + +StarshipOS — Documents + + + + + + + + + +
+ +
+ +
+ +
+

ok> documents

+

How it was built, and what it says.

+

Every release builds its documents from the same tree as its images: the design record, the dictionary of every word, the book, the formal volumes. Each is a PDF on the release, with its checksum.

+ +
+ +
+

Start here

+

Two books, kept with every release.

+
+ +
+

The Book of Fabric

the design record, as it happened

+

Every ruling, the reasoning behind it, what was built and what was abandoned, written as the work went -- reproduced as written, corrections and all. The living record first, then the closed ones in order. Fabric is about steady state: this is how it was reached.

+
SHA-256 [SHA:fabric.pdf]
+fabric.pdf +
+ +
+

The Dictionary of v4

every word, alphabetical

+

Every word StarForth v4 boots with: its stack notation, what it does, and where it is defined. The names come from the booted system itself, and the build refuses a dictionary that has fallen behind it.

+
DUP      ( x -- x x )       Copy the top value.
+MINT-ZUSE ( -- )            Mint the real Zuse onto a second stick.
+
SHA-256 [SHA:dictionary.pdf]
+dictionary.pdf +
+ +
+
+ +
+

And the rest

+

The book, the formal set, the API.

+ +
+ +
+
+
+

The paper

+

The ideas behind it, on SSRN.

+

The whitepaper the design starts from.

+
+Read the paper → +
+
+ + + +
+
+ + + diff --git a/site/com/canvas/Downloads.dc.html b/site/com/canvas/Downloads.dc.html new file mode 100644 index 00000000..f87c1b63 --- /dev/null +++ b/site/com/canvas/Downloads.dc.html @@ -0,0 +1,145 @@ + + + + +StarshipOS — Downloads + + + + + + + + + +
+ +
+ +
+ +
+

ok> downloads

+

StarshipOS, on a machine of yours.

+

One image per board, straight from the release. Write it to a USB stick or an SD card, boot, and the first boot makes you Zuse, the superuser: your identity lives on the stick.

+
+[TAG] +alpha · pre-release +All releases → +
+
+ +
+
+
+

An alpha, honestly.

+

Every release is booted in QEMU on amd64, aarch64 and riscv64, passes its 538 power-on checks on each, and its proofs, before it is published. On real hardware it has not been booted yet. If you try it, tell us what happened.

+
+
+
✓ QEMU, three architectures
+
○ Real hardware: not yet
+Report how it went → +
+
+
+ +
+

The images

+

Pick your board.

+
+ +
+

PC

any x86-64 PC that boots UEFI from USB

+

A USB stick image. Its first boot makes your Zuse on the stick: no account, no password — the stick is your key.

+
Write it (it replaces everything on the stick):
+
xz -dc pc-stick.img.xz | sudo dd of=/dev/sdX bs=4M conv=fsync status=progress
+

or balenaEtcher, which reads the .xz as it is.

+pc-stick.img.xz +
+ +
+

Raspberry Pi 5

an SD card

+

Boots straight from the Pi's own firmware. Until first-boot Zuse reaches this board, it needs a Zuse identity drive made on a PC.

+
Write it with Raspberry Pi Imager (Use custom), or:
+
xz -dc raspi5.img.xz | sudo dd of=/dev/sdX bs=4M conv=fsync status=progress
+raspi5.img.xz +
+ +
+

Milk-V Mars

RISC-V, an SD card

+

Found by the board's own U-Boot. Until first-boot Zuse reaches this board, it needs a Zuse identity drive made on a PC.

+
Write it:
+
xz -dc MilkV.img.xz | sudo dd of=/dev/sdX bs=4M conv=fsync status=progress
+MilkV.img.xz +
+ +
+

QEMU

for developers

+

The ISO every release's amd64 acceptance boots, byte for byte. To boot it the way we do, see the developer guide in the repository.

+The repository → +StarshipOS.iso +
+ +
+

Rather not write anything? Try it in your browser — a real machine, at its prompt in seconds.

+
+ +
+
+
+

Verify

+

Know it's ours.

+

Every file's SHA-256 is in the release's SHA256SUMS. Inside each image, every capsule the system loads is signed by StarshipOS Capsule Signing, issued by the StarshipOS Root CA, and checked at boot.

+
sha256sum -c SHA256SUMS --ignore-missing
+
+
+
StarshipOS Root CA, SHA-256
+DA:A7:40:56:65:41:1A:FE:E6:08:85:3F:6E:AD:83:3A:38:30:83:E6:F0:6D:69:A6:A8:9E:92:BD:85:93:39:81 +SHA256SUMS → +The Root CA's certificate → +
+
+
+ +
+
+
+

Read

+

The documents of this release.

+

The Book of Fabric, the Dictionary of v4, the book and the formal set -- built from the same tree as these images.

+
+Documents → +
+
+ + + +
+
+ + + diff --git a/site/com/canvas/Inquiries.dc.html b/site/com/canvas/Inquiries.dc.html index 5e483cab..9c5e8b84 100644 --- a/site/com/canvas/Inquiries.dc.html +++ b/site/com/canvas/Inquiries.dc.html @@ -23,6 +23,8 @@ a{color:#F5891F}a:hover{color:#FFB061} diff --git a/site/com/canvas/Main.dc.html b/site/com/canvas/Main.dc.html index 63283bb2..19489a63 100644 --- a/site/com/canvas/Main.dc.html +++ b/site/com/canvas/Main.dc.html @@ -25,6 +25,8 @@ a{color:#F5891F}a:hover{color:#FFB061} Research Silicon Try it +Downloads +Documents Inquiries Contributors →
diff --git a/site/com/canvas/TryIt.dc.html b/site/com/canvas/TryIt.dc.html index e6cc2522..3eea8198 100644 --- a/site/com/canvas/TryIt.dc.html +++ b/site/com/canvas/TryIt.dc.html @@ -23,6 +23,8 @@ a{color:#F5891F}a:hover{color:#FFB061} diff --git a/site/com/install.sh b/site/com/install.sh index 7187d028..695aaa1b 100755 --- a/site/com/install.sh +++ b/site/com/install.sh @@ -39,10 +39,17 @@ attach Main badge.jpg attach Main captain-bob.jpg page Inquiries "Inquiries" Inquiries.WebHome.xwiki page TryIt "Try StarshipOS" TryIt.WebHome.xwiki +# Downloads and Documents exist once build.py has been given a release (#126, #157) +pages="/bin/view/Main/ /bin/view/Inquiries/ /bin/view/TryIt/" +if [ -f Downloads.WebHome.xwiki ] && [ -f Documents.WebHome.xwiki ]; then + page Downloads "Downloads" Downloads.WebHome.xwiki + page Documents "Documents" Documents.WebHome.xwiki + pages="$pages /bin/view/Downloads/ /bin/view/Documents/" +fi echo "=== as a guest" bad= -for u in /bin/view/Main/ /bin/view/Inquiries/ /bin/view/TryIt/ \ +for u in $pages \ /bin/download/Main/WebHome/badge.jpg /bin/download/Main/WebHome/captain-bob.jpg; do code=$(curl -s -o /dev/null -w '%{http_code}' "$base$u") if [ "$code" = 200 ]; then echo " $u: 200"; else echo " $u: $code -- NOT readable"; bad=1; fi -- 2.54.0 From da14d9afc633bb66b0c21b1ac6a9a3627fa0e34d Mon Sep 17 00:00:00 2001 From: Claude Date: Tue, 10 Nov 2026 11:40:07 +0000 Subject: [PATCH 08/10] site(com): every email address to rajames440@gmail.com (#161) [skip ci] Home's footer and announcements form, and the four addresses of Inquiries' 'Or write directly' (info@, licensing@, press@starshipos.com, security@starshipos.org): all rajames440@gmail.com (Captain Bob, 2026-11-10). On the design canvas (v18) and in site/com; the pages rebuilt. Installed on the .com with site/com/install.sh. Co-Authored-By: Claude Opus 5.5 Claude-Session: https://claude.ai/code/session_01GE3SDvQn5EoxdGufk84zz2 --- site/com/Inquiries.WebHome.xwiki | 8 ++++---- site/com/Main.WebHome.xwiki | 4 ++-- site/com/build.py | 2 +- site/com/canvas/Inquiries.dc.html | 8 ++++---- site/com/canvas/Main.dc.html | 2 +- 5 files changed, 12 insertions(+), 12 deletions(-) diff --git a/site/com/Inquiries.WebHome.xwiki b/site/com/Inquiries.WebHome.xwiki index d8d9c05e..d2b4e789 100644 --- a/site/com/Inquiries.WebHome.xwiki +++ b/site/com/Inquiries.WebHome.xwiki @@ -91,19 +91,19 @@ html, body, #xwikimaincontainer, #xwikimaincontainerinner, #contentcontainer, #c

Or write directly

Licensing & partnerships
-licensing@starshipos.com +rajames440@gmail.com

StarshipOS is developed in Ashland, Ohio.

diff --git a/site/com/Main.WebHome.xwiki b/site/com/Main.WebHome.xwiki index c202de3f..ec7f0ca2 100644 --- a/site/com/Main.WebHome.xwiki +++ b/site/com/Main.WebHome.xwiki @@ -134,7 +134,7 @@ html, body, #xwikimaincontainer, #xwikimaincontainerinner, #contentcontainer, #c

Launch announcements

Milestones, releases and papers. Low volume. Unsubscribe anytime.

-
+ @@ -145,7 +145,7 @@ html, body, #xwikimaincontainer, #xwikimaincontainerinner, #contentcontainer, #c diff --git a/site/com/build.py b/site/com/build.py index 14f2b198..cd73890d 100755 --- a/site/com/build.py +++ b/site/com/build.py @@ -80,7 +80,7 @@ def try_it(html): def forms(html, page): if page == "Main": html = html.replace('', - '') + '') html = html.replace('Or write directly
Licensing & partnerships
-licensing@starshipos.com +rajames440@gmail.com

StarshipOS is developed in Ashland, Ohio.

diff --git a/site/com/canvas/Main.dc.html b/site/com/canvas/Main.dc.html index 19489a63..6d3e5346 100644 --- a/site/com/canvas/Main.dc.html +++ b/site/com/canvas/Main.dc.html @@ -137,7 +137,7 @@ a{color:#F5891F}a:hover{color:#FFB061} -- 2.54.0 From 468b03a093c8f771959eef236f6d5a647e72c531 Mon Sep 17 00:00:00 2001 From: Claude Date: Tue, 10 Nov 2026 11:45:40 +0000 Subject: [PATCH 09/10] site(org): each .com inquiry also e-mailed to rajames440@gmail.com, Reply-To the visitor (#161) [skip ci] PublicInquiry files the inquiry as before, then mails it through the wiki's mail service: plain text, the subject fixed but for the topic (line breaks removed), Reply-To the visitor's checked address, a link to the filed entry. If it is not sent the inquiry is filed all the same; a new class field, mailed (E-mailed to Captain Bob), says which, shown on the entry's sheet. Installed on the .org; a guest's test inquiry filed and thanked, mailed 0 -- the wiki has no SMTP account yet (Mail.MailConfig: no host), which Captain Bob sets in Administration > Email. Co-Authored-By: Claude Opus 5.5 Claude-Session: https://claude.ai/code/session_01GE3SDvQn5EoxdGufk84zz2 --- site/org/inquiries/ClassSetup.xwiki | 1 + site/org/inquiries/InquirySheet.xwiki | 1 + .../org/inquiries/PublicInquiry.WebHome.xwiki | 32 +++++++++++++++++++ 3 files changed, 34 insertions(+) diff --git a/site/org/inquiries/ClassSetup.xwiki b/site/org/inquiries/ClassSetup.xwiki index 13863aec..ca40e5a9 100644 --- a/site/org/inquiries/ClassSetup.xwiki +++ b/site/org/inquiries/ClassSetup.xwiki @@ -15,6 +15,7 @@ Needs the programming right. #if (!$c.get('announcements'))#set ($d = $c.addBooleanField('announcements', 'Wants announcements', 'yesno'))#set ($changed = true)#end #if (!$c.get('source'))#set ($d = $c.addTextField('source', 'Source', 60))#set ($changed = true)#end #if (!$c.get('status'))#set ($d = $c.addStaticListField('status', 'Status', 1, false, 'new|read|answered|closed'))#set ($changed = true)#end +#if (!$c.get('mailed'))#set ($d = $c.addBooleanField('mailed', 'E-mailed to Captain Bob', 'yesno'))#set ($changed = true)#end #if ($changed) #set ($d = $xdoc.setTitle('Inquiry class')) #set ($d = $xwiki.getXWiki().saveDocument($xdoc, 'The inquiry fields', $xcontext.getContext())) diff --git a/site/org/inquiries/InquirySheet.xwiki b/site/org/inquiries/InquirySheet.xwiki index 7a78e4a1..443908a3 100644 --- a/site/org/inquiries/InquirySheet.xwiki +++ b/site/org/inquiries/InquirySheet.xwiki @@ -12,6 +12,7 @@ This page holds no inquiry. |=Organization|$doc.display('organization', $o) |=Topic|$doc.display('topic', $o) |=Announcements|$doc.display('announcements', $o) +|=E-mailed|$doc.display('mailed', $o) |=Source|$doc.display('source', $o) == Message == diff --git a/site/org/inquiries/PublicInquiry.WebHome.xwiki b/site/org/inquiries/PublicInquiry.WebHome.xwiki index 0ff60d79..6d7066f8 100644 --- a/site/org/inquiries/PublicInquiry.WebHome.xwiki +++ b/site/org/inquiries/PublicInquiry.WebHome.xwiki @@ -4,6 +4,11 @@ ## rights (getDocumentAsAuthor, saveAsAuthor): a guest can reach nothing but this. Nothing a visitor ## sends is ever written into this page's output or an entry's title: with this author's rights, ## text that reached the wiki markup could run a macro. +## Each inquiry filed is also e-mailed to Captain Bob (#161; ruled 2026-11-10: "server emails each +## one"), plain text, through the wiki's mail service (Administration > Email > Mail Sending: the +## SMTP account is set there, not here). Reply-To is the visitor's address, which has passed the +## address check above; the subject is fixed but for the topic, its line breaks removed. If the +## mail is not sent the inquiry is filed all the same, and its entry says so (its E-mailed field: no). #macro (field $n $max)#set ($v = "$!request.getParameter($n)")#set ($v = $v.trim())#if ($v.length() > $max)#set ($v = $v.substring(0, $max))#end#end #if ($request.method != 'POST') This page receives the inquiry form on [[starshipos-f18.com>>https://starshipos-f18.com/bin/view/Inquiries/]]. @@ -35,6 +40,33 @@ This page receives the inquiry form on [[starshipos-f18.com>>https://starshipos- #set ($d = $o.set('source', 'starshipos-f18.com')) #set ($d = $o.set('status', 'new')) #set ($d = $entry.saveAsAuthor('An inquiry from the .com form')) +## the e-mail: every visitor-given value is plain text in a text/plain part, never markup +#set ($subjectTopic = $topic.replaceAll('[\r\n]+', ' ')) +#set ($body = "A new inquiry from starshipos-f18.com, filed as ${ref}: +${xwiki.getURL($ref, 'view')} + +Name: $name +E-mail: $email +Organization: $org +Topic: $topic +Announcements: #if ($wants)yes#{else}no#end + + +$message +") +#set ($mail = $services.mail.sender.createMessage('rajames440@gmail.com', "Inquiry from starshipos-f18.com: $subjectTopic")) +#if ($mail) +#set ($d = $mail.addPart('text/plain', $body)) +#set ($d = $mail.setHeader('Reply-To', $email)) +#set ($sent = $services.mail.sender.send($mail)) +#set ($ok = $sent && !$services.mail.sender.lastError) +#if ($ok)#set ($ok = $sent.statusResult.waitTillProcessed(30000))#set ($ok = !$sent.statusResult.allErrors.hasNext())#end +#else +#set ($ok = false) +#end +#set ($e2 = $xwiki.getDocumentAsAuthor($ref)) +#set ($o2 = $e2.getObject('Inquiries.Code.InquiryClass')) +#if ($o2)#if ($ok)#set ($d = $o2.set('mailed', 1))#else#set ($d = $o2.set('mailed', 0))#end#set ($d = $e2.saveAsAuthor('Whether the inquiry was e-mailed'))#end {{html}}

Thank you. Your inquiry has been received; every message is read by a human, and replies usually come within a few days.

Back to starshipos-f18.com

{{/html}} #end #end -- 2.54.0 From 93828e0eb98402978bb21101f8e814aa5bdd8bf5 Mon Sep 17 00:00:00 2001 From: Robert James Date: Tue, 10 Nov 2026 15:19:25 +0000 Subject: [PATCH 10/10] release: v4.0.0-alpha.4 --- CHANGELOG.md | 4 ++++ README.md | 6 +++--- kernel/Makefile | 2 +- 3 files changed, 8 insertions(+), 4 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 3db4fa1d..e565b1ea 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,9 @@ # Changelog +## 2026-11-10 — LithosAnanke v4.0.0-alpha.4: the plug-and-play stick + +The plug-and-play stick (#92), the BAM flush (#155), the Book of Fabric (#138), the Dictionary of v4 (#157), and the .com’s Downloads and Documents pages (#126, #161). + ## 2026-11-07 — LithosAnanke v4.0.0-alpha.3: the first published alpha `v4.0.0-alpha.2` (`2d43db25`) used the image's HOL-Library as intended, but its release run (200) ran diff --git a/README.md b/README.md index 06e0c135..f05db317 100644 --- a/README.md +++ b/README.md @@ -1,4 +1,4 @@ -# LithosAnanke v4.0.0-alpha.3 +# LithosAnanke v4.0.0-alpha.4 **UEFI-bootable FORTH microkernel.** Boots from firmware, initialises memory and interrupts, then runs StarForth as its sole userspace runtime. No libc. No OS. Just stone and necessity. @@ -7,8 +7,8 @@ runs StarForth as its sole userspace runtime. No libc. No OS. Just stone and nec **Try it in your browser:** [starshipos-f18.com](https://starshipos-f18.com/) — a StarshipOS machine of your own, booted to `[zuse@Hera] ok>`, thrown away when you leave. **Releases:** -[v4.0.0-alpha.3](https://gitea.starshipos-f18.org/rajames/LithosAnanke/releases) (pre-release), -the kernel `4.0.0-alpha.3` with the StarForth engine `3.2.0`. +[v4.0.0-alpha.4](https://gitea.starshipos-f18.org/rajames/LithosAnanke/releases) (pre-release), +the kernel `4.0.0-alpha.4` with the StarForth engine `3.2.0`. --- diff --git a/kernel/Makefile b/kernel/Makefile index b6115efc..02221281 100644 --- a/kernel/Makefile +++ b/kernel/Makefile @@ -112,7 +112,7 @@ endif # the messaging layer was replaced by kernel-Hermes, PLOT/FB-* relocated to Hestia — # all dictionary-visible, no FORTH-79 word semantics changed. VERSION ?= 3.2.0 -LITHOS_VERSION ?= 4.0.0-alpha.3 +LITHOS_VERSION ?= 4.0.0-alpha.4 # ============================================================================== # BUILD PATHS -- 2.54.0