I have a partial version of this macro working, without xparse, but I needed to tweak it and it got too unwieldy, so I'm trying to use xparse instead. It's admittedly a farfetched macro, but I'm trying to learn how xparse works... Suppose I'm trying to typeset references to sentences and their variants, like so:
X1
X2*
X3**
X4'
X56''
where:
- There can be some number of stars (indicating malformed variants), the number of these is unknown a priori
- There can be some number of primes (indicating well-formed variants), the number of these is also unknown
- The number afterwards may be more than one digit. The number is terminated by a space, a comma, semicolon, or other punctuation. For example,
$(\X'3; \X**45)$. Xcould be any alphabetic string.- It should preferably work in both text and math modes.
I'd like to input these as \X**3, or \X'4. (I'm choosing to place the punctuation first, because it's easier to convince TeX to parse things that way...) Suppose I already have an internal macro to typeset the actual thing:
\newcommand\@X[2]{ % #1 is the decorations, #2 is the number ...
\mbox{\ifmmode\text{X#2#1}\else X#2#1\fi}
}
Now I'm trying to define the user visible macro. My first hunch was
\DeclareDocumentCommand\X{ s s t' t' m }{ stuff }
but:
- This only handles two stars and two primes
- Actually, I can't seem to make it work even with just those two stars and primes :( It's giving me errors about missing
$signs, but the math-mode code I had in\@Xwas working before... - The
margument is only grabbing the first digit, rather than the whole number token as my old version used to do.
Is there a way to program this using xparse, or is this just too weird a macro to try to support?
Edited to add: I got the following definitions working last night. They work for only ' and numbers, but don't use xparse.
\newcommand\X{\@ifnextchar'\@Xprime{\@X{}}}
\newcommand\@Xprime[1]{\@ifnextchar'{\def\temp{\@Xprime{#1{'}}}\ex\temp\@gobble}{\@Xdigit{#1}}}
\newcommand\@Xdigit[2]{%
\@ifnextchar1{\def\temp{\@Xdigit{#1}{{#2}1}}\ex\temp\@gobble}{%
\@ifnextchar2{\def\temp{\@Xdigit{#1}{{#2}2}}\ex\temp\@gobble}{%
\@ifnextchar3{\def\temp{\@Xdigit{#1}{{#2}3}}\ex\temp\@gobble}{%
\@ifnextchar4{\def\temp{\@Xdigit{#1}{{#2}4}}\ex\temp\@gobble}{%
\@ifnextchar5{\def\temp{\@Xdigit{#1}{{#2}5}}\ex\temp\@gobble}{%
\@ifnextchar6{\def\temp{\@Xdigit{#1}{{#2}6}}\ex\temp\@gobble}{%
\@ifnextchar7{\def\temp{\@Xdigit{#1}{{#2}7}}\ex\temp\@gobble}{%
\@ifnextchar8{\def\temp{\@Xdigit{#1}{{#2}8}}\ex\temp\@gobble}{%
\@ifnextchar9{\def\temp{\@Xdigit{#1}{{#2}9}}\ex\temp\@gobble}{%
\@ifnextchar0{\def\temp{\@Xdigit{#1}{{#2}0}}\ex\temp\@gobble}{%
\@X{#1}{#2}\xspace}%
}}}}}}}}}}
The idea was for \@Xprime to scrape up all the primes, one at a time and in brace groups to prevent them from turning into double-quotes, and accumulate them into its argument. It then passes that string off to \@Xdigit, which scrapes up all digits (digits don't have their own catcode, so I couldn't figure out any shorter test than this one...) and accumulates them into its second argument. (I didn't intend for each digit to be in a brace group, but got stuck on how to append a digit to #2.) Finally, once it's got both the primes and the digits, it calls \@X to typeset them, and then uses \xspace to restore space handling.
I couldn't figure out an equivalent \@Xstar, so to speak, because \@ifnextchar* didn't seem to do what I meant, and I think \@ifstar only gobbles spaces following a command, not following the arguments to a command.
Edited I've figured out the following version, which behaves correctly, but still doesn't use xparse. It uses \futuredef from etextools, and has the advantage of being much more concise and understandable than the previous version... I'm still curious how this would work with 'xparse`.
\DeclareRobustCommand\X{\@ifstar\@Xstar\@Xnostar}
\newcommand\@Xstar{\futuredef[*]\@Xstars{\futuredef[1234567890]\@Xdigits{\@X{\@Xstars}{\@Xdigits}}}}
\newcommand\@Xnostar{\@ifnextchar'\@Xprime{\futuredef[1234567890]\@Xdigits{\@X{}{\@Xdigits}}}}
\newcommand\@Xprime{\futuredef[']\@Xprimes{\futuredef[1234567890]\@Xdigits{\@X{\@Xprimes}{\@Xdigits}}}}
\newcommand\@X[2]{\mbox{\ifmmode\text{X#2#1}\else X#2#1\fi}\xspace}
*and'is variable, and we don't know the maximum in advance? How is the end of the numerical part of the argument delimited? – Joseph Wright May 27 '11 at 07:29?{\test}, where?is a mnemonic for "predicate", and\testis used as "\ifthenelse{\test{char}}{grab next char and keep going}{argument is completed}"? (Or something like that, appropriately latex3-ified...) – Ben Lerner Jun 15 '11 at 23:02\X**{23}defined by an argument specl m)? – Bruno Le Floch Jun 15 '11 at 23:14