% \iffalse meta-comment % % Copyright (C) 2016 by Thomas Colcombet % ----------------------------------- % % This file may be distributed and/or modified under the % conditions of the LaTeX Project Public License, either version 1.2 % of this license or (at your option) any later version. % The latest version of this license is in: % % http://www.latex-project.org/lppl.txt % % and version 1.2 or later is part of all distributions of LaTeX % version 1999/12/01 or later. % % \fi % % \iffalse % %<*driver> \documentclass{ltxdoc} \usepackage[utf8]{inputenc} \usepackage[T1]{fontenc} \usepackage{paralist} \usepackage{hyperref} \usepackage{expl3} \EnableCrossrefs \CodelineIndex \RecordChanges \newcommand\kl[2][]{#2} \begin{document} \DocInput{knowledge-utils.dtx} \end{document} % % \fi % % \CheckSum{0} % % \CharacterTable % {Upper-case \A\B\C\D\E\F\G\H\I\J\K\L\M\N\O\P\Q\R\S\T\U\V\W\X\Y\Z % Lower-case \a\b\c\d\e\f\g\h\i\j\k\l\m\n\o\p\q\r\s\t\u\v\w\x\y\z % Digits \0\1\2\3\4\5\6\7\8\9 % Exclamation \! Double quote \" Hash (number) \# % Dollar \$ Percent \% Ampersand \& % Acute accent \' Left paren \( Right paren \) % Asterisk \* Plus \+ Comma \, % Minus \- Point \. Solidus \/ % Colon \: Semicolon \; Less than \< % Equals \= Greater than \> Question mark \? % Commercial at \@ Left bracket \[ Backslash \\ % Right bracket \] Circumflex \^ Underscore \_ % Grave accent \` Left brace \{ Vertical bar \| % Right brace \} Tilde \~} % % % \changes{v2.0}{2016/02/26}{Initial version} % % \GetFileInfo{knowledge-utils.sty} % % \DoNotIndex{} % % \title{The \textsf{knowledge-utils} package} % \author{Thomas Colcombet \\ \texttt{thomas.colcombet@irif.fr}} % \maketitle % % \begin{abstract} % This code is incorporated in the knowledge.sty file. It essentially contains a certain number of useful functions. % % This includes dealing with the kaux file (a single file which is output during the compilation, and then postprocessed for extracting information). % In contains some primitives for creating fields in tuples, macros for overloading other macros, and code for recreating an argument list (reversing par of the xparse syntax). % % This file needs to be cleaned and reorganize. % \end{abstract} % %\tableofcontents % % % \section{Introduction} % % The main file is knowledge.dtx. % % % % % % \section{Implementation} % % \def\cs{\ExplSyntaxOn\csARG} % \def\csARG#1{\ExplSyntaxOff\texttt{\detokenize{#1}}} % %\let\ifcode\iffalse % % \subsection{Code preparation} % \begin{macrocode} \usepackage{xparse} \usepackage{currfile} % \end{macrocode} % The interface of expl3 since Mars 2015 has changed and a small patch is required: % \begin{macrocode} \tl_if_exist:NTF \c_sys_jobname_str {} {\tl_set_eq:Nc\c_sys_jobname_str{c_job_name_tl}} % \end{macrocode} % The interface of expl3 since Mars 2018 has changed and a small patch is required: % \begin{macrocode} %\tl_if_exist:NTF % \ior_str_get:NN % {} % {\tl_set_eq:NN\ior_str_get:NN\ior_get_str:NN} % \end{macrocode} % Some useful adding (newset). % \begin{macrocode} \cs_new:Nn\tl_new:Nn {\tl_new:N#1 \tl_set:Nn#1{#2}} \cs_new:Nn\tl_gnew:Nn {\tl_new:N#1 \tl_gset:Nn#1{#2}} \cs_new:Nn\tl_new_eq:NN {\tl_new:N#1 \tl_set_eq:NN#1#2} \cs_new:Nn\bool_new_true:N {\bool_new:N#1 \bool_set_true:N#1} \cs_new:Nn\bool_new_false:N {\bool_new:N#1 \bool_set_false:N#1} \cs_new:Nn\bool_set_text:Nn {\use:c{bool_set_#2:N}#1} \cs_generate_variant:Nn\tl_new:Nn{Nx,cn,cx} \cs_generate_variant:Nn\tl_new_eq:NN{Nc,cN,cc} \cs_generate_variant:Nn\bool_new_true:N{c} \cs_generate_variant:Nn\bool_new_false:N{c} \cs_generate_variant:Nn\bool_set_text:Nn{cn} % \end{macrocode} % Some new \kl{latex3} commands: % \cs{cs_new_with_variants:Nnn}, % \cs{if_mode_preamble:TF}, % \cs{kl_hide_begin:} and \cs{kl_hide_begin:} % (that stand for \cs{@bsphack} and \cs{@esphack} of \kl{latex2e}). % % Creating new macros with their variants at the same time... % \begin{macrocode} \cs_new_protected:Nn\cs_new_protected_with_variants:Nnn {\cs_new_protected:Nn#1{#3} \cs_generate_variant:Nn#1{#2}} \cs_generate_variant:Nn\cs_new_protected_with_variants:Nnn{cnn} \cs_new_protected:Nn\cs_new_with_variants:Nnn {\cs_new:Nn#1{#3} \cs_generate_variant:Nn#1{#2}} \cs_generate_variant:Nn\cs_new_with_variants:Nnn{cnn} \cs_generate_variant:Nn\tl_show:n{x} % \end{macrocode} % % Now comes a command for simultaneously testing existence and non emptiness. % \begin{macrocode} \cs_new:Npn\tl_if_exist_ne:NTF#1 {\tl_if_exist:NTF#1{\tl_if_empty:NTF#1\use_ii:nn\use_i:nn}\use_ii:nn} \cs_new:Npn\tl_if_exist_ne:NT#1 {\tl_if_exist:NTF#1{\tl_if_empty:NTF#1\use_none:n\use:n}\use_none:n} \cs_generate_variant:Nn\tl_if_empty:nTF{xTF} % \end{macrocode} % % % Immediate execution of code with parameters without explicit abstraction. %\begin{description} %\item \cs{kl_apply_inline:nnw} takes an argument specifier, a code with % arguments ($\sharp$1,\dots), and sufficiently many arguments, and executes % the code with arguments substituted (this is not expandable). %\item \cs{kl_apply_inline_variant:nnnw} does the same thing, and furthermore use a variant % of the code in which the arguments are executed as specified in the second argument. % For instance, %\begin{verbatim} %\kl_apply_inline_variant:nnnw{nn}{VV} % {\cs_new:Nn\wrap:n{#1##1#2}} % \leftdelimiter\rightdelimiter %\end{verbatim} %Define a new function \cs{wrap:n} that takes a parameter, and encloses it with the \textbf{content} of %\cs{leftdelimiter} and \cs{rightdelimiter}. %\end{description} %\ifcode % \begin{macrocode} \cs_new_protected:Npn\kl_apply_inline:nnw#1#2 {\group_begin: \cs_set:cn{g_tmpa_cs:#1} {\group_end:#2} \use:c{g_tmpa_cs:#1}} \cs_new_protected:Npn\kl_apply_inline_variant:nnnw#1#2#3 {\group_begin: \cs_set:cn{g_tmpa_cs:#1} {\group_end:#3} \exp_args:Nc \cs_generate_variant:Nn{g_tmpa_cs:#1}{#2} \use:c{g_tmpa_cs:#2}} % \end{macrocode} %\fi % % \begin{macrocode} \cs_new:Nn\mode_if_preamble:TF{#1} \AtBeginDocument {\cs_gset:Nn\mode_if_preamble:TF{#2}} % \end{macrocode} % \begin{macrocode} \makeatletter \int_new:N\kl_sphack_int \cs_set_eq:NN\kl_stored_@bsphack\@bsphack \cs_set_eq:NN\kl_stored_@esphack\@esphack \cs_set:Nn\kl_hide_begin: {\@bsphack \cs_set_eq:NN\@bsphack\relax \cs_set_eq:NN\@esphack\relax \int_incr:N\kl_sphack_int} \cs_set:Nn\kl_hide_end: {\int_decr:N\kl_sphack_int \int_compare:nNnTF{\kl_sphack_int}=0 {\cs_set_eq:NN\@bsphack\kl_stored_@bsphack \cs_set_eq:NN\@esphack\kl_stored_@esphack}{} \@esphack} \makeatother % \end{macrocode} % \begin{macrocode} \cs_new:Nn\if_true:TF{#1} \cs_new:Nn\if_false:TF{#2} %\cs_generate_variant:Nn\int_compare:nNnTF{cNnTF} \cs_generate_variant:Nn\use:nn{nx} % \end{macrocode} % The command |\cs_apply_inline:n| takes a first argument % with as many parameters as in ||, and apply it to them. % \ifcode % \begin{macrocode} \cs_new:Npn\cs_apply_inline:nn#1 {\cs_set:Npn\g_tmpa_cs:##1{#1} \g_tmpa_cs:} \cs_new:Npn\cs_apply_inline:nnn#1 {\cs_set:Npn\g_tmpa_cs:##1##2{#1} \g_tmpa_cs:} \cs_new:Npn\cs_apply_inline:nnnn#1 {\cs_set:Npn\g_tmpa_cs:##1##2##3{#1} \g_tmpa_cs:} \cs_new:Npn\cs_apply_inline:nnnnn#1 {\cs_set:Npn\g_tmpa_cs:##1##2##3##4{#1} \g_tmpa_cs:} \cs_new:Npn\cs_apply_inline:nnnnnn#1 {\cs_set:Npn\g_tmpa_cs:##1##2##3##4##5{#1} \g_tmpa_cs:} \cs_new:Npn\cs_apply_inline:nnnnnnn#1 {\cs_set:Npn\g_tmpa_cs:##1##2##3##4##5##6{#1} \g_tmpa_cs:} \cs_new:Npn\cs_apply_inline:nnnnnnnn#1 {\cs_set:Npn\g_tmpa_cs:##1##2##3##4##5##6##7{#1} \g_tmpa_cs:} \cs_new:Npn\cs_apply_inline:nnnnnnnnn#1 {\cs_set:Npn\g_tmpa_cs:##1##2##3##4##5##6##7##8{#1} \g_tmpa_cs:} \cs_new:Npn\cs_apply_inline:nnnnnnnnnn#1 {\cs_set:Npn\g_tmpa_cs:##1##2##3##4##5##6##7##8##9{#1} \g_tmpa_cs:} % \end{macrocode} % % \begin{macrocode} %\cs_generate_variant:Nn\cs_apply_inline:nn{nc} %\cs_generate_variant:Nn\cs_apply_inline:nnn{ncc} %\cs_generate_variant:Nn\cs_apply_inline:nnnn{nccc} %\cs_generate_variant:Nn\cs_apply_inline:nnnnn{ncccc} %\cs_generate_variant:Nn\cs_apply_inline:nnnnnn{nccccc} %\cs_generate_variant:Nn\cs_apply_inline:nnnnnnn{ncccccc} %\cs_generate_variant:Nn\cs_apply_inline:nnnnnnnn{nccccccc} %\cs_generate_variant:Nn\cs_apply_inline:nnnnnnnnn{ncccccccc} %\cs_generate_variant:Nn\cs_apply_inline:nnnnnnnnnn{nccccccccc} % \end{macrocode} %\fi % % % \subsection{KAux file} % % We introduce here the KAux file. % The principle of the KAux file is to play the role of an aux file, but at the same time it % has some facilities for using it with several subpackages. % Essentially, the code in the KAUX file is executed, and is supposed to use only functions defined by the % |\NewKAuxCommand| (using \texttt{xparse} syntax). Such functions do nothing by default, % unless explicitly activated when rereading the file. % At the beginning of the KAUX file, each command that may occur is declared, % the package that produced it is named, its arguments are provided, and its use % is succinctly described. % % By default, the location is stored in the KAux file, unless the writing command has a star. % When the KAux file is processed, the variables \cs{\kauxCurrentFile} % and \cs{\kauxCurrentLine} contain the filename and the line at the moment the information was processed. % The location can be explicitely written using \cs{\KAuwWriteLocation}. % Thus, by the user can decide when to write the line (at the begining of a command may be better when it has several lines). % % The file is open for writing when \cs{KAuxActivate} is called the % first time. So, commands may ensure that the file is activated using % \cs{KAuxActivate}, but this should be used after the configuration % is finished. So the rule is: when a package is loaded, one should never use % \cs{KAuxActivate}. % \begin{macrocode} \AtBeginDocument{\KAuxActivate} \NewDocumentCommand\KAuxActivate{}{ \cs_gset_eq:NN\KAuxActivate\relax \KAuxOpen{}} % \end{macrocode} % \begin{macrocode} \AtEndDocument{\KAuxClose} % \end{macrocode} % \begin{macrocode} \iow_new:N\knowledge_kaux_iow \NewDocumentCommand\KAuxOpen{} {\kaux_pretreatment_tl \tl_gclear:N\kaux_pretreatment_tl \iow_open:Nn \knowledge_kaux_iow {\c_sys_jobname_str.kaux} \KAuxWrite*{} \kaux_inittreatment_tl \tl_gclear:N\kaux_inittreatment_tl \KAuxWrite*{}} \NewDocumentCommand\KAuxClose{} {\KAuxWrite*{\KAuxEOF {}} \iow_close:N\knowledge_kaux_iow \bool_gset_true:N\kaux_can_be_used_bool \kaux_posttreatment_tl } % \end{macrocode} % Testing if the KAux file is complete. % |\kaux_if_exist_containline:nnTF| tests if a line of a file is equal to the argument. % This has to be used with care: the searched line is transformed into a str, thus introducing % potential extra spaces after the control sequences. % Also, when lines are read from the files, the trailing spaces are lost. % Anyway, |IfKAuxReadyTF| tests for the presence of |\KAuxEOF {}| in a proper way. % \begin{macrocode} \cs_new:Npn\kaux_if_exist_containline:nnTF#1#2{ \file_if_exist:nTF{#1}{ \ior_open:Nn\klg_tmpa_ior{#1} \bool_gset_false:N\g_tmpa_bool \bool_gset_false:N\g_tmpb_bool \str_gset:Nn\g_tmpb_str{#2} \bool_do_until:Nn\g_tmpa_bool{ \ior_if_eof:NTF\klg_tmpa_ior {\bool_set_true:N\g_tmpa_bool} { \ior_str_get:NN\klg_tmpa_ior\g_tmpa_str %\iow_term:x{(\exp_not:V\g_tmpa_str)=?(\exp_not:V\g_tmpb_str)} \str_if_eq:NNTF\g_tmpa_str\g_tmpb_str {\bool_gset_true:N\g_tmpa_bool \bool_gset_true:N\g_tmpb_bool} {} }} \ior_close:N\klg_tmpa_ior %\bool_show:N\g_tmpb_bool \bool_if:NTF\g_tmpb_bool } {\use_ii:nn} } \NewDocumentCommand\IfKAuxReadyTF{} {\exp_args:Nx\kaux_if_exist_containline:nnTF{\c_sys_jobname_str.kaux}{\KAuxEOF {}}} % \end{macrocode} % % % % Using the command |\KAuxBefore|, |\KAuxInit| and |\KAuxAfter|, one can add % code to be executed before the opening, at the beginning and after the writing of the KAUX file. % \begin{macrocode} \NewDocumentCommand\KAuxBefore{m} {\tl_gput_right:Nn\kaux_pretreatment_tl{#1}} \NewDocumentCommand\KAuxAfter{m} {\tl_gput_right:Nn\kaux_posttreatment_tl{#1}} \NewDocumentCommand\KAuxInit{m} {\tl_gput_right:Nn\kaux_inittreatment_tl{#1}} % \end{macrocode} % \begin{macrocode} \tl_new:N\kaux_pretreatment_tl \tl_gset:Nn\kaux_pretreatment_tl{} \tl_new:N\kaux_posttreatment_tl \tl_gset:Nn\kaux_posttreatment_tl{} \tl_new:N\kaux_inittreatment_tl \tl_gset:Nn\kaux_inittreatment_tl{} % \end{macrocode} % % % \subsubsection{KAux phases and commands} % % The idea is that the KAux file will be read several times. Once at the begining, before rewriting it, % and then more times afterward (maybe several times). Each time it is read corresponds to a `phase'. % By default, when a KAuxCommand is defined, then it does nothing (and absorbs its only parameter). % Then, when a phase is activated (and several phases can be activated simultaneously), each command % executes the corresponding code (declares using |\DeclareKAuxPhaseCommand|). % % % The list of KAux commands are kept in |\kaux_command_list_tl|. % \begin{macrocode} \tl_new:N\kaux_command_list_tl % \end{macrocode} % % When one wants to use some command in the KAux file, one has to first declare % it using |\NewKAuxCommand\commandname{arguments}{description}| % in which |\commandname| is the token of the command, |arguments| % is the description of arguments following the \texttt{xparse} package syntax, % and |description| is an informal description of what the command is intended to do. % Note that for efficiency reason, in the KAux file itself, % \emph{arguments have to be surrounded by curly brackets}, % but when used, these will parsed using the argument description as defined by \texttt{xparse}. % % Technically, |\NewKAuxCommand| declares the command in the KAux file using |\KAuxCommand|, % then the arguments description is stored in |\kaux_commandname-args_tl|, and |\commandname| % itself is created, as a code that executes, followed by the arguments, all the tokens in |\kaux_commandname-active_tl|. Also, the command is appended to |\kaux_command_list_tl|. % \begin{macrocode} \NewDocumentCommand\NewKAuxCommand{ m m m } { \ProvideDocumentCommand#1{#2}{} \KAuxInit {\KAuxWrite*{\KAuxCommand#1{#2}{#3}}} \tl_new:c{kaux_\cs_to_str:N#1-args_tl} \tl_gset:cn{kaux_\cs_to_str:N#1-args_tl}{#2} \tl_gput_right:Nn\kaux_command_list_tl{#1} } % \end{macrocode} % As an example, |\KAuxEOF| describes is used at the end of the Kaux file. % \begin{macrocode} \NewKAuxCommand\KAuxEOF{m} {Does~nothing,~useless~empty~argument ,~and~is~searched~for~knowing~if~the~kaux~file~was~written~till~the~end.} % \end{macrocode} % \begin{macrocode} \NewDocumentCommand\KAuxCommand{mmm} { \cs_if_exist:NTF #1 {} {\ProvideDocumentCommand#1{#2}} } % \end{macrocode} % % |\ActivateKAuxPhase{phase1,phase2,...}| sets the KAux commands to use % for all KAux commands the % code corresponding to |phase1|, |phase2|, \dots{} This is obtained by % defining for each command |\commandname| the tl variable |\kaux_commandname-active_tl| % accordingly. % \begin{macrocode} \NewDocumentCommand\ActivateKAuxPhase{m}{ \tl_map_inline:Nn\kaux_command_list_tl {\kaux_activate_command_phase:Nn##1{#1}} } % \end{macrocode} % \begin{macrocode} \cs_new:Nn\kaux_activate_command_phase:Nn { \tl_clear_new:c{kaux_\cs_to_str:N#1-active_tl} % \kaux_code_set:Nn\tmpa_code{} \clist_map_inline:nn{#2} {\cs_if_exist:cTF{kaux_\cs_to_str:N#1-##1:nnnnnnnnn} {\exp_args:NNc \kaux_code_put_right:NN \tmpa_code{kaux_\cs_to_str:N#1-##1:nnnnnnnnn}} {}} \kaux_code_wrap:N\tmpa_code \exp_args:NNx\kaux_code_put_left:Nn\tmpa_code {\DeclareDocumentCommand\exp_not:N#1 {\exp_not:v{kaux_\cs_to_str:N#1-args_tl}}} \kaux_code_exec:N\tmpa_code } % \end{macrocode} % % Here comes a bunch of commands for manipulating code. % The idea is to replicate some of the commands for the type tl (in a much less efficient way, it is true), % but make it compatible with arguments |#1|, |#2|. There may be much more efficient ways to do that, % but the expansion system makes it not obvious for me. % \begin{macrocode} \cs_new:Nn\kaux_code_set_eq:NN {\cs_set_eq:NN#1#2} \cs_new:Nn\kaux_code_set:Nn {\cs_set:Npn#1##1##2##3##4##5##6##7##8##9{#2}} \cs_new:Nn\kaux_code_put_right:NN {\exp_args:NNo\kaux_code_put_right:Nn#1 {#2{##1}{##2}{##3}{##4}{##5}{##6}{##7}{##8}{##9}}} \cs_new:Nn\kaux_code_put_right:Nn {\exp_args:NNo\cs_set:Nn \tmp:nnnnnnnnn {#1{##1}{##2}{##3}{##4}{##5}{##6}{##7}{##8}{##9}#2} \cs_set_eq:NN#1\tmp:nnnnnnnnn} \cs_new:Nn\kaux_code_put_left:Nn {\kaux_code_set:Nn\g_tmpc_code{#2} \kaux_code_put_right:NN\g_tmpc_code#1 \kaux_code_set_eq:NN#1\g_tmpc_code} \cs_new:Nn\kaux_code_wrap:N {\expandafter\cs_set:Nn \expandafter\tmp:nnnnnnnnn \expandafter{ \expandafter{#1{##1}{##2}{##3}{##4}{##5}{##6}{##7}{##8}{##9}}} \cs_set_eq:NN#1\tmp:nnnnnnnnn} \cs_new:Nn\kaux_code_exec:N {#1{##1}{##2}{##3}{##4}{##5}{##6}{##7}{##8}{##9}} % \end{macrocode} % Declaring a code for a KAuxCommand associated to a specific phase % is done using |\DeclareKAuxPhaseCommand\commandname{phase}{code}|. % The code is stored in a `cs' with nine parameters. % \begin{macrocode} \NewDocumentCommand\DeclareKAuxPhaseCommand{mmm} { \tl_if_exist:cTF{kaux_\cs_to_str:N#1-args_tl}{} {\ERROR_KAuxCommand_UNDEFINED} \cs_gset:cn{kaux_\cs_to_str:N#1-#2:nnnnnnnnn}{#3}} % \end{macrocode} % % \begin{macrocode} \NewDocumentCommand\KAuxWriteLocation{} {\KAuxActivate \group_begin: \tl_set:Nx\l_tmpa_tl{{\currfilename}{\the\inputlineno}} \tl_if_eq:NNTF\l_tmpa_tl\kaux_location_tl {} {\tl_gset_eq:NN\kaux_location_tl\l_tmpa_tl \iow_now:Nx\knowledge_kaux_iow{} \iow_now:Nx\knowledge_kaux_iow {\KAuxFileAt\kaux_location_tl}} \group_end:} \tl_new:N\kaux_location_tl \NewDocumentCommand\KAuxWriteX{sm} {\KAuxActivate \IfBooleanTF{#1}{}{\KAuxWriteLocation} \exp_args:NNx\iow_now:Nn\knowledge_kaux_iow{#2}} \NewDocumentCommand\KAuxWrite{sm} {\KAuxActivate \IfBooleanTF{#1}{}{\KAuxWriteLocation} \iow_now:Nn\knowledge_kaux_iow{#2}} \NewDocumentCommand\KAuxFileAt{mm} {\tl_set:Nn\kauxCurrentFile{#1} \tl_set:Nn\kauxCurrentLine{#2}} \cs_new:Nn\kaux_current_location:{\kauxCurrentFile :\kauxCurrentLine :} \tl_new:N\kauxCurrentFile \tl_new:N\kauxCurrentLine % \end{macrocode} % % The kaux file is allowed to be used at the beginning if it exists (of course), % and if the aux file also exists. This is for tools that delete the aux file for cleaning the directory % to virtually also delete the kaux file. % \begin{macrocode} \bool_new:N\kaux_can_be_used_bool \bool_gset_false:N\kaux_can_be_used_bool \file_if_exist:nT{\c_sys_jobname_str.aux} {\file_if_exist:nT{\c_sys_jobname_str.kaux} {\bool_gset_true:N\kaux_can_be_used_bool}} \NewDocumentCommand\KAuxProcess{m} { \bool_if:NT\kaux_can_be_used_bool {\ActivateKAuxPhase{#1} \input{\c_sys_jobname_str.kaux}}} % \end{macrocode} % % % % % % % % % \subsection{References} % % This is an old part of the code which is difficult to read. % It should be cleaned. But this is a lot of work. % % When using \cs{\cell_new_type:nnn}, a new name space is created, % in which can be stored information. The first parameter is the name space. The second % is the description (expl3 style) of the parameters defining an entry, and the third is the % string of character (that can use the arguments defined) used to address this space. % For instance \verb|\cell_new_type:nnn{carots}{nn}{K@ROT:{#1}{#2}}| % define a cell type named `carot' that is indexed by two parameters. % In particular, it defines the macro \cs{\carot:Nnn} that takes a token, and parameters, that executes the % token with as first parameter the token identifying the location of the carot. % For instance, \verb|\carot_at:Nnn\show{A}{B}| will display the content of the carot defined by the parameters A and B. % \begin{macrocode} \cs_new:Nn\cell_new_type:nnn { \cs_new:cn{#1_id:#2}{#3} \cs_new:cn{#1_id_b:#2}{{#3}} \cs_new:cpx{#1_at:N#2}##1 {\exp_not:n{\exp_after:wN\exp_args:Nc\exp_after:wN} ##1 \exp_not:c{#1_id_b:#2}} } % \end{macrocode} % \begin{macrocode} \cs_new:Nn\cell_specialize:nnn {\cs_new:cpx{#1_#2_id:#3} {\exp_not:c{#1_id:n#3}{#2}} \cs_new:cpx{#1_#2_at:N#3}##1 {\exp_not:c{#1_at:Nn#3}##1{#2}} } \cs_new:Nn\cell_make_tl:nn {\cs_set:Nn\l_tmpa_cs:nn {\cs_new:cpx{#1_##1:#2##2} {\exp_not:c{#1_at:N#2}\exp_not:c{tl_##1:N##2}}} \l_tmpa_cs:nn{set}{n} \l_tmpa_cs:nn{gset}{n} \l_tmpa_cs:nn{set}{x} \l_tmpa_cs:nn{gset}{x} \l_tmpa_cs:nn{gput_right}{n} \l_tmpa_cs:nn{gput_left}{n} \l_tmpa_cs:nn{put_right}{n} \l_tmpa_cs:nn{put_left}{n} \l_tmpa_cs:nn{show}{} \l_tmpa_cs:nn{use}{} \l_tmpa_cs:nn{if_exist}{TF} \l_tmpa_cs:nn{if_empty}{TF} } \cs_new:Nn\cell_make_int:nn { \cs_set:Nn\l_tmpa_cs:nn {\cs_new:cpx{#1_##1:#2##2} {\exp_not:c{#1_at:N#2}\exp_not:c{int_##1:N##2}}} \l_tmpa_cs:nn{new}{} \l_tmpa_cs:nn{show}{} \l_tmpa_cs:nn{set}{n} \l_tmpa_cs:nn{gset}{n} \l_tmpa_cs:nn{incr}{} \l_tmpa_cs:nn{gincr}{} \l_tmpa_cs:nn{decr}{} \l_tmpa_cs:nn{gdecr}{} \l_tmpa_cs:nn{use}{} \l_tmpa_cs:nn{to_arabic}{} } \cs_new:Nn\cell_make_bool:nn { \cs_set:Nn\l_tmpa_cs:nn {\cs_new:cpx{#1_##1:#2##2} {\exp_not:c{#1_at:N#2}\exp_not:c{bool_##1:N##2}}} \l_tmpa_cs:nn{set_true}{} \l_tmpa_cs:nn{set_false}{} \l_tmpa_cs:nn{gset_true}{} \l_tmpa_cs:nn{gset_false}{} \l_tmpa_cs:nn{set}{n} \l_tmpa_cs:nn{gset}{n} \l_tmpa_cs:nn{if}{TF} } \cs_new:Nn\seq_use:N {\seq_use:Nn#1,} \cs_new:Nn\cell_make_seq:nn { \cs_set:Nn\l_tmpa_cs:nn {\cs_new:cpx{#1_##1:#2##2} {\exp_not:c{#1_at:N#2}\exp_not:c{seq_##1:N##2}}} \l_tmpa_cs:nn{new}{} \l_tmpa_cs:nn{set_from_clist}{n} \l_tmpa_cs:nn{gset_from_clist}{n} \l_tmpa_cs:nn{gpush}{n} \l_tmpa_cs:nn{if_exist}{TF} \l_tmpa_cs:nn{if_empty}{TF} \l_tmpa_cs:nn{if_in}{nTF} \l_tmpa_cs:nn{show}{} \l_tmpa_cs:nn{use}{n} \l_tmpa_cs:nn{use}{} \l_tmpa_cs:nn{map_inline}{n} } \cs_new:Nn\cell_make_tuple:nn { \cs_set:Nn\l_tmpa_cs:nn {\cs_new:cpx{#1_##1:#2##2} {\exp_not:c{#1_at:N#2}\exp_not:c{tuple_##1:N##2}}} } \cs_new:Nn\cell_specialize_int:nnn { \cell_specialize:nnn{#1}{#2}{#3} \cell_make_int:nn{#1_#2}{#3} } \cs_new:Nn\cell_specialize_tl:nnn { \cell_specialize:nnn{#1}{#2}{#3} \cell_make_tl:nn{#1_#2}{#3} } \cs_new:Nn\cell_specialize_bool:nnn { \cell_specialize:nnn{#1}{#2}{#3} \cell_make_bool:nn{#1_#2}{#3} } \cs_new:Nn\cell_specialize_seq:nnn { \cell_specialize:nnn{#1}{#2}{#3} \cell_make_seq:nn{#1_#2}{#3} } % \end{macrocode} % % \subsection{Tuples} % % ensure % \cs{\tuple_empty_tl}, \cs{\tuple_new:N}, % \cs{\tuple_gclear:N}, \cs{\tuple_if_exist:NTF}, % \cs{\tuple_if_no_index:NNTF}, % \cs{\tuple_gset:NNn}, \cs{\tuple_gset:NNN}, % \cs{\tuple_gdel:NN}, \cs{\tuple_apply:NNNn}, % \cs{\tuple_apply_inline:NNnn}, \cs{\tuple_get_default_tl:NNNn}, % \cs{\tuple_get_int:NNN}, \cs{\tuple_incr:NN}, % \cs{\tuple_decr:NN}, \cs{\tuple_gset_true:NN}, % \cs{\bool_set_false:NN} % % \cs\tuple_new_expandable_index:Nnn takes a token acting as an index, a name, % and a default value, and creates a command |tuple_exp_|name|:N| which given % a tuple, retrieve in an expandable way the index, and if it does not exists outputs the default value. % % % % % % % % % % % \begin{macrocode} \cs_new:Nn\tuple_new:n { \cs_new:cx{#1_new:N} {\exp_not:N\tl_new:N##1 \exp_not:N\tl_gset:Nn##1{\exp_not:c{#1:}}} \cs_new:cx{#1_gclear:N} {\exp_not:N\tl_gset:Nn##1{\exp_not:c{#1:}}} \cs_new:cx{#1_ensure:N} {\exp_not:N\tl_if_exist:NTF##1{} {\exp_not:N\tl_new:N##1 \exp_not:N\tl_gset:Nn##1{\exp_not:c{#1:}}}} \cs_new:cn{#1:}{} % \cs_set:Nn\l_tmpa_cs:nn { \cs_new:cpx{#1_##1:##2} {\exp_not:c{tuple_##1:n##2}{\exp_not:n{#1}}}} \l_tmpa_cs:nn{new_tl_index}{Nnn} \l_tmpa_cs:nn{new_tl_index}{nn} \l_tmpa_cs:nn{new_tl_index}{n} \l_tmpa_cs:nn{new_int_index}{Nnn} \l_tmpa_cs:nn{new_int_index}{nn} \l_tmpa_cs:nn{new_int_index}{n} \l_tmpa_cs:nn{new_bool_index}{Nn} \l_tmpa_cs:nn{new_bool_index}{n} \l_tmpa_cs:nn{show}{N} \l_tmpa_cs:nn{if_exist}{NTF} } \cs_new:Npn\tuple_if_exist:nNTF#1{\tl_if_exist:NTF} % \end{macrocode} % % % \begin{macrocode} \cs_new:Nn\tuple_new_bool_index:nNNn { \cs_new:cn{#1_#4_expands_to:TF}{ \cs_set:Nn#2{##1} \cs_set:Nn#3{##2}} \cs_gset:Nn#2{} \cs_gset:Nn#3{} % \cs_new:cx{#1_#4_if:NTF} {\exp_not:N\expandafter \exp_not:c{#1_#4_if~i:w} ##1 \exp_not:n{#2 ab #2 c \tuple_end:}{##3}{##2}} \cs_new:cpn{#1_#4_if~i:w} ##1 #2 ##2 #2 ##3 \tuple_end: {\tl_if_single:nTF{##3}} % \cs_new:cx{#1_#4_gset_false:N} {\exp_not:N\expandafter \exp_not:c{#1_#4_gset_false~i:w} ##1 \exp_not:n{#2 ab #2 c \tuple_end:}##1} \cs_new:cpn{#1_#4_gset_false~i:w} ##1 #2 ##2 #2 ##3 \tuple_end:##4 {\tl_if_single:nTF{##3} {\tl_gset:Nn##4{##1}} {\tl_gset:Nn##4{##1##2}}} % \cs_new:cx{#1_#4_gset_true:N} {\exp_not:N\expandafter \exp_not:c{#1_#4_gset_true~:w} ##1 \exp_not:n{#2 ab #2 c \tuple_end:}##1} \cs_new:cpn{#1_#4_gset_true~:w} ##1 #2 ##2 #2 ##3 \tuple_end: ##4 {\tl_if_single:nTF{##4} {\tl_gset:Nn##4{##1 #2}}{}} } \cs_new:Nn\tuple_new_bool_index:nn {\use:x{\exp_not:n{\tuple_new_bool_index:nNNn{#1}} \exp_not:c{BOOL_#1_#2_true:} \exp_not:c{BOOL_#1_#2_false:} {#2}}} % \end{macrocode} % % % % \begin{macrocode} \cs_new:Nn\tuple_new_index:nNnn{ \cs_new:cn{#1_#3_expands_to:n}{\cs_set:Nn#2{##1}} \cs_gset:Nn#2{} % \cs_new:cx{#1_#3_use:N} {\exp_not:N\expandafter \exp_not:c{#1_#3_use~i:w} ##1 \exp_not:n{#2 {#4}\tuple_end:}} \cs_new:cpx{#1_#3_use~i:w} ##1 #2 ##2 ##3 \tuple_end:{##2} % \cs_new:cx{#1_#3_if_exist:NTF} {\exp_not:N\expandafter \exp_not:c{#1_#3_if_exist~i:w} ##1 \exp_not:n{#2 ab #2 c \tuple_end:}{##3}{##2}} \cs_new:cpn{#1_#3_if_exist~i:w} ##1 #2 ##2 ##3 #2 ##4 \tuple_end: {\tl_if_single:nTF{##4}} % \cs_new:cx{#1_#3_gdel:N} {\exp_not:N\expandafter \exp_not:c{#1_#3_gdel~i:w} ##1 \exp_not:n{#2 ab #2 c \tuple_end:}##1} \cs_new:cpn{#1_#3_gdel~i:w} ##1 #2 ##2 ##3 #2 ##4 \tuple_end:##5 {\tl_if_single:nTF{##4} {\tl_gset:Nn##5{##1}} {\tl_gset:Nn##5{##1##3}}} % \cs_new:cx{#1_#3_gapply:NN} {\exp_not:N\expandafter \exp_not:c{#1_#3_gapply~i:w} ##1 \exp_not:n{#2 ab #2 c \tuple_end:}##1##2} \cs_new:cpn{#1_#3_gapply~i:w} ##1 #2 ##2 ##3 #2 ##4 \tuple_end: ##5 ##6 {\tl_if_single:nTF{##4} {\tl_gset:Nx##5{\exp_not:n{##1 #2}{##6{#4}}}} {\tl_gset:Nx##5{\exp_not:n{##1 #2}{##6{##2}} \exp_not:n{##3}}}} } \cs_new:Nn\tuple_new_tl_index:nNnn {\tuple_new_index:nNnn{#1}#2{#3}{#4} % \cs_new:cx{#1_#3_gset:Nn} {\exp_not:N\expandafter \exp_not:c{#1_#3_gset~:w} ##1 \exp_not:n{#2 ab #2 c \tuple_end:}##1{##2}} \cs_new:cpn{#1_#3_gset~:w} ##1 #2 ##2 ##3 #2 ##4 \tuple_end: ##5 ##6 {\tl_if_single:nTF{##4} {\tl_gset:Nn##5{##1 #2{##6}}} {\tl_gset:Nn##5{##1 #2{##6} ##3}}} } \cs_new:Nn\tuple_new_tl_index:nnn {\exp_args:Nnc\tuple_new_tl_index:nNnn{#1}{TL_#1_#2_:n}{#2}{#3}} \cs_new:Nn\tuple_new_tl_index:nn {\tuple_new_tl_index:nnn{#1}{#2}{}} \cs_new:Nn\int_incr:n{\int_eval:n{#1+1}} \cs_new:Nn\int_decr:n{\int_eval:n{#1-1}} \cs_new:Nn\tuple_new_int_index:nNnn{ \tuple_new_index:nNnn{#1}#2{#3}{#4} % \cs_new:cx{#1_#3_get_int:NN} {\exp_not:n{\int_set:Nn} ##2 {\exp_not:c{#1_#3_use:N} \exp_not:N##1}} % \cs_new:cx{#1_#3_gincr:N} {\exp_not:c{#1_#3_gapply:NN}##1\exp_not:N\int_incr:n} % \cs_new:cx{#1_#3_gdecr:N} {\exp_not:c{#1_#3_gapply:NN}##1\exp_not:N\int_decr:n} % \cs_new:cx{#1_#3_gset:Nn} {\exp_not:N\expandafter \exp_not:c{#1_#3_gset~:w} ##1 \exp_not:n{#2 ab #2 c \tuple_end:}##1{##2}} \cs_new:cpn{#1_#3_gset~:w} ##1 #2 ##2 ##3 #2 ##4 \tuple_end: ##5 ##6 {\tl_if_single:nTF{##4} {\tl_gset:Nx##5{##1 \exp_not:N#2 {\int_eval:n{##6}}}} {\tl_gset:Nx##5{##1 \exp_not:N#2 {\int_eval:n{##6}} ##3}}} % \cs_new:cpx{#1_#3_case:NnTF}##1 {\exp_not:N\int_case:nnTF{\exp_not:c{#1_#3_eval:n}##1}} } \cs_new:Nn\tuple_new_int_index:nnn {\exp_args:Nnc\tuple_new_int_index:nNnn{#1}{INT_#1_#2_:n}{#2}{#3}} \cs_new:Nn\tuple_new_int_index:nn {\tuple_new_int_index:nnn{#1}{#2}{0}} % \end{macrocode} % % \begin{macrocode} %\cs_new:Nn\tuple_index_default_cs_set_eq:NN{ % \cs_set:Npn#1{\cs_set_eq:NN#2}} %\cs_new:Nn\tuple_index_default_tl_set:NN{ % \cs_set:Npn#1{\tl_set:Nn#2}} %\cs_new:Nn\tuple_index_default_tl_put_left:NN{ % \cs_set:Npn#1{\tl_put_left:Nn#2}} %\cs_new:Nn\tuple_index_default_tl_put_right:NN{ % \cs_set:Npn#1{\tl_put_right:Nn#2}} %\cs_new:Nn\tuple_index_default_int_set:NN{ % \int_new:N#2 % \cs_set:Npn#1{\int_set:Nn#2}} %\cs_new:Nn\tuple_index_default_bool_set:NN{ % \bool_new:N#2 % \cs_set:Npn#1##1{##1#2}} %\cs_new:Nn\tuple_index_default_ignore:N{ % \cs_set:Npn#1##1{}} %\cs_new:Nn\tuple_index_default_show:N{ % \cs_set_eq:NN#1\tl_show:n} % \end{macrocode} % % % \subsection{Variables encoding} % % We use an automatic generator of variables, of different types. % For global \texttt{tl} variables, the command is |\NewGTl| %\begin{verbatim} %\NewGTl{variable}[default value]{arguments}[identifier] %\end{verbatim} % The arguments satisfy the syntax of \texttt{xparse}, and the identifier uses these arguments for % creating a token list that can be used in a csname. % The result of this command is that new commands |\variableIfTF|, |\variableSet|, % |\variableSetX|, |\variableGet|, |\variableApply|, |\variableApplyInline| and |\variableIfEmpty| % are created. % For instance %\begin{verbatim} % \NewGTl{Test}{ m o }[test default]{\IfNoValue{#1}{#2}{#2-#1}} %\end{verbatim} % Gives right to |\TestSet{first}[1]{success}| % |\TestApplyInline{first}[1]{The test was a #1!}|. % % % % \begin{macrocode} \cs_new:Nn\int_get_default:N {\int_if_exist:NTF#1{\int_use:N#1}{0}} \cs_generate_variant:Nn\int_get_default:N{c} \cs_new:Nn\int_force:N {\int_if_exist:NTF#1{}{\int_new:N#1}} \cs_new:Npn\int_force_gset:Nn#1 {\int_force:N#1\int_gset:Nn#1} % \end{macrocode} % % % \begin{macrocode} \NewDocumentCommand\NewGBool{ m m o }{ \IfNoValueTF{#3}{ \tl_gset:Nn\g_tmpa_tl{#2} \tl_remove_all:Nn\g_tmpa_tl{~} \int_case:nnTF{\tl_count:N\g_tmpa_tl}{ 0 {\NewGBoolComplete {#1}{#2}{#1:_bool}} 1 {\NewGBoolComplete {#1}{#2}{#1:##1_bool}} 2 {\NewGBoolComplete {#1}{#2}{#1:##1...##2_bool}} 3 {\NewGBoolComplete {#1}{#2}{#1:##1...##2...##3_bool}} 4 {\NewGBoolComplete {#1}{#2}{#1:##1...##2...##3...##4_bool}} 5 {\NewGBoolComplete {#1}{#2}{#1:##1...##2...##3...##4...##5_bool}} 6 {\NewGBoolComplete {#1}{#2}{#1:##1...##2...##3...##4...##5...##6_bool}} 7 {\NewGBoolComplete {#1}{#2}{#1:##1...##2...##3...##4...##5...##6...##7_bool}} 8 {\NewGBoolComplete {#1}{#2}{#1:##1...##2...##3...##4...##5...##6...##7...##8_bool}} 9 {\NewGBoolComplete {#1}{#2}{#1:##1...##2...##3...##4...##5...##6...##7...##8...##9_bool}} }{}{\ERROR}}{\NewGBoolComplete{#1}{#2}{#3}}} % \end{macrocode} % % \begin{macrocode} \NewDocumentCommand\NewGBoolComplete{ m m m }{ \exp_args:Nc\NewDocumentCommand{#1SetTrue}{#2} {\bool_gset_true:c{#3}} \exp_args:Nc\NewDocumentCommand{#1SetFalse}{#2} {\bool_gset_false:c{#3}} \exp_args:Nc\NewDocumentCommand{#1Set}{#2} {\exp_args:Nc\bool_gset_tl:Nn{#3}} \exp_args:Nc\DeclareExpandableDocumentCommand{#1IfTF}{#2} {\bool_if:cTF{#3}} } \cs_new:Nn\bool_gset_tl:Nn {\use:c{bool_gset_#2:N}#1} % \end{macrocode} % % % % \begin{macrocode} \NewDocumentCommand\NewGCs{ m m o }{ \IfNoValueTF{#3}{ \tl_gset:Nn\g_tmpa_tl{#2} \tl_remove_all:Nn\g_tmpa_tl{~} \int_case:nnTF{\tl_count:N\g_tmpa_tl}{ 0 {\NewGCsComplete {#1}{#2}{#1_:}} 1 {\NewGCsComplete {#1}{#2}{#1_##1_cs:w}} 2 {\NewGCsComplete {#1}{#2}{#1_##1...##2_cs:w}} 3 {\NewGCsComplete {#1}{#2}{#1_##1...##2...##3_cs:w}} 4 {\NewGCsComplete {#1}{#2}{#1_##1...##2...##3...##4_cs:w}} 5 {\NewGCsComplete {#1}{#2}{#1_##1...##2...##3...##4...##5_cs:w}} 6 {\NewGCsComplete {#1}{#2}{#1_##1...##2...##3...##4...##5...##6_cs:w}} 7 {\NewGCsComplete {#1}{#2}{#1_##1...##2...##3...##4...##5...##6...##7_cs:w}} 8 {\NewGCsComplete {#1}{#2}{#1_##1...##2...##3...##4...##5...##6...##7...##8_cs:w}} 9 {\NewGCsComplete {#1}{#2}{#1_##1...##2...##3...##4...##5...##6...##7...##8...##9_cs:w}} }{}{\ERROR}}{\NewGCsComplete{#1}{#2}{#3}}} % \end{macrocode} % % \begin{macrocode} \NewDocumentCommand\NewGCsComplete{ m m m }{} % \exp_args:Nc\NewDocumentCommand{#1New_internal}{ m m m } % {\NewDocumentCommand##1{ m ##2}{##3} } % \exp_args:Nc\NewDocumentCommand{#1New}{#2} % {\cs_new:cn{#2}{#2_internal{#2}} % \exp_args:Nc{#2_internal}} % \end{macrocode} % % % % % % \subsection{The \texttt{knowledge} file handling} % % % The knowledge package uses two macros for file handling: %\begin{itemize} %\item |\kl_copy_file:nn|(|nV|, |Vn|, |VV|) takes the name of two files % and copies the first one to the second (does nothing if the first file does not exist), %\item |kl_compare_files:Nnn|(|NnV|, |NVn|, |NVV|) takes a boolean and the name of two files, % and sets the boolean (locally) according to the result of their comparison for equality, %\item |\kl_tl_to_file:nn| takes the name of a file, and a token list, and creates the file with the tl as content. % This is a conveninent way to create empty files (since deleting files is not possible with \kl{TeX}...) %\end{itemize} % % \begin{macrocode} \cs_new_protected_with_variants:Nnn\kl_copy_file:nn{nV,Vn,VV} {\file_if_exist:nT{#1} {\group_begin: \bool_set_false:N\l_tmpa_bool \ior_open:Nn\klg_tmpa_ior{#1} \iow_open:Nn\klg_tmpa_iow{#2} \bool_do_until:Nn\l_tmpa_bool{ \ior_if_eof:NTF\klg_tmpa_ior {\bool_set_true:N\l_tmpa_bool} {\ior_str_get:NN \klg_tmpa_ior \l_tmpa_tl \exp_args:NNV \iow_now:Nn \klg_tmpa_iow \l_tmpa_tl} } \ior_close:N\klg_tmpa_ior \iow_close:N\klg_tmpa_iow \group_end:}} % \end{macrocode} % \begin{macrocode} \cs_new_protected_with_variants:Nnn\kl_compare_files:Nnn{NnV,NVn,NVV} {\group_begin: \bool_set_false:N\l_tmpa_bool% set when finished \bool_set_true:N\l_tmpb_bool% result \file_if_exist:nTF{#2} {\file_if_exist:nF{#3} {\bool_set_true:N\l_tmpa_bool \bool_set_false:N\l_tmpb_bool}} {\bool_set_true:N\l_tmpa_bool \file_if_exist:nT{#3} {\bool_set_false:N\l_tmpb_bool}} \bool_if:NF\l_tmpa_bool{ \ior_open:Nn\klg_tmpa_ior{#2} \ior_open:Nn\klg_tmpb_ior{#3} % \bool_do_until:Nn\l_tmpa_bool{ \ior_if_eof:NTF\klg_tmpa_ior {\bool_set_true:N\l_tmpa_bool \ior_if_eof:NF\klg_tmpb_ior {\bool_set_false:N\l_tmp_bool}} {\ior_if_eof:NTF\klg_tmpb_ior {\bool_set_true:N\l_tmpa_bool \bool_set_false:N\l_tmp_bool} {\ior_str_get:NN \klg_tmpa_ior \l_tmpa_tl \ior_str_get:NN \klg_tmpb_ior \l_tmpb_tl \tl_if_eq:NNF \l_tmpa_tl\l_tmpb_tl {\bool_set_true:N\l_tmpa_bool \bool_set_false:N\l_tmpb_bool} }} } \ior_close:N\klg_tmpa_ior \ior_close:N\klg_tmpb_ior} \bool_if:NTF\l_tmpb_bool {\group_end:\bool_set_true:N#1} {\group_end:\bool_set_false:N#1} } % \end{macrocode} % \begin{macrocode} \cs_new_protected:Nn\kl_tl_to_file:nn {\iow_open:Nn\klg_tmpa_iow{#1} \iow_now:Nn\klg_tmpa_iow{#2} \iow_close:N\klg_tmpa_iow} % \end{macrocode} % % These macros are used for testing the modification of knowledges. % At the begining, jobname.knowledge.aux is transferred to jobname.knowledge.tmp. % During the file reading, the file jobname.knowledge.aux is again written. % At the end, the two are compared. % % \begin{macrocode} \ior_new:N\klg_tmpa_ior \ior_new:N\klg_tmpb_ior \iow_new:N\klg_tmpa_iow % \end{macrocode} % % % % % % \subsection{Dealing with LaTeX commands} % % % We define now a certain number of commands that allows to % \begin{itemize} % \item Store known commands with a protected name. % \item Declare commands as obsolete when used in a document. % \item Create variants using \textsc{xsparse} that remove the hassle of optional parameters/stars, and so on. % \end{itemize} % % % \subsubsection{Overloading command} % Overloading a command is similar in syntax to |\NewDocumentCommand| % of the \texttt{xparse} package, but it further offers the macro |\SUPERcommand| % (where |\command| is the token under definition), which can be used for calling the original code. % Formally, the syntax is:\\ % |\OverloadCommand\command{arguments}[variant name]{code}|\\ % The arguments follox the \texttt{xparse} syntax. The variant name is by default ``\texttt{NEW}''. % Other variants of the commands can be defined by changing this text. % The macro |\ChooseCommand| allows to choose between the variant. It takes as first argument a control sequence (or a chain of control sequences), and as second argument the variant that should be taken. % Hence |\ChooseCommand\command{SUPER}| reverts |\command| to its original behaviour. % \begin{macrocode} \cs_new:Npn\commandVariant#1#2{#2\cs_to_str:N#1} \cs_generate_variant:Nn\cs_gset_eq:NN{cN,cc} \NewDocumentCommand\OverloadCommand{mmO{NEW}m} {\cs_if_exist:cTF {\commandVariant#1{switch~}} {} {\exp_args:Nc\cs_gset_eq:NN {\commandVariant#1{SUPER}} #1 \exp_args:NNx\cs_gset:Npn #1{\exp_not:c{\commandVariant#1{switch~}}} } \exp_args:Nc\NewDocumentCommand {\commandVariant#1{#3}}{#2}{#4} \ChooseCommand#1{#3} } \NewDocumentCommand\ChooseCommand{mm}{ \tl_map_inline:nn{#1} {\cs_if_exist:cTF {\commandVariant##1{#2}} {\cs_gset_eq:cc {\commandVariant##1{switch~}} {\commandVariant##1{#2}}} {\tl_show:n {knowledge_utils~ERROR:~##1~has~no~variant~{#2}}} } } % \end{macrocode} % % % % \subsubsection{Xparse reversing} % The package \texttt{xparse} offers a convenient way for describing the profile of parameters % of new commands (optional star, optional parameters, and so on) % We provide converse commands, that recreates these arguments. % For instance, the command |\section| is supposed to have an optional star (numbering or not), followed by an optional argument (title in the toc), followed by a mandatory argument (the title of the section). % This would be defined with \texttt{xparse} as |\NewDocumentCommand\section{som}{...code...}|, % where \texttt{som} means star-optional-mandatory. % Imagine you want to overload this command. The command |\XParseArgs| is meant for that. % One first stores the old command |\section| as |\oldsection|: %\begin{verbatim} % \let\oldsection\section %\end{verbatim} % and then redefine it, calling in the code the old version: % \begin{verbatim} % \RenewDocumentCommand\section{som}{% % % % \XparseArgs\oldsection{som}{#1}{#2}{#3} % } % \end{verbatim} % (please refer to \texttt{xparse} documentation for knowing how to use the arguments % in the pre- and post-code) % % The command |\ExpXparseArgs| is the expandable variant of it (I was unable to produce % a version that would work in both situations !). % % For parameter types treated so far are: % \begin{compactdesc} % \item[i]= ignore % \item[m]= mandatory % \item[o]= optional (under square brackets) % \item[s]= stars % \end{compactdesc} % \begin{macrocode} \NewDocumentCommand\XparseArgs{mm} {\kutils_reverting_args:Nn#1{#2}} \DeclareExpandableDocumentCommand\ExpXparseArgs{mm} {\kutils_exec_xreverting_args:Nn#1{#2}} % \end{macrocode} % \begin{macrocode} \cs_new:Nn\kutils_xrevert_arg_s:n { \IfBooleanTF{#1}{*}{}} \cs_new:Nn\kutils_xrevert_arg_o:n { \IfValueTF{#1}{[\exp_not:n{#1}]}{}} \cs_new:Nn\kutils_xrevert_arg_m:n { {\exp_not:n{#1}} } \cs_new:Nn\kutils_xrevert_arg_i:n { } % \end{macrocode} % \begin{macrocode} \cs_new:Nn\kutils_reverting_args:Nn{ \use:c{kutils_process_\tl_count:n{#2}:w} #1{\kutils_exec_xreverting_args:n{#2}}} % \end{macrocode} % \begin{macrocode} \cs_new:Nn\kutils_exec_xreverting_args:n{ \tl_if_empty:nTF{#1}{} {\kutils_exec_xreverting_args_internal:w#1\kutils_end:}} \cs_new:Nn\kutils_exec_xreverting_args:Nn{ \exp_not:N#1 \tl_if_empty:nTF{#2}{} {\kutils_exec_xreverting_args_internal:w#2\kutils_end:}} \cs_new:Npn\kutils_exec_xreverting_args_internal:w#1#2\kutils_end:#3 { \use:c{kutils_xrevert_arg_#1:n}{#3} \tl_if_empty:nTF{#2}{} {\kutils_exec_xreverting_args_internal:w#2\kutils_end:}} % \end{macrocode} % The following internal commands |\cs_new:Nn\kutils_process_args:nn...| eat a token list `|\com|', % then an expandable command token list |\proc|, that is totally expanded with as arguments the following arguments (the number of which is fixed from the signature). % In the end, |\com| is prefixed. % Hence |\kutils_process_args:nnnn\com\proc a b| % completely expands |{\proc a b}| yielding some `result', and then executes |\com| followed by the `result'. % E.g. if |\swap#1#2| is |{#2}{#1}|, then |\kutils_process_args:nnnn\com\swap| % expands to the same as |\com| but with arguments reversed. % \begin{macrocode} \cs_new:Nn\kutils_process_args:nn {#1} \cs_new:Nn\kutils_process_args:nnn {\use:nx{#1}{#2{#3}}} \cs_new:Nn\kutils_process_args:nnnn {\use:nx{#1}{#2{#3}{#4}}} \cs_new:Nn\kutils_process_args:nnnnn {\use:nx{#1}{#2{#3}{#4}{#5}}} \cs_new:Nn\kutils_process_args:nnnnnn {\use:nx{#1}{#2{#3}{#4}{#5}{#6}}} \cs_new:Nn\kutils_process_args:nnnnnnn {\use:nx{#1}{#2{#3}{#4}{#5}{#6}{#7}}} \cs_new:Nn\kutils_process_args:nnnnnnnn {\use:nx{#1}{#2{#3}{#4}{#5}{#6}{#7}{#8}}} \cs_new:Nn\kutils_process_args:nnnnnnnnn {\use:nx{#1}{#2{#3}{#4}{#5}{#6}{#7}{#8}{#9}}} \exp_args:Nc\let{kutils_process_0:w}\kutils_process_args:nn \exp_args:Nc\let{kutils_process_1:w}\kutils_process_args:nnn \exp_args:Nc\let{kutils_process_2:w}\kutils_process_args:nnnn \exp_args:Nc\let{kutils_process_3:w}\kutils_process_args:nnnnn \exp_args:Nc\let{kutils_process_4:w}\kutils_process_args:nnnnnn \exp_args:Nc\let{kutils_process_5:w}\kutils_process_args:nnnnnnn \exp_args:Nc\let{kutils_process_6:w}\kutils_process_args:nnnnnnnn \exp_args:Nc\let{kutils_process_7:w}\kutils_process_args:nnnnnnnnn % \end{macrocode} % % \subsubsection{Obsolescence and vaulting} % % \begin{macrocode} \msg_new:nnn{scope}{LaTeX~obsolete} {The~command~'#1'~is~deprecated.~ See~documentation~for~a~replacement.} \cs_new:Nn\kutils_secure_latex_command:N {\cs_new_eq:cN{LaTeX\cs_to_str:N#1}#1} \cs_new:Nn\kutils_latex_to_vault:N {\kutils_secure_latex_command:N#1 \cs_undefine:N#1} \cs_new:Nn\kutils_latex_to_obsolete:N {\kutils_latex_to_vault:N#1 \cs_new:Npx#1{\exp_not:N\msg_error:nnn{scope}{LaTeX~obsolete}{\string #1}}} \cs_new:Nn\kutils_secure_latex_command:n {\tl_map_function:nN{#1}\kutils_secure_latex_command:N} \cs_new:Nn\kutils_latex_to_vault:n {\tl_map_function:nN{#1}\kutils_latex_to_vault:N} \cs_new:Nn\kutils_latex_to_obsolete:n {\tl_map_function:nN{#1}\kutils_latex_to_obsolete:N} % \end{macrocode} % % % % % \Finale \endinput