ludic.ui, runtime, compiler: an expression's Value comes from the screen's pool while it is built (never a state's start or an action's), true and false shared, calc() terms pooled and read in place, a model's list fields filled in place (value_set_ints/strs/floats/bools); reseeded
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
parent
25f4d49e43
commit
fa119d234b
18 changed files with 44838 additions and 44532 deletions
|
|
@ -55,13 +55,15 @@ function el_pct(v: Val) -> float {
|
|||
return 0.0
|
||||
}
|
||||
# the number at the front of `12.5px`
|
||||
function el_num(s: string) -> float {
|
||||
function el_num(s: string) -> float { return el_num_in(s, 0, len(s)) }
|
||||
# the number in s from..to, read where it stands
|
||||
function el_num_in(s: string, from: int, to: int) -> float {
|
||||
var f = 0.0
|
||||
var unit = 0.0
|
||||
var neg = false
|
||||
for i in 0 .. len(s) {
|
||||
for i in from .. to {
|
||||
let c = s[i]
|
||||
if c == 45 and i == 0 { neg = true } else if c == 46 { unit = 0.1 } else if c >= 48 and c <= 57 {
|
||||
if c == 45 and i == from { neg = true } else if c == 46 { unit = 0.1 } else if c >= 48 and c <= 57 {
|
||||
if unit > 0.0 {
|
||||
f = f + float(c - 48) * unit
|
||||
unit = unit * 0.1
|
||||
|
|
|
|||
|
|
@ -34,12 +34,19 @@ function ui_default_backend() -> UiBackend {
|
|||
export state UiState {
|
||||
pl_pools: []UiPool = new []UiPool # pool.ludic: each screen's nodes and envs, used again
|
||||
pl_cur: UiPool = null
|
||||
uv_on: bool = false # pool.ludic: a build is under way, so expressions' Values are pooled
|
||||
uv_true: Val = null
|
||||
uv_false: Val = null
|
||||
mm: UiMemo = null # memo.ludic: splits, gradients and keys by their text
|
||||
mm_ints: []string = null
|
||||
mm_ivals: []Val = null
|
||||
ev_depth: int = 0 # eval.ludic: a call's arguments, in lists kept one a depth
|
||||
ev_args: []Val = new []Val
|
||||
cv_texts: []string = new []string # units_pieces.ludic: a value's var() pieces, kept
|
||||
ca_pool: []CaVal = new []CaVal # calc.ludic: an evaluation's terms, used again by the next
|
||||
ca_used: int = 0
|
||||
ca_args: [][]CaVal = new [][]CaVal
|
||||
ca_depth: int = 0
|
||||
fit_out: []float = new []float # draw_content.ludic: an image's box, kept
|
||||
r3n_cut: []float = new []float # render3d_nine.ludic: a nine-slice's edges, kept
|
||||
r3n_xs: []float = new []float
|
||||
|
|
|
|||
|
|
@ -7,6 +7,8 @@ function ca_eval(ui_st: mut UiState, s: string) -> bool {
|
|||
ui_st.ca_s = s
|
||||
ui_st.ca_i = 0
|
||||
ui_st.ca_bad = ""
|
||||
ui_st.ca_used = 0 # this evaluation's terms come from the pool, from the start
|
||||
ui_st.ca_depth = 0
|
||||
let v = ca_factor(ui_st)
|
||||
ca_ws(ui_st)
|
||||
if ui_st.ca_bad == "" and ui_st.ca_i < len(ui_st.ca_s) { ui_st.ca_bad = "nothing after its closing )" }
|
||||
|
|
@ -42,10 +44,10 @@ function ca_sum(ui_st: mut UiState) -> CaVal {
|
|||
}
|
||||
return a
|
||||
}
|
||||
function ca_add(ui_st: UiState, a: CaVal, b: CaVal, minus: bool) -> CaVal {
|
||||
function ca_add(ui_st: mut UiState, a: CaVal, b: CaVal, minus: bool) -> CaVal {
|
||||
var sign = 1.0
|
||||
if minus { sign = -1.0 }
|
||||
let r = new CaVal
|
||||
let r = ca_new(ui_st)
|
||||
if a.bare and b.bare {
|
||||
r.n = a.n + sign * b.n
|
||||
return r
|
||||
|
|
@ -56,10 +58,22 @@ function ca_add(ui_st: UiState, a: CaVal, b: CaVal, minus: bool) -> CaVal {
|
|||
return r
|
||||
}
|
||||
# a bare number beside a length is a length in px
|
||||
function ca_len(ui_st: UiState, a: CaVal) -> CaVal {
|
||||
function ca_len(ui_st: mut UiState, a: CaVal) -> CaVal {
|
||||
if not a.bare { return a }
|
||||
let r = new CaVal
|
||||
let r = ca_new(ui_st)
|
||||
r.bare = false
|
||||
r.px = el_px(ui_st, a.n)
|
||||
return r
|
||||
}
|
||||
# a term, blank, from the pool: an evaluation is over before the next begins, and what it works out is
|
||||
# copied into ca_px / ca_pct, so its records are used again by the next one (never made a frame)
|
||||
function ca_new(ui_st: mut UiState) -> CaVal {
|
||||
if ui_st.ca_used >= len(ui_st.ca_pool) { push(ui_st.ca_pool, new CaVal) }
|
||||
let r = ui_st.ca_pool[ui_st.ca_used]
|
||||
ui_st.ca_used += 1
|
||||
r.px = 0.0
|
||||
r.pct = 0.0
|
||||
r.n = 0.0
|
||||
r.bare = true
|
||||
return r
|
||||
}
|
||||
|
|
|
|||
|
|
@ -11,23 +11,28 @@ function ca_fn_at(s: string, at: int) -> string {
|
|||
function ca_word(s: string, at: int, w: string) -> bool { return at + len(w) <= len(s) and sat(s, at, w) }
|
||||
function ca_fn(ui_st: mut UiState, f: string) -> CaVal {
|
||||
ui_st.ca_i = ui_st.ca_i + len(f) + 1
|
||||
let args = new []CaVal
|
||||
let depth = ui_st.ca_depth # min(calc(...)) nests: a kept list of arguments a depth
|
||||
while len(ui_st.ca_args) <= depth { push(ui_st.ca_args, new []CaVal) }
|
||||
let args = ui_st.ca_args[depth]
|
||||
List.clear(args)
|
||||
ui_st.ca_depth = depth + 1
|
||||
while ui_st.ca_bad == "" {
|
||||
push(args, ca_sum(ui_st))
|
||||
ca_ws(ui_st)
|
||||
if ui_st.ca_i < len(ui_st.ca_s) and ui_st.ca_s[ui_st.ca_i] == 44 { ui_st.ca_i += 1 } else { break }
|
||||
}
|
||||
ui_st.ca_depth = depth
|
||||
if ui_st.ca_bad == "" and (ui_st.ca_i >= len(ui_st.ca_s) or ui_st.ca_s[ui_st.ca_i] != 41) { ui_st.ca_bad = "a closing )" }
|
||||
ui_st.ca_i += 1
|
||||
if ui_st.ca_bad != "" { return new CaVal }
|
||||
if ui_st.ca_bad != "" { return ca_new(ui_st) }
|
||||
if f == "clamp" and len(args) != 3 {
|
||||
ui_st.ca_bad = "clamp(least, want, most)"
|
||||
return new CaVal
|
||||
return ca_new(ui_st)
|
||||
}
|
||||
for i in 0 .. len(args) {
|
||||
if args[i].pct != 0.0 {
|
||||
ui_st.ca_bad = `lengths in {f}(), not a percentage`
|
||||
return new CaVal
|
||||
return ca_new(ui_st)
|
||||
}
|
||||
}
|
||||
if f == "clamp" { return ca_pick(ui_st, ca_pick(ui_st, args[1], args[0], false), args[2], true) }
|
||||
|
|
@ -36,7 +41,7 @@ function ca_fn(ui_st: mut UiState, f: string) -> CaVal {
|
|||
return r
|
||||
}
|
||||
# the smaller (or the larger) of two, a bare number beside a length being px
|
||||
function ca_pick(ui_st: UiState, a: CaVal, b: CaVal, smaller: bool) -> CaVal {
|
||||
function ca_pick(ui_st: mut UiState, a: CaVal, b: CaVal, smaller: bool) -> CaVal {
|
||||
if a.bare and b.bare {
|
||||
if (b.n < a.n) == smaller and b.n != a.n { return b }
|
||||
return a
|
||||
|
|
|
|||
|
|
@ -10,7 +10,7 @@ function ca_product(ui_st: mut UiState) -> CaVal {
|
|||
ui_st.ca_i += 1
|
||||
let b = ca_factor(ui_st)
|
||||
if div and not b.bare and not a.bare and a.pct == 0.0 and b.pct == 0.0 and b.px != 0.0 {
|
||||
a = ca_ratio(a.px / b.px)
|
||||
a = ca_ratio(ui_st, a.px / b.px)
|
||||
continue
|
||||
}
|
||||
if div and not b.bare {
|
||||
|
|
@ -21,7 +21,7 @@ function ca_product(ui_st: mut UiState) -> CaVal {
|
|||
ui_st.ca_bad = "a number that is not 0 after /"
|
||||
return a
|
||||
}
|
||||
if div { a = ca_scale(a, 1.0 / b.n) } else if a.bare { a = ca_scale(b, a.n) } else if b.bare { a = ca_scale(a, b.n) } else {
|
||||
if div { a = ca_scale(ui_st, a, 1.0 / b.n) } else if a.bare { a = ca_scale(ui_st, b, a.n) } else if b.bare { a = ca_scale(ui_st, a, b.n) } else {
|
||||
ui_st.ca_bad = "a plain number on one side of *"
|
||||
return a
|
||||
}
|
||||
|
|
@ -29,13 +29,13 @@ function ca_product(ui_st: mut UiState) -> CaVal {
|
|||
return a
|
||||
}
|
||||
# a length over a length is a plain number (100vw / 1800px)
|
||||
function ca_ratio(k: float) -> CaVal {
|
||||
let r = new CaVal
|
||||
function ca_ratio(ui_st: mut UiState, k: float) -> CaVal {
|
||||
let r = ca_new(ui_st)
|
||||
r.n = k
|
||||
return r
|
||||
}
|
||||
function ca_scale(a: CaVal, k: float) -> CaVal {
|
||||
let r = new CaVal
|
||||
function ca_scale(ui_st: mut UiState, a: CaVal, k: float) -> CaVal {
|
||||
let r = ca_new(ui_st)
|
||||
r.bare = a.bare
|
||||
r.n = a.n * k
|
||||
r.px = a.px * k
|
||||
|
|
@ -46,12 +46,12 @@ function ca_factor(ui_st: mut UiState) -> CaVal {
|
|||
ca_ws(ui_st)
|
||||
if ui_st.ca_i >= len(ui_st.ca_s) {
|
||||
ui_st.ca_bad = "a value"
|
||||
return new CaVal
|
||||
return ca_new(ui_st)
|
||||
}
|
||||
let c = ui_st.ca_s[ui_st.ca_i]
|
||||
if c == 45 {
|
||||
ui_st.ca_i += 1
|
||||
return ca_scale(ca_factor(ui_st), -1.0)
|
||||
return ca_scale(ui_st, ca_factor(ui_st), -1.0)
|
||||
}
|
||||
let f = ca_fn_at(ui_st.ca_s, ui_st.ca_i)
|
||||
if f == "min" or f == "max" or f == "clamp" { return ca_fn(ui_st, f) }
|
||||
|
|
@ -71,18 +71,21 @@ function ca_number(ui_st: mut UiState) -> CaVal {
|
|||
while ui_st.ca_i < len(ui_st.ca_s) and ((ui_st.ca_s[ui_st.ca_i] >= 48 and ui_st.ca_s[ui_st.ca_i] <= 57) or ui_st.ca_s[ui_st.ca_i] == 46) { ui_st.ca_i += 1 }
|
||||
let num_end = ui_st.ca_i
|
||||
while ui_st.ca_i < len(ui_st.ca_s) and ((ui_st.ca_s[ui_st.ca_i] >= 97 and ui_st.ca_s[ui_st.ca_i] <= 122) or ui_st.ca_s[ui_st.ca_i] == 37) { ui_st.ca_i += 1 }
|
||||
let r = new CaVal
|
||||
let r = ca_new(ui_st)
|
||||
if num_end == at {
|
||||
ui_st.ca_bad = "a number"
|
||||
return r
|
||||
}
|
||||
let word: string = ui_st.ca_s[at..ui_st.ca_i]
|
||||
let unit: string = ui_st.ca_s[num_end..ui_st.ca_i]
|
||||
if unit == "" {
|
||||
r.n = el_num(word)
|
||||
let s = ui_st.ca_s
|
||||
let end = ui_st.ca_i
|
||||
let f = el_num_in(s, at, num_end)
|
||||
if end == num_end {
|
||||
r.n = f
|
||||
return r
|
||||
}
|
||||
r.bare = false
|
||||
if unit == "%" { r.pct = el_num(word) } else if unit == "px" or unit == "em" or unit == "rem" or unit == "vw" or unit == "vh" { r.px = el_unit(ui_st, word) } else { ui_st.ca_bad = `a unit calc() knows, not {unit}` }
|
||||
if ca_unit(s, num_end, end, "%") { r.pct = f } else if ca_unit(s, num_end, end, "px") { r.px = el_px(ui_st, f) } else if ca_unit(s, num_end, end, "rem") { r.px = f * el_px(ui_st, 16.0) } else if ca_unit(s, num_end, end, "em") { r.px = f * el_em(ui_st) } else if ca_unit(s, num_end, end, "vw") { r.px = f * ui_st.ly_vp_w / 100.0 } else if ca_unit(s, num_end, end, "vh") { r.px = f * ui_st.ly_vp_h / 100.0 } else { ui_st.ca_bad = `a unit calc() knows, not {s[num_end..end]}` }
|
||||
return r
|
||||
}
|
||||
# the unit between `from` and `to` is `u`, compared in place
|
||||
function ca_unit(s: string, from: int, to: int, u: string) -> bool { return to - from == len(u) and sat(s, from, u) }
|
||||
|
|
|
|||
|
|
@ -23,7 +23,7 @@ function ct_sound(n: UiNode) -> string {
|
|||
function ct_activate(ui_st: mut UiState, n: UiNode) -> void {
|
||||
if not n.enabled { return }
|
||||
let t = ct_type(n)
|
||||
if t == "checkbox" { ct_change(ui_st, n, vl_bool(not ui_attr_on(n, "checked"))) } else if t == "radio" { ct_change(ui_st, n, ui_attr(n, "value")) } else if t == "select" { ct_step(ui_st, n, 1) } else if t == "key" { ct_key_start(ui_st, n) } else if t == "number" { ct_num_commit(ui_st, n) } else if t == "text" { ct_submit(ui_st, n) } else if t == "" {
|
||||
if t == "checkbox" { ct_change(ui_st, n, vl_bool(ui_st, not ui_attr_on(n, "checked"))) } else if t == "radio" { ct_change(ui_st, n, ui_attr(n, "value")) } else if t == "select" { ct_step(ui_st, n, 1) } else if t == "key" { ct_key_start(ui_st, n) } else if t == "number" { ct_num_commit(ui_st, n) } else if t == "text" { ct_submit(ui_st, n) } else if t == "" {
|
||||
ui_sound(ui_st, ct_sound(n))
|
||||
ui_fire(ui_st, n, "press", Value.null())
|
||||
}
|
||||
|
|
|
|||
|
|
@ -5,21 +5,21 @@ function ui_eval(ui_st: mut UiState, x: UiExpr, e: UiEnv) -> Val {
|
|||
if x.op == X_MEMBER {
|
||||
let o = ui_eval(ui_st, x.a, e)
|
||||
if Value.kind(o) == 6 and Value.has(o, x.s) != 0 { return Value.get(o, x.s) }
|
||||
if Value.kind(o) == 5 and x.s == "length" { return Value.int(Value.count(o)) }
|
||||
if Value.kind(o) == 5 and x.s == "length" { return uv_int(ui_st, Value.count(o)) }
|
||||
ui_err(ui_st, `.{x.s}: there is no such field`)
|
||||
return Value.null()
|
||||
}
|
||||
if x.op == X_INDEX { return Value.at(ui_eval(ui_st, x.a, e), Value.as_int(ui_eval(ui_st, x.b, e))) }
|
||||
if x.op == X_CALL { return ev_call(ui_st, x, e) }
|
||||
if x.op == X_NOT { return vl_bool(not vl_truthy(ui_eval(ui_st, x.a, e))) }
|
||||
if x.op == X_NEG { return vl_arith("-", Value.int(0), ui_eval(ui_st, x.a, e)) }
|
||||
if x.op == X_NOT { return vl_bool(ui_st, not vl_truthy(ui_eval(ui_st, x.a, e))) }
|
||||
if x.op == X_NEG { return vl_arith(ui_st, "-", mm_ival(ui_st, 0), ui_eval(ui_st, x.a, e)) }
|
||||
if x.op == X_AND {
|
||||
if not vl_truthy(ui_eval(ui_st, x.a, e)) { return vl_bool(false) }
|
||||
return vl_bool(vl_truthy(ui_eval(ui_st, x.b, e)))
|
||||
if not vl_truthy(ui_eval(ui_st, x.a, e)) { return vl_bool(ui_st, false) }
|
||||
return vl_bool(ui_st, vl_truthy(ui_eval(ui_st, x.b, e)))
|
||||
}
|
||||
if x.op == X_OR {
|
||||
if vl_truthy(ui_eval(ui_st, x.a, e)) { return vl_bool(true) }
|
||||
return vl_bool(vl_truthy(ui_eval(ui_st, x.b, e)))
|
||||
if vl_truthy(ui_eval(ui_st, x.a, e)) { return vl_bool(ui_st, true) }
|
||||
return vl_bool(ui_st, vl_truthy(ui_eval(ui_st, x.b, e)))
|
||||
}
|
||||
if x.op == X_COND {
|
||||
if vl_truthy(ui_eval(ui_st, x.a, e)) { return ui_eval(ui_st, x.b, e) }
|
||||
|
|
@ -27,10 +27,10 @@ function ui_eval(ui_st: mut UiState, x: UiExpr, e: UiEnv) -> Val {
|
|||
}
|
||||
if x.op == X_TEXT {
|
||||
var out = ""
|
||||
for i in 0 .. len(x.args) { out = out + vl_text(ui_eval(ui_st, x.args[i], e)) }
|
||||
return Value.str(out)
|
||||
for i in 0 .. len(x.args) { out = out + vl_text_u(ui_st, ui_eval(ui_st, x.args[i], e)) }
|
||||
return uv_str(ui_st, out)
|
||||
}
|
||||
return vl_binary(x.s, ui_eval(ui_st, x.a, e), ui_eval(ui_st, x.b, e))
|
||||
return vl_binary(ui_st, x.s, ui_eval(ui_st, x.a, e), ui_eval(ui_st, x.b, e))
|
||||
}
|
||||
# a call: len() and range() are the template's own, anything else is the view's
|
||||
function ev_call(ui_st: mut UiState, x: UiExpr, e: UiEnv) -> Val {
|
||||
|
|
@ -46,10 +46,10 @@ function ev_call(ui_st: mut UiState, x: UiExpr, e: UiEnv) -> Val {
|
|||
}
|
||||
# the call itself, its arguments evaluated; a callee reads them and keeps none
|
||||
function ev_call_with(ui_st: mut UiState, x: UiExpr, e: UiEnv, args: Val) -> Val {
|
||||
if x.s == "len" and len(x.args) == 1 { return Value.int(Value.count(Value.at(args, 0))) }
|
||||
if x.s == "len" and len(x.args) == 1 { return uv_int(ui_st, Value.count(Value.at(args, 0))) }
|
||||
if x.s == "range" and len(x.args) == 1 {
|
||||
let out = Value.list()
|
||||
for i in 0 .. Value.as_int(Value.at(args, 0)) { Value.add(out, Value.int(i)) }
|
||||
let out = uv_list(ui_st)
|
||||
for i in 0 .. Value.as_int(Value.at(args, 0)) { Value.add(out, uv_int(ui_st, i)) }
|
||||
return out
|
||||
}
|
||||
var at = e
|
||||
|
|
|
|||
|
|
@ -40,6 +40,7 @@ export function ui_nodes(ui_st: mut UiState, screen: string, view: UiView) -> Ui
|
|||
}
|
||||
let t = ui_st.lib_screens[si]
|
||||
pl_begin(ui_st, screen)
|
||||
ui_st.uv_on = true
|
||||
lc_begin(ui_st)
|
||||
ui_st.ui_view = view
|
||||
let e = env_new(ui_st, null)
|
||||
|
|
@ -55,6 +56,7 @@ export function ui_nodes(ui_st: mut UiState, screen: string, view: UiView) -> Ui
|
|||
}
|
||||
bd_kids(ui_st, t.kids, e, screen, root.children)
|
||||
cs_tree(ui_st, root)
|
||||
ui_st.uv_on = false
|
||||
return root
|
||||
}
|
||||
# the room vw, vh and @media measure against; ui_show sets it to the screen's rectangle
|
||||
|
|
|
|||
|
|
@ -12,6 +12,9 @@ export property UiPool {
|
|||
envs1: []UiEnv = null
|
||||
un: int = 0
|
||||
ue: int = 0
|
||||
vals0: []Val = null # what expressions work out while the screen is built (uv_new)
|
||||
vals1: []Val = null
|
||||
uv: int = 0
|
||||
}
|
||||
# a build of `screen` begins: its other generation is handed out again from the start
|
||||
function pl_begin(ui_st: mut UiState, screen: string) -> void {
|
||||
|
|
@ -26,11 +29,14 @@ function pl_begin(ui_st: mut UiState, screen: string) -> void {
|
|||
p.nodes1 = new []UiNode
|
||||
p.envs0 = new []UiEnv
|
||||
p.envs1 = new []UiEnv
|
||||
p.vals0 = new []Val
|
||||
p.vals1 = new []Val
|
||||
push(ui_st.pl_pools, p)
|
||||
}
|
||||
p.gen = 1 - p.gen
|
||||
p.un = 0
|
||||
p.ue = 0
|
||||
p.uv = 0
|
||||
ui_st.pl_cur = p
|
||||
}
|
||||
# a node, blank, with its own lists (empty) - from the pool when a build is under way
|
||||
|
|
@ -82,3 +88,37 @@ function env_fresh() -> UiEnv {
|
|||
e.on_envs = new []UiEnv
|
||||
return e
|
||||
}
|
||||
# a Value an expression works out while a screen is built: from the pool while the build runs (none
|
||||
# outlives the screen's next build but one, like its nodes), made as ever outside one - an action's
|
||||
# `set`, a <state>'s start, what a handler is given are kept, so they are never pooled
|
||||
function uv_new(ui_st: mut UiState, tag: int) -> Val {
|
||||
let p = ui_st.pl_cur
|
||||
if not ui_st.uv_on or p == null { return Value.become(Value.null(), tag) }
|
||||
var list = p.vals0
|
||||
if p.gen == 1 { list = p.vals1 }
|
||||
if p.uv < len(list) {
|
||||
let v = list[p.uv]
|
||||
p.uv += 1
|
||||
return Value.become(v, tag)
|
||||
}
|
||||
let v = Value.become(Value.null(), tag)
|
||||
push(list, v)
|
||||
p.uv += 1
|
||||
return v
|
||||
}
|
||||
function uv_int(ui_st: mut UiState, n: int) -> Val {
|
||||
if n >= 0 and n < 1024 { return mm_ival(ui_st, n) }
|
||||
return Value.be_int(uv_new(ui_st, 1), n)
|
||||
}
|
||||
function uv_float(ui_st: mut UiState, x: float) -> Val { return Value.be_float(uv_new(ui_st, 7), x) }
|
||||
function uv_str(ui_st: mut UiState, s: string) -> Val { return Value.be_str(uv_new(ui_st, 4), s) }
|
||||
function uv_list(ui_st: mut UiState) -> Val { return uv_new(ui_st, 5) }
|
||||
# true and false, one of each for good: read them, never change them
|
||||
function uv_bool(ui_st: mut UiState, b: bool) -> Val {
|
||||
if ui_st.uv_true == null {
|
||||
ui_st.uv_true = Value.bool(1)
|
||||
ui_st.uv_false = Value.bool(0)
|
||||
}
|
||||
if b { return ui_st.uv_true }
|
||||
return ui_st.uv_false
|
||||
}
|
||||
|
|
|
|||
|
|
@ -2,6 +2,7 @@
|
|||
# a Ludic component shown as a screen: an instance at the top, keyed by its name
|
||||
function fr_class_nodes(ui_st: mut UiState, cls: UiClass) -> UiNode {
|
||||
pl_begin(ui_st, cls.name)
|
||||
ui_st.uv_on = true
|
||||
lc_begin(ui_st)
|
||||
ui_st.in_build += 1
|
||||
ui_st.ui_view = null
|
||||
|
|
@ -26,6 +27,7 @@ function fr_class_nodes(ui_st: mut UiState, cls: UiClass) -> UiNode {
|
|||
bd_class(ui_st, use, e, cls.name, root.children)
|
||||
in_sweep(ui_st, cls.name)
|
||||
cs_tree(ui_st, root)
|
||||
ui_st.uv_on = false
|
||||
return root
|
||||
}
|
||||
function fr_root(ui_st: mut UiState, key: string, sheet: UiSheet) -> UiNode {
|
||||
|
|
|
|||
|
|
@ -10,7 +10,10 @@ function st_for(ui_st: mut UiState, key: string, t: UiTpl, e: UiEnv) -> Val {
|
|||
if ui_st.st_keys[i] == key { return ui_st.st_vals[i] }
|
||||
}
|
||||
let o = Value.object()
|
||||
let was = ui_st.uv_on # a state's start is kept for good: its values are never pooled
|
||||
ui_st.uv_on = false
|
||||
for i in 0 .. len(decl.keys) { Value.put(o, decl.keys[i], ui_eval(ui_st, decl.vals[i], e)) }
|
||||
ui_st.uv_on = was
|
||||
push(ui_st.st_keys, key)
|
||||
push(ui_st.st_vals, o)
|
||||
return o
|
||||
|
|
|
|||
|
|
@ -1,6 +1,12 @@
|
|||
# units.ludic - lengths in px (the renderer's scale), em (the font size here), rem (the root's),
|
||||
# vw and vh (the screen's), and custom properties: --name set on an element, read by var(--name)
|
||||
# in it and everything inside it
|
||||
# the px of one em for the element being styled (a font size's em is its parent's)
|
||||
function el_em(ui_st: UiState) -> float {
|
||||
if ui_st.el_cur == null { return el_px(ui_st, 16.0) }
|
||||
if ui_st.el_key == "size" and ui_st.el_cur.parent != null { return cs_fs(ui_st, ui_st.el_cur.parent) }
|
||||
return cs_fs(ui_st, ui_st.el_cur)
|
||||
}
|
||||
function el_unit(ui_st: UiState, s: string) -> float {
|
||||
let f = el_num(s)
|
||||
let n = len(s)
|
||||
|
|
|
|||
|
|
@ -1,9 +1,6 @@
|
|||
# values.ludic - arithmetic, comparison, truth and text over a template's values. A sum of two
|
||||
# whole numbers stays whole; anything with a fraction in it is a float; a string in a sum joins.
|
||||
function vl_bool(b: bool) -> Val {
|
||||
if b { return Value.bool(1) }
|
||||
return Value.bool(0)
|
||||
}
|
||||
function vl_bool(ui_st: mut UiState, b: bool) -> Val { return uv_bool(ui_st, b) }
|
||||
function vl_num(v: Val) -> bool { return Value.kind(v) == 1 or Value.kind(v) == 2 or Value.kind(v) == 7 }
|
||||
function vl_truthy(v: Val) -> bool {
|
||||
let k = Value.kind(v)
|
||||
|
|
@ -29,25 +26,25 @@ function vl_text(v: Val) -> string {
|
|||
}
|
||||
return Json.encode(v)
|
||||
}
|
||||
function vl_arith(op: string, a: Val, b: Val) -> Val {
|
||||
function vl_arith(ui_st: mut UiState, op: string, a: Val, b: Val) -> Val {
|
||||
if Value.kind(a) == 1 and Value.kind(b) == 1 {
|
||||
let x = Value.as_int(a)
|
||||
let y = Value.as_int(b)
|
||||
if op == "+" { return Value.int(x + y) }
|
||||
if op == "-" { return Value.int(x - y) }
|
||||
if op == "*" { return Value.int(x * y) }
|
||||
if y == 0 { return Value.int(0) }
|
||||
if op == "/" { return Value.int(x / y) }
|
||||
return Value.int(x % y)
|
||||
if op == "+" { return uv_int(ui_st, x + y) }
|
||||
if op == "-" { return uv_int(ui_st, x - y) }
|
||||
if op == "*" { return uv_int(ui_st, x * y) }
|
||||
if y == 0 { return uv_int(ui_st, 0) }
|
||||
if op == "/" { return uv_int(ui_st, x / y) }
|
||||
return uv_int(ui_st, x % y)
|
||||
}
|
||||
let x = Value.as_float(a)
|
||||
let y = Value.as_float(b)
|
||||
if op == "+" { return Value.float(x + y) }
|
||||
if op == "-" { return Value.float(x - y) }
|
||||
if op == "*" { return Value.float(x * y) }
|
||||
if y == 0.0 { return Value.float(0.0) }
|
||||
if op == "/" { return Value.float(x / y) }
|
||||
return Value.float(x - y * float(int(x / y)))
|
||||
if op == "+" { return uv_float(ui_st, x + y) }
|
||||
if op == "-" { return uv_float(ui_st, x - y) }
|
||||
if op == "*" { return uv_float(ui_st, x * y) }
|
||||
if y == 0.0 { return uv_float(ui_st, 0.0) }
|
||||
if op == "/" { return uv_float(ui_st, x / y) }
|
||||
return uv_float(ui_st, x - y * float(int(x / y)))
|
||||
}
|
||||
function vl_equal(a: Val, b: Val) -> bool {
|
||||
if vl_num(a) and vl_num(b) { return Value.as_float(a) == Value.as_float(b) }
|
||||
|
|
@ -56,17 +53,17 @@ function vl_equal(a: Val, b: Val) -> bool {
|
|||
if Value.kind(a) == 3 { return Value.as_int(a) == Value.as_int(b) }
|
||||
return Value.kind(a) == 0
|
||||
}
|
||||
function vl_binary(op: string, a: Val, b: Val) -> Val {
|
||||
if op == "==" { return vl_bool(vl_equal(a, b)) }
|
||||
if op == "!=" { return vl_bool(not vl_equal(a, b)) }
|
||||
if op == "+" and (Value.kind(a) == 4 or Value.kind(b) == 4) { return Value.str(vl_text(a) + vl_text(b)) }
|
||||
if op == "+" or op == "-" or op == "*" or op == "/" or op == "%" { return vl_arith(op, a, b) }
|
||||
function vl_binary(ui_st: mut UiState, op: string, a: Val, b: Val) -> Val {
|
||||
if op == "==" { return vl_bool(ui_st, vl_equal(a, b)) }
|
||||
if op == "!=" { return vl_bool(ui_st, not vl_equal(a, b)) }
|
||||
if op == "+" and (Value.kind(a) == 4 or Value.kind(b) == 4) { return uv_str(ui_st, vl_text_u(ui_st, a) + vl_text_u(ui_st, b)) }
|
||||
if op == "+" or op == "-" or op == "*" or op == "/" or op == "%" { return vl_arith(ui_st, op, a, b) }
|
||||
let x = Value.as_float(a)
|
||||
let y = Value.as_float(b)
|
||||
if op == "<" { return vl_bool(x < y) }
|
||||
if op == "<=" { return vl_bool(x <= y) }
|
||||
if op == ">" { return vl_bool(x > y) }
|
||||
return vl_bool(x >= y)
|
||||
if op == "<" { return vl_bool(ui_st, x < y) }
|
||||
if op == "<=" { return vl_bool(ui_st, x <= y) }
|
||||
if op == ">" { return vl_bool(ui_st, x > y) }
|
||||
return vl_bool(ui_st, x >= y)
|
||||
}
|
||||
# a value as text, whatever kind it is: a component's string prop given a number (label="{3}") reads
|
||||
# "3", not nothing
|
||||
|
|
|
|||
|
|
@ -480,6 +480,10 @@ namespace Value {
|
|||
alias as_str(value) = value_as_str
|
||||
alias free(value) = value_free
|
||||
alias clear(value) = value_clear
|
||||
alias become(value, tag) = value_become
|
||||
alias be_int(value, n) = value_be_int
|
||||
alias be_float(value, x) = value_be_float
|
||||
alias be_str(value, s) = value_be_str
|
||||
}
|
||||
namespace Json {
|
||||
alias encode(value) = json_encode
|
||||
|
|
|
|||
|
|
@ -105,6 +105,79 @@ function value_set_bool(o: Val, key: pointer, b: bool) -> void {
|
|||
v.num = 0
|
||||
if b { v.num = 1 }
|
||||
}
|
||||
# a list field of an object set in place: the list under `key` is kept, and item i keeps its Value when it
|
||||
# is already that kind (a component's model, filled into the same object every frame)
|
||||
function value_list_fit(o: Val, key: pointer, n: int) -> Val {
|
||||
let l = value_slot(o, key, 5)
|
||||
value_lists(l)
|
||||
while len(l.kids) > n { List.pop(l.kids) }
|
||||
return l
|
||||
}
|
||||
function value_item(l: Val, i: int, tag: int) -> Val {
|
||||
if i < len(l.kids) {
|
||||
if l.kids[i].tag == tag { return l.kids[i] }
|
||||
let v = value_new(tag)
|
||||
l.kids[i] = v
|
||||
return v
|
||||
}
|
||||
let v = value_new(tag)
|
||||
push(l.kids, v)
|
||||
return v
|
||||
}
|
||||
function value_set_ints(o: Val, key: pointer, xs: []int) -> void {
|
||||
let l = value_list_fit(o, key, len(xs))
|
||||
for i in 0 .. len(xs) {
|
||||
let v = value_item(l, i, 1)
|
||||
v.num = xs[i]
|
||||
}
|
||||
}
|
||||
function value_set_floats(o: Val, key: pointer, xs: []float) -> void {
|
||||
let l = value_list_fit(o, key, len(xs))
|
||||
for i in 0 .. len(xs) {
|
||||
let v = value_item(l, i, 7)
|
||||
v.num = float_bits(xs[i])
|
||||
}
|
||||
}
|
||||
function value_set_strs(o: Val, key: pointer, xs: []string) -> void {
|
||||
let l = value_list_fit(o, key, len(xs))
|
||||
for i in 0 .. len(xs) {
|
||||
let v = value_item(l, i, 4)
|
||||
v.txt = xs[i]
|
||||
}
|
||||
}
|
||||
function value_set_bools(o: Val, key: pointer, xs: []bool) -> void {
|
||||
let l = value_list_fit(o, key, len(xs))
|
||||
for i in 0 .. len(xs) {
|
||||
let v = value_item(l, i, 3)
|
||||
v.num = 0
|
||||
if xs[i] { v.num = 1 }
|
||||
}
|
||||
}
|
||||
# a Value turned into a blank one of kind `tag`, its own lists (if it has them) kept and emptied - for
|
||||
# a pool that hands the same records out again (ludic.ui's, frame after frame)
|
||||
function value_become(v: Val, tag: int) -> Val {
|
||||
v.tag = tag
|
||||
v.num = 0
|
||||
v.txt = null
|
||||
if tag == 5 or tag == 6 { value_lists(v) }
|
||||
if v.kids != null { value_clear(v) }
|
||||
return v
|
||||
}
|
||||
function value_be_int(v: Val, n: int) -> Val {
|
||||
value_become(v, 1)
|
||||
v.num = n
|
||||
return v
|
||||
}
|
||||
function value_be_float(v: Val, x: float) -> Val {
|
||||
value_become(v, 7)
|
||||
v.num = float_bits(x)
|
||||
return v
|
||||
}
|
||||
function value_be_str(v: Val, s: pointer) -> Val {
|
||||
value_become(v, 4)
|
||||
v.txt = s
|
||||
return v
|
||||
}
|
||||
# a list or an object emptied in place, its room kept - for one filled again every frame
|
||||
function value_clear(v: Val) -> void {
|
||||
if v.kids == null { return }
|
||||
|
|
|
|||
|
|
@ -46,6 +46,10 @@ function cm_set(c: int, ty: pointer, k: pointer, x: pointer, head: Node) -> poin
|
|||
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` }
|
||||
return ` value_put(o, "{k}", {cm_to(c, ty, x, head)})\n`
|
||||
}
|
||||
function cm_to(c: int, ty: pointer, x: pointer, head: Node) -> pointer {
|
||||
|
|
|
|||
44538
selfhost/ludicc.seed.ll
44538
selfhost/ludicc.seed.ll
File diff suppressed because it is too large
Load diff
File diff suppressed because it is too large
Load diff
Loading…
Add table
Add a link
Reference in a new issue