%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 ADE \& \CONTEXT\ Development Team}] %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`\{=\begingroupcatcode \catcode`\}=\endgroupcatcode \catcode`\\=\escapecatcode \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 % aap‒noot coördinatie – één % \stopXMLdata % % weird case: % % \chardef\XMLtokensreduction\zerocount % \startXMLdata % \"{a}\"{o}\"{u}\v{c}\v{s}\v{z} % \stopXMLdata % % \chardef\XMLtokensreduction\plustwo % \startXMLdata % \"{a}\"{o}\"{u}\v{c}\v{s}\v{z} % \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`<=\activecatcode \gdef\revertXMLremapping {\gdef\doXMLunknownSE##1##2{<##1>##2}% \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