% \iffalse meta-comment
%
% Copyright (C) 2015-2021 by Emmanuel Beffara
%
% This file may be distributed and/or modified under the conditions of the
% LaTeX Project Public License, either version 1.3 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.3 or later is part of all distributions of LaTeX version
% 2005/12/01 or later.
%
%<*driver>
\documentclass[full]{l3doc}
\usepackage{ebproof}
\usepackage{multicol}
\usepackage{tikz}
\usetikzlibrary{decorations.pathmorphing}
\EnableCrossrefs
\newenvironment{example}{%
  \VerbatimEnvironment
  \begin{VerbatimOut}{example.tex}}{%
  \end{VerbatimOut}
  \begin{center}
  \begin{minipage}{.4\textwidth}
    \input{example.tex}
  \end{minipage}%
  \begin{minipage}{.6\textwidth}
    \small\VerbatimInput[gobble=0]{example.tex}
  \end{minipage}%
  \end{center}
}
\begin{document}
  \DocInput{\jobname.dtx}
\end{document}
%</driver>
% \fi
%
% \title{^^A
%   The \pkg{ebproof} package \\
%   Formal proofs in the style of sequent calculus^^A
% }
%
% \author{^^A
%   Emmanuel Beffara\thanks
%     {^^A
%       E-mail:  \href{mailto:manu@beffara.org}{manu@beffara.org}^^A
%     }^^A
% }
%
% \date{Version 2.1.1 -- Released 2021-01-28}
%
% \maketitle
%
% \setcounter{tocdepth}{2}
% \begin{multicols}{2}
% \tableofcontents
% \end{multicols}
%
% \begin{documentation}
%
% \section{Introduction}
%
% The \pkg{ebproof} package provides commands to typeset proof trees, in the
% style of sequent calculus and related systems:
%
% \begin{example}
%   \begin{prooftree}
%     \hypo{ \Gamma, A &\vdash B }
%     \infer1[abs]{ \Gamma &\vdash A\to B }
%     \hypo{ \Gamma \vdash A }
%     \infer2[app]{ \Gamma \vdash B }
%   \end{prooftree}
% \end{example}
%
% The structure is very much inspired by the
% \href{http://math.ucsd.edu/~sbuss/ResearchWeb/bussproofs/}{\pkg{bussproofs}}
% package, in particular for the postfix notation.
% I actually wrote \pkg{ebproof} because there were some limitations in
% \pkg{bussproofs} that I did not know how to lift, and also because I did
% not like some choices in that package (and also because it was fun to write).
%
% Any feedback is welcome, in the form of bug reports, feature requests or
% suggestions, through the web page of the project at \url{https://framagit.org/manu/ebproof}.
%
% \section{Environments}
%
% \begin{function}{prooftree,prooftree*}
%   \begin{syntax}
%     \verb|\begin{prooftree}|\oarg{options}
%     ~ \meta{statements}
%     \verb|\end{prooftree}|
%   \end{syntax}
%   The package provides the \env{prooftree} environment, in standard and
%   starred variants.
%   This typesets the proof tree described by the \meta{statements}, as
%   described in section~\ref{sec:statements}.
%   The \meta{options} provide default formatting options for the proof tree,
%   available options are described in section~\ref{sec:options}.
%
%   Following the conventions of \pkg{amsmath} for alignment environments, the
%   non-starred version produces a proof tree at the current position in the
%   text flow (it can be used in math mode or text mode) while the starred
%   version typesets the proof on a line of its own, like a displayed formula.
% \end{function}
%
% \begin{example}
%   \[
%     \begin{prooftree}
%       \infer0{ \vdash A }
%       \hypo{ \vdash B } \infer1{ \vdash B, C }
%       \infer2{ \vdash A\wedge B, C }
%     \end{prooftree}
%     \quad \rightsquigarrow \quad
%     \begin{prooftree}
%       \infer0{ \vdash A } \hypo{ \vdash B }
%       \infer2{ \vdash A\wedge B }
%       \infer1{ \vdash A\wedge B, C }
%     \end{prooftree}
%   \]
% \end{example}
%
% \section{Statements}
% \label{sec:statements}
%
% Statements describe proofs in postfix notation: when typesetting a proof tree
% whose last rule has, say, two premisses, you will first write statements for
% the subtree of the first premiss, then statements for the subtree of the
% second premiss, then a statement like \cs{infer2}\{\meta{conclusion}\} to
% build an inference with these two subtrees as premisses and the given text as
% conclusion.
%
% Hence statements operate on a stack of proof trees.
% At the beginning of a \env{prooftree} environment, the stack is empty.
% At the end, it must contain exactly one tree, which is the one that will be
% printed.
%
% Note that the commands defined in this section only exist right inside
% \env{prooftree} environments.
% If you have a macro with the same name as one of the statements, for instance
% \cs{hypo}, then this macro will keep its meaning outside \env{prooftree}
% environments as well as inside the arguments of a statement.
% If you really need to access the statements in another context, you can can
% always call them by prefixing their names with \texttt{ebproof}, for instance as
% \cs{ebproofhypo}.
%
% \subsection{Basic statements}
%
% The basic statements for building proofs are the following, where
% \meta{options} stands for arbitrary options as described in
% section~\ref{sec:options}.
%
% \begin{function}{\hypo}
%   \begin{syntax}
%     \cs{hypo}\oarg{options}\marg{text}
%   \end{syntax}
%   The statement \cs{hypo} pushes a new proof tree consisting only in one
%   conclusion line, with no premiss and no line above, in other words a tree
%   with only a leaf (\cs{hypo} stands for \emph{hypothesis}).
% \end{function}
%
% \begin{function}{\infer}
%   \begin{syntax}
%     \cs{infer}\oarg{options}\meta{arity}\oarg{label}\marg{text}
%   \end{syntax}
%   The statement \cs{infer} builds an inference step by taking some proof
%   trees from the top of the stack, assembling them with a rule joining their
%   conclusions and putting a new conclusion below.
%   The \meta{arity} is the number of sub-proofs, it may be any number
%   including 0 (in this case there will be a line above the conclusion but no
%   sub-proof).
%   If \meta{label} is present, it is used as the label on the right of the
%   inference line; it is equivalent to using the \cmd{right label} option.
% \end{function}
%
% \medskip
%
% The \meta{text} in these statements is the contents of the conclusion at the
% root of the tree that the statements create.
% It is typeset in math mode by default but any kind of formatting can be used
% instead, using the \cmd{template} option.
% The \meta{label} text is formatted in horizontal text mode by default.
%
% Each proof tree has a vertical axis, used for alignment of successive steps.
% The position of the axis is deduced from the text of the conclusion at the
% root of the tree: if \meta{text} contains the alignment character \verb|&|
% then the axis is set at that position, otherwise the axis is set at the center
% of the conclusion text.
% The \cs{infer} statement makes sure that the axis of the premiss is at the
% same position as the axis of the conclusion.
% If there are several premisses, it places the axis at the center between the
% left of the leftmost conclusion and the right of the rightmost conclusion:
%
% \begin{example}
%   \begin{prooftree}
%     \hypo{ &\vdash A, B, C }
%     \infer1{ A &\vdash B, C }
%     \infer1{ A, B &\vdash C }
%     \hypo{ D &\vdash E }
%     \infer2{ A, B, D &\vdash C, E }
%     \infer1{ A, B &\vdash C, D, E }
%     \infer1{ A &\vdash B, C, D, E }
%   \end{prooftree}
% \end{example}
%
% \begin{function}{\ellipsis}
%   \begin{syntax}
%     \cs{ellipsis}\marg{label}\marg{text}
%   \end{syntax}
%   The statement \cs{ellipsis} typesets vertical dots, with a label on the right,
%   and a new conclusion. No inference lines are inserted.
% \end{function}
%
% \begin{example}
%   \begin{prooftree}
%     \hypo{ \Gamma &\vdash A }
%     \ellipsis{foo}{ \Gamma &\vdash A, B }
%   \end{prooftree}
% \end{example}
%
%
% \subsection{Modifying proof trees}
%
% The following additional statements may be used to affect the format of the
% last proof tree on the stack.
%
% \begin{function}{\rewrite}
%   \begin{syntax}
%     \cs{rewrite}\marg{code}
%   \end{syntax}
%   The statement \cs{rewrite} is used to modify the proof of the stack while
%   preserving its size and alignment.
%   The \meta{code} is typeset in horizontal mode, with the following control
%   sequences defined:
%   \begin{itemize}
%   \item \cs{treebox} is a box register that contains the original material,
%   \item \cs{treemark}\marg{name} expands as the position of a given mark with
%     respect to the left of the box.
%   \end{itemize}
% \end{function}
%
% A simple use of this statement is to change the color of a proof tree:
% \begin{example}
%   \begin{prooftree}
%     \hypo{ \Gamma, A &\vdash B }
%     \infer1[abs]{ \Gamma &\vdash A\to B }
%     \rewrite{\color{red}\box\treebox}
%     \hypo{ \Gamma \vdash A }
%     \infer2[app]{ \Gamma \vdash B }
%   \end{prooftree}
% \end{example}
% Note the absence of spaces inside the call to \cs{rewrite}, because spaces
% would affect the position of the tree box.
% Note also that explicit use of \cs{treebox} is required to actually draw the
% subtree.
% Not using it will effectively not render the subtree, while still reserving
% its space in the enclosing tree:
% \begin{example}
%   \begin{prooftree}
%     \hypo{ \Gamma, A &\vdash B }
%     \infer1[abs]{ \Gamma &\vdash A\to B }
%     \rewrite{}
%     \hypo{ \Gamma \vdash A }
%     \infer2[app]{ \Gamma \vdash B }
%   \end{prooftree}
% \end{example}
% This kind of manipulation is useful for instance in conjunction with the
% \pkg{beamer} package to allow revealing subtrees of a proof tree
% progressively in successive slides of a given frame.
%
% \begin{function}{\delims}
%   \begin{syntax}
%     \cs{delims}\marg{left}\marg{right}
%   \end{syntax}
%   The statement \cs{delims} puts left and right delimiters around the whole
%   sub-proof, without changing the alignment (the spacing is affected by the
%   delimiters, however).
%   The \meta{left} text must contain an opening occurrence of \cs{left} and
%   the \meta{right} text must contain a matching occurrence of \cs{right}.
%   For instance, \verb|\delims{\left(}{\right)}| will put the sub-proof
%   between parentheses.
% \end{function}
%
% \begin{example}
%   \begin{prooftree}
%     \hypo{ A_1 \vee \cdots \vee A_n }
%     \hypo{ [A_i] }
%     \ellipsis{}{ B }
%     \delims{ \left( }{ \right)_{1\leq i\leq n} }
%     \infer2{ B }
%   \end{prooftree}
% \end{example}
%
% \begin{function}{\overlay}
%   \begin{syntax}
%     \cs{overlay}
%   \end{syntax}
%   The statement \cs{overlay} combines the last two proofs on the stack into
%   a single one, so that their conclusions are placed at the same point.
% \end{function}
%
% \begin{example}
%   \begin{prooftree}
%     \hypo{Z}
%     \hypo{A} \hypo{B} \infer2{C} \hypo{D} \infer2{D}
%       \rewrite{\color{blue}\box\treebox}
%     \hypo{E} \hypo{F} \hypo{G} \infer2{H} \infer2{I}
%       \rewrite{\color{red}\box\treebox}
%     \overlay \hypo{J} \infer3{K}
%   \end{prooftree}
% \end{example}
%
% The primary use of this feature is for building animated presentations where
% a subtree in a proof has to be modified without affecting the general
% alignment of the surrounding proof. For instance, the example above could be
% used in Beamer to build successive slides in a given frame with two
% different subtrees:
%
% \begin{verbatim}
%   \begin{prooftree}
%     \hypo{Z}
%     \hypo{A} \hypo{B} \infer2{C} \hypo{D} \infer2{D}
%       \only<2>{\rewrite{}} % erases this version on slide 2
%     \hypo{E} \hypo{F} \hypo{G} \infer2{H} \infer2{I}
%       \only<1>{\rewrite{}} % erases this version on slide 1
%     \overlay \hypo{J} \infer3{K}
%   \end{prooftree}
% \end{verbatim}
%
% \section{Options}
% \label{sec:options}
%
% The formatting of trees, conclusion texts and inference rules is affected by
% options, specified using the \LaTeX3 key-value system.
% All options are in the \texttt{ebproof} module in the key tree.
% They can be set locally for a proof tree or for a single statement using
% optional arguments in the associated commands.
%
% \begin{function}{\ebproofset,\set}
%   \begin{syntax}
%     \cs{ebproofset}\marg{options}
%   \end{syntax}
%   The statement \cs{ebproofset} is used to set some options. When used
%   inside a \env{prooftree} environment, it can written \cs{set}.
%   The options will apply in the current scope; using this in preamble will
%   effectively set options globally.
%   Specific options may also be specified for each proof tree and for each
%   statement in a proof tree, using optional arguments.
% \end{function}
%
% \subsection{General shape}
%
% The options in this section only make sense at the global level and at the
% proof level.
% Changing the proof style inside a \env{proof} environment has undefined
% behaviour.
%
% \begin{variable}{proof style}
%   The option \cmd{proof style} sets the general shape for representing proofs.
%   The following styles are provided:
%   \begin{description}
%   \item[upwards] 
%     This is the default style.
%     Proof trees grow upwards, with conclusions below and premisses above.
%   \item[downwards]
%     Proof trees grow downwards, with conclusions above and premisses below.
%     \fvset{gobble=6}
%     \begin{example}
%       \begin{prooftree}[proof style=downwards]
%         \hypo{ \Gamma, A &\vdash B }
%         \infer1[abs]{ \Gamma &\vdash A\to B }
%         \hypo{ \Gamma \vdash A }
%         \infer2[app]{ \Gamma \vdash B }
%       \end{prooftree}
%     \end{example}
%   \end{description}
% \end{variable}
%
% In the optional argument of \env{prooftree} environments, proof styles can
% be specified directly, without prefixing the name by ``\texttt{proof style=}''.
% For instance, the first line of the example above could be written
% \verb|\begin{prooftree}| equivalently.
%
% \begin{variable}{center}
%   The option \cmd{center} toggles vertical centering of typeset proofs.
%   If set to \texttt{true}, the tree produced by the \env{prooftree}
%   environment will be vertically centered around the text line.
%   If set to \texttt{false}, the base line of the tree will be the base line
%   of the conclusion.
%   The default value is \texttt{true}.
% \end{variable}
%
% \begin{example}
%   \begin{prooftree}[center=false]
%     \infer0{ A \vdash A }
%   \end{prooftree}
%   \qquad
%   \begin{prooftree}[center=false]
%     \hypo{ \Gamma, A \vdash B }
%     \infer1{ \Gamma \vdash A \to B }
%   \end{prooftree}
% \end{example}
%
% \subsection{Spacing}
%
% \begin{variable}{separation}
%   Horizontal separation between sub-proofs in an inference is defined by the
%   option \cmd{separation}.
%   The default value is \texttt{1.5em}.
% \end{variable}
%
% \begin{example}
%   \begin{prooftree}[separation=0.5em]
%     \hypo{ A } \hypo{ B } \infer2{ C }
%     \hypo{ D } \hypo{ E } \hypo{ F } \infer3{ G }
%     \hypo{ H } \infer[separation=3em]3{ K }
%   \end{prooftree}
% \end{example}
%
% \begin{variable}{rule margin}
%   The spacing above and below inference lines is defined by the option
%   \cmd{rule margin}.
%   The default value is \texttt{0.7ex}.
% \end{variable}
%
% \begin{example}
%   \begin{prooftree}[rule margin=2ex]
%     \hypo{ \Gamma, A &\vdash B }
%     \infer1[abs]{ \Gamma &\vdash A\to B }
%     \hypo{ \Gamma \vdash A }
%     \infer2[app]{ \Gamma \vdash B }
%   \end{prooftree}
% \end{example}
%
% \subsection{Shape of inference lines}
%
% \begin{variable}{rule style}
%   The shape of inference lines is set by the option \cmd{rule style}.
%   The following values are provided:
%   \begin{center}
%     \begin{tabular}{@{}l@{\qquad}l@{}}
%       \toprule
%       \cmd{simple}  & a simple line (this is the default style) \\
%       \cmd{no rule} & no rule, only a single space of length \texttt{rule margin} \\
%       \cmd{double}  & a double line \\
%       \cmd{dashed}  & a single dashed line \\
%       \bottomrule
%     \end{tabular}
%   \end{center}
% \end{variable}
%
% The precise rendering is influenced by parameters specified below.
% Arbitrary new shapes can defined using the \cs{ebproofnewrulestyle} command
% described in section~\ref{sec:styles}, using \texttt{rule code} option
% described below.
%
% In the optional argument of the \cs{infer} statement, rule styles can be
% specified directly, without prefixing the style name by ``\texttt{rule style=}''.
% For instance, \verb|\infer[dashed]| is equivalent to
% \verb|\infer[rule style=dashed]|.
%
% \begin{example}
%   \begin{prooftree}
%     \hypo{ \Gamma &\vdash A \to B }
%     \infer[no rule]1{ \Gamma &\vdash {!A} \multimap B }
%     \hypo{ \Delta &\vdash A }
%     \infer[rule thickness=2pt]1{ \Delta &\vdash {!A} }
%     \infer0{ B \vdash B }
%     \infer[dashed]2{ \Delta, {!A}\multimap B \vdash B }
%     \infer2{ \Gamma, \Delta &\vdash B }
%     \infer[double]1{ \Gamma \cup \Delta &\vdash B }
%   \end{prooftree}
% \end{example}
%
% \begin{variable}{rule thickness,rule separation}
%   The thickness of inference lines is defined by option \cmd{rule
%   thickness}, it is \texttt{0.4pt} by default.
%   The distance between the two lines in the \texttt{double} rule style is
%   defined by the \cmd{rule separation} option.
%   It is \texttt{2pt} by default.
% \end{variable}
% \begin{variable}{rule dash length,rule dash space}
%   For dashed rules, the length of dashes is defined by the option
%   \cmd{rule dash length} and the space between dashes is defined by the
%   option \cmd{rule dash space}.
%   The default values are \texttt{0.2em} and \texttt{0.3em} respectively.
% \end{variable}
%
% \begin{variable}{rule code}
%   Arbitrary rule shapes can be optained using the \cmd{rule code} option.
%   The argument is code is used to render the rule, it is executed in
%   vertical mode in a \cs{vbox} whose \cs{hsize} is set to the width of the
%   rule.
%   Margins above and below are inserted automatically (they can be removed by
%   setting \texttt{rule margin} to \texttt{0pt}).
% \end{variable}
%
% \begin{example}
%   \begin{prooftree}[rule code={\hbox{\tikz
%       \draw[decorate,decoration={snake,amplitude=.3ex}]
%       (0,0) -- (\hsize,0);}}]
%     \hypo{ \Gamma &\vdash A }
%     \infer1{ \Gamma &\vdash A, \ldots, A }
%     \hypo{ \Delta, A, \ldots, A \vdash \Theta }
%     \infer2{ \Gamma, \Delta \vdash \Theta }
%   \end{prooftree}
% \end{example}
% Note that this example requires the \pkg{tikz} package, with the
% \pkg{decorations.pathmorphing} library for the \texttt{snake} decoration.
%
% \subsection{Format of conclusions and labels}
%
% \begin{variable}{template,left template,right template}
%   The format of text in inferences is defined by templates.
%   The option \cmd{template} is used for text with no alignment mark, the
%   options \cmd{left template} and \cmd{right template} are used for the left
%   and right side of the alignment mark when it is present.
%   The value of these options is arbitrary \TeX\ code, composed in horizontal mode.
%   The macro \cs{inserttext} is used to insert the actual text passed to the
%   \cs{hypo} and \cs{infer} statements.
%   The default value for \cmd{template} is simply \verb|$\inserttext$|, so
%   that conclusions are set in math mode.
%   The default values for \cmd{left template} and \cmd{right template} are
%   similar, with spacing assuming that a relation symbol is put near the
%   alignment mark, so that \verb|\infer1{A &\vdash B}| is spaced correctly.
% \end{variable}
%
% \begin{example}
%   \begin{prooftree}[template=(\textbf\inserttext)]
%     \hypo{ foo }
%     \hypo{ bar }
%     \infer1{ baz }
%     \infer2{ quux }
%   \end{prooftree}
% \end{example}
%
% \begin{variable}{left label,right label}
%   The text to use as the labels of the rules, on the left and on the right
%   of the inference line, is defined by the options \cmd{left label} and
%   \cmd{right label}.
%   Using the second optional argument in \cs{infer} is equivalent to setting
%   the \env{right label} option with the value of that argument.
% \end{variable}
%
% \begin{example}
%   \begin{prooftree}
%     \hypo{ \Gamma, A &\vdash B }
%     \infer[left label=$\lambda$]1[abs]
%       { \Gamma &\vdash A\to B }
%     \hypo{ \Gamma \vdash A }
%     \infer[left label=@]2[app]{ \Gamma \vdash B }
%   \end{prooftree}
% \end{example}
%
% \begin{variable}{left label template,right label template}
%   Similarly to conclusions, labels are formatted according to templates.
%   The code is arbitrary \TeX\ code, composed in horizontal mode, where the
%   macro \cs{inserttext} can be used to insert the actual label text.
%   The default values are simply \cs{inserttext} so that labels are set in
%   plain text mode.
% \end{variable}
% \begin{variable}{label separation}
%   The spacing between an inference line and its labels is defined by the
%   option \cmd{label separation}, the default value is \texttt{0.5em}.
%   The height of the horizontal axis used for aligning the labels with the
%   rules is defined by the option \cmd{label axis}, the default value is
%   \texttt{0.5ex}.
% \end{variable}
%
%
% \subsection{Style macros}
% \label{sec:styles}
%
% The following commands allow for the definition of custom styles using the
% basic style options, in a way similar to PGF's ``styles'' and \LaTeX3's
% ``meta-keys''.
% This allows setting a bunch of options with the same values in many proofs
% using a single definition.
%
% \begin{function}{\ebproofnewstyle}
%   \begin{syntax}
%     \cs{ebproofnewstyle}\marg{name}\marg{options}
%   \end{syntax}
%   The statement \cs{ebproofnewstyle} defines a new style option with some
%   \meta{name} that sets a given set of \meta{options}.
% \end{function}
% For instance, the following code defines a new option \cmd{small} that sets
% various parameters so that proofs are rendered smaller.
% \begin{example}
%   \ebproofnewstyle{small}{
%     separation = 1em, rule margin = .5ex,
%     template = \footnotesize$\inserttext$ }
%   \begin{prooftree}[small]
%     \hypo{ \Gamma, A \vdash B }
%     \infer1{ \Gamma \vdash A\to B }
%     \hypo{ \Gamma \vdash A } \infer2{ \Gamma \vdash B }
%   \end{prooftree}
% \end{example}
%
% \begin{function}{\ebproofnewrulestyle}
%   \begin{syntax}
%     \cs{ebproofnewrulestyle}\marg{name}\marg{options}
%   \end{syntax}
%   The statement \cs{ebproofnewrulestyle} does the same for rule styles.
%   The \meta{options} part includes options used to set how to draw rules in
%   the new style.
% \end{function}
%
% The option \cmd{rule code} is useful in this command as it allows to
% define arbitrary rule styles.
% For instance, the squiggly rule example above could be turned into a new
% rule style \texttt{zigzag} with the following code:
% \begin{example}
%   \ebproofnewrulestyle{zigzag}{
%     rule code = {\hbox{\tikz
%       \draw[decorate,decoration={snake,amplitude=.3ex}]
%       (0,0) -- (\hsize,0);}}}
%   \begin{prooftree}
%     \hypo{ \Gamma &\vdash A }
%     \infer1{ \Gamma &\vdash A, \ldots, A }
%     \hypo{ \Delta, A, \ldots, A \vdash \Theta }
%     \infer[zigzag]2{ \Gamma, \Delta \vdash \Theta }
%   \end{prooftree}
% \end{example}
%
%
% \section{License}
%
% This work may be distributed and/or modified under the
% conditions of the \LaTeX\ Project Public License, either version 1.3
% of this license or (at your option) any later version.
% The latest version of this license is in
% \begin{center}
%   \url{http://www.latex-project.org/lppl.txt}
% \end{center}
% and version 1.3 or later is part of all distributions of \LaTeX\
% version 2005/12/01 or later.
%
% This work has the LPPL maintenance status `maintained'.
%
% The Current Maintainer of this work is Emmanuel Beffara.
%
% This work consists of the files \texttt{ebproof.sty} and \texttt{ebproof.tex}.
%
% \section{History}
%
% This section lists the principal evolutions of the package, in reverse
% chronological order.
% \begin{description}
% \item[Version 2.1.1 (2021-01-28)]
%   Bugfix release, no changes in the user interface.
%   \begin{itemize}
%   \item Fixes a deprecation issue with \LaTeX3 release 2021-01-09 and
%     various warnings that appear in \LaTeX3 debugging mode.
%   \item Fixes \cmd{proof style=downwards}.
%   \end{itemize}
% \item[Version 2.1 (2020-08-19)]
%   Mostly a bugfix release.
%   \begin{itemize}
%   \item Makes the \env{prooftree} environment robust to use in tabular
%     contexts.
%   \item Adds the \cs{overlay} statement.
%   \item Fixes a compatibility issue with \LaTeX\ release 2020-10-01.
%   \end{itemize}
% \item[Version 2.0 (2017-05-17)]
%   A complete rewrite of the code using the \LaTeX3 programming environment. 
%   The incompatible changes from the user's point of view are the following:
%   \begin{itemize}
%   \item Proof statements are now writtten in lowercase ({i.e.} \cs{Infer} is
%     now written \cs{infer} etc.) but the syntax is otherwise unchanged.
%     The old uppercase commands still work but produce a deprecation warning,
%     they will be removed in a future version.
%   \item New styles are now defined using \cs{ebproofnewstyle} and
%     \cs{ebproofnewrulestyle}. The previous method using PGF styles does not
%     work anymore (because PGF is not used anymore).
%   \end{itemize}
%   The new commands and options are the following:
%   \begin{itemize}
%   \item The statement \cs{rewrite} generalizes \cs{Alter},
%   \item The option \cmd{label axis} controls vertical alignment of labels.
%   \end{itemize}
% \item[Version 1.1 (2015-03-13)]
%   A bugfix release.
%   In \cmd{template} options, one now uses \cs{inserttext} instead of \verb|#1|
%   for the text arguments, which improves robustness.
% \item[Version 1.0 (2015-02-04)]
%   The first public release.
% \end{description}
%
% \end{documentation}
%
% \clearpage \appendix
%
% \begin{implementation}
%
% \section{Implementation}
%
%    \begin{macrocode}
%<*package>
\NeedsTeXFormat{LaTeX2e}
\RequirePackage{expl3}
\RequirePackage{xparse}
\ProvidesExplPackage{ebproof}{2021/01/28}{2.1.1}{EB's proof trees}
%<@@=ebproof>
%    \end{macrocode}
%
% \subsection{Parameters}
%
% We first declare all options. For the meaning of options, see
% section~\ref{sec:options}.
%
%    \begin{macrocode}
\bool_new:N \l_@@_updown_bool
\keys_define:nn { ebproof } {
center .bool_set:N = \l_@@_center_bool,
proof~style .choice: ,
proof~style / upwards .code:n = \bool_set_false:N \l_@@_updown_bool,
proof~style / downwards .code:n = \bool_set_true:N \l_@@_updown_bool,
separation .dim_set:N = \l_@@_separation_dim,
rule~margin .dim_set:N = \l_@@_rule_margin_dim,
rule~thickness .dim_set:N = \l_@@_rule_thickness_dim,
rule~separation .dim_set:N = \l_@@_rule_separation_dim,
rule~dash~length .dim_set:N = \l_@@_rule_dash_length_dim,
rule~dash~space .dim_set:N = \l_@@_rule_dash_space_dim,
rule~code .tl_set:N = \l_@@_rule_code_tl,
rule~style .choice:,
template .tl_set:N = \l_@@_template_tl,
left~template .tl_set:N = \l_@@_left_template_tl,
right~template .tl_set:N = \l_@@_right_template_tl,
left~label .tl_set:N = \l_@@_left_label_tl,
right~label .tl_set:N = \l_@@_right_label_tl,
left~label~template .tl_set:N = \l_@@_left_label_template_tl,
right~label~template .tl_set:N = \l_@@_right_label_template_tl,
label~separation .dim_set:N = \l_@@_label_separation_dim,
label~axis .dim_set:N = \l_@@_label_axis_dim,
}
%    \end{macrocode}
%
% \begin{macro}{\ebproofnewrulestyle}
%   We then define the document-level macro \cs{ebproofnewrulestyle} and use
%   it to define the default styles. This simply consists in defining a
%   meta-key.
%    \begin{macrocode}
\NewDocumentCommand \ebproofnewrulestyle { mm } {
  \keys_define:nn { ebproof } {
    rule~style / #1 .meta:nn = { ebproof } { #2 }
  }
}
%    \end{macrocode}
% \end{macro}
%
% The styles |simple|, |no rule| and |double| are defined in a straightforward
% way.
%
%    \begin{macrocode}
\ebproofnewrulestyle { simple } {
  rule~code = { \tex_hrule:D height \l_@@_rule_thickness_dim }
}
\ebproofnewrulestyle { no~rule } {
  rule~code =
}
\ebproofnewrulestyle { double } {
  rule~code = {
    \tex_hrule:D height \l_@@_rule_thickness_dim
    \skip_vertical:N \l_@@_rule_separation_dim
    \tex_hrule:D height \l_@@_rule_thickness_dim
  }
}
%    \end{macrocode}
%
% The |dashed| style uses leaders and filling for repeating a single dash. We
% use \TeX\ primitives that have no \LaTeX3 counterpart for this.
%
%    \begin{macrocode}
\ebproofnewrulestyle { dashed } {
  rule~code = {
    \hbox_to_wd:nn { \tex_hsize:D } {
      \dim_set:Nn \l_tmpa_dim { \l_@@_rule_dash_space_dim / 2 }
      \skip_horizontal:n { -\l_tmpa_dim }
      \tex_cleaders:D \hbox:n {
        \skip_horizontal:N \l_tmpa_dim
        \tex_vrule:D
          height \l_@@_rule_thickness_dim
          width \l_@@_rule_dash_length_dim
        \skip_horizontal:N \l_tmpa_dim
      } \tex_hfill:D
      \skip_horizontal:n { -\l_tmpa_dim }
    }
  }
}
%    \end{macrocode}
%
% Now we can define the default values, including the default rule style.
%
%    \begin{macrocode}
\keys_set:nn { ebproof } {
  center = true,
  proof~style = upwards,
  separation = 1.5em,
  rule~margin = .7ex,
  rule~thickness = .4pt,
  rule~separation = 2pt,
  rule~dash~length = .2em,
  rule~dash~space = .3em,
  rule~style = simple,
  template = $\inserttext$,
  left~template = $\inserttext\mathrel{}$,
  right~template = $\mathrel{}\inserttext$,
  left~label = ,
  right~label = ,
  left~label~template = \inserttext,
  right~label~template = \inserttext,
  label~separation = 0.5em,
  label~axis = 0.5ex
}
%    \end{macrocode}
%
% \begin{macro}{\ebproofnewstyle}
%   Defining a style simply means defining a meta-key.
%    \begin{macrocode}
\NewDocumentCommand \ebproofnewstyle { mm } {
  \keys_define:nn { ebproof } { #1 .meta:n = { #2 } }
}
%    \end{macrocode}
% \end{macro}
%
%
% \subsection{Proof boxes}
%
% \TeX\ does not actually provide data structures, so we have to encode things.
% We provide an allocator for ``registers'' holding boxes with attributes. Such
% a register consists in a box register and a property list for marks, which
% maps mark names to values as explicit dimensions with units.
%
% \begin{macro}{\@@_new:N}
%   Using only public interfaces forces a convoluted approach to allocation:
%   we use a global counter \cs{g_ebproof_register_int} to number registers,
%   then each allocation creates registers named \cs{S_ebproof_K_N} where
%   S is the scope of the register (local or global, deduced from the argument),
%   K is the kind of component (box or marks) and N is the identifier of the
%   register. The proof box register itself only contains the identifier used
%   for indirection.
%    \begin{macrocode}
\int_new:N \g_@@_register_int
\cs_new:Nn \@@_box:N {
  \str_item:nn { #1 } { 2 } __ebproof_ \tl_use:N #1 _box
}
\cs_new:Nn \@@_marks:N {
  \str_item:nn { #1 } { 2 } __ebproof_ \tl_use:N #1 _prop
}
\cs_new:Nn \@@_new:N {
  \tl_new:N #1
  \int_gincr:N \g_@@_register_int
  \str_if_eq:eeTF { \str_item:nn { #1 } { 2 } } { g }
    { \tl_gset:Nx #1 { \int_to_arabic:n { \g_@@_register_int } } }
    { \tl_set:Nx #1 { \int_to_arabic:n { \g_@@_register_int } } }
  \box_new:c { \@@_box:N #1 }
  \prop_new:c { \@@_marks:N #1 }
}
%    \end{macrocode}
% \end{macro}
%
% \begin{macro}{\@@_clear:N}
%   The box is cleared by setting it to an empty hbox.
%   Using \cs{box_clear:N} instead would not work because trying to push this
%   box on the stack would not actually append any box.
%    \begin{macrocode}
\cs_new:Nn \@@_clear:N {
  \hbox_set:cn { \@@_box:N #1 } {}
  \prop_clear:c { \@@_marks:N #1 }
  \@@_set_mark:Nnn #1 { left } { 0pt }
  \@@_set_mark:Nnn #1 { right } { 0pt }
  \@@_set_mark:Nnn #1 { axis } { 0pt }
}
%    \end{macrocode}
% \end{macro}
%
% \subsubsection{Mark operations}
%
% \begin{macro}{\@@_set_mark:Nnn}
%   Setting the value of a mark uses a temporary register to evaluate the
%   dimension expression because values are stored textually in a property
%   list.
%    \begin{macrocode}
\dim_new:N \l_@@_transit_dim
\cs_new:Nn \@@_set_mark:Nnn {
  \dim_set:Nn \l_@@_transit_dim { #3 }
  \prop_put:cnV { \@@_marks:N #1 } { #2 }
    \l_@@_transit_dim
}
%    \end{macrocode}
% \end{macro}
%
% \begin{macro}{\@@_mark:Nn}
%   Getting the value of a mark simply consists in getting an item in a
%   property list.
%    \begin{macrocode}
\cs_new:Nn \@@_mark:Nn {
  \prop_item:cn { \@@_marks:N #1 } { #2 }
}
%    \end{macrocode}
% \end{macro}
%
% \begin{macro}{\@@_shift_x:Nn}
%   This function shifts the marks by a specified amount, without modifying
%   the box.
%    \begin{macrocode}
\cs_new:Nn \@@_shift_x:Nn {
  \prop_map_inline:cn { \@@_marks:N #1 } {
    \@@_set_mark:Nnn #1 { ##1 } { ##2 + #2 }
  }
}
%    \end{macrocode}
% \end{macro}
%
% \begin{macro}{\@@_enlarge_conclusion:NN}
%   This function moves the left and right marks of the first tree so that
%   they are at least as far from the axis as they are in the second tree. 
%   For instance we get the following:
%   \begin{center}
%     \begin{tikzpicture}[y=-3ex]
%       \node (L1) at (1,0) {L}; \node (A1) at (2,0) {A}; \node (R1) at (4,0) {R};
%       \node (L2) at (0,1) {L}; \node (A2) at (2,1) {A}; \node (R2) at (3,1) {R};
%       \node (L3) at (0,2) {L}; \node (A3) at (2,2) {A}; \node (R3) at (4,2) {R};
%       \draw (L1) -- (A1) -- (R1);
%       \draw (L2) -- (A2) -- (R2);
%       \draw (L3) -- (A3) -- (R3);
%       \node[anchor=west] at (5,0) {box 1 before};
%       \node[anchor=west] at (5,1) {box 2 before};
%       \node[anchor=west] at (5,2) {box 1 after};
%     \end{tikzpicture}
%   \end{center}
%   The contents of the trees are unchanged.
%    \begin{macrocode}
\cs_new:Nn \@@_enlarge_conclusion:NN {
  \dim_set:Nn \l_tmpa_dim { \@@_mark:Nn #1 {axis}
    + \@@_mark:Nn #2 {left} - \@@_mark:Nn #2 {axis} }
  \dim_compare:nNnT { \l_tmpa_dim } < { \@@_mark:Nn #1 {left} } {
    \@@_set_mark:Nnn #1 {left} { \l_tmpa_dim } }
  \dim_set:Nn \l_tmpa_dim { \@@_mark:Nn #1 {axis}
    + \@@_mark:Nn #2 {right} - \@@_mark:Nn #2 {axis} }
  \dim_compare:nNnT { \l_tmpa_dim } > { \@@_mark:Nn #1 {right} } {
    \@@_set_mark:Nnn #1 {right} { \l_tmpa_dim } }
}
%    \end{macrocode}
% \end{macro}
%
% \subsubsection{Building blocks}
%
% \begin{macro}{\@@_make_simple:Nn}
%   Make a tree with explicit material in horizontal mode. Set the left and
%   right marks to extremal positions and set the axis in the middle.
%    \begin{macrocode}
\cs_new:Nn \@@_make_simple:Nn {
  \hbox_set:cn { \@@_box:N #1 } { #2 }
  \@@_set_mark:Nnn #1 { left } { 0pt }
  \@@_set_mark:Nnn #1 { axis } { \box_wd:c { \@@_box:N #1 } / 2 }
  \@@_set_mark:Nnn #1 { right } { \box_wd:c { \@@_box:N #1 } }
}
%    \end{macrocode}
% \end{macro}
%
% \begin{macro}{\@@_make_split:Nnn}
%   Make a tree with explicit material in horizontal mode, split in two parts.
%   Set the left and right marks to extremal positions and set the axis
%   between the two parts.
%    \begin{macrocode}
\cs_new:Nn \@@_make_split:Nnn {
  \@@_set_mark:Nnn #1 { left } { 0pt }
  \hbox_set:cn { \@@_box:N #1 } { #2 }
  \@@_set_mark:Nnn #1 { axis } { \box_wd:c { \@@_box:N #1 } }
  \hbox_set:cn { \@@_box:N #1 } { \hbox_unpack:c { \@@_box:N #1 } #3 }
  \@@_set_mark:Nnn #1 { right } { \box_wd:c { \@@_box:N #1 } }
}
%    \end{macrocode}
% \end{macro}
%
% \begin{macro}{\@@_make_vertical:Nnnn}
%   Make a tree with explicit material in vertical mode, using an explicit
%   width and axis.
%    \begin{macrocode}
\cs_new:Nn \@@_make_vertical:Nnnn {
  \@@_set_mark:Nnn #1 { left } { 0pt }
  \@@_set_mark:Nnn #1 { axis } { #2 }
  \@@_set_mark:Nnn #1 { right } { #3 }
  \vbox_set:cn { \@@_box:N #1 } {
    \dim_set:Nn \tex_hsize:D { \@@_mark:Nn #1 {right} }
    #4
  }
  \box_set_wd:cn { \@@_box:N #1 } { \@@_mark:Nn #1 {right} }
}
%    \end{macrocode}
% \end{macro}
%
% \subsubsection{Assembling boxes}
%
% \begin{macro}{\@@_extend:Nnnnn}
%   Extend a tree box. The marks are shifted so that alignment is preserved.
%   The arguments are dimensions for the left, top, right and bottom sides
%   respectively.
%    \begin{macrocode}
\cs_new:Nn \@@_extend:Nnnnn {
  \dim_compare:nNnF { #2 } = { 0pt } {
    \hbox_set:cn { \@@_box:N #1 } {
      \skip_horizontal:n { #2 }
      \box_use:c { \@@_box:N #1 }
    }
    \@@_shift_x:Nn #1 { #2 }
  }
  \box_set_ht:Nn #1 { \box_ht:c { \@@_box:N #1 } + #3 }
  \box_set_wd:Nn #1 { \box_wd:c { \@@_box:N #1 } + #4 }
  \box_set_dp:Nn #1 { \box_dp:c { \@@_box:N #1 } + #5 }
}
%    \end{macrocode}
% \end{macro}
%
% \begin{macro}{\@@_append_right:NnN}
%   Append the contents of the second tree to the first one on the right, with
%   matching baselines. The marks of both trees are preserved. The middle
%   argument specifies the space to insert between boxes.
%    \begin{macrocode}
\cs_new:Nn \@@_append_right:NnN {
  \hbox_set:cn { \@@_box:N #1 } {
    \box_use:c { \@@_box:N #1 }
    \dim_compare:nNnF { #2 } = { 0pt } { \skip_horizontal:n { #2 } }
    \box_use:c { \@@_box:N #3 }
  }
}
%    \end{macrocode}
% \end{macro}
%
% \begin{macro}{\@@_append_left:NnN}
%   Append the contents of the second tree to the first one on the left, with
%   matching baselines. The marks of the first tree are shifted accordingly.
%   The middle argument specifies the space to insert between boxes.
%    \begin{macrocode}
\cs_new:Nn \@@_append_left:NnN {
  \@@_shift_x:Nn #1 { \box_wd:c { \@@_box:N #3 } + #2 }
  \hbox_set:cn { \@@_box:N #1 } {
    \box_use:c { \@@_box:N #3 }
    \dim_compare:nNnF { #2 } = { 0pt } { \skip_horizontal:n { #2 } }
    \box_use:c { \@@_box:N #1 }
  }
}
%    \end{macrocode}
% \end{macro}
%
% \begin{macro}{\@@_align:NN}
%   Shift one of two trees to the right so that their axes match. The marks of
%   the one that is shifted are updated accordingly.
%    \begin{macrocode}
\cs_new:Nn \@@_align:NN {
  \dim_set:Nn \l_tmpa_dim
    { \@@_mark:Nn #2 {axis} - \@@_mark:Nn #1 {axis} }
  \dim_compare:nNnTF \l_tmpa_dim < { 0pt } {
    \@@_extend:Nnnnn #2 { -\l_tmpa_dim } { 0pt } { 0pt } { 0pt }
  } {
    \@@_extend:Nnnnn #1 { \l_tmpa_dim } { 0pt } { 0pt } { 0pt }
  }
}
%    \end{macrocode}
% \end{macro}
%
% \begin{macro}{\@@_append_above:NN}
%   Append the contents of the second tree above the first one, with matching
%   axes. The marks of the first tree are preserved.
%    \begin{macrocode}
\cs_new:Nn \@@_append_above:NN {
  \@@_align:NN #1 #2
  \vbox_set:cn  { \@@_box:N #1 } {
    \box_use:c { \@@_box:N #2 }
    \tex_prevdepth:D -1000pt
    \box_use:c { \@@_box:N #1 }
  }
}
%    \end{macrocode}
% \end{macro}
%
% \begin{macro}{\@@_append_below:NN}
%   Append the contents of the second tree below the first one, with matching
%   axes. The marks of the first tree are preserved.
%    \begin{macrocode}
\cs_new:Nn \@@_append_below:NN {
  \@@_align:NN #1 #2
  \vbox_set_top:cn { \@@_box:N #1 } {
    \box_use:c { \@@_box:N #1 }
    \tex_prevdepth:D -1000pt
    \box_use:c { \@@_box:N #2 }
  }
}
%    \end{macrocode}
% \end{macro}
%
% \begin{macro}{\@@_overlay:NN}
%   Append the second tree as an overlay over the first one, so that the
%   baselines and axes match. The bounding box of the result adjusts to
%   contain both trees.
%    \begin{macrocode}
\cs_new:Nn \@@_overlay:NN {
  \@@_align:NN #1 #2
  \hbox_set:cn { \@@_box:N #1 } {
    \hbox_overlap_right:n { \box_use:c { \@@_box:N #1 } }
    \box_use:c { \@@_box:N #2 }
    \dim_compare:nNnT
      { \box_wd:c { \@@_box:N #2 } } < { \box_wd:c { \@@_box:N #1 } }
      { \skip_horizontal:n
        { \box_wd:c { \@@_box:N #1 } - \box_wd:c { \@@_box:N #2 } } }
  }
}
%    \end{macrocode}
% \end{macro}
%
% \begin{macro}{\@@_vcenter:N}
%   Shift the material in a tree vertically so that the height and depth are
%   equal (like \TeX's \cs{vcenter} but around the baseline).
%    \begin{macrocode}
\cs_new:Nn \@@_vcenter:N {
  \dim_set:Nn \l_tmpa_dim
    { ( \box_ht:c { \@@_box:N #1 } - \box_dp:c { \@@_box:N #1 } ) / 2 }
  \box_set_eq:Nc \l_tmpa_box { \@@_box:N #1 }
  \hbox_set:cn { \@@_box:N #1 }
    { \box_move_down:nn { \l_tmpa_dim } { \box_use:N \l_tmpa_box } }
}
%    \end{macrocode}
% \end{macro}
%
% \subsection{Making inferences}
%
% The following commands use the parameters defined at the beginning of the
% package for actually building proof trees using the commands defined above.
%
% \begin{macro}{\@@_append_vertical:NN}
%   Append the contents of the second tree above or below the first one,
%   depending on current settings. Axes are aligned and the marks of the first
%   tree are preserved.
%    \begin{macrocode}
\cs_new:Nn \@@_append_vertical:NN {
  \bool_if:NTF \l_@@_updown_bool
    { \@@_append_below:NN #1 #2 }
    { \@@_append_above:NN #1 #2 }
}
%    \end{macrocode}
% \end{macro}
%
% \begin{macro}{\@@_make_rule_for:NNN}
%   Make a box containing an inference rule with labels, using the current
%   settings. The width and axis position are taken as those of the conclusion
%   of another tree box. The third argument is used as a temporary register
%   for building labels.
%    \begin{macrocode}
\cs_new:Nn \@@_make_rule_for:NNN {
%    \end{macrocode}
%   Build the rule.
%    \begin{macrocode}
  \@@_make_vertical:Nnnn #1
    { \@@_mark:Nn #2 {axis} - \@@_mark:Nn #2 {left} }
    { \@@_mark:Nn #2 {right} - \@@_mark:Nn #2 {left} }
    {
      \skip_vertical:N \l_@@_rule_margin_dim
      \tl_if_empty:NF { \l_@@_rule_code_tl } {
        \tl_use:N \l_@@_rule_code_tl
        \skip_vertical:N \l_@@_rule_margin_dim
      }
    }
  \@@_vcenter:N #1
%    \end{macrocode}
%   Append the left label.
%    \begin{macrocode}
  \tl_if_blank:VF \l_@@_left_label_tl {
    \@@_make_simple:Nn #3 {
      \box_move_down:nn { \l_@@_label_axis_dim } { \hbox:n {
        \cs_set_eq:NN \inserttext \l_@@_left_label_tl
        \tl_use:N \l_@@_left_label_template_tl
      } }
    }
    \box_set_ht:cn { \@@_box:N #3 } { 0pt }
    \box_set_dp:cn { \@@_box:N #3 } { 0pt }
    \@@_append_left:NnN
      \l_@@_c_box \l_@@_label_separation_dim \l_@@_d_box
  }
%    \end{macrocode}
%   Append the right label.
%    \begin{macrocode}
  \tl_if_blank:VF \l_@@_right_label_tl {
    \@@_make_simple:Nn #3 {
      \box_move_down:nn { \l_@@_label_axis_dim } { \hbox:n {
        \cs_set_eq:NN \inserttext \l_@@_right_label_tl
        \tl_use:N \l_@@_right_label_template_tl
      } }
    }
    \box_set_ht:cn { \@@_box:N #3 } { 0pt }
    \box_set_dp:cn { \@@_box:N #3 } { 0pt }
    \@@_append_right:NnN
      \l_@@_c_box \l_@@_label_separation_dim \l_@@_d_box
  }
}
%    \end{macrocode}
% \end{macro}
%
% \subsection{Stack-based interface}
%
% \subsubsection{The stack}
%
% Logically, box structures are stored on a stack. However, \TeX\ does not
% provide data structures for that and the grouping mechanism is not flexible
% enough, so we encode them using what we actually have. A stack for boxes is
% implemented using a global hbox \cs{g_@@_stack_box} that contains all the
% boxes successively. A sequence \cs{g_@@_stack_seq} is used to store the
% dimensions property lists textually. We maintain a counter
% \cs{g_@@_level_int} with the number of elements on the stack, for
% consistency checks.
%    \begin{macrocode}
\int_new:N \g_@@_level_int
\box_new:N \g_@@_stack_box
\seq_new:N \g_@@_stack_seq
%    \end{macrocode}
%
% \begin{macro}{\@@_clear_stack:}
%   Clear the stack.
%    \begin{macrocode}
\cs_new:Nn \@@_clear_stack: {
  \int_gset:Nn \g_@@_level_int { 0 }
  \hbox_gset:Nn \g_@@_stack_box { }
  \seq_gclear:N \g_@@_stack_seq
}
%    \end{macrocode}
% \end{macro}
%
% \begin{macro}{\@@_push:N}
%   Push the contents of a register on the stack.
%    \begin{macrocode}
\cs_new:Nn \@@_push:N {
  \int_gincr:N \g_@@_level_int
  \hbox_gset:Nn \g_@@_stack_box
    { \hbox_unpack:N \g_@@_stack_box \box_use:c { \@@_box:N #1 } }
  \seq_gput_left:Nv \g_@@_stack_seq
    { \@@_marks:N #1 }
}
%    \end{macrocode}
% \end{macro}
%
% \begin{macro}{\@@_pop:N}
%   Pop the value from the top of the stack into a register.
%    \begin{macrocode}
\cs_new:Nn \@@_pop:N {
  \int_compare:nNnTF { \g_@@_level_int } > { 0 } {
    \int_gdecr:N \g_@@_level_int
    \hbox_gset:Nn \g_@@_stack_box {
      \hbox_unpack:N \g_@@_stack_box
      \box_gset_to_last:N \g_tmpa_box
    }
    \box_set_eq_drop:cN { \@@_box:N #1 } \g_tmpa_box
    \seq_gpop_left:NN \g_@@_stack_seq \l_tmpa_tl
    \tl_set_eq:cN { \@@_marks:N #1 } \l_tmpa_tl
  } {
    \PackageError{ebproof}{Missing~premiss~in~a~proof~tree}{}
    \@@_clear:N #1
  }
}
%    \end{macrocode}
% \end{macro}
%
% \subsubsection{Assembling trees}
%
%    \begin{macrocode}
\@@_new:N \l_@@_a_box
\@@_new:N \l_@@_b_box
\@@_new:N \l_@@_c_box
\@@_new:N \l_@@_d_box
%    \end{macrocode}
%
% \begin{macro}{\@@_join_horizontal:n}
%   Join horizontally a number of elements at the top of the stack. If several
%   trees are joined, use the left mark of the left tree, the right mark of
%   the right tree and set the axis in the middle of these marks.
%    \begin{macrocode}
\cs_new:Nn \@@_join_horizontal:n {
  \int_case:nnF { #1 } {
  { 0 } {
    \group_begin:
    \@@_clear:N \l_@@_a_box
    \@@_push:N \l_@@_a_box
    \group_end:
  }
  { 1 } { }
  } {
    \group_begin:
    \@@_pop:N \l_@@_a_box
    \prg_replicate:nn { #1 - 1 } {
      \@@_pop:N \l_@@_b_box
      \@@_append_left:NnN
        \l_@@_a_box \l_@@_separation_dim \l_@@_b_box
    }
    \@@_set_mark:Nnn \l_@@_a_box { left }
      { \@@_mark:Nn \l_@@_b_box { left } }
    \@@_set_mark:Nnn \l_@@_a_box { axis }
      { ( \@@_mark:Nn \l_@@_a_box { left }
        + \@@_mark:Nn \l_@@_a_box { right } ) / 2 }
    \@@_push:N \l_@@_a_box
    \group_end:
  }
}
%    \end{macrocode}
% \end{macro}
%
% \begin{macro}{\@@_join_vertical:}
%   Join vertically the two elements at the top of the stack, with a
%   horizontal rule of the appropriate size.
%    \begin{macrocode}
\cs_new:Nn \@@_join_vertical: {
  \group_begin:
  \@@_pop:N \l_@@_a_box
  \@@_pop:N \l_@@_b_box
  \@@_enlarge_conclusion:NN \l_@@_b_box \l_@@_a_box
  \@@_make_rule_for:NNN \l_@@_c_box \l_@@_b_box
    \l_@@_d_box
  \@@_append_vertical:NN \l_@@_a_box \l_@@_c_box
  \@@_append_vertical:NN \l_@@_a_box \l_@@_b_box
  \@@_push:N \l_@@_a_box
  \group_end:
}
%    \end{macrocode}
% \end{macro}
%
% \subsubsection{High-level commands}
%
% \begin{macro}{\@@_statement_parse:w}
%   An auxiliary function for parsing the argument in
%   \cs{@@_push_statement:n}.
%    \begin{macrocode}
\cs_new:Npn \@@_statement_parse:w #1 & #2 & #3 \q_stop {
  \tl_if_empty:nTF { #3 } {
    \@@_make_simple:Nn \l_@@_a_box
      { \cs_set:Npn \inserttext { #1 } \tl_use:N \l_@@_template_tl }
  } {
    \@@_make_split:Nnn \l_@@_a_box
      { \cs_set:Npn \inserttext { #1 } \tl_use:N \l_@@_left_template_tl }
      { \cs_set:Npn \inserttext { #2 } \tl_use:N \l_@@_right_template_tl }
  }
  \@@_push:N \l_@@_a_box
}
%    \end{macrocode}
% \end{macro}
%
% \begin{macro}{\@@_push_statement:n}
%   Push a box with default formatting, using explicit alignment if the code
%   contains a |&| character
%    \begin{macrocode}
\cs_new:Nn \@@_push_statement:n {
  \@@_statement_parse:w #1 & & \q_stop
}
%    \end{macrocode}
% \end{macro}
%
% \subsection{Document interface}
%
% \subsubsection{Functions to define statements}
%
% The \cs{g_@@_statements_seq} variable contains the list of all defined
% statements. For each statement |X|, there is a document command \cs{ebproofX}
% and the alias \cs{X} is defined when entering a |prooftree| environment.
%    \begin{macrocode}
\seq_new:N \g_@@_statements_seq
%    \end{macrocode}
%
% \begin{macro}{\@@_setup_statements:}
%   Install the aliases for statements, saving the original value of the control
%   sequences.
%    \begin{macrocode}
\cs_new:Nn \@@_setup_statements: {
  \seq_map_inline:Nn \g_@@_statements_seq {
    \cs_set_eq:cc { ebproof_saved_ ##1 } { ##1 }
    \cs_set_eq:cc { ##1 } { ebproof ##1 }
  }
}
%    \end{macrocode}
% \end{macro}
%
% \begin{macro}{\@@_restore_statements:}
%   Restore the saved meanings of the control sequences. This is useful when
%   interpreting user-provided code in statement arguments. The meanings are
%   automatically restored when leaving a |prooftree| environment because of
%   grouping.
%    \begin{macrocode}
\cs_new:Nn \@@_restore_statements: {
  \seq_map_inline:Nn \g_@@_statements_seq {
    \cs_set_eq:cc { ##1 } { ebproof_saved_ ##1 }
  }
}
%    \end{macrocode}
% \end{macro}
%
% \begin{macro}{\@@_new_statement:nnn}
%   Define a new statement. The first argument is the name, the second one is
%   an argument specifier as used by |xparse| and the third one is the body of
%   the command.
%    \begin{macrocode}
\cs_new:Nn \@@_new_statement:nnn {
  \exp_args:Nc \NewDocumentCommand { ebproof#1 }{ #2 } { #3 }
  \seq_gput_right:Nn \g_@@_statements_seq { #1 }
}
%    \end{macrocode}
% \end{macro}
%
% \begin{macro}{\@@_new_deprecated_statement:nnnn}
%   Define a deprecated statement. The syntax is the same as above except that
%   an extra argument in third position indicates what should be used instead.
%   The effect is the same except that a warning message is issued the first
%   time the statement is used.
%    \begin{macrocode}
\cs_new:Nn \@@_new_deprecated_statement:nnnn {
  \cs_new:cpn { ebproof_#1_warning: } {
    \PackageWarning { ebproof } { \token_to_str:c{#1}~is~deprecated,~#3 }
    \cs_gset:cn { ebproof_#1_warning: } { }
  }
  \@@_new_statement:nnn { #1 } { #2 }
    { \use:c { ebproof_#1_warning: } #4 }
}
%    \end{macrocode}
% \end{macro}
%
%
% \subsubsection{Basic commands}
%
% \begin{macro}{\ebproofset,\set}
%   This is a simple wrapper around \cs{keys_set:nn}.
%    \begin{macrocode}
\@@_new_statement:nnn { set } { m } {
  \keys_set:nn { ebproof } { #1 }
}
%    \end{macrocode}
% \end{macro}
%
% \begin{macro}{\hypo}
%   This is mostly a wrapper around \cs{ebproof_push_statement:n}, with
%   material to handle options and the statements macros.
%    \begin{macrocode}
\@@_new_statement:nnn { hypo } { O{} m } {
  \group_begin:
  \@@_restore_statements:
  \keys_set:nn { ebproof } { #1 }
  \@@_push_statement:n { #2 }
  \group_end:
}
%    \end{macrocode}
% \end{macro}
%
% \begin{macro}{\infer}
%   This is a bit more involved than \cs{hypo} because we have to handle rule
%   style options and joining.
%    \begin{macrocode}
\@@_new_statement:nnn { infer } { O{} m O{} m } {
  \group_begin:
  \@@_restore_statements:
  \keys_set_known:nnN { ebproof / rule~style } { #1 } \l_tmpa_tl
  \keys_set:nV { ebproof } \l_tmpa_tl
  \tl_set:Nn \l_@@_right_label_tl { #3 }
  \@@_join_horizontal:n { #2 }
  \@@_push_statement:n { #4 }
  \@@_join_vertical:
  \group_end:
}
%    \end{macrocode}
% \end{macro}
%
% \begin{macro}{\ellipsis}
%   An ellipsis is made by hand using vertical leaders to render the dots
%   after rendering the label.
%    \begin{macrocode}
\@@_new_statement:nnn { ellipsis } { m m } {
  \group_begin:
  \@@_restore_statements:
  \tl_clear:N \l_@@_rule_code_tl
  \@@_make_split:Nnn \l_@@_a_box { } {
    \vbox_set:Nn \l_tmpa_box {
      \skip_vertical:n { 1.2ex }
      \hbox:n { \tex_ignorespaces:D #1 }
      \skip_vertical:n { 1.2ex }
    }
    \vbox_to_ht:nn { \box_ht:N \l_tmpa_box } {
      \tex_xleaders:D \vbox_to_ht:nn { .8ex }
        { \tex_vss:D \hbox:n { . } \tex_vss:D }
      \tex_vfill:D
    }
    \hbox_overlap_right:n { ~ \box_use:N \l_tmpa_box }
  }
  \@@_push:N \l_@@_a_box
  \@@_join_vertical:
  \@@_push_statement:n {#2}
  \@@_join_vertical:
  \group_end:
}
%    \end{macrocode}
% \end{macro}
%
% \subsubsection{Modifying trees}
%
% \begin{macro}{\rewrite}
%   Rewrite the box at the top of the stack while preserving its dimensions an
%   marks. The code is typeset in horizontal mode, with control sequences to
%   access the original box and its marks.
%    \begin{macrocode}
\@@_new_statement:nnn { rewrite } { m } {
  \group_begin:
  \@@_restore_statements:
  \@@_pop:N \l_@@_a_box
  \box_set_eq:Nc \l_tmpa_box { \@@_box:N \l_@@_a_box }
  \hbox_set:Nn \l_tmpb_box {
    \cs_set_eq:NN \treebox \l_tmpa_box
    \cs_set:Npn \treemark { \@@_mark:Nn \l_@@_a_box }
    { #1 }
  }
  \box_set_wd:Nn \l_tmpb_box { \box_wd:c { \@@_box:N \l_@@_a_box } }
  \box_set_ht:Nn \l_tmpb_box { \box_ht:c { \@@_box:N \l_@@_a_box } }
  \box_set_dp:Nn \l_tmpb_box { \box_dp:c { \@@_box:N \l_@@_a_box } }
  \box_set_eq:cN { \@@_box:N \l_@@_a_box } \l_tmpb_box
  \@@_push:N \l_@@_a_box
  \group_end:
}
%    \end{macrocode}
% \end{macro}
%
% \begin{macro}{\delims}
%   Insert \cs{left} and \cs{right} delimiters without changing the alignment.
%    \begin{macrocode}
\@@_new_statement:nnn { delims } { m m } {
  \group_begin:
  \@@_restore_statements:
  \@@_pop:N \l_@@_a_box
  \hbox_set:Nn \l_tmpa_box
    { $ \tex_vcenter:D { \box_use:c { \@@_box:N \l_@@_a_box } } $ }
  \dim_set:Nn \l_tmpa_dim
    { \box_ht:N \l_tmpa_box - \box_ht:c { \@@_box:N \l_@@_a_box } }
  \hbox_set:cn { \@@_box:N \l_@@_a_box } {
    $ #1 \tex_vrule:D
      height \box_ht:N \l_tmpa_box depth \box_dp:N \l_tmpa_box width 0pt
    \tex_right:D . $
  }
  \@@_shift_x:Nn \l_@@_a_box
    { \box_wd:c { \@@_box:N \l_@@_a_box } }
  \hbox_set:cn { \@@_box:N \l_@@_a_box } {
    \hbox_unpack:c { \@@_box:N \l_@@_a_box }
    $ \tex_left:D . \box_use:N \l_tmpa_box #2 $
  }
  \hbox_set:cn { \@@_box:N \l_@@_a_box }
    { \box_move_down:nn { \l_tmpa_dim }
        { \box_use:c { \@@_box:N \l_@@_a_box } } }
  \@@_push:N \l_@@_a_box
  \group_end:
}
%    \end{macrocode}
% \end{macro}
%
% \begin{macro}{\overlay}
%   Pop two trees and append the second tree as an overlay over the first one,
%   so that the baselines and axes match. The bounding box of the result
%   adjusts to contain both trees.
%    \begin{macrocode}
\@@_new_statement:nnn { overlay } { } {
  \group_begin:
  \@@_pop:N \l_@@_a_box
  \@@_pop:N \l_@@_b_box
  \@@_overlay:NN \l_@@_a_box \l_@@_b_box
  \@@_push:N \l_@@_a_box
  \group_end:
}
%    \end{macrocode}
% \end{macro}
%
% \subsubsection{Deprecated statements}
%
% These statements were defined in versions 1.x of the package, they are
% preserved for temporary upwards compatibility and will be removed in a
% future version.
%    \begin{macrocode}
\@@_new_deprecated_statement:nnnn { Alter } { m }
  { use~\token_to_str:c{rewrite}~instead } { \ebproofrewrite{ #1 \box\treebox } }
\@@_new_deprecated_statement:nnnn { Delims } { }
  { use~\token_to_str:c{delims}~instead } { \ebproofdelims }
\@@_new_deprecated_statement:nnnn { Ellipsis } { }
  { use~\token_to_str:c{ellipsis}~instead } { \ebproofellipsis }
\@@_new_deprecated_statement:nnnn { Hypo } { }
  { use~\token_to_str:c{hypo}~instead } { \ebproofhypo }
\@@_new_deprecated_statement:nnnn { Infer } { }
  { use~\token_to_str:c{infer}~instead } { \ebproofinfer }
%    \end{macrocode}
%
%
% \subsubsection{Environment interface}
%
% The stack is initialised globally. The \env{prooftree} environment does not
% clear the stack, instead it saves the initial level in order to check that
% statements are properly balanced. This allows for nested uses of the
% environment, if it ever happens to be useful.
%
%    \begin{macrocode}
\@@_clear_stack:
\tl_new:N \l_@@_start_level_tl
%    \end{macrocode}
%
% \begin{macro}{prooftree,prooftree*}
%   The \env{prooftree} environment.
%    \begin{macrocode}
\NewDocumentEnvironment { prooftree } { s O{} } {
  \group_align_safe_begin:
  \keys_set_known:nnN { ebproof / proof~style } { #2 } \l_tmpa_tl
  \keys_set:nV { ebproof } \l_tmpa_tl
  \tl_set:Nx \l_@@_start_level_tl { \int_use:N \g_@@_level_int }
  \vbox_set:Nw \l_tmpa_box
  \@@_setup_statements:
} {
  \vbox_set_end:
  \@@_pop:N \l_@@_a_box
  \int_compare:nNnF { \g_@@_level_int } = { \tl_use:N \l_@@_start_level_tl } {
    \PackageError{ebproof}{Malformed~proof~tree}{
      Some~hypotheses~were~declared~but~not~used~in~this~tree.}
  }
  \IfBooleanTF { #1 } {
    \[ \box_use:c { \@@_box:N \l_@@_a_box } \]
    \ignorespacesafterend
  } {
    \hbox_unpack:N \c_empty_box
    \bool_if:NTF \l_@@_center_bool {
      \hbox:n { $ \tex_vcenter:D { \box_use:c { \@@_box:N \l_@@_a_box } } $ }
    } {
      \box_use:c { \@@_box:N \l_@@_a_box }
    }
  }
  \group_align_safe_end:
}
%    \end{macrocode}
% A trick for the starred version:
%    \begin{macrocode}
\cs_new:cpn { prooftree* } { \prooftree* }
\cs_new:cpn { endprooftree* } { \endprooftree }
%    \end{macrocode}
% \end{macro}
%
%    \begin{macrocode}
%</package>
%    \end{macrocode}
%
% \end{implementation}
