[latex3-commits] [latex3/latex2e] ltproperties: move ltproperties, WIP (ae12fe15)

github at latex-project.org github at latex-project.org
Wed Sep 6 00:57:00 CEST 2023


Repository : https://github.com/latex3/latex2e
On branch  : ltproperties
Link       : https://github.com/latex3/latex2e/commit/ae12fe15f9006801d838dc610fe5e490077f9dce

>---------------------------------------------------------------

commit ae12fe15f9006801d838dc610fe5e490077f9dce
Author: Ulrike Fischer <fischer at troubleshooting-tex.de>
Date:   Wed Sep 6 00:57:00 2023 +0200

    move ltproperties, WIP


>---------------------------------------------------------------

ae12fe15f9006801d838dc610fe5e490077f9dce
 base/ltproperties.dtx | 934 ++++++++++++++++++++++++++++++++++++++++++++++++++
 1 file changed, 934 insertions(+)

diff --git a/base/ltproperties.dtx b/base/ltproperties.dtx
new file mode 100644
index 00000000..83cf6663
--- /dev/null
+++ b/base/ltproperties.dtx
@@ -0,0 +1,934 @@
+% \iffalse meta-comment
+%
+%% File: ltproperties.dtx
+%
+% Copyright (C) 2021-2023 The LaTeX3 Project
+%
+% It may be distributed and/or modified under the conditions of the
+% LaTeX Project Public License (LPPL), either version 1.3c of this
+% license or (at your option) any later version.  The latest version
+% of this license is in the file
+%
+%    http://www.latex-project.org/lppl.txt
+%
+% This file is part of the LaTeX base system. (The Work in LPPL)
+% and all files in that bundle must be distributed together.
+%
+% -----------------------------------------------------------------------
+%
+% The development version of the bundle can be found at
+%
+%    https://github.com/latex3/latex2e
+%
+% for those people who are interested.
+%    \begin{macrocode}
+\def\ltpropertiesversion{0.9a}
+\def\ltpropertiesdate{2023-09-05}
+%    \end{macrocode}
+
+%<*driver>
+\documentclass[full]{l3doc}
+\EnableCrossrefs
+\CodelineIndex
+\begin{document}
+  \DocInput{\jobname.dtx}
+\end{document}
+%</driver>
+% \fi
+%
+% \title{^^A
+%   Recording and cross-referencing document properties^^A
+%  \thanks{This module has version
+%    \ltpropertiesversion\ dated \ltpropertiesdate, \copyright\ \LaTeX\
+%    Project.}
+% }
+%
+% \author{^^A
+%  The \LaTeX\ Project\thanks
+%    {^^A
+%      E-mail:
+%        \href{mailto:latex-team at latex-project.org}
+%          {latex-team at latex-project.org}^^A
+%    }^^A
+% }
+%
+% \maketitle
+%
+% \begin{documentation}
+% \begin{abstract}
+% This code implements command to record and (expandably) reference 
+% document properties. It extends the standard \cs{label}/\cs{ref}/\cs{pageref}
+% commands.
+% \end{abstract}
+%  
+% \tableofcontents
+%  
+% \section{Introduction}
+% 
+% The module allows to record the \enquote{current state} of various 
+% document properties (typically the content of macros and values of counters)
+% and to access them in other places through a label.
+% The list of properties that can be recorded and retrieved
+% are not fix and can be extended by the user. The values of the properties 
+% are recorded in the \texttt{.aux} file and can be retrieved at the second compilation.
+% 
+%
+% The module uses the ideas of properties and labels. A label is
+% a document reference point: a name for the user. An property is something
+% that \LaTeX{} can track, such as a page number, section number or name.
+% The names of labels and properties may be arbitrary. Note that there is
+% a single namespace for each.
+%
+% \section{Design discussion}
+%
+% The design here largely follows ideas from \pkg{zref}. In particular, there
+% are two independent concepts: properties that can be recorded between runs,
+% and labels which consist of lists of these properties. The reason for the
+% split is that individual labels will want to record some but not all
+% properties. For examples, a label concerned with position would track
+% the $x$ and $y$ co-ordinates of the current point, but not for example
+% the page number.
+%
+% In the current implementation, properties share a single namespace. This
+% allows multiple lists to re-use the same properties, for example page number,
+% absolute page number, etc. This does mean that \emph{changing} a standard
+% property is an issue. However, some properties have complex definitions
+% (again, see \pkg{zref} at present): having them in a single shared space
+% avoids the need to copy code.
+%
+% Labels could be implemented as |prop| data. That is not done at present as
+% there is no obvious need to map to or copy the data. As such, faster
+% performance is available using a hash table approach as in a \enquote{classical}
+% set up. Data written to the |.aux| file uses simple paired \emph{balanced
+% text} not keyvals: this avoids any restrictions on names and again offers
+% increased performance.
+%
+% The expl3 versions of the label command do not
+% use \cs{@bsphack}/\cs{@esphack} to avoid double spaces, 
+% but the \LaTeX2e command does as it lives at the document command level.  
+% 
+% The reference commands are expandable.
+% 
+% Currently the code has nearly no impact on the main \cs{label} and \cs{ref} commands as
+% too many external packages rely on the concrete implementation.
+% There is one exception:
+% the label names share the same namespace. That means that if both |\label{ABC}| and
+% |\RecordProperties{ABC}{page}| are used there is a warning 
+% \texttt{Label `ABC' multiply defined}. 
+%
+% \section{Handling unknown labels and properties}
+% With the standard \cs{label}/\cs{ref} commands the requested label is 
+% either in the |.aux|-file (and so known) or not. 
+% In the first case the stored value can be used,
+% in the second case the reference commands print two question marks.
+% 
+% With flexible property lists a reference commands asks for the 
+% value of a specific property stored under a label name
+% and we have to consider more variants: 
+% \begin{itemize}
+% \item If the requested property is unknown (not declared) the system 
+% is not correctly set up and an error is issued. 
+% \item If the label is unknown, the default of the property is used. 
+% \item If the label is known, but doesn't provide a value for the 
+% property then again the default of the property is used.
+% \item The command |\property_ref:nnn| allows to give a local default
+% which is used instead of the property default in the two cases before.  
+% \end{itemize}
+% 
+% \section{Rerun messages}
+% 
+% As the reference commands are expandable they can't neither issue a message that
+% the label or the label-property combination is unknown, nor can they trigger the
+% rerun message at the end of the \LaTeX{} run.
+% 
+% Where needed such messages must therefore be triggered manually. For this two commands
+% are provided: \cs{property_ref_undefined_warn:} and  \cs{property_ref_undefined_warn:nn}.
+% See below for a description.
+% 
+% \section{Open points}
+% 
+% \begin{itemize}
+%  \item The \texttt{xpos} and \texttt{ypos} properties require that the position is 
+% stored first but there is no (public) engine independent interface yet. Code must 
+% use either \cs{tex_savepos:D} or \cs{pdfsavepos} (pdftex,xetex) 
+% or \cs{savepos} (luatex). 
+% \end{itemize}
+% 
+% \section{Code interfaces}
+%
+% \begin{function}{\property_new:nnnn,\property_gset:nnnn}
+%   \begin{syntax}
+%     \cs{property_new:nnnn} \Arg{property} \Arg{setpoint} \Arg{default}  \Arg{code}
+%     \cs{property_gset:nnnn} \Arg{property}\Arg{setpoint} \Arg{default}  \Arg{code}
+%   \end{syntax}
+%  \LaTeX2e-interface: see \cs{NewProperty}, \cs{SetProperty}.\\ 
+%   Sets the \meta{property} to have the \meta{default} specified, and at the
+%   \meta{setpoint} (either |now| or |shipout|) to write the result of the
+%   \meta{code} as part of a label. The \meta{code} should be expandable. The expansion
+%   of \meta{code} (the value of the property) is written to the |.aux| file and read
+%   back from there at the next compilation. Values should assume that the 
+%   standard \LaTeX{} catcode régime with |@| a letter is active then. 
+%   
+%   If the property is declared within a package it is suggested
+%   that its name is build from letters, hyphens and slashes,
+%   and is always structured as follows:\\ 
+%   \meta{package-name}\texttt{/}\meta{property-name}. 
+% \end{function}
+%
+% \begin{function}
+%   {
+%     \property_record:nN,
+%     \property_record:nn, \property_record:nV, \property_record:xx
+%   }
+%   \begin{syntax}
+%      \cs{property_record:nN} \Arg{label} \Arg{clist var}
+%      \cs{property_record:nn} \Arg{label} \Arg{clist}
+%   \end{syntax}
+%   \LaTeXe{}-interface: see \cs{RecordProperties}.\\  
+%   Writes the list of properties given by the \meta{clist} to the |.aux|
+%   file with the \meta{label} specified.
+% \end{function}
+%
+% \begin{function}[EXP]{\property_ref:nn}
+%   \begin{syntax}
+%      \cs{property_ref:nn} \Arg{label} \Arg{property}
+%   \end{syntax}
+%   \LaTeXe{}-interface: see \cs{RefProperty}.\\  
+%   Expands to the value of the \meta{property} for the \meta{label}, if
+%   available, and the default value of the property otherwise.
+%   If \meta{property} has not been declared with |\property_new:nnnn| 
+%   an error is issued. The command raises an internal, expandable, local flag 
+%   if the reference can not be resolved.
+% \end{function}
+% 
+% \begin{function}[EXP]{\property_ref:nnn}
+%   \begin{syntax}
+%     \cs{property_ref:nnn} \Arg{label} \Arg{property} \Arg{local default}
+%   \end{syntax}
+%  \LaTeXe{}-interface: see \cs{RefProperty}.\\
+%  Expands to the value of the \meta{property} for the \meta{label}, if
+%  available, and to \meta{local default} otherwise.
+%  If \meta{property} has not been declared with |\property_new:nnnn| 
+%  an error is issued. The command raises an internal, expandable local flag 
+%  if the reference can not be resolved.
+% \end{function}
+% 
+% \begin{function}{\property_ref_undefined_warn:}
+%   \begin{syntax}
+%     \cs{property_ref_undefined_warn:}
+%   \end{syntax}
+%  \LaTeXe{}-interface: not provided.\\ 
+%  The commands triggers the standard warning
+%  \\
+%  \hspace*{1em}\texttt{LaTeX Warning: There were undefined references.}
+%  \\ 
+%  at the end of the document if there was a recent
+%  \cs{property_ref:nn} or \cs{property_ref:nnn} which couldn't be resolved
+%  and so raised the flag. \enquote{Recent} means in the same group 
+%  or in some outer group!
+% \end{function}
+%
+% \begin{function}{\property_ref_undefined_warn:n}
+%   \begin{syntax}
+%     \cs{property_ref_undefined_warn:n} \Arg{label}
+%   \end{syntax}
+% \LaTeXe{}-interface: not provided.\\
+%  The commands triggers the standard warning\\
+%  \hspace*{1em}\texttt{LaTeX Warning: There were undefined references.}
+%  \\ 
+%  at the end of the document if \meta{label} is not known.
+%  At the point where it is called it also issues the warning\\
+%  \hspace*{1em}%
+%  \texttt{Reference~`\meta{label}'~on~page~\meta{page}\space undefined}.
+% \end{function}
+% 
+% \begin{function}{\property_ref_undefined_warn:nn}
+%   \begin{syntax}
+%     \cs{property_ref_undefined_warn:nn} \Arg{label} \Arg{property}
+%   \end{syntax}
+%  \LaTeXe{}-interface: see \cs{RefUndefinedWarn}.\\
+%  The commands triggers the standard warning\\
+%  \hspace*{1em}%  
+%  \texttt{LaTeX Warning: There were undefined references.}\\ 
+%  at the end of the document if the reference can not be resolved.
+%  At the point where it is called it also issues the warning\\
+%  \hspace*{1em}%
+%  \texttt{Reference~`\meta{label}'~on~page~\meta{page}\space undefined}\\
+%   if the label
+%  is unknown, or the more specific\\
+%  \hspace*{1em}%
+%  \texttt{Property `\meta{property}' undefined for reference
+%    `\meta{label}' on page \meta{page}}\\
+%  if the label is known but doesn't provide a value for the requested property.   
+% \end{function}
+% 
+% \begin{function}[pTF]{\property_if_exist:n}
+%   \begin{syntax}
+%     \cs{property_if_exist_p:n} \Arg{property}
+%     \cs{property_if_exist:nTF} \Arg{property} \Arg{true code} \Arg{false code}
+%   \end{syntax}
+%  \LaTeXe{}-interface: \cs{IfPropertyExistTF}.\\   
+%   Tests if the \meta{property} has been declared.
+% \end{function}
+%
+% \begin{function}[pTF]{\property_if_recorded:n}
+%   \begin{syntax}
+%     \cs{property_if_recorded_p:n} \Arg{label} 
+%     \cs{property_if_recorded:nTF} \Arg{label} \Arg{true code} \Arg{false code}
+%   \end{syntax}
+%  \LaTeXe{}-interface: \cs{IfLabelExistTF}\\
+%   Tests if the \meta{label} is known. This is also true if the label has been
+%   set with the standard \cs{label} command.
+% \end{function}
+%
+% \begin{function}[pTF]{\property_if_recorded:nn}
+%   \begin{syntax}
+%     \cs{property_if_recorded_p:nn} \Arg{label} \Arg{property}
+%     \cs{property_if_recorded:nnTF} \Arg{label} \Arg{property} \Arg{true code} \Arg{false code}
+%   \end{syntax}
+%   \LaTeXe{}-interface: \cs{IfPropertyRecordedTF}.\\  
+%   Tests if the label \meta{label} is known and if it provides a value of the \meta{property}.
+% \end{function}
+%
+% \section{Auxiliary file interfaces}
+%
+% \begin{function}{\new at label@record}
+%   \begin{syntax}
+%      \cs{new at label@record} \Arg{label} \Arg{data}
+%   \end{syntax}
+%   This is a command only for use in the |.aux| file. It loads the key--value
+%   list of \meta{data} to be available for the \meta{label}.
+% \end{function}
+%
+% \section{\LaTeXe{} interface}
+% 
+% \begin{function}{\NewProperty,\SetProperty}
+%   \begin{syntax}
+%     \cs{NewProperty} \Arg{property} \Arg{setpoint} \Arg{default}  \Arg{code}
+%     \cs{SetProperty} \Arg{property} \Arg{setpoint} \Arg{default}  \Arg{code}
+%   \end{syntax}
+%   Sets the \meta{property} to have the \meta{default} specified, and at the
+%   \meta{setpoint} (either |now| or |shipout|) to write the result of the
+%   \meta{code} as part of a label. The \meta{code} should be expandable. The expansion
+%   of \meta{code} (the value of the property) is written to the |.aux| file and read
+%   back from there at the next compilation (at which point normally
+%   the standard \LaTeX{} catcode régime with |@| a letter is active).
+%   
+% \end{function}
+% 
+% \begin{function}{\RecordProperties}
+%   \begin{syntax}
+%      \cs{RecordProperties} \Arg{label} \Arg{clist}
+%   \end{syntax}
+%   Writes the list of properties given by the \meta{clist} to the |.aux|
+%   file with the \meta{label} specified. Similar to the standard \cs{label} command
+%   the arguments are expanded. So \meta{clist} can be a macro containing a list
+%   of properties. Also similar to the standard \cs{label} command, the command is surrounded
+%   by an \cs{@bsphack}/\cs{@esphack} pair to preserve spacing.
+% \end{function}
+
+% \begin{function}[EXP]{\RefProperty}
+%   \begin{syntax}
+%      \cs{RefProperty} \oarg{local default} \Arg{label} \Arg{property}
+%   \end{syntax}
+%   Expands to the value of the \meta{property} for the \meta{label}, if
+%   available, and the default value of the property or -- if given -- 
+%   to \meta{local default} otherwise.
+%   If \Arg{property} has not been declared an error is issued.
+% \end{function}
+% 
+% \begin{function}{\IfPropertyExistTF}
+%   \begin{syntax}
+%     \cs{IfPropertyExistTF}  \Arg{property} \Arg{true code} \Arg{false code}
+%   \end{syntax}
+%   Tests if the \meta{property} has been declared.
+% \end{function}
+%
+% \begin{function}{\IfLabelExistTF}
+%   \begin{syntax}
+%     \cs{IfLabelExistTF} \Arg{label} \Arg{true code} \Arg{false code}
+%   \end{syntax}
+%   Tests if the \meta{label} has been recorded. This is also true if a label
+%   has been set with the standard \cs{label} command.
+% \end{function}
+% 
+% \begin{function}{\IfPropertyRecordedTF}
+%   \begin{syntax}
+%     \cs{IfPropertyRecordedTF} \Arg{label} \Arg{property} \Arg{true code} \Arg{false code}
+%   \end{syntax}
+%   Tests if the label and a value of the \meta{property} for the \meta{label} are both known.
+% \end{function}
+
+% \begin{function}{\RefUndefinedWarn}
+%   \begin{syntax}
+%      \cs{RefUndefinedWarn} \Arg{label} \Arg{property}
+%   \end{syntax}
+%  This commands triggers the standard warning\\
+%  \hspace*{1em}%
+%  \texttt{LaTeX Warning: There were undefined references.}\\ 
+%  at the end of the document if the reference for \meta{label} and \meta{property}
+%  can not be resolved.
+%  At the point where it is called it also issues the warning\\
+%  \hspace*{1em}%
+%  \texttt{Reference `\meta{label}' on page \meta{page} undefined}\\
+%  if the label  is unknown, or the more specific\\
+%  \hspace*{1em}%
+%  \texttt{Property `\meta{property}' undefined for reference 
+%  `\meta{label}' on page \meta{page}}
+%  if the label is known but doesn't provide a value for the requested property.   
+% \end{function}
+%
+% \section{Pre-declared properties}
+%
+% \begin{variable}{abspage}
+%   (shipout) The absolute value of the current page: starts at $1$ and increases
+%   monotonically at each shipout.
+% \end{variable}
+%
+% \begin{variable}{page}
+%   (shipout) The current page as given by \cs{thepage}: this may or may not
+%   be a numerical value, depending on the current style. Contrast with
+%   \cs{abspage}. You get this value also  with the standard \cs{label}/\cs{pageref}.
+% \end{variable}
+%
+% \begin{variable}{pagenum}
+%   (shipout) The current page as arabic number. This is suitable for integer operations and
+%   comparisions.
+% \end{variable}
+% 
+% \begin{variable}{currentlabel}
+%   (now) The content of \cs{@currentlabel}. This is the value that
+%   you get also with the standard \cs{label}/\cs{ref}. 
+% \end{variable}
+% 
+% \begin{variable}{title}
+%    (now) The content of \cs{@currentlabelname}. 
+%   This command is filled beside others by the \pkg{nameref} package and some
+%   classes (e.g. \pkg{memoir}).
+% \end{variable}
+% 
+% \begin{variable}{target}
+%   (now)  The content of \cs{@currentHref}. 
+%   This command is normally filled by for example by
+%   \pkg{hyperref} and gives the name of the last destination it created. 
+% \end{variable}
+% 
+% \begin{variable}{counter}
+%   (now) The content of \cs{@currentcounter}. 
+%   This command contains after a \cs{refstepcounter} the name of the counter.
+% \end{variable}
+% 
+% \begin{variable}{xpos,ypos}
+%   (shipout)  This stores the x and y coordinates of a point previously 
+%   stored with \cs{pdfsavepos}/\cs{savepos}. 
+%   E.g. (if bidi is used it can be necessary to save the position
+%   before and after the label):
+%   \begin{verbatim}
+%     \tex_savepos:D 
+%     \property_record:nn{myposition}{xpos,ypos}
+%     \tex_savepos:D 
+%   \end{verbatim}  
+% \end{variable}
+% \end{documentation}
+%
+% \begin{implementation}
+%
+% \section{\pkg{ltproperty} implementation}
+%
+%    \begin{macrocode}
+%<*2ekernel|latexrelease>
+%    \end{macrocode}
+%    \begin{macrocode}
+\ExplSyntaxOn
+%    \end{macrocode}
+%
+%    \begin{macrocode}
+%<@@=property>
+%    \end{macrocode}
+%
+%    \begin{macrocode}
+%<latexrelease>\NewModuleRelease{2023/11/01}{ltproperties}
+%<latexrelease>                 {Cross-referencing~properties}
+%    \end{macrocode}
+%
+% The approach here is based closely on that from \pkg{zref}; separate out
+% lists of properties and the properties themselves, so the latter can be
+% used multiple times and in varying combinations. 
+% However, not everything is a straight copy. Firstly,
+% we treat lists of properties as simple comma lists: that allows us to have
+% either saved or dynamic lists and to avoid another data structure. The cost
+% is that errors are detected at point-of-use, but in any real case that should
+% be true anyway (and is true for \tn{zref at labelbyprop} already). Secondly,
+% we allow properties to have
+% arbitrary names, as the code does not require them to tokenize as control
+% sequences.
+%
+% \begin{macro}{\property_new:nnnn, \property_gset:nnnn, \@@_gset:nnnn}
+%   As properties can be reset, they are not constants. But they also have
+%   various pieces of required data. So we use the same approach as color and
+%   make them declarations. Data-wise, we need the detail of the implementation,
+%   the default and a flag to show if the code works now or at shipout. This
+%   last entry is done using text so needs a check. We could use a set of
+%   |prop| here, but as we never need to map or copy the lists, we can gain
+%   performance using the hash table approach.
+%    \begin{macrocode}
+\cs_new_protected:Npn \property_new:nnnn #1#2#3#4
+  {
+    \cs_if_free:cTF { @@_name_ #1 : }
+      {
+        \exp_args:Nx \@@_gset:nnnn { \tl_to_str:n {#1} }
+         {#2} {#3} {#4}
+      }
+      {
+        \msg_error:nn { property }{ exists }{#1}
+      }   
+  }
+\cs_new_protected:Npn \property_gset:nnnn #1#2#3#4
+  {
+    \exp_args:Nx \@@_gset:nnnn { \tl_to_str:n {#1} }
+      {#2} {#3} {#4}
+  }
+\cs_new_protected:Npn \@@_gset:nnnn #1#2#3#4
+  {
+    \cs_gset:cpn { @@_name_ #1 : } {#4}
+    \tl_gclear_new:c { g_@@_default_ #1 _tl }
+    \tl_gset:cn { g_@@_default_ #1 _tl } {#3}
+    \bool_if_exist:cF { g_@@_shipout_ #1 _tl }
+      { \bool_new:c { g_@@_shipout_ #1 _tl } }
+    \str_case:nnF {#2}
+      {
+        { now } { { \bool_gset_false:c { g_@@_shipout_ #1 _tl } } }
+        { shipout }
+          { \bool_gset_true:c { g_@@_shipout_ #1 _tl } }
+      }
+      { \msg_error:nnnn { property } { unknown-setpoint } {#1} {#2} }
+  }
+%    \end{macrocode}
+% \end{macro}
+% 
+% \begin{macro}{\NewProperty,\SetProperty}
+%    \begin{macrocode}
+\cs_set_eq:NN \NewProperty\property_new:nnnn
+\cs_set_eq:NN \SetProperty\property_gset:nnnn
+%    \end{macrocode}
+% \end{macro}
+% 
+% \begin{macro}{\property_record:nN}
+% \begin{macro}
+%   {
+%     \property_record:nn,
+%     \property_record:nV,
+%     \property_record:xx,
+%     \@@_record:nn,
+%     \@@_record:xn
+%   }
+% \begin{macro}[EXP]
+%   {\@@_record_value:n, \@@_record_value_aux:n, \@@_record_value_aux:e}
+%   Writing data when it is labelled means expanding at this stage and possibly
+%   later too. That is all pretty easy using \pkg{expl3}: we accept a stray
+%   comma at the end of the list as that is easier to deal with than trying
+%   to tidy up, and there is no real downside.
+%    \begin{macrocode}
+\cs_new_protected:Npn \property_record:nN #1#2
+  { \property_record:nV {#1} #2 }
+\cs_new_protected:Npn \property_record:nn #1#2
+  { \@@_record:xn { \tl_to_str:n {#1} } {#2} }
+\cs_generate_variant:Nn \property_record:nn { nV , xx }
+\cs_new_protected:Npn \@@_record:nn #1#2
+  {
+    \legacy_if:nT { @filesw }
+      {
+        \iow_shipout_x:Nx \@auxout
+          {
+            \token_to_str:N \new at label@record 
+              {#1}
+              { \clist_map_function:nN {#2} \@@_record_value:n }
+          }
+      }
+  }
+\cs_generate_variant:Nn \@@_record:nn { x }
+\cs_new:Npn \@@_record_value:n #1
+  { \@@_record_value_aux:e { \tl_to_str:n {#1} } }
+\cs_new:Npn \@@_record_value_aux:n #1
+  {
+    \cs_if_exist:cTF { @@_name_ #1 : }
+      {
+        {#1}
+        {
+          \bool_if:cTF { g_@@_shipout_ #1 _tl }
+            { \exp_not:c }
+            { \use:c }
+              { @@_name_ #1 : }
+        }
+      }
+      { \msg_expandable_error:nnn { property } { not-declared } {#1} }
+  }
+\cs_generate_variant:Nn \@@_record_value_aux:n { e }
+%    \end{macrocode}
+% \end{macro}
+% \end{macro}
+% \end{macro}
+%
+% \begin{macro}{\RecordProperties}
+%    \begin{macrocode}
+\NewDocumentCommand\RecordProperties { m m }
+  {
+    \@bsphack
+    \property_record:xx {#1}{#2}
+    \@esphack
+  }  
+%    \end{macrocode}
+% \end{macro}
+%
+% \subsection{Reference commands}
+%
+% \begin{variable}{ l_@@_ref_flag }
+%   A flag that is set if a reference couldn't be resolved.
+%    \begin{macrocode}
+\flag_new:n { l_@@_ref_flag }
+%    \end{macrocode}
+% \end{variable}
+%
+% \begin{macro}[EXP]{\property_ref:nn, \@@_ref:nn, \@@_ref:ee}
+%   Search for the label/property combination, and if not found fall back
+%   to the default of the property.
+%    \begin{macrocode}
+\cs_new:Npn \property_ref:nn #1#2
+  { \@@_ref:ee { \tl_to_str:n {#1} } { \tl_to_str:n {#2} } }
+\cs_new:Npn \@@_ref:nn #1#2
+  {
+    \tl_if_exist:cTF { g_@@_label_ #1 _ #2 _tl }
+      { \tl_use:c { g_@@_label_ #1 _ #2 _tl } }
+      { 
+        \flag_if_raised:nF 
+          { l_@@_ref_flag } { \flag_raise:n { l_@@_ref_flag } }
+        \tl_if_exist:cTF { g_@@_default_ #2 _tl }
+          { \tl_use:c { g_@@_default_ #2 _tl } }
+          { \msg_expandable_error:nnn { property } { not-declared } {#2} }
+      }
+  }
+\cs_generate_variant:Nn \@@_ref:nn { ee }
+%    \end{macrocode}
+% \end{macro}
+% 
+% \begin{macro}[EXP]{\property_ref:nnn, \@@_ref:nnn, \@@_ref:een}
+%   This allows to set a local default value which overrides the default value
+%   of the property.
+%    \begin{macrocode}
+\cs_new:Npn \property_ref:nnn #1#2#3
+  {
+    \@@_ref:een
+      { \tl_to_str:n {#1} }
+      { \tl_to_str:n {#2} }
+      {#3}
+  }
+\cs_new:Npn \@@_ref:nnn #1#2#3
+  {
+    \tl_if_exist:cTF { g_@@_label_ #1 _ #2 _tl }
+      { \tl_use:c { g_@@_label_ #1 _ #2 _tl } }
+      {
+%    \end{macrocode}
+%   We test for the default of the property only to check if the property has
+%   been declared.
+%    \begin{macrocode}
+        \tl_if_exist:cTF { g_@@_default_ #2 _tl }
+          { #3 }
+          { \msg_expandable_error:nnn { property } { not-declared } {#2} }
+      }
+  }
+\cs_generate_variant:Nn \@@_ref:nnn { ee }
+%    \end{macrocode}
+% \end{macro}
+% 
+% \begin{macro}[EXP]{\RefProperty}
+%   Search for the label/property combination, and if not found fall back
+%   to the default of the property or the given default.
+%    \begin{macrocode}
+\NewExpandableDocumentCommand \RefProperty { o m m }
+  {
+    \IfNoValueTF {#1}
+      {
+        \property_ref:nn {#2}{#3}
+      }
+      {
+        \property_ref:nnn {#2}{#3}{#1}
+      }
+  }
+%    \end{macrocode}
+% \end{macro}
+% 
+% \begin{macro}{\new at label@record}
+% \begin{macro}{\@@_data:nnn}
+%   A standard recursion loop.
+%    \begin{macrocode}
+\cs_new_protected:Npn \new at label@record #1#2
+  {
+    \tl_if_exist:cTF { r@#1 }
+      {
+        \gdef \@multiplelabels
+          { \@latex at warning@no at line { There~were~multiply-defined~labels } }
+        \@latex at warning@no at line { Label~`#1'~multiply~defined }
+      }
+      {
+        \tl_new:c { r@#1 }
+        \tl_gset:cn { r@#1 }{#2}
+      }
+    \@@_data:nnn {#1} #2 { \q_recursion_tail } { ? } \q_recursion_stop
+  }
+\cs_new_protected:Npn \@@_data:nnn #1#2#3
+  {
+    \quark_if_recursion_tail_stop:n {#2}
+    \tl_gclear_new:c { g_@@_label_ \tl_to_str:n {#1} _ \tl_to_str:n {#2} _tl }
+    \tl_gset:cn { g_@@_label_ \tl_to_str:n {#1} _ \tl_to_str:n {#2} _tl } {#3}
+    \@@_data:nnn {#1}
+  }  
+%    \end{macrocode}
+%
+% That is the nearest hook for the test if something has changed.
+%    \begin{macrocode}
+\cs_new_protected:Npn \@kernel at new@label at record@testdef #1 #2
+  {
+    \tl_if_eq:cnF { r@#1 } {#2} 
+      { \@tempswatrue }
+  }
+\AddToHook { enddocument / afterlastpage }
+  {
+    \let \new at label@record \@kernel at new@label at record@testdef
+  }  
+%    \end{macrocode}
+% \end{macro}
+% \end{macro}
+%
+% \subsection{Tests and warnings}
+%
+% \begin{macro}[pTF]{\property_if_exist:n}
+%   Tests if property has been declared.
+%    \begin{macrocode}
+\prg_new_conditional:Npnn \property_if_exist:n #1 { p , T , F,  TF }  
+  % #1 property
+  {
+    \cs_if_exist:cTF { @@_name_ #1 : }
+      {
+        \prg_return_true:
+      }
+      {
+        \prg_return_false:
+      }
+  }
+%    \end{macrocode}
+% \end{macro}
+%
+% \begin{macro}{IfPropertyExistTF}
+%    \begin{macrocode}
+\cs_new_eq:NN \IfPropertyExistTF \property_if_exist:nTF  
+%    \end{macrocode}
+% \end{macro}
+% 
+% \begin{macro}[pTF]{\property_if_recorded:n}
+%   Tests if the label has been set.
+%   This can then be used to setup e.g.~rerun messages.
+%    \begin{macrocode}
+\prg_new_conditional:Npnn \property_if_recorded:n #1 { p , T , F,  TF }  
+  % #1 label 
+  {
+    \tl_if_exist:cTF { r@#1 }
+      {
+        \prg_return_true:
+      }
+      {
+        \prg_return_false:
+      }
+  }
+%    \end{macrocode}
+% \end{macro}
+%
+% \begin{macro}{\IfLabelExistTF}
+%    \begin{macrocode}
+\cs_new_eq:NN \IfLabelExistTF \property_if_recorded:nTF  
+%    \end{macrocode}
+% \end{macro} 
+%
+% \begin{macro}[pTF]{\property_if_recorded:nn}
+% tests if the label/property combination has been set
+% This can then be used to setup e.g. rerun messages.
+%    \begin{macrocode}
+\prg_new_conditional:Npnn \property_if_recorded:nn #1#2 { p , T , F,  TF }  
+  % #1 label #2 property
+  {
+    \tl_if_exist:cTF { g_@@_label_ \tl_to_str:n {#1} _ \tl_to_str:n {#2} _tl }
+      {
+        \prg_return_true:
+      }
+      {
+        \prg_return_false:
+      }
+  }
+%    \end{macrocode}
+% \end{macro}
+%
+% \begin{macro}{\IfPropertyRecordedTF}
+%    \begin{macrocode}
+\cs_new_eq:NN \IfPropertyRecordedTF \property_if_recorded:nnTF  
+%    \end{macrocode}
+% \end{macro}
+% 
+% \begin{macro}{\property_ref_undefined_warn:}
+%    \begin{macrocode}
+\cs_new_protected:Npn \property_ref_undefined_warn:
+ {
+   \flag_if_raised:nT { l_@@_ref_flag }
+    {
+      \G at refundefinedtrue
+    }
+ }
+%    \end{macrocode}
+% \end{macro}
+%
+% \begin{macro}{\property_ref_undefined_warn:n}
+%    \begin{macrocode}
+\cs_new_protected:Npn \property_ref_undefined_warn:n #1 %#1 label
+  {
+   \property_if_recorded:nF {#1}
+    {
+       \G at refundefinedtrue
+       \@latex at warning{Reference~`#1'~on~page~\thepage\space undefined}%
+    }         
+  }
+%    \end{macrocode}
+% \end{macro}
+%
+% \begin{macro}{\property_ref_undefined_warn:nn, \RefUndefinedWarn}
+%    \begin{macrocode}
+\cs_new_protected:Npn \property_ref_undefined_warn:nn #1#2 %#1 label, #2 property
+ {
+   \property_if_recorded:nTF {#1}
+    {
+      \property_if_recorded:nnF {#1}{#2}
+        {
+          \G at refundefinedtrue
+          \@latex at warning
+           { Property~`#2'~undefined~for~reference~`#1'~on~page~\thepage }
+        }
+    }
+    {
+       \G at refundefinedtrue
+       \@latex at warning { Reference~`#1'~on~page~\thepage\space undefined }%
+    }         
+ }
+\cs_set_eq:NN \RefUndefinedWarn \property_ref_undefined_warn:nn
+%    \end{macrocode}
+% \end{macro}
+%
+% \subsection{Predeclared properties}
+%
+% \begin{variable}{abspage}
+%    \begin{macrocode}
+\property_new:nnnn { abspage } { shipout }
+  { 0 } { \int_use:N \g_shipout_readonly_int }
+%    \end{macrocode}
+% \end{variable}
+%
+% \begin{variable}{page}
+%    \begin{macrocode}
+\property_new:nnnn  { page } { shipout } { 0 } { \thepage }
+%    \end{macrocode}
+% \end{variable}
+% 
+% \begin{variable}{pagenum}
+%    \begin{macrocode}
+\property_new:nnnn  { pagenum } { shipout } { 0 } { \the \value { page } }
+%    \end{macrocode}
+% \end{variable}
+% 
+% \begin{variable}{currentlabel}
+%    \begin{macrocode}
+\property_new:nnnn  { currentlabel } { now } { ?? } { \@currentlabel }
+%    \end{macrocode}
+% \end{variable}
+% 
+% \begin{variable}{title}
+%    \begin{macrocode}
+\property_new:nnnn  { title } { now } 
+  { \exp_not:n { \textbf { ?? } } } { \@currentlabelname }
+%    \end{macrocode}
+% \end{variable}
+% 
+% \begin{variable}{target}
+%    \begin{macrocode}
+\property_new:nnnn  { target } { now } { } { \@currentHref }
+%    \end{macrocode}
+% \end{variable}
+%
+% \begin{variable}{counter}
+%    \begin{macrocode}
+\property_new:nnnn  { counter } { now }  { } { \@currentcounter }
+%    \end{macrocode}
+% \end{variable}
+% 
+% \begin{variable}{xpos,ypos}
+%    \begin{macrocode}
+\property_new:nnnn  { xpos } { shipout } { 0} { \int_use:N \tex_lastxpos:D }
+\property_new:nnnn  { ypos } { shipout } { 0} { \int_use:N \tex_lastypos:D } 
+%    \end{macrocode}
+% \end{variable}
+%  
+%  \subsection{Messages}
+%    \begin{macrocode}
+\msg_new:nnnn { property } { exists }
+    { property~'#1'~ has~ already~ been~ declared. }
+    { There~ already~ exists~ a~ property~ declaration~ with~ this~
+      name.\\
+      Please~ use~ a~ different~ name~ for~ your~ property.}
+      
+\msg_new:nnnn { property } { not-declared }
+  { Property~'#1'~not~declared. }
+  {
+    LaTeX~has~been~asked~to~use~property~'#1',~but~this~
+    name~has~not~been~declared.
+  }
+\msg_new:nnnn { property } { unknown-setpoint }
+  { Unknown~keyword~'#3'~for~setting~property~'#1'. }
+  {
+    LaTeX~has~been~asked~to~set~the~property~'#1',~but~the~keyword~
+    '#3'~is~not~one~of~the~two~known~values:~'now'~or~'shipout'.
+  }
+%    \end{macrocode}
+%
+%    \begin{macrocode}
+%
+%<latexrelease>\IncludeInRelease{0000/00/00}{ltproperties}
+%<latexrelease>                 {cross-referencing~properties~(undo)}%
+%<latexrelease>
+%<latexrelease>\let \NewProperty  \@undefined
+%<latexrelease>\let \SetProperty \@undefined
+%<latexrelease>
+%<latexrelease>\let \RecordProperties  \@undefined
+%<latexrelease>\let \RefProperty \@undefined
+%<latexrelease>\let \RefUndefinedWarn  \@undefined
+%<latexrelease>
+%<latexrelease>\let \IfPropertyExistTF \@undefined
+%<latexrelease>\let \IfLabelExistTF \@undefined
+%<latexrelease>\let \IfPropertyRecordedTF  \@undefined
+%<latexrelease>
+%<latexrelease>\let\new at label@record \@undefined
+%<latexrelease>\let\@kernel at new@label at record@testdef\@undefined
+%<latexrelease>\EndModuleRelease
+%    \end{macrocode}
+%
+%    \begin{macrocode}
+\ExplSyntaxOff
+%    \end{macrocode}
+%
+%    \begin{macrocode}
+%</2ekernel|latexrelease>
+%    \end{macrocode}
+%
+%    Reset module prefix:
+%    \begin{macrocode}
+%<@@=>
+%    \end{macrocode}
+%
+%
+% \end{implementation}
+%
+% \Finale





More information about the latex3-commits mailing list.