% \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.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> % \fi % % \maketitle % % \begin{abstract} % General utilities for implementing the selection logic. % Each instanciation of a selector is associated with a \emph{context} which stores the state of the selector. % Among other functionalities, this (sub-)package provides tools to work with these contexts. % \end{abstract} % % Identify the package and give the over all version information. % \begin{macrocode} \ProvidesExplPackage {hexdumptikz-selector} {2026-06-20} {1.0.1} {Parse and work with address/hexdump selectors} % \end{macrocode} % % Requirements % \begin{macrocode} \RequirePackage { hexdumptikz-common } % \end{macrocode} % % \subsection{Errors} % Define the error messages associated with the selector mechanism. % % \begin{error}{invalid-segment-input} % \begin{macrocode} \msg_new:nnn { hexdumptikz-selector } { invalid-segment-input } { '#1'~is~no~valid~input~for~a~segment. } % \end{macrocode} % \end{error} % % \begin{error}{invalid-addr-input} % \begin{macrocode} \msg_new:nnn { hexdumptikz-selector } { invalid-addr-input } { '#1'~is~no~valid~address.~An~address~is~for~example~0x014af1. } % \end{macrocode} % \end{error} % % \begin{error}{invalid-address} % \begin{macrocode} \msg_new:nnn { hexdumptikz-selector } { invalid-address } { '#1'~is~no~valid~address. } % \end{macrocode} % % \begin{error}{invalid-coordinate} % \begin{macrocode} \msg_new:nnn { hexdumptikz-selector } { invalid-coordinate } { '#1'~is~no~valid~coordinate. } % \end{macrocode} % % \end{error} % % \begin{error}{invalid-idx-input} % \begin{macrocode} \msg_new:nnn { hexdumptikz-selector } { invalid-idx-input } { '#1'~is~no~valid~index.~An~index~must~only~consist~of~digits~0-9. } % \end{macrocode} % \end{error} % % \begin{error}{unknown-predicate} % \begin{macrocode} \msg_new:nnn { hexdumptikz-selector } { unknown-predicate } { '#1'~is~not~a~valid~predicate. } % \end{macrocode} % \end{error} % % \begin{error}{invalid-predicate-axis} % \begin{macrocode} \msg_new:nnn { hexdumptikz-selector } { invalid-predicate-axis } { '#1'~is~not~a~valid~axis~for~a~predicate. } % \end{macrocode} % \end{error} % % \begin{error}{invalid-predicate-input} % \begin{macrocode} \msg_new:nnn { hexdumptikz-selector } { invalid-predicate-input } { '#1'~is~not~a~valid~predicate~specification. } % \end{macrocode} % \end{error} % % \begin{error}{invalid-rule} % \begin{macrocode} \msg_new:nnn { hexdumptikz-selector } { invalid-rule } { '#1'~is~not~a~valid~rule. } % \end{macrocode} % \end{error} % % \begin{error}{weird-mod} % \begin{macrocode} \msg_new:nnn { hexdumptikz-selector } { weird-mod } { with~mod~'#1'~a~remainder~of~'#2'~does~not~make~much~sense. } % \end{macrocode} % \end{error} % % \begin{error}{invalid-mod} % \begin{macrocode} \msg_new:nnn { hexdumptikz-selector } { invalid-mod } { With~'#1'~bytes~per~row~(BPR),~mod~'#2'~is~not~valid.~ Mod~must~be~able~to~work~locally~on~the~row.~ BPR~\%~mod~==~0~must~hold. } % \end{macrocode} % \end{error} % % \subsection{Context Helpers} % A context stores its data in \emph{global} variables with the context-id encoded in the variable name. % % \begin{var}{\g_@@_ctx_id_int} % Track the index of the next context which is to be allocated % \begin{macrocode} \int_new:N \g_@@_ctx_id_int % \end{macrocode} % \end{var} % % \begin{fn}{\@@_ctx_var:nn} % Construct the variable name specific to a context. % Leaves the variable name in the output. % % \begin{sideeffects} % \end{sideeffects} % \begin{args} % 1 & \ain & context-id \\ % 2 & \ain & name of the variable in the context \\ % \end{args} % \begin{macrocode} \cs_new:Npn \@@_ctx_var:nn #1 #2 { g_@@_ctx_#1_#2 } % \end{macrocode} % \end{fn} % % \begin{fn}{\hexdumptikz_selector_ctx_new:N} % Allocate a new context. % % \begin{sideeffects} % \end{sideeffects} % \begin{args} % 1 & \aout & context-id which should be used in the future \\ % \end{args} % \begin{macrocode} \cs_new_protected:Npn \hexdumptikz_selector_ctx_new:N #1 { % \end{macrocode} % obtain a new context-id and increment the counter % \begin{macrocode} \int_gincr:N \g_@@_ctx_id_int \tl_set:Ne #1 { \int_use:N \g_@@_ctx_id_int } % \end{macrocode} % % variables to store the parsed rules % \begin{macrocode} \seq_new:c { \@@_ctx_var:nn { #1 } { kind_seq } } \seq_new:c { \@@_ctx_var:nn { #1 } { addr_low_seq } } \seq_new:c { \@@_ctx_var:nn { #1 } { addr_high_seq } } \seq_new:c { \@@_ctx_var:nn { #1 } { idx_low_x_seq } } \seq_new:c { \@@_ctx_var:nn { #1 } { idx_low_y_seq } } \seq_new:c { \@@_ctx_var:nn { #1 } { idx_high_x_seq } } \seq_new:c { \@@_ctx_var:nn { #1 } { idx_high_y_seq } } \seq_new:c { \@@_ctx_var:nn { #1 } { style_seq } } \seq_new:c { \@@_ctx_var:nn { #1 } { pred_idx_seq } } % \end{macrocode} % % variables to store the predicates (pred\_idx\_seq may point to these sequences) % \begin{macrocode} \seq_new:c { \@@_ctx_var:nn { #1 } { pred_kind_seq } } \seq_new:c { \@@_ctx_var:nn { #1 } { pred_axis_seq } } \seq_new:c { \@@_ctx_var:nn { #1 } { pred_mod_seq } } \seq_new:c { \@@_ctx_var:nn { #1 } { pred_res_seq } } % \end{macrocode} % % variables to for iterating over the rules % \begin{macrocode} \seq_new:c { \@@_ctx_var:nn { #1 } { active_rules_seq } } \int_new:c { \@@_ctx_var:nn { #1 } { next_rule_int } } % \end{macrocode} % % variables which store the iterator result -- think of it as \texttt{yield} % \begin{macrocode} \int_new:c { \@@_ctx_var:nn { #1 } { gap_cnt_int } } \tl_new:c { \@@_ctx_var:nn { #1 } { style_tl } } \bool_new:c { \@@_ctx_var:nn { #1 } { show_bool } } \bool_new:c { \@@_ctx_var:nn { #1 } { finished_bool } } } % \end{macrocode} % \end{fn} % % \begin{fn}{\hexdumptikz_selector_ctx_clear_storage:N} % Reset/Clear the storage of a context. % % \begin{sideeffects} % \end{sideeffects} % \begin{args} % 1 & \ain & context-id on which to work on \\ % \end{args} % \begin{macrocode} \cs_new_protected:Npn \hexdumptikz_selector_ctx_clear_storage:N #1 { % \end{macrocode} % Clear the rule storage % \begin{macrocode} \seq_gclear:c { \@@_ctx_var:nn { #1 } { kind_seq } } \seq_gclear:c { \@@_ctx_var:nn { #1 } { addr_low_seq } } \seq_gclear:c { \@@_ctx_var:nn { #1 } { addr_high_seq } } \seq_gclear:c { \@@_ctx_var:nn { #1 } { idx_low_x_seq } } \seq_gclear:c { \@@_ctx_var:nn { #1 } { idx_low_y_seq } } \seq_gclear:c { \@@_ctx_var:nn { #1 } { idx_high_x_seq } } \seq_gclear:c { \@@_ctx_var:nn { #1 } { idx_high_y_seq } } \seq_gclear:c { \@@_ctx_var:nn { #1 } { style_seq } } \seq_gclear:c { \@@_ctx_var:nn { #1 } { pred_idx_seq } } % \end{macrocode} % % Clear the predicate storage % \begin{macrocode} \seq_gclear:c { \@@_ctx_var:nn { #1 } { pred_kind_seq } } \seq_gclear:c { \@@_ctx_var:nn { #1 } { pred_axis_seq } } \seq_gclear:c { \@@_ctx_var:nn { #1 } { pred_mod_seq } } \seq_gclear:c { \@@_ctx_var:nn { #1 } { pred_res_seq } } } % \end{macrocode} % \end{fn} % % \begin{fn}{\hexdumptikz_selector_ctx_clear_iter:N} % Reset/Clear the iterator state. % % \begin{sideeffects} % \end{sideeffects} % \begin{args} % 1 & \ain & context-id on which to work on \\ % \end{args} % \begin{macrocode} \cs_new_protected:Npn \hexdumptikz_selector_ctx_clear_iter:N #1 { \seq_gclear:c { \@@_ctx_var:nn { #1 } { active_rules_seq } } % noqa: S103 \int_gset:cn { \@@_ctx_var:nn { #1 } { next_rule_int } } { 1 } % noqa: S103 } % \end{macrocode} % \end{fn} % % \begin{fn}{\hexdumptikz_selector_ctx_clear_iter_out:N} % Reset/Clear the iterator output. % % \begin{sideeffects} % \end{sideeffects} % \begin{args} % 1 & \ain & context-id on which to work on \\ % \end{args} % \begin{macrocode} \cs_new_protected:Npn \hexdumptikz_selector_ctx_clear_iter_out:N #1 { \tl_gclear:c { \@@_ctx_var:nn { #1 } { style_tl } } \bool_gset_false:c { \@@_ctx_var:nn { #1 } { show_bool } } \bool_gset_false:c { \@@_ctx_var:nn { #1 } { finished_bool } } % noqa: S103 } % \end{macrocode} % \end{fn} % % \begin{fn}{\hexdumptikz_selector_show_bool:N} % Obtain the \texttt{show\_bool} variable. % % \begin{sideeffects} % \end{sideeffects} % \begin{args} % 1 & \ain & context-id on which to work on \\ % \end{args} % \begin{macrocode} \cs_new_protected:Npn \hexdumptikz_selector_show_bool:N #1 { \@@_ctx_var:nn { #1 } { show_bool } } % \end{macrocode} % \end{fn} % % \begin{fn}{\hexdumptikz_selector_finished_bool:N} % Obtain the \texttt{finished\_bool} variable. % % \begin{sideeffects} % \end{sideeffects} % \begin{args} % 1 & \ain & context-id on which to work on \\ % \end{args} % \begin{macrocode} \cs_new_protected:Npn \hexdumptikz_selector_finished_bool:N #1 { \@@_ctx_var:nn { #1 } { finished_bool } } % \end{macrocode} % \end{fn} % % \begin{fn}{\hexdumptikz_selector_gap_int:N} % Obtain the \texttt{gap\_cnt\_int} variable. % % \begin{sideeffects} % \end{sideeffects} % \begin{args} % 1 & \ain & context-id on which to work on \\ % \end{args} % \begin{macrocode} \cs_new_protected:Npn \hexdumptikz_selector_gap_int:N #1 { \@@_ctx_var:nn { #1 } { gap_cnt_int } } % \end{macrocode} % \end{fn} % % \begin{fn}{\hexdumptikz_selector_style_tl:N} % Obtain the \texttt{style\_tl} variable. % % \begin{sideeffects} % \end{sideeffects} % \begin{args} % 1 & \ain & context-id on which to work on \\ % \end{args} % \begin{macrocode} \cs_new_protected:Npn \hexdumptikz_selector_style_tl:N #1 { \@@_ctx_var:nn { #1 } { style_tl } } % \end{macrocode} % \end{fn} % % \subsection{Debugging Helpers} % Expl3 functions and latex macros for inspecting a context. % % TODO: only available in special debug package(s) % % \begin{fn}{\@@_ctx_dump_storage:N} % Dump the storage part of the context % % \begin{sideeffects} % \end{sideeffects} % \begin{args} % 1 & \ain & context-id on which to inspect \\ % \end{args} % \begin{macrocode} \cs_new_protected:Npn \@@_ctx_dump_storage:N #1 { \iow_term:x { ======~Context~#1~Storage~====== } % \end{macrocode} % Dump the part that stores the rules % \begin{macrocode} \@@_ctx_dump_seq:Nn #1 { kind_seq } \@@_ctx_dump_seq:Nn #1 { addr_low_seq } \@@_ctx_dump_seq:Nn #1 { addr_high_seq } \@@_ctx_dump_seq:Nn #1 { idx_low_x_seq } \@@_ctx_dump_seq:Nn #1 { idx_low_y_seq } \@@_ctx_dump_seq:Nn #1 { idx_high_x_seq } \@@_ctx_dump_seq:Nn #1 { idx_high_y_seq } \@@_ctx_dump_seq:Nn #1 { style_seq } \@@_ctx_dump_seq:Nn #1 { pred_idx_seq } % \end{macrocode} % Newline for separation % \begin{macrocode} \iow_term:n { } % \end{macrocode} % % Dump the part that stores the predicates % \begin{macrocode} \@@_ctx_dump_seq:Nn #1 { pred_kind_seq } \@@_ctx_dump_seq:Nn #1 { pred_axis_seq } \@@_ctx_dump_seq:Nn #1 { pred_mod_seq } \@@_ctx_dump_seq:Nn #1 { pred_res_seq } \iow_term:n { ======================== } } % \end{macrocode} % \end{fn} % % \begin{fn}{\@@_ctx_dump_iter:N} % Dump the iterator state of the context % % \begin{sideeffects} % \end{sideeffects} % \begin{args} % 1 & \ain & context-id on which to inspect \\ % \end{args} % \begin{macrocode} \cs_new_protected:Npn \@@_ctx_dump_iter:N #1 { \iow_term:x { ======~Context~#1~Iter~====== } \@@_ctx_dump_seq:Nn #1 { active_rules_seq } \@@_ctx_dump_int:Nn #1 { next_rule_int } \@@_ctx_dump_int:Nn #1 { gap_cnt_int } \@@_ctx_dump_tl:Nn #1 { style_tl } \@@_ctx_dump_bool:Nn #1 { show_bool } \@@_ctx_dump_bool:Nn #1 { finished_bool } \iow_term:n { ======================== } } % \end{macrocode} % \end{fn} % % \begin{fn}{\@@_ctx_dump_seq:Nn} % Helper for dumping a sequence % % \begin{sideeffects} % \end{sideeffects} % \begin{args} % 1 & \ain & context-id on which to inspect \\ % 2 & \ain & (seq) variable to dump \\ % \end{args} % \begin{macrocode} \cs_new_protected:Npn \@@_ctx_dump_seq:Nn #1 #2 { \iow_term:x { #2 ~ = ~ \seq_use:cn { \@@_ctx_var:nn { #1 } { #2 } } { ~|~ } } } % \end{macrocode} % \end{fn} % % \begin{fn}{\@@_ctx_dump_int:Nn} % Helper for dumping an integer % % \begin{sideeffects} % \end{sideeffects} % \begin{args} % 1 & \ain & context-id on which to inspect \\ % 2 & \ain & (int) variable to dump \\ % \end{args} % \begin{macrocode} \cs_new_protected:Npn \@@_ctx_dump_int:Nn #1 #2 { \iow_term:x { #2 ~ = ~ \int_use:c { \@@_ctx_var:nn { #1 } { #2 } } } } % \end{macrocode} % \end{fn} % % \begin{fn}{\@@_ctx_dump_tl:Nn} % Helper for dumping a token list % % \begin{sideeffects} % \end{sideeffects} % \begin{args} % 1 & \ain & context-id on which to inspect \\ % 2 & \ain & (tl) variable to dump \\ % \end{args} % \begin{macrocode} \cs_new_protected:Npn \@@_ctx_dump_tl:Nn #1 #2 { \iow_term:x { #2 ~ = ~ \tl_use:c { \@@_ctx_var:nn { #1 } { #2 } } } } % \end{macrocode} % \end{fn} % % \begin{fn}{\@@_ctx_dump_bool:Nn} % Helper for dumping a boolean % % \begin{sideeffects} % \end{sideeffects} % \begin{args} % 1 & \ain & context-id on which to inspect \\ % 2 & \ain & (bool) variable to dump \\ % \end{args} % \begin{macrocode} \cs_new_protected:Npn \@@_ctx_dump_bool:Nn #1 #2 { \iow_term:x { #2 ~ = ~ \bool_to_str:c { \@@_ctx_var:nn { #1 } { #2 } } } } % \end{macrocode} % \end{fn} % % \begin{var}{\l_@@_dbg_ctx_tl} % Context which is used by the debugging latex macros % \begin{macrocode} \tl_new:N \l_@@_dbg_ctx_tl \hexdumptikz_selector_ctx_new:N \l_@@_dbg_ctx_tl % \end{macrocode} % \end{var} % % \begin{macro}{\hexdumptikzParseRow} % Parse the specified input as selector into the debugging context. % \begin{macrocode} \NewDocumentCommand { \hexdumptikzParseRow } { m } { \hexdumptikz_selector_parse:Nn \l_@@_dbg_ctx_tl { #1 } } % \end{macrocode} % \end{macro} % % \begin{macro}{\hexdumptikzParseDump} % Dump the debugging context to the console. % \begin{macrocode} \NewDocumentCommand { \hexdumptikzParseDump } { } { \@@_ctx_dump_storage:N \l_@@_dbg_ctx_tl } % \end{macrocode} % \end{macro} % % \begin{var}{\l_@@_dbg_tmpa_str} % \begin{var}{\l_@@_dbg_tmpa_int} % \begin{var}{\l_@@_dbg_tmpb_int} % \begin{macro}{\hexdumptikzMatchRow} % Match the specified \marg{addr}, \marg{x-idx}, \marg{y-idx} against the rules stored in the debugging context. % Matches as row-selection matcher (simplified matcher). % \begin{macrocode} \str_new:N \l_@@_dbg_tmpa_str \int_new:N \l_@@_dbg_tmpa_int \int_new:N \l_@@_dbg_tmpb_int \NewDocumentCommand { \hexdumptikzMatchRow } { mmm } { \str_set:Ne \l_@@_dbg_tmpa_str { #1 } \int_set:Nn \l_@@_dbg_tmpa_int { #2 } \int_set:Nn \l_@@_dbg_tmpb_int { #3 } \hexdumptikz_selector_match_rows:NnnN \l_@@_dbg_ctx_tl { \l_@@_dbg_tmpa_int } { \l_@@_dbg_tmpb_int } \l_@@_dbg_tmpa_str } % \end{macrocode} % \end{macro} % \end{var} % \end{var} % \end{var} % % \begin{macro}{\hexdumptikzReset} % Clear/Reset the iterstate of the debugging context % \begin{macrocode} \NewDocumentCommand { \hexdumptikzReset } { } { \hexdumptikz_selector_ctx_clear_iter:N \l_@@_dbg_ctx_tl } % \end{macrocode} % \end{macro} % % \begin{macro}{\hexdumptikzIterDump} % Dump the iterstate of the debugging context. % \begin{macrocode} \NewDocumentCommand { \hexdumptikzIterDump } { } { \@@_ctx_dump_iter:N \l_@@_dbg_ctx_tl } % \end{macrocode} % \end{macro} % % \begin{macro}{\hexdumptikzMatchStyle} % Match the specified \marg{addr}, \marg{x-idx}, \marg{y-idx} against the rules stored in the debugging context. % Matches as styling matcher. % \begin{macrocode} \NewDocumentCommand { \hexdumptikzMatchStyle } { mmm } { \str_set:Ne \l_@@_dbg_tmpa_str { #1 } \int_set:Nn \l_@@_dbg_tmpa_int { #2 } \int_set:Nn \l_@@_dbg_tmpb_int { #3 } \hexdumptikz_selector_match_styles:NnnN \l_@@_dbg_ctx_tl { \l_@@_dbg_tmpa_int } { \l_@@_dbg_tmpb_int } \l_@@_dbg_tmpa_str } % \end{macrocode} % \end{macro} % % TODO: not debugging related anymore % Load the specific packages in the end as their initialization depends on the utils from this (sub-)package being present. % \begin{macrocode} \RequirePackage { hexdumptikz-selector-parser } \RequirePackage { hexdumptikz-selector-matcher } % \end{macrocode} % \iffalse % % \fi % % \Finale