Commit b38654d
compiler: string-wall slice 7 — int_to_string (decimal rendering) (#573)
## Phase F slice 7 — variable-string backend wall (int_to_string)
Seventh slice of the **variable-string backend** wall
(`proposals/MIGRATION-PLAN.adoc` §"The two walls"), and **the last clean
name-dispatched string op**. Adds the wasm-backend lowering for
`int_to_string(n)` over the slice-3 allocation idiom, matching the
interp oracle `string_of_int`.
`int_to_string` was already wired in resolve/typecheck/interp; only
codegen was absent.
### The lowering (`lib/codegen.ml`)
1. `neg = n < 0`.
2. `m = neg ? n : -n` (via `Select`) — the **non-positive magnitude**.
For `INT_MIN`, `m = n` (no negation), so the un-representable positive
magnitude is never formed.
3. **Digit count** — a do-while loop dividing a copy of `m` by 10 until
0 (runs ≥ once, so `n = 0` → one `'0'`, no special case).
4. `len = dc + neg`; allocate `4 + len`; store `len`.
5. **Write digits backwards** — a do-while loop emitting `48 - (m % 10)`
per digit (i32 `rem_s` truncates toward zero, so `m % 10 ≤ 0` and `48 -
(m%10) = '0' + digit`), then `m /= 10`.
6. Leading `'-'` for negatives (the `If`'s else-branch is a single `Nop`
— no empty-else).
**INT_MIN-safe:** `m / 10` (`div_s`) never traps (divisor 10 ≠ 0; the
only trapping case `INT_MIN / -1` never occurs).
### Parity (wasm vs interp oracle)
The wasm string was read back from linear memory and compared to
`String(n)`; the interp oracle confirmed against the real library. Both
agree across:
| n | result | note |
|---|---|---|
| 0 | "0" | do-while yields one '0' |
| 42 / -5 / -123 / 100 | "42" / "-5" / "-123" / "100" | sign, trailing
zeros |
| 2147483647 | "2147483647" | INT_MAX |
| **-2147483648** | **"-2147483648"** | **INT_MIN — negative-space
extraction** |
### Tests
- `test/test_e2e.ml` — new `eval_string_fn` helper + group **"E2E
String-wall slice 7 (int_to_string)"** — five full-string cases (0, 42,
-123, INT_MAX, INT_MIN). Runs under `dune runtest`.
- `tests/codegen/string_int_to_string.affine` +
`tests/codegen/test_string_int_to_string.mjs` — executable wasm parity
via `tools/run_codegen_wasm_tests.sh` (CI).
Local verification: full `run_codegen_wasm_tests.sh` green (all seven
string harnesses, slices 1–7; no sibling regressions); both backends
confirmed against the real library; wasm string read back from memory.
`dune runtest` couldn't run in-sandbox (no `alcotest`), but the new
tests' logic + API usage was validated against the real `affinescript`
library.
### Scope — the clean lane is closed
**Every name-dispatched string builtin with a faithful trap-free wasm
analogue is now lowered** (`string_length`, `string_char_code_at`,
`char_to_int`, `string_from_char_code`, `string_sub`, `to_lowercase`,
`to_uppercase`, `trim`, `string_find`, `int_to_string`).
What remains needs **your direction**:
- **concat (`++`) / polymorphic `slice`** — the highest-value remaining
string work, **blocked on a type-access architectural decision**:
codegen has no per-expression type info and `++` already lowers to
*list* concat. Options: thread typecheck's types into codegen, or add a
dedicated `string_concat` builtin (a language-surface addition). I'll
write up both with a recommendation on request.
- **`float_to_string`** — stays host-side under the float-wall.
- **`string_get` / `int_to_char`** — deferred (raise on out-of-range).
After this, the other half of the migration's compiler work is the
**effect wall** (module-state / `Date.now` / `Console.log`).
Evidence: `proposals/EVIDENCE-stringwall-slice7.adoc`.
https://claude.ai/code/session_01WoKhFQePiRsAj7aqnxbG8s
---
_Generated by [Claude
Code](https://claude.ai/code/session_01WoKhFQePiRsAj7aqnxbG8s)_
Co-authored-by: Claude <noreply@anthropic.com>1 parent 57eb667 commit b38654d
6 files changed
Lines changed: 331 additions & 7 deletions
File tree
- lib
- proposals
- tests/codegen
- test
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
1238 | 1238 | | |
1239 | 1239 | | |
1240 | 1240 | | |
| 1241 | + | |
| 1242 | + | |
| 1243 | + | |
| 1244 | + | |
| 1245 | + | |
| 1246 | + | |
| 1247 | + | |
| 1248 | + | |
| 1249 | + | |
| 1250 | + | |
| 1251 | + | |
| 1252 | + | |
| 1253 | + | |
| 1254 | + | |
| 1255 | + | |
| 1256 | + | |
| 1257 | + | |
| 1258 | + | |
| 1259 | + | |
| 1260 | + | |
| 1261 | + | |
| 1262 | + | |
| 1263 | + | |
| 1264 | + | |
| 1265 | + | |
| 1266 | + | |
| 1267 | + | |
| 1268 | + | |
| 1269 | + | |
| 1270 | + | |
| 1271 | + | |
| 1272 | + | |
| 1273 | + | |
| 1274 | + | |
| 1275 | + | |
| 1276 | + | |
| 1277 | + | |
| 1278 | + | |
| 1279 | + | |
| 1280 | + | |
| 1281 | + | |
| 1282 | + | |
| 1283 | + | |
| 1284 | + | |
| 1285 | + | |
| 1286 | + | |
| 1287 | + | |
| 1288 | + | |
| 1289 | + | |
| 1290 | + | |
| 1291 | + | |
| 1292 | + | |
| 1293 | + | |
| 1294 | + | |
| 1295 | + | |
| 1296 | + | |
| 1297 | + | |
| 1298 | + | |
| 1299 | + | |
| 1300 | + | |
| 1301 | + | |
| 1302 | + | |
| 1303 | + | |
| 1304 | + | |
| 1305 | + | |
| 1306 | + | |
| 1307 | + | |
| 1308 | + | |
| 1309 | + | |
| 1310 | + | |
| 1311 | + | |
| 1312 | + | |
| 1313 | + | |
1241 | 1314 | | |
1242 | 1315 | | |
1243 | 1316 | | |
| |||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
| 1 | + | |
| 2 | + | |
| 3 | + | |
| 4 | + | |
| 5 | + | |
| 6 | + | |
| 7 | + | |
| 8 | + | |
| 9 | + | |
| 10 | + | |
| 11 | + | |
| 12 | + | |
| 13 | + | |
| 14 | + | |
| 15 | + | |
| 16 | + | |
| 17 | + | |
| 18 | + | |
| 19 | + | |
| 20 | + | |
| 21 | + | |
| 22 | + | |
| 23 | + | |
| 24 | + | |
| 25 | + | |
| 26 | + | |
| 27 | + | |
| 28 | + | |
| 29 | + | |
| 30 | + | |
| 31 | + | |
| 32 | + | |
| 33 | + | |
| 34 | + | |
| 35 | + | |
| 36 | + | |
| 37 | + | |
| 38 | + | |
| 39 | + | |
| 40 | + | |
| 41 | + | |
| 42 | + | |
| 43 | + | |
| 44 | + | |
| 45 | + | |
| 46 | + | |
| 47 | + | |
| 48 | + | |
| 49 | + | |
| 50 | + | |
| 51 | + | |
| 52 | + | |
| 53 | + | |
| 54 | + | |
| 55 | + | |
| 56 | + | |
| 57 | + | |
| 58 | + | |
| 59 | + | |
| 60 | + | |
| 61 | + | |
| 62 | + | |
| 63 | + | |
| 64 | + | |
| 65 | + | |
| 66 | + | |
| 67 | + | |
| 68 | + | |
| 69 | + | |
| 70 | + | |
| 71 | + | |
| 72 | + | |
| 73 | + | |
| 74 | + | |
| 75 | + | |
| 76 | + | |
| 77 | + | |
| 78 | + | |
| 79 | + | |
| 80 | + | |
| 81 | + | |
| 82 | + | |
| 83 | + | |
| 84 | + | |
| 85 | + | |
| 86 | + | |
| 87 | + | |
| 88 | + | |
| 89 | + | |
| 90 | + | |
| 91 | + | |
| 92 | + | |
| 93 | + | |
| 94 | + | |
| 95 | + | |
| 96 | + | |
| 97 | + | |
| 98 | + | |
| 99 | + | |
| 100 | + | |
| 101 | + | |
| 102 | + | |
| 103 | + | |
| 104 | + | |
| 105 | + | |
| 106 | + | |
| 107 | + | |
| 108 | + | |
| 109 | + | |
| 110 | + | |
| 111 | + | |
| 112 | + | |
| 113 | + | |
| 114 | + | |
| 115 | + | |
| 116 | + | |
| 117 | + | |
| 118 | + | |
| 119 | + | |
| 120 | + | |
| 121 | + | |
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
42 | 42 | | |
43 | 43 | | |
44 | 44 | | |
45 | | - | |
46 | | - | |
47 | | - | |
48 | | - | |
49 | | - | |
50 | | - | |
| 45 | + | |
| 46 | + | |
| 47 | + | |
| 48 | + | |
| 49 | + | |
| 50 | + | |
| 51 | + | |
51 | 52 | | |
52 | 53 | | |
53 | 54 | | |
| |||
214 | 215 | | |
215 | 216 | | |
216 | 217 | | |
217 | | - | |
| 218 | + | |
| 219 | + | |
218 | 220 | | |
219 | 221 | | |
220 | 222 | | |
| |||
0 commit comments