ludic/selfhost/frontend/component_gen2.ludic
Orkuncakilkaya ce80e64b24 feat(lang): 0.S - components take states in their header; migrate writes them, never inside a name, never outside the programs given, never into a package's state
- component Name (a: mut A, b: B) { ... }: every getter, default, function and event takes the
  header's states before the instance; a member's call to another passes them on; the glue is
  supplied them; the template never sees them; a read-only one is read-only in every member
- ludic migrate state: a component's members' needs go into its header (added to an existing one,
  mut added where now changed); a field read or a member call inside a component is the compiler's,
  so nothing is written inside a name and no ', )' is left; an entry point that declares states
  already gets the rest after them
- a program's module named like a package gets <Name>AppState; a program's own file its own state;
  a friend module's files go by directory; a package's settable var stays state
- it writes only under the programs and directories given (and runtime/ with --runtime), and
  refuses the whole run naming any other file that would have to change
- a name a package already moved into its state is rewritten through it; a read of the runtime's
  var through the runtime function that answers it (gl_w: gl_width())
- a state's instance supplied by the runtime is not a uses reference
- tests: state/component, rejected/state_component_ro, rendering/ui_render3d, migrate component
  and foreign cases

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-09-25 17:11:15 +03:00

91 lines
3.6 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
}
function cm_model_src(c: int, px: pointer, rec: pointer, head: Node) -> pointer {
var src = `function {px}model(p: pointer) -> Val {{\n let s: {rec} = p\n let o = value_object()\n`
var i = 0
while i < len(g_cm_keep[c]) {
let k = g_cm_keep[c][i]
let put = cm_to(c, g_cm_keep_ty[c][i], "s." + k, head)
src = src + ` value_put(o, "{k}", {put})\n`
i += 1
}
i = 0
while i < len(g_cm_fields[c]) {
let g = g_cm_fields[c][i]
let put = cm_to(c, g.ty, g.s + "(s)", head)
src = src + ` value_put(o, "{g_cm_fnames[c][i]}", {put})\n`
i += 1
}
return src + " return o\n}\n"
}
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) -> 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_null()\n` } else {
let back = cm_to(c, f.ty, call, head)
src = src + ` return {back}\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_null()\n }}\n`
}
i += 1
}
return src + " return value_new(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 ""
}