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>
96 lines
2.9 KiB
Text
96 lines
2.9 KiB
Text
# defaults.ludic — L11: a parameter may have a default, `pad: float = 8.0`, and a call may leave it
|
|
# out - positionally from the end, or by not naming it. That is what makes a component's props
|
|
# usable: `button(label: "Buy", on_press: fn buy)` rather than every prop of a button spelt out.
|
|
# The default is an expression written where the function is and evaluated for each call.
|
|
# `f(a, b, size: 2)`: the leading arguments are the first parameters, by position, and the rest
|
|
# are named - the natural call of a component, whose first prop is the one it is about
|
|
function call_mixed_to_named(e: Node, labels: []pointer) -> void {
|
|
var first_named = -1
|
|
var i = 0
|
|
while i < len(e.kids) {
|
|
if e.kids[i].kind == E_FINIT and first_named < 0 { first_named = i }
|
|
if e.kids[i].kind != E_FINIT and first_named >= 0 {
|
|
g_err_file = e.file
|
|
g_err_line = e.line
|
|
perr("a positional argument after a named one")
|
|
}
|
|
i += 1
|
|
}
|
|
if first_named <= 0 { return }
|
|
let out = new []Node
|
|
var k = 0
|
|
while k < len(e.kids) {
|
|
var a = e.kids[k]
|
|
if k < first_named and k < len(labels) {
|
|
let fi = node(E_FINIT)
|
|
fi.s = labels[k]
|
|
fi.a = a
|
|
fi.line = a.line
|
|
fi.file = a.file
|
|
a = fi
|
|
}
|
|
push(out, a)
|
|
k += 1
|
|
}
|
|
e.kids = out
|
|
}
|
|
function call_fill_defaults(e: Node, f: Node) -> void {
|
|
if f == null { return }
|
|
let labels = new []pointer
|
|
let defs = new []Node
|
|
var any = false
|
|
var i = 0
|
|
while i < len(f.kids) {
|
|
let p = f.kids[i]
|
|
if p.kind == N_PARAM {
|
|
push(labels, p.s)
|
|
push(defs, p.a)
|
|
if p.a != null { any = true }
|
|
}
|
|
i += 1
|
|
}
|
|
call_mixed_to_named(e, labels)
|
|
if not any { return }
|
|
if len(e.kids) > 0 and e.kids[0].kind == E_FINIT {
|
|
let out = new []Node
|
|
var li = 0
|
|
while li < len(labels) {
|
|
var found: Node = null
|
|
var k = 0
|
|
while k < len(e.kids) {
|
|
if (e.kids[k].s == labels[li]) { found = e.kids[k] }
|
|
k += 1
|
|
}
|
|
if found == null and defs[li] != null {
|
|
found = node(E_FINIT)
|
|
found.s = labels[li]
|
|
found.a = default_copy(defs[li], e)
|
|
}
|
|
if found != null { push(out, found) }
|
|
li += 1
|
|
}
|
|
# a label the function does not have stays, so the call is refused for it
|
|
var k2 = 0
|
|
while k2 < len(e.kids) {
|
|
if not default_label_in(labels, e.kids[k2].s) { push(out, e.kids[k2]) }
|
|
k2 += 1
|
|
}
|
|
e.kids = out
|
|
return
|
|
}
|
|
while len(e.kids) < len(labels) and defs[len(e.kids)] != null { push(e.kids, default_copy(defs[len(e.kids)], e)) }
|
|
}
|
|
function default_label_in(labels: []pointer, s: pointer) -> bool {
|
|
var i = 0
|
|
while i < len(labels) {
|
|
if (labels[i] == s) { return true }
|
|
i += 1
|
|
}
|
|
return false
|
|
}
|
|
# each call gets its own copy of the default, at the call's line for its errors
|
|
function default_copy(d: Node, at: Node) -> Node {
|
|
let c = gen_clone(d, new []pointer, new []pointer)
|
|
c.line = at.line
|
|
return c
|
|
}
|