3

If I want to conditionally expand a glossary entry based on or related to another glossary term, I find the conditional \ifglsused{}{}{} statement is very helpful.

To satisfy parent modifications in the case where the parent term hasn't yet been used, I use \glsdisp{}{} to make sure the prerequisite gets 'used' but not displayed as programmed by first use.

As demonstrated with \ifglsused{TNF}{\glsdisp{TNF}{\TNFalpha}}{\glsdisp{TNF}{tumor necrosis factor--\textalpha~(\TNFalpha)}} item in the MWE below, this works fine.

Interestingly, this identical evaluation fails when it is part of the \newglossaryentry{} i.e. inside the first={} declaration of the child term itself.

MWE:

\documentclass{article}
\usepackage[utf8]{inputenc}

\setlength\parindent{0pt}

%=========================================================================================================================================
% PACKAGES REQUIRED FOR GLOSSARIES
%=========================================================================================================================================

% Glossaries must be loaded before amsmath as per details in the following forum answer
% https://tex.stackexchange.com/questions/85696/what-causes-this-strange-interaction-between--and-amsmath
\usepackage[nogroupskip,toc,acronym]{glossaries} % must come after href   
\usepackage{scrwfile}%http://www.dickimaw-books.com/cgi-bin/faq.cgi?action=view&categorylabel=glossaries#glsnewwriteexceeded
\usepackage{siunitx,microtype,textcomp,textgreek}
\usepackage{etoolbox}
\makeglossaries

\newglossaryentry{TNF}{ 
    type={acronym}, 
    sort={tumor necrosis factor},  
    name={TNF}, 
    short={TNF}, 
    long={tumor necrosis factor}, 
    first={tumor necrosis factor (TNF)}, 
    description={tumor necrosis factor}     
}

\newcommand{\TNFalpha}{TNF--{\textalpha}}
\newglossaryentry{TNFalpha}{ 
    type={acronym}, 
    sort={tumor necrosis factor alpha},  
    name={TNF--{\textalpha}}, 
    short={TNF--{\textalpha}}, 
    long={tumor necrosis factor alpha}, 
    first={\ifglsused{TNF}{\glsdisp{TNF}{\TNFalpha}}{\glsdisp{TNF}{tumor necrosis factor--\textalpha~(\TNFalpha)}}}, 
    description={tumor necrosis factor alpha}
}

\begin{document}

    \begin{itemize}
        %\item \gls{TNFalpha}
        \item \gls{TNF}
        \item \gls{TNFalpha}
        \item \glsentryfirst{TNFalpha}
        \item \ifglsused{TNF}{\glsdisp{TNF}{\TNFalpha}}{\glsdisp{TNF}{tumor necrosis factor--\textalpha~(\TNFalpha)}}
    \end{itemize}

\end{document} 

Update: I have solved this problem by creating a new glossary key (that specifies the base) and new function that performs the same use and display logic independently of the creation of the glossary entry. The function template that I based this off of is here: Create new fields in glossaries newglossaryentry

So since the reason behind this question - nested glossary expansions with minimal duplication - is resolved via a completely different approach, I would still be interested in learning about the nuances of newglossaryentry

  • I am am guessing that this means that somehow first={} is being evaluated before \gls{} actually calls it and thus no use boolean has been changed thus guaranteeing a perpetual false evaluation without failing, but it would be great to know.

  • My next guess would be that this has something to do with \gls{}'s protected status or even just the order the code is evaluated in?

1 Answer 1

3

There are two main issues here.

Field Expansion

From the user manual:

When you define new glossary entries expansion is performed by default, except for the name, description, descriptionplural, symbol, symbolplural and sort keys (these keys all have expansion suppressed via \glssetnoexpandfield).

(The reason for these exceptions is for backward compatibility to earlier versions that used to write that information to the glossary file. The expansion suppression helped to protect fragile commands in the write process.)

To see this in action we can use the glossaries debugging commands (only described in the documented code, not in the user manual). \showgloname will show the definition of the name field, \showglofirst will show the definition of the first field and \showglotext will show the definition of the text field (one argument required in each case, and that's the entry label). (I've trimmed down the MWE.)

\documentclass{article}

\usepackage{textgreek}
\usepackage[nogroupskip,toc,acronym]{glossaries}
\makeglossaries

\newglossaryentry{TNF}{ 
  type={acronym}, 
  sort={tumor necrosis factor},  
  name={TNF}, 
  first={tumor necrosis factor (TNF)}, 
  description={tumor necrosis factor}
}   

\newcommand{\TNFalpha}{TNF--{\textalpha}}
\newglossaryentry{TNFalpha}{
    type={acronym},
    sort={tumor necrosis factor alpha},
    name={\TNFalpha},
    first={tumor necrosis factor alpha~(\TNFalpha)},   
    description={tumor necrosis factor alpha}
}

\begin{document}
\showglofirst{TNF}
\showglofirst{TNFalpha}

\showgloname{TNF}
\showgloname{TNFalpha}

\showglotext{TNF}
\showglotext{TNFalpha}

\end{document}

This doesn't create any output, but it shows the definitions in the transcript. (When run in TeX's interactive mode, these commands will interrupt the run as though they were error messages.) Below are the relevant parts of the transcript.

The value of the first field for the TNF entry:

> \glo@TNF@first=macro:
->tumor necrosis factor (TNF).

The value of the first field for the TNFalpha entry:

> \glo@TNFalpha@first=macro:
->tumor necrosis factor alpha\protect \nobreakspace  {}(TNF--{\textalpha }).

So here the \TNFalpha command has been expanded and so has the non-breakable space ~ but \textalpha doesn't get expanded because it's protected.

The value of the name field for the TNF entry:

> \glo@TNF@name=macro:
->TNF.

The value of the name field for the TNFalpha entry:

> \glo@TNFalpha@name=macro:
->\TNFalpha .

Here \TNFalpha hasn't been expanded, because the name key isn't expanded by default.

The text key wasn't explicitly used so it picked up its value from the name field, but in this case expansion is performed.

The value of the text field for the TNF entry:

> \glo@TNF@text=macro:
->TNF.

The value of the text field for the TNFalpha entry:

> \glo@TNFalpha@text=macro:
->TNF--{\textalpha }.

Unlike with the name field, \TNFalpha has now been expanded.

Therefore, if you use \ifglsused within the first key, by default it will be evaluated when the entry is defined. If the above example is changed so that the definition for TNFalpha is now:

\newglossaryentry{TNFalpha}{
    type={acronym},
    sort={tumor necrosis factor alpha},
    name={\TNFalpha}, 
    first={\ifglsused{TNF}{\TNFalpha}{tumor necrosis factor alpha~(\TNFalpha)}}, 
    description={tumor necrosis factor alpha}
}

Then \showglofirst{TNFalpha} still produces the same result:

> \glo@TNFalpha@first=macro:
->tumor necrosis factor alpha\protect \nobreakspace  {}(TNF--{\textalpha }).

This is because when TNFalpha is defined, TNF hasn't been used so it definition expands to the false part (third argument) of \ifglsused.

Nested Links

If you add \glsdisp (or any similar command) to the first field, you'll end up with nested links. Both \glsdisp and \gls internally use the same command \@gls@link to deal with the hyperlink and to wrap the link text inside \glstextformat. So nesting these commands can cause problems.

The simplest solution is to switch off the expansion for the first and firstplural fields, remove \glsdisp from the field value and just use \glsunset to mark the TNF entry as having been used. Like this:

\documentclass{article}

\usepackage{textgreek}
\usepackage[nogroupskip,toc,acronym]{glossaries}
\makeglossaries

\newglossaryentry{TNF}{ 
  type={acronym}, 
  sort={tumor necrosis factor},  
  name={TNF}, 
  first={tumor necrosis factor (TNF)}, 
  description={tumor necrosis factor}
}   

\glssetnoexpandfield{first}
\glssetnoexpandfield{firstpl}

\newcommand{\TNFalpha}{TNF--{\textalpha}}
\newglossaryentry{TNFalpha}{
    type={acronym},
    sort={tumor necrosis factor alpha},
    name={\TNFalpha}, 
    first={\ifglsused{TNF}{\TNFalpha}{\glsunset{TNF}tumor necrosis factor alpha~(\TNFalpha)}},
    description={tumor necrosis factor alpha}
}

\begin{document}
\gls{TNFalpha}. \gls{TNF}.

\end{document} 

This produces:

tumor necrosis factor alpha (TNF-alpha). TNF.

If you swap them around so that you have

\gls{TNF}. \gls{TNFalpha}.

instead of

\gls{TNFalpha}. \gls{TNF}.

then the result is

tumor necrosis factor (TNF). TNF-alpha.

Edit: The expansion settings are checked each time a new entry is defined, but you only need to switch expansion on again if you have an entry that needs to have the fields expanded. For example:

\newcommand{\stuff}{foo}
\newglossaryentry{stuff1}{name={\stuff},description={stuff1}}
\renewcommand{\stuff}{bar}
\newglossaryentry{stuff2}{name={\stuff},description={stuff2}}

This type of definition looks a bit weird when done explicitly like this, but it's sometimes done by commands that internally use \newglossaryentry. In the above example, \stuff needs to be expanded when the entry is defined as \stuff is just a temporary command whose definition keeps changing. If you don't have this situation, then you can just put all your \glsetnoexpandfield commands before you start defining any of your entries.

Case-Changing

The first letter upper casing commands such as \Gls use \makefirstuc provided by mfirstuc. This command does try to deal with the possibility that the argument may contain a formatting command, but because there's no generic way to determine the syntax of a command, specifically which argument is text and which is a label, \makefirstuc has to apply some restrictions in order to work properly.

  1. If the argument starts with \protect, this is discarded, and \makefirstuc is applied to the remainder. For example, \makefirstuc{\protect\textbf{foo}} is the same as \makefirstuc{\textbf{foo}}.
  2. The argument of \makefirstuc may start with just text. For example \makefirstuc{foo} just does \MakeUppercase foo which results in Foo. Whereas \makefirstuc{{fo}o} does \MakeUppercase{fo}o which results in FOo.
  3. If the argument of \makefirstuc starts with a control sequence without an argument, that control sequence is assumed to be a character control sequence such as \ae or \o and the case change is applied to that. For example, \makefirstuc{\ae foo} does \MakeUppercase\ae foo which results in Æfoo. This means that

    \newcommand{\foo}{foo}\makefirstuc{\foo}
    

    does \MakeUppercase\foo which produces FOO.

  4. If the argument of \makefirstuc starts with a control sequence followed by a group, the control sequence is assumed to be a formatting command. The grouped material is assumed to be text and the case change is applied to that. For example, \makefirstuc{\textbf{foo}} is equivalent to \textbf{\MakeUppercase foo} which results in Foo.

No expansion is performed on the argument of \makefirstuc as this could cause simple text-block commands with a single argument to be expanded into something too complicated to parse.

Returning to the MWE, \Gls{TNFalpha} on first use (or \Glsfirst{TNFalpha}) attempts

\makefirstuc{\ifglsused{TNF}{\TNFalpha}{\glsunset{TNF}tumor necrosis
factor alpha~(\TNFalpha)}}

This falls into case 4 (control sequence followed by a group). So this attempts to do

{\ifglsused{\MakeUppercase TNF}{\TNFalpha}{\glsunset{TNF}tumor necrosis 
factor alpha~(\TNFalpha)}}

which is the cause of your error message. The only way to resolve the problem is to define a command where the first argument is the text that requires the case change. For example:

% \ifnotused{not used}{used}{label}
\newcommand*{\ifnotused}[3]{%
  \ifglsused{#3}{#2}{\glsunset{#3}#1}%
}

\newcommand{\TNFalpha}{TNF--{\textalpha}}
\newglossaryentry{TNFalpha}{
    type={acronym},
    sort={tumor necrosis factor alpha},
    name={\TNFalpha}, 
    first={\ifnotused{tumor necrosis factor alpha~(\TNFalpha)}{\TNFalpha}{TNF}},
    description={tumor necrosis factor alpha}
}

Since \TNFalpha already starts with a capital there's no need to worry about dealing with the case when the conditional in \ifnotused selects the second argument.

0

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.