% \iffalse % vim: set expandtab: % vim: set shiftwidth=2: % vim: set tabstop=2: % \fi % \iffalse meta-comment % % Copyright (C) 2026 by Lukas Heindl % --------------------------------------------------------------------------- % This work may be distributed and/or modified under the % conditions of the LaTeX Project Public License, either version 1.3c % 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.3c or later is part of all distributions of LaTeX % version 2008/05/04 or later. % % This work has the LPPL maintenance status `maintained'. % % The Current Maintainer of this work is Lukas Heindl. % % This work consists of all files listed in manifest.txt. % % \fi % % \iffalse %<*driver> \ProvidesFile{hexdumptikz-selector-matcher.dtx} % %\NeedsTeXFormat{LaTeX2e}[2022-06-01] % %<*driver> \begin{document} \DocInput{\jobname.dtx} \PrintChanges \PrintIndex \end{document} % % \changes{v0.0.0}{2026-05-14}{First draft} % % % \fi % % \iffalse %<*package> %<@@=hexdumptikz_selector_matcher> % \fi % % \maketitle % % \begin{abstract} % Matches parsed selector/style rules against coordinates and/or addresses. % \end{abstract} % % Identify the package and give the over all version information. % \begin{macrocode} \ProvidesExplPackage {hexdumptikz-selector-matcher} {2026-06-20} {1.0.1} {Match coordinates and addresses with speficied display rules} % \end{macrocode} % % Create a shortcut for obtaining the variable name of a variable in a context. % Only for being able to use \emph{@@} in the \emph{.dtx} file. % \begin{macrocode} \cs_new_eq:NN \@@_ctx_var:nn \__hexdumptikz_selector_ctx_var:nn % \end{macrocode} % % \subsection{Variables} % \begin{var}{\l_@@_range_state_int} % Functions checking whether an element is inside a given range need to return more than just true/false. % Thus, such functions use this (local) variable to return the decision. % It basically is an enum which works as follows: % \begin{itemize} % \item[-1] range has already passed % \item[+0] range is currently active % \item[+1] range is still ahead / did not start yet % \end{itemize} % \begin{macrocode} \int_new:N \l_@@_range_state_int % \end{macrocode} % \end{var} % \begin{var}{\l_@@_rule_id_int} % Scratch variable for storing the rule-id which is currently processed. % (avoids clobbering other scratch variables and makes the code more readable) % \begin{macrocode} \int_new:N \l_@@_rule_id_int % \end{macrocode} % \end{var} % \begin{var}{\l_@@_search_all_bool} % Decision variable whether all active rules should be searched. % Allows to specify the matcher should stop after finding the first active rule. % % Is used as a scratch variable in the public matcher functions (which then gets passed to the util-functions). % \begin{macrocode} \bool_new:N \l_@@_search_all_bool % \end{macrocode} % \end{var} % \begin{var}{\l_@@_match_loop_bool} % Whether to continue searching for a match. % \begin{macrocode} \bool_new:N \l_@@_match_loop_bool % \end{macrocode} % \end{var} % \begin{var}{\l_@@_active_tmp_seq} % Scratch variable for temporarily storing the set of active rules. % \begin{macrocode} \seq_new:N \l_@@_active_tmp_seq % \end{macrocode} % \end{var} % \subsection{Helpers} % \begin{fn}{\@@_match_mod:nnn} % Check a modulos-rule. % Such a rule checks $\mathbf{x} \bmod \mathbf{m} \overset{!}{=} \mathbf{r}$ % \begin{sideeffects} % \end{sideeffects} % \begin{args} % 1 & \ain & item / $\mathbf{x}$ \\ % 2 & \ain & modulos / $\mathbf{m}$ \\ % 3 & \ain & residue / $\mathbf{r}$ \\ % \end{args} % \begin{macrocode} \cs_generate_variant:Nn \int_show:n { e } \cs_generate_variant:Nn \int_mod:nn { en } \prg_new_protected_conditional:Npnn \@@_match_mod:nnn #1 #2 #3 { TF } { \int_compare:nNnTF { \int_mod:nn { #1 } { #2 } } = { #3 } { \prg_return_true: } { \prg_return_false: } } % \end{macrocode} % \end{fn} % % \begin{fn}{\@@_match_apply_style:Nn} % Applies/Appends the style of a specific rule to the current iterator state. % \begin{sideeffects} % \end{sideeffects} % \begin{args} % 1 & \ain & context-id \\ % 2 & \ain & rule-id \\ % \end{args} % \begin{macrocode} \cs_new_protected:Npn \@@_match_apply_style:Nn #1 #2 { \tl_if_empty:cTF { \@@_ctx_var:nn { #1 } { style_tl } } { \tl_gset:ce { \@@_ctx_var:nn { #1 } { style_tl } } { \seq_item:cn { \@@_ctx_var:nn { #1 } { style_seq } } { #2 } } } { \tl_gput_right:ce { \@@_ctx_var:nn { #1 } { style_tl } } { , \seq_item:cn { \@@_ctx_var:nn { #1 } { style_seq } } { #2 } } } } % \end{macrocode} % \end{fn} % % % \subsection{Predicate Checking} % % \begin{fn}{\@@_match_rule_predicate:NnnNn} % Check if the rule matches with respect to the predicate. % Does not check the range. % \begin{sideeffects} % \sclobber & \sdir & \texttt{l\_tmpa\_int} \\ % \sclobber & \sdir & \texttt{l\_tmpa\_tl} \\ % \end{sideeffects} % \begin{args} % 1 & \ain & context-id \\ % 2 & \ain & current y \\ % 3 & \ain & current x \\ % 4 & \ain & current address (currently unused) \\ % 5 & \ain & rule-id \\ % \end{args} % \begin{macrocode} \prg_new_protected_conditional:Npnn \@@_match_rule_predicate:NnnNn #1 #2 #3 #4 #5 { T, TF } { \int_set:Nn \l_tmpa_int { \seq_item:cn { \@@_ctx_var:nn { #1 } { pred_idx_seq } } { #5 } } \int_compare:nNnTF { \l_tmpa_int } = { 0 } { % \end{macrocode} % no predicate present $\to$ matches % \begin{macrocode} \prg_return_true: } { \tl_set:Ne \l_tmpa_tl { \seq_item:cV { \@@_ctx_var:nn { #1 } { pred_kind_seq } } \l_tmpa_int } \str_case:VnF \l_tmpa_tl { { mod } { \tl_set:Ne \l_tmpa_tl { \seq_item:cn { \@@_ctx_var:nn { #1 } { pred_axis_seq } } % noqa: S103 { \l_tmpa_int } } \int_set:Nn \l_tmpb_int { \seq_item:cn { \@@_ctx_var:nn { #1 } { pred_mod_seq } } % noqa: S103 { \l_tmpa_int } } \int_set:Nn \l_tmpa_int { \seq_item:cn { \@@_ctx_var:nn { #1 } { pred_res_seq } } % noqa: S103 { \l_tmpa_int } } \str_case:VnF \l_tmpa_tl { { x } { \@@_match_mod:nnnTF { #3 } { \l_tmpb_int } { \l_tmpa_int } { \prg_return_true: } { \prg_return_false: } } { y } { \@@_match_mod:nnnTF { #2 } { \l_tmpb_int } { \l_tmpa_int } { \prg_return_true: } { \prg_return_false: } } } { \prg_return_false: } } } { \msg_critical:nnV { hexdumptikz-selector } { unknown-predicate } \l_tmpa_tl } } } % \end{macrocode} % \end{fn} % % % \subsection{Coordinate/Address Checking} % % \begin{fn}{\@@_coord_lt:nnnn} % checks whether $a < b$ holds % \begin{sideeffects} % \end{sideeffects} % \begin{args} % 1 & \ain & $y_a$ \\ % 2 & \ain & $x_a$ \\ % 3 & \ain & $y_b$ \\ % 4 & \ain & $x_b$ \\ % \end{args} % \begin{macrocode} \prg_new_protected_conditional:Npnn \@@_coord_lt:nnnn #1 #2 #3 #4 { TF } { \int_compare:nNnTF { #1 } < { #3 } { \prg_return_true: } { \int_compare:nNnTF { #1 } > { #3 } { \prg_return_false: } { \int_compare:nNnTF { #2 } < { #4 } { \prg_return_true: } { \prg_return_false: } } } } % \end{macrocode} % \end{fn} % % \begin{fn}{\@@_coord_gt:nnnn} % checks whether $a > b$ holds % \begin{sideeffects} % \end{sideeffects} % \begin{args} % 1 & \ain & $y_a$ \\ % 2 & \ain & $x_a$ \\ % 3 & \ain & $y_b$ \\ % 4 & \ain & $x_b$ \\ % \end{args} % \begin{macrocode} \prg_new_protected_conditional:Npnn \@@_coord_gt:nnnn #1 #2 #3 #4 { TF } { \int_compare:nNnTF { #1 } > { #3 } { \prg_return_true: } { \int_compare:nNnTF { #1 } < { #3 } { \prg_return_false: } { \int_compare:nNnTF { #2 } > { #4 } { \prg_return_true: } { \prg_return_false: } } } } % \end{macrocode} % \end{fn} % % \begin{fn}{\@@_addr_x_lt:nnnn} % checks whether $a < b$ holds % \begin{sideeffects} % \end{sideeffects} % \begin{args} % 1 & \ain & $\text{addr}_a$ \\ % 2 & \ain & $x_a$ \\ % 3 & \ain & $\text{addr}_b$ \\ % 4 & \ain & $x_b$ \\ % \end{args} % \begin{macrocode} \prg_new_protected_conditional:Npnn \@@_addr_x_lt:nnnn #1 #2 #3 #4 { TF } { \str_compare:eNeTF { #1 } < { #3 } { \prg_return_true: } { \str_compare:eNeTF { #1 } > { #3 } { \prg_return_false: } { \int_compare:nNnTF { #2 } < { #4 } { \prg_return_true: } { \prg_return_false: } } } } % \end{macrocode} % \end{fn} % % \begin{fn}{\@@_addr_x_gt:nnnn} % checks whether $a > b$ holds % \begin{sideeffects} % \end{sideeffects} % \begin{args} % 1 & \ain & $\text{addr}_a$ \\ % 2 & \ain & $x_a$ \\ % 3 & \ain & $\text{addr}_b$ \\ % 4 & \ain & $x_b$ \\ % \end{args} % \begin{macrocode} \prg_new_protected_conditional:Npnn \@@_addr_x_gt:nnnn #1 #2 #3 #4 { TF } { \str_compare:eNeTF { #1 } > { #3 } { \prg_return_true: } { \str_compare:eNeTF { #1 } < { #3 } { \prg_return_false: } { \int_compare:nNnTF { #2 } > { #4 } { \prg_return_true: } { \prg_return_false: } } } } % \end{macrocode} % \end{fn} % % \begin{fn}{\@@_match_rule_idx:nnnn} % Match an index rule against the current item. % \begin{sideeffects} % \sclobber & \sdir & \texttt{l\_tmpa\_int} \\ % \sclobber & \sdir & \texttt{l\_tmpb\_int} \\ % \end{sideeffects} % \begin{args} % 1 & \ain & context-id \\ % 2 & \ain & $y$ \\ % 3 & \ain & $x$ \\ % 4 & \ain & rule-id \\ % - & \aout & \texttt{l\_@@\_range\_state\_int} % \end{args} % \begin{macrocode} \cs_new_protected:Npn \@@_match_rule_idx:nnnn #1 #2 #3 #4 { \int_set:Nn \l_tmpa_int { \seq_item:cn { \@@_ctx_var:nn { #1 } { idx_low_y_seq } } { #4 } } \int_set:Nn \l_tmpb_int { \seq_item:cn { \@@_ctx_var:nn { #1 } { idx_low_x_seq } } { #4 } } \@@_coord_lt:nnnnTF { #2 } { #3 } { \l_tmpa_int } { \l_tmpb_int } { % \end{macrocode} % range is still ahead % \begin{macrocode} \int_set:Nn \l_@@_range_state_int { +1 } } { \int_set:Nn \l_tmpa_int { \seq_item:cn { \@@_ctx_var:nn { #1 } { idx_high_y_seq } } { #4 } } \int_set:Nn \l_tmpb_int { \seq_item:cn { \@@_ctx_var:nn { #1 } { idx_high_x_seq } } { #4 } } \@@_coord_gt:nnnnTF { #2 } { #3 } { \l_tmpa_int } { \l_tmpb_int } { % \end{macrocode} % range has already passed % \begin{macrocode} \int_set:Nn \l_@@_range_state_int { -1 } } { \int_set:Nn \l_@@_range_state_int { 0 } } } } % \end{macrocode} % \end{fn} % % \begin{fn}{\@@_match_rule_addr:nnnn} % Match an address rule against the current item. % \begin{sideeffects} % \sclobber & \sdir & \texttt{l\_tmpa\_tl} \\ % \sclobber & \sdir & \texttt{l\_tmpa\_int} \\ % \end{sideeffects} % \begin{args} % 1 & \ain & context-id \\ % 2 & \ain & address \\ % 3 & \ain & $x$ \\ % 4 & \ain & rule-id \\ % - & \aout & \texttt{l\_@@\_range\_state\_int} % \end{args} % \begin{macrocode} \cs_new_protected:Npn \@@_match_rule_addr:nnnn #1 #2 #3 #4 { \tl_set:Ne \l_tmpa_tl { \seq_item:cn { \@@_ctx_var:nn { #1 } { addr_low_seq } } { #4 } } \int_set:Nn \l_tmpa_int { \seq_item:cn { \@@_ctx_var:nn { #1 } { idx_low_x_seq } } { #4 } } \@@_addr_x_lt:nnnnTF { #2 } { #3 } { \l_tmpa_tl } { \l_tmpa_int } { % \end{macrocode} % range is still ahead % \begin{macrocode} \int_set:Nn \l_@@_range_state_int { +1 } } { \tl_set:Ne \l_tmpa_tl { \seq_item:cn { \@@_ctx_var:nn { #1 } { addr_high_seq } } { #4 } } \int_set:Nn \l_tmpa_int { \seq_item:cn { \@@_ctx_var:nn { #1 } { idx_high_x_seq } } { #4 } } \@@_addr_x_gt:nnnnTF { #2 } { #3 } { \l_tmpa_tl } { \l_tmpa_int } { % \end{macrocode} % range has already passed % \begin{macrocode} \int_set:Nn \l_@@_range_state_int { -1 } } { \int_set:Nn \l_@@_range_state_int { 0 } } } } % \end{macrocode} % \end{fn} % % \subsection{Iteration State Updates} % % \begin{fn}{\@@_match_rule_state:NnnNn} % Matches an entire rule against the current position/address and checks if the rule is active, still ahead or already past. % \begin{sideeffects} % \sclobber & \sdir & \texttt{l\_tmpa\_tl} \\ % \end{sideeffects} % \begin{args} % 1 & \ain & context-id \\ % 2 & \ain & $y$ \\ % 3 & \ain & $x$ \\ % 4 & \ain & address \\ % 5 & \ain & rule-id \\ % - & \aout & \texttt{l\_@@\_range\_state\_int} % \end{args} % \begin{macrocode} \cs_new_protected:Npn \@@_match_rule_state:NnnNn #1 #2 #3 #4 #5 { \tl_set:Ne \l_tmpa_tl { \seq_item:cn { \@@_ctx_var:nn { #1 } { kind_seq } } { #5 } } \str_case:VnF \l_tmpa_tl { { gap } { % \end{macrocode} % range already \enquote{passed} (in a sense this range/rule's processing is already done) % \begin{macrocode} \int_set:Nn \l_@@_range_state_int { -1 } } { addr } { \@@_match_rule_addr:nnnn { #1 } { #4 } { #3 } { #5 } } { idx } { \@@_match_rule_idx:nnnn { #1 } { #2 } { #3 } { #5 } } } { \msg_critical:nnV { hexdumptikz-selector } { invalid-rule } \l_tmpa_tl } } % \end{macrocode} % \end{fn} % % \begin{fn}{\@@_match_add_active_rules:NnnNN} % Search linearly for rules which should become active and add them to the context's \texttt{active\_rules\_seq}. % Note the search starts at \texttt{next\_rule\_int} so the rules must be in ascending order. % \begin{sideeffects} % \sclobber & \sdir & \texttt{l\_tmpa\_tl} \\ % \sclobber & \sdir & \texttt{l\_@@\_match\_loop\_bool} \\ % \end{sideeffects} % \begin{args} % 1 & \ain & context-id \\ % 2 & \ain & $y$ \\ % 3 & \ain & $x$ \\ % 4 & \ain & address \\ % 5 & \ain & bool whether all rules should be added or only a single one \\ % \end{args} % \begin{macrocode} \cs_new_protected:Npn \@@_match_add_active_rules:NnnNN #1 #2 #3 #4 #5 { % \end{macrocode} % add next rules that already started % \begin{macrocode} \bool_set_true:N \l_@@_match_loop_bool \bool_while_do:Nn \l_@@_match_loop_bool { \int_compare:nNnTF { \int_use:c { \@@_ctx_var:nn { #1 } { next_rule_int } } } > { \seq_count:c { \@@_ctx_var:nn { #1 } { kind_seq } } } { \bool_set_false:N \l_@@_match_loop_bool } { \tl_set:Ne \l_tmpa_tl { \seq_item:cn { \@@_ctx_var:nn { #1 } { kind_seq } } { \int_use:c { \@@_ctx_var:nn { #1 } { next_rule_int } } } } \str_case:VnF \l_tmpa_tl { { gap } { % \end{macrocode} % gaps are not active rules; just skip them here % \begin{macrocode} \int_gincr:c { \@@_ctx_var:nn { #1 } { next_rule_int } } \int_gincr:c { \@@_ctx_var:nn { #1 } { gap_cnt_int } } } } { \int_set:Nn \l_@@_rule_id_int { \int_use:c { \@@_ctx_var:nn { #1 } { next_rule_int } } } \@@_match_rule_state:NnnNn #1 { #2 } { #3 } #4 { \l_@@_rule_id_int } \int_case:nn { \l_@@_range_state_int } { { -1 } { % \end{macrocode} % range already passed $\to$ continue searching % \begin{macrocode} \int_gincr:c { \@@_ctx_var:nn { #1 } { next_rule_int } } } { 0 } { % \end{macrocode} % range is active $\to$ add and continue searching % \begin{macrocode} \seq_gput_right:ce { \@@_ctx_var:nn { #1 } { active_rules_seq } } { \int_use:N \l_@@_rule_id_int } \bool_if:NTF #5 { % \end{macrocode} % continue searching % \begin{macrocode} \int_gincr:c { \@@_ctx_var:nn { #1 } { next_rule_int } } } { % \end{macrocode} % stop after first found % \begin{macrocode} \int_gincr:c { \@@_ctx_var:nn { #1 } { next_rule_int } } \bool_set_false:N \l_@@_match_loop_bool } } { +1 } { % \end{macrocode} % range is still ahead $\to$ stop % \begin{macrocode} \bool_set_false:N \l_@@_match_loop_bool } } } } } } % \end{macrocode} % \end{fn} % % \begin{fn}{\@@_match_update_active_rules:NnnN} % Update the set of currently active rules in the context. % This means rules which are not valid anymore get removed and newly activated rules get searched for and get added. % \begin{sideeffects} % \sclobber & \sdir & \texttt{l\_@@\_active\_tmp\_seq} \\ % \end{sideeffects} % \begin{args} % 1 & \ain & context-id \\ % 2 & \ain & $y$ \\ % 3 & \ain & $x$ \\ % 4 & \ain & address \\ % \end{args} % \begin{macrocode} \cs_new_protected:Npn \@@_match_update_active_rules:NnnN #1 #2 #3 #4 { \seq_clear:N \l_@@_active_tmp_seq % \end{macrocode} % % remove rules that are no longer active % % TOOD: can this be done in a more performant way? (than by copying the sequence over) % \begin{macrocode} \seq_map_inline:cn { \@@_ctx_var:nn { #1 } { active_rules_seq } } { \@@_match_rule_state:NnnNn #1 { #2 } { #3 } #4 { ##1 } \int_compare:nNnT { \l_@@_range_state_int } = { 0 } { \seq_put_right:Nn \l_@@_active_tmp_seq { ##1 } } } \seq_gset_eq:cN { \@@_ctx_var:nn { #1 } { active_rules_seq } } \l_@@_active_tmp_seq % \end{macrocode} % search for rules which shall be added and add them % \begin{macrocode} \bool_set_true:N \l_@@_search_all_bool \@@_match_add_active_rules:NnnNN #1 { #2 } { #3 } #4 \l_@@_search_all_bool } % \end{macrocode} % \end{fn} % % \begin{fn}{\@@_match_update_active_rules_fastpath:NnnN} % fast-path when only one element in the active set is allowed % \begin{sideeffects} % \sclobber & \sdir & \texttt{l\_@@\_search\_all\_bool} \\ % \end{sideeffects} % \begin{args} % 1 & \ain & context-id \\ % 2 & \ain & $y$ \\ % 3 & \ain & $x$ \\ % 4 & \ain & address \\ % \end{args} % \begin{macrocode} \cs_new_protected:Npn \@@_match_update_active_rules_fastpath:NnnN #1 #2 #3 #4 { % \end{macrocode} % check whether the current rule shall be removed % \begin{macrocode} \int_compare:nNnT { \seq_count:c { \@@_ctx_var:nn { #1 } { active_rules_seq } } } > { 0 } { \int_set:Nn \l_@@_rule_id_int { \seq_item:cn { \@@_ctx_var:nn { #1 } { active_rules_seq } } { 1 } } \@@_match_rule_state:NnnNn #1 { #2 } { #3 } #4 { \l_@@_rule_id_int } \int_compare:nNnF { \l_@@_range_state_int } = { 0 } { \seq_gclear:c { \@@_ctx_var:nn { #1 } { active_rules_seq } } } } % \end{macrocode} % check if has to search for the next rule whose range fits, if so do it and add it % \begin{macrocode} \bool_set_false:N \l_@@_search_all_bool \int_compare:nNnF { \seq_count:c { \@@_ctx_var:nn { #1 } { active_rules_seq } } } > { 0 } { \@@_match_add_active_rules:NnnNN #1 { #2 } { #3 } #4 \l_@@_search_all_bool } } % \end{macrocode} % \end{fn} % % \subsection{Public} % % \begin{fn}{\hexdumptikz_selector_match_styles:NnnN} % Match the current position/address against the list of rules (adjusts the context's iterator output state). % Intended when the selector specifies a style mapping. % This supports overlapping ranges. % Still the rules needs to be in ascending order, sorted by the start of the respective range. % \begin{sideeffects} % \end{sideeffects} % \begin{args} % 1 & \ain & context-id \\ % 2 & \ain & $y$ \\ % 3 & \ain & $x$ \\ % 4 & \ain & address \\ % \end{args} % \begin{macrocode} \cs_new_protected:Npn \hexdumptikz_selector_match_styles:NnnN #1 #2 #3 #4 { % \end{macrocode} % First clean the iterator output % \begin{macrocode} \hexdumptikz_selector_ctx_clear_iter_out:N #1 % \end{macrocode} % Update the set of active rules (remove and add if needed) % \begin{macrocode} \@@_match_update_active_rules:NnnN #1 { #2 } { #3 } #4 % \end{macrocode} % % collect the style which shall be applied from the set of active rules after filtering them with the corresponding predicate. % \begin{macrocode} \seq_map_inline:cn { \@@_ctx_var:nn { #1 } { active_rules_seq } } { \@@_match_rule_predicate:NnnNnT #1 { #2 } { #3 } #4 { ##1 } { \@@_match_apply_style:Nn #1 { ##1 } \bool_gset_true:c { \@@_ctx_var:nn { #1 } { show_bool } } } } } % \end{macrocode} % \end{fn} % % \begin{fn}{\hexdumptikz_selector_match_rows:NnnN} % Match the current position/address against the list of rules (adjusts the context's iterator output state). % Intended when the selector is a raw specification of the selection. % This does NOT support overlapping ranges. % The rules needs to be in ascending order, sorted by the start of the respective range. % \begin{sideeffects} % \end{sideeffects} % \begin{args} % 1 & \ain & context-id \\ % 2 & \ain & $y$ \\ % 3 & \ain & $x$ \\ % 4 & \ain & address \\ % \end{args} % \begin{macrocode} \cs_new_protected:Npn \hexdumptikz_selector_match_rows:NnnN #1 #2 #3 #4 { % \end{macrocode} % First clean the iterator output % \begin{macrocode} \hexdumptikz_selector_ctx_clear_iter_out:N #1 % \end{macrocode} % Update the set of active rules (remove and add if needed) % Use the fast-path since this needs to only support a single active rule/range. % \begin{macrocode} \@@_match_update_active_rules_fastpath:NnnN #1 { #2 } { #3 } #4 % \end{macrocode} % % check if all rules are already passed $\to$ caller might want to stop at this point % \begin{macrocode} \seq_if_empty:cTF { \@@_ctx_var:nn { #1 } { active_rules_seq } } { \int_compare:nNnT { \int_use:c { \@@_ctx_var:nn { #1 } { next_rule_int } } } > { \seq_count:c { \@@_ctx_var:nn { #1 } { kind_seq } } } { \bool_gset_true:c { \@@_ctx_var:nn { #1 } { finished_bool } } } } { % \end{macrocode} % no overlapping rules are supported, so we can use a fast-path here and just work with the single active rule % \begin{macrocode} \int_set:Nn \l_@@_rule_id_int { \seq_item:cn { \@@_ctx_var:nn { #1 } { active_rules_seq } } { 1 } } \@@_match_rule_predicate:NnnNnTF #1 { #2 } { #3 } #4 { \l_@@_rule_id_int } { \bool_gset_true:c { \@@_ctx_var:nn { #1 } { show_bool } } } { \bool_gset_false:c { \@@_ctx_var:nn { #1 } { show_bool } } } } } % \end{macrocode} % \iffalse % % \fi % % \Finale