% \iffalse meta-comment
% (The MIT License)
%
% Copyright (c) 2023 Yegor Bugayenko
%
% Permission is hereby granted, free of charge, to any person obtaining a copy
% of this software and associated documentation files (the 'Software'), to deal
% in the Software without restriction, including without limitation the rights
% to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
% copies of the Software, and to permit persons to whom the Software is
% furnished to do so, subject to the following conditions:
%
% The above copyright notice and this permission notice shall be included in all
% copies or substantial portions of the Software.
%
% THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
% IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
% FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
% AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
% LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
% OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
% SOFTWARE.
% \fi

% \CheckSum{0}
%
% \CharacterTable
%  {Upper-case    \A\B\C\D\E\F\G\H\I\J\K\L\M\N\O\P\Q\R\S\T\U\V\W\X\Y\Z
%   Lower-case    \a\b\c\d\e\f\g\h\i\j\k\l\m\n\o\p\q\r\s\t\u\v\w\x\y\z
%   Digits        \0\1\2\3\4\5\6\7\8\9
%   Exclamation   \!     Double quote  \"     Hash (number) \#
%   Dollar        \$     Percent       \%     Ampersand     \&
%   Acute accent  \'     Left paren    \(     Right paren   \)
%   Asterisk      \*     Plus          \+     Comma         \,
%   Minus         \-     Point         \.     Solidus       \/
%   Colon         \:     Semicolon     \;     Less than     \<
%   Equals        \=     Greater than  \>     Question mark \?
%   Commercial at \@     Left bracket  \[     Backslash     \\
%   Right bracket \]     Circumflex    \^     Underscore    \_
%   Grave accent  \`     Left brace    \{     Vertical bar  \|
%   Right brace   \}     Tilde         \~}

% \GetFileInfo{naive-ebnf.dtx}
% \DoNotIndex{\endgroup,\begingroup,\let,\else,\s,\n,\r,\\,\1,\fi}

% \iffalse
%<*driver>
\ProvidesFile{naive-ebnf.dtx}
%</driver>
%<package>\NeedsTeXFormat{LaTeX2e}
%<package>\ProvidesPackage{naive-ebnf}
%<*package>
[2023-01-29 0.0.2 EBNF in Plain Text]
%</package>
%<*driver>
\documentclass{ltxdoc}
\usepackage[T1]{fontenc}
\usepackage[tt=false, type1=true]{libertine}
\usepackage{microtype}
\AddToHook{env/verbatim/begin}{\microtypesetup{protrusion=false}}
\usepackage{href-ul}
\usepackage{xcolor}
\usepackage[dtx]{docshots}
\PageIndex
\EnableCrossrefs
\CodelineIndex
\RecordChanges
\begin{document}
  \DocInput{naive-ebnf.dtx}
  \PrintChanges
  \PrintIndex
\end{document}
%</driver>
% \fi

% \title{|naive-ebnf|: \LaTeX{} Package \\ for EBNF in Plain Text\thanks{The sources are in GitHub at \href{https://github.com/yegor256/naive-ebnf}{yegor256/naive-ebnf}}}
% \author{Yegor Bugayenko \\ \texttt{yegor256@gmail.com}}
% \date{\filedate, \fileversion}
%
% \maketitle
%
% \section{Introduction}
%
% This package helps render a
% \href{https://en.wikipedia.org/wiki/Extended_Backus%E2%80%93Naur_form}{Extended Backus-Naur Form}
% using plain text notation:
% \begin{docshot}
% \documentclass{minimal}
% \usepackage{naive-ebnf}
% \usepackage{mathtools}
% \begin{document}
% \begin{ebnf}
% <Expr> := <Var>
%   | "$\lambda$" <Var> "." <Expr>
%   | "$\lparen$" <Expr> <Expr> "$\rparen$"
% \end{ebnf}
% \end{document}
% \end{docshot}

% The following syntax is understood inside the |ebnf| environment:
% \begin{itemize}
% \item |:=| separates the left-hand side from the right-hand side of the production rule;
% \item |<...>| denotes a non-terminal (variable);
% \item |"..."| denotes a terminal symbol;
% \item \texttt{(...\char`\|...)} denotes a series of options to choose from;
% \item |[...]| denotes an optional substitution;
% \item |{...}| denotes a zero or more times repetition;
% \end{itemize}

% \textbf{Attention}: The usage of some symbols is prohibited inside terminals. Instead, the following substitutions are recommended:
% \begin{itemize}
% \item |$\lparen$| and |$\rparen$| instead of ``|(|'' and ``|)|'' (from the \href{https://ctan.org/pkg/mathtools}{mathtools} package);
% \item |$\langle$| and |$\rangle$| instead of ``|<|'' and ``|>|'';
% \item |$\lbrace$| and |$\rbrace$| instead of ``|{|'' and ``|}|'' (also |mathtools|);
% \item |$\lbrack$| and |$\rbrack$| instead of ``|[|'' and ``|]|'' (also |mathtools|);
% \item |$\vert$| instead of ``\texttt{\char`\|}''.
% \end{itemize}

% \DescribeMacro{\terminal}
% \DescribeMacro{\nonterminal}
% Inside the text, terminals and non-terminals may be formatted using two supplementary commands:
% \docshotOptions{firstline=6,lastline=10}
% \begin{docshot}
% \documentclass{article}
% \pagestyle{empty}
% \usepackage[paperwidth=3in]{geometry}
% \usepackage{naive-ebnf}
% \begin{document}
% The non-terminal \nonterminal{Var}
% in $\lambda$-calculus may be equal
% to $v_1, v_2, \dots$. Application
% starts with \terminal{(} and ends
% with \terminal{)}.
% \end{document}
% \end{docshot}

% \section{Package Options}

% It's possible to configure the behavior of the package with the help of a few package options:

% \DescribeMacro{bw}
% By default, some colors are used in the rendered grammar. However, the |bw| package option disables any colors and makes sure the gammar is black-and-white:
%\iffalse
%<*verb>
%\fi
\begin{verbatim}
\usepackage[bw]{naive-ebnf}
\end{verbatim}
%\iffalse
%</verb>
%\fi

% \StopEventually{}

% \section{Implementation}
% \changes{0.0.1}{2023/01/28}{First draft.}
% \changes{0.0.2}{2023/01/29}{Proper parsing of grouping.}
% \changes{0.0.2}{2023/01/29}{Substitutions suggested for special symbols.}

% First, we process package options:
%    \begin{macrocode}
\RequirePackage{pgfopts}
\pgfkeys{
  /ebnf/.cd,
  bw/.store in=\ebnf@bw,
}
\ProcessPgfPackageOptions{/ebnf}
%    \end{macrocode}

% Then, we include a few packages, mostly to deal with \LaTeX{}3 expressions:
%    \begin{macrocode}
\RequirePackage{filecontentsdef}
\RequirePackage{expl3}
%    \end{macrocode}

% \begin{macro}{\ebnf@color}
% Then, we include \href{https://ctan.org/pkg/xcolor}{xcolor} to colorize the output a bit:
%    \begin{macrocode}
\makeatletter\ifdefined\ebnf@bw\else
  \RequirePackage{xcolor}
\fi
\newcommand\ebnf@color[2]
  {\ifdefined\ebnf@bw#2\else\textcolor{#1}{#2}\fi}
\makeatother
%    \end{macrocode}
% \end{macro}

% \begin{macro}{\terminal}
% \changes{0.0.2}{2023/01/29}{New command \texttt{\char`\\terminal} added, to enable rendering terminal symbols outside of the \texttt{ebnf} environment.}
% Then, we a command to render a single terminal:
%    \begin{macrocode}
\makeatletter
\newcommand\terminal[1]{%
  \relax\ifmmode\else\ttfamily\fi%
  \ebnf@color{gray}{"}#1\ebnf@color{gray}{"}}
\makeatother
%    \end{macrocode}
% \end{macro}

% \begin{macro}{\nonterminal}
% \changes{0.0.2}{2023/01/29}{New command \texttt{\char`\\nonterminal} added, to enable rendering non-terminal symbols outside of the \texttt{ebnf} environment.}
% Then, we a command to render a single non-terminal:
%    \begin{macrocode}
\makeatletter
\newcommand\nonterminal[1]
  {\(\ebnf@color{gray}{\langle}\textsf{#1}\ebnf@color{gray}{\rangle}\)}
\makeatother
%    \end{macrocode}
% \end{macro}

% Then, we define supplementary commands:
%    \begin{macrocode}
\makeatletter
\newcommand\ebnf@optional[1]
  {\ebnf@color{gray}{[}#1\ebnf@color{gray}{]}}
\newcommand\ebnf@repetition[1]
  {\ebnf@color{gray}{\{}#1\ebnf@color{gray}{\}}}
\newcommand\ebnf@grouping[1]
  {\ebnf@color{gray}{(}#1\ebnf@color{gray}{)}}
\ExplSyntaxOn
\newcommand\ebnf@terminal[1]{
  \tl_set:Nn \l_ebnf_tl { }
  \tl_set_rescan:Nno \l_ebnf_tl { } { #1 }
  \terminal{\l_ebnf_tl}
}
\ExplSyntaxOff
\newcommand\ebnf@to
  {\ebnf@color{gray}{\(\to\)}}
\newcommand\ebnf@alternation
  {\ebnf@color{gray}{\(\vert\)}}
\newcommand\ebnf@eol{\\}
\makeatother
%    \end{macrocode}

% \begin{macro}{ebnf}
% Then, we define the |ebnf| environment:
%    \begin{macrocode}
\ExplSyntaxOn
\cs_generate_variant:Nn \tl_replace_all:Nnn {Nx}
\NewDocumentEnvironment{ebnf}{}{\filecontentsdefmacro\l__ebnf_tmp_tl}{
  \endfilecontentsdefmacro
  \str_set:NV \l__ebnf_tmp_tl \l__ebnf_tmp_tl
  \str_set:Nx \l__ebnf_tmp_tl {\str_range:Nnn \l__ebnf_tmp_tl {1} {-2}}
  \regex_replace_all:nnN { \{(.+?)\} }
    {\c{ebnf@repetition}{\1}} \l__ebnf_tmp_tl
  \regex_replace_all:nnN { \((.+?)\) }
    {\c{ebnf@grouping}{\1}} \l__ebnf_tmp_tl
  \regex_replace_all:nnN { \[([^\]]+?)\] }
    {\c{ebnf@optional}{\1}} \l__ebnf_tmp_tl
  \regex_replace_all:nnN { <([A-Za-z][a-z-]+)> }
    {\c{nonterminal}{\1}} \l__ebnf_tmp_tl
  \regex_replace_all:nnN { "([^"]+)" }
    {\c{ebnf@terminal}{\1}} \l__ebnf_tmp_tl
  \regex_replace_all:nnN { \^^M\s*\| }
    {\^^M :=} \l__ebnf_tmp_tl
  \regex_replace_all:nnN { \| }
    {\c{ebnf@alternation}{}} \l__ebnf_tmp_tl
  \regex_replace_all:nnN { \^^M\s*:= }
    {\^^M \c{-}\c{hspace}{3em}\c{ebnf@alternation}{}} \l__ebnf_tmp_tl
  \regex_replace_all:nnN { := }
    {\c{ebnf@to}{}} \l__ebnf_tmp_tl
  \regex_replace_all:nnN { \^^M }
    {\c{ebnf@eol}{}} \l__ebnf_tmp_tl
  \tl_put_left:Nn \l__ebnf_tmp_tl {}
  \tl_put_right:Nn \l__ebnf_tmp_tl {}
  \l__ebnf_tmp_tl
}
\ExplSyntaxOff
%    \end{macrocode}
% \end{macro}

%    \begin{macrocode}
\endinput
%    \end{macrocode}

% \Finale

% \clearpage
% \clearpage

% \PrintChanges
% \clearpage
% \PrintIndex
