% \iffalse %% File: l3tlp.dtx Copyright (C) 1990-2007 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/cgi-bin/cvsweb.cgi/ %% %% 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: l3tlp.dtx 735 2008-01-14 22:31:02Z morten $ {L3 Experimental Token List Pointers} %\iffalse %<*driver> %\fi \ProvidesFile{\filename.\filenameext} [\filedate\space v\fileversion\space\filedescription] %\iffalse \documentclass{l3doc} \begin{document} \DocInput{\filename.\filenameext} \end{document} % % \fi % % % \title{The \textsf{l3tlp} package\thanks{This file % has version number \fileversion, last % revised \filedate.}\\ % Token List Pointers} % \author{\Team} % \date{\filedate} % \maketitle % % % % \section {Token list pointers} % % \LaTeX3 stores token lists in so called `token list pointers'. % Variables of this type get the suffix "tlp" and functions of this type % have the prefix "tlp". To use a token list pointer you simply call % the corresponding variable. % % Often you find yourself with not a token list pointer but an % arbitrary token list which has to undergo certain tests. We will % prefix these functions with "tlist". While token list pointers are % always single tokens, token lists are always surrounded by % braces. Perhaps these token lists should have their own module but % for now I decided to put them here because there is quite a bit of % overlap with token list pointers. % % \subsection{Functions} % % \begin{function}{\tlp_new:N | % \tlp_new:c | % \tlp_new:Nn | % \tlp_new:cn | % \tlp_new:Nx } % \begin{syntax} % "\tlp_new:Nn" "{" "}" % \end{syntax} % Defines to be a new variable (or constant) of type token list % pointer. is the initial value of . This % makes it possible to assign values to a constant token list pointer. % % The form "\tlp_new:N" initializes the token list pointer with an empty value. % \end{function} % % \begin{function}{% % \tlp_use:N | % \tlp_use:c % } % \begin{syntax} % "\tlp_use:N" % \end{syntax} % Function that inserts the into the processing stream. Instead of % |\tlp_use:N| simply placing the into the input stream is also % supported.|\tlp_use:c| will complain is the hasn't been declared % previously! % \end{function} % % \begin{function}{% % \tlp_set:Nn | % \tlp_set:Nc | % \tlp_set:No | % \tlp_set:Nd | % \tlp_set:Nf | % \tlp_set:Nx | % \tlp_gset:Nn | % \tlp_gset:Nc | % \tlp_gset:No | % \tlp_gset:Nd | % \tlp_gset:Nx | % \tlp_gset:cn | % \tlp_gset:cx } % \begin{syntax} % "\tlp_set:Nn" "{" "}" % \end{syntax} % Defines to hold the token list . Global % variants of this command assign the value globally the other variants % expand the up to a certain level before the assignment % or interpret the as a character list and form a control % sequence out of it. % \end{function} % % \begin{function}{% % \tlp_clear:N | % \tlp_clear:c | % \tlp_gclear:N | % \tlp_gclear:c % } % \begin{syntax} % "\tlp_clear:N" % \end{syntax} % The is locally or globally cleared. The "c" variants will % generate a control sequence name which is then interpreted as % before clearing. % \end{function} % % \begin{function}{% % \tlp_clear_new:N | % \tlp_clear_new:c | % \tlp_gclear_new:N | % \tlp_gclear_new:c | % } % \begin{syntax} % "\tlp_clear_new:N" % \end{syntax} % These functions check if exists. If it does it will be cleared; % if it doesn't it will be allocated. % \end{function} % % \begin{function}{% % \tlp_put_left:Nn | % \tlp_put_left:No | % \tlp_put_left:Nx | % \tlp_gput_left:Nn | % \tlp_gput_left:No | % \tlp_gput_left:Nx | % \tlp_put_right:Nn | % \tlp_put_right:No | % \tlp_put_right:Nx | % \tlp_put_right:cc | % \tlp_gput_right:Nn | % \tlp_gput_right:No| % \tlp_gput_right:Nx| % \tlp_gput_right:cn| % \tlp_gput_right:co| % } % \begin{syntax} % "\tlp_put_left:Nn" "{" "}" % \end{syntax} % These functions will append to the left or right of % . Assignment is done either locally or globally and might be subject to expansion before assignment. % \end{function} % % A word of warning is appropriate here: Token list pointers 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} % \tlp_set:Nn \l_tmpa_tlp{##1} % \tlp_put_right:Nn \l_tmpa_tlp{##2} % \tlp_set:No \l_tmpb_tlp{\l_tmpa_tlp ##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}{% % \tlp_set_eq:NN | % \tlp_set_eq:Nc | % \tlp_set_eq:cN | % \tlp_set_eq:cc | % \tlp_gset_eq:NN | % \tlp_gset_eq:Nc | % \tlp_gset_eq:cN | % \tlp_gset_eq:cc | % } % \begin{syntax} % "\tlp_set_eq:NN" % \end{syntax} % Fast form for % \begin{syntax} % "\tlp_set:No" "{" "}" % \end{syntax} % when is known to be a variable of type token list pointer. % \end{function} % % \begin{function}{\tlp_to_str:N | % \tlp_to_str:c % } % \begin{syntax} % "\tlp_to_str:N" % \end{syntax} % This function returns the token list kept in as a string list % with all characters catcoded to `other'. % \end{function} % % \subsection{Predicates and conditionals} % % \begin{function}{\tlp_if_empty_p:N| % \tlp_if_empty_p:c % } % \begin{syntax} % "\tlp_if_empty_p:N" % \end{syntax} % This predicate returns `true' if is `empty' i.e., doesn't % contain any tokens. % \end{function} % % \begin{function}{\tlp_if_empty:NTF| % \tlp_if_empty:NT| % \tlp_if_empty:NF| % \tlp_if_empty:cTF | % \tlp_if_empty:cT | % \tlp_if_empty:cF % } % \begin{syntax} % "\tlp_if_empty:NTF" "{""}""{""}" % \end{syntax} % Execute if is empty and if it % contains any tokens. % \end{function} % % \begin{function}{\tlp_if_eq_p:NN | % \tlp_if_eq_p:cN | % \tlp_if_eq_p:Nc | % \tlp_if_eq_p:cc | % } % \begin{syntax} % "\tlp_if_eq_p:NN" % \end{syntax} % Predicate function which returns `true' if the two token list % pointers are identical and `false' otherwise. % \end{function} % % \begin{function}{ % \tlp_if_eq:NNTF | % \tlp_if_eq:NNT | % \tlp_if_eq:NNF | % \tlp_if_eq:cNTF | % \tlp_if_eq:cNT | % \tlp_if_eq:cNF | % \tlp_if_eq:NcTF | % \tlp_if_eq:NcT | % \tlp_if_eq:NcF | % \tlp_if_eq:ccTF | % \tlp_if_eq:ccT | % \tlp_if_eq:ccF | % } % \begin{syntax} % "\tlp_if_eq:NNTF" "{""}""{""}" % \end{syntax} % Execute if holds the same token list as % and otherwise. % \end{function} % % % \subsection{Token lists} % % \begin{function}{\tlist_if_eq:nnTF | % \tlist_if_eq:nnT | % \tlist_if_eq:nnF | % \tlist_if_eq:noTF | % \tlist_if_eq:noT | % \tlist_if_eq:noF | % } % \begin{syntax} % "\tlist_if_eq:nnTF" "{""}" "{""}" "{""}" "{""}" % \end{syntax} % Execute if the two token lists and are % identical. % \end{function} % % % \begin{function}{\tlist_if_empty_p:n | % \tlist_if_empty_p:o | % \tlist_if_empty:nTF | % \tlist_if_empty:nT | % \tlist_if_empty:nF | % \tlist_if_empty:oTF | % \tlist_if_empty:oT | % \tlist_if_empty:oF | % } % \begin{syntax} % "\tlist_if_empty:nTF" "{""}" "{""}" "{""}" % \end{syntax} % Execute if doesn't contain any tokens and otherwise. % \end{function} % % \begin{function}{\tlist_if_blank_p:n | % \tlist_if_blank:nTF | % \tlist_if_blank:nT | % \tlist_if_blank:nF | % \tlist_if_blank_p:o | % \tlist_if_blank:oTF | % \tlist_if_blank:oT | % \tlist_if_blank:oF | % } % \begin{syntax} % "\tlist_if_blank:nTF" "{""}" "{""}" "{""}" % \end{syntax} % Execute if is blank meaning that it is either % empty or contains only blank spaces. % \end{function} % % \begin{function}{\tlist_to_lowercase:n | % \tlist_to_uppercase:n % } % \begin{syntax} % "\tlist_to_lowercase:n" "{""}" % \end{syntax} % "\tlist_to_lowercase:n" converts all tokens in to their % lower case representation. Similar for "\tlist_to_uppercase:n". % \begin{texnote} % These are the \TeX\ primitives \tn{lowercase} and \tn{uppercase} % renamed. % \end{texnote} % \end{function} % % \begin{function}{\tlist_to_str:n % } % \begin{syntax} % "\tlist_to_str:n" "{""}" % \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}{% % \tlist_map_function:nN | % \tlp_map_function:NN | % \tlp_map_function:cN | % } % \begin{syntax} % "\tlist_map_function:nN" "{""}" \\ % "\tlp_map_function:NN" % \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}{% % \tlist_map_inline:nn | % \tlp_map_inline:Nn | % \tlp_map_inline:cn | % } % \begin{syntax} % "\tlist_map_inline:nn" "{""}" "{""}" \\ % "\tlp_map_inline:Nn" "{""}" % \end{syntax} % Allows a syntax like "\tlist_map_inline:nn" "{""}" % "{\token_to_string:N ##1}". This renders it non-expandable though. % Remember to double the "#"s for each level. % \end{function} % % % \begin{function}{% % \tlist_map_variable:nNn | % \tlp_map_variable:NNn | % \tlp_map_variable:cNn | % } % \begin{syntax} % "\tlist_map_variable:nNn" "{""}" "{""}" \\ % "\tlp_map_variable:NNn" "{""}" % \end{syntax} % Assigns to each element on 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}{% % \tlist_map_break:w | % \tlp_map_break:w | % } % \begin{syntax} % "\tlist_map_break:w" % \end{syntax} % For breaking out of a loop. You should take note of the ":w" as % its usage must be precise! % \end{function} % % % \begin{function}{% % \tlist_reverse:n | % } % \begin{syntax} % "\tlist_reverse:n" "{"..."}" % \end{syntax} % Reverse the token list to result in % \dots. Note that spaces in this % token list are gobbled in the process. % \end{function} % % % % \subsubsection{Internal functions} % % % \begin{function}{% % \tlist_map_function_aux:Nn | % \tlist_map_inline_aux:Nn | % \tlist_map_variable_aux:Nnn | % } % \begin{syntax}\end{syntax} % Internal helper functions for the loops. % \end{function} % % % % \begin{function}{\tlist_if_blank_p_aux:w % } % \end{function} % % % % \subsection{Variables and constants} % % \begin{variable}{\C_job_name_tlp} % 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 will be set by \TeX{} and can not be overwritten by the package. % Therefore the "C" % \end{texnote} % \end{variable} % % \begin{variable}{\c_empty_tlp} % 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}{\c_relax_tlp} % Constant holding the token that is assigned to a newly created control % sequence by \TeX. % \end{variable} % % \begin{variable}{% % \l_tmpa_tlp | % \l_tmpb_tlp | % \g_tmpa_tlp | % \g_tmpb_tlp % } % Scratch register for immediate use. They are not used by conditionals % or predicate functions. % \end{variable} % % % \subsubsection{Internal functions} % % \begin{variable}{% % \l_replace_tlp | % } % Internal register used in the replace functions. % \end{variable} % % \begin{variable}{% % \l_testa_tlp | % \l_testb_tlp | % \g_testa_tlp | % \g_testb_tlp | % } % Registers used for conditional processing if the engine doesn't % support arbitrary string comparison. % \end{variable} % % % \begin{function}{\tlp_to_str_aux:w} % Function used to implement "\tlp_to_str:N". % \end{function} % % % \subsection{Search and replace} % % % \begin{function}{ % \tlp_if_in:NnTF | % \tlp_if_in:cnTF | % \tlp_if_in:NnT | % \tlp_if_in:cnT | % \tlp_if_in:NnF | % \tlp_if_in:cnF | % \tlist_if_in:nnTF | % \tlist_if_in:onTF | % } % \begin{syntax} % "\tlp_if_in:NnTF" "{" "}{" "}{" % "}" % \end{syntax} % Function that tests if is in . Depending on the result % either or is executed. Note that % cannot contain brace groups. % \end{function} % % \begin{function}{ % \tlp_replace_in:Nnn | % \tlp_replace_in:cnn | % \tlp_greplace_in:Nnn | % \tlp_greplace_in:cnn | % } % \begin{syntax} % "\tlp_replace_in:Nnn" "{" "}{" "}" % \end{syntax} % Replaces the leftmost occurrence of in with % if present, otherwise the is left untouched. % \end{function} % % \begin{function}{ % \tlp_replace_all_in:Nnn | % \tlp_replace_all_in:cnn | % \tlp_greplace_all_in:Nnn | % \tlp_greplace_all_in:cnn | % } % \begin{syntax} % "\tlp_replace_all_in:Nnn" "{" "}{" "}" % \end{syntax} % Replaces \emph{all} occurrences of in with % . % \end{function} % % % \begin{function}{ % \tlp_remove_in:Nn | % \tlp_remove_in:cn | % \tlp_gremove_in:Nn | % \tlp_gremove_in:cn | % } % \begin{syntax} % "\tlp_remove_in:Nn" "{" "}" % \end{syntax} % Removes the leftmost occurrence of from if present. % \end{function} % % % \begin{function}{ % \tlp_remove_all_in:Nn | % \tlp_remove_all_in:cn | % \tlp_gremove_all_in:Nn | % \tlp_gremove_all_in:cn | % } % \begin{syntax} % "\tlp_remove_all_in:Nn" "{" "}" % \end{syntax} % Removes \emph{all} occurrences of from . % \end{function} % % % \subsection{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}{% % \tlist_head:n | % \tlist_tail:n | % \tlist_head_iii:n | % \tlist_head_iii:f | % \tlist_head:w | % \tlist_tail:w | % \tlist_head_iii:w % } % \begin{syntax} % "\tlist_head:n" "{" ... "}" \\ % "\tlist_tail:n" "{" ... "}" % \end{syntax} % These functions return either the head or the tail of a list, thus in % the above example "\tlist_head:n" would return and "\tlist_tail:n" % would return \dots. "\tlist_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}{% % \tlist_if_head_eq_meaning_p:nN | % \tlist_if_head_eq_meaning:nNTF | % \tlist_if_head_eq_meaning:nNTF | % \tlist_if_head_eq_meaning:nNTF | % } % \begin{syntax} % "\tlist_if_head_eq_meaning:nNTF" "{" "}" % "{""}""{""}" % \end{syntax} % Returns if the first token in is equal to % and otherwise. The |meaning| version compares the % two tokens with |\if_meaning:NN|. % \end{function} % % \begin{function}{% % \tlist_if_head_eq_charcode_p:nN | % \tlist_if_head_eq_charcode:nNTF | % \tlist_if_head_eq_charcode:nNTF | % \tlist_if_head_eq_charcode:nNTF | % } % \begin{syntax} % "\tlist_if_head_eq_charcode:nNTF" "{" "}" % "{""}""{""}" % \end{syntax} % Returns if the first token in 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 |\tlist_if_head_eq_charcode:fNTF| or similar). % \end{function} % % \begin{function}{% % \tlist_if_head_eq_catcode_p:nN | % \tlist_if_head_eq_catcode:nNTF | % \tlist_if_head_eq_catcode:nNTF | % \tlist_if_head_eq_catcode:nNTF | % } % \begin{syntax} % "\tlist_if_head_eq_catcode:nNTF" "{" "}" % "{""}""{""}" % \end{syntax} % Returns if the first token in is equal to % and otherwise. This version uses |\if_catcode:w| for % the test but is otherwise identical to the |charcode| version. % \end{function} % % % \StopEventually{} % % \subsection{The Implementation} % % We start by ensuring that the required packages are loaded. % \begin{macrocode} %\ProvidesExplPackage % {\filename}{\filedate}{\fileversion}{\filedescription} %\RequirePackage{l3basics} %\RequirePackage{l3chk} % \end{macrocode} % \begin{macrocode} %<*initex|package> % \end{macrocode} % % A token list pointer is a control sequence that holds tokens. The % interface is similar to that for token registers, but beware that % the behavior vis \'a vis |\def:Npx| etc. \ldots{} is different. (You % see this comes from Denys' implementation.) % % % % \begin{macro}{\tlp_new:N} % \begin{macro}{\tlp_new:c} % \begin{macro}{\tlp_new:Nn} % \begin{macro}{\tlp_new:cn} % \begin{macro}{\tlp_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} \def_long_new:Npn \tlp_new:Nn #1#2{ \chk_new_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} \gdef:Npn #1{#2} } \def_new:Npn \tlp_new:cn {\exp_args:Nc \tlp_new:Nn } \def_long_new:Npn \tlp_new:Nx #1#2{ \chk_new_cs:N #1 % \chk_var_or_const:N #1 \gdef:Npx #1{#2} } \def_new:Npn \tlp_new:N #1{\tlp_new:Nn #1{}} \def_new:Npn \tlp_new:c #1{\tlp_new:cn {#1}{}} % \end{macrocode} % \end{macro} % \end{macro} % \end{macro} % \end{macro} % \end{macro} % % % \begin{macro}{\tlp_use:N} % \begin{macro}{\tlp_use:c} % Perhaps this should just be enabled when checking? % \begin{macrocode} \def_new:Npn \tlp_use:N #1 { \if_meaning:NN #1 \scan_stop: % \end{macrocode} % If \m{tlp} equals |\scan_stop:| it is probably stemming from a % |\cs:w ... \cd_end:| that was created by mistake somewhere. % \begin{macrocode} \err_latex_bug:x {Token~list~pointer~ `\token_to_string:N #1'~ has~ an~ erroneous~ structure!} \else: \exp_after:NN #1 \fi: } \def_new:Npn \tlp_use:c {\exp_args:Nc \tlp_use:N} % \end{macrocode} % \end{macro} % \end{macro} % % % \begin{macro}{\tlp_set:Nn} % \begin{macro}{\tlp_set:No} % \begin{macro}{\tlp_set:Nd} % \begin{macro}{\tlp_set:Nf} % \begin{macro}{\tlp_set:Nx} % \begin{macro}{\tlp_set:cn} % \begin{macro}{\tlp_set:co} % \begin{macro}{\tlp_set:cx} % \begin{macro}{\tlp_gset:Nn} % \begin{macro}{\tlp_gset:No} % \begin{macro}{\tlp_gset:Nd} % \begin{macro}{\tlp_gset:Nx} % \begin{macro}{\tlp_gset:cn} % \begin{macro}{\tlp_gset:cx} % To set token lists to a specific value to type of functions are % available: |\tlp_set_eq:NN| takes two token-lists as its % arguments assign the first the contents of the second; % |\tlp_set:Nn| has as its second argument a `real' list of tokens. % One can view |\tlp_set_eq:NN| as a special form of |\tlp_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> \def_long_new:Npn \tlp_set:Nn #1#2{ \chk_exist_cs:N #1 \def: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 } \def_long_new:Npn \tlp_set:Nx #1#2{ \chk_exist_cs:N #1 \def:Npx #1{#2} \chk_local:N #1 } % \end{macrocode} % The the global versions. % \begin{macrocode} \def_long_new:Npn \tlp_gset:Nn #1#2{ \chk_exist_cs:N #1 \gdef:Npn #1{#2} \chk_global:N #1 } \def_long_new:Npn \tlp_gset:Nx #1#2{ \chk_exist_cs:N #1 \gdef:Npx #1{#2} \chk_global:N #1 } % % \end{macrocode} % For some functions like |\tlp_set:Nn| we need to define the % `non-check' version with arguments since we want to allow % constructions like |\tlp_set:Nn\l_tmpa_tlp\foo| and so we can't % use the primitive \TeX{} command. % \begin{macrocode} %<*!check> \def_long_new:Npn\tlp_set:Nn#1#2{\def:Npn#1{#2}} \def_long_new:Npn\tlp_set:Nx#1#2{\def:Npx#1{#2}} \def_long_new:Npn\tlp_gset:Nn#1#2{\gdef:Npn#1{#2}} \def_long_new:Npn\tlp_gset:Nx#1#2{\gdef:Npx#1{#2}} % % \end{macrocode} % The remaining functions can just be defined with help from the % expansion module. % \begin{macrocode} \def_new:Npn \tlp_set:No {\exp_args:NNo \tlp_set:Nn} \def_new:Npn \tlp_set:Nd {\exp_args:NNd \tlp_set:Nn} \def_new:Npn \tlp_set:Nf {\exp_args:NNf \tlp_set:Nn} \def_new:Npn \tlp_set:cn {\exp_args:Nc \tlp_set:Nn} \def_new:Npn \tlp_set:co {\exp_args:Nco \tlp_set:Nn} \def_new:Npn \tlp_set:cx {\exp_args:Nc \tlp_set:Nx} \def_new:Npn \tlp_gset:No {\exp_args:NNo \tlp_gset:Nn} \def_new:Npn \tlp_gset:Nd {\exp_args:NNd \tlp_gset:Nn} \def_new:Npn \tlp_gset:cn {\exp_args:Nc \tlp_gset:Nn} \def_new:Npn \tlp_gset:cx {\exp_args:Nc \tlp_gset:Nx} % \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}{\tlp_set_eq:NN} % \begin{macro}{\tlp_set_eq:Nc} % \begin{macro}{\tlp_set_eq:cN} % \begin{macro}{\tlp_set_eq:cc} % \begin{macro}{\tlp_gset_eq:NN} % \begin{macro}{\tlp_gset_eq:Nc} % \begin{macro}{\tlp_gset_eq:cN} % \begin{macro}{\tlp_gset_eq:cc} % For setting token list pointers equal to each other. First checking: % \begin{macrocode} %<*check> \def_new:Npn \tlp_set_eq:NN #1#2{ \chk_exist_cs:N #1 \let:NN #1#2 \chk_local_or_pref_global:N #1 \chk_var_or_const:N #2 } \def_new:Npn \tlp_gset_eq:NN #1#2{ \chk_exist_cs:N #1 \glet:NN #1#2 \chk_global:N #1 \chk_var_or_const:N #2 } % % \end{macrocode} % Non-checking versions are easy. % \begin{macrocode} %<*!check> \let_new:NN \tlp_set_eq:NN \let:NN \let_new:NN \tlp_gset_eq:NN \glet:NN % % \end{macrocode} % The rest again with the expansion module. % \begin{macrocode} \def_new:Npn \tlp_set_eq:Nc {\exp_args:NNc \tlp_set_eq:NN} \def_new:Npn \tlp_set_eq:cN {\exp_args:Nc \tlp_set_eq:NN} \def_new:Npn \tlp_set_eq:cc {\exp_args:Ncc \tlp_set_eq:NN} \def_new:Npn \tlp_gset_eq:Nc {\exp_args:NNc \tlp_gset_eq:NN} \def_new:Npn \tlp_gset_eq:cN {\exp_args:Nc \tlp_gset_eq:NN} \def_new:Npn \tlp_gset_eq:cc {\exp_args:Ncc \tlp_gset_eq:NN} % \end{macrocode} % \end{macro} % \end{macro} % \end{macro} % \end{macro} % \end{macro} % \end{macro} % \end{macro} % \end{macro} % % % % \begin{macro}{\tlp_clear:N} % \begin{macro}{\tlp_clear:c} % \begin{macro}{\tlp_gclear:N} % \begin{macro}{\tlp_gclear:c} % Clearing a token list pointer. % \begin{macrocode} \def_new:Npn \tlp_clear:N #1{\tlp_set_eq:NN #1\c_empty_tlp} \def_new:Npn \tlp_clear:c {\exp_args:Nc \tlp_clear:N} \def_new:Npn \tlp_gclear:N #1{\tlp_gset_eq:NN #1\c_empty_tlp} \def_new:Npn \tlp_gclear:c {\exp_args:Nc \tlp_gclear:N} % \end{macrocode} % \end{macro} % \end{macro} % \end{macro} % \end{macro} % % \begin{macro}{\tlp_clear_new:N} % \begin{macro}{\tlp_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> \def_new:Npn \tlp_clear_new:N #1{ \chk_var_or_const:N #1 \if:w \cs_exist_p:N #1 \tlp_clear:N #1 \else: \tlp_new:Nn #1{} \fi: } % %<-check>\let_new:NN \tlp_clear_new:N \tlp_clear:N \def_new:Npn \tlp_clear_new:c {\exp_args:Nc \tlp_clear_new:N} % \end{macrocode} % \end{macro} % \end{macro} % % \begin{macro}{\tlp_gclear_new:N} % \begin{macro}{\tlp_gclear_new:c} % These are the global versions of the above. % \begin{macrocode} %<*check> \def_new:Npn \tlp_gclear_new:N #1{ \chk_var_or_const:N #1 \if:w \cs_exist_p:N #1 \tlp_gclear:N #1 \else: \tlp_new:Nn #1{} \fi:} % %<-check>\let_new:NN \tlp_gclear_new:N \tlp_gclear:N \def_new:Npn \tlp_gclear_new:c {\exp_args:Nc \tlp_gclear_new:N} % \end{macrocode} % \end{macro} % \end{macro} % % \begin{macro}{\tlp_put_left:Nn} % \begin{macro}{\tlp_put_left:No} % \begin{macro}{\tlp_put_left:Nx} % \begin{macro}{\tlp_gput_left:Nn} % \begin{macro}{\tlp_gput_left:No} % \begin{macro}{\tlp_gput_left:Nx} % 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} % \tlp_set:Nn \l_tmpa_tlp{##1abc##2def} % \tlp_set:Nn \l_tmpb_tlp{##1abc} % \tlp_put_right:Nn \l_tmpb_tlp {##2def} % \end{verbatim} % cause |\l_tmpa_tlp| and |\l_tmpb_tlp| 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 pointer 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} \def_long_new:Npn \tlp_put_left:Nn #1#2{ \tlp_set:Nn \l_exp_tlp{#2} \tlp_set:Nx #1{\exp_not:o{\l_exp_tlp}\exp_not:o{#1}} % \chk_local_or_pref_global:N #1 } \def_long_new:Npn \tlp_put_left:No #1#2{ \tlp_set:Nn \l_exp_tlp{#2} \tlp_set:Nx #1{\exp_not:d{\l_exp_tlp}\exp_not:o{#1}} % \chk_local_or_pref_global:N #1 } \def_long_new:Npn \tlp_put_left:Nx #1#2{ \tlp_set:Nx #1{#2\exp_not:o{#1}} % \chk_local_or_pref_global:N #1 } \def_long_new:Npn \tlp_gput_left:Nn #1#2{ \tlp_set:Nn \l_exp_tlp{#2} \tlp_gset:Nx #1{\exp_not:o{\l_exp_tlp}\exp_not:o{#1}} % \chk_local_or_pref_global:N #1 } \def_long_new:Npn \tlp_gput_left:No #1#2{ \tlp_set:Nn \l_exp_tlp{#2} \tlp_gset:Nx #1{\exp_not:d{\l_exp_tlp}\exp_not:o{#1}} % \chk_local_or_pref_global:N #1 } \def_long_new:Npn \tlp_gput_left:Nx #1#2{ \tlp_gset:Nx #1{#2\exp_not:o{#1}} % \chk_local_or_pref_global:N #1 } \def_long_new:Npn \tlp_put_left:cn{\exp_args:Nc\tlp_put_left:Nn} \def_long_new:Npn \tlp_put_left:co{\exp_args:Nc\tlp_put_left:No} \def_long_new:Npn \tlp_put_left:cx{\exp_args:Nc\tlp_put_left:Nx} \def_long_new:Npn \tlp_gput_left:cn{\exp_args:Nc\tlp_gput_left:Nn} \def_long_new:Npn \tlp_gput_left:co{\exp_args:Nc\tlp_gput_left:No} \def_long_new:Npn \tlp_gput_left:cx{\exp_args:Nc\tlp_gput_left:Nx} % \end{macrocode} % \end{macro} % \end{macro} % \end{macro} % \end{macro} % \end{macro} % \end{macro} % % \begin{macro}{\tlp_put_right:Nn} % \begin{macro}{\tlp_put_right:No} % \begin{macro}{\tlp_put_right:Nx} % \begin{macro}{\tlp_put_right:cc} % \begin{macro}{\tlp_gput_right:Nn} % \begin{macro}{\tlp_gput_right:No} % \begin{macro}{\tlp_gput_right:cn} % \begin{macro}{\tlp_gput_right:co} % \begin{macro}{\tlp_gput_right:Nx} % These are variants of the functions above, but for adding tokens % to the right. % \begin{macrocode} \def_long_new:Npn \tlp_put_right:Nn #1#2{ \tlp_set:Nn \l_exp_tlp{#2} \tlp_set:Nx #1{\exp_not:o{#1}\exp_not:o{\l_exp_tlp}} % \chk_local_or_pref_global:N #1 } \def_long_new:Npn \tlp_gput_right:Nn #1#2{ \tlp_set:Nn \l_exp_tlp{#2} \tlp_gset:Nx #1{\exp_not:o{#1}\exp_not:o{\l_exp_tlp}} % \chk_local_or_pref_global:N #1 } \def_long_new:Npn \tlp_put_right:No #1#2{ \tlp_set:Nn \l_exp_tlp{#2} \tlp_set:Nx #1{\exp_not:o{#1}\exp_not:d{\l_exp_tlp}} % \chk_local_or_pref_global:N #1 } \def_long_new:Npn \tlp_gput_right:No #1#2{ \tlp_set:Nn \l_exp_tlp{#2} \tlp_gset:Nx #1{\exp_not:o{#1}\exp_not:d{\l_exp_tlp}} % \chk_local_or_pref_global:N #1 } \def_long:Npn \tlp_put_right:Nx #1#2{ \tlp_set:Nx #1{\exp_not:o{#1}#2} % \chk_local_or_pref_global:N #1 } \def_long:Npn \tlp_gput_right:Nx #1#2{ \tlp_gset:Nx #1{\exp_not:o{#1}#2} % \chk_local_or_pref_global:N #1 } \def_new:Npn \tlp_gput_right:cn {\exp_args:Nc \tlp_gput_right:Nn} \def_new:Npn \tlp_gput_right:co {\exp_args:Nc \tlp_gput_right:No} \def_new:Npn \tlp_put_right:cc {\exp_args:Ncc \tlp_put_right:Nn} % \end{macrocode} % \end{macro} % \end{macro} % \end{macro} % \end{macro} % \end{macro} % \end{macro} % \end{macro} % \end{macro} % \end{macro} % % % % \begin{macro}{\tlp_gset:Nc} % \begin{macro}{\tlp_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} \def_new:Npn \tlp_gset:Nc { %<*check> \pref_global_chk: % %<-check> \pref_global:D \tlp_set:Nc} % \end{macrocode} % |\pref_global_chk:| will turn the variable check in |\tlp_set:No| % into a global check. % \begin{macrocode} \def_new:Npn \tlp_set:Nc #1#2{\tlp_set:No #1{\cs:w#2\cs_end:}} % \end{macrocode} % \end{macro} % \end{macro} % % % We also provide a few conditionals, both in expandable form (with % |\c_true|) and in `brace-form', the latter are denoted by |TF| at the % end, as explained elsewhere. % % % \begin{macro}{\tlp_if_empty_p:N} % \begin{macro}{\tlp_if_empty_p:c} % Returns |\c_true| iff the token list in the argument is empty. % \begin{macrocode} \def_new:Npn \tlp_if_empty_p:N #1{ \if_meaning:NN#1\c_empty_tlp \c_true \else: \c_false \fi:} \def_new:Npn \tlp_if_empty_p:c {\exp_args:Nc\tlp_if_empty_p:N} % \end{macrocode} % \end{macro} % \end{macro} % % % \begin{macro}{\tlp_if_empty:NTF} % \begin{macro}{\tlp_if_empty:NT} % \begin{macro}{\tlp_if_empty:NF} % \begin{macro}{\tlp_if_empty:cTF} % \begin{macro}{\tlp_if_empty:cT} % \begin{macro}{\tlp_if_empty:cF} % These functions check whether the token list in the argument is % empty and execute the proper code from their argument(s). % \begin{macrocode} \def_test_function_new:npn {tlp_if_empty:N} #1{ \if_meaning:NN#1\c_empty_tlp} \def_new:Npn \tlp_if_empty:cTF {\exp_args:Nc \tlp_if_empty:NTF} \def_new:Npn \tlp_if_empty:cT {\exp_args:Nc \tlp_if_empty:NT} \def_new:Npn \tlp_if_empty:cF {\exp_args:Nc \tlp_if_empty:NF} % \end{macrocode} % \end{macro} % \end{macro} % \end{macro} % \end{macro} % \end{macro} % \end{macro} % % \begin{macro}{\tlp_if_eq_p:NN} % \begin{macro}{\tlp_if_eq_p:Nc} % \begin{macro}{\tlp_if_eq_p:cN} % \begin{macro}{\tlp_if_eq_p:cc} % Returns |\c_true| iff the two token list pointers are equal. % \begin{macrocode} \def_new:Npn \tlp_if_eq_p:NN #1#2{ \if_meaning:NN#1#2 \c_true \else: \c_false \fi:} \def_new:Npn \tlp_if_eq_p:Nc {\exp_args:NNc\tlp_if_empty_p:NN} \def_new:Npn \tlp_if_eq_p:cN {\exp_args:Nc\tlp_if_empty_p:NN} \def_new:Npn \tlp_if_eq_p:cc {\exp_args:Ncc\tlp_if_empty_p:NN} % \end{macrocode} % \end{macro} % \end{macro} % \end{macro} % \end{macro} % % \begin{macro}{\tlp_if_eq:NNTF} % \begin{macro}{\tlp_if_eq:NNT} % \begin{macro}{\tlp_if_eq:NNF} % \begin{macro}{\tlp_if_eq:NcTF} % \begin{macro}{\tlp_if_eq:NcT} % \begin{macro}{\tlp_if_eq:NcF} % \begin{macro}{\tlp_if_eq:cNTF} % \begin{macro}{\tlp_if_eq:cNT} % \begin{macro}{\tlp_if_eq:cNF} % \begin{macro}{\tlp_if_eq:ccTF} % \begin{macro}{\tlp_if_eq:ccT} % \begin{macro}{\tlp_if_eq:ccF} % These function tests whether the token list pointers that are in its % first two arguments are equal. % \begin{macrocode} \def_test_function_new:npn {tlp_if_eq:NN} #1#2{\if_meaning:NN#1#2} \def_new:Npn \tlp_if_eq:cNTF{\exp_args:Nc \tlp_if_eq:NNTF} \def_new:Npn \tlp_if_eq:cNT {\exp_args:Nc \tlp_if_eq:NNT} \def_new:Npn \tlp_if_eq:cNF {\exp_args:Nc \tlp_if_eq:NNF} \def_new:Npn \tlp_if_eq:NcTF{\exp_args:NNc \tlp_if_eq:NNTF} \def_new:Npn \tlp_if_eq:NcT {\exp_args:NNc \tlp_if_eq:NNT} \def_new:Npn \tlp_if_eq:NcF {\exp_args:NNc \tlp_if_eq:NNF} \def_new:Npn \tlp_if_eq:ccTF{\exp_args:Ncc \tlp_if_eq:NNTF} \def_new:Npn \tlp_if_eq:ccT {\exp_args:Ncc \tlp_if_eq:NNT} \def_new:Npn \tlp_if_eq:ccF {\exp_args:Ncc \tlp_if_eq:NNF} % \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}{\c_empty_tlp} % \begin{macro}{\c_relax_tlp} % Two constants which are often used. % \begin{macrocode} \tlp_new:Nn \c_empty_tlp {} \tlp_new:Nn \c_relax_tlp {\scan_stop:} % \end{macrocode} % \end{macro} % \end{macro} % \begin{macro}{\g_tmpa_tlp} % \begin{macro}{\g_tmpb_tlp} % Global temporary token list pointers. % 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} \tlp_new:Nn \g_tmpa_tlp{} \tlp_new:Nn \g_tmpb_tlp{} % \end{macrocode} % \end{macro} % \end{macro} % % \begin{macro}{\l_testa_tlp} % \begin{macro}{\l_testb_tlp} % \begin{macro}{\g_testa_tlp} % \begin{macro}{\g_testb_tlp} % 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} \tlp_new:Nn \l_testa_tlp {} \tlp_new:Nn \l_testb_tlp {} \tlp_new:Nn \g_testa_tlp {} \tlp_new:Nn \g_testb_tlp {} % \end{macrocode} % \end{macro} % \end{macro} % \end{macro} % \end{macro} % % \begin{macro}{\l_tmpa_tlp} % \begin{macro}{\l_tmpb_tlp} % These are local temporary token list pointers. % \begin{macrocode} \tlp_new:Nn \l_tmpa_tlp{} \tlp_new:Nn \l_tmpb_tlp{} % \end{macrocode} % \end{macro} % \end{macro} % % \begin{macro}{\tlp_to_str:N} % \begin{macro}{\tlp_to_str:c} % \begin{macro}{\tlp_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} \def_new:Npn \tlp_to_str:N {\exp_after:NN\tlp_to_str_aux:w \token_to_meaning:N} \def_new:Npn \tlp_to_str_aux:w #1>{} \def_new:Npn\tlp_to_str:c{\exp_args:Nc\tlp_to_str:N} % \end{macrocode} % \end{macro} % \end{macro} % \end{macro} % % % % % \begin{macro}{\tlist_if_empty_p:n} % It would be tempting to just use "\if_meaning:NN\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:NN", 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} \def_long_new:Npn \tlist_if_empty_p:n #1{ \exp_after:NN\if_meaning:NN\exp_after:NN\q_nil\tlist_to_str:n{#1}\q_nil \c_true \else: \c_false \fi: } % \end{macrocode} % \end{macro} % % \begin{macro}{\tlist_if_empty_p:o} % \begin{macro}{\tlist_if_empty:nTF} % \begin{macro}{\tlist_if_empty:nT} % \begin{macro}{\tlist_if_empty:nF} % \begin{macro}{\tlist_if_empty:oTF} % \begin{macro}{\tlist_if_empty:oT} % \begin{macro}{\tlist_if_empty:oF} % \begin{macrocode} \def_new:Npn \tlist_if_empty_p:o {\exp_args:No\tlist_if_empty_p:n} \def_long_test_function_new:npn{tlist_if_empty:n}#1{ \if:w\tlist_if_empty_p:n{#1}} \def_long_test_function_new:npn{tlist_if_empty:o}#1{ \if:w\tlist_if_empty_p:o{#1}} % \end{macrocode} % \end{macro} % \end{macro} % \end{macro} % \end{macro} % \end{macro} % \end{macro} % \end{macro} % % \begin{macro}{\tlist_if_blank_p:n} % \begin{macro}{\tlist_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 |\tlist_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} \def_long_new:Npn \tlist_if_blank_p:n #1{ \exp_after:NN\tlist_if_blank_p_aux:w\tlist_to_str:n{#1}aa..\q_nil } \def_new:Npn \tlist_if_blank_p_aux:w #1#2a#3#4\q_nil{ \if_meaning:NN #3#4\c_true\else:\c_false\fi:} % \end{macrocode} % \end{macro} % \end{macro} % % \begin{macro}{\tlist_if_blank:nTF} % \begin{macro}{\tlist_if_blank:nT} % \begin{macro}{\tlist_if_blank:nF} % \begin{macro}{\tlist_if_blank_p:o} % \begin{macro}{\tlist_if_blank:oTF} % \begin{macro}{\tlist_if_blank:oT} % \begin{macro}{\tlist_if_blank:oF} % Variations on the original function above. % \begin{macrocode} \def_long_test_function_new:npn{tlist_if_blank:n}#1{ \if:w\tlist_if_blank_p:n{#1}} \def:Npn \tlist_if_blank_p:o{\exp_args:No\tlist_if_blank_p:n} \def_long_test_function_new:npn{tlist_if_blank:o}#1{ \if:w\tlist_if_blank_p:o{#1}} % \end{macrocode} % \end{macro} % \end{macro} % \end{macro} % \end{macro} % \end{macro} % \end{macro} % \end{macro} % % \begin{macro}{\tlist_to_lowercase:n} % \begin{macro}{\tlist_to_uppercase:n} % Just some names for a few primitives. % \begin{macrocode} \let_new:NN \tlist_to_lowercase:n \tex_lowercase:D \let_new:NN \tlist_to_uppercase:n \tex_uppercase:D % \end{macrocode} % \end{macro} % \end{macro} % % \begin{macro}{\tlist_to_str:n} % Another name for a primitive. % \begin{macrocode} \let_new:NN \tlist_to_str:n \etex_detokenize:D % \end{macrocode} % \end{macro} % % % \begin{macro}{\tlist_map_function:nN} % \begin{macro}{\tlp_map_function:NN} % \begin{macro}{\tlp_map_function:cN} % \begin{macro}{\tlist_map_function_aux:NN} % Expandable loop macro for |tlist|s. 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} \def_long_new:Npn \tlist_map_function:nN #1#2{ \tlist_map_function_aux:Nn #2 #1 \q_recursion_tail \q_recursion_stop } \def_new:Npn \tlp_map_function:NN #1#2{ \exp_after:NN \tlist_map_function_aux:Nn \exp_after:NN #2 #1 \q_recursion_tail \q_recursion_stop } \def_long_new:Npn \tlist_map_function_aux:Nn #1#2{ \quark_if_recursion_tail_stop:n{#2} #1{#2} \tlist_map_function_aux:Nn #1 } \def_new:Npn\tlp_map_function:cN{\exp_args:Nc\tlp_map_function:NN} % \end{macrocode} % \end{macro} % \end{macro} % \end{macro} % \end{macro} % % % \begin{macro}{\tlist_map_inline:nn} % \begin{macro}{\tlp_map_inline:Nn} % \begin{macro}{\tlp_map_inline:cn} % \begin{macro}{\tlist_map_inline_aux:n} % \begin{macro}{\l_tlp_inline_level_num} % The inline functions are straight forward by now. We use a little % a trick with the fake counter |\l_tlp_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 |\tlist_map_function:Nn| from before. % \begin{macrocode} \def_long_new:Npn \tlist_map_inline:nn #1#2{ \num_incr:N \l_tlp_inline_level_num \def_long:cpn {tlist_map_inline_ \num_use:N \l_tlp_inline_level_num :n} ##1{#2} \exp_args:Nc \tlist_map_function_aux:Nn {tlist_map_inline_ \num_use:N \l_tlp_inline_level_num :n} #1 \q_recursion_tail\q_recursion_stop \num_decr:N \l_tlp_inline_level_num } \def_long_new:Npn \tlp_map_inline:Nn #1#2{ \num_incr:N \l_tlp_inline_level_num \def_long:cpn {tlist_map_inline_ \num_use:N \l_tlp_inline_level_num :n} ##1{#2} \exp_args:NcE \tlist_map_function_aux:Nn {tlist_map_inline_ \num_use:N \l_tlp_inline_level_num :n} #1 \q_recursion_tail\q_recursion_stop \num_decr:N \l_tlp_inline_level_num } \def_new:Npn\tlp_map_inline:cN{\exp_args:Nc\tlp_map_inline:NN} \tlp_new:Nn \l_tlp_inline_level_num{0} % \end{macrocode} % \end{macro} % \end{macro} % \end{macro} % \end{macro} % \end{macro} % % % \begin{macro}{\tlist_map_variable:nNn} % \begin{macro}{\tlp_map_variable:NNn} % \begin{macro}{\tlp_map_variable:cNn} % |\tlist_map:nNn| \zz{tlist} \zz{temp} \zz{action} assigns % \zz{temp} to each element and executes \zz{action}. % \begin{macrocode} \def_long_new:Npn \tlist_map_variable:nNn #1#2#3{ \tlist_map_variable_aux:Nnn #2 {#3} #1 \q_recursion_tail \q_recursion_stop } \def_new:Npn \tlp_map_variable:NNn {\exp_args:No \tlist_map_variable:nNn} \def_new:Npn \tlp_map_variable:cNn {\exp_args:Nc \tlp_map_variable:NNn} % \end{macrocode} % \end{macro} % \end{macro} % \end{macro} % % \begin{macro}{\tlist_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} \def_long_new:Npn \tlist_map_variable_aux:Nnn #1#2#3{ \tlp_set:Nn #1{#3} \quark_if_recursion_tail_stop:N #1 #2 \tlist_map_variable_aux:Nnn #1{#2} } % \end{macrocode} % \end{macro} % % \begin{macro}{\tlist_map_break:w} % \begin{macro}{\tlp_map_break:w} % The break statement. % \begin{macrocode} \let_new:NN \tlist_map_break:w \use_none_delimit_by_q_recursion_stop:w \let_new:NN \tlp_map_break:w \tlist_map_break:w % \end{macrocode} % \end{macro} % \end{macro} % % % % % % % \begin{macro}{\tlist_if_eq:nnTF} % \begin{macro}{\tlist_if_eq:nnT} % \begin{macro}{\tlist_if_eq:nnF} % 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} \let_new:NN \tlist_compare:xx \pdfstrcmp \def_long_new:NNn \tlist_compare:nn 2{ \tlist_compare:xx{\exp_not:n{#1}}{\exp_not:n{#2}} } \def_long_new:NNn \tlist_compare:nx 1{ \tlist_compare:xx{\exp_not:n{#1}} } \def_long_new:NNn \tlist_compare:xn 2{ \tlist_compare:xx{#1}{\exp_not:n{#2}} } \def_long_new:NNn \tlist_compare:no 2{ \tlist_compare:xx{\exp_not:n{#1}}{\exp_not:n\exp_after:NN{#2}} } \def_long_new:NNn \tlist_compare:on 2{ \tlist_compare:xx{\exp_not:n\exp_after:NN{#1}}{\exp_not:n{#2}} } \def_long_new:NNn \tlist_compare:oo 2{ \tlist_compare:xx{\exp_not:n\exp_after:NN{#1}}{\exp_not:n\exp_after:NN{#2}} } \def_long_new:NNn \tlist_compare:xo 2{ \tlist_compare:xx{#1}{\exp_not:n\exp_after:NN{#2}} } \def_long_new:NNn \tlist_compare:ox 2{ \tlist_compare:xx{\exp_not:n\exp_after:NN{#1}}{\exp_not:n{#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|\tlist_map_inline:nn| function yet. % \begin{macrocode} \def:Npn \tmp:w #1{ \def_long_new:cNx {tlist_if_eq_p:#1} 2{ \exp_not:N \if_num:w \exp_after:NN \exp_not:N \cs:w tlist_compare:#1\cs_end:{##1}{##2} \exp_not:n{ =\c_zero \c_true \else: \c_false \fi: } } \def_long_test_function_new:npx{tlist_if_eq:#1}##1##2{ \exp_not:N \if_num:w \exp_after:NN \exp_not:N \cs:w tlist_compare:#1\cs_end:{##1}{##2} \exp_not:n{ =\c_zero } } } \tmp:w{xx} \tmp:w{nn} \tmp:w{oo} \tmp:w{xn} \tmp:w{nx} \tmp:w{on} \tmp:w{no} \tmp:w{xo} \tmp:w{ox} % \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|\tlist_if_eq:nn| the old fashioned (and unexpandable) % way. % \begin{macrocode} \cs_if_really_free:cT{pdf_strcmp:D}{ \def_long_test_function:npn{tlist_if_eq:nn}#1#2{ \tlp_set:Nx \l_testa_tlp {\exp_not:n{#1}} \tlp_set:Nx \l_testb_tlp {\exp_not:n{#2}} \if_meaning:NN\l_testa_tlp \l_testb_tlp } \def_long_test_function:npn{tlist_if_eq:no}#1#2{ \tlp_set:Nx \l_testa_tlp {\exp_not:n{#1}} \tlp_set:Nx \l_testb_tlp {\exp_not:o{#2}} \if_meaning:NN\l_testa_tlp \l_testb_tlp } \def_long_test_function:npn{tlist_if_eq:nx}#1#2{ \tlp_set:Nx \l_testa_tlp {\exp_not:n{#1}} \tlp_set:Nx \l_testb_tlp {#2} \if_meaning:NN\l_testa_tlp \l_testb_tlp } \def_long_test_function:npn{tlist_if_eq:on}#1#2{ \tlp_set:Nx \l_testa_tlp {\exp_not:o{#1}} \tlp_set:Nx \l_testb_tlp {\exp_not:n{#2}} \if_meaning:NN\l_testa_tlp \l_testb_tlp } \def_long_test_function:npn{tlist_if_eq:oo}#1#2{ \tlp_set:Nx \l_testa_tlp {\exp_not:o{#1}} \tlp_set:Nx \l_testb_tlp {\exp_not:o{#2}} \if_meaning:NN\l_testa_tlp \l_testb_tlp } \def_long_test_function:npn{tlist_if_eq:ox}#1#2{ \tlp_set:Nx \l_testa_tlp {\exp_not:o{#1}} \tlp_set:Nx \l_testb_tlp {#2} \if_meaning:NN\l_testa_tlp \l_testb_tlp } \def_long_test_function:npn{tlist_if_eq:xn}#1#2{ \tlp_set:Nx \l_testa_tlp {#1} \tlp_set:Nx \l_testb_tlp {\exp_not:n{#2}} \if_meaning:NN\l_testa_tlp \l_testb_tlp } \def_long_test_function:npn{tlist_if_eq:xo}#1#2{ \tlp_set:Nx \l_testa_tlp {#1} \tlp_set:Nx \l_testb_tlp {\exp_not:o{#2}} \if_meaning:NN\l_testa_tlp \l_testb_tlp } \def_long_test_function:npn{tlist_if_eq:xx}#1#2{ \tlp_set:Nx \l_testa_tlp {#1} \tlp_set:Nx \l_testb_tlp {#2} \if_meaning:NN\l_testa_tlp \l_testb_tlp } } % \end{macrocode} % \end{macro} % \end{macro} % \end{macro} % % % % % \subsubsection{Checking for and replacing tokens} % % \begin{macro}{\tlp_if_in:NnTF} % \begin{macro}{\tlp_if_in:cnTF} % \begin{macro}{\tlp_if_in:NnT} % \begin{macro}{\tlp_if_in:cnT} % \begin{macro}{\tlp_if_in:NnF} % \begin{macro}{\tlp_if_in:cnF} % \begin{macro}{\tlist_if_in:nnTF} % \begin{macro}{\tlist_if_in:onTF} % 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} \def_long:Npn \tlp_if_in:NnTF #1#2{ \def_long:Npn\tmp:w ##1 #2 ##2\q_stop{ \quark_if_no_value:nFT{##2} } \exp_after:NN \tmp:w #1 #2 \q_no_value \q_stop } \def_new:Npn \tlp_if_in:cnTF {\exp_args:Nc\tlp_if_in:NnTF} \def_long:Npn \tlp_if_in:NnT #1#2{ \def_long:Npn\tmp:w ##1 #2 ##2\q_stop{ \quark_if_no_value:nF{##2} } \exp_after:NN \tmp:w #1 #2 \q_no_value \q_stop } \def_new:Npn \tlp_if_in:cnT {\exp_args:Nc\tlp_if_in:NnT} \def_long:Npn \tlp_if_in:NnF #1#2{ \def_long:Npn\tmp:w ##1 #2 ##2\q_stop{ \quark_if_no_value:nT{##2} } \exp_after:NN \tmp:w #1 #2 \q_no_value \q_stop } \def_new:Npn \tlp_if_in:cnF {\exp_args:Nc\tlp_if_in:NnF} \def_long_new:Npn \tlist_if_in:nnTF #1#2{ \def_long:Npn\tmp:w ##1 #2 ##2\q_stop{ \quark_if_no_value:nFT{##2} } \tmp:w #1 #2 \q_no_value \q_stop } \def_new:Npn \tlist_if_in:onTF {\exp_args:No\tlist_if_in:nnTF} % \end{macrocode} % \end{macro} % \end{macro} % \end{macro} % \end{macro} % \end{macro} % \end{macro} % \end{macro} % \end{macro} % % \begin{macro}{\l_tlp_replace_tlp} % A temp variable for the replace operations. % \begin{macrocode} \tlp_new:Nn\l_tlp_replace_tlp{} % \end{macrocode} % \end{macro} % % \begin{macro}{\tlp_replace_in:Nnn} % \begin{macro}{\tlp_replace_in:cnn} % \begin{macro}{\tlp_greplace_in:Nnn} % \begin{macro}{\tlp_greplace_in:cnn} % \begin{macro}{\tlp_replace_in_aux:NNnn} % Replacing the first item in a token list pointer 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 tlp 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 tlp % with |\q_mark| later to be gobbled and also prepend the remainder % of the first split operation with |\q_mark| also to be gobbled % again later on. % \begin{macrocode} \def_long_new:NNn \tlp_replace_in_aux:NNnn 4{ \def_long:Npn \tmp:w ##1#3##2\q_stop{ \quark_if_no_value:nF{##2} { % \end{macrocode} % At this point |##1| starts with a |\q_mark| so remove it. % \begin{macrocode} \tlp_set:Nx\l_tlp_replace_tlp{\exp_not:o{\use_none:n##1#4}} \def_long:Npn \tmp:w ####1#3\q_no_value{ \tlp_put_right:Nx \l_tlp_replace_tlp {\exp_not:o{\use_none:n ####1}} } \tmp:w \q_mark ##2 } % \end{macrocode} % Now all that is done is setting the token list pointer equal to the % expansion of the token register. % \begin{macrocode} #1#2\l_tlp_replace_tlp } % \end{macrocode} % Here is where we start the process. Note that the tlp might start % with a space token so we use this little trick with |\use_arg_i:n| % to prevent it from being removed. % \begin{macrocode} \use_arg_i:n{\exp_after:NN \tmp:w\exp_after:NN\q_mark} #2#3 \q_no_value\q_stop } % \end{macrocode} % Now the various versions doing the replacement either globally or % locally. % \begin{macrocode} \def_new:Npn \tlp_replace_in:Nnn {\tlp_replace_in_aux:NNnn \tlp_set_eq:NN} \def_new:Npn \tlp_replace_in:cnn{\exp_args:Nc\tlp_replace_in:Nnn} \def_new:Npn \tlp_greplace_in:Nnn {\tlp_replace_in_aux:NNnn \tlp_gset_eq:NN} \def_new:Npn \tlp_greplace_in:cnn{\exp_args:Nc\tlp_greplace_in:Nnn} % \end{macrocode} % \end{macro} % \end{macro} % \end{macro} % \end{macro} % \end{macro} % % \begin{macro}{\tlp_replace_all_in:Nnn} % \begin{macro}{\tlp_greplace_all_in:cnn} % \begin{macro}{\tlp_replace_all_in:Nnn} % \begin{macro}{\tlp_greplace_all_in:cnn} % \begin{macro}{\tlp_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} \def_long:NNn \tlp_replace_all_in_aux:NNnn 4{ \tlp_clear:N \l_tlp_replace_tlp \def_long:Npn \tmp:w ##1#3##2\q_stop{ \quark_if_no_value:nTF{##2} { \tlp_put_right:Nx \l_tlp_replace_tlp {\exp_not:o{\use_none:n##1}} } { \tlp_put_right:Nx \l_tlp_replace_tlp {\exp_not:o{\use_none:n##1 #4}} \tmp:w \q_mark##2 \q_stop } } \use_arg_i:n{\exp_after:NN \tmp:w\exp_after:NN\q_mark} #2#3 \q_no_value\q_stop #1#2\l_tlp_replace_tlp } % \end{macrocode} % Now the various forms. % \begin{macrocode} \def_new:Npn \tlp_replace_all_in:Nnn { \tlp_replace_all_in_aux:NNnn \tlp_set_eq:NN} \def_new:Npn \tlp_replace_all_in:cnn{\exp_args:Nc\tlp_replace_all_in:Nnn} \def_new:Npn \tlp_greplace_all_in:Nnn { \tlp_replace_all_in_aux:NNnn \tlp_gset_eq:NN} \def_new:Npn \tlp_greplace_all_in:cnn{\exp_args:Nc\tlp_greplace_all_in:Nnn} % \end{macrocode} % \end{macro} % \end{macro} % \end{macro} % \end{macro} % \end{macro} % % % % \begin{macro}{\tlp_remove_in:Nn} % \begin{macro}{\tlp_remove_in:cn} % \begin{macro}{\tlp_gremove_in:Nn} % \begin{macro}{\tlp_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} \def_long_new:NNn \tlp_remove_in:Nn 2{\tlp_replace_in:Nnn #1{#2}{}} \def_long_new:NNn \tlp_gremove_in:Nn 2{\tlp_greplace_in:Nnn #1{#2}{}} \def_new:Npn \tlp_remove_in:cn{\exp_args:Nc\tlp_remove_in:Nn} \def_new:Npn \tlp_gremove_in:cn{\exp_args:Nc\tlp_gremove_in:Nn} % \end{macrocode} % \end{macro} % \end{macro} % \end{macro} % \end{macro} % % % \begin{macro}{\tlp_remove_all_in:Nn} % \begin{macro}{\tlp_remove_all_in:Nn} % \begin{macro}{\tlp_gremove_all_in:Nn} % \begin{macro}{\tlp_gremove_all_in:Nn} % Same old, same old. % \begin{macrocode} \def_long_new:Npn \tlp_remove_all_in:Nn #1#2{ \tlp_replace_all_in:Nnn #1{#2}{} } \def_long_new:Npn \tlp_gremove_all_in:Nn #1#2{ \tlp_greplace_all_in:Nnn #1{#2}{} } \def_new:Npn \tlp_remove_all_in:cn{\exp_args:Nc\tlp_remove_all_in:Nn} \def_new:Npn \tlp_gremove_all_in:cn{\exp_args:Nc\tlp_gremove_all_in:Nn} % \end{macrocode} % \end{macro} % \end{macro} % \end{macro} % \end{macro} % % % \subsubsection{Heads or tails?} % % \begin{macro}{\tlist_head:n} % \begin{macro}{\tlist_head_i:n} % \begin{macro}{\tlist_tail:n} % \begin{macro}{\tlist_tail:f} % \begin{macro}{\tlist_head_iii:n} % \begin{macro}{\tlist_head_iii:f} % \begin{macro}{\tlist_head:w} % \begin{macro}{\tlist_tail:w} % \begin{macro}{\tlist_head_iii:w} % These functions pick up either the head or the tail of a list. % "\tlist_head_iii:n" returns the first three items on a list. % \begin{macrocode} \def_long_new:Npn \tlist_head:n #1{\tlist_head:w #1\q_nil} \let_new:NN \tlist_head_i:n \tlist_head:n \def_long_new:Npn \tlist_tail:n #1{\tlist_tail:w #1\q_nil} \def_new:Npn \tlist_tail:f {\exp_args:Nf \tlist_tail:n} \def_long_new:Npn \tlist_head_iii:n #1{\tlist_head_iii:w #1\q_nil} \def_new:Npn \tlist_head_iii:f {\exp_args:Nf \tlist_head_iii:n} \let_new:NN \tlist_head:w \use_arg_i_delimit_by_q_nil:nw \def_long_new:Npn \tlist_tail:w #1#2\q_nil{#2} \def_long_new:Npn \tlist_head_iii:w #1#2#3#4\q_nil{#1#2#3} % \end{macrocode} % \end{macro} % \end{macro} % \end{macro} % \end{macro} % \end{macro} % \end{macro} % \end{macro} % \end{macro} % \end{macro} % % \begin{macro}{\tlist_if_head_eq_meaning_p:nN} % \begin{macro}{\tlist_if_head_eq_meaning:nNTF} % \begin{macro}{\tlist_if_head_eq_meaning:nNT} % \begin{macro}{\tlist_if_head_eq_meaning:nNF} % \begin{macro}{\tlist_if_head_eq_charcode_p:nNTF} % \begin{macro}{\tlist_if_head_eq_charcode:nNTF} % \begin{macro}{\tlist_if_head_eq_charcode:nNT} % \begin{macro}{\tlist_if_head_eq_charcode:nNF} % \begin{macro}{\tlist_if_head_eq_catcode_p:nNTF} % \begin{macro}{\tlist_if_head_eq_catcode:nNTF} % \begin{macro}{\tlist_if_head_eq_catcode:nNT} % \begin{macro}{\tlist_if_head_eq_catcode:nNF} % 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. % |\tlist_if_head_meaning_eq:nNTF| uses |\if_meaning:NN| and will % consider the tokens |b|$\sb{11}$ and |b|$\sb{12}$ different. % |\tlist_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} \def_long_new:Npn \tlist_if_head_eq_meaning_p:nN #1#2{ \exp_after:NN \if_meaning:NN \tlist_head:w #1\q_nil#2 \c_true \else: \c_false \fi: } \def_long_test_function_new:npn {tlist_if_head_eq_meaning:nN}#1#2{ \if:w \tlist_if_head_eq_meaning_p:nN{#1}#2} % \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} \def_long_new:Npn \tlist_if_head_eq_charcode_p:nN #1#2{ \exp_after:NN\if_charcode:w \exp_after:NN\exp_not:N \tlist_head:w #1\q_nil\exp_not:N#2 \c_true \else: \c_false \fi: } \def_long_test_function_new:npn {tlist_if_head_eq_charcode:nN}#1#2{ \if:w\tlist_if_head_eq_charcode_p:nN{#1}#2} % \end{macrocode} % Actually the default is already an |f| type expansion. % \begin{macrocode} \def_long_new:Npn \tlist_if_head_eq_charcode_p:fN #1#2{ \exp_after:NN\if_charcode:w \tlist_head:w #1\q_nil\exp_not:N#2 \c_true \else: \c_false \fi: } \def_long_test_function_new:npn {tlist_if_head_eq_charcode:fN}#1#2{ \if:w\tlist_if_head_eq_charcode_p:fN{#1}#2} \def_long_new:Npn \tlist_if_head_eq_catcode_p:nN #1#2{ \exp_after:NN\if_charcode:w \exp_after:NN\exp_not:N \tlist_head:w #1\q_nil\exp_not:N#2 \c_true \else: \c_false \fi: } \def_long_test_function_new:npn {tlist_if_head_eq_catcode:nN}#1#2{ \if:w\tlist_if_head_eq_catcode_p:nN{#1}#2} % \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}{\tlist_reverse:n} % \begin{macro}{\tlist_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} \def_long_new:Npn \tlist_reverse:n #1{ \tlist_reverse_aux:nN {} #1 \q_recursion_tail\q_stop } \def_long_new:Npn \tlist_reverse_aux:nN #1#2{ \quark_if_recursion_tail_stop_do:nn {#2}{ #1 } \tlist_reverse_aux:nN {#2#1} } % \end{macrocode} % \end{macro} % \end{macro} % % % % As this package relies heavily on a lot of the expansion tricks used % in \textsf{l3expan} we make sure to load it automatically at the end % when used as a package. Probably not needed but I'm just such a nice % guy\dots % \begin{macrocode} %\RequirePackage{l3expan} %\RequirePackage{l3num}\par % \end{macrocode} % Show token usage: % \begin{macrocode} % %<*showmemory> \showMemUsage % % \end{macrocode} % % \endinput % % $Log$ % Revision 1.28 2006/07/30 15:51:53 morten % Made \tlist_map_inline:nn and variants nestable. Reimplemented % \tlp_if_in:..., \tlp_replace_in:..., \tlp_replace_all_in:... % % Revision 1.27 2006/07/25 15:32:48 morten % More \tlist_if_blank functions % % Revision 1.26 2006/07/06 14:58:50 morten % Documentation touch-ups. Added more functions plus some new comparison % functions which are dependent on a reasonably new pdfTeX. % % Revision 1.25 2006/06/05 14:55:14 morten % Function for reversing a list of tokens. % % Revision 1.24 2006/06/03 16:31:19 morten % Another version of \tlist_head... % % Revision 1.23 2006/03/20 18:26:41 braams % Updated the copyright notice (2006) and demoted all implementation % sections to subsections and so on to clean up the toc for source3.tex % % Revision 1.22 2006/01/26 14:00:42 morten % Added a few missing functions (that were documented to be there!) % % Revision 1.21 2006/01/19 22:27:08 morten % Added \tlp_replace_ and \tlp_remove_if_in functions. % % Revision 1.20 2006/01/17 23:07:06 morten % Added "if_in" functions. % % Revision 1.19 2005/12/27 10:09:07 morten % Changed RCS information retrieval, imported all tlist functions since % half of them dealt with token list pointers anyway % % Revision 1.18 2005/07/25 19:50:40 morten % Fixed the \long-ness aspects. % % Revision 1.17 2005/04/12 16:43:03 morten % Minor updates % % Revision 1.16 2005/04/12 12:10:28 morten % Rearranged and cleaned up code to avoid much doubling. % % Revision 1.15 2005/04/06 21:34:07 morten % Fixed all tlp_set:XX so that they're \long. Added \tlp_set:Nf % % Revision 1.14 2005/03/22 23:20:43 morten % Fixed \long versions according to Benjamin's suggestions. Slightly reorganized. % % Revision 1.13 2005/03/16 22:35:41 braams % Added the tweaks necessary to be able to load with initex % % Revision 1.12 2005/03/11 21:43:58 braams % Fixed the use of RCS information; % Fixed a documentation typo %