% \iffalse meta-comment %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% % This LaTeX docstrip file is free software and is licensed under the % % Latex Project Public License v1.3c, or (at your choice) any later version % % The latest version of the license can be found at % % https://www.latex-project.org/lppl/lppl-1-3c.txt % % You may use it freely for your purposes. % % The packages home is https://ctan.org/pkg/grading-scheme % %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% %<*internal> % \begingroup % \input docstrip.tex % \keepsilent % \preamble % ___________________________________________________________ % The grading-scheme package % Copyright (C) 2022 Maximilian Kessler % License information appended. % % \endpreamble % \postamble % % Copyright 2022 Maximilian Kessler % % 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 % % %\ProvidesExplPackage {grading-scheme} {2022/02/24} {0.1.1} % {Typeset grading schemes in tabular format} % %<*driver> \documentclass[full]{l3doc} \usepackage{grading-scheme} \begin{document} \RecordChanges \DocInput{grading-scheme.dtx} \end{document} % % \fi % % \changes{0.1}{2022/02/22}{Initial release} % \changes{0.1.1}{2022/02/24}{Bug fix (thanks to Denis Bitouzé), typos.} % % % % % \title{The \pkg{grading-scheme} package} % % \author{Maximilian Keßler\thanks{ctan@maximilian-kessler.de}} % \date{Released 2022/02/24\\ \vskip 1.5em {\small version 0.1.1}} % % \maketitle % % \begin{abstract} % This package aims at an easy-to-use interface to typeset % grading schemes in tabular format, in particular grading-schemes % of exercises of mathematical olympiads where multiple % solutions have to be graded and might offer mutual % exclusive ways of receiving points. % \end{abstract} % % \setcounter{tocdepth}{2} % \tableofcontents % % \begin{documentation} % % \section{General notions} % % Probably, an example is fitting best at this point. % If you want to set up a grading scheme, with the \pkg{grading-scheme} package % all you have to do to get the output of \autoref{tab:grading-scheme} % is type the following: % % \iffalse % Since the docstrip format already uses the pipe character % to denote verbatim, we actually use \entry to typeset the table. % In a user document, one could of course use the pipe syntax again. % \fi % \begin{table}[tp] % \centering % \begin{gradingscheme}{sum} % \entry{At least one correct solution}{1} % \entry{All correct solutions}{1} % \begin{block}{max} % \begin{block}[Uniqueness proofs]{max} % \begin{block}[First solution]{sum} % \entry{Show that either $4 \mid p - 2$ or $3 \mid p - 4$.}{3} % \entry{Show that $m = 5$}{2} % \entry{Show that $ n = 4 $}{1} % \entry{Conclude solutions}{1} % \end{block} % \begin{block}[Second solution]{sum} % \entry{Show that $p$ is even}{2} % \entry{Reduce to at most 30 candidates}{3} % \entry{Test all candidates}{2} % \end{block} % \end{block} % \begin{block}{min(2)} % \begin{block}[Plausible Arguments]{sum} % \entry{plausible argument that there are finitely many solutions}{1} % \entry{suggestion that divisibility by 4 is useful}{1} % \entry{suggesting that not both of $m$, $n$ can be 'large'.}{1} % \end{block} % \end{block} % \end{block} % \end{gradingscheme} % \caption{Example output of a grading scheme.} % \label{tab:grading-scheme} % \end{table} % % \begin{verbatim} % \begin{gradingscheme}{sum} % | At least one correct solution & 1 % | All correct solutions & 1 % \begin{block}{max} % \begin{block}[Uniqueness proofs]{max} % \begin{block}[First solution]{sum} % | Show that either $4 \mid p - 2$ or $3 \mid p - 4$. & 3 % | Show that $m = 5$ & 2 % | Show that $ n = 4 $ & 1 % | Conclude solutions & 1 % \end{block} % \begin{block}[Second solution]{sum} % | Show that $p$ is even & 2 % | Reduce to at most 30 candidates & 3 % | Test all candidates & 2 % \end{block} % \end{block} % \begin{block}{min(2)} % \begin{block}[Plausible Arguments]{sum} % | plausible argument that there are finitely many solutions & 1 % | suggestion that divisibility by 4 is useful & 1 % | suggesting that not both of $m$, $n$ can be 'large'. & 1 % \end{block} % \end{block} % \end{block} % \end{gradingscheme} % \end{verbatim} % % Note in particular that the correct width of the rows and columns % are automatically adjusted. % % % For us, a grading scheme consists of several statements, each worth % some specific number of points, that will be combined into a final % number of points using a nested expression of operations, typically % sums, maximums or minimus. % % An abstract grammar defining our notions is defined in % \autoref{tab:grading-scheme-grammar}. % The meanings of \meta{balanced text} and \meta{number} are % as usual. % % \begin{table}[htpb] % \centering % \begin{tabular}{r@{\;:=\;}l} % grading scheme & block \\ % block & [\meta{block description}], \meta{block operation}, \meta{element list} \\ % element list & \meta{element} $\mid$ \meta{element} : \meta{element list} \\ % element & \meta{block} $\mid$ \meta{entry} \\ % entry & \meta{entry description}, \meta{entry points} \\ % block description & \meta{balanced text} \\ % block operation & \meta{balanced text} \\ % entry description & \meta{balanced text} \\ % entry points & \meta{number} \\ % \end{tabular} % \caption{Abstract grammer for a grading scheme} % \label{tab:grading-scheme-grammar} % \end{table} % % The package facilitates specifying a grading scheme according to this grammar % and typesets these. % % \section{Usage} % % Load the package as usual by % \begin{verbatim} % \usepackage[pipe]{grading-scheme} % \end{verbatim} % % The |pipe| option is of course optional and the only currently supported option. % It activates the pipe syntax explained later. % % \begin{environment}{gradingscheme} % \begin{syntax} % \cs{begin}\{gradingscheme\}\oarg{description text}\Arg{operator} % \cs{end}\{gradingscheme\} % \end{syntax} % The main environment provided by this package. % Inside the grading scheme, any number of blocks and entries can % be specified. % The grading scheme environment will act like the outer-most block % and typeset all its contents. % % Grading schemes cannot contain each other. As they are implemented % as |tabular|s, you can (and maybe should) put a grading scheme % into a |table| environment as if it were a regular |tabular|. % % \begin{texnote} % Actually, the gradingscheme environment just sets up some hooks % that further block environments and entries will fill % and typesets these at the end of the environment. % % You could technically put any regular text inside the grading % scheme and this will be typeset as if it had been specified % before the grading scheme. However, this is definitely not % recommended. % \end{texnote} % \end{environment} % % \begin{environment}{block} % \begin{syntax} % \cs{begin}\{block\}\oarg{description text}\Arg{operator} % \cs{end}\{block\} % \end{syntax} % % The \meta{description text} will be shown in a separate row % on top of the block. If not specfied, no such row is inserted. % The \meta{operator} is printed in small capitals (and rotated) % in a column that spans the left side of the block, % indicating that this operation is applied to the block. % % The block will be an element of the outer block or % grading scheme it is contained in. % % Inside the block, any number of blocks or entries % can be specified (in order) that will be typeset % as members of this block. % % A block environment can only be used inside a gradingscheme. % Block environments can contain each other in balanced form. % \end{environment} % % \begin{function}{\entry} % \begin{syntax} % \cs{entry}\Arg{entry description}\Arg{points} % \end{syntax} % % Specifies an entry with given description and points. % The entry is typeset as part of the block/grading scheme % containing it. % \end{function} % % \begin{function}{|} % \begin{syntax} % $\mid$ \meta{entry description} \& \meta{points} \textbackslash{}n % \end{syntax} % This is an alternative form to specify an entry. The |\n| % is to be interpreted as a newline in the source file, so $\mid$ reads % until the end of the line. % % This is equivalent to calling \cs{entry}\Arg{entry description}\Arg{points} % % The package option |pipe| has to be specified to activate this % syntax. % % \begin{texnote} % The $\mid$ character (pipe) is made an active character for the scope % of each gradingscheme environment. % Thus, this syntax only works if the gradingscheme is read in % expansion mode by \LaTeX{}, since otherwise the category code % will be messed up. % \end{texnote} % \end{function} % % % \section{\LaTeX3 interface} % % There is also a \LaTeX3 interface for dealing with this package % that can deal with all of the mentioned notions separately, % technically allowing you to combine them in other ways or formatting % them into your custom tables. % % These functions are for now not documented separately, since the author % thinks that such use cases would be rather rare. % % The \LaTeX3 interface is, however, \emph{not} considered to be stable % yet, so if you really have to rely on this, feel free to contact % the author. % % \PrintChanges % % \end{documentation} % % % \begin{implementation} % % \section{\pkg{grading-scheme} implementation} % \begin{macrocode} %<*package> % \end{macrocode} % % \begin{macrocode} %<@@=grading_scheme> % \end{macrocode} % % \subsection{Dependencies and options} % % Currently, there is only one option % % \begin{variable}{\g_@@_pipe_syntax_bool} % % This will toggle the pipe syntax option of the package. % % \begin{macrocode} \bool_new:N \g_@@_pipe_syntax_bool % \end{macrocode} % \end{variable} % % % \begin{macrocode} \keys_define:nn { grading-scheme } { pipe .bool_gset:N = \g_@@_pipe_syntax_bool, pipe .default:n = { true }, unknown .code:n = { \msg_error:nnn { grading-scheme } { unknown-option} { \l_keys_key_str } } } \msg_new:nnn { grading-scheme } { unknown-option} { Unknown ~ option ~ '#1'. } \RequirePackage { l3keys2e } \ProcessKeysOptions { grading-scheme } \RequirePackage{multirow} \RequirePackage{rotating} % \end{macrocode} % % \subsection{A simple logging module} % % \begin{macrocode} %<*log> % \end{macrocode} % % We provide a simple log/debugging facility for this package. % % \begin{variable}{\g_@@_log_depth_int} % Stores the log depth for indentation in the log file. % \begin{macrocode} \int_new:N \g_@@_log_depth_int % \end{macrocode} % \end{variable} % % \begin{macro}{\@@_log_incr:} % % Increments the log depth % % \begin{macrocode} \cs_new:Npn \@@_log_incr: { \int_gincr:N \g_@@_log_depth_int } % \end{macrocode} % \end{macro} % % % \begin{macro}{\@@_log_decr:} % % Decrements the log depth % % \begin{macrocode} \cs_new:Npn \@@_log_decr: { \int_gdecr:N \g_@@_log_depth_int } % \end{macrocode} % \end{macro} % % % \begin{macro}{\@@_log:n, \@@_log:x} % % Logs to the terminal and log file using the current depth. % % \begin{macrocode} \cs_new:Npn \@@_log:n #1 { \iow_term:x { [gs] \prg_replicate:nn \g_@@_log_depth_int { . . } \exp_not:n { #1 } } \iow_log:x { [gs] \prg_replicate:nn \g_@@_log_depth_int { . . } \exp_not:n { #1 } } } \cs_generate_variant:Nn \@@_log:n { x } % \end{macrocode} % \end{macro} % % % \begin{macrocode} % % \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: % \@@_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' } % } \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: % % \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' } % } \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: % % \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' } % } \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: % \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'. } % } \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: % \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' } % } % \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 % \@@_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' } % } \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} % % \end{macrocode} % % \end{implementation} % % \PrintIndex %