% \iffalse meta-comment
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
% This LaTeX docstrip file is free software and is licensed under the          %
% Latex Project Public License v1.3c, or (at your choice) any later version    %
% The latest version of the license can be found at                            %
% https://www.latex-project.org/lppl/lppl-1-3c.txt                             %
% You may use it freely for your purposes.                                     %
% The packages home is https://ctan.org/pkg/grading-scheme                     %
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%<*internal>
% \begingroup
% \input docstrip.tex
% \keepsilent
% \preamble
%  ___________________________________________________________
%   The grading-scheme package
%   Copyright (C) 2022 Maximilian Kessler
%   License information appended.
% 
% \endpreamble
% \postamble
% 
% Copyright 2022 Maximilian Kessler <ctan@maximilian-kessler.de>
% 
% Distributable under the LaTeX Project Public License,
% version 1.3c or higher (your choice). The latest version of
% this license is at: http://www.latex-project.org/lppl.txt
% 
% This work is "author-maintained"
% 
% This work consists of the files grading-scheme.dtx, 
% grading-scheme.ins, a README file
% and the derived files grading-scheme.sty and grading-scheme.pdf.
% 
% \endpostamble
% \askforoverwritefalse
% 
% \generate{\file{grading-scheme.sty}{\from{grading-scheme.dtx}{package}}}
% 
% \def\tmpa{plain}
% \ifx\tmpa\fmtname\endgroup\expandafter\bye\fi
% \endgroup
%</internal>
%
%<package>\ProvidesExplPackage {grading-scheme} {2022/02/24} {0.1.1}
%<package>  {Typeset grading schemes in tabular format}
%
%<*driver>
\documentclass[full]{l3doc}
\usepackage{grading-scheme}
\begin{document}
\RecordChanges
\DocInput{grading-scheme.dtx}
\end{document}
%</driver>
% \fi
%
% \changes{0.1}{2022/02/22}{Initial release}
% \changes{0.1.1}{2022/02/24}{Bug fix (thanks to Denis Bitouzé), typos.}
%
%
%
%
% \title{The \pkg{grading-scheme} package}
% 
% \author{Maximilian Keßler\thanks{ctan@maximilian-kessler.de}}
% \date{Released 2022/02/24\\ \vskip 1.5em {\small version 0.1.1}}
% 
% \maketitle
% 
% \begin{abstract}
%  This package aims at an easy-to-use interface to typeset
%  grading schemes in tabular format, in particular grading-schemes
%  of exercises of mathematical olympiads where multiple
%  solutions have to be graded and might offer mutual
%  exclusive ways of receiving points.
% \end{abstract}
% 
% \setcounter{tocdepth}{2}
% \tableofcontents
% 
% \begin{documentation}
% 
% \section{General notions}
% 
% Probably, an example is fitting best at this point.
% If you want to set up a grading scheme, with the \pkg{grading-scheme} package
% all you have to do to get the output of \autoref{tab:grading-scheme}
% is type the following:
% 
% \iffalse
% Since the docstrip format already uses the pipe character
% to denote verbatim, we actually use \entry to typeset the table.
% In a user document, one could of course use the pipe syntax again.
% \fi
% \begin{table}[tp]
%  \centering
%  \begin{gradingscheme}{sum}
%    \entry{At least one correct solution}{1}
%    \entry{All correct solutions}{1}
%    \begin{block}{max}
%      \begin{block}[Uniqueness proofs]{max}
%        \begin{block}[First solution]{sum}
%          \entry{Show that either $4 \mid p - 2$ or  $3 \mid p - 4$.}{3}
%          \entry{Show that  $m = 5$}{2}
%          \entry{Show that $ n = 4 $}{1}
%          \entry{Conclude solutions}{1}
%        \end{block}
%        \begin{block}[Second solution]{sum}
%          \entry{Show that $p$ is even}{2}
%          \entry{Reduce to at most 30 candidates}{3}
%          \entry{Test all candidates}{2}
%        \end{block}
%      \end{block}
%      \begin{block}{min(2)}
%        \begin{block}[Plausible Arguments]{sum}
%          \entry{plausible argument that there are finitely many solutions}{1}
%          \entry{suggestion that divisibility by 4 is useful}{1}
%          \entry{suggesting that not both of $m$,  $n$ can be 'large'.}{1}
%        \end{block}
%      \end{block}
%    \end{block}
%  \end{gradingscheme}
%  \caption{Example output of a grading scheme.}
%  \label{tab:grading-scheme}
% \end{table}
% 
% \begin{verbatim}
%  \begin{gradingscheme}{sum}
%    | At least one correct solution & 1
%    | All correct solutions & 1
%    \begin{block}{max}
%      \begin{block}[Uniqueness proofs]{max}
%        \begin{block}[First solution]{sum}
%          | Show that either $4 \mid p - 2$ or  $3 \mid p - 4$. & 3
%          | Show that  $m = 5$ & 2
%          | Show that $ n = 4 $ & 1
%          | Conclude solutions & 1
%        \end{block}
%        \begin{block}[Second solution]{sum}
%          | Show that $p$ is even & 2
%          | Reduce to at most 30 candidates & 3
%          | Test all candidates & 2
%        \end{block}
%      \end{block}
%      \begin{block}{min(2)}
%        \begin{block}[Plausible Arguments]{sum}
%          | plausible argument that there are finitely many solutions & 1
%          | suggestion that divisibility by 4 is useful & 1
%          | suggesting that not both of $m$,  $n$ can be 'large'. & 1
%        \end{block}
%      \end{block}
%    \end{block}
%  \end{gradingscheme}
% \end{verbatim}
% 
% Note in particular that the correct width of the rows and columns
% are automatically adjusted.
% 
% 
% For us, a grading scheme consists of several statements, each worth
% some specific number of points, that will be combined into a final
% number of points using a nested expression of operations, typically
% sums, maximums or minimus.
% 
% An abstract grammar defining our notions is defined in
% \autoref{tab:grading-scheme-grammar}.
% The meanings of \meta{balanced text} and \meta{number} are
% as usual.
% 
% \begin{table}[htpb]
%  \centering
% \begin{tabular}{r@{\;:=\;}l}
%  grading scheme & block \\
%  block & [\meta{block description}], \meta{block operation}, \meta{element list} \\
%  element list & \meta{element} $\mid$ \meta{element} : \meta{element list} \\
%  element & \meta{block} $\mid$ \meta{entry} \\
%  entry & \meta{entry description}, \meta{entry points} \\
%  block description & \meta{balanced text} \\
%  block operation & \meta{balanced text} \\
%  entry description & \meta{balanced text} \\
%  entry points & \meta{number} \\
% \end{tabular}
%  \caption{Abstract grammer for a grading scheme}
%  \label{tab:grading-scheme-grammar}
% \end{table}
% 
% The package facilitates specifying a grading scheme according to this grammar
% and typesets these.
% 
% \section{Usage}
% 
% Load the package as usual by
% \begin{verbatim}
%  \usepackage[pipe]{grading-scheme}
% \end{verbatim}
% 
% The |pipe| option is of course optional and the only currently supported option.
% It activates the pipe syntax explained later.
% 
% \begin{environment}{gradingscheme}
%  \begin{syntax}
%    \cs{begin}\{gradingscheme\}\oarg{description text}\Arg{operator}
%    \cs{end}\{gradingscheme\}
%  \end{syntax}
%  The main environment provided by this package.
%  Inside the grading scheme, any number of blocks and entries can
%  be specified.
%  The grading scheme environment will act like the outer-most block
%  and typeset all its contents.
% 
%  Grading schemes cannot contain each other. As they are implemented
%  as |tabular|s, you can (and maybe should) put a grading scheme
%  into a |table| environment as if it were a regular |tabular|.
% 
%  \begin{texnote}
%    Actually, the gradingscheme environment just sets up some hooks
%    that further block environments and entries will fill
%    and typesets these at the end of the environment.
% 
%    You could technically put any regular text inside the grading
%    scheme and this will be typeset as if it had been specified
%    before the grading scheme. However, this is definitely not
%    recommended.
%  \end{texnote}
% \end{environment}
% 
% \begin{environment}{block}
%  \begin{syntax}
%    \cs{begin}\{block\}\oarg{description text}\Arg{operator}
%    \cs{end}\{block\}
%  \end{syntax}
% 
%  The \meta{description text} will be shown in a separate row
%  on top of the block. If not specfied, no such row is inserted.
%  The \meta{operator} is printed in small capitals (and rotated)
%  in a column that spans the left side of the block,
%  indicating that this operation is applied to the block.
% 
%  The block will be an element of the outer block or
%  grading scheme it is contained in.
% 
%  Inside the block, any number of blocks or entries
%  can be specified (in order) that will be typeset
%  as members of this block.
% 
%  A block environment can only be used inside a gradingscheme.
%  Block environments can contain each other in balanced form.
% \end{environment}
% 
% \begin{function}{\entry}
%  \begin{syntax}
%    \cs{entry}\Arg{entry description}\Arg{points}
%  \end{syntax}
% 
%  Specifies an entry with given description and points.
%  The entry is typeset as part of the block/grading scheme
%  containing it.
% \end{function}
% 
% \begin{function}{|}
%  \begin{syntax}
%    $\mid$ \meta{entry description} \& \meta{points} \textbackslash{}n
%  \end{syntax}
%  This is an alternative form to specify an entry. The |\n|
%  is to be interpreted as a newline in the source file, so $\mid$ reads
%  until the end of the line.
% 
%  This is equivalent to calling \cs{entry}\Arg{entry description}\Arg{points}
% 
%  The package option |pipe| has to be specified to activate this
%  syntax.
% 
%  \begin{texnote}
%    The $\mid$ character (pipe) is made an active character for the scope
%    of each gradingscheme environment.
%    Thus, this syntax only works if the gradingscheme is read in
%    expansion mode by \LaTeX{}, since otherwise the category code
%    will be messed up.
%  \end{texnote}
% \end{function}
% 
% 
% \section{\LaTeX3 interface}
% 
% There is also a \LaTeX3 interface for dealing with this package
% that can deal with all of the mentioned notions separately,
% technically allowing you to combine them in other ways or formatting
% them into your custom tables.
% 
% These functions are for now not documented separately, since the author
% thinks that such use cases would be rather rare.
% 
% The \LaTeX3 interface is, however, \emph{not} considered to be stable
% yet, so if you really have to rely on this, feel free to contact
% the author.
%
% \PrintChanges
% 
% \end{documentation}
% 
% 
% \begin{implementation}
% 
% \section{\pkg{grading-scheme} implementation}
%    \begin{macrocode}
%<*package>
%    \end{macrocode}
% 
%    \begin{macrocode}
%<@@=grading_scheme>
%    \end{macrocode}
% 
% \subsection{Dependencies and options}
% 
% Currently, there is only one option
% 
% \begin{variable}{\g_@@_pipe_syntax_bool}
% 
%  This will toggle the pipe syntax option of the package.
% 
%    \begin{macrocode}
\bool_new:N \g_@@_pipe_syntax_bool
%    \end{macrocode}
% \end{variable}
% 
% 
%    \begin{macrocode}
\keys_define:nn { grading-scheme }
  {
    pipe    .bool_gset:N =  \g_@@_pipe_syntax_bool,
    pipe    .default:n  = { true },
    unknown .code:n       =
      {
        \msg_error:nnn { grading-scheme } { unknown-option} { \l_keys_key_str }
      }
  }
\msg_new:nnn { grading-scheme } { unknown-option}
  {
    Unknown ~ option ~ '#1'.
  }
\RequirePackage { l3keys2e }
\ProcessKeysOptions { grading-scheme }
\RequirePackage{multirow}
\RequirePackage{rotating}
%    \end{macrocode}
% 
% \subsection{A simple logging module}
% 
%    \begin{macrocode}
%<*log>
%    \end{macrocode}
% 
% We provide a simple log/debugging facility for this package.
% 
% \begin{variable}{\g_@@_log_depth_int}
%  Stores the log depth for indentation in the log file.
%    \begin{macrocode}
\int_new:N \g_@@_log_depth_int
%    \end{macrocode}
% \end{variable}
% 
% \begin{macro}{\@@_log_incr:}
% 
%  Increments the log depth
% 
%    \begin{macrocode}
\cs_new:Npn \@@_log_incr:
  {
    \int_gincr:N \g_@@_log_depth_int
  }
%    \end{macrocode}
% \end{macro}
% 
% 
% \begin{macro}{\@@_log_decr:}
% 
%  Decrements the log depth
% 
%    \begin{macrocode}
\cs_new:Npn \@@_log_decr:
  {
    \int_gdecr:N \g_@@_log_depth_int
  }
%    \end{macrocode}
% \end{macro}
% 
% 
% \begin{macro}{\@@_log:n, \@@_log:x}
% 
%  Logs to the terminal and log file using the current depth.
% 
%    \begin{macrocode}
\cs_new:Npn \@@_log:n #1
  {
    \iow_term:x
      {
        [gs] \prg_replicate:nn \g_@@_log_depth_int { . . }
        \exp_not:n { #1 }
      }
    \iow_log:x
      {
        [gs] \prg_replicate:nn \g_@@_log_depth_int { . . }
        \exp_not:n { #1 }
      }
  }
\cs_generate_variant:Nn \@@_log:n { x }
%    \end{macrocode}
% \end{macro}
% 
% 
%    \begin{macrocode}
%</log>
%    \end{macrocode}
% 
% \subsection{Wrappers}
% 
% We provide some \LaTeX3 wrappers for private usage.
% 
% \begin{macro}{\@@_multicolumn:nnn}
% 
%  Just a wrapper around \cs{multicolumn} from \LaTeXe.
% 
%    \begin{macrocode}
\cs_set_eq:NN \@@_multicolumn:nnn \multicolumn
%    \end{macrocode}
% \end{macro}
% 
% \begin{macro}{\@@_multirow:nnn}
% 
%  Just a wrapper around \cs{multirow} from \LaTeXe.
% 
%    \begin{macrocode}
\cs_set_eq:NN \@@_multirow:nnn \multirow
%    \end{macrocode}
% \end{macro}
% 
% \begin{macro}{\@@_cline:n, \@@_cline:nn}
% 
%  A wrapper around \cs{cline}.
% 
%  The second form takes the beginning and ending
%  column as separate arguments.
% 
%    \begin{macrocode}
\cs_set_eq:NN \@@_cline:n \cline
\cs_new:Npn \@@_cline:nn #1 #2
  {
    \@@_cline:n { #1 - #2 }
  }
%    \end{macrocode}
% \end{macro}
% 
% \begin{macro}{\@@_hline:}
% 
%  Wrapper for a \cs{hline}
% 
%    \begin{macrocode}
\cs_set_eq:NN \@@_hline: \hline
%    \end{macrocode}
% \end{macro}
% 
% 
% \begin{macro}{\@@_rotatebox:nnn}
% 
%  Wrapper around rotatebox
% 
%    \begin{macrocode}
\cs_new:Npn \@@_rotatebox:nnn #1 %implicit #2, #3
  {
    \rotatebox [ #1 ]
  }
%    \end{macrocode}
% \end{macro}
% 
% 
% \subsection{Customizable backends}
% 
% We set up some functions that will be used internally,
% but could technically be customized at some point.
% For now, these are just constants.
% 
% \begin{macro}{\@@_points:n}
% 
%  Prints the number of points.
%  Used in the last column of the grading scheme.
% 
%    \begin{macrocode}
\cs_new:Npn \@@_points:n #1
  {
    \textbf{ #1 ~ P. }
  }
%    \end{macrocode}
% \end{macro}
% 
% \begin{macro}{\@@_operation:n}
% 
%  Prints an operation.
%  Used in the leftmost column of a block.
% 
%    \begin{macrocode}
\cs_new:Npn \@@_operation:n #1
  {
    { \sc #1 }
  }
%    \end{macrocode}
% \end{macro}
% 
% 
% \subsection{Resources}
% 
% \subsubsection{Some general thoughts}
% 
% This is some general reasoning about why we model data structures
% as we do in this package, comparing parts of \LaTeX3 to plain |C| code.
% 
% We have to model elements, blocks and entries here.
% \LaTeX3 provides some data structures for this,
% and we will use property-lists to model our data structures for this package.
% 
% When thinking about what a property list is,
% a call to \cs{prop_new:N} essentially allocates some memory for us
% and makes our given argument a \emph{pointer} to a data structure
% that we refer to as a \enquote{property list}.
% 
% We want to think of these as pointers since modifying a property list
% at some place really also modifies this property list at another,
% so these behave like pointers.
% 
% Considering the grouping mechanism of \TeX,
% this is of course not quite right for local variables
% (we assume that in the \LaTeX3 spirit,
% all variables are either local or global once and for all,
% to avoid confusion)
% since these rather correspond to one pointer at each
% grouping level, where upon entering such a grouping
% level, the (new) pointer is initialized with a copy of the outer instance of
% the pointer and destroyed again when leaving the current grouping level.
% 
% In this spirit, we really want to think of grouping levels as \emph{scopes}
% like in \texttt{C}.
% 
% Considering functions now, a typical \LaTeX3 function has no return value
% (these would be functions with the |_p| predicate or |_use:| functions
% that leave contents in the input stream),
% since this is in general not desired.
% Rather, we pass pointers as a function argument and receive the desired
% \enquote{return value} as written into our given pointer (overwriting
% any prior data), which is also a common behaviour in large parts of |C|.
% 
% Now, as mentioned earlier, data structures such as property queues are
% just pointers that refer to some tokens with specific internal structure.
% All functions dealing with such pointers have to ensure that these
% invariants remain satisfied, and this is what functions like
% \cs{prop_get:NnN} or \cs{prop_put:Nn} do.
% The key point here is that we refer to these structures only
% with pointers and do not deal with them directly.
% This is what gives us some very nice abstrict model,
% hiding the class invariants of a property queue from the user and providing
% high-level semantic access to such data structures.
% 
% Just for completeness, we mention that this is not the case for \emph{all}
% data structures.
% For example, a |clist| has some structure that is well-known and can be
% dealt with by the user directly.
% That is why there are also |clist| commands acceptiong |n|-type arguments
% for dealing with a |clist|, but no such commands for property-lists, as
% they only use |N| and thus essentially pointers.
% 
% If we want to model data structures now, especially ours from this package,
% we even want data structures containing other data structures.
% Although technically, storing all data of a grading scheme in a single macro
% would be possible, due to its recursive structure, parsing would be quite
% a challenge and possibly also rather slow.
% Also, this would require \emph{direct} access to the representation of blocks
% contained in this block, which is undesired.
% 
% Compare this to the question of putting a property-list inside a property-list.
% Since there is no (semantic/provided) possibility of actually putting a
% property-list anywhere, we cannot store property-lists in property-lists
% directly.
% We can, however, store the name of a property list inside of another one,
% this amounts to actually storing the \emph{pointer} to the first list
% inside the second one.
% 
% Now, back to our package, we essentially want to model blocks and alike
% as |C|-style |struct|s that contain \emph{pointers} to their elements
% and similar.
% The classic question of ownership arises in this context,
% so we would have to consider shallow or deep copies and a user
% has to watch out when dealing with our data structures.
% 
% Since only a limited set of operations is needed in our case,
% we take a simple approach by saying that each struct owns
% all its contents, that is, owns everythin its data member pointers
% point to, also recursively.
% This essentially forbids shallow-copying (violating our assumption)
% but we do not need this here, so this is okay.
% 
% Still, this hase some undesired consequences:
% Since in \LaTeX3 each pointer has some name, we need to construct
% a lot of pointers on the fly where we are not able to actually
% specify their names directly, so we have to indirectly deal with pointers.
% That means that we actually need double-pointers, i.e. local variables
% \emph{containing} pointers to the data structures we are actually interested
% in. This is what a |void**| would correspond to in |C|.
% Also, note that once we store a pointer to some element in a property
% queue (e.g. the name of the list of members when dealing with a block),
% we actually also need a |void**| when retrieving this value again from
% the property queue, since we return by pointer.
% 
% Consequently, this leads to quite a lot of expansion-control when dealing
% with such pointers, and if not taking precautions, also to easy confusion
% about what is a pointer and what not.
% 
% Our approach to tackle these problems is described in \autoref{subsec:pointers}.
% 
% \subsection{Pointers}
% \label{subsec:pointers}
% 
% This module should/will be reworked into an own package in the future.
% Also, there is no proper documentation for it apart from the comments
% in the implementation, since this is not meant to be used in other
% code than this package currently, nor regarded stable.
% 
% We introduce a new data type of |pointer| and a new argument
% type of |P|. The argument type |P| shall indicate a single expansion
% \emph{with the assumption that this expansion will yield a single token}.
% So, from an implementation point of view, this is the same as an |o|-type
% argument, just with further restricted assumptions.
% 
% This also enables us to generate |P| variant arguments from |N| variant
% macros. Compare this to the usual deprecation warning that \LaTeX3
% gives when generating an |o| variant from an |N|-type argument:
% 
% \begin{verbatim}
%  ! LaTeX3 Error: Variant form 'o' deprecated for base form '\foo:N'. One
%  (LaTeX3)        should not change an argument from type 'N' to type 'o':
%  (LaTeX3)        base form only accepts a single token argument.
% \end{verbatim}
% 
% 
% Since basically anything in \LaTeX3 is in fact a pointer, we will
% introduce the |pointer| structure with the |ptr| type (suffix)
% and actually mean that this is a |void**|.
% 
% Thus, we introduce the following conventions:
% \begin{enumerate}
%  \item
%    A \TeX-pointer is a \TeX control sequence that represents
%    some data.
%  \item
%    A |pointer| is a control sequence that
%    expands to a single \TeX{} control sequence that is a
%    \TeX-pointer.
%  \item
%    The \emph{name} of a |pointer| is the name of the control sequence
%    representing it.
%    Thus, such a name always ends with |_ptr| by convetion.
%  \item
%    A |ptr| is said to be |NULL| if its expansion yields an undefined
%    \TeX{} control sequence.
%    We also refer to this as a |nullptr|.
%  \item
%    A \emph{typed pointer} is a pointer where the underlying type is specified.
%    By this, we mean that expanding the token exactly once yields a token
%    representing a data structure of the type we said this pointer to have
%    or that the pointer is |NULL|.
%  \item
%    A \emph{void pointer} shall be a pointer whose actual type is unknown.
%  \item
%    When a |ptr| is of type |type|, we denote these by the suffix
%    |_type_ptr|.
%  \item
%    \emph{Dereferencing} a pointer amounts to expanding it exactly once.
%  \item
%    A |P|-type argument accepts a single token that has to be of type |ptr|.
%    The token is expanded exactly once and then treated as an |N|-type argument
%    in the corresponding function.
% \end{enumerate}
% 
% 
% \begin{variable}{\g__ptr_counter_int}
% 
%  Counts the number of pointers given by \cs{ptr_new:N}.
%  This ensures that each pointer can have a unique name
%  regardless of grouping.
% 
%    \begin{macrocode}
\int_new:N \g__ptr_counter_int
%    \end{macrocode}
% \end{variable}
% 
% \begin{variable}{\l__ptr_var_prefix_str}
% 
%  Stores the prefix of new pointer variables.
% 
%    \begin{macrocode}
\str_new:N \l__ptr_var_prefix_str
%    \end{macrocode}
% \end{variable}
% 
% 
% 
% \begin{macro}{\ptr_new:N}
%  \begin{syntax}
%    \cs{ptr_new:N}\meta{pointer}
%  \end{syntax}
% 
%  Gives a new unique pointer that is |NULL|.
%  Gives an error when the \meta{pointer} already exists.
%  The pointer variable is created globally.
% 
%    \begin{macrocode}
\cs_new:Npn \ptr_new:N #1
  {
    \__ptr_get_var_prefix:NN #1 \l__ptr_var_prefix_str
    \ptr_new:NVn #1 \l__ptr_var_prefix_str { any }
  }
\cs_new:Npn \ptr_new:Nn #1 #2
  {
    \ptr_new:Nnn { #1 } { #2 } { any }
  }
% ptr var, prefix, type
\cs_new:Npn \ptr_new:Nnn #1 #2 %i #3
  {
    \str_case:nnT
      { #2 }
      {
        { l } { }
        { g } { }
      }
      {
        \tl_new:N #1
      }
    \__ptr_init:Nnn #1 { #2 } %i { #3 }
  }
\cs_generate_variant:Nn \ptr_new:Nnn { N V n }
% ptr var, prefix, type
% assumes that the pointer name exists already in case of l or g prefix
\cs_new:Npn \__ptr_init:Nnn #1 #2 % implicit #3
  {
    \__ptr_get_var_prefix:NN #1 \l__ptr_var_prefix_str
    \str_case:VnF
      \l__ptr_var_prefix_str
      {
        { l }
        {
          \cs_set_eq:NN \__ptr_poly_tl_set:Nx \tl_set:Nx
        }
        { g }
        {
          \cs_set_eq:NN \__ptr_poly_tl_set:Nx \tl_gset:Nx
        }
        { c }
        {
          \cs_set_eq:NN \__ptr_poly_tl_set:Nx \tl_const:Nx
        }
      }
      {
        \str_show:N \l__ptr_var_prefix_str % TODO: show error
      }
    \__ptr_init_aux:Nnn #1 { #2 }
  }
\cs_generate_variant:Nn \__ptr_init:Nnn { N V n }
% ptr var, prefix, type. assumes poly_tl_set:Nx has been set
\cs_new:Npn \__ptr_init_aux:Nnn #1 #2 #3
  {
    \__ptr_poly_tl_set:Nx #1
      {
        \exp_not:c
          {
            #2
            __ptr__unique__
            \int_use:N \g__ptr_counter_int
            _
            #3
          }
      }
    \int_gincr:N \g__ptr_counter_int
  }
\cs_generate_variant:Nn \__ptr_init:Nnn { N V n }
%    \end{macrocode}
% \end{macro}
% 
% 
% \begin{macro}{\__ptr_get_var_prefix:NN}
% 
%  Gets the first character of the name of the variable given as |#1|.
%  The first character is assumed to be one of |c|, |l| or |g| by
%  usual naming conventions.
% 
%    \begin{macrocode}
\cs_new:Npn \__ptr_get_var_prefix:NN #1 #2
  {
    \tl_if_head_eq_charcode:fNTF { \cs_to_str:N #1 } l
      {
        \str_set:Nn #2 { l }
      }
      {
        \tl_if_head_eq_charcode:fNTF { \cs_to_str:N #1 } g
          {
            \str_set:Nn #2 { g }
          }
          {
            \tl_if_head_eq_charcode:fNTF { \cs_to_str:N #1 } c
              {
                \str_set:Nn #2 { c }
              }
              {
                \msg_error:nnx { ptr } { variable-name-illegal-prefix }
                  {
                    \token_to_str:N #1
                  }
              }
          }
      }
  }
%    \end{macrocode}
%    \begin{macrocode}
  \msg_new:nnnn { ptr } { variable-name-illegal-prefix }
    {
      Requested ~ new ~ pointer ~ '#1' ~ has ~ illegal ~ prefix.
    }
    {
      Prefix ~ should ~ be ~ one ~ of ~ 'l', ~ 'g' ~ or ~ 'c'.
    }
%    \end{macrocode}
% \end{macro}
% 
% 
% 
% \begin{macro}{\ptr_clear:N}
% 
%  Clears this pointer.
%  This makes the pointer equivalent to a new |nullptr|.
% 
%    \begin{macrocode}
\cs_new:Npn \ptr_clear:N #1
  {
    \__ptr_get_var_prefix:NN #1 \l__ptr_var_prefix_str
    \__ptr_init:NVn #1 \l__ptr_var_prefix_str { any }
  }
\cs_new:Npn \ptr_clear:Nn #1 #2
  {
    \__ptr_init:Nnn #1 { #2 } { any }
  }
%    \end{macrocode}
% \end{macro}
% 
% \begin{macro}{\ptr_set:NN}
% 
%  Sets the pointer to represent the given argument.
% 
%    \begin{macrocode}
\cs_set_eq:NN \ptr_set:NN \tl_set:Nn
%    \end{macrocode}
% \end{macro}
% 
% \begin{macro}{\ptr_set_eq:NN}
% 
%  Accepts two pointers.
%  The first is set to be equivalent to the second.
% 
%    \begin{macrocode}
\cs_set_eq:NN \ptr_set_eq:NN \tl_set_eq:NN
%    \end{macrocode}
% \end{macro}
% 
% 
% \begin{macro}{\ptr_use:N}
% 
%  Uses the pointer, i.e.~expands to the stored \TeX{}-pointer.
% 
%    \begin{macrocode}
\cs_set_eq:NN \ptr_use:N \tl_use:N
%    \end{macrocode}
% \end{macro}
% 
% \begin{macro}{\__ptr_check:N}
% 
%  Checks that the given argument is a pointer.
% 
%    \begin{macrocode}
\cs_new:Npn \__ptr_check:N #1
  {
    \tl_if_single:NF #1
      {
        \msg_error:nnx { ptr } { is-not-a-pointer }
          {
            \token_to_str:N #1
          }
      }
  }
%    \end{macrocode}
%    \begin{macrocode}
\msg_new:nnn { ptr } { is-not-a-pointer }
  {
    The ~ control ~ sequence ~ '#1' ~ is ~ not ~ a ~ valid ~ pointer.
  }
%    \end{macrocode}
% \end{macro}
% 
% \begin{macro}{\__ptr_check:NN}
% 
%  Checks that the second argument is a pointer.
% 
%    \begin{macrocode}
\cs_new:Npn \__ptr_check:NN #1 #2
  {
    \__ptr_check:N #2
    #1 #2
  }
%    \end{macrocode}
% \end{macro}
% 
% \begin{macro}{\__ptr_check:nN}
% 
%  Checks that the second argument is a pointer.
% 
%    \begin{macrocode}
\cs_new:Npn \__ptr_check:nN #1 #2
  {
    \__ptr_check:N #2
    { #1 } #2
  }
%    \end{macrocode}
% \end{macro}
% 
% 
% 
% \begin{variable}{\l__ptr_content_tl}
% 
%  Stores the (internal) \TeX-pointer
%  of a |pointer|.
% 
%    \begin{macrocode}
\tl_new:N \l__ptr_content_tl
%    \end{macrocode}
% \end{variable}
% 
% \begin{macro}{\exp_args:NP, \exp_args:NNP, \exp_args:NNNP, \exp_args:NNNNP, \exp_args:NPP}
% 
%  Expands a pointer.
%  This leaves the first tokens as a single token in the input
%  stream, followed by the value of the pointer
%  as a single token.
% 
%  If the last argument does not expand to
%  a single token,
%  an error is given.
% 
%    \begin{macrocode}
\cs_new:Npn \exp_args:NP #1 #2
  {
    \__ptr_check:N #2
    \exp_last_unbraced:No #1 #2
  }
\cs_new:Npn \exp_args:NNP #1 #2 #3
  {
    \__ptr_check:N #3
    \exp_last_unbraced:NNo #1 #2 #3
  }
\cs_new:Npn \exp_args:NNNP #1 #2 #3 #4
  {
    \__ptr_check:N #4
    \exp_last_unbraced:NNNo #1 #2 #3 #4
  }
\cs_new:Npn \exp_args:NNNNP #1 #2 #3 #4 #5
  {
    \__ptr_check:N #5
    \exp_last_unbraced:NNNNo #1 #2 #3 #4 #5
  }
\cs_new:Npn \exp_args:NPP #1 #2 #3
  {
    \__ptr_check:N #2
    \__ptr_check:N #3
    \exp_last_two_unbraced:Noo #1 #2 #3
  }
%    \end{macrocode}
% \end{macro}
% 
% 
% \begin{macro}[TF]{\ptr_if_null:N}
% 
%  Checkes if the pointer is |NULL|.
% 
%    \begin{macrocode}
\cs_new:Npn \ptr_if_null:NT
  {
    \exp_args:NP \cs_if_exist:NF
  }
\cs_new:Npn \ptr_if_null:NF
  {
    \exp_args:NP \cs_if_exist:NT
  }
\cs_new:Npn \ptr_if_null:NTF #1 #2 #3
  {
    \exp_args:NP \cs_if_exist:NTF #1 { #3 } { #2 }
  }
%    \end{macrocode}
% \end{macro}
% 
% \begin{macro}{\tl_set_eq:NP, \tl_set_eq:PN, \tl_set_eq:PP, \tl_use:P}
% \begin{macro}{\exp_not:P}
% \begin{macro}{\clist_new:P, \clist_put_right:Pn, \clist_gput_right:Pn, \clist_map_function:PN, \prop_show:P, \clist_show:P}
% 
% 
%  Just variants of \cs{tl_set_eq:NN} with indicated signature.
% 
%    \begin{macrocode}
\cs_new:Npn \tl_set_eq:NP
  {
    \exp_args:NNP \tl_set_eq:NN
  }
\cs_new:Npn \tl_set_eq:PN
  {
    \exp_args:NP \tl_set_eq:NN
  }
\cs_new:Npn \tl_set_eq:PP
  {
    \exp_args:NPP \tl_set_eq:NN
  }
\cs_new:Npn \tl_use:P
  {
    \exp_args:NP \tl_use:N
  }
\cs_new:Npn \exp_not:P
  {
    \exp_args:NP\exp_not:N
  }
\cs_new:Npn \clist_new:P
  {
    \exp_args:NP \clist_new:N
  }
\cs_new:Npn \clist_show:P
  {
    \exp_args:NP \clist_show:N
  }
\cs_new:Npn \clist_put_right:Pn
  {
    \exp_args:NP \clist_put_right:Nn
  }
\cs_new:Npn \clist_gput_right:Pn
  {
    \exp_args:NP \clist_gput_right:Nn
  }
\cs_new:Npn \clist_map_function:PN
  {
    \exp_args:NP \clist_map_function:NN
  }
\cs_new:Npn \prop_show:P
  {
    \exp_args:NP \prop_show:N
  }
%    \end{macrocode}
% \end{macro}
% \end{macro}
% \end{macro}
% 
% 
% 
% 
% \begin{macro}{\ptr_show:N}
% 
%  Shows information about this pointer,
%  namely:
% 
%  If it is |NULL|, then it indicates this.
%  If it is not |NULL|, then the \TeX-pointer
%  and the contained contents of the \TeX-pointer
%  (in unexpanded form) are shown.
% 
%    \begin{macrocode}
\cs_new:Npn \ptr_show:N #1
  {
    \__ptr_check:N #1
    \ptr_if_null:NTF #1
      {
        \tl_show:x
          {
            \token_to_str:N #1 -> \exp_not:P #1 = NULL
          }
      }
      {
        \tl_show:x
          {
            \token_to_str:N #1 -> \exp_not:P #1 -> \exp_args:NP \exp_not:V #1
          }
      }
  }
%    \end{macrocode}
% \end{macro}
% 
% 
% \subsection{Structs}
% 
% We will model |C|-style |struct|s as property-lists in this package.
% Each struct member is put into the property list, using its
% 'name' as a key, and we store the corresponding contents there.
% 
% We will also have a local variable named correspondingly for each
% |struct| member. In case we deal with a struct and want to acces
% its data, we load the values from the property-list into these
% local variables.
% Thus, the full information about a |struct| is contained in the
% property-list, and we can work with them quite conveniently
% when implementing functions.
% 
% 
% \subsection{Entries}
% 
% An entry is for us the following:
% 
% \begin{verbatim}
%  struct Entry {
%    tl description;
%    int points;
%  }
% \end{verbatim}
% 
% \begin{macro}{\grading_scheme_entry_new:N, \grading_scheme_entry_clear:N, \grading_scheme_entry_set_eq:NN, \grading_scheme_entry_gclear:N, \grading_scheme_entry_gset_eq:NN}
% 
%  Do what their signature suggests.
%  We just forward them to the property lists.
% 
%    \begin{macrocode}
\cs_set_eq:NN \grading_scheme_entry_new:N      \prop_new:N
\cs_set_eq:NN \grading_scheme_entry_clear:N    \prop_clear:N
\cs_set_eq:NN \grading_scheme_entry_set_eq:NN  \prop_set_eq:NN
\cs_set_eq:NN \grading_scheme_entry_gclear:N   \prop_gclear:N
\cs_set_eq:NN \grading_scheme_entry_gset_eq:NN \prop_gset_eq:NN
%    \end{macrocode}
% \end{macro}
% 
% \begin{variable}{\l_@@_entry_points_int, \l_@@_entry_description_tl}
% 
%  The mentioned local variables where we retrieve values.
% 
%    \begin{macrocode}
\int_new:N \l_@@_entry_points_int
\tl_new:N  \l_@@_entry_description_tl
%    \end{macrocode}
% \end{variable}
% 
% 
% \begin{macro}{\grading_scheme_entry_set_description:Nn, \grading_scheme_entry_gset_description:Nn}
% 
%  Sets the description.
% 
%    \begin{macrocode}
\cs_new:Npn \grading_scheme_entry_set_description:Nn #1 % implicit description
  {
    \prop_put:Nnn #1 { description }
  }
\cs_new:Npn \grading_scheme_entry_gset_description:Nn #1 % implicit description
  {
    \prop_gput:Nnn #1 { description }
  }
%    \end{macrocode}
% \end{macro}
% 
% 
% \begin{macro}{\grading_scheme_entry_set_points:Nn, \grading_scheme_entry_gset_points:Nn}
% 
%  Sets the points.
% 
%    \begin{macrocode}
\cs_new:Npn \grading_scheme_entry_set_points:Nn #1 #2
  {
    \prop_put:Nnx #1 { points } { \int_eval:n { #2 } }
  }
\cs_new:Npn \grading_scheme_entry_gset_points:Nn #1 #2
  {
    \prop_gput:Nnn #1 { points } { \int_eval:n { #2 } }
  }
%    \end{macrocode}
% \end{macro}
% 
% \begin{macro}{\grading_scheme_entry_get_description:NN}
% 
%  Gets the description of the entry and stores it in |#2|.
%  The assignment is local.
% 
%    \begin{macrocode}
\cs_new:Npn \grading_scheme_entry_get_description:NN #1 %implicit #2
  {
    \prop_get:NnN #1 { description }
  }
%    \end{macrocode}
% \end{macro}
% 
% \begin{macro}{\grading_scheme_entry_get_points:NN}
% 
%  Gets the points of the entry and stores it in |#2|.
%  The assignment is local.
% 
%    \begin{macrocode}
\cs_new:Npn \grading_scheme_entry_get_points:NN #1 %implicit #2
  {
    \prop_get:NnN #1 { points }
  }
%    \end{macrocode}
% \end{macro}
% 
% 
% \begin{macro}{\@@_entry_load_points:N}
% 
%  Loads the points into the local variable
% 
%    \begin{macrocode}
\cs_new:Npn \@@_entry_load_points:N #1
  {
    \grading_scheme_entry_get_points:NN #1 \l_@@_entry_points_int
  }
%    \end{macrocode}
% \end{macro}
% 
% \begin{macro}{\@@_entry_load_description:N}
% 
%  Loads the description of an entry
% 
%    \begin{macrocode}
\cs_new:Npn \@@_entry_load_description:N #1
  {
    \grading_scheme_entry_get_description:NN #1 \l@@_entry_description_tl
  }
%    \end{macrocode}
% \end{macro}
% 
% 
% \begin{macro}{\grading_scheme_entry_format:NnnN, \grading_scheme_entry_gformat:NnnN,\grading_scheme_entry_format:PnnN, \grading_scheme_entry_gformat:PnnN}
%  \begin{syntax}
%    \cs{grading_scheme_entry_put:NnnN}\Arg{entry}\Arg{indent}\Arg{width}\Arg{tl var}
%  \end{syntax}
% 
%  Puts the formatted contents of the entry into the \meta{tl var}.
%  The \meta{indent} specify how many tabs will be inserted
%  before adding the contents.
%  The \meta{width} specifies how many columns this entry will occupy.
%  This \meta{width} has to be at least 2.
% 
%  An |\\| is inserted at the end of the entry.
% 
%    \begin{macrocode}
\cs_new:Npn \grading_scheme_entry_format:NnnN
  {
    \cs_set_eq:NN \@@_tl_put_right:Nx \tl_put_right:Nx
    \@@_entry_format:NnnN
  }
\cs_new:Npn \grading_scheme_entry_gformat:NnnN
  {
    \cs_set_eq:NN \@@_tl_put_right:Nx \tl_gput_right:Nx
    \@@_entry_format:NnnN
  }
\cs_new:Npn \grading_scheme_entry_format:PnnN
  {
    \exp_args:NP \grading_scheme_entry_format:NnnN
  }
\cs_new:Npn \grading_scheme_entry_gformat:PnnN
  {
    \exp_args:NP \grading_scheme_entry_gformat:NnnN
  }
%    \end{macrocode}
% \end{macro}
% 
% \begin{macro}{\@@_entry_format:NnnN, \@@_entry_format:PnnN}
% 
%  Aux function that assumes that \cs{@@_tl_put_right:Nx}
%  has been so to globally or locally putting into the token list.
% 
%    \begin{macrocode}
\cs_new:Npn \@@_entry_format:NnnN #1 #2 #3 #4
  {
%<*log>
    \@@_log:x
      { Formatting ~ entry ~ '\token_to_str:N #1' ~ into ~ '\token_to_str:N #4' with }
    \@@_log:n { indent = '#2' ~ and ~ width ~ '#3' }
    \prop_log:N #1
    \@@_log_incr:
%</log>
    \@@_entry_assert_description:N #1 % implicitly loads value
    \@@_entry_assert_points:N #1      % implicitly loads value
    \@@_tl_put_right:Nx #4
      {
        \prg_replicate:nn { #2 } { & }
        \exp_not:N \@@_multicolumn:nnn
          {
            \int_eval:n { #3 -1 }
          }
          { l }
          {
            \exp_not:V \l_@@_entry_description_tl
          }
        &
        \exp_not:N \@@_points:n
          {
            \tl_use:N \l_@@_entry_points_int
          }
        \\
      }
%<*log>
    \@@_log_decr:
    \@@_log:x { / Formatting ~ entry ~ '\token_to_str:N #1' }
%</log>
  }
\cs_new:Npn \@@_entry_forrmat:PnnN % implicit #1-4
  {
    \exp_args:NP \@@_entry_format:NnnN
  }
%    \end{macrocode}
% \end{macro}
% 
% 
% 
% \begin{macro}{\@@_entry_assert_description:N, \@@_entry_assert_points:N}
% 
%  These functions check the presence of values of an entry.
%  If an entry is absent, an error message is omitted.
% 
%    \begin{macrocode}
\cs_new:Npn \@@_entry_assert_description:N #1
  {
    \@@_entry_load_description:N #1
    \quark_if_no_value:NT \l_@@_entry_description_tl
      {
        \msg_error:nnxxx { grading-scheme } { missing-value }
          { entry } { \token_to_str:N #1 } { description }
      }
  }
\cs_new:Npn \@@_entry_assert_points:N #1
  {
    \@@_entry_load_points:N #1
    \quark_if_no_value:NT \l_@@_entry_points_int
      {
        \msg_error:nnxxx { grading-scheme } { missing-value }
          { entry } { \token_to_str:N #1 } { points }
      }
  }
\msg_new:nnn { grading-scheme } { missing-value }
  {
    #1 ~ '#2' ~ has ~ no ~ #3.
  }
%    \end{macrocode}
% \end{macro}
% 
% 
% \subsection{Blocks}
% 
% \subsubsection{Struct setting / reading}
% 
% A \meta{block} is for us the following:
% 
% \begin{verbatim}
%  struct Block {
%    clist* elements;
%    tl     text;
%    tl     operation;
%  }
% \end{verbatim}
% 
% \begin{macro}{\grading_scheme_block_new:N, \grading_scheme_block_clear:N, \grading_scheme_block_set_eq:NN, \grading_scheme_block_gclear:N, \grading_scheme_block_gset_eq:NN}
% 
%  Do what thear names suggest. We just forward these to the property lists.
% 
%    \begin{macrocode}
\cs_set_eq:NN \grading_scheme_block_new:N      \prop_new:N
\cs_set_eq:NN \grading_scheme_block_clear:N    \prop_clear:N
\cs_set_eq:NN \grading_scheme_block_set_eq:NN  \prop_gset_eq:NN
\cs_set_eq:NN \grading_scheme_block_gclear:N   \prop_gclear:N
\cs_set_eq:NN \grading_scheme_block_gset_eq:NN \prop_gset_eq:NN
%    \end{macrocode}
% \end{macro}
% 
% \begin{variable}{\l_@@_block_elements_clist_ptr, \l_@@_block_description_tl, \l_@@_block_operation_tl}
% 
%  The mentioned local variables where we retrieve values.
% 
%    \begin{macrocode}
\ptr_new:N \l_@@_block_elements_clist_ptr
\tl_new:N  \l_@@_block_description_tl
\tl_new:N  \l_@@_block_operation_tl
%    \end{macrocode}
% \end{variable}
% 
% \begin{macro}
%  {
%    \grading_scheme_block_set_description:Nn,  \grading_scheme_block_gset_description:Nn,
%    \grading_scheme_block_set_operation_tl:Nn,  \grading_scheme_block_gset_operation_tl:Nn,
%    \grading_scheme_block_set_elements:NN, \grading_scheme_block_gset_elements:NN,
%    \grading_scheme_block_set_elements:NP, \grading_scheme_block_gset_elements:NP,
%  }
% 
%  Set the description / operation or elements of the block.
% 
%  When setting elements, a \meta{clist var} is expected.
% 
%    \begin{macrocode}
\cs_new:Npn \grading_scheme_block_set_description:Nn #1 % implicit description
  {
    \prop_put:Nnn #1 { description }
  }
\cs_new:Npn \grading_scheme_block_gset_description:Nn #1 % implicit description
  {
    \prop_gput:Nnn #1 { description }
  }
\cs_new:Npn \grading_scheme_block_set_operation:Nn #1 % implicit operation
  {
    \prop_put:Nnn #1 { operation }
  }
\cs_new:Npn \grading_scheme_block_gset_operation:Nn #1 % implicit operation
  {
    \prop_gput:Nnn #1 { operation }
  }
\cs_new:Npn \grading_scheme_block_set_elements:NN #1 % implicit elements clist
  {
    \prop_put:Nnn #1 { elements }
  }
\cs_new:Npn \grading_scheme_block_gset_elements:NN #1 % implicit elements clist
  {
    \prop_gput:Nnn #1 { elements }
  }
\cs_new:Npn \grading_scheme_block_set_elements:NP
  {
    \exp_args:NNP \grading_scheme_block_set_elements:NN
  }
\cs_new:Npn \grading_scheme_block_gset_elements:NP
  {
    \exp_args:NNP \grading_scheme_block_gset_elements:NN
  }
%    \end{macrocode}
% \end{macro}
% 
% \begin{macro}{\grading_scheme_block_get_description:NN, \grading_scheme_block_get_operation:NN, \grading_scheme_block_get_elements:NN}
% 
%  \begin{syntax}
%    \cs{grading_scheme_block_get_description:NN}\meta{block var}\meta{tl var}
%    \cs{grading_scheme_block_get_operation:NN}\meta{block var}\meta{tl var}
%    \cs{grading_scheme_block_get_elements:NN}\meta{block var}\meta{clist ptr}
%  \end{syntax}
% 
%  Get access to the members of the block.
%  The assignment is local.
%  The returned values might be \cs{q_no_value}
%  if no value is present.
% 
%    \begin{macrocode}
\cs_new:Npn \grading_scheme_block_get_description:NN #1 % implicit #2
  {
    \prop_get:NnN #1 { description }
  }
\cs_new:Npn \grading_scheme_block_get_operation:NN #1 % implicit #2
  {
    \prop_get:NnN #1 { operation }
  }
\cs_new:Npn \grading_scheme_block_get_elements:NN #1 % implicit #2
  {
    \prop_get:NnN #1 { elements }
  }
%    \end{macrocode}
% \end{macro}
% 
% \begin{macro}{\@@_block_load_description:N, \@@_block_load_operation:N, \@@_block_load_elements:NN}
% 
%  Loads the members into the corresponding local variables.
% 
%    \begin{macrocode}
\cs_new:Npn \@@_block_load_description:N #1
  {
    \grading_scheme_block_get_description:NN #1 \l_@@_block_description_tl
  }
\cs_new:Npn \@@_block_load_operation:N #1
  {
    \grading_scheme_block_get_operation:NN #1 \l_@@_block_operation_tl
  }
\cs_new:Npn \@@_block_load_elements:N #1
  {
    \grading_scheme_block_get_elements:NN #1 \l_@@_block_elements_clist_ptr
  }
%    \end{macrocode}
% \end{macro}
% 
% 
% 
% \begin{macro}{\@@_block_ensure_elements:N}
% 
%  Ensures that this block has an elements attribute that is a valid
%  clist pointer.
% 
%  If no value is present, or the pointer is |NULL|,
%  a pointer and/or the clist variable
%  are created.
% 
%    \begin{macrocode}
\cs_new:Npn \@@_block_ensure_elements:N % implicit #1
  {
    \cs_set_eq:NN
      \@@_block_set_elements_optg:NP
      \grading_scheme_block_set_elements:NP
    \@@_block_ensure_elements_aux:nN { l }
  }
\cs_new:Npn \@@_block_gensure_elements:N % implicit #1
  {
    \cs_set_eq:NN
      \@@_block_set_elements_optg:NP
      \grading_scheme_block_gset_elements:NP
    \@@_block_ensure_elements_aux:nN { g } %i #1
  }
% prefix, block
% assumes that \@@_block_set_elements_optg:NP has been
\cs_new:Npn \@@_block_ensure_elements_aux:nN #1 #2
  {
    \@@_block_load_elements:N #2
    \quark_if_no_value:NT \l_@@_block_elements_clist_ptr
      {
        \ptr_clear:Nn \l_@@_block_elements_clist_ptr { #1 }
        \@@_block_set_elements_optg:NP
          #2
          \l_@@_block_elements_clist_ptr
      }
    \ptr_if_null:NT \l_@@_block_elements_clist_ptr
      {
        \clist_new:P \l_@@_block_elements_clist_ptr
      }
  }
%    \end{macrocode}
% \end{macro}
% 
% 
% \begin{macro}{\grading_scheme_block_add_element:NN, \grading_scheme_block_gadd_element:NN, \grading_scheme_block_add_element:PP, \grading_scheme_block_gadd_element:PP, \grading_scheme_block_add_element:PN, \grading_scheme_block_gadd_element:PN}
%  \begin{syntax}
%    \cs{grading_scheme_block_add_element:NN}\meta{block var}\meta{element var}
%  \end{syntax}
% 
%  Add an element to the block.
% 
%    \begin{macrocode}
\cs_new:Npn \grading_scheme_block_add_element:NN #1 % implicit element
  {
    \@@_block_ensure_elements:N #1 % also loads local variable
    \clist_put_right:Pn \l_@@_block_elements_clist_ptr
  }
\cs_new:Npn \grading_scheme_block_gadd_element:NN #1 % implicit element
  {
    \@@_block_gensure_elements:N #1 % also loads local variable
    \clist_gput_right:Pn \l_@@_block_elements_clist_ptr
  }
\cs_new:Npn \grading_scheme_block_add_element:PP
  {
    \exp_args:NPP \grading_scheme_block_add_element:NN
  }
\cs_new:Npn \grading_scheme_block_gadd_element:PP
  {
    \exp_args:NPP \grading_scheme_block_gadd_element:NN
  }
\cs_new:Npn \grading_scheme_block_add_element:PN
  {
    \exp_args:NP \grading_scheme_block_add_element:NN
  }
\cs_new:Npn \grading_scheme_block_gadd_element:PN
  {
    \exp_args:NP \grading_scheme_block_gadd_element:NN
  }
%    \end{macrocode}
% \end{macro}
% 
% \subsubsection{Formatting blocks}
% 
% 
% \begin{macro}{\grading_scheme_block_format:NnnnN, \grading_scheme_block_gformat:NnnnN, \grading_scheme_block_format:PnnnN, \grading_scheme_block_gformat:PnnnN}
% 
%  \begin{syntax}
%    \cs{grading_scheme_block_format:NnnnN}\meta{block var}\Arg{indent}\Arg{width}\Arg{indent first row}\meta{tl var}
%  \end{syntax}
% 
%  Formats this block and puts the control sequence into \meta{tl var}.
% 
%  The \meta{indent} and \meta{width} work as in \cs{grading_scheme_entry_format:NnnN}.
% 
%  \meta{indent first row} can be any \meta{boolean expression} and indicates
%  if the first row is also indented.
%  Set this to false to start the block in a row of the tabular that already
%  has contents.
% 
%    \begin{macrocode}
\cs_new:Npn \grading_scheme_block_format:NnnnN %implicit #1-5
  {
    \cs_set_eq:NN \@@_tl_put_right:Nx \tl_put_right:Nx
    \cs_set_eq:NN \@@_element_format_optg:NnnnN \grading_scheme_element_format:NnnnN
    \cs_set_eq:NN \@@_tl_set:Nn \tl_set:Nn
    \@@_block_format:NnnnN
  }
\cs_new:Npn \grading_scheme_block_gformat:NnnnN %implicit #1-5
  {
    \cs_set_eq:NN \@@_tl_put_right:Nx \tl_gput_right:Nx
    \cs_set_eq:NN \@@_element_format_optg:NnnnN \grading_scheme_element_gformat:NnnnN
    \cs_set_eq:NN \@@_tl_set:Nn \tl_gset:Nn
    \@@_block_format:NnnnN
  }
\cs_new:Npn \grading_scheme_block_format:PnnnN % implicit #1-5
  {
    \exp_args:NP
    \grading_scheme_block_format:NnnnN
  }
\cs_new:Npn \grading_scheme_block_gformat:PnnnN % implicit #1-5
  {
    \exp_args:NP
    \grading_scheme_block_gformat:NnnnN
  }
%    \end{macrocode}
% \end{macro}
% 
% 
% For formatting the block, we need some local variables:
% 
% 
% \begin{variable}{\l_@@_block_indent_bool}
% 
%  Controls whether the \cs{@@_block_indent:nN} macro
%  will actually perform an indent.
% 
%    \begin{macrocode}
\bool_new:N \l_@@_block_indent_bool
%    \end{macrocode}
% \end{variable}
% 
% \begin{variable}{\@@_block_height_int}
% 
%  Locally stores the height of the block to be typeset.
% 
%    \begin{macrocode}
\int_new:N \l_@@_block_height_int
%    \end{macrocode}
% \end{variable}
% 
% 
% 
% \begin{macro}{\@@_block_format:NnnnN, \@@_block_format:PnnnN}
% 
%  Aux function.
%  Assumes that \cs{@@__tl_put_right:Nx}
%  has been set properly.
% 
%    \begin{macrocode}
\cs_new:Npn \@@_block_format:NnnnN #1 #2 #3 #4 #5
  {
%<*log>
    \@@_log:x
      {
        Formatting ~ block ~ '\token_to_str:N #1' ~ into ~ '\token_to_str:N #5'
        ~ with ~ indent ='#2', ~ width ~ '#3' ~ and ~ first ~
        row = '\bool_to_str:n { #4 }'
      }
    \prop_log:N #1
    \@@_block_load_elements:N #1
    \exp_args:NP \clist_log:N \l_@@_block_elements_clist_ptr
    \@@_log_incr:
%</log>
%    \end{macrocode}
%  We need grouping here so that our indentation boolean
%  is not messed up by recursive calls:
%    \begin{macrocode}
    \bool_set:Nn \l_@@_block_indent_bool { #4 && \c_true_bool }
    \group_begin:
    \@@_block_load_description:N #1
    \grading_scheme_block_get_height:NN #1 \l_@@_block_height_int
%    \end{macrocode}
% We now format the description of the block if a value is present.
%    \begin{macrocode}
    \quark_if_no_value:NF \l_@@_block_description_tl
      {
        \@@_block_indent:nN { #2 } #5
        \@@_tl_put_right:Nx #5
          {
            \exp_not:N \@@_multicolumn:nnn
              {
                \int_eval:n { #3 }
              }
              { l| }
              {
                \exp_not:V \l_@@_block_description_tl
              }
            \\
            \exp_not:N \@@_cline:nn
              {
                \int_eval:n { #2 + 2 }
              }
              {
                \int_eval:n { #2 + #3 }
              }
          }
      }
%    \end{macrocode}
% Now, we have to format the operation of this block.
% This is a multirow with rotated description
%    \begin{macrocode}
    \@@_block_indent:nN { #2 } #5
    \@@_block_assert_operation:N #1
    \@@_tl_put_right:Nx #5
      {
        \exp_not:N \@@_multirow:nnn
          {
            \int_eval:n { \int_use:N \l_@@_block_height_int - 1 }
          }
          { * }
          {
            \exp_not:N \@@_rotatebox:nnn
              { origin = c }
              { 90 }
              {
                \exp_not:N \@@_operation:n
                   {
                    \exp_not:V \l_@@_block_operation_tl
                  }
              }
          }
        &
      }
%    \end{macrocode}
%    The first element of our block must not be indented:
%    \begin{macrocode}
    \bool_set_false:N \l_@@_block_indent_bool
%    \end{macrocode}
%    Now, we want to recursively typeset all elements of this block.
%    We need a customized function for this to map over the elements.
%    \begin{macrocode}
    \cs_set:Npn \@@_block_format_element:N ##1
    {
      \@@_element_format_optg:NnnnN
        ##1
        { #2 + 1 }  % indent is one more that the current block
        { #3 - 1 }  % width is one less than current block
        { \l_@@_block_indent_bool }
        #5
%    \end{macrocode}
%    This ensures that the second and all further elements will get indented:
%    \begin{macrocode}
      \bool_set_true:N \l_@@_block_indent_bool
    }
    \@@_block_ensure_elements:N #1 % load + sanitize elements
    \clist_map_function:PN
      \l_@@_block_elements_clist_ptr
      \@@_block_format_element:N
%    \end{macrocode}
%  Now, we need to 'smuggle out' the output token list of the current group.
%    \begin{macrocode}
    \exp_args:NNNV
      \group_end:
      \@@_tl_set:Nn
      #5
      #5
%<*log>
    \@@_log_decr:
    \@@_log:x { / Formatting ~ block ~ '\token_to_str:N #1' }
%</log>
  }
\cs_new:Npn \@@_block_format:PnnnN
  {
    \exp_args:NP \@@_block_format:NnnnN
  }
%    \end{macrocode}
% \end{macro}
% 
% 
% \begin{macro}{\@@_block_assert_operation:N}
% 
%  Asserts that the block has an operation
%  and loads it.
%  If no operation is set,
%  an error is emitted.
% 
%    \begin{macrocode}
\cs_new:Npn \@@_block_assert_operation:N #1
  {
    \@@_block_load_operation:N #1
    \quark_if_no_value:NT \l_@@_block_operation_tl
      {
         \msg_error:nnxxx { grading-scheme } { missing-value}
           { block }
           { \token_to_str:N #1 }
           { operation }
      }
  }
%    \end{macrocode}
% \end{macro}
% 
% 
% 
% \begin{macro}{\@@_block_indent:nN}
%    \begin{syntax}
%      \cs{@@_block_indent:nN}\Arg{indent}\meta{tl var}
%    \end{syntax}
% 
%    Performs the indent into \meta{tl var}
%    iff \cs{l_@@_block_indent_bool} is true.
% 
%    Sets \cs{l_@@_block_indent_bool} to true afterwards.
% 
%    \begin{macrocode}
\cs_new:Npn \@@_block_indent:nN #1 #2
  {
    \bool_if:NT \l_@@_block_indent_bool
      {
        \@@_tl_put_right:Nx #2
          {
            \prg_replicate:nn { #1 } { & }
          }
      }
    \bool_set_true:N \l_@@_block_indent_bool
  }
%    \end{macrocode}
% \end{macro}
% 
% 
% \subsubsection{Width and height of a block}
% 
% \begin{variable}{\l_@@_height_sum_int, \l_@@_height_acc_int}
% 
%  Some temporary int values
% 
%    \begin{macrocode}
\int_new:N \l_@@_height_sum_int
\int_new:N \l_@@_height_acc_int
%    \end{macrocode}
% \end{variable}
% 
% 
% \begin{macro}{\grading_scheme_block_get_height:NN, \grading_scheme_block_get_height:PN}
%  \begin{syntax}
%    \cs{grading_scheme_block_get_height:NN}\meta{block var}\meta{int var}
%  \end{syntax}
% 
%  Gets the height of a block and stores
%  it into the \meta{int var}.
% 
%    \begin{macrocode}
\cs_new:Npn \grading_scheme_block_get_height:NN #1 #2
{
%<*log>
  \@@_log:x { Getting ~ height ~ of ~ block ~ '\token_to_str:N #1' }
  \@@_log_incr:
%</log>
%    \end{macrocode}
% Grouping is needed to not mess up local variables in the recursion:
%    \begin{macrocode}
  \group_begin:
  \@@_block_load_elements:N #1
  \int_zero:N \l_@@_height_sum_int
  \clist_map_function:PN
    \l_@@_block_elements_clist_ptr
    \@@_block_height_accumulator:N
  \grading_scheme_block_if_description:NT #1
    {
      \int_incr:N \l_@@_height_sum_int
    }
%    \end{macrocode}
% Smuggle out this length at return it to the caller:
%    \begin{macrocode}
  \exp_args:NNNV
    \group_end:
    \int_set:Nn #2 \l_@@_height_sum_int
%<*log>
  \@@_log_decr:
  \@@_log:x
    {
      / Getting ~ height ~ of ~ block ~ '\token_to_str:N #1':
      '\int_use:N #2'
    }
%</log>
}
\cs_new:Npn \grading_scheme_block_get_height:PN
  {
    \exp_args:NP \grading_scheme_block_get_height:NN
  }
%    \end{macrocode}
% \end{macro}
% 
% 
% \begin{macro}{\@@_block_height_accumulator:N}
% 
%  This is mapped on the elements of a block
%  and accumulates the heights.
% 
%    \begin{macrocode}
\cs_new:Npn \@@_block_height_accumulator:N #1
  {
    \grading_scheme_element_get_height:NN #1 \l_@@_height_acc_int
    \int_add:Nn \l_@@_height_sum_int
      { \int_use:N \l_@@_height_acc_int }
  }
%    \end{macrocode}
% \end{macro}
% 
% \begin{macro}[TF]{\grading_scheme_block_if_description:N}
% 
%    Tests if the given \meta{block var} has a description
%    set or not.
%    Also loads this description
% 
%    \begin{macrocode}
\cs_new:Npn \grading_scheme_block_if_description:NT #1 % implicit #2
  {
    \@@_block_load_description:N #1
    \quark_if_no_value:NF \l_@@_block_description_tl
  }
\cs_new:Npn \grading_scheme_block_if_description:NF #1 % implicit #2
  {
    \@@_block_load_description:N #1
    \quark_if_no_value:NT \l_@@_block_description_tl
  }
\cs_new:Npn \grading_scheme_block_if_description:NTF #1 #2 #3
  {
      \@@_block_load_description:N #1
      \quark_if_no_value:NTF \l_@@_block_description_tl
        { #3 }
        { #2 }
  }
%    \end{macrocode}
% \end{macro}
% 
% 
% \begin{variable}{\l_@@_max_width_int}
% 
%    \begin{macrocode}
%    \end{macrocode}
% \end{variable}
% 
% \begin{variable}{\l_@@_width_acc_int,\l_@@_max_width_int}
% 
%  Some temporary int values
% 
%    \begin{macrocode}
\int_new:N \l_@@_max_width_int
\int_new:N \l_@@_width_acc_int
%    \end{macrocode}
% \end{variable}
% 
% 
% 
% 
% \begin{macro}{\grading_scheme_block_get_natural_width:NN,\grading_scheme_block_get_natural_width:PN}
% 
%  Gets the \enquote{natural} width of a block,
%  that is: The minimal width of this block so
%  that it can be typeset properly.
% 
%    \begin{macrocode}
\cs_new:Npn \grading_scheme_block_get_natural_width:NN #1 #2
  {
%<*log>
     \@@_log:x
       { Getting ~ natural ~ width ~ of ~ block ~ '\token_to_str:N #1'. }
     \@@_log_incr:
%</log>
     \group_begin:
     \@@_block_load_elements:N #1
     \int_zero:N \l_@@_max_width_int
     \clist_map_function:PN
       \l_@@_block_elements_clist_ptr
       \@@_block_width_accumulator:N
     \int_incr:N \l_@@_max_width_int
     \exp_args:NNNV
       \group_end:
       \int_set:Nn #2 \l_@@_max_width_int
%<*log>
     \@@_log_decr:
     \@@_log:x
       { / Getting ~ natural ~ width ~ of ~ block ~ '\token_to_str:N #1'. }
%</log>
  }
\cs_new:Npn \grading_scheme_block_get_natural_width:PN
  {
    \exp_args:NP \grading_scheme_block_get_natural_width:NN
  }
%    \end{macrocode}
% \end{macro}
% 
% \begin{macro}{\@@_block_width_accumulator:N}
% 
%  Gets the natural width of subelements
%  and accumulates the maximum of them.
% 
%    \begin{macrocode}
\cs_new:Npn \@@_block_width_accumulator:N #1
  {
%<*log>
    \@@_log:x { Accumulating ~ width ~ of ~ '\token_to_str:N #1' }
    \@@_log_incr:
%</log>
    \grading_scheme_element_get_natural_width:NN #1 \l_@@_width_acc_int
    \int_set:Nn \l_@@_max_width_int
      {
        \int_max:nn
          \l_@@_width_acc_int
          \l_@@_max_width_int
      }
%<*log>
    \@@_log_decr:
    \@@_log:x { / Accumulationg ~ width ~ of ~ '\token_to_str:N #1' }
%</log>
  }
%    \end{macrocode}
% \end{macro}
% 
% 
% \subsection{Elements}
% 
% As discussed earlier, an \meta{element} is either a \meta{block}
% or an \meta{entry}.
% Thus, our natural representation is as follows:
% 
% \begin{verbatim}
%  struct Element {
%    void* content;
%    tl type;
%  }
% \end{verbatim}
% 
% which essentially just implements a |union| that knows of its current
% member.
% 
% 
% \begin{macro}{\@@_element_new:N, \@@_element_clear:N, \@@_element_set_eq:NN, \@@_element_gclear:N, \@@_element_gset_eq:NN}
% 
%  Do what these say. Forward to the underlying property lists.
% 
%    \begin{macrocode}
\cs_set_eq:NN \grading_scheme_element_new:N      \prop_new:N
\cs_set_eq:NN \grading_scheme_element_clear:N    \prop_clear:N
\cs_set_eq:NN \grading_scheme_element_set_eq:NN  \prop_set_eq:NN
\cs_set_eq:NN \grading_scheme_element_gclear:N   \prop_gclear:N
\cs_set_eq:NN \grading_scheme_element_gset_eq:NN \prop_gset_eq:NN
%    \end{macrocode}
% \end{macro}
% 
% \begin{variable}{\l_@@_element_content_void_ptr, \l_@@_element_type_str}
% 
%  Mentioned local variables.
% 
%    \begin{macrocode}
\ptr_new:N \l_@@_element_content_void_ptr
\str_new:N \l_@@_element_type_str
%    \end{macrocode}
% \end{variable}
% 
% 
% \begin{macro}{\grading_scheme_element_set_block:NN, \grading_scheme_element_gset_block:NN, \grading_scheme_element_set_entry:NN, \grading_scheme_element_gset_entry:NN}
%  \begin{syntax}
%    \cs{grading_scheme_element_set_block:NN} \meta{element var}\meta{block var}
%  \end{syntax}
% 
%    \begin{macrocode}
\cs_new:Npn \grading_scheme_element_set_block:NN #1 % implicit #2
  {
    \prop_put:Nnn #1 { type } { block }
    \prop_put:Nnn #1 { content }
  }
\cs_new:Npn \grading_scheme_element_gset_block:NN #1 % implicit #2
  {
    \prop_gput:Nnn #1 { type } { block }
    \prop_gput:Nnn #1 { content }
  }
\cs_new:Npn \grading_scheme_element_set_entry:NN #1 % implicit #2
  {
    \prop_put:Nnn #1 { type } { entry }
    \prop_put:Nnn #1 { content }
  }
\cs_new:Npn \grading_scheme_element_gset_entry:NN #1 % implicit #2
  {
    \prop_gput:Nnn #1 { type } { entry }
    \prop_gput:Nnn #1 { content }
  }
%    \end{macrocode}
% \end{macro}
% 
% \begin{macro}{\grading_scheme_element_get_content:NN, \grading_scheme_element_get_type:NN}
%  \begin{syntax}
%    \cs{grading_scheme_element_get_content:NN} \meta{element var}\meta{void ptr}
%      \cs{grading_scheme_element_get_type:NN} \meta{element var}\meta{str var}
%  \end{syntax}
% 
%  Get the contents. The assignment is local.
% 
%    \begin{macrocode}
\cs_new:Npn \grading_scheme_element_get_content:NN #1 % implicit #2
  {
    \prop_get:NnN #1 { content }
  }
\cs_new:Npn \grading_scheme_element_get_type:NN #1 % implicit #2
  {
    \prop_get:NnN #1 { type }
  }
%    \end{macrocode}
% \end{macro}
% 
% \begin{macro}{\@@_element_load_content:N, \@@_element_load_type:NN}
% 
%  Loads the element into the local variables.
% 
%    \begin{macrocode}
\cs_new:Npn \@@_element_load_content:N #1
  {
    \grading_scheme_element_get_content:NN #1 \l_@@_element_content_void_ptr
  }
\cs_new:Npn \@@_element_load_type:N #1
  {
    \grading_scheme_element_get_type:NN #1 \l_@@_element_type_str
  }
%    \end{macrocode}
% \end{macro}
% 
% \begin{macro}[noTF]{\grading_scheme_element_cases:Nnn}
%  \begin{syntax}
%    \cs{grading_scheme_element_cases}\meta{element var}\Arg{entry code}\Arg{block code}
%  \end{syntax}
% 
%  Distinguishes between the
%  cases of the element type.
% 
%  Also loads the element type.
% 
%    \begin{macrocode}
\cs_new:Npn \grading_scheme_element_cases:Nnn % implicit #1-3
  {
    \cs_set_eq:NN \@@_str_case:Vnw \str_case:Vn
    \@@_element_cases:Nnnw
  }
\cs_new:Npn \grading_scheme_element_cases:NnnT % implicit #1-4
  {
    \cs_set_eq:NN \@@_str_case:Vnw \str_case:VnT
    \@@_element_cases:Nnnw
  }
\cs_new:Npn \grading_scheme_element_cases:NnnF % implicit #1-4
  {
    \cs_set_eq:NN \@@_str_case:Vnw \str_case:VnF
    \@@_element_cases:Nnnw
  }
\cs_new:Npn \grading_scheme_element_cases:NnnTF % implicit #1-5
  {
    \cs_set_eq:NN \@@_str_case:Vnw \str_case:VnTF
    \@@_element_cases:Nnnw
  }
%    \end{macrocode}
% \end{macro}
% 
% \begin{macro}{\@@_element_cases:Nnnw}
% 
%  This assumes that \cs{@@_str_case:Vnw}
%  has been set to a \cs{str_case:VnTF}
%  variant and uses this variant for
%  switching the type cases.
% 
%    \begin{macrocode}
\cs_new:Npn \@@_element_cases:Nnnw #1 #2 #3 % implicit branches
  {
    \@@_element_load_type:N #1
    \@@_str_case:Vnw \l_@@_element_type_str
      {
        { entry } { #2 }
        { block } { #3 }
      }
    % implicit branches
  }
%    \end{macrocode}
% \end{macro}
% 
% \begin{macro}{\grading_scheme_element_format:NnnnN, \grading_scheme_element_gformat:NnnnN, \grading_scheme_element_format:PnnnN, \grading_scheme_element_gformat:PnnnN}
% 
%  Same syntax as \cs{grading_scheme_block_format:NnnnN}.
% 
%    \begin{macrocode}
\cs_new:Npn \grading_scheme_element_format:NnnnN % implicit #1-5
  {
    \cs_set_eq:NN \@@_entry_format_aux:PnnN  \grading_scheme_entry_format:PnnN
    \cs_set_eq:NN \@@_block_format_aux:PnnnN \grading_scheme_block_format:PnnnN
    \cs_set_eq:NN \@@_tl_put_right:Nx        \tl_put_right:Nx
    \@@_element_format:NnnnN
  }
\cs_new:Npn \grading_scheme_element_gformat:NnnnN % implicit #1-5
  {
    \cs_set_eq:NN \@@_entry_format_aux:PnnN  \grading_scheme_entry_gformat:PnnN
    \cs_set_eq:NN \@@_block_format_aux:PnnnN \grading_scheme_block_gformat:PnnnN
    \cs_set_eq:NN \@@_tl_put_right:Nx        \tl_put_gright:Nx
    \@@_element_format:NnnnN
  }
\cs_new:Npn \grading_scheme_element_format:PnnnN % implicit #1-5
  {
    \exp_args:NP \grading_scheme_element_format:NnnnN
  }
\cs_new:Npn \grading_scheme_element_gformat:PnnnN % implicit #1-5
  {
    \exp_args:NP \grading_scheme_element_gformat:NnnnN
  }
%    \end{macrocode}
% \end{macro}
% 
% 
% \begin{macro}{\@@_element_format:NnnnN, \@@_element_format:PnnnN}
% 
%    Aux function.
% 
%    \begin{macrocode}
\cs_new:Npn \@@_element_format:NnnnN #1 #2 #3 #4 #5
  {
%<*log>
    \@@_log:x
      {
        Formatting ~ element ~ '\token_to_str:N #1' ~ into ~ '\token_to_str:N #5'.
      }
    \@@_log_incr:
    \prop_log:N #1
%</log>
    \@@_element_load_content:N #1
    \grading_scheme_element_cases:NnnF #1
        {
          \bool_if:nTF { #4 }
            {
              \@@_entry_format_aux:PnnN
                \l_@@_element_content_void_ptr
                { #2 }
                { #3 }
                #5
            }
            {
              \@@_entry_format_aux:PnnN
                \l_@@_element_content_void_ptr
                { 0 }
                { #3 }
                #5
            }
        }
        {
          \@@_block_format_aux:PnnnN
            \l_@@_element_content_void_ptr
            { #2 }
            { #3 }
            { #4 }
            #5
        }
        {
          \msg_error:nnxxx { grading-scheme } { missing-value }
            { element }
            { \token_to_str:N #1 }
            { type / content }
        }
    \@@_tl_put_right:Nx #5
      {
        \exp_not:N \@@_cline:nn
          {
            \int_eval:n { #2 +1 }
          }
          {
            \int_eval:n { #2 + #3 }
          }
      }
%<*log>
    \@@_log_decr:
    \@@_log:x { Done typesetting ~ element ~ '\token_to_str:N #1' }
%</log>
  }
\cs_new:Npn \@@_element_format:PnnnN % implicit #1-5
  {
    \exp_args:NP \@@_element_format:NnnnN
  }
%    \end{macrocode}
% \end{macro}
% 
% 
% \begin{macro}{\grading_scheme_element_get_height:NN, \grading_scheme_element_get_height:PN}
% 
%  Get the the height of an element.
% 
%    \begin{macrocode}
\cs_new:Npn \grading_scheme_element_get_height:NN #1 #2
  {
    \grading_scheme_element_cases:NnnF #1
        {
          \int_set:Nn #2 { 1 }
        }
        {
          \@@_element_load_content:N #1
          \grading_scheme_block_get_height:PN
            \l_@@_element_content_void_ptr
            #2
        }
        {
          \msg_error:nnxxx { grading-scheme } { missing-value }
            { element }
            { \token_to_str:N #1 }
            { type / content }
        }
  }
\cs_new:Npn \grading_scheme_element_get_height:PN
  {
    \exp_args:NP \grading_scheme_element_get_height:NN
  }
%    \end{macrocode}
% \end{macro}
% 
% \begin{macro}{\grading_scheme_element_get_natural_width:NN, \grading_scheme_element_get_natural_width:PN}
% 
% Get the natural width of an element
% 
%    \begin{macrocode}
\cs_new:Npn \grading_scheme_element_get_natural_width:NN #1 #2
  {
    \grading_scheme_element_cases:NnnF #1
      {
        \int_set:Nn { #2 } { 2 }
      }
      {
        \@@_element_load_content:N #1
        \grading_scheme_block_get_natural_width:PN
          \l_@@_element_content_void_ptr
          #2
      }
      {
        \msg_error:nnxxx { grading-scheme } { missing-value }
          { element }
          { \token_to_str:N #1 }
          { type / content }
      }
  }
\cs_new:Npn \grading_scheme_element_get_natural_width:PN
  {
    \exp_args:NP \grading_scheme_element_get_natural_width:NN
  }
%    \end{macrocode}
% \end{macro}
% 
% 
% \begin{variable}{\l_@@_width_int}
% 
%  Local int vars for typesetting.
% 
%    \begin{macrocode}
\int_new:N \l_@@_width_int
%    \end{macrocode}
% \end{variable}
% 
% \begin{variable}{\g_@@_table_tl}
% 
%  Token list where we will build the table.
% 
%    \begin{macrocode}
\tl_new:N \g_@@_table_tl
%    \end{macrocode}
% \end{variable}
% 
% 
% 
% 
% \begin{macro}{\grading_scheme_element_typeset:N, \grading_scheme_element_typeset:P}
% 
%  Typesets this element as a tabular and inserts this into the output stream.
% 
%    \begin{macrocode}
\cs_new:Npn \grading_scheme_element_typeset:N #1
  {
    \grading_scheme_element_get_natural_width:NN #1 \l_@@_width_int
    \tl_gclear:N \g_@@_table_tl
    \tl_gput_right:Nn \g_@@_table_tl
      {
        \begin{tabular}
      }
    \tl_gput_right:Nx \g_@@_table_tl
      {
        {
          \prg_replicate:nn { \l_@@_width_int -1 } { |l }
          | l |
        }
        \exp_not:N \@@_hline:
      }
    \grading_scheme_element_gformat:NnnnN
      #1
      { 0 }
      { \l_@@_width_int }
      { \c_false_bool }
      \g_@@_table_tl
    \tl_gput_right:Nn \g_@@_table_tl
      {
        \end{tabular}
      }
    \group_begin:
    \tl_set:Nn \arraystretch { 1.5 }
    \tl_use:N \g_@@_table_tl
    \group_end:
  }
\cs_new:Npn \grading_scheme_element_typeset:P
  {
    \exp_args:NP \grading_scheme_element_typeset:N
  }
%    \end{macrocode}
% \end{macro}
% 
% 
% \subsection{Facilities for populating data structures}
% 
% TODO: what about global / local assignments here?
% 
% 
% \begin{macro}{\grading_scheme_entry_new:Nnn, \grading_scheme_entry_new:Pnn}
%  \begin{syntax}
%     \cs{grading_scheme_entry_new:Nnn}\meta{entry var}\Arg{description}\Arg{points}
%  \end{syntax}
% 
%    \begin{macrocode}
\cs_new:Npn \grading_scheme_entry_new:Nnn #1 #2 % implcit #3
  {
    \grading_scheme_entry_new:N #1
    \grading_scheme_entry_gset_description:Nn #1 { #2 }
    \grading_scheme_entry_gset_points:Nn #1
  }
\cs_new:Npn \grading_scheme_entry_new:Pnn
  {
    \exp_args:NP \grading_scheme_entry_new:Nnn
  }
%    \end{macrocode}
% \end{macro}
% 
% \begin{macro}{\grading_scheme_block_new:Nnn, \grading_scheme_block_new:Pnn}
%  \begin{syntax}
%    \cs{grading_scheme_block_new:Nnn}\meta{block var}\Arg{description}\Arg{operation}
%  \end{syntax}
% 
%    \begin{macrocode}
\cs_new:Npn \grading_scheme_block_new:Nnn #1 #2 % implicit #3
  {
    \grading_scheme_block_new:N #1
    \grading_scheme_block_gset_description:Nn #1 { #2 }
    \grading_scheme_block_gset_operation:Nn #1
  }
\cs_new:Npn \grading_scheme_block_new:Pnn
  {
    \exp_args:NP \grading_scheme_block_new:Nnn
  }
%    \end{macrocode}
% \end{macro}
% 
% \begin{macro}{\grading_scheme_block_new:Nn, \grading_scheme_block_new:Pn}
%  \begin{syntax}
%    \cs{grading_scheme_block_new:Nn}\meta{block var}\Arg{operation}
%  \end{syntax}
% 
%  Same as above, but no description provided.
% 
%    \begin{macrocode}
\cs_new:Npn \grading_scheme_block_new:Nn #1 % implicit #2
  {
    \grading_scheme_block_new:N #1
    \grading_scheme_block_gset_operation:Nn #1
  }
\cs_new:Npn \grading_scheme_block_new:Pn
  {
    \exp_args:NP \grading_scheme_block_new:Nn
  }
%    \end{macrocode}
% \end{macro}
% 
% \begin{macro}{\grading_scheme_element_from_entry_new:NN, \grading_scheme_element_from_entry_new:NP, \grading_scheme_element_from_entry_new:PP,
% \grading_scheme_element_gfrom_entry_new:NN, \grading_scheme_element_gfrom_entry_new:NP, \grading_scheme_element_gfrom_entry_new:PP}
%  \begin{syntax}
%    \cs{grading_scheme_element_from_entry_new:NN}\meta{element var}\meta{entry var}
%  \end{syntax}
% 
%  wraps the entry into a new element.
% 
%    \begin{macrocode}
\cs_new:Npn \grading_scheme_element_from_entry_new:NN #1 #2
  {
    \grading_scheme_element_new:N #1
    \grading_scheme_element_set_entry:NN #1 #2
  }
\cs_new:Npn \grading_scheme_element_from_entry_new:NP
  {
    \exp_args:NP \grading_scheme_element_from_entry_new:NN
  }
\cs_new:Npn \grading_scheme_element_from_entry_new:PP
  {
    \exp_args:NPP \grading_scheme_element_from_entry_new:NN
  }
\cs_new:Npn \grading_scheme_element_gfrom_entry_new:NN #1 #2
  {
    \grading_scheme_element_new:N #1
    \grading_scheme_element_gset_entry:NN #1 #2
  }
\cs_new:Npn \grading_scheme_element_gfrom_entry_new:NP
  {
    \exp_args:NP \grading_scheme_element_gfrom_entry_new:NN
  }
\cs_new:Npn \grading_scheme_element_gfrom_entry_new:PP
  {
    \exp_args:NPP \grading_scheme_element_gfrom_entry_new:NN
  }
%    \end{macrocode}
% \end{macro}
% 
% \begin{macro}{\grading_scheme_element_from_block_new:NN, \grading_scheme_element_from_block_new:NP, \grading_scheme_element_from_block_new:NP,
% \grading_scheme_element_gfrom_block_new:NN, \grading_scheme_element_from_gblock_new:NP, \grading_scheme_element_gfrom_block_new:NP}
%  \begin{syntax}
%    \cs{grading_scheme_element_from_block_new:NN}\meta{element var}\meta{block var}
%  \end{syntax}
% 
%  wraps the block into a new element.
% 
%    \begin{macrocode}
\cs_new:Npn \grading_scheme_element_from_block_new:NN #1 #2
  {
    \grading_scheme_element_new:N #1
    \grading_scheme_element_set_block:NN #1 #2
  }
\cs_new:Npn \grading_scheme_element_from_block_new:NP
  {
    \exp_args:NP \grading_scheme_element_from_block_new:NN
  }
\cs_new:Npn \grading_scheme_element_from_block_new:PP
  {
    \exp_args:NPP \grading_scheme_element_from_block_new:NN
  }
\cs_new:Npn \grading_scheme_element_gfrom_block_new:NN #1 #2
  {
    \grading_scheme_element_new:N #1
    \grading_scheme_element_gset_block:NN #1 #2
  }
\cs_new:Npn \grading_scheme_element_gfrom_block_new:NP
  {
    \exp_args:NP \grading_scheme_element_gfrom_block_new:NN
  }
\cs_new:Npn \grading_scheme_element_gfrom_block_new:PP
  {
    \exp_args:NPP \grading_scheme_element_gfrom_block_new:NN
  }
%    \end{macrocode}
% \end{macro}
% 
% \subsection{Grading scheme environment}
% 
% When building a grading scheme,
% we introduce a \cs{g_@@_curr_block_ptr}
% that is a pointer to the current block
% that is being populated.
% 
% Block environments and entries will create
% new blocks/entries and add themselves to
% the block pointed out by \cs{g_@@_curr_block_ptr}
% 
% 
% \begin{variable}{\l_@@_entry_ptr, \l_@@_element_ptr, \l_@@_curr_block_ptr}
% 
%    \begin{macrocode}
\ptr_new:Nn \l_@@_entry_ptr { g }
\ptr_new:Nn \l_@@_element_ptr { g }
\ptr_new:Nn \l_@@_curr_block_ptr { g }
%    \end{macrocode}
% \end{variable}
% 
% 
% 
% \begin{macro}{\@@_entry:nn}
% 
%  Creates an entry in the current block.
% 
%    \begin{macrocode}
\cs_new:Npn \@@_entry:nn #1 #2
  {
    \ptr_clear:Nn \l_@@_entry_ptr { g }
    \ptr_clear:Nn \l_@@_element_ptr { g }
    \grading_scheme_entry_new:Pnn
      \l_@@_entry_ptr
      { #1 }
      { #2 }
    \grading_scheme_element_gfrom_entry_new:PP
      \l_@@_element_ptr
      \l_@@_entry_ptr
    \grading_scheme_block_gadd_element:PP
      \l_@@_curr_block_ptr
      \l_@@_element_ptr
  }
%    \end{macrocode}
% \end{macro}
% 
% \begin{macro}{\@@_block_begin:nn, \@@_block_begin:n, \@@_block_end:}
% 
% 
% 
%    \begin{macrocode}
\cs_new:Npn \@@_block_begin:nn % implicit #1, #2
  {
    \ptr_clear:Nn \l_@@_curr_block_ptr { g }
    \grading_scheme_block_new:Pnn \l_@@_curr_block_ptr
  }
\cs_new:Npn \@@_block_begin:n % implicit #1
  {
      \ptr_clear:Nn \l_@@_curr_block_ptr { g }
      \grading_scheme_block_new:Pn \l_@@_curr_block_ptr
  }
\cs_new:Npn \@@_block_end:
  {
    \ptr_clear:Nn \l_@@_element_ptr { g }
    \grading_scheme_element_gfrom_block_new:PP
      \l_@@_element_ptr
      \l_@@_curr_block_ptr
    \cs_gset:Npx \@@_after_group:
      {
        \exp_not:N \grading_scheme_block_gadd_element:PN
          \exp_not:N \l_@@_curr_block_ptr
          \exp_not:P \l_@@_element_ptr
      }
    \group_insert_after:N \@@_after_group:
  }
%    \end{macrocode}
% \end{macro}
% 
% 
% \begin{macro}{\entry}
% 
% 
% 
%    \begin{macrocode}
\NewDocumentCommand \entry { m m }
  {
    \@@_entry:nn { #1 } { #2 }
  }
%    \end{macrocode}
% \end{macro}
% 
% 
% \begin{macro}{block}
% 
%    \begin{macrocode}
\NewDocumentEnvironment { block } { o m }
  {
    \IfValueTF { #1 }
      {
        \@@_block_begin:nn { #1 } { #2 }
      }
      {
        \@@_block_begin:n { #2 }
      }
  }
  {
    \@@_block_end:
  }
%    \end{macrocode}
% \end{macro}
% 
% \begin{macro}{gradingscheme}
% 
% 
% 
%    \begin{macrocode}
\NewDocumentEnvironment {gradingscheme} { o m }
  {
    \bool_if:NT \g_@@_pipe_syntax_bool
      {
        \char_set_active_eq:NN | \@@_entry_oneline:w
        \char_set_catcode_active:N |
      }
    \IfValueTF { #1 }
      {
        \@@_block_begin:nn { #1 } { #2 }
      }
      {
        \@@_block_begin:n { #2 }
      }
  }
  {
    \ptr_clear:Nn \l_@@_element_ptr { g }
    \grading_scheme_element_gfrom_block_new:PP
      \l_@@_element_ptr
      \l_@@_curr_block_ptr
    \grading_scheme_element_typeset:P \l_@@_element_ptr
  }
%    \end{macrocode}
% \end{macro}
% 
% \subsection{Implementing the pipe syntax}
% 
% Everything left is conditional on the pipe option having been specified:
%    \begin{macrocode}
\bool_if:NF \g_@@_pipe_syntax_bool
  {
    \endinput
  }
%    \end{macrocode}
% 
% 
% In order to offer syntax based on $\mid$, we make $\mid$ an active character
% that will read until the end of the line, split at the |@| character,
% and pass this on to a \cs{@@_entry:nn}
% 
% \begin{macro}{\s_@@_endline}
% 
%  A scan mark that will denote the end of the line
%  for us.
% 
%    \begin{macrocode}
\scan_new:N \s_@@_endline
%    \end{macrocode}
% \end{macro}
% 
% \begin{macro}{\@@_parse_entry:w}
%  \begin{syntax}
%    \cs{@@_parse_entry:w} \meta{description} \& \meta{points} \cs{s_endline}
%  \end{syntax}
% 
%  Creates a new entry based on our scan mark.
% 
%    \begin{macrocode}
\cs_new:Npn \@@_parse_entry:w #1 & #2 \s_endline
  {
    \@@_entry:nn { #1 } { #2  }
  }
%    \end{macrocode}
% \end{macro}
% 
% \begin{macro}{\@@_replace_newline:w, \@@_replace_newline_aux:w}
% 
%  Calling \cs{@@_replace_newline:w}
%  replaces the next occurence of a newline character with
%  \cs{s_@@_endline}.
% 
%  This is done by briefly switching category codes.
% 
%    \begin{macrocode}
\cs_new:Npn \@@_replace_newline:w
  {
    \group_begin:
    \char_set_catcode_active:N\^^M
    \@@_replace_newline_aux:w
  }
%    \end{macrocode}
% 
% We need to briefly make the newline character an active character
% in this source file so that pattern matching works correctly.
%    \begin{macrocode}
\char_set_catcode_active:N\^^M
\cs_new:Npn \@@_replace_newline_aux:w #1 ^^M
  {
    \group_end:
    #1 \s_endline
  }
\char_set_catcode_end_line:N\^^M
%    \end{macrocode}
% \end{macro}
% 
% 
% \begin{macro}{\@@_entry_oneline:w}
% 
%  Parses a one-line entry.
% 
%    \begin{macrocode}
\cs_new:Npn \@@_entry_oneline:w
  {
    \@@_replace_newline:w
    \@@_parse_entry:w
  }
%    \end{macrocode}
% \end{macro}
% 
% 
%    \begin{macrocode}
%</package>
%    \end{macrocode}
% 
% \end{implementation}
% 
% \PrintIndex
% 
