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.0 KiB
2. Getting started
The prompt
Run basic with no arguments and you get a prompt:
$ ./build/basic
READY
READY is printed whenever the interpreter is waiting for you, which is at startup
and after a program stops.
Anything you type with a line number is stored as part of a program. Anything you type without one runs immediately:
PRINT 2 + 2
4
Your first program
10 PRINT "WHAT IS YOUR NAME"
20 INPUT "> " N$
30 PRINT "HELLO, " + N$
RUN
ADA
WHAT IS YOUR NAME
> HELLO, ADA
READY
The last line of the first block is not part of the program — it is what you type when
INPUT asks.
Type LIST to see it back, RUN to run it again, and NEW to throw it away.
Line numbers
Lines are stored under their numbers and run in numeric order, so the gaps are what let you insert later:
10 PRINT "FIRST"
30 PRINT "THIRD"
20 PRINT "SECOND"
LIST
10 PRINT "FIRST"
20 PRINT "SECOND"
30 PRINT "THIRD"
Typing a line number with nothing after it deletes that line. DELETE 20-40 removes a
range, and RENUMBER tidies the whole program up — it rewrites every GOTO and
GOSUB to match, so it will not break your branches.
AUTO 10 turns on automatic numbering so you do not have to type them; AUTO 0 turns
it off again.
Several statements on one line
Statements are separated by colons:
10 A# = 1 : B# = 2 : PRINT A# + B#
3
There is one important limit: block structures do not work inside a single line.
10 FOR I# = 1 TO 3 : PRINT I# : NEXT I#
20 PRINT "DONE"
DONE
prints nothing at all. The loop body is skipped entirely, because the interpreter skips
forward a line at a time looking for the NEXT and never finds one on the line it is
already past. Write loops across several lines:
10 FOR I# = 1 TO 3
20 PRINT I#
30 NEXT I#
1
2
3
The same applies to DO/LOOP.
Running a file
$ ./build/basic program.bas
HELLO FROM A FILE
The file is read, stored, and run. It is exactly the same as typing the program in and saving yourself the trouble.
You can also pipe a program in:
$ echo '10 PRINT "HI"
RUN' | ./build/basic
READY
HI
READY
Saving and loading
10 PRINT "HI"
DSAVE "myprogram.bas"
NEW
DLOAD "myprogram.bas"
LIST
10 PRINT "HI"
That is a whole round trip: save it, throw it away with NEW, load it back, and LIST
shows it again.
SAVE and LOAD are the same verbs under their other names. VERIFY "myprogram.bas"
compares what is in memory against the file and prints OK if they match.
Stopping
QUIT ends the interpreter. STOP stops a program and returns you to the prompt,
where CONT resumes it from where it stopped. END also stops the program, but does
not arm CONT.
In the SDL build, closing the window stops the program too.