ludic.ui: t(key, holes...) in a template - the key marked (byte 1, a marked key kept as it is) and each hole's value after a byte 31, the text the translator decodes at draw time; joined through the text memo, so nothing is made while it reads the same (phase 26)

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
Orkun ÇAKILKAYA 2026-09-29 23:16:05 +03:00
parent 82c652ca31
commit b5b3edc6ec
8 changed files with 72 additions and 1 deletions

View file

@ -34,6 +34,8 @@ function ui_default_backend() -> UiBackend {
return UiRenderers[0].make()
}
export state UiState {
tk_mark: string = tk_byte(1) # tkey.ludic: a key's mark, and the byte between its holes
tk_sep: string = tk_byte(31)
@max(64) ov_path: []string = new []string # override.ludic: paths a studio's text stands in for ...
@max(64) ov_text: []string = new []string # ... and the texts
@max(256) ph_keys: []string = new []string # held.ludic: elements a preview holds a pseudo-class on for ...

View file

@ -29,7 +29,7 @@ function ui_eval(ui_st: mut UiState, x: UiExpr, e: UiEnv) -> Val {
if x.op == X_TEXT { return ev_text(ui_st, x, e) }
return vl_binary(ui_st, x.s, ui_eval(ui_st, x.a, e), ui_eval(ui_st, x.b, e))
}
# a call: len() and range() are the template's own, anything else is the view's
# a call: len(), range() and t() are the template's own, anything else is the view's
function ev_call(ui_st: mut UiState, x: UiExpr, e: UiEnv) -> Val {
let depth = ui_st.ev_depth
@alloc_ok("once per depth of nesting, then kept")
@ -45,6 +45,7 @@ function ev_call(ui_st: mut UiState, x: UiExpr, e: UiEnv) -> Val {
# the call itself, its arguments evaluated; a callee reads them and keeps none
function ev_call_with(ui_st: mut UiState, x: UiExpr, e: UiEnv, args: Val) -> Val {
if x.s == "len" and len(x.args) == 1 { return uv_int(ui_st, Value.count(Value.at(args, 0))) }
if x.s == "t" and len(x.args) >= 1 { return ev_t(ui_st, args) } # tkey.ludic: a key, and its holes
if x.s == "range" and len(x.args) == 1 {
let out = uv_list(ui_st)
for i in 0 .. Value.as_int(Value.at(args, 0)) { Value.add(out, uv_int(ui_st, i)) }

View file

@ -10,6 +10,10 @@ function ev_text(ui_st: mut UiState, x: UiExpr, e: UiEnv) -> Val {
List.clear(ps)
for i in 0 .. len(x.args) { tx_piece(ui_st, ps, ui_eval(ui_st, x.args[i], e)) }
ui_st.tx_depth = depth
return tx_memo(ui_st, ps)
}
# the text these pieces join into, from the memo when it read this way before, else joined and kept
function tx_memo(ui_st: mut UiState, ps: []string) -> Val {
let m = mm_of(ui_st)
var h = 216613626 ^ (MM_TEXT & 255)
for p in 0 .. len(ps) {

View file

@ -113,3 +113,4 @@ import "held.ludic"
import "override.ludic"
import "override_paths.ludic"
import "mounted.ludic"
import "tkey.ludic"

View file

@ -0,0 +1,30 @@
# tkey.ludic - t('pause.resume') and t('hud.day', day) in a template: the key as text, marked, its holes'
# values after it ("\x01hud.day\x1f12"), handed to the translator at draw time like any text (the game's
# ludic.i18n decodes it). A key already marked (a Key held in the model) keeps its mark, a plural's too.
# Joined through the memo, so a key and holes that read the same as last frame make nothing.
function ev_t(ui_st: mut UiState, args: Val) -> Val {
let depth = ui_st.tx_depth
@alloc_ok("once per depth of nesting, then kept")
while len(ui_st.tx_pieces) <= depth { push(ui_st.tx_pieces, new []string) }
let ps = ui_st.tx_pieces[depth]
ui_st.tx_depth = depth + 1
List.clear(ps)
let key = vl_text_u(ui_st, Value.at(args, 0))
if len(key) == 0 or (key[0] != 1 and key[0] != 2) { ui_kept(ps, ui_st.tk_mark) }
ui_kept(ps, key)
for i in 1 .. Value.count(args) {
ui_kept(ps, ui_st.tk_sep)
tx_piece(ui_st, ps, Value.at(args, i))
}
ui_st.tx_depth = depth
return tx_memo(ui_st, ps)
}
# one byte as a string, made once for the state's two markers
@alloc_ok("once: a marker the state keeps")
function tk_byte(b: int) -> string {
let out = bytes(2)
out[0] = b
out[1] = 0
let s: string = out
return s
}