diff --git a/packages/ludic.ui/class.ludic b/packages/ludic.ui/class.ludic index c5db0c95..2294a31f 100644 --- a/packages/ludic.ui/class.ludic +++ b/packages/ludic.ui/class.ludic @@ -40,7 +40,9 @@ function uc_load(ui_st: mut UiState, k: UiClass) -> void { if k.lss != "" { lss_text(ui_st, f, k.lss) } f.sheet = sheet_of(f) tp_lines(ui_st, k.xml) - let root = tpl_read(ui_st, Xml.parse(k.xml), k.xml_path) + let doc = Xml.parse(k.xml) + let root = tpl_read(ui_st, doc, k.xml_path) + Xml.free(doc) # the template keeps the tree's strings, never its nodes k.sheet = f.sheet lib_link(ui_st, f, root) k.root = root diff --git a/packages/ludic.ui/expr_parse.ludic b/packages/ludic.ui/expr_parse.ludic index f14f6f12..1a574eb3 100644 --- a/packages/ludic.ui/expr_parse.ludic +++ b/packages/ludic.ui/expr_parse.ludic @@ -48,10 +48,13 @@ function px_bin(op: string, a: UiExpr, b: UiExpr) -> UiExpr { } function px_cmp(r: UiRd) -> UiExpr { let a = px_sum(r) - let ops = ["==", "!=", "<=", ">=", "<", ">"] - for k in 0 .. len(ops) { - if rd_op(r, ops[k]) { return px_bin(ops[k], a, px_sum(r)) } - } + # literals, longest first: a list of them was made on every call + if rd_op(r, "==") { return px_bin("==", a, px_sum(r)) } + if rd_op(r, "!=") { return px_bin("!=", a, px_sum(r)) } + if rd_op(r, "<=") { return px_bin("<=", a, px_sum(r)) } + if rd_op(r, ">=") { return px_bin(">=", a, px_sum(r)) } + if rd_op(r, "<") { return px_bin("<", a, px_sum(r)) } + if rd_op(r, ">") { return px_bin(">", a, px_sum(r)) } return a } function px_sum(r: UiRd) -> UiExpr { diff --git a/packages/ludic.ui/expr_prim.ludic b/packages/ludic.ui/expr_prim.ludic index c95a36a3..41e05067 100644 --- a/packages/ludic.ui/expr_prim.ludic +++ b/packages/ludic.ui/expr_prim.ludic @@ -62,6 +62,7 @@ function ex_parse(src: string, err: []string) -> UiExpr { rd_ws(r) if r.err == "" and r.i < len(r.s) { rd_fail(r, "the end") } if r.err != "" { push(err, r.err) } + unsafe { free(r) } # the reader only: what the expression keeps is copied out return e } # text with {holes}: a literal when it has none, the expression itself when it is one hole and diff --git a/packages/ludic.ui/lib.ludic b/packages/ludic.ui/lib.ludic index 1588fc0c..c05f4302 100644 --- a/packages/ludic.ui/lib.ludic +++ b/packages/ludic.ui/lib.ludic @@ -39,7 +39,9 @@ function lib_read(ui_st: mut UiState, src: string, path: string) -> UiFile { let f = lib_new(ui_st, path) f.src = src tp_lines(ui_st, src) - let root = tpl_read(ui_st, Xml.parse(src), path) + let doc = Xml.parse(src) + let root = tpl_read(ui_st, doc, path) + Xml.free(doc) # the template keeps the tree's strings, never its nodes var tops = root.kids if root.tag != "ui" { tops = [root] } for i in 0 .. len(tops) { lib_top(ui_st, f, tops[i]) } diff --git a/packages/ludic.ui/select.ludic b/packages/ludic.ui/select.ludic index 63d44029..7672f296 100644 --- a/packages/ludic.ui/select.ludic +++ b/packages/ludic.ui/select.ludic @@ -51,6 +51,8 @@ function sel_parse(src: string, err: []string) -> UiSel { comb = 0 } if r.err != "" { push(err, r.err) } + unsafe { free(r) } # the reader only: every name read from it is a copy + @alloc_ok("only when a template is wrong: a broken screen, never a working one") if cur == null { push(err, `"{src}" selects nothing`) } return cur } diff --git a/packages/ludic.ui/style_rule.ludic b/packages/ludic.ui/style_rule.ludic index 4243927c..69b07d20 100644 --- a/packages/ludic.ui/style_rule.ludic +++ b/packages/ludic.ui/style_rule.ludic @@ -10,7 +10,10 @@ function lss_rule(f: UiFile, sels: string, body: string, err: []string) -> void free(each[i]) } } - unsafe { free(each) } + unsafe { + free(each) + free(decls) # the holder only: each rule keeps its keys and values lists + } } function lss_rule_add(f: UiFile, decls: UiRule, sel: string, err: []string) -> void { let r = new UiRule diff --git a/runtime/native/namespaces.ludic b/runtime/native/namespaces.ludic index 60d17447..2b0fc065 100644 --- a/runtime/native/namespaces.ludic +++ b/runtime/native/namespaces.ludic @@ -495,6 +495,7 @@ namespace Json { } namespace Xml { alias parse(text) = xml_parse + alias free(node) = xml_free # the tree's records and lists; its strings are kept alias tag(node) = xml_tag alias text(node) = xml_text alias attr(node, key) = xml_attr diff --git a/runtime/native/xml.ludic b/runtime/native/xml.ludic index f82ec980..b59400d4 100644 --- a/runtime/native/xml.ludic +++ b/runtime/native/xml.ludic @@ -322,8 +322,26 @@ function xml_parse(rt_xml_st: mut RtXmlState, s: pointer) -> Xml { if p.i >= p.n { break } if p.s[p.i] == '<' { # '<' if xp_skip_misc(p) { } # prolog / comment / doctype - else { return xp_element(rt_xml_st, p) } + else { + let root = xp_element(rt_xml_st, p) + free(p) + return root + } } else { p.i += 1 } } + free(p) return xml_new("") } + +# the tree's records and lists given back once a reader has built what it keeps from it; its +# strings (tags, text, names, values) are not, since what was built may hold them. Every element +# is in its parent's `mixed`, with the runs of text +function xml_free(n: Xml) -> void { + if n == null { return } + for i in 0 .. len(n.mixed) { xml_free(n.mixed[i]) } + free(n.akeys) + free(n.avals) + free(n.kids) + free(n.mixed) + free(n) +}