diff options
Diffstat (limited to 'Master')
-rw-r--r-- | Master/texmf-dist/doc/latex/simplebnf/simplebnf-doc.pdf | bin | 156004 -> 80558 bytes | |||
-rw-r--r-- | Master/texmf-dist/doc/latex/simplebnf/simplebnf-doc.tex | 645 | ||||
-rw-r--r-- | Master/texmf-dist/tex/latex/simplebnf/simplebnf.sty | 577 |
3 files changed, 968 insertions, 254 deletions
diff --git a/Master/texmf-dist/doc/latex/simplebnf/simplebnf-doc.pdf b/Master/texmf-dist/doc/latex/simplebnf/simplebnf-doc.pdf Binary files differindex f0b419d363f..c8fb4b8444a 100644 --- a/Master/texmf-dist/doc/latex/simplebnf/simplebnf-doc.pdf +++ b/Master/texmf-dist/doc/latex/simplebnf/simplebnf-doc.pdf diff --git a/Master/texmf-dist/doc/latex/simplebnf/simplebnf-doc.tex b/Master/texmf-dist/doc/latex/simplebnf/simplebnf-doc.tex index 72fa982e240..1181d3f2033 100644 --- a/Master/texmf-dist/doc/latex/simplebnf/simplebnf-doc.tex +++ b/Master/texmf-dist/doc/latex/simplebnf/simplebnf-doc.tex @@ -1,18 +1,78 @@ -\documentclass[a4paper]{article} +\documentclass[11pt]{article} +\usepackage{geometry} +\geometry{a4paper,top=1in,bottom=1in,left=1.2in,right=1.2in} -\usepackage{simplebnf} -\usepackage{hyperref} +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% Font setup %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% + +% Works for pdfLaTeX, XeLaTeX, and LuaLaTeX +\usepackage{libertine} + +% Math/Mono font setup +\ExplSyntaxOn +\bool_if:nTF { \sys_if_engine_xetex_p: || \sys_if_engine_luatex_p: } + { + % Load mathtools before unicode-math + \usepackage{mathtools} + \usepackage[ + math-style=ISO, + warnings-off={mathtools-colon, mathtools-overbracket}, + ]{unicode-math} + \setmathfont{LibertinusMath-Regular.otf}[Scale = MatchUppercase] + \setmathfont{Garamond-Math.otf}[ + Scale = MatchUppercase, + range = {\Coloneq} + ] + + \setmonofont{Inconsolatazi4}[ + Extension = .otf, + UprightFont = *-Regular, + BoldFont = *-Bold, + Scale=MatchLowercase, + AutoFakeSlant = 0.2, + ] + } + { + \usepackage[libertine]{newtxmath} + \usepackage{inconsolata} + } +\ExplSyntaxOff + +% Some fancy symbols (prevent being overwritten by math fonts) +\AtBeginDocument{% + \usepackage[ + only, fatsemi, fatslash, talloblong, rightarrowtriangle + ]{stmaryrd}% +} + + +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% Presentations %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% \usepackage{tcolorbox} \tcbuselibrary{listings,breakable} -\tcbset{listing engine=listings,colframe=black,colback=white,size=small} +\tcbset{listing engine=listings, colframe=black, colback=white, size=small} + +\NewTCBListing { example } { !O{} !s } + { + sharp corners, + IfBooleanTF = { #2 } + { listing above text } + { listing side text }, + fontlower = \small, + #1 + } + +\NewTCBListing { listing } { !O{} } + { sharp corners, listing only, #1 } + \NewDocumentEnvironment {exampleside} {} - { \tcblisting{listing side text,righthand width=.55\textwidth} } + { \tcblisting{listing side text,righthand width=.5\textwidth} } { \endtcblisting } -\NewDocumentCommand \cmd { m } {\texttt{\textbackslash#1}} - \NewDocumentEnvironment { presentcommand } { b } {% \vspace*{0.5\baselineskip}\noindent\fbox{% @@ -22,84 +82,432 @@ } { } + +\NewDocumentCommand \cmd { m } {\texttt{\char`\\#1}} \NewDocumentCommand \env { m m } { \texttt{% - \textbackslash begin\{#1\} \textrm{#2}% - \textbackslash end\{#1\}% + \char`\\begin\{#1\} \textrm{#2}% + \char`\\end\{#1\}% }% } + +\usepackage{tabularray} +\UseTblrLibrary{booktabs} +% \fakeverb +\usepackage{codehigh} + + +\newcommand*{\secref}[1]{\hyperref[{#1}]{section~\ref*{#1} -- \emph{\nameref*{#1}}}} +\newcommand*{\subsecref}[1]{\hyperref[{#1}]{subsection~\ref*{#1} -- \emph{\nameref*{#1}}}} +\newcommand*{\subsubsecref}[1]{\hyperref[{#1}]{\nameref*{#1}}} +\newcommand*{\parref}[1]{\hyperref[{#1}]{\nameref*{#1}}} + + +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% simplebnf %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% + +\usepackage{simplebnf} + + +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% Miscellaneous %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% + +\usepackage{hologo} +\newcommand*{\XeLaTeX}{\hologo{XeLaTeX}} +\newcommand*{\LuaLaTeX}{\hologo{LuaLaTeX}} + +% ``Make sure it comes *last* of your loaded packages" +\usepackage[pdfborder={0 0 0}, colorlinks=true]{hyperref} + + +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% Metadata %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% + \title{% \textsf{simplebnf} --- A simple package to format Backus-Naur form% - \footnote{This file describes v0.3.2.}} + \footnote{This file describes v1.0.0.}} \author{Jay Lee\footnote{E-mail: % \href{mailto:jaeho.lee@snu.ac.kr}{\texttt{jaeho.lee@snu.ac.kr}}}} -\date{2023/01/07} +\date{2023/11/25} + \begin{document} \maketitle -This package provides a simple way to typeset grammars written in Backus-Naur form (BNF). - -\begin{presentcommand} - \cmd{SimpleBNFDefEq} -\end{presentcommand} -This command is used to typeset the definition symbol separate a nonterminal from its productions. It defaults to \SimpleBNFDefEq. It can be redefined using \verb|RenewDocumentCommand|. - -\begin{presentcommand} - \cmd{SimpleBNFDefOr} -\end{presentcommand} -This command is used to typeset the separator symbol between productions. It defaults to \SimpleBNFDefOr. It can be redefined using \verb|RenewDocumentCommand|. - -\begin{presentcommand} - \cmd{SimpleBNFStretch} -\end{presentcommand} -This command is used to control the vertical spacing between consecutive rules. -It defaults to 0. -It can be redefined using \verb/Renewdocumentcommand/. - -\begin{presentcommand} - \cmd{bnfexpr} -\end{presentcommand} -This command is used when typesetting the BNF nonterminal and productions. It defaults to a wrappers around \cmd{texttt}. It can be redefined to customized output using \verb|RenewDocumentCommand|. - -\begin{presentcommand} - \cmd{bnfannot} -\end{presentcommand} -This command is used when typesetting the annotations on nonterminals and productions. It defaults to a wrappers around \cmd{textit}. It can be redefined to customized output using \verb|RenewDocumentCommand|. - -\begin{presentcommand} - \env{bnfgrammar}{text} -\end{presentcommand} -can be used to typeset BNF grammars. The \textit{text} inside the environment should be formatted as: -\begin{verbatim} - term1 ::= rhs1 + +\vfill +This package provides a simple way for typesetting grammars in Backus-Naur form (BNF). +It features a flexible configuration system, allowing for the customization of the domain-specific language (DSL) used in typesetting the grammar. +Additionally, the package comes with sensible defaults. + +Below is the metagrammar of the DSL as defined in this package, which is typeset using the package itself.\footnote{The code is shown in \nameref{sec:appendix}.} +\begin{tcolorbox}[sharp corners] + \begin{center} + \begin{bnf}( + prod-delim = ;;;, + new-line-delim = !, + single-line-delim = ?, + comment = //, + relation = {:::=|:in:}, + relation-sym-map = + { + {:::=} = $\Coloneqq$, + {:in:} = $\in$, + }, + )[ + colspec = lrcll, + column{2} = {mode=dmath}, + column{4} = {mode=text, font=\ttfamily}, + ] + G // Gramar :::= + ! $P$ // production + ! $P \fatsemi {}$ // production w/ a trailing delimiter + ! $P \fatsemi G$ // production sequence + ;;; + P // Production :::= $L \rightarrowtriangle R$ + ;;; + L // LHS :::= + ! $v$ // metavariable + ! $v\!\fatslash\,c$ // annotated metavariable + ;;; + R // RHS :::= + ! $\talloblong$ // delimiter + ! $A \talloblong R$ // alternative sequence + ;;; + A // Alternative :::= + ! $f$ // syntactic form + ! $f\!\fatslash\,c$ // annotated syntactic form + ;;; + \fatsemi // Prod. delimiter :::= + ! ;; // default symbol + ! $\cdots$ // user-defined + ;;; + \rightarrowtriangle // Rule relation :::= + ! ::= // $\Coloneqq$ + ! -> // $\to$ + ! \texttt{\char`\\in} // $\in$ + ! $\cdots$ // user-defined + ;;; + \fatslash // Annot. symbol :::= + ! : // default symbol + ! $\cdots$ // user-defined + ;;; + \talloblong // Alt. delimiter :::= + ! | // new-line delimiter + ! || // single-line delimiter + ! $\cdots$ // user-defined + ;;; + v, f, c :in: \textsf{\TeX{} tl} // valid \TeX{} token lists + ;;; + \end{bnf} + \end{center} +\end{tcolorbox} +\vfill + +\section{Tutorial for the impatient}\label{sec:tutorial} +Typesetting a grammar is as simple as typing the BNF grammar in the \verb/bnf/ environment: +\begin{example}[righthand width=.52\linewidth] +\begin{center} +\begin{bnf} + $\tau$ : \textsf{Type} ::= + | \texttt{num} : numbers + | \texttt{str} : strings + ;; + $e$ : \textsf{Expr} ::= + | $x$ : variable + | $n$ : numeral + | \texttt{$e$ + $e$} : addition + | \texttt{$e$ * $e$} : multiplication + | \texttt{$e$ \textasciicircum{} $e$} : concatenation + | \texttt{len($e$)} : length + | \texttt{let $x$ = $e_1$ in $e_2$} : definition +\end{bnf} +\end{center} +\end{example} + +Typically, each column in the BNF grammar has the same font style. +In the example above, the comments in the first column, e.g., \textsf{Type}, is typeset in sans-serif, and the second column in math mode, e.g., $\tau$. +The column for the right-hand side is (mostly) typeset in typewriter font with a bit of non-terminals like $e$ typeset in math mode. + +\textsf{simplebnf} provides a straightforward way to customize the font styles for each column\footnote{Note that you must provide a manual alignment specification with the key \texttt{colspec}.}: +\begin{listing} +\begin{bnf}[ + colspec = {llcll}, + column{1} = {font = \sffamily}, + column{2} = {mode = dmath}, + column{4} = {font = \ttfamily}, +] + \tau : Type ::= + | num : numbers + | str : strings ;; - term2 ::= rhs2 + e : Expr ::= + | $x$ : variable + | $n$ : numeral + | $e$ + $e$ : addition + | $e$ * $e$ : multiplication + | $e$ \textasciicircum{} $e$ : concatenation + | len($e$) : length + | let $x$ = $e_1$ in $e_2$ : definition +\end{bnf} +\end{listing} +If you find yourself using the same configuration repeatedly, you can fix the desired configuration using \cmd{SetBNFLayout}. +Once set using \cmd{SetBNFLayout}, the configuration is applied to all subsequent \verb/bnf/ environments: +\begin{listing} +% Some where above... +\SetBNFLayout{ + colspec = {llcll}, + column{1} = {font = \sffamily}, + column{2} = {mode = dmath}, + column{4} = {font = \ttfamily}, +} +% ... +\begin{bnf} + \tau : Type ::= + | num : numbers + | str : strings ;; - ... - termk ::= rhsk -\end{verbatim} -where each of the \textit{rhs} represents alternative syntactic forms of the \textit{term}. An annotation may accompany each alternative in which case the alternative must be separated from its annotation with a colon (\verb/:/). If you don't need annotations, simply omit the colons and annotations altogether. The alternatives themselves are separated using the pipe symbol (\verb/|/). + e : Expr ::= + | $x$ : variable + | $n$ : numeral + | $e$ + $e$ : addition + | $e$ * $e$ : multiplication + | $e$ \textasciicircum{} $e$ : concatenation + | len($e$) : length + | let $x$ = $e_1$ in $e_2$ : definition +\end{bnf} +\end{listing} -A sample code and the result is shown below: -\begin{exampleside} - \begin{bnfgrammar} - a \in \textit{Vars} - ;; - expr ::= - expr + term : sum - | term : term - ;; - term ::= - term * a : product - | a : variable - \end{bnfgrammar} -\end{exampleside} +Since these customizations are provided by the backend \textsf{tabularray}\footnote{\url{https://github.com/lvjr/tabularray}}, consult its documentation for more details. +The corresponding command is \cmd{SetTblrInner}. + +For more advanced customization, such as changing default delimiters and symbols, continue to \secref{sec:config}. -Annotations can also be provided on left-hand sides, to label the nonterminal instead of a specific production. -\begin{exampleside} +\section{Configuration}\label{sec:config} +While the default configuration should be sufficient for most use cases, some grammars may require using different delimiters and symbols. +For example, the language itself may contain \verb/->/ as a syntactic form, which conflicts with one of the default symbols for the rule relation. + +To this end, \textsf{simplebnf} provides a number of options to configure the DSL used to typeset the grammar. +Some examples of customizable symbols include the delimiters for productions and alternatives, the relation symbol between the left-hand side and the right-hand side, the annotation symbol, and more. + +The default configuration is shown in \autoref{tab:config}. +\begin{longtblr}[ + caption = {Default key-values for the configuration of \textsf{simplebnf}.}, + label = {tab:config} +]{ + colspec={ll}, + row{2-Z}={font=\ttfamily} +} + \toprule + Key & Default value \\ + \midrule + prod-delim & ;;\\ + new-line-delim & \fakeverb{\|}\\ + single-line-delim & //\\ + comment & :\\ + relation & \fakeverb{{::=|->|:in:}}\\ + relation-sym-map & { + \{\\ + \quad\fakeverb{{::=} = {\ensuremath{\Coloneqq}},}\\ + \quad\fakeverb{{->} = {\ensuremath{\to}},}\\ + \quad\fakeverb{{:in:} = {\ensuremath{\in}},}\\ + \} + }\\ + or-sym & \fakeverb{$|$}\\ + prod-sep & 2pt\\ + row-sep & 0pt\\ + \bottomrule +\end{longtblr} + +The key-value list can be provided as an optional argument to the \verb/bnf/ environment, wrapped in \verb/()/. +The optional key-value list for the layout---wrapped in \verb/[]/---should be provided after the configuration key-value list. +See \subsecref{subsec:layout} for more details. + +An exmaple of customizing the configuration is shown below: +\begin{example}* +\begin{center} +\begin{bnf}( + prod-delim = {--}, + new-line-delim = {\&}, + single-line-delim = {\&\&}, + comment = {//}, + relation-sym-map = + { + {->} = {\ensuremath{\hookrightarrow}}, + {:in:} = {\ensuremath{\in}}, + }, + or-sym = {}, +)[ + colspec = {llcll}, + column{1} = {font = \sffamily}, + column{2} = {mode = dmath}, + column{4} = {font = \ttfamily}, +] + \tau // Type -> + & num // numbers + & str // strings + -- + e // Expr -> + & $x$ // variable + & $n$ // numeral + & $e$ + $e$ // addition + & $e$ * $e$ // multiplication + & $e$ \textasciicircum{} $e$ // concatenation + & len($e$) // length + & let $x$ = $e_1$ in $e_2$ // definition +\end{bnf} +\end{center} +\end{example} + + +Furthermore, the configuration can be set globally using \cmd{SetBNFConfig}, similar to \cmd{SetBNFLayout}: +\begin{listing} +% Some where above... +\SetBNFConfig{ + prod-delim = {--}, + new-line-delim = {\&}, + single-line-delim = {\&\&}, + comment = {//}, + relation-sym-map = + { + {->} = {\ensuremath{\hookrightarrow}}, + {:in:} = {\ensuremath{\in}}, + }, + or-sym = {}, +} +% ... +\begin{bnf}[ + colspec = {llcll}, + column{1} = {font = \sffamily}, + column{2} = {mode = dmath}, + column{4} = {font = \ttfamily}, +] + \tau // Type -> + & num // numbers + & str // strings + -- + e // Expr -> + & $x$ // variable + & $n$ // numeral + & $e$ + $e$ // addition + & $e$ * $e$ // multiplication + & $e$ \textasciicircum{} $e$ // concatenation + & len($e$) // length + & let $x$ = $e_1$ in $e_2$ // definition +\end{bnf} +\end{listing} + + +\subsection{Production delimiter} +The default production delimiter \verb/prod-delim/ is \verb/;;/. +This will separate different productions in the same grammar. + +\subsection{New-line alternative delimiter} +The default new-line alternative delimiter \verb/new-line-delim/ is \verb/\|/, which will actually match verbatim \verb/\|/ in the grammar. +This will separate different alternatives into different lines in the same production. + +\subsection{Single-line alternative delimiter} +The default single-line alternative delimiter \verb/single-line-delim/ is \verb|//|. +The difference between the single-line alternative delimiter and the new-line alternative delimiter is that the former will not add a new line after the delimiter. + +For consecutive alternatives delimited by the single-line alternative delimiter, only the last of them can be annotated. + +Note that the single-line alternative delimiter must not contain the new-line alternative delimiter as a substring. + +\subsection{Annotation delimiter} +The default annotation delimiter \verb/comment/ is \verb/:/. +This will separate the syntactic form and the annotation within the alternative. + +\subsection{Rule relations and the symbol map} +\textsf{simplebnf} provides a \verb/::=/, \verb/->/, and \verb/:in:/ for the rule relation between the left-hand side and the right-hand side. +Each of them is mapped to a symbol in the \verb/relation-sym-map/ key-value list; By default, they are mapped to $\Coloneqq$ (\verb/\ensuremath{\Coloneqq}/), $\to$ (\verb/\ensuremath{\to}/), and $\in$ (\verb/\ensuremath{\in}/), respectively. + +To provide a custom relation symbol, provide the desired symbol and the mapping to the \verb/relation-sym-map/ key-value list and to the \verb/relation/. + +\subsection{Or symbol} +The default or symbol \verb/or-sym/ is \verb/$|$/. +Probably the most common use case for this option is to remove the or symbol altogether by setting it to \verb/{}/. + +\subsection{Production separation} +The default production separation \verb/prod-sep/ is \verb/2pt/. +This will add a vertical space between the productions. + +\subsection{Row separation} +The default row separation \verb/row-sep/ is \verb/0pt/. +This will add a vertical space between the alternatives in the same production. + +\subsection{Layout}\label{subsec:layout} +As mentioned in \secref{sec:tutorial}, the layout of the \verb/bnf/ environment can be customized per-enviroment using the key-value list in the optional argument wrapped in \verb/[]/. +When provided, these should be provided after the configuration key-value list, which is wrapped in \verb/()/. + +The optional key-value list for the layout is passed to the backend \textsf{tabularray} package. + +An example of customizing the layout is shown below: +\begin{example}[righthand width=.53\linewidth] +\begin{center} +\begin{bnf}( + prod-delim = {--}, + or-sym = {}, + prod-sep = 5pt, +)[ + colspec = {|[2pt, gray5]llcll}, + column{1} = {font = \sffamily}, + column{2} = {mode = dmath}, + column{4} = {font = \ttfamily}, + column{5} = {font = \itshape}, + cell{9}{4} = {cmd = \fbox}, + row{-} = {bg = azure9}, +] + \tau : Type ::= + | num : numbers + | str : strings + -- + e : Expr ::= + | $x$ : variable + | $n$ : numeral + | $e$ + $e$ : addition + | $e$ * $e$ : multiplication + | $e$ \textasciicircum{} $e$ : concatenation + | len($e$) : length + | let $x$ = $e_1$ in $e_2$ : definition +\end{bnf} +\end{center} +\end{example} + + +\section{Caveats} +The choices of delimiters and symbols should be carefully made, in order to avoid conflicts with the grammar of the target language and our meta-language, \TeX{}. + +Another subtlety to consider while customizing delimiters, is that certain delimiters not contain the others as a substring. +For instance, as mentioned in \secref{sec:config}, the single-line alternative delimiter must not contain the new-line alternative delimiter as a substring. + +When using custom OpenType fonts with the \verb/unicode-math/ package in \XeLaTeX{} or \LuaLaTeX{}, beware of the fact that many fonts lack the \verb/\Coloneqq/ symbol, $\Coloneqq$. +The easiest way to fix this is to actually use a font that provides the symbol, such as \verb/Garamond-Math/, which comes with the \TeX{}Live installation\footnote{\fakeverb{\Coloneq} is not a typo of \fakeverb{\Coloneqq}.}. +\begin{listing} +\setmathfont{Garamond-Math.otf}[ + Scale = MatchUppercase, + range = {\Coloneq} +] +\end{listing} + + +\section{The deprecated \texttt{bnfgrammar} environment} +Prior to version 1.0.0, the package provided a sole environment \verb/bnfgrammar/ for typesetting grammars. +While it is still available, it is now deprecated and will be removed in a future release. +Using it will result in a deprecation warning. + +To migrate to the new \verb/bnf/ environment, simply replace the \verb/bnfgrammar/ environment with \verb/bnf/, and replace \verb/\in/ with \verb/:in:/ and \verb/||/ with \verb|//|. +Moreover, the new environment does not center the grammar by default, so wrapping it in a \verb/center/ environment is necessary to reproduce the same output. +While there will be some differences in the spacings and the font styles, it is an easy fix. +See \secref{sec:tutorial} for more details. + +Below is an example of using the deprecated \verb/bnfgrammar/ environment, left here for historical reasons. +\begin{example} \begin{bnfgrammar} a : Variables \in \textit{Vars} ;; @@ -107,48 +515,71 @@ Annotations can also be provided on left-hand sides, to label the nonterminal in expr + term | term ;; - term ::= - term * a - | a - \end{bnfgrammar} -\end{exampleside} - -You can also provide an optional specification to the grammar environment, to redefine alignment or spacing. -\begin{tcblisting}{text above listing} - \begin{bnfgrammar}[lr@{\hspace{4pt}}c@{\hspace{2pt}}ll] - a : Variables \in \textit{Vars} - ;; - expr ::= - expr + term : sum - | term : term - ;; - term ::= - term * a : product - | a : variable - \end{bnfgrammar} -\end{tcblisting} - -If you want to typeset multiple productions on a single line, you can use double vertical bars by default. -\begin{exampleside} - \begin{bnfgrammar} - a \in \textit{Vars} - ;; - expr ::= expr + term || term - ;; term ::= term * a || a \end{bnfgrammar} -\end{exampleside} +\end{example} -The second and third optional arguments specify regular expressions for the line-breaking and non-breaking RHS seperators: -\begin{tcblisting}{text above listing} - \begin{bnfgrammar}[llcll][\|\|][\|] - a \in \textit{Vars} - ;; - expr ::= expr + term | term - ;; - term ::= term * a - || a - \end{bnfgrammar} -\end{tcblisting} + +\section*{Appendix}\label{sec:appendix} +The following is the code used to typeset the metagrammar of the \textsf{simplebnf} DSL in the first page. +\begin{listing}[breakable] +\begin{bnf}( + prod-delim = ;;;, + new-line-delim = !, + single-line-delim = ?, + comment = //, + relation = {:::=|:in:}, + relation-sym-map = + { + {:::=} = $\Coloneqq$, + {:in:} = $\in$, + }, +)[ + colspec = lrcll, + column{2} = {mode=dmath}, + column{4} = {mode=text, font=\ttfamily}, +] + G // Gramar :::= + ! $P$ // production + ! $P \fatsemi {}$ // production w/ a trailing delimiter + ! $P \fatsemi G$ // production sequence +;;; + P // Production :::= $L \rightarrowtriangle R$ +;;; + L // LHS :::= + ! $v$ // metavariable + ! $v\!\fatslash\,c$ // annotated metavariable +;;; + R // RHS :::= + ! $\talloblong$ // delimiter + ! $A \talloblong R$ // alternative sequence +;;; + A // Alternative :::= + ! $f$ // syntactic form + ! $f\!\fatslash\,c$ // annotated syntactic form +;;; + \fatsemi // Prod. delimiter :::= + ! ;; // default symbol + ! $\cdots$ // user-defined +;;; + \rightarrowtriangle // Rule relation :::= + ! ::= // $\Coloneqq$ + ! -> // $\to$ + ! \texttt{\char`\\in} // $\in$ + ! $\cdots$ // user-defined +;;; + \fatslash // Annot. symbol :::= + ! : // default symbol + ! $\cdots$ // user-defined +;;; + \talloblong // Alt. delimiter :::= + ! | // new-line delimiter + ! || // single-line delimiter + ! $\cdots$ // user-defined +;;; + v, f, c :in: \textsf{\TeX{} tl} // valid \TeX{} token lists +;;; +\end{bnf} +\end{listing} \end{document} diff --git a/Master/texmf-dist/tex/latex/simplebnf/simplebnf.sty b/Master/texmf-dist/tex/latex/simplebnf/simplebnf.sty index 82d39139aa8..5ddb566e28c 100644 --- a/Master/texmf-dist/tex/latex/simplebnf/simplebnf.sty +++ b/Master/texmf-dist/tex/latex/simplebnf/simplebnf.sty @@ -8,191 +8,474 @@ %% Released under the MIT License. %% --------------------------------------------------------------------------- %% +\NeedsTeXFormat{LaTeX2e}[2018-04-01] +\providecommand\DeclareRelease[3]{} +\providecommand\DeclareCurrentRelease[2]{} +\DeclareRelease{0.3.2}{2023-01-07}{simplebnf-2023-01-07.sty} +\DeclareCurrentRelease{}{2023-11-25} + \RequirePackage{expl3,xparse} -% mathtools is needed for the \Coloneqq simbol +% The new backend is tabularray +\RequirePackage{tabularray} +% mathtools is needed for the \Coloneqq symbol \RequirePackage{mathtools} + \ProvidesExplPackage {simplebnf} - {2023/01/07} - {0.3.2} + {2023-11-25} + {1.0.0} {A simple package to format Backus–Naur form} -\cs_generate_variant:Nn \regex_split:nnNTF {nVNTF} -\cs_generate_variant:Nn \regex_split:NnN {NVN} -\NewDocumentCommand\SimpleBNFDefEq{}{\ensuremath{\Coloneqq}} +\tl_new:N \l__simplebnf_prod_delim_tl +\tl_new:N \l__simplebnf_new_line_delim_tl +\tl_new:N \l__simplebnf_single_line_delim_tl +\tl_new:N \l__simplebnf_comment_tl +\tl_new:N \l__simplebnf_or_sym_tl +\tl_new:N \l__simplebnf_relation_tl +\dim_new:N \l__simplebnf_prod_sep_dim +\dim_new:N \l__simplebnf_row_sep_dim +\prop_new:N \l__simplebnf_relation_sym_map_prop -\NewDocumentCommand\SimpleBNFDefOr{}{\ensuremath{|}} +\keys_define:nn { simplebnf } + { + prod-delim .tl_set:N = \l__simplebnf_prod_delim_tl, + new-line-delim .tl_set:N = \l__simplebnf_new_line_delim_tl, + single-line-delim .tl_set:N = \l__simplebnf_single_line_delim_tl, + comment .tl_set:N = \l__simplebnf_comment_tl, + relation .tl_set:N = \l__simplebnf_relation_tl, + relation-sym-map .code:n = + { \prop_set_from_keyval:Nn \l__simplebnf_relation_sym_map_prop { #1 } }, + or-sym .tl_set:N = \l__simplebnf_or_sym_tl, + prod-sep .dim_set:N = \l__simplebnf_prod_sep_dim, + row-sep .dim_set:N = \l__simplebnf_row_sep_dim, + } -\NewDocumentCommand\SimpleBNFStretch{}{0em} +% default key-values +\keys_set:nn { simplebnf } + { + prod-delim = ;;, + new-line-delim = \|, + single-line-delim = //, + comment = :, + relation = {::=|->|:in:}, + relation-sym-map = + { + {::=} = {\ensuremath{\Coloneqq}}, + {->} = {\ensuremath{\to}}, + {:in:} = {\ensuremath{\in}}, + }, + or-sym = $|$, + prod-sep = 2pt, + row-sep = 0pt, + } -\seq_new:N \l__input_seq -\seq_new:N \l__term_seq -\tl_new:N \l__term_tl -\tl_new:N \l__keypairs_tl -\tl_new:N \l__table_tl -\seq_new:N \l__keypairs_seq -\bool_new:N \l__first_rhs -\regex_new:N \g_simplebnf_rhs_newline_r -\regex_new:N \g_simplebnf_rhs_nb_r +% bnf environment is internally a tabularray environment +\NewTblrEnviron{@simplebnf_tblr_env} +\SetTblrInner[@simplebnf_tblr_env]{rowsep=\l__simplebnf_row_sep_dim} + + +% The main token list that stores the table. +\tl_new:N \l__simplebnf_table_tl + +\seq_new:N \l__simplebnf_lhs_rhs_pair_seq +\tl_new:N \l__simplebnf_lhs_tl +\tl_new:N \l__simplebnf_matched_relation_tl +\tl_new:N \l__simplebnf_rhs_tl + +\seq_new:N \l__simplebnf_alternative_seq +\bool_new:N \l__simplebnf_first_alternative_bool + +\regex_new:N \l__simplebnf_new_line_delim_regex +\regex_new:N \l__simplebnf_single_line_delim_regex +\regex_new:N \l__simplebnf_comment_regex +\regex_new:N \l__simplebnf_prod_delim_regex +\regex_new:N \l__simplebnf_relation_regex + +\cs_generate_variant:Nn \regex_split:NnNTF {NVNTF} +\cs_generate_variant:Nn \regex_split:NnN {NVN} +\cs_generate_variant:Nn \regex_set:Nn {NV} +\cs_generate_variant:Nn \regex_replace_all:NnN {NVN} + + +\msg_new:nnn { simplebnf } { lhs } { Malformed~LHS~\msg_line_context: } +\msg_new:nnn { simplebnf } { alt } { Malformed~alternative~\msg_line_context: } +\msg_new:nnn { simplebnf } { prod } { Malformed~production~\msg_line_context: } +\msg_new:nnn { simplebnf } { unk-rel } { Unknown~relation~\msg_line_context: } +\msg_new:nnn { simplebnf } { no-rel } { No~relation~found~\msg_line_context: } +\msg_new:nnn { simplebnf } { dep } + { + Environment~bnfgrammar~is~deprecated.~ + Consider~using~the~environment~bnf.~ + Consult~the~documentation~for~more~information. + } -%% Typeset a single rhs of a production. -%% \l__first_rhs = true => `::=' already typeset -%% \l__first_rhs = false => move to a newline and typeset `|' -%% #1 - rhs : annot or rhs -\cs_new:Nn \simplebnf_typeset_rhs:n -{ - \bool_if:NTF \l__first_rhs - { - \bool_set_false:N \l__first_rhs - } - { - \tl_put_right:Nn \l__table_tl { \\ && \SimpleBNFDefOr & } - } - - \tl_set:Nn \l_tmpa_tl { #1 } - \regex_replace_once:nnN { ^\s+ } {} \l_tmpa_tl - \regex_replace_once:nnN { \s+$ } {} \l_tmpa_tl - \regex_split:nVNTF { : } \l_tmpa_tl \l_tmpa_seq - { - \seq_pop_left:NNT \l_tmpa_seq \l_tmpa_tl - { - \regex_replace_all:NnN \g_simplebnf_rhs_nb_r { \c{SimpleBNFDefOr} } \l_tmpa_tl - % Expand only the local temporary variable. - \tl_put_right:No \l__table_tl - { - \exp_after:wN\bnfexpr\exp_after:wN{\l_tmpa_tl} & - } - } - - \seq_pop_left:NNT \l_tmpa_seq \l_tmpb_tl - { - \regex_replace_once:nnN { ^\s+ } {} \l_tmpb_tl - \tl_put_right:No \l__table_tl - { - \exp_after:wN\bnfannot\exp_after:wN{\l_tmpb_tl} - } - } - } - { - \regex_replace_all:NnN \g_simplebnf_rhs_nb_r { \c{SimpleBNFDefOr} } \l_tmpa_tl - - \tl_put_right:No \l__table_tl - { - \exp_after:wN\bnfexpr\exp_after:wN{\l_tmpa_tl} - } - } -} %% Typeset a single lhs of a production. %% #1 - lhs : either term or (term : annotation) -\cs_new:Nn \simplebnf_typeset_lhs:n -{ - \tl_set:Nx \l_tmpa_tl { #1 } - \regex_replace_once:nnN { ^\s+ } {} \l_tmpa_tl - \regex_replace_once:nnN { \s+$ } {} \l_tmpa_tl - - \regex_split:nVNTF { : } \l_tmpa_tl \l_tmpa_seq - { - \seq_pop_right:NN \l_tmpa_seq \l_tmpa_tl - \regex_replace_once:nnN { ^\s+ } {} \l_tmpa_tl - \tl_put_right:No \l__table_tl - { - \exp_after:wN\bnfannot\exp_after:wN{\l_tmpa_tl} & - } - \seq_pop_left:NN \l_tmpa_seq \l_tmpa_tl - \tl_put_right:No \l__table_tl - { - \exp_after:wN\bnfexpr\exp_after:wN{\l_tmpa_tl} - } - } - { - \tl_put_right:No \l__table_tl - { - \exp_after:wN&\exp_after:wN\bnfexpr\exp_after:wN{\l_tmpa_tl} - } - } -} +\cs_new:Nn \__simplebnf_typeset_lhs:n + { + \tl_set:No \l_tmpa_tl { #1 } + \regex_replace_once:nnN { ^\s+ } {} \l_tmpa_tl + \regex_replace_once:nnN { \s+$ } {} \l_tmpa_tl + + \regex_split:NVNTF \l__simplebnf_comment_regex \l_tmpa_tl \l_tmpa_seq + { % annotation + \seq_pop_right:NN \l_tmpa_seq \l_tmpa_tl + \regex_replace_once:nnN { ^\s+ } {} \l_tmpa_tl + \tl_put_right:NV \l__simplebnf_table_tl \l_tmpa_tl + \tl_put_right:Nn \l__simplebnf_table_tl { & } + % metavariable + \seq_pop_left:NN \l_tmpa_seq \l_tmpa_tl + \tl_put_right:NV \l__simplebnf_table_tl \l_tmpa_tl + } + { % not annotated + \tl_put_right:Nn \l__simplebnf_table_tl { & } + \seq_pop_left:NN \l_tmpa_seq \l_tmpa_tl + \tl_put_right:NV \l__simplebnf_table_tl \l_tmpa_tl + } + + % There should be no more tokens in the sequence. + \seq_if_empty:NF \l_tmpa_seq + { \msg_error:nnn { simplebnf } { lhs } { #1 } } + } + + +% Handle a single alternative (or single-line alternatives) of an RHS. +\cs_new:Nn \__simplebnf_typeset_rhs:n + { + \tl_if_blank:nF { #1 } + { + \bool_if:NTF \l__simplebnf_first_alternative_bool + { \bool_set_false:N \l__simplebnf_first_alternative_bool } + { + \tl_put_right:Nn \l__simplebnf_table_tl + { \\ & & \l__simplebnf_or_sym_tl & } + } + + \tl_set:Nn \l_tmpa_tl { #1 } + \regex_replace_once:nnN { ^\s+ } {} \l_tmpa_tl + \regex_replace_once:nnN { \s+$ } {} \l_tmpa_tl + + \regex_replace_all:NVN + \l__simplebnf_single_line_delim_regex + \l__simplebnf_or_sym_tl + \l_tmpa_tl + + \regex_split:NVNTF \l__simplebnf_comment_regex \l_tmpa_tl \l_tmpa_seq + { % put an alternate syntax form + \seq_pop_left:NN \l_tmpa_seq \l_tmpa_tl + \tl_put_right:NV \l__simplebnf_table_tl \l_tmpa_tl + \tl_put_right:Nn \l__simplebnf_table_tl { & } + + % put an annotation + \seq_pop_left:NN \l_tmpa_seq \l_tmpa_tl + \regex_replace_once:nnN { ^\s+ } {} \l_tmpa_tl + \tl_put_right:NV \l__simplebnf_table_tl \l_tmpa_tl + + % There should be no more tokens in the sequence. + \seq_if_empty:NF \l_tmpa_seq + { \msg_error:nnn { simplebnf } { alt } { #1 } } + } + { % no annotation + \tl_put_right:NV \l__simplebnf_table_tl \l_tmpa_tl + } + } + } + + +% Handle a single production P +\cs_new:Nn \__simplebnf_typeset_production:n + { + \regex_split:NnNTF + \l__simplebnf_relation_regex + { #1 } + \l__simplebnf_lhs_rhs_pair_seq + { + % LHS + \seq_pop_left:NN \l__simplebnf_lhs_rhs_pair_seq \l__simplebnf_lhs_tl + % Rule relation + \seq_pop_left:NN \l__simplebnf_lhs_rhs_pair_seq \l__simplebnf_matched_relation_tl + % RHS + \seq_pop_left:NN \l__simplebnf_lhs_rhs_pair_seq \l__simplebnf_rhs_tl + + \seq_if_empty:NF \l__simplebnf_lhs_rhs_pair_seq + { \msg_error:nnn { simplebnf } { prod } { #1 } } + % Build the LHS + \__simplebnf_typeset_lhs:n \l__simplebnf_lhs_tl + + % Build the rule relation + \prop_get:NVNTF + \l__simplebnf_relation_sym_map_prop + \l__simplebnf_matched_relation_tl + \l_tmpa_tl + { + \tl_put_right:Nn \l__simplebnf_table_tl { & } + \tl_put_right:NV \l__simplebnf_table_tl \l_tmpa_tl + \tl_put_right:Nn \l__simplebnf_table_tl { & } + } + { + \msg_warning:nnn { simplebnf } { unk-rel } { #1 } + \tl_put_right:Nn \l__simplebnf_table_tl { & & } + } + % Build the RHS + %% \l__simplebnf_alternative_seq - (rhs:annot | rhs)... + \regex_split:NVN + \l__simplebnf_new_line_delim_regex + \l__simplebnf_rhs_tl + \l__simplebnf_alternative_seq + + \bool_set_true:N \l__simplebnf_first_alternative_bool + \seq_map_function:NN \l__simplebnf_alternative_seq \__simplebnf_typeset_rhs:n + } + { \msg_error:nnn { simplebnf } { no-rel } { #1 } } + } + + +% The main control sequence that builds the grammar G into \l__simplebnf_table_tl +\cs_new:Nn \__simplebnf_build_grammar:n + { + \regex_set:NV \l__simplebnf_new_line_delim_regex \l__simplebnf_new_line_delim_tl + \regex_set:NV \l__simplebnf_single_line_delim_regex \l__simplebnf_single_line_delim_tl + \regex_set:NV \l__simplebnf_comment_regex \l__simplebnf_comment_tl + \regex_set:NV \l__simplebnf_prod_delim_regex \l__simplebnf_prod_delim_tl + \tl_put_left:Nn \l__simplebnf_relation_tl { ( } + \tl_put_right:Nn \l__simplebnf_relation_tl { ) } + \regex_set:NV \l__simplebnf_relation_regex \l__simplebnf_relation_tl + + % \l_tmpa_seq contains the sequence of productions (P; ...). + \regex_split:NnN \l__simplebnf_prod_delim_regex { #1 } \l_tmpa_seq + + % Is this the first production of this grammar? + \bool_set_true:N \l_tmpa_bool + \seq_map_inline:Nn \l_tmpa_seq + { + \tl_if_blank:nF { ##1 } % ignore empty productions + { % If not first, change row + \bool_if:NTF \l_tmpa_bool + { \bool_set_false:N \l_tmpa_bool } + { + \tl_put_right:Nn \l__simplebnf_table_tl { \\[\l__simplebnf_prod_sep_dim] } + } + \__simplebnf_typeset_production:n { ##1 } + } + } + } -\NewDocumentCommand \bnfexpr { m } { \texttt { #1 } } -\NewDocumentCommand \bnfannot { m } { \textit{ #1 } } %% Typeset a BNF grammar. -%% #1 - tabular specification (llcll) -%% #2 - regexp for newline separator for rhses -%% #2 - regexp for non-breaking separator for rhses -%% #3 - grammar -\NewDocumentEnvironment { bnfgrammar } { O{llcll} O{[^\|]\|[^\|]} O{\|\|} +b } +%% #1 - key-value options for simplebnf +%% #2 - tabularray specification (default: llcll) +%% #3 - grammar body +\NewDocumentEnvironment { bnf } { d() O{llcll} +b } { - \regex_gset:Nn \g_simplebnf_rhs_newline_r { #2 } - \regex_gset:Nn \g_simplebnf_rhs_nb_r { #3 } + \IfNoValueF { #1 } + { \keys_set:nn { simplebnf } { #1 } } - %% \l__input_seq is a list of term definitions. - \regex_split:nnN { ;; } { #4 } \l__input_seq - \begin{center} - \tl_set:Nn \l__table_tl - { - \begin{tabular}{#1} - } + \__simplebnf_build_grammar:n { #3 } - \bool_set_true:N \l_tmp_first_term % Is this the first term in this grammar? - \seq_map_inline:Nn \l__input_seq + \begin{@simplebnf_tblr_env}[expand=\l__simplebnf_table_tl]{#2} + \tl_use:N \l__simplebnf_table_tl + \end{@simplebnf_tblr_env} + } + { } + + +\NewDocumentCommand \SetBNFLayout { m } + { + \SetTblrInner[@simplebnf_tblr_env]{#1} + } + + +\NewDocumentCommand \SetBNFConfig { m } + { + \keys_set:nn { simplebnf } { #1 } + } + + +% DEPRECATED APIs + +% Redefinable user-level variables +\newcommand*\SimpleBNFDefEq{\ensuremath{\Coloneqq}} +\newcommand*\SimpleBNFDefOr{\ensuremath{|}} +\newcommand*\SimpleBNFStretch{0em} + +\cs_generate_variant:Nn \regex_split:nnNTF {nVNTF} + +%% Typeset a single lhs of a production. +%% #1 - lhs : either term or (term : annotation) +\cs_new:Nn \@dep__simplebnf_typeset_lhs:n + { + \tl_set:Nx \l_tmpa_tl { #1 } + \regex_replace_once:nnN { ^\s+ } {} \l_tmpa_tl + \regex_replace_once:nnN { \s+$ } {} \l_tmpa_tl + + \regex_split:nVNTF { : } \l_tmpa_tl \l_tmpa_seq { - %% If not-first, add newline - \bool_if:NTF \l_tmp_first_term + \seq_pop_right:NN \l_tmpa_seq \l_tmpa_tl + \regex_replace_once:nnN { ^\s+ } {} \l_tmpa_tl + \tl_put_right:No \l__simplebnf_table_tl { - \bool_set_false:N \l_tmp_first_term + \exp_after:wN\bnfannot\exp_after:wN{\l_tmpa_tl} & } + \seq_pop_left:NN \l_tmpa_seq \l_tmpa_tl + \tl_put_right:No \l__simplebnf_table_tl { - \tl_put_right:Nn \l__table_tl { \\[\SimpleBNFStretch] } + \exp_after:wN\bnfexpr\exp_after:wN{\l_tmpa_tl} } + } + { + \tl_put_right:No \l__simplebnf_table_tl + { + \exp_after:wN&\exp_after:wN\bnfexpr\exp_after:wN{\l_tmpa_tl} + } + } + } + + +%% Typeset a single rhs of a production. +%% \l__simplebnf_first_alternative_bool = true => `::=' already typeset +%% \l__simplebnf_first_alternative_bool = false => move to a newline and typeset `|' +%% #1 - rhs : annot or rhs +\cs_new:Nn \@dep__simplebnf_typeset_rhs:n + { + \bool_if:NTF \l__simplebnf_first_alternative_bool + { + \bool_set_false:N \l__simplebnf_first_alternative_bool + } + { + \tl_put_right:Nn \l__simplebnf_table_tl { \\ && \SimpleBNFDefOr & } + } - \regex_split:nnNTF { ::= } { ##1 } \l__term_seq - % Parse a ::= definition + \tl_set:Nn \l_tmpa_tl { #1 } + \regex_replace_once:nnN { ^\s+ } {} \l_tmpa_tl + \regex_replace_once:nnN { \s+$ } {} \l_tmpa_tl + \regex_split:nVNTF { : } \l_tmpa_tl \l_tmpa_seq + { + \seq_pop_left:NNT \l_tmpa_seq \l_tmpa_tl { - %% \l__term_seq - (lhs, rhses)... - %% \l__term_tl - lhs - %% \l__keypairs_tl - rhses - \seq_pop_left:NN \l__term_seq \l__term_tl - \seq_pop_left:NN \l__term_seq \l__keypairs_tl - - \simplebnf_typeset_lhs:n{\l__term_tl} - \tl_put_right:Nn \l__table_tl + \regex_replace_all:NnN \l__simplebnf_single_line_delim_regex { \c{SimpleBNFDefOr} } \l_tmpa_tl + % Expand only the local temporary variable. + \tl_put_right:No \l__simplebnf_table_tl { - & \SimpleBNFDefEq & + \exp_after:wN\bnfexpr\exp_after:wN{\l_tmpa_tl} & } - %% \l__keypairs_seq - (rhs:annot | rhs)... - \regex_split:NVN \g_simplebnf_rhs_newline_r \l__keypairs_tl \l__keypairs_seq + } - \bool_set_true:N \l__first_rhs - \seq_map_function:NN \l__keypairs_seq \simplebnf_typeset_rhs:n + \seq_pop_left:NNT \l_tmpa_seq \l_tmpb_tl + { + \regex_replace_once:nnN { ^\s+ } {} \l_tmpb_tl + \tl_put_right:No \l__simplebnf_table_tl + { + \exp_after:wN\bnfannot\exp_after:wN{\l_tmpb_tl} + } } + } + { + \regex_replace_all:NnN \l__simplebnf_single_line_delim_regex { \c{SimpleBNFDefOr} } \l_tmpa_tl + + \tl_put_right:No \l__simplebnf_table_tl { - % Else, parse a \in declaration - \regex_split:nnNTF { \c{in} } { ##1 } \l__term_seq + \exp_after:wN\bnfexpr\exp_after:wN{\l_tmpa_tl} + } + } + } + +\cs_new:Nn \@dep__simplebnf_typeset_production:n + { + \regex_split:nnNTF { ::= } { #1 } \l__simplebnf_lhs_rhs_pair_seq + % Parse a ::= definition + { + %% \l__simplebnf_lhs_rhs_pair_seq - (lhs, rhses)... + %% \l__simplebnf_lhs_tl - lhs + %% \l__simplebnf_rhs_tl - rhses + \seq_pop_left:NN \l__simplebnf_lhs_rhs_pair_seq \l__simplebnf_lhs_tl + \seq_pop_left:NN \l__simplebnf_lhs_rhs_pair_seq \l__simplebnf_rhs_tl + + \@dep__simplebnf_typeset_lhs:n{\l__simplebnf_lhs_tl} + \tl_put_right:Nn \l__simplebnf_table_tl + { + & \SimpleBNFDefEq & + } + %% \l__simplebnf_alternative_seq - (rhs:annot | rhs)... + \regex_split:NVN \l__simplebnf_new_line_delim_regex \l__simplebnf_rhs_tl \l__simplebnf_alternative_seq + + \bool_set_true:N \l__simplebnf_first_alternative_bool + \seq_map_function:NN \l__simplebnf_alternative_seq \@dep__simplebnf_typeset_rhs:n + } + { + % Else, parse a \in declaration (TODO: There is a huge room for generalization) + \regex_split:nnNTF { \c{in} } { #1 } \l__simplebnf_lhs_rhs_pair_seq + { + %% \l__simplebnf_lhs_rhs_pair_seq - (lhs, rhses)... + %% \l__simplebnf_lhs_tl - lhs + %% \l__simplebnf_rhs_tl - rhses + \seq_pop_left:NN \l__simplebnf_lhs_rhs_pair_seq \l__simplebnf_lhs_tl + \seq_pop_left:NN \l__simplebnf_lhs_rhs_pair_seq \l__simplebnf_rhs_tl + + \@dep__simplebnf_typeset_lhs:n{\l__simplebnf_lhs_tl} + \tl_put_right:Nn \l__simplebnf_table_tl { - %% \l__term_seq - (lhs, rhs) - \seq_pop_left:NN \l__term_seq \l_tmpa_tl - - \simplebnf_typeset_lhs:n{\l_tmpa_tl} - \tl_put_right:Nn \l__table_tl - { - & $\in$ & $ - } - \seq_pop_left:NN \l__term_seq \l_tmpa_tl - \tl_put_right:NV \l__table_tl \l_tmpa_tl - \tl_put_right:Nn \l__table_tl - { - $ & - } + & $\in$ & } - { \msg_error:nn {simplebnf} { Could not parser ##1 } } + %% \l__simplebnf_alternative_seq - (rhs:annot | rhs)... + \regex_split:NVN \l__simplebnf_new_line_delim_regex \l__simplebnf_rhs_tl \l__simplebnf_alternative_seq + + \bool_set_true:N \l__simplebnf_first_alternative_bool + \seq_map_function:NN \l__simplebnf_alternative_seq \@dep__simplebnf_typeset_rhs:n + } + { + \msg_error:nnn { simplebnf } { error-message } + { Could~not~parse~#1 } } } + } - \tl_put_right:Nn \l__table_tl { \end{tabular} } - \tl_use:N \l__table_tl + +\seq_new:N \l__input_seq +\cs_new:Nn \@dep__simplebnf_typeset_grammar:nnn + { + \regex_set:Nn \l__simplebnf_new_line_delim_regex { #1 } + \regex_set:Nn \l__simplebnf_single_line_delim_regex { #2 } + + %% \l__input_seq is a list of term definitions. + \regex_split:nnN { ;; } { #3 } \l__input_seq + + \bool_set_true:N \l_tmp_first_term % Is this the first term in this grammar? + \seq_map_inline:Nn \l__input_seq + { + %% If not-first, add newline + \bool_if:NTF \l_tmp_first_term + { + \bool_set_false:N \l_tmp_first_term + } + { + \tl_put_right:Nn \l__simplebnf_table_tl { \\[\SimpleBNFStretch] } + } + + \@dep__simplebnf_typeset_production:n { ##1 } + } + } + + +\NewDocumentCommand \bnfexpr { m } { \texttt { #1 } } +\NewDocumentCommand \bnfannot { m } { \textit { #1 } } + + +%% Typeset a BNF grammar. +%% #1 - tabular specification (llcll) +%% #2 - regexp for newline separator for rhses +%% #2 - regexp for non-breaking separator for rhses +%% #3 - grammar +\NewDocumentEnvironment { bnfgrammar } { O{llcll} O{[^\|]\|[^\|]} O{\|\|} +b } + { + \msg_warning:nn { simplebnf } { dep } + \begin{center} + \begin{tabular}{#1} + \@dep__simplebnf_typeset_grammar:nnn { #2 } { #3 } { #4 } + \tl_use:N \l__simplebnf_table_tl + \end{tabular} \end{center} } { } |