Mechanical sweep of all .sx sources, plan docs, and tests/expected snapshots for the sx language rename (s8/s16/s32/s64 -> i8/i16/i32/i64). Verified: tools/run_tests.sh 23/23. Note: the ios-sim build has 2 pre-existing 'restart' dot-call errors from the sx opt-in UFCS change (sx a47ea14) — independent of this rename (present pre-sweep); migrated in the follow-up commit.
10 KiB
sx std-library gaps surfaced by m3te
m3te is authored entirely in sx. While building it, a handful of general-purpose
utilities had to be hand-rolled because the sx standard library / library modules
don't provide them. This report enumerates those candidates — the ones that
arguably belong in sx std (or a library module) — and, for each, records an
explicit cross-check against the real sx library so the list contains true gaps
only.
- sx library checked:
/Users/agra/projects/sx/library/modules/—std.sx,math/(math.sx,vector2.sx,matrix44.sx),std/(objc.sx,cli.sx,hash.sx,json.sx,uikit.sx,objc_block.sx),process.sx,fs.sx,test.sx,ui/(types.sx,animation.sx,image.sx, …),gpu/,platform/. - m3te source read:
*.sx(main.sx,board.sx,board_view.sx,board_anim.sx,board_fx.sx,board_layout.sx,gem_anim.sx,audio.sx,swipe.sx,build.sx),tests/test.sx,tools/.
Highest-value candidates first. Borderline items are flagged. A separate "Already provided" section lists helpers m3te also hand-rolled but which the library does ship (so they are honestly not gaps) — included because the step asks for an honest accounting.
Category: Numerics / parsing
1. String → number parsing (parse_i64, parse_f32)
- m3te location:
main.sx:156(parse_i64),main.sx:122(parse_f32). - What it does: decimal ASCII
string→i64/f32(sign + integer + optional fractional part). - Why general-purpose: the exact inverse of the formatters everyone already uses; needed by any program that reads numbers from argv, env, config, or a text file. Nothing game-specific about it.
- Suggested home:
modules/std(alongsideint_to_string/float_to_string), or a dedicatedmodules/strings. - Not already in sx library (checked):
std.sxships only the forward direction —int_to_string(std.sx:68),uint_to_string,float_to_string(std.sx:121),int_to_hex_string— and no parse. Noatoi/strtol/strtod/string_to_intanywhere underlibrary/modules(grepped).std/json.sxhas aParser.parse_number(json.sx:547) but it is a private method consuming a JSON parser's buffer, not a reusablestring → number.
2. Seedable deterministic PRNG (Rng / LCG)
- m3te location:
board.sx:55(Rngstruct:next_u32board.sx:59,next_rangeboard.sx:66),board.sx:71(rng_seeded), constantsboard.sx:51-53. - What it does: a 32-bit linear-congruential generator carried in
i64and masked to 32 bits —next_u32(),next_range(n)(uniform-ish[0,n)),rng_seeded(seed). - Why general-purpose: a seedable, reproducible RNG is a textbook stdlib
primitive (simulations, procedural generation, shuffles, tests). m3te's copy
is written to be host-width-independent; only its consumer (
pick_gem) is game-specific — the generator itself is not. - Suggested home:
modules/math(or a newmodules/random). - Not already in sx library (checked): no
rand/rng/lcg/xorshift/random/seedgenerator anywhere underlibrary/modules(grepped names and the classic LCG constants1664525/1013904223).std/hash.sxis SHA-256 only (content addressing, not a PRNG).math/math.sxhassin/cos/sqrt/clamp/… but no randomness.
Category: FFI / system ergonomics
3. NUL-terminated C string (*u8) → sx string bridge (from_cstr)
- m3te location:
main.sx:108-117(read_env, copying form:strlen+cstring+memcpy);audio.sx:144-148(viewing form over agetcwdbuffer usingc_strlen). libcstrlenis re-bound in bothmain.sx:28andaudio.sx:30. - What it does: turn a foreign NUL-terminated byte pointer into an sx
string— either by copying (strlen+ alloc +memcpy) or by building a length-bounded view. - Why general-purpose: every FFI surface that returns
char*needs this (env vars,getcwd,UTF8String, libc results, …). It is the single most repeated FFI chore in m3te and has no game content. - Suggested home:
modules/std(e.g.from_cstr(*u8) -> stringplus a zero-copycstr_view), and a publicstrlenbinding so callers stop re-declaring it. - Not already in sx library (checked):
process.env(process.sx:86) performs exactly this copy inline but does not expose it as a reusable helper, and re-bindsstrlenprivately (process.sx:28).std/objc.sxexposesNSString.UTF8Stringreturning[*]u8(objc.sx:113) with no helper to convert it back to astring. No publicfrom_cstr/cstr_to_stringexists.
4. getcwd binding / cwd() helper
- m3te location:
audio.sx:29(getcwd#foreignbinding), used inload_system_sound(audio.sx:141) to absolutize a bundle-relative path. - What it does: read the process's current working directory into a string.
- Why general-purpose: resolving relative paths to absolute is a routine filesystem need, unrelated to match-3.
- Suggested home:
modules/fs(next tobasename/dirname) ormodules/process. - Not already in sx library (checked):
fs.sxbindsopen/read/mkdir/rename/… and shipsbasename/dirname/path_joinbut nogetcwd.process.sxbindsgetenv/popen/systembut notgetcwd. The platform modules bind onlychdir(platform/uikit.sx:20,platform/sdl3.sx:14), nevergetcwd;platform/bundle.sxobtains a cwd by shelling out, not via a binding. (greppedgetcwd/chdir/cwd.)
Category: Graphics (borderline — partly engine-specific)
5. Image file / RGBA buffer → GPU texture (load_texture, upload_rgba)
- m3te location:
board_view.sx:132(load_texture: PNG path → texture handle),board_fx.sx:104(upload_rgba: in-memory RGBA buffer → texture handle). Both branchgpu.create_texturevs a raw-GL fallback. - What it does: decode an image (via
stbi_load) or take an RGBA buffer and upload it as a.rgba8texture, returning the handle. - Why general-purpose: "load a PNG into a texture" is needed by any app that
draws sprites; the decode +
create_texturecore is reusable. - Suggested home:
modules/ui/image.sx(a loader to complement the existingImageView) ormodules/gpu. - Not already in sx library (checked):
ui/image.sxdefinesImageViewwhich takes an already-uploadedtexture_id— it does not load from disk.stbi_loadappears only in m3te, never inlibrary/modules/ui(grepped). The GPU layer exposesgpu.create_texturebut no file/decode front-end. - Borderline: the raw-GL fallback branch is engine-specific; only the
"decode →
create_texture" half is cleanly general. Worth shipping as the GPU-only path; the GL fallback can stay app-side.
Category: Testing (borderline — a stub exists)
6. Failing assertion with source location (expect)
- m3te location:
tests/test.sx:10(expect(cond, msg)→ printsFAIL <file>:<line>: <msg>and exits non-zero viaprocess.exit). - What it does: a test assertion that, on failure, reports the caller's
file:lineand fails the process so a harness/CI catches it. - Why general-purpose: every sx test suite needs a failing assertion;
m3te's whole
tests/gate is built on this one helper. - Suggested home:
modules/test. - Partially exists (checked):
modules/test.sxships onlyassert(condition)which prints"assertion failed\n"and does not exit (test.sx:3-7) — useless as a gate.process.assert(cond, msg)(process.sx:148) does reportfile:lineand exit 1, so the capability is half-present but split awkwardly: the dedicated test module can't fail a run, and the failing variant lives inprocess. A realmodules/testexpect/assertthat reports location and fails is the gap.
Already provided by the sx library — NOT gaps (honest accounting)
m3te also re-implemented or could have reused these; the library does ship them, so they are explicitly not counted above:
clamp/lerp/min/max/abs/sign— m3te uses these throughout (e.g.gem_anim.sx:45,board_layout.sx:20,swipe.sx:31) and they all come frommath/math.sx:11-37. Not hand-rolled; not a gap.read_env(main.sx:108) — duplicatesprocess.env(process.sx:86) almost line-for-line. m3te re-declares its owngetenv(main.sx:27) instead of importingprocess, but the capability exists. Not a gap (it motivates #3, the reusablefrom_cstrunderneath it).ease_out_cubic/ease_in_quad(board_anim.sx:28-29) — identical toui/animation.sx:19andui/animation.sx:13. Re-implemented only becauseboard_animimportsui/typesbut not all ofui/animation. Not a gap. Placement note: these are pure curves and arguably belong inmodules/mathtoo, so non-UI code can reach them without pulling in the UI module.fx_lerp_u8/ per-channel colour lerp (board_fx.sx:97) —Color.lerp(ui/types.sx:124) already lerps all four channels. Not a gap.- NSString /
NSLogbridging (used inaudio.sx) —std/objc.sxshipsNSLog(objc.sx:75) and anInto(*NSString) for string(objc.sx:119), which m3te uses viaxx. Not a gap.
Summary (top gaps, ranked)
- String → number parsing (
parse_i64/parse_f32) — clean, universally needed, the missing inverse of the existing formatters. - Seedable PRNG (
RngLCG) — textbook stdlib primitive, entirely absent. from_cstrC-string bridge (+ publicstrlen) — the most-repeated FFI chore;process.envinlines it but never exposes it.getcwd/cwd()— onlychdiris bound; the read side is missing.- Image-file → GPU texture loader — borderline; the decode-and-upload core is general, the GL fallback is app-side.
expecttest assertion — borderline; capability is split between a non-failingtest.assertstub andprocess.assert.