% \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-parser.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_parser>
% \fi
%
% \maketitle
%
% \begin{abstract}
%   Parser for the selection logic.
%   Parses an (user supplied) \enquote{string} to a \emph{context} which can be queried later.
% \end{abstract}
%
% Identify the package and give the over all version information.
%    \begin{macrocode}
\ProvidesExplPackage {hexdumptikz-selector-parser} {2026-06-20} {1.0.1}
  {Parse address/hexdump selectors}
%    \end{macrocode}
%
% Load internal dependencies
%    \begin{macrocode}
\RequirePackage { hexdumptikz-addrcalc }
%    \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}
% Generally, the parsers parse the input to (local) variables.
% Then later, the information from these variables gets collectively pushed to the context.
%
% Predicate related variables
% \begin{var}{\l_@@_pred_kind_tl}
% Type of the predicate
%    \begin{macrocode}
\tl_new:N \l_@@_pred_kind_tl
%    \end{macrocode}
% \end{var}
% \begin{var}{\l_@@_pred_axis_tl}
% On which axis the predicate operates on (\emph{x} or \emph{y})?
%    \begin{macrocode}
\tl_new:N \l_@@_pred_axis_tl
%    \end{macrocode}
% \end{var}
% \begin{var}{\l_@@_pred_mod_int}
% For modulo predicates: what is the modulo ($x/y \bmod \mathbf{m} = r$) of this rule
%    \begin{macrocode}
\int_new:N \l_@@_pred_mod_int
%    \end{macrocode}
% \end{var}
% \begin{var}{\l_@@_pred_res_int}
% For modulo predicates: what is the residue ($x/y \bmod m = \mathbf{r}$) of this rule
%    \begin{macrocode}
\int_new:N \l_@@_pred_res_int
%    \end{macrocode}
% \end{var}
%
% \begin{var}{\l_@@_kind_tl}
% Type/Kind of the current rule
%    \begin{macrocode}
\tl_new:N \l_@@_kind_tl
%    \end{macrocode}
% \end{var}
%
% \begin{var}{\l_@@_low_tl}
% \begin{var}{\l_@@_high_tl}
% Tokenlist-low/high part of the current rule (typically the address)
%    \begin{macrocode}
\tl_new:N \l_@@_low_tl
\tl_new:N \l_@@_high_tl
%    \end{macrocode}
% \end{var}
% \end{var}
%
% \begin{var}{\l_@@_low_x_int}
% \begin{var}{\l_@@_low_y_int}
% \begin{var}{\l_@@_high_x_int}
% \begin{var}{\l_@@_high_y_int}
% Integer low/high x/y part of the current rule (typically the index)
%    \begin{macrocode}
\int_new:N \l_@@_low_x_int
\int_new:N \l_@@_low_y_int
\int_new:N \l_@@_high_x_int
\int_new:N \l_@@_high_y_int
%    \end{macrocode}
% \end{var}
% \end{var}
% \end{var}
% \end{var}
%
% \begin{var}{\l_@@_gap_count_int}
% Amount of gap-nodes to insert before the current rule takes effect
%    \begin{macrocode}
\int_new:N \l_@@_gap_count_int
%    \end{macrocode}
% \end{var}
%
% \begin{var}{\l_@@_style_tl}
% Style which to apply when this rule matches
%    \begin{macrocode}
\tl_new:N \l_@@_style_tl
%    \end{macrocode}
% \end{var}
%
% \begin{var}{\l_@@_pred_idx_int}
% Which predicate to check when this rule is in range (index in the context's predicates sequence)
%    \begin{macrocode}
\int_new:N \l_@@_pred_idx_int
%    \end{macrocode}
% \end{var}
%
% \begin{var}{\l_@@_tmpa_tl}
% Scratch-like variable:
%    \begin{macrocode}
\tl_new:N \l_@@_tmpa_tl
%    \end{macrocode}
% \end{var}
%
% \subsection{Regexes}
% \begin{var}{\c_@@_mod_regex}
% Rexex for parsing modulo predicates.
% Also extracts the arguments with capture groups.
%    \begin{macrocode}
\regex_const:Nn
\c_@@_mod_regex
{ \A mod \( \s* ([0-9]+) \s* , \s* ([0-9]+) \s* \) \Z }
%    \end{macrocode}
% \end{var}
%
% \subsection{Parsers for individual components}
% \begin{fn}{\@@_parse_pred:n}
% Parse a predicate.
% Yes it is not nice to force parsing into (local) variables, but that many argument would also be not so nice
% \begin{sideeffects}
%   \sclobber & \sdir & \texttt{l\_tmpa\_tl} \\
%   \sclobber & \sdir & \texttt{l\_tmpa\_seq} \\
% \end{sideeffects}
% \begin{args}
%   1 & \ain  & tl variable to parse \\
%   - & \aout & \texttt{l\_@@\_pred\_axis\_tl} \\
%   - & \aout & \texttt{l\_@@\_pred\_kind\_tl} \\
%   - & \aout & \texttt{l\_@@\_pred\_mod\_tl} \\
%   - & \aout & \texttt{l\_@@\_pred\_res\_tl} \\
%   - & \ain  & \texttt{l\_hexdumptikz\_common\_bytes\_per\_row\_int} \\
% \end{args}
%    \begin{macrocode}
\cs_new_protected:Npn \@@_parse_pred:n #1
{
  \tl_set:Ne \l_tmpa_tl { \tl_trim_spaces:n {#1} }
  \tl_if_empty:NF \l_tmpa_tl
  {
    \seq_set_split:Nne \l_tmpa_seq { / } { \l_tmpa_tl }
    \int_case:nnF { \seq_count:N \l_tmpa_seq }
    {
      { 1 }
      {
%    \end{macrocode}
% No axis prefix $\to$ default axis = x
%    \begin{macrocode}
        \tl_set:Nn \l_@@_pred_axis_tl { x }
        \tl_set:Ne \l_tmpa_tl { \seq_item:Nn \l_tmpa_seq { 1 } }
      }
      { 2 }
      {
        \tl_set:Ne
          \l_@@_pred_axis_tl
          { \seq_item:Nn \l_tmpa_seq { 1 } }
        \tl_set:Ne
          \l_tmpa_tl
          { \seq_item:Nn \l_tmpa_seq { 2 } }
        \str_case:VnF \l_@@_pred_axis_tl
        {
          { x } { }
          { y } { }
        }
        {
          \msg_critical:nnn
          { hexdumptikz-selector }
          { invalid-predicate-axis }
          { #1 }
        }
      }
    }
    {
      \msg_critical:nnn
      { hexdumptikz-selector }
      { invalid-predicate-input }
      { #1 }
    }
%    \end{macrocode}
%
% Parse the right-hand-side
%    \begin{macrocode}
    \str_case:VnF \l_tmpa_tl
    {
      { odd }
      {
        \tl_set:Nn \l_@@_pred_kind_tl { mod }
        \int_set:Nn \l_@@_pred_mod_int { 2 }
        \int_set:Nn \l_@@_pred_res_int { 1 }
      }
      { even }
      {
        \tl_set:Nn \l_@@_pred_kind_tl { mod }
        \int_set:Nn \l_@@_pred_mod_int { 2 }
        \int_set:Nn \l_@@_pred_res_int { 0 }
      }
    }
    {
%    \end{macrocode}
%
% Else assume it is plain \emph{mod(s,i )}
%    \begin{macrocode}
      \regex_extract_once:NVNTF
      \c_@@_mod_regex
      \l_tmpa_tl
      \l_tmpa_seq
      {
        \tl_set:Nn \l_@@_pred_kind_tl { mod }
        \int_set:Nn \l_@@_pred_mod_int { \seq_item:Nn \l_tmpa_seq { 2 } } % noqa: S103
        \int_set:Nn \l_@@_pred_res_int { \seq_item:Nn \l_tmpa_seq { 3 } } % noqa: S103
%    \end{macrocode}
%
% Enforce additional constraints when the predicate is about the x-axis
%    \begin{macrocode}
        \tl_if_eq:NnT \l_@@_pred_axis_tl { x }
        {
          \int_compare:nNnF
          {
            \int_mod:nn
            { \l_hexdumptikz_common_bytes_per_row_int }
            { \l_@@_pred_mod_int }
          }
          =
          { 0 }
          {
            \msg_critical:nnVV
            { hexdumptikz-selector }
            { invalid-mod }
            \l_hexdumptikz_common_bytes_per_row_int
            \l_@@_pred_mod_int
          }
        }
%    \end{macrocode}
%
% Sanity check on the modulos arguments
%    \begin{macrocode}
        \int_compare:nNnF
        { \l_@@_pred_res_int }
        <
        { \l_@@_pred_mod_int }
        {
          \msg_warning:nnVV
          { hexdumptikz-selector }
          { weird-mod }
          \l_@@_pred_mod_int
          \l_@@_pred_res_int
        }
      }
      {
        \msg_critical:nnn
          { hexdumptikz-selector }
          { unknown-predicate }
          { #1 }
      }
    }
  }
}
\cs_generate_variant:Nn \@@_parse_pred:n { V }
%    \end{macrocode}
% \end{fn}
%
% \begin{fn}{\@@_parse_coord:NNN}
% Parse a coordinate (y and optionally also x).
% \begin{sideeffects}
%   \sclobber & \sdir & \texttt{l\_tmpa\_seq} \\
% \end{sideeffects}
% \begin{args}
%   1 & \ain  & tl variable to parse \\
%   2 & \ain  & parsed \emph{x}-coordinate (zero if unset) \\
%   3 & \ain  & parsed \emph{y}-coordinate \\
% \end{args}
%    \begin{macrocode}
\cs_new_protected:Npn \@@_parse_coord:NNN #1 #2 #3
{
  \seq_set_split:NnV \l_tmpa_seq { / } #1
  \int_case:nnF { \seq_count:N \l_tmpa_seq }
  {
    { 1 }
    {
      \int_zero:N #2
      \int_set:Nn #3 { \seq_item:Nn \l_tmpa_seq { 1 } }
    }
    { 2 }
    {
      \int_set:Nn #2 { \seq_item:Nn \l_tmpa_seq { 2 } }
      \int_set:Nn #3 { \seq_item:Nn \l_tmpa_seq { 1 } }
    }
  }
  {
    \msg_critical:nnN
    { hexdumptikz-selector }
    { invalid-coordinate }
    #1
  }
}
%    \end{macrocode}
% \end{fn}
%
% \begin{fn}{\@@_parse_addr:NNN}
% Parse an address.
% \begin{sideeffects}
%   \sclobber & \sdir & \texttt{l\_tmpa\_seq} \\
%   \sclobber & \sdir & \texttt{l\_@@\_tmpa\_int} \\
% \end{sideeffects}
% \begin{args}
%   1 & \ain  & tl variable to parse \\
%   2 & \ain  & parsed \emph{x}-coordinate (int) \\
%   3 & \ain  & parsed address (tl) \\
% \end{args}
%    \begin{macrocode}
\cs_new_protected:Npn \@@_parse_addr:NNN #1 #2 #3
{
  \hexdumptikz_address_to_nodename_components:eNN
    { #1 }
    #3
    \l_@@_tmpa_tl
  \tl_put_left:Nn #3 { 0x }
  \int_set:Nn #2 { \l_@@_tmpa_tl }
}
%    \end{macrocode}
% \end{fn}
%
% \begin{fn}{\@@_parse_core:n}
% Parses the whole range (without style or predicate)
% \begin{sideeffects}
%   \sclobber & \sdir & \texttt{l\_tmpa\_seq} \\
%   \sclobber & \sdir & \texttt{l\_tmpa\_tl} \\
%   \sclobber & \sdir & \texttt{l\_tmpb\_tl} \\
% \end{sideeffects}
% \begin{args}
%   1 & \ain  & tl variable to parse \\
%   - & \aout & \texttt{l\_@@\_low\_tl} \\
%   - & \aout & \texttt{l\_@@\_high\_tl} \\
%   - & \aout & \texttt{l\_@@\_low\_x\_int} \\
%   - & \aout & \texttt{l\_@@\_low\_y\_int} \\
%   - & \aout & \texttt{l\_@@\_high\_x\_int} \\
%   - & \aout & \texttt{l\_@@\_high\_y\_int} \\
%   - & \aout & \texttt{l\_@@\_kind\_tl} \\
%   - & \aout & \texttt{l\_@@\_gap\_count\_int} \\
% \end{args}
%    \begin{macrocode}
\cs_new_protected:Npn \@@_parse_core:n #1
{
%    \end{macrocode}
% Clear all variables which potentially are not reset to avoid leaking data from the previous parse
%    \begin{macrocode}
  \tl_clear:N \l_@@_low_tl
  \tl_clear:N \l_@@_high_tl
  \int_zero:N \l_@@_low_x_int
  \int_zero:N \l_@@_low_y_int
  \int_zero:N \l_@@_high_x_int
  \int_zero:N \l_@@_high_y_int
%    \end{macrocode}
% Some fast-path checks
%    \begin{macrocode}
  \tl_set:Ne \l_tmpa_tl { \tl_trim_spaces:n {#1} }
  \tl_if_eq:NnTF \l_tmpa_tl { x }
  {
    \tl_set:Nn \l_@@_kind_tl { gap }
    \int_set:Nn \l_@@_gap_count_int { 1 }
  }
%    \end{macrocode}
% Start actual parsing/splitting
%    \begin{macrocode}
  {
    \seq_set_split:NnV \l_tmpa_seq { - } \l_tmpa_tl
    \int_compare:nNnTF { \seq_count:N \l_tmpa_seq } = { 1 }
    {
%    \end{macrocode}
% Atom
%    \begin{macrocode}
      \tl_set:Ne \l_tmpa_tl { \seq_item:Nn \l_tmpa_seq { 1 } }
      \regex_if_match:NVTF \c_hexdumptikz_common_hex_x_regex \l_tmpa_tl
      {
        \tl_set:Nn \l_@@_kind_tl { addr }
        \@@_parse_addr:NNN
          \l_tmpa_tl
          \l_@@_low_x_int
          \l_@@_low_tl
        \tl_set_eq:NN
          \l_@@_high_tl
          \l_@@_low_tl
        \int_set_eq:NN
          \l_@@_high_x_int
          \l_@@_low_x_int
      }
      {
        \regex_if_match:NVTF \c_hexdumptikz_common_idx_regex \l_tmpa_tl
        {
          \tl_set:Nn \l_@@_kind_tl { idx }
          \@@_parse_coord:NNN
            \l_tmpa_tl
            \l_@@_low_x_int
            \l_@@_low_y_int
          \int_set_eq:NN
            \l_@@_high_x_int
            \l_@@_low_x_int
          \int_set_eq:NN
            \l_@@_high_y_int
            \l_@@_low_y_int
        }
        {
          \msg_critical:nnV
          { hexdumptikz-selector }
          { invalid-segment-input }
          \l_tmpa_tl
        }
      }
    }
    {
%    \end{macrocode}
% Range
%    \begin{macrocode}
      \tl_set:Ne \l_tmpa_tl { \seq_item:Nn \l_tmpa_seq { 1 } }
      \tl_set:Ne \l_tmpb_tl { \seq_item:Nn \l_tmpa_seq { 2 } }
      \regex_if_match:NVTF \c_hexdumptikz_common_hex_x_regex \l_tmpa_tl
      {
%    \end{macrocode}
% Address range
%    \begin{macrocode}
        \regex_if_match:NVF \c_hexdumptikz_common_hex_x_regex \l_tmpb_tl
        {
          \msg_critical:nnV
          { hexdumptikz-selector }
          { invalid-addr-input }
          \l_tmpb_tl
        }
        \tl_set:Nn \l_@@_kind_tl { addr }
        \@@_parse_addr:NNN
          \l_tmpa_tl
          \l_@@_low_x_int
          \l_@@_low_tl
        \@@_parse_addr:NNN
          \l_tmpb_tl
          \l_@@_high_x_int
          \l_@@_high_tl
      }
      {
%    \end{macrocode}
% Check if its really a valid index range
%    \begin{macrocode}
        \regex_if_match:NVF \c_hexdumptikz_common_idx_regex \l_tmpa_tl
        {
          \msg_critical:nnV
          { hexdumptikz-selector }
          { invalid-segment-input }
          \l_tmpa_tl
        }
        \regex_if_match:NVF \c_hexdumptikz_common_idx_regex \l_tmpb_tl
        {
          \msg_critical:nnV
          { hexdumptikz-selector }
          { invalid-idx-input }
          \l_tmpb_tl
        }
%    \end{macrocode}
% Index range
%    \begin{macrocode}
        \tl_set:Nn \l_@@_kind_tl { idx }
        \@@_parse_coord:NNN
          \l_tmpa_tl
          \l_@@_low_x_int
          \l_@@_low_y_int
        \@@_parse_coord:NNN
          \l_tmpb_tl
          \l_@@_high_x_int
          \l_@@_high_y_int
      }
    }
  }
}
\cs_generate_variant:Nn \@@_parse_core:n { V }
%    \end{macrocode}
% \end{fn}
%
% \subsection{Pushing to the context}
% \begin{fn}{\@@_push_rule:N}
% Push the parsed range / rule to the context
% \begin{sideeffects}
% \end{sideeffects}
% \begin{args}
%   1 & \ain  & context to push to \\
%   - & \ain  & \texttt{l\_@@\_kind\_tl} \\
%   - & \ain  & \texttt{l\_@@\_low\_tl} \\
%   - & \ain  & \texttt{l\_@@\_high\_tl} \\
%   - & \ain  & \texttt{l\_@@\_low\_x\_int} \\
%   - & \ain  & \texttt{l\_@@\_low\_y\_int} \\
%   - & \ain  & \texttt{l\_@@\_high\_x\_int} \\
%   - & \ain  & \texttt{l\_@@\_high\_y\_int} \\
%   - & \ain  & \texttt{l\_@@\_style\_tl} \\
%   - & \ain  & \texttt{l\_@@\_pred\_idx\_tl} \\
% \end{args}
%    \begin{macrocode}
\cs_new_protected:Npn \@@_push_rule:N #1
{
%    \end{macrocode}
% rule storage
%    \begin{macrocode}
  \seq_gput_right:cV
    { \@@_ctx_var:nn { #1 } { kind_seq } }
    \l_@@_kind_tl
  \seq_gput_right:cV
    { \@@_ctx_var:nn { #1 } { addr_low_seq } }
    \l_@@_low_tl
  \seq_gput_right:cV
    { \@@_ctx_var:nn { #1 } { addr_high_seq } }
    \l_@@_high_tl
  \seq_gput_right:cV
    { \@@_ctx_var:nn { #1 } { idx_low_x_seq } }
    \l_@@_low_x_int
  \seq_gput_right:cV
    { \@@_ctx_var:nn { #1 } { idx_low_y_seq } }
    \l_@@_low_y_int
  \seq_gput_right:cV
    { \@@_ctx_var:nn { #1 } { idx_high_x_seq } }
    \l_@@_high_x_int
  \seq_gput_right:cV
    { \@@_ctx_var:nn { #1 } { idx_high_y_seq } }
    \l_@@_high_y_int
  \seq_gput_right:cV
    { \@@_ctx_var:nn { #1 } { style_seq } }
    \l_@@_style_tl
  \seq_gput_right:cV
    { \@@_ctx_var:nn { #1 } { pred_idx_seq } }
    \l_@@_pred_idx_int
}
%    \end{macrocode}
% \end{fn}
%
% \begin{fn}{\@@_push_pred:N}
% Push the parsed predicate to the context
% \begin{sideeffects}
% \end{sideeffects}
% \begin{args}
%   1 & \ain  & context to push to \\
%   - & \ain  & \texttt{l\_@@\_pred\_kind\_tl} \\
%   - & \ain  & \texttt{l\_@@\_pred\_axis\_tl} \\
%   - & \ain  & \texttt{l\_@@\_pred\_mod\_int} \\
%   - & \ain  & \texttt{l\_@@\_pred\_res\_int} \\
% \end{args}
%    \begin{macrocode}
\cs_new_protected:Npn \@@_push_pred:N #1
{
%    \end{macrocode}
% pred storage
%    \begin{macrocode}
  \seq_gput_right:cV
    { \@@_ctx_var:nn { #1 } { pred_kind_seq } }
    \l_@@_pred_kind_tl
  \seq_gput_right:cV
    { \@@_ctx_var:nn { #1 } { pred_axis_seq } }
    \l_@@_pred_axis_tl
  \seq_gput_right:cV
    { \@@_ctx_var:nn { #1 } { pred_mod_seq } }
    \l_@@_pred_mod_int
  \seq_gput_right:cV
    { \@@_ctx_var:nn { #1 } { pred_res_seq } }
    \l_@@_pred_res_int
}
%    \end{macrocode}
% \end{fn}
%
% \subsection{Public}
% \begin{fn}{\hexdumptikz_selector_parse:Nn}
% Parses a complete (c)list of rules.
% \begin{sideeffects}
%   \sclobber & \sdir & \texttt{l\_tmpa\_seq} \\
%   \sclobber & \sdir & \texttt{l\_tmpb\_seq} \\
%   \sclobber & \sdir & \texttt{l\_tmpa\_tl} \\
%   \sclobber & \sdir & \texttt{l\_tmpb\_tl} \\
% \end{sideeffects}
% \begin{args}
%   1 & \ain  & context to push to \\
%   2 & \ain  & tl to parse as comma separated list of rules \\
% \end{args}
%    \begin{macrocode}
\cs_new_protected:Npn \hexdumptikz_selector_parse:Nn #1 #2
{
%    \end{macrocode}
% First empty the context in terms of storage
%    \begin{macrocode}
  \hexdumptikz_selector_ctx_clear_storage:N #1
%    \end{macrocode}
%
% Iterate over the whole (c)list of rules
%    \begin{macrocode}
  \clist_map_inline:nn { #2 }
  {
    \tl_set:Ne \l_tmpa_tl { \tl_trim_spaces:n {##1} }
    \tl_clear:N \l_@@_style_tl
%    \end{macrocode}
%
% Split off the style part if present
%    \begin{macrocode}
    \seq_set_split:NnV \l_tmpa_seq { -> } \l_tmpa_tl
    \int_compare:nNnT { \seq_count:N \l_tmpa_seq } > { 1 }
    {
      \tl_set:Ne
      \l_@@_style_tl
      { \seq_item:Nn \l_tmpa_seq { 2 } }
    }
%    \end{macrocode}
%
% Handle the rest
%    \begin{macrocode}
    \tl_set:Ne \l_tmpa_tl { \seq_item:Nn \l_tmpa_seq { 1 } }
    \tl_if_empty:NF \l_tmpa_tl
    {
      \tl_replace_all:Nee \l_tmpa_tl
        { \char_set_catcode_active:N | }
        { \char_set_catcode_other:N | }
    }
    \seq_set_split:Nne \l_tmpb_seq { | } { \l_tmpa_tl }
    \int_compare:nNnTF { \seq_count:N \l_tmpb_seq } = { 1 }
    {
%    \end{macrocode}
% no predicate present
%    \begin{macrocode}
      \tl_set:Ne \l_tmpa_tl { \seq_item:Nn \l_tmpb_seq { 1 } }
      \int_zero:N \l_@@_pred_idx_int
      \@@_parse_core:V \l_tmpa_tl
      \@@_push_rule:N #1
    }
    {
%    \end{macrocode}
% one predicate present
%
% parse and push the predicate
%    \begin{macrocode}
      \tl_set:Ne \l_tmpa_tl { \seq_item:Nn \l_tmpb_seq { 2 } }
      \@@_parse_pred:V \l_tmpa_tl
      \@@_push_pred:N #1
%    \end{macrocode}
%
% obtain the index of the just pushed predicate which needs to be referenced in the current rule.
%    \begin{macrocode}
      \int_set:Nn
        \l_@@_pred_idx_int
        {
          \seq_count:c
          {
            \@@_ctx_var:nn
            { #1 }
            { pred_kind_seq }
          }
        }
%    \end{macrocode}
%
% parse and push the rule
%    \begin{macrocode}
      \tl_set:Ne \l_tmpa_tl { \seq_item:Nn \l_tmpb_seq { 1 } }
      \@@_parse_core:V \l_tmpa_tl
      \@@_push_rule:N #1
    }
  }
}
\cs_generate_variant:Nn \hexdumptikz_selector_parse:Nn { Ne }
%    \end{macrocode}
% \end{fn}
% \iffalse
%</package>
% \fi
%
% \Finale
