Phase 7d: string interpolation text {expr} text

The readable way to build strings, as you noted `{a} {b}` beats `a + " " + b`.
A backtick string embeds any expression in `{…}` and desugars to the Phase-7c
`+` chain, wrapping each hole in `str(...)`:

  `hello {name}, n={count + 1}`
    ==  "hello " + name + ", n=" + str(count + 1)

- Lexer: a backtick captures its content raw as TK_INTERP.
- Parser: parse_interp splits literal runs from `{…}` holes (brace-depth aware,
  `{{`/`}}` escape to literal braces), re-lexes each hole as a full expression
  (save/restore toks/pi like an import), and folds it all into E_BIN(+) nodes —
  so no new AST or runtime beyond the existing concat.
- str(x): a string passes through; int/bool/fixed convert via a small emitted
  @fn_int_str prelude (digits from the end of a buffer, '-' for negatives),
  emitted once into any program that uses it.

examples/strings.ludic gains interpolation cases (now prints 1..7); the smoke
covers it. Grammar + ludic_syntax.h tokenize backtick strings (holes highlighted
as embedded code). LANGUAGE.md documents it as the preferred form.

Reseeded (22530 lines); C-free fixpoint holds; goldens identical; 18/18; vocab +
doc-fences clean.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
Orkun ÇAKILKAYA 2026-08-28 01:12:07 +03:00
parent a997e34b2d
commit ad63f09d53
13 changed files with 4748 additions and 3962 deletions

View file

@ -68,6 +68,7 @@ fn emit_program() -> void {
head = buf_new()
code = buf_new()
g_uses_str = false
g_uses_intstr = false
loc_name = new []ptr; loc_reg = new []ptr; loc_ty = new []ptr; loc_mut = new []int
brk_lbl = new []ptr; cnt_lbl = new []ptr
self_stk = new []ptr
@ -84,6 +85,7 @@ fn emit_program() -> void {
while i < len(prog) { if prog[i].kind == N_MAIN { emit_main(prog[i]) }; i = i + 1 }
}
if g_uses_str { emit_str_prelude() } # @fn_str_eq / @fn_str_concat, after all uses are seen
if g_uses_intstr { emit_int_str() } # @fn_int_str, for str(int) in interpolation
}
# Flush the emitted IR. With a null path it goes to stdout (the pipe the shell