Проблема с расширением команды в макросе xstring? "Неполный \iffalse"

Проблема с расширением команды в макросе xstring? "Неполный \iffalse"

Я написал следующие команды, которые сначала делят входной текст на части, ;а затем делают «дроби» из текста, разделенного /. Моя цель — отобразить генотипы с правильным форматированием.

\documentclass{article}

\usepackage{amsmath}
\usepackage{xstring}

\newcommand\genoLineDist{.2mm}
\newcommand{\genoSplit}[2]{%
    $\begin{array}{@{}c@{}}
    \text{\protect#1} \\
    \noalign{\vskip\genoLineDist}
    \hline
    \noalign{\vskip\genoLineDist}
    \text{\protect#2}
    \end{array}$%
}

\newcommand{\geno}[1]{%
    \def\remainder{#1}%
    \def\splitchar{;}%
    \genoHelper
}
\newcommand{\genoHelper}{%
    \IfSubStr{\remainder}{\splitchar}{%
        \StrBefore{\remainder}{\splitchar}[\firstpart]%
        \StrBehind{\remainder}{\splitchar}[\remainder]%
        \genoSplitHelper
        \ifx\remainder\empty
        \else
        \ ;\ \genoHelper
        \fi
    }{%
        \def\firstpart{\remainder}%
        \genoSplitHelper
    }%
}
\newcommand{\genoSplitHelper}{%
    \IfSubStr{\firstpart}{/}{%
        \StrBefore{\firstpart}{/}[\upperGeno]%
        \StrBehind{\firstpart}{/}[\lowerGenoTemp]%
        \IfSubStr{\lowerGenoTemp}{\splitchar}{%
            \StrBefore{\lowerGenoTemp}{\splitchar}[\lowerGeno]%
            \StrBehind{\lowerGenoTemp}{\splitchar}[\remainder]%
        }{\def\lowerGeno{\lowerGenoTemp}}%
        \genoSplit{\upperGeno}{\lowerGeno}%
    }{%
        \firstpart\unskip%
    }%
}

\begin{document}

%working
\geno{y w ; ap$^{DG3}$ / CyO}
%breaks
%\geno{ y w ; ap\textsuperscript{DG3} / CyO}

\end{document}

Теперь, введя следующую команду в тексте документа

\geno{y w ; ap$^{DG3}$ / CyO}

Мы производим следующий вывод:

генотип

Однако при попытке включить какие-либо команды, например \textsuperscript, или символы, например \Maleили \Femaleв любое место входной строки, я сталкиваюсь со следующей ошибкой:

Incomplete \iffalse; all text was ignored after line [line with \geno command].

Моя лучшая догадка заключается в том, что я сталкиваюсь с проблемами с расширением аргумента в одном из операторов if, поэтому я попытался разместить некоторые \protects в местах, которые, как я подозревал, могли бы вызвать проблемы, например, когда \remainderis first defined в \genoкоманде и \IfSubStrto little success. Я также пытался использовать \MakeRobustкоманду следующим образом:

%definitions above here
\MakeRobust{\geno}
\MakeRobust{\genoHelper}
\MakeRobust{\genoSplitHelper}

Но, к сожалению, проблема осталась.

Другие слепые выстрелы в темноте включают в себя:

  • заменив \ifxна \if\relax\detokenize{\remainder}\relax(та же ошибка)
  • \detokenizeвводим данные в \IfSubStr(кажется, все они возвращают false, может быть, делаем что-то неправильно?)
  • Использование различных альтернатив для \genoSplitкоманды (та же ошибка, теперь я уверен, что проблема не в этом)
  • Переписываем все команды для использования \newcommandи \renewcommand(та же ошибка)

Я также нашелэтот вопрос, но я не уверен, что это та же проблема, с которой я столкнулся, и принятый ответ ( \begingroup\noexpandarg [...] \endgroup) препятствует работе макроса.

Я в растерянности и был бы очень признателен, если бы мне объяснили, что является причиной этой проблемы.

решение1

xstringsне любит хрупких команд в своих аргументах.

Вот expl3реализация: сначала мы разделяем по точкам с запятой; затем каждый элемент передается в качестве аргумента функции, которая создает поддельную дробь, если /она присутствует.

\documentclass{article}

\usepackage{amsmath}

\newcommand\genoLineDist{.2mm}

\ExplSyntaxOn
\NewDocumentCommand{\geno}{m}
 {
  \tired_geno:n { #1 }
 }

\seq_new:N \l__tired_geno_parts_in_seq
\seq_new:N \l__tired_geno_parts_out_seq
\seq_new:N \l__tired_geno_temp_seq

\cs_new_protected:Nn \tired_geno:n
 {
  \seq_set_split:Nnn \l__tired_geno_parts_in_seq { ; } { #1 }
  \seq_set_map:NNn \l__tired_geno_parts_out_seq \l__tired_geno_parts_in_seq
   {
    \__tired_geno_split:n { ##1 }
   }
  \seq_use:Nn \l__tired_geno_parts_out_seq { \ ; \  }
 }

\cs_new_protected:Nn \__tired_geno_split:n
 {
  \seq_set_split:Nnn \l__tired_geno_temp_seq { / } { #1 }
  \int_compare:nTF { \seq_count:N \l__tired_geno_temp_seq == 1 }
   {
    #1 % no /
   }
   {
    \begin{tabular}{@{}c@{}}
    \seq_item:Nn \l__tired_geno_temp_seq { 1 } \\
    \noalign{\vskip\genoLineDist}
    \hline
    \noalign{\vskip\genoLineDist}
    \seq_item:Nn \l__tired_geno_temp_seq { 2 } \\ 
    \end{tabular}
   }
 }

\ExplSyntaxOff

\begin{document}

\geno{y w ; ap$^{DG3}$ / CyO}

\geno{ y w ; ap\textsuperscript{DG3} / CyO}

\geno{y/w ; ap$^{DG3}$ / CyO}

\end{document}

введите описание изображения здесь

решение2

Используя только примитивные команды TeX, вы можете определить свой \genoмакрос следующим образом:

\def\geno#1{\def\genoS{\def\genoS{; }}\genoA #1; \end; }
\def\genoA#1; {\ifx\end#1\else
      \genoS 
      \isinslash#1/\iffalse #1\else \genoB #1/\fi
      \expandafter\genoA
   \fi
} 
\def\genoB #1/#2/{$\displaystyle{\hbox{#1\unskip}\over\hbox{\ignorespaces#2\unskip}}$ }
\def\isinslash #1/#2\iffalse{\ifx/#2/} 

\geno{y w ; ap$^{DG3}$ / CyO ; next ; A/B}

решение3

Этого может быть достаточно, используя listofitemsсинтаксический анализ. ОТРЕДАКТИРОВАНО для обработки более общего синтаксиса, указанного автором в комментарии.

\documentclass{article}
\usepackage{amsmath}
\usepackage{listofitems}
\newcommand\geno[1]{%
  \setsepchar[&]{;&/}%
  \readlist*\myterm{#1}%
  \foreachitem\z\in\myterm[]{%
    \ifnum\zcnt=1 \else \ ;\ \fi
    \ifnum\listlen\myterm[\zcnt]=1
      \myterm[\zcnt]%
    \else
      $\displaystyle
        \frac{\text{\myterm[\zcnt,1]}}{\text{\myterm[\zcnt,2]}}$%
    \fi
  }%
}
\begin{document}
\geno{y w ; ap$^{DG3}$ / CyO}

\bigskip
\geno{ y w ; ap\textsuperscript{DG3} / CyO}

\bigskip
\geno{ y w ; ap\textsuperscript{DG3} / CyO ;
   bp\textsuperscript{DG0} / CzO ; z x}
\end{document}

введите описание изображения здесь

Связанный контент