Living in a structural world is nice, but sometimes we need to convert structural content to/from plain text representation, e.g. for file I/O and system clipboard. Neomacs provides some low-level hooks and a table-driven parser framework for converting to/from plain text.
Interface to parsers:
*dom-output*read-dom (stream &optional recursive-p)*syntax-table*.read-dom, for example some
function that is bound in *syntax-table*.*dom-output* is bound, append the results as children of *dom-output*. Otherwise, return the results as a list of DOM nodes.read-dom-from-file (file)*syntax-table*.*dom-output* is bound, append the results as children of *dom-output*. Otherwise, return the results as a list of DOM nodes.The following hooks implement actual (de)-serialization:
read-dom-aux (buffer stream)*dom-output*. Reading can stop at whatever boundary that makes
sense, i.e. multiple children can be built and appended.write-dom-aux (buffer node stream)read-dom-aux.Note that parsers use append-child to append children to *dom-output* directly, i.e. they use low-level DOM edits instead of editing-primitives. It is therefore important to never bind *dom-output* to live DOM nodes in any buffer, which would result in corrupted state (inconsistency between Lisp-side and renderer-side DOM). Typically, one wants to call the parser to build DOM nodes outside any buffer, then use insert-nodes to insert them into some buffer if needed.
Neomacs provides a parser framework driven by syntax-tables, which binds characters to functions that consume some (more) characters and builds DOM nodes. The parser reads a character from input stream, looks it up in *syntax-table*, and calls the function with two arguments: the stream and the character. If the character is not bound to any function, the special value t is looked up in *syntax-table* next, and the function (if any) bound to t is used as default. If t does not have binding either, an error is signaled.
get-syntax-table (char table)set-syntax-range (table beg end symbol)make-syntax-table (&rest bindings)*syntax-table*It's quite common to read consecutive characters of the same "category" as a string and process as a whole, possibly building a DOM element or inserting as text. The following functions help with such cases:
read-constituent (stream symbol escape-chars)*syntax-table* is
encountered, with one exception: a character in the list ESCAPE-CHARS
makes the next character accepted unconditionally.append-text (parent string)last-child, concat into the
text node instead.read-newline (stream c)*dom-output*.read-text (stream c)*dom-output*.read-ignore (stream c)