% \iffalse meta-comment % %% File: l3cctab.dtx % % Copyright (C) 2018-2024 The LaTeX Project % % It may be distributed and/or modified under the conditions of the % LaTeX Project Public License (LPPL), either version 1.3c of this % license or (at your option) any later version. The latest version % of this license is in the file % % http://www.latex-project.org/lppl.txt % % This file is part of the "l3kernel bundle" (The Work in LPPL) % and all files in that bundle must be distributed together. % % ----------------------------------------------------------------------- % % The development version of the bundle can be found at % % https://github.com/latex3/latex3 % % for those people who are interested. % %<*driver> \documentclass[full]{l3doc} \begin{document} \DocInput{\jobname.dtx} \end{document} % % \fi % % \title{^^A % The \pkg{l3cctab} module\\ Category code tables^^A % } % % \author{^^A % The \LaTeX{} Project\thanks % {^^A % E-mail: % \href{mailto:latex-team@latex-project.org} % {latex-team@latex-project.org}^^A % }^^A % } % % \date{Released 2024-03-14} % % \maketitle % % \begin{documentation} % % A category code table enables rapid switching of all category codes in % one operation. For \LuaTeX{}, this is possible over the entire Unicode % range. For other engines, only the $8$-bit range ($0$--$255$) is covered by % such tables. The implementation of category code tables in \pkg{expl3} % also saves and restores the \TeX{} \tn{endlinechar} primitive value, meaning % they could be used for example to implement \cs{ExplSyntaxOn}. % % \section{Creating and initialising category code tables} % % \begin{function}[updated = 2020-07-02]{\cctab_new:N,\cctab_new:c} % \begin{syntax} % \cs{cctab_new:N} \meta{category code table} % \end{syntax} % Creates a new \meta{category code table} variable or raises an error if % the name is already taken. The declaration is global. The % \meta{category code table} is initialised with the codes % as used by \IniTeX{}. % \end{function} % % \begin{function}[updated = 2020-07-07]{\cctab_const:Nn,\cctab_const:cn} % \begin{syntax} % \cs{cctab_const:Nn} \meta{category code table} \Arg{category code set up} % \end{syntax} % Creates a new \meta{category code table}, applies (in a group) the % \meta{category code set up} on top of \IniTeX{} settings, % then saves them globally as a constant % table. The \meta{category code set up} can include a call to % \cs{cctab_select:N}. % \end{function} % % \begin{function}[updated = 2020-07-07]{\cctab_gset:Nn,\cctab_gset:cn} % \begin{syntax} % \cs{cctab_gset:Nn} \meta{category code table} \Arg{category code set up} % \end{syntax} % Starting from the \IniTeX{} category codes, % applies (in a group) the \meta{category code set up}, then saves them % globally in the \meta{category code table}. The \meta{category code set up} % can include a call to \cs{cctab_select:N}. % \end{function} % % \begin{function}[added = 2023-05-26] % {\cctab_gsave_current:N,\cctab_gsave_current:c} % \begin{syntax} % \cs{cctab_gsave_current:N} \meta{category code table} % \end{syntax} % Saves the current prevailing category codes in the % \meta{category code table}. % \end{function} % % \section{Using category code tables} % % \begin{function}[updated = 2020-07-02]{\cctab_begin:N,\cctab_begin:c} % \begin{syntax} % \cs{cctab_begin:N} \meta{category code table} % \end{syntax} % Switches locally the category codes in force to those stored in the % \meta{category code table}. The prevailing codes before the % function is called are added to a stack, for use with % \cs{cctab_end:}. This function does not start a \TeX{} group. % \end{function} % % \begin{function}[updated = 2020-07-02]{\cctab_end:} % \begin{syntax} % \cs{cctab_end:} % \end{syntax} % Ends the scope of a \meta{category code table} started using % \cs{cctab_begin:N}, returning the codes to those in force before the % matching \cs{cctab_begin:N} was used. This must be used within the % same \TeX{} group (and at the same \TeX{} group level) as the % matching \cs{cctab_begin:N}. % \end{function} % % \begin{function}[added = 2020-05-19, updated = 2020-07-02]{\cctab_select:N, \cctab_select:c} % \begin{syntax} % \cs{cctab_select:N} \meta{category code table} % \end{syntax} % Selects the \meta{category code table} for the scope of the current % group. This is in particular useful in the \meta{setup} arguments % of \cs{tl_set_rescan:Nnn}, \cs{tl_rescan:nn}, \cs{cctab_const:Nn}, % and \cs{cctab_gset:Nn}. % \end{function} % % \begin{function}[EXP, added = 2021-05-10]{\cctab_item:Nn, \cctab_item:cn} % \begin{syntax} % \cs{cctab_item:Nn} \meta{category code table} \Arg{int expr} % \end{syntax} % Determines the \meta{character} with character code given by the % \meta{int expr} and expands to its category code specified % by the \meta{category code table}. % \end{function} % % \section{Category code table conditionals} % % \begin{function}[pTF]{\cctab_if_exist:N, \cctab_if_exist:c} % \begin{syntax} % \cs{cctab_if_exist_p:N} \meta{category code table} % \cs{cctab_if_exist:NTF} \meta{category code table} \Arg{true code} \Arg{false code} % \end{syntax} % Tests whether the \meta{category code table} is currently defined. % This does not check that the \meta{category code table} really is a % category code table. % \end{function} % % \section{Constant and scratch category code tables} % % \begin{variable}[updated = 2020-07-10]{\c_code_cctab} % Category code table for the \pkg{expl3} code environment; this does % \emph{not} include \texttt{@}, which is retained as an \enquote{other} % character. Sets the \tn{endlinechar} value to $32$ (a space). % \end{variable} % % \begin{variable}[updated = 2020-07-08]{\c_document_cctab} % Category code table for a standard \LaTeX{} document, as set by the \LaTeX{} % kernel. In particular, the upper-half of the $8$-bit range will be set to % \enquote{active} with \pdfTeX{} \emph{only}. No \pkg{babel} shorthands % will be activated. Sets the \tn{endlinechar} value to $13$ (normal % line ending). % \end{variable} % % \begin{variable}[updated = 2020-07-02]{\c_initex_cctab} % Category code table as set up by \IniTeX{}. % \end{variable} % % \begin{variable}[updated = 2020-07-02]{\c_other_cctab} % Category code table where all characters have category code $12$ % (other). Sets the \tn{endlinechar} value to $-1$. % \end{variable} % % \begin{variable}[updated = 2020-07-02]{\c_str_cctab} % Category code table where all characters have category code $12$ % (other) with the exception of spaces, which have category code % $10$ (space). Sets the \tn{endlinechar} value to $-1$. % \end{variable} % % \begin{variable}[added = 2023-05-26]{\g_tmpa_cctab, \g_tmpb_cctab} % Scratch category code tables. % \end{variable} % % \end{documentation} % % \begin{implementation} % % \section{\pkg{l3cctab} implementation} % % \begin{macrocode} %<*package> % \end{macrocode} % % \begin{macrocode} %<@@=cctab> % \end{macrocode} % % As \LuaTeX{} offers engine support for category code tables, and this % is entirely lacking from the other engines, we need two complementary % approaches. (Some future \XeTeX{} may add support, at which point the % conditionals below would be different.) % % \subsection{Variables} % % \begin{variable}{\g_@@_stack_seq, \g_@@_unused_seq} % List of catcode tables saved by nested \cs{cctab_begin:N}, to % restore catcodes at the matching \cs{cctab_end:}. When popped from % the \cs{g_@@_stack_seq} the table numbers are stored in % \cs{g_@@_unused_seq} for later reuse. % \begin{macrocode} \seq_new:N \g_@@_stack_seq \seq_new:N \g_@@_unused_seq % \end{macrocode} % \end{variable} % % \begin{variable}{\g_@@_group_seq} % A stack to store the group level when a catcode table started. % \begin{macrocode} \seq_new:N \g_@@_group_seq % \end{macrocode} % \end{variable} % % \begin{variable}{\g_@@_allocate_int} % Integer to keep track of what category code table to allocate. In % \LuaTeX{} it is only used in format mode to implement % \cs{cctab_new:N}. In other engines it is used to make csnames for % dynamic tables. % \begin{macrocode} \int_new:N \g_@@_allocate_int % \end{macrocode} % \end{variable} % % \begin{variable}{\l_@@_internal_a_tl,\l_@@_internal_b_tl} % Scratch space. For instance, when popping % \cs{g_@@_stack_seq}/\cs{g_@@_unused_seq}, consists of the % catcodetable number (integer denotation) in \LuaTeX{}, or of an % intarray variable (as a single token) in other engines. % \begin{macrocode} \tl_new:N \l_@@_internal_a_tl \tl_new:N \l_@@_internal_b_tl % \end{macrocode} % \end{variable} % % \begin{variable}{\g_@@_endlinechar_prop} % In \LuaTeX{} we store the \tn{endlinechar} associated to each % \tn{catcodetable} in a property list, unless it is the default % value~$13$. % \begin{macrocode} \prop_new:N \g_@@_endlinechar_prop % \end{macrocode} % \end{variable} % % \subsection{Allocating category code tables} % % \begin{macro}{\cctab_new:N, \cctab_new:c, \@@_new:N, \@@_gstore:Nnn} % The \cs{@@_new:N} auxiliary allocates a new catcode table but does % not attempt to set its value consistently across engines. It is % used both in \cs{cctab_new:N}, which sets catcodes to \IniTeX{} % values, and in \cs{cctab_begin:N}/\cs{cctab_end:} for dynamically % allocated tables. % % First, the \LuaTeX{} case. % Creating a new category code table is done like other registers. % In Con\TeX{}t, \tn{newcatcodetable} does not include the initialisation, % so that is added explicitly. % \begin{macrocode} \sys_if_engine_luatex:TF { \cs_new_protected:Npn \cctab_new:N #1 { \__kernel_chk_if_free_cs:N #1 \@@_new:N #1 } \cs_new_protected:Npn \@@_new:N #1 { \newcatcodetable #1 \tex_initcatcodetable:D #1 } } % \end{macrocode} % Now the case for other engines. Here, each table is an integer % array. Following the \LuaTeX{} pattern, a new table starts with % \IniTeX{} codes. The index base is out-by-one, so we have an % internal function to handle that. The \IniTeX{} \tn{endlinechar} is % $13$. % \begin{macrocode} { \cs_new_protected:Npn \@@_new:N #1 { \intarray_new:Nn #1 { 257 } } \cs_new_protected:Npn \@@_gstore:Nnn #1#2#3 { \intarray_gset:Nnn #1 { \int_eval:n { #2 + 1 } } {#3} } \cs_new_protected:Npn \cctab_new:N #1 { \__kernel_chk_if_free_cs:N #1 \@@_new:N #1 \int_step_inline:nn { 256 } { \__kernel_intarray_gset:Nnn #1 {##1} { 12 } } \__kernel_intarray_gset:Nnn #1 { 257 } { 13 } \@@_gstore:Nnn #1 { 0 } { 9 } \@@_gstore:Nnn #1 { 13 } { 5 } \@@_gstore:Nnn #1 { 32 } { 10 } \@@_gstore:Nnn #1 { 37 } { 14 } \int_step_inline:nnn { 65 } { 90 } { \@@_gstore:Nnn #1 {##1} { 11 } } \@@_gstore:Nnn #1 { 92 } { 0 } \int_step_inline:nnn { 97 } { 122 } { \@@_gstore:Nnn #1 {##1} { 11 } } \@@_gstore:Nnn #1 { 127 } { 15 } } } \cs_generate_variant:Nn \cctab_new:N { c } % \end{macrocode} % \end{macro} % % \subsection{Saving category code tables} % % \begin{macro}{\@@_gset:n, \@@_gset_aux:n} % In various functions we need to save the current catcodes (globally) % in a table. In \LuaTeX{}, saving the catcodes is a primitives, but % the \tn{endlinechar} needs more work: to avoid filling % \cs{g_@@_endlinechar_prop} with many entries we special-case the % default value $13$. In other engines we store $256$ current % catcodes and the \tn{endlinechar} in an intarray variable. % \begin{macrocode} \sys_if_engine_luatex:TF { \cs_new_protected:Npn \@@_gset:n #1 { \exp_args:Nf \@@_gset_aux:n { \int_eval:n {#1} } } \cs_new_protected:Npn \@@_gset_aux:n #1 { \tex_savecatcodetable:D #1 \scan_stop: \int_compare:nNnTF { \tex_endlinechar:D } = { 13 } { \prop_gremove:Nn \g_@@_endlinechar_prop {#1} } { \prop_gput:NnV \g_@@_endlinechar_prop {#1} \tex_endlinechar:D } } } { \cs_new_protected:Npn \@@_gset:n #1 { \int_step_inline:nn { 256 } { \__kernel_intarray_gset:Nnn #1 {##1} { \char_value_catcode:n { ##1 - 1 } } } \__kernel_intarray_gset:Nnn #1 { 257 } { \tex_endlinechar:D } } } % \end{macrocode} % \end{macro} % % \begin{macro}{\cctab_gset:Nn, \cctab_gset:cn} % Category code tables are always global, so only one version of % assignments is needed. Simply run the setup in a group and save the % result in a category code table~|#1|, provided it is valid. The % internal function is defined above depending on the engine. % \begin{macrocode} \cs_new_protected:Npn \cctab_gset:Nn #1#2 { \@@_chk_if_valid:NT #1 { \group_begin: \cctab_select:N \c_initex_cctab #2 \scan_stop: \@@_gset:n {#1} \group_end: } } \cs_generate_variant:Nn \cctab_gset:Nn { c } % \end{macrocode} % \end{macro} % % \begin{macro}{\cctab_gsave_current:N, \cctab_gsave_current:c} % Very simple. % \begin{macrocode} \cs_new_protected:Npn \cctab_gsave_current:N #1 { \@@_chk_if_valid:NT #1 { \@@_gset:n {#1} } } \cs_generate_variant:Nn \cctab_gsave_current:N { c } % \end{macrocode} % \end{macro} % % \subsection{Using category code tables} % % \begin{variable}{\g_@@_internal_cctab} % \begin{macro}[EXP]{\@@_internal_cctab_name:} % In \LuaTeX{}, we must ensure that the saved tables are read-only. % This is done by applying the saved table, then switching immediately % to a scratch table. Any later catcode assignment will affect that % scratch table rather than the saved one. If we simply switched to % the saved tables, then \cs{char_set_catcode_other:N} in the example % below would change \cs{c_document_cctab} and a later use of that % table would give the wrong category code to |_|. % \begin{verbatim} % \use:n % { % \cctab_begin:N \c_document_cctab % \char_set_catcode_other:N \_ % \cctab_end: % \cctab_begin:N \c_document_cctab % \int_compare:nTF { \char_value_catcode:n { `_ } = 8 } % { \TRUE } { \ERROR } % \cctab_end: % } % \end{verbatim} % We must also make sure that a scratch table is never reused in a % nested group: in the following example, the scratch table used by % the first \cs{cctab_begin:N} would be changed globally by the second % one issuing \tn{savecatcodetable}, and after \cs{group_end:} the % wrong category codes (those of \cs{c_str_cctab}) would be imposed. % Note that the inner \cs{cctab_end:} restores the correct catcodes % only locally, so the problem really comes up because of the % different grouping level. The simplest is to use a scratch table % labeled by the \tn{currentgrouplevel}. We initialize one of them as % an example. % \begin{verbatim} % \use:n % { % \cctab_begin:N \c_document_cctab % \group_begin: % \cctab_begin:N \c_str_cctab % \cctab_end: % \group_end: % \cctab_end: % } % \end{verbatim} % \begin{macrocode} \sys_if_engine_luatex:T { \@@_new:N \g_@@_internal_cctab \cs_new:Npn \@@_internal_cctab_name: { g_@@_internal \tex_romannumeral:D \tex_currentgrouplevel:D _cctab } } % \end{macrocode} % \end{macro} % \end{variable} % % \begin{macro}{\cctab_select:N, \cctab_select:c} % \begin{macro}{\@@_select:N} % The public function simply checks the \meta{cctab~var} exists before % using the engine-dependent \cs{@@_select:N}. Skipping these checks % would result in low-level engine-dependent errors. First, the % \LuaTeX{} case. In other engines, selecting a catcode table is a matter % of doing $256$ catcode assignments and setting the \tn{endlinechar}. % \begin{macrocode} \cs_new_protected:Npn \cctab_select:N #1 { \@@_chk_if_valid:NT #1 { \@@_select:N #1 } } \cs_generate_variant:Nn \cctab_select:N { c } \sys_if_engine_luatex:TF { \cs_new_protected:Npn \@@_select:N #1 { \tex_catcodetable:D #1 \prop_get:NVNTF \g_@@_endlinechar_prop #1 \l_@@_internal_a_tl { \int_set:Nn \tex_endlinechar:D { \l_@@_internal_a_tl } } { \int_set:Nn \tex_endlinechar:D { 13 } } \cs_if_exist:cF { \@@_internal_cctab_name: } { \exp_args:Nc \@@_new:N { \@@_internal_cctab_name: } } \exp_args:Nc \tex_savecatcodetable:D { \@@_internal_cctab_name: } \exp_args:Nc \tex_catcodetable:D { \@@_internal_cctab_name: } } } { \cs_new_protected:Npn \@@_select:N #1 { \int_step_inline:nn { 256 } { \char_set_catcode:nn { ##1 - 1 } { \__kernel_intarray_item:Nn #1 {##1} } } \int_set:Nn \tex_endlinechar:D { \__kernel_intarray_item:Nn #1 { 257 } } } } % \end{macrocode} % \end{macro} % \end{macro} % % \begin{variable}{\g_@@_next_cctab} % \begin{macro}{\@@_begin_aux:} % For \cs{cctab_begin:N}/\cs{cctab_end:} we will need to allocate % dynamic tables. This is done here by \cs{@@_begin_aux:}, which puts % a table number (in \LuaTeX{}) or name (in other engines) into % \cs{l_@@_internal_a_tl}. In \LuaTeX{} this simply calls \cs{@@_new:N} % and uses the resulting catcodetable number; in other engines we need % to give a name to the intarray variable and use that. In \LuaTeX{}, % to restore catcodes at \cs{cctab_end:} we cannot just set % \tn{catcodetable} to its value before \cs{cctab_begin:N}, because % that table may have been altered by other code in the mean time. So % we must make sure to save the catcodes in a table we control and % restore them at \cs{cctab_end:}. % \begin{macrocode} \sys_if_engine_luatex:TF { \cs_new_protected:Npn \@@_begin_aux: { \@@_new:N \g_@@_next_cctab \tl_set:NV \l_@@_internal_a_tl \g_@@_next_cctab \cs_undefine:N \g_@@_next_cctab } } { \cs_new_protected:Npn \@@_begin_aux: { \int_gincr:N \g_@@_allocate_int \exp_args:Nc \@@_new:N { g_@@_ \int_use:N \g_@@_allocate_int _cctab } \exp_args:NNc \tl_set:Nn \l_@@_internal_a_tl { g_@@_ \int_use:N \g_@@_allocate_int _cctab } } } % \end{macrocode} % \end{macro} % \end{variable} % % \begin{macro}{\cctab_begin:N, \cctab_begin:c} % Check the \meta{cctab~var} exists, to avoid low-level errors. Get % in \cs{l_@@_internal_a_tl} the number/name of a dynamic table, either % from \cs{g_@@_unused_seq} where we save tables that are not % currently in use, or from \cs{@@_begin_aux:} if none are available. % Then save the current catcodes into the table (pointed to by) % \cs{l_@@_internal_a_tl} and save that table number in a stack before % selecting the desired catcodes. % \begin{macrocode} \cs_new_protected:Npn \cctab_begin:N #1 { \@@_chk_if_valid:NT #1 { \seq_gpop:NNF \g_@@_unused_seq \l_@@_internal_a_tl { \@@_begin_aux: } \@@_chk_group_begin:e { \@@_nesting_number:N \l_@@_internal_a_tl } \seq_gpush:NV \g_@@_stack_seq \l_@@_internal_a_tl \exp_args:NV \@@_gset:n \l_@@_internal_a_tl \@@_select:N #1 } } \cs_generate_variant:Nn \cctab_begin:N { c } % \end{macrocode} % \end{macro} % % \begin{macro}{\cctab_end:} % Make sure a \cs{cctab_begin:N} was used some time earlier, get in % \cs{l_@@_internal_a_tl} the catcode table number/name in which the % prevailing catcodes were stored, then restore these catcodes. The % dynamic table is now unused hence stored in \cs{g_@@_unused_seq} for % recycling by later \cs{cctab_begin:N}. % \begin{macrocode} \cs_new_protected:Npn \cctab_end: { \seq_gpop:NNTF \g_@@_stack_seq \l_@@_internal_a_tl { \seq_gpush:NV \g_@@_unused_seq \l_@@_internal_a_tl \exp_args:Ne \@@_chk_group_end:n { \@@_nesting_number:N \l_@@_internal_a_tl } \@@_select:N \l_@@_internal_a_tl } { \msg_error:nn { cctab } { extra-end } } } % \end{macrocode} % \end{macro} % % \begin{macro} % {\@@_chk_group_begin:n,\@@_chk_group_begin:e, \@@_chk_group_end:n} % Catcode tables are not allowed to be intermixed with groups, so here % we check that they are properly nested regarding \TeX{} groups. % \cs{@@_chk_group_begin:n} stores the current group level in a stack, % and locally defines a dummy control sequence % \cs[no-index]{@@_group_\meta{cctab-level}_chk:}. % % \cs{@@_chk_group_end:n} pops the stack, and compares the returned % value with \cs{tex_currentgrouplevel:D}. If they differ, % \cs{cctab_end:} is in a different grouping level than the matching % \cs{cctab_begin:N}. If they are the same, both happened at the same % level, however a group might have ended and another started between % \cs{cctab_begin:N} and \cs{cctab_end:}: % \begin{verbatim} % \group_begin: % \cctab_begin:N \c_document_cctab % \group_end: % \group_begin: % \cctab_end: % \group_end: % \end{verbatim} % In this case checking \cs{tex_currentgrouplevel:D} is not enough, so % we locally define \cs[no-index]{@@_group_\meta{cctab-level}_chk:}, % and then check if it exist in \cs{cctab_end:}. If it doesn't, % we know there was a group end where it shouldn't. % % The \meta{cctab-level} in the sentinel macro above cannot be % replaced by the more convenient \cs{tex_currentgrouplevel:D} because % with the latter we might be tricked. Suppose: % \begin{verbatim} % \group_begin: % \cctab_begin:N \c_code_cctab % A % \group_end: % \group_begin: % \cctab_begin:N \c_code_cctab % B % \cctab_end: % C % \cctab_end: % D % \group_end: % \end{verbatim} % The line marked with |A| would start a |cctab| with a sentinel token % named \cs[no-index]{@@_group_1_chk:}, which would disappear at the % \cs{group_end:} that follows. But |B| would create the same % sentinel token, since both are at the same group level. Line |C| % would end the |cctab| from line |B| correctly, but so would line |D| % because line |B| created the same sentinel token. Using % \meta{cctab-level} works correctly because it signals that certain % |cctab| level was activated somewhere, but if it doesn't exist when % the \cs{cctab_end:} is reached, we had a problem. % % Unfortunately these tests only flag the wrong usage at the % \cs{cctab_end:}, which might be far from the \cs{cctab_begin:N}. % However it isn't possible to signal the wrong usage at the % \cs{group_end:} without using \cs{tex_aftergroup:D}, which is % unsafe in certain types of groups. % % The three cases checked here just raise an error, and no recovery is % attempted: usually interleaving groups and catcode tables will work % predictably. % \begin{macrocode} \cs_new_protected:Npn \@@_chk_group_begin:n #1 { \seq_gpush:Ne \g_@@_group_seq { \int_use:N \tex_currentgrouplevel:D } \cs_set_eq:cN { @@_group_ #1 _chk: } \prg_do_nothing: } \cs_generate_variant:Nn \@@_chk_group_begin:n { e } \cs_new_protected:Npn \@@_chk_group_end:n #1 { \seq_gpop:NN \g_@@_group_seq \l_@@_internal_b_tl \bool_lazy_and:nnF { \int_compare_p:nNn { \tex_currentgrouplevel:D } = { \l_@@_internal_b_tl } } { \cs_if_exist_p:c { @@_group_ #1 _chk: } } { \msg_error:nne { cctab } { group-mismatch } { \int_sign:n { \tex_currentgrouplevel:D - \l_@@_internal_b_tl } } } \cs_undefine:c { @@_group_ #1 _chk: } } % \end{macrocode} % \end{macro} % % \begin{macro}{\@@_nesting_number:N,\@@_nesting_number:w} % This macro returns the numeric index of the current catcode table. % In \LuaTeX{} this is just the argument, which is a count reference % to a \tn{catcodetable} register. In other engines, the number is % extracted from the |cctab| variable. % \begin{macrocode} \sys_if_engine_luatex:TF { \cs_new:Npn \@@_nesting_number:N #1 {#1} } { \cs_new:Npn \@@_nesting_number:N #1 { \exp_after:wN \exp_after:wN \exp_after:wN \@@_nesting_number:w \exp_after:wN \token_to_str:N #1 } \use:e { \cs_new:Npn \exp_not:N \@@_nesting_number:w #1 \tl_to_str:n { g_@@_ } #2 \tl_to_str:n { _cctab } {#2} } } % \end{macrocode} % \end{macro} % % Finally, install some code at the end of the \TeX{} run to check that % all \cs{cctab_begin:N} were ended by some \cs{cctab_end:}. % \begin{macrocode} \cs_if_exist:NT \hook_gput_code:nnn { \hook_gput_code:nnn { enddocument/end } { cctab } { \seq_if_empty:NF \g_@@_stack_seq { \msg_error:nn { cctab } { missing-end } } } } % \end{macrocode} % % % \begin{macro}{\cctab_item:Nn, \cctab_item:cn} % Evaluate the integer argument only once. In most engines the % |cctab| variable only has $256$ entries so we only look up the % catcode for these entries, otherwise we use the current catcode. In % particular, for out-of-range values we use whatever fall-back % \cs{char_value_catcode:n}. In \LuaTeX{}, we use the % |tex.getcatcode| function. % \begin{macrocode} \cs_new:Npn \cctab_item:Nn #1#2 { \exp_args:Nf \@@_item:nN { \int_eval:n {#2} } #1 } \sys_if_engine_luatex:TF { \cs_new:Npn \@@_item:nN #1#2 { \lua_now:e { tex.print(-2, tex.getcatcode(\int_use:N #2, #1)) } } } { \cs_new:Npn \@@_item:nN #1#2 { \int_compare:nNnTF {#1} < { 256 } { \intarray_item:Nn #2 { #1 + 1 } } { \char_value_catcode:n {#1} } } } \cs_generate_variant:Nn \cctab_item:Nn { c } % \end{macrocode} % \end{macro} % % \subsection{Category code table conditionals} % % \begin{macro}[pTF]{\cctab_if_exist:N,\cctab_if_exist:c} % Checks whether a \meta{cctab~var} is defined. % \begin{macrocode} \prg_new_eq_conditional:NNn \cctab_if_exist:N \cs_if_exist:N { TF , T , F , p } \prg_new_eq_conditional:NNn \cctab_if_exist:c \cs_if_exist:c { TF , T , F , p } % \end{macrocode} % \end{macro} % % \begin{macro}[TF]{\@@_chk_if_valid:N} % \begin{macro}{\@@_chk_if_valid_aux:NTF} % Checks whether the argument is defined and whether it is a valid % \meta{cctab~var}. In \LuaTeX{} the validity of the \meta{cctab~var} % is checked by the engine, which complains if the argument is not a % \cs{chardef}'ed constant. In other engines, check if the given % command is an intarray variable (the underlying definition is a copy % of the \texttt{cmr10} font). % \begin{macrocode} \prg_new_protected_conditional:Npnn \@@_chk_if_valid:N #1 { TF , T , F } { \cctab_if_exist:NTF #1 { \@@_chk_if_valid_aux:NTF #1 { \prg_return_true: } { \msg_error:nne { cctab } { invalid-cctab } { \token_to_str:N #1 } \prg_return_false: } } { \msg_error:nne { kernel } { command-not-defined } { \token_to_str:N #1 } \prg_return_false: } } \sys_if_engine_luatex:TF { \cs_new_protected:Npn \@@_chk_if_valid_aux:NTF #1 { \int_compare:nNnTF {#1-1} < { \e@alloc@ccodetable@count } } \cs_if_exist:NT \c_syst_catcodes_n { \cs_gset_protected:Npn \@@_chk_if_valid_aux:NTF #1 { \int_compare:nTF { #1 <= \c_syst_catcodes_n } } } } { \cs_new_protected:Npn \@@_chk_if_valid_aux:NTF #1 { \exp_args:Nf \str_if_in:nnTF { \cs_meaning:N #1 } { select~font~cmr10~at~ } } } % \end{macrocode} % \end{macro} % \end{macro} % % \subsection{Constant category code tables} % % \begin{macro}{\cctab_const:Nn,\cctab_const:cn} % Creates a new \meta{cctab~var} then sets it with the current and % user-supplied codes. % \begin{macrocode} \cs_new_protected:Npn \cctab_const:Nn #1#2 { \cctab_new:N #1 \cctab_gset:Nn #1 {#2} } \cs_generate_variant:Nn \cctab_const:Nn { c } % \end{macrocode} % \end{macro} % % \begin{variable} % { % \c_initex_cctab , % \c_other_cctab , % \c_str_cctab % } % Creating category code tables means thinking starting from \IniTeX{}. % For all-other and the standard \enquote{string} tables that's easy. % \begin{macrocode} \cctab_new:N \c_initex_cctab \cctab_const:Nn \c_other_cctab { \cctab_select:N \c_initex_cctab \int_set:Nn \tex_endlinechar:D { -1 } \int_step_inline:nnn { 0 } { 127 } { \char_set_catcode_other:n {#1} } } \cctab_const:Nn \c_str_cctab { \cctab_select:N \c_other_cctab \char_set_catcode_space:n { 32 } } % \end{macrocode} % \end{variable} % % \begin{variable}{\c_code_cctab, \c_document_cctab} % To pick up document-level category codes, we need to delay set up to the % end of the format, where that's possible. Also, as there are a \emph{lot} % of category codes to set, we avoid using the official interface and store the % document codes using internal code. Depending on whether we are in the hook % or not, the catcodes may be code or document, so we explicitly set up both % correctly. % \begin{macrocode} \cs_if_exist:NTF \@expl@finalise@setup@@@@ { \tl_gput_right:Nn \@expl@finalise@setup@@@@ } { \use:n } { \@@_new:N \c_code_cctab \group_begin: \int_set:Nn \tex_endlinechar:D { 32 } \char_set_catcode_invalid:n { 0 } \bool_lazy_or:nnTF { \sys_if_engine_xetex_p: } { \sys_if_engine_luatex_p: } { \int_step_function:nN { 31 } \char_set_catcode_invalid:n } { \int_step_function:nN { 31 } \char_set_catcode_active:n } \int_step_function:nnN { 33 } { 64 } \char_set_catcode_other:n \int_step_function:nnN { 65 } { 90 } \char_set_catcode_letter:n \int_step_function:nnN { 91 } { 96 } \char_set_catcode_other:n \int_step_function:nnN { 97 } { 122 } \char_set_catcode_letter:n \char_set_catcode_ignore:n { 9 } % tab \char_set_catcode_other:n { 10 } % lf \char_set_catcode_active:n { 12 } % ff \char_set_catcode_end_line:n { 13 } % cr \char_set_catcode_ignore:n { 32 } % space \char_set_catcode_parameter:n { 35 } % hash \char_set_catcode_math_toggle:n { 36 } % dollar \char_set_catcode_comment:n { 37 } % percent \char_set_catcode_alignment:n { 38 } % ampersand \char_set_catcode_letter:n { 58 } % colon \char_set_catcode_escape:n { 92 } % backslash \char_set_catcode_math_superscript:n { 94 } % circumflex \char_set_catcode_letter:n { 95 } % underscore \char_set_catcode_group_begin:n { 123 } % left brace \char_set_catcode_other:n { 124 } % pipe \char_set_catcode_group_end:n { 125 } % right brace \char_set_catcode_space:n { 126 } % tilde \char_set_catcode_invalid:n { 127 } % ^^? \bool_lazy_or:nnF { \sys_if_engine_xetex_p: } { \sys_if_engine_luatex_p: } { \int_step_function:nnN { 128 } { 255 } \char_set_catcode_active:n } \@@_gset:n { \c_code_cctab } \group_end: \cctab_const:Nn \c_document_cctab { \cctab_select:N \c_code_cctab \int_set:Nn \tex_endlinechar:D { 13 } \char_set_catcode_space:n { 9 } \char_set_catcode_space:n { 32 } \char_set_catcode_other:n { 58 } \char_set_catcode_math_subscript:n { 95 } \char_set_catcode_active:n { 126 } } } % \end{macrocode} % \end{variable} % % \begin{variable}{\g_tmpa_cctab, \g_tmpb_cctab} % \begin{macrocode} \cctab_new:N \g_tmpa_cctab \cctab_new:N \g_tmpb_cctab % \end{macrocode} % \end{variable} % % \subsection{Messages} % % \begin{macrocode} \msg_new:nnnn { cctab } { stack-full } { The~category~code~table~stack~is~exhausted. } { LaTeX~has~been~asked~to~switch~to~a~new~category~code~table,~ but~there~is~no~more~space~to~do~this! } \msg_new:nnnn { cctab } { extra-end } { Extra~\iow_char:N\\cctab_end:~ignored~\msg_line_context:. } { LaTeX~came~across~a~\iow_char:N\\cctab_end:~without~a~matching~ \iow_char:N\\cctab_begin:N.~This~command~will~be~ignored. } \msg_new:nnnn { cctab } { missing-end } { Missing~\iow_char:N\\cctab_end:~before~end~of~TeX~run. } { LaTeX~came~across~more~\iow_char:N\\cctab_begin:N~than~ \iow_char:N\\cctab_end:. } \msg_new:nnnn { cctab } { invalid-cctab } { Invalid~\iow_char:N\\catcode~table. } { You~can~only~switch~to~a~\iow_char:N\\catcode~table~that~is~ initialized~using~\iow_char:N\\cctab_new:N~or~ \iow_char:N\\cctab_const:Nn. } \msg_new:nnnn { cctab } { group-mismatch } { \iow_char:N\\cctab_end:~occurred~in~a~ \int_case:nn {#1} { { 0 } { different~group } { 1 } { higher~group~level } { -1 } { lower~group~level } } ~than~ the~matching~\iow_char:N\\cctab_begin:N. } { Catcode~tables~and~groups~must~be~properly~nested,~but~ you~tried~to~interleave~them.~LaTeX~will~try~to~proceed,~ but~results~may~be~unexpected. } \prop_gput:Nnn \g_msg_module_name_prop { cctab } { LaTeX } \prop_gput:Nnn \g_msg_module_type_prop { cctab } { } % \end{macrocode} % % \begin{macrocode} % % \end{macrocode} % % \end{implementation} % %\PrintIndex