9

I would like to write a macro (using Latex3/expl3) that can automatically refer to say plot using several key-value parameters. Here is what I currently have:

\keys_define:nn{label}{
    Re .tl_set:N = \l_label_Re_tl, 
    DOF .tl_set:N = \l_label_DOF_tl,
    statsName .tl_set:N = \l_label_statsName_tl,
}


\NewDocumentCommand{\getProjOneLabel}{m}{
    \keys_set:nn{label}{#1}
    \newcommand{\Stat}{\StrLeft{\l_label_statsName_tl}{3}} %1st 3 letters of
    fig:{\Stat}Re\l_label_Re_tl DOF\l_label_DOF_tl
}

when I test it with: label=\getProjOneLabel{statsName=Strouhal,DOF=4M, Re=50} It shows:

label=fig:StrRe50DOF4M

which is correct. But when I use it for real referencing like this:

\ref{\getProjOneLabel{statsName=Strouhal,DOF=4M, Re=50}}

instead of reference in the output this shown:

??fig:StrRe50DOF4M

I think this has smth to do with order of expansion, but I don't understand expansion process fully yet. Tried putting \expandafter many times, still fails. Could smbd please explain step by step what goes wrong here? When I hardcode label as

\ref{fig:StrRe50DOF4M}

it does work. The goal is to write a macro say '\plotref' that will execute

\ref{\getProjOneLabel{statsName=Strouhal,DOF=4M, Re=50}}

where all RHS values will be passed as key-valued parameters. I am new to Latex3 and even to regular Latex/Tex, the more details the better. Thank you very much.

Update: Below is a test code based on egreg's sample. I removed colon from label to simplify troubleshooting. When I comment line

\tl_set:Nx \l_mylabel_tl{ \getProjOneLabel{#1} }

and uncomment

%\tl_set:Nx \l_mylabel_tl{StrRe50DOF4M}

it works.

\documentclass{article}
    \usepackage{xparse}
    \ExplSyntaxOn

\keys_define:nn { label }
 {
  Re        .tl_set:N = \l_label_Re_tl, 
  DOF       .tl_set:N = \l_label_DOF_tl,
  statsName .tl_set:N = \l_label_statsName_tl,
  Re        .value_required:n = true,
  DOF       .value_required:n = true,
  statsName .value_required:n = true,
 }
    \NewDocumentCommand{\getProjOneLabel}{m}{
        \keys_set:nn{label}{#1}
        %fig \c_colon_str
        Str Re\l_label_Re_tl DOF\l_label_DOF_tl
    }


\NewDocumentCommand{\keyref}{m}
{
    %\keys_set:nn { label } { #1 }
  \tl_new:N \l_mylabel_tl
    %\tl_set:Nx \l_mylabel_tl{StrRe50DOF4M}
    \tl_set:Nx \l_mylabel_tl{ \getProjOneLabel{#1} }
    \\ label=\l_mylabel_tl \\ %checking label
  Reference~ \ref{ \l_mylabel_tl  }
}

\ExplSyntaxOff

\begin{document}

\section{X}
\label{StrRe50DOF4M} % just to experiment

\keyref{statsName=Strouhal,DOF=4M,Re=50} (should be 1)

\end{document}

%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% addpic macro code:

%usage example
% \addpic{
  % width=0.3,
  % image=example-image,
  % caption={This is an example image, and a comma in the caption},
  % label=one,
% }

% \addpic{
  % placement=bp,
  % width=0.2,
  % options={angle=90},
  % image=example-image-a,
  % caption=Rotated image,
  % shortcaption=In the text the image is rotated!,
  %}


\ExplSyntaxOn

% the user level command
\NewDocumentCommand{\addpic}{m}
 {
  \group_begin: % localize the changes to the variables
  \simonson_pic:n { #1 }
  \group_end:
 }

% the key-value interface
\keys_define:nn { simonson/pic }
 {
  placement .tl_set:N = \l_simonson_pic_placement_tl,
  placement .initial:n = htp,
  width .tl_set:N = \l_simonson_pic_width_tl,
  width .initial:n = 1,
  options .tl_set:N = \l_simonson_pic_options_tl,
  image .tl_set:N = \l_simonson_pic_image_tl,
  caption .tl_set:N = \l_simonson_pic_caption_tl,
  shortcaption .tl_set:N = \l_simonson_pic_shortcaption_tl,
  label .tl_set:N = \l_simonson_pic_label_tl,
 }

% the main command
\cs_new_protected:Nn \simonson_pic:n
 {
  % set the keys from the argument
  \keys_set:nn { simonson/pic } { #1 }
  % start the figure environment
  \__simonson_start_figure:V \l_simonson_pic_placement_tl
  \centering
  % include the image
  \__simonson_pic_image:VVV
    \l_simonson_pic_width_tl % the text width fraction
    \l_simonson_pic_options_tl % other options
    \l_simonson_pic_image_tl % the image name
  % the caption
  \tl_if_empty:NTF \l_simonson_pic_shortcaption_tl
   {
    \caption{\l_simonson_pic_caption_tl}
   }
   {
    \caption[\l_simonson_pic_shortcaption_tl]{\l_simonson_pic_caption_tl}
   }
   % the label
     In \ addpics \ macro:\ label=\l_simonson_pic_label_tl %for test(my code)
   \tl_if_empty:NF \l_simonson_pic_label_tl
    {
     \label{\l_simonson_pic_label_tl}
    }
   % end the figure environment

   %### %for parser to ignore
   \end{figure}
   %###
}

% syntactic sugar: we want some token lists to be expanded before usage
\cs_new_protected:Nn \__simonson_start_figure:n
 {
  \begin{figure}[#1]
 }
\cs_generate_variant:Nn \__simonson_start_figure:n { V }

\cs_new_protected:Nn \__simonson_pic_image:nnn
 {
  \includegraphics[width=#1\textwidth,#2]{#3}
 }
\cs_generate_variant:Nn \__simonson_pic_image:nnn { VVV }

\ExplSyntaxOff

In case smbd would like to follow continuation of this for plotting macro purpose, new thread is here: Macro for pictures with generated labels

  • 1
    Welcome to TeX.SX! Please provide a compilable document, not just fragments. We don't want to make guess-work and glue fragments together! – user31729 Apr 5 '16 at 21:09
  • And I guess you run into issues about the : character. See this recent question about : and expl3 : tex.stackexchange.com/questions/302387/… – user31729 Apr 5 '16 at 21:10
  • I also think that your \getProjOneLabel command is not expandable – user31729 Apr 5 '16 at 21:17
  • 1
    Assignments like key settings are not permitted in the argument of \ref which should be expandable with the reference name as expansion result. A solution would be to move \getProjOneLabel outside of \ref and define a macro with the label name there. Then the expandable macro name can be used inside \ref. – Heiko Oberdiek Apr 5 '16 at 21:18
  • Thank you guys for help! I put \getProjOneLabel outside of \ref, still doesn't work. Also removed colon for testing. Why is \getProjOneLabel not expandable? Is there a way to make it expandable? – strider Apr 6 '16 at 0:46
8

\ref should be passed something that fully expands to a string, not instructions to build a string.

So first you have to set the keys and then you can pass them in the desired way to \ref. The same code can be shared between \keyref and \keylabel.

\documentclass{article}

\usepackage{xparse}

\ExplSyntaxOn
\keys_define:nn { strider/label }
 {
  Re        .tl_set:N = \l_strider_label_Re_tl, 
  DOF       .tl_set:N = \l_strider_label_DOF_tl,
  statsName .tl_set:N = \l_strider_label_statsName_tl,
  Re        .value_required:n = true,
  DOF       .value_required:n = true,
  statsName .value_required:n = true,
 }

\NewDocumentCommand{\keyref}{m}
 {
  \strider_label:Nn \ref { #1 }
 }
\NewDocumentCommand{\keylabel}{m}
 {
  \strider_label:Nn \label { #1 }
 }

\cs_new_protected:Nn \strider_label:Nn
 {
  \keys_set:nn { strider/label } { #2 }
  #1 % will be \label or \ref
   {
    fig \c_colon_str 
    \tl_item:Nn \l_strider_label_statsName_tl { 1 }
    \tl_item:Nn \l_strider_label_statsName_tl { 2 }
    \tl_item:Nn \l_strider_label_statsName_tl { 3 }
    Re \l_strider_label_Re_tl DOF \l_strider_label_DOF_tl
   }
 }
\ExplSyntaxOff

\begin{document}

\section{X}
\keylabel{statsName=Strouhal,DOF=4M, Re=50} % just to experiment

This gives a number: \keyref{statsName=Strouhal,DOF=4M, Re=50} (should be 1)

\end{document}

enter image description here

  • This works, but I would like to make the code inside \ref reusable, that is put it in some macro. For example the label will be generated by the same code when inserting a plot. – strider Apr 6 '16 at 0:31
  • @strider Updated, with a \keylabel command – egreg Apr 6 '16 at 8:29
  • sorry, I had to put this question aside for a while. I like your approach, but I use another macro \addpic which takes parameters including label. And I cannot figure out how to pass label as parameter to it. Can of course hardcode your code for generating label inside \addpic, but would like to avoid this as it makes \addpic very specific. I found \addpic code on the internet, don't understand it fully. It seems that passing any hardcoded label to a macro is possible b/c \addpic code works like this. – strider May 20 '16 at 0:07
  • I added \addpic macro code to the bottom of first post. I mean your macros seems to assume they are used at the top level of the code, right? or can I pass result of \keylabel{...} as parameter to \addpic? – strider May 20 '16 at 0:16
  • @strider Why changing the question with something that seems completely unrelated? – egreg May 20 '16 at 9:55
2

Your \getProjOneLabel was not full expandable because \newcommand (i.e. assignment) was in it. You can try the "full expandability" by \message primitive:

\message{LABEL=\getProjOneLabel{statsName=Strouhal,DOF=4M, Re=50}}

It would print the result LABEL=fig:StrRe50DOF4M on the terminal.

I don't prefer expl3 programming, so my solution uses only TeX primitives:

\def\getProjOneLabel#1{fig:\gpName#1,\end Re\gpRe#1,\end DOF\gpDOF#1,\end}
\def\gpName#1statsName=#2#3#4#5\end{#2#3#4}
\def\gpRe#1Re=#2,#3\end{#2}
\def\gpDOF#1DOF=#2,#3\end{#2}

\message{LABEL=\getProjOneLabel{statsName=Strouhal,DOF=4M, Re=50}}

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.