diff options
| author | salmonik <salmonik@tuta.io> | 2026-08-01 13:14:07 +0200 |
|---|---|---|
| committer | salmonik <salmonik@tuta.io> | 2026-08-01 13:14:07 +0200 |
| commit | 6eb14b64209b9d24554c587f610d7e300ef809ea (patch) | |
| tree | c308507a2140680eb82fdb8b360aa346e7c5c9fe /docs | |
| parent | Bump workspace to edition 2024 (diff) | |
| download | rs-dos-6eb14b64209b9d24554c587f610d7e300ef809ea.tar.gz rs-dos-6eb14b64209b9d24554c587f610d7e300ef809ea.zip | |
shell: keyword redirection + post-filter flags (ADR 0002)
Implement the shell experience redesign: one canonical grammar for every
command — 'cmd args [USE src] [TIL marker] [filters...] [output kw dst]'.
- Keyword redirection (uppercase-only, aliases valid, fully reserved):
WRITING/WRITE overwrite, TO append, USE/USING input source,
TIL/UNTIL heredoc/terminator (exact full-line marker). > >> < << are
removed; '>' is plain text.
- Post-filter stages applied left to right over a line stream, each
repeatable: -h N (head kills the upstream), -t N (tail holds a
rolling window, flushes at EOF/^C), -g PAT (smart-case literal
substring), -p (screenful pager, terminal only). A file sink
replaces the screen; -p is a no-op with one.
- C-style quoting: "..." groups and literalizes, \x escapes one char;
quoted/escaped text is never an operator, never split.
- Input consumers: cat/hexdump, path-less only (stdin until ^D,
USE f, TIL m heredoc, USE f TIL m truncation). Other commands
reject input keywords; paths + input keyword is a syntax error.
- ^D is EOF everywhere and never powers off; poweroff/exit are the
only shutdown verbs.
- help rewritten; test-disk.sh and sendkeys.py use the keyword syntax.
Verified in QEMU: 26 grammar checks on fresh and reused disks, disk
round trip, pager pacing, -h stopping /dev/mem at N lines, ^C flushing
a -t window. Two bugs found during verification and fixed:
1. WRITING over an existing file wrote after stale NUL padding —
fs::size() is stale after set_len(0); the sink writes at offset 0
after truncation.
2. USE f TIL m leaked the post-marker remainder — the final
partial-line emit ran after the marker break; guarded with a
stopped flag.
Diffstat (limited to 'docs')
| -rw-r--r-- | docs/adr/0002-keyword-redirection-and-post-filters.md | 76 |
1 files changed, 76 insertions, 0 deletions
diff --git a/docs/adr/0002-keyword-redirection-and-post-filters.md b/docs/adr/0002-keyword-redirection-and-post-filters.md new file mode 100644 index 0000000..1584077 --- /dev/null +++ b/docs/adr/0002-keyword-redirection-and-post-filters.md | |||
| @@ -0,0 +1,76 @@ | |||
| 1 | # ADR 0002 — Keyword Redirection and Post-Filter Flags Instead of Pipes | ||
| 2 | |||
| 3 | ## Status | ||
| 4 | |||
| 5 | Accepted. | ||
| 6 | |||
| 7 | ## Context | ||
| 8 | |||
| 9 | The shell (a freestanding, no_std kernel component) needs the data-flow | ||
| 10 | features every shell has: redirecting command output to files, feeding | ||
| 11 | input from files or typed blocks, and trimming output to `head`/`tail`/ | ||
| 12 | `grep`/pager views. The obvious model — Unix pipes plus `> < << >>` | ||
| 13 | operators — was considered and rejected. The project is explicitly | ||
| 14 | not POSIX-compatible, is TempleOS-inspired, and values minimal machinery | ||
| 15 | and natural-language-flavoured input over familiarity. The old shell | ||
| 16 | had only an ad-hoc `>`/`>>` parse baked into `cmd_echo`, no head/tail/ | ||
| 17 | grep at all, and ^D at the prompt powered the machine off. | ||
| 18 | |||
| 19 | ## Decision | ||
| 20 | |||
| 21 | One canonical command-line grammar, applied uniformly to every command: | ||
| 22 | |||
| 23 | ``` | ||
| 24 | cmd args [USE src] [TIL marker] [filters…] [output kw dst] | ||
| 25 | ``` | ||
| 26 | |||
| 27 | - **Redirection uses reserved uppercase keywords**, never symbols: | ||
| 28 | `WRITING`/`WRITE` overwrite, `TO` appends, `USE`/`USING` names the | ||
| 29 | input source, `TIL`/`UNTIL` truncates the input at an exact full-line | ||
| 30 | marker (marker and everything after it is discarded). At most one | ||
| 31 | input keyword (USE and TIL may pair, USE first) and one output | ||
| 32 | keyword; input keywords precede the filters, the output keyword | ||
| 33 | comes last. | ||
| 34 | - **Filters are trailing flags** applied left to right as stages over a | ||
| 35 | line stream: `-h N` (emit first N lines, then kill the upstream | ||
| 36 | command), `-t N` (hold back a rolling window of the last N lines; | ||
| 37 | emit only when the stream ends — EOF or ^C, which flushes), | ||
| 38 | `-g PAT` (smart-case literal substring match: case-insensitive unless | ||
| 39 | PAT contains an uppercase letter), `-p` (pause at each screenful, | ||
| 40 | `more`-style, only when output exceeds one screenful). Every flag | ||
| 41 | may repeat; each instance is another stage. After `-g`, the next | ||
| 42 | token is the pattern verbatim. | ||
| 43 | - **There are no pipes.** The four built-in filters cover the cases | ||
| 44 | pipes are usually built for; arbitrary command composition is | ||
| 45 | deliberately out of scope. | ||
| 46 | - **`> >> < <<` are removed** — `>` is now ordinary text. The shell | ||
| 47 | prints to both GOP and serial; a file sink replaces terminal output | ||
| 48 | entirely. | ||
| 49 | - **^D is EOF everywhere and no longer powers off** (^C keeps its | ||
| 50 | line-cancel / stream-abort role; `poweroff`/`exit` are the only | ||
| 51 | shutdown verbs). | ||
| 52 | - **Quoting (`"..."` groups and literalizes, `\x` escapes one char)** | ||
| 53 | is the escape hatch: quoted/escaped text is never an operator, which | ||
| 54 | makes keywords expressible as data and enables spaces in filenames. | ||
| 55 | |||
| 56 | ## Consequences | ||
| 57 | |||
| 58 | - **Positive:** One grammar, one parser — the per-command redirection | ||
| 59 | hacks (`cmd_echo`'s `>` scan) disappear. Filters compose uniformly | ||
| 60 | with any command and with each other, including intermediate views | ||
| 61 | (`-h N -p -g pat -p` shows each stage's result). No pipe machinery, | ||
| 62 | no process/stream abstraction to build. Lowercase text can never | ||
| 63 | collide with operators, so ordinary words stay ordinary. | ||
| 64 | - **Positive:** `tail` on an infinite stream (`cat /dev/mem -g 100 | ||
| 65 | -t 10`) is expressible without a pipe; ^C flushes the held-back | ||
| 66 | window instead of losing it. | ||
| 67 | - **Negative:** Breaks existing muscle memory and tooling: POSIX users | ||
| 68 | must learn the keywords; `scripts/test-disk.sh` and the `help` text | ||
| 69 | must be rewritten from `echo >` to the keyword syntax. | ||
| 70 | - **Negative:** Keywords are reserved, so printing text like `WRITING` | ||
| 71 | requires quoting; unknown commands cannot be filtered with flags | ||
| 72 | beyond the four built-ins; there is no way to compose arbitrary | ||
| 73 | commands (accepted — the shell has no external commands today). | ||
| 74 | - **Negative:** `-t` on a stream that never ends shows nothing until | ||
| 75 | the user ^C's — an intentional trade-off for simplicity (no pager | ||
| 76 | machinery, no infinite buffering beyond the window). | ||
