7

How to define a macro which takes a string made by number and an optional letter and then branches on the basis on the letter?
Since this is an extension of an existing package, it is preferable not to use any package or LaTeX3; furthermore, since the syntax is not defined yet, you could implement the answer with the letter following or preceding the number, with any separator you want (none is better).

\documentclass{article}

\def\myparse#1{...} % or \newcommand{\myparse}[1]{...}

% Suppose the letter has to be after the number with no separator,
% and the command branches on letters "b", "m", "e" to represent
% the beginning, middle or end of a year.

\begin{document}
\myparse{2004b} % Should print "Beginning of 2004"
\myparse{2005}  % Should print "2005"
\myparse{2005m} % Should print "2005" too
\myparse{2006e} % Should print "End of 2006"
\end{document}
  • Why complicating it too much? xparse and or xstring are quite stable packages which can be used basically everywhere? – user31729 Mar 9 '15 at 10:36
  • We might need some more context: for example, how are you defining a 'number' here, do you need an expandable solution, etc. – Joseph Wright Mar 9 '15 at 10:50
  • @JosephWright "Number": sequence of digits (we only deal with positive integers). The number needs to be handled by pgf, but there is no imaginable need to use macros as arguments. – Astrinus Mar 9 '15 at 10:53
10

enter image description here

\documentclass{article}

\makeatletter
\def\myparse#1{\afterassignment\xmyparse\@tempcnta#1\relax}
\def\xmyparse#1\relax{\csname myparsexx#1\endcsname}
\def\myparsexxb{Beginning of \the\@tempcnta}
\def\myparsexx{\the\@tempcnta}
\def\myparsexxm{\the\@tempcnta}
\def\myparsexxe{End of \the\@tempcnta}
\makeatother

% Suppose the letter has to be after the number with no separator,
% and the command branches on letters "b", "m", "e" to represent
% the beginning, middle or end of a year.

\begin{document}
\myparse{2004b} % Should print "Beginning of 2004"

\myparse{2005}  % Should print "2005"

\myparse{2005m} % Should print "2005" too

\myparse{2006e} % Should print "End of 2006"
\end{document}
| improve this answer | |
  • Superb! I really need to read the TeXbook... The definition of \myparse assigns the number to \@tempcnta and gobbles it, then the non-number part of #1 is passed to \xmyparse, right? – Astrinus Mar 9 '15 at 11:04
  • @Astrinus: Right. The really cute idiom, IMHO, is the b, e, m dispatch via the macro identifiers. – Daniel Mar 9 '15 at 11:17
  • @Astrinus yes, the \relax is added as a terminator so after the number assignment xmyparse skoops up everything up to \relax which is nothing or a letter (hopefully:-) – David Carlisle Mar 9 '15 at 11:22
6

You can be liberal in your syntax, allowing the key before or after the year (actually, the key can appear anywhere and multiple times; the last letter appearing will set the behavior).

\documentclass{article}
\usepackage{xparse}

\ExplSyntaxOn
\NewDocumentCommand{\myparse}{m}
 {
  \astrinus_myparse:n { #1 }
 }

\tl_new:N \l_astrinus_myparse_year_tl
\tl_new:N \l_astrinus_myparse_key_tl

\cs_new_protected:Npn \astrinus_myparse:n #1
 {
  \tl_clear:N \l_astrinus_myparse_year_tl
  \tl_clear:N \l_astrinus_myparse_key_tl
  \tl_map_inline:nn { #1 } { \astrinus_add_token:n { ##1 } }
  \astrinus_print:
 }

\cs_new_protected:Npn \astrinus_add_token:n #1
 {
  \tl_if_in:nnTF { 0123456789 } { #1 }
   {
    \tl_put_right:Nn \l_astrinus_myparse_year_tl { #1 }
   }
   {
    \tl_set:Nn \l_astrinus_myparse_key_tl { #1 }
   }
 }

\cs_new_protected:Npn \astrinus_print:
 {
  \str_case:Vn \l_astrinus_myparse_key_tl
   {
    {b}{Beginning~of~}
    {e}{End~of~}
   }
  \tl_use:N \l_astrinus_myparse_year_tl
 }

\cs_generate_variant:Nn \str_case:nn { V }
\ExplSyntaxOff

\begin{document}

\myparse{2004b} % Should print "Beginning of 2004"

\myparse{b2004} % Should print "Beginning of 2004"

\myparse{2005}  % Should print "2005"

\myparse{2005m} % Should print "2005" too

\myparse{m2005} % Should print "2005" too

\myparse{2006e} % Should print "End of 2006"

\myparse{e2006} % Should print "End of 2006"

\end{document}

The input is scanned token by token; digits are accumulated in \l_astrinus_myparse_year_tl, while a letter sets \l_astrinus_myparse_key_tl. Finally we match \l_astrinus_myparse_key_tl with the possible cases and do the appropriate action.

enter image description here

| improve this answer | |
  • 1
    This is also superb, but it loads xparse... and I'm not the package maintainer, who absolutely doesn't want to make it depends on any further package. – Astrinus Mar 9 '15 at 11:06
  • 2
    @Astrinus, tell him that if xparse is used, his package will be future-proof. – AlexG Mar 9 '15 at 11:14
5

An approach using e-TeX to give an expandable solution. The idea is to loop over the input, testing if each token is a digit or not. If it is, simply keep it, while if it's not then assume we've reached the end of the number and trigger a begin/middle/end phase.

\documentclass{article}
\usepackage[T1]{fontenc} % Just so \meaning looks right when printed

\makeatletter
\newcommand*\myparse[1]{%
  \myparse@auxi#1\q@tail\q@stop{}%
}
\newcommand*\myparse@auxi[1]{%
  \ifx\q@tail#1%
    \expandafter\myparse@end
  \fi
  \if\number\numexpr0#1-0#1\relax0%
    \expandafter\myparse@auxii
  \else
    \expandafter\myparse@auxiii
  \fi
    #1%
}
\newcommand*\myparse@auxii{}
\def\myparse@auxii#1#2\q@stop#3{%
  \myparse@auxi#2\q@stop{#3#1}%
}
\newcommand*\myparse@auxiii{}
\def\myparse@auxiii#1#2\q@stop{%
  \csname myparse@aux@#1\endcsname
}
\newcommand*\myparse@aux@b[1]{Beginning of #1}
\newcommand*\myparse@aux@m[1]{#1}
\newcommand*\myparse@aux@e[1]{End of #1}

\newcommand*\myparse@end{}
\def\myparse@end#1\q@stop#2{#2}

\def\q@tail{\q@tail}
\makeatother
\begin{document}
\myparse{2004b} % Should print "Beginning of 2004"

\myparse{2005}  % Should print "2005"

\myparse{2005m} % Should print "2005" too

\myparse{2006e} % Should print "End of 2006"

\edef\test{\myparse{2006e}}\meaning\test
\end{document}

The solution is in this sense similar to that by David Carlisle except that the digit detection is done in macros rather than using a TeX primitive assignment.

| improve this answer | |
  • I'm assuming 'well-behaved' input, no spaces, no macros, etc. but the question seems to suggest this is OK. – Joseph Wright Mar 9 '15 at 11:28
  • I've accepted David Carlisle answer, because is shorter and simpler and, for the time being, it is enough. But this is beautiful (e)TeX too! Should the need arise, I'll remember of this answer (upvoted). – Astrinus Mar 9 '15 at 11:58

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.