% \iffalse %% File: l3prg.dtx Copyright (C) 2005-2006 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: l3prg.dtx 768 2008-08-05 19:45:06Z morten $ {L3 Experimental control structures} %\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{l3prg} package\thanks{This file % has version number \fileversion, last % revised \filedate.}\\ % Program control structures} % \author{\Team} % \date{\filedate} % \maketitle % % % \section{Control structures} % % \subsection{Choosing modes} % % \begin{function}{\mode_if_vertical_p:| % \mode_if_vertical:TF | % \mode_if_vertical:T | % \mode_if_vertical:F % } % \begin{syntax} % "\mode_if_vertical:TF" "{""}" "{""}" % \end{syntax} % Determines if \TeX{} is in vertical mode or not and executes either % or accordingly. % \end{function} % % \begin{function}{\mode_if_horizontal_p:| % \mode_if_horizontal:TF | % \mode_if_horizontal:T | % \mode_if_horizontal:F % } % \begin{syntax} % "\mode_if_horizontal:TF" "{""}" "{""}" % \end{syntax} % Determines if \TeX{} is in horizontal mode or not and executes either % or accordingly. % \end{function} % % % \begin{function}{ % \mode_if_inner_p:| % \mode_if_inner:TF| % \mode_if_inner:T| % \mode_if_inner:F % } % \begin{syntax} % "\mode_if_inner:TF" "{""}" "{""}" % \end{syntax} % Determines if \TeX{} is in inner mode or not and executes either % or accordingly. % \end{function} % % \begin{function}{ % \mode_if_math:TF| % \mode_if_math:T| % \mode_if_math:F| % } % \begin{syntax} % "\mode_if_math:TF" "{""}" "{""}" % \end{syntax} % Determines if \TeX{} is in math mode or not and executes either % or accordingly. % \begin{texnote} % This version will choose the right branch even at the beginning of % an alignment cell. % \end{texnote} % \end{function} % % % \subsubsection{Alignment safe grouping and scanning} % % \begin{function}{\scan_align_safe_stop:} % \begin{syntax} % "\scan_align_safe_stop:" % \end{syntax} % This function gets \TeX{} on the right track inside an alignment % cell but without destroying any kerning. % \end{function} % % % \begin{function}{\group_align_safe_begin:| % \group_align_safe_end:} % \begin{syntax} % "\group_align_safe_begin:" <...> "\group_align_safe_end:" % \end{syntax} % Encloses <...> inside a group but is safe inside an alignment cell. % See the implementation of |\peek_token_generic:NNTF| for an % application. % \end{function} % % % \subsection{Producing $n$ copies} % % There are often several different requirements for producing % multiple copies of something. Sometimes one might want to produce a % number of identical copies of a sequence of tokens whereas at other % times the goal is to simulate a for loop as known from most real % programming languages. % % \begin{function}{\prg_replicate:nn } % \begin{syntax} % "\prg_replicate:nn" "{" "}" "{" "}" % \end{syntax} % Creates copies of . Expandable. % \end{function} % % % \begin{function}{\prg_stepwise_function:nnnN} % \begin{syntax} % "\prg_stepwise_function:nnnN" "{""}" "{""}" % "{""}" % \end{syntax} % This function performs once for each step starting at % and ending once is passed. is placed % directly in front of a brace group holding the current number so it % should usually be a function taking one argument. The % |\prg_stepwise_function:nnnN| function is expandable. % \end{function} % % \begin{function}{\prg_stepwise_inline:nnnn} % \begin{syntax} % "\prg_stepwise_inline:nnnn" "{""}" "{""}" "{""}" % "{""}" % \end{syntax} % Same as |\prg_stepwise_function:nnnN| except here is % performed each time with |##1| as a placeholder for the number % currently being tested. This function is not expandable and it is % nestable. % \end{function} % % \begin{function}{\prg_stepwise_variable:nnnNn} % \begin{syntax} % "\prg_stepwise_variable:nnnn" "{""}" "{""}" "{""}" % "{""}" % \end{syntax} % Same as |\prg_stepwise_inline:nnnn| except here the current value is % stored in and the programmer can use it in . This % function is not expandable. % \end{function} % % % % \subsection{Conditionals and logical operations} % % % \LaTeX3 has two primary forms of conditional flow processing. The % one type deals with the truth value of a test directly as in % "\cs_free:NTF" where you test if a control sequence was undefined % and then execute either the \m{true} or \m{false} part depending on % the result and after exiting the underlying "\if...\fi:" structure. % The second type has to do with predicate functions like % "\cs_free_p:N" which return either "\c_true" or "\c_false" to be % used in testing with "\if:w". % % % This section describes a boolean data type which is closely % connected to both parts as sometimes you want to execute some code % depending on the value of a switch (e.g.,~draft/final) and other % times you perhaps want to use it as a predicate function in an % "\if:w" test. Parsing "\iffalse" % and "\iftrue" tokens can be quite tricky at times so the easiest is to % simply let a boolean either be "\c_true" or "\c_false". This % also means we get the logical operations And, Or, and Not which can % then be used on both the boolean type and predicate functions. All % functions by the name |\predicate| are expandable and expect the % input to also be fully expandable. More generic constructs do not % contain |predicate| in their names. % % % \subsubsection{The boolean data type} % % \begin{function}{% % \bool_new:N | % \bool_new:c | % } % \begin{syntax} % "\bool_new:N" % \end{syntax} % Define a new boolean variable. The initial value is . A % boolean is actually just either "\c_true" or "\c_false". % \end{function} % % \begin{function}{% % \bool_set_true:N | % \bool_set_true:c | % \bool_set_false:N | % \bool_set_false:c | % \bool_gset_true:N | % \bool_gset_true:c | % \bool_gset_false:N | % \bool_gset_false:c | % } % \begin{syntax} % "\bool_gset_false:N" % \end{syntax} % Set either true or false. We can also do this globally. % \end{function} % % % \begin{function}{% % \bool_set_eq:NN | % \bool_set_eq:Nc | % \bool_set_eq:cN | % \bool_set_eq:cc | % \bool_gset_eq:NN | % \bool_gset_eq:Nc | % \bool_gset_eq:cN | % \bool_gset_eq:cc | % % } % \begin{syntax} % "\bool_set_eq:NN" % \end{syntax} % Set equal to the value of . % \end{function} % % \begin{function}{% % \bool_if:NTF | % \bool_if:NT | % \bool_if:NF | % \bool_if_p:N | % % } % \begin{syntax} % "\bool_if:NTF" "{"\m{true}"}" "{"\m{false}"}" \\ % "\bool_if_p:N" % \end{syntax} % Test the truth value of the boolean and execute the \m{true} or % \m{false} code. "\bool_if_p:N" is a predicate function for use in % "\if:w" tests. % \end{function} % % \begin{function}{% % \bool_whiledo:NT | % \bool_whiledo:NF | % \bool_dowhile:NT | % \bool_dowhile:NF | % % } % \begin{syntax} % "\bool_whiledo:NT" "{"\m{true}"}" \\ % "\bool_whiledo:NF" "{"\m{false}"}" \\ % \end{syntax} % The "T" versions execute the \m{true} code as long as the boolean is % true and the "F" versions execute the \m{false} code as long as the % boolean is false. The "whiledo" functions execute the body after % testing the boolean and the "dowhile" functions executes the body % first and then tests the boolean. % \end{function} % % % \begin{function}{% % \l_tmpa_bool | % \g_tmpa_bool | % % } % \begin{syntax} % \end{syntax} % Reserved booleans. % \end{function} % % \subsubsection{Logical operations} % % Somewhat related to the subject of conditional flow processing is % logical operators as these deal with \m{true} and \m{false} % statements which is precisely what the predicate functions return. % % % \begin{function}{% % \predicate_p:n | % \predicate:nTF | % \predicate:nT | % \predicate:nF | % } % \begin{syntax} % "\predicate:nTF" "{""}" "{""}" % "{""}" % \end{syntax} % The functions evaluate the truth value of \m{list of predicates} % where each predicate is separated by \verb+&&+ or \verb+||+ % denoting logical And and Or functions. Minimal evaluation is % carried out so that whenever a truth value cannot be changed % anymore, the remainding tests are not carried out. Hence % \begin{verbatim} % \predicate_p:n{ % \int_compare_p:nNn 1=1 && % \predicate_p:n { % \int_compare_p:nNn 2=3 || % \int_compare_p:nNn 4=4 || % \int_compare_p:nNn 1=\error % is skipped % } && % \int_compare_p:nNn 2=2 % } % \end{verbatim} % returns \meta{true}. % \end{function} % % % % % \begin{function}{% % \predicate_not_p:n | % } % \begin{syntax} % "\predicate_not_p:n" "{""}" % \end{syntax} % "\predicate_not_p:n" % reverses the truth value of its argument. Thus % \begin{quote} % "\prg_if_predicate_not_p:n {\prg_if_predicate_not_p:n {\c_true}}" % \end{quote} % ultimately returns \m{true}. % \end{function} % % \subsubsection{Case switches} % % % \begin{function}{ % \prg_case_int:nnn | % % } % \begin{syntax} % "\prg_case_int:nnn" "{""}" "{" % " {"\m{integer expr$\sb 1$}"}""{"\m{code$\sb 1$}"}""{"\m{integer expr$\sb 2$}"}""{"\m{code$\sb 2$}"}"\\ % " ...""{"\m{integer expr$\sb n$}"}""{"\m{code$\sb n$}"}"\\ % "}" "{"\m{else case}"}" % \end{syntax} % This function evaluates the first \meta{integer expr} and then compares it % to the values found in the list. Thus the expression % \begin{verbatim} % \prg_case:nnn{2*5}{ % {5}{Small} {4+6}{Medium} {-2*10}{Negative} % }{Other} % \end{verbatim} % evaluates first the term to look for and then tries to find this % value in the list of values. If the value is found, the code on its % right is executed after removing the remainder of the list. If the % value is not found, the \meta{else case} is executed. The example % above will return ``Medium''. % % The function is expandable and is written in such a way that % \texttt{f} style expansion can take place cleanly, i.e., no tokens % from within the function are left over. % \end{function} % % \begin{function}{ % \prg_case_dim:nnn | % % } % \begin{syntax} % "\prg_case_int:nnn" "{""}" "{" % " {"\m{dim expr$\sb 1$}"}""{"\m{code$\sb 1$}"}""{"\m{dim expr$\sb 2$}"}""{"\m{code$\sb 2$}"}"\\ % " ...""{"\m{dim expr$\sb n$}"}""{"\m{code$\sb n$}"}"\\ % "}" "{"\m{else case}"}" % \end{syntax} % This function works just like |\prg_case_int:nnn| except it works % for \meta{dim} registers. % \end{function} % % \begin{function}{ % \prg_case_str:nnn | % % } % \begin{syntax} % "\prg_case_str:nnn" "{""}" "{" % " {"\m{string$\sb 1$}"}""{"\m{code$\sb 1$}"}""{"\m{string$\sb 2$}"}""{"\m{code$\sb 2$}"}"\\ % " ...""{"\m{string$\sb n$}"}""{"\m{code$\sb n$}"}"\\ % "}" "{"\m{else case}"}" % \end{syntax} % This function works just like |\prg_case_int:nnn| except it % compares strings. Each string is evaluated fully using \texttt{x} % style expansion. % % The function is expandable\footnote{Provided you use pdfTeX v1.30 or % later} and is written in such a way that % \texttt{f} style expansion can take place cleanly, i.e., no tokens % from within the function are left over. % \end{function} % % \subsubsection{Generic loops} % % % \begin{function}{ % \prg_whiledo:nT | % \prg_whiledo:nF | % \prg_dowhile:nT | % \prg_dowhile:nF | % % } % \begin{syntax} % "\prg_whiledo:nT" "{""}" "{"\m{true}"}" \\ % "\prg_whiledo:nF" "{""}" "{"\m{false}"}" % \end{syntax} % The "T" versions execute the \m{true} code as long as is % true and the "F" versions execute the \m{false} code as long as % is false. The "whiledo" functions execute the body after % testing the boolean and the "dowhile" functions executes the body % first and then tests the boolean. For the "T" versions, % should end with a function executing only the \meta{true} code for % some test such as |\tlp_if_eq:NNT|. Similarly the "F" types should % end with |\tlp_if_eq:NNF|. % \end{function} % % \subsection{Sorting} % % % \begin{function}{ % \prg_quicksort:n | % } % \begin{syntax} % "\prg_quicksort:n" "{" "{""}" "{""}" % \dots\space "{""}" "}" % \end{syntax} % Performs a Quicksort on the token list. The comparisons are % performed by the function |\prg_quicksort_compare:nnTF| which is up % to the programmer to define. When the sorting process is over, all % elements are given as argument to the function % |\prg_quicksort_function:n| which the programmer also controls. % \end{function} % % \begin{function}{ % \prg_quicksort_function:n | % \prg_quicksort_compare:nnTF % } % \begin{syntax} % "\prg_quicksort_function:n" "{""}" \\ % "\prg_quicksort_compare:nnTF" "{""}" "{""}"\\ % \end{syntax} % The two functions the programmer must define before calling % |\prg_quicksort:n|. As an example we could define % \begin{quote} % |\def:NNn\prg_quicksort_function:n 1{{#1}}|\\ % |\def:NNn\prg_quicksort_compare:nnTF 2{\num_compare:nNnTF{#1}>{#2}}| % \end{quote} % Then the function call % \begin{quote} % |\prg_quicksort:n {876234520}| % \end{quote} % would return |{0}{2}{2}{3}{4}{5}{6}{7}{8}|. An alternative example % where one sorts a list of words, |\prg_quicksort_compare:nnTF| could % be defined as % \begin{quote} % |\def:NNn\prg_quicksort_compare:nnTF 2{|\\ % | \num_compare:nNnTF{\tlist_compare:nn{#1}{#2}}>\c_zero }| % \end{quote} % % \end{function} % % % \StopEventually{} % % \subsection{The Implementation} % % % We start by ensuring that the required packages are loaded. % \begin{macrocode} %<*package> \ProvidesExplPackage {\filename}{\filedate}{\fileversion}{\filedescription} \RequirePackage{l3quark} \RequirePackage{l3toks} \RequirePackage{l3int} % %<*initex|package> % \end{macrocode} % % % \subsubsection{Choosing modes} % % \begin{macro}{\mode_if_vertical_p:} % \begin{macro}{\mode_if_vertical:TF} % \begin{macro}{\mode_if_vertical:T} % \begin{macro}{\mode_if_vertical:F} % For testing vertical mode. % \begin{macrocode} \def_new:Npn \mode_if_vertical_p: { \if_mode_vertical: \c_true \else: \c_false\fi:} \def_test_function_new:npn{mode_if_vertical:}{\if_mode_vertical:} % \end{macrocode} % \end{macro} % \end{macro} % \end{macro} % \end{macro} % % \begin{macro}{\mode_if_horizontal_p:} % \begin{macro}{\mode_if_horizontal:TF} % \begin{macro}{\mode_if_horizontal:T} % \begin{macro}{\mode_if_horizontal:F} % For testing horizontal mode. % \begin{macrocode} \def_new:Npn \mode_if_horizontal_p: { \if_mode_horizontal: \c_true \else: \c_false\fi:} \def_test_function_new:npn{mode_if_horizontal:}{\if_mode_horizontal:} % \end{macrocode} % \end{macro} % \end{macro} % \end{macro} % \end{macro} % % \begin{macro}{\mode_if_inner_p:} % \begin{macro}{\mode_if_inner:TF} % \begin{macro}{\mode_if_inner:T} % \begin{macro}{\mode_if_inner:F} % For testing inner mode. % \begin{macrocode} \def_new:Npn \mode_if_inner_p: { \if_mode_inner: \c_true \else: \c_false\fi:} \def_test_function_new:npn{mode_if_inner:}{\if_mode_inner:} % \end{macrocode} % \end{macro} % \end{macro} % \end{macro} % \end{macro} % % \begin{macro}{\mode_if_math:TF} % \begin{macro}{\mode_if_math:T} % \begin{macro}{\mode_if_math:F} % For testing math mode. Uses the kern-save |\scan_align_safe_stop:|. % \begin{macrocode} \def_test_function_new:npn{mode_if_math:} { \scan_align_safe_stop: \if_mode_math: } % \end{macrocode} % \end{macro} % \end{macro} % \end{macro} % % \paragraph{Alignment safe grouping and scanning} % % % \begin{macro}{\group_align_safe_begin:} % \begin{macro}{\group_align_safe_end:} % \TeX's alignment structures present many problems. As Knuth says % himself in \emph{\TeX: The Program}: ``It's sort of a miracle % whenever |\halign| or |\valign| work, [\ldots]'' One problem relates % to commands that internally issues a |\cr| but also peek ahead for % the next character for use in, say, an optional argument. If the % next token happens to be a |&| with category code~4 we will get some % sort of weird error message because the underlying % |\tex_futurelet:D| will store the token at the end of the alignment % template. This could be a |&|$\sb4$ giving a message like % |! Misplaced \cr.| or even worse: it could be the |\endtemplate| % token causing even more trouble! To solve this we have to open a % special group so that \TeX{} still thinks it's on safe ground but at % the same time we don't want to introduce any brace group that may % find its way to the output. The following functions help with this % by using code documented only in Appendix~D of % \emph{The \TeX book}\dots % \begin{macrocode} \def_new:Npn \group_align_safe_begin: { \if_false:{\fi:\if_num:w`}=\c_zero\fi:} \def_new:Npn \group_align_safe_end: {\if_num:w`{=\c_zero}\fi:} % \end{macrocode} % \end{macro} % \end{macro} % % % \begin{macro}{\scan_align_safe_stop:} % When \TeX{} is in the beginning of an align cell (right after the % |\cr|) it is in a somewhat strange mode as it is looking ahead to % find an |\tex_omit:D| or |\tex_noalign:D| and hasn't looked at the % preamble yet. Thus an |\tex_ifmmode:D| test will always fail unless % we insert |\scan_stop:| to stop \TeX's scanning ahead. On the other % hand we don't want to insert a |\scan_stop:| every time as that will % destroy kerning between letters\footnote{Unless we enforce an extra % pass with an appropriate value of \texttt{\string\pretolerance}.} % Unfortunately there is no way to detect if we're in the beginning of % an alignment cell as they have different characteristics depending % on column number etc. However we \emph{can} detect if we're in an % alignment cell by checking the current group type and we can also % check if the previous node was a character or ligature. What is done % here is that |\scan_stop:| is only inserted iff a)~we're in the % outer part of an alignment cell and b)~the last node \emph{wasn't} a % char node or a ligature node. % \begin{macrocode} \def_new:Npn \scan_align_safe_stop: { \num_compare:nNnT \etex_currentgrouptype:D = \c_six { \num_compare:nNnF \etex_lastnodetype:D = \c_zero { \num_compare:nNnF \etex_lastnodetype:D = \c_seven \scan_stop: } } } % \end{macrocode} % \end{macro} % % \subsubsection{Making $n$ copies} % % \begin{macro}{\prg_replicate:nn} % \begin{macro}{\prg_replicate_aux:N} % \begin{macro}{\prg_replicate_first_aux:N} % This function uses a cascading csname technique by David Kastrup % (who else :-) % % The idea is to make the input "25" result in first adding five, and % then 20 copies of the code to be replicated. The technique uses % cascading csnames which means that we start building several csnames % so we end up with a list of functions to be called in reverse % order. This is important here (and other places) because it means % that we can for instance make the function that inserts five copies % of something to also hand down ten to the next function in % line. This is exactly what happens here: in the example with "25" % then the next function is the one that inserts two copies but it % sees the ten copies handed down by the previous function. In order % to avoid the last function to insert say, 100 copies of the original % argument just to gobble them again we define separate functions to % be inserted first. Finally we must ensure that the cascade comes to % a peaceful end so we make it so that the original csname \TeX{} is % creating is simply "\use_noop:" expanding to nothing. % % This function has one flaw though: Since it constantly passes down % ten copies of its previous argument it will severely affect the main % memory once you start demanding hundreds of thousands of copies. Now % I don't think this is a real limitation for any ordinary use. An % alternative approach is to create a string of "m"'s with % "\int_to_roman:w" which can be done with just four macros but that % method has its own problems since it can exhaust the string % pool. Also, it is considerably slower than what we use here so the % few extra csnames are well spent I would say. % \begin{macrocode} \def_new:Npn \prg_replicate:nn #1{ \cs:w use_noop: \exp_after:NN\prg_replicate_first_aux:N \int_use:N \int_eval:n{#1} \cs_end: \cs_end: } \def_new:Npn \prg_replicate_aux:N#1{ \cs:w prg_replicate_#1:n\prg_replicate_aux:N } \def_new:Npn \prg_replicate_first_aux:N#1{ \cs:w prg_replicate_first_#1:n\prg_replicate_aux:N } % \end{macrocode} % \end{macro} % \end{macro} % \end{macro} % Then comes all the functions that do the hard work of inserting all % the copies. % \begin{macrocode} \def_new:Npn \prg_replicate_ :n #1{}% no, this is not a typo! \def_long_new:cpn {prg_replicate_0:n}#1{\cs_end:{#1#1#1#1#1#1#1#1#1#1}} \def_long_new:cpn {prg_replicate_1:n}#1{\cs_end:{#1#1#1#1#1#1#1#1#1#1}#1} \def_long_new:cpn {prg_replicate_2:n}#1{\cs_end:{#1#1#1#1#1#1#1#1#1#1}#1#1} \def_long_new:cpn {prg_replicate_3:n}#1{ \cs_end:{#1#1#1#1#1#1#1#1#1#1}#1#1#1} \def_long_new:cpn {prg_replicate_4:n}#1{ \cs_end:{#1#1#1#1#1#1#1#1#1#1}#1#1#1#1} \def_long_new:cpn {prg_replicate_5:n}#1{ \cs_end:{#1#1#1#1#1#1#1#1#1#1}#1#1#1#1#1} \def_long_new:cpn {prg_replicate_6:n}#1{ \cs_end:{#1#1#1#1#1#1#1#1#1#1}#1#1#1#1#1#1} \def_long_new:cpn {prg_replicate_7:n}#1{ \cs_end:{#1#1#1#1#1#1#1#1#1#1}#1#1#1#1#1#1#1} \def_long_new:cpn {prg_replicate_8:n}#1{ \cs_end:{#1#1#1#1#1#1#1#1#1#1}#1#1#1#1#1#1#1#1} \def_long_new:cpn {prg_replicate_9:n}#1{ \cs_end:{#1#1#1#1#1#1#1#1#1#1}#1#1#1#1#1#1#1#1#1} % \end{macrocode} % Users shouldn't ask for something to be replicated once or even % not at all but\dots % \begin{macrocode} \def_long_new:cpn {prg_replicate_first_0:n}#1{\cs_end: } \def_long_new:cpn {prg_replicate_first_1:n}#1{\cs_end: #1} \def_long_new:cpn {prg_replicate_first_2:n}#1{\cs_end: #1#1} \def_long_new:cpn {prg_replicate_first_3:n}#1{\cs_end: #1#1#1} \def_long_new:cpn {prg_replicate_first_4:n}#1{\cs_end: #1#1#1#1} \def_long_new:cpn {prg_replicate_first_5:n}#1{\cs_end: #1#1#1#1#1} \def_long_new:cpn {prg_replicate_first_6:n}#1{\cs_end: #1#1#1#1#1#1} \def_long_new:cpn {prg_replicate_first_7:n}#1{\cs_end: #1#1#1#1#1#1#1} \def_long_new:cpn {prg_replicate_first_8:n}#1{\cs_end: #1#1#1#1#1#1#1#1} \def_long_new:cpn {prg_replicate_first_9:n}#1{\cs_end: #1#1#1#1#1#1#1#1#1} % \end{macrocode} % % % % % \begin{macro}{\prg_stepwise_function:nnnN} % \begin{macro}{\prg_stepwise_function_incr:nnnN} % \begin{macro}{\prg_stepwise_function_decr:nnnN} % A stepwise function. Firstly we check the direction of the steps % |#2| since that will depend on which test we should use. If the % step is positive we use a greater than test, otherwise a less than % test. If the test comes out true exit, otherwise perform |#4|, % add the step to |#1| and try again with this new value of |#1|. % \begin{macrocode} \def_long_new:NNn \prg_stepwise_function:nnnN 2{ \num_compare:nNnTF{#2}<\c_zero {\exp_args:No\prg_stepwise_function_decr:nnnN } {\exp_args:No\prg_stepwise_function_incr:nnnN } {\int_use:N\int_eval:n{#1}}{#2} } \def_long_new:NNn \prg_stepwise_function_incr:nnnN 4{ \num_compare:nNnF {#1}>{#3} { #4{#1} \exp_args:No \prg_stepwise_function_incr:nnnN {\int_use:N\int_eval:n{#1 + #2}} {#2}{#3}{#4} } } \def_long_new:NNn \prg_stepwise_function_decr:nnnN 4{ \num_compare:nNnF {#1}<{#3} { #4{#1} \exp_args:No \prg_stepwise_function_decr:nnnN {\int_use:N\int_eval:n{#1 + #2}} {#2}{#3}{#4} } } % \end{macrocode} % \end{macro} % \end{macro} % \end{macro} % % \begin{macro}{\g_prg_inline_level_int} % \begin{macro}{\prg_stepwise_inline:nnnn} % \begin{macro}{\prg_stepwise_inline_decr:nnnn} % \begin{macro}{\prg_stepwise_inline_incr:nnnn} % This function uses the same approach as for instance % |\clist_map_inline:Nn| to allow arbitrary nesting. First construct % the special function and then call an auxiliary one which just % carries the newly constructed csname. Must make assignments global % when we maintain our own stack. % \begin{macrocode} \int_new:N\g_prg_inline_level_int \def_long_new:NNn\prg_stepwise_inline:nnnn 4{ \int_gincr:N \g_prg_inline_level_int \gdef:cpn{prg_stepwise_inline_\int_use:N\g_prg_inline_level_int :n}##1{#4} \num_compare:nNnTF {#2}<\c_zero {\exp_args:Nco \prg_stepwise_inline_decr:Nnnn } {\exp_args:Nco \prg_stepwise_inline_incr:Nnnn } {prg_stepwise_inline_\int_use:N\g_prg_inline_level_int :n} {\int_use:N\int_eval:n{#1}} {#2} {#3} \int_gdecr:N \g_prg_inline_level_int } \def_long_new:NNn \prg_stepwise_inline_incr:Nnnn 4{ \num_compare:nNnF {#2}>{#4} { #1{#2} \exp_args:NNo \prg_stepwise_inline_incr:Nnnn #1 {\int_use:N\int_eval:n{#2 + #3}} {#3}{#4} } } \def_long_new:NNn \prg_stepwise_inline_decr:Nnnn 4{ \num_compare:nNnF {#2}<{#4} { #1{#2} \exp_args:NNo \prg_stepwise_inline_decr:Nnnn #1 {\int_use:N\int_eval:n{#2 + #3}} {#3}{#4} } } % \end{macrocode} % \end{macro} % \end{macro} % \end{macro} % \end{macro} % % \begin{macro}{\prg_stepwise_variable:nnnNn} % \begin{macro}{\prg_stepwise_variable_decr:nnnNn} % \begin{macro}{\prg_stepwise_variable_incr:nnnNn} % Almost the same as above. Just store the value in |#4| and execute % |#5|. % \begin{macrocode} \def_long_new:NNn \prg_stepwise_variable:nnnNn 2 { \num_compare:nNnTF {#2}<\c_zero {\exp_args:No\prg_stepwise_variable_decr:nnnNn} {\exp_args:No\prg_stepwise_variable_incr:nnnNn} {\int_use:N\int_eval:n{#1}}{#2} } \def_long_new:NNn \prg_stepwise_variable_incr:nnnNn 5 { \num_compare:nNnF {#1}>{#3} { \def:Npn #4{#1} #5 \exp_args:No \prg_stepwise_variable_incr:nnnNn {\int_use:N\int_eval:n{#1 + #2}}{#2}{#3}#4{#5} } } \def_long_new:NNn \prg_stepwise_variable_decr:nnnNn 5 { \num_compare:nNnF {#1}<{#3} { \def:Npn #4{#1} #5 \exp_args:No \prg_stepwise_variable_decr:nnnNn {\int_use:N\int_eval:n{#1 + #2}}{#2}{#3}#4{#5} } } % \end{macrocode} % \end{macro} % \end{macro} % \end{macro} % % % \subsubsection{Booleans} % For normal booleans we set them to either "\c_true" or "\c_false" % and then use "\if:w" to choose the right branch. The functions % return either the TF, T, or F case \emph{after} ending the |\if:w|. % We only define the |N| versions here as the |c| versions can easily % be constructed with the expansion module. % % \begin{macro}{\bool_new:N} % \begin{macro}{\bool_new:c} % \begin{macro}{\bool_set_true:N} % \begin{macro}{\bool_set_true:c} % \begin{macro}{\bool_set_false:N} % \begin{macro}{\bool_set_false:c} % \begin{macro}{\bool_gset_true:N} % \begin{macro}{\bool_gset_true:c} % \begin{macro}{\bool_gset_false:N} % \begin{macro}{\bool_gset_false:c} % Defining and setting a boolean is easy. % \begin{macrocode} \def_new:Npn \bool_new:N #1 { \let_new:NN #1 \c_false } \def_new:Npn \bool_new:c #1 { \let_new:cN {#1} \c_false } \def_new:Npn \bool_set_true:N #1 { \let:NN #1 \c_true } \def_new:Npn \bool_set_true:c #1 { \let:cN {#1} \c_true } \def_new:Npn \bool_set_false:N #1 { \let:NN #1 \c_false } \def_new:Npn \bool_set_false:c #1 { \let:cN {#1} \c_false } \def_new:Npn \bool_gset_true:N #1 { \glet:NN #1 \c_true } \def_new:Npn \bool_gset_true:c #1 { \glet:cN {#1} \c_true } \def_new:Npn \bool_gset_false:N #1 { \glet:NN #1 \c_false } \def_new:Npn \bool_gset_false:c #1 { \glet:cN {#1} \c_false } % \end{macrocode} % \end{macro} % \end{macro} % \end{macro} % \end{macro} % \end{macro} % \end{macro} % \end{macro} % \end{macro} % \end{macro} % \end{macro} % % \begin{macro}{\bool_set_eq:NN} % \begin{macro}{\bool_set_eq:Nc} % \begin{macro}{\bool_set_eq:cN} % \begin{macro}{\bool_set_eq:cc} % \begin{macro}{\bool_gset_eq:NN} % \begin{macro}{\bool_gset_eq:Nc} % \begin{macro}{\bool_gset_eq:cN} % \begin{macro}{\bool_gset_eq:cc} % Setting a boolean to another is also pretty easy. % \begin{macrocode} \let_new:NN \bool_set_eq:NN \let:NN \let_new:NN \bool_set_eq:Nc \let:Nc \let_new:NN \bool_set_eq:cN \let:cN \let_new:NN \bool_set_eq:cc \let:cc \let_new:NN \bool_gset_eq:NN \glet:NN \let_new:NN \bool_gset_eq:Nc \glet:Nc \let_new:NN \bool_gset_eq:cN \glet:cN \let_new:NN \bool_gset_eq:cc \glet:cc % \end{macrocode} % \end{macro} % \end{macro} % \end{macro} % \end{macro} % \end{macro} % \end{macro} % \end{macro} % \end{macro} % % \begin{macro}{\l_tmpa_bool} % \begin{macro}{\g_tmpa_bool} % A few booleans just if you need them. % \begin{macrocode} \bool_new:N \l_tmpa_bool \bool_new:N \g_tmpa_bool % \end{macrocode} % \end{macro} % \end{macro} % % \begin{macro}{\bool_if:NTF} % \begin{macro}{\bool_if:NT} % \begin{macro}{\bool_if:NF} % \begin{macro}{\bool_if:cTF} % \begin{macro}{\bool_if:cT} % \begin{macro}{\bool_if:cF} % Straight forward here. % \begin{macrocode} \def_test_function_new:npn{bool_if:N}#1{\if:w #1} \def_new:Npn \bool_if:cTF{\exp_args:Nc\bool_if:NTF} \def_new:Npn \bool_if:cT{\exp_args:Nc\bool_if:NT} \def_new:Npn \bool_if:cF{\exp_args:Nc\bool_if:NF} % \end{macrocode} % \end{macro} % \end{macro} % \end{macro} % \end{macro} % \end{macro} % \end{macro} % % \begin{macro}{\bool_if_p:N} % \begin{macro}{\bool_if_p:c} % We also make a predicate function for the "bool" data type but since % we use "\c_true" and "\c_false" it's rather simple\dots{} % Not that there's anything wrong in simplicity -- on the contrary! % \begin{macrocode} \def_new:Npn \bool_if_p:N #1 { #1 } \let_new:NN \bool_if_p:c \cs_use:c % \end{macrocode} % \end{macro} % \end{macro} % % % \begin{macro}{\bool_whiledo:NT} % \begin{macro}{\bool_whiledo:cT} % \begin{macro}{\bool_whiledo:NF} % \begin{macro}{\bool_whiledo:cF} % A "while" loop where the boolean is tested before executing the % statement. The "NT" version executes the "T" part as long as the % boolean is true while the "NF" version executes the "F" part as % long as the boolean is false. % \begin{macrocode} \def_long_new:Npn \bool_whiledo:NT #1 #2 { \bool_if:NT #1 {#2 \bool_whiledo:NT #1 {#2}} } \def_new:Npn \bool_whiledo:cT{\exp_args:Nc\bool_whiledo:NT} \def_long_new:Npn \bool_whiledo:NF #1 #2 { \bool_if:NF #1 {#2 \bool_whiledo:NF #1 {#2}} } \def_new:Npn \bool_whiledo:cF{\exp_args:Nc\bool_whiledo:NF} % \end{macrocode} % \end{macro} % \end{macro} % \end{macro} % \end{macro} % % \begin{macro}{\bool_dowhile:NT} % \begin{macro}{\bool_dowhile:cT} % \begin{macro}{\bool_dowhile:NF} % \begin{macro}{\bool_dowhile:cF} % A "do-while" loop where the body is performed at least once and the % boolean is tested after executing the body. Otherwise identical to % the above functions. % \begin{macrocode} \def_long_new:Npn \bool_dowhile:NT #1 #2 { #2 \bool_if:NT #1 {\bool_dowhile:NT #1 {#2}} } \def_new:Npn \bool_dowhile:cT{\exp_args:Nc\bool_dowhile:NT} \def_long_new:Npn \bool_dowhile:NF #1 #2 { #2 \bool_if:NF #1 {\bool_dowhile:NF #1 {#2}} } \def_new:Npn \bool_dowhiledo:cF{\exp_args:Nc\bool_dowhile:cF} % \end{macrocode} % \end{macro} % \end{macro} % \end{macro} % \end{macro} % % \begin{macro}{\bool_double_if:NNnnnn} % \begin{macro}{\bool_double_if:cNnnnn} % \begin{macro}{\bool_double_if:Ncnnnn} % \begin{macro}{\bool_double_if:ccnnnn} % Execute |#3| iff TT, |#4| iff TF, |#5| iff FT and |#6| iff % FF. The name isn't that great but I'll have to think about % that. Ideally it should be something with |TF| since only one of % the cases is executed but we haven't got any naming scheme for % this kind of thing so for now I'll just stick to simple |nnnn|. % \begin{macrocode} \def_new:Npn \bool_double_if:NNnnnn#1#2{ \if_case:w \num_eval:w #1\scan_stop: \if_case:w \num_eval:w #2\scan_stop: \exp_after:NN\exp_after:NN\exp_after:NN \use_arg_i:nnnn \else: \exp_after:NN\exp_after:NN\exp_after:NN \use_arg_ii:nnnn \fi: \else: \if_case:w \num_eval:w #2\scan_stop: \exp_after:NN\exp_after:NN\exp_after:NN \use_arg_iii:nnnn \else: \exp_after:NN\exp_after:NN\exp_after:NN \use_arg_iv:nnnn \fi: \fi: } \def_new:Npn \bool_double_if:cNnnnn{\exp_args:Nc\bool_double_if:NNnnnn} \def_new:Npn \bool_double_if:Ncnnnn{\exp_args:NNc\bool_double_if:NNnnnn} \def_new:Npn \bool_double_if:ccnnnn{\exp_args:Ncc\bool_double_if:NNnnnn} % \end{macrocode} % \end{macro} % \end{macro} % \end{macro} % \end{macro} % \subsubsection{Generic testing} % % \begin{macro}{\prg_whiledo:nT} % \begin{macro}{\prg_whiledo:nF} % \begin{macro}{\prg_dowhile:nT} % \begin{macro}{\prg_dowhile:nF} % We provide these four generic while loops. |#1| is a test function % and for the |T| functions it should be a test function ending with % just the true case. Similar for the |F| types. % \begin{macrocode} \def_long_new:Npn \prg_whiledo:nT #1#2{ #1 {#2 \prg_whiledo:nT {#1}{#2}} } \def_long_new:Npn \prg_whiledo:nF #1#2{ #1 {#2 \prg_whiledo:nF {#1}{#2}} } \def_long_new:Npn \prg_dowhile:nT #1#2{ #2 #1 {\prg_dowhile:nT {#1}{#2}} } \def_long_new:Npn \prg_dowhile:nF #1#2{ #2 #1 {\prg_dowhile:nF {#1}{#2}} } % \end{macrocode} % \end{macro} % \end{macro} % \end{macro} % \end{macro} % % \begin{macro}{\predicate_p:n} % \begin{macro}{\predicate:nTF} % \begin{macro}{\predicate:nT} % \begin{macro}{\predicate:nF} % \begin{macro}{\predicate_auxi:NN} % \begin{macro}{\predicate_auxii:NNN} % \begin{macro}{\predicate_88_0:w}^^A% fix! % \begin{macro}{\predicate_88_1:w}^^A% fix! % \begin{macro}{\predicate_II_0:w}^^A% fix! % \begin{macro}{\predicate_II_1:w}^^A fix! % \begin{macro}{\predicate_02_0:w} % \begin{macro}{\predicate_02_1:w} % % Evaluating the truth value of a list of predicates is done using % an input syntax somewhat similar to the one found in other % programming languages. The function evaluates predicates from % left to right, expanding them to 00 and 01 resp., which leads to % six different situations of tokens in the input stream: % \begin{itemize} % \item[\texttt{00\&\&}] Current truth value is true, logical And % seen, continue to see if next is also true. % \item[\texttt{01\&\&}] Current truth value is false, logical And % seen, break the scanning and return \meta{false}. % \item[\texttt{00\char`\|\char`\|}] Current truth value is true, % logical Or seen, break the scanning and return \meta{true}. % \item[\texttt{01\char`\|\char`\|}] Current truth value is false, % logical Or seen, continue to see if a later predicate is true. % \item[\texttt{0002}] Current truth value is true, end marker % seen, return \meta{true}. % \item[\texttt{0102}] Current truth value is false, end marker % seen, return \meta{false}. % \end{itemize} % To accomplish this we pre-expand the predicate list using |f| % type expansion which leads to |00| or |01|, possibly with a % sequence of unfinished |\else: \c_false \fi:| or similar after % it, which we remove using the same trick. We also carry over the % truth value of the evaluated predicate. The expansion stops when % it sees the end marker or \verb+&&+ or \verb+||+ (assuming these % are not active characters at the programming level). % \begin{macrocode} \def_long_new:Npn \predicate_p:n #1{ \group_align_safe_begin: \exp_after:NN \predicate_auxi:NN \int_to_roman:w-`\q #1 02\scan_stop: } \def_long_test_function_new:npn {predicate:n}#1{ \group_align_safe_begin: \if:w \exp_after:NN \predicate_auxi:NN \int_to_roman:w-`\q #1 02\scan_stop: } \def_new:Npn \predicate_auxi:NN 0 #1{ \exp_after:NN \predicate_auxii:NNN \exp_after:NN #1 \int_to_roman:w-`\q } % \end{macrocode} % After removing trailing conditionals we call a macro for the case we % are in (see list above). % \begin{macrocode} \def_new:Npn \predicate_auxii:NNN #1#2#3{ \cs_use:c{predicate_#2#3_#1:w} } \def_new:cpn{predicate_&&_0:w}{ \exp_after:NN \predicate_auxi:NN\int_to_roman:w-`\q } \def_long_new:cpn{predicate_&&_1:w} #1 02\scan_stop:{ \group_align_safe_end: 01} \def_long_new:cpn{predicate_||_0:w} #1 02\scan_stop:{ \group_align_safe_end: 00} \def_new:cpn{predicate_||_1:w}{ \exp_after:NN \predicate_auxi:NN\int_to_roman:w-`\q } \def_new:cpn{predicate_02_0:w}\scan_stop:{ \group_align_safe_end: 00 } \def_new:cpn{predicate_02_1:w}\scan_stop:{ \group_align_safe_end: 01 } % \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}{\predicate_not_p:n} % The "not" variant just reverses the outcome of |\predicate_p:n|. % \begin{macrocode} \def_long_new:Npn \predicate_not_p:n #1{ \if:w \predicate_p:n{#1} \c_false \else: \c_true \fi: } % \end{macrocode} % \end{macro} % % \subsubsection{Case switch} % % \begin{macro}{\prg_case_int:nnn} % \begin{macro}{\prg_case_int_aux:nnn} % This case switch is in reality quite simple. It takes three arguments: % \begin{enumerate} % \item An integer expression you wish to find. % \item A list of pairs of "{"\meta{integer % expr}"}""{"\meta{code}"}". The list can be as long as is desired % and \meta{integer expr} can be negative. % \item The code to be executed if the value wasn't found. % \end{enumerate} % We don't need the else case here yet, so leave it dangling in the % input stream. % \begin{macrocode} \def_long:Npn \prg_case_int:nnn #1 #2 { % \end{macrocode} % We will be parsing on |#1| for each step so we might as well % evaluate it first in case it is complicated. % \begin{macrocode} \exp_args:No \prg_case_int_aux:nnn {\num_value:w \int_eval:n{#1}} #2 % \end{macrocode} % The \texttt{?} below is just so there are enough arguments when we % reach the end. And it made you look.~\texttt{;-)} % \begin{macrocode} \q_recursion_tail ? \q_recursion_stop } \def_long_new:Npn \prg_case_int_aux:nnn #1#2#3{ % \end{macrocode} % If we reach the end, return the else case. We just remove braces. % \begin{macrocode} \quark_if_recursion_tail_stop_do:nn{#2}{\use_arg_i:n} % \end{macrocode} % Otherwise we compare (which evaluates |#2| for us) % \begin{macrocode} \num_compare:nNnTF{#1}={#2} % \end{macrocode} % If true, we want to remove the remainder of the list, the else case % and then execute the code specified. Why not use |#3\use_none:n|? % Because if we are doing |f| style expansion, we will get % leftovers. If the test was false, we try the next pair, carrying the % |#1|. % \begin{macrocode} { \use_arg_i_delimit_by_q_recursion_stop:nw {\use_arg_i:nn{#3}} } { \prg_case_int_aux:nnn {#1}} } % \end{macrocode} % \end{macro} % \end{macro} % % \begin{macro}{\prg_case_dim:nnn} % \begin{macro}{\prg_case_dim_aux:nnn} % Same as |\prg_case_dim:nnn| except it is for \meta{dim} registers. % \begin{macrocode} \def_long:Npn \prg_case_dim:nnn #1 #2 { \exp_args:No \prg_case_dim_aux:nnn {\dim_use:N \dim_eval:n{#1}} #2 \q_recursion_tail ? \q_recursion_stop } \def_long_new:Npn \prg_case_dim_aux:nnn #1#2#3{ \quark_if_recursion_tail_stop_do:nn{#2}{\use_arg_i:n} \dim_compare:nNnTF{#1}={#2} { \use_arg_i_delimit_by_q_recursion_stop:nw {\use_arg_i:nn{#3}} } { \prg_case_dim_aux:nnn {#1}} } % \end{macrocode} % \end{macro} % \end{macro} % % \begin{macro}{\prg_case_str:nnn} % \begin{macro}{\prg_case_str_aux:nnn} % Same as |\prg_case_dim:nnn| except it is for strings. % \begin{macrocode} \def_long:Npn \prg_case_str:nnn #1 #2 { \prg_case_str_aux:nnn {#1} #2 \q_recursion_tail ? \q_recursion_stop } \def_long_new:Npn \prg_case_str_aux:nnn #1#2#3{ \quark_if_recursion_tail_stop_do:nn{#2}{\use_arg_i:n} \tlist_if_eq:xxTF{#1}{#2} { \use_arg_i_delimit_by_q_recursion_stop:nw {\use_arg_i:nn{#3}} } { \prg_case_str_aux:nnn {#1}} } % \end{macrocode} % \end{macro} % \end{macro} % % \subsubsection{Sorting} % % % \begin{macro}{\prg_define_quicksort:nnn} % |#1| is the name, |#2| and |#3| are the tokens enclosing the % argument. For the somewhat strange \meta{clist} type which doesn't % enclose the items but uses a separator we define it by hand % afterwards. When doing the first pass, the algorithm wraps all % elements in braces and then uses a generic quicksort which works % on token lists. % % As an example % \begin{quote} % |\prg_define_quicksort:nnn{seq}{\seq_elt:w}{\seq_elt_end:w}| % \end{quote} % defines the user function |\seq_quicksort:n| and furthermore % expects to use the two functions |\seq_quicksort_compare:nnTF| % which compares the items and |\seq_quicksort_function:n| which is % placed before each sorted item. It is up to the programmer to % define these functions when needed. For the |seq| type a sequence % is a token list pointer, so one additionally has to define % \begin{quote} % |\def:Npn \seq_quicksort:N{\exp_args:No\seq_quicksort:n}| % \end{quote} % % % For details on the implementation see ``Sorting in \TeX's Mouth'' % by Bernd Raichle. Firstly we define the function for parsing the % ininital list and then the braced list afterwards. % \begin{macrocode} \def_new:NNn \prg_define_quicksort:nnn 3 { \def_long:cNx{#1_quicksort:n}1{ \exp_not:c{#1_quicksort_start_partition:w} ##1 \exp_not:n{#2\q_nil#3\q_stop} } \def_long:cNx{#1_quicksort_braced:n}1{ \exp_not:c{#1_quicksort_start_partition_braced:n} ##1 \exp_not:N\q_nil\exp_not:N\q_stop } \def_long:cpx {#1_quicksort_start_partition:w} #2 ##1 #3{ \exp_not:N \quark_if_nil:nT {##1}\exp_not:N \use_none_delimit_by_q_stop:w \exp_not:c{#1_quicksort_do_partition_i:nnnw} {##1}{}{} } \def_long:cNx {#1_quicksort_start_partition_braced:n} 1 { \exp_not:N \quark_if_nil:nT {##1}\exp_not:N \use_none_delimit_by_q_stop:w \exp_not:c{#1_quicksort_do_partition_i_braced:nnnn} {##1}{}{} } % \end{macrocode} % Now for doing the partitions. % \begin{macrocode} \def_long:cpx {#1_quicksort_do_partition_i:nnnw} ##1##2##3 #2 ##4 #3 { \exp_not:N \quark_if_nil:nTF {##4} \exp_not:c {#1_do_quicksort_braced:nnnnw} { \exp_not:c{#1_quicksort_compare:nnTF}{##1}{##4} \exp_not:c{#1_quicksort_partition_greater_ii:nnnn} \exp_not:c{#1_quicksort_partition_less_ii:nnnn} } {##1}{##2}{##3}{##4} } \def_long:cNx {#1_quicksort_do_partition_i_braced:nnnn} 4 { \exp_not:N \quark_if_nil:nTF {##4} \exp_not:c {#1_do_quicksort_braced:nnnnw} { \exp_not:c{#1_quicksort_compare:nnTF}{##1}{##4} \exp_not:c{#1_quicksort_partition_greater_ii_braced:nnnn} \exp_not:c{#1_quicksort_partition_less_ii_braced:nnnn} } {##1}{##2}{##3}{##4} } \def_long:cpx {#1_quicksort_do_partition_ii:nnnw} ##1##2##3 #2 ##4 #3 { \exp_not:N \quark_if_nil:nTF {##4} \exp_not:c {#1_do_quicksort_braced:nnnnw} { \exp_not:c{#1_quicksort_compare:nnTF}{##4}{##1} \exp_not:c{#1_quicksort_partition_less_i:nnnn} \exp_not:c{#1_quicksort_partition_greater_i:nnnn} } {##1}{##2}{##3}{##4} } \def_long:cNx {#1_quicksort_do_partition_ii_braced:nnnn} 4 { \exp_not:N \quark_if_nil:nTF {##4} \exp_not:c {#1_do_quicksort_braced:nnnnw} { \exp_not:c{#1_quicksort_compare:nnTF}{##4}{##1} \exp_not:c{#1_quicksort_partition_less_i_braced:nnnn} \exp_not:c{#1_quicksort_partition_greater_i_braced:nnnn} } {##1}{##2}{##3}{##4} } % \end{macrocode} % This part of the code handles the two branches in each % sorting. Again we will also have to do it braced. % \begin{macrocode} \def_long:cNx {#1_quicksort_partition_less_i:nnnn} 4{ \exp_not:c{#1_quicksort_do_partition_i:nnnw}{##1}{##2}{{##4}##3}} \def_long:cNx {#1_quicksort_partition_less_ii:nnnn} 4{ \exp_not:c{#1_quicksort_do_partition_ii:nnnw}{##1}{##2}{##3{##4}}} \def_long:cNx {#1_quicksort_partition_greater_i:nnnn} 4{ \exp_not:c{#1_quicksort_do_partition_i:nnnw}{##1}{{##4}##2}{##3}} \def_long:cNx {#1_quicksort_partition_greater_ii:nnnn} 4{ \exp_not:c{#1_quicksort_do_partition_ii:nnnw}{##1}{##2{##4}}{##3}} \def_long:cNx {#1_quicksort_partition_less_i_braced:nnnn} 4{ \exp_not:c{#1_quicksort_do_partition_i_braced:nnnn}{##1}{##2}{{##4}##3}} \def_long:cNx {#1_quicksort_partition_less_ii_braced:nnnn} 4{ \exp_not:c{#1_quicksort_do_partition_ii_braced:nnnn}{##1}{##2}{##3{##4}}} \def_long:cNx {#1_quicksort_partition_greater_i_braced:nnnn} 4{ \exp_not:c{#1_quicksort_do_partition_i_braced:nnnn}{##1}{{##4}##2}{##3}} \def_long:cNx {#1_quicksort_partition_greater_ii_braced:nnnn} 4{ \exp_not:c{#1_quicksort_do_partition_ii_braced:nnnn}{##1}{##2{##4}}{##3}} % \end{macrocode} % Finally, the big kahuna! This is where the sub-lists are sorted. % \begin{macrocode} \def_long:cpx {#1_do_quicksort_braced:nnnnw} ##1##2##3##4\q_stop { \exp_not:c{#1_quicksort_braced:n}{##2} \exp_not:c{#1_quicksort_function:n}{##1} \exp_not:c{#1_quicksort_braced:n}{##3} } } % \end{macrocode} % \end{macro} % % % \begin{macro}{\prg_quicksort:n} % A simple version. Sorts a list of tokens, uses the function % |\prg_quicksort_compare:nnTF| to compare items, and places the % function |\prg_quicksort_function:n| in front of each of them. % \begin{macrocode} \prg_define_quicksort:nnn {prg}{}{} % \end{macrocode} % \end{macro} % % \begin{macro}{\prg_quicksort_function:n} % \begin{macro}{\prg_quicksort_compare:nnTF} % \begin{macrocode} \let:NN \prg_quicksort_function:n \ERROR \let:NN \prg_quicksort_compare:nnTF \ERROR % \end{macrocode} % \end{macro} % \end{macro} % % % That's it (for now). % \begin{macrocode} % %<*showmemory> \showMemUsage % % \end{macrocode} % % \endinput % % $Log$ % Revision 1.19 2006/08/08 10:26:09 morten % Fixed bugs in stepwise functions % % Revision 1.18 2006/07/30 14:41:28 morten % Added stepwise functions (loops going from i to k with step of j) plus quicksort. % % Revision 1.17 2006/07/06 14:57:13 morten % Moved code in the `miscellaneus' section to l3basics. % % Revision 1.16 2006/06/03 18:55:12 morten % Added special double boolean switches. % % Revision 1.15 2006/03/20 18:26:38 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.14 2006/01/19 22:31:56 morten % Added \bool_set_eq:NN functions plus made function collection % complete. % % Revision 1.13 2006/01/04 00:58:17 morten % Added generic loops. Changed some names plus syntax for some of the % logical operations. Added code for defining functions with specified % number of arguments. % % Revision 1.12 2005/12/27 10:01:55 morten % Changed RCS information retrieval. Moved tlist code to l3tlp. Added % code for inserting n copies of something. Changed \bool_while to % \bool_whiledo. % % Revision 1.11 2005/04/25 15:01:59 morten % Fixed some names. Improved \peek_char_generic:NNTF to not use token registers. % % Revision 1.10 2005/04/23 14:36:12 morten % Changed \c_left|right_brace_token to \c_group_begin_token and % \c_group_end_token. Fixed definitions of \tlist_if_head_XXX functions. % Added example for PR/3080. % % Revision 1.9 2005/04/09 21:08:43 morten % Documentation blunders fixed % % Revision 1.8 2005/04/06 21:27:15 morten % More tlist functions, Moved \engine_aleph:TF to l3basics, new peek-ahead functions, definitions of implicit characters. % % Revision 1.7 2005/03/26 21:11:14 morten % Fix typo in \scan_align_safe_stop: % % Revision 1.6 2005/03/22 23:23:30 morten % Moved \tlist_ functions from l3basics. Added align-safe versions of important functions. Reorganized documentation slightly. % % Revision 1.5 2005/03/16 22:36:10 braams % Added the tweaks necessary to be able to load with initex % % Revision 1.4 2005/03/11 21:28:20 braams % Fixed the use of RCS information; added \StopEventually %