Split the documentation by who reads it
Some checks failed
akbasic CI Build / cmake_build (push) Successful in 3m2s
akbasic CI Build / sanitizers (push) Successful in 3m51s
akbasic CI Build / coverage (push) Failing after 3m24s
akbasic CI Build / akgl_build (push) Failing after 20s
akbasic CI Build / mutation_test (push) Has been cancelled
Some checks failed
akbasic CI Build / cmake_build (push) Successful in 3m2s
akbasic CI Build / sanitizers (push) Successful in 3m51s
akbasic CI Build / coverage (push) Failing after 3m24s
akbasic CI Build / akgl_build (push) Failing after 20s
akbasic CI Build / mutation_test (push) Has been cancelled
README.md was 577 lines and answered four different questions at once: what
the project is, how to build it, every verb and function in the language, and
how to maintain the test harness. The verb and function lists had already been
written a second time in docs/11 and docs/12, which is how a list of that size
goes stale -- there is no way to notice the two have drifted apart.
README.md is now 150 lines and holds only what somebody evaluating the project
needs: what it is, the quickstart, why it was rewritten in C, the five rules
embedding imposes on the design, the two ways to use it, and where everything
else lives. Technical detail goes to docs/, maintenance to MAINTENANCE.md.
The akbasic_TextSink struct moved to docs/10-embedding.md rather than being
deleted. It was the corpus's only `c excerpt=` block -- the check that caught
the stale struct two commits ago -- so dropping it with the README would have
quietly retired a test. docs/10 also stopped claiming README.md carries the
full API surface and the pool limits, which the trim made false.
CLAUDE.md went from 458 lines to 62, because almost none of it was
agent-specific. The project goals, the Go reference and its architecture, the
dependency version and ABI rules, the four ways an embedded build collides,
the libakerror convention, the error-code range map and the style rules are
all things a maintainer needs, and they are now in MAINTENANCE.md with one
copy to keep true. CLAUDE.md points there and keeps only the rules no test
enforces: tests in the same commit asserting the correct contract, file a
missing dependency capability upstream, do not edit generated output or
tests/reference/, co-author your commits.
Four claims did not survive the move, having gone stale where nothing could
notice:
- "The repository is currently empty apart from its submodules -- no
commits, no source tree, no build files." There are 43 commits.
- libakgl's target_compile_definitions(akerror PUBLIC AKERR_MAX_ERR_VALUE)
at deps/libakgl/CMakeLists.txt:44, described as inert but present. It is
gone; only a historical mention in a comment remains.
- "akbasic_init() claims 512-767." There is no akbasic_init. It is
akbasic_error_register(), called from akbasic_runtime_init().
- Time-relative phrasing ("libakgl hit two of them in the last week").
Five places pointed at CLAUDE.md for the range map or the file-it-upstream
rule and now point at MAINTENANCE.md: include/akbasic/error.h,
src/runtime_disk.c and three entries in TODO.md. Both source changes are
comments. deps/libakgl/TODO.md cites it too and is left alone; it is a
submodule, and the rule it quotes is still reachable from CLAUDE.md.
ctest is green at 95 of 95, docs_examples included: 36 programs, 9
transcripts, 44 output comparisons, 3 C snippets, 1 excerpt.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -78,6 +78,33 @@ bounded run is usually inside a `FOR` or `GOSUB` body, and a variable created th
|
||||
dies when the body pops — silently, with the script reading it correctly right up until
|
||||
it stops.
|
||||
|
||||
## Where the output goes
|
||||
|
||||
`PRINT` writes through an `akbasic_TextSink`, which is a record of function pointers plus
|
||||
whatever state you hang off `self`:
|
||||
|
||||
```c excerpt=include/akbasic/sink.h
|
||||
typedef struct akbasic_TextSink
|
||||
{
|
||||
void *self;
|
||||
akerr_ErrorContext AKERR_NOIGNORE *(*write)(struct akbasic_TextSink *self, const char *text);
|
||||
akerr_ErrorContext AKERR_NOIGNORE *(*writeln)(struct akbasic_TextSink *self, const char *text);
|
||||
akerr_ErrorContext AKERR_NOIGNORE *(*readline)(struct akbasic_TextSink *self, char *dest, size_t len, bool *eof);
|
||||
akerr_ErrorContext AKERR_NOIGNORE *(*clear)(struct akbasic_TextSink *self);
|
||||
akerr_ErrorContext AKERR_NOIGNORE *(*moveto)(struct akbasic_TextSink *self, int col, int row);
|
||||
akerr_ErrorContext AKERR_NOIGNORE *(*window)(struct akbasic_TextSink *self, int left, int top, int right, int bottom);
|
||||
} akbasic_TextSink;
|
||||
```
|
||||
|
||||
`akbasic_sink_init_stdio()` ships with the library and is what the driver uses. A game
|
||||
supplies its own and draws into a text layer. `readline` is expected to set `*eof` rather
|
||||
than block — that is how `INPUT` behaves sanely inside a frame.
|
||||
|
||||
`akbasic_sink_init_tee()` also ships, and composes two sinks into one: writes go to both,
|
||||
and `readline` comes from whichever of the two you name as the reader. That is how the SDL
|
||||
build puts `PRINT` in a window *and* on stdout. It needs no SDL, so you can use it to log a
|
||||
script's output to a file while you draw.
|
||||
|
||||
## Lending devices
|
||||
|
||||
```c wrap=hostbody
|
||||
@@ -109,5 +136,8 @@ program keeps running.
|
||||
|
||||
## Reading it all
|
||||
|
||||
`README.md` in the repository root carries the full API surface, the pool limits, and
|
||||
the exact list of what each target pulls in.
|
||||
Two complete hosts are checked in and built by every build, so neither can rot:
|
||||
`examples/embed.c` runs a script a bounded number of steps at a time, and
|
||||
`examples/hostvars.c` passes integers, floats and strings in both directions. The full API
|
||||
surface is the headers under `include/akbasic/`, which `doxygen Doxyfile` renders; the pool
|
||||
limits are the table at the end of [Chapter 13](13-differences.md).
|
||||
|
||||
Reference in New Issue
Block a user