docs/ and README.md carry 85 fenced blocks. Every one was checked by hand exactly once, when it was written, which is not a standard that survives a changing interpreter -- and four were already wrong: two transcripts showing a leading space PRINT does not emit, akbasic_TextSink in README.md missing the two members it had grown hours earlier, and FILTER's refusal quoted with wording the code does not use. tests/docs_examples.sh reads a fence-tag vocabulary and runs what it finds. BASIC programs and transcripts run and are byte-compared against an `output` block; C snippets compile with -fsyntax-only against the real include path, which CMake writes out because it is transitive through akerror, akstdlib and akgl; shell blocks run in a sandbox. Anything that would reconfigure the build tree, hit the network or re-enter the suite is tagged norun with the reason in MAINTENANCE.md, and the two cmake blocks stay hand-maintained by decision. An untagged block is a failure rather than a default, and the pass line reports what it executed by kind. Both exist because the way a harness like this dies is by quietly matching nothing and passing -- which it duly did on the first CTest run, where a generator expression evaluating to nothing still contributed an empty argument that the script read as a filename. The count is what caught it. The excerpt check earns its own mention: a block tagged `c excerpt=include/akbasic/sink.h` must still appear in that header, comments and whitespace ignored. Compiling it would only redefine the type, so a compile check could not have found the stale struct, and did not. Registered as the CTest case docs_examples in both configurations. Fixing the four wrong examples turned up two interpreter defects, fixed in the previous commit and recorded in TODO.md section 8. MAINTENANCE.md is new: the fence-tag reference, what to do when the case fails, and the conventions that until now only existed inside source comments -- the three test lists and how two of them invert "passed", the sorted verb table, that a golden file is never edited to suit this interpreter, and that a fix gets mutation-checked with a file copy rather than git checkout. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
3.6 KiB
5. Strings and formatting
The string functions
| Function | What it gives you |
|---|---|
LEN(A$) |
how many characters, or how many elements in an array |
LEFT(A$, N#) |
the leftmost N# characters |
RIGHT(A$, N#) |
the rightmost N# characters |
MID(A$, START#, LENGTH#) |
a substring, counting from zero |
INSTR(A$, B$) |
where B$ appears in A$ counting from zero, or -1 |
CHR(N#) |
the character for a Unicode code point |
STR(N#) |
a number as a string |
VAL(A$) |
a string as a number |
HEX(N#) |
a number as hexadecimal text |
SPC(N#) |
that many spaces |
LEFT and RIGHT clamp rather than failing, so asking for more characters than the
string has gives you the whole string.
MID and INSTR count from zero, where a C128's MID$ and INSTR count from one.
MID("HELLO WORLD", 6, 5) is WORLD, and INSTR("HELLO WORLD", "WORLD") is 6. A
failed INSTR gives -1, not 0, so there is no ambiguity with a match at the start.
10 A$ = "HELLO WORLD"
20 PRINT LEN(A$)
30 PRINT LEFT(A$, 5)
40 PRINT RIGHT(A$, 5)
50 PRINT MID(A$, 6, 5)
60 PRINT INSTR(A$, "WORLD")
11
HELLO
WORLD
WORLD
6
VAL refuses text that is not a number rather than quietly returning zero, so a
program can tell "the user typed 0" from "the user typed nonsense".
PRINT USING
PRINT USING lays a value out in a fixed field, which is what makes columns line up.
10 PRINT USING "###.##"; 3.14159
20 PRINT USING "TOTAL: $#,###.##"; 1234.5
30 PRINT USING "####-"; -42
3.14
TOTAL: $1,234.50
42-
The field is built from these characters, and any text around it is printed as it stands:
| In the field | Means |
|---|---|
# |
one digit position |
. |
the decimal point |
, |
group the integer part in threes |
$ |
a currency sign |
+ or - at the front |
a sign position before the number |
+ or - at the end |
a sign position after it |
= |
centre a string in the field |
> |
right-justify a string |
A value too wide for its field fills the field with *. That is deliberate and it
is loud: printing more digits than the field asked for would push every later column
out of line.
10 PRINT USING "###"; 99999
***
A negative number in a field with no sign position overflows the same way, because
printing -5 as 5 would be worse.
String fields centre and right-justify:
10 PRINT USING "=========="; "MID"
20 PRINT USING ">>>>>>>>>>"; "RIGHT"
MID
RIGHT
One field per statement. PRINT USING "### ###"; A, B is not supported — see
Chapter 13.
PUDEF
PUDEF changes the characters a numeric field pads and punctuates with. It takes up
to four, in this order: the leading blank, the thousands separator, the decimal point,
and the currency sign.
10 PUDEF "*"
20 PRINT USING "#####"; 42
***42
Positions you leave out keep what they had, so the one-character form above changes only the padding.
CHAR
CHAR puts text at a character position rather than at the cursor:
10 CHAR 1, 10, 5, "HERE"
20 PRINT
HERE
The arguments are colour, column, row and the text. CHAR writes no newline of its
own and leaves the cursor after the text, which is why line 20 is there. It needs a text device with a
cursor, which means the SDL build — a terminal's cursor is not this library's to move,
and it refuses by name in the default build.
The colour argument is accepted and ignored; the sink draws in one colour, chosen by whoever built it.