8

Given this code that barely parse XML and print it as SXML (s-exp XML, aka lisp-style XML, aka XML-with-parenthesis):

\catcode`@=11
\let\@xp\expandafter
\def\@makeletter#1#2\@nil{\catcode`#1 \ifx\relax#2\relax\else\@makeletter#2\@nil\fi}
\def\@gobble#1 {}
\def\@nil{}
\catcode`<13
\catcode`>12
\def\@split #1#2 #3\@nil{#1{#2} \ifx\relax#3\relax\else\@split#1#3\@nil\fi}
\def\defelt#1{\def<#1##1>##2</#1>{ (#1 {\ifx\relax##1\relax\else \@xp\@split\@xp\@key\@gobble ##1 \@nil\fi} ##2) }}
\defelt{div}
\defelt{span}

\catcode`?6
\catcode`#11
\def\@key?1=?2{ {\tt #:?1} ?2}
\def\@key?1{ {\tt #:?1}}
\catcode`^^J=10 \catcode`?=11 \catcode`^=11 \catcode`_=11 \catcode`~=11 \catcode`{=11 \catcode`}=11 %\catcode`\=11
%\@makeletter?^_~{}%\@nil

<span>first<span foo bar=yes>second</span>three</span>

\eject
\end

The first problem is redefining \ catcode doesn’t work since after that \<string> still gives the “undefined control sequence” error, the second (less important) is I can’t write a loop to redefine repeatedly catcodes, and the third, most important:

Why doesn’t TeX support macro overloading? I mean like you can see I did with \xattr, but, even worst, the \defelt which redefines “<” with several stuff in between its arguments. The problem is either I find a way of making the “<” control sequence emulate all xml tags, either I make “<” catcode 0 and then that means I have to give access to aaaall TeX control sequences from within the XML I want to parse (which is ugly): how can I do this then?

Is there another solution than defining alone the < command then recursively read the text that come after without benefiting the TeX parsing capabilities?

7
  • 2
    Have you looked at xmltex?
    – jon
    Jun 11, 2016 at 5:01
  • Oh, didn’t knew about it… I’m looking… yet source code isn’t enough readable to me to quickly find relevant parts/an answer there ^^"
    – galex-713
    Jun 11, 2016 at 5:08
  • 1
    ConTeXt has extensive features for parsing XML. See the ConTeXt XML manual. Also see the XML section of ConTeXt wiki.
    – Aditya
    Jun 11, 2016 at 5:12
  • 1
    @JosephWright The older MkII version of the XML parser is a pure TeX solution. Jun 11, 2016 at 12:32
  • 1
    @JosephWright: I was not sure if the OP's objective is to write a parser (as an academic exercise), or process XML with TeX. In the latter case, using luatex is a viable option. From the comment, I see that it was the former. In that case, as Metafox said, the old MkIi code is still relevant.
    – Aditya
    Jun 11, 2016 at 15:17

1 Answer 1

7

Suppose following input example:

<?xml version="1.0" encoding="utf8"?>
<cenik>
  <nazev>Počítačové komponenty</nazev>
  <platnost od="1.1.2000" do="31.3.2000"/>
  <dodavatel>
    <nazev>První hardwarová, s.r.o.</nazev>
    <adresa>
      <ulice>Průmyslová 12</ulice>
      <mesto>Praha 10</mesto>
      <psc>100 000</psc>
      <email>[email protected]</email>
   </adresa>
  </dodavatel> 
  <nabidka>
    <produkt kategorie="polohovací zařízení" kod="pxbd-21">
      <nazev>Hyperoptická <em>digitální</em> myš</nazev>
      <cena mena="CZK">368.30</cena>
    </produkt>
    <produkt kategorie="pevné disky" kod="sbhd-99">
      <nazev>Soft-slow disc &lt; 19,3 GB</nazev>
      <cena mena="CZK">8500</cena>
    </produkt>
    <produkt kategorie="polohovací zařízení" kod="pxbd-13">
      <nazev>Tlakový tablet</nazev>
      <cena mena="CZK">5635.20</cena>
    </produkt>
  </nabidka>
</cenik>

Then try to process it by my macro \xmlprep {input} {output} using pdftex thisfile command for example.

\newwrite\xmloutfile
\def\xmlprep#1#2{% #1=input file, #2=output file
   \ifx\relax#2\relax \chardef\xmloutfile=16 \else
   \immediate\openout\xmloutfile=#2 \fi
   \begingroup \everypar={\setbox0=\lastbox\par \xscan}\input#1 \endgroup
   \immediate\closeout\xmloutfile
}
\long\def\xscan#1<{\ifx\xscan#1\xscan \else
   \toks0={#1}\xprint{\the\toks0\npercent}\fi\xtag}

\def\nob#1{}\edef\nob{\expandafter\nob\string\{}
\def\ncb#1{}\edef\ncb{\expandafter\ncb\string\}}
\def\npercent#1{}\edef\npercent{\expandafter\npercent\string\%}

\def\xprint#1{\immediate\write\xmloutfile{\xindent#1}}
\def\xindent{}

\def\xtag#1#2>{\ifx#1?\xtagD#2>\else\ifx#1/\xtagC#2>\else\xtagE#1#2>/>\end\fi\fi}
\def\xtagE#1/>#2\end{\ifx>#2>\let\tmp=n\xtagA#1 \end\else \let\tmp=/\xtagA#1> \end\fi}
\def\xtagA#1 #2\end{\def\currargs{}\ifx>#2>\xtagB#1\else \xargsB#2\xtagB#1>\fi}
\def\xtagB#1>{\bgroup\def\currtag{#1}%
   \ifx\tmp/\xprint{\string\XML#1{\currargs}{}}\egroup\else
   \xprint{\string\XML#1{\currargs}\nob\npercent}%
   \edef\xindent{\xindent\space\space}\fi}
\def\xtagD#1?>{\xprint{\string\META{#1}}}
\def\xargsB#1>{\def\currargs{#1}}
\def\xtagC#1>{\def\tmp{#1}\ifx\tmp\currtag\else
   \message{WARNING: <\currtag>...</#1> doesn't match}\fi
   \egroup\xprint{\ncb}%
}

\xmlprep {test.xml} {test.out}

\end

You get the following output test.out

\META{xml version="1.0" encoding="utf8"}
\XMLcenik{}{%
  \XMLnazev{}{%
    Počítačové komponenty%
  }
  \XMLplatnost{od="1.1.2000" do="31.3.2000"}{}
  \XMLdodavatel{}{%
    \XMLnazev{}{%
      První hardwarová, s.r.o.%
    }
    \XMLadresa{}{%
      \XMLulice{}{%
        Průmyslová 12%
      }
      \XMLmesto{}{%
        Praha 10%
      }
      \XMLpsc{}{%
        100 000%
      }
      \XMLemail{}{%
        [email protected]%
      }
    }
  }
  \XMLnabidka{}{%
    \XMLprodukt{kategorie="polohovací zařízení" kod="pxbd-21"}{%
      \XMLnazev{}{%
        Hyperoptická %
        \XMLem{}{%
          digitální%
        }
        myš%
      }
      \XMLcena{mena="CZK"}{%
        368.30%
      }
    }
    \XMLprodukt{kategorie="pevné disky" kod="sbhd-99"}{%
      \XMLnazev{}{%
        Soft-slow disc &lt; 19,3 GB%
      }
      \XMLcena{mena="CZK"}{%
        8500%
      }
    }
    \XMLprodukt{kategorie="polohovací zařízení" kod="pxbd-13"}{%
      \XMLnazev{}{%
        Tlakový tablet%
      }
      \XMLcena{mena="CZK"}{%
        5635.20%
      }
    }
  }
}

This is TeX-friendly format. You can simply define used tags \XMLcenik, \XMLnazev etc. macros with two parameters (arguments and body-text). Then you can process it by TeX. If these macros are prepared then you can process XML file in one TeX run:

\xmlprep {test.xml} {text.out}
\input test.out
1

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .