% \iffalse %% File: l3tl.dtx Copyright (C) 1990-2009 LaTeX3 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 ``expl3 bundle'' (The Work in LPPL) %% and all files in that bundle must be distributed together. %% %% The released version of this bundle is available from CTAN. %% %% ----------------------------------------------------------------------- %% %% The development version of the bundle can be found at %% %% http://www.latex-project.org/svnroot/experimental/trunk/ %% %% for those people who are interested. %% %%%%%%%%%%% %% NOTE: %% %%%%%%%%%%% %% %% Snapshots taken from the repository represent work in progress and may %% not work or may contain conflicting material! We therefore ask %% people _not_ to put them into distributions, archives, etc. without %% prior consultation with the LaTeX Project Team. %% %% ----------------------------------------------------------------------- % %<*driver|package> \RequirePackage{l3names} % %\fi \GetIdInfo$Id: l3tl.dtx 1505 2009-09-03 20:29:19Z joseph $ {L3 Experimental Token Lists} %\iffalse %<*driver> %\fi \ProvidesFile{\filename.\filenameext} [\filedate\space v\fileversion\space\filedescription] %\iffalse \documentclass[full]{l3doc} \begin{document} \DocInput{l3tl.dtx} \end{document} % % \fi % % % \title{The \textsf{l3tl} package\thanks{This file % has version number \fileversion, last % revised \filedate.}\\ % Token Lists} % \author{\Team} % \date{\filedate} % \maketitle % % \def\tlvar{tl var.} % \def\tlist{token list} % % \begin{documentation} % % \LaTeX3 stores token lists in variables also called `token lists'. % Variables of this type get the suffix "tl" and functions of this type % have the prefix "tl". To use a token list variable you simply call % the corresponding variable. % % Often you find yourself with not a token list variable but an % arbitrary token list which has to undergo certain tests. We will \emph{also} % prefix these functions with "tl". While token list variables are % always single tokens, token lists are always surrounded by % braces. % % \section{Functions} % % \begin{function}{\tl_new:N | % \tl_new:c | % \tl_new:Nn | % \tl_new:cn | % \tl_new:Nx } % \begin{syntax} % "\tl_new:Nn" <\tlvar> \Arg{initial token list} % \end{syntax} % Defines <\tlvar> to be a new variable to store a token list. % is the initial value of <\tlvar>. This % makes it possible to assign values to a constant token list variable. % % The form "\tl_new:N" initializes the token list variable with an empty value. % \end{function} % % \begin{function}{% % \tl_use:N | % \tl_use:c % } % \begin{syntax} % "\tl_use:N" <\tlvar> % \end{syntax} % Function that inserts the <\tlvar> into the processing stream. Instead of % |\tl_use:N| simply placing the <\tlvar> into the input stream is also % supported. |\tl_use:c| will complain if the <\tlvar> hasn't been declared % previously! % \end{function} % % \begin{function}{\tl_show:N | % \tl_show:c | % \tl_show:n } % \begin{syntax} % "\tl_show:N" <\tlvar> % "\tl_show:n" \Arg{\tlist} % \end{syntax} % Function that pauses the compilation and displays the <\tlvar> % or <\tlist> on the console output and in the log file. % \end{function} % % \begin{function}{% % \tl_set:Nn | % \tl_set:Nc | % \tl_set:NV | % \tl_set:No | % \tl_set:Nv | % \tl_set:Nf | % \tl_set:Nx | % \tl_set:cn | % \tl_set:co | % \tl_set:cV | % \tl_set:cx | % \tl_gset:Nn | % \tl_gset:Nc | % \tl_gset:No | % \tl_gset:NV | % \tl_gset:Nv | % \tl_gset:Nx | % \tl_gset:cn | % \tl_gset:cx } % \begin{syntax} % "\tl_set:Nn" <\tlvar> \Arg{\tlist} % \end{syntax} % Defines <\tlvar> to hold the token list <\tlist>. Global % variants of this command assign the value globally the other variants % expand the <\tlist> up to a certain level before the assignment % or interpret the <\tlist> as a character list and form a control % sequence out of it. % \end{function} % % \begin{function}{% % \tl_clear:N | % \tl_clear:c | % \tl_gclear:N | % \tl_gclear:c % } % \begin{syntax} % "\tl_clear:N" <\tlvar> % \end{syntax} % The <\tlvar> is locally or globally cleared. The "c" variants will % generate a control sequence name which is then interpreted as % <\tlvar> before clearing. % \end{function} % % \begin{function}{% % \tl_clear_new:N | % \tl_clear_new:c | % \tl_gclear_new:N | % \tl_gclear_new:c | % } % \begin{syntax} % "\tl_clear_new:N" <\tlvar> % \end{syntax} % These functions check if <\tlvar> exists. If it does it will be cleared; % if it doesn't it will be allocated. % \end{function} % % \begin{function}{% % \tl_put_left:Nn | % \tl_put_left:NV | % \tl_put_left:No | % \tl_put_left:Nx | % \tl_put_left:cn | % \tl_put_left:cV | % \tl_put_left:co | % } % \begin{syntax} % "\tl_put_left:Nn" <\tlvar> \Arg{\tlist} % \end{syntax} % These functions will append <\tlist> to the left of % <\tlvar>. <\tlist> might be subject to expansion before assignment. % \end{function} % % \begin{function}{% % \tl_put_right:Nn | % \tl_put_right:NV | % \tl_put_right:No | % \tl_put_right:Nx | % \tl_put_right:cn | % \tl_put_right:cV | % \tl_put_right:co | % } % \begin{syntax} % "\tl_put_right:Nn" <\tlvar> \Arg{\tlist} % \end{syntax} % These functions append <\tlist> to the right of % <\tlvar>. % \end{function} % % \begin{function}{% % \tl_gput_left:Nn | % \tl_gput_left:No | % \tl_gput_left:NV | % \tl_gput_left:Nx | % \tl_gput_left:cn | % \tl_gput_left:co | % \tl_gput_left:cV | % } % \begin{syntax} % "\tl_gput_left:Nn" <\tlvar> \Arg{\tlist} % \end{syntax} % These functions will append <\tlist> globally to the left of % <\tlvar>. % \end{function} % % \begin{function}{% % \tl_gput_right:Nn| % \tl_gput_right:No| % \tl_gput_right:NV| % \tl_gput_right:Nx| % \tl_gput_right:cn| % \tl_gput_right:co| % \tl_gput_right:cV| % } % \begin{syntax} % "\tl_gput_right:Nn" <\tlvar> \Arg{\tlist} % \end{syntax} % These functions will globally append <\tlist> to the right of % <\tlvar>. % \end{function} % % A word of warning is appropriate here: Token list variables are % implemented as macros and as such currently inherit some of the % peculiarities of how \TeX\ handles "#"s in the argument of % macros. In particular, the following actions are legal % \begin{verbatim} % \tl_set:Nn \l_tmpa_tl{##1} % \tl_put_right:Nn \l_tmpa_tl{##2} % \tl_set:No \l_tmpb_tl{\l_tmpa_tl ##3} % \end{verbatim} % |x| type expansions where macros being expanded contain |#|s do not % work and will not work until there is an |\expanded| primitive in % the engine. If you want them to work you must double |#|s another % level. % % % \begin{function}{% % \tl_set_eq:NN | % \tl_set_eq:Nc | % \tl_set_eq:cN | % \tl_set_eq:cc | % \tl_gset_eq:NN | % \tl_gset_eq:Nc | % \tl_gset_eq:cN | % \tl_gset_eq:cc | % } % \begin{syntax} % "\tl_set_eq:NN" <\tlvar1> <\tlvar2> % \end{syntax} % Fast form for % \begin{syntax} % "\tl_set:No" <\tlvar1> \Arg{\tlvar2} % \end{syntax} % when <\tlvar2> is known to be a variable of type "tl". % \end{function} % % \begin{function}{\tl_to_str:N | % \tl_to_str:c % } % \begin{syntax} % "\tl_to_str:N" <\tlvar> % \end{syntax} % This function returns the token list kept in <\tlvar> as a string list % with all characters catcoded to `other'. % \end{function} % % \begin{function}{ \tl_to_str:n } % \begin{syntax} % "\tl_to_str:n" \Arg{\tlist} % \end{syntax} % This function turns its argument into a string where all characters % have catcode `other'. % \begin{texnote} % This is the \eTeX\ primitive \tn{detokenize}. % \end{texnote} % \end{function} % % % \begin{function}{ \tl_rescan:nn } % \begin{syntax} % "\tl_rescan:nn" \Arg{catcode setup} \Arg{\tlist} % \end{syntax} % Returns the result of re-tokenising <\tlist> with the catcode setup % (and whatever other redefinitions) specified. This is useful because % the catcodes of characters are `frozen' when first tokenised; this allows % their meaning to be changed even after they've been read as an argument. % Also see "\tl_set_rescan:Nnn" below. % \begin{texnote} % This is a wrapper around \eTeX's "\scantokens". % \end{texnote} % \end{function} % % \begin{function}{ \tl_set_rescan:Nnn | \tl_set_rescan:Nnx | % \tl_gset_rescan:Nnn | \tl_gset_rescan:Nnx } % \begin{syntax} % "\tl_set_rescan:Nnn" <\tlvar> \Arg{catcode setup} \Arg{\tlist} % \end{syntax} % Sets <\tlvar> to the result of re-tokenising <\tlist> with the catcode setup % (and whatever other redefinitions) specified. % \begin{texnote} % This is a wrapper around \eTeX's "\scantokens". % \end{texnote} % \end{function} % % % % \section{Predicates and conditionals} % % \begin{function}{\tl_if_empty_p:N / (EXP) | % \tl_if_empty_p:c / (EXP) % } % \begin{syntax} % "\tl_if_empty_p:N" <\tlvar> % \end{syntax} % This predicate returns `true' if <\tlvar> is `empty' i.e., doesn't % contain any tokens. % \end{function} % % \begin{function}{\tl_if_empty:N / (TF)(EXP) | % \tl_if_empty:c / (TF)(EXP) | % } % \begin{syntax} % "\tl_if_empty:NTF" <\tlvar> \Arg{true code} \Arg{false code} % \end{syntax} % Execute if <\tlvar> is empty and if it % contains any tokens. % \end{function} % % \begin{function}{\tl_if_eq_p:NN / (EXP) | % \tl_if_eq_p:cN / (EXP) | % \tl_if_eq_p:Nc / (EXP) | % \tl_if_eq_p:cc / (EXP) | % } % \begin{syntax} % "\tl_if_eq_p:NN" <\tlvar1> <\tlvar2> % \end{syntax} % Predicate function which returns `true' if the two token list % variables are identical and `false' otherwise. % \end{function} % % \begin{function}{ % \tl_if_eq:NN / (TF)(EXP) | % \tl_if_eq:cN / (TF)(EXP) | % \tl_if_eq:Nc / (TF)(EXP) | % \tl_if_eq:cc / (TF)(EXP) | % } % \begin{syntax} % "\tl_if_eq:NNTF" <\tlvar1> <\tlvar2> \Arg{true code} \Arg{false code} % \end{syntax} % Execute if <\tlvar1> holds the same token list as <\tlvar2> % and otherwise. % \end{function} % % % \begin{function}{\tl_if_eq:nn / (TF)(EXP) | % \tl_if_eq:nV / (TF)(EXP) | % \tl_if_eq:no / (TF)(EXP) | % \tl_if_eq:Vn / (TF)(EXP) | % \tl_if_eq:on / (TF)(EXP) | % \tl_if_eq:VV / (TF)(EXP) | % \tl_if_eq:oo / (TF)(EXP) | % \tl_if_eq:xx / (TF)(EXP) | % \tl_if_eq:xn / (TF)(EXP) | % \tl_if_eq:nx / (TF)(EXP) | % \tl_if_eq:xV / (TF)(EXP) | % \tl_if_eq:xo / (TF)(EXP) | % \tl_if_eq:Vx / (TF)(EXP) | % \tl_if_eq:ox / (TF)(EXP) } % \begin{syntax} % "\tl_if_eq:nnTF" \Arg{tlist1} \Arg{tlist2} \Arg{true code} \Arg{false code} % \end{syntax} % Execute if the two token lists and are % identical. These functions are expandable if a new enough version of % pdf\TeX\ is being used. % \end{function}% % % \begin{function}{\tl_if_eq_p:nn / (EXP) | % \tl_if_eq_p:nV / (EXP) | % \tl_if_eq_p:no / (EXP) | % \tl_if_eq_p:Vn / (EXP) | % \tl_if_eq_p:on / (EXP) | % \tl_if_eq_p:VV / (EXP) | % \tl_if_eq_p:oo / (EXP) | % \tl_if_eq_p:xx / (EXP) | % \tl_if_eq_p:xn / (EXP) | % \tl_if_eq_p:nx / (EXP) | % \tl_if_eq_p:xV / (EXP) | % \tl_if_eq_p:xo / (EXP) | % \tl_if_eq_p:Vx / (EXP) | % \tl_if_eq_p:ox / (EXP) } % \begin{syntax} % "\tl_if_eq_p:nn" \Arg{tlist1} \Arg{tlist2} % \end{syntax} % Predicates function which returns `true' if the two token list % are identical and `false' otherwise. These are only defined if a new enough version % of pdf\TeX\ is in use. % \end{function} % % % \begin{function}{\tl_if_empty_p:n / (EXP) | % \tl_if_empty_p:V / (EXP) | % \tl_if_empty_p:o / (EXP) | % \tl_if_empty:n / (TF) | % \tl_if_empty:V / (TF) | % \tl_if_empty:o / (TF) | % } % \begin{syntax} % "\tl_if_empty:nTF" \Arg{\tlist} \Arg{true code} \Arg{false code} % \end{syntax} % Execute if <\tlist> doesn't contain any tokens and otherwise. % \end{function} % % \begin{function}{\tl_if_blank_p:n / (EXP)| % \tl_if_blank:n / (TF)(EXP)| % \tl_if_blank_p:V / (EXP)| % \tl_if_blank_p:o / (EXP)| % \tl_if_blank:V / (TF)(EXP)| % \tl_if_blank:o / (TF)(EXP)| % } % \begin{syntax} % "\tl_if_blank:nTF" \Arg{\tlist} \Arg{true code} \Arg{false code} % \end{syntax} % Execute if <\tlist> is blank meaning that it is either % empty or contains only blank spaces. % \end{function} % % \begin{function}{\tl_to_lowercase:n | % \tl_to_uppercase:n % } % \begin{syntax} % "\tl_to_lowercase:n" \Arg{\tlist} % \end{syntax} % "\tl_to_lowercase:n" converts all tokens in <\tlist> to their % lower case representation. Similar for "\tl_to_uppercase:n". % \begin{texnote} % These are the \TeX\ primitives \tn{lowercase} and \tn{uppercase} % renamed. % \end{texnote} % \end{function} % % \section{Working with the contents of token lists} % % \begin{function}{% % \tl_map_function:nN / (EXP) | % \tl_map_function:NN | % \tl_map_function:cN | % } % \begin{syntax} % "\tl_map_function:nN" \Arg{\tlist} \\ % "\tl_map_function:NN" <\tlvar> % \end{syntax} % Runs through all elements in a <\tlist> from left to right and places % in front of each element. As this function will also pick % up elements in brace groups, the element is returned with braces and % hence should be a function with a |:n| suffix even though % it may very well only deal with a single token. % % This function uses a % purely expandable loop function and will stay so as long as % is expandable too. % \end{function} % % \begin{function}{% % \tl_map_inline:nn | % \tl_map_inline:Nn | % \tl_map_inline:cn | % } % \begin{syntax} % "\tl_map_inline:nn" \Arg{\tlist} \Arg{inline~function} \\ % "\tl_map_inline:Nn" <\tlvar> \Arg{inline~function} % \end{syntax} % Allows a syntax like "\tl_map_inline:nn" \Arg{\tlist} % "{\token_to_str:N ##1}". This renders it non-expandable though. % Remember to double the "#"s for each level. % \end{function} % % % \begin{function}{% % \tl_map_variable:nNn | % \tl_map_variable:NNn | % \tl_map_variable:cNn | % } % \begin{syntax} % "\tl_map_variable:nNn" \Arg{\tlist} \Arg{action} \\ % "\tl_map_variable:NNn" <\tlvar> \Arg{action} % \end{syntax} % Assigns to each element on <\tlist> and executes . % As there is an assignment in this process it is not expandable. % \begin{texnote} % This is the \LaTeX2{} function \tn{@tfor} but with a more sane syntax. % Also it works by tail recursion and so is faster as lists grow longer. % \end{texnote} % \end{function} % % \begin{function}{% % \tl_map_break: | % } % \begin{syntax} % "\tl_map_break:" % \end{syntax} % For breaking out of a loop. Must not be nested inside a primitive % "\if" structure. % \end{function} % % % \begin{function}{ \tl_reverse:n | % \tl_reverse:V | % \tl_reverse:o | % \tl_reverse:N } % \begin{syntax} % "\tl_reverse:n" "{"..."}"\\ % "\tl_reverse:N" <\tlvar> % \end{syntax} % Reverse the token list (or the token list in the <\tlvar>) to result in % \dots. % Note that spaces in this % token list are gobbled in the process. % % Note also that braces are lost in the process of reversing a <\tlvar>. % That is,\\ % "\tl_set:Nn \l_tmpa_tl {a{bcd}e} \tl_reverse:N \l_tmpa_tl"\\ % will result in "ebcda". This behaviour is probably more of a bug than % a feature. % \end{function} % % \begin{function}{% % \tl_elt_count:n / (EXP) | % \tl_elt_count:V / (EXP) | % \tl_elt_count:o / (EXP) | % \tl_elt_count:N / (EXP) | % } % \begin{syntax} % "\tl_elt_count:n" \Arg{\tlist} \\ % "\tl_elt_count:N" <\tlvar> % \end{syntax} % Returns the number of elements in the token list. Brace groups % encountered count as one element. Note that spaces in this % token list are gobbled in the process. % \end{function} % % % \section{Variables and constants} % % \begin{variable}{\c_job_name_tl} % Constant that gets the `job name' assigned when \TeX{} starts. % \begin{texnote} % This is the new name for the primitive \tn{jobname}. It is a constant % that is set by \TeX{} and should not be overwritten by the package. % \end{texnote} % \end{variable} % % \begin{variable}{\c_empty_tl} % Constant that is always empty. % \begin{texnote} % This was named \tn{@empty} in \LaTeX2 and \tn{empty} in plain \TeX{}. % \end{texnote} % \end{variable} % % \begin{variable}{% % \l_tmpa_tl | % \l_tmpb_tl | % \g_tmpa_tl | % \g_tmpb_tl % } % Scratch register for immediate use. They are not used by conditionals % or predicate functions. % \end{variable} % % \begin{variable}{% % \l_tl_replace_toks | % } % Internal register used in the replace functions. % \end{variable} % % \begin{variable}{% % \l_testa_tl | % \l_testb_tl | % \g_testa_tl | % \g_testb_tl | % } % Registers used for conditional processing if the engine doesn't % support arbitrary string comparison. % \end{variable} % % \begin{variable}{% % \g_tl_inline_level_num | % } % Internal register used in the inline map functions. % \end{variable}% % % \section{Searching for and replacing tokens} % % \begin{function}{ % \tl_if_in:Nn / (TF) | % \tl_if_in:cn / (TF) | % \tl_if_in:nn / (TF) | % \tl_if_in:Vn / (TF) | % \tl_if_in:on / (TF) | % } % \begin{syntax} % "\tl_if_in:NnTF" <\tlvar> \Arg{item} \Arg{true code} \Arg{false code} % \end{syntax} % Function that tests if is in <\tlvar>. Depending on the result % either or is executed. Note that % cannot contain brace groups nor "#"$_6$ tokens. % \end{function} % % \begin{function}{ % \tl_replace_in:Nnn | % \tl_replace_in:cnn | % \tl_greplace_in:Nnn | % \tl_greplace_in:cnn | % } % \begin{syntax} % "\tl_replace_in:Nnn" <\tlvar> \Arg{item1} \Arg{item2} % \end{syntax} % Replaces the leftmost occurrence of in <\tlvar> with % if present, otherwise the <\tlvar> is left untouched. % Note that % cannot contain brace groups nor "#"$_6$ tokens, and % cannot contain "#"$_6$ tokens. % \end{function} % % \begin{function}{ % \tl_replace_all_in:Nnn | % \tl_replace_all_in:cnn | % \tl_greplace_all_in:Nnn | % \tl_greplace_all_in:cnn | % } % \begin{syntax} % "\tl_replace_all_in:Nnn" <\tlvar> \Arg{item1} \Arg{item2} % \end{syntax} % Replaces \emph{all} occurrences of in <\tlvar> with % . % Note that % cannot contain brace groups nor "#"$_6$ tokens, and % cannot contain "#"$_6$ tokens. % \end{function} % % % \begin{function}{ % \tl_remove_in:Nn | % \tl_remove_in:cn | % \tl_gremove_in:Nn | % \tl_gremove_in:cn | % } % \begin{syntax} % "\tl_remove_in:Nn" <\tlvar> \Arg{item} % \end{syntax} % Removes the leftmost occurrence of from <\tlvar> if present. % Note that % cannot contain brace groups nor "#"$_6$ tokens. % \end{function} % % % \begin{function}{ % \tl_remove_all_in:Nn | % \tl_remove_all_in:cn | % \tl_gremove_all_in:Nn | % \tl_gremove_all_in:cn | % } % \begin{syntax} % "\tl_remove_all_in:Nn" <\tlvar> \Arg{item} % \end{syntax} % Removes \emph{all} occurrences of from <\tlvar>. Note that % cannot contain brace groups nor "#"$_6$ tokens. % \end{function} % % % \section{Heads or tails?} % % Here are some functions for grabbing either the head or tail of a % list and perform some tests on it. % % \begin{function}{% % \tl_head:n / (EXP) | % \tl_head:V / (EXP) | % \tl_tail:n / (EXP) | % \tl_tail:V / (EXP) | % \tl_tail:f / (EXP) | % \tl_head_i:n / (EXP) | % \tl_head_iii:n / (EXP) | % \tl_head_iii:f / (EXP) | % \tl_head:w / (EXP) | % \tl_tail:w / (EXP) | % \tl_head_i:w / (EXP) | % \tl_head_iii:w / (EXP) % } % \begin{syntax} % "\tl_head:n" "{" ... "}" \\ % "\tl_tail:n" "{" ... "}" \\ % "\tl_head:w" ... "\q_nil" % \end{syntax} % These functions return either the head or the tail of a list, thus in % the above example "\tl_head:n" would return and "\tl_tail:n" % would return \dots. "\tl_head_iii:n" returns the first % three tokens. The ":w" versions require some care as they use a % delimited argument internally. % \begin{texnote} % These are the Lisp functions "car" and "cdr" but with \LaTeX3 names. % \end{texnote} % \end{function} % % % % % \begin{function}{% % \tl_if_head_eq_meaning_p:nN / (EXP) | % \tl_if_head_eq_meaning:nN / (TF)(EXP) | % } % \begin{syntax} % "\tl_if_head_eq_meaning:nNTF" \Arg{\tlist} % \Arg{true} \Arg{false} % \end{syntax} % Returns if the first token in <\tlist> is equal to % and otherwise. The |meaning| version compares the % two tokens with |\if_meaning:w|. % \end{function} % % \begin{function}{% % \tl_if_head_eq_charcode_p:nN / (EXP) | % \tl_if_head_eq_charcode_p:fN / (EXP) | % \tl_if_head_eq_charcode:nN / (TF)(EXP) | % \tl_if_head_eq_charcode:fN / (TF)(EXP) | % } % \begin{syntax} % "\tl_if_head_eq_charcode:nNTF" \Arg{\tlist} % \Arg{true} \Arg{false} % \end{syntax} % Returns if the first token in <\tlist> is equal to % and otherwise. The |meaning| version compares the % two tokens with |\if_charcode:w| but it prevents expansion of % them. If you want them to expand, you can use an |f| type expansion % first (define |\tl_if_head_eq_charcode:fNTF| or similar). % \end{function} % % \begin{function}{% % \tl_if_head_eq_catcode_p:nN / (EXP) | % \tl_if_head_eq_catcode:nN / (TF)(EXP) | % } % \begin{syntax} % "\tl_if_head_eq_catcode:nNTF" \Arg{\tlist} % \Arg{true} \Arg{false} % \end{syntax} % Returns if the first token in <\tlist> is equal to % and otherwise. This version uses |\if_catcode:w| for % the test but is otherwise identical to the |charcode| version. % \end{function} % % % \end{documentation} % % \begin{implementation} % % \section{\pkg{l3tl} implementation} % % We start by ensuring that the required packages are loaded. % \begin{macrocode} %<*package> \ProvidesExplPackage {\filename}{\filedate}{\fileversion}{\filedescription} \package_check_loaded_expl: % % \end{macrocode} % \begin{macrocode} %<*initex|package> % \end{macrocode} % % A token list variable is a control sequence that holds tokens. The % interface is similar to that for token registers, but beware that % the behavior vis \'a vis |\cs_set_nopar:Npx| etc. \ldots{} is different. (You % see this comes from Denys' implementation.) % % % \subsection{Functions} % % \begin{macro}{\tl_new:N} % \begin{macro}{\tl_new:c} % \begin{macro}{\tl_new:Nn} % \begin{macro}{\tl_new:cn} % \begin{macro}{\tl_new:Nx} % We provide one allocation function (which checks that the name is % not used) and two clear functions that locally or globally clear % the token list. The allocation function has two arguments to % specify an initial value. This is the only way to give values to % constants. % \begin{macrocode} \cs_new:Npn \tl_new:Nn #1#2{ \chk_if_free_cs:N #1 % \end{macrocode} % If checking we don't allow constants to be defined. % \begin{macrocode} %<*check> \chk_var_or_const:N #1 % % \end{macrocode} % Otherwise any variable type is allowed. % \begin{macrocode} \cs_gset_nopar:Npn #1{#2} } \cs_generate_variant:Nn \tl_new:Nn {cn} \cs_new:Npn \tl_new:Nx #1#2{ \chk_if_free_cs:N #1 % \chk_var_or_const:N #1 \cs_gset_nopar:Npx #1{#2} } \cs_new_nopar:Npn \tl_new:N #1{\tl_new:Nn #1{}} \cs_new_nopar:Npn \tl_new:c #1{\tl_new:cn {#1}{}} % \end{macrocode} % \end{macro} % \end{macro} % \end{macro} % \end{macro} % \end{macro} % % \begin{macro}{\tl_use:N} % \begin{macro}{\tl_use:c} % Perhaps this should just be enabled when checking? % \begin{macrocode} \cs_new_nopar:Npn \tl_use:N #1 { \if_meaning:w #1 \tex_relax:D % \end{macrocode} % If \m{\tlvar} equals |\tex_relax:D| it is probably stemming from a % |\cs:w|\dots|\cs_end:| that was created by mistake somewhere. % \begin{macrocode} \msg_kernel_bug:x {Token~list~variable~ `\token_to_str:N #1'~ has~ an~ erroneous~ structure!} \else: \exp_after:wN #1 \fi: } \cs_generate_variant:Nn \tl_use:N {c} % \end{macrocode} % \end{macro} % \end{macro} % % \begin{macro}{\tl_show:N,\tl_show:c,\tl_show:n} % Showing a \meta{\tlvar} is just "\show"ing it and I don't really care % about checking that it's malformed at this stage. % \begin{macrocode} \cs_new_nopar:Npn \tl_show:N #1 { \cs_show:N #1 } \cs_generate_variant:Nn \tl_show:N {c} \cs_set_eq:NN \tl_show:n \etex_showtokens:D % \end{macrocode} % \end{macro} % % % \begin{macro}{\tl_set:Nn} % \begin{macro}{\tl_set:NV} % \begin{macro}{\tl_set:No} % \begin{macro}{\tl_set:Nv} % \begin{macro}{\tl_set:Nf} % \begin{macro}{\tl_set:Nx} % \begin{macro}{\tl_set:cn} % \begin{macro}{\tl_set:cV} % \begin{macro}{\tl_set:co} % \begin{macro}{\tl_set:cx} % \begin{macro}{\tl_gset:Nn} % \begin{macro}{\tl_gset:NV} % \begin{macro}{\tl_gset:No} % \begin{macro}{\tl_gset:Nv} % \begin{macro}{\tl_gset:Nx} % \begin{macro}{\tl_gset:cn} % \begin{macro}{\tl_gset:cx} % To set token lists to a specific value to type of functions are % available: |\tl_set_eq:NN| takes two token-lists as its % arguments assign the first the contents of the second; % |\tl_set:Nn| has as its second argument a `real' list of tokens. % One can view |\tl_set_eq:NN| as a special form of |\tl_set:No|. % Both functions have global counterparts. % % During development we check if the token list that is being % assigned to exists. If not, a warning will be issued. % \begin{macrocode} %<*check> \cs_new:Npn \tl_set:Nn #1#2{ \chk_exist_cs:N #1 \cs_set_nopar:Npn #1{#2} % \end{macrocode} % We use |\chk_local_or_pref_global:N| after the assignment to % allow constructs with |\pref_global_chk:|. But one should note % that this is less efficient then using the real global variant % since they are built-in. % \begin{macrocode} \chk_local_or_pref_global:N #1 } \cs_new:Npn \tl_set:Nx #1#2{ \chk_exist_cs:N #1 \cs_set_nopar:Npx #1{#2} \chk_local:N #1 } % \end{macrocode} % The the global versions. % \begin{macrocode} \cs_new:Npn \tl_gset:Nn #1#2{ \chk_exist_cs:N #1 \cs_gset_nopar:Npn #1{#2} \chk_global:N #1 } \cs_new:Npn \tl_gset:Nx #1#2{ \chk_exist_cs:N #1 \cs_gset_nopar:Npx #1{#2} \chk_global:N #1 } % % \end{macrocode} % For some functions like |\tl_set:Nn| we need to define the % `non-check' version with arguments since we want to allow % constructions like |\tl_set:Nn\l_tmpa_tl\foo| and so we can't % use the primitive \TeX{} command. % \begin{macrocode} %<*!check> \cs_new:Npn\tl_set:Nn#1#2{\cs_set_nopar:Npn#1{#2}} \cs_new:Npn\tl_set:Nx#1#2{\cs_set_nopar:Npx#1{#2}} \cs_new:Npn\tl_gset:Nn#1#2{\cs_gset_nopar:Npn#1{#2}} \cs_new:Npn\tl_gset:Nx#1#2{\cs_gset_nopar:Npx#1{#2}} % % \end{macrocode} % The remaining functions can just be defined with help from the % expansion module. % \begin{macrocode} \cs_generate_variant:Nn \tl_set:Nn {NV,No,Nv,Nf,cn,cV,co,cx} \cs_generate_variant:Nn \tl_gset:Nn {NV,No,Nv,cn,cx} % \end{macrocode} % \end{macro} % \end{macro} % \end{macro} % \end{macro} % \end{macro} % \end{macro} % \end{macro} % \end{macro} % \end{macro} % \end{macro} % \end{macro} % \end{macro} % \end{macro} % \end{macro} % \end{macro} % \end{macro} % \end{macro} % % \begin{macro}{\tl_set_eq:NN} % \begin{macro}{\tl_set_eq:Nc} % \begin{macro}{\tl_set_eq:cN} % \begin{macro}{\tl_set_eq:cc} % \begin{macro}{\tl_gset_eq:NN} % \begin{macro}{\tl_gset_eq:Nc} % \begin{macro}{\tl_gset_eq:cN} % \begin{macro}{\tl_gset_eq:cc} % For setting token list variables equal to each other. First checking: % \begin{macrocode} %<*check> \cs_new_nopar:Npn \tl_set_eq:NN #1#2{ \chk_exist_cs:N #1 \cs_set_eq:NN #1#2 \chk_local_or_pref_global:N #1 \chk_var_or_const:N #2 } \cs_new_nopar:Npn \tl_gset_eq:NN #1#2{ \chk_exist_cs:N #1 \cs_gset_eq:NN #1#2 \chk_global:N #1 \chk_var_or_const:N #2 } % % \end{macrocode} % Non-checking versions are easy. % \begin{macrocode} %<*!check> \cs_new_eq:NN \tl_set_eq:NN \cs_set_eq:NN \cs_new_eq:NN \tl_gset_eq:NN \cs_gset_eq:NN % % \end{macrocode} % The rest again with the expansion module. % \begin{macrocode} \cs_generate_variant:Nn \tl_set_eq:NN {Nc,c,cc} \cs_generate_variant:Nn \tl_gset_eq:NN {Nc,c,cc} % \end{macrocode} % \end{macro} % \end{macro} % \end{macro} % \end{macro} % \end{macro} % \end{macro} % \end{macro} % \end{macro} % % % % \begin{macro}{\tl_clear:N} % \begin{macro}{\tl_clear:c} % \begin{macro}{\tl_gclear:N} % \begin{macro}{\tl_gclear:c} % Clearing a token list variable. % \begin{macrocode} \cs_new_nopar:Npn \tl_clear:N #1{\tl_set_eq:NN #1\c_empty_tl} \cs_generate_variant:Nn \tl_clear:N {c} \cs_new_nopar:Npn \tl_gclear:N #1{\tl_gset_eq:NN #1\c_empty_tl} \cs_generate_variant:Nn \tl_gclear:N {c} % \end{macrocode} % \end{macro} % \end{macro} % \end{macro} % \end{macro} % % \begin{macro}{\tl_clear_new:N} % \begin{macro}{\tl_clear_new:c} % These macros check whether a token list exists. If it does it % is cleared, if it doesn't it is allocated. % \begin{macrocode} %<*check> \cs_new_nopar:Npn \tl_clear_new:N #1{ \chk_var_or_const:N #1 \if_predicate:w \cs_if_exist_p:N #1 \tl_clear:N #1 \else: \tl_new:Nn #1{} \fi: } % %<-check>\cs_new_eq:NN \tl_clear_new:N \tl_clear:N \cs_generate_variant:Nn \tl_clear_new:N {c} % \end{macrocode} % \end{macro} % \end{macro} % % \begin{macro}{\tl_gclear_new:N} % \begin{macro}{\tl_gclear_new:c} % These are the global versions of the above. % \begin{macrocode} %<*check> \cs_new_nopar:Npn \tl_gclear_new:N #1{ \chk_var_or_const:N #1 \if_predicate:w \cs_if_exist_p:N #1 \tl_gclear:N #1 \else: \tl_new:Nn #1{} \fi:} % %<-check>\cs_new_eq:NN \tl_gclear_new:N \tl_gclear:N \cs_generate_variant:Nn \tl_gclear_new:N {c} % \end{macrocode} % \end{macro} % \end{macro} % % \begin{macro}{\tl_put_left:Nn} % \begin{macro}{\tl_put_left:NV} % \begin{macro}{\tl_put_left:No} % \begin{macro}{\tl_put_left:Nx} % \begin{macro}{\tl_put_left:cn} % \begin{macro}{\tl_put_left:cV} % \begin{macro}{\tl_put_left:co} % \begin{macro}{\tl_gput_left:Nn} % \begin{macro}{\tl_gput_left:NV} % \begin{macro}{\tl_gput_left:No} % \begin{macro}{\tl_gput_left:Nx} % \begin{macro}{\tl_gput_left:cn} % \begin{macro}{\tl_gput_left:cV} % \begin{macro}{\tl_gput_left:co} % We can add tokens to the left (either globally or locally). It is % not quite as easy as we would like because we have to ensure the % assignments % \begin{verbatim} % \tl_set:Nn \l_tmpa_tl{##1abc##2def} % \tl_set:Nn \l_tmpb_tl{##1abc} % \tl_put_right:Nn \l_tmpb_tl {##2def} % \end{verbatim} % cause |\l_tmpa_tl| and |\l_tmpb_tl| to be identical. The old % code did not succeed in doing this (it gave an error) and so we % use a different technique where the item(s) to be added are first % stored in a temporary variable and then added using an |x| type % expansion combined with the appropriate level of % non-expansion. Putting the tokens directly into one assignment % does not work unless we want full expansion. Note (according to % the warning earlier) \TeX\ does not allow us to treat |#|s the % same in all cases. Tough. % \begin{macrocode} \cs_new:Npn \tl_put_left:Nn #1#2 { \tl_set:Nn \l_exp_tl {#2} \tl_set:Nx #1 { \exp_not:V \l_exp_tl \exp_not:V #1 } % \chk_local_or_pref_global:N #1 } \cs_new:Npn \tl_put_left:NV #1#2 { \tl_set:Nx #1 { \exp_not:V #2 \exp_not:V #1 } } \cs_new:Npn \tl_put_left:No #1#2{ \tl_set:No \l_exp_tl {#2} \tl_set:Nx #1 { \exp_not:V \l_exp_tl \exp_not:V #1 } % \chk_local_or_pref_global:N #1 } \cs_new:Npn \tl_put_left:Nx #1#2{ \tl_set:Nx #1 { #2 \exp_not:V #1 } % \chk_local_or_pref_global:N #1 } \cs_new:Npn \tl_gput_left:Nn #1#2{ \tl_set:Nn \l_exp_tl{#2} \tl_gset:Nx #1 { \exp_not:V \l_exp_tl \exp_not:V #1 } % \chk_local_or_pref_global:N #1 } \cs_new:Npn \tl_gput_left:NV #1#2 { \tl_gset:Nx #1 { \exp_not:V #2 \exp_not:V #1 } } \cs_new:Npn \tl_gput_left:No #1#2{ \tl_set:No \l_exp_tl {#2} \tl_gset:Nx #1 { \exp_not:V \l_exp_tl \exp_not:V #1 } % \chk_local_or_pref_global:N #1 } \cs_new:Npn \tl_gput_left:Nx #1#2{ \tl_gset:Nx #1 { #2 \exp_not:V #1 } % \chk_local_or_pref_global:N #1 } \cs_generate_variant:Nn \tl_put_left:Nn {cn,co,cV} % \end{macrocode} % % \begin{macrocode} \cs_generate_variant:Nn \tl_gput_left:Nn {cn,co} \cs_generate_variant:Nn \tl_gput_left:NV {cV} % \end{macrocode} % \end{macro} % \end{macro} % \end{macro} % \end{macro} % \end{macro} % \end{macro} % \end{macro} % \end{macro} % \end{macro} % \end{macro} % \end{macro} % \end{macro} % \end{macro} % \end{macro} % % \begin{macro}{\tl_put_right:Nn} % \begin{macro}{\tl_put_right:NV} % \begin{macro}{\tl_put_right:No} % \begin{macro}{\tl_put_right:Nx} % \begin{macro}{\tl_put_right:cn} % \begin{macro}{\tl_put_right:cV} % \begin{macro}{\tl_put_right:co} % \begin{macro}{\tl_gput_right:Nn} % \begin{macro}{\tl_gput_right:NV} % \begin{macro}{\tl_gput_right:No} % \begin{macro}{\tl_gput_right:Nx} % \begin{macro}{\tl_gput_right:cn} % \begin{macro}{\tl_gput_right:cV} % \begin{macro}{\tl_gput_right:co} % These are variants of the functions above, but for adding tokens % to the right. % \begin{macrocode} \cs_new:Npn \tl_put_right:Nn #1#2 { \tl_set:Nn \l_exp_tl {#2} \tl_set:Nx #1 { \exp_not:V #1 \exp_not:V \l_exp_tl } % \chk_local_or_pref_global:N #1 } \cs_new:Npn \tl_gput_right:Nn #1#2{ \tl_set:Nn \l_exp_tl {#2} \tl_gset:Nx #1 { \exp_not:V #1 \exp_not:V \l_exp_tl } % \chk_local_or_pref_global:N #1 } \cs_new:Npn \tl_put_right:NV #1#2 { \tl_set:Nx #1 { \exp_not:V #1 \exp_not:V #2 } } \cs_new:Npn \tl_put_right:No #1#2 { \tl_set:No \l_exp_tl {#2} \tl_set:Nx #1 { \exp_not:V #1 \exp_not:V \l_exp_tl } % \chk_local_or_pref_global:N #1 } \cs_new:Npn \tl_gput_right:NV #1#2 { \tl_gset:Nx #1 { \exp_not:V #1 \exp_not:V #2 } } \cs_new:Npn \tl_gput_right:No #1#2 { \tl_set:No \l_exp_tl {#2} \tl_gset:Nx #1 { \exp_not:V #1 \exp_not:V \l_exp_tl } % \chk_local_or_pref_global:N #1 } \cs_set:Npn \tl_put_right:Nx #1#2 { \tl_set:Nx #1 { \exp_not:V #1 #2 } % \chk_local_or_pref_global:N #1 } \cs_set:Npn \tl_gput_right:Nx #1#2 { \tl_gset:Nx #1 { \exp_not:V #1 #2 } % \chk_local_or_pref_global:N #1 } \cs_generate_variant:Nn \tl_put_right:Nn {cn,co} \cs_generate_variant:Nn \tl_put_right:NV {cV} % \end{macrocode} % % \begin{macrocode} \cs_generate_variant:Nn \tl_gput_right:Nn {cn,co,cV} % \end{macrocode} % \end{macro} % \end{macro} % \end{macro} % \end{macro} % \end{macro} % \end{macro} % \end{macro} % \end{macro} % \end{macro} % \end{macro} % \end{macro} % \end{macro} % \end{macro} % \end{macro} % % % % \begin{macro}{\tl_gset:Nc} % \begin{macro}{\tl_set:Nc} % These two functions are included because they are necessary in % Denys' implementations. The |:Nc| convention (see the expansion % module) is very unusual at first sight, but it works nicely % over all modules, so we would like to keep it. % % Construct a control sequence on the fly from |#2| and save it in % |#1|. % \begin{macrocode} \cs_new_nopar:Npn \tl_gset:Nc { %<*check> \pref_global_chk: % %<-check> \pref_global:D \tl_set:Nc} % \end{macrocode} % |\pref_global_chk:| will turn the variable check in |\tl_set:No| % into a global check. % \begin{macrocode} \cs_new_nopar:Npn \tl_set:Nc #1#2{\tl_set:No #1{\cs:w#2\cs_end:}} % \end{macrocode} % \end{macro} % \end{macro} % % \subsection{Variables and constants} % % \begin{macro}{\c_job_name_tl} % Inherited from the expl3 name for the primitive. % \begin{macrocode} \tl_new:Nn \c_job_name_tl {\tex_jobname:D} % \end{macrocode} % \end{macro} % % \begin{macro}{\c_empty_tl} % Two constants which are often used. % \begin{macrocode} \tl_new:Nn \c_empty_tl {} % \end{macrocode} % \end{macro} % % \begin{macro}{\g_tmpa_tl} % \begin{macro}{\g_tmpb_tl} % Global temporary token list variables. % They are supposed to be set and used immediately, % with no delay between the definition and the use because you % can't count on other macros not to redefine them from under you. % % \begin{macrocode} \tl_new:Nn \g_tmpa_tl{} \tl_new:Nn \g_tmpb_tl{} % \end{macrocode} % \end{macro} % \end{macro} % % \begin{macro}{\l_testa_tl} % \begin{macro}{\l_testb_tl} % \begin{macro}{\g_testa_tl} % \begin{macro}{\g_testb_tl} % Global and local temporaries. These are the ones for test % routines. This means that one can safely use other temporaries % when calling test routines. % \begin{macrocode} \tl_new:Nn \l_testa_tl {} \tl_new:Nn \l_testb_tl {} \tl_new:Nn \g_testa_tl {} \tl_new:Nn \g_testb_tl {} % \end{macrocode} % \end{macro} % \end{macro} % \end{macro} % \end{macro} % % \begin{macro}{\l_tmpa_tl} % \begin{macro}{\l_tmpb_tl} % These are local temporary token list variables. % \begin{macrocode} \tl_new:Nn \l_tmpa_tl{} \tl_new:Nn \l_tmpb_tl{} % \end{macrocode} % \end{macro} % \end{macro} % % % \subsection{Predicates and conditionals} % % We also provide a few conditionals, both in expandable form (with % |\c_true_bool|) and in `brace-form', the latter are denoted by |TF| at the % end, as explained elsewhere. % % % \begin{macro}{\tl_if_empty_p:N,\tl_if_empty_p:c} % \begin{macro}[TF]{\tl_if_empty:N,\tl_if_empty:c} % These functions check whether the token list in the argument is % empty and execute the proper code from their argument(s). % \begin{macrocode} \prg_set_conditional:Npnn \tl_if_empty:N #1 {p,TF,T,F} { \if_meaning:w #1 \c_empty_tl \prg_return_true: \else: \prg_return_false: \fi: } \cs_generate_variant:Nn \tl_if_empty_p:N {c} \cs_generate_variant:Nn \tl_if_empty:NTF {c} \cs_generate_variant:Nn \tl_if_empty:NT {c} \cs_generate_variant:Nn \tl_if_empty:NF {c} % \end{macrocode} % \end{macro} % \end{macro} % % \begin{macro}{\tl_if_eq_p:NN,\tl_if_eq_p:Nc,\tl_if_eq_p:cN,\tl_if_eq_p:cc} % \begin{macro}[TF]{\tl_if_eq:NN,\tl_if_eq:Nc,\tl_if_eq:cN,\tl_if_eq:cc} % Returns |\c_true_bool| iff the two token list variables are equal. % \begin{macrocode} \prg_new_conditional:Npnn \tl_if_eq:NN #1#2 {p,TF,T,F} { \if_meaning:w #1 #2 \prg_return_true: \else: \prg_return_false: \fi: } \cs_generate_variant:Nn \tl_if_eq_p:NN {Nc,c,cc} \cs_generate_variant:Nn \tl_if_eq:NNTF {Nc,c,cc} \cs_generate_variant:Nn \tl_if_eq:NNT {Nc,c,cc} \cs_generate_variant:Nn \tl_if_eq:NNF {Nc,c,cc} % \end{macrocode} % \end{macro} % \end{macro} % % % \begin{macro}{\tl_if_empty_p:n,\tl_if_empty_p:V,\tl_if_empty_p:o} % \begin{macro}[TF]{\tl_if_empty:n,\tl_if_empty:V,\tl_if_empty:o} % It would be tempting to just use "\if_meaning:w\q_nil#1\q_nil" as % a test since this works really well. However it fails on a token % list starting with "\q_nil" of course but more troubling is the % case where argument is a complete conditional such as "\if_true:" % a "\else:" b "\fi:" because then "\if_true:" is used by % "\if_meaning:w", the test turns out false, the "\else:" executes % the false branch, the "\fi:" ends it and the "\q_nil" at the end % starts executing\dots{} A safer route is to convert the entire % token list into harmless characters first and then compare % that. This way the test will even accept "\q_nil" as the first % token. % \begin{macrocode} \prg_new_conditional:Npnn \tl_if_empty:n #1 {p,TF,T,F} { \exp_after:wN \if_meaning:w \exp_after:wN \q_nil \tl_to_str:n {#1} \q_nil \prg_return_true: \else: \prg_return_false: \fi: } \cs_generate_variant:Nn \tl_if_empty_p:n {V} \cs_generate_variant:Nn \tl_if_empty:nTF {V} \cs_generate_variant:Nn \tl_if_empty:nT {V} \cs_generate_variant:Nn \tl_if_empty:nF {V} \cs_generate_variant:Nn \tl_if_empty_p:n {o} \cs_generate_variant:Nn \tl_if_empty:nTF {o} \cs_generate_variant:Nn \tl_if_empty:nT {o} \cs_generate_variant:Nn \tl_if_empty:nF {o} % \end{macrocode} % \end{macro} % \end{macro} % % \begin{macro}{\tl_if_blank_p:n,\tl_if_blank_p:V,\tl_if_blank_p:o} % \begin{macro}[TF]{\tl_if_blank:n,\tl_if_blank:V,\tl_if_blank:o} % \begin{macro}[aux]{\tl_if_blank_p_aux:w} % This is based on the answers in ``Around the Bend No~2'' but is % safer as the tests listed there all have one small flaw: If the % input in the test is two tokens with the same meaning as the % internal delimiter, they will fail since one of them is mistaken % for the actual delimiter. In our version below we make sure to % pass the input through |\tl_to_str:n| which ensures that all % the tokens are converted to catcode 12. However we use an |a| with % catcode 11 as delimiter so we can \emph{never} get into the same % problem as the solutions in ``Around the Bend No~2''. % \begin{macrocode} \prg_new_conditional:Npnn \tl_if_blank:n #1 {p,TF,T,F} { \exp_after:wN \tl_if_blank_p_aux:w \tl_to_str:n {#1} aa..\q_nil } \cs_new:Npn \tl_if_blank_p_aux:w #1#2 a #3#4 \q_nil { \if_meaning:w #3 #4 \prg_return_true: \else: \prg_return_false: \fi: } \cs_generate_variant:Nn \tl_if_blank_p:n {V} \cs_generate_variant:Nn \tl_if_blank:nTF {V} \cs_generate_variant:Nn \tl_if_blank:nT {V} \cs_generate_variant:Nn \tl_if_blank:nF {V} \cs_generate_variant:Nn \tl_if_blank_p:n {o} \cs_generate_variant:Nn \tl_if_blank:nTF {o} \cs_generate_variant:Nn \tl_if_blank:nT {o} \cs_generate_variant:Nn \tl_if_blank:nF {o} % \end{macrocode} % \end{macro} % \end{macro} % \end{macro} % % \begin{macro}[TF]{\tl_if_eq:xx,\tl_if_eq:nn,\tl_if_eq:VV,\tl_if_eq:oo, % \tl_if_eq:xn,\tl_if_eq:nx,\tl_if_eq:on,\tl_if_eq:no, % \tl_if_eq:Vn,\tl_if_eq:nV, % \tl_if_eq:xV,\tl_if_eq:xo,\tl_if_eq:Vx,\tl_if_eq:ox} % \begin{macro}{\tl_if_eq_p:xx,\tl_if_eq_p:nn,\tl_if_eq_p:VV,\tl_if_eq_p:oo, % \tl_if_eq_p:xn,\tl_if_eq_p:nx,\tl_if_eq_p:Vn,\tl_if_eq_p:on, % \tl_if_eq_p:nV,\tl_if_eq_p:no,\tl_if_eq_p:xV,\tl_if_eq_p:Vx, % \tl_if_eq_p:xo,\tl_if_eq_p:ox} % Test if two token lists are identical. pdf\TeX\ contains a most % interesting primitive for expandable string comparison so we make % use of it if available. Presumable it will be in the final % version. % % Firstly we give it an appropriate name. Note that this primitive % actually performs an \texttt{x} type expansion but it is still % expandable! Hence we must program these functions backwards to add % \verb|\exp_not:n|. We provide the combinations for the types % \texttt{n}, \texttt{o} and \texttt{x}. % \begin{macrocode} \cs_new_eq:NN \tl_compare:xx \pdf_strcmp:D \cs_new:Npn \tl_compare:nn #1#2{ \tl_compare:xx{\exp_not:n{#1}}{\exp_not:n{#2}} } \cs_new:Npn \tl_compare:nx #1{ \tl_compare:xx{\exp_not:n{#1}} } \cs_new:Npn \tl_compare:xn #1#2{ \tl_compare:xx{#1}{\exp_not:n{#2}} } \cs_new:Npn \tl_compare:nV #1#2 { \tl_compare:xx { \exp_not:n {#1} } { \exp_not:V #2 } } \cs_new:Npn \tl_compare:no #1#2{ \tl_compare:xx{\exp_not:n{#1}}{\exp_not:n\exp_after:wN{#2}} } \cs_new:Npn \tl_compare:Vn #1#2 { \tl_compare:xx { \exp_not:V #1 } { \exp_not:n {#2} } } \cs_new:Npn \tl_compare:on #1#2{ \tl_compare:xx{\exp_not:n\exp_after:wN{#1}}{\exp_not:n{#2}} } \cs_new:Npn \tl_compare:VV #1#2 { \tl_compare:xx { \exp_not:V #1 } { \exp_not:V #2 } } \cs_new:Npn \tl_compare:oo #1#2{ \tl_compare:xx{\exp_not:n\exp_after:wN{#1}}{\exp_not:n\exp_after:wN{#2}} } \cs_new:Npn \tl_compare:xV #1#2 { \tl_compare:xx {#1} { \exp_not:V #2 } } \cs_new:Npn \tl_compare:xo #1#2{ \tl_compare:xx{#1}{\exp_not:n\exp_after:wN{#2}} } \cs_new:Npn \tl_compare:Vx #1#2 { \tl_compare:xx { \exp_not:V #1 } {#2} } \cs_new:Npn \tl_compare:ox #1#2{ \tl_compare:xx{\exp_not:n\exp_after:wN{#1}}{#2} } % \end{macrocode} % Since we have a lot of basically identical functions to define we % define one to define the rest. Unfortunately we aren't quite set up % to use the new \verb|\tl_map_inline:nn| function yet. % \begin{macrocode} \cs_set_nopar:Npn \tl_tmp:w #1 { \tl_set:Nx \l_tmpa_tl { \exp_not:N \prg_new_conditional:Npnn \exp_not:c {tl_if_eq:#1} ####1 ####2 {p,TF,T,F} { \exp_not:N \tex_ifnum:D \exp_not:c {tl_compare:#1} {####1}{####2} \exp_not:n{ =\c_zero \prg_return_true: \else: \prg_return_false: \fi: } } } \l_tmpa_tl } \tl_tmp:w{xx} \tl_tmp:w{nx} \tl_tmp:w{ox} \tl_tmp:w{Vx} \tl_tmp:w{xn} \tl_tmp:w{nn} \tl_tmp:w{on} \tl_tmp:w{Vn} \tl_tmp:w{xo} \tl_tmp:w{no} \tl_tmp:w{oo} \tl_tmp:w{xV} \tl_tmp:w{nV} \tl_tmp:w{VV} % \end{macrocode} % However all of this only makes sense if we actually have that % primitive. Therefore we disable it again if it is not there and % define \verb|\tl_if_eq:nn| the old fashioned (and unexpandable) % way. % % In some cases below, since arbitrary token lists could be being used in % this function, you can't assume (as token list variables usually do) that there won't be % any |#| tokens. Therefore, "\tl_set:Nx" and "\exp_not:n" is used instead % of plain "\tl_set:Nn". % % \begin{macrocode} \cs_if_exist:cF{pdf_strcmp:D}{ \prg_set_protected_conditional:Npnn \tl_if_eq:nn #1#2 {TF,T,F} { \tl_set:Nx \l_testa_tl {\exp_not:n{#1}} \tl_set:Nx \l_testb_tl {\exp_not:n{#2}} \if_meaning:w\l_testa_tl \l_testb_tl \prg_return_true: \else: \prg_return_false: \fi: } \prg_set_protected_conditional:Npnn \tl_if_eq:nV #1#2 {TF,T,F} { \tl_set:Nx \l_testa_tl { \exp_not:n {#1} } \tl_set:Nx \l_testb_tl { \exp_not:V #2 } \if_meaning:w \l_testa_tl \l_testb_tl \prg_return_true: \else: \prg_return_false: \fi: } \prg_set_protected_conditional:Npnn \tl_if_eq:no #1#2 {TF,T,F} { \tl_set:Nx \l_testa_tl {\exp_not:n{#1}} \tl_set:Nx \l_testb_tl {\exp_not:o{#2}} \if_meaning:w\l_testa_tl \l_testb_tl \prg_return_true: \else: \prg_return_false: \fi: } \prg_set_protected_conditional:Npnn \tl_if_eq:nx #1#2 {TF,T,F} { \tl_set:Nx \l_testa_tl {\exp_not:n{#1}} \tl_set:Nx \l_testb_tl {#2} \if_meaning:w\l_testa_tl \l_testb_tl \prg_return_true: \else: \prg_return_false: \fi: } \prg_set_protected_conditional:Npnn \tl_if_eq:Vn #1#2 {TF,T,F} { \tl_set:Nx \l_testa_tl { \exp_not:V #1 } \tl_set:Nx \l_testb_tl { \exp_not:n{#2} } \if_meaning:w \l_testa_tl \l_testb_tl \prg_return_true: \else: \prg_return_false: \fi: } \prg_set_protected_conditional:Npnn \tl_if_eq:on #1#2 {TF,T,F} { \tl_set:Nx \l_testa_tl {\exp_not:o{#1}} \tl_set:Nx \l_testb_tl {\exp_not:n{#2}} \if_meaning:w\l_testa_tl \l_testb_tl \prg_return_true: \else: \prg_return_false: \fi: } \prg_set_protected_conditional:Npnn \tl_if_eq:VV #1#2 {TF,T,F} { \tl_set:Nx \l_testa_tl { \exp_not:V #1 } \tl_set:Nx \l_testb_tl { \exp_not:V #2 } \if_meaning:w \l_testa_tl \l_testb_tl \prg_return_true: \else: \prg_return_false: \fi: } \prg_set_protected_conditional:Npnn \tl_if_eq:oo #1#2 {TF,T,F} { \tl_set:Nx \l_testa_tl {\exp_not:o{#1}} \tl_set:Nx \l_testb_tl {\exp_not:o{#2}} \if_meaning:w\l_testa_tl \l_testb_tl \prg_return_true: \else: \prg_return_false: \fi: } \prg_set_protected_conditional:Npnn \tl_if_eq:Vx #1#2 {TF,T,F} { \tl_set:Nx \l_testa_tl { \exp_not:V #1 } \tl_set:Nx \l_testb_tl {#2} \if_meaning:w \l_testa_tl \l_testb_tl \prg_return_true: \else: \prg_return_false: \fi: } \prg_set_protected_conditional:Npnn \tl_if_eq:ox #1#2 {TF,T,F} { \tl_set:Nx \l_testa_tl {\exp_not:o{#1}} \tl_set:Nx \l_testb_tl {#2} \if_meaning:w\l_testa_tl \l_testb_tl \prg_return_true: \else: \prg_return_false: \fi: } \prg_set_protected_conditional:Npnn \tl_if_eq:xn #1#2 {TF,T,F} { \tl_set:Nx \l_testa_tl {#1} \tl_set:Nx \l_testb_tl {\exp_not:n{#2}} \if_meaning:w\l_testa_tl \l_testb_tl \prg_return_true: \else: \prg_return_false: \fi: } \prg_set_protected_conditional:Npnn \tl_if_eq:xV #1#2 {TF,T,F} { \tl_set:Nx \l_testa_tl {#1} \tl_set:Nx \l_testb_tl { \exp_not:V #2 } \if_meaning:w \l_testa_tl \l_testb_tl \prg_return_true: \else: \prg_return_false: \fi: } \prg_set_protected_conditional:Npnn \tl_if_eq:xo #1#2 {TF,T,F} { \tl_set:Nx \l_testa_tl {#1} \tl_set:Nx \l_testb_tl {\exp_not:o{#2}} \if_meaning:w\l_testa_tl \l_testb_tl \prg_return_true: \else: \prg_return_false: \fi: } \prg_set_protected_conditional:Npnn \tl_if_eq:xx #1#2 {TF,T,F} { \tl_set:Nx \l_testa_tl {#1} \tl_set:Nx \l_testb_tl {#2} \if_meaning:w\l_testa_tl \l_testb_tl \prg_return_true: \else: \prg_return_false: \fi: } } % \end{macrocode} % \end{macro} % \end{macro} % % % \subsection{Working with the contents of token lists} % % % \begin{macro}{\tl_to_lowercase:n} % \begin{macro}{\tl_to_uppercase:n} % Just some names for a few primitives. % \begin{macrocode} \cs_new_eq:NN \tl_to_lowercase:n \tex_lowercase:D \cs_new_eq:NN \tl_to_uppercase:n \tex_uppercase:D % \end{macrocode} % \end{macro} % \end{macro} % % \begin{macro}{\tl_to_str:n} % Another name for a primitive. % \begin{macrocode} \cs_new_eq:NN \tl_to_str:n \etex_detokenize:D % \end{macrocode} % \end{macro} % % \begin{macro}{\tl_to_str:N} % \begin{macro}{\tl_to_str:c} % \begin{macro}[aux]{\tl_to_str_aux:w} % These functions return the replacement text of a token list as a % string list with all characters catcoded to `other'. % \begin{macrocode} \cs_new_nopar:Npn \tl_to_str:N {\exp_after:wN\tl_to_str_aux:w \token_to_meaning:N} \cs_new_nopar:Npn \tl_to_str_aux:w #1>{} \cs_generate_variant:Nn \tl_to_str:N {c} % \end{macrocode} % \end{macro} % \end{macro} % \end{macro} % % \begin{macro}{\tl_map_function:nN} % \begin{macro}{\tl_map_function:NN} % \begin{macro}{\tl_map_function:cN} % \begin{macro}[aux]{\tl_map_function_aux:NN} % Expandable loop macro for token lists. These have the advantage of not % needing to test if the argument is empty, because if it is, the stop % marker will be read immediately and the loop terminated. % \begin{macrocode} \cs_new:Npn \tl_map_function:nN #1#2{ \tl_map_function_aux:Nn #2 #1 \q_recursion_tail \q_recursion_stop } \cs_new_nopar:Npn \tl_map_function:NN #1#2{ \exp_after:wN \tl_map_function_aux:Nn \exp_after:wN #2 #1 \q_recursion_tail \q_recursion_stop } \cs_new:Npn \tl_map_function_aux:Nn #1#2{ \quark_if_recursion_tail_stop:n{#2} #1{#2} \tl_map_function_aux:Nn #1 } \cs_generate_variant:Nn \tl_map_function:NN {cN} % \end{macrocode} % \end{macro} % \end{macro} % \end{macro} % \end{macro} % % % \begin{macro}{\tl_map_inline:nn} % \begin{macro}{\tl_map_inline:Nn} % \begin{macro}{\tl_map_inline:cn} % \begin{macro}[aux]{\tl_map_inline_aux:n} % \begin{macro}{\g_tl_inline_level_num} % The inline functions are straight forward by now. We use a little % trick with the fake counter |\g_tl_inline_level_num| to make % them nestable.\footnote{This should be a proper integer, but I % don't want to mess with the dependencies right now\dots} We can % also make use of |\tl_map_function:Nn| from before. % \begin{macrocode} \cs_new:Npn \tl_map_inline:nn #1#2{ \num_gincr:N \g_tl_inline_level_num \cs_gset:cpn {tl_map_inline_ \num_use:N \g_tl_inline_level_num :n} ##1{#2} \exp_args:Nc \tl_map_function_aux:Nn {tl_map_inline_ \num_use:N \g_tl_inline_level_num :n} #1 \q_recursion_tail\q_recursion_stop \num_gdecr:N \g_tl_inline_level_num } \cs_new:Npn \tl_map_inline:Nn #1#2{ \num_gincr:N \g_tl_inline_level_num \cs_gset:cpn {tl_map_inline_ \num_use:N \g_tl_inline_level_num :n} ##1{#2} \exp_last_unbraced:NcV \tl_map_function_aux:Nn {tl_map_inline_ \num_use:N \g_tl_inline_level_num :n} #1 \q_recursion_tail\q_recursion_stop \num_gdecr:N \g_tl_inline_level_num } \cs_generate_variant:Nn \tl_map_inline:Nn {c} \tl_new:Nn \g_tl_inline_level_num{0} % \end{macrocode} % \end{macro} % \end{macro} % \end{macro} % \end{macro} % \end{macro} % % % \begin{macro}{\tl_map_variable:nNn} % \begin{macro}{\tl_map_variable:NNn} % \begin{macro}{\tl_map_variable:cNn} % |\tl_map_variable:nNn| \meta{\tlist} \meta{temp} \meta{action} assigns % \meta{temp} to each element and executes \meta{action}. % \begin{macrocode} \cs_new:Npn \tl_map_variable:nNn #1#2#3{ \tl_map_variable_aux:Nnn #2 {#3} #1 \q_recursion_tail \q_recursion_stop } % \end{macrocode} % Next really has to be v/V args % \begin{macrocode} \cs_new_nopar:Npn \tl_map_variable:NNn {\exp_args:No \tl_map_variable:nNn} \cs_generate_variant:Nn \tl_map_variable:NNn {c} % \end{macrocode} % \end{macro} % \end{macro} % \end{macro} % % \begin{macro}[aux]{\tl_map_variable_aux:NnN} % The general loop. Assign the temp variable |#1| to the current item % |#3| and then check if that's the stop marker. If it is, break the % loop. If not, execute the action |#2| and continue. % \begin{macrocode} \cs_new:Npn \tl_map_variable_aux:Nnn #1#2#3{ \tl_set:Nn #1{#3} \quark_if_recursion_tail_stop:N #1 #2 \tl_map_variable_aux:Nnn #1{#2} } % \end{macrocode} % \end{macro} % % \begin{macro}{\tl_map_break:} % The break statement. % \begin{macrocode} \cs_new_eq:NN \tl_map_break: \use_none_delimit_by_q_recursion_stop:w % \end{macrocode} % \end{macro} % % \begin{macro}{\tl_reverse:n} % \begin{macro}{\tl_reverse:V} % \begin{macro}{\tl_reverse:o} % \begin{macro}[aux]{\tl_reverse_aux:nN} % Reversal of a token list is done by taking one token at a time % and putting it in front of the ones before it. % \begin{macrocode} \cs_new:Npn \tl_reverse:n #1{ \tl_reverse_aux:nN {} #1 \q_recursion_tail\q_recursion_stop } \cs_new:Npn \tl_reverse_aux:nN #1#2{ \quark_if_recursion_tail_stop_do:nn {#2}{ #1 } \tl_reverse_aux:nN {#2#1} } \cs_generate_variant:Nn \tl_reverse:n {V,o} % \end{macrocode} % \end{macro} % \end{macro} % \end{macro} % \end{macro} % % \begin{macro}{\tl_reverse:N} % This reverses the list, leaving "\exp_stop_f:" in front, which in turn % is removed by the "f" expansion which comes to a halt. % \begin{macrocode} \cs_new_nopar:Npn \tl_reverse:N #1 { \tl_set:Nf #1 { \tl_reverse:o { #1 \exp_stop_f: } } } % \end{macrocode} % \end{macro} % % \begin{macro}{\tl_elt_count:n} % \begin{macro}{\tl_elt_count:V} % \begin{macro}{\tl_elt_count:o} % \begin{macro}{\tl_elt_count:N} % Count number of elements within a token list or token list % variable. Brace groups within the list are read as a single % element. % "\num_elt_count:n" grabs the element and replaces it by "+1". The % "0" to ensure it works on an empty list. % \begin{macrocode} \cs_new:Npn \tl_elt_count:n #1{ \intexpr_eval:n { \tl_map_function:nN {#1}\num_elt_count:n } } \cs_generate_variant:Nn \tl_elt_count:n {V,o} \cs_new_nopar:Npn \tl_elt_count:N #1{ \intexpr_eval:n { \tl_map_function:NN #1 \num_elt_count:n } } % \end{macrocode} % \end{macro} % \end{macro} % \end{macro} % \end{macro} % % \begin{macro}{\tl_set_rescan:Nnn, \tl_gset_rescan:Nnn} % These functions store the \marg{\tlist} in \meta{\tlvar} after % redefining catcodes, etc., in argument "#2". % \begin{arguments} % \item \meta{\tlvar} % \item \marg{catcode setup, etc.} % \item \marg{\tlist} % \end{arguments} % \begin{macrocode} \cs_new:Npn \tl_set_rescan:Nnn { \tl_set_rescan_aux:NNnn \tl_set:Nn } \cs_new:Npn \tl_gset_rescan:Nnn { \tl_set_rescan_aux:NNnn \tl_gset:Nn } % \end{macrocode} % % \begin{macro}[aux]{\tl_set_rescan_aux:NNnn} % This macro uses a trick to extract an unexpanded token list after it's % rescanned with "\etex_scantokens:D". This technique was first used (as far as I % know) by Heiko Oberdiek in his \pkg{catchfile} package, albeit for real % files rather than the `fake' "\scantokens" one. % % The basic problem arises because "\etex_scantokens:D" emulates a file read, which % inserts an EOF marker into the expansion; the simplistic\\ % "\exp_args:NNo \cs_set:Npn \tmp:w { \etex_scantokens:D {some text} }"\\ % unfortunately doesn't work, calling the error:\\ % "! File ended while scanning definition of \tmp:w."\\ % (Lua\TeX\ works around this % problem with its "\scantextokens" primitive.) % % Usually, we'd define "\etex_everyeof:D" to be "\exp_not:N" to gobble the EOF % marker, but since we're not expanding the token list, it gets left in there % and we have the same basic problem. % % Instead, we define "\etex_everyeof:D" to contain a marker that's impossible % to occur within the scanned text; that is, the same char twice with % different catcodes. (For some reason, we \emph{don't} need to insert % a "\exp_not:N" token after it to prevent the EOF marker to expand. Anyone % know why?) % % A helper function is can be used to save the token list delimited by the % special marker, keeping the catcode redefinitions hidden away in a group. % % \begin{macro}[aux]{\c_two_ats_with_two_catcodes_tl} % A tl with two "@" characters with two different catcodes. % Used as a special marker for delimited text. % \begin{macrocode} \group_begin: \tex_lccode:D `\A = `\@ \scan_stop: \tex_lccode:D `\B = `\@ \scan_stop: \tex_catcode:D `\A = 8 \scan_stop: \tex_catcode:D `\B = 3 \scan_stop: \tl_to_lowercase:n { \group_end: \tl_new:Nn \c_two_ats_with_two_catcodes_tl {AB} } % \end{macrocode} % \end{macro} % % \begin{arguments} % \item "\tl_set" function % \item \meta{\tlvar} % \item \marg{catcode setup, etc.} % \item \marg{\tlist} % \end{arguments} % Note that if you change "\etex_everyeof:D" in "#3" % then you'd better do it correctly! % \begin{macrocode} \cs_new:Npn \tl_set_rescan_aux:NNnn #1#2#3#4 { \group_begin: \toks_set:NV \etex_everyeof:D \c_two_ats_with_two_catcodes_tl \tex_endlinechar:D = \c_minus_one #3 \exp_after:wN \tl_rescan_aux:w \etex_scantokens:D {#4} \exp_args:NNNV \group_end: #1 #2 \l_tmpa_toks } % \end{macrocode} % % \begin{macro}[aux]{\tl_rescan_aux:w} % \begin{macrocode} \exp_after:wN \cs_set:Npn \exp_after:wN \tl_rescan_aux:w \exp_after:wN # \exp_after:wN 1 \c_two_ats_with_two_catcodes_tl { \tl_set:Nn \l_tmpa_toks {#1} } % \end{macrocode} % % \end{macro} % \end{macro} % \end{macro} % % \begin{macro}{\tl_set_rescan:Nnx,\tl_gset_rescan:Nnx} % These functions store the full expansion of \marg{\tlist} in % \meta{\tlvar} after redefining catcodes, etc., in argument "#2". % \begin{arguments} % \item \meta{\tlvar} % \item \marg{catcode setup, etc.} % \item \marg{\tlist} % \end{arguments} % The expanded versions are much simpler because the "\etex_scantokens:D" % can occur within the expansion. % \begin{macrocode} \cs_new:Npn \tl_set_rescan:Nnx #1#2#3 { \group_begin: \etex_everyeof:D { \exp_not:N } \tex_endlinechar:D = \c_minus_one #2 \tl_set:Nx \l_tmpa_tl { \etex_scantokens:D {#3} } \exp_args:NNNV \group_end: \tl_set:Nn #1 \l_tmpa_tl } % \end{macrocode} % Globally is easier again: % \begin{macrocode} \cs_new:Npn \tl_gset_rescan:Nnx #1#2#3 { \group_begin: \etex_everyeof:D { \exp_not:N } \tex_endlinechar:D = \c_minus_one #2 \tl_gset:Nx #1 { \etex_scantokens:D {#3} } \group_end: } % \end{macrocode} % \end{macro} % % \begin{macro}{\tl_rescan:nn} % The inline wrapper for "\etex_scantokens:D". % \begin{arguments} % \item Catcode changes (etc.) % \item Token list to re-tokenise % \end{arguments} % \begin{macrocode} \cs_new:Npn \tl_rescan:nn #1#2 { \group_begin: \toks_set:NV \etex_everyeof:D \c_two_ats_with_two_catcodes_tl \tex_endlinechar:D = \c_minus_one #1 \exp_after:wN \tl_rescan_aux:w \etex_scantokens:D {#2} \exp_args:NV \group_end: \l_tmpa_toks } % \end{macrocode} % \end{macro} % % \subsection{Checking for and replacing tokens} % % \begin{macro}[TF]{\tl_if_in:Nn,\tl_if_in:cn} % See the replace functions for further comments. In this part we % don't care too much about brace stripping since we are not % interested in passing on the tokens which are split off in the % process. % \begin{macrocode} \prg_new_protected_conditional:Npnn \tl_if_in:Nn #1#2 {TF,T,F} { \cs_set:Npn \tl_tmp:w ##1 #2 ##2 \q_stop { \quark_if_no_value:nTF {##2} {\prg_return_false:} {\prg_return_true:} } \exp_after:wN \tl_tmp:w #1 #2 \q_no_value \q_stop } \cs_generate_variant:Nn \tl_if_in:NnTF {c} \cs_generate_variant:Nn \tl_if_in:NnT {c} \cs_generate_variant:Nn \tl_if_in:NnF {c} % \end{macrocode} % \end{macro} % % \begin{macro}[TF]{\tl_if_in:nn,\tl_if_in:Vn,\tl_if_in:on} % \begin{macrocode} \prg_new_protected_conditional:Npnn \tl_if_in:nn #1#2 {TF,T,F} { \cs_set:Npn \tl_tmp:w ##1 #2 ##2 \q_stop { \quark_if_no_value:nTF {##2} {\prg_return_false:} {\prg_return_true:} } \tl_tmp:w #1 #2 \q_no_value \q_stop } \cs_generate_variant:Nn \tl_if_in:nnTF {V} \cs_generate_variant:Nn \tl_if_in:nnT {V} \cs_generate_variant:Nn \tl_if_in:nnF {V} \cs_generate_variant:Nn \tl_if_in:nnTF {o} \cs_generate_variant:Nn \tl_if_in:nnT {o} \cs_generate_variant:Nn \tl_if_in:nnF {o} % \end{macrocode} % \end{macro} % % \begin{macro}{\l_tl_replace_toks} % A temp variable for the replace operations. % \begin{macrocode} %%\l_tl_replace_toks % moved to l3token as alloc not set up yet. % \end{macrocode} % \end{macro} % % \begin{macro}{\tl_replace_in:Nnn} % \begin{macro}{\tl_replace_in:cnn} % \begin{macro}{\tl_greplace_in:Nnn} % \begin{macro}{\tl_greplace_in:cnn} % Replacing the first item in a token list variable goes like this: % Define a temporary function with delimited arguments containing % the search term and take a closer look at what is left. We append % the expansion of the token list with the search term plus the quark % |\q_no_value|. If the search term isn't present this last one is % found and the following token is the quark, so we test for % that. If the search term is present we will have to split off the % |#3\q_no_value| we had, so we define yet another function with % delimited arguments to do this. The advantage here is that now we % have a special end sequence so there is no problem if the search % term appears more than once. Only problem left is to prevent brace % stripping in both ends, so we prepend the expansion of the token list % with |\prg_do_nothing:| later to be expanded and also prepend the % remainder of the first split operation with |\prg_do_nothing:| also to % be expanded again later on. % % \begin{macro}[aux]{\tl_replace_in_aux:NNnn} % \begin{arguments} % \item "\tl_set:Nx" or "\tl_gset:Nx" % \item \meta{\tlvar} % \item item to find % \item replacement text % \end{arguments} % \begin{macrocode} \cs_new:Npn \tl_replace_in_aux:NNnn #1#2#3#4{ \cs_set:Npn \tl_tmp:w ##1#3##2\q_stop{ \quark_if_no_value:nF{##2} { % \end{macrocode} % At this point |##1| starts with a |\prg_do_nothing:| so we expand it to % remove it. % \begin{macrocode} \toks_set:No\l_tl_replace_toks{##1#4} \cs_set:Npn \tl_tmp:w ####1#3\q_no_value{ \toks_put_right:No \l_tl_replace_toks { ####1 } } \tl_tmp:w \prg_do_nothing: ##2 % \end{macrocode} % Now all that is done is setting the token list variable equal to the % expansion of the token register. % \begin{macrocode} #1#2{\toks_use:N\l_tl_replace_toks} } } % \end{macrocode} % Here is where we start the process. Note that the token list might start % with a space token so we use this little trick with |\use:n| % to prevent it from being removed. % \begin{macrocode} \use:n{\exp_after:wN \tl_tmp:w\exp_after:wN\prg_do_nothing:} #2#3 \q_no_value\q_stop } % \end{macrocode} % Now the various versions doing the replacement either globally or % locally. % \begin{macrocode} \cs_new_nopar:Npn \tl_replace_in:Nnn {\tl_replace_in_aux:NNnn \tl_set:Nx} \cs_generate_variant:Nn\tl_replace_in:Nnn {cnn} % \end{macrocode} % % \begin{macrocode} \cs_new_nopar:Npn \tl_greplace_in:Nnn {\tl_replace_in_aux:NNnn \tl_gset:Nx} \cs_generate_variant:Nn\tl_greplace_in:Nnn {cnn} % \end{macrocode} % \end{macro} % \end{macro} % \end{macro} % \end{macro} % \end{macro} % % \begin{macro}{\tl_replace_all_in:Nnn} % \begin{macro}{\tl_replace_all_in:cnn} % \begin{macro}{\tl_greplace_all_in:Nnn} % \begin{macro}{\tl_greplace_all_in:cnn} % \begin{macro}[aux]{\tl_replace_all_in_aux:NNnn} % The version for replacing \emph{all} occurrences of the search % term is fairly easy since we just have to keep doing the % replacement on the split-off part until all are % replaced. Otherwise it is pretty much the same as above. % \begin{macrocode} \cs_set:Npn \tl_replace_all_in_aux:NNnn #1#2#3#4{ \toks_clear:N \l_tl_replace_toks \cs_set:Npn \tl_tmp:w ##1#3##2\q_stop{ \quark_if_no_value:nTF{##2} { \toks_put_right:No \l_tl_replace_toks {##1} } { \toks_put_right:No \l_tl_replace_toks {##1 #4} \tl_tmp:w \prg_do_nothing: ##2 \q_stop } } \use:n{\exp_after:wN \tl_tmp:w \exp_after:wN \prg_do_nothing:} #2#3 \q_no_value\q_stop #1#2{\toks_use:N\l_tl_replace_toks} } % \end{macrocode} % Now the various forms. % \begin{macrocode} \cs_new_nopar:Npn \tl_replace_all_in:Nnn { \tl_replace_all_in_aux:NNnn \tl_set:Nx} \cs_generate_variant:Nn \tl_replace_all_in:Nnn {cnn} % \end{macrocode} % % \begin{macrocode} \cs_new_nopar:Npn \tl_greplace_all_in:Nnn { \tl_replace_all_in_aux:NNnn \tl_gset:Nx} \cs_generate_variant:Nn \tl_greplace_all_in:Nnn {cnn} % \end{macrocode} % \end{macro} % \end{macro} % \end{macro} % \end{macro} % \end{macro} % % % % \begin{macro}{\tl_remove_in:Nn} % \begin{macro}{\tl_remove_in:cn} % \begin{macro}{\tl_gremove_in:Nn} % \begin{macro}{\tl_gremove_in:cn} % Next comes a series of removal functions. I have just implemented % them as subcases of the replace functions for now (I'm lazy). % \begin{macrocode} \cs_new:Npn \tl_remove_in:Nn #1#2{\tl_replace_in:Nnn #1{#2}{}} \cs_new:Npn \tl_gremove_in:Nn #1#2{\tl_greplace_in:Nnn #1{#2}{}} \cs_generate_variant:Nn \tl_remove_in:Nn {cn} \cs_generate_variant:Nn \tl_gremove_in:Nn {cn} % \end{macrocode} % \end{macro} % \end{macro} % \end{macro} % \end{macro} % % % \begin{macro}{\tl_remove_all_in:Nn} % \begin{macro}{\tl_remove_all_in:cn} % \begin{macro}{\tl_gremove_all_in:Nn} % \begin{macro}{\tl_gremove_all_in:cn} % Same old, same old. % \begin{macrocode} \cs_new:Npn \tl_remove_all_in:Nn #1#2{ \tl_replace_all_in:Nnn #1{#2}{} } \cs_new:Npn \tl_gremove_all_in:Nn #1#2{ \tl_greplace_all_in:Nnn #1{#2}{} } \cs_generate_variant:Nn \tl_remove_all_in:Nn {cn} \cs_generate_variant:Nn \tl_gremove_all_in:Nn {cn} % \end{macrocode} % \end{macro} % \end{macro} % \end{macro} % \end{macro} % % % \subsection{Heads or tails?} % % \begin{macro}{\tl_head:n} % \begin{macro}{\tl_head:V} % \begin{macro}{\tl_head_i:n} % \begin{macro}{\tl_tail:n} % \begin{macro}{\tl_tail:V} % \begin{macro}{\tl_tail:f} % \begin{macro}{\tl_head_iii:n} % \begin{macro}{\tl_head_iii:f} % \begin{macro}{\tl_head:w} % \begin{macro}{\tl_head_i:w} % \begin{macro}{\tl_tail:w} % \begin{macro}{\tl_head_iii:w} % These functions pick up either the head or the tail of a list. % "\tl_head_iii:n" returns the first three items on a list. % \begin{macrocode} \cs_new:Npn \tl_head:n #1{\tl_head:w #1\q_nil} \cs_new_eq:NN \tl_head_i:n \tl_head:n \cs_new:Npn \tl_tail:n #1{\tl_tail:w #1\q_nil} \cs_generate_variant:Nn \tl_tail:n {f} \cs_new:Npn \tl_head_iii:n #1{\tl_head_iii:w #1\q_nil} \cs_generate_variant:Nn \tl_head_iii:n {f} \cs_new_eq:NN \tl_head:w \use_i_delimit_by_q_nil:nw \cs_new_eq:NN \tl_head_i:w \tl_head:w \cs_new:Npn \tl_tail:w #1#2\q_nil{#2} \cs_new:Npn \tl_head_iii:w #1#2#3#4\q_nil{#1#2#3} \cs_generate_variant:Nn \tl_head:n { V } \cs_generate_variant:Nn \tl_tail:n { V } % \end{macrocode} % \end{macro} % \end{macro} % \end{macro} % \end{macro} % \end{macro} % \end{macro} % \end{macro} % \end{macro} % \end{macro} % \end{macro} % \end{macro} % \end{macro} % % \begin{macro}{\tl_if_head_eq_meaning_p:nN} % \begin{macro}[TF]{\tl_if_head_eq_meaning:nN} % \begin{macro}{\tl_if_head_eq_charcode_p:nN} % \begin{macro}{\tl_if_head_eq_charcode_p:fN} % \begin{macro}[TF]{\tl_if_head_eq_charcode:nN} % \begin{macro}[TF]{\tl_if_head_eq_charcode:fN} % \begin{macro}{\tl_if_head_eq_catcode_p:nN} % \begin{macro}[TF]{\tl_if_head_eq_catcode:nN} % When we want to check if the first token of a list equals something % specific it is usually either to see if it is a control sequence or % a character. Hence we make two different functions as the internal % test is different. % |\tl_if_head_meaning_eq:nNTF| uses |\if_meaning:w| and will % consider the tokens |b|$\sb{11}$ and |b|$\sb{12}$ different. % |\tl_if_head_char_eq:nNTF| on the other hand only compares % character codes so would regard |b|$\sb{11}$ and |b|$\sb{12}$ as % equal but would also regard two primitives as equal. % \begin{macrocode} \prg_new_conditional:Npnn \tl_if_head_eq_meaning:nN #1#2 {p,TF,T,F} { \exp_after:wN \if_meaning:w \tl_head:w #1 \q_nil #2 \prg_return_true: \else: \prg_return_false: \fi: } % \end{macrocode} % For the charcode and catcode versions we insert |\exp_not:N| in % front of both tokens. If you need them to expand fully as \TeX{} % does itself with these you can use an |f| type expansion. % \begin{macrocode} \prg_new_conditional:Npnn \tl_if_head_eq_charcode:nN #1#2 {p,TF,T,F} { \exp_after:wN \if:w \exp_after:wN \exp_not:N \tl_head:w #1 \q_nil \exp_not:N #2 \prg_return_true: \else: \prg_return_false: \fi: } % \end{macrocode} % Actually the default is already an |f| type expansion. % \begin{macrocode} %% \cs_new:Npn \tl_if_head_eq_charcode_p:fN #1#2{ %% \exp_after:wN\if_charcode:w \tl_head:w #1\q_nil\exp_not:N#2 %% \c_true_bool %% \else: %% \c_false_bool %% \fi: %% } %% \def_long_test_function_new:npn {tl_if_head_eq_charcode:fN}#1#2{ %% \if_predicate:w \tl_if_head_eq_charcode_p:fN {#1}#2} % \end{macrocode} % These ":fN" variants are broken; temporary patch: % \begin{macrocode} \cs_generate_variant:Nn \tl_if_head_eq_charcode_p:nN {f} \cs_generate_variant:Nn \tl_if_head_eq_charcode:nNTF {f} \cs_generate_variant:Nn \tl_if_head_eq_charcode:nNT {f} \cs_generate_variant:Nn \tl_if_head_eq_charcode:nNF {f} % \end{macrocode} % And now catcodes: % \begin{macrocode} \prg_new_conditional:Npnn \tl_if_head_eq_catcode:nN #1#2 {p,TF,T,F} { \exp_after:wN \if_catcode:w \exp_after:wN \exp_not:N \tl_head:w #1 \q_nil \exp_not:N #2 \prg_return_true: \else: \prg_return_false: \fi: } % \end{macrocode} % \end{macro} % \end{macro} % \end{macro} % \end{macro} % \end{macro} % \end{macro} % \end{macro} % \end{macro} % % Show token usage: % \begin{macrocode} %<*showmemory> \showMemUsage % % \end{macrocode} % % \end{implementation} % \PrintIndex % % \endinput