From a436cfbe35f49fe9887b788090691da1d2eb4acf Mon Sep 17 00:00:00 2001 From: Orkuncakilkaya Date: Tue, 29 Sep 2026 20:10:13 +0300 Subject: [PATCH] ludic.ui: a rule's selector text is interned - `{sel}` was sel itself (a template of one string hole passes the string through) and lss_rule frees sel, so rules named whatever reused the bytes; smoke.txt's tree order follows the document, and smoke.py runs the transcript Co-Authored-By: Claude Opus 5.5 --- packages/ludic.ui/style_rule.ludic | 2 +- tools/ui-preview/README.md | 3 ++- tools/ui-preview/smoke.py | 34 ++++++++++++++++++++++++++++++ tools/ui-preview/smoke.txt | 2 +- 4 files changed, 38 insertions(+), 3 deletions(-) create mode 100644 tools/ui-preview/smoke.py diff --git a/packages/ludic.ui/style_rule.ludic b/packages/ludic.ui/style_rule.ludic index 8fa4a2e5..f02ef70b 100644 --- a/packages/ludic.ui/style_rule.ludic +++ b/packages/ludic.ui/style_rule.ludic @@ -19,7 +19,7 @@ function lss_rule_add(f: UiFile, decls: UiRule, sel: string, err: []string, line let r = new UiRule r.keys = decls.keys r.vals = decls.vals - r.text = `{sel}` # a copy: the selector's text is given back below + r.text = intern(sel) # a copy: lss_rule frees sel (`{sel}` was sel itself, and dangled) r.at = `{f.path}:{line}` r.sel = sel_parse(sel, err) if r.sel != null { diff --git a/tools/ui-preview/README.md b/tools/ui-preview/README.md index d2089eba..e7af88db 100644 --- a/tools/ui-preview/README.md +++ b/tools/ui-preview/README.md @@ -10,7 +10,8 @@ ludic ui-preview [--font DIR] # the host on stdin/stdout; DIR holds the gam ``` **The wire protocol is [protocol-v1.md](protocol-v1.md)**, frozen: every request, every reply and -every line format. `smoke.txt` is a scripted session with what each reply must contain. +every line format. `smoke.txt` is a scripted session with what each reply must contain, and `smoke.py` runs it +(`python3 tools/ui-preview/smoke.py tools/ui-preview/smoke.txt bin/ludic-ui-preview`). ## What it does diff --git a/tools/ui-preview/smoke.py b/tools/ui-preview/smoke.py new file mode 100644 index 00000000..bb84b526 --- /dev/null +++ b/tools/ui-preview/smoke.py @@ -0,0 +1,34 @@ +#!/usr/bin/env python3 +# smoke.py - runs smoke.txt against a ui-preview binary and checks each reply: `> ` lines are requests, +# `< ` a reply line must contain the text (in order), `<= ` the exact final line, ` ") or line == ">": reqs.append(line[2:]); exp.append([]) + elif line.startswith(("< ", "<= ", " tree < node 0 < "root" -< "go" < "Food" +< "go" <= ok > box label