ludic/packages/ludic.ui/units_pieces.ludic
Orkuncakilkaya 338202c41b play 2's frame keeps and a dropped file: a var() join made at once, the hint rail emptied in place, save_read gives its text back
- ludic.ui cv_join (units_pieces.ludic:93): a var() value's pieces were joined by +, a text left
  behind per piece on every memo miss; it is written at its length at once (units_join.ludic), and
  the miss is its own declared function (cs_vars_miss), bounded by the memo.
- ludic.hints hn_slots (rail.ludic:41): hints_reset made a new rail every time; the rail is
  emptied in place, made again only for another count (rail_slots.ludic).
- value_spare_put (value.ludic:138): a list's spares grow only past the most it has ever cut off;
  declared as such.
- ludic.save: save_read reads, parses and frees the file's text - the tree's strings are the
  parser's own - and says whether the text was whole (SaveRead.intact) for the words of a refusal.
  Maroon Lake's trip and home reads kept the whole file on every load and every menu card read.

Tests: ludic.save (10), ludic.hints (8); the 36 ui examples; value_list_regrow, json_saves and
alloc_fence_declared unchanged.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-09-29 13:33:54 +03:00

90 lines
3 KiB
Text

# units_pieces.ludic - a value with var()s, parsed once by its text into its stretches, the names it
# asks for and their fallbacks; each frame its pieces are those stretches and what the names hold
# here, in a list kept on the state, hashed and compared against the memo without a copy
export property UiVarTpl {
lits: []string = null # the text around the var()s: one more than there are names
names: []string = null
falls: []string = null # a var's fallback, or null
}
function cv_tpl(ui_st: mut UiState, s: string) -> UiVarTpl {
let m = mm_of(ui_st)
let i = mm_find(m, MM_VTPL, s)
if i >= 0 { return m.vtpls[i] }
let t = cv_parse(ui_st, s)
let j = mm_add(m, MM_VTPL, s)
if j >= 0 { m.vtpls[j] = t }
return t
}
@alloc_ok("a memo miss (cv_tpl): once per value with var() in it")
function cv_parse(ui_st: mut UiState, s: string) -> UiVarTpl {
let t = new UiVarTpl
t.lits = new []string
t.names = new []string
t.falls = new []string
var at = 0
var i = 0
while i + 4 <= len(s) {
if sat(s, i, "var(") {
var j = i + 4
var depth = 1
while j < len(s) and depth > 0 {
if s[j] == 40 { depth += 1 }
if s[j] == 41 { depth -= 1 }
j += 1
}
push(t.lits, s[at..i])
let inner = s[i + 4..j - 1]
let parts = lss_split(inner, 44)
push(t.names, tpl_words(parts[0]))
if len(parts) > 1 { push(t.falls, tpl_words(inner[len(parts[0]) + 1..len(inner)])) } else { push(t.falls, null) }
at = j
i = j
} else { i += 1 }
}
push(t.lits, s[at..len(s)])
return t
}
# this element's pieces for `s` into cv_texts; false when it has no var() at all
function cv_pieces(ui_st: mut UiState, n: UiNode, s: string) -> bool {
if not cv_has(s) { return false }
let t = cv_tpl(ui_st, s)
List.clear(ui_st.cv_texts)
for k in 0 .. len(t.names) {
ui_kept(ui_st.cv_texts, t.lits[k])
ui_kept(ui_st.cv_texts, cv_value(ui_st, n, t, k))
}
ui_kept(ui_st.cv_texts, t.lits[len(t.names)])
return true
}
function cv_has(s: string) -> bool {
for i in 0 .. len(s) - 3 {
if s[i] == 118 and sat(s, i, "var(") { return true }
}
return false
}
function cv_value(ui_st: mut UiState, n: UiNode, t: UiVarTpl, k: int) -> string {
let v = cs_var(n, t.names[k])
if v != null { return v }
if t.falls[k] != null { return t.falls[k] }
@alloc_ok("only when a template is wrong: a broken screen, never a working one")
ui_err(ui_st, `var({t.names[k]}): no element here or above sets it`)
return ""
}
function cv_hash(ui_st: UiState) -> int {
var h = 216613626 ^ (MM_VARS & 255)
for p in 0 .. len(ui_st.cv_texts) {
let t = ui_st.cv_texts[p]
for i in 0 .. len(t) { h = ((h ^ t[i]) * 16777619) & 2147483647 }
}
return h
}
# the pieces, one after another, are exactly `k`
function cv_same(ui_st: UiState, k: string) -> bool {
var at = 0
for p in 0 .. len(ui_st.cv_texts) {
let t = ui_st.cv_texts[p]
if not sat(k, at, t) { return false }
at += len(t)
}
return at == len(k)
}