% \iffalse %% File: l3xref.dtx Copyright (C) 2006 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/cgi-bin/cvsweb.cgi/ %% %% 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. %% %% ----------------------------------------------------------------------- % %\RequirePackage{l3names} %<*dtx|testfile> %\fi \def\GetIdInfo$Id: #1.dtx #2 #3-#4-#5 #6 #7$#8{% \def\fileversion{#2}% \def\filedate{#3/#4/#5}% %\iffalse % \ProvidesFile{#1.dtx}[#3/#4/#5 v#2 #8]% %<-dtx> \ProvidesFile{\jobname.tex}[#3/#4/#5 v#2 #8]% %\fi } %\iffalse % %\fi \GetIdInfo$Id: l3xref.dtx 579 2006-11-25 23:38:03Z morten $ {L3 Experimental cross referencing} % % \iffalse %<*driver> \documentclass{l3doc} \begin{document} \DocInput{l3xref.dtx} \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 % % % \section{Cross references} % % \begin{function}{\xref_set_label:n} % \begin{syntax} % "\xref_set_label:n" "{""}" % \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" "{""}" "{""}" % \end{syntax} % Defines a new cross reference type . This defines the token % list pointer |\l_xref_curr_||_tlp| 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" "{""}" "{""}" % \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} % \begin{syntax} % "\xref_get_value:nn" "{""}" "{""}" % \end{syntax} % Extracts the cross reference information of type for the % label . This operation is expandable. % \end{function} % % % \StopEventually{} % % \subsection{Implementation} % % % We start by ensuring that the required packages are loaded. % \begin{macrocode} %<*package> \RequirePackage{l3quark} \RequirePackage{l3toks} \RequirePackage{l3io} \RequirePackage{l3prop} \RequirePackage{l3int} \RequirePackage{l3token} % %<*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_plist| and % |\g_xref_all_curr_deferred_fields_plist| and the reference type % \meta{xyz} exists as the key-info pair |\xref_|\meta{xyz}|_key| % |{\l_xref_curr_|\meta{xyz}|_tlp}| 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_plist|. % % 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_plist} % \begin{macro}{\g_xref_all_curr_deferred_fields_plist} % 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_plist \prop_new:N \g_xref_all_curr_deferred_fields_plist % \end{macrocode} % \end{macro} % \end{macro} % % \begin{macro}{\xref_new:nn} % \begin{macro}{\xref_deferred_new:nn} % \begin{macro}{\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} \def_new:Npn \xref_new:nn {\xref_new_aux:nnn{immediate}} \def_new:Npn \xref_deferred_new:nn {\xref_new_aux:nnn{deferred}} \def_new: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_plist} { xref_ #2 _key } { \exp_not:c {l_xref_curr_#2_tlp }} % \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} \def_protected:cpn { xref_#2_key }{} \tlp_new:cn{l_xref_curr_#2_tlp}{#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:NN| and other goodies % but I prefer readable code. The end result for the input |xyz| is % \begin{verbatim} % \def: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 { \def:cpn {xref_get_value_#2_aux:w} ##1 } \exp_not:c { xref_#2_key } } \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} \def_new:Npn \xref_get_value:nn #1#2 { \cs_if_really_free:cTF{g_xref_#2_plist} {??} { % \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:NN {xref_get_value_#1_aux:w} \prop_use:N {g_xref_#2_plist} % \end{macrocode} % Better put in the stop marker. % \begin{macrocode} \q_nil } } \def:NNn \exp_after:cc 2 { \exp_after:NN \exp_after:NN \cs:w #1\exp_after:NN\cs_end: \cs:w #2\cs_end: } % \end{macrocode} % \end{macro} % % \begin{macro}{\xref_define_label:nn} % \begin{macro}{\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} \def_protected_new: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} \def_new:Npn \xref_define_label_aux:nn #1#2 { \cs_if_really_free:cTF{g_xref_#1_plist} {\prop_new:c{g_xref_#1_plist}}{\WARNING} \toks_gset:cn{g_xref_#1_plist}{#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} \def:Npn \xref_set_label:n #1{ \def:Npx \tmp:w{\prop_use:N\g_xref_all_curr_immediate_fields_plist} \exp_args:NNx\iow_deferred_expanded:Nn \xref_write{ \xref_define_label:nn {#1} { \tmp:w \prop_use:N \g_xref_all_curr_deferred_fields_plist } } } % \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} % %<*showmemory> \showMemUsage % % \end{macrocode} % % % \begin{macrocode} %<*testfile> \documentclass{article} \usepackage{l3xref} \CodeStart \def:Npn \startrecording {\iow_open:Nn \xref_write {\jobname.xref}} \def:Npn \DefineCrossReferences { \group_begin: \NamesStart \InputIfFileExists{\jobname.xref}{}{} \group_end: } \AtBeginDocument{\DefineCrossReferences\startrecording} \xref_new:nn {name}{} \def:Npn \setname{\tlp_set:Nn\l_xref_curr_name_tlp} \def:Npn \getname{\xref_get_value:nn{name}} \xref_deferred_new:nn {page}{\thepage} \def:Npn \getpage{\xref_get_value:nn{page}} \xref_deferred_new:nn {valuepage}{\number\value{page}} \def:Npn \getvaluepage{\xref_get_value:nn{valuepage}} \let:NN \setlabel \xref_set_label:n \CodeStop \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} % % % \endinput % % $Log$ % Revision 1.4 2006/03/20 18:26:42 braams % Updated the copyright notice (2006) and demoted all implementation % sections to subsections and so on to clean up the toc for source3.tex % % Revision 1.3 2006/01/16 11:26:57 morten % Minor changes % % Revision 1.2 2006/01/15 14:07:31 braams % Activated \StopEventually{} % % Revision 1.1 2006/01/15 07:55:06 morten % New cross reference module including test file. % %