% \iffalse meta-comment
% SPDX-FileCopyrightText: Copyright (c) 2023-2026 Yegor Bugayenko
% SPDX-License-Identifier: MIT
% \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>
[2026-10-15 0.1.0 EBNF in Plain Text]
%</package>
%<*driver>
\documentclass{ltxdoc}
\usepackage[T1]{fontenc}
\usepackage{lmodern}
\usepackage{fail-fast}
\usepackage{silence}
\WarningFilter{latex}{Marginpar on page}
\usepackage{microtype}
\AddToHook{env/verbatim/begin}{\microtypesetup{protrusion=false}}
\PassOptionsToPackage{hyperindex=false}{hyperref}
\usepackage{href-ul}
\usepackage{bookmark}
\renewcommand{\RerunFileCheck}[3]{\AddToHook{enddocument/info}{#2}}
\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
%
% \textbf{\color{red}NB!}
% Large EBNF snippets may take too long to render!
%
% \section{Introduction}
%
% This package renders the
% \href{https://en.wikipedia.org/wiki/Extended_Backus%E2%80%93Naur_form}{Extended Backus-Naur Form}
% by means of a plain-text notation:
% \begin{docshot}
% \documentclass{minimal}
% \usepackage{naive-ebnf}
% \usepackage{mathtools}
% \begin{document}
% \begin{ebnf}
% <$\lambda$-Expr> := <Var> \\
%   || "$\lambda$" <Var> "." <Expr> \\
%   || "\char`\(" <Expr> <Expr> "\char`\)"
% \end{ebnf}
% \end{document}
% \end{docshot}

% \DescribeMacro{ebnf}
% The |ebnf| environment \emph{does not} apply any formatting to the paragraph; rather, it merely substitutes plain-text symbols, such as ``|:=|'' and ``|<Var>|'', with the appropriate \LaTeX{} commands.
% The following syntax is recognised within the |ebnf| environment:
% \begin{itemize}
% \item |:=| separates the left-hand side from the right-hand side of a production rule;
% \item |<...>| denotes a non-terminal (variable);
% \item |"..."| denotes a terminal symbol;
% \item |'...'| denotes a special, non-printable terminal symbol, such as |'EOL'|;
% \item \texttt{(...\char`\|...)} denotes a series of alternatives among which one is selected;
% \item |/.../| denotes a regular expression, such as |/[a-z]+/|;
% \item |[...]| denotes an optional substitution;
% \item |{...}| denotes a repetition of zero or more occurrences;
% \item |{...}+| denotes a repetition of one or more occurrences;
% \item \texttt{\char`\|\char`\|\char`\|} denotes an indent at the beginning of a line;
% \item \texttt{\char`\|\char`\|} denotes an indented vertical bar at the beginning of a line.
% \end{itemize}

% Brackets of all three kinds are recognised only when whitespace separates them from their content: \texttt{( <x> \textbar{} <y> )} is a group, whereas \texttt{(<x>\textbar<y>)} is left as plain text. The same rule is what keeps |"("| a terminal.

% \textbf{Attention}: Braces never print within a terminal: a balanced pair vanishes into a \TeX{} group, an unbalanced one stops the compilation. The symbols |(|, |)|, |<|, |>|, |[|, |]| and \texttt{\char`\|} print literally as long as no space is adjacent to them, as in |"("| or |"a<b"|; once surrounded by spaces, as in |"a < b"|, they are taken for EBNF syntax. The following substitutions are recommended in all other cases:
% \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 ``|}|'';
% \item |$\lbrack$| and |$\rbrack$| instead of ``|[|'' and ``|]|'';
% \item |$\vert$| instead of ``\texttt{\char`\|}''.
% \end{itemize}

% A still more agreeable rendering may be obtained through the following notation:
% \begin{itemize}
% \item |\char`\(| and |\char`\)| instead of ``|(|'' and ``|)|'';
% \item |\char`\<| and |\char`\>| instead of ``|<|'' and ``|>|'';
% \item |\char`\{| and |\char`\}| instead of ``|{|'' and ``|}|'';
% \item |\char`\[| and |\char`\]| instead of ``|[|'' and ``|]|''.
% \end{itemize}

% \DescribeMacro{width}
% The |ebnf| environment accepts an optional argument that determines the width allocated to the left-hand side of each rule
% (the default value being |4em|):
% \begin{docshot}[options={firstline=4,lastline=10}]
% \documentclass{minimal}
% \usepackage{naive-ebnf}
% \begin{document}\noindent
% This EBNF has a larger width of \\
% the left-hand side than usual: \par
% \begin{ebnf}[1.5in]
% <VeryLongVariable> := <X> | <Y> \\
% <X> := "X" 'EOL' \\
% <Y> := "Y" \\
% \end{ebnf}
% \end{document}
% \end{docshot}

% \DescribeMacro{\EbnfTerminal}
% \DescribeMacro{\EbnfNonTerminal}
% \DescribeMacro{\EbnfSpecial}
% Within the text, terminals, non-terminals, and special terminals may be typeset by means of three supplementary commands:
% \begin{docshot}[options={firstline=6,lastline=10}]
% \documentclass{article}
% \pagestyle{empty}
% \usepackage[paperwidth=3in]{geometry}
% \usepackage{naive-ebnf}
% \begin{document}
% The non-terminal \EbnfNonTerminal{Var}
% in $\lambda$-calculus may be equal
% to $v_1, v_2, \dots$. Application
% starts with \EbnfTerminal{(} and ends
% with \EbnfTerminal{)}.
% \end{document}
% \end{docshot}
% These commands are likewise admissible within math mode, for example:
% \begin{docshot}[options={firstline=6,lastline=9}]
% \documentclass{article}
% \pagestyle{empty}
% \usepackage[paperwidth=3in]{geometry}
% \usepackage{naive-ebnf}
% \begin{document}
% If $\EbnfTerminal{(} f_1
% \EbnfNonTerminal{$\lambda$-Var}
% \EbnfTerminal{)}$ is always true, then
% $f_1$ is a tautology.
% \end{document}
% \end{docshot}

% \DescribeMacro{\EbnfRegex}
% A regular expression is also admissible:
% \begin{docshot}[options={firstline=6,lastline=12}]
% \documentclass{article}
% \pagestyle{empty}
% \usepackage[paperwidth=4in]{geometry}
% \usepackage{naive-ebnf}
% \begin{document}
% \begin{ebnf}
% <data> := <bool> | <integer> | <byte> \\
% <bool> := "TRUE" | "FALSE" \\
% <integer> := /(+\char`\|-)?[0-9]+/ \\
% <byte> := /[0-9a-f]\char`\{2\char`\}/ \\
% <number> := /[1-9]+/ /[0-9]+/
% \end{ebnf}
% \end{document}
% \end{docshot}

% Special symbols print literally when they sit right next to the quotes:
% \begin{docshot}[options={firstline=5,lastline=9}]
% \documentclass{minimal}
% \usepackage[T1]{fontenc}
% \usepackage{naive-ebnf}
% \begin{document}
% \begin{ebnf}
% <X> := 'EOL' "'" "|" \\
% <Y> := ">" "<" "[" "]" "/" "/" \\
% <Z> := "\LaTeX" "\textdollar" \\
% \end{ebnf}
% \end{document}
% \end{docshot}

% Nested brackets are likewise handled correctly:
% \begin{docshot}[options={firstline=5,lastline=11}]
% \documentclass{minimal}
% \usepackage[T1]{fontenc}
% \usepackage{naive-ebnf}
% \begin{document}
% \begin{ebnf}
% % There is no meaning in this:
% <x> := ( "x" ( "y" | ( "z" | <z> ) ) ) \\
% <y> := [ [ "x1" ] { /[a-z]+/ } ] \\
% <z> := { { { <x> }+ <y> } <z> }+ \\
% <t> := [ <x> ] [ <y> ] \\
% \end{ebnf}
% \end{document}
% \end{docshot}


% The \texttt{\char`\|\char`\|\char`\|} sequence permits the indentation of text on a new line, thus enabling the breaking of long expressions:
% \begin{docshot}[options={firstline=5,lastline=9}]
% \documentclass{minimal}
% \usepackage[T1]{fontenc}
% \usepackage{naive-ebnf}
% \begin{document}
% \begin{ebnf}
% <x> := "beginning"   \\
% |||    ( <y> | <z> ) \\
% |||    "ending"      \\
% \end{ebnf}
% \end{document}
% \end{docshot}

% \section{Package Options}

% The behaviour of the package may be configured by means of several package options:

% \DescribeMacro{bw}
% By default, certain colours are employed in the rendered grammar. The |bw| package option, however, disables all colours and ensures that the grammar is rendered in black and white:
%\iffalse
%<*verb>
%\fi
\begin{verbatim}
\usepackage[bw]{naive-ebnf}
\end{verbatim}
%\iffalse
%</verb>
%\fi

% \DescribeMacro{trail}
% The |ebnf| environment performs a pre-processing step on the supplied \TeX{} commands before delegating their rendering to \LaTeX{}. Inspection of the output produced by this pre-processing can prove useful. The |trail| option, accompanied by a file name, instructs the package to save the post-processed content of every |ebnf| environment, in document order, to the designated file:
%\iffalse
%<*verb>
%\fi
\begin{verbatim}
\usepackage[trail=log.tex]{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.}
% \changes{0.0.5}{2023/02/04}{New package option \texttt{trail} added, to enable saving of the generated \TeX{} content to a file, for debugging purposes.}

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

% \begin{macro}{\ebnf@write}
% If the |trail| option is set, we allocate a single output stream for the trail file, since \TeX{} has only sixteen of them, open the file once, and close it at the end of the document, so that every |ebnf| environment appends to it:
%    \begin{macrocode}
\makeatletter
\ifdefined\ebnf@trail
  \newwrite\ebnf@write
  \immediate\openout\ebnf@write\ebnf@trail\relax
  \AtEndDocument{\immediate\closeout\ebnf@write}
\fi
\makeatother
%    \end{macrocode}
% \end{macro}

% Then, we include a few packages, mostly to deal with \LaTeX{}3 expressions:
%    \begin{macrocode}
\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}{\EbnfTerminal}
% \changes{0.0.2}{2023/01/29}{New command \texttt{\char`\\EbnfTerminal} added, to enable rendering terminal symbols outside of the \texttt{ebnf} environment.}
% \changes{0.0.3}{2023/01/30}{Quotes fixed in both text and math modes.}
% Then, we define a command to render a single terminal:
%    \begin{macrocode}
\makeatletter
\newcommand\EbnfTerminal[1]{{%
  \relax\ifmmode\else\ttfamily\fi%
  \ebnf@color{gray}{\relax\ifmmode\textsf{``}\else{\sffamily``}\fi}%
  #1%
  \ebnf@color{gray}{\relax\ifmmode\textsf{''}\else{\sffamily''}\fi}}}
\makeatother
%    \end{macrocode}
% \end{macro}

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

% \begin{macro}{\EbnfSpecial}
% \changes{0.0.6}{2023/05/27}{New command \texttt{\char`\\EbnfSpecial} added, to enable rendering of special non-printable terminal symbols outside of the \texttt{ebnf} environment.}
% Then, we define a command to render a single special non-printable terminal:
%    \begin{macrocode}
\makeatletter
\newcommand\EbnfSpecial[1]{{\relax\ifmmode\else\ttfamily\fi#1}}%
\makeatother
%    \end{macrocode}
% \end{macro}

% \begin{macro}{\EbnfRegex}
% \changes{0.0.8}{2023/07/11}{New command \texttt{\char`\\EbnfRegex} added, to enable rendering of regular expressions outside of the \texttt{ebnf} environment.}
% Then, we define a command to render a regular expression:
%    \begin{macrocode}
\makeatletter
\newcommand\EbnfRegex[1]{{\relax\ifmmode\else\ttfamily\fi/#1/}}%
\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[2][]
  {\ebnf@color{gray}{\{}#2\ebnf@color{gray}{\}\(^{\scriptscriptstyle #1}\)}}
\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:Nnn \l_ebnf_tl {} { #1 }
  \EbnfTerminal{\l_ebnf_tl}
}
\newcommand\ebnf@special[1]{
  \tl_set:Nn \l_ebnf_tl {}
  \tl_set_rescan:Nnn \l_ebnf_tl {} { #1 }
  \EbnfSpecial{\l_ebnf_tl}
}
\newcommand\ebnf@nonterminal[1]{
  \tl_set:Nn \l_ebnf_tl {}
  \tl_set_rescan:Nnn \l_ebnf_tl {} { #1 }
  \EbnfNonTerminal{\l_ebnf_tl}
}
\newcommand\ebnf@regexp[1]{
  \tl_set:Nn \l_ebnf_tl {}
  \tl_set_rescan:Nnn \l_ebnf_tl {} { #1 }
  \EbnfRegex{\l_ebnf_tl}
}
\ExplSyntaxOff
\newcommand\ebnf@to
  {\ebnf@color{gray}{\(\to\)}}
\newcommand\ebnf@alternation
  {\ebnf@color{gray}{\(\vert\)}}
\makeatother
%    \end{macrocode}

% \begin{macro}{ebnf}
% \changes{0.0.4}{2023/02/03}{Any symbols are allowed inside \texttt{\char`\\EbnfNonTerminal} commands and inside the \texttt{ebnf} environment, where non-terminals are mentioned.}
% \changes{0.0.11}{2023/07/12}{Many bugs fixed in the area of regular expression matching.}
% \changes{0.0.14}{2023/08/08}{One-or-more repetition introduced with \texttt{\char`\{...\char`\}+} syntax.}
% \changes{0.0.15}{2023/08/11}{The \texttt{iteration} removed, only \texttt{repetition} is left, with the second optional parameter.}
% Then, we define the |ebnf| environment:
%    \begin{macrocode}
\ExplSyntaxOn
\makeatletter
\NewDocumentEnvironment{ebnf}{O{4em}+b}
  {\tl_set:Nn\ebnf_tmp{#2}}
  {%
  \regex_replace_all:nnN
    { ([^\s])/([^\s]) } {\1\\slash{}\2} \ebnf_tmp%
  \regex_replace_all:nnN
    { ([^\s])< } {\1\\textless{}} \ebnf_tmp%
  \regex_replace_all:nnN
    { >([^\s]) } {\\textgreater{}\1} \ebnf_tmp%
  \regex_replace_all:nnN
    { ([^\s])'([^\s]) } {\1\\textquotesingle{}\2} \ebnf_tmp%
  \regex_replace_all:nnN { \|\|\| }%
    {\c{makebox}[#1][r]{ }} \ebnf_tmp%
  \regex_replace_all:nnN
    { ([^\s])\|([^\s]) } {\1\\textbar{}\2} \ebnf_tmp%
  %
  \regex_replace_all:nnN
    { /(.+?)/ }%
    {\c{ebnf@regexp}{\1}} \ebnf_tmp%
  \cs_undefine:N\ebnf_curled%
  \cs_new:Npn\ebnf_curled{%
    \regex_replace_all:nnNT
    { \{\s(([^\s]*(\s[^\}\{]|\s(\}|\{)[^\s])?)*)\s\}([\+\*])? }%
    {\c{ebnf@repetition}[\5]{\1}} \ebnf_tmp \ebnf_curled}%
  \ebnf_curled%
  \cs_undefine:N\ebnf_brackets%
  \cs_new:Npn\ebnf_brackets{%
    \regex_replace_all:nnNT
    { \(\s(([^\s]*(\s[^\)\(]|\s(\)|\()[^\s])?)*)\s\) }%
    {\c{ebnf@grouping}{\1}} \ebnf_tmp \ebnf_brackets}%
  \ebnf_brackets%
  \cs_undefine:N\ebnf_squares%
  \cs_new:Npn\ebnf_squares{%
    \regex_replace_all:nnNT
    { \[\s(([^\s]*(\s[^\]\[]|\s(\]|\[)[^\s])?)*)\s\] }%
    {\c{ebnf@optional}{\1}} \ebnf_tmp \ebnf_squares}%
  \ebnf_squares%
  \regex_replace_all:nnN { (<[^>]+?>\s:=) }%
    {\c{makebox}[#1][r]{\1}} \ebnf_tmp%
  \regex_replace_all:nnN { <(.+?)> }%
    {\c{ebnf@nonterminal}{\1}} \ebnf_tmp%
  \regex_replace_all:nnN { "(.+?)" }%
    {\c{ebnf@terminal}{\1}} \ebnf_tmp%
  \regex_replace_all:nnN { '(.+?)' }%
    {\c{ebnf@special}{\1}} \ebnf_tmp%
  \regex_replace_all:nnN { \|(\|) }%
    {\c{makebox}[#1][r]{ \1 }} \ebnf_tmp%
  \regex_replace_all:nnN { \| }%
    {\c{ebnf@alternation}{}} \ebnf_tmp%
  \regex_replace_all:nnN { := }%
    {\c{ebnf@to}{}} \ebnf_tmp%
  \tl_put_left:Nn \ebnf_tmp {\noindent}
  \ifdefined\ebnf@trail%
    \immediate\write\ebnf@write{\unexpanded\expandafter{\ebnf_tmp}}%
    \message{naive-ebnf:\space pre-processed\space TeX
      \space saved\space to\space "\ebnf@trail"^^J}%
  \fi%
  \ebnf_tmp}
\makeatother
\ExplSyntaxOff
%    \end{macrocode}
% \end{macro}

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

% \Finale

% \clearpage
% \clearpage

% \PrintChanges
% \clearpage
% \PrintIndex
