texlive[62155] Master: grading-scheme (23feb22)

commits+karl at tug.org commits+karl at tug.org
Wed Feb 23 21:55:03 CET 2022


Revision: 62155
          http://tug.org/svn/texlive?view=revision&revision=62155
Author:   karl
Date:     2022-02-23 21:55:03 +0100 (Wed, 23 Feb 2022)
Log Message:
-----------
grading-scheme (23feb22)

Modified Paths:
--------------
    trunk/Master/tlpkg/bin/tlpkg-ctan-check
    trunk/Master/tlpkg/libexec/ctan2tds
    trunk/Master/tlpkg/tlpsrc/collection-latexextra.tlpsrc

Added Paths:
-----------
    trunk/Master/texmf-dist/doc/latex/grading-scheme/
    trunk/Master/texmf-dist/doc/latex/grading-scheme/README.md
    trunk/Master/texmf-dist/doc/latex/grading-scheme/grading-scheme.pdf
    trunk/Master/texmf-dist/source/latex/grading-scheme/
    trunk/Master/texmf-dist/source/latex/grading-scheme/grading-scheme.dtx
    trunk/Master/texmf-dist/source/latex/grading-scheme/grading-scheme.ins
    trunk/Master/texmf-dist/tex/latex/grading-scheme/
    trunk/Master/texmf-dist/tex/latex/grading-scheme/grading-scheme.sty
    trunk/Master/tlpkg/tlpsrc/grading-scheme.tlpsrc

Added: trunk/Master/texmf-dist/doc/latex/grading-scheme/README.md
===================================================================
--- trunk/Master/texmf-dist/doc/latex/grading-scheme/README.md	                        (rev 0)
+++ trunk/Master/texmf-dist/doc/latex/grading-scheme/README.md	2022-02-23 20:55:03 UTC (rev 62155)
@@ -0,0 +1,27 @@
+# The grading-scheme package
+Copyiright (C) 2020 Maximilian Keßler <ctan at maximilian-kessler.de>
+
+This matrial is subject to the LaTeX Project Public License 1.3c.
+The latest version of this license is at: http://www.latex-project.org/lppl.txt
+
+
+This bundle contains the files:
+
+README.md            this file
+grading-scheme.dtx   documented LaTeX source file
+grading-scheme.pdf   documentation produced from the dtx file
+grading-scheme.ins   run to generate the package
+
+This work is author-maintained.
+
+This is version 0.1 of the package, dated 2022-02-22
+
+# Description
+
+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.
+
+It can be run with LaTeX directly.


Property changes on: trunk/Master/texmf-dist/doc/latex/grading-scheme/README.md
___________________________________________________________________
Added: svn:eol-style
## -0,0 +1 ##
+native
\ No newline at end of property
Added: trunk/Master/texmf-dist/doc/latex/grading-scheme/grading-scheme.pdf
===================================================================
(Binary files differ)

Index: trunk/Master/texmf-dist/doc/latex/grading-scheme/grading-scheme.pdf
===================================================================
--- trunk/Master/texmf-dist/doc/latex/grading-scheme/grading-scheme.pdf	2022-02-23 20:53:27 UTC (rev 62154)
+++ trunk/Master/texmf-dist/doc/latex/grading-scheme/grading-scheme.pdf	2022-02-23 20:55:03 UTC (rev 62155)

Property changes on: trunk/Master/texmf-dist/doc/latex/grading-scheme/grading-scheme.pdf
___________________________________________________________________
Added: svn:mime-type
## -0,0 +1 ##
+application/pdf
\ No newline at end of property
Added: trunk/Master/texmf-dist/source/latex/grading-scheme/grading-scheme.dtx
===================================================================
--- trunk/Master/texmf-dist/source/latex/grading-scheme/grading-scheme.dtx	                        (rev 0)
+++ trunk/Master/texmf-dist/source/latex/grading-scheme/grading-scheme.dtx	2022-02-23 20:55:03 UTC (rev 62155)
@@ -0,0 +1,2768 @@
+% \iffalse meta-comment
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% This LaTeX docstrip file is free software and is dual-licensed               %
+% under the LPPLv1.3c and the GPLv3 licenses.                                  %
+% You may use it freely for your purposes.                                     %
+% The latest version of the docstrip file sources can be obtained              %
+% via GitLab under                                                             %
+%    https://gitlab.com/latexci/packages/LatexPackages                         %
+% The latest version of the built packages can be obtained via GitLab under    %
+%    https://gitlab.com/latexci/packages/LatexPackagesBuild                    %
+% For further information see the urls above.                                  %
+% Reportings of bugs, suggestions and improvements are welcome, see the README %
+% at the Git repository for further information.                               %
+%                                                                              %
+%                                                                              %
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%<*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 at 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/22} {bla}
+%<package>  {test}
+%
+%<*driver>
+\documentclass[full]{l3doc}
+\usepackage{grading-scheme}
+\begin{document}
+\DocInput{grading-scheme.dtx}
+\end{document}
+%</driver>
+% \fi
+%
+% 
+% \title{The \pkg{grading-scheme} package}
+% 
+% \author{Maximilian Keßler\thanks{ctan at maximilian-kessler.de}}
+% \date{Released 2022/02/22\\ \vskip 1.5em {\small version 0.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.
+% 
+% \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
+% 


Property changes on: trunk/Master/texmf-dist/source/latex/grading-scheme/grading-scheme.dtx
___________________________________________________________________
Added: svn:eol-style
## -0,0 +1 ##
+native
\ No newline at end of property
Added: trunk/Master/texmf-dist/source/latex/grading-scheme/grading-scheme.ins
===================================================================
--- trunk/Master/texmf-dist/source/latex/grading-scheme/grading-scheme.ins	                        (rev 0)
+++ trunk/Master/texmf-dist/source/latex/grading-scheme/grading-scheme.ins	2022-02-23 20:55:03 UTC (rev 62155)
@@ -0,0 +1,32 @@
+\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 at 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

Added: trunk/Master/texmf-dist/tex/latex/grading-scheme/grading-scheme.sty
===================================================================
--- trunk/Master/texmf-dist/tex/latex/grading-scheme/grading-scheme.sty	                        (rev 0)
+++ trunk/Master/texmf-dist/tex/latex/grading-scheme/grading-scheme.sty	2022-02-23 20:55:03 UTC (rev 62155)
@@ -0,0 +1,1143 @@
+%%
+%% This is file `grading-scheme.sty',
+%% generated with the docstrip utility.
+%%
+%% The original source files were:
+%%
+%% grading-scheme.dtx  (with options: `package')
+%%  ___________________________________________________________
+%%   The grading-scheme package
+%%   Copyright (C) 2022 Maximilian Kessler
+%%   License information appended.
+%% 
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+\ProvidesExplPackage {grading-scheme} {2022/02/22} {bla}
+  {test}
+\bool_new:N \g__grading_scheme_pipe_syntax_bool
+\keys_define:nn { grading-scheme }
+  {
+    pipe    .bool_gset:N =  \g__grading_scheme_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}
+\cs_set_eq:NN \__grading_scheme_multicolumn:nnn \multicolumn
+\cs_set_eq:NN \__grading_scheme_multirow:nnn \multirow
+\cs_set_eq:NN \__grading_scheme_cline:n \cline
+\cs_new:Npn \__grading_scheme_cline:nn #1 #2
+  {
+    \__grading_scheme_cline:n { #1 - #2 }
+  }
+\cs_set_eq:NN \__grading_scheme_hline: \hline
+\cs_new:Npn \__grading_scheme_rotatebox:nnn #1 %implicit #2, #3
+  {
+    \rotatebox [ #1 ]
+  }
+\cs_new:Npn \__grading_scheme_points:n #1
+  {
+    \textbf{ #1 ~ P. }
+  }
+\cs_new:Npn \__grading_scheme_operation:n #1
+  {
+    { \sc #1 }
+  }
+\int_new:N \g__ptr_counter_int
+\str_new:N \l__ptr_var_prefix_str
+\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 }
+  }
+\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 }
+\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 }
+\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 }
+\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
+                  }
+              }
+          }
+      }
+  }
+  \msg_new:nnnn { ptr } { variable-name-illegal-prefix }
+    {
+      Requested ~ new ~ pointer ~ '#1' ~ has ~ illegal ~ prefix.
+    }
+    {
+      Prefix ~ should ~ be ~ one ~ of ~ 'l', ~ 'g' ~ or ~ 'c'.
+    }
+\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 }
+  }
+\cs_set_eq:NN \ptr_set:NN \tl_set:Nn
+\cs_set_eq:NN \ptr_set_eq:NN \tl_set_eq:NN
+\cs_set_eq:NN \ptr_use:N \tl_use:N
+\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
+          }
+      }
+  }
+\msg_new:nnn { ptr } { is-not-a-pointer }
+  {
+    The ~ control ~ sequence ~ '#1' ~ is ~ not ~ a ~ valid ~ pointer.
+  }
+\cs_new:Npn \__ptr_check:NN #1 #2
+  {
+    \__ptr_check:N #2
+    #1 #2
+  }
+\cs_new:Npn \__ptr_check:nN #1 #2
+  {
+    \__ptr_check:N #2
+    { #1 } #2
+  }
+\tl_new:N \l__ptr_content_tl
+\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
+  }
+\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 }
+  }
+\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
+  }
+\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
+          }
+      }
+  }
+\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
+\int_new:N \l__grading_scheme_entry_points_int
+\tl_new:N  \l__grading_scheme_entry_description_tl
+\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 }
+  }
+\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 } }
+  }
+\cs_new:Npn \grading_scheme_entry_get_description:NN #1 %implicit #2
+  {
+    \prop_get:NnN #1 { description }
+  }
+\cs_new:Npn \grading_scheme_entry_get_points:NN #1 %implicit #2
+  {
+    \prop_get:NnN #1 { points }
+  }
+\cs_new:Npn \__grading_scheme_entry_load_points:N #1
+  {
+    \grading_scheme_entry_get_points:NN #1 \l__grading_scheme_entry_points_int
+  }
+\cs_new:Npn \__grading_scheme_entry_load_description:N #1
+  {
+    \grading_scheme_entry_get_description:NN #1 \l__grading_scheme_entry_description_tl
+  }
+\cs_new:Npn \grading_scheme_entry_format:NnnN
+  {
+    \cs_set_eq:NN \__grading_scheme_tl_put_right:Nx \tl_put_right:Nx
+    \__grading_scheme_entry_format:NnnN
+  }
+\cs_new:Npn \grading_scheme_entry_gformat:NnnN
+  {
+    \cs_set_eq:NN \__grading_scheme_tl_put_right:Nx \tl_gput_right:Nx
+    \__grading_scheme_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
+  }
+\cs_new:Npn \__grading_scheme_entry_format:NnnN #1 #2 #3 #4
+  {
+    \__grading_scheme_entry_assert_description:N #1 % implicitly loads value
+    \__grading_scheme_entry_assert_points:N #1      % implicitly loads value
+    \__grading_scheme_tl_put_right:Nx #4
+      {
+        \prg_replicate:nn { #2 } { & }
+        \exp_not:N \__grading_scheme_multicolumn:nnn
+          {
+            \int_eval:n { #3 -1 }
+          }
+          { l }
+          {
+            \exp_not:V \l__grading_scheme_entry_description_tl
+          }
+        &
+        \exp_not:N \__grading_scheme_points:n
+          {
+            \tl_use:N \l__grading_scheme_entry_points_int
+          }
+        \\
+      }
+  }
+\cs_new:Npn \__grading_scheme_entry_forrmat:PnnN % implicit #1-4
+  {
+    \exp_args:NP \__grading_scheme_entry_format:NnnN
+  }
+\cs_new:Npn \__grading_scheme_entry_assert_description:N #1
+  {
+    \__grading_scheme_entry_load_description:N #1
+    \quark_if_no_value:NT \l__grading_scheme_entry_description_tl
+      {
+        \msg_error:nnxxx { grading-scheme } { missing-value }
+          { entry } { \token_to_str:N #1 } { description }
+      }
+  }
+\cs_new:Npn \__grading_scheme_entry_assert_points:N #1
+  {
+    \__grading_scheme_entry_load_points:N #1
+    \quark_if_no_value:NT \l__grading_scheme_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.
+  }
+\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
+\ptr_new:N \l__grading_scheme_block_elements_clist_ptr
+\tl_new:N  \l__grading_scheme_block_description_tl
+\tl_new:N  \l__grading_scheme_block_operation_tl
+\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
+  }
+\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 }
+  }
+\cs_new:Npn \__grading_scheme_block_load_description:N #1
+  {
+    \grading_scheme_block_get_description:NN #1 \l__grading_scheme_block_description_tl
+  }
+\cs_new:Npn \__grading_scheme_block_load_operation:N #1
+  {
+    \grading_scheme_block_get_operation:NN #1 \l__grading_scheme_block_operation_tl
+  }
+\cs_new:Npn \__grading_scheme_block_load_elements:N #1
+  {
+    \grading_scheme_block_get_elements:NN #1 \l__grading_scheme_block_elements_clist_ptr
+  }
+\cs_new:Npn \__grading_scheme_block_ensure_elements:N % implicit #1
+  {
+    \cs_set_eq:NN
+      \__grading_scheme_block_set_elements_optg:NP
+      \grading_scheme_block_set_elements:NP
+    \__grading_scheme_block_ensure_elements_aux:nN { l }
+  }
+\cs_new:Npn \__grading_scheme_block_gensure_elements:N % implicit #1
+  {
+    \cs_set_eq:NN
+      \__grading_scheme_block_set_elements_optg:NP
+      \grading_scheme_block_gset_elements:NP
+    \__grading_scheme_block_ensure_elements_aux:nN { g } %i #1
+  }
+\cs_new:Npn \__grading_scheme_block_ensure_elements_aux:nN #1 #2
+  {
+    \__grading_scheme_block_load_elements:N #2
+    \quark_if_no_value:NT \l__grading_scheme_block_elements_clist_ptr
+      {
+        \ptr_clear:Nn \l__grading_scheme_block_elements_clist_ptr { #1 }
+        \__grading_scheme_block_set_elements_optg:NP
+          #2
+          \l__grading_scheme_block_elements_clist_ptr
+      }
+    \ptr_if_null:NT \l__grading_scheme_block_elements_clist_ptr
+      {
+        \clist_new:P \l__grading_scheme_block_elements_clist_ptr
+      }
+  }
+\cs_new:Npn \grading_scheme_block_add_element:NN #1 % implicit element
+  {
+    \__grading_scheme_block_ensure_elements:N #1 % also loads local variable
+    \clist_put_right:Pn \l__grading_scheme_block_elements_clist_ptr
+  }
+\cs_new:Npn \grading_scheme_block_gadd_element:NN #1 % implicit element
+  {
+    \__grading_scheme_block_gensure_elements:N #1 % also loads local variable
+    \clist_gput_right:Pn \l__grading_scheme_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
+  }
+\cs_new:Npn \grading_scheme_block_format:NnnnN %implicit #1-5
+  {
+    \cs_set_eq:NN \__grading_scheme_tl_put_right:Nx \tl_put_right:Nx
+    \cs_set_eq:NN \__grading_scheme_element_format_optg:NnnnN \grading_scheme_element_format:NnnnN
+    \cs_set_eq:NN \__grading_scheme_tl_set:Nn \tl_set:Nn
+    \__grading_scheme_block_format:NnnnN
+  }
+\cs_new:Npn \grading_scheme_block_gformat:NnnnN %implicit #1-5
+  {
+    \cs_set_eq:NN \__grading_scheme_tl_put_right:Nx \tl_gput_right:Nx
+    \cs_set_eq:NN \__grading_scheme_element_format_optg:NnnnN \grading_scheme_element_gformat:NnnnN
+    \cs_set_eq:NN \__grading_scheme_tl_set:Nn \tl_gset:Nn
+    \__grading_scheme_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
+  }
+\bool_new:N \l__grading_scheme_block_indent_bool
+\int_new:N \l__grading_scheme_block_height_int
+\cs_new:Npn \__grading_scheme_block_format:NnnnN #1 #2 #3 #4 #5
+  {
+    \bool_set:Nn \l__grading_scheme_block_indent_bool { #4 && \c_true_bool }
+    \group_begin:
+    \__grading_scheme_block_load_description:N #1
+    \grading_scheme_block_get_height:NN #1 \l__grading_scheme_block_height_int
+    \quark_if_no_value:NF \l__grading_scheme_block_description_tl
+      {
+        \__grading_scheme_block_indent:nN { #2 } #5
+        \__grading_scheme_tl_put_right:Nx #5
+          {
+            \exp_not:N \__grading_scheme_multicolumn:nnn
+              {
+                \int_eval:n { #3 }
+              }
+              { |l| }
+              {
+                \exp_not:V \l__grading_scheme_block_description_tl
+              }
+            \\
+            \exp_not:N \__grading_scheme_cline:nn
+              {
+                \int_eval:n { #2 + 2 }
+              }
+              {
+                \int_eval:n { #2 + #3 }
+              }
+          }
+      }
+    \__grading_scheme_block_indent:nN { #2 } #5
+    \__grading_scheme_block_assert_operation:N #1
+    \__grading_scheme_tl_put_right:Nx #5
+      {
+        \exp_not:N \__grading_scheme_multirow:nnn
+          {
+            \int_eval:n { \int_use:N \l__grading_scheme_block_height_int - 1 }
+          }
+          { * }
+          {
+            \exp_not:N \__grading_scheme_rotatebox:nnn
+              { origin = c }
+              { 90 }
+              {
+                \exp_not:N \__grading_scheme_operation:n
+                   {
+                    \exp_not:V \l__grading_scheme_block_operation_tl
+                  }
+              }
+          }
+        &
+      }
+    \bool_set_false:N \l__grading_scheme_block_indent_bool
+    \cs_set:Npn \__grading_scheme_block_format_element:N ##1
+    {
+      \__grading_scheme_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__grading_scheme_block_indent_bool }
+        #5
+      \bool_set_true:N \l__grading_scheme_block_indent_bool
+    }
+    \__grading_scheme_block_ensure_elements:N #1 % load + sanitize elements
+    \clist_map_function:PN
+      \l__grading_scheme_block_elements_clist_ptr
+      \__grading_scheme_block_format_element:N
+    \exp_args:NNNV
+      \group_end:
+      \__grading_scheme_tl_set:Nn
+      #5
+      #5
+  }
+\cs_new:Npn \__grading_scheme_block_format:PnnnN
+  {
+    \exp_args:NP \__grading_scheme_block_format:NnnnN
+  }
+\cs_new:Npn \__grading_scheme_block_assert_operation:N #1
+  {
+    \__grading_scheme_block_load_operation:N #1
+    \quark_if_no_value:NT \l__grading_scheme_block_operation_tl
+      {
+         \msg_error:nnxxx { grading-scheme } { missing-value}
+           { block }
+           { \token_to_str:N #1 }
+           { operation }
+      }
+  }
+\cs_new:Npn \__grading_scheme_block_indent:nN #1 #2
+  {
+    \bool_if:NT \l__grading_scheme_block_indent_bool
+      {
+        \__grading_scheme_tl_put_right:Nx #2
+          {
+            \prg_replicate:nn { #1 } { & }
+          }
+      }
+    \bool_set_true:N \l__grading_scheme_block_indent_bool
+  }
+\int_new:N \l__grading_scheme_height_sum_int
+\int_new:N \l__grading_scheme_height_acc_int
+\cs_new:Npn \grading_scheme_block_get_height:NN #1 #2
+{
+  \group_begin:
+  \__grading_scheme_block_load_elements:N #1
+  \int_zero:N \l__grading_scheme_height_sum_int
+  \clist_map_function:PN
+    \l__grading_scheme_block_elements_clist_ptr
+    \__grading_scheme_block_height_accumulator:N
+  \grading_scheme_block_if_description:NT #1
+    {
+      \int_incr:N \l__grading_scheme_height_sum_int
+    }
+  \exp_args:NNNV
+    \group_end:
+    \int_set:Nn #2 \l__grading_scheme_height_sum_int
+}
+\cs_new:Npn \grading_scheme_block_get_height:PN
+  {
+    \exp_args:NP \grading_scheme_block_get_height:NN
+  }
+\cs_new:Npn \__grading_scheme_block_height_accumulator:N #1
+  {
+    \grading_scheme_element_get_height:NN #1 \l__grading_scheme_height_acc_int
+    \int_add:Nn \l__grading_scheme_height_sum_int
+      { \int_use:N \l__grading_scheme_height_acc_int }
+  }
+\cs_new:Npn \grading_scheme_block_if_description:NT #1 % implicit #2
+  {
+    \__grading_scheme_block_load_description:N #1
+    \quark_if_no_value:NF \l__grading_scheme_block_description_tl
+  }
+\cs_new:Npn \grading_scheme_block_if_description:NF #1 % implicit #2
+  {
+    \__grading_scheme_block_load_description:N #1
+    \quark_if_no_value:NT \l__grading_scheme_block_description_tl
+  }
+\cs_new:Npn \grading_scheme_block_if_description:NTF #1 #2 #3
+  {
+      \__grading_scheme_block_load_description:N #1
+      \quark_if_no_value:NTF \l__grading_scheme_block_description_tl
+        { #3 }
+        { #2 }
+  }
+\int_new:N \l__grading_scheme_max_width_int
+\int_new:N \l__grading_scheme_width_acc_int
+\cs_new:Npn \grading_scheme_block_get_natural_width:NN #1 #2
+  {
+     \group_begin:
+     \__grading_scheme_block_load_elements:N #1
+     \int_zero:N \l__grading_scheme_max_width_int
+     \clist_map_function:PN
+       \l__grading_scheme_block_elements_clist_ptr
+       \__grading_scheme_block_width_accumulator:N
+     \int_incr:N \l__grading_scheme_max_width_int
+     \exp_args:NNNV
+       \group_end:
+       \int_set:Nn #2 \l__grading_scheme_max_width_int
+  }
+\cs_new:Npn \grading_scheme_block_get_natural_width:PN
+  {
+    \exp_args:NP \grading_scheme_block_get_natural_width:NN
+  }
+\cs_new:Npn \__grading_scheme_block_width_accumulator:N #1
+  {
+    \grading_scheme_element_get_natural_width:NN #1 \l__grading_scheme_width_acc_int
+    \int_set:Nn \l__grading_scheme_max_width_int
+      {
+        \int_max:nn
+          \l__grading_scheme_width_acc_int
+          \l__grading_scheme_max_width_int
+      }
+  }
+\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
+\ptr_new:N \l__grading_scheme_element_content_void_ptr
+\str_new:N \l__grading_scheme_element_type_str
+\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 }
+  }
+\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 }
+  }
+\cs_new:Npn \__grading_scheme_element_load_content:N #1
+  {
+    \grading_scheme_element_get_content:NN #1 \l__grading_scheme_element_content_void_ptr
+  }
+\cs_new:Npn \__grading_scheme_element_load_type:N #1
+  {
+    \grading_scheme_element_get_type:NN #1 \l__grading_scheme_element_type_str
+  }
+\cs_new:Npn \grading_scheme_element_cases:Nnn % implicit #1-3
+  {
+    \cs_set_eq:NN \__grading_scheme_str_case:Vnw \str_case:Vn
+    \__grading_scheme_element_cases:Nnnw
+  }
+\cs_new:Npn \grading_scheme_element_cases:NnnT % implicit #1-4
+  {
+    \cs_set_eq:NN \__grading_scheme_str_case:Vnw \str_case:VnT
+    \__grading_scheme_element_cases:Nnnw
+  }
+\cs_new:Npn \grading_scheme_element_cases:NnnF % implicit #1-4
+  {
+    \cs_set_eq:NN \__grading_scheme_str_case:Vnw \str_case:VnF
+    \__grading_scheme_element_cases:Nnnw
+  }
+\cs_new:Npn \grading_scheme_element_cases:NnnTF % implicit #1-5
+  {
+    \cs_set_eq:NN \__grading_scheme_str_case:Vnw \str_case:VnTF
+    \__grading_scheme_element_cases:Nnnw
+  }
+\cs_new:Npn \__grading_scheme_element_cases:Nnnw #1 #2 #3 % implicit branches
+  {
+    \__grading_scheme_element_load_type:N #1
+    \__grading_scheme_str_case:Vnw \l__grading_scheme_element_type_str
+      {
+        { entry } { #2 }
+        { block } { #3 }
+      }
+    % implicit branches
+  }
+\cs_new:Npn \grading_scheme_element_format:NnnnN % implicit #1-5
+  {
+    \cs_set_eq:NN \__grading_scheme_entry_format_aux:PnnN  \grading_scheme_entry_format:PnnN
+    \cs_set_eq:NN \__grading_scheme_block_format_aux:PnnnN \grading_scheme_block_format:PnnnN
+    \cs_set_eq:NN \__grading_scheme_tl_put_right:Nx        \tl_put_right:Nx
+    \__grading_scheme_element_format:NnnnN
+  }
+\cs_new:Npn \grading_scheme_element_gformat:NnnnN % implicit #1-5
+  {
+    \cs_set_eq:NN \__grading_scheme_entry_format_aux:PnnN  \grading_scheme_entry_gformat:PnnN
+    \cs_set_eq:NN \__grading_scheme_block_format_aux:PnnnN \grading_scheme_block_gformat:PnnnN
+    \cs_set_eq:NN \__grading_scheme_tl_put_right:Nx        \tl_put_gright:Nx
+    \__grading_scheme_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
+  }
+\cs_new:Npn \__grading_scheme_element_format:NnnnN #1 #2 #3 #4 #5
+  {
+    \__grading_scheme_element_load_content:N #1
+    \grading_scheme_element_cases:NnnF #1
+        {
+          \bool_if:nTF { #4 }
+            {
+              \__grading_scheme_entry_format_aux:PnnN
+                \l__grading_scheme_element_content_void_ptr
+                { #2 }
+                { #3 }
+                #5
+            }
+            {
+              \__grading_scheme_entry_format_aux:PnnN
+                \l__grading_scheme_element_content_void_ptr
+                { 0 }
+                { #3 }
+                #5
+            }
+        }
+        {
+          \__grading_scheme_block_format_aux:PnnnN
+            \l__grading_scheme_element_content_void_ptr
+            { #2 }
+            { #3 }
+            { #4 }
+            #5
+        }
+        {
+          \msg_error:nnxxx { grading-scheme } { missing-value }
+            { element }
+            { \token_to_str:N #1 }
+            { type / content }
+        }
+    \__grading_scheme_tl_put_right:Nx #5
+      {
+        \exp_not:N \__grading_scheme_cline:nn
+          {
+            \int_eval:n { #2 +1 }
+          }
+          {
+            \int_eval:n { #2 + #3 }
+          }
+      }
+  }
+\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_get_height:NN #1 #2
+  {
+    \grading_scheme_element_cases:NnnF #1
+        {
+          \int_set:Nn #2 { 1 }
+        }
+        {
+          \__grading_scheme_element_load_content:N #1
+          \grading_scheme_block_get_height:PN
+            \l__grading_scheme_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
+  }
+\cs_new:Npn \grading_scheme_element_get_natural_width:NN #1 #2
+  {
+    \grading_scheme_element_cases:NnnF #1
+      {
+        \int_set:Nn { #2 } { 2 }
+      }
+      {
+        \__grading_scheme_element_load_content:N #1
+        \grading_scheme_block_get_natural_width:PN
+          \l__grading_scheme_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
+  }
+\int_new:N \l__grading_scheme_width_int
+\tl_new:N \g__grading_scheme_table_tl
+\cs_new:Npn \grading_scheme_element_typeset:N #1
+  {
+    \grading_scheme_element_get_natural_width:NN #1 \l__grading_scheme_width_int
+    \tl_gclear:N \g__grading_scheme_table_tl
+    \tl_gput_right:Nn \g__grading_scheme_table_tl
+      {
+        \begin{tabular}
+      }
+    \tl_gput_right:Nx \g__grading_scheme_table_tl
+      {
+        {
+          \prg_replicate:nn { \l__grading_scheme_width_int -1 } { |l }
+          | l |
+        }
+        \exp_not:N \__grading_scheme_hline:
+      }
+    \grading_scheme_element_gformat:NnnnN
+      #1
+      { 0 }
+      { \l__grading_scheme_width_int }
+      { \c_false_bool }
+      \g__grading_scheme_table_tl
+    \tl_gput_right:Nn \g__grading_scheme_table_tl
+      {
+        \end{tabular}
+      }
+    \group_begin:
+    \tl_set:Nn \arraystretch { 1.5 }
+    \tl_use:N \g__grading_scheme_table_tl
+    \group_end:
+  }
+\cs_new:Npn \grading_scheme_element_typeset:P
+  {
+    \exp_args:NP \grading_scheme_element_typeset:N
+  }
+\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
+  }
+\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
+  }
+\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
+  }
+\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
+  }
+\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
+  }
+\ptr_new:Nn \l__grading_scheme_entry_ptr { g }
+\ptr_new:Nn \l__grading_scheme_element_ptr { g }
+\ptr_new:Nn \l__grading_scheme_curr_block_ptr { g }
+\cs_new:Npn \__grading_scheme_entry:nn #1 #2
+  {
+    \ptr_clear:Nn \l__grading_scheme_entry_ptr { g }
+    \ptr_clear:Nn \l__grading_scheme_element_ptr { g }
+    \grading_scheme_entry_new:Pnn
+      \l__grading_scheme_entry_ptr
+      { #1 }
+      { #2 }
+    \grading_scheme_element_gfrom_entry_new:PP
+      \l__grading_scheme_element_ptr
+      \l__grading_scheme_entry_ptr
+    \grading_scheme_block_gadd_element:PP
+      \l__grading_scheme_curr_block_ptr
+      \l__grading_scheme_element_ptr
+  }
+\cs_new:Npn \__grading_scheme_block_begin:nn % implicit #1, #2
+  {
+    \ptr_clear:Nn \l__grading_scheme_curr_block_ptr { g }
+    \grading_scheme_block_new:Pnn \l__grading_scheme_curr_block_ptr
+  }
+\cs_new:Npn \__grading_scheme_block_begin:n % implicit #1
+  {
+      \ptr_clear:Nn \l__grading_scheme_curr_block_ptr { g }
+      \grading_scheme_block_new:Pn \l__grading_scheme_curr_block_ptr
+  }
+\cs_new:Npn \__grading_scheme_block_end:
+  {
+    \ptr_clear:Nn \l__grading_scheme_element_ptr { g }
+    \grading_scheme_element_gfrom_block_new:PP
+      \l__grading_scheme_element_ptr
+      \l__grading_scheme_curr_block_ptr
+    \cs_gset:Npx \__grading_scheme_after_group:
+      {
+        \exp_not:N \grading_scheme_block_gadd_element:PN
+          \exp_not:N \l__grading_scheme_curr_block_ptr
+          \exp_not:P \l__grading_scheme_element_ptr
+      }
+    \group_insert_after:N \__grading_scheme_after_group:
+  }
+\NewDocumentCommand \entry { m m }
+  {
+    \__grading_scheme_entry:nn { #1 } { #2 }
+  }
+\NewDocumentEnvironment { block } { o m }
+  {
+    \IfValueTF { #1 }
+      {
+        \__grading_scheme_block_begin:nn { #1 } { #2 }
+      }
+      {
+        \__grading_scheme_block_begin:n { #2 }
+      }
+  }
+  {
+    \__grading_scheme_block_end:
+  }
+\NewDocumentEnvironment {gradingscheme} { o m }
+  {
+    \bool_if:NT \g__grading_scheme_pipe_syntax_bool
+      {
+        \char_set_active_eq:NN | \__grading_scheme_entry_oneline:w
+        \char_set_catcode_active:N |
+      }
+    \IfValueTF { #1 }
+      {
+        \__grading_scheme_block_begin:nn { #1 } { #2 }
+      }
+      {
+        \__grading_scheme_block_begin:n { #2 }
+      }
+  }
+  {
+    \ptr_clear:Nn \l__grading_scheme_element_ptr { g }
+    \grading_scheme_element_gfrom_block_new:PP
+      \l__grading_scheme_element_ptr
+      \l__grading_scheme_curr_block_ptr
+    \grading_scheme_element_typeset:P \l__grading_scheme_element_ptr
+  }
+\bool_if:NF \g__grading_scheme_pipe_syntax_bool
+  {
+    \endinput
+  }
+\scan_new:N \s__grading_scheme_endline
+\cs_new:Npn \__grading_scheme_parse_entry:w #1 & #2 \s_endline
+  {
+    \__grading_scheme_entry:nn { #1 } { #2  }
+  }
+\cs_new:Npn \__grading_scheme_replace_newline:w
+  {
+    \group_begin:
+    \char_set_catcode_active:N\^^M
+    \__grading_scheme_replace_newline_aux:w
+  }
+\char_set_catcode_active:N\^^M
+\cs_new:Npn \__grading_scheme_replace_newline_aux:w #1 ^^M
+  {
+    \group_end:
+    #1 \s_endline
+  }
+\char_set_catcode_end_line:N\^^M
+\cs_new:Npn \__grading_scheme_entry_oneline:w
+  {
+    \__grading_scheme_replace_newline:w
+    \__grading_scheme_parse_entry:w
+  }
+%% 
+%% Copyright 2022 Maximilian Kessler <ctan at 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.
+%% 
+%%
+%% End of file `grading-scheme.sty'.


Property changes on: trunk/Master/texmf-dist/tex/latex/grading-scheme/grading-scheme.sty
___________________________________________________________________
Added: svn:eol-style
## -0,0 +1 ##
+native
\ No newline at end of property
Modified: trunk/Master/tlpkg/bin/tlpkg-ctan-check
===================================================================
--- trunk/Master/tlpkg/bin/tlpkg-ctan-check	2022-02-23 20:53:27 UTC (rev 62154)
+++ trunk/Master/tlpkg/bin/tlpkg-ctan-check	2022-02-23 20:55:03 UTC (rev 62155)
@@ -371,7 +371,7 @@
     gmdoc gmdoc-enhance
     gmiflink gmp gmutils gmverb gmverse gnuplottex
     go gobble gofonts gost gothic gotoh
-    grabbox gradientframe gradstudentresume grafcet grant
+    grabbox gradientframe grading-scheme gradstudentresume grafcet grant
     graph35 graphbox graphics
     graphics-cfg graphics-def graphics-pln
     graphicx-psmin graphicscache graphicxbox graphicxpsd

Modified: trunk/Master/tlpkg/libexec/ctan2tds
===================================================================
--- trunk/Master/tlpkg/libexec/ctan2tds	2022-02-23 20:53:27 UTC (rev 62154)
+++ trunk/Master/tlpkg/libexec/ctan2tds	2022-02-23 20:55:03 UTC (rev 62155)
@@ -3027,6 +3027,7 @@
  'german'       => 'tex',
  'gost'         => 'tex',
  'grabbox'	=> 'tex',
+ 'grading-scheme' => 'etex-answer-y',
  'graphics-pln' => "env TEXINPUTS=$Master/texmf-dist/source/latex/base: latex",
  'grffile'	=> 'tex',
  'halloweenmath' => 'latex',  # requires interaction

Modified: trunk/Master/tlpkg/tlpsrc/collection-latexextra.tlpsrc
===================================================================
--- trunk/Master/tlpkg/tlpsrc/collection-latexextra.tlpsrc	2022-02-23 20:53:27 UTC (rev 62154)
+++ trunk/Master/tlpkg/tlpsrc/collection-latexextra.tlpsrc	2022-02-23 20:55:03 UTC (rev 62155)
@@ -587,6 +587,7 @@
 depend gmutils
 depend gmverb
 depend grabbox
+depend grading-scheme
 depend graphbox
 depend graphicscache
 depend graphicx-psmin

Added: trunk/Master/tlpkg/tlpsrc/grading-scheme.tlpsrc
===================================================================


More information about the tex-live-commits mailing list.