I created a macro to recognize between two patterns #1 - #2
or #1 - #2 - #3
and assure an output on the form # - # - #
(for dates).
\def\cal@ymd#1:#2:#3:{#1-#2-#3}
\def\cal@md#1:#2:#3:{\cal@Year-#1-#2}
\def\cal@datesplit#1-#2-#3-#4#5#6{#5#1:#2:#3:}
\def\cal@Xdateexpand#1{\expandafter\cal@datesplit#1--\cal@ymd\cal@md\empty}
This version above "almost" does what I want (it works, but fails if the calling argument includes some other macros calls).
If instead, I use the following definition
\def\cal@Ydateexpand#1{\edef\cal@arg{#1}\expandafter\cal@datesplit\cal@arg--\cal@ymd\cal@md\empty}
It works even if the macro is called using other macros as arguments.
My first question is if there is another way to assure that #1
will be expanded before calling \cal@datesplit
?
I had expected that \expandafter\cal@datesplit#1
would be enough for this...
Now, the real question that is bugging me. when I try to use both macros in the definition of another macro, using \edef
, the first definition \cal@Xdateexpand
"is ok" but the second \cal@Ydateexpand
fails to compile.
Below is the minimum code showing it all... and I would like to know why it doesn't even compile (cases B.7 and B.8 below)?
I'm using MiKTeX 2.9.7347.
\documentclass{article}
\makeatletter
\def\cal@ymd#1:#2:#3:{#1-#2-#3}
\def\cal@md#1:#2:#3:{\cal@Year-#1-#2}
\def\cal@datesplit#1-#2-#3-#4#5#6{#5#1:#2:#3:}
\def\cal@Xdateexpand#1{\expandafter\cal@datesplit#1--\cal@ymd\cal@md\empty}
\def\cal@Ydateexpand#1{\edef\cal@arg{#1}\expandafter\cal@datesplit\cal@arg--\cal@ymd\cal@md\empty}
\def\cal@Year{2020}
\begin{document}
\def\dateMD{03-01} $\backslash$dateMD :: (\dateMD)
\def\dateYMD{2021-03-01} $\backslash$dateYMD :: (\dateYMD)
\def\dateY{2020} $\backslash$dateY :: (\dateY)
%% Using the \cal@X macro
A.1 (X) 03-01 :: (\cal@Xdateexpand{03-01})
A.2 (X) 2020-03-01 :: (\cal@Xdateexpand{2020-03-01})
A.3 (X) $\backslash$dateMD :: (\cal@Xdateexpand{\dateMD})
A.4 (X) 2020-$\backslash$dateMD :: (\cal@Xdateexpand{2020-\dateMD}) -- fails%% it fails in this case
A.5 (X) $\backslash$dateYMD :: (\cal@Xdateexpand{\dateYMD})
A.6 (X) $\backslash$dateY-$\backslash$dateMD :: (\cal@Xdateexpand{\dateY-\dateMD}) -- fails %% it fails in this case
%%
A.7 (X) edef 2020-03-01 :: \edef\SOME{\cal@Xdateexpand{2020-03-01}} (\SOME)
A.8 (X) edef 2020-$\backslash$dateMD :: \edef\SOME{\cal@Xdateexpand{2020-\dateMD}} (\SOME) -- fails but compile%% it still fails, but compiles
%% Using the \cal@Y macro
B.1 (Y) 03-01 :: (\cal@Ydateexpand{03-01})
B.2 (Y) 2020-03-01 :: (\cal@Ydateexpand{2020-03-01})
B.3 (Y) $\backslash$dateMD :: (\cal@Ydateexpand{\dateMD})
B.4 (Y) 2020-$\backslash$dateMD :: (\cal@Ydateexpand{2020-\dateMD}) -- desired behaviour % This is the intended behaviour
B.5 (Y) $\backslash$dateYMD :: (\cal@Ydateexpand{\dateYMD})
B.6 (Y) $\backslash$dateY-$\backslash$dateMD :: (\cal@Ydateexpand{\dateY-\dateMD}) -- desired behaviour
%% the two edef below fails to compile, with a "missing control sequence inserted"
%B.7 (X) edef 2020-03-01 :: \edef\SOME{\cal@Ydateexpand{2020-03-01}} (\SOME) -- fails to compile
%B.8 (X) edef 2020-$\backslash$dateMD :: \edef\SOME{\cal@Ydateexpand{2020-\dateMD}} (\SOME) -- fails to compile
\end{document}
\expandafter\A\B\C
, the\expandafter
skips over\A
, expands\B
(once), and does nothing to\C
. You casesA.4
,A.6
, andA.8
don't work because not only the first token needs expanding, so\expandafter
isn't enough (in caseA.8
, if you put\noexpand
before\cal@Xdateexpand
it will work). CasesB.7
andB.8
don't work because nested\(e,x,g)def
don't work as you are expecting them to.\edef
doesn't expand inside another\edef
. When you do\edef\SOME{\cal@Ydateexpand{2020-\dateMD}}
,\cal@Ydateexpand
expands, leaving you with\edef\SOME{\edef\cal@arg{#1}\expandafter\cal@datesplit\cal@arg--\cal@ymd\cal@md\empty}
, then the (nested)\edef
is skipped, and\cal@arg
expands to whatever, and so on. If nothing went wrong so far, you are left with\def\SOME{\edef<expansion-of-\cal@arg>{<expansion-of-#1>}<expansion...>}
. Then only when you use\SOME
, the inner\edef
will be executed.\edef
first triggers the expansion stage, which only expands expandable tokens, and once the expansion is over, then it does the assignment in the second stage (expandable tokens can be macros or expandable primitives (see the list here). Everything else is left untouched).