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\expandafterskips over\A, expands\B(once), and does nothing to\C. You casesA.4,A.6, andA.8don't work because not only the first token needs expanding, so\expandafterisn't enough (in caseA.8, if you put\noexpandbefore\cal@Xdateexpandit will work). CasesB.7andB.8don't work because nested\(e,x,g)defdon't work as you are expecting them to. – Phelype Oleinik Mar 09 '20 at 18:32\edefdoesn't expand inside another\edef. When you do\edef\SOME{\cal@Ydateexpand{2020-\dateMD}},\cal@Ydateexpandexpands, leaving you with\edef\SOME{\edef\cal@arg{#1}\expandafter\cal@datesplit\cal@arg--\cal@ymd\cal@md\empty}, then the (nested)\edefis skipped, and\cal@argexpands 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\edefwill be executed. – Phelype Oleinik Mar 09 '20 at 19:21\edeffirst 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). – Phelype Oleinik Mar 09 '20 at 19:47