% \iffalse
% vim: set expandtab:
% vim: set shiftwidth=2:
% vim: set tabstop=2:
% \fi
% \iffalse meta-comment
%
% Copyright (C) 2026 by Lukas Heindl <oss.heindl+latex@protonmail.com>
% ---------------------------------------------------------------------------
% 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}
%</driver>
%<package>\NeedsTeXFormat{LaTeX2e}[2022-06-01]
%
%<*driver>
\begin{document}
  \DocInput{\jobname.dtx}
  \PrintChanges
  \PrintIndex
\end{document}
%
% \changes{v0.0.0}{2026-05-14}{First draft}
%
%</driver>
% \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
%</package>
% \fi
%
% \Finale
