From af0d54d352be3a858a0d407ac55d40552f4afed1 Mon Sep 17 00:00:00 2001 From: CharlesKWON Date: Mon, 4 May 2026 21:06:13 +0900 Subject: [PATCH] fix(lexer): {array}[index] no longer mis-tokenises [ as bracket-string MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The lexer's isStringBracket disambiguator decides whether `[` opens an indexing operator or a Harbour bracket-string literal. The heuristic checks the previous token's kind and treats the bracket as indexing only when preceded by an IDENT, RPAREN, RBRACKET, or a literal. RBRACE was missing — so FieldPut(3, {"Kim","Lee","Park","Choi","Yoon"}[Int(Mod(i-1,5))+1]) tokenised the `[` after `}` as a bracket-string opener, swallowed through the first `]` it found, and produced bogus parse errors ("expected ), got STRING …"). RBRACE is now in the indexing-context set, so an inline array-literal followed by `[index]` works. Surfaced by the examples/ build sweep — fixed test_all_rdd, test_index_adv, test_multi_rdd, test_rdd_full all in one go. The sweep itself is committed as tests/examples_build.sh — builds every PRG under examples/ and reports any compiler / preprocessor errors. Run it after compiler changes to catch regressions in broad-coverage user-style code that the focused suites don't exercise. Current sweep state: 65 / 71 examples build cleanly. The remaining 6 failures are all #pragma BEGINDUMP blocks that import external Go packages (http, websocket, sqlite, time) — not Five-side bugs. Other gates green: go test ./... : PASS FiveSql2 SQL:1999 : 43/43 Harbour compat : 56/56 std.ch suite : 16/16 FRB suite : 7/7 examples build : 65/71 (rest = external Go deps) Co-Authored-By: Claude Opus 4.7 (1M context) --- compiler/lexer/lexer.go | 5 +++-- tests/examples_build.sh | 42 +++++++++++++++++++++++++++++++++++++++++ 2 files changed, 45 insertions(+), 2 deletions(-) create mode 100755 tests/examples_build.sh diff --git a/compiler/lexer/lexer.go b/compiler/lexer/lexer.go index 383670f..1bcfe41 100644 --- a/compiler/lexer/lexer.go +++ b/compiler/lexer/lexer.go @@ -330,10 +330,11 @@ func (l *Lexer) scanString(quote byte) token.Token { } // isStringBracket returns true if [ should be treated as string delimiter. -// Harbour: [text] is string when not preceded by ident, ), ], literal. +// Harbour: [text] is string when not preceded by ident, ), ], literal, +// or `}` (close of array literal — `{1,2,3}[2]` is index access). func (l *Lexer) isStringBracket() bool { switch l.lastKind { - case token.IDENT, token.RPAREN, token.RBRACKET, + case token.IDENT, token.RPAREN, token.RBRACKET, token.RBRACE, token.INT, token.LONG, token.DOUBLE, token.STRING, token.TRUE, token.FALSE, token.NIL_LIT: return false // array index context diff --git a/tests/examples_build.sh b/tests/examples_build.sh new file mode 100755 index 0000000..bba82a6 --- /dev/null +++ b/tests/examples_build.sh @@ -0,0 +1,42 @@ +#!/usr/bin/env bash +# Build-only sweep of every example PRG. Doesn't run them (many are +# interactive / database / network). Just checks the compiler accepts +# them cleanly. +set -e +cd /Users/charleskwon/Projects/fivedev/five + +work=$(mktemp -d) +trap 'rm -rf "$work"' EXIT + +pass=0 +fail=0 +declare -a failed + +for src in examples/*.prg; do + name=$(basename "$src" .prg) + if ./five build "$src" -o "$work/$name" >"$work/$name.out" 2>&1; then + # Check for parse / preprocessor errors even when build "succeeded" + if grep -qE '^(five: [0-9]+ (parse|preprocessor) error|panic:)' "$work/$name.out"; then + echo "FAIL $name (build returned 0 but errors detected)" + tail -5 "$work/$name.out" | sed 's/^/ /' + fail=$((fail+1)) + failed+=("$name") + else + pass=$((pass+1)) + fi + else + echo "FAIL $name" + tail -5 "$work/$name.out" | sed 's/^/ /' + fail=$((fail+1)) + failed+=("$name") + fi +done + +echo +echo "================================================================" +echo " Build sweep: $pass / $((pass+fail)) examples build cleanly" +echo "================================================================" +if [ ${#failed[@]} -gt 0 ]; then + echo "Failed:" + for n in "${failed[@]}"; do echo " $n"; done +fi