% \iffalse %% File: l3xref.dtx Copyright (C) 2006,2009 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 ``expl3 bundle'' (The Work in LPPL) %% and all files in that bundle must be distributed together. %% %% The released version of this bundle is available from CTAN. %% %% ----------------------------------------------------------------------- %% %% The development version of the bundle can be found at %% %% http://www.latex-project.org/svnroot/experimental/trunk/ %% %% for those people who are interested. %% %%%%%%%%%%% %% NOTE: %% %%%%%%%%%%% %% %% Snapshots taken from the repository represent work in progress and may %% not work or may contain conflicting material! We therefore ask %% people _not_ to put them into distributions, archives, etc. without %% prior consultation with the LaTeX Project Team. %% %% ----------------------------------------------------------------------- % %<*driver|package|testfile> \RequirePackage{l3names} % %\fi \GetIdInfo$Id: l3xref.dtx 1362 2009-05-28 20:19:21Z joseph $ {L3 Experimental cross referencing} %\iffalse %<*testfile> \ProvidesFile{\jobname.tex} [\filedate\space v\fileversion\space\filedescription] % %<*driver> %\fi \ProvidesFile{\filename.\filenameext} [\filedate\space v\fileversion\space\filedescription] %\iffalse \documentclass[full]{l3doc} \begin{document} \DocInput{\filename.\filenameext} \end{document} % % \fi % % % \title{The \textsf{l3xref} package\thanks{This file % has version number \fileversion, last % revised \filedate.}\\ % Cross references} % \author{\Team} % \date{\filedate} % \maketitle % % \begin{documentation} % % \begin{function}{\xref_set_label:n} % \begin{syntax} % "\xref_set_label:n" \Arg{name} % \end{syntax} % Sets a label in the text. Note that this function does not do % anything else than setting the correct labels. In particular, it % does not try to fix any spacing around the write node; this is a % task for the \textsf{galley2} module. % \end{function} % % \begin{function}{\xref_new:nn} % \begin{syntax} % "\xref_new:nn" \Arg{type} \Arg{value} % \end{syntax} % Defines a new cross reference type . This defines the token % list variable |\l_xref_curr_||_tl| with default value % which gets written fully expanded when |\xref_set_label:n| % is called. % \end{function} % % % \begin{function}{\xref_deferred_new:nn} % \begin{syntax} % "\xref_deferred_new:nn" \Arg{type} \Arg{value} % \end{syntax} % Same as |\xref_new:n| except for this one, the value written % happens when \TeX\ ships out the page. Page numbers use this one % obviously. % \end{function} % % \begin{function}{\xref_get_value:nn / (EXP)} % \begin{syntax} % "\xref_get_value:nn" \Arg{type} \Arg{name} % \end{syntax} % Extracts the cross reference information of type for the % label . This operation is expandable. % \end{function} % % % \end{documentation} % % \begin{implementation} % % \section{\pkg{l3xref} implementation} % % \subsection{Internal functions and variables} % % \begin{variable}{ \g_xref_all_curr_immediate_fields_prop | % \g_xref_all_curr_deferred_fields_prop } % What they say they are ":)" % \end{variable} % % \begin{variable}{ \xref_write } % A stream for writing cross references, although they are not required % to be in a separate file. % \end{variable} % % \begin{function}{\xref_define_label:nn} % \begin{syntax} % "\xref_define_label:nn" \Arg{name} \Arg{plist contents} % \end{syntax} % Define the property list for each label; used internally by % "\xref_set_label:n". % \end{function} % % \subsection{Module code} % % We start by ensuring that the required packages are loaded. % \begin{macrocode} %<*package> \ProvidesExplPackage {\filename}{\filedate}{\fileversion}{\filedescription} \package_check_loaded_expl: % %<*initex|package> % \end{macrocode} % % There are two kinds of information, namely information which is % \emph{immediate} like a section title and then there's \emph{deferred} % information like page numbers. Each reference type belong to one of % these categories, which we save internally as the property lists % |\g_xref_all_curr_immediate_fields_prop| and % |\g_xref_all_curr_deferred_fields_prop| and the reference type % \meta{xyz} exists as the key-info pair |\xref_|\meta{xyz}|_key| % |{\l_xref_curr_|\meta{xyz}|_tl}| on one of these lists. This way each % new entry type is just added as another key-info pair. % % When the cross references are generated at the beginning of the % document each will turn into a control sequence. Thus |\label{mylab}| % will internally refer to the property list |\g_xref_mylab_prop|. % % The extraction of values from this property list can be done in % several different ways but we want to keep the operation % expandable. Therefore we use a dedicated function for each type of % cross reference, which looks like this: % \begin{verbatim} % \xref_get_value_xyz_aux:w -> #1 \xref_xyz_key #2#3\q_nil{#2} % \end{verbatim} % This will throw away all the bits we don't need. In case |xyz| is % the first on the |mylab| property list |#1| is empty, if it's the % last key-info pair |#3| is empty. The value of the field can be % extracted with the function |\xref_get_value:nn| where the first % argument is the type and the second the label name so here it would % be |\xref_get_value:nn| |{xyz}| |{mylab}|. % % \begin{macro}{\g_xref_all_curr_immediate_fields_prop} % \begin{macro}{\g_xref_all_curr_deferred_fields_prop} % The two main property lists for storing information. They contain % key-info pairs for all known types. % \begin{macrocode} \prop_new:N \g_xref_all_curr_immediate_fields_prop \prop_new:N \g_xref_all_curr_deferred_fields_prop % \end{macrocode} % \end{macro} % \end{macro} % % \begin{macro}{\xref_new:nn} % \begin{macro}{\xref_deferred_new:nn} % \begin{macro}[aux]{\xref_new_aux:nnn} % Setting up a new cross reference type is fairly straight forward % when we follow the game plan mentioned earlier. % \begin{macrocode} \cs_new_nopar:Npn \xref_new:nn {\xref_new_aux:nnn{immediate}} \cs_new_nopar:Npn \xref_deferred_new:nn {\xref_new_aux:nnn{deferred}} \cs_new_nopar:Npn \xref_new_aux:nnn #1#2#3{ % \end{macrocode} % First put the new type in the relevant property list. % \begin{macrocode} \prop_gput:ccx {g_xref_all_curr_ #1 _fields_prop} { xref_ #2 _key } { \exp_not:c {l_xref_curr_#2_tl }} % \end{macrocode} % Then define the key to be a protected macro.\footnote{We could also % set it equal to \cs{scan_stop:} but this just feels ``cleaner''.} % \begin{macrocode} \cs_set_protected_nopar:cpn { xref_#2_key }{} \tl_new:cn{l_xref_curr_#2_tl}{#3} % \end{macrocode} % Now for the function extracting the value of a reference. We could % do this with a simple |\prop_if_in| thing put since we want to do % things in an expandable way we make a separate grabber for each % type---this is also faster. The grabber function can be defined by % using an intricate construction of |\exp_after:wN| and other goodies % but I prefer readable code. The end result for the input |xyz| is % \begin{verbatim} % \cs_set_nopar:Npn\xref_get_value_xyz_aux:w #1\xref_xyz_key #2#3\q_nil{#2} % \end{verbatim} % \begin{macrocode} \toks_set:Nx \l_tmpa_toks { \exp_not:n { \cs_set_nopar:cpn {xref_get_value_#2_aux:w} ##1 } \exp_not:N \q_prop \exp_not:c { xref_#2_key } \exp_not:N \q_prop } \toks_use:N \l_tmpa_toks ##2 ##3\q_nil {##2} } % \end{macrocode} % \end{macro} % \end{macro} % \end{macro} % % % \begin{macro}{\xref_get_value:nn} % Getting the correct value for a given label-type pair is a matter % of connecting the correct grabber functions and property list. % \begin{macrocode} \cs_new_nopar:Npn \xref_get_value:nn #1#2 { \cs_if_exist:cTF{g_xref_#2_prop} { % \end{macrocode} % This next expansion may look a little weird but it isn't if you % think about it! % \begin{macrocode} \exp_args:NcNc \exp_after:wN {xref_get_value_#1_aux:w} \toks_use:N {g_xref_#2_prop} % \end{macrocode} % Better put in the stop marker. % \begin{macrocode} \q_nil } {??} } % \end{macrocode} % Temporary! We expand the property list and so we can't have the % |\q_prop| marker just expand! % \begin{macrocode} \cs_set_nopar:Npn \exp_after:cc #1#2 { \exp_after:wN \exp_after:wN \cs:w #1\exp_after:wN\cs_end: \cs:w #2\cs_end: } \cs_set_protected:Npn \q_prop {\q_prop} % \end{macrocode} % \end{macro} % % \begin{macro}{\xref_define_label:nn} % \begin{macro}[aux]{\xref_define_label_aux:nn} % Define the property list for each label. We better do this in two % steps because the special catcode regime is in effect and since % some of the info fields are very likely to contain actual text, % we better make sure spaces aren't ignored! As for the meaning of % other characters then it is a possibility to also have a field % containing catcode instructions which can then be activated with % |\etex_scantokens:D|. % \begin{macrocode} \cs_new_protected_nopar:Npn \xref_define_label:nn { \group_begin: \char_set_catcode:nn {`\ }\c_ten \xref_define_label_aux:nn } % \end{macrocode} % If the label is already taken we have a multiply defined label and % we should do something about it. For now we don't do anything % spectacular. % \begin{macrocode} \cs_new_nopar:Npn \xref_define_label_aux:nn #1#2 { \cs_if_free:cTF{g_xref_#1_prop} {\prop_new:c{g_xref_#1_prop}}{\WARNING} \toks_gset:cn{g_xref_#1_prop}{#2} \group_end: } % \end{macrocode} % \end{macro} % \end{macro} % % % \begin{macro}{\xref_set_label:n} % Then the generic command for setting a label. We expand the % immediate labels fully before calling the write function but make % sure the deferred fields aren't expanded just yet. Due to property % lists being implemented as token list registers we must expand the % `immediate' fields twice. % \begin{macrocode} \cs_set_nopar:Npn \xref_set_label:n #1{ \cs_set_nopar:Npx \xref_tmp:w{\toks_use:N\g_xref_all_curr_immediate_fields_prop} \exp_args:NNx\iow_shipout_x:Nn \xref_write{ \xref_define_label:nn {#1} { \xref_tmp:w \toks_use:N \g_xref_all_curr_deferred_fields_prop } } } % \end{macrocode} % \end{macro} % % \begin{macro}{\xref_write} % A stream for writing cross references although they do not require % to be in a separate file. % \begin{macrocode} \iow_new:N \xref_write % \end{macrocode} % \end{macro} % % That's it (for now). % \begin{macrocode} % % \end{macrocode} % % \begin{macrocode} %<*showmemory> \showMemUsage % % \end{macrocode} % % \section{\pkg{l3xref} test file} % % \begin{macrocode} %<*testfile> \documentclass{article} \usepackage{l3xref} \ExplSyntaxOn \cs_set_nopar:Npn \startrecording {\iow_open:Nn \xref_write {\jobname.xref}} \cs_set_nopar:Npn \DefineCrossReferences { \group_begin: \ExplSyntaxNamesOn \InputIfFileExists{\jobname.xref}{}{} \group_end: } \AtBeginDocument{\DefineCrossReferences\startrecording} \xref_new:nn {name}{} \cs_set_nopar:Npn \setname{\tl_set:Nn\l_xref_curr_name_tl} \cs_set_nopar:Npn \getname{\xref_get_value:nn{name}} \xref_deferred_new:nn {page}{\thepage} \cs_set_nopar:Npn \getpage{\xref_get_value:nn{page}} \xref_deferred_new:nn {valuepage}{\number\value{page}} \cs_set_nopar:Npn \getvaluepage{\xref_get_value:nn{valuepage}} \cs_set_eq:NN \setlabel \xref_set_label:n \ExplSyntaxOff \begin{document} \pagenumbering{roman} Text\setname{This is a name}\setlabel{testlabel1}. More text\setname{This is another name}\setlabel{testlabel2}. \clearpage Text\setname{This is a third name}\setlabel{testlabel3}. More text\setname{Hello World!}\setlabel{testlabel4}. \clearpage \pagenumbering{arabic} Text\setname{Name 5}\setlabel{testlabel5}. More text\setname{Name 6}\setlabel{testlabel6}. \clearpage Text\setname{Name 7}\setlabel{testlabel 7}. More text\setname{Name 8}\setlabel{testlabel8}. \clearpage Now let's extract some values. \getname{testlabel1} on page \getpage{testlabel1} with value \getvaluepage{testlabel1}. Now let's extract some values. \getname{testlabel 7} on page \getpage{testlabel 7} with value \getvaluepage{testlabel 7}. \end{document} % % \end{macrocode} % % \end{implementation} % \PrintIndex % % \endinput