From 179dd60536f7a2444749335d44a62b03cf41b49c Mon Sep 17 00:00:00 2001 From: Orkuncakilkaya Date: Mon, 28 Sep 2026 20:06:25 +0300 Subject: [PATCH] ludic.ui: parsing at load gives back what it does not keep - tpl_words written once into bytes of its own (a string a character before), a stylesheet's stripped text, its error list and each reader freed once read, ex_text's joining node freed when it collapses to one piece or none Co-Authored-By: Claude Opus 5.5 --- packages/ludic.ui/expr_prim.ludic | 13 ++++++++++--- packages/ludic.ui/style.ludic | 6 +++++- packages/ludic.ui/tpl.ludic | 21 ++++++++++++++++----- 3 files changed, 31 insertions(+), 9 deletions(-) diff --git a/packages/ludic.ui/expr_prim.ludic b/packages/ludic.ui/expr_prim.ludic index 38350312..c95a36a3 100644 --- a/packages/ludic.ui/expr_prim.ludic +++ b/packages/ludic.ui/expr_prim.ludic @@ -82,7 +82,14 @@ function ex_text(src: string, err: []string) -> UiExpr { i += 1 } if at < len(src) { push(e.args, ex_lit(Value.str(src[at..len(src)]))) } - if len(e.args) == 0 { return ex_lit(Value.str("")) } - if len(e.args) == 1 { return e.args[0] } - return e + if len(e.args) > 1 { return e } + # one piece or none: the joining node is not kept, so it is given back + var one: UiExpr = null + if len(e.args) == 1 { one = e.args[0] } + unsafe { + free(e.args) + free(e) + } + if one == null { return ex_lit(Value.str("")) } + return one } diff --git a/packages/ludic.ui/style.ludic b/packages/ludic.ui/style.ludic index 4a654a82..88c5a824 100644 --- a/packages/ludic.ui/style.ludic +++ b/packages/ludic.ui/style.ludic @@ -30,9 +30,12 @@ function lss_file(ui_st: mut UiState, src: string, path: string) -> UiFile { @alloc_ok("a stylesheet being read: once per component class (uc_load)") function lss_text(ui_st: mut UiState, f: UiFile, src: string) -> void { let err = new []string - lss_block(ui_st, f, lss_strip(src), null, err) + let body = lss_strip(src) # always a copy of its own (lss_strip joins its pieces) + lss_block(ui_st, f, body, null, err) + unsafe { free(body) } # every rule, selector and value read from it is a copy @alloc_ok("only when a template is wrong: a broken screen, never a working one") for i in 0 .. len(err) { ui_err(ui_st, `{f.path}: