# A LaTeX 3 macro to typeset chemical compounds?

Out of curiosity, I wonder how would a macro coded in expl3 that does the basics of mhchem (as in section basics in the mhchem manual) looks like?

I have found examples in TeX and LuaTeX in http://wiki.contextgarden.net/Programming_in_LuaTeX but their input style is too close to math (in my view, not as convenient asmhchem).

So, gurus, please showcase the power of expl3.

-
I wrote the wiki page that you cite. I will start by noting that i know nothin about typesetring chemical formulas but what is so so ugly about the input syntax. I glanced at mhchem syntax and the main difference appears to how underscores are handled. If you have a formal grammer of how mhchem parses its input it should be straigjtforward to write the parse in lpeg. – Aditya Sep 6 '10 at 23:15
mhchem.sty is 1300 lines long. What exactly are you asking to be reimplemented? – Will Robertson Sep 7 '10 at 0:39
And what would a reimplementation get us? P.S. @Aditya: "without exploding your head" is a great phrase :) I haven't used LPEG but that seems like a good approach. – Will Robertson Sep 7 '10 at 4:32
@Aditya: the syntax is too close to math, isn't it? @Will: I don't know if implementing the basics of mhchem is a reimplementation of mhchem. It is 1300 lines and not easy to read. I was curious about whether using expl3 will make the code easier to read just like what LPEG from Lua does. – Leo Liu Sep 7 '10 at 6:30
I like the question. I don’t know expl3 at all but if it can simplify parsing TeX, then a simple grammar such as the above might be well suited to showcase it. However, @Leo, providing a simple formal grammar might help make the question clearer and encourage answers. – Konrad Rudolph Sep 7 '10 at 7:04

I wouldn't have a clue if this is sufficient to fully implement what \ce is capable of, but here's a quick example of how I'd go about this. Note that there are no state machines or active characters, just judicious use of search'n'replace. The TeX equivalent, I suppose, of a regexp solution. Who knows if it's efficient, though.

\documentclass{article}
\usepackage{expl3,xparse,
fixltx2e, % for \textsubscript
amstext   % for \text
}

\newcommand\textsubsuperscript[2]{$_{\text{#1}}^{\text{#2}}$}
\newcommand\textsupersubscript[2]{$_{\text{#2}}^{\text{#1}}$}

\ExplSyntaxOn

% \catcode 12 strings of otherwise special chars:
\tl_set:Nx \c_underscore_char { \cs_to_str:N \_ }
\tl_set:Nx \c_caret_char      { \cs_to_str:N \^ }
\tl_set:Nx \c_bgroup_char     { \cs_to_str:N \{ }
\tl_set:Nx \c_egroup_char     { \cs_to_str:N \} }

\DeclareDocumentCommand \ce {m} {
\tl_set:Nx \l_ce_tl { \tl_to_str:n {#1} }

\tl_replace_all_in:Nnn \l_ce_tl {1} {\ce_sub:w{1}}
\tl_replace_all_in:Nnn \l_ce_tl {2} {\ce_sub:w{2}}
\tl_replace_all_in:Nnn \l_ce_tl {3} {\ce_sub:w{3}}
\tl_replace_all_in:Nnn \l_ce_tl {4} {\ce_sub:w{4}}
\tl_replace_all_in:Nnn \l_ce_tl {5} {\ce_sub:w{5}}
\tl_replace_all_in:Nnn \l_ce_tl {6} {\ce_sub:w{6}}
\tl_replace_all_in:Nnn \l_ce_tl {7} {\ce_sub:w{7}}
\tl_replace_all_in:Nnn \l_ce_tl {8} {\ce_sub:w{8}}
\tl_replace_all_in:Nnn \l_ce_tl {9} {\ce_sub:w{9}}
\tl_replace_all_in:Nnn \l_ce_tl {0} {\ce_sub:w{0}}

\tl_replace_all_in:Non \l_ce_tl \c_caret_char {\ce_super:w}
\tl_replace_all_in:Non \l_ce_tl \c_underscore_char {\ce_sub:w}

\tl_replace_all_in:Nnn \l_ce_tl {+} {\ce_super:w{+}}
\tl_replace_all_in:Nnn \l_ce_tl {-} {\ce_super:w{$-$}}

% fix syntax like "^2":
\tl_replace_all_in:Nnn \l_ce_tl {\ce_super:w \ce_sub:w} {\ce_super:w}

% finally, fix up braces that were detokenized:
\tl_rescan:nV {\ExplSyntaxOn} \l_ce_tl
}

\cs_set:Npn \ce_super:w #1 {
\peek_meaning_remove_ignore_spaces:NTF \ce_sub:w
{\textsupersubscript{\ce_disable_subsuper: #1}}
{\textsuperscript{\ce_disable_subsuper: #1}}
}

\cs_set:Npn \ce_sub:w #1 {
\peek_meaning_remove_ignore_spaces:NTF \ce_super:w
{\textsubsuperscript{\ce_disable_subsuper: #1}}
{\textsubscript{\ce_disable_subsuper: #1}}
}

\cs_set:Nn \ce_disable_subsuper: {
\cs_set_eq:NN \ce_super:w \use:n
\cs_set_eq:NN \ce_sub:w \use:n
}

% Need these variants:
\cs_generate_variant:Nn \tl_replace_all_in:Nnn {Non}
\cs_generate_variant:Nn \tl_rescan:nn {nV}

\ExplSyntaxOff

\begin{document}
\ce{Y^{99}+}\par
\ce{Y^{99+}}\par
\ce{H20}\par
\ce{Sb2O3}\par
\ce{H+}\par
\ce{CrO4^2-}\par
\ce{AgCl2-}\par
\ce{[AgCl2]-}\par
\ce{H2_{(aq)}}\par
\ce{NO3-}\par
\ce{(NH4)2S}\par
\end{document}


The premise of \ce is that the argument is turned into a string before replacing the various characters with macros to expand to the desired output. First the numbers are replaced by themselves inside a conditional subscript, then ^ and _ are replaced macros to perform "conditional superscripts and subscripts" (resp.), and finally + and - are replaced by themselves superscripted.

In case of syntax like ^2, which would be replaced by [superscript]+[subscript 2], we replace the combination of [superscript]+[subscript] with the conditional superscript alone.

Finally, the string is the "retokenised" using the expl3 wrapper around \scantokens to perform all of the typesetting.

The "conditional subscripts" and "conditional superscripts" that I refer to above are simply text sub-/super-scripts that deactivates themselves for nested material. E.g., [subscript [subscript x]] is interpreted as [subscript x]. This allows things like + inside a superscript not superscripting itself again.

-
If you read through mhchem then I don't think efficiency comes into it. The approach above would leave a few questions, for example how to deal with single bonds (\ce{C-H}) and also escape to math mode (\ce{$i\text{-Pr}$-O}). However, it'd a good place to start! – Joseph Wright Sep 29 '10 at 17:44
Although I do not understand how this works, the expl3 code is significantly more readable than the traditional TeX code. Great. – Aditya Sep 29 '10 at 19:43
@Joseph I was literally just coding to the tests shown :) I'm sure it can be improved. @Aditya some explanation to the code added at the bottom. – Will Robertson Sep 30 '10 at 0:58
thanks for the example. Sorry I am late to tag this as the answer. – Leo Liu May 21 '11 at 7:58