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>
This commit is contained in:
Orkun ÇAKILKAYA 2026-09-24 15:58:47 +03:00
parent 334469ef61
commit 66a2bc2214
64 changed files with 74806 additions and 63975 deletions

View file

@ -13,6 +13,7 @@ function ck_params(f: Node, labels: []pointer, tys: []pointer) -> void {
}
}
function ck_call_fn(e: Node, name: pointer, f: Node) -> pointer {
call_fill_defaults(e, f)
let labels = new []pointer
let tys = new []pointer
ck_params(f, labels, tys)
@ -21,6 +22,7 @@ function ck_call_fn(e: Node, name: pointer, f: Node) -> pointer {
return f.ty
}
function ck_call_alias(e: Node, name: pointer, al: int, f: Node) -> pointer {
if g_al_labels[al] == null { call_fill_defaults(e, f) } else { call_mixed_to_named(e, ns_alias_labels(al)) }
let labels = new []pointer
let tys = new []pointer
ck_params(f, labels, tys)
@ -50,11 +52,18 @@ function ck_args(e: Node, name: pointer, labels: []pointer, tys: []pointer) -> v
let vt = ck_expr(fi.a)
let at = ck_label_at(labels, fi.s)
if at >= 0 { ck_give(tys[at], vt, fi.a, `argument {fi.s} of {name}`) }
if at < 0 and len(labels) > 0 { ck_err("arity", fi.a, `{name} has no parameter {fi.s}`) }
j += 1
}
var m = 0
while m < len(labels) {
if not ck_named_has(e, labels[m]) { ck_err("arity", e, `this call to {name} leaves out {labels[m]}, which has no default`) }
m += 1
}
return
}
if n != len(tys) { ck_err("arity", e, `{name} takes {itoa(len(tys))} argument(s) and this call gives {itoa(n)}`) }
if n < len(tys) and len(labels) == len(tys) { ck_err("arity", e, `this call to {name} leaves out {labels[n]}, which has no default`) }
else { if n != len(tys) { ck_err("arity", e, `{name} takes {itoa(len(tys))} argument(s) and this call gives {itoa(n)}`) } }
var i = 0
while i < n {
if i < len(tys) { ck_expect = tys[i] }
@ -63,6 +72,14 @@ function ck_args(e: Node, name: pointer, labels: []pointer, tys: []pointer) -> v
i += 1
}
}
function ck_named_has(e: Node, label: pointer) -> bool {
var i = 0
while i < len(e.kids) {
if (e.kids[i].s == label) { return true }
i += 1
}
return false
}
function ck_walk_args(e: Node) -> void {
var i = 0
while i < len(e.kids) {

View file

@ -45,6 +45,7 @@ function gen_arg_nodes(e: Node, labels: []pointer) -> []Node {
}
function gen_call(e: Node, name: pointer, t: Node) -> pointer {
let expect = ck_call_expect
call_fill_defaults(e, t)
let tps = gen_split(t.tps)
let labels = new []pointer
let ptys = new []pointer

View file

@ -147,10 +147,24 @@ function ck_listener(l: Node) -> void {
ck_block(l.a)
ck_pop(m)
}
# a program's function named like one of the runtime's takes every call the runtime makes to its
# own - the runtime's ui_measure started answering to a package's - so the name is refused
function ck_runtime_shadows() -> void {
var r = g_prog_user_end
while r < len(prog) {
let d = prog[r]
if d.kind == N_FN {
let u = ck_fn(d.s)
if u != null and u != d { ck_err("shadow", u, `{d.s} is also a function of the engine runtime ({d.file}); choose another name`) }
}
r += 1
}
}
# the pass also makes the generics real (check_gen.ludic), so it always runs
function check_program() -> void {
gen_collect()
ck_index()
ck_runtime_shadows()
var i = 0
while i < len(prog) {
let d = prog[i]