Есть ли способ получить фактический токен символа из его представления кода символа? В частности, я хотел бы иметь макрос \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