5 Commits

Author SHA1 Message Date
e82efc5bc1 Merge branch 'feature/reduce_memory_usage' into galaga-tutorial
All checks were successful
akbasic CI Build / cmake_build (push) Successful in 3m22s
akbasic CI Build / sanitizers (push) Successful in 4m31s
akbasic CI Build / coverage (push) Successful in 4m19s
akbasic CI Build / akgl_build (push) Successful in 8m7s
akbasic CI Build / mutation_test (push) Successful in 23m42s
2026-08-04 16:39:29 -04:00
7d0ec1fe83 Keep BASIC fixtures within the input line limit
All checks were successful
akbasic CI Build / cmake_build (push) Successful in 3m50s
akbasic CI Build / coverage (push) Successful in 4m4s
akbasic CI Build / sanitizers (push) Successful in 4m48s
akbasic CI Build / akgl_build (push) Successful in 9m55s
akbasic CI Build / mutation_test (push) Successful in 17m50s
Reflow the approved reference cases, shorten local fixture comments, and split the executable docs line so the 80-byte input buffer can read every source line.

Co-authored-by: andrew <andrew@aklabs.net>
2026-08-04 16:12:42 -04:00
e13a8a6da1 revert 3a8478131a
Some checks failed
akbasic CI Build / sanitizers (push) Failing after 6m58s
akbasic CI Build / cmake_build (push) Failing after 19m38s
akbasic CI Build / akgl_build (push) Failing after 12m29s
akbasic CI Build / mutation_test (push) Failing after 4m30s
akbasic CI Build / coverage (push) Failing after 20m51s
revert Register line-limit fixtures as expected failures

This issue is not something this branch should solve
2026-08-04 11:38:32 -04:00
46cb4549fe Merge pull request 'Rework the megademo for the 80-column line limit' (#35) from fix/megademo-80col into feature/reduce_memory_usage
Some checks failed
akbasic CI Build / cmake_build (push) Failing after 5m21s
akbasic CI Build / sanitizers (push) Failing after 5m2s
akbasic CI Build / coverage (push) Failing after 6m49s
akbasic CI Build / mutation_test (push) Failing after 8m59s
akbasic CI Build / akgl_build (push) Has been cancelled
Reviewed-on: #35
2026-08-04 10:50:44 -04:00
01adf80751 Rework the megademo to fit the 80-column source line limit
Some checks failed
akbasic CI Build / cmake_build (push) Failing after 3m29s
akbasic CI Build / coverage (push) Failing after 3m58s
akbasic CI Build / sanitizers (push) Failing after 4m36s
akbasic CI Build / mutation_test (push) Failing after 3m52s
akbasic CI Build / akgl_build (push) Failing after 7m29s
AKBASIC_MAX_LINE_LENGTH's cut from 256 to 80 left seventeen lines of
examples/megademo unloadable: the sixteen IM$() picture strings (up to
252 characters) and TUNEA/TUNEB's four-bar PLAY strings (174 and 175).
The real ceiling is 78 characters, not 80 -- stdio_readline() refuses a
read that fills the 80-byte buffer without a newline, so content plus
its terminator must fit in 79.

The picture: vaporwave.py's PAYLOAD drops from 240 to 64, so every
emitted IM$(NN) = "..." line fits under the ceiling. chop() no longer
slices blind; it walks the stream a record at a time -- two characters
for a run, three for an R row record -- and never cuts inside one,
because the decoder reads a record's tail with MID on the string it is
walking and a record straddling two IM$ entries decodes as garbage.
The old blind slice at 240 only happened to be safe. verify() now
simulates the CHOPPED strings with the cursor threaded across the
boundaries exactly the way DRAWSTREAM executes them, so a bad cut is
an assertion failure instead of a corrupted screen, and emit_block()
asserts every emitted line fits. The picture is 56 strings where it
was 16; the decoder needed no changes at all, since it already carries
X#/Y# from one IM$ entry to the next.

The music: TUNEA and TUNEB each become four PLAY statements, one bar
apiece. play.c keeps voice, envelope, level and duration state on the
runtime across statements and every PLAY appends to the same queue, so
four bars queue exactly as one long string did. Each bar restates the
V1T3U9S prefix so a bar dropped by QFULL cannot leave the next batch
playing on the drum kit's envelope.

Everything still clears the shrunken pools with room to spare: 1625
source lines of 2048, ~704 array slots of 2048, identifiers within the
24-character symtab key. Verified end to end against this branch's
build: the demo loads, the offscreen host renders every scene, and the
scene-5 still is pixel-identical to vaporwave.py's own preview. The
test suite fails the same seventeen cases with and without this
commit.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01ACffnV6F7sxQuG3Y8a1L3s
2026-08-03 22:57:57 -04:00
21 changed files with 185 additions and 119 deletions

View File

@@ -609,11 +609,6 @@ file(GLOB_RECURSE AKBASIC_GOLDEN_CASES
"${CMAKE_CURRENT_SOURCE_DIR}/tests/reference/*.bas" "${CMAKE_CURRENT_SOURCE_DIR}/tests/reference/*.bas"
) )
set(AKBASIC_LINE_LIMIT_GOLDEN_CASES
language/flowcontrol/nestedforloopwaitingforcommand.bas
language/functions/mod.bas
)
foreach(_case IN LISTS AKBASIC_GOLDEN_CASES) foreach(_case IN LISTS AKBASIC_GOLDEN_CASES)
string(REGEX REPLACE "^tests/" "" _name "${_case}") string(REGEX REPLACE "^tests/" "" _name "${_case}")
string(REGEX REPLACE "\\.bas$" "" _name "${_name}") string(REGEX REPLACE "\\.bas$" "" _name "${_name}")
@@ -625,12 +620,6 @@ foreach(_case IN LISTS AKBASIC_GOLDEN_CASES)
-DCASE=${CMAKE_CURRENT_SOURCE_DIR}/tests/reference/${_case} -DCASE=${CMAKE_CURRENT_SOURCE_DIR}/tests/reference/${_case}
-P ${CMAKE_CURRENT_SOURCE_DIR}/tests/golden.cmake -P ${CMAKE_CURRENT_SOURCE_DIR}/tests/golden.cmake
) )
# The reference corpus is immutable, and these two source lines are longer
# than the 80-column input contract. Keep the cases visible in CTest while
# issue #32 remains open rather than editing the upstream-derived fixtures.
if(_case IN_LIST AKBASIC_LINE_LIMIT_GOLDEN_CASES)
_set_tests_properties(golden_${_name} PROPERTIES WILL_FAIL TRUE)
endif()
endforeach() endforeach()
if(AKBASIC_GOLDEN_CASES) if(AKBASIC_GOLDEN_CASES)
@@ -669,23 +658,6 @@ file(GLOB_RECURSE AKBASIC_LOCAL_CASES
"${CMAKE_CURRENT_SOURCE_DIR}/tests/language/*.bas" "${CMAKE_CURRENT_SOURCE_DIR}/tests/language/*.bas"
) )
set(AKBASIC_LINE_LIMIT_LOCAL_CASES
language/arrays_in_parameter_lists.bas
language/audio/filter_refused.bas
language/audio/no_device.bas
language/functions/recursion.bas
language/graphics/argument_errors.bas
language/graphics/no_device.bas
language/housekeeping/verbs.bas
language/numeric/octal_literal.bas
language/numeric/truth_value_arithmetic.bas
language/statements/multiple_per_line.bas
language/structures/missing_field.bas
language/structures/parameters.bas
language/structures/records.bas
language/structures/reserved_names.bas
)
# #
# One family of local cases is driver-specific and has to be. A case named # One family of local cases is driver-specific and has to be. A case named
# no_device.bas asserts that a verb needing a device refuses politely when it was # no_device.bas asserts that a verb needing a device refuses politely when it was
@@ -712,12 +684,6 @@ foreach(_case IN LISTS AKBASIC_LOCAL_CASES)
-P ${CMAKE_CURRENT_SOURCE_DIR}/tests/golden.cmake -P ${CMAKE_CURRENT_SOURCE_DIR}/tests/golden.cmake
) )
list(APPEND AKBASIC_LOCAL_NAMES local_${_name}) list(APPEND AKBASIC_LOCAL_NAMES local_${_name})
# These fixtures exercise behavior unrelated to the line-length contract,
# but their existing source lines exceed the current 80-column limit. Keep
# them registered and visible until issue #32 is resolved.
if(_case IN_LIST AKBASIC_LINE_LIMIT_LOCAL_CASES)
_set_tests_properties(local_${_name} PROPERTIES WILL_FAIL TRUE)
endif()
endforeach() endforeach()
if(AKBASIC_LOCAL_NAMES) if(AKBASIC_LOCAL_NAMES)

View File

@@ -1423,7 +1423,8 @@ IF STATE# = 2 THEN GOSUB UNSTICK
RETURN RETURN
LABEL PRESSPAUSE LABEL PRESSPAUSE
IF STATE# = 2 THEN STATE# = 6 : GMTYP# = 0 : BAN$ = "PAUSED" : GOSUB SETBANNER : RETURN IF STATE# = 2 THEN STATE# = 6 : GMTYP# = 0 : BAN$ = "PAUSED"
IF STATE# = 2 THEN BAN$ = "PAUSED" : GOSUB SETBANNER : RETURN
IF STATE# = 6 THEN STATE# = 2 : BAN$ = "" : GOSUB SETBANNER IF STATE# = 6 THEN STATE# = 2 : BAN$ = "" : GOSUB SETBANNER
RETURN RETURN
``` ```

View File

@@ -56,7 +56,10 @@ This is a tour of the interpreter's edges, on purpose:
counterpoint: a genuine 24x21 `SPRSAV` type-in sprite, 63 bytes, which is counterpoint: a genuine 24x21 `SPRSAV` type-in sprite, 63 bytes, which is
everything `DATA` has room to say. The paint is the reveal, and the exit is a everything `DATA` has room to say. The paint is the reveal, and the exit is a
stride-63 column dissolve — 63 is coprime to 160, so the walk hits every stride-63 column dissolve — 63 is coprime to 160, so the walk hits every
column once and looks random while carrying no state. column once and looks random while carrying no state. The strings come in
64-character chunks because a source line is 80 columns, like the machines
this pretends to be — the decoder carries its cursor from one `IM$` entry to
the next, and the generator refuses to cut inside a run.
- **And then the picture is video.** Six delta frames loop the floor grid toward - **And then the picture is video.** Six delta frames loop the floor grid toward
you and crawl the sun's slice pattern — full motion video, 1.3 KB total, about you and crawl the sun's slice pattern — full motion video, 1.3 KB total, about
220 bytes a frame. A delta re-encodes only the rows that changed: an `R` 220 bytes a frame. A delta re-encodes only the rows that changed: an `R`
@@ -82,6 +85,10 @@ This is a tour of the interpreter's edges, on purpose:
batch a bar of noise drums. Noise is real: `ENVELOPE`'s sixth argument is a batch a bar of noise drums. Noise is real: `ENVELOPE`'s sixth argument is a
waveform, 0 to 3, which Chapter 7 forgot to mention. Voice 1 carries the tune so waveform, 0 to 3, which Chapter 7 forgot to mention. Voice 1 carries the tune so
the collision blips (voice 2) and scene sweeps (voice 3) never steal its channel. the collision blips (voice 2) and scene sweeps (voice 3) never steal its channel.
A batch is four `PLAY` statements, one bar each — the 80-column line limit will
not hold four bars in one string, and it does not need to: the parser's voice,
envelope and duration state persists across statements and every `PLAY` appends
to the same queue, so four bars queue exactly as one line would.
It also probes for its hardware like a proper boot loader, with one `TRAP` per It also probes for its hardware like a proper boot loader, with one `TRAP` per
device: no graphics refuses by name and exits, no audio mutes the soundtrack, no device: no graphics refuses by name and exits, no audio mutes the soundtrack, no

View File

@@ -461,38 +461,78 @@ BS% = W# / 160
IF BS% < 1 THEN BS% = 1 IF BS% < 1 THEN BS% = 1
REM ---- PICTURE-BEGIN (generated by vaporwave.py; do not REM ---- PICTURE-BEGIN (generated by vaporwave.py; do not
REM ---- hand-edit -- rerun the script to change the picture) REM ---- hand-edit -- rerun the script to change the picture)
DIM IM$(16) DIM IM$(56)
DIM VA#(6) DIM VA#(6)
DIM VB#(6) DIM VB#(6)
NS# = 9 NS# = 32
VA#(0) = 9 VA#(0) = 32
VB#(0) = 9 VB#(0) = 35
VA#(1) = 10 VA#(1) = 36
VB#(1) = 10 VB#(1) = 39
VA#(2) = 11 VA#(2) = 40
VB#(2) = 12 VB#(2) = 43
VA#(3) = 13 VA#(3) = 44
VB#(3) = 13 VB#(3) = 47
VA#(4) = 14 VA#(4) = 48
VB#(4) = 14 VB#(4) = 51
VA#(5) = 15 VA#(5) = 52
VB#(5) = 15 VB#(5) = 55
IM$(0) = "G9G9G9G9GPGHEHG9GTEHG9GTEHGPGPEHG9GTEHG9GTEHGHGXEHGTBAG8EHG9GTEHG5EHGPEHG5EHGPEHG5EHGAPAG3EHGPEHG5EHGPEHGXGHEHG5EHGPEHG5EHGFBAGIEHGPGPEHGPEHGHEHGPEHGPEHGHEHGPEHGHEHGPEHGPEHGHEHGBPAGMEHGPEHGHEHGPEHGHEHGPEHGPEHGHEHGPEHGPEHGHEHGPEHGHEPGHEHGPEH" IM$(0) = "G9G9G9G9GPGHEHG9GTEHG9GTEHGPGPEHG9GTEHG9GTEHGHGXEHGTBAG8EHG9GTEH"
IM$(1) = "GHEPGHEHGPEHGHEPGHEHGHEGBAEHGHEHGPEHGHEPGHEHGPEHGHEHGPEHGHEPGHEHGPEHGHEPGHEHGPECPAEDGHEPGHEHGHEPGHEPGHEHGHEPGHEPGHEHGHEPGHEHGHEPGHEPGHECBAEDGHEPGHEPGHEHGHEPGHEHGHEPGHEPGHEHGHEPGHEPEPGHEPGHE5GHEPGHE5GHEHEXGHEPGHEEPAEZGHEPGHE5GHE5GHEPGHE5GHEP" IM$(1) = "G5EHGPEHG5EHGPEHG5EHGAPAG3EHGPEHG5EHGPEHGXGHEHG5EHGPEHG5EHGFBAGI"
IM$(2) = "GHE5GHE9ETGHE9ETGHEXEHGHE9ETGHHAE9ESGHEPEPGBBAGEE9EMHOE9ETGHEHE9E7HUE9E6E9E4H0E9E3E9E3H2E9EHBAETEHKHE9ELH6E9ECKHEPEPKHE9EBH9HAE9EIKHEHEXKHE2H9HCE9EPKHE5KHEPKDH9HEKCEPKHE5KHE5KHEKH9HGEBKHEPKHEXEHKHE5KHEBH9HIEIKHEPKHEPEPKHEPKHEHKAH9HKKHEHKHEP" IM$(2) = "EHGPGPEHGPEHGHEHGPEHGPEHGHEHGPEHGHEHGPEHGPEHGHEHGBPAGMEHGPEHGHEH"
IM$(3) = "KHEHKHEPKHEPKHH9HMEGKHEHKHEPKHEHKHEPKHEPH9HMEOKHEHKHEPKHEHKPEHKHEGH9HOKFEPKHEHKPEHKHEHKPEHKGH9HOEFKHEPKHEHKHEPKHEHKPEFH9HQKEEHKHEPKHEHKHEPKHEHKMH9HSKLEHKHEPKHEHKPEHKHEHKEH9HSEDKPEHKPEHKHEHKPEHKHEEH9HSKDEHKPEHKPKPEHKPEHKDH9HUECKHEHKPEHKHKXEH" IM$(3) = "GPEHGHEHGPEHGPEHGHEHGPEHGPEHGHEHGPEHGHEPGHEHGPEHGHEPGHEHGPEHGHEP"
IM$(4) = "KPEDH9HUKCEHK5EHK5EHKLH9HUKKEHK5EHK5EHKCH9HWEBKPEHKXKHEHK8I9IWKZEHKPKPEHK0I9IWK7EHKHKXEHKSI9IWK9KFEHK9KOI9IWK9KNK9KOI9IWK9KNK9KOI9IWK9KNKPIHKZI9IYK6IHKHKXIHKSI9IWK9KFIHK5IHKKI9IWK9KNIHK5IHKCI9IYKPIHKXKHIHK5IHKPIHK5IHKPIHKPKPIHK0I9IWKJIHKPIH" IM$(4) = "GHEHGHEGBAEHGHEHGPEHGHEPGHEHGPEHGHEHGPEHGHEPGHEHGPEHGHEPGHEHGPEC"
IM$(5) = "KHIHKPIHKPI9IZKBIHKHIHKPIHKHIHKPIHKKI9IWKJIHKHIHKPIHKHIHKPIHKDI9IXKPIHKHIHKHKHIHKHIPKHI9IYKCIHKPIHKHIHKPIHKHIPKHIHKPIHKHIPKHIHKPIHKHIHKPIHKHI9I9IHKHIHKPIHKHIPKHIHKHIEC9CSKDIPKHIPKHIHKHIPKHIHKEC9CSIDKHIPKHIPIPKHIPKHIFC9CQKEIHKHIPKHIHIXKHIPKG" IM$(5) = "PAEDGHEPGHEHGHEPGHEPGHEHGHEPGHEPGHEHGHEPGHEHGHEPGHEPGHECBAEDGHEP"
IM$(6) = "C9COIFKHI5KHI5KHIPKHI5KHIPKHI5KHI5KHIPKHI5KHIPKHIXIHKHI9IDC9CMI4KHIPIPKHI6C9CKI9IDKHIHIXKHIZC9CII9IMKHI9IWC9CGI9IVI9I9I9I9IPI9I9I9I9IPQ9Q9Q9Q9QPE9E9E9E9EPE9E9E9E9EPQ9Q9Q9Q9QPE9E9E9E9EPQ9Q9Q9Q9QPE9E9E9E9EPQ9Q9Q9Q9QPQ9Q9Q9Q9QPQ9Q9Q9Q9QPQ9Q9Q9" IM$(6) = "GHEPGHEHGHEPGHEHGHEPGHEPGHEHGHEPGHEPEPGHEPGHE5GHEPGHE5GHEHEXGHEP"
IM$(7) = "Q9QPE9E9E9E9EPQ9Q9Q9Q9QPQ9Q9Q9Q9QPQ9Q9Q9Q9QPQ9Q9Q9Q9QPQ9Q9Q9Q9QPQ9Q9Q9Q9QPE9E9E9E9EPQ9Q9Q9Q9QPQ9Q9Q9Q9QPQ9Q9Q9Q9QPQ9Q9Q9Q9QPQ9Q9Q9Q9QPQ9Q9Q9Q9QPQ9Q9Q9Q9QPQ9Q9Q9Q9QPQ9Q9Q9Q9QPQ9Q9Q9Q9QPE9E9E9E9EPQ9Q9Q9Q9QPQ9Q9Q9Q9QPQ9Q9Q9Q9QPQ9Q9Q9Q9QPQ9Q9Q9" IM$(7) = "GHEEPAEZGHEPGHE5GHE5GHEPGHE5GHEPGHE5GHE9ETGHE9ETGHEXEHGHE9ETGHHA"
IM$(8) = "Q9QPQ9Q9Q9Q9QPQ9Q9Q9Q9QPQ9Q9Q9Q9QPQ9Q9Q9Q9QPQ9Q9Q9Q9QPQ9Q9Q9Q9QPQ9Q9Q9Q9QPQ9Q9Q9Q9QPQ9Q9Q9Q9QPQ9Q9Q9Q9QPQ9Q9Q9Q9QP" IM$(8) = "E9ESGHEPEPGBBAGEE9EMHOE9ETGHEHE9E7HUE9E6E9E4H0E9E3E9E3H2E9EHBAET"
IM$(9) = "RBRQ9QOKMQHK5RBSQ9QTI9IRRBXQ9QTKPQHKHQPKERBYQ9QPI9IHRB3Q9QSKAI5KHIJRB5Q9QTC9CMRB9Q9QWI9IGRCBQ9QYC9CCRCIA9A9A9A9APRCJE9E9E9E9EPRCNA9A9A9A9APRCOE9E9E9E9EPRCUA9A9A9A9APRCVE9E9E9E9EPRC5A9A9A9A9APRC7E9E9E9E9EP" IM$(9) = "EHKHE9ELH6E9ECKHEPEPKHE9EBH9HAE9EIKHEHEXKHE2H9HCE9EPKHE5KHEPKDH9"
IM$(10) = "RBQQ9QOK9KIQHKFRBRQ9QOI9IQRBWQ9QPKLQHKHQHKPRBXQ9QTI9QLIERB2Q9QRIBKHIPKHIPKCRB4Q9QSC9CORB8Q9QVI3KHIGRCAQ9QXC9CERCJA9A9A9A9APRCKE9E9E9E9EPRCOA9A9A9A9APRCPE9E9E9E9EPRCVA9A9A9A9APRCXE9E9E9E9EPRC7A9A9A9A9APRDAE9E9E9E9EP" IM$(10) = "HEKCEPKHE5KHE5KHEKH9HGEBKHEPKHEXEHKHE5KHEBH9HIEIKHEPKHEPEPKHEPKH"
IM$(11) = "RBPQ9QOK9KAQHKNRBQQ9QOI9IWRBVQ9QOKEQHKHQHKPQHKFRBWQ9QPI9IPRB1Q9QQKCIPKHIPKHIDRB3Q9QSC9CORB7Q9QUIWKHIPRB9Q9QWC9CGRCEA9A9A9A9APRCFE9E9E9E9EPRCGA9A9A9A9APRCHE9E9E9E9EPRCPA9A9A9A9APRCQE9E9E9E9EPRCXA9A9A9A9APRCZE9E9E9E9EPRDAA9A9A9A9APRDCE9E9E9E9" IM$(11) = "EHKAH9HKKHEHKHEPKHEHKHEPKHEPKHH9HMEGKHEHKHEPKHEHKHEPKHEPH9HMEOKH"
IM$(12) = "EP" IM$(12) = "EHKHEPKHEHKPEHKHEGH9HOKFEPKHEHKPEHKHEHKPEHKGH9HOEFKHEPKHEHKHEPKH"
IM$(13) = "RBOQ9QNK3QHKWRBPQ9QOI9IWRBUQ9QTKHQHKPQHKNRBVQ9QOI9IWRB0Q9QQIKKHIPKHIHKDRB1Q9QQC9CSRB2Q9QRC9CQRB6Q9QTIPKHIYRB8Q9QVC9CIRCKA9A9A9A9APRCLE9E9E9E9EPRCQA9A9A9A9APRCRE9E9E9E9EPRCZA9A9A9A9APRC1E9E9E9E9EPRDCA9A9A9A9APRDFE9E9E9E9EP" IM$(13) = "EHKPEFH9HQKEEHKHEPKHEHKHEPKHEHKMH9HSKLEHKHEPKHEHKPEHKHEHKEH9HSED"
IM$(14) = "RBOQ9QNI9IYRBTQ9QOKEQHKPQHKVRBUQ9QTI9IRRBZQ9QTKHQHKPQHKHRB0Q9QQC9CSRB5Q9QTIHKHI5KARB7Q9QUC9CKRCBQ9QYI9ICRCHA9A9A9A9APRCIE9E9E9E9EPRCLA9A9A9A9APRCME9E9E9E9EPRCRA9A9A9A9APRCTE9E9E9E9EPRC1A9A9A9A9APRC3E9E9E9E9EPRDFA9A9A9A9APRDIE9E9E9E9EP" IM$(14) = "KPEHKPEHKHEHKPEHKHEEH9HSKDEHKPEHKPKPEHKPEHKDH9HUECKHEHKPEHKHKXEH"
IM$(15) = "RBSQ9QTKPQHK3RBTQ9QOI9IWRBYQ9QPKDQHKPQHKHRBZQ9QTI9ILRB4Q9QSIAKHI5KHIBRB6Q9QTC9CMRCAQ9QXI9IERCEE9E9E9E9EPRCFA9A9A9A9APRCGE9E9E9E9EPRCMA9A9A9A9APRCNE9E9E9E9EPRCTA9A9A9A9APRCUE9E9E9E9EPRC3A9A9A9A9APRC5E9E9E9E9EPRDIA9A9A9A9AP" IM$(15) = "KPEDH9HUKCEHK5EHK5EHKLH9HUKKEHK5EHK5EHKCH9HWEBKPEHKXKHEHK8I9IWKZ"
IM$(16) = "EHKPKPEHK0I9IWK7EHKHKXEHKSI9IWK9KFEHK9KOI9IWK9KNK9KOI9IWK9KNK9KO"
IM$(17) = "I9IWK9KNKPIHKZI9IYK6IHKHKXIHKSI9IWK9KFIHK5IHKKI9IWK9KNIHK5IHKCI9"
IM$(18) = "IYKPIHKXKHIHK5IHKPIHK5IHKPIHKPKPIHK0I9IWKJIHKPIHKHIHKPIHKPI9IZKB"
IM$(19) = "IHKHIHKPIHKHIHKPIHKKI9IWKJIHKHIHKPIHKHIHKPIHKDI9IXKPIHKHIHKHKHIH"
IM$(20) = "KHIPKHI9IYKCIHKPIHKHIHKPIHKHIPKHIHKPIHKHIPKHIHKPIHKHIHKPIHKHI9I9"
IM$(21) = "IHKHIHKPIHKHIPKHIHKHIEC9CSKDIPKHIPKHIHKHIPKHIHKEC9CSIDKHIPKHIPIP"
IM$(22) = "KHIPKHIFC9CQKEIHKHIPKHIHIXKHIPKGC9COIFKHI5KHI5KHIPKHI5KHIPKHI5KH"
IM$(23) = "I5KHIPKHI5KHIPKHIXIHKHI9IDC9CMI4KHIPIPKHI6C9CKI9IDKHIHIXKHIZC9CI"
IM$(24) = "I9IMKHI9IWC9CGI9IVI9I9I9I9IPI9I9I9I9IPQ9Q9Q9Q9QPE9E9E9E9EPE9E9E9"
IM$(25) = "E9EPQ9Q9Q9Q9QPE9E9E9E9EPQ9Q9Q9Q9QPE9E9E9E9EPQ9Q9Q9Q9QPQ9Q9Q9Q9QP"
IM$(26) = "Q9Q9Q9Q9QPQ9Q9Q9Q9QPE9E9E9E9EPQ9Q9Q9Q9QPQ9Q9Q9Q9QPQ9Q9Q9Q9QPQ9Q9"
IM$(27) = "Q9Q9QPQ9Q9Q9Q9QPQ9Q9Q9Q9QPE9E9E9E9EPQ9Q9Q9Q9QPQ9Q9Q9Q9QPQ9Q9Q9Q9"
IM$(28) = "QPQ9Q9Q9Q9QPQ9Q9Q9Q9QPQ9Q9Q9Q9QPQ9Q9Q9Q9QPQ9Q9Q9Q9QPQ9Q9Q9Q9QPQ9"
IM$(29) = "Q9Q9Q9QPE9E9E9E9EPQ9Q9Q9Q9QPQ9Q9Q9Q9QPQ9Q9Q9Q9QPQ9Q9Q9Q9QPQ9Q9Q9"
IM$(30) = "Q9QPQ9Q9Q9Q9QPQ9Q9Q9Q9QPQ9Q9Q9Q9QPQ9Q9Q9Q9QPQ9Q9Q9Q9QPQ9Q9Q9Q9QP"
IM$(31) = "Q9Q9Q9Q9QPQ9Q9Q9Q9QPQ9Q9Q9Q9QPQ9Q9Q9Q9QPQ9Q9Q9Q9QP"
IM$(32) = "RBRQ9QOKMQHK5RBSQ9QTI9IRRBXQ9QTKPQHKHQPKERBYQ9QPI9IHRB3Q9QSKAI5"
IM$(33) = "KHIJRB5Q9QTC9CMRB9Q9QWI9IGRCBQ9QYC9CCRCIA9A9A9A9APRCJE9E9E9E9EP"
IM$(34) = "RCNA9A9A9A9APRCOE9E9E9E9EPRCUA9A9A9A9APRCVE9E9E9E9EPRC5A9A9A9A9"
IM$(35) = "APRC7E9E9E9E9EP"
IM$(36) = "RBQQ9QOK9KIQHKFRBRQ9QOI9IQRBWQ9QPKLQHKHQHKPRBXQ9QTI9QLIERB2Q9QR"
IM$(37) = "IBKHIPKHIPKCRB4Q9QSC9CORB8Q9QVI3KHIGRCAQ9QXC9CERCJA9A9A9A9APRCK"
IM$(38) = "E9E9E9E9EPRCOA9A9A9A9APRCPE9E9E9E9EPRCVA9A9A9A9APRCXE9E9E9E9EP"
IM$(39) = "RC7A9A9A9A9APRDAE9E9E9E9EP"
IM$(40) = "RBPQ9QOK9KAQHKNRBQQ9QOI9IWRBVQ9QOKEQHKHQHKPQHKFRBWQ9QPI9IPRB1Q9"
IM$(41) = "QQKCIPKHIPKHIDRB3Q9QSC9CORB7Q9QUIWKHIPRB9Q9QWC9CGRCEA9A9A9A9AP"
IM$(42) = "RCFE9E9E9E9EPRCGA9A9A9A9APRCHE9E9E9E9EPRCPA9A9A9A9APRCQE9E9E9E9"
IM$(43) = "EPRCXA9A9A9A9APRCZE9E9E9E9EPRDAA9A9A9A9APRDCE9E9E9E9EP"
IM$(44) = "RBOQ9QNK3QHKWRBPQ9QOI9IWRBUQ9QTKHQHKPQHKNRBVQ9QOI9IWRB0Q9QQIKKH"
IM$(45) = "IPKHIHKDRB1Q9QQC9CSRB2Q9QRC9CQRB6Q9QTIPKHIYRB8Q9QVC9CIRCKA9A9A9"
IM$(46) = "A9APRCLE9E9E9E9EPRCQA9A9A9A9APRCRE9E9E9E9EPRCZA9A9A9A9APRC1E9E9"
IM$(47) = "E9E9EPRDCA9A9A9A9APRDFE9E9E9E9EP"
IM$(48) = "RBOQ9QNI9IYRBTQ9QOKEQHKPQHKVRBUQ9QTI9IRRBZQ9QTKHQHKPQHKHRB0Q9QQ"
IM$(49) = "C9CSRB5Q9QTIHKHI5KARB7Q9QUC9CKRCBQ9QYI9ICRCHA9A9A9A9APRCIE9E9E9"
IM$(50) = "E9EPRCLA9A9A9A9APRCME9E9E9E9EPRCRA9A9A9A9APRCTE9E9E9E9EPRC1A9A9"
IM$(51) = "A9A9APRC3E9E9E9E9EPRDFA9A9A9A9APRDIE9E9E9E9EP"
IM$(52) = "RBSQ9QTKPQHK3RBTQ9QOI9IWRBYQ9QPKDQHKPQHKHRBZQ9QTI9ILRB4Q9QSIAKH"
IM$(53) = "I5KHIBRB6Q9QTC9CMRCAQ9QXI9IERCEE9E9E9E9EPRCFA9A9A9A9APRCGE9E9E9"
IM$(54) = "E9EPRCMA9A9A9A9APRCNE9E9E9E9EPRCTA9A9A9A9APRCUE9E9E9E9EPRC3A9A9"
IM$(55) = "A9A9APRC5E9E9E9E9EPRDIA9A9A9A9AP"
REM ---- PICTURE-END REM ---- PICTURE-END
SS# = 0 SS# = 0
SE# = NS# - 1 SE# = NS# - 1
@@ -1317,13 +1357,25 @@ TB# = TB# + 1
IF TB# > 2 THEN TB# = 0 IF TB# > 2 THEN TB# = 0
RETURN RETURN
REM A batch is four PLAY statements, one bar each: the parser's V, T,
REM U and duration state persists across statements and every PLAY
REM appends to the same queue, so four bars queue exactly as one long
REM string would -- which the 80-column line limit no longer allows.
REM Each bar restates the prefix anyway, so a bar dropped by QFULL
REM never leaves the next one playing with drum-kit state.
LABEL TUNEA LABEL TUNEA
PLAY "V1T3U9S O1AO2AO3AO2AO3AO4CEAO1AO2AO4CEAO5CO4AE O1FO2FO3FO2FO3FAO4CFO1FO2FO3AO4CFAO5CO4A O1CO2CO3CO2CO3CEGO4CO1CO2CO3EGO4CEGO5C O1GO2GO3GO2GO3GBO4DGO1GO2GO3BO4DGBO5DO4B" PLAY "V1T3U9S O1AO2AO3AO2AO3AO4CEAO1AO2AO4CEAO5CO4AE"
PLAY "V1T3U9S O1FO2FO3FO2FO3FAO4CFO1FO2FO3AO4CFAO5CO4A"
PLAY "V1T3U9S O1CO2CO3CO2CO3CEGO4CO1CO2CO3EGO4CEGO5C"
PLAY "V1T3U9S O1GO2GO3GO2GO3GBO4DGO1GO2GO3BO4DGBO5DO4B"
MT# = TI# + 270 MT# = TI# + 270
RETURN RETURN
LABEL TUNEB LABEL TUNEB
PLAY "V1T3U9S O1AO2AO3AO2AO3AO4CEAO1AO2AO4CEAO5CEO4A O1FO2FO3FO2FO3FAO4CFO1FO2FO3AO4CFAO5CO4A O1DO2DO3DO2DO3DFAO4DO1DO2DO3FAO4DFAO5D O1EO2EO3EO2EO3E#GBO4EO1EO2EO3#GBO4E#GBO5E" PLAY "V1T3U9S O1AO2AO3AO2AO3AO4CEAO1AO2AO4CEAO5CEO4A"
PLAY "V1T3U9S O1FO2FO3FO2FO3FAO4CFO1FO2FO3AO4CFAO5CO4A"
PLAY "V1T3U9S O1DO2DO3DO2DO3DFAO4DO1DO2DO3FAO4DFAO5D"
PLAY "V1T3U9S O1EO2EO3EO2EO3E#GBO4EO1EO2EO3#GBO4E#GBO5E"
MT# = TI# + 270 MT# = TI# + 270
RETURN RETURN

View File

@@ -58,7 +58,14 @@ SKIP = "Q"
ROWREC = "R" ROWREC = "R"
LENCH = "ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789" LENCH = "ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789"
MAXRUN = len(LENCH) MAXRUN = len(LENCH)
PAYLOAD = 240
# The interpreter reads source through an 80-byte line buffer and refuses
# any line that fills it (AKBASIC_MAX_LINE_LENGTH, sink_stdio.c), so a
# stored line is at most 78 characters plus its newline. 'IM$(NN) = "' and
# the closing quote spend 12 of those; 64 keeps the emitted lines under
# the ceiling with margin to spare while the index stays two digits.
PAYLOAD = 64
MAXLINE = 78
SUN_CX, SUN_CY, SUN_R = 80, 50, 30 SUN_CX, SUN_CY, SUN_R = 80, 50, 30
HORIZON = 74 HORIZON = 74
@@ -206,13 +213,32 @@ def encode_delta(prev, cur):
def chop(blob): def chop(blob):
return [blob[i:i + PAYLOAD] for i in range(0, len(blob), PAYLOAD)] """Split a stream into strings of at most PAYLOAD characters, cutting
only between records. The decoder reads a record's tail characters
with MID on the string it is walking, so a run (two characters) or a
row record (three) that straddled two IM$ entries would decode as
garbage; DRAWSTREAM only carries the cursor, never a partial record."""
out, cur = [], ""
p = 0
while p < len(blob):
n = 3 if blob[p] == ROWREC else 2
if len(cur) + n > PAYLOAD:
out.append(cur)
cur = ""
cur += blob[p:p + n]
p += n
if cur:
out.append(cur)
return out
def simulate(raster, blob): def simulate(raster, blob, x=0, y=0):
"""Apply one encoded stream to a raster exactly the way the BASIC """Apply one encoded stream to a raster exactly the way the BASIC
decoder does, skips-draw-nothing and all.""" decoder does, skips-draw-nothing and all. The cursor comes in and
x = y = p = 0 goes back out because DRAWSTREAM carries it from one IM$ entry to
the next -- decoding the chopped strings one at a time with the
cursor threaded through is exactly what the demo will execute."""
p = 0
while p < len(blob): while p < len(blob):
c = blob[p] c = blob[p]
if c == ROWREC: if c == ROWREC:
@@ -230,20 +256,31 @@ def simulate(raster, blob):
x = 0 x = 0
y += 1 y += 1
p += 2 p += 2
return raster, x, y
def simulate_lines(raster, lines):
"""One stream as its chopped strings, cursor carried across the
boundaries the way DRAWSTREAM carries X# and Y#."""
x = y = 0
for line in lines:
raster, x, y = simulate(raster, line, x, y)
return raster return raster
def verify(frames, base_blob, delta_blobs): def verify(frames, base_lines, delta_line_groups):
"""The base must reproduce frame 0 exactly, and each delta must """The base must reproduce frame 0 exactly, and each delta must
carry the raster exactly to the next frame. A skip leaves the cell carry the raster exactly to the next frame. A skip leaves the cell
the encoder promised was already right, so equality is total and the encoder promised was already right, so equality is total and
any difference at all is an encoder bug.""" any difference at all is an encoder bug. This decodes the CHOPPED
strings, not the blobs, so a chop that split a record would fail
here instead of corrupting the screen."""
raster = [[1] * W for _ in range(H)] raster = [[1] * W for _ in range(H)]
raster = simulate(raster, base_blob) raster = simulate_lines(raster, base_lines)
assert raster == frames[0], "base stream does not reproduce frame 0" assert raster == frames[0], "base stream does not reproduce frame 0"
for i, blob in enumerate(delta_blobs): for i, lines in enumerate(delta_line_groups):
want = frames[(i + 1) % PHASES] want = frames[(i + 1) % PHASES]
raster = simulate(raster, blob) raster = simulate_lines(raster, lines)
assert raster == want, "delta %d does not reproduce its frame" % i assert raster == want, "delta %d does not reproduce its frame" % i
@@ -261,6 +298,9 @@ def emit_block(base_lines, delta_ranges, all_lines):
for i, s in enumerate(all_lines): for i, s in enumerate(all_lines):
out.append('IM$(%d) = "%s"' % (i, s)) out.append('IM$(%d) = "%s"' % (i, s))
out.append("REM ---- PICTURE-END") out.append("REM ---- PICTURE-END")
for line in out:
assert len(line) <= MAXLINE, "emitted line over %d chars: %r" % (
MAXLINE, line)
return out return out
@@ -303,15 +343,15 @@ def main():
base_lines = chop(base_blob) base_lines = chop(base_blob)
all_lines = list(base_lines) all_lines = list(base_lines)
delta_ranges = [] delta_ranges = []
delta_blobs = [] delta_line_groups = []
for i in range(PHASES): for i in range(PHASES):
blob = encode_delta(frames[i], frames[(i + 1) % PHASES]) blob = encode_delta(frames[i], frames[(i + 1) % PHASES])
delta_blobs.append(blob)
lines = chop(blob) lines = chop(blob)
delta_line_groups.append(lines)
delta_ranges.append((len(all_lines), len(all_lines) + len(lines) - 1)) delta_ranges.append((len(all_lines), len(all_lines) + len(lines) - 1))
all_lines.extend(lines) all_lines.extend(lines)
verify(frames, base_blob, delta_blobs) verify(frames, base_lines, delta_line_groups)
dbytes = sum(len(b) for b in delta_blobs) dbytes = sum(len(l) for g in delta_line_groups for l in g)
print("base %d bytes in %d strings; video %d bytes in %d strings; " print("base %d bytes in %d strings; video %d bytes in %d strings; "
"%d strings total" % "%d strings total" %
(sum(len(s) for s in base_lines), len(base_lines), dbytes, (sum(len(s) for s in base_lines), len(base_lines), dbytes,

View File

@@ -1,6 +1,6 @@
10 REM An array reference used as a function argument, and as one of several. 10 REM An array reference used as a function argument, and as one of several.
20 REM An identifier's subscript list used to hang off .right, which is also 20 REM An identifier's subscript list used to hang off .right, which is also
30 REM where an argument list chains its arguments -- so the arity counter walked 30 REM where arguments chain their arguments -- so the arity counter walked
40 REM straight into the subscripts and refused the call. TODO.md section 4. 40 REM straight into the subscripts and refused the call. TODO.md section 4.
50 DIM C#(4) 50 DIM C#(4)
60 C#(1) = -9 60 C#(1) = -9

View File

@@ -1,6 +1,6 @@
10 REM FILTER has no device capability behind it -- akgl_audio_* synthesises and 10 REM FILTER has no device capability -- audio synthesises and mixes but
20 REM mixes but has no filter stage, and SDL3 supplies no primitive to build one 20 REM has no filter stage; SDL3 supplies no primitive to build one from. It
30 REM from. It is refused rather than silently ignored, so a program that asked 30 REM is refused rather than silently ignored, so a program that asked
40 REM for a low-pass finds out it did not get one. 40 REM for a low-pass finds out it did not get one.
50 PRINT "BEFORE" 50 PRINT "BEFORE"
60 FILTER 1000, 1, 0, 0, 5 60 FILTER 1000, 1, 0, 0, 5

View File

@@ -1,4 +1,4 @@
10 REM The standalone driver lends the script no audio device. ENVELOPE, VOL and 10 REM The standalone driver has no audio device. ENVELOPE, VOL and
20 REM TEMPO only change interpreter state, so they work regardless; SOUND and 20 REM TEMPO only change interpreter state, so they work regardless; SOUND and
30 REM PLAY need the device and must name themselves when there is none. 30 REM PLAY need the device and must name themselves when there is none.
40 ENVELOPE 1, 5, 9, 12, 2 40 ENVELOPE 1, 5, 9, 12, 2

View File

@@ -1,6 +1,6 @@
10 REM A DEF call takes its environment from the pool, one per call, exactly as 10 REM A DEF call takes an environment from the pool, one per call, exactly as
20 REM GOSUB does. It used to be owned by the funcdef and reset on every call, 20 REM GOSUB does. It used to be owned by the funcdef and reset on every call,
30 REM which cost two silent defects: two calls in one expression shared a slot, 30 REM which cost two silent defects: two calls shared a slot,
40 REM and recursion never came back at all. 40 REM and recursion never came back at all.
50 DEF FACT(N#) 50 DEF FACT(N#)
60 IF N# <= 1 THEN RETURN 1 60 IF N# <= 1 THEN RETURN 1
@@ -15,9 +15,9 @@
150 DEF DBL(N#) = N# * 2 150 DEF DBL(N#) = N# * 2
160 PRINT DBL(10) + DBL(1) 160 PRINT DBL(10) + DBL(1)
170 PRINT DBL(1) + DBL(10) + DBL(100) 170 PRINT DBL(1) + DBL(10) + DBL(100)
180 REM Depth answers to the environment pool now, so runaway recursion reports 180 REM Depth answers to the pool, so runaway recursion reports
190 REM "Environment pool exhausted" rather than hanging. It is not exercised here 190 REM "Environment pool exhausted" rather than hanging. It is not exercised
200 REM because the statement containing a failed call still prints a junk value 200 REM here because a failed call's statement still prints a junk value
210 REM afterwards -- a separate, pre-existing defect recorded in TODO.md, and one 210 REM afterwards -- a TODO.md defect this file would pin
220 REM this golden file would pin if it went in. tests/user_functions.c asserts 220 REM if it went in. tests/user_functions.c asserts
230 REM the message instead. 230 REM the message instead.

View File

@@ -1,4 +1,4 @@
10 REM A range check reported through the driver, end to end. The program stops 10 REM A range check reported through the driver. The program stops
20 REM at the first error, so this file covers one; the rest of the checks are 20 REM at the first error, so this file covers one; the rest of the checks are
30 REM asserted in tests/graphics_verbs.c against the recording backend. 30 REM asserted in tests/graphics_verbs.c against the recording backend.
40 PRINT "BEFORE" 40 PRINT "BEFORE"

View File

@@ -1,5 +1,5 @@
10 REM The standalone driver lends the script no graphics device. 10 REM The standalone driver lends the script no graphics device.
20 REM COLOR, LOCATE and SCALE only touch interpreter state and must still work. 20 REM COLOR, LOCATE and SCALE touch interpreter state and must still work.
30 COLOR 1, 3 30 COLOR 1, 3
40 LOCATE 40, 50 40 LOCATE 40, 50
50 SCALE 1, 640, 400 50 SCALE 1, 640, 400

View File

@@ -1,5 +1,5 @@
10 REM Group B: the housekeeping verbs. None of these is in the Go reference -- 10 REM Group B: housekeeping verbs. None is in the Go reference --
20 REM they are on its own unimplemented list -- so what each one means here is 20 REM they are on its unimplemented list -- so what each one means here is
30 REM a decision, recorded in src/runtime_housekeeping.c beside the verb. 30 REM a decision, recorded in src/runtime_housekeeping.c beside the verb.
40 A# = 1 : B# = 2 40 A# = 1 : B# = 2
50 PRINT A# : PRINT B# 50 PRINT A# : PRINT B#
@@ -11,7 +11,7 @@
110 P#(2) = 7 : Q#(2) = 9 110 P#(2) = 7 : Q#(2) = 9
120 SWAP P#, Q# 120 SWAP P#, Q#
130 PRINT P#(2) : PRINT Q#(2) 130 PRINT P#(2) : PRINT Q#(2)
140 REM TRON prints each line number inline before the line runs, as a C128 does. 140 REM TRON prints each line number inline, as a C128 does.
150 TRON 150 TRON
160 PRINT "TRACED" 160 PRINT "TRACED"
170 TROFF 170 TROFF

View File

@@ -1,7 +1,7 @@
10 REM A leading zero is padding, not a radix. The reference selects base 8 for 10 REM A leading zero is padding, not a radix. The reference selects base 8;
20 REM any lexeme starting with 0, so 010 printed 8 and 08 was a parse error -- 20 REM 010 printed 8 and 08 was a parse error -- TODO.md section 6 item 10.
30 REM TODO.md section 6 item 10, fixed. Commodore BASIC has no octal literals. 30 REM Commodore BASIC has no octal literals.
40 REM 0x is the one prefix that changes the base, and it now reaches the scanner 40 REM 0x is the one prefix that changes the base and reaches the scanner
50 REM whole: that was section 6 item 15. 50 REM whole: that was section 6 item 15.
60 PRINT 010 60 PRINT 010
70 PRINT 08 70 PRINT 08

View File

@@ -1,12 +1,12 @@
10 REM A truth value carries its payload in boolvalue, not floatval. The three 10 REM A truth value carries its payload in boolvalue, not floatval. The three
20 REM numeric operators were `if ( INTEGER ) ... else <treat as float>`, and 20 REM numeric operators were `if ( INTEGER ) ... else <treat as float>`, and
30 REM that else was a catch-all rather than a float branch -- so a truth value 30 REM that else was a catch-all, not a float branch -- so a truth value
40 REM on the LEFT computed 0 - 1 into a field nothing reads, kept its BOOLEAN 40 REM on the LEFT computed 0 - 1 into a field nothing reads, kept its BOOLEAN
50 REM type, and printed `true` instead of -2. Silent, and wrong twice over. 50 REM type, and printed `true` instead of -2. Silent, and wrong twice over.
60 A# = 1 60 A# = 1
70 PRINT (A# == 1) 70 PRINT (A# == 1)
80 REM On the RIGHT it stays legal and stays -1, which is the same property that 80 REM On the RIGHT it stays legal and stays -1, the same property that
90 REM lets AND and OR double as logical operators. Refusing it here would have 90 REM lets AND and OR double as logical operators. Refusing it would have
100 REM broken every condition in the language. 100 REM broken every condition in the language.
110 PRINT 5 - (A# == 1) 110 PRINT 5 - (A# == 1)
120 PRINT 5 * (A# == 1) 120 PRINT 5 * (A# == 1)

View File

@@ -1,11 +1,11 @@
10 REM Statements separated by colons. The COLON token existed from the start of 10 REM Statements separated by colons. The COLON token existed from the start,
20 REM the port and nothing consumed it, so a line could hold only one statement. 20 REM but nothing consumed it, so a line could hold only one statement.
30 PRINT "A" : PRINT "B" 30 PRINT "A" : PRINT "B"
40 A# = 1 : B# = 2 : PRINT A# + B# 40 A# = 1 : B# = 2 : PRINT A# + B#
50 REM An empty statement is not an error: a trailing separator, or a run of them. 50 REM An empty statement is no error: a trailing separator or run of them.
60 PRINT "C" : 60 PRINT "C" :
70 PRINT "D" :: PRINT "E" 70 PRINT "D" :: PRINT "E"
80 REM Everything after THEN belongs to the condition, which is BASIC 7.0 and is 80 REM Everything after THEN belongs to the condition, as in BASIC 7.0, and is
90 REM not something the reference had an opinion about -- it never got here. 90 REM not something the reference had an opinion about -- it never got here.
100 IF 1 == 1 THEN PRINT "TRUE-1" : PRINT "TRUE-2" 100 IF 1 == 1 THEN PRINT "TRUE-1" : PRINT "TRUE-2"
110 IF 1 == 0 THEN PRINT "NEVER-1" : PRINT "NEVER-2" 110 IF 1 == 0 THEN PRINT "NEVER-1" : PRINT "NEVER-2"

View File

@@ -1,5 +1,5 @@
10 REM A field name is checked against a closed set the program declared, which 10 REM A field name is checked against the type's closed set, which
20 REM is the one thing in this language whose valid spellings are written down. 20 REM is the one thing here whose valid spellings are written down.
30 REM A misspelled *variable* is still silent -- see the last two lines. 30 REM A misspelled *variable* is still silent -- see the last two lines.
40 TYPE RECT 40 TYPE RECT
50 W# 50 W#

View File

@@ -17,7 +17,7 @@
170 RETURN B@.W# 170 RETURN B@.W#
180 PRINT WIDEN(A@) 180 PRINT WIDEN(A@)
190 PRINT A@.W# 190 PRINT A@.W#
200 REM To change one on purpose, pass a pointer. Assignment copies a pointer's 200 REM To change one, pass a pointer. Assignment copies the pointer.
210 REM reference, so the callee is looking at the caller's own record. 210 REM reference, so the callee is looking at the caller's own record.
220 DIM Q@ AS PTR TO CRATE 220 DIM Q@ AS PTR TO CRATE
230 POINT Q@ AT A@ 230 POINT Q@ AT A@
@@ -37,7 +37,7 @@
370 N2@.COUNT# = 20 370 N2@.COUNT# = 20
380 POINT N1@.TAIL@ AT N2@ 380 POINT N1@.TAIL@ AT N2@
390 DEF TOTAL(P@ AS PTR TO NODE) 390 DEF TOTAL(P@ AS PTR TO NODE)
395 REM A pointer is true when it points at something, which is how a walk knows 395 REM A pointer is true when it points at something, so a walk knows
396 REM where the list ends. NOT is the bitwise operator here, so the test is 396 REM where the list ends. NOT is the bitwise operator here, so the test is
397 REM written the positive way round. 397 REM written the positive way round.
400 IF P@->TAIL@ THEN RETURN P@->COUNT# + TOTAL(P@->TAIL@) 400 IF P@->TAIL@ THEN RETURN P@->COUNT# + TOTAL(P@->TAIL@)

View File

@@ -1,4 +1,4 @@
10 REM A TYPE declares its fields; each takes its own type from its own suffix, 10 REM A TYPE declares fields; each takes its type from its own suffix,
20 REM which is the same rule every other name in this language follows. 20 REM which is the same rule every other name in this language follows.
30 TYPE COORD 30 TYPE COORD
40 X# 40 X#

View File

@@ -1,6 +1,6 @@
10 REM A type name and a field name are bare words, and so is every verb, so 10 REM A type name and a field name are bare words, and so is every verb, so
20 REM they share a namespace whether we like it or not. Both are refused with 20 REM they share a namespace whether we like it or not. Both are refused with
30 REM the same rule the scanner already applies to variable names -- and a type 30 REM the same rule the scanner applies to variable names -- and a type
40 REM name is refused by the prescan, which can say so plainly rather than 40 REM name is refused by the prescan, which can say so plainly rather than
50 REM leaving the parser to report "Expected expression or literal". 50 REM leaving the parser to report "Expected expression or literal".
60 TYPE POINT 60 TYPE POINT

View File

@@ -1,6 +1,6 @@
10 REM This shows the waitingForCommand utility in the BasicEnvironment 10 REM This shows the waitingForCommand utility in the BasicEnvironment
11 REM when we have a nested for loop. The inner loop SHOULD execute, but 11 REM when we have a nested for loop. The inner loop SHOULD execute, but
12 REM the outer loop should NOT execute. Therefore, neither loop should execute. 12 REM the outer loop should NOT execute. Neither loop should execute.
20 FOR I# = 1 TO 0 20 FOR I# = 1 TO 0
25 FOR J# = 2 TO 4 25 FOR J# = 2 TO 4
30 PRINT "waitingForCommand FAILS if this is seen" 30 PRINT "waitingForCommand FAILS if this is seen"
@@ -8,4 +8,4 @@
35 NEXT J# 35 NEXT J#
40 NEXT I# 40 NEXT I#
50 PRINT "SUCCESS" 50 PRINT "SUCCESS"
80 QUIT 80 QUIT

View File

@@ -1,6 +1,6 @@
10 PRINT MOD(10, 3) 10 PRINT MOD(10, 3)
20 PRINT MOD(12, 5) 20 PRINT MOD(12, 5)
30 PRINT MOD(4, 2) 30 PRINT MOD(4, 2)
40 REM MOD() ONLY WORKS WITH INTEGERS - RESULTS WITH FLOATING POINT ARE UNRELIABLE 40 REM MOD() ONLY WORKS WITH INTEGERS - FLOATING-POINT RESULTS ARE UNRELIABLE
50 REM PRINT MOD(1.2, 0.4) 50 REM PRINT MOD(1.2, 0.4)
60 REM THERE IS NO ERROR THROWN HERE. JUST DONT DO IT. 60 REM THERE IS NO ERROR THROWN HERE. JUST DONT DO IT.