ludic/selfhost/frontend/view_conv.ludic
Orkuncakilkaya 66a2bc2214 feat(lang): L11 views and templates - the UI is markup, not code
A `view Name { field = x; function q(..); on e(..) }` declaration is the one bridge between a
program and its UI: it writes view_<name>() -> UiView, whose model is a Value of every field and
whose call runs a query or an event by name.

ludic.ui is a template runtime:
- HTML-shaped XML screens and components, loaded at run time;
- {expression} bindings, if/else/each, props, slots, per-instance state;
- on-press / onclick actions (event, set, emit);
- component libraries (export="true", <import src as>).

Styling:
- stylesheets in <style> or importable .lss files (@import);
- CSS selectors (#id, .class, [attr=v], descendant and > combinators, :hover, :disabled,
  :first-child, :last-child, :nth-child, :not) weighed by specificity;
- the box model and flex under CSS's property names.

Also:
- default parameters, and positional-then-named calls;
- Value gains a float kind;
- a function shadowing a runtime one is refused;
- an index is evaluated before the slice is read;
- runtime errors name the right file.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-09-24 15:58:47 +03:00

68 lines
2.5 KiB
Text

# view_conv.ludic — L11: a value of the program as a Value a template reads. A number, a bool
# and a string are themselves; a list is a list of its items; a record is an object of every
# field a template could read (a field it could not - a function, a buffer - is left out).
# Anything else answers null, and the caller says so.
function vw_conv(v: int, ty: pointer, x: pointer, head: Node) -> pointer {
if (ty == "int") { return `value_int({x})` }
if (ty == "float") { return `value_float({x})` }
if (ty == "string") { return `value_str({x})` }
if (ty == "bool") {
vw_helper(`function view_val_bool(b: bool) -> Val {{\n if b {{ return value_bool(1) }}\n return value_bool(0)\n}}\n`, "view_val_bool", head)
return `view_val_bool({x})`
}
if len(ty) > 2 and ty[0] == '[' and ty[1] == ']' {
let et: pointer = ty[2..len(ty)]
let name = `view_val_list_{vw_mangle(et)}`
if vw_have(name) { return `{name}({x})` }
let item = vw_conv(v, et, "xs[i]", head)
if item == null { return null }
push(g_vw_helpers, name)
vw_parse(`function {name}(xs: {ty}) -> Val {{\n let out = value_list()\n var i = 0\n while i < len(xs) {{\n value_add(out, {item})\n i += 1\n }}\n return out\n}}\n`, head.file, head.line, 1)
return `{name}({x})`
}
let comp = find_comp(ty)
if comp == null { return null }
let name = `view_val_{vw_mangle(ty)}`
if vw_have(name) { return `{name}({x})` }
push(g_vw_helpers, name)
var src = `function {name}(r: {ty}) -> Val {{\n if r == null {{ return value_null() }}\n let o = value_object()\n`
var i = 0
while i < len(comp.kids) {
let f = comp.kids[i]
if f.ty != null {
let c = vw_conv(v, f.ty, `r.{f.s}`, head)
if c != null { src = src + ` value_put(o, "{f.s}", {c})\n` }
}
i += 1
}
vw_parse(src + " return o\n}\n", head.file, head.line, 1)
return `{name}({x})`
}
function vw_have(name: pointer) -> bool {
var i = 0
while i < len(g_vw_helpers) {
if (g_vw_helpers[i] == name) { return true }
i += 1
}
return false
}
function vw_helper(src: pointer, name: pointer, head: Node) -> void {
if vw_have(name) { return }
push(g_vw_helpers, name)
vw_parse(src, head.file, head.line, 1)
}
# a type as part of a name: []int -> list_int, a.B -> a_B
function vw_mangle(ty: pointer) -> pointer {
let n = len(ty)
let out = bytes(n + 1)
var i = 0
while i < n {
var c = ty[i]
let ok = (c >= 'a' and c <= 'z') or (c >= 'A' and c <= 'Z') or (c >= '0' and c <= '9')
if not ok { c = '_' }
out[i] = c
i += 1
}
out[n] = 0
return out
}