τau /

encoding/xml

module
xml = import("encoding/xml")

xml - reading and writing XML.

Parse gives back the root element, and an element is a plain object with four fields: Name, Attr, Children and Text.

xml = import("encoding/xml")
doc = xml.Parse(`<catalog n="2">
	<book id="a"><title>Tau</title></book>
	<book id="b"><title>More tau</title></book>
</catalog>`)
println(doc.Name, doc.Attr["n"])
for i = 0; i < len(doc.Children); i++ {
	b = doc.Children[i]
	println(xml.Get(b, "id"), xml.Child(b, "title").Text)
}

A node holds no functions of its own, and that is on purpose: Child, All and Get are module functions taking a node. A document of any size is tens of thousands of elements, and three closures on each of them would cost more than the document does.

Names are kept as they are written, prefix and all: an attribute called c:identifier is looked up under that name. Resolving prefixes to the URI their xmlns declared would mean carrying a scope through the whole parse, and a reader that wants it can read the xmlns attributes, which are there like any others.

Text is the character data directly inside an element, as written. Nothing is trimmed: whitespace between elements is data as far as this is concerned, and strings.TrimSpace is there for whoever knows it is not.

Text is all of an element's character data put together, so where text and elements are interleaved - a paragraph with emphasis in the middle of it - the order between the two is not kept, and writing such a node back out puts its text first. XML that carries data rather than prose does not interleave them, and that is the XML this is for.

What it does not do: validation, DTDs, entities of your own. The five named ones are understood, and so are &#nn; and &#xhh;.

How it goes about it, because it decides how fast it is:

- The scan runs over the bytes of the source and not its characters.

Indexing a string gives back a string, which is an allocation for every
character looked at; indexing bytes gives an integer and allocates
nothing.

- A run of text with no entity in it is handed back as one slice of the

source, which shares the buffer rather than copying it.

- The hot loops read their state into locals first. Reading a field of an

object costs a hash and a walk, and doing that per character is most of
the time a parser written the obvious way spends.

- Where and what line an error is on is worked out when there is an error,

never before. Counting lines while scanning costs every document to
serve the ones that fail.

Node = fn(name)source

Node builds an element. Exported because a program that writes XML rather than reading it has to make one.

Name = namevaluesource

Attr = { }valuesource

Children = []valuesource

Text = ""valuesource

Parser = fn(src)source

Parse = fn(src)source

Parse reads a document and gives back its root element.

Child = fn(node, name)source

Child is the first child element called name, and null when there is none.

All = fn(node, name)source

All is every child element called name, in the order they were written.

Get = fn(node, name)source

Get is the value of an attribute, and "" when it is not there. An attribute written empty and one that is absent are told apart by node.Attr.

Escape = fn(s)source

Escape makes a string safe between tags and inside an attribute value. A string with nothing to escape comes back as it was, without a copy.

String = fn(node)source

String writes a node back out. Attributes come out in the order keys gives them and not the order they were written: XML says an attribute list is a set, and this holds one.