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/Makefile b/Makefile index c4754628..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 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 @@ -203,6 +203,14 @@ 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 + +# 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/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/docs/book/Makefile b/docs/book/Makefile index b797a48a..82b00b45 100644 --- a/docs/book/Makefile +++ b/docs/book/Makefile @@ -57,8 +57,24 @@ 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) + +# 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=""; \ @@ -86,25 +102,34 @@ $(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) +$(DICT_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 +137,37 @@ $(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 $@" + +# 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/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/reference/v4-dictionary.tsv b/docs/reference/v4-dictionary.tsv new file mode 100644 index 00000000..d53df2c8 --- /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. +! ( 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+ ( 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 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. +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.* ( 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. 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..6580aa22 --- /dev/null +++ b/docs/v4.0.0/plans/2026-11-10-alpha4-documents.md @@ -0,0 +1,32 @@ +# 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 +- [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`) +- [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 +- [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 +- [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/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 diff --git a/scripts/nightly.sh b/scripts/nightly.sh index c127c679..c46890e4 100755 --- a/scripts/nightly.sh +++ b/scripts/nightly.sh @@ -42,7 +42,9 @@ # 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), +# 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 # tag). NEXUS_USER (default ci-publisher) and NEXUS_PASSWORD; publishing needs mvn and a JRE. @@ -173,8 +175,12 @@ publish) 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) @@ -189,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 @@ -214,11 +228,13 @@ 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 - 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/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/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 7bb4c353..cd73890d 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") @@ -68,7 +80,7 @@ def try_it(html): def forms(html, page): if page == "Main": html = html.replace('', - '') + '') html = html.replace(' 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..aa0fc934 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} @@ -80,19 +82,19 @@ a{color:#F5891F}a:hover{color:#FFB061}

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 63283bb2..6d3e5346 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 →
@@ -135,7 +137,7 @@ a{color:#F5891F}a:hover{color:#FFB061} 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 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 diff --git a/v4/tools/mkdictionary.py b/v4/tools/mkdictionary.py new file mode 100644 index 00000000..1ad21e6d --- /dev/null +++ b/v4/tools/mkdictionary.py @@ -0,0 +1,173 @@ +#!/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("|", "\\|") + # 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 + sys.exit(__doc__) + + +if __name__ == "__main__": + main()