summaryrefslogtreecommitdiff
path: root/macros/latex/contrib/grading-scheme/grading-scheme.dtx
diff options
context:
space:
mode:
Diffstat (limited to 'macros/latex/contrib/grading-scheme/grading-scheme.dtx')
-rw-r--r--macros/latex/contrib/grading-scheme/grading-scheme.dtx2768
1 files changed, 2768 insertions, 0 deletions
diff --git a/macros/latex/contrib/grading-scheme/grading-scheme.dtx b/macros/latex/contrib/grading-scheme/grading-scheme.dtx
new file mode 100644
index 0000000000..a63a466573
--- /dev/null
+++ b/macros/latex/contrib/grading-scheme/grading-scheme.dtx
@@ -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@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@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
+%