Xml parse gives back what no node keeps: a closing tag's name, and the text runs and joins an element's text leaves behind (a run a #text node shares is kept); ludic.ui frees an attribute's value once parsed (every reader copies what it keeps) and act_parse's reader
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
parent
7b20f305ff
commit
765d800842
3 changed files with 27 additions and 5 deletions
|
|
@ -38,6 +38,7 @@ function act_parse(src: string, err: []string) -> UiAct {
|
||||||
rd_ws(r)
|
rd_ws(r)
|
||||||
if r.err == "" and r.i < len(r.s) { rd_fail(r, "';' or the end") }
|
if r.err == "" and r.i < len(r.s) { rd_fail(r, "';' or the end") }
|
||||||
if r.err != "" { push(err, r.err) }
|
if r.err != "" { push(err, r.err) }
|
||||||
|
unsafe { free(r) } # the reader only: names and expressions are copied out
|
||||||
return a
|
return a
|
||||||
}
|
}
|
||||||
# `set` or `emit` as the action's first word - not a function of that name, as in `set(4)`
|
# `set` or `emit` as the action's first word - not a function of that name, as in `set(4)`
|
||||||
|
|
|
||||||
|
|
@ -40,6 +40,9 @@ function tpl_read(ui_st: mut UiState, x: Xml, file: string) -> UiTpl {
|
||||||
push(t.keys, k)
|
push(t.keys, k)
|
||||||
push(t.vals, tpl_value(v, err))
|
push(t.vals, tpl_value(v, err))
|
||||||
}
|
}
|
||||||
|
# the value is parsed (an action, a style, an expression), each copying what it keeps; an
|
||||||
|
# empty one is the reader's own literal
|
||||||
|
if len(v) > 0 { unsafe { free(v) } }
|
||||||
}
|
}
|
||||||
if tp_mixed(x) { tp_read_mixed(ui_st, t, x, file, err) } else {
|
if tp_mixed(x) { tp_read_mixed(ui_st, t, x, file, err) } else {
|
||||||
for i in 0 .. Xml.child_count(x) { push(t.kids, tpl_read(ui_st, Xml.child(x, i), file)) }
|
for i in 0 .. Xml.child_count(x) { push(t.kids, tpl_read(ui_st, Xml.child(x, i), file)) }
|
||||||
|
|
|
||||||
|
|
@ -23,6 +23,7 @@ property Xml {
|
||||||
kids: []Xml # child elements, in document order
|
kids: []Xml # child elements, in document order
|
||||||
at: int = 0 # where its '<' is in the text, for an error that names a line
|
at: int = 0 # where its '<' is in the text, for an error that names a line
|
||||||
mixed: []Xml # its elements and its runs of text ("#text"), in document order
|
mixed: []Xml # its elements and its runs of text ("#text"), in document order
|
||||||
|
joined: bool = false # `text` is a join made here (not a run a #text node shares): free to replace
|
||||||
}
|
}
|
||||||
|
|
||||||
function xml_new(tag: pointer) -> Xml {
|
function xml_new(tag: pointer) -> Xml {
|
||||||
|
|
@ -267,6 +268,7 @@ function xp_element(rt_xml_st: mut RtXmlState, p: XP) -> Xml {
|
||||||
if p.i + 1 < p.n and p.s[p.i + 1] == '/' { # '</' close
|
if p.i + 1 < p.n and p.s[p.i + 1] == '/' { # '</' close
|
||||||
p.i += 2
|
p.i += 2
|
||||||
let cn = xp_name(p)
|
let cn = xp_name(p)
|
||||||
|
free(cn) # the closing name is only read past
|
||||||
while p.i < p.n and p.s[p.i] != '>' { p.i += 1 }
|
while p.i < p.n and p.s[p.i] != '>' { p.i += 1 }
|
||||||
p.i += 1 # skip '>'
|
p.i += 1 # skip '>'
|
||||||
return node
|
return node
|
||||||
|
|
@ -276,7 +278,7 @@ function xp_element(rt_xml_st: mut RtXmlState, p: XP) -> Xml {
|
||||||
p.i += 9 # past "<![CDATA["
|
p.i += 9 # past "<![CDATA["
|
||||||
let start = p.i
|
let start = p.i
|
||||||
while p.i + 2 < p.n and not (p.s[p.i] == ']' and p.s[p.i + 1] == ']' and p.s[p.i + 2] == '>') { p.i += 1 }
|
while p.i + 2 < p.n and not (p.s[p.i] == ']' and p.s[p.i + 1] == ']' and p.s[p.i + 2] == '>') { p.i += 1 }
|
||||||
node.text += p.s[start..p.i]
|
xp_text_add(node, p.s[start..p.i], true)
|
||||||
p.i += 3 # past "]]>"
|
p.i += 3 # past "]]>"
|
||||||
} else {
|
} else {
|
||||||
if xp_skip_misc(p) { } # comment / PI inside content
|
if xp_skip_misc(p) { } # comment / PI inside content
|
||||||
|
|
@ -291,15 +293,15 @@ function xp_element(rt_xml_st: mut RtXmlState, p: XP) -> Xml {
|
||||||
let start = p.i
|
let start = p.i
|
||||||
while p.i < p.n and p.s[p.i] != '<' { p.i += 1 }
|
while p.i < p.n and p.s[p.i] != '<' { p.i += 1 }
|
||||||
let run = xml_unescape(rt_xml_st, p.s[start..p.i])
|
let run = xml_unescape(rt_xml_st, p.s[start..p.i])
|
||||||
node.text += run
|
let kept = xp_text_run(node, run)
|
||||||
xp_text_run(node, run)
|
xp_text_add(node, run, not kept)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return node
|
return node
|
||||||
}
|
}
|
||||||
|
|
||||||
# a run of text among an element's children, kept in order unless it is only white space
|
# a run of text among an element's children, kept in order unless it is only white space
|
||||||
function xp_text_run(node: Xml, run: string) -> void {
|
function xp_text_run(node: Xml, run: string) -> bool {
|
||||||
var blank = true
|
var blank = true
|
||||||
var i = 0
|
var i = 0
|
||||||
while i < len(run) {
|
while i < len(run) {
|
||||||
|
|
@ -307,10 +309,26 @@ function xp_text_run(node: Xml, run: string) -> void {
|
||||||
if not (c == ' ' or c == '\t' or c == '\n' or c == '\r') { blank = false }
|
if not (c == ' ' or c == '\t' or c == '\n' or c == '\r') { blank = false }
|
||||||
i += 1
|
i += 1
|
||||||
}
|
}
|
||||||
if blank { return }
|
if blank { return false }
|
||||||
let t = xml_new("#text")
|
let t = xml_new("#text")
|
||||||
t.text = run
|
t.text = run
|
||||||
push(node.mixed, t)
|
push(node.mixed, t)
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
# a run joined onto the element's text. What the join leaves behind is given back: the text it
|
||||||
|
# replaces when that was a join of its own, and the run when nothing else keeps it (`mine`)
|
||||||
|
function xp_text_add(node: Xml, run: string, mine: bool) -> void {
|
||||||
|
if len(node.text) == 0 {
|
||||||
|
node.text = run # its first text is the run itself
|
||||||
|
node.joined = mine
|
||||||
|
return
|
||||||
|
}
|
||||||
|
let old = node.text
|
||||||
|
node.text = old + run
|
||||||
|
if node.joined { free(old) }
|
||||||
|
if mine { free(run) }
|
||||||
|
node.joined = true
|
||||||
}
|
}
|
||||||
|
|
||||||
# parse a whole document -> its root element (or the synthetic empty node).
|
# parse a whole document -> its root element (or the synthetic empty node).
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue