I want to parse subscripts and superspricts using expl3 for which I used \def before:



\cs_new:Npn \l__parsing_parse_superscript:n ^#1 {superscript = #1}
\cs_new:Npn \l__parsing_parse_subscript:n _#1 {subscript = #1}

\parsesubscript_100 % TODO error

The superscript gets parsed correctly. However for the subscript an error occurs, probably because after \ExplSyntaxOn the underscore is treated as a letter. I also tried \c_underscore_str and \c_math_subscript_token instead of _ in the command definition. How can the underscore be escaped for parsing a subscript? Or are there other solutions for parsing arguments with expl3 that should be prefered?


  • 4
    It should also be mentioned that commands with nonstandard syntaxes like <command>^#1 and <command>_#1 should be denoted :w (for “weird”) rather than :n.
    – Gaussler
    Aug 23, 2021 at 17:23
  • 1
    @egreg I want do collect multiple subscripts and superscripts like I commented below the answer of Phelype Oleinik. The idea is based on your solution to avoid "double subscript" errors but extended to allow also superscripts and using expl3.
    – Michael
    Aug 28, 2021 at 14:59

1 Answer 1


Your diagnostics is correct: the problem is that in expl3 syntax, the _ has catcode 11. However using \c_underscore_str is no good either because that has catcode 12, but in the document the _ (usually) has catcode 8.

In your definition you have to enforce catcode 8 for the underscore:

\cs_new:Npn \__michael_parse_superscript:w ^#1 {superscript = #1.}
    \cs_new:Npn \exp_not:N \__michael_parse_subscript:w
        \char_generate:nn { `\_ } { 8 } #1
      {subscript = #1.}

(note also that with \parsesubscript_100, only the 1 is grabbed as argument: you need \parsesubscript_{100}).

But that's not really parsing, because if you use the command without the following _ or ^ you'll get an error. You could use the e-type argument instead (note that it works regardless of the order the actual arguments appear):

\NewDocumentCommand \parsesupsub { e{^_} }
    \IfValueT{#1}{superscript = #1.\\}
    \IfValueT{#2}{subscript = #2.\\}




You could also parse manually with \peek_charcode_remove:NTF _ { <with> } { <without> }, then it would work regardless of the current catcode of _.

On naming (I wrote an explanation here), the \l_ (or \g_ or \c_) prefix should be used for variables only. You are defining commands, so they should start with \module_... if public, or \__module_... if private (I used michael as the module name). Also, as Gaussler noted in the comment, the :n argument type should be for "normal" arguments (tokens delimited by {...}). Since you have a "weird" token in the parameter text, you should use :w.

  • 1
    @Michael Yes, \peek_charcode_remove:NTF is indeed better in this case. The e argument type only takes one argument per token, so e{^_} grabs at most two arguments. Aug 29, 2021 at 1:23

You must log in to answer this question.

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