Phase 2: enforce statement separators (Rule B)

The parser now requires a newline or ';' between statements (block() in
selfhost/parse.ludic); two statements may no longer sit adjacent with only
spaces. Also fixed if-without-else swallowing its trailing separator.

Migration: tools/ludic-tools/migrate_separators.c inserts ';' at statement
boundaries corpus-wide (examples, runtime, 25 self-host fragments, ~1100
boundaries). Verified semantically identical — the migrated compiler compiles
itself to IR byte-identical to the pre-migration seed, and every golden game
renders identically. Reseeded to the strict compiler; C-free fixpoint holds;
test.sh 14/14.

Docs: Rule B documented in LANGUAGE.md; BOOTSTRAP.md R1 + stale fences updated;
check-docs green across all docs. Fixed a multi-line string literal in
emit_expr.ludic (byte-identical \n escape) that the C toolchain lexer mis-lexes.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
Orkun ÇAKILKAYA 2026-08-27 15:41:29 +03:00
parent 985f9ad8f2
commit eac8f8f335
35 changed files with 3861 additions and 3588 deletions

View file

@ -18,7 +18,7 @@
fn base_name(path: ptr) -> ptr {
let last = 0 - 1
let i = 0
while peek8(path, i) != 0 { if peek8(path, i) == 47 { last = i } i = i + 1 }
while peek8(path, i) != 0 { if peek8(path, i) == 47 { last = i }; i = i + 1 }
return substr(path, last + 1, i - (last + 1))
}
@ -72,7 +72,7 @@ main {
else { if streq(a, "--fmt") { fmt = true }
else { if streq(a, "--save-temps") { save = true }
else { if streq(a, "--run") { run = true }
else { if streq(a, "-o") { ai = ai + 1 if ai < os_argc() { out = os_arg(ai) } }
else { if streq(a, "-o") { ai = ai + 1; if ai < os_argc() { out = os_arg(ai) } }
else {
# an unknown flag is ignored (with a note) rather than mistaken for the
# input file — '-' is ASCII 45
@ -112,8 +112,8 @@ main {
emit_program()
# --emit-llvm, or no binary target: emit IR and stop (stdout when out is null).
if emit_ir { ir_flush(out) return }
if not has_target { ir_flush(ptr_null()) return }
if emit_ir { ir_flush(out); return }
if not has_target { ir_flush(ptr_null()); return }
# a run with no explicit -o lands in a temp file
if run and ptr_is_null(out) {