Добавление символа в список токенов с использованием его кода символа

Добавление символа в список токенов с использованием его кода символа

Есть ли способ получить фактический токен символа из его представления кода символа? В частности, я хотел бы иметь макрос \prepend#1#2, который берет список токенов и добавляет к нему #1токен символа, соответствующий коду символа .#2

Вот краткая демонстрация желаемого результата:

\newtoks\test
\test={bc}
\prepend\test{97}
\showthe\test % should print abc

решение1

Вы можете использовать \char_generate:nn { <charcode> } { <catcode> }:

\input expl3-generic
\ExplSyntaxOn
\cs_new_eq:NN \toks_use:N \tex_the:D
\cs_new_protected:Npn \prepend #1 #2
  {
    \if:w \exp_not:N #1 #1
      \use:x { #1 = { \char_generate:nn {#2} { 12 } \toks_use:N #1 } }
    \else:
      \tl_put_left:Nx #1 { \char_generate:nn {#2} { 12 } }
    \fi:
  }
\ExplSyntaxOff

\newtoks\test
\test={bc}
\prepend\test{97}
\showthe\test % should print abc

\def\test{bc}
\prepend\test{97}
\show\test % should print abc

\bye

Вывод терминала будет следующим:

> abc.
l.16 \showthe\test
                   % should print abc
? 
> \test=macro:
->abc.
l.21 \show\test
                % should print abc again
?

То, как \char_generate:nnгенерируются символы, зависит от используемого движка. В LuaTeX он использует tex.cprint(<catcode>, utf8_char(<charcode>)), аналогично ответу Анри, но с возможной <catcode>настройкой. В XeTeX он использует \Ucharcat <charcode> <catcode>.

В других поддерживаемых движках expl3( pdftex, ε-pTeX, и ε-upTeX) нет возможности фактическигенерироватьсимволы в контекстах, предназначенных только для расширения (ключевая особенность \char_generate:nn), поэтому expl3предварительно генерирует эти символы, используя тот же подход, что и в ответе egreg, а затем \char_generate:nnпросто использует эти символы по запросу.

Как и в ответе egreg, вы не можете генерировать символы некоторых catcodes, а именно 0, 5, 9, 14 и 15, потому что они не производят токены (они исчезают, когда TeX сканирует входные данные, поэтому они не существуют на уровне макрорасширения). Кроме того, реализация expl3не позволяет генерировать символы пробелов для согласованности между движками, потому что версия Lua этого не позволяет. Однако, поскольку вам нужна версия Knuth TeX, символы пробелов также разрешены.


Код ниже — это адаптация кода expl3для \char_generate:nnмодифицированного для работы в Knuth TeX. Код в основном тот же, за исключением того, что необходимо несколько дополнительных усложнений, в основном из-за отсутствия \unexpanded, что позволяет вам иметь токены с одним параметром в макросе и позволяет вам легко добавлять что-то в макрос без необходимости регистра toks. В остальном это то же самое.

Сначала код определяет временный регистр toks, который содержит нулевой символ ( ^^@) с различными возможными кодами catcodes, разделенными знаком \or:

\or ^^@% 1
\or ^^@% 2
\or ^^@% 3
\or ^^@% 4
\or    % 5 Invalid
\or ^^@^^@% 6 Has to be doubled for a later `\def`
\or ^^@% 7
\or ^^@% 8
\or    % 9 Invalid
\or ^^@% 10
\or ^^@% 11
\or ^^@% 12
\or ^^@% 13

затем он перебирает все 256коды символов и устанавливает значение \lccodeнулевого символа в , #1а затем использует \lowercaseтрюк из ответа egreg:

    \begingroup
      \lccode0=#1
      \lccode32=#1
      \edef\x{\endgroup
      \gdef\expandafter\noexpand
        \csname c__char_\romannumeral#1_tl\endcsname{\the\tmptoks}}%
      \lowercase\expandafter{\x}

что для кода символа, скажем, 97, приводит к:

\gdef\c__char_xcvii_tl{\or a\or a\or a\or a\or \or aa\or a\or a\or \or a\or a\or a\or a}

затем, имея код символа, <charcode>вы можете получить доступ к этому списку токенов с помощью \csname c__char_\romannumeral<charcode>_tl\endcsname, а затем с помощью `\ifcase\fi вы получаете требуемый символ.

Макрос \chargenerateсначала проверяет (в \generateaux), находятся ли аргументы в допустимом диапазоне (catcode от 1 до 13, за исключением 5 и 9, и charcode от 0 до 255, хотя с помощью Knuth TeX вам может потребоваться изменить его на 127), а затем вызывает \generateauxiс аргументами, который затем использует \ifcaseприведенную выше проверку (с несколькими дополнительными частями для управления расширением), чтобы оставить запрошенный символ.

Запустив код ниже, texя получаю:

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

% Auxiliaries
\long\def\gobbletoqstop#1\qstop{}
\long\def\firstofone#1{#1}
\chardef\expend=0
% Expandable error message
\begingroup
\long\xdef\expandableerror#1{%
  \noexpand\expandafter\noexpand\expandafter\noexpand\expandafter
    \noexpand\gobbletoqstop\noexpand\firstofone
      {\csname Error! \endcsname#1}\noexpand\qstop}
\endgroup
% Append stuff to a toks register
\def\toksputright#1{%
  \begingroup
    \def\toksputtoks{#1}%
    \afterassignment\toksputrightaux
    \toks0=}
\def\toksputrightaux{%
    \edef\x{\endgroup
      \toksputtoks={\the\toksputtoks\the\toks0}}%
  \x}
% Set up constant token lists
\newtoks\tmptoks
\begingroup
  \tmptoks{ \noexpand\or}%
  \catcode0=1
  \toksputright\tmptoks{^^@\iffalse}}%
  \catcode0=2
  \toksputright\tmptoks{{\fi\noexpand\or^^@}%
  \begingroup
    \def\noop{}%
    \edef\x{\expandafter\noop\the\tmptoks}%
  \expandafter\endgroup
  \expandafter\tmptoks\expandafter{\x}%
  \catcode0=3  \toksputright\tmptoks{\or^^@}%
  \catcode0=4  \toksputright\tmptoks{\or^^@}%
  \catcode0=5  \toksputright\tmptoks{\or}%
  \catcode0=6  \toksputright\tmptoks{\or^^@^^@}%
  \catcode0=7  \toksputright\tmptoks{\or^^@}%
  \catcode0=8  \toksputright\tmptoks{\or^^@}%
  \catcode0=9  \toksputright\tmptoks{\or}%
  \catcode0=10 \toksputright\tmptoks\expandafter{\firstofone{\or}^^@}%
  \catcode0=11 \toksputright\tmptoks{\or ^^@}%
  \catcode0=12 \toksputright\tmptoks{\or^^@}%
  \catcode0=13 \toksputright\tmptoks{\or^^@}%
  \def\chartmp#1;{%
    \begingroup
      \lccode0=#1
      \lccode32=#1
      \edef\x{\endgroup
      \gdef\expandafter\noexpand
        \csname c__chargen_\romannumeral#1_tl\endcsname{\the\tmptoks}}%
      \lowercase\expandafter{\x}}%
  \let^^L\relax
  \catcode`^^L=12
  \count0=0
  \loop
    \expandafter\chartmp\number\count0;
    \advance\count0 by 1
    \ifnum\count0<256 \repeat
\endgroup
% Main definition
\def\chargenerate#1#2{%
  \romannumeral\expandafter\generateaux
    \number#1\expandafter;\number#2;}
% Check for invalid input
\def\generateaux#1;#2;{%
  \ifnum0%
      \ifnum#1=0  1\fi
      \ifnum#2=10 1\fi
      =11
    \expandableerror{Cannot generate null char as a space.}%
  \else
    \ifodd0%
        \ifnum#2< 1 1\fi
        \ifnum#2= 5 1\fi
        \ifnum#2= 9 1\fi
        \ifnum#2>13 1\fi\space
      \expandableerror{Invalid catcode for char generation.}%
    \else
      \ifodd0%
          \ifnum#1<  0 1\fi
          \ifnum#1>"FF 1\fi\space
        \expandableerror{Charcode requested out of engine range.}%
      \else
        \generateauxi{#1}{#2}%
      \fi
    \fi
  \fi
  \expend}
% Actual char generation
\def\generateauxi#1#2#3\expend{%
  #3%
  \iffalse{\fi
  \expandafter\expandafter
  \expandafter\expend
  \expandafter\expandafter
  \ifcase#2%
    \csname c__chargen_\romannumeral#1_tl\endcsname
  \or}
  \fi}

% Testing
\def\empty{}
\begingroup
  \lccode`\~=`a
  \lowercase{\endgroup
  \gdef ~{\ active character a}%
}
\def\test#1{%
  \edef\x{%
    \ifnum#1=2 {\iffalse}\fi\space\noexpand\meaning\fi % add { if a is a }
    \chargenerate{97}{#1}%
    \ifnum#1=6 \chargenerate{97}{#1}\fi% add another # if a is a #
    \ifnum#1=1 \iffalse{\fi\space\noexpand\meaning}\fi % if a is a {, add a }
  }%
  \ifx\x\empty
    #1: ERROR
  \else
    #1: \expandafter\meaning\x
  \fi\par}

\tt\scrollmode
\count2=0
\loop
\test{\the\count2 }%
\advance\count2 by 1
\ifnum\count2<16
\repeat

\bye

решение2

\lowercase— хороший способ сделать это с помощью любого TeX.

\def\prepend#1#2{% toks, charcode
 \begingroup
  \lccode`9=#2\relax
  \lowercase{%
    \edef\0{\endgroup 
       #1={9\the#1}}%
  \0}}

Предположим, что регистр токов не является \0.

решение3

\newtoks\test

\def\prepend#1#2{%
  \ifcase\catcode#2\relax
    % 0, do nothing
    \or
    % 1, do nothing
    \or
    % 2, do nothing
    \or
    \prependaux#1{#2}{$}% 3
    \or
    \prependaux#1{#2}{&}% 4
    \or
    % 5, do nothing
    \or
    \prependaux#1{#2}{##}% 6
    \or
    \prependaux#1{#2}{^}% 7
    \or
    \prependaux#1{#2}{_}% 8
    \or
    % 9, do nothing
    \or
    \prependaux#1{#2}{ }% 10
    \or
    \prependaux#1{#2}{a}% 11
    \or
    \prependaux#1{#2}{?}% 12
    \or
    \prependaux#1{#2}{~}% 13
    % 14 or 15, do nothing
  \fi
}
\def\prependaux#1#2#3{%
  \begingroup\lccode`#3=#2\relax
  \lowercase{\endgroup\toks0={#3}}%
  #1\expandafter{\the\toks\expandafter0\the#1}%
}

\test={bc}
\prepend\test{97}

\message{\number`?}

\catcode`?=3

\prepend\test{63}

\the\test$

\prepend\test{`\#}

\showthe\test

\bye

Вы не можете добавлять символы с кодами категорий 0, 1, 2, 5, 9, 14 или 15.

Как видите, я добавил символ «странного» кода категории 3, и код \the\test$выводит математическую формулу.

Ограничение: #1не может быть \toks0.

решение4

Вы можете использовать LuaTeX и string.charфункцию для преобразования кода ASCII в соответствующий символ.

\newtoks\test
\test={bc}
\tokspre\test\expandafter{\directlua{tex.sprint(string.char(97))}}
\showthe\test
\bye

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