% \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-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_parser>
% \fi
%
% \maketitle
%
% \begin{abstract}
%   General variables and helpers for parsing hexdump data from file.
%   Also includes some documentation about the general parsing architcture.
% \end{abstract}
%
% Generally, the idea is as follows.
% The user (indirectly) calls the respective parser and passes a callback along.
% This callback is then responsible for drawing a line of the hexdump.
%
% \subsection{Interfaces}
% Callback:
% \begin{args}
%   1 & \ain & line-index \\
%   2 & \ain & parsed offset/address \\
%   3 & \ain & parsed bytes (seq) \\
%   4 & \aout & variable to signal whether printing / the selection has finished \\
% \end{args}
%
% Parser:
% \begin{args}
%   1 & \ain & filename which shall be parsed \\
%   2 & \ain & callback \\
% \end{args}
%
% \subsection{Implementation}
%
% Identify the package and give the over all version information.
%    \begin{macrocode}
\ProvidesExplPackage {hexdumptikz-parser} {2026-06-20} {1.0.1}
  {Printing and annotating hexdumps with TikZ}

\RequirePackage { hexdumptikz-parser-hd }

%    \end{macrocode}
% \begin{var}{\l_hexdumptikz_parser_ior}
% IO variable for opening the file
%    \begin{macrocode}
\ior_new:N  \l_hexdumptikz_parser_ior
%    \end{macrocode}
% \end{var}
% \begin{var}{\l_hexdumptikz_parser_line_int}
% Counter for the lines in the input
%    \begin{macrocode}
\int_new:N  \l_hexdumptikz_parser_line_int
%    \end{macrocode}
% \end{var}
%
% \begin{var}{\l_hexdumptikz_parser_offset_tl}
% Parsed offset / the address
%    \begin{macrocode}
\tl_new:N   \l_hexdumptikz_parser_offset_tl
%    \end{macrocode}
% \end{var}
% \begin{var}{\l_hexdumptikz_parser_line_tl}
% Complete parsed line
%    \begin{macrocode}
\tl_new:N   \l_hexdumptikz_parser_line_tl
%    \end{macrocode}
% \end{var}
%
% \begin{var}{\l_hexdumptikz_parser_bytes_seq}
% Sequence of the parsed bytes
%    \begin{macrocode}
\seq_new:N  \l_hexdumptikz_parser_bytes_seq
%    \end{macrocode}
% \end{var}

% \begin{var}{\l_hexdumptikz_parser_last_line_seen_bool}
% Whether the parser assumes the last line has already been processed (might be useful for some stricter input validation tests)
%    \begin{macrocode}
\bool_new:N \l_hexdumptikz_parser_last_line_seen_bool
%    \end{macrocode}
% \end{var}

% \begin{var}{\l_hexdumptikz_parser_finished_bool}
% Variable which is passed to the callback as notification channel in the other direction
%    \begin{macrocode}
\bool_new:N  \l_hexdumptikz_parser_finished_bool
%    \end{macrocode}
% \end{var}
%
% Variables configuring the parsers.
% \begin{var}{\l_hexdumptikz_parser_strict_byte_num_bool}
% Whether the number of bytes should be checked and an error being raised if there is a missmatch with the expected number.
%    \begin{macrocode}
\bool_new:N \l_hexdumptikz_parser_strict_byte_num_bool
%    \end{macrocode}
% \end{var}
% \begin{var}{\l_hexdumptikz_parser_strict_hex_bool}
% Whether to check for valid hex characters in the byte input.
%    \begin{macrocode}
\bool_new:N \l_hexdumptikz_parser_strict_hex_bool
%    \end{macrocode}
% \end{var}
% \begin{var}{\l_hexdumptikz_parser_leading_base_bool}
% Whether the value specification includes the base (\texttt{0x})
%    \begin{macrocode}
\bool_new:N \l_hexdumptikz_parser_leading_base_bool
%    \end{macrocode}
% \end{var}
%
% \begin{fn}{\hexdumptikz_parser_parse:nN}
% An alias to the active parser.
%    \begin{macrocode}
\cs_new_eq:NN \hexdumptikz_parser_parse:nN \hexdumptikz_parser_hd:nN
%    \end{macrocode}
% \end{fn}
%
%
%    \begin{macrocode}
\cs_generate_variant:Nn \str_range_ignore_spaces:nnn { Vnn }
%    \end{macrocode}
%
%
% \begin{var}{\c_hexdumptikz_parser_addr_regex}
% Matches an address specifier with an optional leading \texttt{0x} and an optional trailing \texttt{:}.
% Extracts the raw address in its first capturing group.
%    \begin{macrocode}
\regex_const:Nn
  \c_hexdumptikz_parser_addr_regex
  { \A \s* (?:0x)? ([0-9A-Fa-f]+) \s* :? \s* }
%    \end{macrocode}
% \end{var}

% \begin{var}{\c_hexdumptikz_parser_leading_hex_byte_regex}
% Matches a byte in its hexadecimal representation (2 hex-digits).
%    \begin{macrocode}
\regex_const:Nn
  \c_hexdumptikz_parser_leading_hex_byte_regex
  { \A [0-9A-Fa-f] { 2 } }
%    \end{macrocode}
% \end{var}
%
% \subsubsection{Pgfkeys}
% \begin{pgfkey}{parser}
%    \begin{macrocode}
\pgfkeys {
  /hexdumptikz,
  parser/.is~choice,
  parser/hd/.code = {
    \cs_set_eq:NN \hexdumptikz_parser_parse:nN \hexdumptikz_parser_hd:nN
  }
}
%    \end{macrocode}
% \end{pgfkey}
%
% Define interface to simplify setting parser options.
%    \begin{macrocode}
\pgfkeys {
  /hexdumptikz/parser~opts~hd/.code={
    \pgfkeys{
      /hexdumptikz/parser~opts/hd,
      #1
    }
  },
}
%    \end{macrocode}
% Define parser options
%    \begin{macrocode}
\pgfkeys {
  /hexdumptikz/parser~opts/hd/.is~family,
  /hexdumptikz/parser~opts/hd,
%    \end{macrocode}
%
% \begin{pgfkey}{strict~byte~number~per~row}
%    \begin{macrocode}
  strict~byte~number~per~row/.code={
    \hexdumptikz_common_parse_bool:Nn
      \l_hexdumptikz_parser_strict_byte_num_bool
      { #1 }
  },
  strict~byte~number~per~row/.default={true},
%    \end{macrocode}
% \end{pgfkey}
%
% \begin{pgfkey}{strict~hex~digits}
%    \begin{macrocode}
  strict~hex~digits/.code={
    \hexdumptikz_common_parse_bool:Nn
      \l_hexdumptikz_parser_strict_hex_bool
      { #1 }
  },
  strict~hex~digits/.default={true},
%    \end{macrocode}
% \end{pgfkey}
%
% \begin{pgfkey}{leading~value~base}
%    \begin{macrocode}
  leading~value~base/.code={
    \hexdumptikz_common_parse_bool:Nn
      \l_hexdumptikz_parser_leading_base_bool
      { #1 }
  },
  leading~value~base/.default={true},
%    \end{macrocode}
% \end{pgfkey}
%    \begin{macrocode}
}
%    \end{macrocode}
%
% \subsubsection{Errors}
% \begin{error}{no-valid-offset}
%    \begin{macrocode}
\msg_new:nnn { hexdumptikz-parser } { no-valid-offset }
  { No~valid~offset/address~found~in~line~'#1' }
%    \end{macrocode}
% \end{error}

% \begin{error}{too-many-bytes}
%    \begin{macrocode}
\msg_new:nnn { hexdumptikz-parser } { too-many-bytes }
  { Line~with~offset~#1~has~more~than~#2~bytes.~Left~over~is:~#3 }
%    \end{macrocode}
% \end{error}

% \begin{error}{invalid-hex-digits}
%    \begin{macrocode}
\msg_new:nnn { hexdumptikz-parser } { invalid-hex-digits }
  { The~next~bytes~on~the~following~line~are~no~valid~hex~bytes:~#1 }
%    \end{macrocode}
% \end{error}

% \begin{error}{weird-byte-count}
%    \begin{macrocode}
\msg_new:nnn { hexdumptikz-parser } { weird-byte-count }
  {
    A~line~with~less~than~'bytes~per~row'~
    (#1)~
    bytes~is~only~allowed~as~last~line.~Line~at~offset:~#2
  }
%    \end{macrocode}
% \end{error}

% \begin{error}{leading-base-missing}
%    \begin{macrocode}
\msg_new:nnn { hexdumptikz-parser } { leading-base-missing }
  { Expected~leading~'0x'~missing~in~line~#1 }
%    \end{macrocode}
% \end{error}
% \iffalse
%</package>
% \fi
%
% \Finale
