Skip to content

Fix INPUT into DATA arrays, -ls listings, native long lines, block order and INC optimization - #123

Open
kimslawson wants to merge 8 commits into
dmsc:masterfrom
kimslawson:parser-fixes
Open

kimslawson wants to merge 8 commits into
dmsc:masterfrom
kimslawson:parser-fixes

Conversation

@kimslawson

Copy link
Copy Markdown
Contributor

Daniel,

While building fbp, a minimizer/pretty-printer that reuses FastBasic's .syn grammar and checks every rewrite by comparing bytecode, I ran into a few bugs in FastBasic. (I did this work with Claude Code, an AI assistant.) Each fix is a separate commit, so you can take any of them on their own.

Everything was tested against master (212de72). "Native" means fbc.xex/fbci.xex run in the testsuite's mini65, not the IDE or real hardware.

The fixes

  1. -ls wrote a raw $9B inside strings (986ba16). ? "A"$9B"B" became ?"A<$9B>B". The cross compiler reads that back, but the native compiler fails with parse error at line 1 column 3. add_s_str() already escapes it, but tests (unsigned) c == 155, which never matches where char is signed (x86, x86-64, Windows, Apple arm64). Fixed with (unsigned char). On the samples, the -ls:120 output still compiles to identical code, and mastodon.bas now has 8 real lines instead of 12 $9B bytes.
  2. -ls:N only made lines of N−1 characters (4584961). USAGE.md says "N characters or less". Eleven 10-character statements now fit one 120-character line. carrera3d.bas goes from 15 lines to 14.
  3. INPUT into byte or floating point DATA arrays was a parse error (91da76c). DATA D() BYTE = 1,2,3 : INPUT D(1) gave "expected: ','", both native and cross, int and fp. Word DATA and DIM arrays work. INPUT_VAR tried VAR_WORD_LVALUE_SADDR first. Its E_VAR_CREATE creates a variable D, the next alternative accepts it as a word variable, and (1) then fails outside INPUT_VAR.
    • In basic.syn, ARRAY_BYTE_ADDR now comes first, as in GETK_EXPR/GET_EXPR. It only searches, so other cases compile as before.
    • In float.syn, the FP DATA alternative is added with INPUT_VAR:<. Putting all of VAR_FP_LVALUE_SADDR first would break INPUT A% for a new A%.
    • New tests: input-data, fp-input-data.
  4. Native compiler hung on lines over 255 characters (a270b3a). ucase_line indexes with Y until it finds $9B, so it wrapped around forever. It now reports too long. Costs 4 bytes. New test: err-line-long.
  5. Cross compiler placed blocks of one line out of order (37b0b70). After four FOR loops, DATA FIRST() BYTE=1 : DATA SECOND() BYTE=2 put SECOND before FIRST. With 17 DATA arrays in one line, the order was Q I H G F E D C B A P O N M L K J. Native keeps source order.
    • Cause: full_code() takes the blocks from a map ordered by label string (jump_lbl_10 < jump_lbl_9), then uses std::sort, which isn't stable.
    • Fix: it now records the creation order and uses std::stable_sort.
    • No existing test or sample changes. New test: data-order.
  6. X=X+1 wasn't optimized to INC X (c8ded2b). The TOK_VAR / x / TOK_PUSH / TOK_NUM / y / TOK_ADD rule matched first, so the INC rule never did, and X=X+1 used 5 bytes instead of 2. X=X-1 already became DEC. A new rule matches the rewritten form, covering X+1 and 1+X. Saves 3 bytes each in fedora.bas and stmt-data.bas, and 18 in testloop.bas. The old TOK_VAR_LOAD INC rule is now unreachable and could be removed.
  7. Typo in parse::peek() (fa23ab9): p >= 'z' should be p <= 'z'. Harmless, as it's only called with ':'.

Testing

  • make test: 87/87, which is the 83 existing tests plus the 4 new ones. Each new test fails without its fix.
  • Every testsuite and sample program compiles to the same assembler for atari-fp and atari-int, except the INC savings above.
  • -ls has no testsuite coverage, so I checked fixes 1 and 2 by hand on all the samples.
  • Native size: fb.xex/fbc.xex grow by 20 bytes (16 from the FP INPUT_VAR table, 4 from the line check). fbi.xex/fbci.xex grow by 4.

Not in this PR

  • mini65 (dmsc/mini65-sim): dosfopen() declares char fullname[strlen(root) + strlen(name) + 1] but writes "%s/%s", one byte past the end. With Ubuntu 24.04's default _FORTIFY_SOURCE=3, every fbtest run aborts with "buffer overflow detected". + 2 fixes it. I ran the tests above with that change applied locally.
  • CHR$/STR$ share one buffer: CHR$(65)=CHR$(66) is true. func-chr.chk expects that, but the manual only mentions the shared buffer for [] slicing.

Thanks for FastBasic!

🤖 Generated with Claude Code

https://claude.ai/code/session_01NASRhH2Xg6HmeBUZTSPhwM

claude added 8 commits October 4, 2026 20:31
On platforms where char is signed (x86, x86-64, Windows), the test for
$9B never matched, so '-ls' wrote a raw ATASCII EOL inside the string.
The cross compiler reads it back, but the native compiler and the IDE
split the line there.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01NASRhH2Xg6HmeBUZTSPhwM
The documentation says each line is of N characters or less, but lines
were only joined up to N-1 characters.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01NASRhH2Xg6HmeBUZTSPhwM
INPUT_VAR tried VAR_WORD_LVALUE_SADDR first, which creates a variable
with the name of the DATA array and then accepts it as a word variable,
so "INPUT D(1)" gave a parse error at the parenthesis. Try the byte
array first, like GET does, and add the floating point DATA alternative
at the start of the table.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01NASRhH2Xg6HmeBUZTSPhwM
The loop that copies the line to the buffer indexes with Y until it
finds the EOL, so with 256 or more characters it wrapped around and
never ended. Costs 4 bytes.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01NASRhH2Xg6HmeBUZTSPhwM
Blocks were collected from a map ordered by label name, so "jump_lbl_10"
came before "jump_lbl_9", and sorted with std::sort, which is not stable.
Several DATA arrays in one line could be placed in any order, unlike in
the native compiler.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01NASRhH2Xg6HmeBUZTSPhwM
The rule "TOK_VAR / x / TOK_PUSH / TOK_NUM / y / TOK_ADD" matched first,
so the existing INC rule never did and "X=X+1" used 5 bytes instead of
2 ("X=X-1" already gave DEC). Saves 3 bytes in fedora.bas and 18 in
testloop.bas.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01NASRhH2Xg6HmeBUZTSPhwM
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01NASRhH2Xg6HmeBUZTSPhwM
The Windows fallback was a static "strndup", which conflicts with the
declaration in newer MinGW <string.h>, failing the Windows CI. Use
another name for it.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01NASRhH2Xg6HmeBUZTSPhwM
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants