From the fence's kept frames in 22 minutes of play: - A component field that is a record or a list of records was `value_put(o, k, view_val_T(x))`, a whole new tree every frame (HudPrompt's notifications). view_fill.ludic generates view_set_T / view_set_list_T / view_fill_T that fill the object and list under the key in place. - value_list_fit dropped the items it cut off and value_item made new ones as the list grew back, a Value per item per regrowth (value_item / value_set_strs); the cut-off items are now the list's spares (Val.spare), and an item of another kind is turned rather than replaced. - ludic.ui: a scroll box's "scroll" and a slider's "change" fired a fresh Value.float a frame (sc_walk, scroll.ludic:36); ui_fire_float takes one from a ring kept with the state (fired.ludic). ui_object_fit_into is exported, for a draw that keeps its list. - Json.read_file(path): read, parsed, and the file's text given back - Json.parse(Fs.read_text()) kept the whole file on every read (Maroon Lake's settings peeks). Golden value_list_regrow: a list alternating 6 and 2 items every frame keeps nothing (the toolchain before this fails it: +128 B new Val from value_item). Game compiles; ludic.i18n/settings/hints/ base tests pass (ludic.ui has none); arena and fence goldens unchanged. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
116 lines
5.2 KiB
Text
116 lines
5.2 KiB
Text
# component_gen2.ludic — L11: a component's props from the parent's attributes, its model for the
|
|
# template (props, state and fields), and its call (functions, events, and `set` of a state)
|
|
function cm_props_src(c: int, px: pointer, rec: pointer) -> pointer {
|
|
var src = `function {px}props(p: pointer, v: Val) -> void {{\n let s: {rec} = p\n`
|
|
var i = 0
|
|
while i < len(g_cm_keep[c]) {
|
|
if g_cm_prop[c][i] {
|
|
let k = g_cm_keep[c][i]
|
|
let got = cm_from(c, g_cm_keep_ty[c][i], "value_get(v, \"" + k + "\")")
|
|
src = src + ` if value_has(v, "{k}") != 0 {{ s.{k} = {got} }}\n`
|
|
}
|
|
i += 1
|
|
}
|
|
return src + "}\n"
|
|
}
|
|
# a template's value as a prop or state of the component's own type
|
|
function cm_from(c: int, ty: pointer, x: pointer) -> pointer {
|
|
if (ty == "Val") { return x }
|
|
if (ty == "int") { return `value_as_int({x})` }
|
|
if (ty == "float") { return `value_as_float({x})` }
|
|
if (ty == "bool") { return `value_as_int({x}) != 0` }
|
|
if (ty == "string") { return `ui_val_text({x})` }
|
|
cm_err(c, `a prop or state is an int, a float, a bool, a string or a Val, not a {ty}`)
|
|
return x
|
|
}
|
|
# the model is filled into the object the instance keeps (ludic.ui's): a scalar field updates its own
|
|
# Value in place, so showing a component allocates nothing for it frame after frame
|
|
function cm_model_src(c: int, px: pointer, rec: pointer, head: Node) -> pointer {
|
|
var src = `function {px}model(p: pointer, o: Val) -> Val {{\n let s: {rec} = p\n`
|
|
var i = 0
|
|
while i < len(g_cm_keep[c]) {
|
|
let k = g_cm_keep[c][i]
|
|
src = src + cm_set(c, g_cm_keep_ty[c][i], k, "s." + k, head)
|
|
i += 1
|
|
}
|
|
i = 0
|
|
while i < len(g_cm_fields[c]) {
|
|
let g = g_cm_fields[c][i]
|
|
src = src + cm_set(c, g.ty, g_cm_fnames[c][i], g.s + "(s)", head)
|
|
i += 1
|
|
}
|
|
return src + " return o\n}\n"
|
|
}
|
|
# what a call answers, written into `into` (ludic.ui's pooled record while it builds, null otherwise)
|
|
function cm_into(c: int, ty: pointer, x: pointer, head: Node) -> pointer {
|
|
if (ty == "int") { return `value_into_int(into, {x})` }
|
|
if (ty == "float") { return `value_into_float(into, {x})` }
|
|
if (ty == "string") { return `value_into_str(into, {x})` }
|
|
if (ty == "bool") { return `value_into_bool(into, {x})` }
|
|
if (ty == "[]int") { return `value_into_ints(into, {x})` }
|
|
if (ty == "[]float") { return `value_into_floats(into, {x})` }
|
|
if (ty == "[]string") { return `value_into_strs(into, {x})` }
|
|
if (ty == "[]bool") { return `value_into_bools(into, {x})` }
|
|
return cm_to(c, ty, x, head)
|
|
}
|
|
function cm_set(c: int, ty: pointer, k: pointer, x: pointer, head: Node) -> pointer {
|
|
if (ty == "int") { return ` value_set_int(o, "{k}", {x})\n` }
|
|
if (ty == "float") { return ` value_set_float(o, "{k}", {x})\n` }
|
|
if (ty == "string") { return ` value_set_str(o, "{k}", {x})\n` }
|
|
if (ty == "bool") { return ` value_set_bool(o, "{k}", {x})\n` }
|
|
if (ty == "[]int") { return ` value_set_ints(o, "{k}", {x})\n` }
|
|
if (ty == "[]float") { return ` value_set_floats(o, "{k}", {x})\n` }
|
|
if (ty == "[]string") { return ` value_set_strs(o, "{k}", {x})\n` }
|
|
if (ty == "[]bool") { return ` value_set_bools(o, "{k}", {x})\n` }
|
|
let line = vw_set(0, ty, "o", k, x, head) # a record or a list of them: filled in place (view_fill)
|
|
if line == null {
|
|
cm_err(c, `a {ty} is something a template cannot read`)
|
|
return ""
|
|
}
|
|
return line
|
|
}
|
|
function cm_to(c: int, ty: pointer, x: pointer, head: Node) -> pointer {
|
|
if (ty == "Val") { return x }
|
|
let conv = vw_conv(0, ty, x, head)
|
|
if conv == null { cm_err(c, `a {ty} is something a template cannot read`) }
|
|
return conv
|
|
}
|
|
# call(name, args): a function or an event by name, or "set:NAME" for a state the template sets.
|
|
# A name it does not know answers a Value of kind 99, which the runtime reports.
|
|
function cm_call_src(c: int, px: pointer, rec: pointer, head: Node) -> pointer {
|
|
var src = `function {px}call(p: pointer, name: string, args: Val, into: Val) -> Val {{\n let s: {rec} = p\n`
|
|
let calls = g_cm_calls[c]
|
|
var i = 0
|
|
while i < len(calls) {
|
|
let f = calls[i]
|
|
var args = "s" # 0.S: its states are supplied, not passed
|
|
let lead = len(g_cm_ps[c]) + 1
|
|
var k = lead
|
|
while k < len(f.kids) {
|
|
args = args + ", " + cm_arg(c, f.kids[k], k - lead)
|
|
k += 1
|
|
}
|
|
src = src + ` if name == "{g_cm_cnames[c][i]}" {{\n`
|
|
let call = f.s + "(" + args + ")"
|
|
if f.ty == "void" { src = src + ` {call}\n return value_into_null(into)\n` } else { src = src + ` return {cm_into(c, f.ty, call, head)}\n` }
|
|
src = src + " }\n"
|
|
i += 1
|
|
}
|
|
i = 0
|
|
while i < len(g_cm_keep[c]) {
|
|
if not g_cm_prop[c][i] {
|
|
let k = g_cm_keep[c][i]
|
|
let got = cm_from(c, g_cm_keep_ty[c][i], "value_at(args, 0)")
|
|
src = src + ` if name == "set:{k}" {{\n s.{k} = {got}\n return value_into_null(into)\n }}\n`
|
|
}
|
|
i += 1
|
|
}
|
|
return src + " return value_into(into, 99)\n}\n"
|
|
}
|
|
# a parameter out of the template's argument list
|
|
function cm_arg(c: int, p: Node, i: int) -> pointer {
|
|
let at = `value_at(args, {i})`
|
|
if (p.ty == "int") or (p.ty == "float") or (p.ty == "bool") or (p.ty == "string") or (p.ty == "Val") { return cm_from(c, p.ty, at) }
|
|
cm_err(c, `{p.s} is a {p.ty}; a template can pass an int, a float, a bool, a string or a Val`)
|
|
return ""
|
|
}
|