% \iffalse meta-comment % % File: ltx-talk-decode.dtx Copyright (C) 2023-2025 Joseph Wright % % 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 % % https://www.latex-project.org/lppl.txt % % This file is part of the "ltx-talk 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 % % https://github.com/josephwright/ltx-talk % % for those people who are interested. % % ----------------------------------------------------------------------- % %<*driver> \documentclass{l3doc} % Additional commands needed in this source \NewDocumentCommand\email{m}{\href{mailto:#1}{\nolinkurl{#1}}} \begin{document} \DocInput{\jobname.dtx} \end{document} % % \fi % % ^^A As we are dealing with a class, this has to be done manually % \def\filedate{2025-07-12} % \def\fileversion{v0.1.0} % % \title{^^A % \pkg{ltx-talk-decode} -- Decoding overlay specs^^A % \thanks{This file describes \fileversion, % last revised \filedate.}^^A % } % % \author{^^A % Joseph Wright^^A % \thanks{^^A % E-mail: \email{joseph@texdev.net}^^A % }^^A % } % % \date{Released \filedate} % % \maketitle % % \begin{documentation} % % \end{documentation} % % \begin{implementation} % % \section{\pkg{ltx-talk-decode} implementation} % % Start the \pkg{DocStrip} guards. % \begin{macrocode} %<*class> % \end{macrocode} % % Identify the internal prefix. % \begin{macrocode} %<@@=talk> % \end{macrocode} % % \begin{variable}{\l_@@_decode_overlays_bool} % The result from decoding: are we on the current slide. This may well be % better handled by moving to a |TF| signature: to be explored. % \begin{macrocode} \bool_new:N \l_@@_decode_overlays_bool % \end{macrocode} % \end{variable} % % \begin{variable}{\g_@@_pauses_int, \c@pauses, \thepauses} % The automatically-incremented value for the relative overlay value. % \begin{macrocode} \int_new:N \g_@@_pauses_int \cs_new_eq:NN \c@pauses \g_@@_pauses_int \cs_new:Npn \thepauses { \@arabic \g_@@_pauses_int } % \end{macrocode} % \end{variable} % % \begin{variable}{\l_@@_decode_pure_bool} % Tracks whether only mode specifications were given. % \begin{macrocode} \bool_new:N \l_@@_decode_pure_bool % \end{macrocode} % \end{variable} % % \begin{variable}{\l_@@_decode_step_bool} % Tracks whether to step \cs{g_@@_pauses_int}. % \begin{macrocode} \bool_new:N \l_@@_decode_step_bool % \end{macrocode} % \end{variable} % % \begin{variable}{\l_@@_decode_arg_str} % For error usage. % \begin{macrocode} \str_new:N \l_@@_decode_arg_str % \end{macrocode} % \end{variable} % % \begin{variable}{\l_@@_decode_overlays_clist, \l_@@_decode_overlays_str} % The decoded overlay specification: will have only absolute slide numbers % present, potentially along with ranges. % \begin{macrocode} \clist_new:N \l_@@_decode_overlays_clist \str_new:N \l_@@_decode_overlays_str % \end{macrocode} % \end{variable} % % \begin{variable}{\l_@@_decode_action_str} % The action which is active, if any. % \begin{macrocode} \str_new:N \l_@@_decode_action_str % \end{macrocode} % \end{variable} % % \begin{variable} % { % \l_@@_decode_actions_bool , % \l_@@_decode_actions_clist , % \l_@@_decode_actions_str % } % For the actions versions of overlay tracking. % \begin{macrocode} \bool_new:N \l_@@_decode_actions_bool \clist_new:N \l_@@_decode_actions_clist \str_new:N \l_@@_decode_actions_str % \end{macrocode} % \end{variable} % % \begin{macro}{\@@_decode_parse:n, \@@_decode_parse_aux:n} % \begin{macro}{\@@_decode_parse:w} % First a simple check for an entirely blank argument: if that's the case, % there is no additional overlay to consider. Then deal with any category % code issues before looping over blocks divided by \verb"|" tokens. % \begin{macrocode} \cs_new_protected:Npn \@@_decode_parse:n #1 { \str_clear:N \l_@@_decode_action_str \bool_lazy_or:nnTF { \tl_if_blank_p:n {#1} } { \str_if_eq_p:nn {#1} { all } } { \bool_set_true:N \l_@@_decode_overlays_bool } { \str_set:Nn \l_@@_decode_arg_str {#1} \bool_set_false:N \l_@@_decode_actions_bool \bool_set_false:N \l_@@_decode_overlays_bool \bool_set_true:N \l_@@_decode_pure_bool \str_clear:N \l_@@_decode_overlays_str \str_clear:N \l_@@_decode_actions_str \exp_args:No \@@_decode_parse_aux:n { \l_@@_decode_arg_str } } } \cs_new_protected:Npn \@@_decode_parse_aux:n #1 { \@@_decode_parse:w #1 | \q_recursion_tail | \q_recursion_stop } % \end{macrocode} % The end-of-loop test here covers the case where the active mode is not % mentioned at all in the specification. % \begin{macrocode} \cs_new_protected:Npn \@@_decode_parse:w #1 | { \quark_if_recursion_tail_stop_do:nn {#1} { \bool_lazy_and:nnT { \str_if_empty_p:N \l_@@_decode_overlays_str } { ! \l_@@_decode_pure_bool } { \bool_set_true:N \l_@@_decode_overlays_bool } } \exp_args:Ne \@@_decode_mode:n { \tl_trim_spaces:n {#1} } \@@_decode_parse:w } % \end{macrocode} % \end{macro} % \end{macro} % % \begin{variable}{\c_@@_modes_clist} % The possible modes: detokenized as that is applied up-front in decoding. % \begin{macrocode} \clist_const:Ne \c_@@_modes_clist { \tl_to_str:n { handout } , \tl_to_str:n { projector } } % \end{macrocode} % \end{variable} % % \begin{macro}{\@@_decode_mode:n} % \begin{macro}{\@@_decode_mode:w} % \begin{macro}{\@@_decode_mode_aux:n} % Check if the mode is known and current. If we find an action but have no % overlay details, they is filled in with a \verb|*|. % \begin{macrocode} \cs_new_protected:Npe \@@_decode_mode:n #1 { \clist_if_in:NnTF \exp_not:N \c_@@_modes_clist {#1} { \exp_not:N \str_if_eq:VnT \exp_not:N \l_@@_mode_str {#1} { \bool_set_true:N \exp_not:N \l_@@_decode_overlays_bool } } { \exp_not:N \@@_decode_mode:w #1 \tl_to_str:n { : : } \exp_not:N \q_stop } } \use:e { \cs_new_protected:Npe \exp_not:N \@@_decode_mode:w #1 \token_to_str:N : #2 \token_to_str:N : #3 \exp_not:N \q_stop } { \exp_not:N \tl_if_blank:nTF {#2} { \exp_not:N \@@_decode_mode:nn { \tl_to_str:n { projector } } {#1} } { \exp_not:N \@@_decode_mode:nn {#1} {#2} } } \cs_new_protected:Npn \@@_decode_mode:nn #1#2 { \str_if_eq:VnTF \l_@@_mode_str {#1} { \@@_decode_action:n {#2} \str_if_empty:NT \l_@@_decode_overlays_str { \@@_decode_overlays:nn { overlays } { * } } } { \tl_if_blank:nF {#2} { \bool_set_false:N \l_@@_decode_pure_bool } } } % \end{macrocode} % \end{macro} % \end{macro} % \end{macro} % % \begin{macro}{\@@_decode_action:n} % \begin{macro}{\@@_decode_action:w} % Here, we have two valid possibilities: no action specification at all, or % or from the known list. If we don't find one of those outcomes, we can % issue an error. % \begin{macrocode} \cs_new_protected:Npe \@@_decode_action:n #1 { \exp_not:N \@@_decode_action:w #1 \tl_to_str:n { @ @ } \exp_not:N \q_stop } \use:e { \cs_new_protected:Npn \exp_not:N \@@_decode_action:w #1 \tl_to_str:n { @ } #2 \tl_to_str:n { @ } #3 \exp_not:N \q_stop } { \tl_if_blank:nTF {#2} { \@@_decode_overlays:nn { overlays } {#1} } { \cs_if_exist:cTF { @@_action_ #1 :N } { \bool_set_false:N \l_@@_decode_pure_bool \str_set:Nn \l_@@_decode_action_str {#1} \tl_if_blank:nF {#2} { \@@_decode_overlays:nn { actions } {#2} } } { \msg_error:nnV { talk } { bad-action-spec } \l_@@_decode_arg_str } } } % \end{macrocode} % \end{macro} % \end{macro} % % \begin{macro}{\@@_decode_overlays:nn} % \begin{macro}{\@@_decode_overlays:nN} % \begin{macro}{\@_decode_overlay_+:nw, \@@_decode_overlay_.:nw} % \begin{macro}{\@@_decode_overlay_aux:nNN} % \begin{macro}{\@@_decode_overlay_offset:nNnN} % \begin{macro}{\@@_decode_overlay_offset:nNn} % The loop here needs to replace all |+| and |.| characters by the current % automatic value, allowing for any offsets. This step also needs to track % whether to increment the automatic value: true if a |+| is seen, false % otherwise. % \begin{macrocode} \cs_new_protected:Npn \@@_decode_overlays:nn #1#2 { \bool_set_false:N \l_@@_decode_step_bool \@@_decode_overlays:nN {#1} #2 \q_recursion_tail \q_recursion_stop \bool_if:NT \l_@@_decode_step_bool { \int_gincr:N \g_@@_pauses_int } \@@_decode_check:n {#1} } \cs_new_protected:Npn \@@_decode_overlays:nN #1#2 { \quark_if_recursion_tail_stop:N #2 \cs_if_exist_use:cF { @@_decode_overlay_ #2 :nw } { \str_put_right:cn { l_@@_decode_ #1 _str } {#2} \@@_decode_overlays:nN } {#1} } \cs_new_protected:cpn { @@_decode_overlay_+:nw } #1 { \bool_set_true:N \l_@@_decode_step_bool \@@_decode_overlay_aux:nNN {#1} 0 } \cs_new_protected:cpn { @@_decode_overlay_.:nw } #1 { \@@_decode_overlay_aux:nNN {#1} 1 } % \end{macrocode} % The look-ahead for an offset to a relative specification. If the % end-of-loop is reached, the value still needs to be inserted: to % share auxiliaries, that is done by using the same function as elsewhere, % so the end-of-loop markers are re-inserted. Otherwise, there is a check % to see if the next token is a |(|. % \begin{macrocode} \cs_new_protected:Npn \@@_decode_overlay_aux:nNN #1#2#3 { \quark_if_recursion_tail_stop_do:Nn #3 { \@@_decode_overlay_offset:nNn {#1} #2 { 0 } \q_recursion_tail \q_recursion_stop } \token_if_eq_meaning:NNTF #3 ( % ) { \@@_decode_overlay_offset:nNnN {#1} #2 { } } { \@@_decode_overlay_offset:nNn {#1} #2 { 0 } #3 } } % \end{macrocode} % For the end of an offset, any valid overlay specification must have a % closing |)|, so this time the end-of-loop case is an error. Otherwise % simply collect up tokens until the closing |)| is found. % \begin{macrocode} \cs_new_protected:Npn \@@_decode_overlay_offset:nNnN #1#2#3#4 { \quark_if_recursion_tail_stop_do:Nn #4 { \msg_error:nnV { talk } { bad-action-spec } \l_@@_decode_arg_str } % ( \token_if_eq_meaning:NNTF #4 ) { \@@_decode_overlay_offset:nNn {#1} #2 {#3} } { \@@_decode_overlay_offset:nNnN {#1} #2 {#3#4} } } % \end{macrocode} % Overlay values can never be negative: this is enforced here. % \begin{macrocode} \cs_new_protected:Npn \@@_decode_overlay_offset:nNn #1#2#3 { \str_put_right:ce { l_@@_decode_ #1 _str } { \int_max:nn { 0 } { #3 + \g_@@_pauses_int - #2 } } \@@_decode_overlays:nN {#1} } % \end{macrocode} % \end{macro} % \end{macro} % \end{macro} % \end{macro} % \end{macro} % \end{macro} % % \begin{macro}{\@@_decode_check:n} % \begin{macro}{\@@_decode_check:nw} % \begin{macro}{\@@_decode_check_single:nn} % \begin{macro}{\@@_decode_check_range:nnn} % At this stage we have a fully \enquote{written out} overlay specification, % and need to work out if the current slide is included. We need to look at % each entry in the comma-separated list to sort this out. First we filter % out the case of a |*|, then it's a question of working out whether each % entry is a single number or a range, and if the latter, whether it's % open at either the start or the end. % \begin{macrocode} \cs_new_protected:Npn \@@_decode_check:n #1 { \clist_set:cv { l_@@_decode_ #1 _clist } { l_@@_decode_ #1 _str } \clist_if_in:cnTF { l_@@_decode_ #1 _clist } { * } { \bool_set_true:c { l_@@_decode_ #1 _bool } } { \clist_map_inline:cn { l_@@_decode_ #1 _clist } { \@@_decode_check:nw {#1} 0 ##1 - - \q_stop } } } % \end{macrocode} % If |#3| is empty, both of the \enquote{filler} |-| tokens were consumed: we % have a single value. Otherwise there is a range: the setup above ensures % that there will be starting value in all cases, but there may not be an % end one. % \begin{macrocode} \cs_new_protected:Npn \@@_decode_check:nw #1#2 - #3 - #4 \q_stop { \tl_if_blank:nTF {#4} { \@@_decode_check_single:nn {#1} {#2} } { \tl_if_blank:nTF {#3} { \@@_decode_check_range:nnn {#1} {#2} { \c_max_int } } { \@@_decode_check_range:nnn {#1} {#2} {#3} } } } \cs_set_protected:Npn \@@_decode_check_single:nn #1#2 { \int_compare:nNnTF \g_@@_slide_int = {#2} { \bool_set_true:c { l_@@_decode_ #1 _bool } \clist_map_break: } { \int_compare:nNnT {#2} > \g_@@_slide_int { \bool_gset_true:N \g_@@_slide_continue_bool } } } % \end{macrocode} % TODO: In the following we might want to add a check whether the range was % given with |#2| being smaller than |#3|, to be decided upon. % \begin{macrocode} \cs_set_protected:Npn \@@_decode_check_range:nnn #1#2#3 { \int_compare:nNnF \g_@@_slide_int > {#3} { \int_compare:nNnTF \g_@@_slide_int < {#2} { \bool_gset_true:N \g_@@_slide_continue_bool } { \bool_set_true:c { l_@@_decode_ #1 _bool } \bool_lazy_and:nnT { \int_compare_p:nNn \g_@@_slide_int < {#3} } { \int_compare_p:nNn {#3} < \c_max_int } { \bool_gset_true:N \g_@@_slide_continue_bool } \clist_map_break: } } } % \end{macrocode} % \end{macro} % \end{macro} % \end{macro} % \end{macro} % % \begin{macrocode} \msg_new:nnnn { talk } { bad-action-spec } { Bad~overlay~specification~"#1". } { The~overlay~specification~given~doesn't~follow~the~pattern~described~in~ the~ltx-talk~manual:~it~has~been~ignored. } % \end{macrocode} % % \begin{macrocode} % % \end{macrocode} % % \end{implementation} % % \PrintIndex