Sobrecarga de macro, redefinição de catcode… ou como posso analisar XML com TeX?

Sobrecarga de macro, redefinição de catcode… ou como posso analisar XML com TeX?

Dado este código que mal analisa XML e o imprime como SXML (s-exp XML, também conhecido como XML estilo lisp, também conhecido como XML com parênteses):

\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

O primeiro problema é que a redefinição \do catcode não funciona, pois depois disso \<string>ainda dá o erro de “sequência de controle indefinida”, o segundo (menos importante) é que não consigo escrever um loop para redefinir catcodes repetidamente e o terceiro, mais importante:

Por que o TeX não suporta sobrecarga de macro? Quero dizer, como você pode ver, eu fiz com \xattr, mas, pior ainda, o \defeltwhich redefine “<” com várias coisas entre seus argumentos. O problema é que eu encontro uma maneira de fazer a sequência de controle “<” emular todas as tags xml, ou faço “<” catcode 0 e isso significa que tenho que dar acesso a aaaatodas as sequências de controle do TeX de dentro do XML que desejo analisar (o que é feio): como posso fazer isso então?

Existe outra solução além de definir sozinho o comando < e ler recursivamente o texto que vem depois, sem beneficiar os recursos de análise do TeX?

Responder1

Suponha o seguinte exemplo de entrada:

<?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>

Em seguida, tente processá-lo pela minha macro \xmlprep {input} {output}usando pdftex thisfileo comando, por exemplo.

\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

Você obtém a seguinte saídatest.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%
      }
    }
  }
}

Este é um formato compatível com TeX. Você pode simplesmente definir tags usadas \XMLcenik, \XMLnazevetc. macros com dois parâmetros (argumentos e corpo de texto). Então você pode processá-lo pelo TeX. Se essas macros estiverem preparadas, você poderá processar o arquivo XML em uma execução do TeX:

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

informação relacionada