%D \module
%D [ file=xtag-map,
%D version=2000.12.20,
%D title=\CONTEXT\ XML Macros,
%D subtitle=Remapping,
%D author=Hans Hagen,
%D date=\currentdate,
%D copyright={PRAGMA / Hans Hagen \& Ton Otten}]
%C
%C This module is part of the \CONTEXT\ macro||package and is
%C therefore copyrighted by \PRAGMA. See mreadme.pdf for
%C details.
%D There is a more versatile mapper available in \type {xtag-rem.tex}!
%D We also need something that lets content as-is, like for
%D instance XML embedded in a chemical caption.
\writestatus{loading}{ConTeXt XML Macros / Remapping}
%D A fundamental characteristic of \TEX\ is that much
%D processing depends on picking up one or more arguments and
%D acting upon them. In \type {xtag-ini} we have implemented
%D the normal (high) level interface between \XML\ and
%D \CONTEXT, and there we already saw some ways to pick up an
%D element as argument.
%D
%D In this module we will implement a preprocessor. An element
%D that feeds its content to the preprocessor, becomes a token
%D list consisting of \TEX\ macros, which in turn may expand to
%D their meanings.
%D
%D This module is only tested with \ETEX. In principle we can
%D make it work with good old \TEX, but we see no objection
%D against using \ETEX, especially since it's part of every
%D grown up \TEX\ distribution.
\unprotect
\let\normalparseXMLescape \parseXMLescape
\let\normalparseXMLelement\parseXMLelement
\let\normalparseXMLprocess\parseXMLprocess
\let\normaldoXMLelement \doXMLelement
\let\normaldoXMLentity \doXMLentity
\def\setnormalXMLhandler
{\let\doXMLelement \normaldoXMLelement
\let\parseXMLelement\normalparseXMLelement
\let\parseXMLescape \normalparseXMLescape
\let\parseXMLprocess\normalparseXMLprocess
\let\doXMLentity \normaldoXMLentity}
%D A careful reader will notice that we do a full expansion of
%D the content of the element, although commands that are
%D protected will stay untouched. In this stage we also
%D collect key|/|value pairs and pass them onto the \TEX\
%D macros if needed. Again, we need a fully expandable parser
%D to handle this, which make the core macros slightly
%D unreadable.
%D
%D The interface presented here evolved from an older module,
%D written on top of \type {m-sgml}, that could take care of
%D \MATHML\ (version 1). The implementation here is more
%D advanced in the sense that it permits all kind of parsers.
\def\findendofXMLelement#1% space after 0/1 prevents auto \relax
{\if#1>0 \else
\if#1/1 \endofXMLelementE\else
\if#1"\endofXMLelementD \else
\if#1'\endofXMLelementS \else
\endofXMLelementN \fi\fi\fi\fi}
\def\endofXMLelementE#1\fi\fi\fi\fi#2>{\fi\fi}
\def\endofXMLelementD#1\fi\fi\fi\fi#2"{\fi\fi\fi \findendofXMLelement}
\def\endofXMLelementS#1\fi\fi\fi\fi#2'{\fi\fi\fi\fi\findendofXMLelement}
\def\endofXMLelementN \fi\fi\fi\fi {\fi\fi\fi\fi\findendofXMLelement}
% not faster
%
% \def\findendofXMLelement#1%
% {\csname**\if#1>>\else\if#1//\else\if#1""\else\if#1''\else.\fi\fi\fi\fi\endcsname}
%
% \def\findendofXMLelement#1%
% {\csname**\ifcsname**#1\endcsname#1\else.\fi\endcsname}
%
% \setvalue{**>}{0 }
% \setvalue{**/}#1>{1 }
% \setvalue{**"}#1"{\findendofXMLelement}
% \setvalue{**'}#1'{\findendofXMLelement}
% \letvalue{**.}\findendofXMLelement
\newif\ifremapXMLunknown
%D We need three steps to avoid namespace: tag since comment
%D and processing instructions don't have a namespace. The
%D first step distinguishes between comment, processing
%D instructions and elements. The second step (which is
%D defined in the main mapping macro) either or not grabs the
%D namespace. We may extend this model later to a more
%D versatile one, using remapping.
%D Parsing escapes is done by specific macros. For the
%D moment we assume that the sequence ends with an \type {>}
%D (which is definietly not the case for \type {CDATA}).
\long\def\remapXMLescape#1#2>{}
%D Processing instructions are remapped and only certain
%D cases are handled.
\long\def\remapXMLprocess#1#2{\xmlp{procins/}{X}{#1}{#2}}
%D This one is more efficient (although no one will notice
%D this since this macro is used seldom).
\long\def\remapXMLprocess{\xmlp{procins/}{X}}
%D Element need a bit more work; \type {#4} consumes spaces.
% \def\remapXMLunknownONE#1#2% name args
% {\ifremapXMLunknown\remapXMLone{\s!unknown}{#1 #2}\fi}
%
% \def\remapXMLunknownTWO#1#2% name args
% {\ifremapXMLunknown\remapXMLtwo{\s!unknown}{#1 #2}\fi}
%
% \def\remapXMLunknownTHREE#1#2% name args
% {\ifremapXMLunknown\remapXMLthree{\s!unknown/}{#1 #2}\fi}
%
% \long\def\remapXMLelement#1#2 #3>#4% todo: we need to get rid of the end /
% {\ifcase\findendofXMLelement#2#3>%
% \if#1/%
% \expandafter\ifx\csname\@@XML\@@XMLmapping:#2:M\endcsname\relax
% \remapXMLunknownONE{#2}{}%
% \else
% \remapXMLone{#2}{}%
% \fi
% \else
% \expandafter\ifx\csname\@@XML\@@XMLmapping:#1#2:M\endcsname\relax
% \remapXMLunknownTWO{#1#2}{#3}%
% \else
% \remapXMLtwo{#1#2}{#3}%
% \fi
% \fi
% \else
% \expandafter\ifx\csname\@@XML\@@XMLmapping:#1#2:M\endcsname\relax
% \expandafter\ifx\csname\@@XML\@@XMLmapping:#1#2/:M\endcsname\relax
% \remapXMLunknownTHREE{#1#2}{#3}%
% \else
% \remapXMLthree{#1#2/}{#3}%
% \fi
% \else
% \remapXMLthree{#1#2}{#3}%
% \fi
% \fi#4}
\def\remapXMLthreeempty#1%
{\remapXMLthree{#1/}}
\def\remapXMLunknownONE#1#2% name args
{\ifremapXMLunknown\remapXMLone\s!unknown{#1 #2}\fi}
\def\remapXMLunknownTWO#1#2% name args
{\ifremapXMLunknown\remapXMLtwo\s!unknown{#1 #2}\fi}
\def\remapXMLunknownTHREE#1#2% name args
{\ifremapXMLunknown\remapXMLthreeempty\s!unknown{#1 #2}\fi}
\long\def\remapXMLelement#1#2 #3>#4% todo: we need to get rid of the end /
{\ifcase\findendofXMLelement#2#3>%
\if#1/%
\expandafter\ifx\csname\@@XML\@@XMLmapping:#2:M\endcsname\relax
\@EA\remapXMLunknownONE
\else
\@EA\remapXMLone
\fi{#2}\empty
\else
\expandafter\ifx\csname\@@XML\@@XMLmapping:#1#2:M\endcsname\relax
\@EA\remapXMLunknownTWO
\else
\@EA\remapXMLtwo
\fi{#1#2}{#3}%
\fi
\else
\expandafter\ifx\csname\@@XML\@@XMLmapping:#1#2:M\endcsname\relax
\expandafter\ifx\csname\@@XML\@@XMLmapping:#1#2/:M\endcsname\relax
\@EAEAEA\remapXMLunknownTHREE
\else
\@EAEAEA\remapXMLthreeempty
\fi
\else
\@EA\remapXMLthree
\fi{#1#2}{#3}%
\fi#4}
\unexpanded\def\xmlr#1#2{\csname\@@XML\@@XMLmapping:#1:#2\endcsname}
\unexpanded\def\xmlp#1#2{\csname\@@XML :#1:#2\endcsname}
\def \expandedxmlr#1#2{\csname\@@XML\@@XMLmapping:#1:#2\endcsname}
\def\expandXMLremapping{\let\xmlr\expandedxmlr}
\def\@@XMLremap{XMLremap}
\def\remapXMLone#1#2%
{\ifcase\csname\@@XMLremap\csname\@@XML\@@XMLmapping:#1:M\endcsname\endcsname
\or % GCPA
\XMLeg\XMLeg
\or % GCP-
\XMLeg
\or % GC-A
\XMLeg\XMLeg
\or % GC--
\XMLeg
\or % -CPA
\XMLeg
\or % -CP-
%
\or % -C-A
\XMLeg
\or % -C--
%
\or % G---
\XMLeg
\or % GLR-
\xmlr{#1}{R}\XMLeg
\or % -LR-
\xmlr{#1}{R}%
\fi}
\def\remapXMLtwo#1#2%
{\ifcase\csname\@@XMLremap\csname\@@XML\@@XMLmapping:#1:M\endcsname\endcsname
\or % GCPA
\XMLbg\xmlr{#1}{X}{#2}\XMLbg
\or % GCP-
\XMLbg\xmlr{#1}{X}{#2}%
\or % GC-A
\XMLbg\xmlr{#1}{X}\XMLbg
\or % GC--
\XMLbg\xmlr{#1}{X}%
\or % -CPA
\xmlr{#1}{X}{#2}\XMLbg
\or % -CP-
\xmlr{#1}{X}{#2}%
\or % -C-A
\xmlr{#1}{X}\XMLbg
\or % -C--
\xmlr{#1}{X}%
\or % G---
\XMLbg
\or % GLR-
\XMLbg\xmlr{#1}{L}%
\or % -LR-
\xmlr{#1}{L}%
\fi}
\def\remapXMLthree#1#2%
{\ifcase\csname\@@XMLremap\csname\@@XML\@@XMLmapping:#1:M\endcsname\endcsname
\or % GCPA
\XMLbg\xmlr{#1}{X}{#2}\XMLbg\XMLeg\XMLeg
\or % GCP-
\XMLbg\xmlr{#1}{X}{#2}\XMLeg
\or % GC-A
\XMLbg\xmlr{#1}{X}\XMLbg\XMLeg\XMLeg
\or % GC--
\XMLbg\xmlr{#1}{X}\XMLeg
\or % -CPA
\xmlr{#1}{X}{#2}\XMLbg\XMLeg
\or % -CP-
\xmlr{#1}{X}{#2}%
\or % -C-A
\xmlr{#1}{X}\XMLbg\XMLeg
\or % -C--
\xmlr{#1}{X}%
\or % G---
\XMLbg\XMLeg
\fi}
\scratchtoks\@EA{\string{} \edef\XMLbg{\the\scratchtoks}
\scratchtoks\@EA{\string}} \edef\XMLeg{\the\scratchtoks}
\chardef\XMLremapGCPA = 1 % {\command {arg} { } }
\chardef\XMLremapGCP = 2 % {\command {arg} } %
\chardef\XMLremapGCA = 3 % {\command { } }
\chardef\XMLremapGC = 4 % {\command } %
\chardef\XMLremapCPA = 5 % \command {arg} { }
\chardef\XMLremapCP = 6 % \command {arg} %
\chardef\XMLremapCA = 7 % \command { }
\chardef\XMLremapC = 8 % \command %
\chardef\XMLremapG = 9 % { }
\chardef\XMLremapGLR = 10 % { \bcom \ecom }
\chardef\XMLremapLR = 11 % \bcom \ecom
%D The remapping is controlled by only a few definition
%D macros, that both deal with elements. We distinguish
%D between normal and empty elements.
%D
%D \starttyping
%D \remapXMLsequence [name] [result map] \unexpandablecommand
%D \remapXMLsequence [name] [result map] \unexpandablecommand
%D \stoptyping
%D
%D The \MATHML\ module demonstrates how these can be used.
%D The element is converted into a sequence with one or more
%D of the following components.
%D
%D \starttyping
%D { \command {parameters} {argument} }
%D \stoptyping
%D
%D The following combinations are supported.
%D
%D \starttabulate[|c|c|c|c|c|]
%D \NC GCPA \NC grouped \NC command \NC parameters \NC argument \NC \NR
%D \NC GCP \NC grouped \NC command \NC parameters \NC \NC \NR
%D \NC GCA \NC grouped \NC command \NC \NC argument \NC \NR
%D \NC GC \NC grouped \NC command \NC \NC \NC \NR
%D \NC CPA \NC \NC command \NC parameters \NC argument \NC \NR
%D \NC CP \NC \NC command \NC parameters \NC \NC \NR
%D \NC CA \NC \NC command \NC \NC argument \NC \NR
%D \NC C \NC \NC command \NC \NC \NC \NR
%D \NC G \NC grouped \NC \NC \NC \NC \NR
%D \stoptabulate
%D
%D Empty elements (singular ones) never get an argument,
%D which makes sense, since they have at most parameters.
\def\remapXMLsequence{\doquadrupleargument\doremapXML []}
\def\remapXMLsingular{\doquadrupleargument\doremapXML[/]}
\def\doremapXML[#1][#2][#3][#4]%
{\iffourthargument
\def\next{\dodoremapXML[#2][#1][#3][#4]}%
\else
\def\next{\dodoremapXML[\@@XMLmapping][#1][#2][#3]}%
\fi
\next}
%\def\dodoremapXML[#1][#2][#3][#4]% class / name pattern
% {\doifinstringelse{LR}{#4}
% {\let\next\doremapXMLtwo}
% {\let\next\doremapXMLone}%
% \next[#1][#2][#3][#4]}%
\def\dodoremapXML[#1][#2][#3][#4]% class / name pattern
{\doifinstringelse{LR}{#4}\doremapXMLtwo\doremapXMLone[#1][#2][#3][#4]}
\def\doremapXMLone[#1][#2][#3][#4]#5%
{\setvalue{\@@XML#1:#3#2:M}{#4}%
\setvalue{\@@XML#1:#3#2:X}{#5}}
\def\doremapXMLtwo[#1][#2][#3][#4]#5#6%
{\setvalue{\@@XML#1:#3:M}{#4}%
\setvalue{\@@XML#1:#3:L}{#5}%
\setvalue{\@@XML#1:#3:R}{#6}}
\let\dowithentity\empty
%D We handle processing instructions and unknown elements with:
\remapXMLsingular [procins] [CPA] \normalparseXMLprocess
\remapXMLsingular [\s!unknown] [CPA] \doXMLunknownSI
\remapXMLsequence [\s!unknown] [CPA] \doXMLunknownSE
\def\doXMLunknownSI#1#2{{\tttf[#1 #2]}}
\def\doXMLunknownSE#1#2{{\tttf[#1 #2]}}
%D In a similar way, we can remap entities.
\def\remapXMLentity#1;#2%
{\doremapXMLentity{#1}#2}%
\def\doremapXMLentity
{\xmlrent}
% \unexpanded\def\xmlrent#1%
% {\getXMLentity{#1}}
%
% replaced by:
\unexpanded\def\xmlrent#1%
{\doXMLentity#1;}
%D The remapping is taken care of by the following macro,
%D which takes three arguments.
%D
%D \starttyping
%D \XMLremapdata{before}{after}{content}
%D \stoptyping
%D
%D After the remapping, the content is executed (expanded)
%D under the normal \TEX\ catcode regime. The intermediate
%D result can be traced by turning on the following switch.
\newif\iftraceXMLremapping
\newtoks \everyXMLremapping
\appendtoks
\defineXMLentity[tex-hash]{\letterhash}%
\defineXMLentity[tex-bar]{\myspecialnormalvert}%
\to \everyXMLremapping
\def\setnormalXMLentities% will change ! ! ! ! !
{\defineXMLentity[tex-hash]\letterhash
\defineXMLentity[tex-dollar]\letterdollar
\defineXMLentity[tex-percent]\letterpercent
\defineXMLentity[tex-backslash]\letterbackslash
\defineXMLentity[tex-hat]\letterhat
\defineXMLentity[tex-underscore]\letterunderscore
\defineXMLentity[tex-leftbrace]\letterbgroup
\defineXMLentity[tex-rightbrace]\letteregroup
\defineXMLentity[tex-bar]\letterbar}
\let\XMLremappedpar\empty
%D Here we implement the second step in the element grabber.
\long\def\XMLremapdata
{\dosingleempty\doXMLremapdata}
\long\def\doXMLremapdata[#1]#2#3#4%
{\bgroup
\startXMLmapping[#1]%
% enable unknown elements (should be macro)
\doifsomething{#1}
{\doifdefinedelse{\@@XML#1:\s!unknown:M}
{\remapXMLunknowntrue}{\remapXMLunknownfalse}}%
%
\pushmacro\doXMLentity % needed ?
% this will change, proper split in element itself
\ifx\currentXMLnamespace\empty
\let\parseXMLelement\remapXMLelement
\else
% here we need to get rid of the namespace; we also
% have to preserve the leading / if present
\@EA\long\@EA\def\@EA\parseXMLelement\@EA
##\@EA1\currentXMLnamespace:{\remapXMLelement##1}%
% ##2 removes leading spaces
\fi
%
\let\parseXMLescape \remapXMLescape
\let\parseXMLprocess\remapXMLprocess
%
\let\doXMLentity \remapXMLentity
%
\enableXML % sets entities
\enableXMLexpansion
\let\par\XMLremappedpar
\the\everyXMLremapping
%\ignorelines
\catcode\tabasciicode \spacecatcode
\catcode\endoflineasciicode\spacecatcode
\catcode\formfeedasciicode \spacecatcode
\catcode\endoffileasciicode\spacecatcode
\pushmacro\unicodechar
\let\unicodechar\relax
\xdef\remappedXMLdata{#4\empty}%
\popmacro\unicodechar
\let\par\endgraf
\popmacro\doXMLentity % needed ?
\disableXMLexpansion
\catcode`\{=\@@begingroup
\catcode`\}=\@@endgroup
\catcode`\\=\@@escape
\iftraceXMLremapping
\ifmmode\vbox\fi\bgroup
\defconvertedcommand\ascii\remappedXMLdata
\tttf\veryraggedright\ascii\par
\writestatus{xml-remap}{\ascii}%
\egroup
\fi
#2\scantokens\@EA{\remappedXMLdata\empty\empty}#3%
\stopXMLmapping
\egroup}
% testcase:
%
% aap‒noot coördinatie – één
%
% \startXMLdata
% aap‒noot coördinatie – één
%
% \stopXMLdata
%
% weird case:
%
% \chardef\XMLtokensreduction\zerocount
% \startXMLdata
%
% \stopXMLdata
%
% \chardef\XMLtokensreduction\plustwo
% \startXMLdata
%
% \stopXMLdata
% rename to better names
\newtoks \XMLRtoks
\newcount \nofXMLRchildren
\def\naturalxmlr#1#2{\getvalue{\@@XML\@@XMLmapping:#1:#2}}
\def\ignoreXMLRelement#1#2{}
\def\normalXMLRelement#1#2{#2}
\let\nextXMLRelement \empty
\let\firstXMLRelement \empty
\let\secondXMLRelement\empty
% \def\withnextXMLRelement#1%
% {\pushmacro\dowithnextXMLRelement
% \def\dowithnextXMLRelement##1##2##3##4##5%
% {\popmacro\dowithnextXMLRelement
% \def\nextXMLRelement{##1{##2}{##3}{##4}{##5}}%
% #1}%
% \doifnextcharelse\empty\empty\dowithnextXMLRelement}
%
% better and faster:
\def\dowithnextXMLRelement#1#2#3#4#5#6%
{\def\nextXMLRelement{#2{#3}{#4}{#5}{#6}}#1}%
\def\withnextXMLRelement#1%
{\doifnextcharelse\empty\empty{\dowithnextXMLRelement{#1}}}
\def\withnexttwoXMLRelements#1%
{\pushmacro\firstXMLRelement
\pushmacro\secondXMLRelement
\withnextXMLRelement
{\let\firstXMLRelement\nextXMLRelement
\withnextXMLRelement
{\let\secondXMLRelement\nextXMLRelement
#1%
\popmacro\secondXMLRelement
\popmacro\firstXMLRelement}}}
\def\withnextthreeXMLRelements#1% korter, met two
{\pushmacro\firstXMLRelement
\pushmacro\secondXMLRelement
\pushmacro\thirdXMLRelement
\withnextXMLRelement
{\let\firstXMLRelement\nextXMLRelement
\withnextXMLRelement
{\let\secondXMLRelement\nextXMLRelement
\withnextXMLRelement
{\let\thirdXMLRelement\nextXMLRelement
#1%
\popmacro\thirdXMLRelement
\popmacro\secondXMLRelement
\popmacro\firstXMLRelement}}}}
\def\doifXMLRchildelse#1#2#3#4%
{\pushmacro\xmlr
\def\next{#4}%
\def\xmlr##1##2##3##4% the / should be sorted out in the mapper
{\rawdoifinsetelse{##1}{#1}
{\def\next{#3}}
{\doif{##1}{#1/}{\def\next{#3}}}}%
#2\empty
\popmacro\xmlr
\next}
\def\doifXMLRchild#1#2#3%
{\pushmacro\xmlr
\let\next\empty
\def\xmlr##1##2##3##4% the / should be sorted out in the mapper
{\rawdoifinsetelse{##1}{#1}
{\def\next{#3}}
{\doif{##1}{#1/}{\def\next{#3}}}}%
#2\empty
\popmacro\xmlr
\next}
\def\encapsulateXMLRchild#1#2#3#4#5%
{\pushmacro\xmlr
\def\xmlr##1##2##3##4%
{\doifelse{##1}{#1}
{\def\next{\doencapsulateXMLRchild{#2}{#3}{#4}{##4}}}
{\let\next\empty}%
\next}%
#5\empty
\popmacro\xmlr}
\def\encapsulateXMLRchildren#1#2#3#4#5%
{\pushmacro\xmlr
\pushmacro\betweenXMLRchild
\def\betweenXMLRchild{\def\betweenXMLRchild{#3}}%
\def\xmlr##1##2##3##4%
{\rawdoifinsetelse{##1}{#1}
{\pushmacro\xmlr
\let\xmlr\naturalxmlr
\betweenXMLRchild\xmlr{##1}{##2}{##3}{##4}%
\popmacro\xmlr}
{}}%
#2#5\empty#4%
\popmacro\betweenXMLRchild
\popmacro\xmlr}
\def\doencapsulateXMLRchild#1#2#3#4%
{\pushmacro\xmlr
\pushmacro\betweenXMLRchild
\def\betweenXMLRchild{\def\betweenXMLRchild{#2}}%
\def\xmlr##1##2##3##4%
{\pushmacro\xmlr
\let\xmlr\naturalxmlr
\betweenXMLRchild\xmlr{##1}{##2}{##3}{##4}%
\popmacro\xmlr}%
#1#4\empty#3%
\popmacro\betweenXMLRchild
\popmacro\xmlr}
\let\encapsulateXMLR\doencapsulateXMLRchild
\def\withnextXMLRelementelse#1#2%
{\def\xdowithnextXMLRelement##1##2##3##4##5%
{\def\nextXMLRelement{##1{##2}{##3}{##4}{##5}}#1}%
\def\xnowithnextXMLRelement%
{\let\nextXMLRelement\empty#2}%
\doifnextcharelse\xmlr\xdowithnextXMLRelement\xnowithnextXMLRelement}
\def\encapsulatenextXMLRelements#1#2#3#4% oude bewaren
{\pushmacro\betweenXMLRchild
\pushmacro\afterXMLRchild
\def\betweenXMLRchild{#1\def\betweenXMLRchild{#2}}%
\let\afterXMLRchild\empty
\withnextXMLRelementelse
{\betweenXMLRchild
\def\afterXMLRchild{#3}%
\nextXMLRelement
\doifnextcharelse\empty\xnowithnextXMLRelement\xdowithnextXMLRelement}
{\afterXMLRchild
\popmacro\afterXMLRchild
\popmacro\betweenXMLRchild}%
#4}
\def\collectXMLRchild#1#2%
{\XMLRtoks\emptytoks
\pushmacro\xmlr
\def\xmlr##1##2##3##4%
{\doif{##1}{#1}{\appendtoks##4\to\XMLRtoks}}%
#2\empty
\popmacro\xmlr}
\def\doifelseXMLRneighbors#1#2%
{\XMLRtoks\emptytoks
\pushmacro\xmlr
\donefalse
\let\prevXMLRchild\empty
\def\xmlr##1##2##3##4%
{\doif{##1}{#1}{\doif{##1}\prevXMLRchild{\donetrue}}%
\def\prevXMLRchild{##1}}%
#2\empty
\popmacro\xmlr
\ifdone
\expandafter\firstoftwoarguments
\else
\expandafter\secondoftwoarguments
\fi}
\def\collectbetweenXMLRchild#1#2#3%
{\XMLRtoks\emptytoks
\pushmacro\xmlr
\pushmacro\betweenXMLRchild
\def\betweenXMLRchild{\def\betweenXMLRchild{\appendtoks#2\to\XMLRtoks}}%
\def\xmlr##1##2##3##4%
{\rawdoifinsetelse{##1}{#1}
{\betweenXMLRchild\appendtoks\xmlr{##1}{##2}{##3}{##4}\to\XMLRtoks}{}}%
#3\empty
\popmacro\betweenXMLRchild
\popmacro\xmlr}
\def\dorawcollectbetweenXMLR#1#2%
{\pushmacro\xmlr
\pushmacro\betweenXMLRchild
\def\betweenXMLRchild{\def\betweenXMLRchild{#1}}%
\def\xmlr##1##2##3##4%
{\betweenXMLRchild\appendtoks\xmlr{##1}{##2}{##3}{##4}\to\XMLRtoks}%
#2\empty
\popmacro\betweenXMLRchild
\popmacro\xmlr}
\def\rawcollectbetweenXMLR%
{\XMLRtoks\emptytoks\dorawcollectbetweenXMLR}
\def\docollectbetweenXMLR#1%
{\dorawcollectbetweenXMLR{\appendtoks#1\to\XMLRtoks}}
\def\collectbetweenXMLR%
{\XMLRtoks\emptytoks\docollectbetweenXMLR}
\def\processXMLRchildren#1%
{\pushmacro\xmlr
\let\xmlr\naturalxmlr
#1\empty
\popmacro\xmlr}
\def\processXMLRchild#1#2% slow but more versatile
{\pushmacro\xmlr
\XMLRtoks\emptytoks
\def\xmlr##1##2##3##4%
{\rawdoifinsetelse{##1}{#1}
{\appendtoks\xmlr{##1}{##2}{##3}{##4}\to\XMLRtoks}{}}%
#2%
\popmacro\xmlr
\the\XMLRtoks\empty}
\def\countXMLRchildren#1%
{\pushmacro\xmlr
\nofXMLRchildren=0
\def\xmlr##1##2##3##4{\advance\nofXMLRchildren\plusone}
#1\empty
\popmacro\xmlr}
\def\countXMLRchild#1#2%
{\pushmacro\xmlr
\nofXMLRchildren=0
\def\xmlr##1##2##3##4%
{\rawdoifinsetelse{##1}{#1}{\advance\nofXMLRchildren\plusone}{}}
#2\empty
\popmacro\xmlr}
\def\installXMLunknownremapping
{\remapXMLsingular[\s!unknown][CPA]\doXMLunknownSI
\remapXMLsequence[\s!unknown][CPA]\doXMLunknownSE}
\bgroup \catcode`<=\active
\gdef\revertXMLremapping
{\gdef\doXMLunknownSE##1##2{<##1>##2##1>}%
\gdef\doXMLunknownSI##1##2{<##1>}}
\gdef\unmapXMLdata#1#2% todo: singular, evt ##2 space ervoor en ##1##2
{\bgroup
\revertXMLremapping
\expandXMLremapping % now we can roll back
\setnormalXMLhandler % using the normal parser
\resetXMLmapping % and leaving the mapping namespace
\xdef\unmappedXMLdata{#2}% recreate the original
\enableXMLelements % enable normal handler
\unmappedXMLdata % off we go ...
\egroup}
\egroup
\protect \endinput