summary_strcat.md 3.1 KB

Step: string concatenation + string assignment/LEN polish

Tag v3-strcat. Suite 128/128; fixpoint OK (bootstrap/fixpoint.sh, image 2,080,607 bytes).

String concatenation +

  • SymTab.IsStrType(t) (exported): a string literal (ClStr) or a 1-D ARRAY OF CHAR.
  • The SimExpr action gained an OpAdd string branch before the arithmetic path: a + b where both are string-compatible, or where one is a string and the other a CHAR (the char becomes a one-character string literal via QbeGen.DeclCharStr). The result type is NewStr() (a string literal value).
  • QbeGen.StrCat emits %t =l call $m2strcat(l a, l b); the shim concatenates into a static buffer and returns its descriptor address. The value is valid until the next concatenation, so it must be consumed (assigned/compared/passed) before another one.
  • shim.c: m2strcat.

String assignment is now a content copy

Previously a := "…" did a whole-array copy with a runtime count check, so any length mismatch trapped (the old t_trap_count/t_trap_emptystr tests). It now copies content:

  • QbeGen.StrAssign emits call $m2strassign(l dst, l src).
  • shim.c: m2strassign copies up to cap characters (a CHAR array has cap content slots plus a reserved terminator at index cap), truncates to fit, and NUL-terminates.
  • a := "abcd" into ARRAY [0..2] OF CHAR now yields "abc" (truncated); a := "" clears the destination.
  • Assignment also works when the RHS is a + concatenation (result type ClStr).

LEN / LENGTH on string literals

LEN("hello") / LENGTH("abc") now fold to the content length at compile time; the non-literal forms were already supported (LEN of an array is its capacity, LEN of an open array reads the count).

Tests

  • t_strcat.mod (exit 63): array + array, literal + CHAR, string assignment, LEN(array)/LEN("hello")/LENGTH("abc").
  • t_strtrunc.mod (exit 42): "abcd" into capacity 3 → "abc".
  • t_strempty.mod (exit 42): "" clears the destination.
  • t_trap_count.mod / t_trap_emptystr.mod removed (obsolete).
  • expect_run/expect_trap now link shim.c (string assignment calls into it).

Notes / caveats

  • "x" + "y" is accepted through the CHAR rule (single-character literals are CHAR, as in classic Modula-2, so they are not ClStr).
  • The concat buffer is a single static 8 KiB; nested/interleaved concatenations must be consumed in order. This is sufficient for ordinary expressions (("a" + "b") + "c" is fine: each result is consumed by the next + immediately).
  • LEN of an array remains the capacity, not the current NUL-terminated length (use the shim-backed Strings.Length/LEN of the string for that).

Files

compiler/src/M2.atg (string +, string-assign branches, LEN literal), compiler/src/QbeGen.def/.mod (StrCat/StrAssign/ StrLen), compiler/src/SymTab.def (IsStrType), runtime/syslib/shim.c (m2strcat/m2strassign/m2strlen), compiler/run_tests.sh, compiler/tests/{t_strcat,t_strtrunc, t_strempty}.mod, docs/features.md.